diff --git a/.gitattributes b/.gitattributes index 6126f5aac22..5aea6a69309 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,5 +2,9 @@ # needs additional setup, see tools/mapmerge/install.txt *.dmm merge=merge-dmm +# dmi icon merger hook +# needs additional setup, see tools/dmitool/merging.txt +*.dmi merge=merge-dmi + # force changelog merging to use union html/changelog.html merge=union diff --git a/README.md b/README.md index 8f2d270db37..d7e17e395b8 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,8 @@ where the admin rank must be properly capitalised. Finally, to start the server, run Dream Daemon and enter the path to your compiled tgstation.dmb file. Make sure to set the port to the one you specified in the config.txt, and set the Security box to 'Safe'. Then press GO -and the server should start up and be ready to join. +and the server should start up and be ready to join. It is also recommended that +you set up the SQL backend (see below). ###HOSTING ON LINUX We use BYGEX for some of our text replacement related code. Unfortunately, we @@ -112,9 +113,7 @@ To enable an away mission open fileList.txt in the _maps/RandomZLevels directory ##SQL SETUP -The SQL backend for the library and stats tracking requires a -MySQL server. Your server details go in /config/dbconfig.txt, and the SQL -schema is in /SQL/tgstation_schema.sql and /SQL/tgstation_schema_prefix.sql depending on if you want table prefixes. More detailed setup instructions are located here: http://www.tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database +The SQL backend requires a MySQL server. SQL is required for the library, stats tracking, admin notes, and job-only bans, among other features, mostly related to server administration. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql and /SQL/tgstation_schema_prefix.sql depending on if you want table prefixes. More detailed setup instructions are located here: http://www.tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database ##IRC BOT SETUP diff --git a/_maps/RandomZLevels/Academy.dmm b/_maps/RandomZLevels/Academy.dmm index e4cda266055..5feea503c69 100644 --- a/_maps/RandomZLevels/Academy.dmm +++ b/_maps/RandomZLevels/Academy.dmm @@ -22,7 +22,7 @@ "av" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "aw" = (/obj/structure/table/reinforced,/obj/item/weapon/paper_bin,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "ax" = (/obj/structure/bed/stool,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) -"ay" = (/obj/effect/decal/cleanable/cobweb,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) +"ay" = (/obj/effect/decal/cleanable/cobweb,/obj/structure/mirror/magic/lesser{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) "az" = (/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/item/stack/sheet/animalhide/monkey,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) "aA" = (/obj/structure/cable{d2 = 8; icon_state = "0-8"},/obj/machinery/light/small{dir = 1},/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) "aB" = (/obj/structure/bed,/obj/item/weapon/bedsheet/purple,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) @@ -30,12 +30,12 @@ "aD" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "aE" = (/obj/structure/filingcabinet,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) "aF" = (/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) -"aG" = (/obj/structure/safe/floor,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) +"aG" = (/turf/indestructible/rock,/area/space) "aH" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0},/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "aI" = (/obj/structure/table/reinforced,/obj/item/device/laser_pointer/upgraded,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) -"aJ" = (/obj/structure/cult/tome,/obj/item/weapon/staff,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) +"aJ" = (/obj/structure/cult/tome,/obj/item/weapon/dice/d20/fate,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) "aK" = (/obj/structure/bed/chair/wood/wings{dir = 8},/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) -"aL" = (/obj/item/clothing/suit/space/hardsuit/wizard,/obj/structure/table/wood,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) +"aL" = (/turf/simulated/wall/mineral/wood,/area/awaymission/academy/academycellar) "aM" = (/obj/structure/table/reinforced,/obj/item/weapon/storage/briefcase,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "aN" = (/obj/structure/table/reinforced,/obj/item/weapon/coin/plasma,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "aO" = (/obj/machinery/light/small{dir = 1},/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) @@ -94,7 +94,7 @@ "bP" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/awaymission/academy/headmaster) "bQ" = (/obj/effect/decal/cleanable/cobweb2,/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/turf/simulated/floor/plasteel{icon_state = "vault"; dir = 5},/area/awaymission/academy/headmaster) "bR" = (/obj/structure/grille,/obj/structure/cable{icon_state = "0-2"; pixel_y = 1; d2 = 2},/obj/structure/cable{d2 = 8; icon_state = "0-8"},/obj/structure/window/reinforced/fulltile,/turf/simulated/floor/plating,/area/awaymission/academy/headmaster) -"bS" = (/obj/effect/landmark{name = "awaystart"},/turf/simulated/floor/plasteel{icon_state = "showroomfloor"},/area/awaymission/academy/headmaster) +"bS" = (/obj/structure/academy_wizard_spawner,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) "bT" = (/turf/simulated/floor/plasteel{icon_state = "showroomfloor"},/area/awaymission/academy/headmaster) "bU" = (/obj/machinery/autolathe,/obj/structure/cable{icon_state = "0-2"; pixel_y = 1; d2 = 2},/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "bV" = (/turf/simulated/floor/plasteel{icon_state = "floorgrime"},/area/awaymission/academy/classrooms) @@ -136,7 +136,7 @@ "cF" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/machinery/door/airlock/plasma,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "cG" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/awaymission/academy/headmaster) "cH" = (/obj/structure/sign/nosmoking_1,/turf/simulated/wall,/area/awaymission/academy/headmaster) -"cI" = (/obj/structure/table,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0},/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) +"cI" = (/obj/structure/divine/trap/damage,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "cJ" = (/obj/item/stack/sheet/metal,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) "cK" = (/turf/simulated/floor/plasteel{dir = 1; icon_state = "warning"},/area/awaymission/academy/classrooms) "cL" = (/obj/structure/bed/chair{dir = 1},/turf/simulated/floor/plasteel,/area/awaymission/academy/headmaster) @@ -178,7 +178,7 @@ "dv" = (/obj/item/seeds/eggyseed,/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "dw" = (/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "dx" = (/turf/simulated/floor/plasteel{icon_state = "green"; dir = 4},/area/awaymission/academy/classrooms) -"dy" = (/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/effect/landmark{name = "awaystart"},/obj/item/weapon/weldingtool,/turf/simulated/floor/greengrid,/area/awaymission/academy/classrooms) +"dy" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel,/area/awaymission/academy/headmaster) "dz" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/structure/reagent_dispensers/fueltank,/turf/simulated/floor/greengrid,/area/awaymission/academy/classrooms) "dA" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/structure/reagent_dispensers/fueltank,/turf/simulated/floor/greengrid,/area/awaymission/academy/classrooms) "dB" = (/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0},/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) @@ -190,7 +190,7 @@ "dH" = (/obj/machinery/mech_bay_recharge_port,/obj/structure/cable,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) "dI" = (/obj/machinery/computer/mech_bay_power_console,/obj/structure/cable,/turf/simulated/floor/greengrid,/area/awaymission/academy/classrooms) "dJ" = (/obj/machinery/hydroponics/constructable,/obj/item/seeds/replicapod,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) -"dK" = (/obj/structure/table/wood,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) +"dK" = (/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) "dL" = (/obj/structure/bed/chair{dir = 8},/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "dM" = (/obj/machinery/hydroponics/constructable,/obj/item/seeds/bluespacetomatoseed,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "dN" = (/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) @@ -301,21 +301,21 @@ "fO" = (/obj/structure/closet/crate/trashcart,/turf/simulated/floor/plasteel{icon_state = "grimy"},/area/awaymission/academy/academyaft) "fP" = (/turf/simulated/floor/plasteel{icon_state = "vault"},/area/awaymission/academy/classrooms) "fQ" = (/obj/structure/cable{icon_state = "0-2"; pixel_y = 1; d2 = 2},/turf/simulated/floor/plasteel{icon_state = "vault"},/area/awaymission/academy/classrooms) -"fR" = (/obj/structure/bed/chair,/turf/simulated/floor/plasteel{icon_state = "vault"},/area/awaymission/academy/classrooms) +"fR" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{dir = 1; icon_state = "warning"},/area/awaymission/academy/classrooms) "fS" = (/obj/structure/window/reinforced,/turf/simulated/floor/plasteel{icon_state = "delivery"; name = "floor"},/area/awaymission/academy/classrooms) "fT" = (/obj/structure/table/reinforced,/turf/simulated/floor/plasteel{dir = 4; icon_state = "warning"},/area/awaymission/academy/classrooms) "fU" = (/obj/item/target,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "fV" = (/obj/machinery/light/small{dir = 1},/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "fW" = (/obj/structure/target_stake,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "fX" = (/obj/machinery/light/small{dir = 8},/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) -"fY" = (/obj/effect/landmark{name = "awaystart"},/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) +"fY" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{icon_state = "red"},/area/awaymission/academy/classrooms) "fZ" = (/obj/structure/grille,/obj/structure/cable,/obj/structure/cable{icon_state = "0-2"; pixel_y = 1; d2 = 2},/obj/structure/window/reinforced/fulltile,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) "ga" = (/turf/simulated/floor/plasteel{dir = 8; icon_state = "warning"},/area/awaymission/academy/classrooms) "gb" = (/obj/structure/table,/obj/item/weapon/pen/red,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) "gc" = (/obj/structure/table,/obj/item/weapon/lazarus_injector,/turf/simulated/floor/plasteel{icon_state = "whitehall"; dir = 4},/area/awaymission/academy/classrooms) "gd" = (/obj/structure/bed/chair/wood/normal,/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) -"ge" = (/obj/structure/bookcase,/obj/item/weapon/book/manual/wiki/engineering_hacking,/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) -"gf" = (/obj/structure/bookcase,/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) +"ge" = (/obj/item/candle/infinite,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"gf" = (/obj/structure/holohoop,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) "gg" = (/obj/structure/grille,/obj/structure/cable{icon_state = "0-4"; d2 = 4},/obj/structure/cable{icon_state = "0-2"; pixel_y = 1; d2 = 2},/obj/structure/window/reinforced/fulltile,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) "gh" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/turf/simulated/floor/plasteel{icon_state = "grimy"},/area/awaymission/academy/academyaft) "gi" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/turf/simulated/floor/carpet,/area/awaymission/academy/academyaft) @@ -333,7 +333,7 @@ "gu" = (/obj/structure/window/reinforced,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) "gv" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor/plasteel{icon_state = "whitehall"; dir = 4},/area/awaymission/academy/classrooms) "gw" = (/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) -"gx" = (/obj/structure/bookcase,/obj/item/weapon/book/manual/medical_cloning,/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) +"gx" = (/obj/structure/closet/athletic_mixed,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) "gy" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor/plasteel{icon_state = "grimy"},/area/awaymission/academy/academyaft) "gz" = (/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/plasteel{icon_state = "vault"; dir = 5},/area/awaymission/academy/classrooms) "gA" = (/obj/machinery/door/window{dir = 4},/obj/item/ammo_casing,/turf/simulated/floor/plasteel{dir = 4; icon_state = "warning"},/area/awaymission/academy/classrooms) @@ -348,8 +348,8 @@ "gJ" = (/obj/structure/table,/turf/simulated/floor/plasteel{icon_state = "vault"; dir = 5},/area/awaymission/academy/classrooms) "gK" = (/obj/structure/window/reinforced,/obj/item/ammo_casing,/turf/simulated/floor/plasteel{icon_state = "delivery"; name = "floor"},/area/awaymission/academy/classrooms) "gL" = (/mob/living/simple_animal/hostile/bear,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) -"gM" = (/obj/structure/bookcase,/obj/item/weapon/book/manual/hydroponics_pod_people,/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) -"gN" = (/obj/structure/bookcase,/obj/item/weapon/book/manual/barman_recipes,/obj/item/weapon/book/manual/wiki/security_space_law,/obj/item/weapon/book/manual/wiki/security_space_law,/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) +"gM" = (/obj/item/toy/beach_ball/holoball,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"gN" = (/obj/structure/mineral_door/wood,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) "gO" = (/obj/structure/bed/stool,/turf/simulated/floor/plasteel{icon_state = "vault"; dir = 5},/area/awaymission/academy/classrooms) "gP" = (/turf/simulated/floor/plasteel{dir = 4; icon_state = "warning"},/area/awaymission/academy/classrooms) "gQ" = (/obj/machinery/light/small{dir = 8},/mob/living/simple_animal/hostile/bear,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) @@ -399,7 +399,7 @@ "hI" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/turf/simulated/floor/carpet,/area/awaymission/academy/academyaft) "hJ" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/turf/simulated/floor/carpet,/area/awaymission/academy/academyaft) "hK" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/item/weapon/caution,/turf/simulated/floor/plasteel{icon_state = "green"; dir = 4},/area/awaymission/academy/academyaft) -"hL" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/turf/simulated/floor/plating,/area/awaymission/academy/academyaft) +"hL" = (/obj/structure/mecha_wreckage/durand,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) "hM" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/turf/simulated/floor/plasteel{icon_state = "hydrofloor"},/area/awaymission/academy/academyaft) "hN" = (/obj/machinery/constructable_frame/machine_frame,/turf/simulated/floor/plating,/area/awaymission/academy/academyaft) "hO" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/turf/simulated/floor/plating,/area/awaymission/academy/academyaft) @@ -442,7 +442,7 @@ "iz" = (/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/plasteel{icon_state = "cafeteria"; dir = 2},/area/awaymission/academy/academyaft) "iA" = (/obj/structure/sink{dir = 4; icon_state = "sink"; pixel_x = 11; pixel_y = 0},/turf/simulated/floor/plasteel{icon_state = "cafeteria"; dir = 2},/area/awaymission/academy/academyaft) "iB" = (/obj/structure/rack,/obj/item/weapon/stock_parts/manipulator,/obj/item/weapon/stock_parts/subspace/filter,/turf/simulated/floor/plasteel{icon_state = "hydrofloor"},/area/awaymission/academy/academyaft) -"iC" = (/obj/structure/toilet{dir = 8},/obj/machinery/light/small{dir = 1},/obj/effect/landmark{name = "awaystart"},/turf/simulated/floor/plasteel{icon_state = "cafeteria"; dir = 2},/area/awaymission/academy/academyaft) +"iC" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/carpet,/area/awaymission/academy/headmaster) "iD" = (/obj/structure/toilet{dir = 4},/obj/machinery/light/small{dir = 1},/turf/simulated/floor/plasteel{icon_state = "cafeteria"; dir = 2},/area/awaymission/academy/academyaft) "iE" = (/obj/machinery/light{icon_state = "tube1"; dir = 8},/obj/structure/rack,/obj/item/stack/sheet/metal,/turf/simulated/floor/plasteel{icon_state = "hydrofloor"},/area/awaymission/academy/academyaft) "iF" = (/obj/machinery/power/port_gen/pacman,/turf/simulated/floor/plasteel{icon_state = "hydrofloor"},/area/awaymission/academy/academyaft) @@ -481,7 +481,7 @@ "jm" = (/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/carpet,/area/awaymission/academy/academyaft) "jn" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0},/turf/simulated/floor/plasteel/airless{icon_state = "white"; dir = 4},/area/awaymission/academy/academyaft) "jo" = (/obj/structure/table,/obj/item/organ/internal/brain{name = "The preserved brain of Harry Houdini"},/turf/simulated/floor/plasteel/airless{icon_state = "whitered"; dir = 4},/area/awaymission/academy/academyaft) -"jp" = (/obj/effect/landmark{name = "awaystart"},/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/turf/simulated/floor/plasteel{icon_state = "red"; dir = 4},/area/awaymission/academy/academyaft) +"jp" = (/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/item/weapon/weldingtool,/turf/simulated/floor/greengrid,/area/awaymission/academy/classrooms) "jq" = (/turf/simulated/floor/plasteel{dir = 8; icon_state = "yellow"},/area/awaymission/academy/academyaft) "jr" = (/turf/simulated/floor/plasteel{dir = 4; icon_state = "yellow"},/area/awaymission/academy/academyaft) "js" = (/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/turf/simulated/floor/plasteel/airless{icon_state = "white"; dir = 4},/area/awaymission/academy/academyaft) @@ -534,7 +534,7 @@ "kn" = (/obj/machinery/gateway{dir = 1},/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"},/turf/simulated/floor/plating,/area/awaymission/academy/academygate) "ko" = (/obj/machinery/gateway{dir = 5},/turf/simulated/floor/plating,/area/awaymission/academy/academygate) "kp" = (/obj/machinery/gateway{dir = 8},/turf/simulated/floor/plating,/area/awaymission/academy/academygate) -"kq" = (/obj/structure/cable,/obj/machinery/gateway/centeraway{calibrated = 0},/turf/simulated/floor/plating,/area/awaymission/academy/academygate) +"kq" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{dir = 1; icon_state = "yellow"},/area/awaymission/academy/classrooms) "kr" = (/obj/machinery/gateway{dir = 4},/turf/simulated/floor/plating,/area/awaymission/academy/academygate) "ks" = (/obj/machinery/gateway{dir = 10},/turf/simulated/floor/plating,/area/awaymission/academy/academygate) "kt" = (/obj/machinery/gateway,/turf/simulated/floor/plating,/area/awaymission/academy/academygate) @@ -543,131 +543,261 @@ "kw" = (/obj/machinery/button/door{id = "AcademyGate"; pixel_y = -24},/turf/simulated/floor/carpet{icon_state = "carpetsymbol"},/area/awaymission/academy/academygate) "kx" = (/turf/simulated/floor/carpet{icon_state = "carpetsymbol"},/area/awaymission/academy/academygate) "ky" = (/obj/machinery/door/poddoor/shutters{id = "AcademyGate"},/turf/simulated/floor/plating{dir = 2; icon_state = "warnplate"},/area/awaymission/academy/academygate) +"kz" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/carpet,/area/awaymission/academy/classrooms) +"kA" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{icon_state = "escape"; dir = 6},/area/awaymission/academy/classrooms) +"kB" = (/obj/structure/bed/chair,/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{icon_state = "vault"},/area/awaymission/academy/classrooms) +"kC" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/carpet,/area/awaymission/academy/academyaft) +"kD" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel,/area/awaymission/academy/academyaft) +"kE" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{icon_state = "hydrofloor"},/area/awaymission/academy/academyaft) +"kF" = (/obj/structure/sink{icon_state = "sink"; dir = 8; pixel_x = -12; pixel_y = 2},/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{icon_state = "cafeteria"; dir = 2},/area/awaymission/academy/academyaft) +"kG" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel{icon_state = "cafeteria"; dir = 2},/area/awaymission/academy/academyaft) +"kH" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/turf/simulated/floor/plasteel{icon_state = "red"; dir = 4},/area/awaymission/academy/academyaft) +"kI" = (/obj/structure/cable,/obj/machinery/gateway/centeraway{calibrated = 1},/turf/simulated/floor/plating,/area/awaymission/academy/academygate) +"kJ" = (/obj/structure/mecha_wreckage/honker,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) +"kK" = (/obj/structure/table,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0},/obj/item/weapon/book/manual/ripley_build_and_repair,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) +"kL" = (/obj/item/weapon/bikehorn,/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) +"kM" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0},/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/structure/divine/trap/chill,/turf/simulated/floor/plating,/area/awaymission/academy/academyaft) +"kN" = (/obj/structure/divine/trap/fire,/turf/simulated/floor/engine,/area/awaymission/academy/academyaft) +"kO" = (/mob/living/simple_animal/hostile/retaliate/clown,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kP" = (/obj/effect/decal/cleanable/cobweb,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kQ" = (/mob/living/simple_animal/hostile/syndicate,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kR" = (/obj/effect/rune/malformed,/mob/living/simple_animal/hostile/skeleton,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kS" = (/obj/structure/table/wood,/obj/item/candle/infinite,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kT" = (/obj/structure/table/wood,/obj/item/weapon/kitchen/knife/ritual,/obj/item/candle/infinite,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kU" = (/obj/effect/decal/remains/human,/obj/effect/rune/malformed,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kV" = (/obj/effect/spawner/structure/window/reinforced,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kW" = (/obj/structure/ladder/unbreakable/rune{id = "academy_cellar"},/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kX" = (/obj/structure/bookcase/random,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kY" = (/obj/effect/decal/cleanable/xenoblood,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"kZ" = (/obj/effect/decal/cleanable/cobweb2,/obj/effect/decal/cleanable/blood/old,/obj/effect/decal/remains/human,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"la" = (/obj/structure/divine/trap/damage,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"lb" = (/mob/living/simple_animal/hostile/retaliate/bat,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"lc" = (/obj/effect/decal/cleanable/cobweb,/turf/simulated/floor/mineral/uranium,/area/awaymission/academy/academycellar) +"ld" = (/turf/simulated/floor/mineral/uranium,/area/awaymission/academy/academycellar) +"le" = (/obj/effect/spider/stickyweb,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"lf" = (/obj/effect/decal/cleanable/cobweb2,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"lg" = (/obj/structure/table/wood,/obj/item/weapon/guardiancreator/choose,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"lh" = (/obj/structure/falsewall/wood,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"li" = (/mob/living/simple_animal/hostile/retaliate/bat,/turf/simulated/floor/mineral/uranium,/area/awaymission/academy/academycellar) +"lj" = (/mob/living/simple_animal/hostile/poison/giant_spider/hunter,/turf/simulated/floor/engine/cult,/area/awaymission/academy/academycellar) +"lk" = (/obj/structure/safe/floor,/obj/item/voodoo,/obj/item/weapon/gun/magic/wand/fireball,/obj/item/clothing/suit/space/hardsuit/wizard,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) +"ll" = (/turf/indestructible/riveted,/area/awaymission/academy/academyengine) +"lm" = (/obj/structure/table/wood,/turf/simulated/floor/wood,/area/awaymission/academy/headmaster) +"ln" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0},/obj/structure/cable{icon_state = "0-2"; d2 = 2},/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"lo" = (/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"lp" = (/obj/machinery/power/apc{dir = 1; pixel_y = 32},/obj/structure/cable{d2 = 8; icon_state = "0-8"},/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"lq" = (/turf/simulated/wall/rust,/area/awaymission/academy/academyengine) +"lr" = (/obj/machinery/power/terminal{dir = 8},/obj/structure/cable,/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"ls" = (/obj/machinery/power/smes/magical,/obj/structure/cable,/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"lt" = (/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"lu" = (/obj/machinery/light{dir = 1},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lv" = (/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lw" = (/obj/item/weapon/ectoplasm,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lx" = (/obj/effect/rune/malformed,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"ly" = (/obj/effect/decal/cleanable/oil,/obj/structure/table,/obj/item/weapon/staff,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lz" = (/obj/structure/table,/obj/item/weapon/storage/toolbox/electrical,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lA" = (/obj/structure/table,/obj/item/stack/rods,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lB" = (/obj/effect/decal/cleanable/oil,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lC" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lD" = (/obj/structure/table,/obj/item/weapon/weldingtool,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lE" = (/obj/structure/rack,/obj/item/stack/cable_coil,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lF" = (/turf/indestructible/fakeglass,/area/awaymission/academy/academyengine) +"lG" = (/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lH" = (/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lI" = (/obj/structure/ladder/unbreakable/rune{id = "academy_engine"},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lJ" = (/obj/structure/mineral_door/iron,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lK" = (/obj/effect/immovablerod{dir = 4},/obj/structure/cable,/obj/structure/cable{icon_state = "0-2"; pixel_y = 1; d2 = 2},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lL" = (/mob/living/simple_animal/slaughter{name = "engine demon"},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lM" = (/obj/machinery/light,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lN" = (/obj/structure/rack,/obj/item/weapon/storage/toolbox/mechanical,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lO" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lP" = (/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lQ" = (/obj/structure/table,/obj/item/weapon/gun/magic/wand/polymorph,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lR" = (/obj/structure/table,/obj/item/stack/sheet/animalhide/monkey,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lS" = (/obj/structure/table,/obj/item/stack/sheet/runed_metal,/obj/item/stack/sheet/mineral/wood,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lT" = (/obj/structure/table,/obj/item/stack/cable_coil,/obj/item/stack/sheet/xenochitin,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lU" = (/obj/structure/closet/radiation,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lV" = (/obj/structure/reagent_dispensers/fueltank,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"lW" = (/obj/structure/ladder/unbreakable/rune{height = 1; id = "academy_storage"},/turf/simulated/floor/plating,/area/awaymission/academy/classrooms) +"lX" = (/obj/structure/ladder/unbreakable/rune{height = 1; id = "academy_coldroom"},/turf/simulated/floor/plasteel{icon_state = "white"},/area/awaymission/academy/classrooms) +"lY" = (/obj/structure/table/wood,/obj/item/seeds/kudzuseed,/turf/simulated/floor/plasteel,/area/awaymission/academy/classrooms) +"lZ" = (/obj/structure/bookcase/random,/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) +"ma" = (/obj/structure/ladder/unbreakable/rune{height = 1; id = "academy_cellar"},/turf/simulated/floor/wood,/area/awaymission/academy/classrooms) +"mb" = (/obj/structure/ladder/unbreakable/rune{height = 1; id = "academy_engine"},/turf/simulated/floor/plasteel,/area/awaymission/academy/academyaft) +"mc" = (/turf/simulated/wall,/area/awaymission/academy/academyengine) +"md" = (/turf/simulated/wall/r_wall,/area/awaymission/academy/academyengine) +"me" = (/obj/structure/constructshell,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"mf" = (/obj/structure/constructshell,/obj/machinery/light{dir = 1},/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"mg" = (/obj/effect/decal/cleanable/cobweb,/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"mh" = (/obj/machinery/light{dir = 1},/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"mi" = (/obj/machinery/door/airlock/vault,/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"mj" = (/obj/structure/rack,/obj/item/weapon/spellbook/oneuse/summonitem,/obj/item/weapon/pen/fourcolor,/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"mk" = (/obj/structure/rack,/obj/item/weapon/claymore,/obj/item/toy/figure/wizard,/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"ml" = (/mob/living/simple_animal/hostile/creature,/turf/simulated/floor/vault,/area/awaymission/academy/academyengine) +"mm" = (/obj/structure/closet/coffin,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"mn" = (/obj/structure/rack,/obj/item/weapon/gun/magic/wand,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"mo" = (/obj/structure/rack,/obj/item/weapon/staff,/turf/simulated/floor/plating,/area/awaymission/academy/academyengine) +"mp" = (/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/plasteel,/area/awaymission/academy/academyengine) +"mq" = (/turf/simulated/floor/plasteel,/area/awaymission/academy/academyengine) +"mr" = (/obj/machinery/light{dir = 1},/turf/simulated/floor/plasteel,/area/awaymission/academy/academyengine) +"ms" = (/obj/machinery/light{dir = 1},/turf/simulated/floor/light,/area/awaymission/academy/academyengine) +"mt" = (/turf/simulated/floor/light,/area/awaymission/academy/academyengine) +"mu" = (/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mv" = (/obj/machinery/light{dir = 1},/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mw" = (/obj/item/target,/turf/simulated/floor/plasteel,/area/awaymission/academy/academyengine) +"mx" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/light,/area/awaymission/academy/academyengine) +"my" = (/obj/structure/bed/chair/wood/normal,/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mz" = (/obj/structure/table/wood/poker,/obj/item/toy/cards/deck,/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mA" = (/obj/structure/bed/chair/wood/normal{dir = 4},/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mB" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mC" = (/obj/structure/bed/chair/wood/normal{dir = 8},/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mD" = (/turf/simulated/mineral/random,/area/awaymission/academy/academycellar) +"mE" = (/obj/effect/decal/cleanable/cobweb,/turf/simulated/floor/plating,/area/awaymission/academy/academygate) +"mF" = (/obj/effect/decal/cleanable/blood/old,/turf/simulated/floor/plating,/area/awaymission/academy/academygate) +"mG" = (/mob/living/simple_animal/hostile/skeleton,/turf/simulated/floor/plating,/area/awaymission/academy/academygate) +"mH" = (/obj/effect/decal/remains/robot,/turf/simulated/floor/plating,/area/awaymission/academy/academygate) +"mI" = (/mob/living/simple_animal/hostile/wizard,/turf/simulated/floor/plasteel,/area/awaymission/academy/academyengine) +"mJ" = (/obj/structure/table/wood,/obj/item/weapon/reagent_containers/food/drinks/bottle/gin,/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mK" = (/obj/machinery/vending/magivend,/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mL" = (/obj/structure/table/wood,/obj/item/weapon/reagent_containers/food/drinks/drinkingglass,/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mM" = (/obj/structure/bed/chair/wood/normal{dir = 1},/turf/simulated/floor/wood,/area/awaymission/academy/academyengine) +"mN" = (/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mO" = (/obj/structure/reagent_dispensers/beerkeg,/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mP" = (/obj/effect/decal/remains/human,/turf/simulated/floor/plating,/area/awaymission/academy/academygate) +"mQ" = (/obj/structure/ladder/unbreakable/rune{id = "academy_storage"},/turf/simulated/floor/plasteel,/area/awaymission/academy/academyengine) +"mR" = (/obj/machinery/light,/turf/simulated/floor/plasteel,/area/awaymission/academy/academyengine) +"mS" = (/obj/structure/ladder/unbreakable/rune{id = "academy_coldroom"},/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mT" = (/obj/effect/decal/cleanable/blood/old,/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mU" = (/obj/item/weapon/reagent_containers/food/snacks/meat/slab,/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mV" = (/obj/structure/rack,/obj/item/weapon/reagent_containers/food/drinks/bottle/wine,/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mW" = (/obj/effect/decal/remains/human,/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mX" = (/mob/living/simple_animal/hostile/bear,/turf/simulated/floor/plating/snow/cold,/area/awaymission/academy/academycellar) +"mY" = (/turf/simulated/mineral/random,/area/space) (1,1,1) = {" -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabacacacacacacacacabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababadaeafaeaeagaeahababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababahahaiahahahaiahahajababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacakahahahahalamananaoapaqacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacarahahasasatasasasauavawacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaeaxahasayazaAaBasapaxaeacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaCahaDasaEaFaFaGasaHahaIacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaeaxahasaJaKaFaLasapaxaeacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaMahahasasasasasasapahakacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaNahahahahaOahalanavahafacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababahahahahahahapahahahababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPababababaQababaRababababaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaTaFaFahaFaFapaFaFasabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaFaFaFahaFaUavaFaFaFabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPacaFahahahaVapahahahaWacaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaXahahaYaZbabbahahbcabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPaPabbdahbebfbgbhbiahahbjabaPaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPaPaaacaFahahahahbkblahahbmacaaaPaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaPaPabaFbnaFaFahapaFaFbnboabaPaPabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaLaLaLaLaLaLaLaLaLaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaLaLdKdKgegfgedKdKaLaLaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaLaLdKdKdKdKdKdKdKdKdKaLaLaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaLgxdKdKdKdKdKdKdKdKdKdKaLaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaLgxdKdKdKgedKgedKdKdKdKaLaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaLaLdKdKdKdKgMdKdKdKdKaLaLaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaLaLdKdKdKdKdKdKdKaLaLaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaLaLdKdKdKdKdKaLaLaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaLaLgedKgeaLaLaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaLaLaLaLaLaLaLaLaGaLaLgNaLaLaGaLaLaLaLaLaLaLaLaLaLaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaLdKdKdKdKdKdKaLaGaGaLdKaLaGaGaLkOaLaLkPaLaLkQaLaLaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaLdKkRkSdKkUkTaLaLaLgedKgeaLaLaLkVaLaLkVaLaLkVaLaLaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaLdKdKdKdKdKdKgNdKdKdKkWdKdKdKgNdKdKdKdKdKdKdKgeaLaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaLdKdKdKdKdKdKaLaLaLgedKgeaLaLaLkVaLaLkVaLaLkVaLaLaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaLkXkXkXkXkXkXaLaGaGaLdKaLaGaGaLkYaLaLdKaLaLkZaLaLaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaLaLaLaLaLaLaLaLaLaLaLaLaLgNaLaLaLaLaLaLaLaLaLaLaLaLaLaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaLdKladKaLlclblddKldlbaLledKdKlfaLaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabacacacacacacacacabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaLlgladKlhlblddKlilbldaLledKljdKaLaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababadaeafaeaeagaeahababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaLdKdKdKaLlddKlidKlddKlhleledKljaLaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababahahaiahahahaiahahajababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaLaLaLaLaLaLaLaLaLaLaLaLaLaLaLaLaLaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacakahahahahalamananaoapaqacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacarahahasasatasasasauavawacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaeaxahasayazaAaBasapaxaeacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaCahaDasaEaFaFlkasaHahaIacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGllllllllllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaeaxahasaJaKbSlmasapaxaeacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGlllolnlpllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaMahahasasasasasasapahakacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGlqlqlqlqlqlqlqlqlqlqlqlqlqlqlqaGaGaGlllslrltllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaNahahahahaOahalanavahafacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGlqlvlulvlvlvlwlxlvlulvlzlylAlqlqlqlqllllllllllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababahahahahahahapahahahababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGlqlvlvlBlvlvlvlClwlvlvlvlvlDlqlElulBlFlHlGlvllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPababababaQababaRababababaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGlqlIlvlvlvlvlvlvlvlvlvlvlvlvlJlvlvlvlFlvlKlLllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaTaFaFcIaFaFapaFaFasabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGlqlvlvlvlvlBlvlvlvlvlBlvlvlvlqlNlMlvlFlPlOlvllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaFaFaFahaFaUavaFaFaFabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGlqlvlvlMlvlRlQlTlSlvlMlvlUlVlqlqlqlqllllllllllaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPacaFahahahaVapahahahaWacaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGlqlqlqlqlqlqlqlqlqlqlqlqlqlqlqaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaXahahaYaZbabbahahbcabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPaPabbdahbebfbgbhbiahahbjabaPaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPaPaaacaFahahahahbkblahahbmacaaaPaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaG +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPabaPaPabaFbnaFaFahapaFaFbnboabaPaPabaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPaPaPaaaaabasasasasbpbqasasasasabaaaaaPaPaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaPaPaaaPaaaaabbrbrbrbrahapbrbrbrbrabaaaaaPaaaPaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaabsbtbubvbvbwbtbsbsbsbsbsbsbsbsbsbsabbrbxbybrahapbrbxbybrababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaabsbsbzbAbBbBbCbzbDbEbFbGbGbGbGbGbGbHasbIbJbKbrahapbrbJbKbLasbMbNbObPbQbRbSbTababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbUbzbBbBbBbBbVbzbEbWbXbYbZbZcabZbWasbrcbccbrahapbrbrbrbrabcdcecdasascfbTcgchabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscibzbBcjbBbBbzbzbEbWbZbZbZbXbZbZbWasbrbrbrbrahapahahahahckclcmcnascocfbTbScpabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscqbVbBbBbBbBbzcrbEcsbZbZbZbZbZbZcsasbrctctbrahapcuahcuahabcdcecdcvcwcxbTbTcyabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsczbzbBbBbBbBbzbzbEcAcBbZbZbZbZbZcCasbrcDcDbrahcEanananancFbOcGclcHascfbTbScpabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscIbzbBbBcJbBbzbVbEcAcKcKcKcKcKcKcCasbrcLbrbrahapbrbrbrbrabcdclcdclcdcfcgbTchabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscMbzcNbBbBcObzbVbEcPcQcQcQcQcQcQcRascSbxbybrahapbrbxbybLascTcdclcUcVcWbTbTababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaabsbsbzbAbBbBbCbzbDbEbFbGbGbGbGbGbGbHasbIbJbKbrahapbrbJbKbLasbMbNbObPbQbRbTbTababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbUbzbBbBbBbBbVbzbEbWbXbYbZbZcabZbWasbrcbccbrahapbrdybrbrabcdcecdasascfbTcgchabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscibzbBcjhLbBbzbzbEbWbZbZbZbXbZbZbWasbrbrbrbrahapahahahahckclcmcnascocfbTbTcpabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscqbVbBbBbBbBbzcrbEcsbZbZbZbZbZbZcsasbrctctdyahapcuahcuahabcdcecdcvcwcxbTbTcyabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsczbzkJbBlWbBbzbzbEcAcBbZbZbZbZbZcCasbrcDcDbrahcEanananancFbOcGclcHascfbTbTcpabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabskKbzkLbBcJbBbzbVbEcAcKcKcKcKfRcKcCasbrcLbrbrahapbrdybrbrabcdclcdclcdcfcgbTchabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscMbzcNbBbBcObzbVbEcPcQfYcQcQcQcQcRascSbxbybriCapbrbxbybLascTcdclcUcVcWbTbTababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabscXcYcZcZcZcZdabzbEdbdcdcdcdcdcdcddasbrbJbKbrahapbrbJbKbrasasasasasasasasasabbsbsbsbsbsbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsdededebVbVbzcMbzbEdcdcdfdcdfdcdgdcasbrbrbrbrahapbrbrbrbrasdhdidjdkdidididjdidlbEdmdmdnbsbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsdededebVbVbzcMbzbEdcdcdfdcdfdcdgdcasbrbrbrbrahapbrbrbrbrasdhdidjdkdidididjdidlbElXdmdnbsbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsdodpdqbVbzbzcMdrbEdcdcdsdcdsdcdsdcasaFaFaFaFahapaFaFaFaFasdtdudvbzdudwbzbzbzdxbEdmdmdmdmbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsdydzdAcYcYcYdBbzbEdcdcdcdcdfdcdfdcasaFdCdCaFahapaFdCdCaFasdtbzbzbzbzbzbzdDdEdxdFdmdmdmdGbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsdHdpdIbVbzbzcMbzbEdcdcdcdcdsdcdsdcasaFdCdCaFahapaFdCdCaFasdtdudwbzdudJbzdKdLdxbEdmdmdmdmbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsjpdzdAcYcYcYdBbzbEdcdcdcdcdfdcdfdcasaFdCdCaFahapaFdCdCaFasdtbzbzbzbzbzbzdDdEdxdFdmdmdmdGbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsdHdpdIbVbzbzcMbzbEdcdcdcdcdsdcdsdcasaFdCdCaFahapaFdCdCaFasdtdudwbzdudJbzlYdLdxbEdmdmdmdmbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsdededebzbVbzcMbzbEdcdcdcdcdcdcdcdcasaFdCdCaFahapaFdCdCaFasdtbzbzbzbzbzbzdMdNdOdPdQdRdmdSbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbsbsbsbEbEbEdTdUbEbEbEdVdVbEbEbEbEbEasasasasasahapasasasasasdtdudwbzbzdWbzbzdXdxbEdYdZdmdSbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabseaeabsebecedeeefegeheiejejejejejekejehejejejejelemejejejejbEeneoeoeoeoeobzbzepeqbEdYdZdmerbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsesesbseteueveeefejejejejejejejejewejejejejejejejewejejejejbEbEbEbEbEbEbEexexbEbEbEbEeybEbsbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaezeAbBbBeBbzeCbzbzefejelelelelelelelemelelelelelelelemelelelejbEeDeDeDeEeFeDeDeDeDeGeHeHeIeHbsbsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeJbsbBbBeseKeKeKeLefejeleMeNeleMeNeleOeNeleMeNeleMeNemeMeNelePbEeDeDeDeQeDeDeDeDeDeReHeHeIeSbsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeJbsbBbBeseTeTeTeUefejeleVeWeleVeWeleXeWeleVeWeleVeWemeVeWelejeYeDeDeDeZeDeEeDeDeDeGeHeHeIfabsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeJbsbBbBeseKkqeKeLefejeleMeNeleMeNeleOeNeleMeNkzeMeNemeMeNelePbEeDeDeDeQeDkAeDeDeDeReHeHeIeSbsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeJbsbBbBeseTeTeTeUefejeleVeWkzeVeWeleXeWeleVeWeleVeWemeVeWelejeYeDeDeDeZeDeEeDeDeDeGeHeHeIfabsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafbeAbBbBeBbzfcbzbzefejelelelelelelelemelelelfdfefefefffefefefgfhfifififififjfifififkflflfmfnbsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsesesbsfoeufpeeefejejejejejejejejewejejejewejejejejejejejejbEeDfqfrfseDeZeDeDeDeGeHeHeHftbsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsesesbsfoeufpeeefejejejejejejejejewejejejewejejejejejejejejbEeDfqfrfseDeZeDkAeDeGeHeHeHftbsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabseaeabsfufvfweefxfyfgfgfgfgfzejejfAfgfgfgfBelejejelelejejejbEeDeDeDeDfCeDeDeDeDeGeHeHeHbsbsaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbsbsbsbsbsbEdTdUbEbEbEbEbEesesesesbEbEfDfEfFfDfDfFfFfDbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbsbsaPaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbBbBbBfGfHfIfJfKfKfLbEfMdcdcdcdcdcbEfNfEfFfNfNfFfFfObEfPfQfRfPfPcAfSfTfUfVbzbzfWbsbsaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsfXfYbBfZgabzbzbzgbgcbEdcgddcgegfdcggghgifFfNfNfFfFfNbEgjgkglgmgjcAbzgnbzbzbzbzbsbsaPaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbBbBbBfGfHfIfJfKfKfLbEfMdcdcdcdcdcbEfNfEfFfNfNfFfFfObEfPfQkBfPfPcAfSfTfUfVbzbzfWbsbsaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsfXbBbBfZgabzbzbzgbgcbEdcgddclZlZdcggghgifFfNfNfFfFfNbEgjgkglgmgjcAbzgnbzbzbzbzbsbsaPaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaabsgobBbBgpgabzgqbzbzgrbEdcdfdcdcdcdcgsfNfFfFfNfNfFfFfNbEgjgtgjgjgjcAfSfTbzbzfWbsbsaPaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaabsgugugufZgabzdubzbzgvbEgwdsdcgfgxdcgsfNfFfFfNfNfFfFgybEgzgtgjgjgjcAbzgAbzgBbsbsaPaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaabsgugugufZgabzdubzbzgvbEgwdsdclZlZdcgsfNfFfFfNfNfFfFgybEgzgtgjgjgjcAbzgAbzgBbsbsaPaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaabsgCgCgCfZgabzgDbzgqgrbEdcgddcdcdcdcgEghgFgGfNfNfFfFfNbEgjgtgHgIgJcAgKfTfWbsbsaPaaaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaaaabsbBgLbBgpgabzdubzdugrbEdcdfdcgMgNdcbEfNfFfEfNfNfFfFfNbEgjgtgOgOgOcAbzgPbsbsaPaaaaaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaaaabsbBgLbBgpgabzdubzdugrbEdcdfdclZlZdcbEfNfFfEfNfNfFfFfNbEgjgtgOgOgOcAbzgPbsbsaPaaaaaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaabsgQbBbBfZgabzgqbzgqgrbEdcdsdcdcdcdcgRfFfFgSghghgFgFgFgTgUgVgJgJgWcAgXbsbsaPaaaaaaaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbBbBgLgYgZhahbhahbdmbEdcdcdcdcdcdcbEfNfFfEfNfNfFfFfNbEgjgjgOgOgOcAbsbsaPaaaaaaaaaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsbBbBgLgYgZhahbhahbdmbEmadcdcdcdcdcbEfNfFfEfNfNfFfFfNbEgjgjgOgOgOcAbsbsaPaaaaaaaaaaaaaPaaaPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchchchchchchchchchchcfDfDfDfDfDfDfDfDfDhdfDfDhefDfDfDfDfDfDfDfDfDhchchchchchchchchchchchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhghhhhhihjhkhlhchmhchnhohohphohohqfFfFfEfFfFfFfFfFhnhohohphohohrhchmhchshthuhvhwhxhfhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhyhzhhhAhohBhChDhEhDhFhGhGhGhGhGhHgFgFhIhJgFgFgFgFhFhGhGhGhGhGhKhDhLhDhjhMhshohfhfhNhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhghhhhhihjhkhlhchmhchnhohohphohohqfFkCfEfFfFfFfFfFhnhohohphohohrhchmhchshthuhvhwhxhfhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhyhzhhhAhohBhChDhEhDhFhGhGhGhGhGhHgFgFhIhJgFgFgFgFhFhGhGhGhGhGhKhDkMhDhjhMhshohfhfhNhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhOhfhfhPhshshQhchRhchnhShThUhVhWfDhXhXhXfEfFhXhXhXfDhYhZiaibichqhchRhcidiehshshfhfhfhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhyhzhhifighoihhchchcfDfDfDfDfDfDfDhXiihXfEfFhXiihXfDfDfDfDfDfDfDhchchcijhshshshfhfhNhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhOhfhfikhshsihhcaahcilililimilinfDhXiihXfEfFhXiihXfDililimilililhcaahciohshohshfhfhfhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhOhfhfikhshsihhcaahcilililimilinfDhXiihXfEkChXiihXfDililimilililhcaahciohskDhshfhfhfhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfiphzhhhMhshsiqhcaahcililililililirhXiihXfEfFhXiihXirililililililhcaahcisithshshfhfhNhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhfhfhfhshohsiuhcaahcivililiwfDfDfDixiihXfEfFhXiiiyfDfDfDizililiAhcaahciBhshshshfhfhfhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchshoighshshohQhcaahcililililiriCfDhXiihXfEfFhXiihXfDiDirililililhcaahciEhohshshshohshchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchshshohohsiFhcaahcivilililfDfDfDhXiihXfEfFhXiihXfDfDfDilililiAhcaahchohohshshohshchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchfhfhfhfkEhohsiuhcaahcivililiwfDfDfDixiihXfEfFhXiiiyfDfDfDizililiAhcaahciBhskEhshfhfhfhfhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchshoighshshohQhcaahcililililiriGfDhXiihXfEfFhXiihXfDiDirililililhcaahciEhohshshshohshchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchshsmbhohsiFhcaahckFilililfDfDfDhXiihXfEfFhXiihXfDfDfDilililiAhcaahchohohshshohshchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchshohshshohcaPhcililililiriGfDiHiihXfEfFhXiihXfDiDirililililhcaPhchohshshsiIhchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchchciJhchcaahcivililiwfDfDfDhXiihXfEfFhXiihXfDfDfDizililiAhcaahchciJhchchchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchchchciJhchcaahcivilkGiwfDfDfDhXiihXfEfFhXiihXfDfDfDizilkGiAhcaahchciJhchchchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahciKiLiMiMhcaPhcililililiriNfDhXhXiOgifFhXhXhXfDiDirililililhcaPhciMiMiLiPhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaahciMiMiMiMhcaPhchchcfDfDfDfDfDfDfDhdfDfDhefDfDfDfDfDfDfDhchchcaPhciMiMiMiMhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaahckNkNkNkNhcaPhchchcfDfDfDfDfDfDfDhdfDfDhefDfDfDfDfDfDfDhchchcaPhckNkNkNkNhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaahciQiQiQiQhcaaaaaahciRiSiTghiUgFgFhIgFgFgFgFgFgFghiViWiXhcaaaaaahciQiQiQiQhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaaaaaaaSaSaaaaaaaaaaaaaaaaaahciYiZiZjahcaaaaaajbhGjcjdfNfEfFfFfFfFfFfFfFfFfFfNjejfjgjhaaaaaahciYiZiZjahcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaaaaaaaSaSaaaaaaaaaaaaaaaaaahciYiZiZjahcaaaaaajbhGjcjdfNfEfFfFfFfFfFkCfFfFfFfNjejfjgjhaaaaaahciYiZiZjahcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahcjijjjdfNjkfDfDjlfDfDjlfDfDjmfNjejnjohcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajbhGjpjdfNfEfFjqhohohohojrfFfFfNjejsjgjtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajbhGkHjdfNfEfFjqkDhohohojrfFfFfNjejsjgjtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSaSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahcjujvjdfNfEfFjwjxjxjxjxjyfFfFfNjejzjAhcjBaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchcjCjdfNfEjDfFfFfFfFfFfFjDfFfNjejChchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchcjEfNjFeujGfFfFfFfFjHeujGfNjEhchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahchcfNjIjJgFgGfFfFfFfFjKfFfNhchcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahcfNfNfNfNfEfFfFfFfNfNfNfNhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahcfNjLjMfNfEfFfFfFfNjNjOfNhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahcjPhchchcjQhchcjRhchchcjPhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSjTjUjVjVjWjTjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajXjVjUjVjVjWjVjXaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajXjVjUjVjVjWjVjXaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajXjYjZjYjYkajYjXaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSjWkbkckckckdjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSjWjWkejWjWkfkgkhjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWjWkikjkfkfkfkkkljWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWkfkfkmknkokfkfkfjWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWkfkfkpkqkrkfkfkfjWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWjWkfksktkukfkfjWjWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSkvjWkfkfkfkfjWkvjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSkwkfkfkfkfkxjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSkykykykyjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahcjPhchchcjQhchcjRhchchcjPhcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSjTjUjVjVjWjTjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajXjVjUjVjVjWjVjXaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGmcmcmcmcmcmcmcmcmcmcmcmdmdmdmdmdaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajXjVjUjVjVjWjVjXaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGmcmemflvlvlvlulvlvlvlumdmgltmhmdaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajXjYjZjYjYkajYjXaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmemelvlvlvlvlvlvlvlvmiltmjltmdaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSjWkbkckckckdjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmemelvlvlBlvlvlvlClvmdltmkltmdaGaGaGaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSjWjWkejWjWkfkgkhjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmclvlvlvlvlvlvlvlvlvlvmdltmlltmdaGaGaGaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWjWkikjkfkfkfkkkljWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmmmmlvmnmnmcmcmcmclJmcmdmdmdmdmcmcaGaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWkfkfkmknkokfkfkfjWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmmmmlvmomomcmpmqmqmqmqmqmrmqmqmqmcaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWkfkfkpkIkrkfkfkfjWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmmmmlMmomomcmqmcmcmcmcmcmcmcmclJmcmcmcaGaGaGaGaGaGaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjWjWkfksktkukfkfjWjWjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmcmcmcmcmcmcmcmqmcmcmtmsmtmumumumumumvmcaGaGaGaGaGaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSkvjWkfkfkfkfjWkvjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmqmrmqmwmwmwmqmqmcmcmtmtmtmumumumumumumcaGaGaGaGaGaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSkwkfkfkfkfkxjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmqmqmqmqmqmqmqmqmcmcmtmtmxmumumymumumumcaGaGaGaGaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSkykykykyjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmqmqmqmqmqmqmqmqmcmcmumumumumAmzmCmBmumcaGaGaGaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaGaGaGaGaGaGmDmDmDmDmDmDaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSmEkfmFkfmGmHjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmwmqmqmqmqmImqmqmcmcmKmJmLmumumMmumumumcaGaGaGaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaGaGaGmDmDmDmDmDmDmNmNmOmOmDaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSkfmGkfmPmFkfjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmwmQmqmqmqmqmqmqmcmcmcmcmclFlFlFlFlFlFmcaGaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaGaGmDmDmNmNmNmNmNmNmNmOmOmDmDmDaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSkfmFmGkfmGkfjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGmcmwmqmqmqmRmqmqmqmcaGaGaGaGaaaaaaaaaaaaaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaGmDmNmNmSmNmNmNmTmNmOmOmDmDmDmDmDmDaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajSjSjSjSjSjSjSjSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGmcmcmcmcmcmcmcmcmcmcaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaGmDmNmNmNmNmUmNmNmNmNmDmDmDmNmNmNmDmDmDaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaGaGaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaGmDmVmNmTmUmNmUmNmNmNmDmDmNmNmWmNmNmNmDaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaGmDmVmNmNmNmUmNmNmNmDmDmDmNmNmNmNmXmNmDaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaGmDmVmUmNmNmTmNmNmNmDmDmDmNmXmNmNmNmNmDaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaGmDmDmNmNmNmNmNmUmNmNmNmNmNmNmUmNmXmNmDaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaGmDmDmDmNmNmNmNmNmNmNmNmNmNmTmNmNmDmDaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaGaGmDmDmDmDmNmNmNmNmDmDmDmDmDmDmDaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaGaGaGmDmDmDmDmDmDmYaGaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaGaGaGaGaGaGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 4b6d445a3db..95aa9e2af9a 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -140,7 +140,7 @@ Pipelines + Other Objects -> Pipe network 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, 20, target = src) && !gc_destroyed) + if (do_after(user, 20/W.toolspeed, target = src) && !gc_destroyed) user.visible_message( \ "[user] unfastens \the [src].", \ "You unfasten \the [src].", \ diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm index 8af67d3555a..67581c3aa98 100644 --- a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm @@ -266,7 +266,7 @@ if (WT.remove_fuel(0,user)) playsound(loc, 'sound/items/Welder.ogg', 40, 1) user << "You begin welding the vent..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) if(!welded) diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm index ac52a1d1178..c67090bf6d5 100644 --- a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm @@ -297,7 +297,7 @@ if(WT.remove_fuel(0,user)) playsound(loc, 'sound/items/Welder.ogg', 40, 1) user << "Now welding the scrubber." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index dc7a82ac4ae..0791d5c563f 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -8,8 +8,8 @@ var/datum/subsystem/events/SSevent var/list/running = list() //list of all existing /datum/round_event var/scheduled = 0 //The next world.time that a naturally occuring random event can be selected. - var/frequency_lower = 3000 //5 minutes lower bound. - var/frequency_upper = 9000 //15 minutes upper bound. Basically an event will happen every 5 to 15 minutes. + var/frequency_lower = 1800 //3 minutes lower bound. + var/frequency_upper = 6000 //10 minutes upper bound. Basically an event will happen every 3 to 10 minutes. var/list/holidays //List of all holidays occuring today or null if no holidays var/wizardmode = 0 diff --git a/code/datums/spell.dm b/code/datums/spell.dm index 8596b5267fe..29fe4dfd7ed 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -41,6 +41,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin 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 times has as a cooldown. Make sure to set this for every spell + var/player_lock = 1 //If it can be used by simple mobs var/overlay = 0 var/overlay_icon = 'icons/obj/wizard.dmi' @@ -62,9 +63,13 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin /obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0,mob/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)) - user << "You shouldn't have this spell! Something's wrong." - return 0 + if(player_lock) + if(!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list)) + user << "You shouldn't have this spell! Something's wrong." + return 0 + else + if(!(src in user.mob_spell_list)) + return 0 if(user.z == ZLEVEL_CENTCOM && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel return 0 @@ -170,8 +175,9 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin /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() - user.attack_log += text("\[[time_stamp()]\] [user.real_name] ([user.ckey]) cast the spell [name].") + invocation(user) + if(user.ckey) + user.attack_log += text("\[[time_stamp()]\] [user.real_name] ([user.ckey]) cast the spell [name].") spawn(0) if(charge_type == "recharge" && recharge) start_recharge() @@ -180,7 +186,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin if(prob(critfailchance)) critfail(targets) else - cast(targets) + cast(targets,user=user) after_cast(targets) /obj/effect/proc_holder/spell/proc/before_cast(list/targets) @@ -227,7 +233,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin smoke.start() -/obj/effect/proc_holder/spell/proc/cast(list/targets) +/obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr) return /obj/effect/proc_holder/spell/proc/critfail(list/targets) @@ -334,7 +340,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin revert_cast(user) return - perform(targets) + perform(targets,user=user) return @@ -349,7 +355,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin revert_cast() return - perform(targets) + perform(targets,user=user) return @@ -418,7 +424,7 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin if(!user) revert_cast() return - perform(user) + perform(null,user=user) /obj/effect/proc_holder/spell/self/basic_heal //This spell exists mainly for debugging purposes, and also to show how casting works name = "Lesser Heal" diff --git a/code/datums/spells/area_teleport.dm b/code/datums/spells/area_teleport.dm index 49f19c3a23f..8497a05007a 100644 --- a/code/datums/spells/area_teleport.dm +++ b/code/datums/spells/area_teleport.dm @@ -32,8 +32,8 @@ return thearea -/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea) - playsound(get_turf(usr), sound1, 50,1) +/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/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)) @@ -67,22 +67,22 @@ if(!success) target.loc = pick(L) - playsound(get_turf(usr), sound2, 50,1) + playsound(get_turf(user), sound2, 50,1) return -/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null) +/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null,mob/user = usr) 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) + user.say("[invocation] [uppertext(chosenarea.name)]") + if(user.gender==MALE) + playsound(user.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) + playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) if("whisper") - usr.whisper("[invocation] [uppertext(chosenarea.name)]") + user.whisper("[invocation] [uppertext(chosenarea.name)]") return diff --git a/code/datums/spells/charge.dm b/code/datums/spells/charge.dm index 0c7c2800b2b..0b7ba942075 100644 --- a/code/datums/spells/charge.dm +++ b/code/datums/spells/charge.dm @@ -12,9 +12,9 @@ include_user = 1 -/obj/effect/proc_holder/spell/targeted/charge/cast(list/targets) - for(var/mob/living/user in targets) - var/list/hand_items = list(user.get_active_hand(),user.get_inactive_hand()) +/obj/effect/proc_holder/spell/targeted/charge/cast(list/targets,mob/user = usr) + for(var/mob/living/L in targets) + var/list/hand_items = list(L.get_active_hand(),L.get_inactive_hand()) var/charged_item = null var/burnt_out = 0 for(var/obj/item in hand_items) @@ -38,15 +38,15 @@ if(istype(item, /obj/item/weapon/spellbook/oneuse)) var/obj/item/weapon/spellbook/oneuse/I = item if(prob(80)) - user.visible_message("[I] catches fire!") + L.visible_message("[I] catches fire!") qdel(I) else I.used = 0 charged_item = I break else - user << "Glowing red letters appear on the front cover..." - user << "[pick("NICE TRY BUT NO!","CLEVER BUT NOT CLEVER ENOUGH!", "SUCH FLAGRANT CHEESING IS WHY WE ACCEPTED YOUR APPLICATION!", "CUTE!", "YOU DIDN'T THINK IT'D BE THAT EASY, DID YOU?")]" + L << "Glowing red letters appear on the front cover..." + L << "[pick("NICE TRY BUT NO!","CLEVER BUT NOT CLEVER ENOUGH!", "SUCH FLAGRANT CHEESING IS WHY WE ACCEPTED YOUR APPLICATION!", "CUTE!", "YOU DIDN'T THINK IT'D BE THAT EASY, DID YOU?")]" burnt_out = 1 else if(istype(item, /obj/item/weapon/gun/magic)) var/obj/item/weapon/gun/magic/I = item @@ -86,9 +86,9 @@ charged_item = item break if(!charged_item) - user << "you feel magical power surging to your hands, but the feeling rapidly fades..." + L << "you feel magical power surging to your hands, but the feeling rapidly fades..." else if(burnt_out) - user << "[charged_item] doesn't seem to be reacting to the spell..." + L << "[charged_item] doesn't seem to be reacting to the spell..." else - playsound(get_turf(usr), "sound/magic/Charge.ogg", 50, 1) - user << "[charged_item] suddenly feels very warm!" + playsound(get_turf(L), "sound/magic/Charge.ogg", 50, 1) + L << "[charged_item] suddenly feels very warm!" diff --git a/code/datums/spells/conjure.dm b/code/datums/spells/conjure.dm index 82320dbc4ca..c6d5f24bf8f 100644 --- a/code/datums/spells/conjure.dm +++ b/code/datums/spells/conjure.dm @@ -15,8 +15,8 @@ var/cast_sound = 'sound/items/welder.ogg' -/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets) - playsound(get_turf(usr), cast_sound, 50,1) +/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/user = usr) + playsound(get_turf(user), cast_sound, 50,1) for(var/turf/T in targets) if(T.density && !summon_ignore_density) targets -= T diff --git a/code/datums/spells/dumbfire.dm b/code/datums/spells/dumbfire.dm index 66b0dfbe183..38a42ccb037 100644 --- a/code/datums/spells/dumbfire.dm +++ b/code/datums/spells/dumbfire.dm @@ -22,13 +22,13 @@ /obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr) - var/turf/T = get_turf(usr) + var/turf/T = get_turf(user) for(var/i = 1; i < range; i++) - var/turf/new_turf = get_step(T, usr.dir) + var/turf/new_turf = get_step(T, user.dir) if(new_turf.density) break T = new_turf - perform(list(T)) + perform(list(T),user = user) /obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr) playMagSound() @@ -46,7 +46,7 @@ projectile.dir = get_dir(projectile, target) projectile.name = proj_name - var/current_loc = usr.loc + var/current_loc = user.loc projectile.loc = current_loc @@ -60,12 +60,12 @@ step(projectile, projectile.dir) if(projectile.loc == current_loc || i == proj_lifespan) - projectile.cast(current_loc) + projectile.cast(current_loc,user=user) break - var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - usr + var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user if(L && L.stat != DEAD) - projectile.cast(L.loc) + projectile.cast(L.loc,user=user) break if(proj_trail && projectile) diff --git a/code/datums/spells/emplosion.dm b/code/datums/spells/emplosion.dm index ce92204f70b..4004237e7fd 100644 --- a/code/datums/spells/emplosion.dm +++ b/code/datums/spells/emplosion.dm @@ -8,8 +8,8 @@ action_icon_state = "emp" sound = "sound/weapons/ZapBang.ogg" -/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets) - playsound(get_turf(usr), sound, 50,1) +/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets,mob/user = usr) + playsound(get_turf(user), sound, 50,1) for(var/mob/living/target in targets) empulse(target.loc, emp_heavy, emp_light) diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm index 3fd6266d19c..bb0a0d7b8c6 100644 --- a/code/datums/spells/ethereal_jaunt.dm +++ b/code/datums/spells/ethereal_jaunt.dm @@ -15,8 +15,8 @@ var/jaunt_duration = 50 //in deciseconds action_icon_state = "jaunt" -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets) //magnets, so mostly hardcoded - playsound(get_turf(usr), 'sound/magic/Ethereal_Enter.ogg', 50, 1, -1) +/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) target.notransform = 1 //protects the mob from being transformed (replaced) midjaunt and getting stuck in bluespace spawn(0) @@ -50,7 +50,7 @@ jaunt_steam(mobloc) target.canmove = 0 holder.reappearing = 1 - playsound(get_turf(usr), 'sound/magic/Ethereal_Exit.ogg', 50, 1, -1) + playsound(get_turf(user), 'sound/magic/Ethereal_Exit.ogg', 50, 1, -1) sleep(20) jaunt_reappear(animation, target) sleep(5) diff --git a/code/datums/spells/explosion.dm b/code/datums/spells/explosion.dm index 7bb7195c2dd..def7db816f2 100644 --- a/code/datums/spells/explosion.dm +++ b/code/datums/spells/explosion.dm @@ -7,7 +7,7 @@ var/ex_light = 3 var/ex_flash = 4 -/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets) +/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) diff --git a/code/datums/spells/genetic.dm b/code/datums/spells/genetic.dm index 969372b1203..575ab6767ae 100644 --- a/code/datums/spells/genetic.dm +++ b/code/datums/spells/genetic.dm @@ -15,7 +15,7 @@ 6th bit - ? */ -/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets,mob/user = usr) playMagSound() for(var/mob/living/carbon/target in targets) if(!target.dna) diff --git a/code/datums/spells/inflict_handler.dm b/code/datums/spells/inflict_handler.dm index e82d3ce2003..fe91ace65a7 100644 --- a/code/datums/spells/inflict_handler.dm +++ b/code/datums/spells/inflict_handler.dm @@ -19,7 +19,7 @@ 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) +/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets,mob/user = usr) for(var/mob/living/target in targets) playsound(target,sound, 50,1) diff --git a/code/datums/spells/knock.dm b/code/datums/spells/knock.dm index 6b772162c7b..647c6a5d7f7 100644 --- a/code/datums/spells/knock.dm +++ b/code/datums/spells/knock.dm @@ -12,8 +12,8 @@ action_icon_state = "knock" -/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets) - usr << sound("sound/magic/Knock.ogg") +/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets,mob/user = usr) + user << sound("sound/magic/Knock.ogg") for(var/turf/T in targets) for(var/obj/machinery/door/door in T.contents) spawn(1) diff --git a/code/datums/spells/lichdom.dm b/code/datums/spells/lichdom.dm index ca80db2b644..0e1042a8d1e 100644 --- a/code/datums/spells/lichdom.dm +++ b/code/datums/spells/lichdom.dm @@ -34,35 +34,35 @@ ticker.mode.round_ends_with_antag_death = 1 ..() -/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets) - for(var/mob/user in targets) +/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets,mob/user = usr) + for(var/mob/M in targets) var/list/hand_items = list() - if(iscarbon(user)) - hand_items = list(user.get_active_hand(),user.get_inactive_hand()) + if(iscarbon(M)) + hand_items = list(M.get_active_hand(),M.get_inactive_hand()) if(marked_item && !stat_allowed) //sanity, shouldn't happen without badminry marked_item = null return if(stat_allowed) //Death is not my end! - if(user.stat == CONSCIOUS && iscarbon(user)) - user << "You aren't dead enough to revive!" //Usually a good problem to have + if(M.stat == CONSCIOUS && iscarbon(M)) + M << "You aren't dead enough to revive!" //Usually a good problem to have charge_counter = charge_max return if(!marked_item || qdeleted(marked_item)) //Wait nevermind - user << "Your phylactery is gone!" + M << "Your phylactery is gone!" return - var/turf/user_turf = get_turf(user) + var/turf/user_turf = get_turf(M) var/turf/item_turf = get_turf(marked_item) if(user_turf.z != item_turf.z) - user << "Your phylactery is out of range!" + M << "Your phylactery is out of range!" return - if(isobserver(user)) - var/mob/dead/observer/O = user + if(isobserver(M)) + var/mob/dead/observer/O = M O.reenter_corpse() var/mob/living/carbon/human/lich = new /mob/living/carbon/human(item_turf) @@ -72,8 +72,8 @@ lich.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(lich), slot_wear_suit) lich.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(lich), slot_head) - lich.real_name = user.mind.name - user.mind.transfer_to(lich) + lich.real_name = M.mind.name + M.mind.transfer_to(lich) lich.hardset_dna(null,null,lich.real_name,null,/datum/species/skeleton) lich << "Your bones clatter and shutter as they're pulled back into this world!" charge_max += 600 @@ -99,15 +99,15 @@ if(ABSTRACT in item.flags || NODROP in item.flags) continue marked_item = item - user << "You begin to focus your very being into the [item.name]..." + M << "You begin to focus your very being into the [item.name]..." break if(!marked_item) - user << "You must hold an item you wish to make your phylactery..." + M << "You must hold an item you wish to make your phylactery..." spawn(50) - if(marked_item.loc != user) //I changed my mind I don't want to put my soul in a cheeseburger! - user << "Your soul snaps back to your body as you drop the [marked_item.name]!" + if(marked_item.loc != M) //I changed my mind I don't want to put my soul in a cheeseburger! + M << "Your soul snaps back to your body as you drop the [marked_item.name]!" marked_item = null return name = "RISE!" @@ -118,11 +118,11 @@ marked_item.name = "Ensouled [marked_item.name]" marked_item.desc = "A terrible aura surrounds this item, its very existence is offensive to life itself..." marked_item.color = "#003300" - user << "With a hideous feeling of emptiness you watch in horrified fascination as skin sloughs off bone! Blood boils, nerves disintegrate, eyes boil in their sockets! As your organs crumble to dust in your fleshless chest you come to terms with your choice. You're a lich!" - user.set_species(/datum/species/skeleton) - current_body = user.mind.current - if(ishuman(user)) - var/mob/living/carbon/human/H = user + M << "With a hideous feeling of emptiness you watch in horrified fascination as skin sloughs off bone! Blood boils, nerves disintegrate, eyes boil in their sockets! As your organs crumble to dust in your fleshless chest you come to terms with your choice. You're a lich!" + M.set_species(/datum/species/skeleton) + current_body = M.mind.current + if(ishuman(M)) + var/mob/living/carbon/human/H = M H.unEquip(H.wear_suit) H.unEquip(H.head) H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit) diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm index da2e4fe3466..7dc5189f21c 100644 --- a/code/datums/spells/lightning.dm +++ b/code/datums/spells/lightning.dm @@ -27,7 +27,7 @@ Snd = new/sound('sound/magic/lightning_chargeup.ogg',channel = 7) halo = image("icon"='icons/effects/effects.dmi',"icon_state" ="electricity","layer" = EFFECTS_LAYER) user.overlays.Add(halo) - playsound(get_turf(usr), Snd, 50, 0) + playsound(get_turf(user), Snd, 50, 0) if(do_mob(user,user,100,uninterruptible=1)) if(ready && cast_check(skipcharge=1)) choose_targets() @@ -48,13 +48,13 @@ ready = 0 var/mob/living/carbon/target = targets[1] Snd=sound(null, repeat = 0, wait = 1, channel = Snd.channel) //byond, why you suck? - playsound(get_turf(usr),Snd,50,0)// Sorry MrPerson, but the other ways just didn't do it the way i needed to work, this is the only way. + playsound(get_turf(user),Snd,50,0)// Sorry MrPerson, but the other ways just didn't do it the way i needed to work, this is the only way. if(get_dist(user,target)>range) user << "They are too far away!" Reset(user) return - playsound(get_turf(usr), 'sound/magic/lightningbolt.ogg', 50, 1) + playsound(get_turf(user), 'sound/magic/lightningbolt.ogg', 50, 1) user.Beam(target,icon_state="lightning",icon='icons/effects/effects.dmi',time=5) Bolt(user,target,30,5,user) diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm index 6ad4f4243a3..cae1181058e 100644 --- a/code/datums/spells/mime.dm +++ b/code/datums/spells/mime.dm @@ -53,7 +53,7 @@ still_recharging_msg = "You'll have to wait before you can give your vow of silence again!" ..() -/obj/effect/proc_holder/spell/targeted/mime/speak/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/mime/speak/cast(list/targets,mob/user = usr) for(var/mob/living/carbon/human/H in targets) H.mind.miming=!H.mind.miming if(H.mind.miming) diff --git a/code/datums/spells/projectile.dm b/code/datums/spells/projectile.dm index 5439486b935..e12ccde655e 100644 --- a/code/datums/spells/projectile.dm +++ b/code/datums/spells/projectile.dm @@ -38,7 +38,7 @@ projectile.dir = get_dir(target,projectile) projectile.name = proj_name - var/current_loc = usr.loc + var/current_loc = user.loc projectile.loc = current_loc @@ -76,7 +76,7 @@ qdel(trail) if(projectile.loc in range(target.loc,proj_trigger_range)) - projectile.perform(list(target)) + projectile.perform(list(target),user=user) break current_loc = projectile.loc diff --git a/code/datums/spells/shapeshift.dm b/code/datums/spells/shapeshift.dm index 52a6c24785f..1a11d0476a5 100644 --- a/code/datums/spells/shapeshift.dm +++ b/code/datums/spells/shapeshift.dm @@ -15,7 +15,7 @@ var/list/current_shapes = list() var/list/current_casters = list() -/obj/effect/proc_holder/spell/targeted/shapeshift/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/shapeshift/cast(list/targets,mob/user = usr) for(var/mob/living/M in targets) if(M in current_shapes) Restore(M) diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm index 2f8116ffe6b..9de9508525a 100644 --- a/code/datums/spells/summonitem.dm +++ b/code/datums/spells/summonitem.dm @@ -15,9 +15,9 @@ action_icon_state = "summons" -/obj/effect/proc_holder/spell/targeted/summonitem/cast(list/targets) - for(var/mob/living/user in targets) - var/list/hand_items = list(user.get_active_hand(),user.get_inactive_hand()) +/obj/effect/proc_holder/spell/targeted/summonitem/cast(list/targets,mob/user = usr) + for(var/mob/living/L in targets) + var/list/hand_items = list(L.get_active_hand(),L.get_inactive_hand()) var/butterfingers = 0 var/message @@ -58,7 +58,7 @@ var/obj/item/organ/internal/organ = item_to_retrive if(organ.owner) // If this code ever runs I will be happy - add_logs(user, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(user.a_intent)]") + add_logs(L, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(L.a_intent)]") organ.Remove(organ.owner) else while(!isturf(item_to_retrive.loc) && infinite_recursion < 10) //if it's in something you get the whole thing. @@ -67,7 +67,7 @@ if(issilicon(M)) //Items in silicons warp the whole silicon M.loc.visible_message("[M] suddenly disappears!") - M.loc = user.loc + M.loc = L.loc M.loc.visible_message("[M] suddenly appears!") item_to_retrive = null break @@ -95,22 +95,22 @@ item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly disappears!") - if(user.hand) //left active hand - if(!user.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1)) - if(!user.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1)) + if(L.hand) //left active hand + if(!L.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1)) + if(!L.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1)) butterfingers = 1 else //right active hand - if(!user.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1)) - if(!user.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1)) + if(!L.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1)) + if(!L.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1)) butterfingers = 1 if(butterfingers) - item_to_retrive.loc = user.loc + item_to_retrive.loc = L.loc item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly appears!") - playsound(get_turf(user),"sound/magic/SummonItems_generic.ogg",50,1) + playsound(get_turf(L),"sound/magic/SummonItems_generic.ogg",50,1) else - item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly appears in [user]'s hand!") - playsound(get_turf(user),"sound/magic/SummonItems_generic.ogg",50,1) + item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly appears in [L]'s hand!") + playsound(get_turf(L),"sound/magic/SummonItems_generic.ogg",50,1) if(message) - user << message + L << message diff --git a/code/datums/spells/touch_attacks.dm b/code/datums/spells/touch_attacks.dm index cd83738c1f7..51fa79bccc7 100644 --- a/code/datums/spells/touch_attacks.dm +++ b/code/datums/spells/touch_attacks.dm @@ -14,10 +14,10 @@ return 0 ..() -/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets) - for(var/mob/living/carbon/user in targets) +/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets,mob/user = usr) + for(var/mob/living/carbon/C in targets) if(!attached_hand) - if(!ChargeHand(user)) + if(!ChargeHand(C)) return 0 while(attached_hand) //hibernate untill the spell is actually used charge_counter = 0 diff --git a/code/datums/spells/trigger.dm b/code/datums/spells/trigger.dm index 3988f3bae16..9c6dc7490f3 100644 --- a/code/datums/spells/trigger.dm +++ b/code/datums/spells/trigger.dm @@ -19,7 +19,7 @@ starting_spells = null return ..() -/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets,mob/user = usr) playMagSound() for(var/mob/living/target in targets) for(var/obj/effect/proc_holder/spell/spell in contents) diff --git a/code/datums/spells/turf_teleport.dm b/code/datums/spells/turf_teleport.dm index 1d2b47f11c0..7010b9bf705 100644 --- a/code/datums/spells/turf_teleport.dm +++ b/code/datums/spells/turf_teleport.dm @@ -11,8 +11,8 @@ var/sound1 = "sound/weapons/ZapBang.ogg" var/sound2 = "sound/weapons/ZapBang.ogg" -/obj/effect/proc_holder/spell/targeted/turf_teleport/cast(list/targets) - playsound(get_turf(usr), sound1, 50,1) +/obj/effect/proc_holder/spell/targeted/turf_teleport/cast(list/targets,mob/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)) @@ -41,4 +41,4 @@ if(target.buckled_mob) target.unbuckle_mob(force=1) target.loc = picked - playsound(get_turf(usr), sound2, 50,1) + playsound(get_turf(user), sound2, 50,1) diff --git a/code/datums/spells/wizard.dm b/code/datums/spells/wizard.dm index 028d23d4291..87f896b3f55 100644 --- a/code/datums/spells/wizard.dm +++ b/code/datums/spells/wizard.dm @@ -273,7 +273,7 @@ action_icon_state = "fireball" sound = "sound/magic/Fireball.ogg" -/obj/effect/proc_holder/spell/turf/fireball/cast(turf/T) +/obj/effect/proc_holder/spell/turf/fireball/cast(turf/T,mob/user = usr) explosion(T, -1, 0, 2, 3, 0, flame_range = 2) @@ -303,8 +303,7 @@ action_icon_state = "repulse" -/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets) - var/mob/user = usr +/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets,mob/user = usr) var/list/thrownatoms = list() var/atom/throwtarget var/distfromcaster @@ -351,9 +350,9 @@ action_icon_state = "tailsweep" action_background_icon_state = "bg_alien" -/obj/effect/proc_holder/spell/aoe_turf/repulse/xeno/cast(list/targets) - if(istype(usr, /mob/living/carbon)) - var/mob/living/carbon/C = usr +/obj/effect/proc_holder/spell/aoe_turf/repulse/xeno/cast(list/targets,mob/user = usr) + if(istype(user, /mob/living/carbon)) + var/mob/living/carbon/C = user playsound(C.loc, 'sound/voice/hiss5.ogg', 80, 1, 1) C.spin(6,1) ..() diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 60dc9db40be..561a583bc7c 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -373,7 +373,6 @@ var/list/uplink_items = list() desc = "A box of shurikens from ancient Earth martial arts. They are highly effective throwing weapons, and will embed into limbs when possible." item = /obj/item/weapon/storage/box/throwing_stars cost = 6 - excludefrom = list(/datum/game_mode/nuclear) /datum/uplink_item/stealthy_weapons/edagger name = "Energy Dagger" @@ -583,6 +582,16 @@ var/list/uplink_items = list() cost = 8 excludefrom = list(/datum/game_mode/gang) +/datum/uplink_item/device_tools/hardsuit + name = "Elite Syndicate Hardsuit" + desc = "The elite Syndicate hardsuit is worn by only the best nuclear agents. Features much better armoring and complete fireproofing, as well as a built in jetpack. \ + When the built in helmet is deployed your identity will be protected, even in death, as the suit cannot be removed by outside forces. Toggling the suit into combat mode \ + will allow you all the mobility of a loose fitting uniform without sacrificing armoring. Additionally the suit is collapsible, small enough to fit within a backpack. \ + Nanotrasen crewmembers are trained to report red space suit sightings; these suits in particular are known to drive employees into a panic." + item = /obj/item/clothing/suit/space/hardsuit/syndi/elite + cost = 8 + gamemodes = list(/datum/game_mode/nuclear) + /datum/uplink_item/device_tools/thermal name = "Thermal Imaging Glasses" desc = "These goggles can be turned to resemble common eyewears throughout the station. \ @@ -793,7 +802,6 @@ var/list/uplink_items = list() You can also play card games with them or leave them on your victims." item = /obj/item/toy/cards/deck/syndicate cost = 1 - excludefrom = list(/datum/game_mode/nuclear) surplus = 40 /datum/uplink_item/badass/syndiecash @@ -874,4 +882,4 @@ var/list/uplink_items = list() U.purchase_log += "\icon[C]" for(var/item in bought_items) new item(C) - U.purchase_log += "\icon[item]" + U.purchase_log += "\icon[item]" \ No newline at end of file diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index cdcbfd1526a..53d7093e514 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -30,6 +30,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station var/lightswitch = 1 var/valid_territory = 1 //If it's a valid territory for gangs to claim + var/blob_allowed = 1 //Does it count for blobs score? By default, all areas count. + var/eject = null var/requires_power = 1 @@ -97,6 +99,7 @@ var/list/teleportlocs = list() power_environ = 0 valid_territory = 0 ambientsounds = list('sound/ambience/ambispace.ogg','sound/ambience/title2.ogg',) + blob_allowed = 0 //Eating up space doesn't count for victory as a blob. /area/space/nearstation icon_state = "space_near" @@ -164,6 +167,7 @@ var/list/teleportlocs = list() icon_state = "centcom" requires_power = 0 has_gravity = 1 + blob_allowed = 0 //Should go without saying, no blobs should take over centcom as a win condition. /area/centcom/control name = "Centcom Docks" @@ -190,6 +194,7 @@ var/list/teleportlocs = list() icon_state = "syndie-ship" requires_power = 0 has_gravity = 1 + blob_allowed = 0 //Not... entirely sure this will ever come up... but if the bus makes blobs AND ops, it shouldn't aim for the ops to win. /area/syndicate_mothership/control name = "Syndicate Control Room" @@ -206,6 +211,8 @@ var/list/teleportlocs = list() icon_state = "asteroid" requires_power = 0 has_gravity = 1 + blob_allowed = 0 //Nope, no winning on the asteroid as a blob. Gotta eat the station. + valid_territory = 0 /area/asteroid/cave name = "Asteroid - Underground" @@ -1011,6 +1018,7 @@ var/list/teleportlocs = list() name = "Ruskie DJ Station" icon_state = "DJ" has_gravity = 1 + blob_allowed = 0 //Nope, no winning on the DJ station as a blob. Gotta eat the main station. /area/djstation/solars name = "DJ Station Solars" @@ -1022,6 +1030,7 @@ var/list/teleportlocs = list() /area/derelict name = "Derelict Station" icon_state = "storage" + blob_allowed = 0 //Nope, no winning on the derelict as a blob. Gotta eat the station. /area/derelict/hallway/primary name = "Derelict Primary Hallway" diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm index f8f120c4b22..de3df02c364 100644 --- a/code/game/gamemodes/blob/blob.dm +++ b/code/game/gamemodes/blob/blob.dm @@ -1,9 +1,10 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 //Few global vars to track the blob -var/list/blobs = list() +var/list/blobs = list() //complete list of all blobs made. var/list/blob_cores = list() var/list/blob_nodes = list() +var/list/blobs_legit = list() //used for win-score calculations, contains only blobs counted for win condition /datum/game_mode/blob diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm index 55f79d883b8..5bd7dc2b321 100644 --- a/code/game/gamemodes/blob/blob_finish.dm +++ b/code/game/gamemodes/blob/blob_finish.dm @@ -1,7 +1,7 @@ /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 + if(blobwincount <= blobs_legit.len)//Blob took over return 1 if(!blob_cores.len) // blob is dead if(config.continuous["blob"]) @@ -19,7 +19,7 @@ /datum/game_mode/blob/declare_completion() if(round_converted) //So badmin blobs later don't step on the dead natural blobs metaphorical toes ..() - if(blobwincount <= blobs.len) + if(blobwincount <= blobs_legit.len) feedback_set_details("round_end_result","win - blob took over") world << "The blob has taken over the station!" world << "The entire station was eaten by the Blob" diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm index 70d6796e6ce..157c5760e69 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -17,9 +17,11 @@ var/fire_resist = 1 var/mob/camera/blob/overmind - /obj/effect/blob/New(loc) - blobs += src + var/area/Ablob = get_area(loc) + if(Ablob.blob_allowed) //Is this area allowed for winning as blob? + blobs_legit += src + blobs += src //Keep track of the blob in the normal list either way src.dir = pick(1, 2, 4, 8) src.update_icon() ..(loc) @@ -31,15 +33,19 @@ return /obj/effect/blob/Destroy() - 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) + var/area/Ablob = get_area(loc) + if(Ablob.blob_allowed) //Only remove for blobs in areas that counted for the win + blobs_legit -= src + blobs -= src //It's still removed from the normal list + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary. return ..() /obj/effect/blob/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) return 1 - if(istype(mover) && mover.checkpass(PASSBLOB)) return 1 + if(height==0) + return 1 + if(istype(mover) && mover.checkpass(PASSBLOB)) + return 1 return 0 @@ -93,7 +99,8 @@ 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 + if(!dirs.len) + break var/dirn = pick(dirs) dirs.Remove(dirn) var/turf/T = get_step(src, dirn) @@ -120,28 +127,37 @@ var/dirn = pick(dirs) dirs.Remove(dirn) T = get_step(src, dirn) - if(!(locate(/obj/effect/blob) in T)) break - else T = null - - if(!T) return 0 - var/obj/effect/blob/B = new /obj/effect/blob/normal(src.loc) + if(!(locate(/obj/effect/blob) in T)) + break + else + T = null + if(!T) + return 0 + var/Blob_spawnable = 1 if(istype(T, /turf/space) && prob(65)) - B.health = 0 - 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 - B.update_icon() - 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() + Blob_spawnable = 0 + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Let's give some feedback that we DID try to spawn in space, since players are used to it + for(var/atom/A in T) + if(A.density) //Unless density is 0, don't spawn a blob + Blob_spawnable = 0 + A.blob_act() //Hit everything + if(T.density) //Check for walls and such dense turfs + Blob_spawnable = 0 + T.blob_act() //Hit the turf + if(Blob_spawnable) + var/obj/effect/blob/B = new /obj/effect/blob/normal(src.loc) + B.color = a_color + B.density = 1 + if(T.Enter(B,src)) //NOW we can attempt to move into the tile + B.density = initial(B.density) + B.loc = T + B.update_icon() + else + T.blob_act() //If we cant move in hit the turf + qdel(B) //We should never get to this point, since we checked before moving in. Destroy blob anyway for cleanliness though return 1 + /obj/effect/blob/ex_act(severity, target) ..() var/damage = 150 - 20 * severity diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 427de546b75..bd417180b6d 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -358,12 +358,8 @@ var/list/slot2type = list("head" = /obj/item/clothing/head/changeling, "wear_mas return return 1 -/datum/changeling/proc/add_profile(var/mob/living/carbon/human/H, var/mob/living/carbon/user, protect = 0) - if(stored_profiles.len > dna_max) - if(!push_out_profile()) - return - - var/datum/changelingprofile/prof = new() +/datum/changeling/proc/create_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, protect = 0) + var/datum/changelingprofile/prof = new H.dna.real_name = H.real_name //Set this again, just to be sure that it's properly set. var/datum/dna/new_dna = new H.dna.type @@ -391,12 +387,22 @@ var/list/slot2type = list("head" = /obj/item/clothing/head/changeling, "wear_mas else continue + return prof + +/datum/changeling/proc/add_profile(datum/changelingprofile/prof) + if(stored_profiles.len > dna_max) + if(!push_out_profile()) + return + stored_profiles += prof absorbedcount++ +/datum/changeling/proc/add_new_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, protect = 0) + var/datum/changelingprofile/prof = create_profile(H, protect) + add_profile(prof) return prof -/datum/changeling/proc/remove_profile(var/mob/living/carbon/human/H, force = 0) +/datum/changeling/proc/remove_profile(mob/living/carbon/human/H, force = 0) for(var/datum/changelingprofile/prof in stored_profiles) if(H.real_name == prof.name) if(prof.protected && !force) @@ -416,7 +422,7 @@ var/list/slot2type = list("head" = /obj/item/clothing/head/changeling, "wear_mas return 1 return 0 -/proc/changeling_transform(var/mob/living/carbon/human/user, var/datum/changelingprofile/chosen_prof) +/proc/changeling_transform(mob/living/carbon/human/user, datum/changelingprofile/chosen_prof) var/datum/dna/chosen_dna = chosen_prof.dna user.real_name = chosen_prof.name user.underwear = chosen_prof.underwear @@ -476,4 +482,19 @@ var/list/slot2type = list("head" = /obj/item/clothing/head/changeling, "wear_mas /datum/changelingprofile/Destroy() qdel(dna) - return ..() \ No newline at end of file + return ..() + +/datum/changelingprofile/proc/copy_profile(datum/changelingprofile/newprofile) + newprofile.name = name + newprofile.protected = protected + newprofile.dna = new dna.type + dna.copy_dna(newprofile.dna) + newprofile.name_list = name_list.Copy() + newprofile.appearance_list = appearance_list.Copy() + newprofile.flags_cover_list = flags_cover_list.Copy() + newprofile.exists_list = exists_list.Copy() + newprofile.item_color_list = item_color_list.Copy() + newprofile.item_state_list = item_state_list.Copy() + newprofile.underwear = underwear + newprofile.undershirt = undershirt + newprofile.socks = socks diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm index db88f244ce0..af9cd88b91b 100644 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ b/code/game/gamemodes/changeling/evolution_menu.dm @@ -372,7 +372,7 @@ var/list/sting_paths var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste if(ishuman(C)) - var/datum/changelingprofile/prof = mind.changeling.add_profile(C) + var/datum/changelingprofile/prof = mind.changeling.add_new_profile(C, src) mind.changeling.first_prof = prof return 1 diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm index 0bdc2becb54..97aa442b108 100644 --- a/code/game/gamemodes/changeling/powers/absorb.dm +++ b/code/game/gamemodes/changeling/powers/absorb.dm @@ -54,7 +54,7 @@ target << "You are absorbed by the changeling!" if(!changeling.has_dna(target.dna)) - changeling.add_profile(target, user) + changeling.add_new_profile(target, user) if(user.nutrition < NUTRITION_LEVEL_WELL_FED) user.nutrition = min((user.nutrition + target.nutrition), NUTRITION_LEVEL_WELL_FED) @@ -152,7 +152,7 @@ target << "[user] tightens their grip as a painful sensation invades your body." if(!changeling.has_dna(target.dna)) - changeling.add_profile(target, user) + changeling.add_new_profile(target, user) changeling.remove_profile(user) var/mob/dead/observer/ghost = target.ghostize(0) diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm index 74bee7886f1..d7497e6d290 100644 --- a/code/game/gamemodes/changeling/powers/hivemind.dm +++ b/code/game/gamemodes/changeling/powers/hivemind.dm @@ -47,7 +47,9 @@ var/list/datum/dna/hivemind_bank = list() if(!chosen_dna) return - hivemind_bank += chosen_dna + var/datum/changelingprofile/uploaded_dna = new chosen_dna.type + chosen_dna.copy_profile(uploaded_dna) + hivemind_bank += uploaded_dna user << "We channel the DNA of [chosen_name] to the air." feedback_add_details("changeling_powers","HU") return 1 @@ -85,7 +87,9 @@ var/list/datum/dna/hivemind_bank = list() if(!chosen_prof) return - changeling.add_profile(chosen_prof, user) + var/datum/changelingprofile/downloaded_prof = new chosen_prof.type + chosen_prof.copy_profile(downloaded_prof) + changeling.add_profile(downloaded_prof) user << "We absorb the DNA of [S] from the air." feedback_add_details("changeling_powers","HD") return 1 diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index da55f48d844..80df685d5a5 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -175,7 +175,7 @@ /obj/effect/proc_holder/changeling/sting/extract_dna/sting_action(mob/user, mob/living/carbon/human/target) add_logs(user, target, "stung", "extraction sting") if(!(user.mind.changeling.has_dna(target.dna))) - user.mind.changeling.add_profile(target, user) + user.mind.changeling.add_new_profile(target, user) feedback_add_details("changeling_powers","ED") return 1 diff --git a/code/game/gamemodes/gang/recaller.dm b/code/game/gamemodes/gang/recaller.dm index 236be07cb64..293855f00b1 100644 --- a/code/game/gamemodes/gang/recaller.dm +++ b/code/game/gamemodes/gang/recaller.dm @@ -286,7 +286,8 @@ usr << "The dominator will secure your gang's dominance over the station. Turn it on when you are ready to defend it." pointcost = 30 else - usr << "The dominator can be spawned only on territory controlled by your gang." + usr << "The dominator can be spawned only on territory controlled by your gang!" + return if(item_type) gang.points -= pointcost diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index 23b3667caeb..80cd214ec74 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -75,7 +75,7 @@ var/bomb_set if(istype(I, /obj/item/weapon/screwdriver/nuke)) playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1) user << "You start removing [src]'s front panel's screws..." - if(do_after(user, 60,target=src)) + if(do_after(user, 60/I.toolspeed,target=src)) deconstruction_state = NUKESTATE_UNSCREWED user << "You remove the screws from [src]'s front panel." update_icon() @@ -84,7 +84,7 @@ var/bomb_set if(istype(I, /obj/item/weapon/crowbar)) user << "You start removing [src]'s front panel..." playsound(loc, 'sound/items/Crowbar.ogg', 100, 1) - if(do_after(user,30,target=src)) + if(do_after(user,30/I.toolspeed,target=src)) user << "You remove [src]'s front panel." deconstruction_state = NUKESTATE_PANEL_REMOVED update_icon() @@ -95,7 +95,7 @@ var/bomb_set playsound(loc, 'sound/items/Welder.ogg', 100, 1) user << "You start cutting [src]'s inner plate..." if(welder.remove_fuel(1,user)) - if(do_after(user,80,target=src)) + if(do_after(user,80/I.toolspeed,target=src)) user << "You cut [src]'s inner plate." deconstruction_state = NUKESTATE_WELDED update_icon() @@ -104,7 +104,7 @@ var/bomb_set if(istype(I, /obj/item/weapon/crowbar)) user << "You start prying off [src]'s inner plate..." playsound(loc, 'sound/items/Crowbar.ogg', 100, 1) - if(do_after(user,50,target=src)) + if(do_after(user,50/I.toolspeed,target=src)) user << "You pry off [src]'s inner plate. You can see the core's green glow!" deconstruction_state = NUKESTATE_CORE_EXPOSED update_icon() diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm index 18c97414a8c..9d9457f7d7b 100644 --- a/code/game/gamemodes/shadowling/shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm @@ -19,28 +19,28 @@ clothes_req = 0 action_icon_state = "glare" -/obj/effect/proc_holder/spell/targeted/glare/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/glare/cast(list/targets,mob/user = usr) for(var/mob/living/target in targets) if(!ishuman(target)) - usr << "You may only glare at humans!" + user << "You may only glare at humans!" revert_cast() return - if(!shadowling_check(usr)) + if(!shadowling_check(user)) revert_cast() return if(target.stat) - usr << "[target] must be conscious!" + user << "[target] must be conscious!" revert_cast() return if(is_shadow_or_thrall(target)) - usr << "You cannot glare at allies!" + user << "You cannot glare at allies!" revert_cast() return var/mob/living/carbon/human/M = target - usr.visible_message("[usr]'s eyes flash a blinding red!") + user.visible_message("[user]'s eyes flash a blinding red!") target.visible_message("[target] freezes in place, their eyes glazing over...") - if(in_range(target, usr)) - target << "Your gaze is forcibly drawn into [usr]'s eyes, and you are mesmerized by the heavenly lights..." + if(in_range(target, user)) + target << "Your gaze is forcibly drawn into [user]'s eyes, and you are mesmerized by the heavenly lights..." else //Only alludes to the shadowling if the target is close by target << "Red lights suddenly dance in your vision, and you are mesmerized by their heavenly beauty..." target.Stun(10) @@ -80,11 +80,11 @@ blacklistLuminosity += extinguishItem(F) H.SetLuminosity(blacklistLuminosity) //I hate lightcode for making me do it this way -/obj/effect/proc_holder/spell/aoe_turf/veil/cast(list/targets) - if(!shadowling_check(usr) && !admin_override) +/obj/effect/proc_holder/spell/aoe_turf/veil/cast(list/targets,mob/user = usr) + if(!shadowling_check(user) && !admin_override) revert_cast() return - usr << "You silently disable all nearby lights." + user << "You silently disable all nearby lights." for(var/turf/T in targets) for(var/obj/item/F in T.contents) extinguishItem(F) @@ -95,7 +95,7 @@ for(var/obj/machinery/computer/C in T.contents) C.SetLuminosity(0) C.visible_message("[C] grows dim, its screen barely readable.") - for(var/obj/effect/glowshroom/G in orange(7, usr)) //High radius because glowshroom spam wrecks shadowlings + for(var/obj/effect/glowshroom/G in orange(7, user)) //High radius because glowshroom spam wrecks shadowlings G.visible_message("[G] withers away!") qdel(G) for(var/mob/living/H in T.contents) @@ -142,15 +142,15 @@ action_icon_state = "icy_veins" sound = 'sound/effects/ghost2.ogg' -/obj/effect/proc_holder/spell/aoe_turf/flashfreeze/cast(list/targets) - if(!shadowling_check(usr)) +/obj/effect/proc_holder/spell/aoe_turf/flashfreeze/cast(list/targets,mob/user = usr) + if(!shadowling_check(user)) revert_cast() return - usr << "You freeze the nearby air." + user << "You freeze the nearby air." for(var/turf/T in targets) for(var/mob/living/carbon/M in T.contents) if(is_shadow_or_thrall(M)) - if(M == usr) //No message for the user, of course + if(M == user) //No message for the user, of course continue else M << "You feel a blast of paralyzingly cold air wrap around you and flow past, but you are unaffected!" @@ -175,79 +175,78 @@ action_icon_state = "enthrall" var/enthralling = 0 -/obj/effect/proc_holder/spell/targeted/enthrall/cast(list/targets) - var/mob/living/carbon/human/user = usr +/obj/effect/proc_holder/spell/targeted/enthrall/cast(list/targets,mob/living/carbon/human/user = usr) listclearnulls(ticker.mode.thralls) - if(!(usr.mind in ticker.mode.shadows)) return + if(!(user.mind in ticker.mode.shadows)) return if(user.dna.species.id != "shadowling") if(ticker.mode.thralls.len >= 5) revert_cast() return for(var/mob/living/carbon/human/target in targets) - if(!in_range(usr, target)) - usr << "You need to be closer to enthrall [target]!" + if(!in_range(user, target)) + user << "You need to be closer to enthrall [target]!" revert_cast() return if(!target.key || !target.mind) - usr << "The target has no mind!" + user << "The target has no mind!" revert_cast() return if(target.stat) - usr << "The target must be conscious!" + user << "The target must be conscious!" revert_cast() return if(is_shadow_or_thrall(target)) - usr << "You can not enthrall allies!" + user << "You can not enthrall allies!" revert_cast() return if(!ishuman(target)) - usr << "You can only enthrall humans!" + user << "You can only enthrall humans!" revert_cast() return if(enthralling) - usr << "You are already enthralling!" + user << "You are already enthralling!" revert_cast() return if(!target.client) - usr << "[target]'s mind is vacant of activity." + user << "[target]'s mind is vacant of activity." enthralling = 1 - usr << "This target is valid. You begin the enthralling..." - target << "[usr] stares at you. You feel your head begin to pulse." + user << "This target is valid. You begin the enthralling..." + target << "[user] stares at you. You feel your head begin to pulse." for(var/progress = 0, progress <= 3, progress++) switch(progress) if(1) - usr << "You place your hands to [target]'s head..." - usr.visible_message("[usr] places their hands onto the sides of [target]'s head!") + user << "You place your hands to [target]'s head..." + user.visible_message("[user] places their hands onto the sides of [target]'s head!") if(2) - usr << "You begin preparing [target]'s mind as a blank slate..." - usr.visible_message("[usr]'s palms flare a bright red against [target]'s temples!") + user << "You begin preparing [target]'s mind as a blank slate..." + user.visible_message("[user]'s palms flare a bright red against [target]'s temples!") target << "A terrible red light floods your mind. You collapse as conscious thought is wiped away." target.Weaken(12) sleep(20) if(isloyal(target)) - usr << "They are enslaved by Nanotrasen. You begin to shut down the nanobot implant - this will take some time." - usr.visible_message("[usr] pauses, then dips their head in concentration!") + user << "They are enslaved by Nanotrasen. You begin to shut down the nanobot implant - this will take some time." + user.visible_message("[user] pauses, then dips their head in concentration!") target << "You feel your loyalties begin to weaken!" sleep(100) //10 seconds - not spawn() so the enthralling takes longer - usr << "The nanobots composing the loyalty implant have been rendered inert. Now to continue." - usr.visible_message("[user] relaxes again.") + user << "The nanobots composing the loyalty implant have been rendered inert. Now to continue." + user.visible_message("[user] relaxes again.") for(var/obj/item/weapon/implant/loyalty/L in target) if(L && L.implanted) qdel(L) target << "Your unwavering loyalty to Nanotrasen unexpectedly falters, dims, dies." if(3) - usr << "You begin planting the tumor that will control the new thrall..." - usr.visible_message("A strange energy passes from [usr]'s hands into [target]'s head!") + user << "You begin planting the tumor that will control the new thrall..." + user.visible_message("A strange energy passes from [user]'s hands into [target]'s head!") target << "You feel your memories twisting, morphing. A sense of horror dominates your mind." - if(!do_mob(usr, target, 70)) //around 21 seconds total for enthralling, 31 for someone with a loyalty implant - usr << "The enthralling has been interrupted - your target's mind returns to its previous state." - target << "You wrest yourself away from [usr]'s hands and compose yourself" + if(!do_mob(user, target, 70)) //around 21 seconds total for enthralling, 31 for someone with a loyalty implant + user << "The enthralling has been interrupted - your target's mind returns to its previous state." + target << "You wrest yourself away from [user]'s hands and compose yourself" enthralling = 0 return enthralling = 0 - usr << "You have enthralled [target.real_name]!" + user << "You have enthralled [target.real_name]!" target.visible_message("[target] looks to have experienced a revelation!", \ "False faces all dark not real not real not--") target.setOxyLoss(0) //In case the shadowling was choking them out @@ -264,7 +263,7 @@ clothes_req = 0 action_icon_state = "commune" -/obj/effect/proc_holder/spell/self/shadowling_hivemind/cast(mob/living/user) +/obj/effect/proc_holder/spell/self/shadowling_hivemind/cast(mob/living/user,mob/user = usr) if(!is_shadow(user)) user << "You must be a shadowling to do that!" return @@ -273,7 +272,7 @@ return for(var/mob/M in mob_list) if(is_shadow_or_thrall(M) || (M in dead_mob_list)) - M << "\[Shadowling\] [usr.real_name]: [text]" + M << "\[Shadowling\] [user.real_name]: [text]" /obj/effect/proc_holder/spell/self/shadowling_regenarmor //Resets a shadowling's species to normal, removes genetic defects, and re-equips their armor @@ -293,13 +292,13 @@ user.visible_message("[user]'s skin suddenly bubbles and shifts around their body!", \ "You regenerate your protective armor and cleanse your form of defects.") user.adjustCloneLoss(user.getCloneLoss()) - user.equip_to_slot_or_del(new /obj/item/clothing/under/shadowling(usr), slot_w_uniform) - user.equip_to_slot_or_del(new /obj/item/clothing/shoes/shadowling(usr), slot_shoes) - user.equip_to_slot_or_del(new /obj/item/clothing/suit/space/shadowling(usr), slot_wear_suit) - user.equip_to_slot_or_del(new /obj/item/clothing/head/shadowling(usr), slot_head) - user.equip_to_slot_or_del(new /obj/item/clothing/gloves/shadowling(usr), slot_gloves) - user.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/shadowling(usr), slot_wear_mask) - user.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/shadowling(usr), slot_glasses) + user.equip_to_slot_or_del(new /obj/item/clothing/under/shadowling(user), slot_w_uniform) + user.equip_to_slot_or_del(new /obj/item/clothing/shoes/shadowling(user), slot_shoes) + user.equip_to_slot_or_del(new /obj/item/clothing/suit/space/shadowling(user), slot_wear_suit) + user.equip_to_slot_or_del(new /obj/item/clothing/head/shadowling(user), slot_head) + user.equip_to_slot_or_del(new /obj/item/clothing/gloves/shadowling(user), slot_gloves) + user.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/shadowling(user), slot_wear_mask) + user.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/shadowling(user), slot_glasses) user.set_species(/datum/species/shadow/ling) @@ -362,8 +361,8 @@ user << "You do not have the power to ascend. You require [victory_threshold] thralls, but only [thralls] living thralls are present." else if(thralls >= victory_threshold) - usr << "You are now powerful enough to ascend. Use the Ascendance ability when you are ready. This will kill all of your thralls." - usr << "You may find Ascendance in the Shadowling Evolution tab." + user << "You are now powerful enough to ascend. Use the Ascendance ability when you are ready. This will kill all of your thralls." + user << "You may find Ascendance in the Shadowling Evolution tab." for(M in living_mob_list) if(is_shadow(M)) var/obj/effect/proc_holder/spell/self/collective_mind/CM @@ -372,7 +371,7 @@ qdel(CM) M.mind.remove_spell(/obj/effect/proc_holder/spell/self/shadowling_hatch) M.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_ascend(null)) - if(M == usr) + if(M == user) M << "You project this power to the rest of the shadowlings." else M << "[user.real_name] has coalesced the strength of the thralls. You can draw upon it at any time to ascend. (Shadowling Evolution Tab)" //Tells all the other shadowlings @@ -440,15 +439,15 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell action_icon_state = "screech" sound = 'sound/effects/screech.ogg' -/obj/effect/proc_holder/spell/aoe_turf/unearthly_screech/cast(list/targets) - if(!shadowling_check(usr)) +/obj/effect/proc_holder/spell/aoe_turf/unearthly_screech/cast(list/targets,mob/user = usr) + if(!shadowling_check(user)) revert_cast() return - usr.audible_message("[usr] lets out a horrible scream!") + user.audible_message("[user] lets out a horrible scream!") for(var/turf/T in targets) for(var/mob/target in T.contents) if(is_shadow_or_thrall(target)) - if(target == usr) //No message for the user, of course + if(target == user) //No message for the user, of course continue else continue @@ -482,32 +481,32 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell var/targetsDrained var/list/nearbyTargets -/obj/effect/proc_holder/spell/aoe_turf/drain_life/cast(list/targets, mob/living/carbon/human/U = usr) - if(!shadowling_check(U)) +/obj/effect/proc_holder/spell/aoe_turf/drain_life/cast(list/targets, mob/living/carbon/human/user = usr) + if(!shadowling_check(user)) revert_cast() return targetsDrained = 0 nearbyTargets = list() for(var/turf/T in targets) for(var/mob/living/carbon/M in T.contents) - if(M == usr) continue + if(M == user) continue targetsDrained++ nearbyTargets.Add(M) if(!targetsDrained) revert_cast() - usr << "There were no nearby humans for you to drain." + user << "There were no nearby humans for you to drain." return for(var/mob/living/carbon/M in nearbyTargets) - U.heal_organ_damage(10, 10) - U.adjustToxLoss(-10) - U.adjustOxyLoss(-10) - U.adjustStaminaLoss(-20) - U.AdjustWeakened(-1) - U.AdjustStunned(-1) + user.heal_organ_damage(10, 10) + user.adjustToxLoss(-10) + user.adjustOxyLoss(-10) + user.adjustStaminaLoss(-20) + user.AdjustWeakened(-1) + user.AdjustStunned(-1) M.adjustOxyLoss(20) M.adjustStaminaLoss(20) M << "You feel a wave of exhaustion and a curious draining sensation directed towards [usr]!" - usr << "You draw life from those around you to heal your wounds." + user << "You draw life from those around you to heal your wounds." /obj/effect/proc_holder/spell/targeted/revive_thrall //Completely revives a dead thrall @@ -521,20 +520,20 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell include_user = 0 action_icon_state = "revive_thrall" -/obj/effect/proc_holder/spell/targeted/revive_thrall/cast(list/targets) - if(!shadowling_check(usr)) +/obj/effect/proc_holder/spell/targeted/revive_thrall/cast(list/targets,mob/user = usr) + if(!shadowling_check(user)) revert_cast() return for(var/mob/living/carbon/human/thrallToRevive in targets) - var/choice = alert(usr,"Empower a living thrall or revive a dead one?",,"Empower","Revive","Cancel") + var/choice = alert(user,"Empower a living thrall or revive a dead one?",,"Empower","Revive","Cancel") switch(choice) if("Empower") if(!is_thrall(thrallToRevive)) - usr << "[thrallToRevive] is not a thrall." + user << "[thrallToRevive] is not a thrall." revert_cast() return if(thrallToRevive.stat != CONSCIOUS) - usr << "[thrallToRevive] must be conscious to become empowered." + user << "[thrallToRevive] must be conscious to become empowered." revert_cast() return var/empowered_thralls = 0 @@ -545,21 +544,21 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell if(H.dna.species.id == "l_shadowling") empowered_thralls++ if(empowered_thralls >= EMPOWERED_THRALL_LIMIT) - usr << "You cannot spare this much energy. There are too many empowered thralls." + user << "You cannot spare this much energy. There are too many empowered thralls." revert_cast() return - usr.visible_message("[usr] places their hands over [thrallToRevive]'s face, red light shining from beneath.", \ + user.visible_message("[user] places their hands over [thrallToRevive]'s face, red light shining from beneath.", \ "You place your hands on [thrallToRevive]'s face and begin gathering energy...") - thrallToRevive << "[usr] places their hands over your face. You feel energy gathering. Stand still..." - if(!do_mob(usr, thrallToRevive, 80)) - usr << "Your concentration snaps. The flow of energy ebbs." + thrallToRevive << "[user] places their hands over your face. You feel energy gathering. Stand still..." + if(!do_mob(user, thrallToRevive, 80)) + user << "Your concentration snaps. The flow of energy ebbs." revert_cast() return - usr << "You release a massive surge of power into [thrallToRevive]!" - usr.visible_message("Red lightning surges into [thrallToRevive]'s face!") + user << "You release a massive surge of power into [thrallToRevive]!" + user.visible_message("Red lightning surges into [thrallToRevive]'s face!") playsound(thrallToRevive, 'sound/weapons/Egloves.ogg', 50, 1) playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1) - usr.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) + user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) thrallToRevive.Weaken(5) thrallToRevive.visible_message("[thrallToRevive] collapses, their skin and face distorting!", \ "AAAAAAAAAAAAAAAAAAAGH-") @@ -574,25 +573,25 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadow_walk(null)) if("Revive") if(!is_thrall(thrallToRevive)) - usr << "[thrallToRevive] is not a thrall." + user << "[thrallToRevive] is not a thrall." revert_cast() return if(thrallToRevive.stat != DEAD) - usr << "[thrallToRevive] is not dead." + user << "[thrallToRevive] is not dead." revert_cast() return - usr.visible_message("[usr] kneels over [thrallToRevive], placing their hands on \his chest.", \ + user.visible_message("[user] kneels over [thrallToRevive], placing their hands on \his chest.", \ "You crouch over the body of your thrall and begin gathering energy...") thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive) - if(!do_mob(usr, thrallToRevive, 30)) - usr << "Your concentration snaps. The flow of energy ebbs." + if(!do_mob(user, thrallToRevive, 30)) + user << "Your concentration snaps. The flow of energy ebbs." revert_cast() return - usr << "You release a massive surge of power into [thrallToRevive]!" - usr.visible_message("Red lightning surges from [usr]'s hands into [thrallToRevive]'s chest!") + user << "You release a massive surge of power into [thrallToRevive]!" + user.visible_message("Red lightning surges from [user]'s hands into [thrallToRevive]'s chest!") playsound(thrallToRevive, 'sound/weapons/Egloves.ogg', 50, 1) playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1) - usr.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) + user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) sleep(10) thrallToRevive.revive() thrallToRevive.visible_message("[thrallToRevive] heaves in breath, dim red light shining in their eyes.", \ @@ -615,8 +614,8 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell charge_max = 600 action_icon_state = "extend_shuttle" -/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle/cast(list/targets, mob/living/carbon/human/U = usr) - if(!shadowling_check(U)) +/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle/cast(list/targets, mob/living/carbon/human/user = usr) + if(!shadowling_check(user)) revert_cast() return for(var/mob/living/carbon/human/target in targets) @@ -624,23 +623,23 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell revert_cast() return if(!is_thrall(target)) - usr << "[target] must be a thrall." + user << "[target] must be a thrall." revert_cast() return if(SSshuttle.emergency.mode != SHUTTLE_CALL) - usr << "The shuttle must be inbound only to the station." + user << "The shuttle must be inbound only to the station." revert_cast() return var/mob/living/carbon/human/M = target - U.visible_message("[U]'s eyes flash a bright red!", \ + user.visible_message("[user]'s eyes flash a bright red!", \ "You begin to draw [M]'s life force.") M.visible_message("[M]'s face falls slack, their jaw slightly distending.", \ "You are suddenly transported... far, far away...") - if(!do_after(U, 50, target = M)) + if(!do_after(user, 50, target = M)) M << "You are snapped back to reality, your haze dissipating!" - U << "You have been interrupted. The draw has failed." + user << "You have been interrupted. The draw has failed." return - U << "You project [M]'s life force toward the approaching shuttle, extending its arrival duration!" + user << "You project [M]'s life force toward the approaching shuttle, extending its arrival duration!" M.visible_message("[M]'s eyes suddenly flare red. They proceed to collapse on the floor, not breathing.", \ "...speeding by... ...pretty blue glow... ...touch it... ...no glow now... ...no light... ...nothing at all...") M.death() @@ -650,7 +649,7 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell timer += more_minutes priority_announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 15 minutes..", "System Failure", 'sound/misc/notice1.ogg') SSshuttle.emergency.setTimer(timer) - U.mind.spell_list.Remove(src) //Can only be used once! + user.mind.spell_list.Remove(src) //Can only be used once! qdel(src) @@ -666,25 +665,25 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell clothes_req = 0 action_icon_state = "glare" -/obj/effect/proc_holder/spell/targeted/lesser_glare/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/lesser_glare/cast(list/targets,mob/user = usr) for(var/mob/living/target in targets) if(!ishuman(target) || !target) - usr << "You nay only glare at humans!" + user << "You nay only glare at humans!" revert_cast() return if(target.stat) - usr << "[target] must be conscious!" + user << "[target] must be conscious!" revert_cast() return if(is_shadow_or_thrall(target)) - usr << "You cannot glare at allies!" + user << "You cannot glare at allies!" revert_cast() return var/mob/living/carbon/human/M = target - usr.visible_message("[usr]'s eyes flash a bright red!") + user.visible_message("[user]'s eyes flash a bright red!") target.visible_message("[target] freezes in place, their eyes clouding...") - if(in_range(target, usr)) - target << "Your gaze is forcibly drawn into [usr]'s eyes, and you are starstruck by the heavenly lights..." + if(in_range(target, user)) + target << "Your gaze is forcibly drawn into [user]'s eyes, and you are starstruck by the heavenly lights..." else //Only alludes to the shadowling if the target is close by target << "Red lights suddenly dance in your vision, and you are starstruck by their heavenly beauty..." target.Stun(5) //Roughly 50% as long as the normal one @@ -749,7 +748,7 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell return for(var/mob/M in mob_list) if(is_shadow_or_thrall(M) || (M in dead_mob_list)) - M << "\[Thrall\] [usr.real_name]: [text]" + M << "\[Thrall\] [user.real_name]: [text]" log_say("[user.real_name]/[user.key] : [text]") @@ -766,19 +765,17 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell action_icon_state = "annihilate" sound = 'sound/magic/Staff_Chaos.ogg' -/obj/effect/proc_holder/spell/targeted/annihilate/cast(list/targets) - var/mob/living/simple_animal/ascendant_shadowling/SHA = usr - if(SHA.phasing) - usr << "You are not in the same plane of existence. Unphase first." +/obj/effect/proc_holder/spell/targeted/annihilate/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr) + if(user.phasing) + user << "You are not in the same plane of existence. Unphase first." revert_cast() return - for(var/mob/living/boom in targets) if(is_shadow(boom)) //Used to not work on thralls. Now it does so you can PUNISH THEM LIKE THE WRATHFUL GOD YOU ARE. - usr << "Making an ally explode seems unwise." + user << "Making an ally explode seems unwise." revert_cast() return - usr.visible_message("[usr]'s markings flare as they gesture at [boom]!", \ + user.visible_message("[user]'s markings flare as they gesture at [boom]!", \ "You direct a lance of telekinetic energy into [boom].") sleep(4) if(iscarbon(boom)) @@ -796,32 +793,31 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell clothes_req = 0 action_icon_state = "enthrall" -/obj/effect/proc_holder/spell/targeted/hypnosis/cast(list/targets) - var/mob/living/simple_animal/ascendant_shadowling/SHA = usr - if(SHA.phasing) +/obj/effect/proc_holder/spell/targeted/hypnosis/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr) + if(user.phasing) revert_cast() - usr << "You are not in the same plane of existence. Unphase first." + user << "You are not in the same plane of existence. Unphase first." return for(var/mob/living/carbon/human/target in targets) if(is_shadow_or_thrall(target)) - usr << "You cannot enthrall an ally." + user << "You cannot enthrall an ally." revert_cast() return if(!target.ckey || !target.mind) - usr << "The target has no mind." + user << "The target has no mind." revert_cast() return if(target.stat) - usr << "The target must be conscious." + user << "The target must be conscious." revert_cast() return if(!ishuman(target)) - usr << "You can only enthrall humans." + user << "You can only enthrall humans." revert_cast() return - usr << "You instantly rearrange [target]'s memories, hyptonitizing them into a thrall." + user << "You instantly rearrange [target]'s memories, hyptonitizing them into a thrall." target << "An agonizing spike of pain drives into your mind, and--" target.mind.special_role = "thrall" ticker.mode.add_thrall(target.mind) @@ -835,20 +831,19 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell clothes_req = 0 action_icon_state = "shadow_walk" -/obj/effect/proc_holder/spell/self/shadowling_phase_shift/cast(list/targets) - var/mob/living/simple_animal/ascendant_shadowling/SHA = usr - for(SHA in targets) - SHA.phasing = !SHA.phasing - if(SHA.phasing) - SHA.visible_message("[SHA] suddenly vanishes!", \ +/obj/effect/proc_holder/spell/self/shadowling_phase_shift/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr) + for(user in targets) + user.phasing = !user.phasing + if(user.phasing) + user.visible_message("[user] suddenly vanishes!", \ "You begin phasing through planes of existence. Use the ability again to return.") - SHA.incorporeal_move = 1 - SHA.alpha = 0 + user.incorporeal_move = 1 + user.alpha = 0 else - SHA.visible_message("[SHA] suddenly appears from nowhere!", \ + user.visible_message("[user] suddenly appears from nowhere!", \ "You return from the space between worlds.") - SHA.incorporeal_move = 0 - SHA.alpha = 255 + user.incorporeal_move = 0 + user.alpha = 255 /obj/effect/proc_holder/spell/aoe_turf/ascendant_storm //Releases bolts of lightning to everyone nearby @@ -861,13 +856,12 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell action_icon_state = "lightning_storm" sound = 'sound/magic/lightningbolt.ogg' -/obj/effect/proc_holder/spell/aoe_turf/ascendant_storm/cast(list/targets) - var/mob/living/simple_animal/ascendant_shadowling/SHA = usr - if(SHA.phasing) - usr << "You are not in the same plane of existence. Unphase first." +/obj/effect/proc_holder/spell/aoe_turf/ascendant_storm/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr) + if(user.phasing) + user << "You are not in the same plane of existence. Unphase first." revert_cast() return - usr.visible_message("A massive ball of lightning appears in [usr]'s hands and flares out!", \ + user.visible_message("A massive ball of lightning appears in [user]'s hands and flares out!", \ "You conjure a ball of lightning and release it.") for(var/turf/T in targets) @@ -878,7 +872,7 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell playsound(target, 'sound/magic/LightningShock.ogg', 50, 1) target.Weaken(8) target.take_organ_damage(0,50) - usr.Beam(target,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) + user.Beam(target,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) /obj/effect/proc_holder/spell/self/shadowling_hivemind_ascendant //Large, all-caps text in shadowling chat @@ -896,5 +890,5 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell text = "[text]" for(var/mob/M in mob_list) if(is_shadow_or_thrall(M) || (M in dead_mob_list)) - M << "\[Ascendant\] [usr.real_name]: [text]" //Bigger text for ascendants. + M << "\[Ascendant\] [user.real_name]: [text]" //Bigger text for ascendants. log_say("[user.real_name]/[user.key] : [text]") diff --git a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm index b09c475a63d..f280d39eaed 100644 --- a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm @@ -9,8 +9,8 @@ var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "N clothes_req = 0 action_icon_state = "hatch" -/obj/effect/proc_holder/spell/self/shadowling_hatch/cast(mob/living/carbon/human/H) - if(usr.stat || !ishuman(usr) || !usr || !is_shadow(usr)) return +/obj/effect/proc_holder/spell/self/shadowling_hatch/cast(mob/living/carbon/human/H,mob/user = usr) + if(user.stat || !ishuman(user) || !user || !is_shadow(user)) return var/hatch_or_no = alert(H,"Are you sure you want to hatch? You cannot undo this!",,"Yes","No") switch(hatch_or_no) if("No") @@ -27,8 +27,8 @@ var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "N sleep(50) var/turf/simulated/floor/F - var/turf/shadowturf = get_turf(usr) - for(F in orange(1, usr)) + var/turf/shadowturf = get_turf(user) + for(F in orange(1, user)) new /obj/structure/alien/resin/wall/shadowling(F) for(var/obj/structure/alien/resin/wall/shadowling/R in shadowturf) //extremely hacky qdel(R) @@ -56,14 +56,13 @@ var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "N sleep(10) playsound(H.loc, 'sound/weapons/slice.ogg', 25, 1) H << "You are free!" - + H.status_flags = temp_flags sleep(10) playsound(H.loc, 'sound/effects/ghost.ogg', 100, 1) var/newNameId = pick(possibleShadowlingNames) possibleShadowlingNames.Remove(newNameId) H.real_name = newNameId - H.name = usr.real_name - H.status_flags = temp_flags + H.name = user.real_name H.stunned = 0 //Same as above. Due to hulks. H << "YOU LIVE!!!" @@ -110,7 +109,7 @@ var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "N clothes_req = 0 action_icon_state = "ascend" -/obj/effect/proc_holder/spell/self/shadowling_ascend/cast(mob/living/carbon/human/H) +/obj/effect/proc_holder/spell/self/shadowling_ascend/cast(mob/living/carbon/human/H,mob/user = usr) if(!shadowling_check(H)) return var/hatch_or_no = alert(H,"It is time to ascend. Are you sure about this?",,"Yes","No") diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index 483b0c97a41..cb04b9085df 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -839,3 +839,8 @@ ..() user <<"[src] suddenly vanishes!" qdel(src) + +/obj/item/weapon/spellbook/oneuse/random/New() + var/real_type = pick(subtypesof(/obj/item/weapon/spellbook/oneuse)) + new real_type(loc) + qdel(src) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 0d8dc0c0e56..10d8d09d843 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -734,7 +734,7 @@ user.visible_message("[user.name] removes the electronics from [src.name].",\ "You start prying out the circuit...") playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - if (do_after(user, 20, target = src)) + if (do_after(user, 20/W.toolspeed, target = src)) if (buildstage == 1) user <<"You remove the air alarm electronics." new /obj/item/weapon/electronics/airalarm( src.loc ) @@ -956,7 +956,7 @@ FIRE ALARM playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) user.visible_message("[user.name] removes the electronics from [src.name].", \ "You start prying out the circuit...") - if(do_after(user, 20, target = src)) + if(do_after(user, 20/W.toolspeed, target = src)) if(buildstage == 1) if(stat & BROKEN) user << "You remove the destroyed circuit." diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 621bd8221b1..fed2393ef09 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -95,7 +95,7 @@ if (istype(W, /obj/item/weapon/wrench)) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You begin to unfasten \the [src]..." - if (do_after(user, 40, target = src)) + if (do_after(user, 40/W.toolspeed, target = src)) user.visible_message( \ "[user] unfastens \the [src].", \ "You unfasten \the [src].", \ diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 19e4235007a..838cc8ddc9b 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -94,7 +94,7 @@ if(!device && !board && istype(W, /obj/item/weapon/wrench)) user << "You start unsecuring the button frame..." playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) user << "You unsecure the button frame." transfer_fingerprints_to(new /obj/item/wallframe/button(get_turf(src))) playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index dffd5241c8c..db965e30f6a 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -309,7 +309,7 @@ if(istype(P, /obj/item/weapon/wrench)) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You start wrenching the frame into place..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) user << "You wrench the frame into place." anchored = 1 state = 1 @@ -321,7 +321,7 @@ return playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) user << "You start deconstructing the frame..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) if(!src || !WT.isOn()) return user << "You deconstruct the frame." var/obj/item/stack/sheet/metal/M = new (loc, 5) @@ -331,7 +331,7 @@ if(istype(P, /obj/item/weapon/wrench)) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You start to unfasten the frame..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) user << "You unfasten the frame." anchored = 0 state = 0 @@ -372,7 +372,7 @@ if(C.get_amount() >= 5) playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) user << "You start adding cables to the frame..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) if(C.get_amount() >= 5 && state == 2) C.use(5) user << "You add cables to the frame." diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 4e83a662499..0ff76ba9274 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -90,11 +90,11 @@ update_icon() return -/obj/machinery/computer/attackby(obj/I, mob/user, params) +/obj/machinery/computer/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/weapon/screwdriver) && circuit) playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) user << " You start to disconnect the monitor..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/I.toolspeed, target = src)) var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) A.circuit = circuit A.anchored = 1 diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 71e3c28f84c..dd65277eb70 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -29,6 +29,7 @@ var/global/datum/crewmonitor/crewmonitor = new var/const/MAX_ICON_DIMENSION = 1024 var/const/ICON_SIZE = 4 var/initialized = FALSE + var/max_initialized_zlevel = 0 /datum/crewmonitor/New() . = ..() @@ -79,8 +80,6 @@ var/global/datum/crewmonitor/crewmonitor = new src.data = list() register_asset("crewmonitor.js",'crew.js') register_asset("crewmonitor.css",'crew.css') - for (var/z = 1 to world.maxz) - register_asset("minimap_[z].png", file("[getMinimapFile(z)].png")) /datum/crewmonitor/Destroy() if (src.interfaces) @@ -261,15 +260,18 @@ var/global/datum/crewmonitor/crewmonitor = new /datum/crewmonitor/proc/generateMiniMaps() for(var/z = 1 to world.maxz) generateMiniMap(z) + for (var/z = 1 to world.maxz) + register_asset("minimap_[z].png", file("[getMinimapFile(z)].png")) + + max_initialized_zlevel = world.maxz + world << "All minimaps have been generated." - for(var/client/C in clients) - sendResources(C) initialized = TRUE /datum/crewmonitor/proc/sendResources(var/client/client) send_asset(client, "crewmonitor.js") send_asset(client, "crewmonitor.css") - for (var/z = 1 to world.maxz) + for (var/z = 1 to max_initialized_zlevel) send_asset(client, "minimap_[z].png") /datum/crewmonitor/proc/getMinimapFile(z) diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index e781f80f81c..4cd84a452be 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -67,7 +67,7 @@ if(C.get_amount() >= 5) playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) user << "You start to add cables to the frame..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) if(C.get_amount() >= 5 && state == 1) C.use(5) user << "You add cables to the frame." @@ -80,7 +80,7 @@ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) user.visible_message("[user] disassembles the frame.", \ "You start to disassemble the frame...", "You hear banging and clanking.") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/P.toolspeed, target = src)) if(state == 1) user << "You disassemble the frame." var/obj/item/stack/sheet/metal/M = new (loc, 5) @@ -89,7 +89,7 @@ if(istype(P, /obj/item/weapon/wrench)) user << "You start [anchored ? "un" : ""]securing [name]..." playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/P.toolspeed, target = src)) if(state == 1) user << "You [anchored ? "un" : ""]secure [name]." anchored = !anchored @@ -98,7 +98,7 @@ if(istype(P, /obj/item/weapon/wrench)) user << "You start [anchored ? "un" : ""]securing [name]..." playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/P.toolspeed, target = src)) user << "You [anchored ? "un" : ""]secure [name]." anchored = !anchored diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 3eb9c4243e3..f03cb2eaa8b 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -797,7 +797,7 @@ About the new airlock wires panel: updateUsrDialog() return -/obj/machinery/door/airlock/attackby(obj/C, mob/user, params) +/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params) if(!istype(usr, /mob/living/silicon)) if(src.isElectrified()) if(src.shock(user, 75)) @@ -816,7 +816,7 @@ About the new airlock wires panel: "You begin [welded ? "unwelding":"welding"] the airlock...", \ "You hear welding.") playsound(loc, 'sound/items/Welder.ogg', 40, 1) - if(do_after(user,40,5,1, target = src)) + if(do_after(user,40/C.toolspeed,5,1, target = src)) if(density && !operating)//Door must be closed to weld. if( !istype(src, /obj/machinery/door/airlock) || !user || !W || !W.isOn() || !user.loc ) return @@ -847,7 +847,7 @@ About the new airlock wires panel: if(p_open && charge) user << "You carefully start removing [charge] from [src]..." playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) - if(!do_after(user, 150, target = src)) + if(!do_after(user, 150/C.toolspeed, target = src)) user << "You slip and [charge] detonates!" charge.ex_act(1) user.Weaken(3) @@ -861,7 +861,7 @@ About the new airlock wires panel: playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) user.visible_message("[user] removes the electronics from the airlock assembly.", \ "You start to remove electronics from the airlock assembly...") - if(do_after(user,40, target = src)) + if(do_after(user,40/C.toolspeed, target = src)) if(src.loc) if(src.doortype) var/obj/structure/door_assembly/A = new src.doortype(src.loc) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 2d85875a50b..9e23c4b60c3 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -54,7 +54,7 @@ playsound(get_turf(src), 'sound/items/Ratchet.ogg', 50, 1) user.visible_message("[user] starts undoing [src]'s bolts...", \ "You start unfastening [src]'s floor bolts...") - if(!do_after(user, 50, target = src)) return + if(!do_after(user, 50/C.toolspeed, target = src)) return playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) user.visible_message("[user] unfastens [src]'s bolts.", \ "You undo [src]'s floor bolts.") @@ -208,7 +208,7 @@ playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) user.visible_message("[user] starts prying something out from [src]...", \ "You begin prying out the wire cover...") - if(!do_after(user, 50, target = src)) return + if(!do_after(user, 50/C.toolspeed, target = src)) return if(constructionStep != CONSTRUCTION_PANEL_OPEN) return playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) user.visible_message("[user] pries out a metal plate from [src], exposing the wires.", \ @@ -220,7 +220,7 @@ playsound(get_turf(src), 'sound/items/Ratchet.ogg', 50, 1) user.visible_message("[user] starts bolting down [src]...", \ "You begin bolting [src]...") - if(!do_after(user, 30, target = src)) return + if(!do_after(user, 30/C.toolspeed, target = src)) return user.visible_message("[user] finishes the firelock.", \ "You finish the firelock.") playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) @@ -235,7 +235,7 @@ playsound(get_turf(src), 'sound/items/Wirecutter.ogg', 50, 1) user.visible_message("[user] starts cutting the wires from [src]...", \ "You begin removing [src]'s wires...") - if(!do_after(user, 60, target = src)) return + if(!do_after(user, 60/C.toolspeed, target = src)) 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.") @@ -250,7 +250,7 @@ playsound(get_turf(src), 'sound/items/Welder.ogg', 50, 1) user.visible_message("[user] starts welding a metal plate into [src]...", \ "You begin welding the cover plate back onto [src]...") - if(!do_after(user, 80, target = src)) return + if(!do_after(user, 80/C.toolspeed, target = src)) return if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) return playsound(get_turf(src), 'sound/items/Welder2.ogg', 50, 1) user.visible_message("[user] welds the metal plate into [src].", \ @@ -263,7 +263,7 @@ user.visible_message("[user] begins removing the circuit board from [src]...", \ "You begin prying out the circuit board from [src]...") playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) - if(!do_after(user, 50, target = src)) return + if(!do_after(user, 50/C.toolspeed, target = src)) return if(constructionStep != CONSTRUCTION_GUTTED) return user.visible_message("[user] removes [src]'s circuit board.", \ "You remove the circuit board from [src].") @@ -296,7 +296,7 @@ playsound(get_turf(src), 'sound/items/Welder.ogg', 50, 1) user.visible_message("[user] begins cutting apart [src]'s frame...", \ "You begin slicing [src] apart...") - if(!do_after(user, 80, target = src)) return + if(!do_after(user, 80/C.toolspeed, target = src)) return if(constructionStep != CONSTRUCTION_NOCIRCUIT) return user.visible_message("[user] cuts apart [src]!", \ "You cut [src] into metal.") diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 1ca5d29b760..5d7c87cccd5 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -279,7 +279,7 @@ playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) user.visible_message("[user] removes the electronics from the [src.name].", \ "You start to remove electronics from the [src.name]...") - if(do_after(user,40, target = src)) + if(do_after(user,40/I.toolspeed, target = src)) if(src.p_open && !src.density && !src.operating && src.loc) var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(src.loc) switch(base_state) diff --git a/code/game/machinery/droneDispenser.dm b/code/game/machinery/droneDispenser.dm index 86bdf54f452..1917c75065f 100644 --- a/code/game/machinery/droneDispenser.dm +++ b/code/game/machinery/droneDispenser.dm @@ -201,7 +201,7 @@ playsound(src, 'sound/items/Welder.ogg', 50, 1) user.visible_message("[user] begins patching up [src] with [WT].", \ "You begin restoring the damage to [src]...") - if(!do_after(user, 40, target = src)) + if(!do_after(user, 40/O.toolspeed, target = src)) return if(!src || !WT.remove_fuel(1, user)) return user.visible_message("[user] fixes [src]!", \ diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 407db623ab9..c261956806f 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -43,7 +43,7 @@ if (bulb) user.visible_message("[user] begins to disconnect [src]'s flashbulb.", "You begin to disconnect [src]'s flashbulb...") playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) - if(do_after(user, 30, target = src) && bulb) + if(do_after(user, 30/W.toolspeed, target = src) && bulb) user.visible_message("[user] has disconnected [src]'s flashbulb!", "You disconnect [src]'s flashbulb.") bulb.loc = src.loc bulb = null diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index a05910ac463..c8d12f9c780 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -71,7 +71,10 @@ var/const/HOLOPAD_MODE = RANGE_BASED default_deconstruction_crowbar(P) -/obj/machinery/hologram/holopad/attack_hand(mob/living/carbon/human/user) //Carn: Hologram requests. +/obj/machinery/hologram/holopad/AltClick(mob/living/carbon/human/user) + interact(user) + +/obj/machinery/hologram/holopad/interact(mob/living/carbon/human/user) //Carn: Hologram requests. if(!istype(user)) return if(user.stat || stat & (NOPOWER|BROKEN)) @@ -108,7 +111,7 @@ var/const/HOLOPAD_MODE = RANGE_BASED else if(href_list["mainmenu"]) temp = "" - updateUsrDialog() + updateDialog() add_fingerprint(usr) /obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 1c2c307973c..56d0ebeb3d9 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -326,7 +326,7 @@ Class Procs: if(istype(W)) user << "You begin [anchored ? "un" : ""]securing [name]..." playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - if(do_after(user, time, target = src)) + if(do_after(user, time/W.toolspeed, target = src)) user << "You [anchored ? "un" : ""]secure [name]." anchored = !anchored playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index a0b3c353862..3eedfeec907 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -723,7 +723,7 @@ var/list/obj/machinery/newscaster/allCasters = list() if(istype(I, /obj/item/weapon/wrench)) user << "You start [anchored ? "un" : ""]securing [name]..." playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) - if(do_after(user, 60, target = src)) + if(do_after(user, 60/I.toolspeed, target = src)) user << "You [anchored ? "un" : ""]secure [name]." new /obj/item/wallframe/newscaster(loc) playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1) diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm index a23489333ea..64be7f512b5 100644 --- a/code/game/machinery/pipe/pipe_dispenser.dm +++ b/code/game/machinery/pipe/pipe_dispenser.dm @@ -81,7 +81,7 @@ if (!anchored && !isinspace()) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You begin to fasten \the [src] to the floor..." - if (do_after(user, 40, target = src)) + if (do_after(user, 40/W.toolspeed, target = src)) add_fingerprint(user) user.visible_message( \ "[user] fastens \the [src].", \ @@ -94,7 +94,7 @@ else if(anchored) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You begin to unfasten \the [src] from the floor..." - if (do_after(user, 20, target = src)) + if (do_after(user, 20/W.toolspeed, target = src)) add_fingerprint(user) user.visible_message( \ "[user] unfastens \the [src].", \ diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 53570ee256d..dfd806fab82 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -734,7 +734,7 @@ playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1) user << "You start to remove the turret's interior metal armor..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/I.toolspeed, target = src)) if(!src || !WT.remove_fuel(5, user)) return build_step = 1 user << "You remove the turret's interior metal armor." @@ -810,7 +810,7 @@ playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1) user << "You begin to weld the turret's armor down..." - if(do_after(user, 30, target = src)) + if(do_after(user, 30/I.toolspeed, target = src)) if(!src || !WT.remove_fuel(5, user)) return build_step = 8 diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 053485e1d40..179eff07051 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -530,7 +530,7 @@ user.visible_message("[user] starts removing [src]'s damaged wires.", \ "You begin removing the damaged wires from [src]...") playsound(src, 'sound/items/Wirecutter.ogg', 50, 1) - if(!do_after(user, 30, target = src)) + if(!do_after(user, 30/I.toolspeed, target = src)) return user.visible_message("[user] removes the damaged wires from [src].", \ "You remove the damaged wiring from [src].") @@ -560,7 +560,7 @@ user.visible_message("[user] starts removing [src]'s broken interior plating.", \ "You begin removing the damaged interior plating from [src]...") playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - if(!do_after(user, 30, target = src)) + if(!do_after(user, 30/I.toolspeed, target = src)) return user.visible_message("[user] removes the damaged interior plating from [src].", \ "You remove the damaged interior plating from [src].") diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 9e13b5adca4..75cf6e6f70f 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -873,7 +873,9 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/item/clothing/suit/whitedress = 1, /obj/item/clothing/under/jester = 1, /obj/item/clothing/head/jester = 1, /obj/item/clothing/suit/hooded/carp_costume = 1, - /obj/item/clothing/suit/hooded/ian_costume = 1) + /obj/item/clothing/suit/hooded/ian_costume = 1, + /obj/item/clothing/suit/snowman = 1, + /obj/item/clothing/head/snowman = 1) contraband = list(/obj/item/clothing/suit/judgerobe = 1,/obj/item/clothing/head/powdered_wig = 1,/obj/item/weapon/gun/magic/wand = 2,/obj/item/clothing/glasses/sunglasses/garb = 2) premium = list(/obj/item/clothing/suit/hgpirate = 2, /obj/item/clothing/head/hgpiratecap = 2, /obj/item/clothing/head/helmet/roman = 1, /obj/item/clothing/head/helmet/roman/legionaire = 1, /obj/item/clothing/under/roman = 1, /obj/item/clothing/shoes/roman = 1, /obj/item/weapon/shield/riot/roman = 1) refill_canister = /obj/item/weapon/vending_refill/autodrobe diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 0a460ac7b83..b15607062af 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -97,6 +97,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES var/heat = 0 var/sharpness = IS_BLUNT + var/toolspeed = 1 /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)) || is_type_in_list(target, can_be_placed_into)) diff --git a/code/game/objects/items/candle.dm b/code/game/objects/items/candle.dm index ba55eebe335..1171c646104 100644 --- a/code/game/objects/items/candle.dm +++ b/code/game/objects/items/candle.dm @@ -8,10 +8,14 @@ w_class = 1 var/wax = 200 var/lit = 0 + var/infinite = 0 + var/start_lit = 0 heat = 1000 - proc - light(var/flavor_text = "[usr] lights the [name].") +/obj/item/candle/New() + ..() + if(start_lit) + light() /obj/item/candle/update_icon() var/i @@ -51,14 +55,16 @@ light() //honk return -/obj/item/candle/light(var/flavor_text = "[usr] lights the [name].") +/obj/item/candle/proc/light(var/flavor_text = "[usr] lights the [name].") if(!src.lit) src.lit = 1 //src.damtype = "fire" for(var/mob/O in viewers(usr, null)) O.show_message(flavor_text, 1) SetLuminosity(CANDLE_LUMINOSITY) - SSobj.processing |= src + if(!infinite) + SSobj.processing |= src + update_icon() /obj/item/candle/process() @@ -99,4 +105,9 @@ /obj/item/candle/is_hot() return lit * heat + +/obj/item/candle/infinite + infinite = 1 + start_lit = 1 + #undef CANDLE_LUMINOSITY \ No newline at end of file diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm index 49b124453b7..b3e4214e7af 100644 --- a/code/game/objects/items/devices/geiger_counter.dm +++ b/code/game/objects/items/devices/geiger_counter.dm @@ -122,7 +122,7 @@ return 0 user.visible_message("[user] unscrews [src]'s maintenance panel and begins fiddling with its innards...", "You begin resetting [src]...") playsound(user, 'sound/items/Screwdriver.ogg', 50, 1) - if(!do_after(user, 40, target = user)) + if(!do_after(user, 40/I.toolspeed, target = user)) return 0 user.visible_message("[user] refastens [src]'s maintenance panel!", "You reset [src] to its factory settings!") playsound(user, 'sound/items/Screwdriver2.ogg', 50, 1) diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index e722223bc6a..47020fbc330 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -98,6 +98,33 @@ user << "You need a working light!" return + if(istype(W, /obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = W + var/found_good_light = 0 + var/replaced_something = 0 + + for(var/obj/item/I in S.contents) + if(istype(I,/obj/item/weapon/light)) + var/obj/item/weapon/light/L = I + if(L.status == LIGHT_OK) + found_good_light = 1 + if(src.uses < max_uses) + qdel(L) + AddUses(1) + replaced_something = 1 + else + break + + if(!found_good_light) + user << "\The [S] contains no useable lights!" + return + + if(!replaced_something && src.uses == max_uses) + user << "\The [src] is full!" + return + + user << "You fill \the [src] with lights from \the [S]. You have [uses] lights remaining." + /obj/item/device/lightreplacer/emag_act() if(!emagged) Emag() diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index 39a8afa9ff8..d5d116a4392 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -17,6 +17,7 @@ origin_tech = "magnets=1;engineering=1" var/obj/machinery/buffer // simple machine buffer for device linkage hitsound = 'sound/weapons/tap.ogg' + toolspeed = 1 // Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby. diff --git a/code/game/objects/items/devices/pizza_bomb.dm b/code/game/objects/items/devices/pizza_bomb.dm index 1e6d2a5244b..3bcfbdc464d 100644 --- a/code/game/objects/items/devices/pizza_bomb.dm +++ b/code/game/objects/items/devices/pizza_bomb.dm @@ -76,7 +76,7 @@ user << "You can't see the box well enough to cut the wires out!" return user.visible_message("[user] starts removing the payload and wires from \the [src].", "You start removing the payload and wires from \the [src]...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/I.toolspeed, target = src)) playsound(src, 'sound/items/Wirecutter.ogg', 50, 1, 1) user.unEquip(src) user.visible_message("[user] removes the insides of \the [src]!", "You remove the insides of \the [src].") diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 2266c0aacc0..2c3baa3985e 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -269,7 +269,7 @@ /obj/item/device/tape/attackby(obj/item/I, mob/user, params) if(ruined && istype(I, /obj/item/weapon/screwdriver)) user << "You start winding the tape back in..." - if(do_after(user, 120, target = src)) + if(do_after(user, 120/I.toolspeed, target = src)) user << "You wound the tape back in." fix() diff --git a/code/game/objects/items/nuke_tools.dm b/code/game/objects/items/nuke_tools.dm index f8a0a093a12..dc36d390d6c 100644 --- a/code/game/objects/items/nuke_tools.dm +++ b/code/game/objects/items/nuke_tools.dm @@ -66,6 +66,7 @@ icon = 'icons/obj/nuke_tools.dmi' icon_state = "screwdriver_nuke" item_state = "screwdriver_nuke" + toolspeed = 2 /obj/item/weapon/screwdriver/nuke/New() //to skip screwdriver icon update diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index 181dd0a771a..a91ba35482c 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -34,14 +34,14 @@ /obj/item/weapon/storage/secure/attackby(obj/item/weapon/W, mob/user, params) if(locked) if (istype(W, /obj/item/weapon/screwdriver)) - if (do_after(user, 20, target = src)) + if (do_after(user, 20/W.toolspeed, target = src)) src.open =! src.open user.show_message(text("You [] the service panel.", (src.open ? "open" : "close"))) return if ((istype(W, /obj/item/device/multitool)) && (src.open == 1)&& (!src.l_hacking)) user.show_message(text("Now attempting to reset internal memory, please hold."), 1) src.l_hacking = 1 - if (do_after(usr, 100, target = src)) + if (do_after(usr, 100/W.toolspeed, target = src)) if (prob(40)) src.l_setshort = 1 src.l_set = 0 diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index f30bfdfc4da..029c0fd0238 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -145,9 +145,9 @@ Frequency: for(var/obj/machinery/computer/teleporter/com in machines) if(com.target) if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged) - L["[com.id] (Active)"] = com.target + L["[get_area(com.target)] (Active)"] = com.target else - L["[com.id] (Inactive)"] = com.target + L["[get_area(com.target)] (Inactive)"] = com.target var/list/turfs = list( ) for(var/turf/T in ultra_range(10, orange=1)) if(T.x>world.maxx-8 || T.x<8) continue //putting them at the edge is dumb diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index 8d9cdb1d80e..f878422481f 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -27,6 +27,7 @@ materials = list(MAT_METAL=150) origin_tech = "materials=1;engineering=1" attack_verb = list("bashed", "battered", "bludgeoned", "whacked") + toolspeed = 1 /obj/item/weapon/wrench/suicide_act(mob/user) user.visible_message("[user] is beating \himself to death with the [src.name]! It looks like \he's trying to commit suicide.") @@ -51,6 +52,7 @@ materials = list(MAT_METAL=75) attack_verb = list("stabbed") hitsound = 'sound/weapons/bladeslice.ogg' + toolspeed = 1 /obj/item/weapon/screwdriver/suicide_act(mob/user) user.visible_message(pick("[user] is stabbing the [src.name] into \his temple! It looks like \he's trying to commit suicide.", \ @@ -114,6 +116,7 @@ origin_tech = "materials=1;engineering=1" attack_verb = list("pinched", "nipped") hitsound = 'sound/items/Wirecutter.ogg' + toolspeed = 1 /obj/item/weapon/wirecutters/New(loc, var/param_color = null) ..() @@ -165,6 +168,7 @@ var/can_off_process = 0 var/light_intensity = 2 //how powerful the emitted light is when used. heat = 3800 + toolspeed = 1 /obj/item/weapon/weldingtool/New() ..() @@ -429,6 +433,7 @@ change_icons = 0 can_off_process = 1 light_intensity = 1 + toolspeed = 2 //Proc to make the experimental welder generate fuel, optimized as fuck -Sieve @@ -465,6 +470,7 @@ materials = list(MAT_METAL=50) origin_tech = "engineering=1" attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked") + toolspeed = 1 /obj/item/weapon/crowbar/suicide_act(mob/user) user.visible_message("[user] is beating \himself to death with the [src.name]! It looks like \he's trying to commit suicide.") @@ -485,3 +491,4 @@ throw_range = 3 materials = list(MAT_METAL=70) icon_state = "crowbar_large" + toolspeed = 2 diff --git a/code/game/objects/items/weapons/vending_items.dm b/code/game/objects/items/weapons/vending_items.dm index ef6efa237bb..0192ee513a1 100644 --- a/code/game/objects/items/weapons/vending_items.dm +++ b/code/game/objects/items/weapons/vending_items.dm @@ -63,8 +63,8 @@ /obj/item/weapon/vending_refill/autodrobe machine_name = "AutoDrobe" icon_state = "refill_costume" - charges = list(25, 2, 3)// of 75 standard, 6 contraband, 9 premium - init_charges = list(25, 2, 3) + charges = list(27, 2, 3)// of 75 standard, 6 contraband, 9 premium + init_charges = list(27, 2, 3) /obj/item/weapon/vending_refill/clothing machine_name = "ClothesMate" diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm index b56722da39d..7b97f9cc674 100644 --- a/code/game/objects/structures/ai_core.dm +++ b/code/game/objects/structures/ai_core.dm @@ -16,7 +16,7 @@ if(istype(P, /obj/item/weapon/wrench)) playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You start wrenching the frame into place..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) user << "You wrench the frame into place." anchored = 1 state = 1 @@ -27,7 +27,7 @@ return playsound(loc, 'sound/items/Welder.ogg', 50, 1) user << "You start to deconstruct the frame..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) if(!src || !WT.remove_fuel(0, user)) return user << "You deconstruct the frame." new /obj/item/stack/sheet/plasteel( loc, 4) @@ -36,7 +36,7 @@ if(istype(P, /obj/item/weapon/wrench)) playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You start to unfasten the frame..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/P.toolspeed, target = src)) user << "You unfasten the frame." anchored = 0 state = 0 diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index fc34408821c..cdf69fe37b9 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -213,7 +213,7 @@ return user << "You begin cutting \the [src] apart..." playsound(loc, cutting_sound, 40, 1) - if(do_after(user,40,5,1, target = src)) + if(do_after(user,40/W.toolspeed,5,1, target = src)) if( !opened || !istype(src, /obj/structure/closet) || !user || !WT || !WT.isOn() || !user.loc ) return playsound(loc, cutting_sound, 50, 1) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 4f7d765d7c0..bdeda3da687 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -126,7 +126,7 @@ qdel(src) return user << "You start to [open ? "close":"open"] the [src]" - if(do_after(user, 20, target = src)) + if(do_after(user, 20/W.toolspeed, target = src)) user << "You [open ? "close":"open"] the [src]" open = !open update_icon() @@ -187,7 +187,7 @@ if(istype(I, /obj/item/weapon/wrench)) user << "You start disassembling [src]..." playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - if(do_after(user, 30, target = src)) + if(do_after(user, 30/I.toolspeed, target = src)) playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) new /obj/item/stack/sheet/mineral/wood(get_turf(src)) qdel(src) diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index d1c53fa6d5f..e7df765745b 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -460,7 +460,7 @@ "You start to disassemble the airlock assembly...") playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if( !WT.isOn() ) return user << "You disassemble the airlock assembly." @@ -492,7 +492,7 @@ "You start to secure the airlock assembly to the floor...", \ "You hear wrenching.") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if( src.anchored ) return user << "You secure the airlock assembly." @@ -506,7 +506,7 @@ user.visible_message("[user] unsecures the airlock assembly from the floor.", \ "You start to unsecure the airlock assembly from the floor...", \ "You hear wrenching.") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if( !src.anchored ) return user << "You unsecure the airlock assembly." @@ -532,7 +532,7 @@ user.visible_message("[user] cuts the wires from the airlock assembly.", \ "You start to cut the wires from the airlock assembly...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if( src.state != 1 ) return user << "You cut the wires from the airlock assembly." @@ -562,7 +562,7 @@ user.visible_message("[user] removes the electronics from the airlock assembly.", \ "You start to remove electronics from the airlock assembly...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if( src.state != 2 ) return user << "You remove the airlock electronics." @@ -625,7 +625,7 @@ user.visible_message("[user] finishes the airlock.", \ "You start finishing the airlock...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(src.loc && state == 2) user << "You finish the airlock." var/obj/machinery/door/airlock/door diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 5b1b6e07594..295499ee016 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -20,7 +20,7 @@ playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) user.visible_message("[user] disassembles the girder.", \ "You start to disassemble the girder...", "You hear clanking and banging noises.") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(state != GIRDER_DISPLACED) return state = GIRDER_DISASSEMBLED @@ -31,7 +31,7 @@ else if(state == GIRDER_REINF) playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) user << "You start unsecuring support struts..." - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(state != GIRDER_REINF) return user << "You unsecure the support struts." @@ -44,7 +44,7 @@ return playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) user << "You start securing the girder..." - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) user << "You secure the girder." var/obj/structure/girder/G = new (loc) transfer_fingerprints_to(G) @@ -52,7 +52,7 @@ else if(state == GIRDER_NORMAL) playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) user << "You start unsecuring the girder..." - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) user << "You unsecure the girder." var/obj/structure/girder/displaced/D = new (loc) transfer_fingerprints_to(D) @@ -77,7 +77,7 @@ else if(istype(W, /obj/item/weapon/wirecutters) && state == GIRDER_REINF_STRUTS) playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) user << "You start removing support struts..." - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) user << "You remove the support struts." new /obj/item/stack/sheet/plasteel(get_turf(src)) var/obj/structure/girder/G = new (loc) @@ -125,7 +125,7 @@ transfer_fingerprints_to(T) qdel(src) return - + if(!istype(W,/obj/item/stack/sheet)) return @@ -322,7 +322,7 @@ if(WT.remove_fuel(0,user)) playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) user << "You start slicing apart the girder..." - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if( !WT.isOn() ) return user << "You slice apart the girder." diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index aa6b7d59ecf..a02c6ea02f8 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -80,7 +80,7 @@ mybag.attackby(I, user) else if(istype(I, /obj/item/weapon/crowbar)) user.visible_message("[user] begins to empty the contents of [src].", "You begin to empty the contents of [src]...") - if(do_after(user, 30, target = src)) + if(do_after(user, 30/I.toolspeed, target = src)) usr << "You empty the contents of [src]'s bucket onto the floor." reagents.reaction(src.loc) src.reagents.clear_reagents() diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index 4bc45ba6bcf..14ad3320de6 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -40,7 +40,7 @@ if(istype(I, /obj/item/weapon/crowbar)) if(!src.buckled_mob) playsound(loc, 'sound/items/Crowbar.ogg', 100, 1) - if(do_after(user, 20, target = src)) + if(do_after(user, 20/I.toolspeed, target = src)) user << "You pry the spikes out of the frame." new /obj/item/stack/rods(loc, 4) var/obj/F = new /obj/structure/kitchenspike_frame(src.loc,) diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index a1f3b4079de..d5dc481041d 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -41,35 +41,38 @@ else //wtf make your ladders properly assholes icon_state = "ladder00" -/obj/structure/ladder/attack_hand(mob/user) +/obj/structure/ladder/proc/go_up(mob/user,is_ghost) + if(!is_ghost) + show_fluff_message(1,user) + up.add_fingerprint(user) + user.loc = get_turf(up) + +/obj/structure/ladder/proc/go_down(mob/user,is_ghost) + if(!is_ghost) + show_fluff_message(0,user) + down.add_fingerprint(user) + user.loc = get_turf(down) + +/obj/structure/ladder/proc/use(mob/user,is_ghost=0) if(up && down) switch( alert("Go up or down the ladder?", "Ladder", "Up", "Down", "Cancel") ) if("Up") - user.visible_message("[user] climbs up \the [src].", \ - "You climb up \the [src].") - user.loc = get_turf(up) - up.add_fingerprint(user) + go_up(user,is_ghost) if("Down") - user.visible_message("[user] climbs down \the [src].", \ - "You climb down \the [src].") - user.loc = get_turf(down) - down.add_fingerprint(user) + go_down(user,is_ghost) if("Cancel") return - else if(up) - user.visible_message("[user] climbs up \the [src].", \ - "You climb up \the [src].") - user.loc = get_turf(up) - up.add_fingerprint(user) - + go_up(user,is_ghost) else if(down) - user.visible_message("[user] climbs down \the [src].", \ - "You climb down \the [src].") - user.loc = get_turf(down) - down.add_fingerprint(user) + go_down(user,is_ghost) + + if(!is_ghost) + add_fingerprint(user) - add_fingerprint(user) +/obj/structure/ladder/attack_hand(mob/user) + if(can_use(user)) + use(user) /obj/structure/ladder/attack_paw(mob/user) return attack_hand(user) @@ -77,6 +80,18 @@ /obj/structure/ladder/attackby(obj/item/weapon/W, mob/user, params) return attack_hand(user) +/obj/structure/ladder/attack_ghost(mob/dead/observer/user) + use(user,1) + +/obj/structure/ladder/proc/show_fluff_message(up,mob/user) + if(up) + user.visible_message("[user] climbs up \the [src].","You climb up \the [src].") + else + user.visible_message("[user] climbs down \the [src].","You climb down \the [src].") + +/obj/structure/ladder/proc/can_use(mob/user) + return 1 + /obj/structure/ladder/unbreakable/Destroy() return QDEL_HINT_LETMELIVE diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 04ce58ae6d6..27d46c1b551 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -70,7 +70,7 @@ if(WT.remove_fuel(0, user)) user << "You begin repairing [src]..." playsound(src, 'sound/items/Welder.ogg', 100, 1) - if(do_after(user, 10, target = src)) + if(do_after(user, 10/I.toolspeed, target = src)) if(!user || !WT || !WT.isOn()) return user << "You repair [src]." diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm index 5969168b473..b497f7e3e6b 100644 --- a/code/game/objects/structures/musician.dm +++ b/code/game/objects/structures/musician.dm @@ -349,7 +349,7 @@ if (!anchored && !isinspace()) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << " You begin to tighten \the [src] to the floor..." - if (do_after(user, 20, target = src)) + if (do_after(user, 20/O.toolspeed, target = src)) user.visible_message( \ "[user] tightens \the [src]'s casters.", \ "You tighten \the [src]'s casters. Now it can be played again.", \ @@ -358,7 +358,7 @@ else if(anchored) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << " You begin to loosen \the [src]'s casters..." - if (do_after(user, 40, target = src)) + if (do_after(user, 40/O.toolspeed, target = src)) user.visible_message( \ "[user] loosens \the [src]'s casters.", \ "You loosen \the [src]. Now it can be pulled somewhere else.", \ diff --git a/code/game/objects/structures/reflector.dm b/code/game/objects/structures/reflector.dm index 17705c71df7..f2857ca7a8b 100644 --- a/code/game/objects/structures/reflector.dm +++ b/code/game/objects/structures/reflector.dm @@ -41,7 +41,7 @@ if(istype(W, /obj/item/weapon/wrench)) if(anchored) user << "Unweld the [src] first!" - if(do_after(user, 80, target = src)) + if(do_after(user, 80/W.toolspeed, target = src)) playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You dismantle the [src]." qdel(src) @@ -54,7 +54,7 @@ user.visible_message("[user.name] starts to weld the [src.name] to the floor.", \ "You start to weld \the [src] to the floor...", \ "You hear welding.") - if (do_after(user,20, target = src)) + if (do_after(user,20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return anchored = 1 @@ -65,7 +65,7 @@ user.visible_message("[user.name] starts to cut the [src.name] free from the floor.", \ "You start to cut \the [src] free from the floor...", \ "You hear welding.") - if (do_after(user,20, target = src)) + if (do_after(user,20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return anchored = 0 diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm index 5c21ff8be4c..ae64e41c6a9 100644 --- a/code/game/objects/structures/signs.dm +++ b/code/game/objects/structures/signs.dm @@ -22,7 +22,7 @@ user.visible_message("[user] starts removing [src]...", \ "You start unfastening [src].") playsound(src, 'sound/items/Ratchet.ogg', 50, 1) - if(!do_after(user, 30, target = src)) + if(!do_after(user, 30/O.toolspeed, target = src)) return playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) user.visible_message("[user] unfastens [src].", \ diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index f54046e7065..94e395d6ac9 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -24,7 +24,7 @@ playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) user.visible_message("[user] is loosening the [name]'s bolts.", \ "You are loosening the [name]'s bolts...") - if(do_after(user,40, target = src)) + if(do_after(user,40/W.toolspeed, target = src)) if(!src.loc || !anchored) return user.visible_message("[user] loosened the [name]'s bolts!", \ @@ -37,7 +37,7 @@ playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) user.visible_message("[user] is securing the [name]'s bolts...", \ "You are securing the [name]'s bolts...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(!src.loc || anchored) return user.visible_message("[user] has secured the [name]'s bolts.", \ @@ -68,7 +68,7 @@ playsound(loc, 'sound/items/Welder.ogg', 40, 1) user.visible_message("[user] is slicing apart the [name].", \ "You are slicing apart the [name]...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(!src.loc) return playsound(loc, 'sound/items/Welder2.ogg', 50, 1) diff --git a/code/game/objects/structures/table_frames.dm b/code/game/objects/structures/table_frames.dm index cddb027e0e8..c05d1251165 100644 --- a/code/game/objects/structures/table_frames.dm +++ b/code/game/objects/structures/table_frames.dm @@ -24,7 +24,7 @@ if(istype(I, /obj/item/weapon/wrench)) user << "You start disassembling [src]..." playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - if(do_after(user, 30, target = src)) + if(do_after(user, 30/I.toolspeed, target = src)) playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) for(var/i = 1, i <= framestackamount, i++) new framestack(get_turf(src)) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 2d8da625c67..f114c0842c6 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -340,14 +340,14 @@ if(src.status == 2) user << "You start weakening the reinforced table..." playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) - if (do_after(user, 50, target = src)) + if (do_after(user, 50/W.toolspeed, target = src)) if(!src || !WT.isOn()) return user << "You weaken the table." src.status = 1 else user << "You start strengthening the reinforced table..." playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) - if (do_after(user, 50, target = src)) + if (do_after(user, 50/W.toolspeed, target = src)) if(!src || !WT.isOn()) return user << "You strengthen the table." src.status = 2 diff --git a/code/game/objects/structures/transit_tubes/transit_tube.dm b/code/game/objects/structures/transit_tubes/transit_tube.dm index e0429724bd5..28e788c0c2f 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube.dm @@ -46,7 +46,7 @@ obj/structure/transit_tube/ex_act(severity, target) return user.visible_message("[user] starts to deattach \the [src].", "You start to deattach the [name]...") playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) - if(do_after(user, 35, target = src)) + if(do_after(user, 35/W.toolspeed, target = src)) user << "You deattach the [name]." var/obj/structure/R = new tube_construction(src.loc) R.icon_state = src.icon_state diff --git a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm index 772b8e33c1b..010200185ee 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm @@ -95,7 +95,7 @@ if(istype(I, /obj/item/weapon/wrench)) user << "You start attaching the [name]..." src.add_fingerprint(user) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/I.toolspeed, target = src)) if(!src) return user << "You attach the [name]." var/obj/structure/transit_tube/R = src.buildtube() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 27a759dfc2b..365acba4a54 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -50,7 +50,7 @@ if(istype(I, /obj/item/weapon/crowbar)) user << "You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]..." playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 50, 1) - if(do_after(user, 30, target = src)) + if(do_after(user, 30/I.toolspeed, target = src)) user.visible_message("[user] [cistern ? "replaces the lid on the cistern" : "lifts the lid off the cistern"]!", "You [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]!", "You hear grinding porcelain.") cistern = !cistern update_icon() @@ -185,7 +185,7 @@ user << "The water temperature seems to be [watertemp]." if(istype(I, /obj/item/weapon/wrench)) user << "You begin to adjust the temperature valve with \the [I]..." - if(do_after(user, 50, target = src)) + if(do_after(user, 50/I.toolspeed, target = src)) switch(watertemp) if("normal") watertemp = "freezing" diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index ba1f9b119cd..829869976a4 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -83,7 +83,7 @@ user.visible_message("[user] disassembles the windoor assembly.", "You start to disassemble the windoor assembly...") playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(!src || !WT.isOn()) return user << "You disassemble the windoor assembly." var/obj/item/stack/sheet/rglass/RG = new (get_turf(src), 5) @@ -104,7 +104,7 @@ playsound(loc, 'sound/items/Ratchet.ogg', 100, 1) user.visible_message("[user] secures the windoor assembly to the floor.", "You start to secure the windoor assembly to the floor...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(!src || anchored) return for(var/obj/machinery/door/window/WD in loc) @@ -123,7 +123,7 @@ playsound(loc, 'sound/items/Ratchet.ogg', 100, 1) user.visible_message("[user] unsecures the windoor assembly to the floor.", "You start to unsecure the windoor assembly to the floor...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(!src || !anchored) return user << "You unsecure the windoor assembly." @@ -180,7 +180,7 @@ playsound(loc, 'sound/items/Wirecutter.ogg', 100, 1) user.visible_message("[user] cuts the wires from the airlock assembly.", "You start to cut the wires from airlock assembly...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(!src || state != "02") return @@ -218,7 +218,7 @@ playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1) user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to uninstall electronics from the airlock assembly...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(!src || !electronics) return user << "You remove the airlock electronics." @@ -248,7 +248,7 @@ playsound(loc, 'sound/items/Crowbar.ogg', 100, 1) user.visible_message("[user] pries the windoor into the frame.", "You start prying the windoor into the frame...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/W.toolspeed, target = src)) if(loc && electronics) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index cb0c4bc4957..9431ee3dc3d 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -200,7 +200,7 @@ else if(!reinf) user << (anchored ? "You begin to unscrew the window from the floor..." : "You begin to screw the window to the floor...") - if(do_after(user, 40, target = src)) + if(do_after(user, 40/I.toolspeed, target = src)) if(reinf && (state == 1 || state == 2)) //If state was unfastened, fasten it, else do the reverse state = (state == 1 ? 2 : 1) @@ -217,7 +217,7 @@ else if (istype(I, /obj/item/weapon/crowbar) && reinf && (state == 0 || state == 1)) user << (state == 0 ? "You begin to lever the window into the frame..." : "You begin to lever the window out of the frame...") playsound(loc, 'sound/items/Crowbar.ogg', 75, 1) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/I.toolspeed, target = src)) //If state was out of frame, put into frame, else do the reverse state = (state == 0 ? 1 : 0) user << (state == 1 ? "You pry the window into the frame." : "You pry the window out of the frame.") @@ -228,7 +228,7 @@ if(WT.remove_fuel(0,user)) user << "You begin repairing [src]..." playsound(loc, 'sound/items/Welder.ogg', 40, 1) - if(do_after(user, 40, target = src)) + if(do_after(user, 40/I.toolspeed, target = src)) health = maxhealth playsound(loc, 'sound/items/Welder2.ogg', 50, 1) else @@ -239,7 +239,7 @@ else if(istype(I, /obj/item/weapon/wrench) && !anchored) playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) user << " You begin to disassemble [src]..." - if(do_after(user, 40, target = src)) + if(do_after(user, 40/I.toolspeed, target = src)) if(disassembled) return //Prevents multiple deconstruction attempts diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 067ebe88eed..30db7ef2984 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -51,6 +51,9 @@ /turf/simulated/floor/plating/snow/ex_act(severity, target) contents_explosion(severity, target) +/turf/simulated/floor/plating/snow/cold + temperature = 220 //Enough to deal cold damage on breathing. TODO : Make these points defines + /turf/simulated/floor/plating/snow/gravsnow icon_state = "gravsnow" diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index b2db1033a60..d75c02fe808 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -98,7 +98,7 @@ if(istype(C, /obj/item/weapon/wrench)) user << "You begin removing rods..." playsound(src, 'sound/items/Ratchet.ogg', 80, 1) - if(do_after(user, 30, target = src)) + if(do_after(user, 30/C.toolspeed, target = src)) if(!istype(src, /turf/simulated/floor/engine)) return new /obj/item/stack/rods(src, 2) diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 5f9d7596609..074a31dabc3 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -178,7 +178,7 @@ if( WT.remove_fuel(0,user) ) user << "You begin slicing through the outer plating..." playsound(src, 'sound/items/Welder.ogg', 100, 1) - if(do_after(user, slicing_duration, target = src)) + if(do_after(user, slicing_duration/W.toolspeed, target = src)) if( !istype(src, /turf/simulated/wall) || !user || !WT || !WT.isOn() || !T ) return 1 if( user.loc == T && user.get_active_hand() == WT ) diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index 51dafa08dd5..3dae0081cdf 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -38,6 +38,10 @@ 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 + + if((copytext(msg, 1, 2) in list(".",";",":","#")) || (findtext(lowertext(copytext(msg, 1, 5)), "say"))) + if(alert("Your message \"[msg]\" looks like it was meant for in game communication, say it in OOC?", "Meant for OOC?", "No", "Yes") != "Yes") + return log_ooc("[mob.name]/[key] : [msg]") diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 642e7003c2b..47ec6ac32b3 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -380,19 +380,6 @@ var/global/floorIsLava = 0 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(0)) return @@ -695,14 +682,6 @@ var/global/floorIsLava = 0 message_admins("[key_name_admin(usr)] toggled guests game entering [guests_allowed?"":"dis"]allowed.") feedback_add_details("admin_verb","TGU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/unjobban_panel() - set name = "Unjobban Panel" - set category = "Admin" - if (src.holder) - src.holder.unjobbanpanel() - feedback_add_details("admin_verb","UJBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - /datum/admins/proc/output_ai_laws() var/ai_number = 0 for(var/mob/living/silicon/S in mob_list) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 4a7d2553f84..894d847646b 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -64,8 +64,6 @@ var/list/admin_verbs_admin = list( ) var/list/admin_verbs_ban = list( /client/proc/unban_panel, - /client/proc/jobbans, - /client/proc/unjobban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel ) @@ -372,17 +370,6 @@ var/list/admin_verbs_hideable = list( 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(holder) - 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" diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 1399c6310a5..d3eede9153b 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -1,109 +1,36 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -var/jobban_runonce // Updates legacy bans with new info -var/jobban_keylist[0] //to store the keys & ranks - -/proc/jobban_fullban(mob/M, rank, reason) - if (!M || !M.key) return - jobban_keylist.Add(text("[M.ckey] - [rank] ## [reason]")) - jobban_savebanfile() - -/proc/jobban_client_fullban(ckey, rank) - if (!ckey || !rank) return - jobban_keylist.Add(text("[ckey] - [rank]")) - jobban_savebanfile() - //returns a reason if M is banned from rank, returns 0 otherwise /proc/jobban_isbanned(mob/M, rank) - if(M && rank) - for (var/s in jobban_keylist) - if( findtext(s,"[M.ckey] - [rank]") == 1 ) - var/startpos = findtext(s, "## ")+3 - if(startpos && startpos Now())) AND isnull(unbanned)") + if(!query.Execute()) + log_game("SQL ERROR obtaining jobbans. Error : \[[query.ErrorMsg()]\]\n") + return + if(query.NextRow()) + var/reason = query.item[1] + return reason ? reason : 1 //we don't want to return "" if there is no ban reason, as that would evaluate to false + else + return 0 + + if(!M.client.jobbancache) + jobban_buildcache(M.client) + + if(rank in M.client.jobbancache) + var/reason = M.client.jobbancache[rank] + return (reason) ? reason : 1 //see above for why we need to do this return 0 -/* -DEBUG -/mob/verb/list_all_jobbans() - set name = "list all jobbans" - - for(var/s in jobban_keylist) - world << s - -/mob/verb/reload_jobbans() - set name = "reload jobbans" - - jobban_loadbanfile() -*/ - -/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") - else - if(!establish_db_connection()) - world.log << "Database connection failed. Reverting to the legacy ban system." - diary << "Database connection failed. Reverting to the legacy ban system." - config.ban_legacy_system = 1 - jobban_loadbanfile() +/proc/jobban_buildcache(client/C) + if(C && istype(C)) + C.jobbancache = list() + var/DBQuery/query = dbcon.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") + if(!query.Execute()) + log_game("SQL ERROR obtaining jobbans. Error : \[[query.ErrorMsg()]\]\n") return - - //Job permabans - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") - query.Execute() - while(query.NextRow()) - var/ckey = query.item[1] - var/job = query.item[2] - - jobban_keylist.Add("[ckey] - [job]") - - //Job tempbans - var/DBQuery/query1 = dbcon.NewQuery("SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") - query1.Execute() - - while(query1.NextRow()) - var/ckey = query1.item[1] - var/job = query1.item[2] - - jobban_keylist.Add("[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]") - jobban_savebanfile() - + C.jobbancache[query.item[1]] = query.item[2] /proc/ban_unban_log_save(var/formatted_log) text2file(formatted_log,"data/ban_unban_log.txt") - - -/proc/jobban_updatelegacybans() - if(!jobban_runonce) - log_admin("Updating jobbanfile!") - // Updates bans.. Or fixes them. Either way. - for(var/T in jobban_keylist) - if(!T) continue - jobban_runonce++ //don't run this update again - - -/proc/jobban_remove(X) - for (var/i = 1; i <= length(jobban_keylist); i++) - if( findtext(jobban_keylist[i], "[X]") ) - jobban_keylist.Remove(jobban_keylist[i]) - jobban_savebanfile() - return 1 - return 0 diff --git a/code/modules/admin/newbanjob.dm b/code/modules/admin/newbanjob.dm deleted file mode 100644 index 2c429f6de82..00000000000 --- a/code/modules/admin/newbanjob.dm +++ /dev/null @@ -1,276 +0,0 @@ -var/savefile/Banlistjob - - -/proc/_jobban_isbanned(client/clientvar, rank) - if(!clientvar) return 1 - ClearTempbansjob() - var/id = clientvar.computer_id - var/key = clientvar.ckey - if (guest_jobbans(rank)) - if(config.guest_jobban && IsGuestKey(key)) - return 1 - Banlistjob.cd = "/base" - if (Banlistjob.dir.Find("[key][id][rank]")) - return 1 - - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - if ((id == Banlistjob["id"] || key == Banlistjob["key"]) && rank == Banlistjob["rank"]) - return 1 - return 0 - - -///proc/UpdateTime() //No idea why i made this a proc. -// CMinutes = (world.realtime / 10) / 60 -// return 1 - -/proc/LoadBansjob() - - Banlistjob = new("data/job_fullnew.bdb") - log_admin("Loading Banlistjob") - - if (!length(Banlistjob.dir)) log_admin("Banlistjob is empty.") - - if (!Banlistjob.dir.Find("base")) - log_admin("Banlistjob missing base dir.") - Banlistjob.dir.Add("base") - Banlistjob.cd = "/base" - else if (Banlistjob.dir.Find("base")) - Banlistjob.cd = "/base" - - ClearTempbansjob() - return 1 - -/proc/ClearTempbansjob() - UpdateTime() - - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - //if (!Banlistjob["key"] || !Banlistjob["id"]) - // RemoveBanjob(A, "full") - // log_admin("Invalid Ban.") - // message_admins("Invalid Ban.") - // continue - - if (!Banlistjob["temp"]) continue - if (CMinutes >= Banlistjob["minutes"]) RemoveBanjob(A) - - return 1 - - -/proc/AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, rank) - UpdateTime() - var/bantimestamp - if (temp) - UpdateTime() - bantimestamp = CMinutes + minutes - if(rank == "Heads") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Personnel") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Captain") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - return 1 - if(rank == "Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Warden") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Security Officer") - return 1 - if(rank == "Engineering") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Station Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "Research") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - return 1 - if(rank == "Medical") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") - return 1 - if(rank == "CE_Station_Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Station Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "CE_Atmospheric_Tech") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "CE_Shaft_Miner") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Shaft Miner") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "Chemist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") - return 1 - if(rank == "Geneticist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - return 1 - if(rank == "MD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") - return 1 - if(rank == "Scientist_RD") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") - return 1 - if(rank == "AI_Cyborg") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "AI") - return 1 - if(rank == "Detective_HoS") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - return 1 - if(rank == "Virologist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Virologist") - return 1 - - Banlistjob.cd = "/base" - if ( Banlistjob.dir.Find("[ckey][computerid][rank]") ) - usr << text("Banjob already exists.") - return 0 - else - Banlistjob.dir.Add("[ckey][computerid][rank]") - Banlistjob.cd = "/base/[ckey][computerid][rank]" - Banlistjob["key"] << ckey - Banlistjob["id"] << computerid - Banlistjob["rank"] << rank - Banlistjob["reason"] << reason - Banlistjob["bannedby"] << bannedby - Banlistjob["temp"] << temp - if (temp) - Banlistjob["minutes"] << bantimestamp - - return 1 - -/proc/RemoveBanjob(foldername) - var/key - var/id - var/rank - Banlistjob.cd = "/base/[foldername]" - Banlistjob["key"] >> key - Banlistjob["id"] >> id - Banlistjob["rank"] >> rank - Banlistjob.cd = "/base" - - if (!Banlistjob.dir.Remove(foldername)) return 0 - - if(!usr) - log_admin("Banjob Expired: [key]") - message_admins("Banjob Expired: [key]") - else - log_admin("[key_name(usr)] unjobbanned [key] from [rank]") - message_admins("[key_name_admin(usr)] unjobbanned:[key] from [rank]") - ban_unban_log_save("[key_name(usr)] unjobbanned [key] from [rank]") - feedback_inc("ban_job_unban",1) - feedback_add_details("ban_job_unban","- [rank]") - - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - if ((key == Banlistjob["key"] || id == Banlistjob["id"]) && (rank == Banlistjob["rank"])) - Banlistjob.cd = "/base" - Banlistjob.dir.Remove(A) - continue - - return 1 - -/proc/GetExpjob(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/unjobbanpanel() - var/count = 0 - var/dat - //var/dat = "
Unban Player: \blue(U) = Unban , (E) = Edit Ban\green (Total
" - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - count++ - Banlistjob.cd = "/base/[A]" - dat += text("") - - dat += "
(U) Key: [Banlistjob["key"]] Rank: [Banlistjob["rank"]] ([Banlistjob["temp"] ? "[GetExpjob(Banlistjob["minutes"]) ? GetExpjob(Banlistjob["minutes"]) : "Removal pending" ]" : "Permaban"])(By: [Banlistjob["bannedby"]])(Reason: [Banlistjob["reason"]])
" - dat = "
Bans: (U) = Unban , - ([count] Bans)
[dat]" - usr << browse(dat, "window=unbanp;size=875x400") - -/*/datum/admins/proc/permjobban(ckey, computerid, reason, bannedby, temp, minutes, rank) - if(AddBanjob(ckey, computerid, reason, usr.ckey, 0, 0, job)) - M << "\redYou have been banned from [job] by [usr.client.ckey].\nReason: [reason]" - M << "\red This is a permanent ban." - if(config.banappeals) - M << "\red To try to resolve this matter head to [config.banappeals]" - else - M << "\red No ban appeals URL has been set." - log_admin("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") - message_admins("\blue[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") -/datum/admins/proc/timejobban(ckey, computerid, reason, bannedby, temp, minutes, rank) - if(AddBanjob(ckey, computerid, reason, usr.ckey, 1, mins, job)) - M << "\redYou have been jobbanned from [job] by [usr.client.ckey].\nReason: [reason]" - M << "\red This is a temporary ban, it will be removed in [mins] minutes." - if(config.banappeals) - M << "\red To try to resolve this matter head to [config.banappeals]" - else - M << "\red No ban appeals URL has been set." - log_admin("[usr.client.ckey] has jobbanned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("\blue[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")*/ -//////////////////////////////////// DEBUG //////////////////////////////////// - -/proc/CreateBansjob() - - UpdateTime() - - var/i - var/last - - for(i=0, i<1001, i++) - var/a = pick(1,0) - var/b = pick(1,0) - if(b) - Banlistjob.cd = "/base" - Banlistjob.dir.Add("trash[i]trashid[i]") - Banlistjob.cd = "/base/trash[i]trashid[i]" - Banlistjob["key"] << "trash[i]" - else - Banlistjob.cd = "/base" - Banlistjob.dir.Add("[last]trashid[i]") - Banlistjob.cd = "/base/[last]trashid[i]" - Banlistjob["key"] << last - Banlistjob["id"] << "trashid[i]" - Banlistjob["reason"] << "Trashban[i]." - Banlistjob["temp"] << a - Banlistjob["minutes"] << CMinutes + rand(1,2000) - Banlistjob["bannedby"] << "trashmin" - last = "trash[i]" - - Banlistjob.cd = "/base" - -/proc/ClearAllBansjob() - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - RemoveBanjob(A, "full") diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 39f37bc3d1a..dff4818781c 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -544,7 +544,7 @@ if(istype(ticker.mode, /datum/game_mode/blob)) var/datum/game_mode/blob/mode = ticker.mode dat += "
" - dat += "" + dat += "" for(var/datum/mind/blob in mode.infected_crew) var/mob/M = blob.current diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index dc08a87351b..e5123e44cb8 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -483,10 +483,6 @@ var/banreason = appearance_isbanned(M) if(banreason) - /* if(!config.ban_legacy_system) - 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") @@ -895,9 +891,6 @@ if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. switch(alert("Temporary Ban?",,"Yes","No", "Cancel")) if("Yes") - if(config.ban_legacy_system) - 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 @@ -912,7 +905,6 @@ 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 @@ -934,7 +926,6 @@ 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) @@ -961,7 +952,6 @@ DB_ban_unban(M.ckey, BANTYPE_ANY_JOB, 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 @@ -1029,21 +1019,6 @@ 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]") - jobban_remove(t) - href_list["ban"] = 1 // lets it fall through and refresh - var/t_split = text2list(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 @@ -1103,19 +1078,6 @@ if("Cancel") return - else if(href_list["unjobbanf"]) - if(!check_rights(R_BAN)) return - - var/banfolder = href_list["unjobbanf"] - Banlist.cd = "/base/[banfolder]" - var/key = Banlist["key"] - if(alert(usr, "Are you sure you want to unban [key]?", "Confirmation", "Yes", "No") == "Yes") - if (RemoveBanjob(banfolder)) - unjobbanpanel() - else - alert(usr,"This ban has already been lifted / does not exist.","Error","Ok") - unjobbanpanel() - //Watchlist else if(href_list["watchadd"]) var/target_ckey = locate(href_list["watchadd"]) diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 399afc597b5..dce65ac5269 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -97,26 +97,3 @@ message_admins("[key_name_admin(usr)] 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" - - usr << "Jobbans active in this round." - for(var/t in jobban_keylist) - usr << "[t]" - -/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" - - var/filter = input("Contains what?","Filter") as text|null - if(!filter) - return - - usr << "Jobbans active in this round." - for(var/t in jobban_keylist) - if(findtext(t, filter)) - usr << "[t]" diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 255c01abe52..74384f6a8c4 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -153,8 +153,6 @@ var/intercom_range_display_status = 0 src.verbs += /client/proc/cmd_admin_areatest src.verbs += /client/proc/cmd_admin_rejuvenate src.verbs += /datum/admins/proc/show_traitor_panel - src.verbs += /client/proc/print_jobban_old - src.verbs += /client/proc/print_jobban_old_filter src.verbs += /client/proc/disable_communication src.verbs += /client/proc/print_pointers src.verbs += /client/proc/count_movable_instances @@ -162,7 +160,6 @@ var/intercom_range_display_status = 0 src.verbs += /client/proc/cmd_show_at_list src.verbs += /client/proc/manipulate_organs src.verbs += /client/proc/reload_nanoui_resources - //src.verbs += /client/proc/cmd_admin_rejuvenate 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/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index 1e1f03457ac..ba6feda23f2 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -20,6 +20,14 @@ name = "\improper Academy Gateway" icon_state = "away4" +/area/awaymission/academy/academycellar + name = "\improper Academy Cellar" + icon_state = "away4" + +/area/awaymission/academy/academyengine + name = "\improper Academy Engine" + icon_state = "away4" + //Academy Items /obj/singularity/academy @@ -40,4 +48,295 @@ name = "The Lens of Truesight" desc = "I can see forever!" icon_state = "monocle" - item_state = "headset" \ No newline at end of file + item_state = "headset" + + +/obj/structure/academy_wizard_spawner + name = "Academy Defensive System" + desc = "Made by Abjuration Inc" + icon = 'icons/obj/cult.dmi' + icon_state = "forge" + anchored = 1 + var/health = 200 + var/mob/living/current_wizard = null + var/next_check = 0 + var/cooldown = 600 + var/faction = "wizard" + var/broken = 0 + var/braindead_check = 0 + +/obj/structure/academy_wizard_spawner/New() + SSobj.processing |= src + +/obj/structure/academy_wizard_spawner/process() + if(next_check < world.time) + if(!current_wizard) + for(var/mob/living/L in player_list) + if(L.z == src.z && L.stat != DEAD && !(faction in L.faction)) + summon_wizard() + break + else + if(current_wizard.stat == DEAD) + current_wizard = null + summon_wizard() + if(!current_wizard.client) + if(!braindead_check) + braindead_check = 1 + else + braindead_check = 0 + give_control() + next_check = world.time + cooldown + +/obj/structure/academy_wizard_spawner/proc/give_control() + if(!current_wizard) + return + spawn(0) + var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as Wizard Academy Defender?", "wizard") + var/mob/dead/observer/chosen = null + + if(candidates.len) + chosen = pick(candidates) + message_admins("[key_name_admin(chosen)] was spawned as Wizard Academy Defender") + current_wizard.ghostize() // on the off chance braindead defender gets back in + current_wizard.key = chosen.key + +/obj/structure/academy_wizard_spawner/proc/summon_wizard() + var/turf/T = src.loc + + var/mob/living/carbon/human/wizbody = new(T) + wizbody.equipOutfit(/datum/outfit/wizard/academy) + var/obj/item/weapon/implant/exile/Implant = new/obj/item/weapon/implant/exile(wizbody) + Implant.implant(wizbody) + wizbody.faction |= "wizard" + wizbody.real_name = "Academy Teacher" + wizbody.name = "Academy Teacher" + + var/datum/mind/wizmind = new /datum/mind() + wizmind.name = "Wizard Defender" + wizmind.special_role = "Academy Defender" + var/datum/objective/O = new("Protect Wizard Academy from the intruders") + wizmind.objectives += O + wizmind.transfer_to(wizbody) + ticker.mode.wizards |= wizmind + + wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt) + wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile) + wizmind.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball) + + current_wizard = wizbody + + give_control() + +/obj/structure/academy_wizard_spawner/proc/update_status() + if(health<0) + visible_message("[src] breaks down!") + icon_state = "forge_off" + SSobj.processing.Remove(src) + broken = 1 + +/obj/structure/academy_wizard_spawner/attackby(obj/item/weapon/W, mob/living/user, params) + add_fingerprint(user) + user.changeNext_move(CLICK_CD_MELEE) + if(!broken) + health -= W.force + update_status() + ..() + +/obj/structure/academy_wizard_spawner/bullet_act(obj/item/projectile/Proj) + if(!broken) + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + health -= Proj.damage + update_status() + ..() + return + +/datum/outfit/wizard/academy + name = "Academy Wizard" + r_pocket = null + r_hand = null + suit = /obj/item/clothing/suit/wizrobe/red + head = /obj/item/clothing/head/wizard/red + +/obj/item/weapon/dice/d20/fate + name = "Dice of Fate" + desc = "A die with twenty sides. You can feel unearthly energies radiating from it. Using this might be VERY risky." + icon_state = "d20" + sides = 20 + var/used = 0 + var/rigged = -1 + +/obj/item/weapon/dice/d20/fate/diceroll(mob/user) + ..() + if(!used) + if(!ishuman(user) || !user.mind || (user.mind in ticker.mode.wizards)) + user << "You feel the magic of the dice is restricted to ordinary humans!" + return + if(rigged > 0) + effect(user,rigged) + else + effect(user,result) + +/obj/item/weapon/dice/d20/fate/equipped(mob/user, slot) + if(!ishuman(user) || !user.mind || (user.mind in ticker.mode.wizards)) + user << "You feel the magic of the dice is restricted to ordinary humans! You should leave it alone." + user.drop_item() + + +/obj/item/weapon/dice/d20/fate/proc/effect(var/mob/living/carbon/human/user,roll) + used = 1 + visible_message("The die flare briefly.") + switch(roll) + if(1) + //Dust + user.dust() + if(2) + //Death + user.death() + if(3) + //Swarm of creatures + for(var/direction in alldirs) + var/turf/T = get_turf(src) + new /mob/living/simple_animal/hostile/creature(get_step(T,direction)) + if(4) + //Destroy Equipment + for (var/obj/item/I in user) + if (istype(I, /obj/item/weapon/implant)) + continue + qdel(I) + if(5) + //Monkeying + user.monkeyize() + if(6) + //Cut speed + var/datum/species/S = user.dna.species + S.speedmod += 1 + if(7) + //Throw + user.Stun(3) + user.adjustBruteLoss(50) + var/throw_dir = pick(cardinal) + var/atom/throw_target = get_edge_target_turf(user, throw_dir) + user.throw_at(throw_target, 200, 4) + if(8) + //Fueltank Explosion + explosion(src.loc,-1,0,2, flame_range = 2) + if(9) + //Cold + var/datum/disease/D = new /datum/disease/cold + user.ForceContractDisease(D) + if(10) + //Nothing + visible_message("[src] roll perfectly.") + if(11) + //Cookie + var/obj/item/weapon/reagent_containers/food/snacks/cookie/C = new(get_turf(src)) + C.name = "Cookie of Fate" + if(12) + //Healing + user.revive() + if(13) + //Mad Dosh + var/turf/Start = get_turf(src) + for(var/direction in alldirs) + var/turf/T = get_step(Start,direction) + if(rand(0,1)) + new /obj/item/stack/spacecash/c1000(T) + else + var/obj/item/weapon/moneybag/M = new(T) + for(var/i in 1 to rand(5,50)) + new /obj/item/weapon/coin/gold(M) + if(14) + //Free Gun + new /obj/item/weapon/gun/projectile/revolver/mateba(get_turf(src)) + if(15) + //Random One-use spellbook + new /obj/item/weapon/spellbook/oneuse/random(get_turf(src)) + if(16) + //Servant & Servant Summon + var/mob/living/carbon/human/H = new(get_turf(src)) + H.equipOutfit(/datum/outfit/butler) + var/datum/mind/servant_mind = new /datum/mind() + var/datum/objective/O = new("Serve [user.real_name].") + servant_mind.objectives += O + servant_mind.transfer_to(H) + + var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [user.real_name] Servant?", "wizard") + var/mob/dead/observer/chosen = null + + if(candidates.len) + chosen = pick(candidates) + message_admins("[key_name_admin(chosen)] was spawned as Dice Servant") + H.key = chosen.key + + var/obj/effect/proc_holder/spell/targeted/summonmob/S = new + S.target_mob = H + user.mind.AddSpell(S) + + if(17) + //Tator Kit + new /obj/item/weapon/storage/box/syndicate/(get_turf(src)) + if(18) + //Captain ID + new /obj/item/weapon/card/id/captains_spare(get_turf(src)) + if(19) + //Instrinct Resistance + user << "You feel robust." + var/datum/species/S = user.dna.species + S.brutemod *= 0.5 + S.burnmod *= 0.5 + S.coldmod *= 0.5 + if(20) + //Free wizard! + user.mind.make_Wizard() + + +/datum/outfit/butler + name = "Butler" + uniform = /obj/item/clothing/under/suit_jacket/really_black + shoes = /obj/item/clothing/shoes/laceup + head = /obj/item/clothing/head/bowler + glasses = /obj/item/clothing/glasses/monocle + gloves = /obj/item/clothing/gloves/color/white + +/obj/effect/proc_holder/spell/targeted/summonmob + name = "Summon Servant" + desc = "This spell can be used to call your servant, whenever you need it." + charge_max = 100 + clothes_req = 0 + invocation = "JE VES" + invocation_type = "whisper" + range = -1 + level_max = 0 //cannot be improved + cooldown_min = 100 + include_user = 1 + + var/mob/living/target_mob + + action_icon_state = "summons" + +/obj/effect/proc_holder/spell/targeted/summonmob/cast(list/targets,mob/user = usr) + if(!target_mob) + return + var/turf/Start = get_turf(user) + for(var/direction in alldirs) + var/turf/T = get_step(Start,direction) + if(!T.density) + target_mob.Move(T) + +/obj/structure/ladder/unbreakable/rune + name = "Teleportation Rune" + desc = "Could lead anywhere." + icon = 'icons/obj/rune.dmi' + icon_state = "1" + color = rgb(0,0,255) + +/obj/structure/ladder/unbreakable/rune/update_icon() + return + +/obj/structure/ladder/unbreakable/rune/show_fluff_message(up,mob/user) + user.visible_message("[user] activates \the [src].","You activate \the [src].") + +/obj/structure/ladder/can_use(mob/user) + if(user.mind in ticker.mode.wizards) + return 0 + return 1 \ No newline at end of file diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 7cc89dcd5f2..721a7827941 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -1,4 +1,21 @@ -#define ASSET_CACHE_SEND_TIMEOUT 25 // Amount of time MAX to send an asset, if this get exceeded we cancel the sleeping. +/* +Asset cache quick users guide: + +Make a datum at the bottom of this file with your assets for your thing. +The simple subsystem will most like be of use for most cases. +Then call get_asset_datum() with the type of the datum you created and store the return +Then call .send(client) on that stored return value. + +You can set verify to TRUE if you want send() to sleep until the client has the assets. +*/ + + +// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping. +// This is doubled for the first asset, then added per asset after +#define ASSET_CACHE_SEND_TIMEOUT 7 + +//When sending mutiple assets, how many before we give the client a quaint little sending resources message +#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 //List of ALL assets for the above, format is list(filename = asset). /var/global/list/asset_cache = list() @@ -10,6 +27,7 @@ var/last_asset_job = 0 // Last job done. //This proc sends the asset to the client, but only if it needs it. +//This proc blocks(sleeps) unless verify is set to false /proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE) if(!istype(client)) if(ismob(client)) @@ -28,8 +46,11 @@ client << browse_rsc(asset_cache[asset_name], asset_name) if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. - client.cache += asset_name + if (client) + client.cache += asset_name return 1 + if (!client) + return 0 client.sending |= asset_name var/job = ++client.last_asset_job @@ -41,7 +62,7 @@ "}, "window=asset_cache_browser") var/t = 0 - var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len + var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() sleep(1) // Lock up the caller until this is received. t++ @@ -53,6 +74,7 @@ return 1 +//This proc blocks(sleeps) unless verify is set to false /proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE) if(!istype(client)) if(ismob(client)) @@ -69,15 +91,18 @@ var/list/unreceived = asset_list - (client.cache + client.sending) if(!unreceived || !unreceived.len) return 0 - + if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) + client << "Sending Resources..." for(var/asset in unreceived) if (asset in asset_cache) client << browse_rsc(asset_cache[asset], asset) if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. - client.cache += unreceived + if (client) + client.cache += unreceived return 1 - + if (!client) + return 0 client.sending |= unreceived var/job = ++client.last_asset_job @@ -101,11 +126,15 @@ return 1 //This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start. +//The proc calls procs that sleep for long times. proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) for(var/file in files) + if (!client) + break if (register_asset) register_asset(file,files[file]) send_asset(client,file) + sleep(-1) //queuing calls like this too quickly can cause issues in some client versions //This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. //if it's an icon or something be careful, you'll have to copy it before further use. @@ -118,6 +147,11 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) var/datum/asset/A = new type() A.register() + for(var/client/C in clients) + //doing this to a client too soon after they've connected can cause issues, also the proc we call sleeps + spawn(10) + getFilesSlow(C, asset_cache, FALSE) + //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 @@ -238,5 +272,5 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) if(!islist(uncommon)) uncommon = list(uncommon) - send_asset_list(client, uncommon, FALSE) - send_asset_list(client, common, TRUE) \ No newline at end of file + send_asset_list(client, uncommon) + send_asset_list(client, common) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index c9217c164f7..e5b4a1d2895 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -6,6 +6,7 @@ var/datum/admins/holder = null var/buildmode = 0 + var/jobbancache = null //Used to cache this client's jobbans to save on DB queries var/last_message = "" //Contains the last message sent by this client - used to protect against copy-paste spamming. var/last_message_count = 0 //contins a number of how many times a message identical to last_message was sent. diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 44115f007f3..162739f4b2c 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -179,14 +179,8 @@ var/next_external_rsc = 0 else if (isnum(player_age) && player_age < config.notify_new_player_age) message_admins("New user: [key_name_admin(src)] just connected with an age of [player_age] day[(player_age==1?"":"s")]") - - - if (!ticker || ticker.current_state == GAME_STATE_PREGAME) - spawn (rand(10,150)) - if (src) - sync_client_with_db() - else - sync_client_with_db() + + sync_client_with_db() send_resources() diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index 1e84341c640..c733bde4e74 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -71,6 +71,14 @@ flags_cover = HEADCOVERSEYES flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE +/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_cover = HEADCOVERSEYES + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + /obj/item/clothing/head/justice name = "justice hat" desc = "Fight for what's righteous!" diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 1e9bf5303a6..095465dd72e 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -254,6 +254,33 @@ jetpack = new /obj/item/weapon/tank/jetpack/suit(src) ..() + +//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." + alt_desc = "An elite version of the syndicate helmet, with improved armour and fire shielding. It is in combat mode. Property of Gorlex Marauders." + icon_state = "hardsuit1-syndi" + item_state = "syndie_helm" + item_color = "syndi" + armor = list(melee = 60, bullet = 60, laser = 50, energy = 25, bomb = 55, bio = 100, rad = 70) + heat_protection = HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + + +/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." + alt_desc = "An elite version of the syndicate hardsuit, with improved armour and fire shielding. It is in combat mode." + icon_state = "hardsuit1-syndi" + item_state = "syndie_suit" + item_color = "syndi" + 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) + heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + + //The Owl Hardsuit /obj/item/clothing/head/helmet/space/hardsuit/syndi/owl name = "owl hardsuit helmet" diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 322ac1f17a0..321556ec6ff 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -165,6 +165,14 @@ body_parts_covered = CHEST|GROIN flags_inv = HIDEJUMPSUIT +/obj/item/clothing/suit/snowman + name = "snowman outfit" + desc = "Two white spheres covered in white glitter. 'Tis the season." + icon_state = "snowman" + item_state = "snowman" + body_parts_covered = CHEST|GROIN + flags_inv = HIDEJUMPSUIT + /obj/item/clothing/suit/poncho name = "poncho" desc = "Your classic, non-racist poncho." diff --git a/code/modules/events/false_alarm.dm b/code/modules/events/false_alarm.dm index 5c7a0c4d47a..38cdd546c5c 100644 --- a/code/modules/events/false_alarm.dm +++ b/code/modules/events/false_alarm.dm @@ -11,11 +11,18 @@ /datum/round_event/falsealarm/announce() var/list/events_list = list() for(var/datum/round_event_control/E in SSevent.control) - if(!E.wizardevent && !E.holidayID) //No holiday cheer allowed during non-holidays. Not even fake holiday cheer. - events_list += E //No holiday cheer allowed during non-holidays. Not even fake holiday cheer. + if(E.holidayID || E.wizardevent) + continue + var/datum/round_event/event = E.typepath + if(initial(event.announceWhen) <= 0) + continue + events_list += E + var/datum/round_event_control/event_control = pick(events_list) if(event_control) var/datum/round_event/Event = new event_control.typepath() message_admins("False Alarm: [Event]") Event.kill() //do not process this event - no starts, no ticks, no ends - Event.announce() //just announce it like it's happening \ No newline at end of file + Event.announce() //just announce it like it's happening + + diff --git a/code/modules/food&drinks/kitchen machinery/microwave.dm b/code/modules/food&drinks/kitchen machinery/microwave.dm index 94326f51489..878d51853b1 100644 --- a/code/modules/food&drinks/kitchen machinery/microwave.dm +++ b/code/modules/food&drinks/kitchen machinery/microwave.dm @@ -66,7 +66,7 @@ "[user] starts to fix part of the microwave.", \ "You start to fix part of the microwave..." \ ) - if (do_after(user,20, target = src)) + if (do_after(user,20/O.toolspeed, target = src)) user.visible_message( \ "[user] fixes part of the microwave.", \ "You fix part of the microwave." \ @@ -77,7 +77,7 @@ "[user] starts to fix part of the microwave.", \ "You start to fix part of the microwave..." \ ) - if (do_after(user,20, target = src)) + if (do_after(user,20/O.toolspeed, target = src)) user.visible_message( \ "[user] fixes the microwave.", \ "You fix the microwave." \ diff --git a/code/modules/html_interface/html_interface.dm b/code/modules/html_interface/html_interface.dm index 07a436813ac..38fb620fddb 100644 --- a/code/modules/html_interface/html_interface.dm +++ b/code/modules/html_interface/html_interface.dm @@ -109,7 +109,7 @@ mob/verb/test() // A type associated list of assets the interface needs. //Sent to the client when the interface opens on the client for the first time. - var/static/asset_list = list() + var/static/list/asset_list /datum/html_interface/New(atom/ref, title, width = 700, height = 480, head = "") html_interfaces.Add(src) @@ -143,7 +143,8 @@ mob/verb/test() for (var/R in resources) register_asset(R,resources[R]) assetlist += R - + if (!asset_list) + asset_list = list() asset_list[type] = assetlist /datum/html_interface/proc/createWindow(datum/html_interface_client/hclient) @@ -220,7 +221,8 @@ mob/verb/test() hclient = getClient(hclient, TRUE) if (istype(hclient)) - send_asset_list(hclient.client,asset_list[type], TRUE) + if (type in asset_list && islist(asset_list[type])) + send_asset_list(hclient.client, asset_list[type], TRUE) if (!winexists(hclient.client, "browser_\ref[src]")) src.createWindow(hclient) diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 85d0851f300..13c40680a50 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -768,7 +768,7 @@ user.visible_message("[user] begins to wrench [src] into place.", \ "You begin to wrench [src] in place...") playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) - if (do_after(user, 20, target = src)) + if (do_after(user, 20/O.toolspeed, target = src)) if(anchored) return anchored = 1 @@ -778,7 +778,7 @@ user.visible_message("[user] begins to unwrench [src].", \ "You begin to unwrench [src]...") playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) - if (do_after(user, 20, target = src)) + if (do_after(user, 20/O.toolspeed, target = src)) if(!anchored) return anchored = 0 diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 3cb02b4f5ed..1af63f8b489 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -38,13 +38,13 @@ if(0) if(istype(I, /obj/item/weapon/wrench)) playsound(loc, 'sound/items/Ratchet.ogg', 100, 1) - if(do_after(user, 20, target = src)) + if(do_after(user, 20/I.toolspeed, target = src)) user << "You wrench the frame into place." anchored = 1 state = 1 if(istype(I, /obj/item/weapon/crowbar)) playsound(loc, 'sound/items/Crowbar.ogg', 100, 1) - if(do_after(user, 20, target = src)) + if(do_after(user, 20/I.toolspeed, target = src)) user << "You pry the frame apart." new /obj/item/stack/sheet/mineral/wood(loc, 4) qdel(src) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index e9955f28606..f7791f68467 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -429,7 +429,7 @@ if (WT.remove_fuel(0, user)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away. if(src == user) user << "You start fixing youself..." - if(!do_after(user, 50, target = src)) + if(!do_after(user, 50/W.toolspeed, target = src)) return adjustBruteLoss(-30) @@ -510,7 +510,7 @@ else playsound(src, 'sound/items/Ratchet.ogg', 50, 1) user << "You start to unfasten [src]'s securing bolts..." - if(do_after(user, 50, target = src) && !cell) + if(do_after(user, 50/W.toolspeed, target = src) && !cell) user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!") deconstruct() diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index 7e8d710e44b..75e71a6ed77 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -154,4 +154,11 @@ corpsehelmet = /obj/item/clothing/head/bearpelt /obj/effect/landmark/mobcorpse/russian/ranged - corpsehelmet = /obj/item/clothing/head/ushanka \ No newline at end of file + corpsehelmet = /obj/item/clothing/head/ushanka + +/obj/effect/landmark/mobcorpse/wizard + name = "Space Wizard" + corpseuniform = /obj/item/clothing/under/color/lightpurple + corpsesuit = /obj/item/clothing/suit/wizrobe + corpseshoes = /obj/item/clothing/shoes/sandal + corpsehelmet = /obj/item/clothing/head/wizard \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm index 603edee9c56..9e4eac0075c 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm @@ -98,7 +98,7 @@ if(istype(I, /obj/item/weapon/screwdriver) && stat != DEAD) if(health < maxHealth) user << "You start to tighten loose screws on [src]..." - if(do_after(user,80,target=user)) + if(do_after(user,80/I.toolspeed,target=user)) adjustBruteLoss(-getBruteLoss()) visible_message("[user] tightens [src == user ? "their" : "[src]'s"] loose screws!", "You tighten [src == user ? "your" : "[src]'s"] loose screws.") else @@ -110,7 +110,7 @@ user.visible_message("[user] starts resetting [src]...", \ "You press down on [src]'s factory reset control...") playsound(src, 'sound/items/Ratchet.ogg', 50, 1) - if(!do_after(user, 50, target = src)) + if(!do_after(user, 50/I.toolspeed, target = src)) return user.visible_message("[user] resets [src]!", \ "You reset [src]'s directives to factory defaults!") diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index b9a2a102043..14a03d2dbb9 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -156,6 +156,33 @@ log_say("[src.real_name]/[src.key] : [text]") +/mob/living/proc/guardian_recall() + set name = "Recall Guardian" + set category = "Guardian" + set desc = "Forcibly recall your guardian." + for(var/mob/living/simple_animal/hostile/guardian/G in mob_list) + if(G.summoner == src) + G.Recall() + +/mob/living/proc/guardian_reset() + set name = "Reset Guardian Player (One Use)" + set category = "Guardian" + set desc = "Re-rolls which ghost will control your Guardian. One use." + for(var/mob/living/simple_animal/hostile/guardian/G in mob_list) + if(G.summoner == src) + var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [G.real_name]?", "pAI", null, FALSE, 100) + var/mob/dead/observer/new_stand = null + if(candidates.len) + new_stand = pick(candidates) + G << "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance." + src << "Your guardian has been successfully reset." + message_admins("[key_name_admin(new_stand)] has taken control of ([key_name_admin(G)])") + G.ghostize() + G.key = new_stand.key + src.verbs -= /mob/living/proc/guardian_reset + else + src << "There were no ghosts willing to take control. Looks like you're stuck with your Guardian for now." + /mob/living/simple_animal/hostile/guardian/proc/ToggleLight() if(!luminosity) SetLuminosity(3) @@ -598,6 +625,8 @@ G << "While personally invincible, you will die if [user.real_name] does, and any damage dealt to you will have a portion passed on to them as you feed upon them to sustain yourself." G << "[G.playstyle_string]" user.verbs += /mob/living/proc/guardian_comm + user.verbs += /mob/living/proc/guardian_recall + user.verbs += /mob/living/proc/guardian_reset switch (theme) if("magic") G.name = "[mob_name] [capitalize(picked_color)]" diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm index ea921265e4a..9f4c0d7d686 100644 --- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm +++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm @@ -80,7 +80,7 @@ if(!origin.changeling) M.make_changeling() if(origin.changeling.can_absorb_dna(M, owner)) - origin.changeling.add_profile(owner, M) + origin.changeling.add_new_profile(owner, M) origin.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) M.key = origin.key diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index d5b8135442b..b16d811e5ac 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -163,7 +163,7 @@ clothes_req = 0 range = 14 -/obj/effect/proc_holder/spell/aoe_turf/flicker_lights/cast(list/targets) +/obj/effect/proc_holder/spell/aoe_turf/flicker_lights/cast(list/targets,mob/user = usr) for(var/turf/T in targets) for(var/obj/machinery/light/L in T) L.flicker() @@ -179,7 +179,7 @@ clothes_req = 0 range = 10 -/obj/effect/proc_holder/spell/aoe_turf/blindness/cast(list/targets) +/obj/effect/proc_holder/spell/aoe_turf/blindness/cast(list/targets,mob/user = usr) for(var/mob/living/L in living_mob_list) var/turf/T = get_turf(L.loc) if(T && T in targets) @@ -198,7 +198,7 @@ range = -1 include_user = 1 -/obj/effect/proc_holder/spell/targeted/night_vision/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/night_vision/cast(list/targets,mob/user = usr) for(var/mob/living/target in targets) if(istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/H = target diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm new file mode 100644 index 00000000000..b8ddd18df4f --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm @@ -0,0 +1,79 @@ +/mob/living/simple_animal/hostile/wizard + name = "Space Wizard" + desc = "EI NATH?" + icon_state = "wizard" + icon_living = "wizard" + icon_dead = "wizard_dead" + speak_chance = 0 + turns_per_move = 3 + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + speed = 0 + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 5 + melee_damage_upper = 5 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = "harm" + 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 = 15 + faction = list("wizard") + status_flags = CANPUSH + + retreat_distance = 3 //out of fireball range + minimum_distance = 3 + + var/obj/effect/proc_holder/spell/dumbfire/fireball/fireball = null + var/obj/effect/proc_holder/spell/targeted/turf_teleport/blink/blink = null + var/obj/effect/proc_holder/spell/targeted/projectile/magic_missile/mm = null + + var/next_cast = 0 + + +/mob/living/simple_animal/hostile/wizard/New() + ..() + fireball = new /obj/effect/proc_holder/spell/dumbfire/fireball + fireball.clothes_req = 0 + fireball.human_req = 0 + fireball.player_lock = 0 + AddSpell(fireball) + + mm = new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile + mm.clothes_req = 0 + mm.human_req = 0 + mm.player_lock = 0 + AddSpell(mm) + + blink = new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink + blink.clothes_req = 0 + blink.human_req = 0 + blink.player_lock = 0 + blink.outer_tele_radius = 3 + AddSpell(blink) + +/mob/living/simple_animal/hostile/wizard/death(gibbed) + ..(gibbed) + new /obj/effect/landmark/mobcorpse/wizard(src.loc) + new /obj/item/weapon/staff(src.loc) + qdel(src) + return + +/mob/living/simple_animal/hostile/wizard/handle_automated_action() + . = ..() + if(target && next_cast < world.time) + if((get_dir(src,target) in list(SOUTH,EAST,WEST,NORTH)) && fireball.cast_check(0,src)) //Lined up for fireball + src.dir = get_dir(src,target) + fireball.choose_targets(src) + next_cast = world.time + 10 //One spell per second + return . + if(mm.cast_check(0,src)) + mm.choose_targets(src) + next_cast = world.time + 10 + return . + if(blink.cast_check(0,src)) //Spam Blink when you can + blink.choose_targets(src) + next_cast = world.time + 10 + return . diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 99fcaf0a589..75e9cc96ee3 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -262,7 +262,7 @@ return playsound(loc, 'sound/items/Ratchet.ogg', 50, 1) user << "You start [anchored ? "unwrenching" : "wrenching"] [src]..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/O.toolspeed, target = src)) if(gc_destroyed) return user << "You [anchored ? "unwrench" : "wrench"] [src]." diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index bf39d7dc76d..1507f09f2f4 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -386,7 +386,7 @@ return playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) user << "You are trying to remove the power control board..." //lpeters - fixed grammar issues - if(do_after(user, 50, target = src)) + if(do_after(user, 50/W.toolspeed, target = src)) if (has_electronics==1) has_electronics = 0 if ((stat & BROKEN) || malfhack) @@ -518,7 +518,7 @@ "You start welding the APC frame...", \ "You hear welding.") playsound(src.loc, 'sound/items/Welder.ogg', 50, 1) - if(do_after(user, 50, target = src)) + if(do_after(user, 50/W.toolspeed, target = src)) if(!src || !WT.remove_fuel(3, user)) return if (emagged || malfhack || (stat & BROKEN) || opened==2) new /obj/item/stack/sheet/metal(loc) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 62a288969c4..75484746e4c 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -60,7 +60,7 @@ if(istype(W, /obj/item/weapon/wrench)) playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) usr << "You begin deconstructing [src]..." - if (!do_after(usr, 30, target = 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].", \ diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 63c1566b534..eecacac5f85 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -228,7 +228,7 @@ user.visible_message("[user.name] starts to weld the [src.name] to the floor.", \ "You start to weld \the [src] to the floor...", \ "You hear welding.") - if (do_after(user,20, target = src)) + if (do_after(user,20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return state = 2 user << "You weld \the [src] to the floor." @@ -239,7 +239,7 @@ user.visible_message("[user.name] starts to cut the [src.name] free from the floor.", \ "You start to cut \the [src] free from the floor...", \ "You hear welding.") - if (do_after(user,20, target = src)) + if (do_after(user,20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return state = 1 user << "You cut \the [src] free from the floor." diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 92c5e2070d1..008416ac9e5 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -130,7 +130,7 @@ field_generator power level display user.visible_message("[user.name] starts to weld the [src.name] to the floor.", \ "You start to weld \the [src] to the floor...", \ "You hear welding.") - if (do_after(user,20, target = src)) + if (do_after(user,20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return state = 2 user << "You weld the field generator to the floor." @@ -142,7 +142,7 @@ field_generator power level display user.visible_message("[user.name] starts to cut the [src.name] free from the floor.", \ "You start to cut \the [src] free from the floor...", \ "You hear welding.") - if (do_after(user,20, target = src)) + if (do_after(user,20/W.toolspeed, target = src)) if(!src || !WT.isOn()) return state = 1 user << "You cut \the [src] free from the floor." diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index d7a58ac043f..3810f00ff43 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -60,7 +60,7 @@ if(istype(W, /obj/item/weapon/crowbar)) playsound(src.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(do_after(user, 50, target = src)) + if(do_after(user, 50/W.toolspeed, target = src)) var/obj/item/solar_assembly/S = locate() in src if(S) S.loc = src.loc @@ -386,10 +386,10 @@ data["connected_tracker"] = (connected_tracker ? 1 : 0) return data -/obj/machinery/power/solar_control/attackby(obj/I, mob/user, params) +/obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/weapon/screwdriver)) playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 20, target = src)) + if(do_after(user, 20/I.toolspeed, target = src)) if (src.stat & BROKEN) user << "The broken glass falls out." var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index 874b2ca1733..233685983a3 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -63,7 +63,7 @@ if(istype(W, /obj/item/weapon/crowbar)) playsound(src.loc, 'sound/machines/click.ogg', 50, 1) user.visible_message("[user] begins to take the glass off the solar tracker.", "You begin to take the glass off the solar tracker...") - if(do_after(user, 50, target = src)) + if(do_after(user, 50/W.toolspeed, target = src)) var/obj/item/solar_assembly/S = locate() in src if(S) S.loc = src.loc diff --git a/code/modules/procedural mapping/mapGenerators/syndicate.dm b/code/modules/procedural mapping/mapGenerators/syndicate.dm index 7973471ed54..a9fe00c8b6d 100644 --- a/code/modules/procedural mapping/mapGenerators/syndicate.dm +++ b/code/modules/procedural mapping/mapGenerators/syndicate.dm @@ -49,4 +49,4 @@ /datum/mapGeneratorModule/border/syndieWalls,\ /datum/mapGeneratorModule/syndieFurniture, \ /datum/mapGeneratorModule/splatterLayer/syndieMobs, \ - /datum/mapGeneratorModule/bottomLayer/repressurize) \ No newline at end of file + /datum/mapGeneratorModule/bottomLayer/repressurize) diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index 7cc46f27a26..2b98c2d7e15 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -122,7 +122,7 @@ afterattack(user, user) //you know the drill user.visible_message("[src] goes off!", "[src] goes off in your face!") return - if(do_after(user, 30, target = src)) + if(do_after(user, 30/A.toolspeed, target = src)) if(magazine.ammo_count()) user << "You can't modify it!" return @@ -135,7 +135,7 @@ afterattack(user, user) //and again user.visible_message("[src] goes off!", "[src] goes off in your face!") return - if(do_after(user, 30, target = src)) + if(do_after(user, 30/A.toolspeed, target = src)) if(magazine.ammo_count()) user << "You can't modify it!" return diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/recycling/disposal-construction.dm index 5d869bde26c..b1227bb5b80 100644 --- a/code/modules/recycling/disposal-construction.dm +++ b/code/modules/recycling/disposal-construction.dm @@ -237,7 +237,7 @@ if(W.remove_fuel(0,user)) playsound(loc, 'sound/items/Welder2.ogg', 100, 1) user << "You start welding the [nicetype] in place..." - if(do_after(user, 20, target = src)) + if(do_after(user, 20/I.toolspeed, target = src)) if(!loc || !W.isOn()) return user << "The [nicetype] has been welded in place." diff --git a/code/modules/recycling/disposal-structures.dm b/code/modules/recycling/disposal-structures.dm index 0589e10be6d..7ea048f1bf3 100644 --- a/code/modules/recycling/disposal-structures.dm +++ b/code/modules/recycling/disposal-structures.dm @@ -693,7 +693,7 @@ if(W.remove_fuel(0,user)) playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1) user << "You start slicing the disposal pipe..." - if(do_after(user,30, target = src)) + if(do_after(user,30/I.toolspeed, target = src)) if(!src || !W.isOn()) return Deconstruct() user << "You slice the disposal pipe." @@ -827,7 +827,7 @@ if(W.remove_fuel(0,user)) playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1) user << "You start slicing the floorweld off \the [src]..." - if(do_after(user,20, target = src)) + if(do_after(user,20/I.toolspeed, target = src)) if(!src || !W.isOn()) return user << "You slice the floorweld off \the [src]." stored.loc = loc diff --git a/code/modules/recycling/disposal-unit.dm b/code/modules/recycling/disposal-unit.dm index 0607f28e607..74be5a2317d 100644 --- a/code/modules/recycling/disposal-unit.dm +++ b/code/modules/recycling/disposal-unit.dm @@ -89,7 +89,7 @@ return playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1) user << "You start slicing the floorweld off \the [src]..." - if(do_after(user,20, target = src)) + if(do_after(user,20/I.toolspeed, target = src)) if(!W.isOn()) return user << "You slice the floorweld off \the [src]." diff --git a/code/world.dm b/code/world.dm index fe34671e0e2..d47c2a97b62 100644 --- a/code/world.dm +++ b/code/world.dm @@ -34,12 +34,9 @@ var/global/list/map_transition_config = MAP_TRANSITION_CONFIG load_mode() load_motd() load_admins() - LoadBansjob() if(config.usewhitelist) load_whitelist() - jobban_loadbanfile() appearance_loadbanfile() - jobban_updatelegacybans() LoadBans() investigate_reset() diff --git a/html/changelog.html b/html/changelog.html index c517cd19db7..a745df8b633 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,10 +56,30 @@ -->

29 November 2015

+

Firecage updated:

+
    +
  • Hand tools now have variable operating speeds.
  • +
+

GunHog updated:

+
    +
  • Nanotrasen has improved the interface for the hand-held teleportation device. It will now provide the user with the name of the destination tracking beacon.
  • +
+

JJRcop updated:

+
    +
  • Fixes changeling Hive Absorb DNA ability runtime.
  • +
+

KorPhaeron updated:

+
    +
  • Guardians and holoparasites can now be manually recalled by their master.
  • +
  • The ghost controlling a guardian or holoparasite can be repicked by their master, only one use which is removed when a new ghost is chosen.
  • +

MrStonedOne updated:

  • Re-adds 100% chance timed injectors, as it was removed over a misunderstanding.
  • Fixes bug with injectors having a 100% power chance despite not being timed
  • +
  • Fixes the excessive amount of time it took the game to initialize.
  • +
  • Fixes some interface bugs.
  • +
  • when an interface window has to send a lot of resources before it can open, it will now tell you with a "sending resources" line so you know why it's not opening right away.

PKPenguin321 updated:

    @@ -67,6 +87,29 @@
  • Teleprods can have their crystal removed and be made back into stunprods by using them in your hand when they have no cell installed.
  • Mounted chainsaws now have a unique inhand sprite.
+

Remie + Kor updated:

+
    +
  • Holopads now use AltClick to request the AI
  • +
+

TheNightingale updated:

+
    +
  • Nuclear operatives can now buy the Elite Syndicate Hardsuit for 8 TC that has better armor and fireproofing.
  • +
  • Added throwing stars and Syndicate playing cards to nuclear operative's uplink.
  • +
+

Yolopanther12 updated:

+
    +
  • Added the ability to use a box of lights on a light replacer to more quickly refill it to capacity.
  • +
+

incoming5643 updated:

+
    +
  • OOC will now take steps to try and prevent accidental IC from people who mistakenly use OOC instead of say. This doesn't catch all mistakes, so please don't test it.
  • +
+

ktccd updated:

+
    +
  • Blob pieces on some areas no longer count for blob mode victory (Such as space or asteroid).
  • +
  • Blobcode no longer spawns and destroys blobs uselessly, which fixes other stuff too.
  • +
  • Asteroid areas are no longer valid gang territory.
  • +

neersighted updated:

  • Nanotrasen would like to announce NanoUI 2.0, now being rolled out across all our stations. Existing NanoUI devices have been updated, and more will be added soon. Please report any bugs to Nanotrasen Support.
  • @@ -74,6 +117,11 @@
  • Some NanoUI interfaces have had more information added.
  • Drones can now interact with NanoUI windows.
+

swankcookie updated:

+
    +
  • Fixes issue of space-Christians forgetting about the true, humbling values of space-Christmas.
  • +
  • Adds snowmen outfits
  • +

28 November 2015

TechnoAlchemisto updated:

diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 56d72683ca9..6abd4e726ed 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -2604,10 +2604,26 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. TechnoAlchemisto: - tweak: Cloaks are now worn in your exosuit slot! 2015-11-29: + Firecage: + - rscadd: Hand tools now have variable operating speeds. + GunHog: + - tweak: Nanotrasen has improved the interface for the hand-held teleportation device. + It will now provide the user with the name of the destination tracking beacon. + JJRcop: + - bugfix: Fixes changeling Hive Absorb DNA ability runtime. + KorPhaeron: + - rscadd: Guardians and holoparasites can now be manually recalled by their master. + - rscadd: The ghost controlling a guardian or holoparasite can be repicked by their + master, only one use which is removed when a new ghost is chosen. MrStonedOne: - rscadd: Re-adds 100% chance timed injectors, as it was removed over a misunderstanding. - bugfix: Fixes bug with injectors having a 100% power chance despite not being timed + - bugfix: Fixes the excessive amount of time it took the game to initialize. + - bugfix: Fixes some interface bugs. + - rscadd: when an interface window has to send a lot of resources before it can + open, it will now tell you with a "sending resources" line so you know why it's + not opening right away. PKPenguin321: - rscadd: Teleprods can now be constructed. Make them via tablecrafting, or by putting a bluespace crystal onto a stunprod. They warp the target to a random nearby @@ -2616,6 +2632,26 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - rscadd: Teleprods can have their crystal removed and be made back into stunprods by using them in your hand when they have no cell installed. - imageadd: Mounted chainsaws now have a unique inhand sprite. + Remie + Kor: + - tweak: Holopads now use AltClick to request the AI + TheNightingale: + - rscadd: Nuclear operatives can now buy the Elite Syndicate Hardsuit for 8 TC that + has better armor and fireproofing. + - tweak: Added throwing stars and Syndicate playing cards to nuclear operative's + uplink. + Yolopanther12: + - rscadd: Added the ability to use a box of lights on a light replacer to more quickly + refill it to capacity. + incoming5643: + - rscadd: OOC will now take steps to try and prevent accidental IC from people who + mistakenly use OOC instead of say. This doesn't catch all mistakes, so please + don't test it. + ktccd: + - bugfix: Blob pieces on some areas no longer count for blob mode victory (Such + as space or asteroid). + - bugfix: Blobcode no longer spawns and destroys blobs uselessly, which fixes other + stuff too. + - bugfix: Asteroid areas are no longer valid gang territory. neersighted: - experiment: Nanotrasen would like to announce NanoUI 2.0, now being rolled out across all our stations. Existing NanoUI devices have been updated, and more @@ -2623,3 +2659,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: Many NanoUI interfaces have had +/- buttons replaced with input fields. - tweak: Some NanoUI interfaces have had more information added. - bugfix: Drones can now interact with NanoUI windows. + swankcookie: + - bugfix: Fixes issue of space-Christians forgetting about the true, humbling values + of space-Christmas. + - rscadd: Adds snowmen outfits diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 93856d3e80d..b1c94f4780c 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 009bba1db5c..c58a95408ab 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index 847f3d4302c..ef3d349f6c8 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index 4f6d6740f67..d56674bce74 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 82e89958418..9941fef084c 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/nano/images/icon-eye.xcf b/nano/image_source/icon-eye.xcf similarity index 100% rename from nano/images/icon-eye.xcf rename to nano/image_source/icon-eye.xcf diff --git a/nano/images/source/NTLogoRevised.fla b/nano/image_source/source/NTLogoRevised.fla similarity index 100% rename from nano/images/source/NTLogoRevised.fla rename to nano/image_source/source/NTLogoRevised.fla diff --git a/nano/images/source/icon-eye.xcf b/nano/image_source/source/icon-eye.xcf similarity index 100% rename from nano/images/source/icon-eye.xcf rename to nano/image_source/source/icon-eye.xcf diff --git a/nano/images/source/uiBackground-Syndicate.xcf b/nano/image_source/source/uiBackground-Syndicate.xcf similarity index 100% rename from nano/images/source/uiBackground-Syndicate.xcf rename to nano/image_source/source/uiBackground-Syndicate.xcf diff --git a/nano/images/source/uiBackground.fla b/nano/image_source/source/uiBackground.fla similarity index 100% rename from nano/images/source/uiBackground.fla rename to nano/image_source/source/uiBackground.fla diff --git a/nano/images/source/uiBackground.xcf b/nano/image_source/source/uiBackground.xcf similarity index 100% rename from nano/images/source/uiBackground.xcf rename to nano/image_source/source/uiBackground.xcf diff --git a/nano/images/source/uiBasicBackground.xcf b/nano/image_source/source/uiBasicBackground.xcf similarity index 100% rename from nano/images/source/uiBasicBackground.xcf rename to nano/image_source/source/uiBasicBackground.xcf diff --git a/nano/images/source/uiIcons16Green.xcf b/nano/image_source/source/uiIcons16Green.xcf similarity index 100% rename from nano/images/source/uiIcons16Green.xcf rename to nano/image_source/source/uiIcons16Green.xcf diff --git a/nano/images/source/uiIcons16Red.xcf b/nano/image_source/source/uiIcons16Red.xcf similarity index 100% rename from nano/images/source/uiIcons16Red.xcf rename to nano/image_source/source/uiIcons16Red.xcf diff --git a/nano/images/source/uiIcons24.xcf b/nano/image_source/source/uiIcons24.xcf similarity index 100% rename from nano/images/source/uiIcons24.xcf rename to nano/image_source/source/uiIcons24.xcf diff --git a/nano/images/source/uiNoticeBackground.xcf b/nano/image_source/source/uiNoticeBackground.xcf similarity index 100% rename from nano/images/source/uiNoticeBackground.xcf rename to nano/image_source/source/uiNoticeBackground.xcf diff --git a/nano/images/source/uiTitleBackground.xcf b/nano/image_source/source/uiTitleBackground.xcf similarity index 100% rename from nano/images/source/uiTitleBackground.xcf rename to nano/image_source/source/uiTitleBackground.xcf diff --git a/nano/images/uiBackground.xcf b/nano/image_source/uiBackground.xcf similarity index 100% rename from nano/images/uiBackground.xcf rename to nano/image_source/uiBackground.xcf diff --git a/nano/images/uiIcons24.xcf b/nano/image_source/uiIcons24.xcf similarity index 100% rename from nano/images/uiIcons24.xcf rename to nano/image_source/uiIcons24.xcf diff --git a/nano/images/uiNoticeBackground.xcf b/nano/image_source/uiNoticeBackground.xcf similarity index 100% rename from nano/images/uiNoticeBackground.xcf rename to nano/image_source/uiNoticeBackground.xcf diff --git a/nano/images/uiTitleBackground.xcf b/nano/image_source/uiTitleBackground.xcf similarity index 100% rename from nano/images/uiTitleBackground.xcf rename to nano/image_source/uiTitleBackground.xcf diff --git a/tgstation.dme b/tgstation.dme index eae34d028c4..b7d5c907c8f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -842,7 +842,6 @@ #include "code\modules\admin\holder2.dm" #include "code\modules\admin\IsBanned.dm" #include "code\modules\admin\NewBan.dm" -#include "code\modules\admin\newbanjob.dm" #include "code\modules\admin\player_panel.dm" #include "code\modules\admin\secrets.dm" #include "code\modules\admin\sql_notes.dm" @@ -1329,6 +1328,7 @@ #include "code\modules\mob\living\simple_animal\hostile\statue.dm" #include "code\modules\mob\living\simple_animal\hostile\syndicate.dm" #include "code\modules\mob\living\simple_animal\hostile\tree.dm" +#include "code\modules\mob\living\simple_animal\hostile\wizard.dm" #include "code\modules\mob\living\simple_animal\hostile\zombie.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\bat.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" diff --git a/tools/dmitool/README.txt b/tools/dmitool/README.txt new file mode 100644 index 00000000000..409ab873c3d --- /dev/null +++ b/tools/dmitool/README.txt @@ -0,0 +1,5 @@ +Uses PNGJ: https://code.google.com/p/pngj/. + +For help, use "java -jar dmitool.jar help". + +Requires Java 7. \ No newline at end of file diff --git a/tools/dmitool/build.gradle b/tools/dmitool/build.gradle new file mode 100644 index 00000000000..2e5fbc5b08e --- /dev/null +++ b/tools/dmitool/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + compile group: 'ar.com.hjg', name: 'pngj', version: '2.1.0' +} + +jar { + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + manifest { + attributes 'Main-Class': 'dmitool.Main' + } +} \ No newline at end of file diff --git a/tools/dmitool/dmimerge.sh b/tools/dmitool/dmimerge.sh new file mode 100644 index 00000000000..dbe846fc6a9 --- /dev/null +++ b/tools/dmitool/dmimerge.sh @@ -0,0 +1,8 @@ +java -jar tools/dmitool/dmitool.jar merge $1 $2 $3 $2 +if [ "$?" -gt 0 ] +then + echo "Unable to automatically resolve all icon_state conflicts, please merge manually." + exit 1 +fi + +exit 0 diff --git a/tools/dmitool/dmitool.jar b/tools/dmitool/dmitool.jar new file mode 100644 index 00000000000..4cfa49f36de Binary files /dev/null and b/tools/dmitool/dmitool.jar differ diff --git a/tools/dmitool/dmitool.py b/tools/dmitool/dmitool.py new file mode 100644 index 00000000000..390f0d745ff --- /dev/null +++ b/tools/dmitool/dmitool.py @@ -0,0 +1,94 @@ +""" Python 2.7 wrapper for dmitool. +""" + +import os +from subprocess import Popen, PIPE + +_JAVA_PATH = ["java"] +_DMITOOL_CMD = ["-jar", "dmitool.jar"] + +def _dmitool_call(*dmitool_args, **popen_args): + return Popen(_JAVA_PATH + _DMITOOL_CMD + [str(arg) for arg in dmitool_args], **popen_args) + +def _safe_parse(dict, key, deferred_value): + try: + dict[key] = deferred_value() + except Exception as e: + print "Could not parse property '%s': %s"%(key, e) + return e + return False + +def version(): + """ Returns the version as a string. """ + stdout, stderr = _dmitool_call("version", stdout=PIPE).communicate() + return str(stdout).strip() + +def help(): + """ Returns the help text as a string. """ + stdout, stderr = _dmitool_call("help", stdout=PIPE).communicate() + return str(stdout).strip() + +def info(filepath): + """ Totally not a hack that parses the output from dmitool into a dictionary. + May break at any moment. + """ + subproc = _dmitool_call("info", filepath, stdout=PIPE) + stdout, stderr = subproc.communicate() + + result = {} + data = stdout.split(os.linesep)[1:] + #for s in data: print s + + #parse header line + if len(data) > 0: + header = data.pop(0).split(",") + #don't need to parse states, it's redundant + _safe_parse(result, "images", lambda: int(header[0].split()[0].strip())) + _safe_parse(result, "size", lambda: header[2].split()[1].strip()) + + #parse state information + states = [] + for item in data: + if not len(item): continue + + stateinfo = {} + item = item.split(",", 3) + _safe_parse(stateinfo, "name", lambda: item[0].split()[1].strip(" \"")) + _safe_parse(stateinfo, "dirs", lambda: int(item[1].split()[0].strip())) + _safe_parse(stateinfo, "frames", lambda: int(item[2].split()[0].strip())) + if len(item) > 3: + stateinfo["misc"] = item[3] + + states.append(stateinfo) + + result["states"] = states + return result + +def extract_state(input_path, output_path, icon_state, direction=None, frame=None): + """ Extracts an icon state as a png to a given path. + If provided direction should be a string, one of S, N, E, W, SE, SW, NE, NW. + If provided frame should be a frame number or a string of two frame number separated by a dash. + """ + args = ["extract", input_path, icon_state, output_path] + if direction is not None: args.extend(("direction" , str(direction))) + if frame is not None: args.extend(("frame" , str(frame))) + return _dmitool_call(*args) + +def import_state(target_path, input_path, icon_state, replace=False, delays=None, rewind=False, loop=None, ismovement=False, direction=None, frame=None): + """ Inserts an input png given by the input_path into the target_path. + """ + args = ["import", target_path, icon_state, input_path] + + if replace: args.append("nodup") + if rewind: args.append("rewind") + if ismovement: args.append("movement") + if delays: args.extend(("delays", ",".join(delays))) + if direction is not None: args.extend(("direction", direction)) + if frame is not None: args.extend(("frame", frame)) + + if loop in ("inf", "infinity"): + args.append("loop") + elif loop: + args.extend(("loopn", loop)) + + return _dmitool_call(*args) diff --git a/tools/dmitool/git_merge_installer.bat b/tools/dmitool/git_merge_installer.bat new file mode 100644 index 00000000000..18ab7c8026a --- /dev/null +++ b/tools/dmitool/git_merge_installer.bat @@ -0,0 +1,6 @@ +@echo off +set tab= +echo. >> ../../.git/config +echo [merge "merge-dmi"] >> ../../.git/config +echo %tab%name = iconfile merge driver >> ../../.git/config +echo %tab%driver = ./tools/dmitool/dmimerge.sh %%O %%A %%B >> ../../.git/config diff --git a/tools/dmitool/git_merge_installer.sh b/tools/dmitool/git_merge_installer.sh new file mode 100755 index 00000000000..e0c48823b11 --- /dev/null +++ b/tools/dmitool/git_merge_installer.sh @@ -0,0 +1,6 @@ +F="../../.git/config" + +echo '' >> $F +echo '[merge "merge-dmi"]' >> $F +echo ' name = iconfile merge driver' >> $F +echo ' driver = ./tools/dmitool/dmimerge.sh %O %A %B' >> $F diff --git a/tools/dmitool/merging.txt b/tools/dmitool/merging.txt new file mode 100644 index 00000000000..d8943efb985 --- /dev/null +++ b/tools/dmitool/merging.txt @@ -0,0 +1,14 @@ +1. Install java(http://www.java.com/en/download/index.jsp) +2. Make sure java is in your PATH. To test this, open git bash, and type "java". If it says unknown command, you need to add JAVA/bin to your PATH variable (A guide for this can be found at https://www.java.com/en/download/help/path.xml ). + +Merging +The easiest way to do merging is to install the merge driver. For this, open `-tg-station/.git/config` in a text editor, and paste the following lines to the end of it: + +[merge "merge-dmi"] + name = iconfile merge driver + driver = ./tools/dmitool/dmimerge.sh %O %A %B + +You may optionally instead run git_merge_installer.bat or git_merge_installer.sh which should automatically insert these lines for you at the appropriate location. + +After this, merging DMI files should happen automagically unless there are conflicts (an icon_state that both you and someone else changed). +If there are conflicts, you will unfortunately still be stuck with opening both versions in the editor, and manually resolving the issues with those states. diff --git a/tools/dmitool/src/main/java/dmitool/DMI.java b/tools/dmitool/src/main/java/dmitool/DMI.java new file mode 100644 index 00000000000..bb560107ae9 --- /dev/null +++ b/tools/dmitool/src/main/java/dmitool/DMI.java @@ -0,0 +1,455 @@ +package dmitool; + +import ar.com.hjg.pngj.ImageInfo; +import ar.com.hjg.pngj.ImageLineInt; +import ar.com.hjg.pngj.PngReader; +import ar.com.hjg.pngj.PngWriter; +import ar.com.hjg.pngj.PngjInputException; +import ar.com.hjg.pngj.chunks.PngChunkPLTE; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; + +public class DMI implements Comparator { + int w, h; + List images; + int totalImages = 0; + RGBA[] palette; + boolean isPaletted; + + public DMI(int w, int h) { + this.w = w; + this.h = h; + images = new ArrayList<>(); + isPaletted = false; + palette = null; + } + + public DMI(String f) throws DMIException, FileNotFoundException { + this(new File(f)); + } + + public DMI(File f) throws DMIException, FileNotFoundException { + if(f.length() == 0) { // Empty .dmi is empty file + w = 32; + h = 32; + images = new ArrayList<>(); + isPaletted = false; + palette = null; + return; + } + InputStream in = new FileInputStream(f); + PngReader pngr; + try { + pngr = new PngReader(in); + } catch(PngjInputException pie) { + throw new DMIException("Bad file format!", pie); + } + String descriptor = pngr.getMetadata().getTxtForKey("Description"); + String[] lines = descriptor.split("\n"); + + if(Main.VERBOSITY > 0) System.out.println("Descriptor has " + lines.length + " lines."); + if(Main.VERBOSITY > 3) { + System.out.println("Descriptor:"); + System.out.println(descriptor); + } + + /* length 6 is: + # BEGIN DMI + version = 4.0 + state = "state" + dirs = 1 + frames = 1 + # END DMI + */ + if(lines.length < 6) throw new DMIException(null, 0, "Descriptor too short!"); + + if(!"# BEGIN DMI".equals(lines[0])) throw new DMIException(lines, 0, "Expected '# BEGIN DMI'"); + if(!"# END DMI".equals(lines[lines.length-1])) throw new DMIException(lines, lines.length-1, "Expected '# END DMI'"); + if(!"version = 4.0".equals(lines[1])) throw new DMIException(lines, 1, "Unknown version, expected 'version = 4.0'"); + + this.w = 32; + this.h = 32; + + int i = 2; + + if(lines[i].startsWith("\twidth = ")) { + this.w = Integer.parseInt(lines[2].substring("\twidth = ".length())); + i++; + } + if(lines[i].startsWith("\theight = ")) { + this.h = Integer.parseInt(lines[3].substring("\theight = ".length())); + i++; + } + + List states = new ArrayList<>(); + + while(i < lines.length - 1) { + long imagesInState = 1; + if(!lines[i].startsWith("state = \"") || !lines[i].endsWith("\"")) throw new DMIException(lines, i, "Error reading state string"); + String stateName = lines[i].substring("state = \"".length(), lines[i].length()-1); + i++; + int dirs = 1; + int frames = 1; + float[] delays = null; + boolean rewind = false; + int loop = -1; + String hotspot = null; + boolean movement = false; + while(lines[i].startsWith("\t")) { + if(lines[i].startsWith("\tdirs = ")) { + dirs = Integer.parseInt(lines[i].substring("\tdirs = ".length())); + imagesInState *= dirs; + i++; + } else if(lines[i].startsWith("\tframes = ")) { + frames = Integer.parseInt(lines[i].substring("\tframes = ".length())); + imagesInState *= frames; + i++; + } else if(lines[i].startsWith("\tdelay = ")) { + String delayString = lines[i].substring("\tdelay = ".length()); + String[] delayVals = delayString.split(","); + delays = new float[delayVals.length]; + for(int d=0; d 0) System.out.println(pal.getNentries() + " palette entries"); + + palette = new RGBA[pal.getNentries()]; + int[] rgb = new int[3]; + for(int q=0; q 0) System.out.println("Non-paletted image"); + } + + int iw = pngr.imgInfo.cols; + int ih = pngr.imgInfo.rows; + + if(totalImages > iw * ih) + throw new DMIException(null, 0, "Impossible number of images!"); + + if(Main.VERBOSITY > 0) System.out.println("Image size " + iw+"x"+ih); + int[][] px = new int[ih][]; + + for(int y=0; y statesY) + // this should NEVER happen, we pre-check it + throw new DMIException(null, 0, "CRITICAL: End of image reached with states to go!"); + } + } + if(is.delays != null) { + if((Main.STRICT && is.delays.length*is.dirs != img.length) || is.delays.length*is.dirs < img.length) + throw new DMIException(null, 0, "Delay array size mismatch: " + is.delays.length*is.dirs + " vs " + img.length + "!"); + } + is.images = img; + } + } + + public IconState getIconState(String name) { + for(IconState is: images) { + if(is.name.equals(name)) { + return is; + } + } + return null; + } + + /** + * Makes a copy, unless name is null. + */ + public void addIconState(String name, IconState is) { + if(name == null) { + images.add(is); + totalImages += is.dirs * is.frames; + } else { + IconState newState = (IconState)is.clone(); + newState.name = name; + images.add(newState); + totalImages += is.dirs * is.frames; + } + } + + public boolean removeIconState(String name) { + for(IconState is: images) { + if(is.name.equals(name)) { + images.remove(is); + totalImages -= is.dirs * is.frames; + return true; + } + } + return false; + } + + public boolean setIconState(IconState is) { + for(int i=0; i 0) System.out.println("Fixing PNG chunks..."); + out.writeInt(in.readInt()); + out.writeInt(in.readInt()); + + Deque notZTXT = new ArrayDeque<>(); + + PNGChunk c = null; + + while(c == null || c.type != IEND) { + c = new PNGChunk(in); + if(c.type == zTXt && notZTXT != null) { + PNGChunk cc = null; + while(cc == null || cc.type != IHDR) { + cc = notZTXT.pop(); + cc.write(out); + } + c.write(out); + while(notZTXT.size() != 0) { + PNGChunk pc = notZTXT.pop(); + pc.write(out); + } + notZTXT = null; + } else if(notZTXT != null) { + notZTXT.add(c); + } else { + c.write(out); + } + } + if(Main.VERBOSITY > 0) System.out.println("Chunks fixed."); + } + + @Override public int compare(IconState arg0, IconState arg1) { + return arg0.name.compareTo(arg1.name); + } + + public void writeDMI(OutputStream os) throws IOException { + writeDMI(os, false); + } + public void writeDMI(OutputStream os, boolean sortStates) throws IOException { + if(totalImages == 0) { // Empty .dmis are empty files + os.close(); + return; + } + + // Setup chunk-fix buffer + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + if(sortStates) { + Collections.sort(images, this); + } + + // Write the dmi into the buffer + int sx = (int)Math.ceil(Math.sqrt(totalImages)); + int sy = totalImages / sx; + if(sx*sy < totalImages) { + sy++; + } + if(Main.VERBOSITY > 0) System.out.println("Image size: " + w + "x" + h + "; number of images " + sx + "x" + sy + " (" + totalImages + ")"); + int ix = sx * w; + int iy = sy * h; + ImageInfo ii = new ImageInfo(ix, iy, 8, true); + PngWriter out = new PngWriter(baos, ii); + out.setCompLevel(9); // Maximum compression + String description = getDescriptor(); + if(Main.VERBOSITY > 0) System.out.println("Descriptor has " + (description.split("\n").length) + " lines."); + out.getMetadata().setText("Description", description, true, true); + + Image[][] img = new Image[sx][sy]; + { + int k = 0; + int r = 0; + for(IconState is: images) { + for(Image i: is.images) { + img[k++][r] = i; + + if(k == sx) { + k = 0; + r++; + } + } + } + } + + for(int irow=0; irow myIS = new HashMap<>(); + HashMap dmiIS = new HashMap<>(); + + for(IconState is: images) { + myIS.put(is.name, is); + } + for(IconState is: dmi.images) { + dmiIS.put(is.name, is); + } + if(!myIS.keySet().equals(dmiIS.keySet())) return false; + for(String s: myIS.keySet()) { + if(!myIS.get(s).equals(dmiIS.get(s))) return false; + } + + return true; + } +} diff --git a/tools/dmitool/src/main/java/dmitool/DMIDiff.java b/tools/dmitool/src/main/java/dmitool/DMIDiff.java new file mode 100644 index 00000000000..431d9c1d15a --- /dev/null +++ b/tools/dmitool/src/main/java/dmitool/DMIDiff.java @@ -0,0 +1,194 @@ +package dmitool; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class DMIDiff { + Map newIconStates; + Map modifiedIconStates = new HashMap<>(); + Set removedIconStates; + + DMIDiff() { + newIconStates = new HashMap<>(); + removedIconStates = new HashSet<>(); + } + + public DMIDiff(DMI base, DMI mod) { + if(base.h != mod.h || base.w != mod.w) throw new IllegalArgumentException("Cannot compare non-identically-sized DMIs!"); + + HashMap baseIS = new HashMap<>(); + for(IconState is: base.images) { + baseIS.put(is.name, is); + } + + HashMap modIS = new HashMap<>(); + for(IconState is: mod.images) { + modIS.put(is.name, is); + } + + newIconStates = ((HashMap)modIS.clone()); + for(String s: baseIS.keySet()) { + newIconStates.remove(s); + } + + removedIconStates = new HashSet<>(); + removedIconStates.addAll(baseIS.keySet()); + removedIconStates.removeAll(modIS.keySet()); + + Set retainedStates = new HashSet<>(); + retainedStates.addAll(baseIS.keySet()); + retainedStates.retainAll(modIS.keySet()); + + for(String s: retainedStates) { + if(!baseIS.get(s).equals(modIS.get(s))) { + modifiedIconStates.put(s, new IconStateDiff(baseIS.get(s), modIS.get(s))); + } + } + } + /** + * ASSUMES NO MERGE CONFLICTS - MERGE DIFFS FIRST. + */ + public void applyToDMI(DMI dmi) { + for(String s: removedIconStates) { + dmi.removeIconState(s); + } + for(String s: modifiedIconStates.keySet()) { + dmi.setIconState(modifiedIconStates.get(s).newState); + } + for(String s: newIconStates.keySet()) { + dmi.addIconState(null, newIconStates.get(s)); + } + } + + /** + * @param other The diff to merge with + * @param conflictDMI A DMI to add conflicted icon_states to + * @param merged An empty DMIDiff to merge into + * @param aName The log name for this diff + * @param bName The log name for {@code other} + * @return A Set containing all icon_states which conflicted, along with what was done in each diff, in the format "icon_state: here|there"; here and there are one of "added", "modified", and "removed" + */ + public Set mergeDiff(DMIDiff other, DMI conflictDMI, DMIDiff merged, String aName, String bName) { + HashSet myTouched = new HashSet<>(); + myTouched.addAll(removedIconStates); + myTouched.addAll(newIconStates.keySet()); + myTouched.addAll(modifiedIconStates.keySet()); + + HashSet otherTouched = new HashSet<>(); + otherTouched.addAll(other.removedIconStates); + otherTouched.addAll(other.newIconStates.keySet()); + otherTouched.addAll(other.modifiedIconStates.keySet()); + + HashSet bothTouched = (HashSet)myTouched.clone(); + bothTouched.retainAll(otherTouched); // this set now contains the list of icon_states that *both* diffs modified, which we'll put in conflictDMI for manual merge (unless they were deletions + + if(Main.VERBOSITY > 0) { + System.out.println("a: " + Arrays.toString(myTouched.toArray())); + System.out.println("b: " + Arrays.toString(otherTouched.toArray())); + System.out.println("both: " + Arrays.toString(bothTouched.toArray())); + } + + HashSet whatHappened = new HashSet<>(); + + for(String s: bothTouched) { + String here, there; + if(removedIconStates.contains(s)) { + here = "removed"; + } else if(newIconStates.containsKey(s)) { + here = "added"; + } else if(modifiedIconStates.containsKey(s)) { + here = "modified"; + } else { + System.out.println("Unknown error; state="+s); + here = "???"; + } + + if(other.removedIconStates.contains(s)) { + there = "removed"; + } else if(other.newIconStates.containsKey(s)) { + there = "added"; + } else if(other.modifiedIconStates.containsKey(s)) { + there = "modified"; + } else { + System.out.println("Unknown error; state="+s); + there = "???"; + } + + whatHappened.add(s + ": " + here + "|" + there); + } + + // Removals + for(String s: removedIconStates) { + if(!bothTouched.contains(s)) { + merged.removedIconStates.add(s); + } + } + for(String s: other.removedIconStates) { + if(!bothTouched.contains(s)) { + merged.removedIconStates.add(s); + } + } + + // Modifications + for(String s: modifiedIconStates.keySet()) { + if(!bothTouched.contains(s)) { + merged.modifiedIconStates.put(s, modifiedIconStates.get(s)); + } else { + conflictDMI.addIconState(aName + "|" + s, modifiedIconStates.get(s).newState); + } + } + for(String s: other.modifiedIconStates.keySet()) { + if(!bothTouched.contains(s)) { + merged.modifiedIconStates.put(s, other.modifiedIconStates.get(s)); + } else { + conflictDMI.addIconState(bName + "|" + s, other.modifiedIconStates.get(s).newState); + } + } + + // Additions + for(String s: newIconStates.keySet()) { + if(!bothTouched.contains(s)) { + merged.newIconStates.put(s, newIconStates.get(s)); + } else { + conflictDMI.addIconState(aName + s, newIconStates.get(s)); + } + } + for(String s: other.newIconStates.keySet()) { + if(!bothTouched.contains(s)) { + merged.newIconStates.put(s, other.newIconStates.get(s)); + } else { + conflictDMI.addIconState(bName + s, other.newIconStates.get(s)); + } + } + + return whatHappened; + } + + @Override public String toString() { + String s = ""; + String t = "\t"; + String q = "\""; + String n = "\n"; + if(!removedIconStates.isEmpty()) { + s += "Removed:\n"; + for(String state: removedIconStates) + s += t + q + state + q + n; + } + if(!modifiedIconStates.isEmpty()) { + s += "Modified:\n"; + for(String state: modifiedIconStates.keySet()) + s += t + q + state + q + " [" + modifiedIconStates.get(state).toString() + "]\n"; + } + if(!newIconStates.isEmpty()) { + s += "Added:\n"; + for(String state: newIconStates.keySet()) + s += t + q + state + q + " " + newIconStates.get(state).infoStr() + n; + } + if("".equals(s)) + return "No changes"; + return s; + } +} \ No newline at end of file diff --git a/tools/dmitool/src/main/java/dmitool/DMIException.java b/tools/dmitool/src/main/java/dmitool/DMIException.java new file mode 100644 index 00000000000..e9293162894 --- /dev/null +++ b/tools/dmitool/src/main/java/dmitool/DMIException.java @@ -0,0 +1,24 @@ +package dmitool; + +public class DMIException extends Exception { + String[] desc = null; + int line = 0; + public DMIException(String[] descriptor, int line, String what) { + super(what); + desc = descriptor; + this.line = line; + } + public DMIException(String what) { + super(what); + } + public DMIException(String what, Exception cause) { + super(what, cause); + } + + @Override public String getMessage() { + if(desc != null) + return "\"" + desc[line] + "\" - " + super.getMessage(); + + return super.getMessage(); + } +} \ No newline at end of file diff --git a/tools/dmitool/src/main/java/dmitool/IconState.java b/tools/dmitool/src/main/java/dmitool/IconState.java new file mode 100644 index 00000000000..2a2202c71cd --- /dev/null +++ b/tools/dmitool/src/main/java/dmitool/IconState.java @@ -0,0 +1,280 @@ +package dmitool; + +import java.util.Arrays; +import ar.com.hjg.pngj.ImageInfo; +import ar.com.hjg.pngj.ImageLineInt; +import ar.com.hjg.pngj.PngWriter; +import ar.com.hjg.pngj.PngReader; +import ar.com.hjg.pngj.PngjInputException; +import java.io.InputStream; +import java.io.OutputStream; + +public class IconState { + String name; + int dirs; + int frames; + float[] delays; + Image[] images; // dirs come first + boolean rewind; + int loop; + String hotspot; + boolean movement; + + public String getInfoLine() { + String extraInfo = ""; + if(rewind) extraInfo += " rewind"; + if(frames != 1) { + extraInfo += " loop(" + (loop==-1 ? "infinite" : loop) + ")"; + } + if(hotspot != null) extraInfo += " hotspot('" + hotspot + "')"; + if(movement) extraInfo += " movement"; + if(extraInfo.equals("")) { + return String.format("state \"%s\", %d dir(s), %d frame(s)", name, dirs, frames); + } else { + return String.format("state \"%s\", %d dir(s), %d frame(s),%s", name, dirs, frames, extraInfo); + } + } + + @Override public IconState clone() { + IconState is = new IconState(name, dirs, frames, images.clone(), delays==null ? null : delays.clone(), rewind, loop, hotspot, movement); + is.delays = delays != null ? delays.clone() : null; + is.rewind = rewind; + + return is; + } + + public IconState(String name, int dirs, int frames, Image[] images, float[] delays, boolean rewind, int loop, String hotspot, boolean movement) { + if(delays != null) { + if(Main.STRICT && delays.length != frames) { + throw new IllegalArgumentException("Delays and frames must be the same length!"); + } + } + this.name = name; + this.dirs = dirs; + this.frames = frames; + this.images = images; + this.rewind = rewind; + this.loop = loop; + this.hotspot = hotspot; + this.delays = delays; + this.movement = movement; + } + void setDelays(float[] delays) { + this.delays = delays; + } + void setRewind(boolean b) { + rewind = b; + } + @Override public boolean equals(Object obj) { + if(obj == this) return true; + if(!(obj instanceof IconState)) return false; + + IconState is = (IconState)obj; + + if(!is.name.equals(name)) return false; + if(is.dirs != dirs) return false; + if(is.frames != frames) return false; + if(!Arrays.equals(images, is.images)) return false; + if(is.rewind != rewind) return false; + if(is.loop != loop) return false; + if(!Arrays.equals(delays, is.delays)) return false; + if(!(is.hotspot == null ? hotspot == null : is.hotspot.equals(hotspot))) return false; + if(is.movement != movement) return false; + + return true; + } + public String infoStr() { + return "[" + frames + " frame(s), " + dirs + " dir(s)]"; + } + public String getDescriptorFragment() { + String s = ""; + String q = "\""; + String n = "\n"; + s += "state = " + q + name + q + n; + s += "\tdirs = " + dirs + n; + s += "\tframes = " + frames + n; + if(delays != null) { + s += "\tdelay = " + delayArrayToString(delays) + n; + } + if(rewind) { + s += "\trewind = 1\n"; + } + if(loop != -1) { + s += "\tloop = " + loop + n; + } + if(hotspot != null) { + s += "\thotspot = " + hotspot + n; + } + if(movement) { + s += "\tmovement = 1\n"; + } + return s; + } + + private static String delayArrayToString(float[] d) { + String s = ""; + for(float f: d) { + s += ","+f; + } + return s.substring(1); + } + + /** + * Dump the state to the given OutputStream in PNG format. Frames will be dumped along the X axis of the image, and directions will be dumped along the Y. + */ + public void dumpToPNG(OutputStream outS, int minDir, int maxDir, int minFrame, int maxFrame) { + int totalDirs = maxDir - minDir + 1; + int totalFrames = maxFrame - minFrame + 1; + + int w = images[minDir + minFrame * this.dirs].w; + int h = images[minDir + minFrame * this.dirs].h; + + if(Main.VERBOSITY > 0) System.out.println("Writing " + totalDirs + " dir(s), " + totalFrames + " frame(s), " + totalDirs*totalFrames + " image(s) total."); + ImageInfo ii = new ImageInfo(totalFrames * w, totalDirs * h, 8, true); + PngWriter out = new PngWriter(outS, ii); + out.setCompLevel(9); + + Image[][] img = new Image[totalFrames][totalDirs]; + { + for(int i=0; i= frames) + throw new IllegalArgumentException("Provided frame is out of range: " + frame); + if(dir < 0 || dir >= dirs) + throw new IllegalArgumentException("Provided dir is out of range: " + dir); + + images[getIndex(dir, frame)] = splice; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/dmitool/src/main/java/dmitool/IconStateDiff.java b/tools/dmitool/src/main/java/dmitool/IconStateDiff.java new file mode 100644 index 00000000000..a5d30db7fe0 --- /dev/null +++ b/tools/dmitool/src/main/java/dmitool/IconStateDiff.java @@ -0,0 +1,126 @@ +package dmitool; + +import java.util.HashMap; +import java.util.HashSet; + +public class IconStateDiff { + static class ISAddress { + int dir; + int frame; + + public ISAddress(int dir, int frame) { + this.dir = dir; + this.frame = frame; + } + + public String infoStr(int maxDir, int maxFrame) { + if(maxDir == 1 && maxFrame == 1) { + return ""; + } else if(maxDir == 1) { + return "{" + frame + "}"; + } else if(maxFrame == 1) { + return "{" + Main.dirs[dir] + "}"; + } else { + return "{" + Main.dirs[dir] + " " + frame + "}"; + } + } + } + int oldFrameCount = 0; + int oldDirectionCount = 0; + boolean oldRewind = false; + int oldLoop = -1; + String oldHotspot = null; + + int newFrameCount = 0; + int newDirectionCount = 0; + boolean newRewind = false; + int newLoop = -1; + String newHotspot = null; + + IconState newState; + HashMap modifiedFrames = new HashMap<>(); + HashMap newFrames = new HashMap<>(); + HashSet removedFrames = new HashSet<>(); + + public IconStateDiff(IconState base, IconState mod) { + int maxDir = Math.max(base.dirs, mod.dirs); + int maxFrame = Math.max(base.frames, mod.frames); + + oldFrameCount = base.frames; + oldDirectionCount = base.dirs; + oldRewind = base.rewind; + oldLoop = base.loop; + oldHotspot = base.hotspot; + + newFrameCount = mod.frames; + newDirectionCount = mod.dirs; + newRewind = mod.rewind; + newLoop = mod.loop; + newHotspot = mod.hotspot; + + newState = mod; + + Image baseI, modI; + for(int d=0; d d && base.frames > f) { + baseI = base.images[f * base.dirs + d]; + } else baseI = null; + if(mod.dirs > d && mod.frames > f) { + modI = mod.images[f * mod.dirs + d]; + } else modI = null; + + if(baseI == null && modI == null) continue; + + if(baseI == null) newFrames.put(new ISAddress(d, f), modI); + else if(modI == null) removedFrames.add(new ISAddress(d, f)); + else if(!baseI.equals(modI)) { + modifiedFrames.put(new ISAddress(d, f), modI); + } + } + } + } + + @Override public String toString() { + String s = ""; + String tmp; + + if(newDirectionCount != oldDirectionCount) + s += " | dirs " + oldDirectionCount + "->" + newDirectionCount; + + if(newFrameCount != oldFrameCount) + s += " | frames " + oldFrameCount + "->" + newFrameCount; + + if(newRewind != oldRewind) { + s += " | rewind " + oldRewind + "->" + newRewind; + } + + if(newLoop != oldLoop) { + s += " | loop " + oldLoop + "->" + newLoop; + } + + if(newHotspot == null ? oldHotspot != null : !newHotspot.equals(oldHotspot)) { + s += " | hotspot " + oldHotspot + "->" + newHotspot; + } + + if(!modifiedFrames.isEmpty()) { + int total_frames = Math.min(oldFrameCount, newFrameCount) * Math.min(oldDirectionCount, newDirectionCount); + tmp = ""; + for(ISAddress isa: modifiedFrames.keySet()) { + String str = isa.infoStr(oldDirectionCount, oldFrameCount); + if(!"".equals(str)) { + tmp += ", " + str; + } + } + if(!"".equals(tmp)) { + s += " | modified " + modifiedFrames.size() + " of " + total_frames + ": " + tmp.substring(1); + } else { + s += " | modified " + modifiedFrames.size() + " of " + total_frames; + } + } + + if("".equals(s)) + return "No change"; + return s.substring(3); + } +} \ No newline at end of file diff --git a/tools/dmitool/src/main/java/dmitool/Image.java b/tools/dmitool/src/main/java/dmitool/Image.java new file mode 100644 index 00000000000..2ea7ff41920 --- /dev/null +++ b/tools/dmitool/src/main/java/dmitool/Image.java @@ -0,0 +1,32 @@ +package dmitool; + +import java.io.IOException; +import java.io.OutputStream; + +public abstract class Image { + int w, h; + + abstract RGBA getPixel(int x, int y); + + public Image(int w, int h) { + this.w = w; + this.h = h; + } + + @Override public boolean equals(Object obj) { + if(obj == this) return true; + if(!(obj instanceof Image)) return false; + + Image im = (Image) obj; + + if(w != im.w || h != im.h) return false; + + for(int i=0; i argq = new ArrayDeque<>(); + for(String s: args) { + argq.addLast(s); + } + if(argq.size() == 0) { + System.out.println("No command found; use 'help' for help"); + return; + } + String switches = argq.peekFirst(); + if(switches.startsWith("-")) { + for(char c: switches.substring(1).toCharArray()) { + switch(c) { + case 'v': VERBOSITY++; break; + case 'q': VERBOSITY--; break; + case 'S': STRICT = true; break; + } + } + argq.pollFirst(); + } + String op = argq.pollFirst(); + + switch(op) { + case "diff": { + if(argq.size() < 2) { + System.out.println("Insufficient arguments for command!"); + System.out.println(helpStr); + return; + } + String a = argq.pollFirst(); + String b = argq.pollFirst(); + + if(VERBOSITY >= 0) System.out.println("Loading " + a); + DMI dmi = doDMILoad(a); + if(VERBOSITY >= 0) dmi.printInfo(); + + if(VERBOSITY >= 0) System.out.println("Loading " + b); + DMI dmi2 = doDMILoad(b); + if(VERBOSITY >= 0) dmi2.printInfo(); + + DMIDiff dmid = new DMIDiff(dmi, dmi2); + System.out.println(dmid); + break; + } + case "sort": { + if(argq.size() < 1) { + System.out.println("Insufficient arguments for command!"); + System.out.println(helpStr); + return; + } + String f = argq.pollFirst(); + + if(VERBOSITY >= 0) System.out.println("Loading " + f); + DMI dmi = doDMILoad(f); + if(VERBOSITY >= 0) dmi.printInfo(); + + if(VERBOSITY >= 0) System.out.println("Saving " + f); + dmi.writeDMI(new FileOutputStream(f), true); + break; + } + case "merge": { + if(argq.size() < 4) { + System.out.println("Insufficient arguments for command!"); + System.out.println(helpStr); + return; + } + String baseF = argq.pollFirst(), + aF = argq.pollFirst(), + bF = argq.pollFirst(), + mergedF = argq.pollFirst(); + if(VERBOSITY >= 0) System.out.println("Loading " + baseF); + DMI base = doDMILoad(baseF); + if(VERBOSITY >= 0) base.printInfo(); + + if(VERBOSITY >= 0) System.out.println("Loading " + aF); + DMI aDMI = doDMILoad(aF); + if(VERBOSITY >= 0) aDMI.printInfo(); + + if(VERBOSITY >= 0) System.out.println("Loading " + bF); + DMI bDMI = doDMILoad(bF); + if(VERBOSITY >= 0) bDMI.printInfo(); + + DMIDiff aDiff = new DMIDiff(base, aDMI); + DMIDiff bDiff = new DMIDiff(base, bDMI); + DMIDiff mergedDiff = new DMIDiff(); + DMI conflictDMI = new DMI(32, 32); + + Set cf = aDiff.mergeDiff(bDiff, conflictDMI, mergedDiff, aF, bF); + + mergedDiff.applyToDMI(base); + + base.writeDMI(new FileOutputStream(mergedF)); + + if(!cf.isEmpty()) { + if(VERBOSITY >= 0) for(String s: cf) { + System.out.println(s); + } + conflictDMI.writeDMI(new FileOutputStream(mergedF + ".conflict.dmi"), true); + System.out.println("Add/modify conflicts placed in '" + mergedF + ".conflict.dmi'"); + System.exit(1); // Git expects non-zero on merge conflict + } else { + System.out.println("No conflicts"); + System.exit(0); + } + break; + } + case "extract": { + if(argq.size() < 3) { + System.out.println("Insufficient arguments for command!"); + System.out.println(helpStr); + return; + } + String file = argq.pollFirst(), + state = argq.pollFirst(), + outFile = argq.pollFirst(); + + DMI dmi = doDMILoad(file); + if(VERBOSITY >= 0) dmi.printInfo(); + + IconState is = dmi.getIconState(state); + if(is == null) { + System.out.println("icon_state '"+state+"' does not exist!"); + return; + } + // minDir, Maxdir, minFrame, Maxframe + int mDir=0, Mdir=is.dirs-1; + int mFrame=0, Mframe=is.frames-1; + + while(argq.size() > 1) { + String arg = argq.pollFirst(); + + switch(arg) { + case "d": + case "dir": + case "dirs": + case "direction": + case "directions": + String dString = argq.pollFirst(); + if(dString.contains("-")) { + String[] splitD = dString.split("-"); + if(splitD.length == 2) { + mDir = parseDir(splitD[0], is); + Mdir = parseDir(splitD[1], is); + } else { + System.out.println("Illegal dir string: '" + dString + "'!"); + return; + } + } else { + mDir = parseDir(dString, is); + Mdir = mDir; + } + // Invalid value check, warnings are printed in parseDir() + if(mDir == -1 || Mdir == -1) return; + if(Mdir < mDir) { + System.out.println("Maximum dir greater than minimum dir!"); + System.out.println("Textual direction order is S, N, E, W, SE, SW, NE, NW increasing 0 (S) to 7 (NW)"); + return; + } + break; + case "f": + case "frame": + case "frames": + String fString = argq.pollFirst(); + if(fString.contains("-")) { + String[] splitF = fString.split("-"); + if(splitF.length == 2) { + mFrame = parseFrame(splitF[0], is); + Mframe = parseFrame(splitF[1], is); + } else { + System.out.println("Illegal frame string: '" + fString + "'!"); + return; + } + } else { + mFrame = parseFrame(fString, is); + Mframe = mFrame; + } + // Invalid value check, warnings are printed in parseFrame() + if(mFrame == -1 || Mframe == -1) return; + if(Mframe < mFrame) { + System.out.println("Maximum frame greater than minimum frame!"); + return; + } + break; + default: + System.out.println("Unknown argument '" + arg + "' detected, ignoring."); + } + } + if(!argq.isEmpty()) { + System.out.println("Extra argument '" + argq.pollFirst() + "' detected, ignoring."); + } + is.dumpToPNG(new FileOutputStream(outFile), mDir, Mdir, mFrame, Mframe); + break; + } + case "import": { + if(argq.size() < 3) { + System.out.println("Insufficient arguments for command!"); + System.out.println(helpStr); + return; + } + String dmiFile = argq.pollFirst(), + stateName = argq.pollFirst(), + pngFile = argq.pollFirst(); + + boolean noDup = false; + boolean rewind = false; + int loop = 0; + boolean movement = false; + String hotspot = null; + float[] delays = null; + String replaceDir = null; + String replaceFrame = null; + while(!argq.isEmpty()) { + String s = argq.pollFirst(); + switch(s.toLowerCase()) { + case "nodup": + case "nd": + case "n": + noDup = true; + break; + case "rewind": + case "rw": + case "r": + rewind = true; + break; + case "loop": + case "lp": + case "l": + loop = -1; + break; + case "loopn": + case "lpn": + case "ln": + if(!argq.isEmpty()) { + String loopTimes = argq.pollFirst(); + try { + loop = Integer.parseInt(loopTimes); + } catch(NumberFormatException nfe) { + System.out.println("Illegal number '" + loopTimes + "' as argument to '" + s + "'!"); + return; + } + } else { + System.out.println("Argument '" + s + "' requires a numeric argument following it!"); + return; + } + break; + case "movement": + case "move": + case "mov": + case "m": + movement = true; + break; + case "delays": + case "delay": + case "del": + case "d": + if(!argq.isEmpty()) { + String delaysString = argq.pollFirst(); + String[] delaysSplit = delaysString.split(","); + delays = new float[delaysSplit.length]; + for(int i=0; i= 0) System.out.println("Loading " + dmiFile); + DMI toImportTo = doDMILoad(dmiFile); + if(VERBOSITY >= 0) toImportTo.printInfo(); + IconState is = IconState.importFromPNG(toImportTo, new FileInputStream(pngFile), stateName, delays, rewind, loop, hotspot, movement); + + //image insertion + if(replaceDir != null || replaceFrame != null) { + + IconState targetIs = toImportTo.getIconState(stateName); + if(targetIs == null) { + System.out.println("'direction' or 'frame' specified and no icon state '" + stateName + "' found, aborting!"); + return; + } + if(is.images.length == 0) { + System.out.println("'direction' or 'frame' specified and imported is empty, aborting!"); + return; + } + + if(!noDup) targetIs = targetIs.clone(); + + int dirToReplace, frameToReplace; + if(replaceDir != null && replaceFrame != null) { + frameToReplace = parseFrame(replaceFrame, targetIs); + dirToReplace = parseDir(replaceDir, targetIs); + targetIs.insertImage(dirToReplace, frameToReplace, is.images[0]); + } + else if(replaceDir != null) { + dirToReplace = parseDir(replaceDir, targetIs); + targetIs.insertDir(dirToReplace, is.images); + } + else if(replaceFrame != null) { + frameToReplace = parseFrame(replaceFrame, targetIs); + targetIs.insertFrame(frameToReplace, is.images); + } + + if(!noDup) toImportTo.addIconState(null, targetIs); + } + else { + if(noDup) { + if(!toImportTo.setIconState(is)) { + toImportTo.addIconState(null, is); + } + } else { + toImportTo.addIconState(null, is); + } + } + + if(VERBOSITY >= 0) toImportTo.printInfo(); + + if(VERBOSITY >= 0) System.out.println("Saving " + dmiFile); + toImportTo.writeDMI(new FileOutputStream(dmiFile)); + break; + } + case "verify": { + if(argq.size() < 1) { + System.out.println("Insufficient arguments for command!"); + System.out.println(helpStr); + return; + } + String vF = argq.pollFirst(); + if(VERBOSITY >= 0) System.out.println("Loading " + vF); + DMI v = doDMILoad(vF); + if(VERBOSITY >= 0) v.printInfo(); + break; + } + case "info": { + if(argq.size() < 1) { + System.out.println("Insufficient arguments for command!"); + System.out.println(helpStr); + return; + } + String infoFile = argq.pollFirst(); + if(VERBOSITY >= 0) System.out.println("Loading " + infoFile); + DMI info = doDMILoad(infoFile); + info.printInfo(); + info.printStateList(); + break; + } + case "version": + System.out.println(VERSION); + return; + default: + System.out.println("Command '" + op + "' not found!"); + case "help": + System.out.println(helpStr); + break; + } + } + + static int parseDir(String s, IconState is) { + try { + int i = Integer.parseInt(s); + if(0 <= i && i < is.dirs) { + return i; + } else { + System.out.println("Direction not in valid range [0, "+(is.dirs-1)+"]!"); + return -1; + } + } catch(NumberFormatException nfe) { + for(int q=0; q
Blob
Progress: [blobs.len]/[mode.blobwincount]
Progress: [blobs_legit.len]/[mode.blobwincount]