diff --git a/_maps/deathmatch/finaldestination.dmm b/_maps/deathmatch/finaldestination.dmm new file mode 100644 index 0000000000000..7fef94cd57a16 --- /dev/null +++ b/_maps/deathmatch/finaldestination.dmm @@ -0,0 +1,1254 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"ae" = ( +/obj/effect/turf_decal/siding/yellow/end, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"bF" = ( +/obj/effect/turf_decal/siding/blue/corner{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"bV" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 1 + }, +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 8 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"cu" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 6 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"cW" = ( +/obj/effect/turf_decal/siding/dark_green/corner{ + dir = 1 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"eb" = ( +/obj/effect/turf_decal/siding/dark_green/corner{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"eN" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 8 + }, +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"fp" = ( +/obj/effect/turf_decal/siding/dark_green/end{ + dir = 8 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"gs" = ( +/obj/effect/turf_decal/siding/blue/end{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"hk" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing{ + dir = 1 + }, +/turf/open/chasm, +/area/deathmatch) +"jb" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"jl" = ( +/obj/effect/turf_decal/siding/blue, +/obj/effect/turf_decal/siding/blue{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"kR" = ( +/turf/open/chasm, +/area/deathmatch) +"kS" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 4 + }, +/obj/effect/turf_decal/siding/dark_green{ + dir = 8 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"lq" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"lD" = ( +/obj/effect/turf_decal/siding/dark_red/corner, +/turf/open/indestructible/large, +/area/deathmatch) +"lJ" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 8 + }, +/obj/effect/turf_decal/siding/dark_red{ + dir = 4 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"ok" = ( +/obj/effect/turf_decal/siding/dark_red/end{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"os" = ( +/obj/effect/turf_decal/siding/dark_green, +/obj/effect/turf_decal/siding/dark_green{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"ou" = ( +/obj/effect/turf_decal/siding/dark_green/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/dark_green/corner{ + dir = 1 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"oE" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 1 + }, +/obj/effect/turf_decal/siding/dark_red, +/turf/open/indestructible/large, +/area/deathmatch) +"oX" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"pg" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 1 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"pE" = ( +/obj/effect/turf_decal/siding/dark_green, +/obj/effect/turf_decal/siding/dark_green{ + dir = 1 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"qg" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 8 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"qm" = ( +/obj/effect/turf_decal/siding/yellow, +/turf/open/indestructible/large, +/area/deathmatch) +"ry" = ( +/obj/effect/turf_decal/siding/blue/corner{ + dir = 4 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"rS" = ( +/obj/effect/turf_decal/siding/dark_red/end, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"sb" = ( +/obj/effect/turf_decal/siding/yellow/end{ + dir = 8 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"sL" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 8 + }, +/obj/effect/turf_decal/siding/yellow{ + dir = 5 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"tB" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing{ + dir = 8 + }, +/turf/open/chasm, +/area/deathmatch) +"tF" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 4 + }, +/obj/effect/turf_decal/siding/dark_green{ + dir = 8 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"vn" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 8 + }, +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"vz" = ( +/obj/effect/turf_decal/siding/blue/corner, +/obj/effect/turf_decal/siding/blue/corner{ + dir = 4 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"vF" = ( +/obj/effect/turf_decal/siding/dark_red, +/turf/open/indestructible/large, +/area/deathmatch) +"vV" = ( +/obj/effect/turf_decal/siding/blue/corner{ + dir = 1 + }, +/obj/effect/turf_decal/siding/blue/corner{ + dir = 4 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"wl" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 1 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"xc" = ( +/obj/structure/lattice/catwalk/mining, +/turf/open/chasm, +/area/deathmatch) +"xq" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 8 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"xv" = ( +/obj/effect/turf_decal/siding/dark_red/corner{ + dir = 8 + }, +/obj/effect/turf_decal/siding/dark_red/corner, +/turf/open/indestructible/large, +/area/deathmatch) +"xQ" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 10 + }, +/obj/effect/turf_decal/siding/blue/corner{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"yJ" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 4 + }, +/obj/effect/turf_decal/siding/dark_red{ + dir = 8 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"zB" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/yellow{ + dir = 10 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"AS" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 8 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Bz" = ( +/obj/effect/turf_decal/siding/blue/end{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"BM" = ( +/obj/effect/turf_decal/siding/dark_red, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Cj" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Cw" = ( +/obj/effect/turf_decal/siding/dark_red/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/dark_red/corner, +/turf/open/indestructible/large, +/area/deathmatch) +"CE" = ( +/obj/effect/turf_decal/siding/dark_red, +/obj/effect/turf_decal/siding/dark_red{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"CS" = ( +/obj/effect/turf_decal/siding/yellow, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Dc" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/obj/effect/turf_decal/siding/yellow, +/turf/open/indestructible/large, +/area/deathmatch) +"Df" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 10 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Ei" = ( +/obj/effect/turf_decal/siding/dark_red/corner, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Es" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 1 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Ev" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 8 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Fd" = ( +/turf/open/indestructible/light, +/area/deathmatch) +"FB" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing{ + dir = 4 + }, +/turf/open/chasm, +/area/deathmatch) +"Ge" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 4 + }, +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Hg" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Hm" = ( +/obj/effect/turf_decal/siding/dark_green, +/obj/effect/turf_decal/siding/dark_green{ + dir = 1 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"HM" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 4 + }, +/obj/effect/turf_decal/siding/dark_red{ + dir = 8 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Ij" = ( +/obj/effect/turf_decal/siding/dark_green/end{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"IP" = ( +/obj/effect/turf_decal/siding/dark_green/corner, +/obj/effect/turf_decal/siding/dark_green{ + dir = 9 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Jq" = ( +/obj/effect/turf_decal/siding/blue, +/obj/effect/turf_decal/siding/blue{ + dir = 1 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Jw" = ( +/obj/effect/turf_decal/siding/yellow, +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"JS" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 5 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Kn" = ( +/obj/effect/turf_decal/siding/blue, +/obj/effect/turf_decal/siding/blue{ + dir = 1 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Ks" = ( +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 8 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"KH" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 4 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"KT" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"LD" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing, +/turf/open/chasm, +/area/deathmatch) +"LY" = ( +/obj/effect/turf_decal/siding/blue/corner{ + dir = 8 + }, +/obj/effect/turf_decal/siding/blue{ + dir = 5 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"NE" = ( +/obj/effect/turf_decal/siding/dark_red/corner{ + dir = 1 + }, +/obj/effect/turf_decal/siding/dark_red{ + dir = 6 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"NN" = ( +/obj/effect/turf_decal/siding/dark_red{ + dir = 4 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Pg" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Qn" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 4 + }, +/obj/effect/turf_decal/siding/dark_green{ + dir = 8 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Qy" = ( +/obj/effect/turf_decal/siding/yellow/corner, +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 8 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"QA" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"QR" = ( +/obj/effect/turf_decal/siding/yellow, +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"QS" = ( +/obj/effect/turf_decal/siding/dark_red, +/obj/effect/turf_decal/siding/dark_red{ + dir = 1 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"TA" = ( +/obj/effect/turf_decal/siding/yellow, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"TB" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 1 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Uc" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/obj/effect/turf_decal/siding/yellow{ + dir = 4 + }, +/obj/effect/landmark/deathmatch_player_spawn, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Vh" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Vn" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 6 + }, +/obj/effect/turf_decal/siding/dark_green/corner{ + dir = 1 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"VH" = ( +/obj/effect/turf_decal/siding/dark_green/corner{ + dir = 8 + }, +/obj/effect/turf_decal/siding/dark_green/corner{ + dir = 1 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"VK" = ( +/obj/effect/turf_decal/siding/dark_red, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Wr" = ( +/obj/effect/turf_decal/siding/dark_green{ + dir = 9 + }, +/obj/effect/light_emitter/thunderdome, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"Wz" = ( +/obj/effect/turf_decal/siding/dark_red/corner, +/obj/effect/turf_decal/siding/dark_red{ + dir = 9 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"XR" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 8 + }, +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/turf/open/indestructible/large, +/area/deathmatch) +"Yd" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/obj/effect/turf_decal/siding/yellow{ + dir = 4 + }, +/turf/open/indestructible/dark/smooth_large, +/area/deathmatch) +"YI" = ( +/obj/structure/lattice, +/turf/open/chasm, +/area/deathmatch) + +(1,1,1) = {" +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +"} +(2,1,1) = {" +kR +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +kR +kR +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +kR +"} +(3,1,1) = {" +kR +Fd +Wz +HM +HM +HM +HM +yJ +HM +HM +rS +Fd +Fd +gs +vn +vn +eN +vn +vn +vn +vn +xQ +Fd +kR +"} +(4,1,1) = {" +kR +Fd +CE +lD +NN +NN +NN +NN +NN +NN +Cw +NN +QA +vz +QA +QA +QA +QA +QA +QA +ry +jl +Fd +kR +"} +(5,1,1) = {" +kR +Fd +CE +vF +Fd +FB +FB +xc +FB +Fd +oE +Ei +bF +Jq +Fd +FB +xc +FB +FB +Fd +wl +jl +Fd +kR +"} +(6,1,1) = {" +kR +Fd +CE +vF +LD +kR +kR +YI +kR +hk +oE +VK +oX +Jq +LD +kR +YI +kR +kR +hk +wl +jl +Fd +kR +"} +(7,1,1) = {" +kR +Fd +CE +vF +LD +kR +kR +YI +kR +hk +oE +BM +TB +Jq +LD +kR +YI +kR +kR +hk +wl +jl +Fd +kR +"} +(8,1,1) = {" +kR +Fd +QS +vF +xc +YI +YI +YI +YI +xc +oE +BM +TB +Jq +xc +YI +YI +YI +YI +xc +wl +Kn +Fd +kR +"} +(9,1,1) = {" +kR +Fd +CE +vF +LD +kR +kR +YI +kR +hk +oE +VK +oX +Jq +LD +kR +YI +kR +kR +hk +wl +jl +Fd +kR +"} +(10,1,1) = {" +kR +Fd +CE +vF +Fd +tB +tB +xc +tB +Fd +oE +VK +oX +Jq +Fd +tB +xc +tB +tB +Fd +wl +jl +Fd +kR +"} +(11,1,1) = {" +kR +Fd +ok +xv +lJ +lJ +lJ +lJ +lJ +lJ +NE +VK +oX +LY +XR +XR +XR +XR +XR +XR +vV +Bz +Fd +kR +"} +(12,1,1) = {" +kR +kR +Fd +vF +Ei +Cj +KH +KH +Cj +Cj +Cj +cu +JS +lq +lq +lq +jb +jb +lq +bF +wl +Fd +kR +kR +"} +(13,1,1) = {" +kR +kR +Fd +qm +Ks +Vh +KT +KT +Vh +Vh +Vh +Df +Wr +qg +qg +qg +xq +xq +qg +eb +pg +Fd +kR +kR +"} +(14,1,1) = {" +kR +Fd +sb +Qy +Ge +Ge +Ge +Ge +Ge +Ge +zB +CS +Pg +IP +Qn +Qn +Qn +Qn +Qn +Qn +ou +fp +Fd +kR +"} +(15,1,1) = {" +kR +Fd +QR +qm +Fd +FB +FB +xc +FB +Fd +Dc +CS +Pg +pE +Fd +FB +xc +FB +FB +Fd +pg +os +Fd +kR +"} +(16,1,1) = {" +kR +Fd +QR +qm +LD +kR +kR +YI +kR +hk +Dc +CS +Pg +pE +LD +kR +YI +kR +kR +hk +pg +os +Fd +kR +"} +(17,1,1) = {" +kR +Fd +Jw +qm +xc +YI +YI +YI +YI +xc +Dc +TA +Es +pE +xc +YI +YI +YI +YI +xc +pg +Hm +Fd +kR +"} +(18,1,1) = {" +kR +Fd +QR +qm +LD +kR +kR +YI +kR +hk +Dc +TA +Es +pE +LD +kR +YI +kR +kR +hk +pg +os +Fd +kR +"} +(19,1,1) = {" +kR +Fd +QR +qm +LD +kR +kR +YI +kR +hk +Dc +CS +Pg +pE +LD +kR +YI +kR +kR +hk +pg +os +Fd +kR +"} +(20,1,1) = {" +kR +Fd +QR +qm +Fd +tB +tB +xc +tB +Fd +Dc +Ks +eb +pE +Fd +tB +xc +tB +tB +Fd +pg +os +Fd +kR +"} +(21,1,1) = {" +kR +Fd +QR +Ev +Hg +Hg +Hg +Hg +Hg +Hg +bV +Hg +AS +VH +AS +AS +AS +AS +AS +AS +cW +os +Fd +kR +"} +(22,1,1) = {" +kR +Fd +sL +Yd +Yd +Yd +Yd +Uc +Yd +Yd +ae +Fd +Fd +Ij +kS +kS +tF +kS +kS +kS +kS +Vn +Fd +kR +"} +(23,1,1) = {" +kR +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +kR +kR +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +Fd +kR +"} +(24,1,1) = {" +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +kR +"} diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index dc6252b564ed4..3b567283936bd 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -38619,11 +38619,11 @@ dir = 1 }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/sorting/mail{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/mapping_helpers/mail_sorting/service/janitor_closet, +/obj/structure/disposalpipe/sorting/mail/flip{ + dir = 8 + }, /turf/open/floor/iron/white/small, /area/station/service/janitor) "nVe" = ( diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 580b6ef226456..457f7dfe45183 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -231,6 +231,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// These mobs have particularly hygienic tongues #define TRAIT_WOUND_LICKER "wound_licker" +/// This trait designate that the mob was originally a monkey +#define TRAIT_BORN_MONKEY "born_as_a_monkey" + /// Added to a mob, allows that mob to experience flavour-based moodlets when examining food #define TRAIT_REMOTE_TASTING "remote_tasting" diff --git a/code/__DEFINES/transport.dm b/code/__DEFINES/transport.dm index 452ba28535492..09d4f39280a8d 100644 --- a/code/__DEFINES/transport.dm +++ b/code/__DEFINES/transport.dm @@ -99,12 +99,11 @@ DEFINE_BITFIELD(request_flags, list( #define XING_STATE_RED 2 #define XING_STATE_MALF 3 -#define AMBER_THRESHOLD_NORMAL 45 -#define RED_THRESHOLD_NORMAL 20 -#define AMBER_THRESHOLD_DEGRADED 30 -#define RED_THRESHOLD_DEGRADED 15 +#define XING_THRESHOLD_AMBER 45 +#define XING_THRESHOLD_RED 27 #define DEFAULT_TRAM_LENGTH 10 +#define DEFAULT_TRAM_MIDPOINT 5 // Tram machinery subtype #define TRANSPORT_SYSTEM_NORMAL 0 diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index eb7fa2c3aa36b..5237132e4b10e 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -474,13 +474,18 @@ GLOBAL_LIST_INIT(skin_tone_names, list( . += borg //Returns a list of AI's -/proc/active_ais(check_mind=FALSE, z = null) +/proc/active_ais(check_mind=FALSE, z = null, skip_syndicate, only_syndicate) . = list() for(var/mob/living/silicon/ai/ai as anything in GLOB.ai_list) if(ai.stat == DEAD) continue if(ai.control_disabled) continue + var/syndie_ai = istype(ai, /mob/living/silicon/ai/weak_syndie) + if(skip_syndicate && syndie_ai) + continue + if(only_syndicate && !syndie_ai) + continue if(check_mind) if(!ai.mind) continue @@ -507,8 +512,8 @@ GLOBAL_LIST_INIT(skin_tone_names, list( . = pick(borgs) return . -/proc/select_active_ai(mob/user, z = null) - var/list/ais = active_ais(FALSE, z) +/proc/select_active_ai(mob/user, z = null, skip_syndicate, only_syndicate) + var/list/ais = active_ais(FALSE, z, skip_syndicate, only_syndicate) if(ais.len) if(user) . = input(user,"AI signals detected:", "AI Selection", ais[1]) in sort_list(ais) diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 10b50f03e9ed6..94349d689de66 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -147,6 +147,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_BOMBIMMUNE" = TRAIT_BOMBIMMUNE, "TRAIT_BONSAI" = TRAIT_BONSAI, "TRAIT_BOOZE_SLIDER" = TRAIT_BOOZE_SLIDER, + "TRAIT_BORN_MONKEY" = TRAIT_BORN_MONKEY, "TRAIT_BRAINWASHING" = TRAIT_BRAINWASHING, "TRAIT_BRAWLING_KNOCKDOWN_BLOCKED" = TRAIT_BRAWLING_KNOCKDOWN_BLOCKED, "TRAIT_BYPASS_EARLY_IRRADIATED_CHECK" = TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 3c99364542c5f..ff0da9caa5ed0 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -94,7 +94,7 @@ return if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt) if(LAZYACCESS(modifiers, RIGHT_CLICK)) - alt_click_on_secondary(A) + base_click_alt_secondary(A) else base_click_alt(A) return @@ -386,24 +386,6 @@ A.CtrlClick(src) return - -///The base proc of when something is right clicked on when alt is held - generally use alt_click_secondary instead -/atom/proc/alt_click_on_secondary(atom/A) - . = SEND_SIGNAL(src, COMSIG_MOB_ALTCLICKON_SECONDARY, A) - if(. & COMSIG_MOB_CANCEL_CLICKON) - return - A.alt_click_secondary(src) - -///The base proc of when something is right clicked on when alt is held -/atom/proc/alt_click_secondary(mob/user) - if(!user.can_interact_with(src)) - return FALSE - if(SEND_SIGNAL(src, COMSIG_CLICK_ALT_SECONDARY, user) & COMPONENT_CANCEL_CLICK_ALT_SECONDARY) - return - if(isobserver(user) && user.client && check_rights_for(user.client, R_DEBUG)) - user.client.toggle_tag_datum(src) - return - /mob/proc/TurfAdjacent(turf/tile) return tile.Adjacent(src) diff --git a/code/_onclick/click_alt.dm b/code/_onclick/click_alt.dm index dfda35ebda9c8..957f55ab21793 100644 --- a/code/_onclick/click_alt.dm +++ b/code/_onclick/click_alt.dm @@ -1,5 +1,5 @@ /** - * ### Base proc for alt click interaction. + * ### Base proc for alt click interaction left click. * * If you wish to add custom `click_alt` behavior for a single type, use that proc. */ @@ -53,7 +53,6 @@ client.loot_panel.open(tile) - /** * ## Custom alt click interaction * Override this to change default alt click behavior. Return `CLICK_ACTION_SUCCESS`, `CLICK_ACTION_BLOCKING` or `NONE`. @@ -86,6 +85,46 @@ return NONE +/** + * ### Base proc for alt click interaction right click. + * + * If you wish to add custom `click_alt_secondary` behavior for a single type, use that proc. + */ +/mob/proc/base_click_alt_secondary(atom/target) + SHOULD_NOT_OVERRIDE(TRUE) + + //Hook on the mob to intercept the click + if(SEND_SIGNAL(src, COMSIG_MOB_ALTCLICKON_SECONDARY, target) & COMSIG_MOB_CANCEL_CLICKON) + return + + var/can_use_click_action = FALSE + if(isturf(target)) + // Turfs are special because they can't be used with can_perform_action + can_use_click_action = can_perform_turf_action(target) + else + can_use_click_action = can_perform_action(target, target.interaction_flags_click | SILENT_ADJACENCY) + if(!can_use_click_action) + return + + //Hook on the atom to intercept the click + if(SEND_SIGNAL(target, COMSIG_CLICK_ALT_SECONDARY, src) & COMPONENT_CANCEL_CLICK_ALT_SECONDARY) + return + if(isobserver(src) && client && check_rights_for(client, R_DEBUG)) + client.toggle_tag_datum(src) + return + target.click_alt_secondary(src) + +/** + * ## Custom alt click secondary interaction + * Override this to change default alt right click behavior. + * + * ### Guard clauses + * Consider adding `interaction_flags_click` before adding unique guard clauses. + **/ +/atom/proc/click_alt_secondary(mob/user) + SHOULD_CALL_PARENT(FALSE) + return NONE + /// Helper proc to validate turfs. Used because can_perform_action does not support turfs. /mob/proc/can_perform_turf_action(turf/target) if(!CanReach(target)) // No error message for parity with SILENT_ADJACENCY diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 88d5dada28154..e2e534443691a 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -197,13 +197,13 @@ if(!R.client) return + //Module is not currently active + screenmob.client.screen -= R.model.get_inactive_modules() + if(!R.shown_robot_modules || !screenmob.hud_used.hud_shown) //Modules display is hidden screenmob.client.screen -= module_store_icon //"store" icon - for(var/atom/A in R.model.get_inactive_modules()) - //Module is not currently active - screenmob.client.screen -= A R.shown_robot_modules = 0 screenmob.client.screen -= R.robot_modules_background return diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 95622a7c60a72..18e9fa12a660a 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -234,7 +234,8 @@ user.client.give_award(/datum/award/achievement/misc/selfouch, user) user.do_attack_animation(target_mob) - target_mob.attacked_by(src, user) + if(!target_mob.attacked_by(src, user)) + return TRUE SEND_SIGNAL(src, COMSIG_ITEM_POST_ATTACK, target_mob, user, params) @@ -318,7 +319,7 @@ SEND_SIGNAL(attacking_item, COMSIG_ITEM_ATTACK_ZONE, src, user, targeting) if(damage <= 0) - return FALSE + return TRUE if(ishuman(src) || client) // istype(src) is kinda bad, but it's to avoid spamming the blackbox SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[attacking_item.force]", "[attacking_item.type]")) diff --git a/code/datums/actions/mobs/charge.dm b/code/datums/actions/mobs/charge.dm index 9aee4cd83456a..43fcbd57f69ba 100644 --- a/code/datums/actions/mobs/charge.dm +++ b/code/datums/actions/mobs/charge.dm @@ -23,6 +23,8 @@ /datum/action/cooldown/mob_cooldown/charge/Activate(atom/target_atom) disable_cooldown_actions() + // No charging and meleeing (overridded by StartCooldown after charge ends) + next_melee_use_time = world.time + 100 SECONDS charge_sequence(owner, target_atom, charge_delay, charge_past) StartCooldown() enable_cooldown_actions() diff --git a/code/datums/components/armor_plate.dm b/code/datums/components/armor_plate.dm index 7f56f2d74e4eb..9e495ada52fae 100644 --- a/code/datums/components/armor_plate.dm +++ b/code/datums/components/armor_plate.dm @@ -11,6 +11,8 @@ var/upgrade_name /// Adds a prefix to the item, demonstrating that it is upgraded in some way. var/upgrade_prefix = "reinforced" + /// Tracks whether or not we've received an upgrade or not. + var/have_upgraded = FALSE /datum/armor/armor_plate melee = 10 @@ -81,11 +83,11 @@ to_chat(user, span_info("You strengthen [mecha_for_upgrading], improving its resistance against attacks.")) else SEND_SIGNAL(target_for_upgrading, COMSIG_ARMOR_PLATED, amount, maxamount) - if(upgrade_prefix) + if(upgrade_prefix && !have_upgraded) target_for_upgrading.name = "[upgrade_prefix] [target_for_upgrading.name]" + have_upgraded = TRUE to_chat(user, span_info("You strengthen [target_for_upgrading], improving its resistance against attacks.")) - /datum/component/armor_plate/proc/dropplates(datum/source, force) SIGNAL_HANDLER diff --git a/code/datums/components/crafting/tailoring.dm b/code/datums/components/crafting/tailoring.dm index e2ba25e9c458c..2bcec49aeb504 100644 --- a/code/datums/components/crafting/tailoring.dm +++ b/code/datums/components/crafting/tailoring.dm @@ -255,10 +255,19 @@ /datum/crafting_recipe/drakecloak name = "Ash Drake Armour" result = /obj/item/clothing/suit/hooded/cloak/drake - time = 6 SECONDS + time = 4 SECONDS reqs = list( - /obj/item/stack/sheet/bone = 10, /obj/item/stack/sheet/sinew = 2, + /obj/item/drake_remains = 1, + ) + category = CAT_CLOTHING + +/datum/crafting_recipe/drakeremains + name = "Drake Remains" + result = /obj/item/drake_remains + time = 1 SECONDS + reqs = list( + /obj/item/stack/sheet/bone = 10, /obj/item/stack/sheet/animalhide/ashdrake = 5, ) category = CAT_CLOTHING diff --git a/code/datums/components/energized.dm b/code/datums/components/energized.dm index eb45ee66e2b12..255970bfd311c 100644 --- a/code/datums/components/energized.dm +++ b/code/datums/components/energized.dm @@ -95,7 +95,7 @@ tram_velocity_sign = tram.travel_direction & EAST ? 1 : -1 // How far away are we? negative if already passed. - var/approach_distance = tram_velocity_sign * (plate_pos - (tram_pos + (DEFAULT_TRAM_LENGTH * 0.5))) + var/approach_distance = tram_velocity_sign * (plate_pos - (tram_pos + DEFAULT_TRAM_MIDPOINT)) // Check if our victim is in the active path of the tram. if(!tram.controller_active) @@ -106,7 +106,7 @@ return FALSE if((tram.travel_direction & EAST) && outbound > tram.destination_platform.platform_code) return FALSE - if(approach_distance >= AMBER_THRESHOLD_DEGRADED) + if(approach_distance >= XING_THRESHOLD_AMBER) return FALSE // Finally the interesting part where they ACTUALLY get hit! diff --git a/code/datums/components/shielded.dm b/code/datums/components/shielded.dm index f35ad2ceec0c4..da83c4ad2d29d 100644 --- a/code/datums/components/shielded.dm +++ b/code/datums/components/shielded.dm @@ -28,6 +28,9 @@ var/show_charge_as_alpha = FALSE /// The item we use for recharging var/recharge_path + /// Whether or not we lose a charge when hit by 0 damage items or projectiles + var/lose_charge_on_damageless = FALSE + /// The cooldown tracking when we were last hit COOLDOWN_DECLARE(recently_hit_cd) /// The cooldown tracking when we last replenished a charge @@ -172,6 +175,9 @@ if(lose_multiple_charges) // if the shield has health like damage we'll lose charges equal to the damage of the hit charge_loss = damage + else if(!lose_charge_on_damageless && !damage) + charge_loss = 0 + adjust_charge(-charge_loss) INVOKE_ASYNC(src, PROC_REF(actually_run_hit_callback), owner, attack_text, current_charges) diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index d66bc85bb55ae..639637af1d7e9 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -288,7 +288,6 @@ return FALSE end = Temp - /datum/disease/proc/cure(add_resistance = TRUE) if(severity == DISEASE_SEVERITY_UNCURABLE) //aw man :( return diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index a35e700f7b4ef..0e54bfad385e0 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -53,15 +53,14 @@ if(!suppress_warning) affected_mob.emote("sniff") else - active_disease.airborne_spread(spread_range, force_spread = TRUE, require_facing = TRUE) + if(affected_mob.can_spread_airborne_diseases()) //don't spread germs if they covered their mouth + affected_mob.infectious_sneeze(active_disease, TRUE, range = spread_range) if(cartoon_sneezing) //Yeah, this can fling you around even if you have a space suit helmet on. It's, uh, bluespace snot, yeah. - affected_mob.emote("sneeze") to_chat(affected_mob, span_userdanger("You are launched violently backwards by an all-mighty sneeze!")) var/sneeze_distance = rand(2,4) //twice as far as a normal baseball bat strike will fling you var/turf/target = get_ranged_target_turf(affected_mob, REVERSE_DIR(affected_mob.dir), sneeze_distance) affected_mob.throw_at(target, sneeze_distance, rand(1,4)) //with the wounds update, sneezing at 7 speed was causing peoples bones to spontaneously explode, turning cartoonish sneezing into a nightmarishly lethal GBS 2.0 outbreak else if(COOLDOWN_FINISHED(src, sneeze_cooldown) || !COOLDOWN_FINISHED(src, sneeze_cooldown) && prob(60) && !off_cooldown_sneezed) - affected_mob.emote("sneeze") COOLDOWN_START(src, sneeze_cooldown, 5 SECONDS) if(!off_cooldown_sneezed && !COOLDOWN_FINISHED(src, sneeze_cooldown)) off_cooldown_sneezed = TRUE diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index f7bf6cf4b18a7..3b361e53c236c 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -6,7 +6,7 @@ cures = list(/datum/reagent/medicine/spaceacillin) agent = "XY-rhinovirus" viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 0.5 + spreading_modifier = 0.1 spread_text = "Airborne" severity = DISEASE_SEVERITY_NONTHREAT required_organ = ORGAN_SLOT_LUNGS @@ -20,7 +20,7 @@ switch(stage) if(2) if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(0.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) @@ -33,7 +33,7 @@ return FALSE if(3) if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(0.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) diff --git a/code/datums/diseases/cold9.dm b/code/datums/diseases/cold9.dm index 2e55df23b7ec8..97adebcac7026 100644 --- a/code/datums/diseases/cold9.dm +++ b/code/datums/diseases/cold9.dm @@ -20,7 +20,7 @@ if(2) affected_mob.adjust_bodytemperature(-5 * seconds_per_tick) if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(0.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) @@ -34,7 +34,7 @@ if(3) affected_mob.adjust_bodytemperature(-10 * seconds_per_tick) if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(0.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) diff --git a/code/datums/diseases/dna_spread.dm b/code/datums/diseases/dna_spread.dm index 6fd926f60d3df..e649a557443f1 100644 --- a/code/datums/diseases/dna_spread.dm +++ b/code/datums/diseases/dna_spread.dm @@ -40,7 +40,7 @@ switch(stage) if(2, 3) //Pretend to be a cold and give time to spread. if(SPT_PROB(4, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.sneeze() if(SPT_PROB(4, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) diff --git a/code/datums/diseases/fake_gbs.dm b/code/datums/diseases/fake_gbs.dm index 655439cdc6cc5..939ab620feff4 100644 --- a/code/datums/diseases/fake_gbs.dm +++ b/code/datums/diseases/fake_gbs.dm @@ -19,7 +19,7 @@ switch(stage) if(2) if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.sneeze() if(3) if(SPT_PROB(2.5, seconds_per_tick)) affected_mob.emote("cough") diff --git a/code/datums/diseases/flu.dm b/code/datums/diseases/flu.dm index 9412d2a2a2f63..4ad7bb9b92b59 100644 --- a/code/datums/diseases/flu.dm +++ b/code/datums/diseases/flu.dm @@ -7,7 +7,7 @@ cure_chance = 5 agent = "H13N1 flu virion" viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 0.75 + spreading_modifier = 0.1 desc = "If left untreated the subject will feel quite unwell." severity = DISEASE_SEVERITY_MINOR required_organ = ORGAN_SLOT_LUNGS @@ -20,7 +20,7 @@ switch(stage) if(2) if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(0.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) @@ -38,7 +38,7 @@ if(3) if(SPT_PROB(0.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(0.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) diff --git a/code/datums/diseases/fluspanish.dm b/code/datums/diseases/fluspanish.dm index 6919884b2fe30..1fa6b49d457ad 100644 --- a/code/datums/diseases/fluspanish.dm +++ b/code/datums/diseases/fluspanish.dm @@ -7,7 +7,7 @@ cure_chance = 5 agent = "1nqu1s1t10n flu virion" viable_mobtypes = list(/mob/living/carbon/human) - spreading_modifier = 0.75 + spreading_modifier = 0.1 desc = "If left untreated the subject will burn to death for being a heretic." severity = DISEASE_SEVERITY_DANGEROUS required_organ = ORGAN_SLOT_LUNGS @@ -21,7 +21,7 @@ if(2) affected_mob.adjust_bodytemperature(5 * seconds_per_tick) if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(2.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(0.5, seconds_per_tick)) @@ -31,7 +31,7 @@ if(3) affected_mob.adjust_bodytemperature(10 * seconds_per_tick) if(SPT_PROB(2.5, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.infectious_sneeze(src, TRUE) if(SPT_PROB(2.5, seconds_per_tick)) affected_mob.emote("cough") if(SPT_PROB(2.5, seconds_per_tick)) diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm index fe0502387f9e3..21dce839dc697 100644 --- a/code/datums/diseases/wizarditis.dm +++ b/code/datums/diseases/wizarditis.dm @@ -99,7 +99,7 @@ var/datum/action/cooldown/spell/picked = pick(random_spells) picked.Activate(affected_mob) - affected_mob.emote("sneeze") + affected_mob.sneeze() return /datum/disease/wizarditis/proc/spawn_wizard_clothes(chance = 0) diff --git a/code/datums/elements/rust.dm b/code/datums/elements/rust.dm index 396307197c49d..265e23c5a3171 100644 --- a/code/datums/elements/rust.dm +++ b/code/datums/elements/rust.dm @@ -109,6 +109,8 @@ var/mob/living/victim = entered if(IS_HERETIC(victim)) return + if(victim.can_block_magic(MAGIC_RESISTANCE)) + return victim.apply_status_effect(/datum/status_effect/rust_corruption) /datum/element/rust/heretic/proc/on_exited(turf/source, atom/movable/gone) diff --git a/code/datums/elements/tool_flash.dm b/code/datums/elements/tool_flash.dm index fd7c298d6c54e..f17d60970bf58 100644 --- a/code/datums/elements/tool_flash.dm +++ b/code/datums/elements/tool_flash.dm @@ -34,4 +34,4 @@ SIGNAL_HANDLER if(user && get_dist(get_turf(source), get_turf(user)) <= 1) - user.flash_act(min(flash_strength,1)) + user.flash_act(max(flash_strength,1)) diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index b45d0a6499e83..896820746d61d 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -196,6 +196,7 @@ quality = NEGATIVE remove_on_aheal = FALSE locked = TRUE //Species specific, keep out of actual gene pool + mutadone_proof = TRUE var/datum/species/original_species = /datum/species/human var/original_name diff --git a/code/datums/quirks/negative_quirks/blindness.dm b/code/datums/quirks/negative_quirks/blindness.dm index ce57e946fe92e..d0af915dc32b0 100644 --- a/code/datums/quirks/negative_quirks/blindness.dm +++ b/code/datums/quirks/negative_quirks/blindness.dm @@ -10,8 +10,15 @@ quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_CHANGES_APPEARANCE mail_goodies = list(/obj/item/clothing/glasses/sunglasses, /obj/item/cane/white) +/datum/quirk_constant_data/blindfoldcolor + associated_typepath = /datum/quirk/item_quirk/blindness + customization_options = list(/datum/preference/color/blindfold_color) + /datum/quirk/item_quirk/blindness/add_unique(client/client_source) - give_item_to_holder(/obj/item/clothing/glasses/blindfold/white, list(LOCATION_EYES = ITEM_SLOT_EYES, LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS)) + var/obj/item/clothing/glasses/blindfold/white/blindfold = new + blindfold.add_atom_colour(client_source?.prefs.read_preference(/datum/preference/color/blindfold_color), FIXED_COLOUR_PRIORITY) + blindfold.colored_before = TRUE + give_item_to_holder(blindfold, list(LOCATION_EYES = ITEM_SLOT_EYES, LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS)) /datum/quirk/item_quirk/blindness/add(client/client_source) quirk_holder.become_blind(QUIRK_TRAIT) diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 199840d44ec1b..30fc4a729cd98 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -775,7 +775,10 @@ span_userdanger(pick("Your lungs hurt!", "It hurts to breathe!")), span_warning(pick("You feel nauseated.", "You feel like you're going to throw up!"))) else - fake_emote = pick("cough", "sniff", "sneeze") + if(prob(40)) + fake_emote = "cough" + else + owner.sneeze() if(fake_emote) owner.emote(fake_emote) diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index cf8d9b238867f..2a45b8e2b3d27 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -35,10 +35,11 @@ if(WIRE_AI) // Pulse to pick a new AI. if(!R.emagged) var/new_ai + var/is_a_syndi_borg = (ROLE_SYNDICATE in R.faction) if(user) - new_ai = select_active_ai(user, R.z) + new_ai = select_active_ai(user, R.z, !is_a_syndi_borg, is_a_syndi_borg) else - new_ai = select_active_ai(R, R.z) + new_ai = select_active_ai(R, R.z, !is_a_syndi_borg, is_a_syndi_borg) R.notify_ai(AI_NOTIFICATION_CYBORG_DISCONNECTED) if(new_ai && (new_ai != R.connected_ai)) R.set_connected_ai(new_ai) diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index 46b4a1c391f22..f182d14d97e67 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -565,8 +565,9 @@ newdir = dir return SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) + var/oldDir = dir dir = newdir - SEND_SIGNAL(src, COMSIG_ATOM_POST_DIR_CHANGE, dir, newdir) + SEND_SIGNAL(src, COMSIG_ATOM_POST_DIR_CHANGE, oldDir, newdir) if(smoothing_flags & SMOOTH_BORDER_OBJECT) QUEUE_SMOOTH_NEIGHBORS(src) diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 9b33a901d9ebe..3ad109b30e544 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -329,17 +329,21 @@ if(!directly_use_energy(charge_per_item)) // provide the wait time until lathe is ready var/area/my_area = get_area(src) var/obj/machinery/power/apc/my_apc = my_area.apc - var/charging_wait = my_apc.time_to_charge(charge_per_item) - if(!isnull(charging_wait)) - say("Unable to continue production, APC overload. Wait [DisplayTimeText(charging_wait, round_seconds_to = 1)] and try again.") + if(!QDELETED(my_apc)) + var/charging_wait = my_apc.time_to_charge(charge_per_item) + if(!isnull(charging_wait)) + say("Unable to continue production, APC overload. Wait [DisplayTimeText(charging_wait, round_seconds_to = 1)] and try again.") + else + say("Unable to continue production, power grid overload.") else - say("Unable to continue production, power grid overload.") + say("Unable to continue production, no APC in area.") finalize_build() return var/is_stack = ispath(design.build_path, /obj/item/stack) if(!materials.has_materials(materials_needed, material_cost_coefficient, is_stack ? items_remaining : 1)) say("Unable to continue production, missing materials.") + finalize_build() return materials.use_materials(materials_needed, material_cost_coefficient, is_stack ? items_remaining : 1) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 32e56e2fca920..6570d022a49a9 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -21,7 +21,7 @@ var/obj/item/electronics/airlock/board var/device_type = null var/id = null - var/initialized_button = 0 + var/initialized_button = FALSE var/silicon_access_disabled = FALSE /obj/machinery/button/indestructible @@ -36,6 +36,10 @@ fire = 90 acid = 70 +/** + * INITIALIZATION + */ + /obj/machinery/button/Initialize(mapload, ndir = 0, built = 0) . = ..() if(built) @@ -58,11 +62,23 @@ setup_device() find_and_hang_on_wall() + register_context() -/obj/machinery/button/Destroy() - QDEL_NULL(device) - QDEL_NULL(board) - return ..() +/obj/machinery/button/proc/setup_device() + if(id && istype(device, /obj/item/assembly/control)) + var/obj/item/assembly/control/control_device = device + control_device.id = id + initialized_button = TRUE + +/obj/machinery/button/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock) + if(id) + id = "[port.shuttle_id]_[id]" + setup_device() + + +/** + * APPEARANCE + */ /obj/machinery/button/update_icon_state() icon_state = "[base_icon_state][skin]" @@ -94,53 +110,93 @@ if(!(machine_stat & (NOPOWER|BROKEN)) && !panel_open) . += emissive_appearance(icon, "[base_icon_state]-light-mask", src, alpha = src.alpha) +/obj/machinery/button/on_set_panel_open(old_value) + if(panel_open) // Only allow renaming while the panel is open + obj_flags |= UNIQUE_RENAME + else + obj_flags &= ~UNIQUE_RENAME + + +/** + * INTERACTION + */ + +/obj/machinery/button/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!panel_open) + return NONE + + if(isassembly(tool)) + return assembly_act(user, tool) + else if(istype(tool, /obj/item/electronics/airlock)) + return airlock_electronics_act(user, tool) + +/obj/machinery/button/proc/assembly_act(mob/living/user, obj/item/assembly/new_device) + if(device) + to_chat(user, span_warning("The button already contains a device!")) + return ITEM_INTERACT_BLOCKING + if(!user.transferItemToLoc(new_device, src, silent = FALSE)) + to_chat(user, span_warning("\The [new_device] is stuck to you!")) + return ITEM_INTERACT_BLOCKING + + device = new_device + to_chat(user, span_notice("You add \the [new_device] to the button.")) + + update_appearance() + return ITEM_INTERACT_SUCCESS + +/obj/machinery/button/proc/airlock_electronics_act(mob/living/user, obj/item/electronics/airlock/new_board) + if(board) + to_chat(user, span_warning("The button already contains a board!")) + return ITEM_INTERACT_BLOCKING + if(!user.transferItemToLoc(new_board, src, silent = FALSE)) + to_chat(user, span_warning("\The [new_board] is stuck to you!")) + return ITEM_INTERACT_BLOCKING + + board = new_board + if(board.one_access) + req_one_access = board.accesses + else + req_access = board.accesses + to_chat(user, span_notice("You add \the [new_board] to the button.")) + + update_appearance() + return ITEM_INTERACT_SUCCESS + /obj/machinery/button/screwdriver_act(mob/living/user, obj/item/tool) if(panel_open || allowed(user)) default_deconstruction_screwdriver(user, "[base_icon_state][skin]-open", "[base_icon_state][skin]", tool) update_appearance() - else - balloon_alert(user, "access denied") - flick_overlay_view("[base_icon_state]-overlay-error", 1 SECONDS) + return ITEM_INTERACT_SUCCESS - return TRUE + balloon_alert(user, "access denied") + flick_overlay_view("[base_icon_state]-overlay-error", 1 SECONDS) + return ITEM_INTERACT_BLOCKING -/obj/machinery/button/attackby(obj/item/W, mob/living/user, params) - if(panel_open) - if(!device && isassembly(W)) - if(!user.transferItemToLoc(W, src)) - to_chat(user, span_warning("\The [W] is stuck to you!")) - return - device = W - to_chat(user, span_notice("You add [W] to the button.")) - - if(!board && istype(W, /obj/item/electronics/airlock)) - if(!user.transferItemToLoc(W, src)) - to_chat(user, span_warning("\The [W] is stuck to you!")) - return - board = W - if(board.one_access) - req_one_access = board.accesses - else - req_access = board.accesses - balloon_alert(user, "electronics added") - to_chat(user, span_notice("You add [W] to the button.")) - - if(!device && !board && W.tool_behaviour == TOOL_WRENCH) - to_chat(user, span_notice("You start unsecuring the button frame...")) - W.play_tool_sound(src) - if(W.use_tool(src, user, 40)) - to_chat(user, span_notice("You unsecure the button frame.")) - transfer_fingerprints_to(new /obj/item/wallframe/button(get_turf(src))) - playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) - qdel(src) +/obj/machinery/button/wrench_act(mob/living/user, obj/item/tool) + if(!panel_open) + balloon_alert(user, "open button first!") + return ITEM_INTERACT_BLOCKING - update_appearance() - return + if(device || board) + balloon_alert(user, "empty button first!") + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You start unsecuring the button frame...")) + if(tool.use_tool(src, user, 40, volume=50)) + to_chat(user, span_notice("You unsecure the button frame.")) + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) + deconstruct(TRUE) + + return ITEM_INTERACT_SUCCESS + +/obj/machinery/button/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = ..() + if(.) + return . + // This is in here so it's called only after every other item interaction. + if(!user.combat_mode && !(tool.item_flags & NOBLUDGEON) && !panel_open) + return attempt_press(user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING - if(!user.combat_mode && !(W.item_flags & NOBLUDGEON)) - return attack_hand(user) - else - return ..() /obj/machinery/button/emag_act(mob/user, obj/item/card/emag/emag_card) . = ..() @@ -157,98 +213,168 @@ balloon_alert(user, "access overridden") return TRUE + /obj/machinery/button/attack_ai(mob/user) if(!silicon_access_disabled && !panel_open) - return attack_hand(user) + return attempt_press(user) /obj/machinery/button/attack_robot(mob/user) return attack_ai(user) -/obj/machinery/button/examine(mob/user) +/obj/machinery/button/interact(mob/user) . = ..() + if(.) + return + if(!initialized_button) + setup_device() + add_fingerprint(user) + if(!panel_open) + attempt_press(user) return - if(device) - . += span_notice("There is \a [device] inside, which could be removed with an empty hand.") - if(board) - . += span_notice("There is \a [board] inside, which could be removed with an empty hand.") - if(!board && !device) - . += span_notice("There is nothing currently installed in \the [src].") -/obj/machinery/button/proc/setup_device() - if(id && istype(device, /obj/item/assembly/control)) - var/obj/item/assembly/control/A = device - A.id = id - initialized_button = 1 + if(board) + remove_airlock_electronics(user) + return + if(device) + remove_assembly(user) + return -/obj/machinery/button/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock) - if(id) - id = "[port.shuttle_id]_[id]" - setup_device() + if(can_alter_skin) + if(skin == "") + skin = "-warning" + to_chat(user, span_notice("You change the button frame's front panel to warning lines.")) + else + skin = "" + to_chat(user, span_notice("You change the button frame's front panel to default.")) + update_appearance(UPDATE_ICON) + balloon_alert(user, "style swapped") -/obj/machinery/button/interact(mob/user) - . = ..() - if(.) - return +/obj/machinery/button/attack_hand_secondary(mob/user, list/modifiers) if(!initialized_button) setup_device() add_fingerprint(user) - if(panel_open) - if(device || board) - if(device) - user.put_in_hands(device) - device = null - if(board) - user.put_in_hands(board) - req_access = list() - req_one_access = list() - board = null - update_appearance(UPDATE_ICON) - balloon_alert(user, "electronics removed") - to_chat(user, span_notice("You remove electronics from the button frame.")) - - else if(can_alter_skin) - if(skin == "") - skin = "-warning" - to_chat(user, span_notice("You change the button frame's front panel to warning lines.")) - else - skin = "" - to_chat(user, span_notice("You change the button frame's front panel to default.")) - update_appearance(UPDATE_ICON) - balloon_alert(user, "swapped style") - return + if(!panel_open) + return SECONDARY_ATTACK_CALL_NORMAL + + if(device) + remove_assembly(user) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(board) + remove_airlock_electronics(user) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + + return ..() + +/obj/machinery/button/proc/remove_assembly(mob/user) + user.put_in_hands(device) + to_chat(user, span_notice("You remove \the [device] from the button frame.")) + device = null + update_appearance(UPDATE_ICON) + +/obj/machinery/button/proc/remove_airlock_electronics(mob/user) + user.put_in_hands(board) + to_chat(user, span_notice("You remove the board from the button frame.")) + req_access = list() + req_one_access = list() + board = null + update_appearance(UPDATE_ICON) + +/obj/machinery/button/proc/attempt_press(mob/user) if((machine_stat & (NOPOWER|BROKEN))) - return + return FALSE if(device && device.next_activate > world.time) - return + return FALSE if(!allowed(user)) balloon_alert(user, "access denied") flick_overlay_view("[base_icon_state]-overlay-error", 1 SECONDS) - return + return FALSE use_energy(5 JOULES) flick_overlay_view("[base_icon_state]-overlay-success", 1 SECONDS) if(device) device.pulsed(user) - SEND_GLOBAL_SIGNAL(COMSIG_GLOB_BUTTON_PRESSED,src) + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_BUTTON_PRESSED, src) + return TRUE + /** - * Called when the mounted button's wall is knocked down. + * DECONSTRUCTION */ -/obj/machinery/button/proc/knock_down() + +/obj/machinery/button/on_deconstruction(disassembled) + var/obj/item/wallframe/button/dropped_frame = new /obj/item/wallframe/button(drop_location()) + transfer_fingerprints_to(dropped_frame) + +/obj/machinery/button/dump_inventory_contents(list/subset) + . = ..() + device = null + board = null + req_access = list() + req_one_access = list() + + +/** + * INFORMATION + */ + +/obj/machinery/button/examine(mob/user) + . = ..() + if(!panel_open) + return if(device) - device.forceMove(get_turf(src)) - device = null + . += span_notice("There is \a [device] inside, which could be removed with an empty hand.") if(board) - board.forceMove(get_turf(src)) - req_access = list() - req_one_access = list() - board = null - qdel(src) + . += span_notice("There is \a [board] inside, which could be removed with an empty hand.") + if(isnull(board) && isnull(device)) + . += span_notice("There is nothing currently installed in \the [src].") + +/obj/machinery/button/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) + if(panel_open) + if(isnull(held_item)) + if(board && device) + context[SCREENTIP_CONTEXT_LMB] = "Remove Board" + context[SCREENTIP_CONTEXT_RMB] = "Remove Device" + return CONTEXTUAL_SCREENTIP_SET + else if(board) + context[SCREENTIP_CONTEXT_LMB] = "Remove Board" + return CONTEXTUAL_SCREENTIP_SET + else if(device) + context[SCREENTIP_CONTEXT_LMB] = "Remove Device" + return CONTEXTUAL_SCREENTIP_SET + else if(can_alter_skin) + context[SCREENTIP_CONTEXT_LMB] = "Swap Style" + return CONTEXTUAL_SCREENTIP_SET + else if(isassembly(held_item)) + context[SCREENTIP_CONTEXT_LMB] = "Install Device" + return CONTEXTUAL_SCREENTIP_SET + else if(istype(held_item, /obj/item/electronics/airlock)) + context[SCREENTIP_CONTEXT_LMB] = "Install Board" + return CONTEXTUAL_SCREENTIP_SET + else if(held_item.tool_behaviour == TOOL_WRENCH) + context[SCREENTIP_CONTEXT_LMB] = "Deconstruct Button" + return CONTEXTUAL_SCREENTIP_SET + else if(held_item.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "Close Button" + return CONTEXTUAL_SCREENTIP_SET + else + if(isnull(held_item)) + context[SCREENTIP_CONTEXT_LMB] = "Press Button" + return CONTEXTUAL_SCREENTIP_SET + else if(held_item.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "Open Button" + return CONTEXTUAL_SCREENTIP_SET + + return NONE + + +/** + * MAPPING PRESETS + */ /obj/machinery/button/door name = "door button" @@ -265,13 +391,13 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/button/door, 24) /obj/machinery/button/door/setup_device() if(!device) if(normaldoorcontrol) - var/obj/item/assembly/control/airlock/A = new(src) - A.specialfunctions = specialfunctions - device = A + var/obj/item/assembly/control/airlock/airlock_device = new(src) + airlock_device.specialfunctions = specialfunctions + device = airlock_device else - var/obj/item/assembly/control/C = new(src) - C.sync_doors = sync_doors - device = C + var/obj/item/assembly/control/control_device = new(src) + control_device.sync_doors = sync_doors + device = control_device ..() /obj/machinery/button/door/incinerator_vent_ordmix diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 816177f9f0edd..383a980a64da0 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -48,7 +48,7 @@ return INITIALIZE_HINT_QDEL /obj/machinery/computer/upload/ai/interact(mob/user) - current = select_active_ai(user, z) + current = select_active_ai(user, z, TRUE) if (!current) to_chat(user, span_alert("No active AIs detected!")) diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 0ddbd58a5c8c8..34a161acac1b3 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -17,12 +17,14 @@ text_color = "#F44" header_text_color = "#F88" - var/id = null // id of linked machinery/lockers - + /// ID of linked machinery/lockers. + var/id = null + /// The time at which the timer started. var/activation_time = 0 + /// The time offset from the activation time before releasing. var/timer_duration = 0 - - var/timing = FALSE // boolean, true/1 timer is on, false/0 means it's not timing + /// Is the timer on? + var/timing = FALSE ///List of weakrefs to nearby doors var/list/doors = list() ///List of weakrefs to nearby flashers @@ -138,7 +140,7 @@ sec_radio.talk_into(src, "Timer has expired. Releasing prisoner.", FREQ_SECURITY) timing = FALSE - activation_time = null + activation_time = 0 set_timer(0) end_processing() @@ -168,12 +170,12 @@ /** * Return time left. * Arguments: - * * seconds - return time in seconds it TRUE, else deciseconds. + * * seconds - Return the time in seconds if TRUE, else deciseconds. */ /obj/machinery/status_display/door_timer/proc/time_left(seconds = FALSE) - . = max(0, timer_duration - (activation_time ? world.time - activation_time : 0)) + . = max(0, timer_duration + activation_time - world.time) if(seconds) - . /= 10 + . /= (1 SECONDS) /** * Set the timer. Does NOT automatically start counting down, but does update the display. @@ -184,7 +186,7 @@ * value - time in deciseconds to set the timer for. */ /obj/machinery/status_display/door_timer/proc/set_timer(value) - var/new_time = clamp(value, 0, MAX_TIMER) + var/new_time = clamp(value, 0, MAX_TIMER + world.time - activation_time) . = new_time == timer_duration //return 1 on no change timer_duration = new_time update_content() @@ -229,7 +231,7 @@ if("time") var/value = text2num(params["adjust"]) if(value) - . = set_timer(time_left() + value) + . = set_timer(timer_duration + value) user.investigate_log("modified the timer by [value/10] seconds for cell [id], currently [time_left(seconds = TRUE)]", INVESTIGATE_RECORDS) user.log_message("modified the timer by [value/10] seconds for cell [id], currently [time_left(seconds = TRUE)]", LOG_ATTACK) if("start") diff --git a/code/game/objects/effects/cursor_catcher.dm b/code/game/objects/effects/cursor_catcher.dm index 3229cd44b7083..a8c19e40be80d 100644 --- a/code/game/objects/effects/cursor_catcher.dm +++ b/code/game/objects/effects/cursor_catcher.dm @@ -8,9 +8,9 @@ var/mob/owner /// Client view size of the scoping mob. var/list/view_list - /// Pixel x we send to the scope component. + /// Pixel x relative to the hovered tile we send to the scope component. var/given_x - /// Pixel y we send to the scope component. + /// Pixel y relative to the hovered tile we send to the scope component. var/given_y /// The turf we send to the scope component. var/turf/given_turf diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index fe03aaaf01e0d..880e619da0a8a 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -56,6 +56,7 @@ icon_state = "card_grey" worn_icon_state = "nothing" slot_flags = ITEM_SLOT_ID + interaction_flags_click = FORBID_TELEKINESIS_REACH armor_type = /datum/armor/card_id resistance_flags = FIRE_PROOF | ACID_PROOF @@ -633,9 +634,6 @@ /obj/item/card/id/proc/alt_click_can_use_id(mob/living/user) if(!isliving(user)) return FALSE - if(!user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return FALSE - return TRUE /// Attempts to set a new bank account on the ID card. @@ -706,13 +704,11 @@ registered_account.bank_card_talk(span_warning("ERROR: The linked account requires [difference] more credit\s to perform that withdrawal."), TRUE) return CLICK_ACTION_BLOCKING -/obj/item/card/id/alt_click_secondary(mob/user) - . = ..() +/obj/item/card/id/click_alt_secondary(mob/user) if(!alt_click_can_use_id(user)) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + return if(!registered_account || registered_account.replaceable) set_new_account(user) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/item/card/id/proc/pay_debt(user) var/amount_to_pay = tgui_input_number(user, "How much do you want to pay? (Max: [registered_account.account_balance] cr)", "Debt Payment", max_value = min(registered_account.account_balance, registered_account.account_debt)) diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 1401b057dab14..37cfb70240d67 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -701,7 +701,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM name = "smoking pipe" desc = "A pipe, for smoking. Probably made of meerschaum or something." icon_state = "pipeoff" - icon_on = "pipeff" //Note - these are in masks.dmi + icon_on = "pipeoff" //Note - these are in masks.dmi icon_off = "pipeoff" inhand_icon_state = null inhand_icon_on = null @@ -772,7 +772,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM name = "corn cob pipe" desc = "A nicotine delivery system popularized by folksy backwoodsmen and kept popular in the modern age and beyond by space hipsters. Can be loaded with objects." icon_state = "cobpipeoff" - icon_on = "cobpipeff" //Note - these are in masks.dmi + icon_on = "cobpipeoff" //Note - these are in masks.dmi icon_off = "cobpipeoff" inhand_icon_on = null inhand_icon_off = null diff --git a/code/game/objects/items/devices/aicard_evil.dm b/code/game/objects/items/devices/aicard_evil.dm index 8aaa9f0311116..3e8c56ce940fd 100644 --- a/code/game/objects/items/devices/aicard_evil.dm +++ b/code/game/objects/items/devices/aicard_evil.dm @@ -62,6 +62,11 @@ // Make it look evil!!! new_ai.hologram_appearance = mutable_appearance('icons/mob/silicon/ai.dmi',"xeno_queen") //good enough new_ai.icon_state = resolve_ai_icon("hades") + // Hide PDA from messenger + var/datum/computer_file/program/messenger/msg = locate() in new_ai.modularInterface.stored_files + if(msg) + msg.invisible = TRUE + // Transfer the AI from the core we created into the card, then delete the core capture_ai(new_ai, user) var/obj/structure/ai_core/deactivated/detritus = locate() in get_turf(src) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index b3d33cd99e984..01bbc9262d0af 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -330,9 +330,11 @@ attack_verb_simple = list("flog", "whip", "lash", "discipline") hitsound = 'sound/weapons/whip.ogg' -/obj/item/melee/curator_whip/afterattack(target, mob/user, proximity_flag) +/obj/item/melee/curator_whip/attack(mob/living/target, mob/living/user, params) . = ..() - if(ishuman(target) && proximity_flag) + if(.) + return + if(ishuman(target)) var/mob/living/carbon/human/human_target = target human_target.drop_all_held_items() human_target.visible_message(span_danger("[user] disarms [human_target]!"), span_userdanger("[user] disarmed you!")) diff --git a/code/game/objects/items/rcd/RLD.dm b/code/game/objects/items/rcd/RLD.dm index 6272c7a374d37..e321e5e941d67 100644 --- a/code/game/objects/items/rcd/RLD.dm +++ b/code/game/objects/items/rcd/RLD.dm @@ -188,12 +188,12 @@ if(!useResource(GLOW_STICK_COST, user)) return FALSE activate() - var/obj/item/flashlight/glowstick/G = new /obj/item/flashlight/glowstick(start) - G.color = color_choice - G.set_light_color(G.color) - G.throw_at(A, 9, 3, user) - G.light_on = TRUE - G.update_brightness() + var/obj/item/flashlight/glowstick/new_stick = new /obj/item/flashlight/glowstick(start) + new_stick.color = color_choice + new_stick.set_light_color(new_stick.color) + new_stick.throw_at(A, 9, 3, user) + new_stick.turn_on() + new_stick.update_brightness() return TRUE diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 959894b31638a..3c42e65e29c87 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -400,7 +400,7 @@ found_hypo = TRUE if(!found_hypo) - to_chat(user, span_warning("This unit is already equipped with a piercing hypospray upgrade!")) //check to see if we already have this module + to_chat(user, span_warning("There are no installed hypospray modules to upgrade with piercing!")) //check to see if any hyposprays were upgraded return FALSE /obj/item/borg/upgrade/piercing_hypospray/deactivate(mob/living/silicon/robot/borg, mob/living/user = usr) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index acbd7bfa39539..9aef99482a0b1 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -382,7 +382,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) context[SCREENTIP_CONTEXT_RMB] = anchored ? "Unanchor" : "Anchor" screentip_change = TRUE - if(!locked && (welded || !can_weld_shut)) + if(!locked && !opened && (welded || !can_weld_shut)) if(!secure) if(!broken && can_install_electronics && istype(held_item, /obj/item/electronics/airlock)) context[SCREENTIP_CONTEXT_LMB] = "Install Electronics" @@ -644,7 +644,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /// check if we can install airlock electronics in this closet /obj/structure/closet/proc/can_install_airlock_electronics(mob/user) - if(secure || !can_install_electronics || !(welded || !can_weld_shut)) + if(secure || !can_install_electronics || opened) return FALSE if(broken) @@ -659,9 +659,11 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /// check if we can unscrew airlock electronics from this closet /obj/structure/closet/proc/can_unscrew_airlock_electronics(mob/user) - if(!secure || !(welded || !can_weld_shut)) + if(!secure || opened) + return FALSE + if(card_reader_installed) + balloon_alert(user, "attached to reader!") return FALSE - if(locked) balloon_alert(user, "unlock first!") return FALSE @@ -670,7 +672,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /// check if we can install card reader in this closet /obj/structure/closet/proc/can_install_card_reader(mob/user) - if(card_reader_installed || !can_install_electronics || !length(access_choices) || !(welded || !can_weld_shut)) + if(card_reader_installed || !can_install_electronics || !length(access_choices) || opened) return FALSE if(broken) @@ -689,7 +691,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /// check if we can pry out the card reader from this closet /obj/structure/closet/proc/can_pryout_card_reader(mob/user) - if(!card_reader_installed || !(welded || !can_weld_shut)) + if(!card_reader_installed || opened) return FALSE if(locked) diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index b61fffd944e94..8270e3d791a90 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -157,6 +157,9 @@ /turf/open/indestructible/light icon_state = "light_on-1" + light_range = 3 + light_color = LIGHT_COLOR_CYAN + light_on = TRUE /turf/open/indestructible/permalube icon_state = "darkfull" diff --git a/code/modules/actionspeed/_actionspeed_modifier.dm b/code/modules/actionspeed/_actionspeed_modifier.dm index 761bfc3ff74a4..36b9b9c860dec 100644 --- a/code/modules/actionspeed/_actionspeed_modifier.dm +++ b/code/modules/actionspeed/_actionspeed_modifier.dm @@ -40,7 +40,8 @@ can next move /datum/actionspeed_modifier/New(init_id) . = ..() - id = init_id + if(init_id) + id = init_id if(!id) id = "[type]" //We turn the path into a string. diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm index 671e01603c5c7..56999243b5de3 100644 --- a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm +++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm @@ -76,7 +76,7 @@ /datum/heretic_knowledge/summon/rusty name = "Rusted Ritual" - desc = "Allows you to transmute a pool of vomit, some cable coil, and 5 sheets of iron into a Rust Walker. \ + desc = "Allows you to transmute a pool of vomit, some cable coil, and 5 sheets of titanium into a Rust Walker. \ Rust Walkers excel at spreading rust and are moderately strong in combat." gain_text = "I combined my knowledge of creation with my desire for corruption. The Marshal knew my name, and the Rusted Hills echoed out." next_knowledge = list( @@ -85,7 +85,7 @@ ) required_atoms = list( /obj/effect/decal/cleanable/vomit = 1, - /obj/item/stack/sheet/iron = 5, + /obj/item/stack/sheet/mineral/titanium = 5, /obj/item/stack/cable_coil = 15, ) mob_to_summon = /mob/living/basic/heretic_summon/rust_walker diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index d784175d62b6e..aba8c279456df 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -250,7 +250,7 @@ /datum/antagonist/traitor/proc/forge_single_generic_objective() if(prob(KILL_PROB)) - var/list/active_ais = active_ais() + var/list/active_ais = active_ais(skip_syndicate = TRUE) if(active_ais.len && prob(DESTROY_AI_PROB(GLOB.joined_player_list.len))) var/datum/objective/destroy/destroy_objective = new() destroy_objective.owner = owner diff --git a/code/modules/cargo/exports/lavaland.dm b/code/modules/cargo/exports/lavaland.dm index 51165be191c87..7102db0dd8f0d 100644 --- a/code/modules/cargo/exports/lavaland.dm +++ b/code/modules/cargo/exports/lavaland.dm @@ -33,6 +33,7 @@ export_types = list( /obj/item/dragons_blood, /obj/item/guardian_creator/miner, + /obj/item/drake_remains, /obj/item/lava_staff, /obj/item/melee/ghost_sword, /obj/item/prisoncube, diff --git a/code/modules/client/preferences/blindfold_color.dm b/code/modules/client/preferences/blindfold_color.dm new file mode 100644 index 0000000000000..9e6504579acb6 --- /dev/null +++ b/code/modules/client/preferences/blindfold_color.dm @@ -0,0 +1,14 @@ +/// Preference for the roundstart color of the blindfold given by the Blindness quirk. +/datum/preference/color/blindfold_color + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "blindfold_color" + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/color/blindfold_color/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return /datum/quirk/item_quirk/blindness::name in preferences.all_quirks + +/datum/preference/color/blindfold_color/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 2cac09685796f..7a3c0f1150de4 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -3,6 +3,7 @@ icon = 'icons/obj/clothing/neck.dmi' body_parts_covered = NECK slot_flags = ITEM_SLOT_NECK + interaction_flags_click = NEED_DEXTERITY strip_delay = 40 equip_delay_other = 40 @@ -103,11 +104,8 @@ user.update_clothing(ITEM_SLOT_NECK) return CLICK_ACTION_SUCCESS -/obj/item/clothing/neck/tie/alt_click_secondary(mob/user) - . = ..() - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return - alternate_worn_layer = alternate_worn_layer == initial(alternate_worn_layer) ? NONE : initial(alternate_worn_layer) +/obj/item/clothing/neck/tie/click_alt_secondary(mob/user) + alternate_worn_layer = (alternate_worn_layer == initial(alternate_worn_layer) ? NONE : initial(alternate_worn_layer)) user.update_clothing(ITEM_SLOT_NECK) balloon_alert(user, "wearing [alternate_worn_layer == initial(alternate_worn_layer) ? "below" : "above"] suits") diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index fd8512c3eb83f..40f8068aef56d 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -6,6 +6,7 @@ righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi' body_parts_covered = CHEST|GROIN|LEGS|ARMS slot_flags = ITEM_SLOT_ICLOTHING + interaction_flags_click = NEED_DEXTERITY armor_type = /datum/armor/clothing_under equip_sound = 'sound/items/equip/jumpsuit_equip.ogg' drop_sound = 'sound/items/handling/cloth_drop.ogg' @@ -378,17 +379,10 @@ rolldown() return CLICK_ACTION_SUCCESS -/obj/item/clothing/under/alt_click_secondary(mob/user) - . = ..() - if(.) - return - +/obj/item/clothing/under/click_alt_secondary(mob/user) if(!LAZYLEN(attached_accessories)) balloon_alert(user, "no accessories to remove!") return - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return - pop_accessory(user) /obj/item/clothing/under/verb/jumpsuit_adjust() diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm index ae5f22975a031..9e86bf9befbb7 100644 --- a/code/modules/deathmatch/deathmatch_loadouts.dm +++ b/code/modules/deathmatch/deathmatch_loadouts.dm @@ -543,3 +543,204 @@ granted_spells = list( /datum/action/cooldown/spell/conjure/simian, ) + +/datum/outfit/deathmatch_loadout/head_of_security + name = "Deathmatch: Head of Security" + display_name = "Head of Security" + desc = "Finally, nobody to stop the power from going to your head." + + head = /datum/outfit/job/hos::head + ears = /datum/outfit/job/hos::ears + uniform = /obj/item/clothing/under/rank/security/head_of_security/alt + shoes = /datum/outfit/job/hos::shoes + neck = /datum/outfit/job/hos::neck + glasses = /datum/outfit/job/hos::glasses + suit = /obj/item/clothing/suit/armor/hos/hos_formal + suit_store = /obj/item/gun/ballistic/shotgun/automatic/combat/compact + gloves = /obj/item/clothing/gloves/tackler/combat + belt = /obj/item/gun/energy/e_gun/hos + r_hand = /obj/item/melee/baton/security/loaded + l_hand = /obj/item/shield/riot/tele + l_pocket = /obj/item/grenade/flashbang + r_pocket = /obj/item/restraints/legcuffs/bola/energy + +/datum/outfit/deathmatch_loadout/captain + name = "Deathmatch: Captain" + display_name = "Captain" + desc = "Draw your sword and show the syndicate scum no quarter." + + head = /obj/item/clothing/head/hats/caphat/parade + ears = /obj/item/radio/headset/heads/captain/alt + uniform = /obj/item/clothing/under/rank/captain + suit = /obj/item/clothing/suit/armor/vest/capcarapace/captains_formal + suit_store = /obj/item/gun/energy/e_gun + shoes = /obj/item/clothing/shoes/laceup + neck = /obj/item/bedsheet/captain + glasses = /obj/item/clothing/glasses/sunglasses + gloves = /obj/item/clothing/gloves/captain + belt = /obj/item/storage/belt/sabre + l_hand = /obj/item/gun/energy/laser/captain + r_pocket = /obj/item/assembly/flash + l_pocket = /obj/item/melee/baton/telescopic + +/datum/outfit/deathmatch_loadout/traitor + name = "Deathmatch: Traitor" + display_name = "Traitor" + desc = "The classic; energy sword & energy bow, donning a reflector trenchcoat (stolen)." + + head = /obj/item/clothing/head/chameleon + uniform = /obj/item/clothing/under/chameleon + mask = /obj/item/clothing/mask/chameleon + suit = /obj/item/clothing/suit/hooded/ablative + shoes = /obj/item/clothing/shoes/chameleon/noslip + glasses = /obj/item/clothing/glasses/thermal/syndi + gloves = /obj/item/clothing/gloves/combat + suit_store = /obj/item/gun/energy/recharge/ebow + l_hand = /obj/item/melee/energy/sword + r_pocket = /obj/item/reagent_containers/hypospray/medipen/stimulants + l_pocket = /obj/item/soap/syndie + belt = /obj/item/gun/ballistic/revolver/syndicate + +/datum/outfit/deathmatch_loadout/nukie + name = "Deathmatch: Nuclear Operative" + display_name = "Nuclear Operative" + desc = "Gear afforded to Lone Operatives. Your mission is simple." + + uniform = /obj/item/clothing/under/syndicate/tacticool + back = /obj/item/mod/control/pre_equipped/nuclear + r_hand = /obj/item/gun/ballistic/shotgun/bulldog/unrestricted + belt = /obj/item/gun/ballistic/automatic/pistol/clandestine + r_pocket = /obj/item/reagent_containers/hypospray/medipen/stimulants + l_pocket = /obj/item/grenade/syndieminibomb + implants = list(/obj/item/implant/explosive) + + backpack_contents = list( + /obj/item/ammo_box/c10mm, + /obj/item/ammo_box/magazine/m12g = 2, + /obj/item/pen/edagger, + /obj/item/reagent_containers/hypospray/medipen/atropine, + ) + +/datum/outfit/deathmatch_loadout/pete + name = "Deathmatch: Cuban Pete" + display_name = "Disciple of Pete" + desc = "You took a lesson from Cuban Pete." + + back = /obj/item/storage/backpack/santabag + head = /obj/item/clothing/head/collectable/petehat + uniform = /obj/item/clothing/under/pants/camo + suit = /obj/item/clothing/suit/costume/poncho + belt = /obj/item/storage/belt/grenade/full + shoes = /obj/item/clothing/shoes/workboots + l_hand = /obj/item/reagent_containers/cup/glass/bottle/rum + r_hand = /obj/item/sbeacondrop/bomb + l_pocket = /obj/item/grenade/syndieminibomb + r_pocket = /obj/item/grenade/syndieminibomb + implants = list(/obj/item/implanter/explosive_macro) + backpack_contents = list( + /obj/item/assembly/signaler = 10, + ) + +/datum/outfit/deathmatch_loadout/tider + name = "Deathmatch: Tider" + display_name = "Tider" + desc = "A very high power level Assistant." + + back = /obj/item/melee/baton/security/cattleprod + r_hand = /obj/item/fireaxe + uniform = /obj/item/clothing/under/color/grey/ancient + mask = /obj/item/clothing/mask/gas + shoes = /obj/item/clothing/shoes/sneakers/black + gloves = /obj/item/clothing/gloves/cut + l_pocket = /obj/item/reagent_containers/hypospray/medipen/methamphetamine + r_pocket = /obj/item/stock_parts/cell/high + belt = /obj/item/storage/belt/utility/full + +/datum/outfit/deathmatch_loadout/abductor + name = "Deathmatch: Abductor" + display_name = "Abductor" + desc = "We come in peace." + + species_override = /datum/species/abductor + uniform = /obj/item/clothing/under/abductor + head = /obj/item/clothing/head/helmet/abductor + suit = /obj/item/clothing/suit/armor/abductor/vest + l_pocket = /obj/item/reagent_containers/hypospray/medipen/atropine + r_pocket = /obj/item/grenade/gluon + l_hand = /obj/item/gun/energy/alien + r_hand = /obj/item/gun/energy/alien + belt = /obj/item/gun/energy/shrink_ray + +/datum/outfit/deathmatch_loadout/battler/clown/upgraded + name = "Deathmatch: Clown (Syndicate Gear)" + display_name = "Clown" + desc = "They were bound to show up sooner or later." + + shoes = /obj/item/clothing/shoes/clown_shoes/combat + r_hand = /obj/item/pneumatic_cannon/pie/selfcharge + l_hand = /obj/item/bikehorn/golden + box = /obj/item/storage/box/hug/reverse_revolver + + backpack_contents = list( + /obj/item/paperplane/syndicate = 1, + /obj/item/restraints/legcuffs/bola/tactical = 1, + /obj/item/restraints/legcuffs/beartrap = 1, + /obj/item/food/grown/banana = 1, + /obj/item/food/pie/cream = 1, + /obj/item/dnainjector/clumsymut, + /obj/item/sbeacondrop/clownbomb, + ) + +/datum/outfit/deathmatch_loadout/mime + name = "Deathmatch: Mime" + display_name = "Mime" + desc = "..." + + uniform = /datum/outfit/job/mime::uniform + belt = /obj/item/food/baguette/combat + head = /datum/outfit/job/mime::head + shoes = /datum/outfit/job/mime::shoes + mask = /datum/outfit/job/mime::mask + back = /datum/outfit/job/mime::backpack + box = /datum/outfit/job/mime::box + l_pocket = /obj/item/toy/crayon/spraycan/mimecan + r_pocket = /obj/item/food/grown/banana/mime + neck = /datum/outfit/job/mime::neck + gloves = /datum/outfit/job/mime::gloves + + backpack_contents = list( + /obj/item/reagent_containers/cup/glass/bottle/bottleofnothing, + /obj/item/gun/ballistic/automatic/pistol, + /obj/item/suppressor, + /obj/item/ammo_box/c9mm, + /obj/item/food/croissant/throwing = 2, + ) + + granted_spells = list( + /datum/action/cooldown/spell/vow_of_silence, + /datum/action/cooldown/spell/conjure_item/invisible_box, + /datum/action/cooldown/spell/conjure/invisible_chair, + /datum/action/cooldown/spell/conjure/invisible_wall, + /datum/action/cooldown/spell/forcewall/mime, + /datum/action/cooldown/spell/pointed/projectile/finger_guns, + ) + +/datum/outfit/deathmatch_loadout/chef/upgraded + name = "Deathmatch: Master Chef" + display_name = "Chef" + desc = "Let him cook." + + belt = /obj/item/gun/magic/hook + uniform = /obj/item/clothing/under/costume/buttondown/slacks/service + suit = /obj/item/clothing/suit/toggle/chef + suit_store = /obj/item/knife/kitchen + head = /obj/item/clothing/head/utility/chefhat + mask = /obj/item/clothing/mask/fakemoustache/italian + gloves = /obj/item/clothing/gloves/the_sleeping_carp + back = /obj/item/storage/backpack + + backpack_contents = list( + /obj/item/pizzabox/bomb/armed = 3, + /obj/item/knife/butcher, + /obj/item/sharpener, + ) diff --git a/code/modules/deathmatch/deathmatch_lobby.dm b/code/modules/deathmatch/deathmatch_lobby.dm index 918808126c69c..028653a2f2124 100644 --- a/code/modules/deathmatch/deathmatch_lobby.dm +++ b/code/modules/deathmatch/deathmatch_lobby.dm @@ -491,12 +491,10 @@ return TRUE var/datum/deathmatch_modifier/chosen_modifier = GLOB.deathmatch_game.modifiers[modpath] if(modpath in modifiers) - chosen_modifier.unselect(src) - modifiers -= modpath + unselect_modifier(chosen_modifier) return TRUE if(chosen_modifier.selectable(src)) - chosen_modifier.on_select(src) - modifiers += modpath + select_modifier(chosen_modifier) return TRUE if ("admin") // Admin functions @@ -511,6 +509,15 @@ return FALSE +/// Selects the passed modifier. +/datum/deathmatch_lobby/proc/select_modifier(datum/deathmatch_modifier/modifier) + modifier.on_select(src) + modifiers += modifier.type + +/// Deselects the passed modifier. +/datum/deathmatch_lobby/proc/unselect_modifier(datum/deathmatch_modifier/modifier) + modifier.unselect(src) + modifiers -= modifier.type /datum/deathmatch_lobby/ui_close(mob/user) . = ..() diff --git a/code/modules/deathmatch/deathmatch_maps.dm b/code/modules/deathmatch/deathmatch_maps.dm index b2396915473c6..6a8a245abb795 100644 --- a/code/modules/deathmatch/deathmatch_maps.dm +++ b/code/modules/deathmatch/deathmatch_maps.dm @@ -185,5 +185,24 @@ key = "train" turf_reservation_type = /datum/turf_reservation/indestructible_plating +/datum/lazy_template/deathmatch/finaldestination + name = "Final Destination" + desc = "1v1v1v1, 1 Stock, Final Destination." + max_players = 8 + allowed_loadouts = list( + /datum/outfit/deathmatch_loadout/captain, + /datum/outfit/deathmatch_loadout/head_of_security, + /datum/outfit/deathmatch_loadout/traitor, + /datum/outfit/deathmatch_loadout/nukie, + /datum/outfit/deathmatch_loadout/tider, + /datum/outfit/deathmatch_loadout/abductor, + /datum/outfit/deathmatch_loadout/chef/upgraded, + /datum/outfit/deathmatch_loadout/battler/clown/upgraded, + /datum/outfit/deathmatch_loadout/mime, + /datum/outfit/deathmatch_loadout/pete, + ) + map_name = "finaldestination" + key = "finaldestination" + /datum/turf_reservation/indestructible_plating turf_type = /turf/open/indestructible/plating //a little hacky but i guess it has to be done diff --git a/code/modules/deathmatch/deathmatch_modifier.dm b/code/modules/deathmatch/deathmatch_modifier.dm index ec5c20cf919b1..dadca49d70a4f 100644 --- a/code/modules/deathmatch/deathmatch_modifier.dm +++ b/code/modules/deathmatch/deathmatch_modifier.dm @@ -1,14 +1,16 @@ ///Deathmatch modifiers are little options the host can choose to spice the match a bit. /datum/deathmatch_modifier - ///The name of the modifier + /// The name of the modifier var/name = "Unnamed Modifier" - ///A small description/tooltip shown in the UI + /// A small description/tooltip shown in the UI var/description = "What the heck does this do?" - ///The color of the button shown in the UI + /// The color of the button shown in the UI var/color = "blue" - ///A list of modifiers this is incompatible with. - var/list/blacklisted_modifiers - ///Is this trait exempted from the "Random Modifiers" modifier. + /// A lazylist of modifier typepaths this is incompatible with. + var/list/datum/deathmatch_modifier/blacklisted_modifiers + /// A lazylist of map typepaths this is incomptable with. + var/list/datum/lazy_template/deathmatch/blacklisted_maps + /// Is this trait exempted from the "Random Modifiers" modifier. var/random_exempted = FALSE ///Whether or not this modifier can be selected, for both host and player-selected modifiers. @@ -18,11 +20,20 @@ return FALSE if(length(lobby.modifiers & blacklisted_modifiers)) return FALSE + if (map_incompatible(lobby.map)) + return FALSE for(var/modpath in lobby.modifiers) if(src in GLOB.deathmatch_game.modifiers[modpath].blacklisted_modifiers) return FALSE return TRUE +/// Returns TRUE if map.type is in our blacklisted maps, FALSE otherwise. +/datum/deathmatch_modifier/proc/map_incompatible(datum/lazy_template/deathmatch/map) + if (map?.type in blacklisted_maps) + return TRUE + + return FALSE + ///Called when selecting the deathmatch modifier. /datum/deathmatch_modifier/proc/on_select(datum/deathmatch_lobby/lobby) return @@ -31,9 +42,12 @@ /datum/deathmatch_modifier/proc/unselect(datum/deathmatch_lobby/lobby) return -///Called when the host chooses to change map. +///Called when the host chooses to change map. Returns FALSE if the new map is incompatible, TRUE otherwise. /datum/deathmatch_modifier/proc/on_map_changed(datum/deathmatch_lobby/lobby) - return + if (map_incompatible(lobby.map)) + lobby.unselect_modifier(src) + return FALSE + return TRUE ///Called as the game is about to start. /datum/deathmatch_modifier/proc/on_start_game(datum/deathmatch_lobby/lobby) @@ -533,7 +547,7 @@ /datum/deathmatch_modifier/any_loadout/on_map_changed(datum/deathmatch_lobby/lobby) if(lobby.loadouts == GLOB.deathmatch_game.loadouts) //This arena already allows any loadout for some reason. - lobby.modifiers -= type + lobby.unselect_modifier(src) else lobby.loadouts = GLOB.deathmatch_game.loadouts @@ -554,3 +568,33 @@ return SSquirks.AssignQuirks(player, player.client) + +/datum/deathmatch_modifier/martial_artistry + name = "Random martial arts" + description = "Everyone learns a random martial art!" + blacklisted_maps = list(/datum/lazy_template/deathmatch/meatower) + // krav maga excluded because its too common and too simple, mushpunch excluded because its horrible and not even funny + var/static/list/weighted_martial_arts = list( + // common + /datum/martial_art/cqc = 30, + /datum/martial_art/the_sleeping_carp = 30, + // uncommon + /datum/martial_art/boxing/evil = 20, + // LEGENDARY + /datum/martial_art/plasma_fist = 5, + /datum/martial_art/wrestling = 5, // wrestling is kinda strong ngl + /datum/martial_art/psychotic_brawling = 5, // a complete meme. sometimes you just get hardstunned. sometimes you punch someone across the room + ) + +/datum/deathmatch_modifier/martial_artistry/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby) + . = ..() + + var/datum/martial_art/picked_art_path = pick_weight(weighted_martial_arts) + var/datum/martial_art/instantiated_art = new picked_art_path() + + if (istype(instantiated_art, /datum/martial_art/boxing)) + player.mind.adjust_experience(/datum/skill/athletics, SKILL_EXP_LEGENDARY) + + instantiated_art.teach(player) + + to_chat(player, span_revenboldnotice("Your martial art is [uppertext(instantiated_art.name)]!")) diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm index ad54347332a82..4b7886b02dad2 100644 --- a/code/modules/events/fake_virus.dm +++ b/code/modules/events/fake_virus.dm @@ -32,4 +32,4 @@ if(prob(25))//1/4 odds to get a spooky message instead of coughing out loud addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), onecoughman, span_warning("[pick("Your head hurts.", "Your head pounds.")]")), rand(3 SECONDS, 15 SECONDS)) else - addtimer(CALLBACK(onecoughman, TYPE_PROC_REF(/mob, emote), pick("cough", "sniff", "sneeze")), rand(3 SECONDS, 15 SECONDS))//deliver the message with a slightly randomized time interval so there arent multiple people coughing at the exact same time + addtimer(CALLBACK(onecoughman, TYPE_PROC_REF(/mob, emote), pick("cough", "sniff")), rand(3 SECONDS, 15 SECONDS))//deliver the message with a slightly randomized time interval so there arent multiple people coughing at the exact same time diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm index de458bb218736..8a6b3258a0ef7 100644 --- a/code/modules/food_and_drinks/machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/machinery/smartfridge.dm @@ -173,7 +173,7 @@ tool_tip_set = TRUE else if(held_item.tool_behaviour == TOOL_WRENCH) - context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchore" + context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Una" : "A"]nchor" tool_tip_set = TRUE return tool_tip_set ? CONTEXTUAL_SCREENTIP_SET : NONE diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm index 7b562bca670e6..13a3496c609d3 100644 --- a/code/modules/jobs/job_types/janitor.dm +++ b/code/modules/jobs/job_types/janitor.dm @@ -41,6 +41,7 @@ belt = /obj/item/modular_computer/pda/janitor ears = /obj/item/radio/headset/headset_srv skillchips = list(/obj/item/skillchip/job/janitor) + backpack_contents = list(/obj/item/access_key) /datum/outfit/job/janitor/pre_equip(mob/living/carbon/human/human_equipper, visuals_only) . = ..() @@ -48,11 +49,6 @@ backpack_contents += list(/obj/item/gun/ballistic/revolver) r_pocket = /obj/item/ammo_box/a357 - var/static/access_key_given = FALSE - if(!access_key_given && !visuals_only) - access_key_given = TRUE - backpack_contents += list(/obj/item/access_key) - /datum/outfit/job/janitor/get_types_to_preload() . = ..() if(check_holidays(GARBAGEDAY)) diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm index 19dbc76a32f76..3bb1fb48e599a 100644 --- a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm +++ b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm @@ -160,6 +160,8 @@ var/first_open = FALSE /// was a shovel used to close this grave var/dug_closed = FALSE + /// do we have a mood effect tied to accessing this type of grave? + var/affect_mood = FALSE /obj/structure/closet/crate/grave/add_context(atom/source, list/context, obj/item/held_item, mob/user) if(isnull(held_item)) @@ -180,6 +182,9 @@ . = ..() . += span_notice("It can be [EXAMINE_HINT((opened ? "closed" : "dug open"))] with a shovel.") +/obj/structure/closet/crate/grave/filled + affect_mood = TRUE + /obj/structure/closet/crate/grave/filled/PopulateContents() //GRAVEROBBING IS NOW A FEATURE ..() new /obj/effect/decal/remains/human(src) @@ -252,7 +257,7 @@ if(opened) dug_closed = TRUE close(user) - else if(open(user, force = TRUE)) + else if(open(user, force = TRUE) && affect_mood) if(HAS_MIND_TRAIT(user, TRAIT_MORBID)) user.add_mood_event("morbid_graverobbing", /datum/mood_event/morbid_graverobbing) else diff --git a/code/modules/mining/lavaland/tendril_loot.dm b/code/modules/mining/lavaland/tendril_loot.dm index 7433b2b55daaa..6dfc8cfe40c3a 100644 --- a/code/modules/mining/lavaland/tendril_loot.dm +++ b/code/modules/mining/lavaland/tendril_loot.dm @@ -661,8 +661,8 @@ name = "berserker armor" desc = "This hulking armor seems to possess some kind of dark force within; howling in rage, hungry for carnage. \ The self-sealing stem bolts that allowed this suit to be spaceworthy have long since corroded. However, the entity \ - sealed within the suit seems to hunger for the fleeting lifeforce found in the remains left within drake armor. \ - Feeding it seems to empower a suit piece, though turns the drake armor back to lifeless ash." + sealed within the suit seems to hunger for the fleeting lifeforce found in the remains left in the remains of drakes. \ + Feeding it drake remains seems to empower a suit piece, though turns the remains back to lifeless ash." icon_state = "berserker" icon = 'icons/obj/clothing/suits/armor.dmi' worn_icon = 'icons/mob/clothing/suits/armor.dmi' @@ -697,7 +697,7 @@ /obj/item/clothing/suit/hooded/berserker/Initialize(mapload) . = ..() AddComponent(/datum/component/anti_magic, ALL, inventory_flags = ITEM_SLOT_OCLOTHING) - AddComponent(/datum/component/armor_plate, maxamount = 1, upgrade_item = /obj/item/clothing/suit/hooded/cloak/drake, armor_mod = /datum/armor/drake_empowerment, upgrade_prefix = "empowered") + AddComponent(/datum/component/armor_plate, maxamount = 1, upgrade_item = /obj/item/drake_remains, armor_mod = /datum/armor/drake_empowerment, upgrade_prefix = "empowered") allowed = GLOB.mining_suit_allowed #define MAX_BERSERK_CHARGE 100 @@ -710,8 +710,8 @@ name = "berserker helmet" desc = "This burdensome helmet seems to possess some kind of dark force within; howling in rage, hungry for carnage. \ The self-sealing stem bolts that allowed this helmet to be spaceworthy have long since corroded. However, the entity \ - sealed within the suit seems to hunger for the fleeting lifeforce found in the remains left within drake armor. \ - Feeding it seems to empower a suit piece, though turns the drake armor back to lifeless ash." + sealed within the suit seems to hunger for the fleeting lifeforce found in the remains left in the remains of drakes. \ + Feeding it drake remains seems to empower a suit piece, though turns the remains back to lifeless ash." icon_state = "berserker" icon = 'icons/obj/clothing/head/helmet.dmi' worn_icon = 'icons/mob/clothing/head/helmet.dmi' @@ -732,7 +732,7 @@ /obj/item/clothing/head/hooded/berserker/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_NODROP, LOCKED_HELMET_TRAIT) - AddComponent(/datum/component/armor_plate, maxamount = 1, upgrade_item = /obj/item/clothing/suit/hooded/cloak/drake, armor_mod = /datum/armor/drake_empowerment, upgrade_prefix = "empowered") + AddComponent(/datum/component/armor_plate, maxamount = 1, upgrade_item = /obj/item/drake_remains, armor_mod = /datum/armor/drake_empowerment, upgrade_prefix = "empowered") /obj/item/clothing/head/hooded/berserker/examine() . = ..() @@ -800,6 +800,16 @@ #undef CHARGE_DRAINED_PER_SECOND #undef BERSERK_ATTACK_SPEED_MODIFIER +/obj/item/drake_remains + name = "drake remains" + desc = "The gathered remains of a drake. It still crackles with heat, and smells distinctly of brimstone." + icon = 'icons/obj/clothing/head/helmet.dmi' + icon_state = "dragon" + +/obj/item/drake_remains/Initialize(mapload) + . = ..() + particles = new /particles/bonfire() + /obj/item/clothing/glasses/godeye name = "eye of god" desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index c7c4b1ed06f3e..6d1ad16c8b1c8 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -33,7 +33,7 @@ switch(stage) if(3, 4) if(SPT_PROB(1, seconds_per_tick)) - owner.emote("sneeze") + owner.sneeze() if(SPT_PROB(1, seconds_per_tick)) owner.emote("cough") if(SPT_PROB(1, seconds_per_tick)) @@ -42,7 +42,7 @@ to_chat(owner, span_danger("Mucous runs down the back of your throat.")) if(5) if(SPT_PROB(1, seconds_per_tick)) - owner.emote("sneeze") + owner.sneeze() if(SPT_PROB(1, seconds_per_tick)) owner.emote("cough") if(SPT_PROB(2, seconds_per_tick)) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 4caaac92bd533..da2c057d740bb 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -315,6 +315,8 @@ clone.fully_replace_character_name(null, dna.real_name) copy_clothing_prefs(clone) clone.age = age + clone.voice = voice + clone.pitch = pitch dna.transfer_identity(clone, transfer_SE = TRUE, transfer_species = TRUE) clone.dress_up_as_job(SSjob.GetJob(job)) diff --git a/code/modules/mob/living/carbon/human/monkey.dm b/code/modules/mob/living/carbon/human/monkey.dm index 7a2e7bb74747d..e63b35ab42af4 100644 --- a/code/modules/mob/living/carbon/human/monkey.dm +++ b/code/modules/mob/living/carbon/human/monkey.dm @@ -4,6 +4,7 @@ ai_controller = /datum/ai_controller/monkey /mob/living/carbon/human/species/monkey/Initialize(mapload, cubespawned = FALSE, mob/spawner) + ADD_TRAIT(src, TRAIT_BORN_MONKEY, INNATE_TRAIT) if (cubespawned) var/cap = CONFIG_GET(number/monkeycap) if (LAZYLEN(SSmobs.cubemonkeys) > cap) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index dfd9afeec4516..8354ff0447461 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -42,7 +42,7 @@ var/obj/machinery/ai_voicechanger/ai_voicechanger = null // reference to machine that holds the voicechanger var/malfhacking = FALSE // More or less a copy of the above var, so that malf AIs can hack and still get new cyborgs -- NeoFite /// List of hacked APCs - var/list/hacked_apcs = list() + var/list/hacked_apcs = list() var/malf_cooldown = 0 //Cooldown var for malf modules, stores a worldtime + cooldown var/obj/machinery/power/apc/malfhack @@ -333,7 +333,7 @@ else if(!connected_robot.cell || connected_robot.cell.charge <= 0) robot_status = "DEPOWERED" //Name, Health, Battery, Model, Area, and Status! Everything an AI wants to know about its borgies! - . += "[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[connected_robot.cell.charge]/[connected_robot.cell.maxcharge]" : "Empty"] | \ + . += "[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[display_energy(connected_robot.cell.charge)]/[display_energy(connected_robot.cell.maxcharge)]" : "Empty"] | \ Model: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | Status: [robot_status]" . += "AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]" //Count of total AI shells diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 3fa78b9dda155..6fefbb05f7c0d 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -952,7 +952,9 @@ SIGNAL_HANDLER if(model) - model.respawn_consumable(src, cell.use(cell.charge * 0.005)) + if(cell.charge) + if(model.respawn_consumable(src, cell.charge * 0.005)) + cell.use(cell.charge * 0.005) if(sendmats) model.restock_consumable() if(repairs) diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm index 5ba061233d442..1721d6ec2c102 100644 --- a/code/modules/mob/living/silicon/robot/robot_model.dm +++ b/code/modules/mob/living/silicon/robot/robot_model.dm @@ -82,6 +82,8 @@ for(var/module in get_usable_modules()) if(!(module in cyborg.held_items)) . += module + if(!cyborg.emagged) + . += emag_modules /obj/item/robot_model/proc/add_module(obj/item/added_module, nonstandard, requires_rebuild) if(isstack(added_module)) @@ -121,44 +123,57 @@ var/active_module = cyborg.module_active cyborg.drop_all_held_items() modules = list() - for(var/obj/item/module in basic_modules) + for(var/obj/item/module as anything in basic_modules) add_module(module, FALSE, FALSE) if(cyborg.emagged) - for(var/obj/item/module in emag_modules) + for(var/obj/item/module as anything in emag_modules) add_module(module, FALSE, FALSE) - for(var/obj/item/module in added_modules) + for(var/obj/item/module as anything in added_modules) add_module(module, FALSE, FALSE) - for(var/module in held_modules) - if(module) - cyborg.equip_module_to_slot(module, held_modules.Find(module)) + for(var/obj/item/module as anything in held_modules & modules) + cyborg.equip_module_to_slot(module, held_modules.Find(module)) if(active_module) cyborg.select_module(held_modules.Find(active_module)) if(cyborg.hud_used) cyborg.hud_used.update_robot_modules_display() + +///Restocks things that don't take mats, generally at a power cost. Returns True if anything was restocked/replaced, and False otherwise. /obj/item/robot_model/proc/respawn_consumable(mob/living/silicon/robot/cyborg, coeff = 1) SHOULD_CALL_PARENT(TRUE) + ///If anything was actually replaced/refilled/recharged. If not, we won't draw power. + . = FALSE + for(var/datum/robot_energy_storage/storage_datum in storages) if(storage_datum.renewable == FALSE) continue - storage_datum.energy = min(storage_datum.max_energy, storage_datum.energy + coeff * storage_datum.recharge_rate) + if(storage_datum.energy < storage_datum.max_energy) + . = TRUE + storage_datum.energy = min(storage_datum.max_energy, storage_datum.energy + coeff * storage_datum.recharge_rate) for(var/obj/item/module in get_usable_modules()) if(istype(module, /obj/item/assembly/flash)) var/obj/item/assembly/flash/flash = module + if(flash.burnt_out) + . = TRUE flash.times_used = 0 flash.burnt_out = FALSE flash.update_appearance() else if(istype(module, /obj/item/melee/baton/security)) var/obj/item/melee/baton/security/baton = module - baton.cell?.charge = baton.cell.maxcharge + if(baton.cell?.charge < baton.cell.maxcharge) + . = TRUE //if sec borgs ever make a mainstream return, we should probably do this differntly. + baton.cell?.charge = baton.cell.maxcharge else if(istype(module, /obj/item/gun/energy)) var/obj/item/gun/energy/gun = module if(!gun.chambered) + . = TRUE gun.recharge_newshot() //try to reload a new shot. - cyborg.toner = cyborg.tonermax + if(cyborg.toner < cyborg.tonermax) + . = TRUE + cyborg.toner = cyborg.tonermax /** * Refills consumables that require materials, rather than being given for free. @@ -349,6 +364,7 @@ if(!soap) return if(soap.uses < initial(soap.uses)) + . = TRUE soap.uses += ROUND_UP(initial(soap.uses) / 100) * coeff /obj/item/robot_model/engineering @@ -626,20 +642,29 @@ ..() var/obj/item/lightreplacer/light_replacer = locate(/obj/item/lightreplacer) in basic_modules if(light_replacer) - light_replacer.Charge(cyborg, coeff) + if(light_replacer.uses < light_replacer.max_uses) + . = TRUE + light_replacer.Charge(cyborg, coeff) var/obj/item/reagent_containers/spray/cyborg_drying/drying_agent = locate(/obj/item/reagent_containers/spray/cyborg_drying) in basic_modules if(drying_agent) - drying_agent.reagents.add_reagent(/datum/reagent/drying_agent, 5 * coeff) + var/datum/reagents/anti_water = drying_agent.reagents + if(anti_water.total_volume < anti_water.maximum_volume) + . = TRUE + drying_agent.reagents.add_reagent(/datum/reagent/drying_agent, 5 * coeff) var/obj/item/reagent_containers/spray/cyborg_lube/lube = locate(/obj/item/reagent_containers/spray/cyborg_lube) in emag_modules if(lube) - lube.reagents.add_reagent(/datum/reagent/lube, 2 * coeff) + var/datum/reagents/anti_friction = lube.reagents + if(anti_friction.total_volume < anti_friction.maximum_volume) + . = TRUE + lube.reagents.add_reagent(/datum/reagent/lube, 2 * coeff) var/obj/item/soap/nanotrasen/cyborg/soap = locate(/obj/item/soap/nanotrasen/cyborg) in basic_modules if(!soap) return if(soap.uses < initial(soap.uses)) + . = TRUE soap.uses += ROUND_UP(initial(soap.uses) / 100) * coeff /obj/item/robot_model/medical @@ -759,6 +784,7 @@ var/obj/item/gun/energy/e_gun/advtaser/cyborg/taser = locate(/obj/item/gun/energy/e_gun/advtaser/cyborg) in basic_modules if(taser) if(taser.cell.charge < taser.cell.maxcharge) + . = TRUE var/obj/item/ammo_casing/energy/shot = taser.ammo_type[taser.select] taser.cell.give(shot.e_cost * coeff) taser.update_appearance() @@ -810,7 +836,10 @@ ..() var/obj/item/reagent_containers/enzyme = locate(/obj/item/reagent_containers/condiment/enzyme) in basic_modules if(enzyme) - enzyme.reagents.add_reagent(/datum/reagent/consumable/enzyme, 2 * coeff) + var/datum/reagents/spicyketchup = enzyme.reagents + if(spicyketchup.total_volume < spicyketchup.maximum_volume) + . = TRUE + enzyme.reagents.add_reagent(/datum/reagent/consumable/enzyme, 2 * coeff) /obj/item/robot_model/syndicate name = "Syndicate Assault" diff --git a/code/modules/mob/living/sneeze.dm b/code/modules/mob/living/sneeze.dm new file mode 100644 index 0000000000000..b2cf76c25a6b0 --- /dev/null +++ b/code/modules/mob/living/sneeze.dm @@ -0,0 +1,75 @@ +/// How many degrees, up and down, can our sneeze deviate from our facing direction? +#define SNEEZE_CONE 60 + +/// Launch a sneeze that can infect with a disease +/mob/living/proc/infectious_sneeze(datum/disease/disease, force, range = 4, count = 4, charge_time = 0.5 SECONDS, obj/projectile/sneezoid = /obj/projectile/sneeze) + sneeze(range, count, charge_time, sneezoid, on_sneeze_hit_callback = CALLBACK(src, PROC_REF(try_sneeze_infect), disease.Copy(), force)) + +/// Try and infect following a sneeze hit. force to always infect +/mob/living/proc/try_sneeze_infect(datum/disease/disease, force, mob/living/target) + target.contract_airborne_disease(disease) + +/// Inhale and start the sneeze timer. on_sneeze_callback can be used to do custom sneezes, on_sneeze_hit_callback for special effects, but probably usually making it infect +/mob/living/proc/sneeze(range = 4, count = 3, charge_time = 0.5 SECONDS, obj/projectile/sneezoid = /obj/projectile/sneeze, on_sneeze_callback = null, on_sneeze_hit_callback = null) + if(charge_time) + emote("inhale") + + clear_fullscreen("sneezer", 0) + var/atom/movable/screen/fullscreen/cursor_catcher/catcher = overlay_fullscreen("sneezer", /atom/movable/screen/fullscreen/cursor_catcher, FALSE) + if(client) + catcher.assign_to_mob(src) + var/callback = on_sneeze_callback || CALLBACK(src, PROC_REF(launch_sneeze), range, count, sneezoid, on_sneeze_hit_callback, catcher) + addtimer(callback, charge_time) + +/// Shoot the sneeze projectile +/mob/living/proc/launch_sneeze(range, count, obj/projectile/sneezoid, datum/callback/on_sneeze_hit_callback, atom/movable/screen/fullscreen/cursor_catcher/catcher) + emote("sneeze") + + var/angle = dir2angle(dir) + + if(catcher && catcher.given_turf) + catcher.calculate_params() + /// Take the target and subtract self for relative grid position. Then take the pixel x on the tile and divide by the tiles pixel size, and add 0.5 so it's fired from the center + var/sneeze_x = catcher.given_turf.x - x + catcher.given_x / world.icon_size - 0.5 + var/sneeze_y = catcher.given_turf.y - y + catcher.given_y / world.icon_size - 0.5 + angle = ATAN2(sneeze_y, sneeze_x) + + // Check if we're within the sneeze cone, otherwise just sneeze straight + if(abs(closer_angle_difference(angle, dir2angle(dir) - SNEEZE_CONE)) + abs(closer_angle_difference(angle, dir2angle(dir) + SNEEZE_CONE)) > 2 * SNEEZE_CONE) + angle = dir2angle(dir) + + clear_fullscreen("sneezer", 0) + + for(var/i in 0 to count) + var/obj/projectile/sneezium = new sneezoid(get_turf(src), on_sneeze_hit_callback) + sneezium.range = range + sneezium.firer = src + sneezium.fire(angle) + +/// Sneeze projectile launched by sneezing. gross +/obj/projectile/sneeze + name = "sneeze" + icon_state = "sneeze" + + suppressed = SUPPRESSED_VERY + range = 4 + speed = 4 + spread = 40 + damage_type = BRUTE + damage = 0 + + /// Call this when we hit something + var/datum/callback/sneezie_callback + +/obj/projectile/sneeze/Initialize(mapload, callback) + . = ..() + + sneezie_callback = callback + +/obj/projectile/sneeze/on_hit(atom/target, blocked, pierce_hit) + . = ..() + + if(isliving(target)) + sneezie_callback?.Invoke(target) //you've been sneezered + +#undef SNEEZE_CONE diff --git a/code/modules/pai/software.dm b/code/modules/pai/software.dm index 9876df5a2646a..ab69e69388ccd 100644 --- a/code/modules/pai/software.dm +++ b/code/modules/pai/software.dm @@ -131,12 +131,17 @@ /** * Changes the image displayed on the pAI. * - * @param {mob} user - The user who is changing the image. - * * @returns {boolean} - TRUE if the image was changed, FALSE otherwise. */ /mob/living/silicon/pai/proc/change_image() - var/new_image = tgui_input_list(src, "Select your new display image", "Display Image", possible_overlays) + var/list/possible_choices = list() + for(var/face_option in possible_overlays) + var/datum/radial_menu_choice/choice = new + choice.name = face_option + choice.image = image(icon = card.icon, icon_state = "pai-[face_option]") + possible_choices[face_option] += choice + var/atom/anchor = get_atom_on_turf(src) + var/new_image = show_radial_menu(src, anchor, possible_choices, custom_check = CALLBACK(src, PROC_REF(check_menu), anchor), radius = 40, require_near = TRUE) if(isnull(new_image)) return FALSE card.emotion_icon = new_image diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm index 1a0ce1dc37b80..1eae74d242cef 100644 --- a/code/modules/paperwork/stamps.dm +++ b/code/modules/paperwork/stamps.dm @@ -88,6 +88,11 @@ icon_state = "stamp-clown" dye_color = DYE_CLOWN +/obj/item/stamp/clown/Initialize(mapload) + . = ..() + + AddElement(/datum/element/swabable, CELL_LINE_TABLE_CLOWN, CELL_VIRUS_TABLE_GENERIC, rand(2,3), 0) + /obj/item/stamp/mime name = "mime's rubber stamp" icon_state = "stamp-mime" diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 85f3283ca43d1..a06f6954bf374 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -239,14 +239,13 @@ update_appearance() return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN -/obj/item/gun/ballistic/shotgun/bulldog/alt_click_secondary(mob/user) +/obj/item/gun/ballistic/shotgun/bulldog/click_alt_secondary(mob/user) if(secondary_magazine) var/obj/item/ammo_box/magazine/old_mag = secondary_magazine secondary_magazine = null user.put_in_hands(old_mag) update_appearance() playsound(src, load_empty_sound, load_sound_volume, load_sound_vary) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/item/gun/ballistic/shotgun/bulldog/proc/toggle_magazine() var/primary_magazine = magazine diff --git a/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm b/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm index e11910a13afce..e43d32c661bd9 100644 --- a/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm +++ b/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm @@ -434,13 +434,10 @@ replace_beaker(user, TRUE) return CLICK_ACTION_SUCCESS -/obj/machinery/chem_mass_spec/alt_click_secondary(mob/living/user) - . = ..() - if(!can_interact(user)) - return +/obj/machinery/chem_mass_spec/click_alt_secondary(mob/living/user) if(processing_reagents) balloon_alert(user, "still processing!") - return ..() + return replace_beaker(user, FALSE) /obj/machinery/chem_mass_spec/process(seconds_per_tick) diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index f753f2f1c325c..d3070474558d7 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -358,34 +358,42 @@ description = "Coffee and ice, refreshing and cool." color = "#102838" // rgb: 16, 40, 56 nutriment_factor = 0 + overdose_threshold = 80 taste_description = "bitter coldness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED metabolized_traits = list(TRAIT_STIMULATED) +/datum/reagent/consumable/icecoffee/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) + /datum/reagent/consumable/icecoffee/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() affected_mob.adjust_dizzy(-10 SECONDS * REM * seconds_per_tick) affected_mob.adjust_drowsiness(-6 SECONDS * REM * seconds_per_tick) affected_mob.AdjustSleeping(-40 * REM * seconds_per_tick) affected_mob.adjust_bodytemperature(-5 * REM * TEMPERATURE_DAMAGE_COEFFICIENT * seconds_per_tick, affected_mob.get_body_temp_normal()) - affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) /datum/reagent/consumable/hot_ice_coffee name = "Hot Ice Coffee" description = "Coffee with pulsing ice shards" color = "#102838" // rgb: 16, 40, 56 nutriment_factor = 0 + overdose_threshold = 80 taste_description = "bitter coldness and a hint of smoke" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED metabolized_traits = list(TRAIT_STIMULATED) +/datum/reagent/consumable/hot_ice_coffee/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) + /datum/reagent/consumable/hot_ice_coffee/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() affected_mob.adjust_dizzy(-10 SECONDS * REM * seconds_per_tick) affected_mob.adjust_drowsiness(-6 SECONDS * REM * seconds_per_tick) affected_mob.AdjustSleeping(-60 * REM * seconds_per_tick) affected_mob.adjust_bodytemperature(-7 * REM * TEMPERATURE_DAMAGE_COEFFICIENT * seconds_per_tick, affected_mob.get_body_temp_normal()) - affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) if(affected_mob.adjustToxLoss(1 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype)) return UPDATE_MOB_HEALTH @@ -701,12 +709,17 @@ name = "Soy Latte" description = "A nice and tasty beverage while you are reading your hippie books." color = "#cc6404" // rgb: 204,100,4 + overdose_threshold = 80 quality = DRINK_NICE taste_description = "creamy coffee" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED glass_price = DRINK_PRICE_EASY metabolized_traits = list(TRAIT_STIMULATED) +/datum/reagent/consumable/soy_latte/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) + /datum/reagent/consumable/soy_latte/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() affected_mob.adjust_dizzy(-10 SECONDS * REM * seconds_per_tick) @@ -714,7 +727,6 @@ var/need_mob_update need_mob_update = affected_mob.SetSleeping(0) affected_mob.adjust_bodytemperature(5 * REM * TEMPERATURE_DAMAGE_COEFFICIENT * seconds_per_tick, 0, affected_mob.get_body_temp_normal()) - affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) if(affected_mob.getBruteLoss() && SPT_PROB(10, seconds_per_tick)) need_mob_update += affected_mob.heal_bodypart_damage(brute = 1 * REM * seconds_per_tick, burn = 0, updating_health = FALSE) if(need_mob_update) @@ -724,12 +736,17 @@ name = "Cafe Latte" description = "A nice, strong and tasty beverage while you are reading." color = "#cc6404" // rgb: 204,100,4 + overdose_threshold = 80 quality = DRINK_NICE taste_description = "bitter cream" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED glass_price = DRINK_PRICE_EASY metabolized_traits = list(TRAIT_STIMULATED) +/datum/reagent/consumable/cafe_latte/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) + /datum/reagent/consumable/cafe_latte/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() affected_mob.adjust_dizzy(-10 SECONDS * REM * seconds_per_tick) @@ -737,7 +754,6 @@ var/need_mob_update need_mob_update = affected_mob.SetSleeping(0) affected_mob.adjust_bodytemperature(5 * REM * TEMPERATURE_DAMAGE_COEFFICIENT * seconds_per_tick, 0, affected_mob.get_body_temp_normal()) - affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) if(affected_mob.getBruteLoss() && SPT_PROB(10, seconds_per_tick)) need_mob_update += affected_mob.heal_bodypart_damage(brute = 1 * REM * seconds_per_tick, burn = 0, updating_health = FALSE) if(need_mob_update) @@ -851,12 +867,29 @@ name = "Pumpkin Latte" description = "A mix of pumpkin juice and coffee." color = "#F4A460" + overdose_threshold = 80 quality = DRINK_VERYGOOD nutriment_factor = 3 taste_description = "creamy pumpkin" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED metabolized_traits = list(TRAIT_STIMULATED) +/datum/reagent/consumable/pumpkin_latte/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.set_jitter_if_lower(10 SECONDS * REM * seconds_per_tick) + +/datum/reagent/consumable/pumpkin_latte/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.adjust_dizzy(-10 SECONDS * REM * seconds_per_tick) + affected_mob.adjust_drowsiness(-6 SECONDS * REM * seconds_per_tick) + var/need_mob_update + need_mob_update = affected_mob.SetSleeping(0) + affected_mob.adjust_bodytemperature(5 * REM * TEMPERATURE_DAMAGE_COEFFICIENT * seconds_per_tick, 0, affected_mob.get_body_temp_normal()) + if(affected_mob.getBruteLoss() && SPT_PROB(10, seconds_per_tick)) + need_mob_update += affected_mob.heal_bodypart_damage(brute = 1 * REM * seconds_per_tick, burn = 0, updating_health = FALSE) + if(need_mob_update) + return UPDATE_MOB_HEALTH + /datum/reagent/consumable/gibbfloats name = "Gibb Floats" description = "Ice cream on top of a Dr. Gibb glass." @@ -1033,7 +1066,7 @@ affected_mob.update_transform(newsize/current_size) current_size = newsize if(SPT_PROB(23, seconds_per_tick)) - affected_mob.emote("sneeze") + affected_mob.sneeze() /datum/reagent/consumable/red_queen/on_mob_end_metabolize(mob/living/affected_mob) . = ..() diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index be9f32073157f..40513f930bfba 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -1086,6 +1086,18 @@ ph = 2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/medicine/mutadone/on_mob_metabolize(mob/living/affected_mob) + . = ..() + if (!ishuman(affected_mob)) + return + var/mob/living/carbon/human/human_mob = affected_mob + if (ismonkey(human_mob)) + if (!HAS_TRAIT(human_mob, TRAIT_BORN_MONKEY)) + human_mob.dna.remove_mutation(/datum/mutation/human/race) + else if (HAS_TRAIT(human_mob, TRAIT_BORN_MONKEY)) + human_mob.monkeyize() + + /datum/reagent/medicine/mutadone/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() affected_mob.remove_status_effect(/datum/status_effect/jitter) diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index bcdee284bd78b..15508aa98eecd 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -604,7 +604,7 @@ if(2) affected_mob.emote("cough") if(3) - affected_mob.emote("sneeze") + affected_mob.sneeze() if(4) if(prob(75)) to_chat(affected_mob, span_danger("You scratch at an itch.")) diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm index cc6a45a5f1f13..16ccedaf778b8 100644 --- a/code/modules/research/machinery/_production.dm +++ b/code/modules/research/machinery/_production.dm @@ -384,11 +384,14 @@ if(!directly_use_energy(charge_per_item)) // provide the wait time until lathe is ready var/area/my_area = get_area(src) var/obj/machinery/power/apc/my_apc = my_area.apc - var/charging_wait = my_apc.time_to_charge(charge_per_item) - if(!isnull(charging_wait)) - say("Unable to continue production, APC overload. Wait [DisplayTimeText(charging_wait, round_seconds_to = 1)] and try again.") + if(!QDELETED(my_apc)) + var/charging_wait = my_apc.time_to_charge(charge_per_item) + if(!isnull(charging_wait)) + say("Unable to continue production, APC overload. Wait [DisplayTimeText(charging_wait, round_seconds_to = 1)] and try again.") + else + say("Unable to continue production, power grid overload.") else - say("Unable to continue production, power grid overload.") + say("Unable to continue production, no APC in area.") finalize_build() return diff --git a/code/modules/spells/spell_types/conjure/invisible_wall.dm b/code/modules/spells/spell_types/conjure/invisible_wall.dm index a61db7cf74e19..d2812912f0fc2 100644 --- a/code/modules/spells/spell_types/conjure/invisible_wall.dm +++ b/code/modules/spells/spell_types/conjure/invisible_wall.dm @@ -15,7 +15,7 @@ invocation_self_message = span_notice("You form a wall in front of yourself.") invocation_type = INVOCATION_EMOTE - spell_requirements = SPELL_REQUIRES_HUMAN|SPELL_REQUIRES_MIME_VOW + spell_requirements = SPELL_REQUIRES_MIME_VOW antimagic_flags = NONE spell_max_level = 1 diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm index da3fe3353035c..23f897bce95a2 100644 --- a/code/modules/surgery/organs/external/wings/functional_wings.dm +++ b/code/modules/surgery/organs/external/wings/functional_wings.dm @@ -8,7 +8,7 @@ /datum/action/innate/flight/Activate() var/mob/living/carbon/human/human = owner var/obj/item/organ/external/wings/functional/wings = human.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS) - if(wings && wings.can_fly(human)) + if(wings?.can_fly(human)) wings.toggle_flight(human) if(!(human.movement_type & FLYING)) to_chat(human, span_notice("You settle gently back onto the ground...")) @@ -29,23 +29,26 @@ // grind_results = list(/datum/reagent/flightpotion = 5) food_reagents = list(/datum/reagent/flightpotion = 5) +/obj/item/organ/external/wings/functional/Destroy() + QDEL_NULL(fly) + return ..() + /obj/item/organ/external/wings/functional/Insert(mob/living/carbon/receiver, special, movement_flags) . = ..() - if(. && isnull(fly)) + if(!.) + return + if(QDELETED(fly)) fly = new - fly.Grant(receiver) + fly.Grant(receiver) /obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() - - fly.Remove(organ_owner) - + fly?.Remove(organ_owner) if(wings_open) toggle_flight(organ_owner) /obj/item/organ/external/wings/functional/on_life(seconds_per_tick, times_fired) . = ..() - handle_flight(owner) ///Called on_life(). Handle flight code and check if we're still flying diff --git a/code/modules/transport/tram/tram_signals.dm b/code/modules/transport/tram/tram_signals.dm index eb64866603043..db8aa17ddcb3e 100644 --- a/code/modules/transport/tram/tram_signals.dm +++ b/code/modules/transport/tram/tram_signals.dm @@ -17,7 +17,7 @@ interaction_flags_machine = INTERACT_MACHINE_OPEN circuit = /obj/item/circuitboard/machine/crossing_signal // pointless if it only takes 2 seconds to cross but updates every 2 seconds - subsystem_type = /datum/controller/subsystem/processing/fastprocess + subsystem_type = /datum/controller/subsystem/processing/transport light_color = LIGHT_COLOR_BABY_BLUE /// green, amber, or red for tram, blue if it's emag, tram missing, etc. var/signal_state = XING_STATE_MALF @@ -40,8 +40,8 @@ * Red: decent chance of getting hit, but if you're quick it's a decent gamble. * Amber: slow people may be in danger. */ - var/amber_distance_threshold = AMBER_THRESHOLD_NORMAL - var/red_distance_threshold = RED_THRESHOLD_NORMAL + var/amber_distance_threshold = XING_THRESHOLD_AMBER + var/red_distance_threshold = XING_THRESHOLD_RED /** Crossing signal subtypes * @@ -203,34 +203,18 @@ sensor_ref = null if(operating_status < TRANSPORT_REMOTE_WARNING) operating_status = TRANSPORT_REMOTE_WARNING - degraded_response() update_appearance() /obj/machinery/transport/crossing_signal/proc/wake_sensor() - if(operating_status > TRANSPORT_REMOTE_WARNING) - degraded_response() - return - var/obj/machinery/transport/guideway_sensor/linked_sensor = sensor_ref?.resolve() if(isnull(linked_sensor)) operating_status = TRANSPORT_REMOTE_WARNING - degraded_response() else if(linked_sensor.trigger_sensor()) operating_status = TRANSPORT_SYSTEM_NORMAL - normal_response() else operating_status = TRANSPORT_REMOTE_WARNING - degraded_response() - -/obj/machinery/transport/crossing_signal/proc/normal_response() - amber_distance_threshold = AMBER_THRESHOLD_NORMAL - red_distance_threshold = RED_THRESHOLD_NORMAL - -/obj/machinery/transport/crossing_signal/proc/degraded_response() - amber_distance_threshold = AMBER_THRESHOLD_DEGRADED - red_distance_threshold = RED_THRESHOLD_DEGRADED /obj/machinery/transport/crossing_signal/proc/clear_uplink() inbound = null @@ -316,20 +300,23 @@ end_processing() /obj/machinery/transport/crossing_signal/process() - + // idle aspect is green or blue depending on the signal status + // degraded signal operating conditions of any type show blue + var/idle_aspect = operating_status == TRANSPORT_SYSTEM_NORMAL ? XING_STATE_GREEN : XING_STATE_MALF var/datum/transport_controller/linear/tram/tram = transport_ref?.resolve() - // Check for stopped states. - if(!tram || !tram.controller_operational || !is_operational || !inbound || !outbound) + // Check for stopped states. Will kill the process since tram starting up will restart process. + if(!tram || !tram.controller_operational || !tram.controller_active || !is_operational || !inbound || !outbound) // Tram missing, we lost power, or something isn't right - // Throw the error message (blue) - set_signal_state(XING_STATE_MALF, force = !is_operational) + // Set idle and stop processing, since the tram won't be moving + set_signal_state(idle_aspect, force = !is_operational) return PROCESS_KILL var/obj/structure/transport/linear/tram_part = tram.return_closest_platform_to(src) + // The structure is gone, so we're done here. if(QDELETED(tram_part)) - set_signal_state(XING_STATE_MALF, force = !is_operational) + set_signal_state(idle_aspect, force = !is_operational) return PROCESS_KILL // Everything will be based on position and travel direction @@ -347,41 +334,32 @@ tram_velocity_sign = tram.travel_direction & EAST ? 1 : -1 // How far away are we? negative if already passed. - var/approach_distance = tram_velocity_sign * (signal_pos - (tram_pos + (DEFAULT_TRAM_LENGTH * 0.5))) - - // Check for stopped state. - // Will kill the process since tram starting up will restart process. - if(!tram.controller_active) - set_signal_state(XING_STATE_GREEN) - return PROCESS_KILL + var/approach_distance = tram_velocity_sign * (signal_pos - (tram_pos + DEFAULT_TRAM_MIDPOINT)) // Check if tram is driving away from us. - if(approach_distance < 0) + if(approach_distance < -abs(DEFAULT_TRAM_MIDPOINT)) // driving away. Green. In fact, in order to reverse, it'll have to stop, so let's go ahead and kill. - set_signal_state(XING_STATE_GREEN) + set_signal_state(idle_aspect) return PROCESS_KILL // Check the tram's terminus station. // INBOUND 1 < 2 < 3 // OUTBOUND 1 > 2 > 3 if(tram.travel_direction & WEST && inbound < tram.destination_platform.platform_code) - set_signal_state(XING_STATE_GREEN) + set_signal_state(idle_aspect) return PROCESS_KILL if(tram.travel_direction & EAST && outbound > tram.destination_platform.platform_code) - set_signal_state(XING_STATE_GREEN) + set_signal_state(idle_aspect) return PROCESS_KILL // Finally the interesting part where it's ACTUALLY approaching if(approach_distance <= red_distance_threshold) - if(operating_status != TRANSPORT_SYSTEM_NORMAL) - set_signal_state(XING_STATE_MALF) - else - set_signal_state(XING_STATE_RED) + set_signal_state(XING_STATE_RED) return - if(approach_distance <= amber_distance_threshold) + if(approach_distance <= amber_distance_threshold && operating_status == TRANSPORT_SYSTEM_NORMAL) set_signal_state(XING_STATE_AMBER) return - set_signal_state(XING_STATE_GREEN) + set_signal_state(idle_aspect) /** * Set the signal state and update appearance. diff --git a/code/modules/vehicles/mecha/combat/gygax.dm b/code/modules/vehicles/mecha/combat/gygax.dm index 82fd77f22890d..0284f10d23d43 100644 --- a/code/modules/vehicles/mecha/combat/gygax.dm +++ b/code/modules/vehicles/mecha/combat/gygax.dm @@ -34,7 +34,7 @@ acid = 100 /obj/vehicle/sealed/mecha/gygax/dark - desc = "A lightweight exosuit, painted in a dark scheme. This model appears to have some modifications." + desc = "A lightweight exosuit, painted in a dark scheme. This model's armor has been upgraded with a cutting-edge armor composite, resulting in greater protection and performance at the cost of modularity." name = "\improper Dark Gygax" ui_theme = "syndicate" icon_state = "darkgygax" @@ -53,21 +53,21 @@ MECHA_R_ARM = 1, MECHA_UTILITY = 4, MECHA_POWER = 1, - MECHA_ARMOR = 3, + MECHA_ARMOR = 0, ) equip_by_category = list( MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, MECHA_R_ARM = null, MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/radio, /obj/item/mecha_parts/mecha_equipment/air_tank/full, /obj/item/mecha_parts/mecha_equipment/thrusters/ion), MECHA_POWER = list(), - MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/anticcw_armor_booster, /obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + MECHA_ARMOR = list(), ) destruction_sleep_duration = 20 /datum/armor/gygax_dark - melee = 40 - bullet = 40 - laser = 50 + melee = 70 + bullet = 50 + laser = 55 energy = 35 bomb = 20 fire = 100 diff --git a/code/modules/vehicles/mecha/combat/marauder.dm b/code/modules/vehicles/mecha/combat/marauder.dm index 750223a85d7ad..3cc73214fcac1 100644 --- a/code/modules/vehicles/mecha/combat/marauder.dm +++ b/code/modules/vehicles/mecha/combat/marauder.dm @@ -1,5 +1,5 @@ /obj/vehicle/sealed/mecha/marauder - desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations." + desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations. Its bleeding edge armour ensures maximum usability and protection at the cost of some modularity." name = "\improper Marauder" icon_state = "marauder" base_icon_state = "marauder" @@ -20,16 +20,16 @@ MECHA_R_ARM = 1, MECHA_UTILITY = 5, MECHA_POWER = 1, - MECHA_ARMOR = 3, + MECHA_ARMOR = 0, ) bumpsmash = TRUE /datum/armor/mecha_marauder - melee = 50 - bullet = 55 - laser = 40 + melee = 70 + bullet = 60 + laser = 60 energy = 30 - bomb = 30 + bomb = 50 fire = 100 acid = 100 @@ -44,7 +44,7 @@ MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack, MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/radio, /obj/item/mecha_parts/mecha_equipment/air_tank/full, /obj/item/mecha_parts/mecha_equipment/thrusters/ion), MECHA_POWER = list(), - MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + MECHA_ARMOR = list(), ) /obj/vehicle/sealed/mecha/marauder/loaded/populate_parts() @@ -92,6 +92,7 @@ accesses = list(ACCESS_CENT_SPECOPS) movedelay = 3 max_integrity = 550 + armor_type = /datum/armor/mecha_seraph wreckage = /obj/structure/mecha_wreckage/seraph force = 55 max_equip_by_category = list( @@ -99,22 +100,33 @@ MECHA_R_ARM = 1, MECHA_UTILITY = 5, MECHA_POWER = 1, - MECHA_ARMOR = 3, + MECHA_ARMOR = 0, ) equip_by_category = list( MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse, MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack, MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/radio, /obj/item/mecha_parts/mecha_equipment/air_tank/full, /obj/item/mecha_parts/mecha_equipment/thrusters/ion), MECHA_POWER = list(), - MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + MECHA_ARMOR = list(), ) +/datum/armor/mecha_seraph + melee = 80 + bullet = 65 + laser = 65 + energy = 50 + bomb = 50 + fire = 100 + acid = 100 + + /obj/vehicle/sealed/mecha/marauder/mauler - desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model." + desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model, its hardened exterior prevents the use of add-on armor packages." name = "\improper Mauler" ui_theme = "syndicate" icon_state = "mauler" base_icon_state = "mauler" + armor_type = /datum/armor/mecha_mauler accesses = list(ACCESS_SYNDICATE) wreckage = /obj/structure/mecha_wreckage/mauler mecha_flags = ID_LOCK_ON | CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE @@ -123,7 +135,7 @@ MECHA_R_ARM = 1, MECHA_UTILITY = 4, MECHA_POWER = 1, - MECHA_ARMOR = 4, + MECHA_ARMOR = 0, ) equip_by_category = list( MECHA_L_ARM = null, @@ -134,13 +146,22 @@ ) destruction_sleep_duration = 20 +/datum/armor/mecha_mauler + melee = 80 + bullet = 60 + laser = 50 + energy = 30 + bomb = 50 + fire = 100 + acid = 100 + /obj/vehicle/sealed/mecha/marauder/mauler/loaded equip_by_category = list( MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg, MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack, MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/radio, /obj/item/mecha_parts/mecha_equipment/air_tank/full, /obj/item/mecha_parts/mecha_equipment/thrusters/ion), MECHA_POWER = list(), - MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/antiproj_armor_booster), + MECHA_ARMOR = list(), ) /obj/vehicle/sealed/mecha/marauder/mauler/loaded/Initialize(mapload) diff --git a/config/spaceruinblacklist.txt b/config/spaceruinblacklist.txt index dcff6527bae80..c33d85b54abc9 100644 --- a/config/spaceruinblacklist.txt +++ b/config/spaceruinblacklist.txt @@ -14,7 +14,6 @@ #_maps/RandomRuins/SpaceRuins/asteroid5.dmm #_maps/RandomRuins/SpaceRuins/asteroid6.dmm #_maps/RandomRuins/SpaceRuins/atmosasteroidruin.dmm -#_maps/RandomRuins/SpaceRuins/atmosasteroidruin.dmm #_maps/RandomRuins/SpaceRuins/bigderelict1.dmm #_maps/RandomRuins/SpaceRuins/botanical_haven.dmm #_maps/RandomRuins/SpaceRuins/bus.dmm @@ -23,7 +22,7 @@ #_maps/RandomRuins/SpaceRuins/clownplanet.dmm #_maps/RandomRuins/SpaceRuins/crashedclownship.dmm #_maps/RandomRuins/SpaceRuins/crashedship.dmm -#_maps/RandomRuins/SpaceRuins/dangerous_research.dmm +#_maps/RandomRuins/SpaceRuins/cyborg_mothership.dmm #_maps/RandomRuins/SpaceRuins/dangerous_research.dmm #_maps/RandomRuins/SpaceRuins/deepstorage.dmm #_maps/RandomRuins/SpaceRuins/derelict_construction.dmm @@ -40,23 +39,22 @@ #_maps/RandomRuins/SpaceRuins/emptyshell.dmm #_maps/RandomRuins/SpaceRuins/fasttravel.dmm #_maps/RandomRuins/SpaceRuins/forgottenship.dmm -#_maps/RandomRuins/SpaceRuins/forgottenship.dmm #_maps/RandomRuins/SpaceRuins/garbagetruck1.dmm #_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm #_maps/RandomRuins/SpaceRuins/garbagetruck3.dmm #_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm #_maps/RandomRuins/SpaceRuins/gondolaasteroid.dmm #_maps/RandomRuins/SpaceRuins/hellfactory.dmm -#_maps/RandomRuins/SpaceRuins/hellfactory.dmm -#_maps/RandomRuins/SpaceRuins/hilbertshoteltestingsite.dmm +#_maps/RandomRuins/SpaceRuins/hilbertsresearchfacility.dmm #_maps/RandomRuins/SpaceRuins/infested_frigate.dmm #_maps/RandomRuins/SpaceRuins/intactemptyship.dmm #_maps/RandomRuins/SpaceRuins/interdyne.dmm #_maps/RandomRuins/SpaceRuins/listeningstation.dmm +#_maps/RandomRuins/SpaceRuins/meatderelict.dmm #_maps/RandomRuins/SpaceRuins/meateor.dmm #_maps/RandomRuins/SpaceRuins/mechtransport.dmm #_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm -#_maps/RandomRuins/SpaceRuins/mrow_thats_right +#_maps/RandomRuins/SpaceRuins/mrow_thats_right.dmm #_maps/RandomRuins/SpaceRuins/old_infiltrator.dmm #_maps/RandomRuins/SpaceRuins/oldAIsat.dmm #_maps/RandomRuins/SpaceRuins/oldstation.dmm @@ -66,23 +64,18 @@ #_maps/RandomRuins/SpaceRuins/phonebooth.dmm #_maps/RandomRuins/SpaceRuins/pod_crash.dmm #_maps/RandomRuins/SpaceRuins/prey_pod.dmm -#_maps/RandomRuins/SpaceRuins/prey_pod.dmm #_maps/RandomRuins/SpaceRuins/prison_shuttle.dmm #_maps/RandomRuins/SpaceRuins/russian_derelict.dmm #_maps/RandomRuins/SpaceRuins/shuttlerelic.dmm #_maps/RandomRuins/SpaceRuins/space_billboard.dmm -#_maps/RandomRuins/SpaceRuins/space_billboard.dmm #_maps/RandomRuins/SpaceRuins/space_ghost_restaurant.dmm #_maps/RandomRuins/SpaceRuins/spacehotel.dmm #_maps/RandomRuins/SpaceRuins/spinwardsmoothies.dmm -#_maps/RandomRuins/SpaceRuins/spinwardsmoothies.dmm -#_maps/RandomRuins/SpaceRuins/the_faceoff.dmm #_maps/RandomRuins/SpaceRuins/the_faceoff.dmm #_maps/RandomRuins/SpaceRuins/the_outlet.dmm #_maps/RandomRuins/SpaceRuins/thelizardsgas.dmm #_maps/RandomRuins/SpaceRuins/transit_booth.dmm #_maps/RandomRuins/SpaceRuins/travelers_rest.dmm -#_maps/RandomRuins/SpaceRuins/travelers_rest.dmm #_maps/RandomRuins/SpaceRuins/turretedoutpost.dmm #_maps/RandomRuins/SpaceRuins/vaporwave.dmm #_maps/RandomRuins/SpaceRuins/way_home.dmm diff --git a/html/changelogs/AutoChangeLog-pr-81920.yml b/html/changelogs/AutoChangeLog-pr-81920.yml deleted file mode 100644 index dcf10c410e455..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81920.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Ben10Omintrix" -delete-after: True -changes: - - refactor: "honkbots are now basic mobs, please report any bugs" - - rscadd: "honkbots will try to slip people on banana peels" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83471.yml b/html/changelogs/AutoChangeLog-pr-83471.yml deleted file mode 100644 index b1e825dec9b24..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-83471.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Jacquerel" -delete-after: True -changes: - - balance: "Corrosive slime left behind after a slime fails to eat you can be scraped off with your hands, or shaken off in some other way, by clicking on the debuff. This is slower and less effective than washing it off using water." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83487.yml b/html/changelogs/AutoChangeLog-pr-83487.yml deleted file mode 100644 index c7407a21f5207..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-83487.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - balance: "cultist shades can no longer contribute to rune invocation until they've been out of their soulstone for a minute. Put them in a shell for God's sake!" - - code_imp: "sweeps up cultist antag datum code into its own subfolder." - - code_imp: "cult shades now have their own antag datum." - - bugfix: "constructs now properly clear the cultist antag datum and transfer the mind slightly earlier." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83629.yml b/html/changelogs/AutoChangeLog-pr-83629.yml deleted file mode 100644 index da5ccb97bc66b..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-83629.yml +++ /dev/null @@ -1,10 +0,0 @@ -author: "SyncIt21" -delete-after: True -changes: - - qol: "more examines & screentips for plumbing machinery" - - qol: "plumbing grinder has a grind/juice mode which be toggled by hand" - - code_imp: "improved attack chain for RPLD & plumbing grinder" - - bugfix: "You can deconstruct the plumbing grinder with the RPLD" - - bugfix: "You can attack the plumbing grinder with any item in combat mode without getting that item consumed" - - bugfix: "You cannot grind abstract/ hologram items in the plumbing grinder" - - bugfix: "growing vat now uses the correct layer selected on rapid plumbing device" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83659.yml b/html/changelogs/AutoChangeLog-pr-83659.yml deleted file mode 100644 index 8daf3a629a664..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-83659.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "mc-oofert" -delete-after: True -changes: - - balance: "cult stun gets weaker when they get red eyes and later more when they have halos" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83697.yml b/html/changelogs/AutoChangeLog-pr-83697.yml deleted file mode 100644 index 2d14759bc4c7f..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-83697.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "LT3" -delete-after: True -changes: - - balance: "Doubled number of assignable wildcard slots on grey ID cards" - - balance: "Doubled number of assignable wildcard slots on silver ID cards" - - balance: "Doubled number of assignable wildcard slots on agent/chameleon ID cards" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83698.yml b/html/changelogs/AutoChangeLog-pr-83698.yml deleted file mode 100644 index 56ded989cb45e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-83698.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Jacquerel" -delete-after: True -changes: - - rscadd: "A unique kind of mob is created when a Monkey is infested by a Legion." \ No newline at end of file diff --git a/html/changelogs/archive/2024-06.yml b/html/changelogs/archive/2024-06.yml index a2b1e0f705674..60046e053bd8e 100644 --- a/html/changelogs/archive/2024-06.yml +++ b/html/changelogs/archive/2024-06.yml @@ -229,3 +229,148 @@ sprites. vendingmachine24: - spellcheck: Changed grammar in cat_house.dm +2024-06-06: + Ben10Omintrix: + - refactor: honkbots are now basic mobs, please report any bugs + - rscadd: honkbots will try to slip people on banana peels + Jacquerel: + - balance: Mutadone restores your originally monkey status, rather than always turning + monkeys into humans + - rscadd: A unique kind of mob is created when a Monkey is infested by a Legion. + - balance: Corrosive slime left behind after a slime fails to eat you can be scraped + off with your hands, or shaken off in some other way, by clicking on the debuff. + This is slower and less effective than washing it off using water. + LT3: + - balance: Doubled number of assignable wildcard slots on grey ID cards + - balance: Doubled number of assignable wildcard slots on silver ID cards + - balance: Doubled number of assignable wildcard slots on agent/chameleon ID cards + Rhials: + - balance: cultist shades can no longer contribute to rune invocation until they've + been out of their soulstone for a minute. Put them in a shell for God's sake! + - code_imp: sweeps up cultist antag datum code into its own subfolder. + - code_imp: cult shades now have their own antag datum. + - bugfix: constructs now properly clear the cultist antag datum and transfer the + mind slightly earlier. + SyncIt21: + - qol: more examines & screentips for plumbing machinery + - qol: plumbing grinder has a grind/juice mode which be toggled by hand + - code_imp: improved attack chain for RPLD & plumbing grinder + - bugfix: You can deconstruct the plumbing grinder with the RPLD + - bugfix: You can attack the plumbing grinder with any item in combat mode without + getting that item consumed + - bugfix: You cannot grind abstract/ hologram items in the plumbing grinder + - bugfix: growing vat now uses the correct layer selected on rapid plumbing device + mc-oofert: + - balance: cult stun gets weaker when they get red eyes and later more when they + have halos +2024-06-07: + 00-Steven: + - bugfix: RLD glowsticks actually glow again. + Absolucy: + - bugfix: When implanting functional wings into a new body, they will actually be + able to use said wings now. + EEASAS: + - bugfix: fixed the mis-rotated disposals pipe on birdshot + Erol509: + - qol: Cyborgs on AI statpanel now have jules energy format. + EuSouAFazer: + - qol: 'Swabbing the clown''s stamp also gives clown cells. + + :cl:' + IsaacExists: + - rscadd: You may choose a color preference for your blindfold with the blindness + quirk. + LT3: + - bugfix: Fixed timing issue where tram crossing signals would be out of sync with + the moving tram + - bugfix: Tram crossing signals consistently show green when safe, blue when broken + - bugfix: Tram crossing signals show red instead of yellow when degraded + Pickle-Coding: + - bugfix: Fixes the brig cell timer adjustment not working correctly on live timers. + - bugfix: Fixes paradox clones using a different voice from the owner. + RedBaronFlyer: + - rscadd: Janitorial keyrings are now part of every janitor's toolkit instead of + just the first one. + Sadboysuss: + - bugfix: curator whip will no longer disarm when parried + - qol: you now don't need to weld a closet to install/uninstall electronics/card + readers. + ShizCalev: + - bugfix: Syndicate AI can no longer be selected via the station law upload console. + - bugfix: Non-syndicate borgs can no longer accidentally be slaved to syndicate + AI by pulsing their AI wires. + - bugfix: Syndicate borgs can no longer be slaved to the station AI by pulsing their + AI wires. + - bugfix: Syndicate operatives onboard the station can no longer end up with an + objective to destroy their own syndicate AI. + SyncIt21: + - bugfix: autolathes don't hang when printing items in areas without apc or if it + runs out of materials mid printing + Wayland-Smithy: + - bugfix: Pre-Loaded Syndicate Intellicard AI's no longer appear in PDA messenger. + Whoneedspacee: + - bugfix: Bubblegum can no longer melee you while using his charge abilities. + githubuser4141: + - balance: Antag/Centcom mechs now have top notch un-upgradeable armor out of the + box. You can't add armor to centcom or nukie mechs anymore, but their default + armor rating is a lot higher. + necromanceranne: + - bugfix: The armor plate component only adds the prefix once. + - bugfix: Drake empowerment for berserker armor now uses valuable drake remains, + made from ashdrake hides and bones. + - rscadd: Drake armor is made use drake remains to construct. (This is a net neutral + to the previous recipe) + - qol: Coffee types are overall more consistent, causing jittering only from the + overdose effect. + - bugfix: Pumpkin lattes will actually work like coffee. + nikothedude: + - bugfix: COMSIG_ATOM_POST_DIR_CHANGE should ACTUALLY work now + san7890: + - bugfix: Using the 'ESC' key on your keyboard to unbind a key in the keybindings + preferences menu should now work as expected. This should also be fixed for + people in a variety of other spots too. + zxaber: + - bugfix: 'Tool-based flashes (read: from welders) are no longer incorrectly locked + at flash level 1. Wear proper PPE!' +2024-06-08: + 00-Steven: + - refactor: Refactored wall button code, please report any issues. + - bugfix: Wall buttons actually drop their contents when destroyed. + - sound: Putting items into wall buttons actually plays a sound. This matters for + assembly devices, but airlock electronics do not have a sound. + - qol: Added screentips to wall buttons. + - qol: You can now take out the airlock electronics or assembly device out of wall + buttons individually. Left click prioritises the board, right click prioritises + the device. + - qol: Wall buttons are renamable with a pen when opened. + - qol: Attempting to wrench deconstruct a wall button or put in airlock electronics + or an assembly device when you can't actually gives feedback. + DaCoolBoss: + - bugfix: Fixed entries in config file 'spaceruinsblacklist.txt'. + EnterTheJake: + - balance: Rust walkers' summoning ritual now requires 5 sheets of Titanium instead + of Iron. + - bugfix: Magic resistance grants complete immunity from the passive disgust buildup + from standing on Rusted turfs. + Melbert: + - bugfix: Fix some modifiers to do after speed (sanity, midas gun) stacking when + they shouldn't + MichiRecRoom: + - qol: Personal AI's face display selection is now a radial menu. As a bonus, now + you can see what the faces look like before selecting them. + Rhials: + - bugfix: Only filled graves will impact your mood. + SyncIt21: + - spellcheck: Corrected wrench contextual screentip typo for smart fridge + - refactor: alt right click has been refactored. report bugs on github + - bugfix: techfabs don't runtime & hang when printing in no apc areas + nikothedude: + - rscadd: 'New deathmatch modifier: "Random martial arts"' + projectkepler-ru: + - bugfix: smoking pipe not showing sprite when lit + thegrb93: + - bugfix: Borg emag module jank when no longer emagged + - bugfix: Borg piercing hypospray fail message + zxaber: + - bugfix: Fixed borg chargers (especially unpowered ones) constantly draining a + borg's cell. diff --git a/icons/obj/weapons/guns/projectiles.dmi b/icons/obj/weapons/guns/projectiles.dmi index b4056f661b8d3..120a4c12cd3d9 100644 Binary files a/icons/obj/weapons/guns/projectiles.dmi and b/icons/obj/weapons/guns/projectiles.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 803e8fadfaa14..ab21c37773105 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3664,6 +3664,7 @@ #include "code\modules\client\preferences\ambient_occlusion.dm" #include "code\modules\client\preferences\assets.dm" #include "code\modules\client\preferences\auto_fit_viewport.dm" +#include "code\modules\client\preferences\blindfold_color.dm" #include "code\modules\client\preferences\body_type.dm" #include "code\modules\client\preferences\broadcast_login_logout.dm" #include "code\modules\client\preferences\clothing.dm" @@ -4617,6 +4618,7 @@ #include "code\modules\mob\living\login.dm" #include "code\modules\mob\living\logout.dm" #include "code\modules\mob\living\navigation.dm" +#include "code\modules\mob\living\sneeze.dm" #include "code\modules\mob\living\status_procs.dm" #include "code\modules\mob\living\taste.dm" #include "code\modules\mob\living\ventcrawling.dm" diff --git a/tgui/packages/common/keys.ts b/tgui/packages/common/keys.ts index 34ac9e1614dde..3e913151707ff 100644 --- a/tgui/packages/common/keys.ts +++ b/tgui/packages/common/keys.ts @@ -5,6 +5,7 @@ * Handles modifier keys (Shift, Alt, Control) and arrow keys. * * For alphabetical keys, use the actual character (e.g. 'a') instead of the key code. + * Don't access Esc or Escape directly, use isEscape() instead * * Something isn't here that you want? Just add it: * @url https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values @@ -16,6 +17,8 @@ * // do something * } * ``` + * + * */ export enum KEY { Alt = 'Alt', @@ -25,6 +28,7 @@ export enum KEY { Down = 'ArrowDown', End = 'End', Enter = 'Enter', + Esc = 'Esc', Escape = 'Escape', Home = 'Home', Insert = 'Insert', @@ -37,3 +41,18 @@ export enum KEY { Tab = 'Tab', Up = 'ArrowUp', } + +/** + * ### isEscape + * + * Checks if the user has hit the 'ESC' key on their keyboard. + * There's a weirdness in BYOND where this could be either the string + * 'Escape' or 'Esc' depending on the browser. This function handles + * both cases. + * + * @param key - the key to check, typically from event.key + * @returns true if key is Escape or Esc, false otherwise + */ +export function isEscape(key: string): boolean { + return key === KEY.Esc || key === KEY.Escape; +} diff --git a/tgui/packages/tgui-say/TguiSay.tsx b/tgui/packages/tgui-say/TguiSay.tsx index 39043a978b8cf..7bc459c7f2e84 100644 --- a/tgui/packages/tgui-say/TguiSay.tsx +++ b/tgui/packages/tgui-say/TguiSay.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { BooleanLike } from 'common/react'; import { Component, createRef, RefObject } from 'react'; import { dragStartHandler } from 'tgui/drag'; @@ -245,9 +245,10 @@ export class TguiSay extends Component<{}, State> { this.handleIncrementChannel(); break; - case KEY.Escape: - this.handleClose(); - break; + default: + if (isEscape(event.key)) { + this.handleClose(); + } } } diff --git a/tgui/packages/tgui/components/Button.tsx b/tgui/packages/tgui/components/Button.tsx index 25b1e78f06177..ec621de621ef0 100644 --- a/tgui/packages/tgui/components/Button.tsx +++ b/tgui/packages/tgui/components/Button.tsx @@ -5,7 +5,7 @@ */ import { Placement } from '@popperjs/core'; -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { BooleanLike, classes } from 'common/react'; import { ChangeEvent, @@ -131,7 +131,7 @@ export const Button = (props: Props) => { } // Refocus layout on pressing escape. - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { event.preventDefault(); } }} @@ -343,7 +343,7 @@ const ButtonInput = (props: InputProps) => { commitResult(event); return; } - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { setInInput(false); } }} diff --git a/tgui/packages/tgui/components/Input.tsx b/tgui/packages/tgui/components/Input.tsx index 36d928ce2151a..9bc48aa809406 100644 --- a/tgui/packages/tgui/components/Input.tsx +++ b/tgui/packages/tgui/components/Input.tsx @@ -4,7 +4,7 @@ * @license MIT */ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { classes } from 'common/react'; import { debounce } from 'common/timer'; import { KeyboardEvent, SyntheticEvent, useEffect, useRef } from 'react'; @@ -127,7 +127,7 @@ export function Input(props: Props) { return; } - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { onEscape?.(event); event.currentTarget.value = toInputValue(value); diff --git a/tgui/packages/tgui/components/NumberInput.tsx b/tgui/packages/tgui/components/NumberInput.tsx index 572b0070bcaa9..892a43eddf3c0 100644 --- a/tgui/packages/tgui/components/NumberInput.tsx +++ b/tgui/packages/tgui/components/NumberInput.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { clamp } from 'common/math'; import { BooleanLike, classes } from 'common/react'; import { @@ -239,7 +239,7 @@ export class NumberInput extends Component { onChange?.(targetValue); onDrag?.(targetValue); } - } else if (event.key === KEY.Escape) { + } else if (isEscape(event.key)) { this.setState({ editing: false, }); diff --git a/tgui/packages/tgui/components/TextArea.tsx b/tgui/packages/tgui/components/TextArea.tsx index 82302b98b2b63..0482229b8fd4b 100644 --- a/tgui/packages/tgui/components/TextArea.tsx +++ b/tgui/packages/tgui/components/TextArea.tsx @@ -5,7 +5,7 @@ * @license MIT */ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { classes } from 'common/react'; import { forwardRef, @@ -82,7 +82,7 @@ export const TextArea = forwardRef( return; } - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { onEscape?.(event); if (selfClear) { event.currentTarget.value = ''; diff --git a/tgui/packages/tgui/interfaces/AlertModal.tsx b/tgui/packages/tgui/interfaces/AlertModal.tsx index 62b6e8bbbc328..5924dc7ae7cf2 100644 --- a/tgui/packages/tgui/interfaces/AlertModal.tsx +++ b/tgui/packages/tgui/interfaces/AlertModal.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { BooleanLike } from 'common/react'; import { KeyboardEvent, useState } from 'react'; @@ -55,9 +55,6 @@ export function AlertModal(props) { case KEY.Enter: act('choose', { choice: buttons[selected] }); return; - case KEY.Escape: - act('cancel'); - return; case KEY.Left: event.preventDefault(); onKey(DIRECTION.Decrement); @@ -67,6 +64,12 @@ export function AlertModal(props) { event.preventDefault(); onKey(DIRECTION.Increment); return; + + default: + if (isEscape(event.key)) { + act('cancel'); + return; + } } } diff --git a/tgui/packages/tgui/interfaces/KeyComboModal.tsx b/tgui/packages/tgui/interfaces/KeyComboModal.tsx index e0b598764f156..0340ae811cd77 100644 --- a/tgui/packages/tgui/interfaces/KeyComboModal.tsx +++ b/tgui/packages/tgui/interfaces/KeyComboModal.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { useState } from 'react'; import { useBackend, useLocalState } from '../backend'; @@ -20,7 +20,7 @@ const isStandardKey = (event: React.KeyboardEvent): boolean => { event.key !== KEY.Alt && event.key !== KEY.Control && event.key !== KEY.Shift && - event.key !== KEY.Escape + !isEscape(event.key) ); }; @@ -97,7 +97,7 @@ export const KeyComboModal = (props) => { if (event.key === KEY.Enter) { act('submit', { entry: input }); } - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { act('cancel'); } return; @@ -109,7 +109,7 @@ export const KeyComboModal = (props) => { setValue(formatKeyboardEvent(event)); setBinding(false); return; - } else if (event.key === KEY.Escape) { + } else if (isEscape(event.key)) { setValue(init_value); setBinding(false); return; diff --git a/tgui/packages/tgui/interfaces/LootPanel/index.tsx b/tgui/packages/tgui/interfaces/LootPanel/index.tsx index bc6330b1806f7..c04306204045c 100644 --- a/tgui/packages/tgui/interfaces/LootPanel/index.tsx +++ b/tgui/packages/tgui/interfaces/LootPanel/index.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { isEscape } from 'common/keys'; import { BooleanLike } from 'common/react'; import { useState } from 'react'; @@ -27,7 +27,7 @@ export function LootPanel(props) { { - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { Byond.sendMessage('close'); } }} diff --git a/tgui/packages/tgui/interfaces/NumberInputModal.tsx b/tgui/packages/tgui/interfaces/NumberInputModal.tsx index 938b24d0c6e07..c7c7b1c5831ca 100644 --- a/tgui/packages/tgui/interfaces/NumberInputModal.tsx +++ b/tgui/packages/tgui/interfaces/NumberInputModal.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { useState } from 'react'; import { useBackend } from '../backend'; @@ -44,7 +44,7 @@ export const NumberInputModal = (props) => { if (event.key === KEY.Enter) { act('submit', { entry: input }); } - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { act('cancel'); } }} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index 32d39c287df94..874095b84eac2 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -1,5 +1,5 @@ import { range, sortBy } from 'common/collections'; -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { Component } from 'react'; import { resolveAsset } from '../../assets'; @@ -42,7 +42,7 @@ const isStandardKey = (event: KeyboardEvent): boolean => { event.key !== KEY.Alt && event.key !== KEY.Control && event.key !== KEY.Shift && - event.key !== KEY.Escape + !isEscape(event.key) ); }; @@ -287,7 +287,7 @@ export class KeybindingsPage extends Component<{}, KeybindingsPageState> { if (isStandardKey(event)) { this.setRebindingHotkey(formatKeyboardEvent(event)); return; - } else if (event.key === KEY.Escape) { + } else if (isEscape(event.key)) { this.setRebindingHotkey(undefined); return; } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/blindfold_color.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/blindfold_color.tsx new file mode 100644 index 0000000000000..8a59ced57a8f9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/blindfold_color.tsx @@ -0,0 +1,6 @@ +import { Feature, FeatureColorInput } from '../base'; + +export const blindfold_color: Feature = { + name: 'Blindfold color', + component: FeatureColorInput, +}; diff --git a/tgui/packages/tgui/interfaces/TextInputModal.tsx b/tgui/packages/tgui/interfaces/TextInputModal.tsx index 52517bcbbfe3b..980fa6db797fb 100644 --- a/tgui/packages/tgui/interfaces/TextInputModal.tsx +++ b/tgui/packages/tgui/interfaces/TextInputModal.tsx @@ -1,4 +1,4 @@ -import { KEY } from 'common/keys'; +import { isEscape, KEY } from 'common/keys'; import { KeyboardEvent, useState } from 'react'; import { useBackend } from '../backend'; @@ -67,7 +67,7 @@ export const TextInputModal = (props) => { ) { act('submit', { entry: input }); } - if (event.key === KEY.Escape) { + if (isEscape(event.key)) { act('cancel'); } }} diff --git a/tgui/tsconfig.json b/tgui/tsconfig.json index 9241d7bc9cbb3..99186312c9491 100644 --- a/tgui/tsconfig.json +++ b/tgui/tsconfig.json @@ -14,7 +14,7 @@ "ScriptHost" ], "module": "ESNext", - "moduleResolution": "Node", + "moduleResolution": "Bundler", "noEmit": true, "resolveJsonModule": true, "skipLibCheck": true,