Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -10444,7 +10444,7 @@
|
||||
/area/maintenance/port/fore)
|
||||
"avV" = (
|
||||
/obj/structure/table/wood,
|
||||
/obj/item/reagent_containers/food/snacks/burger/ghost,
|
||||
/obj/item/ectoplasm,
|
||||
/turf/open/floor/wood,
|
||||
/area/maintenance/port/fore)
|
||||
"avW" = (
|
||||
@@ -25957,7 +25957,7 @@
|
||||
/area/medical/paramedic)
|
||||
"bkd" = (
|
||||
/obj/machinery/camera{
|
||||
c_tag = "Medbay Morgue";
|
||||
c_tag = "Paramedic Disbatch";
|
||||
dir = 8;
|
||||
network = list("ss13","medbay")
|
||||
},
|
||||
@@ -26360,7 +26360,7 @@
|
||||
req_access_txt = "6"
|
||||
},
|
||||
/turf/open/floor/plasteel/dark,
|
||||
/area/medical/morgue)
|
||||
/area/maintenance/department/medical/morgue)
|
||||
"blb" = (
|
||||
/obj/machinery/door/airlock/command{
|
||||
name = "Captain's Quarters";
|
||||
@@ -33974,12 +33974,12 @@
|
||||
},
|
||||
/obj/machinery/bloodbankgen,
|
||||
/obj/machinery/camera{
|
||||
c_tag = "Medbay Recovery Room";
|
||||
c_tag = "Medbay Surgery Storage";
|
||||
dir = 6;
|
||||
network = list("ss13","medbay")
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bCD" = (
|
||||
/obj/machinery/computer/operating,
|
||||
/turf/open/floor/plasteel/white/side,
|
||||
@@ -34354,11 +34354,14 @@
|
||||
/area/engineering/storage/tech)
|
||||
"bDA" = (
|
||||
/obj/structure/disposalpipe/segment,
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
|
||||
dir = 4
|
||||
/obj/structure/cable{
|
||||
icon_state = "4-8"
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bDB" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
|
||||
dir = 4
|
||||
@@ -34366,6 +34369,9 @@
|
||||
/obj/structure/cable{
|
||||
icon_state = "1-4"
|
||||
},
|
||||
/obj/structure/cable{
|
||||
icon_state = "4-8"
|
||||
},
|
||||
/turf/open/floor/plasteel/white/side{
|
||||
dir = 4
|
||||
},
|
||||
@@ -35118,7 +35124,7 @@
|
||||
dir = 4
|
||||
},
|
||||
/turf/closed/wall,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bFn" = (
|
||||
/obj/structure/disposalpipe/segment{
|
||||
dir = 5
|
||||
@@ -35130,12 +35136,12 @@
|
||||
/obj/structure/disposalpipe/segment{
|
||||
dir = 10
|
||||
},
|
||||
/obj/machinery/light/small{
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/decal/cleanable/blood/old,
|
||||
/obj/machinery/airalarm{
|
||||
pixel_y = 23
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bFp" = (
|
||||
/obj/structure/closet/crate/freezer,
|
||||
/obj/item/reagent_containers/blood/random,
|
||||
@@ -35162,8 +35168,11 @@
|
||||
dir = 8;
|
||||
sortType = 6
|
||||
},
|
||||
/obj/machinery/light/small{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bFq" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
|
||||
/obj/effect/turf_decal/tile/yellow,
|
||||
@@ -35187,8 +35196,11 @@
|
||||
/obj/structure/disposalpipe/segment,
|
||||
/obj/structure/closet/crate/freezer/surplus_limbs,
|
||||
/obj/item/reagent_containers/glass/beaker/synthflesh,
|
||||
/obj/machinery/atmospherics/components/unary/vent_pump/on{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bFu" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
|
||||
dir = 4
|
||||
@@ -35222,8 +35234,9 @@
|
||||
"bFx" = (
|
||||
/obj/structure/disposalpipe/segment,
|
||||
/obj/machinery/limbgrower,
|
||||
/obj/machinery/atmospherics/components/unary/vent_scrubber/on,
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bFy" = (
|
||||
/obj/machinery/atmospherics/components/unary/vent_pump/on{
|
||||
dir = 1
|
||||
@@ -35809,7 +35822,7 @@
|
||||
dir = 4
|
||||
},
|
||||
/turf/closed/wall,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bGR" = (
|
||||
/obj/structure/table,
|
||||
/obj/item/storage/belt/medical{
|
||||
@@ -35860,11 +35873,9 @@
|
||||
/area/medical/medbay/central)
|
||||
"bGW" = (
|
||||
/obj/structure/disposalpipe/segment,
|
||||
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
|
||||
dir = 1
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/scrubbers,
|
||||
/turf/closed/wall,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bGX" = (
|
||||
/obj/machinery/light{
|
||||
dir = 8
|
||||
@@ -37012,7 +37023,7 @@
|
||||
dir = 9
|
||||
},
|
||||
/turf/closed/wall,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"bJB" = (
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/scrubbers,
|
||||
/turf/open/floor/plasteel,
|
||||
@@ -52309,7 +52320,7 @@
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/science/robotics/mechbay)
|
||||
/area/maintenance/department/medical/morgue)
|
||||
"cHF" = (
|
||||
/obj/machinery/button/door{
|
||||
id = "Skynet_launch";
|
||||
@@ -54428,7 +54439,7 @@
|
||||
"fvk" = (
|
||||
/obj/structure/disposalpipe/segment,
|
||||
/turf/closed/wall,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"fvY" = (
|
||||
/obj/machinery/computer/cryopod{
|
||||
pixel_y = 26
|
||||
@@ -55768,6 +55779,9 @@
|
||||
req_access_txt = "45"
|
||||
},
|
||||
/obj/machinery/door/firedoor,
|
||||
/obj/structure/cable{
|
||||
icon_state = "4-8"
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/medical/surgery)
|
||||
"iVJ" = (
|
||||
@@ -60411,6 +60425,9 @@
|
||||
/obj/effect/spawner/structure/window/reinforced,
|
||||
/turf/open/floor/plating,
|
||||
/area/engineering/atmos)
|
||||
"tBV" = (
|
||||
/turf/closed/wall,
|
||||
/area/medical/storage)
|
||||
"tCa" = (
|
||||
/obj/structure/table/wood,
|
||||
/obj/item/instrument/guitar{
|
||||
@@ -61009,8 +61026,17 @@
|
||||
dir = 4
|
||||
},
|
||||
/obj/structure/disposalpipe/segment,
|
||||
/obj/machinery/power/apc{
|
||||
areastring = "/area/medical/storage";
|
||||
name = "Medbay Surgery Storage";
|
||||
pixel_x = 1;
|
||||
pixel_y = -24
|
||||
},
|
||||
/obj/structure/cable{
|
||||
icon_state = "0-4"
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/medbay/central)
|
||||
/area/medical/storage)
|
||||
"uFZ" = (
|
||||
/obj/structure/closet/emcloset,
|
||||
/obj/effect/turf_decal/stripes/line{
|
||||
@@ -61336,6 +61362,16 @@
|
||||
/obj/structure/pool/Lboard,
|
||||
/turf/open/pool,
|
||||
/area/commons/fitness/pool)
|
||||
"voZ" = (
|
||||
/obj/structure/disposalpipe/segment,
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
|
||||
dir = 4
|
||||
},
|
||||
/obj/structure/cable{
|
||||
icon_state = "4-8"
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/medical/storage)
|
||||
"vpY" = (
|
||||
/obj/structure/closet/lasertag/blue,
|
||||
/obj/item/clothing/under/misc/pj/blue,
|
||||
@@ -95226,10 +95262,10 @@ aJq
|
||||
bHt
|
||||
bBz
|
||||
bzs
|
||||
bof
|
||||
tBV
|
||||
bFm
|
||||
bGI
|
||||
bof
|
||||
tBV
|
||||
cBD
|
||||
bKD
|
||||
bLO
|
||||
@@ -95483,7 +95519,7 @@ bwu
|
||||
kPj
|
||||
bBB
|
||||
eBX
|
||||
bof
|
||||
tBV
|
||||
bFp
|
||||
uFV
|
||||
fvk
|
||||
@@ -95740,7 +95776,7 @@ aJq
|
||||
bAj
|
||||
aJq
|
||||
aKG
|
||||
bof
|
||||
tBV
|
||||
bFo
|
||||
bDA
|
||||
bFt
|
||||
@@ -95997,9 +96033,9 @@ byX
|
||||
aXh
|
||||
bmE
|
||||
bCA
|
||||
bof
|
||||
tBV
|
||||
bCC
|
||||
bDA
|
||||
voZ
|
||||
bFx
|
||||
bGW
|
||||
bKI
|
||||
|
||||
@@ -13232,7 +13232,7 @@
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/hallway/secondary/service)
|
||||
/area/maintenance/port/fore)
|
||||
"aYA" = (
|
||||
/obj/structure/cable/white{
|
||||
icon_state = "4-8"
|
||||
@@ -33522,7 +33522,7 @@
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/range)
|
||||
/area/maintenance/starboard)
|
||||
"cqi" = (
|
||||
/obj/structure/cable/white,
|
||||
/obj/effect/spawner/structure/window/reinforced,
|
||||
@@ -49101,7 +49101,7 @@
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/science/misc_lab)
|
||||
/area/maintenance/port)
|
||||
"dhU" = (
|
||||
/obj/structure/sign/nanotrasen,
|
||||
/turf/closed/wall/r_wall,
|
||||
@@ -71534,6 +71534,9 @@
|
||||
},
|
||||
/turf/open/floor/plasteel/dark,
|
||||
/area/command/heads_quarters/hos)
|
||||
"emZ" = (
|
||||
/turf/closed/wall,
|
||||
/area/hallway/primary/port)
|
||||
"enl" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
|
||||
/obj/effect/turf_decal/stripes/corner,
|
||||
@@ -106478,7 +106481,7 @@
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/commons/dorms)
|
||||
/area/maintenance/starboard)
|
||||
"qtk" = (
|
||||
/obj/structure/table/wood,
|
||||
/obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb,
|
||||
@@ -156429,7 +156432,7 @@ pzz
|
||||
kRu
|
||||
iio
|
||||
aMN
|
||||
alf
|
||||
emZ
|
||||
bqm
|
||||
bsp
|
||||
bug
|
||||
@@ -156686,7 +156689,7 @@ vgS
|
||||
xnz
|
||||
iio
|
||||
aMO
|
||||
alf
|
||||
emZ
|
||||
bqn
|
||||
bsq
|
||||
buh
|
||||
@@ -156943,7 +156946,7 @@ lNq
|
||||
skw
|
||||
iio
|
||||
aMN
|
||||
alf
|
||||
emZ
|
||||
bqo
|
||||
bsr
|
||||
fzc
|
||||
|
||||
@@ -25521,6 +25521,9 @@
|
||||
/obj/effect/turf_decal/tile/purple{
|
||||
dir = 4
|
||||
},
|
||||
/obj/item/paicard{
|
||||
pixel_x = -8
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/science/research)
|
||||
"cav" = (
|
||||
@@ -74083,7 +74086,7 @@
|
||||
},
|
||||
/obj/machinery/door/firedoor,
|
||||
/turf/open/floor/plating,
|
||||
/area/science/circuit)
|
||||
/area/maintenance/starboard/aft)
|
||||
"sFR" = (
|
||||
/obj/structure/cable{
|
||||
icon_state = "2-8"
|
||||
@@ -76385,7 +76388,7 @@
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/service/library)
|
||||
/area/maintenance/port)
|
||||
"ubJ" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
|
||||
/obj/structure/cable/yellow{
|
||||
@@ -80256,7 +80259,7 @@
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
|
||||
/turf/open/floor/plating,
|
||||
/area/commons/vacant_room/office)
|
||||
/area/maintenance/port)
|
||||
"wxP" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
|
||||
dir = 10
|
||||
@@ -97479,8 +97482,8 @@ dne
|
||||
aip
|
||||
dne
|
||||
dne
|
||||
dne
|
||||
dne
|
||||
aRA
|
||||
aRA
|
||||
dne
|
||||
baf
|
||||
bbK
|
||||
|
||||
@@ -705,7 +705,7 @@
|
||||
icon_state = "4-8"
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/commons/fitness/pool)
|
||||
/area/maintenance/department/crew_quarters/dorms)
|
||||
"abG" = (
|
||||
/obj/structure/cable{
|
||||
icon_state = "1-2"
|
||||
@@ -3392,6 +3392,10 @@
|
||||
/obj/vehicle/ridden/secway,
|
||||
/obj/item/key/security,
|
||||
/obj/effect/turf_decal/bot,
|
||||
/obj/machinery/airalarm{
|
||||
dir = 1;
|
||||
pixel_y = -22
|
||||
},
|
||||
/turf/open/floor/plasteel/showroomfloor,
|
||||
/area/security/office)
|
||||
"ais" = (
|
||||
@@ -4891,6 +4895,10 @@
|
||||
/obj/effect/turf_decal/tile/red{
|
||||
dir = 8
|
||||
},
|
||||
/obj/machinery/airalarm{
|
||||
dir = 4;
|
||||
pixel_x = -23
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/brig)
|
||||
"alz" = (
|
||||
@@ -37279,7 +37287,7 @@
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/science/mixing)
|
||||
/area/maintenance/department/science)
|
||||
"bGB" = (
|
||||
/obj/machinery/light/small,
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
|
||||
@@ -45936,14 +45944,8 @@
|
||||
/turf/open/floor/engine,
|
||||
/area/maintenance/disposal/incinerator)
|
||||
"bZV" = (
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
|
||||
dir = 8
|
||||
},
|
||||
/obj/machinery/door/airlock/external{
|
||||
req_access_txt = "13"
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/maintenance/department/cargo)
|
||||
/turf/closed/wall,
|
||||
/area/hallway/primary/fore)
|
||||
"bZY" = (
|
||||
/turf/closed/wall,
|
||||
/area/service/chapel/office)
|
||||
@@ -61261,8 +61263,14 @@
|
||||
/turf/open/floor/plating,
|
||||
/area/maintenance/department/science)
|
||||
"vtT" = (
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
|
||||
dir = 8
|
||||
},
|
||||
/obj/machinery/door/airlock/external{
|
||||
req_access_txt = "13"
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/maintenance/solars/port)
|
||||
/area/maintenance/department/cargo)
|
||||
"vuP" = (
|
||||
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
|
||||
dir = 4
|
||||
@@ -62179,7 +62187,7 @@
|
||||
"xuv" = (
|
||||
/obj/item/broken_bottle,
|
||||
/turf/open/floor/plating,
|
||||
/area/maintenance/solars/port)
|
||||
/area/maintenance/department/security/brig)
|
||||
"xvO" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
|
||||
@@ -79788,11 +79796,11 @@ atp
|
||||
aus
|
||||
aiu
|
||||
wxb
|
||||
axC
|
||||
aiu
|
||||
xuv
|
||||
azN
|
||||
vtT
|
||||
vtT
|
||||
aoe
|
||||
ajD
|
||||
ajD
|
||||
aiu
|
||||
apB
|
||||
aiu
|
||||
@@ -82360,8 +82368,8 @@ apE
|
||||
avq
|
||||
apE
|
||||
ajM
|
||||
aiu
|
||||
aiu
|
||||
bZV
|
||||
bZV
|
||||
gSH
|
||||
xJy
|
||||
sJr
|
||||
@@ -106255,7 +106263,7 @@ aaF
|
||||
aaF
|
||||
aaF
|
||||
abF
|
||||
aaF
|
||||
aiS
|
||||
aiS
|
||||
atn
|
||||
awC
|
||||
@@ -107807,8 +107815,8 @@ aaa
|
||||
aaa
|
||||
aaa
|
||||
aaa
|
||||
aEl
|
||||
aFi
|
||||
aEj
|
||||
vtT
|
||||
aEj
|
||||
aaa
|
||||
aEj
|
||||
@@ -108064,9 +108072,9 @@ aaa
|
||||
aaa
|
||||
aaa
|
||||
aaa
|
||||
aEj
|
||||
bZV
|
||||
aEj
|
||||
cdm
|
||||
cdm
|
||||
cdm
|
||||
aaa
|
||||
aaa
|
||||
aaa
|
||||
@@ -108321,9 +108329,9 @@ aaa
|
||||
aaa
|
||||
aaa
|
||||
aaa
|
||||
ahi
|
||||
ahi
|
||||
ahi
|
||||
bBW
|
||||
bBW
|
||||
bBW
|
||||
aaa
|
||||
aaa
|
||||
aaa
|
||||
|
||||
@@ -16329,7 +16329,7 @@
|
||||
/obj/docking_port/stationary{
|
||||
dwidth = 1;
|
||||
height = 4;
|
||||
id = "pod4_away";
|
||||
id = "pod_4_away";
|
||||
name = "recovery ship";
|
||||
width = 3
|
||||
},
|
||||
@@ -16339,7 +16339,7 @@
|
||||
/obj/docking_port/stationary{
|
||||
dwidth = 1;
|
||||
height = 4;
|
||||
id = "pod3_away";
|
||||
id = "pod_3_away";
|
||||
name = "recovery ship";
|
||||
width = 3
|
||||
},
|
||||
@@ -16489,7 +16489,7 @@
|
||||
dir = 4;
|
||||
dwidth = 1;
|
||||
height = 4;
|
||||
id = "pod2_away";
|
||||
id = "pod_2_away";
|
||||
name = "recovery ship";
|
||||
width = 3
|
||||
},
|
||||
|
||||
@@ -51,3 +51,4 @@
|
||||
#define ORGAN_NO_SPOIL (1<<5) //Do not spoil under any circumstances
|
||||
#define ORGAN_NO_DISMEMBERMENT (1<<6) //Immune to disembowelment.
|
||||
#define ORGAN_EDIBLE (1<<7) //is a snack? :D
|
||||
#define ORGAN_SYNTHETIC_EMP (1<<6) //Synthetic organ affected by an EMP. Deteriorates over time.
|
||||
|
||||
@@ -60,6 +60,5 @@ GLOBAL_LIST_INIT(podstyles, list(\
|
||||
))
|
||||
|
||||
//cit
|
||||
#define PACK_GOODY_NONE 0
|
||||
#define PACK_GOODY_PUBLIC 1 //can be bought by both privates and cargo
|
||||
#define PACK_GOODY_PRIVATE 2 //can be bought only by privates
|
||||
#define PACK_GOODY_NONE 0 // can be bought by cargo and privates
|
||||
#define PACK_GOODY_PRIVATE 1 // can be bought only by privates
|
||||
|
||||
@@ -159,9 +159,6 @@
|
||||
#define SHOVE_STAGGER_DURATION 35
|
||||
/// how long they're off balance for
|
||||
#define SHOVE_OFFBALANCE_DURATION 30
|
||||
//Shove disarming item list
|
||||
GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
|
||||
/obj/item/gun)))
|
||||
|
||||
|
||||
//Embedded objects
|
||||
|
||||
@@ -266,6 +266,7 @@
|
||||
#define COMSIG_MOB_GET_VISIBLE_MESSAGE "mob_get_visible_message" //from base of atom/visible_message(): (atom/A, msg, range, ignored_mobs)
|
||||
#define COMPONENT_NO_VISIBLE_MESSAGE 1 //exactly what's said on the tin.
|
||||
#define COMSIG_MOB_ANTAG_ON_GAIN "mob_antag_on_gain" //from base of /datum/antagonist/on_gain(): (antag_datum)
|
||||
#define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness)
|
||||
|
||||
#define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
|
||||
#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item)
|
||||
@@ -303,6 +304,10 @@
|
||||
#define COMSIG_LIVING_ACTIVE_PARRY_START "active_parry_start" //from base of mob/living/initiate_parry_sequence(): (parrying_method, datum/parrying_item_mob_or_art, list/backup_items, list/override)
|
||||
#define COMPONENT_PREVENT_PARRY_START 1
|
||||
|
||||
#define COMSIG_LIVING_ATTACKER_SET "living_attacker_set" // from base of /mob/living/set_last_attacker(): (attacker)
|
||||
|
||||
#define COMSIG_LIVING_SET_AS_ATTACKER "living_set_as_attacker" // from base of /mob/living/set_last_attacker(): (target)
|
||||
|
||||
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
|
||||
#define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore)
|
||||
#define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore)
|
||||
@@ -357,7 +362,6 @@
|
||||
|
||||
// /obj/item signals
|
||||
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
|
||||
#define COMSIG_MOB_APPLY_DAMGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone)
|
||||
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
|
||||
#define COMPONENT_NO_INTERACT 1
|
||||
#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob)
|
||||
@@ -455,6 +459,10 @@
|
||||
// /datum/mutation signals
|
||||
#define COMSIG_HUMAN_MUTATION_LOSS "human_mutation_loss" //from datum/mutation/human/on_losing(): (datum/mutation/human/lost_mutation)
|
||||
|
||||
///from base of mob/living/death(): (gibbed)
|
||||
// Sent before any of the other death code has run, mob is still alive.
|
||||
#define COMSIG_LIVING_PREDEATH "living_predeath"
|
||||
|
||||
/*******Component Specific Signals*******/
|
||||
//Janitor
|
||||
#define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values.
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
#define PROJECTILE_HIT_THRESHHOLD_LAYER 2.75 //projectiles won't hit objects at or below this layer if possible
|
||||
#define TABLE_LAYER 2.8
|
||||
#define TRAY_LAYER 2.85
|
||||
#define GATEWAY_UNDERLAY_LAYER 2.85
|
||||
#define BELOW_OBJ_LAYER 2.9
|
||||
#define LOW_ITEM_LAYER 2.95
|
||||
//#define OBJ_LAYER 3 //For easy recordkeeping; this is a byond define
|
||||
|
||||
@@ -159,7 +159,7 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
|
||||
#define BLOOD_COLOR_SLIME "#00ff90"
|
||||
#define BLOOD_COLOR_LIZARD "#db004D"
|
||||
#define BLOOD_COLOR_UNIVERSAL "#db3300"
|
||||
#define BLOOD_COLOR_BUG "#a37c0f"
|
||||
#define BLOOD_COLOR_BUG "#ffc933"
|
||||
#define BLOOD_COLOR_PLANT "#3d610e"
|
||||
|
||||
|
||||
|
||||
@@ -215,6 +215,7 @@
|
||||
#define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen" /// Prevents stamina buffer regeneration
|
||||
#define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen" /// Prevents stamina regeneration
|
||||
#define TRAIT_ARMOR_BROKEN "armor_broken" //acts as if you are wearing no clothing when taking damage, does not affect non-clothing sources of protection
|
||||
#define TRAIT_IWASBATONED "iwasbatoned" //some dastardly fellow has struck you with a baton and thought to use another to strike you again, the rogue
|
||||
/// forces update_density to make us not dense
|
||||
#define TRAIT_LIVING_NO_DENSITY "living_no_density"
|
||||
/// forces us to not render our overlays
|
||||
|
||||
@@ -136,3 +136,6 @@
|
||||
|
||||
// paintings
|
||||
#define VV_HK_REMOVE_PAINTING "remove_painting"
|
||||
|
||||
//outfits
|
||||
#define VV_HK_TO_OUTFIT_EDITOR "outfit_editor"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
|
||||
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
|
||||
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
|
||||
#define LAZYFIND(L, V) L ? L.Find(V) : 0
|
||||
#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
|
||||
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
|
||||
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
|
||||
#define LAZYLEN(L) length(L)
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
for(var/spath in subtypesof(/datum/species))
|
||||
var/datum/species/S = new spath()
|
||||
GLOB.species_list[S.id] = spath
|
||||
GLOB.species_datums[S.id] = S
|
||||
|
||||
//Surgeries
|
||||
for(var/path in subtypesof(/datum/surgery))
|
||||
|
||||
@@ -1051,7 +1051,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
|
||||
return 0
|
||||
|
||||
//For creating consistent icons for human looking simple animals
|
||||
/proc/get_flat_human_icon(icon_id, datum/job/J, datum/preferences/prefs, dummy_key, showDirs = GLOB.cardinals, outfit_override = null)
|
||||
/proc/get_flat_human_icon(icon_id, datum/job/J, datum/preferences/prefs, dummy_key, showDirs = GLOB.cardinals, outfit_override = null, no_anim = FALSE)
|
||||
var/static/list/humanoid_icon_cache = list()
|
||||
if(!icon_id || !humanoid_icon_cache[icon_id])
|
||||
var/mob/living/carbon/human/dummy/body = generate_or_wait_for_human_dummy(dummy_key)
|
||||
@@ -1065,10 +1065,9 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
|
||||
|
||||
|
||||
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
COMPILE_OVERLAYS(body)
|
||||
for(var/D in showDirs)
|
||||
body.setDir(D)
|
||||
COMPILE_OVERLAYS(body)
|
||||
var/icon/partial = getFlatIcon(body)
|
||||
var/icon/partial = getFlatIcon(body, defdir = D, no_anim = no_anim)
|
||||
out_icon.Insert(partial,dir=D)
|
||||
|
||||
humanoid_icon_cache[icon_id] = out_icon
|
||||
|
||||
@@ -320,6 +320,8 @@ GLOBAL_LIST_INIT(nonstandard_skin_tones, list("orange"))
|
||||
|
||||
GLOBAL_LIST_EMPTY(species_list)
|
||||
|
||||
GLOBAL_LIST_EMPTY(species_datums)
|
||||
|
||||
/proc/age2agedescription(age)
|
||||
switch(age)
|
||||
if(0 to 1)
|
||||
|
||||
@@ -95,3 +95,9 @@
|
||||
if("I'm feeling lucky")
|
||||
chosen_id = pick(subtypesof(/datum/reagent))
|
||||
return chosen_id
|
||||
|
||||
/proc/find_reagent_object_from_type(input)
|
||||
if(GLOB.chemical_reagents_list[input]) //prefer IDs!
|
||||
return GLOB.chemical_reagents_list[input]
|
||||
else
|
||||
return null
|
||||
|
||||
@@ -11,6 +11,23 @@ GLOBAL_LIST_INIT(bitfields, list(
|
||||
"TILE_BOUND" = TILE_BOUND,
|
||||
"PIXEL_SCALE" = PIXEL_SCALE
|
||||
),
|
||||
"area_flags" = list(
|
||||
"ABDUCTOR_PROOF" = ABDUCTOR_PROOF,
|
||||
"BLOBS_ALLOWED" = BLOBS_ALLOWED,
|
||||
"BLOCK_SUICIDE" = BLOCK_SUICIDE,
|
||||
// "CULT_PERMITTED" = CULT_PERMITTED,
|
||||
"FLORA_ALLOWED" = FLORA_ALLOWED,
|
||||
"HIDDEN_AREA" = HIDDEN_AREA,
|
||||
"MEGAFAUNA_SPAWN_ALLOWED" = MEGAFAUNA_SPAWN_ALLOWED,
|
||||
"MOB_SPAWN_ALLOWED" = MOB_SPAWN_ALLOWED,
|
||||
"NO_ALERTS" = NO_ALERTS,
|
||||
"NOTELEPORT" = NOTELEPORT,
|
||||
"CAVES_ALLOWED" = CAVES_ALLOWED,
|
||||
"UNIQUE_AREA" = UNIQUE_AREA,
|
||||
"VALID_TERRITORY" = VALID_TERRITORY,
|
||||
"XENOBIOLOGY_COMPATIBLE" = XENOBIOLOGY_COMPATIBLE,
|
||||
"NO_ALERTS" = NO_ALERTS,
|
||||
) ,
|
||||
"sight" = list(
|
||||
"SEE_INFRA" = SEE_INFRA,
|
||||
"SEE_SELF" = SEE_SELF,
|
||||
@@ -32,7 +49,12 @@ GLOBAL_LIST_INIT(bitfields, list(
|
||||
"UNIQUE_RENAME" = UNIQUE_RENAME,
|
||||
"USES_TGUI" = USES_TGUI,
|
||||
"FROZEN" = FROZEN,
|
||||
"SHOVABLE_ONTO" = SHOVABLE_ONTO
|
||||
"SHOVABLE_ONTO" = SHOVABLE_ONTO,
|
||||
"BLOCK_Z_OUT_DOWN" = BLOCK_Z_OUT_DOWN,
|
||||
"BLOCK_Z_OUT_UP" = BLOCK_Z_OUT_UP,
|
||||
"BLOCK_Z_IN_DOWN" = BLOCK_Z_IN_DOWN,
|
||||
"BLOCK_Z_IN_UP" = BLOCK_Z_IN_UP,
|
||||
"EXAMINE_SKIP" = EXAMINE_SKIP
|
||||
),
|
||||
"datum_flags" = list(
|
||||
"DF_USE_TAG" = DF_USE_TAG,
|
||||
|
||||
@@ -123,7 +123,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_HIGH_BLOOD" = TRAIT_HIGH_BLOOD,
|
||||
"TRAIT_EMPATH" = TRAIT_EMPATH,
|
||||
"TRAIT_FRIENDLY" = TRAIT_FRIENDLY,
|
||||
"TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT
|
||||
"TRAIT_IWASBATONED" = TRAIT_IWASBATONED
|
||||
),
|
||||
/obj/item/bodypart = list(
|
||||
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS
|
||||
|
||||
@@ -85,21 +85,16 @@
|
||||
makeItemInactive()
|
||||
|
||||
/obj/screen/storage/volumetric_box/proc/makeItemInactive()
|
||||
if(!our_item)
|
||||
return
|
||||
our_item.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
|
||||
our_item.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
|
||||
return
|
||||
|
||||
/obj/screen/storage/volumetric_box/proc/makeItemActive()
|
||||
if(!our_item)
|
||||
return
|
||||
our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others!
|
||||
our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE
|
||||
return
|
||||
|
||||
/obj/screen/storage/volumetric_box/center
|
||||
icon_state = "stored_continue"
|
||||
var/obj/screen/storage/volumetric_edge/stored_left/left
|
||||
var/obj/screen/storage/volumetric_edge/stored_right/right
|
||||
var/obj/screen/storage/item_holder/holder
|
||||
var/pixel_size
|
||||
|
||||
/obj/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item)
|
||||
@@ -110,6 +105,9 @@
|
||||
/obj/screen/storage/volumetric_box/center/Destroy()
|
||||
QDEL_NULL(left)
|
||||
QDEL_NULL(right)
|
||||
vis_contents.Cut()
|
||||
if(holder)
|
||||
QDEL_NULL(holder)
|
||||
return ..()
|
||||
|
||||
/obj/screen/storage/volumetric_box/center/proc/on_screen_objects()
|
||||
@@ -123,13 +121,36 @@
|
||||
return
|
||||
pixel_size = pixels
|
||||
cut_overlays()
|
||||
vis_contents.Cut()
|
||||
//our icon size is 32 pixels.
|
||||
transform = matrix((pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE, 0, 0, 0, 1, 0)
|
||||
var/multiplier = (pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE
|
||||
transform = matrix(multiplier, 0, 0, 0, 1, 0)
|
||||
if(our_item)
|
||||
if(holder)
|
||||
qdel(holder)
|
||||
holder = new(null, src, our_item)
|
||||
holder.transform = matrix(1 / multiplier, 0, 0, 0, 1, 0)
|
||||
holder.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
holder.appearance_flags &= ~RESET_TRANSFORM
|
||||
makeItemInactive()
|
||||
vis_contents += holder
|
||||
left.pixel_x = -((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) - VOLUMETRIC_STORAGE_BOX_BORDER_SIZE
|
||||
right.pixel_x = ((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) + VOLUMETRIC_STORAGE_BOX_BORDER_SIZE
|
||||
add_overlay(left)
|
||||
add_overlay(right)
|
||||
|
||||
/obj/screen/storage/volumetric_box/center/makeItemInactive()
|
||||
if(!holder)
|
||||
return
|
||||
holder.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
|
||||
holder.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
|
||||
|
||||
/obj/screen/storage/volumetric_box/center/makeItemActive()
|
||||
if(!holder)
|
||||
return
|
||||
holder.our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others!
|
||||
holder.our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE
|
||||
|
||||
/obj/screen/storage/volumetric_edge
|
||||
layer = VOLUMETRIC_STORAGE_BOX_LAYER
|
||||
plane = VOLUMETRIC_STORAGE_BOX_PLANE
|
||||
@@ -157,3 +178,20 @@
|
||||
/obj/screen/storage/volumetric_edge/stored_right
|
||||
icon_state = "stored_end"
|
||||
appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM
|
||||
|
||||
/obj/screen/storage/item_holder
|
||||
var/obj/item/our_item
|
||||
vis_flags = NONE
|
||||
|
||||
/obj/screen/storage/item_holder/Initialize(mapload, new_master, obj/item/I)
|
||||
. = ..()
|
||||
our_item = I
|
||||
vis_contents += I
|
||||
|
||||
/obj/screen/storage/item_holder/Destroy()
|
||||
vis_contents.Cut()
|
||||
our_item = null
|
||||
return ..()
|
||||
|
||||
/obj/screen/storage/item_holder/Click(location, control, params)
|
||||
return our_item.Click(location, control, params)
|
||||
|
||||
@@ -94,8 +94,7 @@
|
||||
else if(hitsound)
|
||||
playsound(loc, hitsound, get_clamped_volume(), 1, -1)
|
||||
|
||||
M.lastattacker = user.real_name
|
||||
M.lastattackerckey = user.ckey
|
||||
M.set_last_attacker(user)
|
||||
|
||||
if(force && M == user && user.client)
|
||||
user.client.give_award(/datum/award/achievement/misc/selfouch, user)
|
||||
|
||||
@@ -61,14 +61,14 @@ SUBSYSTEM_DEF(activity)
|
||||
for(var/threat in threat_history)
|
||||
. = max(threat_history[threat], .)
|
||||
|
||||
/datum/controller/subsystem/activity/proc/on_explosion(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
/datum/controller/subsystem/activity/proc/on_explosion(datum/source, atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
if(!("explosions" in deferred_threats))
|
||||
deferred_threats["explosions"] = 0
|
||||
var/area/A = get_area(epicenter)
|
||||
if(is_station_level(epicenter.z) && (A.area_flags & BLOBS_ALLOWED) && !istype(A, /area/asteroid))
|
||||
deferred_threats["explosions"] += devastation_range**2 + heavy_impact_range**2 / 4 + light_impact_range**2 / 8 // 75 for a maxcap
|
||||
|
||||
/datum/controller/subsystem/activity/proc/on_death(mob/M, gibbed)
|
||||
/datum/controller/subsystem/activity/proc/on_death(datum/source, mob/M, gibbed)
|
||||
if(!("crew_deaths" in deferred_threats))
|
||||
deferred_threats["crew_deaths"] = 0
|
||||
if(M?.mind && SSjob.GetJob(M.mind.assigned_role))
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/datum/component/activity
|
||||
var/activity_level = 0
|
||||
var/not_moved_counter = 0
|
||||
var/list/historical_activity_levels = list()
|
||||
|
||||
/datum/component/activity/Initialize()
|
||||
if(!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
var/mob/living/L = parent
|
||||
|
||||
RegisterSignal(L, COMSIG_LIVING_SET_AS_ATTACKER, .proc/on_set_as_attacker)
|
||||
RegisterSignal(L, COMSIG_LIVING_ATTACKER_SET, .proc/on_attacker_set)
|
||||
RegisterSignal(L, COMSIG_MOB_DEATH, .proc/on_death)
|
||||
RegisterSignal(L, COMSIG_EXIT_AREA, .proc/on_exit_area)
|
||||
RegisterSignal(L, COMSIG_LIVING_LIFE, .proc/on_life)
|
||||
RegisterSignal(L, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE, COMSIG_MOB_APPLY_DAMAGE), .proc/minor_activity)
|
||||
|
||||
/datum/component/activity/proc/log_activity()
|
||||
historical_activity_levels[world.time] = activity_level
|
||||
|
||||
/datum/component/activity/proc/minor_activity(datum/source)
|
||||
activity_level += 1
|
||||
|
||||
/datum/component/activity/proc/on_attacker_set(datum/source, mob/attacker)
|
||||
activity_level += 10
|
||||
if(attacker?.mind)
|
||||
activity_level += 10
|
||||
log_activity()
|
||||
|
||||
/datum/component/activity/proc/on_set_as_attacker(datum/source, mob/target)
|
||||
activity_level += 10
|
||||
if(target?.mind)
|
||||
activity_level += 20
|
||||
log_activity()
|
||||
|
||||
/datum/component/activity/proc/on_death(datum/source)
|
||||
activity_level += 100 // dying means you're doing SOMETHING
|
||||
log_activity()
|
||||
|
||||
/datum/component/activity/proc/on_exit_area(datum/source)
|
||||
activity_level += 1
|
||||
not_moved_counter = 0
|
||||
|
||||
/datum/component/activity/proc/on_life(datum/source, seconds, times_fired)
|
||||
var/mob/living/L = source
|
||||
if(L.stat >= UNCONSCIOUS) // can't expect the unconscious to move
|
||||
return
|
||||
not_moved_counter += seconds
|
||||
var/should_log = FALSE
|
||||
switch(not_moved_counter)
|
||||
if(60 to 120)
|
||||
activity_level -= 1
|
||||
if(120 to 600)
|
||||
activity_level -= 5
|
||||
if(600 to 1200)
|
||||
activity_level -= 10
|
||||
should_log = TRUE
|
||||
if(1200 to INFINITY)
|
||||
activity_level -= 20
|
||||
should_log = TRUE
|
||||
activity_level = max(activity_level, 0)
|
||||
if(should_log)
|
||||
log_activity()
|
||||
@@ -56,10 +56,10 @@
|
||||
detonate()
|
||||
|
||||
/datum/component/explodable/proc/on_equip(datum/source, mob/equipper, slot)
|
||||
RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMGE, .proc/explodable_attack_zone, TRUE)
|
||||
RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMAGE, .proc/explodable_attack_zone, TRUE)
|
||||
|
||||
/datum/component/explodable/proc/on_drop(datum/source, mob/user)
|
||||
UnregisterSignal(user, COMSIG_MOB_APPLY_DAMGE)
|
||||
UnregisterSignal(user, COMSIG_MOB_APPLY_DAMAGE)
|
||||
|
||||
/// Checks if we're hitting the zone this component is covering
|
||||
/datum/component/explodable/proc/is_hitting_zone(def_zone)
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
return
|
||||
strength -= strength / hl3_release_date
|
||||
if(strength <= RAD_BACKGROUND_RADIATION)
|
||||
return PROCESS_KILL
|
||||
qdel(src)
|
||||
|
||||
|
||||
/datum/component/radioactive/proc/glow_loop(atom/movable/master)
|
||||
|
||||
@@ -47,18 +47,8 @@
|
||||
|
||||
var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number.
|
||||
|
||||
/// "legacy"/default view mode's storage "boxes"
|
||||
var/obj/screen/storage/boxes/ui_boxes
|
||||
/// New volumetric storage display mode's left side
|
||||
var/obj/screen/storage/left/ui_left
|
||||
/// New volumetric storage display mode's center 'blocks'
|
||||
var/obj/screen/storage/continuous/ui_continuous
|
||||
/// The close button, used in all modes. Frames right side in volumetric mode.
|
||||
var/obj/screen/storage/close/ui_close
|
||||
/// Associative list of list(item = screen object) for volumetric storage item screen blocks
|
||||
var/list/ui_item_blocks
|
||||
|
||||
var/current_maxscreensize
|
||||
/// Ui objects by person. mob = list(objects)
|
||||
var/list/ui_by_mob = list()
|
||||
|
||||
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
|
||||
|
||||
@@ -125,18 +115,16 @@
|
||||
|
||||
/datum/component/storage/Destroy()
|
||||
close_all()
|
||||
QDEL_NULL(ui_boxes)
|
||||
QDEL_NULL(ui_close)
|
||||
QDEL_NULL(ui_continuous)
|
||||
QDEL_NULL(ui_left)
|
||||
// DO NOT USE QDEL_LIST_ASSOC.
|
||||
if(ui_item_blocks)
|
||||
for(var/i in ui_item_blocks)
|
||||
qdel(ui_item_blocks[i]) //qdel the screen object not the item
|
||||
ui_item_blocks.Cut()
|
||||
wipe_ui_objects()
|
||||
LAZYCLEARLIST(is_using)
|
||||
return ..()
|
||||
|
||||
/datum/component/storage/proc/wipe_ui_objects()
|
||||
for(var/i in ui_by_mob)
|
||||
var/list/objects = ui_by_mob[i]
|
||||
QDEL_LIST(objects)
|
||||
ui_by_mob.Cut()
|
||||
|
||||
/datum/component/storage/PreTransfer()
|
||||
update_actions()
|
||||
|
||||
@@ -351,13 +339,6 @@
|
||||
return master._removal_reset(thing)
|
||||
|
||||
/datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing)
|
||||
if(LAZYACCESS(ui_item_blocks, thing))
|
||||
var/obj/screen/storage/volumetric_box/center/C = ui_item_blocks[thing]
|
||||
for(var/i in can_see_contents()) //runtimes result if mobs can access post deletion.
|
||||
var/mob/M = i
|
||||
M.client?.screen -= C.on_screen_objects()
|
||||
ui_item_blocks -= thing
|
||||
qdel(C)
|
||||
_removal_reset(thing) // THIS NEEDS TO HAPPEN AFTER SO LAYERING DOESN'T BREAK!
|
||||
refresh_mob_views()
|
||||
|
||||
@@ -467,14 +448,14 @@
|
||||
return
|
||||
A.add_fingerprint(M)
|
||||
|
||||
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, ghost = FALSE)
|
||||
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE)
|
||||
var/atom/A = parent
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
A.add_fingerprint(M)
|
||||
if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE)))
|
||||
return FALSE
|
||||
ui_show(M, !ghost)
|
||||
ui_show(M)
|
||||
|
||||
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
|
||||
if(isitem(O))
|
||||
@@ -596,7 +577,7 @@
|
||||
return can_be_inserted(I, silent, M)
|
||||
|
||||
/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M)
|
||||
return user_show_to_mob(M, TRUE, TRUE)
|
||||
return user_show_to_mob(M, TRUE)
|
||||
|
||||
/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
|
||||
return user_show_to_mob(showto, force)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
if(QDELETED(I))
|
||||
continue
|
||||
if(!.[I.type])
|
||||
.[I.type] = new /datum/numbered_display(I, 1)
|
||||
.[I.type] = new /datum/numbered_display(I, 1, src)
|
||||
else
|
||||
var/datum/numbered_display/ND = .[I.type]
|
||||
ND.number++
|
||||
@@ -20,6 +20,8 @@
|
||||
. = list()
|
||||
var/list/accessible_contents = accessible_items()
|
||||
var/adjusted_contents = length(accessible_contents)
|
||||
var/obj/screen/storage/close/ui_close
|
||||
var/obj/screen/storage/boxes/ui_boxes
|
||||
|
||||
//Numbered contents display
|
||||
var/list/datum/numbered_display/numbered_contents
|
||||
@@ -60,12 +62,13 @@
|
||||
for(var/obj/O in accessible_items())
|
||||
if(QDELETED(O))
|
||||
continue
|
||||
O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
|
||||
O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
|
||||
var/obj/screen/storage/item_holder/D = new(null, src, O)
|
||||
D.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
|
||||
D.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
|
||||
O.maptext = ""
|
||||
O.layer = ABOVE_HUD_LAYER
|
||||
O.plane = ABOVE_HUD_PLANE
|
||||
. += O
|
||||
. += D
|
||||
cx++
|
||||
if(cx - screen_start_x >= columns)
|
||||
cx = screen_start_x
|
||||
@@ -78,6 +81,9 @@
|
||||
*/
|
||||
/datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns)
|
||||
. = list()
|
||||
var/obj/screen/storage/left/ui_left
|
||||
var/obj/screen/storage/continuous/ui_continuous
|
||||
var/obj/screen/storage/close/ui_close
|
||||
|
||||
// Generate ui_item_blocks for missing ones and render+orient.
|
||||
var/list/atom/contents = accessible_items()
|
||||
@@ -128,14 +134,10 @@
|
||||
var/first = TRUE
|
||||
var/row = 1
|
||||
|
||||
LAZYINITLIST(ui_item_blocks)
|
||||
|
||||
for(var/i in percentage_by_item)
|
||||
I = i
|
||||
var/percent = percentage_by_item[I]
|
||||
if(!ui_item_blocks[I])
|
||||
ui_item_blocks[I] = new /obj/screen/storage/volumetric_box/center(null, src, I)
|
||||
var/obj/screen/storage/volumetric_box/center/B = ui_item_blocks[I]
|
||||
var/obj/screen/storage/volumetric_box/center/B = new /obj/screen/storage/volumetric_box/center(null, src, I)
|
||||
var/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM)
|
||||
var/addrow = FALSE
|
||||
if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1))
|
||||
@@ -143,25 +145,17 @@
|
||||
addrow = TRUE
|
||||
|
||||
// now that we have pixels_to_use, place our thing and add it to the returned list.
|
||||
B.screen_loc = I.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]"
|
||||
B.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]"
|
||||
// add the used pixels to pixel after we place the object
|
||||
current_pixel += pixels_to_use + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING)
|
||||
first = FALSE //apply padding to everything after this
|
||||
|
||||
// set various things
|
||||
B.set_pixel_size(pixels_to_use)
|
||||
B.layer = VOLUMETRIC_STORAGE_BOX_LAYER
|
||||
B.plane = VOLUMETRIC_STORAGE_BOX_PLANE
|
||||
B.name = I.name
|
||||
|
||||
I.mouse_opacity = MOUSE_OPACITY_ICON
|
||||
I.maptext = ""
|
||||
I.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
|
||||
I.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
|
||||
|
||||
// finally add our things.
|
||||
. += B.on_screen_objects()
|
||||
. += I
|
||||
|
||||
// go up a row if needed
|
||||
if(addrow)
|
||||
@@ -185,18 +179,19 @@
|
||||
/**
|
||||
* Shows our UI to a mob.
|
||||
*/
|
||||
/datum/component/storage/proc/ui_show(mob/M, set_screen_size = TRUE)
|
||||
/datum/component/storage/proc/ui_show(mob/M)
|
||||
if(!M.client)
|
||||
return FALSE
|
||||
if(ui_by_mob[M] || LAZYFIND(is_using, M))
|
||||
// something went horribly wrong
|
||||
// hide first
|
||||
ui_hide(M)
|
||||
var/list/cview = getviewsize(M.client.view)
|
||||
// in tiles
|
||||
var/maxallowedscreensize = cview[1]-8
|
||||
if(set_screen_size)
|
||||
current_maxscreensize = maxallowedscreensize
|
||||
else if(current_maxscreensize)
|
||||
maxallowedscreensize = current_maxscreensize
|
||||
// we got screen size, register signal
|
||||
RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_logout, override = TRUE)
|
||||
RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/on_logout, override = TRUE)
|
||||
if(M.active_storage != src)
|
||||
if(M.active_storage)
|
||||
M.active_storage.ui_hide(M)
|
||||
@@ -204,10 +199,14 @@
|
||||
LAZYOR(is_using, M)
|
||||
if(!M.client?.prefs?.no_tetris_storage && volumetric_ui())
|
||||
//new volumetric ui bay-style
|
||||
M.client.screen |= orient2hud_volumetric(M, maxallowedscreensize)
|
||||
var/list/objects = orient2hud_volumetric(M, maxallowedscreensize)
|
||||
M.client.screen |= objects
|
||||
ui_by_mob[M] = objects
|
||||
else
|
||||
//old ui
|
||||
M.client.screen |= orient2hud_legacy(M, maxallowedscreensize)
|
||||
var/list/objects = orient2hud_legacy(M, maxallowedscreensize)
|
||||
M.client.screen |= objects
|
||||
ui_by_mob[M] = objects
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
@@ -236,8 +235,10 @@
|
||||
/datum/component/storage/proc/ui_hide(mob/M)
|
||||
if(!M.client)
|
||||
return TRUE
|
||||
UnregisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT)
|
||||
M.client.screen -= list(ui_boxes, ui_close, ui_left, ui_continuous) + get_ui_item_objects_hide(M)
|
||||
UnregisterSignal(M, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_CLIENT_LOGOUT))
|
||||
M.client.screen -= ui_by_mob[M]
|
||||
var/list/objects = ui_by_mob[M]
|
||||
QDEL_LIST(objects)
|
||||
if(M.active_storage == src)
|
||||
M.active_storage = null
|
||||
LAZYREMOVE(is_using, M)
|
||||
@@ -250,48 +251,26 @@
|
||||
var/atom/real_location = real_location()
|
||||
return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_ITEMS) && !display_numerical_stacking
|
||||
|
||||
/**
|
||||
* Gets the ui item objects to ui_hide.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_item_objects_hide(mob/M)
|
||||
if(!volumetric_ui() || M.client?.prefs?.no_tetris_storage)
|
||||
var/atom/real_location = real_location()
|
||||
return real_location.contents
|
||||
else
|
||||
. = list()
|
||||
for(var/i in ui_item_blocks)
|
||||
// get both the box and the item
|
||||
. += ui_item_blocks[i]
|
||||
. += i
|
||||
|
||||
/**
|
||||
* Gets our ui_boxes, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_boxes()
|
||||
if(!ui_boxes)
|
||||
ui_boxes = new(null, src)
|
||||
return ui_boxes
|
||||
return new /obj/screen/storage/boxes(null, src)
|
||||
|
||||
/**
|
||||
* Gets our ui_left, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_left()
|
||||
if(!ui_left)
|
||||
ui_left = new(null, src)
|
||||
return ui_left
|
||||
return new /obj/screen/storage/left(null, src)
|
||||
|
||||
/**
|
||||
* Gets our ui_close, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_close()
|
||||
if(!ui_close)
|
||||
ui_close = new(null, src)
|
||||
return ui_close
|
||||
return new /obj/screen/storage/close(null, src)
|
||||
|
||||
/**
|
||||
* Gets our ui_continuous, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_continuous()
|
||||
if(!ui_continuous)
|
||||
ui_continuous = new(null, src)
|
||||
return ui_continuous
|
||||
return new /obj/screen/storage/continuous(null, src)
|
||||
|
||||
@@ -384,7 +384,7 @@
|
||||
var/temp_rate = 1
|
||||
threshold_desc = list(
|
||||
"Transmission 6" = "Additionally increases temperature adjustment rate and heals those who love toxins",
|
||||
"Resistance 7" = "Increases healing speed.",
|
||||
"Stage Speed 7" = "Increases healing speed.",
|
||||
)
|
||||
/datum/symptom/heal/plasma/Start(datum/disease/advance/A)
|
||||
if(!..())
|
||||
|
||||
+3
-2
@@ -329,12 +329,13 @@
|
||||
uni_identity = generate_uni_identity()
|
||||
unique_enzymes = generate_unique_enzymes()
|
||||
|
||||
/datum/dna/proc/initialize_dna(newblood_type)
|
||||
/datum/dna/proc/initialize_dna(newblood_type, skip_index = FALSE)
|
||||
if(newblood_type)
|
||||
blood_type = newblood_type
|
||||
unique_enzymes = generate_unique_enzymes()
|
||||
uni_identity = generate_uni_identity()
|
||||
generate_dna_blocks()
|
||||
if(!skip_index) //I hate this
|
||||
generate_dna_blocks()
|
||||
features = random_features(species?.id, holder?.gender)
|
||||
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
|
||||
/obj/item/clothing/head/mob_holder/dropped(mob/user)
|
||||
. = ..()
|
||||
if(held_mob && !ismob(loc))//don't release on soft-drops
|
||||
if(held_mob && !ismob(loc) && !istype(loc,/obj/item/storage))//don't release on soft-drops
|
||||
release()
|
||||
|
||||
/obj/item/clothing/head/mob_holder/proc/release()
|
||||
|
||||
@@ -34,13 +34,13 @@
|
||||
. = ..()
|
||||
UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
|
||||
|
||||
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
|
||||
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user, act_intent)
|
||||
var/mob/living/L = source
|
||||
|
||||
if(L.stat == DEAD)
|
||||
return
|
||||
//we want to delay the effect to be displayed after the mob is petted, not before.
|
||||
switch(user.a_intent)
|
||||
switch(act_intent)
|
||||
if(INTENT_HARM)
|
||||
addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
|
||||
if(INTENT_HELP)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/datum/mutation/human/space_adaptation/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
|
||||
..()
|
||||
if(!(type in visual_indicators))
|
||||
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER))
|
||||
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "space_adapt", -MUTATIONS_LAYER))
|
||||
|
||||
/datum/mutation/human/space_adaptation/get_visual_indicator()
|
||||
return visual_indicators[type][1]
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
var/obj/item/sample_object
|
||||
var/number
|
||||
|
||||
/datum/numbered_display/New(obj/item/sample, _number = 1)
|
||||
/datum/numbered_display/New(obj/item/sample, _number = 1, datum/component/storage/parent)
|
||||
if(!istype(sample))
|
||||
qdel(src)
|
||||
sample_object = sample
|
||||
sample_object = new /obj/screen/storage/item_holder(null, parent, sample)
|
||||
number = _number
|
||||
|
||||
+294
-57
@@ -1,71 +1,185 @@
|
||||
/**
|
||||
* # Outfit datums
|
||||
*
|
||||
* This is a clean system of applying outfits to mobs, if you need to equip someone in a uniform
|
||||
* this is the way to do it cleanly and properly.
|
||||
*
|
||||
* You can also specify an outfit datum on a job to have it auto equipped to the mob on join
|
||||
*
|
||||
* /mob/living/carbon/human/proc/equipOutfit(outfit) is the mob level proc to equip an outfit
|
||||
* and you pass it the relevant datum outfit
|
||||
*
|
||||
* outfits can also be saved as json blobs downloadable by a client and then can be uploaded
|
||||
* by that user to recreate the outfit, this is used by admins to allow for custom event outfits
|
||||
* that can be restored at a later date
|
||||
*/
|
||||
/datum/outfit
|
||||
///Name of the outfit (shows up in the equip admin verb)
|
||||
var/name = "Naked"
|
||||
|
||||
var/uniform = null
|
||||
var/suit = null
|
||||
var/toggle_helmet = TRUE
|
||||
var/back = null
|
||||
var/belt = null
|
||||
var/gloves = null
|
||||
var/shoes = null
|
||||
var/head = null
|
||||
var/mask = null
|
||||
var/neck = null
|
||||
var/ears = null
|
||||
var/glasses = null
|
||||
/// Type path of item to go in the idcard slot
|
||||
var/id = null
|
||||
var/l_pocket = null
|
||||
var/r_pocket = null
|
||||
|
||||
/// Type path of item to go in uniform slot
|
||||
var/uniform = null
|
||||
|
||||
/// Type path of item to go in suit slot
|
||||
var/suit = null
|
||||
|
||||
/**
|
||||
* Type path of item to go in suit storage slot
|
||||
*
|
||||
* (make sure it's valid for that suit)
|
||||
*/
|
||||
var/suit_store = null
|
||||
var/r_hand = null
|
||||
|
||||
/// Type path of item to go in back slot
|
||||
var/back = null
|
||||
|
||||
/**
|
||||
* list of items that should go in the backpack of the user
|
||||
*
|
||||
* Format of this list should be: list(path=count,otherpath=count)
|
||||
*/
|
||||
var/list/backpack_contents = null
|
||||
|
||||
/// Type path of item to go in belt slot
|
||||
var/belt = null
|
||||
|
||||
/// Type path of item to go in ears slot
|
||||
var/ears = null
|
||||
|
||||
/// Type path of item to go in the glasses slot
|
||||
var/glasses = null
|
||||
|
||||
/// Type path of item to go in gloves slot
|
||||
var/gloves = null
|
||||
|
||||
/// Type path of item to go in head slot
|
||||
var/head = null
|
||||
|
||||
/// Type path of item to go in mask slot
|
||||
var/mask = null
|
||||
|
||||
/// Type path of item to go in neck slot
|
||||
var/neck = null
|
||||
|
||||
/// Type path of item to go in shoes slot
|
||||
var/shoes = null
|
||||
|
||||
/// Type path of item for left pocket slot
|
||||
var/l_pocket = null
|
||||
|
||||
/// Type path of item for right pocket slot
|
||||
var/r_pocket = null
|
||||
|
||||
///Type path of item to go in the right hand
|
||||
var/l_hand = null
|
||||
var/internals_slot = null //ID of slot containing a gas tank
|
||||
var/list/backpack_contents = null // In the list(path=count,otherpath=count) format
|
||||
var/box // Internals box. Will be inserted at the start of backpack_contents
|
||||
var/list/implants = null
|
||||
|
||||
//Type path of item to go in left hand
|
||||
var/r_hand = null
|
||||
|
||||
/// Any clothing accessory item
|
||||
var/accessory = null
|
||||
|
||||
var/can_be_admin_equipped = TRUE // Set to FALSE if your outfit requires runtime parameters
|
||||
var/list/chameleon_extras //extra types for chameleon outfit changes, mostly guns
|
||||
/// Internals box. Will be inserted at the start of backpack_contents
|
||||
var/box
|
||||
|
||||
/**
|
||||
* extra types for chameleon outfit changes, mostly guns
|
||||
*
|
||||
* Format of this list is (typepath, typepath, typepath)
|
||||
*
|
||||
* These are all added and returns in the list for get_chamelon_diguise_info proc
|
||||
*/
|
||||
var/list/chameleon_extras
|
||||
|
||||
/**
|
||||
* Any implants the mob should start implanted with
|
||||
*
|
||||
* Format of this list is (typepath, typepath, typepath)
|
||||
*/
|
||||
var/list/implants = null
|
||||
|
||||
///ID of the slot containing a gas tank
|
||||
var/internals_slot = null
|
||||
|
||||
/// Should the toggle helmet proc be called on the helmet during equip
|
||||
var/toggle_helmet = TRUE
|
||||
|
||||
/// Any undershirt. While on humans it is a string, here we use paths to stay consistent with the rest of the equips.
|
||||
var/datum/sprite_accessory/undershirt = null
|
||||
|
||||
/**
|
||||
* Called at the start of the equip proc
|
||||
*
|
||||
* Override to change the value of the slots depending on client prefs, species and
|
||||
* other such sources of change
|
||||
*
|
||||
* Extra Arguments
|
||||
* * visualsOnly true if this is only for display (in the character setup screen)
|
||||
*
|
||||
* If visualsOnly is true, you can omit any work that doesn't visually appear on the character sprite
|
||||
*/
|
||||
/datum/outfit/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
//to be overridden for customization depending on client prefs,species etc
|
||||
return
|
||||
|
||||
/**
|
||||
* Called after the equip proc has finished
|
||||
*
|
||||
* All items are on the mob at this point, use this proc to toggle internals
|
||||
* fiddle with id bindings and accesses etc
|
||||
*
|
||||
* Extra Arguments
|
||||
* * visualsOnly true if this is only for display (in the character setup screen)
|
||||
*
|
||||
* If visualsOnly is true, you can omit any work that doesn't visually appear on the character sprite
|
||||
*/
|
||||
/datum/outfit/proc/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
//to be overridden for toggling internals, id binding, access etc
|
||||
return
|
||||
|
||||
/**
|
||||
* Equips all defined types and paths to the mob passed in
|
||||
*
|
||||
* Extra Arguments
|
||||
* * visualsOnly true if this is only for display (in the character setup screen)
|
||||
*
|
||||
* If visualsOnly is true, you can omit any work that doesn't visually appear on the character sprite
|
||||
*/
|
||||
/datum/outfit/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
pre_equip(H, visualsOnly, preference_source)
|
||||
|
||||
//Start with uniform,suit,backpack for additional slots
|
||||
if(uniform)
|
||||
H.equip_to_slot_or_del(new uniform(H),SLOT_W_UNIFORM)
|
||||
H.equip_to_slot_or_del(new uniform(H), SLOT_W_UNIFORM, TRUE)
|
||||
if(suit)
|
||||
H.equip_to_slot_or_del(new suit(H),SLOT_WEAR_SUIT)
|
||||
H.equip_to_slot_or_del(new suit(H), SLOT_WEAR_SUIT, TRUE)
|
||||
if(back)
|
||||
H.equip_to_slot_or_del(new back(H),SLOT_BACK)
|
||||
H.equip_to_slot_or_del(new back(H), SLOT_BACK, TRUE)
|
||||
if(belt)
|
||||
H.equip_to_slot_or_del(new belt(H),SLOT_BELT)
|
||||
H.equip_to_slot_or_del(new belt(H), SLOT_BELT, TRUE)
|
||||
if(gloves)
|
||||
H.equip_to_slot_or_del(new gloves(H),SLOT_GLOVES)
|
||||
H.equip_to_slot_or_del(new gloves(H), SLOT_GLOVES, TRUE)
|
||||
if(shoes)
|
||||
H.equip_to_slot_or_del(new shoes(H),SLOT_SHOES)
|
||||
H.equip_to_slot_or_del(new shoes(H), SLOT_SHOES, TRUE)
|
||||
if(head)
|
||||
H.equip_to_slot_or_del(new head(H),SLOT_HEAD)
|
||||
H.equip_to_slot_or_del(new head(H), SLOT_HEAD, TRUE)
|
||||
if(mask)
|
||||
H.equip_to_slot_or_del(new mask(H),SLOT_WEAR_MASK)
|
||||
H.equip_to_slot_or_del(new mask(H), SLOT_WEAR_MASK, TRUE)
|
||||
if(neck)
|
||||
H.equip_to_slot_or_del(new neck(H),SLOT_NECK)
|
||||
H.equip_to_slot_or_del(new neck(H), SLOT_NECK, TRUE)
|
||||
if(ears)
|
||||
H.equip_to_slot_or_del(new ears(H),SLOT_EARS)
|
||||
H.equip_to_slot_or_del(new ears(H), SLOT_EARS, TRUE)
|
||||
if(glasses)
|
||||
H.equip_to_slot_or_del(new glasses(H),SLOT_GLASSES)
|
||||
H.equip_to_slot_or_del(new glasses(H), SLOT_GLASSES, TRUE)
|
||||
if(id)
|
||||
H.equip_to_slot_or_del(new id(H),SLOT_WEAR_ID)
|
||||
H.equip_to_slot_or_del(new id(H), SLOT_WEAR_ID, TRUE)
|
||||
if(suit_store)
|
||||
H.equip_to_slot_or_del(new suit_store(H),SLOT_S_STORE)
|
||||
H.equip_to_slot_or_del(new suit_store(H), SLOT_S_STORE, TRUE)
|
||||
if(undershirt)
|
||||
H.undershirt = initial(undershirt.name)
|
||||
|
||||
if(accessory)
|
||||
var/obj/item/clothing/under/U = H.w_uniform
|
||||
@@ -81,9 +195,9 @@
|
||||
|
||||
if(!visualsOnly) // Items in pockets or backpack don't show up on mob's icon.
|
||||
if(l_pocket)
|
||||
H.equip_to_slot_or_del(new l_pocket(H),SLOT_L_STORE)
|
||||
H.equip_to_slot_or_del(new l_pocket(H), SLOT_L_STORE, TRUE)
|
||||
if(r_pocket)
|
||||
H.equip_to_slot_or_del(new r_pocket(H),SLOT_R_STORE)
|
||||
H.equip_to_slot_or_del(new r_pocket(H), SLOT_R_STORE, TRUE)
|
||||
|
||||
if(box)
|
||||
if(!backpack_contents)
|
||||
@@ -97,7 +211,7 @@
|
||||
if(!isnum(number))//Default to 1
|
||||
number = 1
|
||||
for(var/i in 1 to number)
|
||||
H.equip_to_slot_or_del(new path(H),SLOT_IN_BACKPACK)
|
||||
H.equip_to_slot_or_del(new path(H), SLOT_IN_BACKPACK, TRUE)
|
||||
|
||||
if(!H.head && toggle_helmet && istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit))
|
||||
var/obj/item/clothing/suit/space/hardsuit/HS = H.wear_suit
|
||||
@@ -112,55 +226,178 @@
|
||||
H.update_action_buttons_icon()
|
||||
if(implants)
|
||||
for(var/implant_type in implants)
|
||||
var/obj/item/implant/I = new implant_type
|
||||
var/obj/item/implant/I = new implant_type(H)
|
||||
I.implant(H, null, TRUE)
|
||||
|
||||
H.update_body()
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Apply a fingerprint from the passed in human to all items in the outfit
|
||||
*
|
||||
* Used for forensics setup when the mob is first equipped at roundstart
|
||||
* essentially calls add_fingerprint to every defined item on the human
|
||||
*
|
||||
*/
|
||||
/datum/outfit/proc/apply_fingerprints(mob/living/carbon/human/H)
|
||||
if(!istype(H))
|
||||
return
|
||||
if(H.back)
|
||||
H.back.add_fingerprint(H,1) //The 1 sets a flag to ignore gloves
|
||||
H.back.add_fingerprint(H, ignoregloves = TRUE)
|
||||
for(var/obj/item/I in H.back.contents)
|
||||
I.add_fingerprint(H,1)
|
||||
I.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_id)
|
||||
H.wear_id.add_fingerprint(H,1)
|
||||
H.wear_id.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.w_uniform)
|
||||
H.w_uniform.add_fingerprint(H,1)
|
||||
H.w_uniform.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_suit)
|
||||
H.wear_suit.add_fingerprint(H,1)
|
||||
H.wear_suit.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_mask)
|
||||
H.wear_mask.add_fingerprint(H,1)
|
||||
H.wear_mask.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_neck)
|
||||
H.wear_neck.add_fingerprint(H,1)
|
||||
H.wear_neck.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.head)
|
||||
H.head.add_fingerprint(H,1)
|
||||
H.head.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.shoes)
|
||||
H.shoes.add_fingerprint(H,1)
|
||||
H.shoes.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.gloves)
|
||||
H.gloves.add_fingerprint(H,1)
|
||||
H.gloves.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.ears)
|
||||
H.ears.add_fingerprint(H,1)
|
||||
H.ears.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.glasses)
|
||||
H.glasses.add_fingerprint(H,1)
|
||||
H.glasses.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.belt)
|
||||
H.belt.add_fingerprint(H,1)
|
||||
H.belt.add_fingerprint(H, ignoregloves = TRUE)
|
||||
for(var/obj/item/I in H.belt.contents)
|
||||
I.add_fingerprint(H,1)
|
||||
I.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.s_store)
|
||||
H.s_store.add_fingerprint(H,1)
|
||||
H.s_store.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.l_store)
|
||||
H.l_store.add_fingerprint(H,1)
|
||||
H.l_store.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.r_store)
|
||||
H.r_store.add_fingerprint(H,1)
|
||||
H.r_store.add_fingerprint(H, ignoregloves = TRUE)
|
||||
for(var/obj/item/I in H.held_items)
|
||||
I.add_fingerprint(H,1)
|
||||
return 1
|
||||
I.add_fingerprint(H, ignoregloves = TRUE)
|
||||
return TRUE
|
||||
|
||||
/// Return a list of all the types that are required to disguise as this outfit type
|
||||
/datum/outfit/proc/get_chameleon_disguise_info()
|
||||
var/list/types = list(uniform, suit, back, belt, gloves, shoes, head, mask, neck, ears, glasses, id, l_pocket, r_pocket, suit_store, r_hand, l_hand)
|
||||
types += chameleon_extras
|
||||
listclearnulls(types)
|
||||
return types
|
||||
|
||||
/// Return a json list of this outfit
|
||||
/datum/outfit/proc/get_json_data()
|
||||
. = list()
|
||||
.["outfit_type"] = type
|
||||
.["name"] = name
|
||||
.["uniform"] = uniform
|
||||
.["suit"] = suit
|
||||
.["toggle_helmet"] = toggle_helmet
|
||||
.["back"] = back
|
||||
.["belt"] = belt
|
||||
.["gloves"] = gloves
|
||||
.["shoes"] = shoes
|
||||
.["head"] = head
|
||||
.["mask"] = mask
|
||||
.["neck"] = neck
|
||||
.["ears"] = ears
|
||||
.["glasses"] = glasses
|
||||
.["id"] = id
|
||||
.["l_pocket"] = l_pocket
|
||||
.["r_pocket"] = r_pocket
|
||||
.["suit_store"] = suit_store
|
||||
.["r_hand"] = r_hand
|
||||
.["l_hand"] = l_hand
|
||||
.["internals_slot"] = internals_slot
|
||||
.["backpack_contents"] = backpack_contents
|
||||
.["box"] = box
|
||||
.["implants"] = implants
|
||||
.["accessory"] = accessory
|
||||
|
||||
/// Copy most vars from another outfit to this one
|
||||
/datum/outfit/proc/copy_from(datum/outfit/target)
|
||||
name = target.name
|
||||
uniform = target.uniform
|
||||
suit = target.suit
|
||||
toggle_helmet = target.toggle_helmet
|
||||
back = target.back
|
||||
belt = target.belt
|
||||
gloves = target.gloves
|
||||
shoes = target.shoes
|
||||
head = target.head
|
||||
mask = target.mask
|
||||
neck = target.neck
|
||||
ears = target.ears
|
||||
glasses = target.glasses
|
||||
id = target.id
|
||||
l_pocket = target.l_pocket
|
||||
r_pocket = target.r_pocket
|
||||
suit_store = target.suit_store
|
||||
r_hand = target.r_hand
|
||||
l_hand = target.l_hand
|
||||
internals_slot = target.internals_slot
|
||||
backpack_contents = target.backpack_contents
|
||||
box = target.box
|
||||
implants = target.implants
|
||||
accessory = target.accessory
|
||||
|
||||
/// Prompt the passed in mob client to download this outfit as a json blob
|
||||
/datum/outfit/proc/save_to_file(mob/admin)
|
||||
var/stored_data = get_json_data()
|
||||
var/json = json_encode(stored_data)
|
||||
//Kinda annoying but as far as i can tell you need to make actual file.
|
||||
var/f = file("data/TempOutfitUpload")
|
||||
fdel(f)
|
||||
WRITE_FILE(f,json)
|
||||
admin << ftp(f,"[name].json")
|
||||
|
||||
/// Create an outfit datum from a list of json data
|
||||
/datum/outfit/proc/load_from(list/outfit_data)
|
||||
//This could probably use more strict validation
|
||||
name = outfit_data["name"]
|
||||
uniform = text2path(outfit_data["uniform"])
|
||||
suit = text2path(outfit_data["suit"])
|
||||
toggle_helmet = outfit_data["toggle_helmet"]
|
||||
back = text2path(outfit_data["back"])
|
||||
belt = text2path(outfit_data["belt"])
|
||||
gloves = text2path(outfit_data["gloves"])
|
||||
shoes = text2path(outfit_data["shoes"])
|
||||
head = text2path(outfit_data["head"])
|
||||
mask = text2path(outfit_data["mask"])
|
||||
neck = text2path(outfit_data["neck"])
|
||||
ears = text2path(outfit_data["ears"])
|
||||
glasses = text2path(outfit_data["glasses"])
|
||||
id = text2path(outfit_data["id"])
|
||||
l_pocket = text2path(outfit_data["l_pocket"])
|
||||
r_pocket = text2path(outfit_data["r_pocket"])
|
||||
suit_store = text2path(outfit_data["suit_store"])
|
||||
r_hand = text2path(outfit_data["r_hand"])
|
||||
l_hand = text2path(outfit_data["l_hand"])
|
||||
internals_slot = outfit_data["internals_slot"]
|
||||
var/list/backpack = outfit_data["backpack_contents"]
|
||||
backpack_contents = list()
|
||||
for(var/item in backpack)
|
||||
var/itype = text2path(item)
|
||||
if(itype)
|
||||
backpack_contents[itype] = backpack[item]
|
||||
box = text2path(outfit_data["box"])
|
||||
var/list/impl = outfit_data["implants"]
|
||||
implants = list()
|
||||
for(var/I in impl)
|
||||
var/imptype = text2path(I)
|
||||
if(imptype)
|
||||
implants += imptype
|
||||
accessory = text2path(outfit_data["accessory"])
|
||||
return TRUE
|
||||
|
||||
/datum/outfit/vv_get_dropdown()
|
||||
. = ..()
|
||||
VV_DROPDOWN_OPTION("", "---")
|
||||
VV_DROPDOWN_OPTION(VV_HK_TO_OUTFIT_EDITOR, "Outfit Editor")
|
||||
|
||||
/datum/outfit/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
if(href_list[VV_HK_TO_OUTFIT_EDITOR])
|
||||
usr.client.open_outfit_editor(src)
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
/datum/status_effect/off_balance/on_remove()
|
||||
var/active_item = owner.get_active_held_item()
|
||||
if(is_type_in_typecache(active_item, GLOB.shove_disarming_types))
|
||||
if(active_item)
|
||||
owner.visible_message("<span class='warning'>[owner.name] regains their grip on \the [active_item]!</span>", "<span class='warning'>You regain your grip on \the [active_item]</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -412,6 +412,24 @@ If not set, defaults to check_completion instead. Set it. It's used by cryo.
|
||||
counter++
|
||||
return counter >= 8
|
||||
|
||||
/datum/objective/freedom
|
||||
name = "freedom"
|
||||
explanation_text = "Don't get captured by nanotrasen."
|
||||
team_explanation_text = "Have all members of your team free of nanotrasen custody."
|
||||
|
||||
/datum/objective/freedom/check_completion()
|
||||
var/list/datum/mind/owners = get_owners()
|
||||
for(var/m in owners)
|
||||
var/datum/mind/M = m
|
||||
if(!considered_alive(M))
|
||||
return FALSE
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
|
||||
return FALSE
|
||||
var/turf/location = get_turf(M.current)
|
||||
if(!location || istype(location, /turf/open/floor/plasteel/shuttle/red) || istype(location, /turf/open/floor/mineral/plastitanium/red/brig)) // Fails if they are in the shuttle brig
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/escape
|
||||
name = "escape"
|
||||
explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody."
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
var/temp = "Inactive"
|
||||
var/scantemp_ckey
|
||||
var/scantemp_name
|
||||
var/scantemp = "Ready to Scan"
|
||||
var/scantemp = "Inactive"
|
||||
var/menu = 1 //Which menu screen to display
|
||||
var/datum/data/record/active_record = null
|
||||
var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything.
|
||||
@@ -132,7 +132,6 @@
|
||||
src.diskette = W
|
||||
to_chat(user, "<span class='notice'>You insert [W].</span>")
|
||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
|
||||
src.updateUsrDialog()
|
||||
else if(W.tool_behaviour == TOOL_MULTITOOL)
|
||||
if(istype(W.buffer, clonepod_type))
|
||||
if(get_area(W.buffer) != get_area(src))
|
||||
@@ -151,311 +150,233 @@
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/cloning/ui_interact(mob/user)
|
||||
/obj/machinery/computer/cloning/AltClick(mob/user)
|
||||
. = ..()
|
||||
EjectDisk(user)
|
||||
|
||||
updatemodules(TRUE)
|
||||
/obj/machinery/computer/cloning/proc/EjectDisk(mob/user)
|
||||
if(diskette)
|
||||
scantemp = "Disk Ejected"
|
||||
diskette.forceMove(drop_location())
|
||||
usr.put_in_active_hand(diskette)
|
||||
diskette = null
|
||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
|
||||
|
||||
var/dat = ""
|
||||
dat += "<a href='byond://?src=[REF(src)];refresh=1'>Refresh</a>"
|
||||
/obj/machinery/computer/cloning/proc/Save(mob/user, target)
|
||||
var/datum/data/record/GRAB = null
|
||||
for(var/datum/data/record/record in records)
|
||||
if(record.fields["id"] == target)
|
||||
GRAB = record
|
||||
break
|
||||
else
|
||||
continue
|
||||
if(!GRAB || !GRAB.fields)
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
scantemp = "Failed saving to disk: Data Corruption"
|
||||
return FALSE
|
||||
if(!diskette || diskette.read_only)
|
||||
scantemp = !diskette ? "Failed saving to disk: No disk." : "Failed saving to disk: Disk refuses override attempt."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
diskette.fields = GRAB.fields.Copy()
|
||||
diskette.name = "data disk - '[src.diskette.fields["name"]]'"
|
||||
scantemp = "Saved to disk successfully."
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
/obj/machinery/computer/cloning/proc/DeleteRecord(mob/user, target)
|
||||
var/datum/data/record/GRAB = null
|
||||
for(var/datum/data/record/record in records)
|
||||
if(record.fields["id"] == target)
|
||||
GRAB = record
|
||||
break
|
||||
else
|
||||
continue
|
||||
if(!GRAB)
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
scantemp = "Cannot delete: Data Corrupted."
|
||||
return FALSE
|
||||
var/obj/item/card/id/C = usr.get_idcard(hand_first = TRUE)
|
||||
if(istype(C) || istype(C, /obj/item/pda) || istype(C, /obj/item/modular_computer/tablet))
|
||||
if(check_access(C))
|
||||
scantemp = "[GRAB.fields["name"]] => Record deleted."
|
||||
records.Remove(GRAB)
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
var/obj/item/circuitboard/computer/cloning/board = circuit
|
||||
board.records = records
|
||||
return TRUE
|
||||
scantemp = "Cannot delete: Access Denied."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
|
||||
/obj/machinery/computer/cloning/proc/Load(mob/user)
|
||||
if(!diskette || !istype(diskette.fields) || !diskette.fields["name"] || !diskette.fields)
|
||||
scantemp = "Failed loading: Load error."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
for(var/datum/data/record/R in records)
|
||||
if(R.fields["key"] == diskette.fields["key"])
|
||||
scantemp = "Failed loading: Data already exists!"
|
||||
return FALSE
|
||||
var/datum/data/record/R = new(src)
|
||||
for(var/key in diskette.fields)
|
||||
R.fields[key] = diskette.fields[key]
|
||||
records += R
|
||||
scantemp = "Loaded into internal storage successfully."
|
||||
var/obj/item/circuitboard/computer/cloning/board = circuit
|
||||
board.records = records
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
/obj/machinery/computer/cloning/proc/Clone(mob/user, target)
|
||||
var/datum/data/record/C = find_record("id", target, records)
|
||||
//Look for that player! They better be dead!
|
||||
if(C)
|
||||
var/obj/machinery/clonepod/pod = GetAvailablePod()
|
||||
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
|
||||
if(!LAZYLEN(pods))
|
||||
temp = "Error: No Clonepods detected."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(!pod)
|
||||
temp = "Error: No Clonepods available."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(!CONFIG_GET(flag/revival_cloning))
|
||||
temp = "Error: Unable to initiate cloning cycle."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(pod.occupant)
|
||||
temp = "Warning: Cloning cycle already in progress."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["blood_type"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["quirks"], C.fields["bank_account"], C.fields["traumas"]))
|
||||
temp = "Notice: [C.fields["name"]] => Cloning cycle in progress..."
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
records.Remove(C)
|
||||
else
|
||||
temp = "Error: [C.fields["name"]] => Initialisation failure."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
|
||||
else
|
||||
temp = "Failed to clone: Data corrupted."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
. = TRUE
|
||||
|
||||
/obj/machinery/computer/cloning/proc/Toggle_lock(mob/user)
|
||||
if(!scanner.is_operational())
|
||||
return
|
||||
if(!scanner.locked && !scanner.occupant) //I figured out that if you're fast enough, you can lock an open pod
|
||||
return
|
||||
scanner.locked = !scanner.locked
|
||||
playsound(src, scanner.locked ? 'sound/machines/terminal_prompt_deny.ogg' : 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
. = TRUE
|
||||
|
||||
/obj/machinery/computer/cloning/proc/Scan(mob/user)
|
||||
if(!scanner.is_operational() || !scanner.occupant)
|
||||
return
|
||||
scantemp = "[scantemp_name] => Scanning..."
|
||||
loading = TRUE
|
||||
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
|
||||
say("Initiating scan...")
|
||||
var/prev_locked = scanner.locked
|
||||
scanner.locked = TRUE
|
||||
addtimer(CALLBACK(src, .proc/finish_scan, scanner.occupant, prev_locked), 2 SECONDS)
|
||||
. = TRUE
|
||||
|
||||
/obj/machinery/computer/cloning/proc/Toggle_autoprocess(mob/user)
|
||||
autoprocess = !autoprocess
|
||||
if(autoprocess)
|
||||
START_PROCESSING(SSmachines, src)
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
else
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
. = TRUE
|
||||
|
||||
/obj/machinery/computer/cloning/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["useRecords"] = use_records
|
||||
var/list/records_to_send = list()
|
||||
if(use_records)
|
||||
if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL)
|
||||
if(!autoprocess)
|
||||
dat += "<a href='byond://?src=[REF(src)];task=autoprocess'>Autoclone</a>"
|
||||
else
|
||||
dat += "<a href='byond://?src=[REF(src)];task=stopautoprocess'>Stop autoclone</a>"
|
||||
else
|
||||
dat += "<span class='linkOff'>Autoclone</span>"
|
||||
dat += "<h3>Cloning Pod Status</h3>"
|
||||
dat += "<div class='statusDisplay'>[temp] </div>"
|
||||
|
||||
switch(src.menu)
|
||||
if(1)
|
||||
// Modules
|
||||
if (isnull(src.scanner) || !LAZYLEN(pods))
|
||||
dat += "<h3>Modules</h3>"
|
||||
//dat += "<a href='byond://?src=[REF(src)];relmodules=1'>Reload Modules</a>"
|
||||
if (isnull(src.scanner))
|
||||
dat += "<font class='bad'>ERROR: No Scanner detected!</font><br>"
|
||||
if (!LAZYLEN(pods))
|
||||
dat += "<font class='bad'>ERROR: No Pod detected</font><br>"
|
||||
|
||||
// Scanner
|
||||
if (!isnull(src.scanner))
|
||||
var/mob/living/scanner_occupant = get_mob_or_brainmob(scanner.occupant)
|
||||
|
||||
dat += "<h3>Scanner Functions</h3>"
|
||||
|
||||
dat += "<div class='statusDisplay'>"
|
||||
if(!scanner_occupant)
|
||||
dat += "Scanner Unoccupied"
|
||||
else if(loading)
|
||||
dat += "[scanner_occupant] => Scanning..."
|
||||
else
|
||||
if(use_records)
|
||||
if(scanner_occupant.ckey != scantemp_ckey || scanner_occupant.name != scantemp_name)
|
||||
scantemp = "Ready to Scan"
|
||||
scantemp_ckey = scanner_occupant.ckey
|
||||
scantemp_name = scanner_occupant.name
|
||||
else
|
||||
scantemp = "Ready to Clone"
|
||||
dat += "[scanner_occupant] => [scantemp]"
|
||||
dat += "</div>"
|
||||
|
||||
if(scanner_occupant)
|
||||
dat += "<a href='byond://?src=[REF(src)];scan=1'>[use_records ? "Start Scan" : "Clone"]</a>"
|
||||
dat += "<br><a href='byond://?src=[REF(src)];lock=1'>[scanner.locked ? "Unlock Scanner" : "Lock Scanner"]</a>"
|
||||
else
|
||||
dat += "<span class='linkOff'>[use_records ? "Start Scan" : "Clone"]</span>"
|
||||
if(use_records)
|
||||
// Database
|
||||
dat += "<h3>Database Functions</h3>"
|
||||
if (src.records.len && src.records.len > 0)
|
||||
dat += "<a href='byond://?src=[REF(src)];menu=2'>View Records ([src.records.len])</a><br>"
|
||||
else
|
||||
dat += "<span class='linkOff'>View Records (0)</span><br>"
|
||||
if (src.diskette)
|
||||
dat += "<a href='byond://?src=[REF(src)];disk=eject'>Eject Disk</a><br>"
|
||||
|
||||
|
||||
|
||||
if(2)
|
||||
dat += "<h3>Current records</h3>"
|
||||
dat += "<a href='byond://?src=[REF(src)];menu=1'><< Back</a><br><br>"
|
||||
data["hasAutoprocess"] = TRUE
|
||||
if(length(records))
|
||||
for(var/datum/data/record/R in records)
|
||||
dat += "<h4>[R.fields["name"]]</h4>Scan ID [R.fields["id"]] <a href='byond://?src=[REF(src)];view_rec=[R.fields["id"]]'>View Record</a>"
|
||||
if(3)
|
||||
dat += "<h3>Selected Record</h3>"
|
||||
dat += "<a href='byond://?src=[REF(src)];menu=2'><< Back</a><br>"
|
||||
|
||||
if (!src.active_record)
|
||||
dat += "<font class='bad'>Record not found.</font>"
|
||||
else
|
||||
dat += "<h4>[src.active_record.fields["name"]]</h4>"
|
||||
dat += "Scan ID [src.active_record.fields["id"]] <a href='byond://?src=[REF(src)];clone=[active_record.fields["id"]]'>Clone</a><br>"
|
||||
|
||||
var/obj/item/implant/health/H = locate(active_record.fields["imp"])
|
||||
|
||||
if ((H) && (istype(H)))
|
||||
dat += "<b>Health Implant Data:</b><br />[H.sensehealth()]<br><br />"
|
||||
var/list/record_entry = list()
|
||||
record_entry["name"] = "[R.fields["name"]]"
|
||||
record_entry["id"] = "[R.fields["id"]]"
|
||||
var/obj/item/implant/health/H = locate(R.fields["imp"])
|
||||
if(H && istype(H))
|
||||
record_entry["damages"] = H.sensehealth(TRUE)
|
||||
else
|
||||
dat += "<font class='bad'>Unable to locate Health Implant.</font><br /><br />"
|
||||
record_entry["damages"] = FALSE
|
||||
record_entry["UI"] = "[R.fields["UI"]]"
|
||||
record_entry["UE"] = "[R.fields["UE"]]"
|
||||
record_entry["blood_type"] = "[R.fields["blood_type"]]"
|
||||
records_to_send += list(record_entry)
|
||||
data["records"] = records_to_send
|
||||
else
|
||||
data["records"] = list()
|
||||
if(diskette && diskette.fields)
|
||||
var/list/disk_data = list()
|
||||
disk_data["name"] = "[diskette.fields["name"]]"
|
||||
disk_data["id"] = "[diskette.fields["id"]]"
|
||||
disk_data["UI"] = "[diskette.fields["UI"]]"
|
||||
disk_data["UE"] = "[diskette.fields["UE"]]"
|
||||
disk_data["blood_type"] = "[diskette.fields["blood_type"]]"
|
||||
data["diskData"] = disk_data
|
||||
else
|
||||
data["diskData"] = list()
|
||||
else
|
||||
data["hasAutoprocess"] = FALSE
|
||||
data["autoprocess"] = autoprocess
|
||||
var/list/lack_machine = list()
|
||||
if(isnull(src.scanner))
|
||||
lack_machine += "ERROR: No Scanner Detected!"
|
||||
if(!LAZYLEN(pods))
|
||||
lack_machine += "ERROR: No Pod Detected!"
|
||||
data["lacksMachine"] = lack_machine
|
||||
data["temp"] = temp
|
||||
var/build_temp = null
|
||||
var/mob/living/scanner_occupant = get_mob_or_brainmob(scanner?.occupant)
|
||||
if(scanner_occupant?.ckey != scantemp_ckey || scanner_occupant?.name != scantemp_name)
|
||||
if(use_records)
|
||||
build_temp = "Ready to Scan"
|
||||
scantemp_ckey = scanner_occupant?.ckey
|
||||
scantemp_name = scanner_occupant?.name
|
||||
else
|
||||
build_temp = "Ready to Clone"
|
||||
scantemp = "[scanner_occupant] => [build_temp]"
|
||||
data["scanTemp"] = scantemp
|
||||
data["scannerLocked"] = scanner?.locked
|
||||
data["hasOccupant"] = scanner?.occupant
|
||||
data["recordsLength"] = "View Records ([length(records)])"
|
||||
|
||||
dat += "<b>Unique Identifier:</b><br /><span class='highlight'>[src.active_record.fields["UI"]]</span><br>"
|
||||
dat += "<b>Structural Enzymes:</b><br /><span class='highlight'>[src.active_record.fields["SE"]]</span><br>"
|
||||
return data
|
||||
|
||||
if(diskette && diskette.fields)
|
||||
dat += "<div class='block'>"
|
||||
dat += "<h4>Inserted Disk</h4>"
|
||||
dat += "<b>Contents:</b> "
|
||||
var/list/L = list()
|
||||
if(diskette.fields["UI"])
|
||||
L += "Unique Identifier"
|
||||
if(diskette.fields["UE"] && diskette.fields["name"] && diskette.fields["blood_type"])
|
||||
L += "Unique Enzymes"
|
||||
if(diskette.fields["SE"])
|
||||
L += "Structural Enzymes"
|
||||
dat += english_list(L, "Empty", " + ", " + ")
|
||||
dat += "<br /><a href='byond://?src=[REF(src)];disk=load'>Load from Disk</a>"
|
||||
|
||||
dat += "<br /><a href='byond://?src=[REF(src)];disk=save'>Save to Disk</a>"
|
||||
dat += "</div>"
|
||||
|
||||
dat += "<font size=1><a href='byond://?src=[REF(src)];del_rec=1'>Delete Record</a></font>"
|
||||
|
||||
if(4)
|
||||
if (!src.active_record)
|
||||
src.menu = 2
|
||||
dat = "[src.temp]<br>"
|
||||
dat += "<h3>Confirm Record Deletion</h3>"
|
||||
|
||||
dat += "<b><a href='byond://?src=[REF(src)];del_rec=1'>Scan card to confirm.</a></b><br>"
|
||||
dat += "<b><a href='byond://?src=[REF(src)];menu=3'>Cancel</a></b>"
|
||||
|
||||
|
||||
var/datum/browser/popup = new(user, "cloning", "Cloning System Control")
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/cloning/Topic(href, href_list)
|
||||
/obj/machinery/computer/cloning/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("toggle_autoprocess")
|
||||
Toggle_autoprocess(usr)
|
||||
if("scan")
|
||||
Scan(usr)
|
||||
if("toggle_lock")
|
||||
Toggle_lock(usr)
|
||||
if("clone")
|
||||
Clone(usr, params["target"])
|
||||
if("delrecord")
|
||||
DeleteRecord(usr, params["target"])
|
||||
if("save")
|
||||
Save(usr, params["target"])
|
||||
if("load")
|
||||
Load(usr)
|
||||
if("eject")
|
||||
EjectDisk(usr)
|
||||
|
||||
if(loading)
|
||||
/obj/machinery/computer/cloning/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(href_list["task"])
|
||||
switch(href_list["task"])
|
||||
if("autoprocess")
|
||||
if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL)
|
||||
autoprocess = TRUE
|
||||
START_PROCESSING(SSmachines, src)
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
if("stopautoprocess")
|
||||
autoprocess = FALSE
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
src.updateUsrDialog()
|
||||
. = TRUE
|
||||
|
||||
else if ((href_list["scan"]) && !isnull(scanner) && scanner.is_operational())
|
||||
scantemp = ""
|
||||
|
||||
loading = TRUE
|
||||
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
|
||||
say("Initiating scan...")
|
||||
var/prev_locked = scanner.locked
|
||||
scanner.locked = TRUE
|
||||
src.updateUsrDialog()
|
||||
addtimer(CALLBACK(src, .proc/finish_scan, scanner.occupant, prev_locked), 2 SECONDS)
|
||||
. = TRUE
|
||||
|
||||
//No locking an open scanner.
|
||||
else if ((href_list["lock"]) && !isnull(scanner) && scanner.is_operational())
|
||||
if ((!scanner.locked) && (scanner.occupant))
|
||||
scanner.locked = TRUE
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else
|
||||
scanner.locked = FALSE
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
src.updateUsrDialog()
|
||||
. = TRUE
|
||||
|
||||
|
||||
else if (href_list["refresh"])
|
||||
src.updateUsrDialog()
|
||||
playsound(src, "terminal_type", 25, 0)
|
||||
. = TRUE
|
||||
|
||||
if(. || !use_records)
|
||||
return
|
||||
if(href_list["view_rec"])
|
||||
playsound(src, "terminal_type", 25, 0)
|
||||
src.active_record = find_record("id", href_list["view_rec"], records)
|
||||
if(active_record)
|
||||
if(!active_record.fields["ckey"])
|
||||
records -= active_record
|
||||
active_record = null
|
||||
src.temp = "<font class='bad'>Record Corrupt</font>"
|
||||
else
|
||||
src.menu = 3
|
||||
else
|
||||
src.temp = "Record missing."
|
||||
src.updateUsrDialog()
|
||||
. = TRUE
|
||||
|
||||
else if (href_list["del_rec"])
|
||||
if ((!src.active_record) || (src.menu < 3))
|
||||
return
|
||||
if (src.menu == 3) //If we are viewing a record, confirm deletion
|
||||
src.temp = "Delete record?"
|
||||
src.menu = 4
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
|
||||
|
||||
else if (src.menu == 4)
|
||||
var/obj/item/card/id/C = usr.get_active_held_item()
|
||||
if (istype(C)||istype(C, /obj/item/pda))
|
||||
if(src.check_access(C))
|
||||
src.temp = "[src.active_record.fields["name"]] => Record deleted."
|
||||
src.records.Remove(active_record)
|
||||
active_record = null
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
src.menu = 2
|
||||
var/obj/item/circuitboard/computer/cloning/board = circuit
|
||||
board.records = records
|
||||
else
|
||||
src.temp = "<font class='bad'>Access Denied.</font>"
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
. = TRUE
|
||||
|
||||
else if (href_list["disk"] && use_records) //Load or eject.
|
||||
switch(href_list["disk"])
|
||||
if("load")
|
||||
if (!diskette || !istype(diskette.fields) || !diskette.fields["name"] || !diskette.fields)
|
||||
src.temp = "<font class='bad'>Load error.</font>"
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
if (!src.active_record)
|
||||
src.temp = "<font class='bad'>Record error.</font>"
|
||||
src.menu = 1
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
|
||||
for(var/key in diskette.fields)
|
||||
src.active_record.fields[key] = diskette.fields[key]
|
||||
src.temp = "Load successful."
|
||||
src.updateUsrDialog()
|
||||
var/obj/item/circuitboard/computer/cloning/board = circuit
|
||||
board.records = records
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
if("eject")
|
||||
if(src.diskette)
|
||||
src.diskette.forceMove(drop_location())
|
||||
src.diskette = null
|
||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
|
||||
if("save")
|
||||
if(!diskette || diskette.read_only || !active_record || !active_record.fields)
|
||||
src.temp = "<font class='bad'>Save error.</font>"
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
|
||||
diskette.fields = active_record.fields.Copy()
|
||||
diskette.name = "data disk - '[src.diskette.fields["name"]]'"
|
||||
src.temp = "Save successful."
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
. = TRUE
|
||||
|
||||
else if (href_list["clone"])
|
||||
var/datum/data/record/C = find_record("id", href_list["clone"], records)
|
||||
//Look for that player! They better be dead!
|
||||
if(C)
|
||||
var/obj/machinery/clonepod/pod = GetAvailablePod()
|
||||
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
|
||||
if(!LAZYLEN(pods))
|
||||
temp = "<font class='bad'>No Clonepods detected.</font>"
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(!pod)
|
||||
temp = "<font class='bad'>No Clonepods available.</font>"
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(!CONFIG_GET(flag/revival_cloning))
|
||||
temp = "<font class='bad'>Unable to initiate cloning cycle.</font>"
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(pod.occupant)
|
||||
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["blood_type"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["quirks"], C.fields["bank_account"], C.fields["traumas"]))
|
||||
temp = "[C.fields["name"]] => <font class='good'>Cloning cycle in progress...</font>"
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
records.Remove(C)
|
||||
if(active_record == C)
|
||||
active_record = null
|
||||
menu = 1
|
||||
src.updateUsrDialog()
|
||||
else
|
||||
temp = "[C.fields["name"]] => <font class='bad'>Initialisation failure.</font>"
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
|
||||
else
|
||||
temp = "<font class='bad'>Data corruption.</font>"
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
. = TRUE
|
||||
|
||||
else if (href_list["menu"] && use_records)
|
||||
menu = text2num(href_list["menu"])
|
||||
src.updateUsrDialog()
|
||||
playsound(src, "terminal_type", 25, 0)
|
||||
. = TRUE
|
||||
updatemodules(TRUE)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "CloningConsole", "Cloning System Control")
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/computer/cloning/proc/finish_scan(mob/living/L, prev_locked)
|
||||
if(!scanner || !L)
|
||||
@@ -469,7 +390,6 @@
|
||||
|
||||
loading = FALSE
|
||||
scanner.locked = prev_locked
|
||||
src.updateUsrDialog()
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
|
||||
@@ -568,44 +488,44 @@
|
||||
var/obj/machinery/clonepod/pod = GetAvailablePod()
|
||||
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
|
||||
if(!LAZYLEN(pods))
|
||||
temp = "<font class='bad'>No Clonepods detected.</font>"
|
||||
temp = "No Clonepods detected."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(!pod)
|
||||
temp = "<font class='bad'>No Clonepods available.</font>"
|
||||
temp = "No Clonepods available."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else if(pod.occupant)
|
||||
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
|
||||
temp = "Cloning cycle already in progress."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
else
|
||||
pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction)
|
||||
temp = "[mob_occupant.real_name] => <font class='good'>Cloning data sent to pod.</font>"
|
||||
temp = "[mob_occupant.real_name] => Cloning data sent to pod."
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
/obj/machinery/computer/cloning/proc/can_scan(datum/dna/dna, mob/living/mob_occupant, experimental = FALSE, datum/bank_account/account)
|
||||
if(!istype(dna))
|
||||
scantemp = "<font class='bad'>Unable to locate valid genetic data.</font>"
|
||||
scantemp = "Unable to locate valid genetic data."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
if(!experimental)
|
||||
if(mob_occupant.suiciding || mob_occupant.hellbound)
|
||||
scantemp = "<font class='bad'>Subject's brain is not responding to scanning stimuli.</font>"
|
||||
scantemp = "Subject's brain is not responding to scanning stimuli."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
if((HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) && (src.scanner.scan_level < 2))
|
||||
scantemp = "<font class='bad'>Subject no longer contains the fundamental materials required to create a living clone.</font>"
|
||||
scantemp = "Subject no longer contains the fundamental materials required to create a living clone."
|
||||
playsound(src, 'sound/machines/terminal_alert.ogg', 50, 0)
|
||||
return
|
||||
if (!experimental)
|
||||
if(!mob_occupant.ckey || !mob_occupant.client)
|
||||
scantemp = "<font class='bad'>Mental interface failure.</font>"
|
||||
scantemp = "Mental interface failure."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
if (find_record("ckey", mob_occupant.ckey, records))
|
||||
scantemp = "<font class='average'>Subject already in database.</font>"
|
||||
scantemp = "Subject already in database."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
if(SSeconomy.full_ancap && !account)
|
||||
scantemp = "<font class='average'>Subject is either missing an ID card with a bank account on it, or does not have an account to begin with. Please ensure the ID card is on the body before attempting to scan.</font>"
|
||||
scantemp = "Subject is either missing an ID card with a bank account on it, or does not have an account to begin with. Please ensure the ID card is on the body before attempting to scan."
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
return TRUE
|
||||
@@ -618,3 +538,4 @@
|
||||
icon_keyboard = "med_key"
|
||||
circuit = /obj/item/circuitboard/computer/cloning/prototype
|
||||
clonepod_type = /obj/machinery/clonepod/experimental
|
||||
use_records = FALSE //Wait, so you tell me it lacks records but you never set it as false?
|
||||
|
||||
@@ -102,8 +102,14 @@
|
||||
if ("answerMessage")
|
||||
if (!authenticated(usr))
|
||||
return
|
||||
var/answer_index = text2num(params["answer"])
|
||||
var/message_index = text2num(params["message"])
|
||||
var/answer_index = params["answer"]
|
||||
var/message_index = params["message"]
|
||||
|
||||
// If either of these aren't numbers, then bad voodoo.
|
||||
if(!isnum(answer_index) || !isnum(message_index))
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] provided an invalid index type when replying to a message on [src] [ADMIN_JMP(src)]. This should not happen. Please check with a maintainer and/or consult tgui logs.")
|
||||
CRASH("Non-numeric index provided when answering comms console message.")
|
||||
|
||||
if (!answer_index || !message_index || answer_index < 1 || message_index < 1)
|
||||
return
|
||||
var/datum/comm_message/message = messages[message_index]
|
||||
@@ -156,7 +162,11 @@
|
||||
if ("deleteMessage")
|
||||
if (!authenticated(usr))
|
||||
return
|
||||
var/message_index = text2num(params["message"])
|
||||
var/message_index = params["message"]
|
||||
|
||||
if(!isnum(message_index))
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] provided an invalid index type when deleting a message on [src] [ADMIN_JMP(src)]. This should not happen. Please check with a maintainer and/or consult tgui logs.")
|
||||
CRASH("Non-numeric index provided when deleting comms console message.")
|
||||
if (!message_index)
|
||||
return
|
||||
LAZYREMOVE(messages, LAZYACCESS(messages, message_index))
|
||||
|
||||
+234
-195
@@ -1,9 +1,5 @@
|
||||
#define LIMBGROWER_MAIN_MENU 1
|
||||
#define LIMBGROWER_CATEGORY_MENU 2
|
||||
#define LIMBGROWER_CHEMICAL_MENU 3
|
||||
//use these for the menu system
|
||||
|
||||
|
||||
/// The limbgrower. Makes organd and limbs with synthflesh and chems.
|
||||
/// See [limbgrower_designs.dm] for everything we can make.
|
||||
/obj/machinery/limbgrower
|
||||
name = "limb grower"
|
||||
desc = "It grows new limbs using Synthflesh."
|
||||
@@ -15,161 +11,235 @@
|
||||
active_power_usage = 100
|
||||
circuit = /obj/item/circuitboard/machine/limbgrower
|
||||
|
||||
var/operating = FALSE
|
||||
var/disabled = FALSE
|
||||
/// The category of limbs we're browing in our UI.
|
||||
var/selected_category = "human"
|
||||
/// If we're currently printing something.
|
||||
var/busy = FALSE
|
||||
var/prod_coeff = 1
|
||||
/// How efficient our machine is. Better parts = less chemicals used and less power used. Range of 1 to 0.25.
|
||||
var/production_coefficient = 1
|
||||
/// How long it takes for us to print a limb. Affected by production_coefficient.
|
||||
var/production_speed = 3 SECONDS
|
||||
/// The design we're printing currently.
|
||||
var/datum/design/being_built
|
||||
/// Our internal techweb for limbgrower designs.
|
||||
var/datum/techweb/stored_research
|
||||
var/selected_category
|
||||
var/screen = 1
|
||||
/// All the categories of organs we can print.
|
||||
var/list/categories = list(
|
||||
"human" = /datum/species/human,
|
||||
"lizard" = /datum/species/lizard,
|
||||
"mammal" = /datum/species/mammal,
|
||||
"insect" = /datum/species/insect,
|
||||
"fly" = /datum/species/fly,
|
||||
"plasmaman" = /datum/species/plasmaman,
|
||||
"xeno" = /datum/species/xeno,
|
||||
"other" = /datum/species,
|
||||
)
|
||||
var/list/stored_species = list()
|
||||
"human",
|
||||
"lizard",
|
||||
"mammal",
|
||||
"insect",
|
||||
"fly",
|
||||
"plasmaman",
|
||||
"xeno",
|
||||
"other",
|
||||
)
|
||||
var/obj/item/disk/data/dna_disk
|
||||
|
||||
/obj/machinery/limbgrower/Initialize()
|
||||
create_reagents(100, OPENCONTAINER)
|
||||
stored_research = new /datum/techweb/specialized/autounlocking/limbgrower
|
||||
for(var/i in categories)
|
||||
var/species = categories[i]
|
||||
stored_species[i] = new species()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/plumbing/simple_demand)
|
||||
AddComponent(/datum/component/simple_rotation, ROTATION_WRENCH | ROTATION_CLOCKWISE, null, CALLBACK(src, .proc/can_be_rotated))
|
||||
|
||||
/obj/machinery/limbgrower/ui_interact(mob/user)
|
||||
/obj/machinery/limbgrower/ui_interact(mob/user, datum/tgui/ui)
|
||||
. = ..()
|
||||
if(!is_operational())
|
||||
return
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Limbgrower", src)
|
||||
ui.open()
|
||||
|
||||
var/dat = main_win(user)
|
||||
/obj/machinery/limbgrower/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
switch(screen)
|
||||
if(LIMBGROWER_MAIN_MENU)
|
||||
dat = main_win(user)
|
||||
if(LIMBGROWER_CATEGORY_MENU)
|
||||
dat = category_win(user,selected_category)
|
||||
if(LIMBGROWER_CHEMICAL_MENU)
|
||||
dat = chemical_win(user)
|
||||
for(var/datum/reagent/reagent_id in reagents.reagent_list)
|
||||
var/list/reagent_data = list(
|
||||
reagent_name = reagent_id.name,
|
||||
reagent_amount = reagent_id.volume,
|
||||
reagent_type = reagent_id.type
|
||||
)
|
||||
data["reagents"] += list(reagent_data)
|
||||
|
||||
var/datum/browser/popup = new(user, "Limb Grower", name, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
data["total_reagents"] = reagents.total_volume
|
||||
data["max_reagents"] = reagents.maximum_volume
|
||||
data["busy"] = busy
|
||||
var/list/disk_data = list()
|
||||
disk_data["disk"] = dna_disk //Do i, the machine, have a disk?
|
||||
disk_data["name"] = dna_disk?.fields["name"] //Name for the human saved if there is one
|
||||
data["disk"] = disk_data
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/limbgrower/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["categories"] = list()
|
||||
|
||||
var/species_categories = categories.Copy()
|
||||
for(var/species in species_categories)
|
||||
species_categories[species] = list()
|
||||
for(var/design_id in stored_research.researched_designs)
|
||||
var/datum/design/limb_design = SSresearch.techweb_design_by_id(design_id)
|
||||
for(var/found_category in species_categories)
|
||||
if(found_category in limb_design.category)
|
||||
species_categories[found_category] += limb_design
|
||||
|
||||
for(var/category in species_categories)
|
||||
var/list/category_data = list(
|
||||
name = category,
|
||||
designs = list(),
|
||||
)
|
||||
for(var/datum/design/found_design in species_categories[category])
|
||||
var/list/all_reagents = list()
|
||||
for(var/reagent_typepath in found_design.reagents_list)
|
||||
var/datum/reagent/reagent_id = find_reagent_object_from_type(reagent_typepath)
|
||||
var/list/reagent_data = list(
|
||||
name = reagent_id.name,
|
||||
amount = (found_design.reagents_list[reagent_typepath] * production_coefficient),
|
||||
)
|
||||
all_reagents += list(reagent_data)
|
||||
|
||||
category_data["designs"] += list(list(
|
||||
parent_category = category,
|
||||
name = found_design.name,
|
||||
id = found_design.id,
|
||||
needed_reagents = all_reagents,
|
||||
))
|
||||
|
||||
data["categories"] += list(category_data)
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/limbgrower/on_deconstruction()
|
||||
for(var/obj/item/reagent_containers/glass/G in component_parts)
|
||||
reagents.trans_to(G, G.reagents.maximum_volume)
|
||||
for(var/obj/item/reagent_containers/glass/our_beaker in component_parts)
|
||||
reagents.trans_to(our_beaker, our_beaker.reagents.maximum_volume)
|
||||
..()
|
||||
|
||||
/obj/machinery/limbgrower/attackby(obj/item/O, mob/user, params)
|
||||
if(busy)
|
||||
/obj/machinery/limbgrower/attackby(obj/item/user_item, mob/living/user, params)
|
||||
if (busy)
|
||||
to_chat(user, "<span class=\"alert\">\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
return
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "limbgrower_panelopen", "limbgrower_idleoff", O))
|
||||
updateUsrDialog()
|
||||
if(default_deconstruction_screwdriver(user, "limbgrower_panelopen", "limbgrower_idleoff", user_item))
|
||||
ui_close(user)
|
||||
return
|
||||
|
||||
if(panel_open && default_deconstruction_crowbar(O))
|
||||
return
|
||||
|
||||
if(user.a_intent == INTENT_HARM) //so we can hit the machine
|
||||
if(user_item.tool_behaviour == TOOL_WRENCH && panel_open)
|
||||
return ..()
|
||||
|
||||
if(istype(O, /obj/item/disk))
|
||||
if(panel_open && default_deconstruction_crowbar(user_item))
|
||||
return
|
||||
|
||||
if(istype(user_item, /obj/item/disk))
|
||||
if(dna_disk)
|
||||
to_chat(user, "<span class='warning'>\The [src] already has a dna disk, take it out first!</span>")
|
||||
return
|
||||
else
|
||||
O.forceMove(src)
|
||||
dna_disk = O
|
||||
to_chat(user, "<span class='notice'>You insert \the [O] into \the [src].</span>")
|
||||
user_item.forceMove(src)
|
||||
dna_disk = user_item
|
||||
to_chat(user, "<span class='notice'>You insert \the [user_item] into \the [src].</span>")
|
||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
|
||||
return
|
||||
|
||||
/obj/machinery/limbgrower/Topic(href, href_list)
|
||||
if(..())
|
||||
if(user.a_intent != INTENT_HELP)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/limbgrower/proc/can_be_rotated()
|
||||
if(panel_open)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/limbgrower/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if (!busy)
|
||||
if(href_list["menu"])
|
||||
screen = text2num(href_list["menu"])
|
||||
|
||||
if(href_list["category"])
|
||||
selected_category = href_list["category"]
|
||||
if (busy)
|
||||
to_chat(usr, "<span class='danger'>\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
return
|
||||
|
||||
if(href_list["disposeI"]) //Get rid of a reagent incase you add the wrong one by mistake
|
||||
reagents.del_reagent(text2path(href_list["disposeI"]))
|
||||
switch(action)
|
||||
|
||||
if(href_list["make"])
|
||||
if("empty_reagent")
|
||||
reagents.del_reagent(text2path(params["reagent_type"]))
|
||||
. = TRUE
|
||||
|
||||
/////////////////
|
||||
//href protection
|
||||
being_built = stored_research.isDesignResearchedID(href_list["make"]) //check if it's a valid design
|
||||
if("eject_disk")
|
||||
eject_disk(usr)
|
||||
|
||||
if("make_limb")
|
||||
being_built = stored_research.isDesignResearchedID(params["design_id"])
|
||||
if(!being_built)
|
||||
return
|
||||
CRASH("[src] was passed an invalid design id!")
|
||||
|
||||
/// All the reagents we're using to make our organ.
|
||||
var/list/consumed_reagents_list = being_built.reagents_list.Copy()
|
||||
/// The amount of power we're going to use, based on how much reagent we use.
|
||||
var/power = 0
|
||||
|
||||
var/synth_cost = being_built.reagents_list[/datum/reagent/medicine/synthflesh]*prod_coeff
|
||||
var/power = max(2000, synth_cost/5)
|
||||
for(var/reagent_id in consumed_reagents_list)
|
||||
consumed_reagents_list[reagent_id] *= production_coefficient
|
||||
if(!reagents.has_reagent(reagent_id, consumed_reagents_list[reagent_id]))
|
||||
audible_message("<span class='notice'>\The [src] buzzes.</span>")
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
|
||||
return
|
||||
|
||||
if(reagents.has_reagent(/datum/reagent/medicine/synthflesh, being_built.reagents_list[/datum/reagent/medicine/synthflesh]*prod_coeff))
|
||||
busy = TRUE
|
||||
use_power(power)
|
||||
flick("limbgrower_fill",src)
|
||||
icon_state = "limbgrower_idleon"
|
||||
addtimer(CALLBACK(src, .proc/build_item),32*prod_coeff)
|
||||
power = max(2000, (power + consumed_reagents_list[reagent_id]))
|
||||
|
||||
if(href_list["dna_disk"])
|
||||
var/mob/living/carbon/user = usr
|
||||
if(istype(user))
|
||||
if(!dna_disk)
|
||||
var/obj/item/disk/diskette = user.get_active_held_item()
|
||||
if(istype(diskette))
|
||||
diskette.forceMove(src)
|
||||
dna_disk = diskette
|
||||
to_chat(user, "<span class='notice'>You insert \the [diskette] into \the [src].</span>")
|
||||
else
|
||||
dna_disk.forceMove(src.loc)
|
||||
user.put_in_active_hand(dna_disk)
|
||||
to_chat(user, "<span class='notice'>You remove \the [dna_disk] from \the [src].</span>")
|
||||
dna_disk = null
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You are unable to grasp \the [dna_disk] disk from \the [src].</span>")
|
||||
else
|
||||
to_chat(usr, "<span class=\"alert\">\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
busy = TRUE
|
||||
use_power(power)
|
||||
flick("limbgrower_fill",src)
|
||||
icon_state = "limbgrower_idleon"
|
||||
selected_category = params["active_tab"]
|
||||
addtimer(CALLBACK(src, .proc/build_item, consumed_reagents_list), production_speed * production_coefficient)
|
||||
. = TRUE
|
||||
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/limbgrower/proc/build_item()
|
||||
if(reagents.has_reagent(/datum/reagent/medicine/synthflesh, being_built.reagents_list[/datum/reagent/medicine/synthflesh]*prod_coeff)) //sanity check, if this happens we are in big trouble
|
||||
reagents.remove_reagent(/datum/reagent/medicine/synthflesh, being_built.reagents_list[/datum/reagent/medicine/synthflesh]*prod_coeff)
|
||||
var/buildpath = being_built.build_path
|
||||
if(ispath(buildpath, /obj/item/bodypart)) //This feels like spaghetti code, but i need to initiliaze a limb somehow
|
||||
build_limb(buildpath)
|
||||
else if(ispath(buildpath, /obj/item/organ/genital)) //genitals are uhh... customizable
|
||||
build_genital(buildpath)
|
||||
else
|
||||
//Just build whatever it is
|
||||
new buildpath(loc)
|
||||
else
|
||||
src.visible_message("<span class=\"error\"> Something went very wrong and there isnt enough synthflesh anymore!</span>")
|
||||
busy = FALSE
|
||||
flick("limbgrower_unfill",src)
|
||||
icon_state = "limbgrower_idleoff"
|
||||
updateUsrDialog()
|
||||
/*
|
||||
* The process of beginning to build a limb or organ.
|
||||
* Goes through and sanity checks that we actually have enough reagent to build our item.
|
||||
* Then, remove those reagents from our reagents datum.
|
||||
*
|
||||
* After the reagents are handled, we can proceede with making the limb or organ. (Limbs are handled in a separate proc)
|
||||
*
|
||||
* modified_consumed_reagents_list - the list of reagents we will consume on build, modified by the production coefficient.
|
||||
*/
|
||||
/obj/machinery/limbgrower/proc/build_item(list/modified_consumed_reagents_list)
|
||||
for(var/reagent_id in modified_consumed_reagents_list)
|
||||
if(!reagents.has_reagent(reagent_id, modified_consumed_reagents_list[reagent_id]))
|
||||
audible_message("<span class='notice'>\The [src] buzzes.</span>")
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
|
||||
break
|
||||
|
||||
/obj/machinery/limbgrower/proc/build_limb(buildpath)
|
||||
reagents.remove_reagent(reagent_id, modified_consumed_reagents_list[reagent_id])
|
||||
|
||||
var/built_typepath = being_built.build_path
|
||||
// If we have a bodypart, we need to initialize the limb on its own. Otherwise we can build it here.
|
||||
if(ispath(built_typepath, /obj/item/bodypart))
|
||||
build_limb(built_typepath)
|
||||
else if(ispath(built_typepath, /obj/item/organ/genital)) //genitals are uhh... customizable
|
||||
build_genital(built_typepath)
|
||||
else
|
||||
new built_typepath(loc)
|
||||
|
||||
busy = FALSE
|
||||
flick("limbgrower_unfill", src)
|
||||
icon_state = "limbgrower_idleoff"
|
||||
|
||||
/*
|
||||
* The process of putting together a limb.
|
||||
* This is called from after we remove the reagents, so this proc is just initializing the limb type.
|
||||
*
|
||||
* This proc handles skin / mutant color, greyscaling, names and descriptions, and various other limb creation steps.
|
||||
*
|
||||
* built_typepath - the path of the bodypart we're building.
|
||||
*/
|
||||
/obj/machinery/limbgrower/proc/build_limb(built_typepath)
|
||||
//i need to create a body part manually using a set icon (otherwise it doesnt appear)
|
||||
var/obj/item/bodypart/limb
|
||||
var/datum/species/selected = stored_species[selected_category]
|
||||
limb = new buildpath(loc)
|
||||
var/datum/species/selected = GLOB.species_datums[selected_category]
|
||||
limb = new built_typepath(loc)
|
||||
limb.base_bp_icon = selected.icon_limbs || DEFAULT_BODYPART_ICON_ORGANIC
|
||||
limb.species_id = selected.limbs_id
|
||||
limb.color_src = (MUTCOLORS in selected.species_traits ? MUTCOLORS : (selected.use_skintones ? SKINTONE : FALSE))
|
||||
@@ -189,135 +259,103 @@
|
||||
BP.name = "\improper synthetic [lowertext(selected.name)] [limb.name]"
|
||||
BP.desc = "A synthetic [selected_category] limb that will morph on its first use in surgery. This one is for the [parse_zone(limb.body_zone)]."
|
||||
|
||||
/obj/machinery/limbgrower/proc/build_genital(buildpath)
|
||||
/*
|
||||
* Builds genitals, modifies to be the same
|
||||
* as the person's cloning data on the data disk
|
||||
*/
|
||||
/obj/machinery/limbgrower/proc/build_genital(built_typepath)
|
||||
//i needed to create a way to customize gene tools using dna
|
||||
var/list/features = dna_disk?.fields["features"]
|
||||
if(length(features))
|
||||
switch(buildpath)
|
||||
switch(built_typepath)
|
||||
if(/obj/item/organ/genital/penis)
|
||||
var/obj/item/organ/genital/penis/penis = new(loc)
|
||||
if(features["has_cock"])
|
||||
penis.shape = features["cock_shape"]
|
||||
penis.length = features["cock_shape"]
|
||||
penis.diameter_ratio = features["cock_diameter_ratio"]
|
||||
penis.color = sanitize_hexcolor(features["cock_color"], 6)
|
||||
penis.update_icon()
|
||||
penis.color = sanitize_hexcolor(features["cock_color"], 6, TRUE)
|
||||
penis.update()
|
||||
if(/obj/item/organ/genital/testicles)
|
||||
var/obj/item/organ/genital/testicles/balls = new(loc)
|
||||
if(features["has_balls"])
|
||||
balls.color = sanitize_hexcolor(features["balls_color"], 6)
|
||||
balls.color = sanitize_hexcolor(features["balls_color"], 6, TRUE)
|
||||
balls.shape = features["balls_shape"]
|
||||
balls.size = features["balls_size"]
|
||||
balls.fluid_rate = features["balls_cum_rate"]
|
||||
balls.fluid_mult = features["balls_cum_mult"]
|
||||
balls.fluid_efficiency = features["balls_efficiency"]
|
||||
balls.update()
|
||||
if(/obj/item/organ/genital/vagina)
|
||||
var/obj/item/organ/genital/vagina/vegana = new(loc)
|
||||
if(features["has_vagina"])
|
||||
vegana.color = sanitize_hexcolor(features["vag_color"], 6)
|
||||
if(features["has_vag"])
|
||||
vegana.color = sanitize_hexcolor(features["vag_color"], 6, TRUE)
|
||||
vegana.shape = features["vag_shape"]
|
||||
vegana.update()
|
||||
if(/obj/item/organ/genital/breasts)
|
||||
var/obj/item/organ/genital/breasts/boobs = new(loc)
|
||||
if(features["has_breasts"])
|
||||
boobs.color = sanitize_hexcolor(features["breasts_color"], 6)
|
||||
boobs.color = sanitize_hexcolor(features["breasts_color"], 6, TRUE)
|
||||
boobs.size = features["breasts_size"]
|
||||
boobs.shape = features["breasts_shape"]
|
||||
if(!features["breasts_producing"])
|
||||
boobs.genital_flags &= ~(GENITAL_FUID_PRODUCTION|CAN_CLIMAX_WITH|CAN_MASTURBATE_WITH)
|
||||
boobs.update()
|
||||
else
|
||||
new buildpath(loc)
|
||||
new built_typepath(loc)
|
||||
else
|
||||
new buildpath(loc)
|
||||
new built_typepath(loc)
|
||||
|
||||
/obj/machinery/limbgrower/RefreshParts()
|
||||
reagents.maximum_volume = 0
|
||||
for(var/obj/item/reagent_containers/glass/G in component_parts)
|
||||
reagents.maximum_volume += G.volume
|
||||
G.reagents.trans_to(src, G.reagents.total_volume)
|
||||
var/T=1.2
|
||||
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
||||
T -= M.rating*0.2
|
||||
prod_coeff = min(1,max(0,T)) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4
|
||||
for(var/obj/item/reagent_containers/glass/our_beaker in component_parts)
|
||||
reagents.maximum_volume += our_beaker.volume
|
||||
our_beaker.reagents.trans_to(src, our_beaker.reagents.total_volume)
|
||||
production_coefficient = 1.2
|
||||
for(var/obj/item/stock_parts/manipulator/our_manipulator in component_parts)
|
||||
production_coefficient -= our_manipulator.rating * 0.2
|
||||
production_coefficient = clamp(production_coefficient, 0, 1) // coefficient goes from 1 -> 0.8 -> 0.6 -> 0.4
|
||||
|
||||
/obj/machinery/limbgrower/examine(mob/user)
|
||||
. = ..()
|
||||
if(!panel_open)
|
||||
. += "<span class='notice'>It looks like as if the panel were open you could rotate it with a <b>wrench</b>.</span>"
|
||||
else
|
||||
. += "<span class='notice'>The panel is open.</span>"
|
||||
if(in_range(user, src) || isobserver(user))
|
||||
. += "<span class='notice'>The status display reads: Storing up to <b>[reagents.maximum_volume]u</b> of synthflesh.<br>Synthflesh consumption at <b>[prod_coeff*100]%</b>.<span>"
|
||||
. += "<span class='notice'>The status display reads: Storing up to <b>[reagents.maximum_volume]u</b> of reagents.<br>Reagent consumption rate at <b>[production_coefficient * 100]%</b>.</span>"
|
||||
|
||||
/obj/machinery/limbgrower/proc/main_win(mob/user)
|
||||
var/dat = "<div class='statusDisplay'><h3>[src] Menu:</h3><br>"
|
||||
dat += "<A href='?src=[REF(src)];dna_disk=1'>[dna_disk ? "Remove" : "Insert"] cloning data disk</A>"
|
||||
dat += "<hr>"
|
||||
dat += "<A href='?src=[REF(src)];menu=[LIMBGROWER_CHEMICAL_MENU]'>Chemical Storage</A>"
|
||||
dat += materials_printout()
|
||||
dat += "<table style='width:100%' align='center'><tr>"
|
||||
|
||||
for(var/C in categories)
|
||||
dat += "<td><A href='?src=[REF(src)];category=[C];menu=[LIMBGROWER_CATEGORY_MENU]'>[C]</A></td>"
|
||||
dat += "</tr><tr>"
|
||||
//one category per line
|
||||
|
||||
dat += "</tr></table></div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/limbgrower/proc/category_win(mob/user,selected_category)
|
||||
var/dat = "<A href='?src=[REF(src)];menu=[LIMBGROWER_MAIN_MENU]'>Return to main menu</A>"
|
||||
dat += "<div class='statusDisplay'><h3>Browsing [selected_category]:</h3><br>"
|
||||
dat += materials_printout()
|
||||
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(v)
|
||||
if(!(selected_category in D.category))
|
||||
continue
|
||||
if(disabled || !can_build(D))
|
||||
dat += "<span class='linkOff'>[D.name]</span>"
|
||||
else
|
||||
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
|
||||
dat += "[get_design_cost(D)]<br>"
|
||||
|
||||
dat += "</div>"
|
||||
return dat
|
||||
|
||||
|
||||
/obj/machinery/limbgrower/proc/chemical_win(mob/user)
|
||||
var/dat = "<A href='?src=[REF(src)];menu=[LIMBGROWER_MAIN_MENU]'>Return to main menu</A>"
|
||||
dat += "<div class='statusDisplay'><h3>Browsing Chemical Storage:</h3><br>"
|
||||
dat += materials_printout()
|
||||
|
||||
for(var/datum/reagent/R in reagents.reagent_list)
|
||||
dat += "[R.name]: [R.volume]"
|
||||
dat += "<A href='?src=[REF(src)];disposeI=[R]'>Purge</A><BR>"
|
||||
|
||||
dat += "</div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/limbgrower/proc/materials_printout()
|
||||
var/dat = "<b>Total amount:></b> [reagents.total_volume] / [reagents.maximum_volume] cm<sup>3</sup><br>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/limbgrower/proc/can_build(datum/design/D)
|
||||
return (reagents.has_reagent(/datum/reagent/medicine/synthflesh, D.reagents_list[/datum/reagent/medicine/synthflesh]*prod_coeff)) //Return whether the machine has enough synthflesh to produce the design
|
||||
|
||||
/obj/machinery/limbgrower/proc/get_design_cost(datum/design/D)
|
||||
var/dat
|
||||
if(D.reagents_list[/datum/reagent/medicine/synthflesh])
|
||||
dat += "[D.reagents_list[/datum/reagent/medicine/synthflesh] * prod_coeff] Synthetic flesh "
|
||||
return dat
|
||||
/*
|
||||
* Checks our reagent list to see if a design can be built.
|
||||
*
|
||||
* limb_design - the design we're checking for buildability.
|
||||
*
|
||||
* returns TRUE if we have enough reagent to build it. Returns FALSE if we do not.
|
||||
*/
|
||||
/obj/machinery/limbgrower/proc/can_build(datum/design/limb_design)
|
||||
for(var/datum/reagent/reagent_id in limb_design.reagents_list)
|
||||
if(!reagents.has_reagent(reagent_id, limb_design.reagents_list[reagent_id] * production_coefficient))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Emagging a limbgrower allows you to build synthetic armblades.
|
||||
/obj/machinery/limbgrower/emag_act(mob/user)
|
||||
. = ..()
|
||||
if(obj_flags & EMAGGED)
|
||||
return
|
||||
for(var/id in SSresearch.techweb_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(id)
|
||||
if((D.build_type & LIMBGROWER) && ("emagged" in D.category))
|
||||
stored_research.add_design(D)
|
||||
for(var/design_id in SSresearch.techweb_designs)
|
||||
var/datum/design/found_design = SSresearch.techweb_design_by_id(design_id)
|
||||
if((found_design.build_type & LIMBGROWER) && ("emagged" in found_design.category))
|
||||
stored_research.add_design(found_design)
|
||||
to_chat(user, "<span class='warning'>A warning flashes onto the screen, stating that safety overrides have been deactivated!</span>")
|
||||
obj_flags |= EMAGGED
|
||||
return TRUE
|
||||
update_static_data(user)
|
||||
|
||||
/obj/machinery/limbgrower/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
eject_disk(user)
|
||||
|
||||
/obj/machinery/limbgrower/proc/eject_disk(mob/user)
|
||||
if(istype(user) && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
||||
if(busy)
|
||||
to_chat(user, "<span class=\"alert\">\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
@@ -326,6 +364,7 @@
|
||||
dna_disk.forceMove(src.loc)
|
||||
user.put_in_active_hand(dna_disk)
|
||||
to_chat(user, "<span class='notice'>You remove \the [dna_disk] from \the [src].</span>")
|
||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
|
||||
dna_disk = null
|
||||
else
|
||||
to_chat(user, "<span class='warning'>\The [src] has doesn't have a disk on it!")
|
||||
|
||||
@@ -21,6 +21,7 @@ Buildable meters
|
||||
level = 2
|
||||
var/piping_layer = PIPING_LAYER_DEFAULT
|
||||
var/RPD_type
|
||||
var/disposable = TRUE
|
||||
|
||||
/obj/item/pipe/directional
|
||||
RPD_type = PIPE_UNARY
|
||||
@@ -236,3 +237,22 @@ Buildable meters
|
||||
/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT)
|
||||
piping_layer = new_layer
|
||||
PIPING_LAYER_DOUBLE_SHIFT(src, piping_layer)
|
||||
|
||||
/obj/item/pipe/bluespace
|
||||
pipe_type = /obj/machinery/atmospherics/pipe/bluespace
|
||||
var/bluespace_network_name = "default"
|
||||
icon_state = "bluespace"
|
||||
disposable = FALSE
|
||||
|
||||
/obj/item/pipe/bluespace/attack_self(mob/user)
|
||||
var/new_name = input(user, "Enter identifier for bluespace pipe network", "bluespace pipe", bluespace_network_name) as text|null
|
||||
if(!isnull(new_name))
|
||||
bluespace_network_name = new_name
|
||||
|
||||
/obj/item/pipe/bluespace/make_from_existing(obj/machinery/atmospherics/pipe/bluespace/make_from)
|
||||
bluespace_network_name = make_from.bluespace_network_name
|
||||
return ..()
|
||||
|
||||
/obj/item/pipe/bluespace/build_pipe(obj/machinery/atmospherics/pipe/bluespace/A)
|
||||
A.bluespace_network_name = bluespace_network_name
|
||||
return ..()
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
if (. & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(get_charge())
|
||||
use_power(cell.charge*severity/100)
|
||||
use_power((cell.charge/3)*(severity*0.005))
|
||||
take_damage(severity/3, BURN, "energy", 1)
|
||||
mecha_log_message("EMP detected", color="red")
|
||||
|
||||
|
||||
@@ -375,12 +375,14 @@ GLOBAL_LIST_INIT(fluid_duct_recipes, list(
|
||||
. = TRUE
|
||||
|
||||
if((mode & DESTROY_MODE) && istype(A, /obj/item/pipe) || istype(A, /obj/structure/disposalconstruct) || istype(A, /obj/structure/c_transit_tube) || istype(A, /obj/structure/c_transit_tube_pod) || istype(A, /obj/item/pipe_meter))
|
||||
to_chat(user, "<span class='notice'>You start destroying a pipe...</span>")
|
||||
playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1)
|
||||
if(do_after(user, destroy_speed, target = A))
|
||||
activate()
|
||||
qdel(A)
|
||||
return
|
||||
var/obj/item/pipe/P = A
|
||||
if(!istype(P) || P.disposable)
|
||||
to_chat(user, "<span class='notice'>You start destroying a pipe...</span>")
|
||||
playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1)
|
||||
if(do_after(user, destroy_speed, target = A))
|
||||
activate()
|
||||
qdel(A)
|
||||
return
|
||||
|
||||
if((mode & PAINT_MODE))
|
||||
if(istype(A, /obj/machinery/atmospherics/pipe) && !istype(A, /obj/machinery/atmospherics/pipe/layer_manifold))
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
to_chat(user, "<span class='notice'>You need to get closer!</span>")
|
||||
return
|
||||
if(use_paint(user) && isturf(F))
|
||||
F.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', stored_decal_total, turn(stored_dir, -dir2angle(F.dir)), CLEAN_STRONG, color, null, null, alpha)
|
||||
F.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, CLEAN_STRONG, color, null, null, alpha)
|
||||
|
||||
/obj/item/airlock_painter/decal/attack_self(mob/user)
|
||||
if((ink) && (ink.charges >= 1))
|
||||
@@ -180,6 +180,11 @@
|
||||
stored_decal_total = "[stored_decal][yellow_fix][stored_color]"
|
||||
return
|
||||
|
||||
/obj/item/airlock_painter/decal/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/spritesheet/decals)
|
||||
)
|
||||
|
||||
/obj/item/airlock_painter/decal/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
@@ -189,6 +194,7 @@
|
||||
/obj/item/airlock_painter/decal/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["decal_direction"] = stored_dir
|
||||
data["decal_dir_text"] = dir2text(stored_dir)
|
||||
data["decal_color"] = stored_color
|
||||
data["decal_style"] = stored_decal
|
||||
data["decal_list"] = list()
|
||||
|
||||
@@ -75,18 +75,3 @@
|
||||
desc = "A chromosome that reduces action based mutation cooldowns by by 50%."
|
||||
icon_state = "energy"
|
||||
energy_coeff = 0.5
|
||||
|
||||
/obj/item/chromosome/reinforcer
|
||||
name = "reinforcement chromosome"
|
||||
desc = "A chromosome that renders mutations immune to mutadone."
|
||||
icon_state = "reinforcer"
|
||||
weight = 3
|
||||
|
||||
/obj/item/chromosome/reinforcer/can_apply(datum/mutation/human/HM)
|
||||
if(!HM || !(HM.can_chromosome == CHROMOSOME_NONE))
|
||||
return FALSE
|
||||
return !HM.mutadone_proof
|
||||
|
||||
/obj/item/chromosome/reinforcer/apply(datum/mutation/human/HM)
|
||||
HM.mutadone_proof = TRUE
|
||||
..()
|
||||
|
||||
@@ -210,8 +210,7 @@
|
||||
target.apply_effect(EFFECT_STUTTER, stunforce)
|
||||
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
|
||||
if(user)
|
||||
target.lastattacker = user.real_name
|
||||
target.lastattackerckey = user.ckey
|
||||
target.set_last_attacker(user)
|
||||
target.visible_message("<span class='danger'>[user] has shocked [target] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] has shocked you with [src]!</span>")
|
||||
log_combat(user, target, "stunned with an electrostaff")
|
||||
@@ -237,8 +236,7 @@
|
||||
target.adjustFireLoss(lethal_force) //good against ointment spam
|
||||
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
|
||||
if(user)
|
||||
target.lastattacker = user.real_name
|
||||
target.lastattackerckey = user.ckey
|
||||
target.set_last_attacker(user)
|
||||
target.visible_message("<span class='danger'>[user] has seared [target] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] has seared you with [src]!</span>")
|
||||
log_combat(user, target, "burned with an electrostaff")
|
||||
|
||||
@@ -312,7 +312,7 @@
|
||||
trap_damage = 0
|
||||
item_flags = DROPDEL
|
||||
flags_1 = NONE
|
||||
breakouttime = 50
|
||||
breakouttime = 25
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/energy/New()
|
||||
..()
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/datum/deathrattle_group
|
||||
var/name
|
||||
var/list/datum/weakref/implant_refs = list()
|
||||
|
||||
/datum/deathrattle_group/New()
|
||||
// Give the group a unique name for debugging, and possible future
|
||||
// use for making custom linked groups.
|
||||
name = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]"
|
||||
|
||||
/datum/deathrattle_group/proc/rattle(obj/item/implant/deathrattle/origin, mob/living/owner)
|
||||
var/name = owner.mind ? owner.mind.name : owner.real_name
|
||||
var/area = get_area_name(get_turf(owner))
|
||||
|
||||
for(var/r in implant_refs)
|
||||
var/datum/weakref/R = r
|
||||
|
||||
var/obj/item/implant/deathrattle/implant = R.resolve()
|
||||
if(!implant || implant == origin)
|
||||
continue
|
||||
|
||||
// Not all the implants may be actually implanted in people.
|
||||
if(!implant.imp_in)
|
||||
continue
|
||||
|
||||
// Deliberately the same message framing as nanite message + ghost deathrattle
|
||||
var/mob/living/recipient = implant.imp_in
|
||||
to_chat(recipient, "<i>You hear a strange, robotic voice in your head...</i> \"<span class='robot'><b>[name]</b> has died at <b>[area]</b>.</span>\"")
|
||||
SEND_SOUND(recipient, pick(
|
||||
'sound/items/knell1.ogg',
|
||||
'sound/items/knell2.ogg',
|
||||
'sound/items/knell3.ogg',
|
||||
'sound/items/knell4.ogg',
|
||||
))
|
||||
|
||||
/datum/deathrattle_group/proc/register(obj/item/implant/deathrattle/implant)
|
||||
implant.group = src
|
||||
implant_refs += WEAKREF(implant)
|
||||
|
||||
|
||||
/obj/item/implant/deathrattle
|
||||
name = "deathrattle implant"
|
||||
desc = "Hope no one else dies, prepare for when they do."
|
||||
|
||||
activated = FALSE
|
||||
|
||||
var/datum/deathrattle_group/group = null
|
||||
|
||||
/obj/item/implant/deathrattle/Destroy()
|
||||
group = null
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/deathrattle/can_be_implanted_in(mob/living/target)
|
||||
// Can be implanted in anything that's a mob. Syndicate cyborgs, talking fish, humans...
|
||||
return TRUE
|
||||
|
||||
/obj/item/implant/deathrattle/proc/on_predeath(datum/source, gibbed)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(group)
|
||||
group.rattle(origin = src, owner = source)
|
||||
|
||||
/obj/item/implant/deathrattle/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE)
|
||||
. = ..()
|
||||
if(.)
|
||||
RegisterSignal(target, COMSIG_LIVING_PREDEATH, .proc/on_predeath)
|
||||
|
||||
if(!group)
|
||||
to_chat(target, "<i>You hear a strange, robotic voice in your head...</i> \"<span class='robot'>Warning: No other linked implants detected.</span>\"")
|
||||
|
||||
|
||||
/obj/item/implantcase/deathrattle
|
||||
name = "implant case - 'Deathrattle'"
|
||||
desc = "A glass case containing a deathrattle implant."
|
||||
imp_type = /obj/item/implant/deathrattle
|
||||
@@ -139,14 +139,25 @@
|
||||
name = "health implant"
|
||||
activated = 0
|
||||
var/healthstring = ""
|
||||
var/list/raw_data = list()
|
||||
|
||||
/obj/item/implant/health/proc/sensehealth()
|
||||
/obj/item/implant/health/proc/sensehealth(get_list = FALSE)
|
||||
if (!imp_in)
|
||||
return "ERROR"
|
||||
else
|
||||
if(isliving(imp_in))
|
||||
var/mob/living/L = imp_in
|
||||
healthstring = "<small>Oxygen Deprivation Damage => [round(L.getOxyLoss())]<br />Fire Damage => [round(L.getFireLoss())]<br />Toxin Damage => [round(L.getToxLoss())]<br />Brute Force Damage => [round(L.getBruteLoss())]</small>"
|
||||
if (!healthstring)
|
||||
raw_data = list() //Reset list
|
||||
raw_data["oxy"] = list("[round(L.getOxyLoss())]") //Suffocation
|
||||
raw_data["burn"] = list("[round(L.getFireLoss())]") //Burn
|
||||
raw_data["tox"] = list("[round(L.getToxLoss())]") //Tox
|
||||
raw_data["brute"] = list("[round(L.getBruteLoss())]") //Brute
|
||||
if(!healthstring) //I have no idea who made it go this order but okay.
|
||||
healthstring = "ERROR"
|
||||
return healthstring
|
||||
if(!length(raw_data))
|
||||
raw_data = list("ERROR")
|
||||
if(!get_list)
|
||||
return healthstring
|
||||
else
|
||||
return raw_data
|
||||
|
||||
@@ -271,11 +271,14 @@
|
||||
var/force_on // Damage when on - not stunning
|
||||
var/force_off // Damage when off - not stunning
|
||||
var/weight_class_on // What is the new size class when turned on
|
||||
var/sword_point = TRUE
|
||||
|
||||
wound_bonus = 15
|
||||
|
||||
/obj/item/melee/classic_baton/Initialize()
|
||||
. = ..()
|
||||
if(sword_point)
|
||||
AddElement(/datum/element/sword_point)
|
||||
|
||||
// Description for trying to stun when still on cooldown.
|
||||
/obj/item/melee/classic_baton/proc/get_wait_description()
|
||||
@@ -402,6 +405,8 @@
|
||||
weight_class_on = WEIGHT_CLASS_BULKY
|
||||
total_mass = TOTAL_MASS_NORMAL_ITEM
|
||||
bare_wound_bonus = 5
|
||||
sword_point = FALSE
|
||||
var/silent = FALSE
|
||||
|
||||
/obj/item/melee/classic_baton/telescopic/suicide_act(mob/user)
|
||||
var/mob/living/carbon/human/H = user
|
||||
@@ -431,6 +436,9 @@
|
||||
w_class = weight_class_on
|
||||
force = force_on
|
||||
attack_verb = list("smacked", "struck", "cracked", "beaten")
|
||||
AddElement(/datum/element/sword_point)
|
||||
if(!silent)
|
||||
user?.visible_message("<span class='warning'>[user] extends [src] with a flick of their wrist!</span>")
|
||||
else
|
||||
to_chat(user, desc["local_off"])
|
||||
icon_state = off_icon_state
|
||||
@@ -439,6 +447,9 @@
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
force = force_off
|
||||
attack_verb = list("hit", "poked")
|
||||
RemoveElement(/datum/element/sword_point)
|
||||
if(!silent)
|
||||
user?.visible_message("<span class='warning'>[user] collapses [src] back down!</span>")
|
||||
playsound(src.loc, on_sound, 50, 1)
|
||||
add_fingerprint(user)
|
||||
|
||||
@@ -465,6 +476,7 @@
|
||||
force_on = 16
|
||||
force_off = 5
|
||||
weight_class_on = WEIGHT_CLASS_NORMAL
|
||||
silent = TRUE
|
||||
|
||||
/obj/item/melee/classic_baton/telescopic/contractor_baton/get_wait_description()
|
||||
return "<span class='danger'>The baton is still charging!</span>"
|
||||
|
||||
@@ -135,8 +135,8 @@
|
||||
/obj/item/organ/cyberimp/arm/toolset,
|
||||
/obj/item/organ/cyberimp/arm/surgery,
|
||||
/obj/item/organ/cyberimp/chest/thrusters,
|
||||
/obj/item/organ/lungs/cybernetic,
|
||||
/obj/item/organ/liver/cybernetic) //cyberimplants range from a nice bonus to fucking broken bullshit so no subtypesof
|
||||
/obj/item/organ/lungs/cybernetic/tier3,
|
||||
/obj/item/organ/liver/cybernetic/tier3) //cyberimplants range from a nice bonus to fucking broken bullshit so no subtypesof
|
||||
for(var/V in templist)
|
||||
var/atom/A = V
|
||||
augment_list[initial(A.name)] = A
|
||||
|
||||
@@ -272,7 +272,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
|
||||
. = ..()
|
||||
. += GLOB.plastitaniumglass_recipes
|
||||
|
||||
/obj/item/stack/sheet/titaniumglass/on_solar_construction(obj/machinery/power/solar/S)
|
||||
/obj/item/stack/sheet/plastitaniumglass/on_solar_construction(obj/machinery/power/solar/S)
|
||||
S.max_integrity *= 2
|
||||
S.efficiency *= 2
|
||||
|
||||
|
||||
@@ -351,6 +351,75 @@
|
||||
if(!contents.len)
|
||||
. += "[icon_state]_empty"
|
||||
|
||||
//Derringer "Cigarettes"//
|
||||
/obj/item/storage/fancy/cigarettes/derringer
|
||||
name = "\improper Robust packet"
|
||||
desc = "Smoked by the robust."
|
||||
icon_state = "robust"
|
||||
spawn_type = /obj/item/gun/ballistic/derringer/traitor
|
||||
|
||||
/obj/item/storage/fancy/cigarettes/derringer/ComponentInitialize()
|
||||
. = ..()
|
||||
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
|
||||
STR.max_items = 6
|
||||
STR.can_hold = typecacheof(list(/obj/item/clothing/mask/cigarette, /obj/item/lighter, /obj/item/gun/ballistic/derringer, /obj/item/ammo_casing/c38, /obj/item/ammo_casing/a357, /obj/item/ammo_casing/g4570))
|
||||
|
||||
/obj/item/storage/fancy/cigarettes/derringer/AltClick(mob/living/carbon/user)
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
|
||||
return
|
||||
var/obj/item/W = (locate(/obj/item/ammo_casing/a357) in contents) || (locate(/obj/item/clothing/mask/cigarette) in contents) || locate(/obj/item/ammo_casing/g4570) //Easy access smokes and bullets
|
||||
if(W && contents.len > 0)
|
||||
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, W, user)
|
||||
user.put_in_hands(W)
|
||||
contents -= W
|
||||
to_chat(user, "<span class='notice'>You take \a [W] out of the pack.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>There are no items left in the pack.</span>")
|
||||
|
||||
/obj/item/storage/fancy/cigarettes/derringer/PopulateContents()
|
||||
new spawn_type(src)
|
||||
new /obj/item/ammo_casing/a357(src)
|
||||
new /obj/item/ammo_casing/a357(src)
|
||||
new /obj/item/ammo_casing/a357(src)
|
||||
new /obj/item/ammo_casing/a357(src)
|
||||
new /obj/item/clothing/mask/cigarette/syndicate(src)
|
||||
|
||||
//For traitors with luck/class
|
||||
/obj/item/storage/fancy/cigarettes/derringer/gold
|
||||
name = "\improper Robust Gold packet"
|
||||
desc = "Smoked by the truly robust."
|
||||
icon_state = "robustg"
|
||||
spawn_type = /obj/item/gun/ballistic/derringer/gold
|
||||
|
||||
//For operatives, bound in a ka-tet.
|
||||
/obj/item/storage/fancy/cigarettes/derringer/midworld
|
||||
name = "\improper Midworld's Lime Bend"
|
||||
desc = "The wheel of Ka turns, Gunslinger."
|
||||
icon_state = "slime"
|
||||
spawn_type = /obj/item/gun/ballistic/derringer/nukeop
|
||||
|
||||
/obj/item/storage/fancy/cigarettes/derringer/midworld/PopulateContents()
|
||||
new spawn_type(src)
|
||||
new /obj/item/ammo_casing/g4570(src)
|
||||
new /obj/item/ammo_casing/g4570(src)
|
||||
new /obj/item/ammo_casing/g4570(src)
|
||||
new /obj/item/ammo_casing/g4570(src)
|
||||
new /obj/item/clothing/mask/cigarette/xeno(src)
|
||||
|
||||
//For Cargomen, looking for a good deal on arms, with no quarrels as to where they're from.
|
||||
/obj/item/storage/fancy/cigarettes/derringer/smuggled
|
||||
name = "\improper Shady Jim's Super Slims packet"
|
||||
desc = "If you get caught with this, we don't know you, capiche?"
|
||||
icon_state = "shadyjim"
|
||||
spawn_type = /obj/item/gun/ballistic/derringer
|
||||
|
||||
/obj/item/storage/fancy/cigarettes/derringer/smuggled/PopulateContents()
|
||||
new spawn_type(src)
|
||||
new /obj/item/ammo_casing/c38/lethal(src)
|
||||
new /obj/item/ammo_casing/c38/lethal(src)
|
||||
new /obj/item/ammo_casing/c38/lethal(src)
|
||||
new /obj/item/ammo_casing/c38/lethal(src)
|
||||
new /obj/item/clothing/mask/cigarette/shadyjims (src)
|
||||
/////////////
|
||||
//CIGAR BOX//
|
||||
/////////////
|
||||
|
||||
@@ -546,3 +546,21 @@
|
||||
. = ..()
|
||||
new /obj/item/cardpack/syndicate(src)
|
||||
new /obj/item/cardpack/syndicate(src)
|
||||
|
||||
/obj/item/storage/box/syndie_kit/imp_deathrattle
|
||||
name = "deathrattle implant box"
|
||||
desc = "Contains eight linked deathrattle implants."
|
||||
|
||||
/obj/item/storage/box/syndie_kit/imp_deathrattle/PopulateContents()
|
||||
new /obj/item/implanter(src)
|
||||
|
||||
var/datum/deathrattle_group/group = new
|
||||
|
||||
var/implants = list()
|
||||
for(var/j in 1 to 8)
|
||||
var/obj/item/implantcase/deathrattle/case = new (src)
|
||||
implants += case.imp
|
||||
|
||||
for(var/i in implants)
|
||||
group.register(i)
|
||||
desc += " The implants are registered to the \"[group.name]\" group."
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
/obj/item/instrument/harmonica,
|
||||
/obj/item/mining_voucher,
|
||||
/obj/item/suit_voucher,
|
||||
/obj/item/reagent_containers/pill))
|
||||
/obj/item/reagent_containers/pill,
|
||||
/obj/item/gun/ballistic/derringer))
|
||||
|
||||
/obj/item/storage/wallet/Exited(atom/movable/AM)
|
||||
. = ..()
|
||||
|
||||
@@ -16,17 +16,21 @@
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80)
|
||||
attack_speed = CLICK_CD_MELEE
|
||||
|
||||
var/stamforce = 35
|
||||
var/stamina_loss_amount = 35
|
||||
var/turned_on = FALSE
|
||||
var/knockdown = TRUE
|
||||
var/obj/item/stock_parts/cell/cell
|
||||
var/hitcost = 750
|
||||
var/throw_hit_chance = 35
|
||||
var/preload_cell_type //if not empty the baton starts with this type of cell
|
||||
var/cooldown_duration = 5 SECONDS //How long our baton rightclick goes on cooldown for after applying a knockdown
|
||||
var/status_duration = 5 SECONDS //how long our status effects last for otherwise
|
||||
COOLDOWN_DECLARE(shove_cooldown)
|
||||
|
||||
/obj/item/melee/baton/examine(mob/user)
|
||||
. = ..()
|
||||
. += "<span class='notice'>Right click attack while in combat mode to disarm instead of stun.</span>"
|
||||
. += "<span class='notice'>Right click attack while in combat mode to knockdown, but only once per [cooldown_duration / 10] seconds.</span>"
|
||||
. += "<span class='notice'>This knockdown will also put them off balance for [status_duration / 20] seconds, allowing you to shove a weapon out of their hand with a right click in Disarm intent.</span>"
|
||||
|
||||
/obj/item/melee/baton/get_cell()
|
||||
. = cell
|
||||
@@ -53,8 +57,8 @@
|
||||
/obj/item/melee/baton/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
..()
|
||||
//Only mob/living types have stun handling
|
||||
if(turned_on && prob(throw_hit_chance) && iscarbon(hit_atom))
|
||||
baton_stun(hit_atom)
|
||||
if(turned_on && prob(throw_hit_chance) && iscarbon(hit_atom) && thrownby)
|
||||
baton_stun(hit_atom, thrownby, shoving = TRUE)
|
||||
|
||||
/obj/item/melee/baton/loaded //this one starts with a cell pre-installed.
|
||||
preload_cell_type = /obj/item/stock_parts/cell/high/plus
|
||||
@@ -74,6 +78,16 @@
|
||||
//we're below minimum, turn off
|
||||
switch_status(FALSE)
|
||||
|
||||
///Check for our cell to determine how much penetration our weapon does.
|
||||
/obj/item/melee/baton/proc/get_cell_zap_pen()
|
||||
var/obj/item/stock_parts/cell/copper_top = get_cell()
|
||||
if(copper_top)
|
||||
var/chargepower = copper_top.maxcharge
|
||||
var/zap_penetration = (chargepower/1000) //This is our effective penetration. Every 1000 max charge, we get 1 pen power. A high capacity cell is equal to 10 armor pen, as an example.
|
||||
return zap_penetration
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/item/melee/baton/proc/switch_status(new_status = FALSE, silent = FALSE)
|
||||
if(turned_on != new_status)
|
||||
turned_on = new_status
|
||||
@@ -101,6 +115,7 @@
|
||||
var/obj/item/stock_parts/cell/copper_top = get_cell()
|
||||
if(copper_top)
|
||||
. += "<span class='notice'>\The [src] is [round(copper_top.percent())]% charged.</span>"
|
||||
. += "<span class='notice'>\The [src] currently can penetrate [round(copper_top.maxcharge/1000)]% of enemy armor thanks to it's loaded cell.</span>"
|
||||
else
|
||||
. += "<span class='warning'>\The [src] does not have a power source installed.</span>"
|
||||
|
||||
@@ -150,10 +165,10 @@
|
||||
/obj/item/melee/baton/alt_pre_attack(atom/A, mob/living/user, params)
|
||||
if(!user.CheckActionCooldown(CLICK_CD_MELEE))
|
||||
return
|
||||
. = common_baton_melee(A, user, TRUE) //return true (attackchain interrupt) if this also returns true. no harm-disarming.
|
||||
. = common_baton_melee(A, user, TRUE) //return true (attackchain interrupt) if this also returns true. no harm-shoving.
|
||||
|
||||
//return TRUE to interrupt attack chain.
|
||||
/obj/item/melee/baton/proc/common_baton_melee(mob/M, mob/living/user, disarming = FALSE)
|
||||
/obj/item/melee/baton/proc/common_baton_melee(mob/M, mob/living/user, shoving = FALSE)
|
||||
if(iscyborg(M) || !isliving(M)) //can't baton cyborgs
|
||||
return FALSE
|
||||
if(turned_on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
|
||||
@@ -167,21 +182,26 @@
|
||||
if(check_martial_counter(L, user))
|
||||
return TRUE
|
||||
if(turned_on)
|
||||
if(baton_stun(M, user, disarming))
|
||||
if(baton_stun(M, user, shoving))
|
||||
user.do_attack_animation(M)
|
||||
else if(user.a_intent != INTENT_HARM) //they'll try to bash in the last proc.
|
||||
M.visible_message("<span class='warning'>[user] has prodded [M] with [src]. Luckily it was off.</span>", \
|
||||
"<span class='warning'>[user] has prodded you with [src]. Luckily it was off</span>")
|
||||
return disarming || (user.a_intent != INTENT_HARM)
|
||||
return shoving || (user.a_intent != INTENT_HARM)
|
||||
|
||||
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/living/user, disarming = FALSE)
|
||||
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/living/user, shoving = FALSE)
|
||||
var/list/return_list = list()
|
||||
if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; check_shields() handles that
|
||||
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
return FALSE
|
||||
var/stunpwr = stamforce
|
||||
stunpwr = block_calculate_resultant_damage(stunpwr, return_list)
|
||||
var/final_stamina_loss_amount = stamina_loss_amount //Our stunning power for the baton
|
||||
var/shoved = FALSE //Did we succeed on knocking our target over?
|
||||
var/zap_penetration = get_cell_zap_pen() //Find out what kind of cell we have, and calculating the resultant armor pen we get from it
|
||||
var/zap_block = L.run_armor_check(BODY_ZONE_CHEST, "melee", null, null, zap_penetration) //armor check, including calculation for armor penetration, for our attack
|
||||
final_stamina_loss_amount = block_calculate_resultant_damage(final_stamina_loss_amount, return_list)
|
||||
|
||||
var/obj/item/stock_parts/cell/our_cell = get_cell()
|
||||
|
||||
if(!our_cell)
|
||||
switch_status(FALSE)
|
||||
return FALSE
|
||||
@@ -194,26 +214,31 @@
|
||||
L.visible_message("<span class='warning'>[user] has prodded [L] with [src]. Luckily it was out of charge.</span>", \
|
||||
"<span class='warning'>[user] has prodded you with [src]. Luckily it was out of charge.</span>")
|
||||
return FALSE
|
||||
stunpwr *= round(stuncharge/hitcost, 0.1)
|
||||
final_stamina_loss_amount *= round(stuncharge/hitcost, 0.1)
|
||||
|
||||
if(user && !user.UseStaminaBuffer(getweight(user, STAM_COST_BATON_MOB_MULT), warn = TRUE))
|
||||
return FALSE
|
||||
|
||||
if(!disarming)
|
||||
if(knockdown)
|
||||
L.DefaultCombatKnockdown(50, override_stamdmg = 0) //knockdown
|
||||
L.adjustStaminaLoss(stunpwr)
|
||||
else
|
||||
L.drop_all_held_items() //no knockdown/stamina damage, instead disarm.
|
||||
if(shoving && COOLDOWN_FINISHED(src, shove_cooldown) && !HAS_TRAIT(L, TRAIT_IWASBATONED)) //Rightclicking applies a knockdown, but only once every couple of seconds, based on the cooldown_duration var. If they were recently knocked down, they can't be knocked down again by a baton.
|
||||
L.DefaultCombatKnockdown(50, override_stamdmg = 0)
|
||||
L.apply_status_effect(STATUS_EFFECT_TASED_WEAK, status_duration) //Even if they shove themselves up, they're still slowed.
|
||||
L.apply_status_effect(STATUS_EFFECT_OFF_BALANCE, status_duration) //They're very likely to drop items if shoved briefly after a knockdown.
|
||||
shoved = TRUE
|
||||
COOLDOWN_START(src, shove_cooldown, cooldown_duration)
|
||||
ADD_TRAIT(L, TRAIT_IWASBATONED, STATUS_EFFECT_TRAIT) //Prevents swapping to a new baton to avoid the cooldown by just acquiring more batons
|
||||
addtimer(TRAIT_CALLBACK_REMOVE(L, TRAIT_IWASBATONED, STATUS_EFFECT_TRAIT), cooldown_duration)
|
||||
playsound(loc, 'sound/weapons/zapbang.ogg', 50, 1, -1)
|
||||
else //If we cannot/don't knock down the target, we apply a stagger, which keeps them from just running off
|
||||
L.apply_status_effect(STATUS_EFFECT_STAGGERED, status_duration)
|
||||
|
||||
L.apply_effect(EFFECT_STUTTER, stamforce)
|
||||
L.apply_damage (final_stamina_loss_amount, STAMINA, BODY_ZONE_CHEST, zap_block)
|
||||
L.apply_effect(EFFECT_STUTTER, stamina_loss_amount)
|
||||
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
|
||||
if(user)
|
||||
L.lastattacker = user.real_name
|
||||
L.lastattackerckey = user.ckey
|
||||
L.visible_message("<span class='danger'>[user] has [disarming? "disarmed" : "stunned"] [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] has [disarming? "disarmed" : "stunned"] you with [src]!</span>")
|
||||
log_combat(user, L, disarming? "disarmed" : "stunned")
|
||||
L.set_last_attacker(user)
|
||||
L.visible_message("<span class='danger'>[user] has [shoved ? "brutally stunned" : "stunned"] [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] has [shoved ? "brutally stunnned" : "stunned"] you with [src]!</span>")
|
||||
log_combat(user, L, shoved ? "stunned and attempted knockdown" : "stunned")
|
||||
|
||||
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
|
||||
|
||||
@@ -228,7 +253,7 @@
|
||||
user.visible_message("<span class='danger'>[user] accidentally hits [user.p_them()]self with [src]!</span>", \
|
||||
"<span class='userdanger'>You accidentally hit yourself with [src]!</span>")
|
||||
SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK)
|
||||
user.DefaultCombatKnockdown(stamforce*6)
|
||||
user.DefaultCombatKnockdown(stamina_loss_amount*6)
|
||||
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
|
||||
deductcharge(hitcost)
|
||||
|
||||
@@ -306,18 +331,20 @@
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
force = 3
|
||||
throwforce = 5
|
||||
stamforce = 25
|
||||
stamina_loss_amount = 25
|
||||
hitcost = 1000
|
||||
throw_hit_chance = 10
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
cooldown_duration = 7 SECONDS //It's a little on the weak side
|
||||
status_duration = 3 //Slows someone for a tiny bit
|
||||
var/obj/item/assembly/igniter/sparkler
|
||||
|
||||
/obj/item/melee/baton/cattleprod/Initialize()
|
||||
. = ..()
|
||||
sparkler = new (src)
|
||||
sparkler.activate_cooldown = 5
|
||||
sparkler.activate_cooldown = 7 //Helps visualize the knockdown
|
||||
|
||||
/obj/item/melee/baton/cattleprod/baton_stun()
|
||||
/obj/item/melee/baton/cattleprod/baton_stun(mob/living/L, mob/living/carbon/user, shoving = FALSE)
|
||||
sparkler?.activate()
|
||||
. = ..()
|
||||
|
||||
@@ -344,8 +371,8 @@
|
||||
/obj/item/melee/baton/boomerang/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
||||
if(turned_on)
|
||||
var/caught = hit_atom.hitby(src, FALSE, FALSE, throwingdatum=throwingdatum)
|
||||
if(ishuman(hit_atom) && !caught && prob(throw_hit_chance))//if they are a carbon and they didn't catch it
|
||||
baton_stun(hit_atom)
|
||||
if(ishuman(hit_atom) && !caught && prob(throw_hit_chance) && thrownby)//if they are a carbon and they didn't catch it
|
||||
baton_stun(hit_atom, thrownby, shoving = TRUE)
|
||||
if(thrownby && !caught)
|
||||
sleep(1)
|
||||
if(!QDELETED(src))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
item_state = "teleprod"
|
||||
slot_flags = null
|
||||
|
||||
/obj/item/melee/baton/cattleprod/teleprod/baton_stun(mob/living/L, mob/living/carbon/user)//handles making things teleport when hit
|
||||
/obj/item/melee/baton/cattleprod/teleprod/baton_stun(mob/living/L, mob/living/carbon/user, shoving = FALSE)//handles making things teleport when hit
|
||||
. = ..()
|
||||
if(!. || L.anchored)
|
||||
return
|
||||
@@ -16,7 +16,7 @@
|
||||
user.visible_message("<span class='danger'>[user] accidentally hits [user.p_them()]self with [src]!</span>", \
|
||||
"<span class='userdanger'>You accidentally hit yourself with [src]!</span>")
|
||||
SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK)
|
||||
user.DefaultCombatKnockdown(stamforce * 6)
|
||||
user.DefaultCombatKnockdown(stamina_loss_amount * 6)
|
||||
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
|
||||
if(do_teleport(user, get_turf(user), 50, channel = TELEPORT_CHANNEL_BLUESPACE))
|
||||
deductcharge(hitcost)
|
||||
|
||||
@@ -19,3 +19,50 @@
|
||||
desc = "A sturdier card-locked storage unit used for bulky shipments."
|
||||
max_integrity = 500 // Same as crates.
|
||||
melee_min_damage = 25 // Idem.
|
||||
|
||||
/obj/structure/closet/secure_closet/goodies/owned
|
||||
name = "private locker"
|
||||
desc = "A locker designed to only open for who purchased its contents."
|
||||
///Account of the person buying the crate if private purchasing.
|
||||
var/datum/bank_account/buyer_account
|
||||
///Department of the person buying the crate if buying via the NIRN app.
|
||||
var/datum/bank_account/department/department_account
|
||||
///Is the secure crate opened or closed?
|
||||
var/privacy_lock = TRUE
|
||||
///Is the crate being bought by a person, or a budget card?
|
||||
var/department_purchase = FALSE
|
||||
|
||||
/obj/structure/closet/secure_closet/goodies/owned/examine(mob/user)
|
||||
. = ..()
|
||||
. += "<span class='notice'>It's locked with a privacy lock, and can only be unlocked by the buyer's ID.</span>"
|
||||
|
||||
/obj/structure/closet/secure_closet/goodies/owned/Initialize(mapload, datum/bank_account/_buyer_account)
|
||||
. = ..()
|
||||
buyer_account = _buyer_account
|
||||
if(istype(buyer_account, /datum/bank_account/department))
|
||||
department_purchase = TRUE
|
||||
department_account = buyer_account
|
||||
|
||||
/obj/structure/closet/secure_closet/goodies/owned/togglelock(mob/living/user, silent)
|
||||
if(privacy_lock)
|
||||
if(!broken)
|
||||
var/obj/item/card/id/id_card = user.get_idcard(TRUE)
|
||||
if(id_card)
|
||||
if(id_card.registered_account)
|
||||
if(id_card.registered_account == buyer_account || (department_purchase && (id_card.registered_account?.account_job?.paycheck_department) == (department_account.department_id)))
|
||||
if(iscarbon(user))
|
||||
add_fingerprint(user)
|
||||
locked = !locked
|
||||
user.visible_message("<span class='notice'>[user] unlocks [src]'s privacy lock.</span>",
|
||||
"<span class='notice'>You unlock [src]'s privacy lock.</span>")
|
||||
privacy_lock = FALSE
|
||||
update_icon()
|
||||
else if(!silent)
|
||||
to_chat(user, "<span class='notice'>Bank account does not match with buyer!</span>")
|
||||
else if(!silent)
|
||||
to_chat(user, "<span class='notice'>No linked bank account detected!</span>")
|
||||
else if(!silent)
|
||||
to_chat(user, "<span class='notice'>No ID detected!</span>")
|
||||
else if(!silent)
|
||||
to_chat(user, "<span class='warning'>[src] is broken!</span>")
|
||||
else ..()
|
||||
|
||||
@@ -56,6 +56,11 @@
|
||||
name = "gear crate"
|
||||
icon_state = "secgearcrate"
|
||||
|
||||
/obj/structure/closet/crate/secure/soviet
|
||||
desc = "A crate, purportedly from Space Russia."
|
||||
name = "soviet crate"
|
||||
icon_state = "sovietcrate"
|
||||
|
||||
/obj/structure/closet/crate/secure/hydroponics
|
||||
desc = "A crate with a lock on it, painted in the scheme of the station's botanists."
|
||||
name = "secure hydroponics crate"
|
||||
|
||||
@@ -590,7 +590,7 @@
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate
|
||||
name = "space pirate sleeper"
|
||||
desc = "A cryo sleeper smelling faintly of rum."
|
||||
desc = "A cryo sleeper smelling faintly of rum. The sleeper looks unstable. <i>Perhaps the pirate within can be killed with the right tools...</i>"
|
||||
job_description = "Space Pirate"
|
||||
random = TRUE
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
@@ -608,6 +608,54 @@
|
||||
assignedrole = "Space Pirate"
|
||||
var/rank = "Mate"
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(user.mind.has_antag_datum(/datum/antagonist/pirate))
|
||||
to_chat(user, "<span class='notice'>Your shipmate sails within their dreams for now. Perhaps they may wake up eventually.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>If you want to kill the pirate off, something to pry open the sleeper might be the best way to do it.</span>")
|
||||
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/attackby(obj/item/W, mob/user, params)
|
||||
if(W.tool_behaviour == TOOL_CROWBAR && user.a_intent != INTENT_HARM)
|
||||
if(user.mind.has_antag_datum(/datum/antagonist/pirate))
|
||||
to_chat(user,"<span class='warning'>Why would you want to do that to your shipmate? That'd kill them.</span>")
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] start to pry open [src]...</span>",
|
||||
"<span class='notice'>You start to pry open [src]...</span>",
|
||||
"<span class='italics'>You hear prying...</span>")
|
||||
W.play_tool_sound(src)
|
||||
if(do_after(user, 100*W.toolspeed, target = src))
|
||||
user.visible_message("<span class='warning'>[user] pries open [src], disrupting the sleep of the pirate within and killing them.</span>",
|
||||
"<span class='notice'>You pry open [src], disrupting the sleep of the pirate within and killing them.</span>",
|
||||
"<span class='italics'>You hear prying, followed by the death rattling of bones.</span>")
|
||||
log_game("[key_name(user)] has successfully pried open [src] and disabled a space pirate spawner.")
|
||||
W.play_tool_sound(src)
|
||||
playsound(src.loc, 'modular_citadel/sound/voice/scream_skeleton.ogg', 50, 1, 4, 1.2)
|
||||
if(rank == "Captain")
|
||||
new /obj/effect/mob_spawn/human/pirate/corpse/captain(get_turf(src))
|
||||
else
|
||||
new /obj/effect/mob_spawn/human/pirate/corpse(get_turf(src))
|
||||
qdel(src)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/corpse //occurs when someone pries a pirate out of their sleeper.
|
||||
mob_name = "Dead Space Pirate"
|
||||
death = TRUE
|
||||
instant = TRUE
|
||||
random = FALSE
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/corpse/Destroy()
|
||||
return ..()
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/corpse/captain
|
||||
rank = "Captain"
|
||||
mob_name = "Dead Space Pirate Captain"
|
||||
outfit = /datum/outfit/pirate/space/captain
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/special(mob/living/new_spawn)
|
||||
new_spawn.fully_replace_character_name(new_spawn.real_name,generate_pirate_name())
|
||||
new_spawn.mind.add_antag_datum(/datum/antagonist/pirate)
|
||||
|
||||
@@ -199,8 +199,7 @@
|
||||
return TRUE
|
||||
|
||||
/obj/item/gun_control/attack(mob/living/M, mob/living/user)
|
||||
M.lastattacker = user.real_name
|
||||
M.lastattackerckey = user.ckey
|
||||
M.set_last_attacker(user)
|
||||
M.attacked_by(src, user)
|
||||
add_fingerprint(user)
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@
|
||||
to_chat(user, "<span class='notice'>You put [I] in [src].</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/structure/tank_dispenser/attack_robot(mob/user)
|
||||
return _try_interact(user)
|
||||
|
||||
/obj/structure/tank_dispenser/ui_state(mob/user)
|
||||
return GLOB.physical_state
|
||||
|
||||
|
||||
@@ -554,7 +554,7 @@
|
||||
if(B.cell)
|
||||
if(B.cell.charge > 0 && B.turned_on)
|
||||
flick("baton_active", src)
|
||||
var/stunforce = B.stamforce
|
||||
var/stunforce = B.stamina_loss_amount
|
||||
user.DefaultCombatKnockdown(stunforce * 2)
|
||||
user.stuttering = stunforce/20
|
||||
B.deductcharge(B.hitcost)
|
||||
|
||||
@@ -428,7 +428,7 @@
|
||||
output += "<option value='[j]'>[j]</option>"
|
||||
for(var/j in GLOB.nonhuman_positions)
|
||||
output += "<option value='[j]'>[j]</option>"
|
||||
for(var/j in list(ROLE_TRAITOR, ROLE_CHANGELING, ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_WIZARD))
|
||||
for(var/j in list(ROLE_TRAITOR, ROLE_CHANGELING, ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_WIZARD, ROLE_HERETIC))
|
||||
output += "<option value='[j]'>[j]</option>"
|
||||
output += "</select></td></tr></table>"
|
||||
output += "<b>Reason:<br></b><textarea name='dbbanreason' cols='50'></textarea><br>"
|
||||
|
||||
@@ -93,7 +93,7 @@ GLOBAL_PROTECT(admin_verbs_ban)
|
||||
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/manual_play_web_sound, /client/proc/set_round_end_sound))
|
||||
GLOBAL_PROTECT(admin_verbs_sounds)
|
||||
GLOBAL_LIST_INIT(admin_verbs_fun, list(
|
||||
/client/proc/cmd_admin_dress,
|
||||
/client/proc/cmd_select_equipment,
|
||||
/client/proc/cmd_admin_gib_self,
|
||||
/client/proc/drop_bomb,
|
||||
/client/proc/set_dynex_scale,
|
||||
@@ -232,7 +232,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/client/proc/play_local_sound,
|
||||
/client/proc/play_sound,
|
||||
/client/proc/set_round_end_sound,
|
||||
/client/proc/cmd_admin_dress,
|
||||
/client/proc/cmd_select_equipment,
|
||||
/client/proc/cmd_admin_gib_self,
|
||||
/client/proc/drop_bomb,
|
||||
/client/proc/drop_dynex_bomb,
|
||||
|
||||
@@ -101,6 +101,10 @@ GLOBAL_VAR(antag_prototypes)
|
||||
out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]<br>"
|
||||
out += "Assigned role: [assigned_role]. <a href='?src=[REF(src)];role_edit=1'>Edit</a><br>"
|
||||
out += "Faction and special role: <b><font color='red'>[special_role]</font></b><br>"
|
||||
var/datum/component/activity/activity = current.GetComponent(/datum/component/activity)
|
||||
if(activity)
|
||||
out += "Activity level: [activity.activity_level]<br>"
|
||||
out += "Hasn't changed areas in approximately [activity.not_moved_counter] seconds"
|
||||
|
||||
var/special_statuses = get_special_statuses()
|
||||
if(length(special_statuses))
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
|
||||
/client/proc/open_outfit_editor(datum/outfit/target)
|
||||
var/datum/outfit_editor/ui = new(usr, target)
|
||||
ui.ui_interact(usr)
|
||||
|
||||
#define OUTFIT_EDITOR_NAME "Outfit-O-Tron 9000"
|
||||
/datum/outfit_editor
|
||||
var/client/owner
|
||||
|
||||
var/dummy_key
|
||||
|
||||
var/datum/outfit/drip
|
||||
|
||||
/datum/outfit_editor/New(user, datum/outfit/target)
|
||||
owner = CLIENT_FROM_VAR(user)
|
||||
|
||||
if(ispath(target))
|
||||
drip = new /datum/outfit
|
||||
drip.copy_from(new target)
|
||||
else if(istype(target))
|
||||
drip = target
|
||||
else
|
||||
drip = new /datum/outfit
|
||||
drip.name = "New Outfit"
|
||||
|
||||
/datum/outfit_editor/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/outfit_editor/ui_status(mob/user, datum/ui_state/state)
|
||||
if(QDELETED(drip))
|
||||
return UI_CLOSE
|
||||
return ..()
|
||||
|
||||
/datum/outfit_editor/ui_close(mob/user)
|
||||
clear_human_dummy(dummy_key)
|
||||
qdel(src)
|
||||
|
||||
/datum/outfit_editor/proc/init_dummy()
|
||||
dummy_key = "outfit_editor_[owner]"
|
||||
generate_dummy_lookalike(dummy_key, owner.mob)
|
||||
unset_busy_human_dummy(dummy_key)
|
||||
|
||||
/datum/outfit_editor/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "OutfitEditor", OUTFIT_EDITOR_NAME)
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
/datum/outfit_editor/proc/entry(data)
|
||||
if(ispath(data, /obj/item))
|
||||
var/obj/item/item = data
|
||||
return list(
|
||||
"path" = item,
|
||||
"name" = initial(item.name),
|
||||
"desc" = initial(item.desc),
|
||||
// at this point initializing the item is probably faster tbh
|
||||
"sprite" = icon2base64(icon(initial(item.icon), initial(item.icon_state))),
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
/datum/outfit_editor/proc/serialize_outfit()
|
||||
var/list/outfit_slots = drip.get_json_data()
|
||||
. = list()
|
||||
for(var/key in outfit_slots)
|
||||
var/val = outfit_slots[key]
|
||||
. += list("[key]" = entry(val))
|
||||
|
||||
/datum/outfit_editor/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["outfit"] = serialize_outfit()
|
||||
data["saveable"] = !GLOB.custom_outfits.Find(drip)
|
||||
|
||||
if(!dummy_key)
|
||||
init_dummy()
|
||||
var/icon/dummysprite = get_flat_human_icon(null,
|
||||
dummy_key = dummy_key,
|
||||
showDirs = list(SOUTH),
|
||||
outfit_override = drip)
|
||||
data["dummy64"] = icon2base64(dummysprite)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
/datum/outfit_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
if(..())
|
||||
return
|
||||
. = TRUE
|
||||
|
||||
var/slot = params["slot"]
|
||||
switch(action)
|
||||
if("click")
|
||||
choose_item(slot)
|
||||
if("ctrlClick")
|
||||
choose_any_item(slot)
|
||||
if("clear")
|
||||
if(drip.vars.Find(slot))
|
||||
drip.vars[slot] = null
|
||||
|
||||
if("rename")
|
||||
var/newname = stripped_input(owner, "What do you want to name this outfit?", OUTFIT_EDITOR_NAME)
|
||||
if(newname)
|
||||
drip.name = newname
|
||||
if("save")
|
||||
GLOB.custom_outfits |= drip
|
||||
SStgui.update_user_uis(owner.mob)
|
||||
if("delete")
|
||||
GLOB.custom_outfits -= drip
|
||||
SStgui.update_user_uis(owner.mob)
|
||||
if("vv")
|
||||
owner.debug_variables(drip)
|
||||
|
||||
|
||||
/datum/outfit_editor/proc/set_item(slot, obj/item/choice)
|
||||
if(!choice)
|
||||
return
|
||||
if(!ispath(choice))
|
||||
alert(owner, "Invalid item", OUTFIT_EDITOR_NAME, "oh no")
|
||||
return
|
||||
if(initial(choice.icon_state) == null) //hacky check copied from experimentor code
|
||||
var/msg = "Warning: This item's icon_state is null, indicating it is very probably not actually a usable item."
|
||||
if(alert(owner, msg, OUTFIT_EDITOR_NAME, "Use it anyway", "Cancel") != "Use it anyway")
|
||||
return
|
||||
|
||||
if(drip.vars.Find(slot))
|
||||
drip.vars[slot] = choice
|
||||
|
||||
/datum/outfit_editor/proc/choose_any_item(slot)
|
||||
var/obj/item/choice = pick_closest_path(FALSE)
|
||||
|
||||
if(!choice)
|
||||
return
|
||||
|
||||
set_item(slot, choice)
|
||||
|
||||
//this proc will try to give a good selection of items that the user can choose from
|
||||
//it does *not* give a selection of all items that can fit in a slot because lag;
|
||||
//most notably the hand and pocket slots because they accept pretty much anything
|
||||
//also stuff that fits in the belt and back slots are scattered pretty much all over the place
|
||||
/datum/outfit_editor/proc/choose_item(slot)
|
||||
var/list/options = list()
|
||||
|
||||
switch(slot)
|
||||
if("head")
|
||||
options = typesof(/obj/item/clothing/head)
|
||||
if("glasses")
|
||||
options = typesof(/obj/item/clothing/glasses)
|
||||
if("ears")
|
||||
options = typesof(/obj/item/radio/headset)
|
||||
|
||||
if("neck")
|
||||
options = typesof(/obj/item/clothing/neck)
|
||||
if("mask")
|
||||
options = typesof(/obj/item/clothing/mask)
|
||||
|
||||
if("uniform")
|
||||
options = typesof(/obj/item/clothing/under)
|
||||
if("suit")
|
||||
options = typesof(/obj/item/clothing/suit)
|
||||
if("gloves")
|
||||
options = typesof(/obj/item/clothing/gloves)
|
||||
|
||||
if("suit_store")
|
||||
var/obj/item/clothing/suit/suit = drip.suit
|
||||
if(suit)
|
||||
suit = new suit //initial() doesn't like lists
|
||||
options = suit.allowed
|
||||
if(!options.len) //nothing will happen, but don't let the user think it's broken
|
||||
to_chat(owner, "<span class='warning'>No options available for the current suit.</span>")
|
||||
|
||||
if("belt")
|
||||
options = typesof(/obj/item/storage/belt)
|
||||
if("id")
|
||||
options = typesof(/obj/item/card/id)
|
||||
|
||||
if("l_hand")
|
||||
choose_any_item(slot)
|
||||
if("back")
|
||||
options = typesof(/obj/item/storage/backpack)
|
||||
if("r_hand")
|
||||
choose_any_item(slot)
|
||||
|
||||
if("l_pocket")
|
||||
choose_any_item(slot)
|
||||
if("shoes")
|
||||
options = typesof(/obj/item/clothing/shoes)
|
||||
if("r_pocket")
|
||||
choose_any_item(slot)
|
||||
|
||||
if(length(options))
|
||||
set_item(slot, tgui_input_list(owner, "Choose an item", OUTFIT_EDITOR_NAME, options))
|
||||
|
||||
|
||||
#undef OUTFIT_EDITOR_NAME
|
||||
@@ -0,0 +1,73 @@
|
||||
/client/proc/outfit_manager()
|
||||
set category = "Debug"
|
||||
set name = "Outfit Manager"
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
var/datum/outfit_manager/ui = new(usr)
|
||||
ui.ui_interact(usr)
|
||||
|
||||
|
||||
/datum/outfit_manager
|
||||
var/client/owner
|
||||
|
||||
/datum/outfit_manager/New(user)
|
||||
owner = CLIENT_FROM_VAR(user)
|
||||
|
||||
/datum/outfit_manager/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/outfit_manager/ui_close(mob/user)
|
||||
qdel(src)
|
||||
|
||||
/datum/outfit_manager/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "OutfitManager")
|
||||
ui.open()
|
||||
|
||||
/datum/outfit_manager/proc/entry(datum/outfit/outfit)
|
||||
var/vv = FALSE
|
||||
var/datum/outfit/varedit/varoutfit = outfit
|
||||
if(istype(varoutfit))
|
||||
vv = length(varoutfit.vv_values)
|
||||
return list(
|
||||
"name" = "[outfit.name] [vv ? "(VV)" : ""]",
|
||||
"ref" = REF(outfit),
|
||||
)
|
||||
|
||||
/datum/outfit_manager/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/outfits = list()
|
||||
for(var/datum/outfit/custom_outfit in GLOB.custom_outfits)
|
||||
outfits += list(entry(custom_outfit))
|
||||
data["outfits"] = outfits
|
||||
|
||||
return data
|
||||
|
||||
/datum/outfit_manager/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
if(..())
|
||||
return
|
||||
. = TRUE
|
||||
|
||||
switch(action)
|
||||
if("new")
|
||||
owner.open_outfit_editor(new /datum/outfit)
|
||||
if("load")
|
||||
owner.holder.load_outfit(owner.mob)
|
||||
if("copy")
|
||||
var/datum/outfit/outfit = tgui_input_list(owner, "Pick an outfit to copy from", "Outfit Manager", subtypesof(/datum/outfit))
|
||||
if(ispath(outfit))
|
||||
owner.open_outfit_editor(new outfit)
|
||||
|
||||
var/datum/outfit/target_outfit = locate(params["outfit"])
|
||||
if(!istype(target_outfit))
|
||||
return
|
||||
switch(action) //wow we're switching through action again this is horrible optimization smh
|
||||
if("edit")
|
||||
owner.open_outfit_editor(target_outfit)
|
||||
if("save")
|
||||
owner.holder.save_outfit(owner.mob, target_outfit)
|
||||
if("delete")
|
||||
owner.holder.delete_outfit(owner.mob, target_outfit)
|
||||
@@ -0,0 +1,32 @@
|
||||
GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
|
||||
/datum/admins/proc/save_outfit(mob/admin, datum/outfit/O)
|
||||
O.save_to_file(admin)
|
||||
SStgui.update_user_uis(admin)
|
||||
|
||||
/datum/admins/proc/delete_outfit(mob/admin, datum/outfit/O)
|
||||
GLOB.custom_outfits -= O
|
||||
qdel(O)
|
||||
to_chat(admin,"<span class='notice'>Outfit deleted.</span>")
|
||||
SStgui.update_user_uis(admin)
|
||||
|
||||
/datum/admins/proc/load_outfit(mob/admin)
|
||||
var/outfit_file = input("Pick outfit json file:", "File") as null|file
|
||||
if(!outfit_file)
|
||||
return
|
||||
var/filedata = file2text(outfit_file)
|
||||
var/json = json_decode(filedata)
|
||||
if(!json)
|
||||
to_chat(admin,"<span class='warning'>JSON decode error.</span>")
|
||||
return
|
||||
var/otype = text2path(json["outfit_type"])
|
||||
if(!ispath(otype,/datum/outfit))
|
||||
to_chat(admin,"<span class='warning'>Malformed/Outdated file.</span>")
|
||||
return
|
||||
var/datum/outfit/O = new otype
|
||||
if(!O.load_from(json))
|
||||
to_chat(admin,"<span class='warning'>Malformed/Outdated file.</span>")
|
||||
return
|
||||
GLOB.custom_outfits += O
|
||||
SStgui.update_user_uis(admin)
|
||||
|
||||
@@ -870,6 +870,12 @@
|
||||
else
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=changeling;jobban4=[REF(M)]'>Changeling</a></td>"
|
||||
|
||||
//Heretic
|
||||
if(jobban_isbanned(M, ROLE_HERETIC) || isbanned_dept)
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=heretic;jobban4=[REF(M)]'><font color=red>Heretic</font></a></td>"
|
||||
else
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=heretic;jobban4=[REF(M)]'>Heretic</a></td>"
|
||||
|
||||
//Nuke Operative
|
||||
if(jobban_isbanned(M, ROLE_OPERATIVE) || isbanned_dept)
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=operative;jobban4=[REF(M)]'><font color=red>Nuke Operative</font></a></td>"
|
||||
|
||||
@@ -484,74 +484,52 @@
|
||||
set name = "Test Areas (ALL)"
|
||||
cmd_admin_areatest(FALSE)
|
||||
|
||||
/client/proc/cmd_admin_dress(mob/M in GLOB.mob_list)
|
||||
set category = "Admin.Events"
|
||||
set name = "Select equipment"
|
||||
if(!(ishuman(M) || isobserver(M)))
|
||||
alert("Invalid mob")
|
||||
return
|
||||
|
||||
var/dresscode = robust_dress_shop()
|
||||
|
||||
if(!dresscode)
|
||||
return
|
||||
|
||||
var/delete_pocket
|
||||
var/mob/living/carbon/human/H
|
||||
if(isobserver(M))
|
||||
H = M.change_mob_type(/mob/living/carbon/human, null, null, TRUE)
|
||||
else
|
||||
H = M
|
||||
if(alert("Drop Items in Pockets? No will delete them.", "Robust quick dress shop", "Yes", "No") == "No")
|
||||
delete_pocket = TRUE
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Select Equipment") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
for (var/obj/item/I in H.get_equipped_items(delete_pocket))
|
||||
qdel(I)
|
||||
if(dresscode != "Naked")
|
||||
H.equipOutfit(dresscode)
|
||||
|
||||
H.regenerate_icons()
|
||||
|
||||
log_admin("[key_name(usr)] changed the equipment of [key_name(H)] to [dresscode].")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] changed the equipment of [ADMIN_LOOKUPFLW(H)] to [dresscode].</span>")
|
||||
|
||||
/client/proc/robust_dress_shop()
|
||||
var/list/outfits = list("Cancel","Naked","Custom","As Job...")
|
||||
var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job)
|
||||
|
||||
var/list/baseoutfits = list("Naked","Custom","As Job...", "As Plasmaman...")
|
||||
var/list/outfits = list()
|
||||
var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - typesof(/datum/outfit/plasmaman)
|
||||
|
||||
for(var/path in paths)
|
||||
var/datum/outfit/O = path //not much to initalize here but whatever
|
||||
if(initial(O.can_be_admin_equipped))
|
||||
outfits[initial(O.name)] = path
|
||||
outfits[initial(O.name)] = path
|
||||
|
||||
var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in outfits
|
||||
var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in baseoutfits + sortList(outfits)
|
||||
if (isnull(dresscode))
|
||||
return
|
||||
|
||||
if (outfits[dresscode])
|
||||
dresscode = outfits[dresscode]
|
||||
|
||||
if(dresscode == "Cancel")
|
||||
return
|
||||
|
||||
if (dresscode == "As Job...")
|
||||
var/list/job_paths = subtypesof(/datum/outfit/job)
|
||||
var/list/job_outfits = list()
|
||||
for(var/path in job_paths)
|
||||
var/datum/outfit/O = path
|
||||
if(initial(O.can_be_admin_equipped))
|
||||
job_outfits[initial(O.name)] = path
|
||||
job_outfits[initial(O.name)] = path
|
||||
|
||||
dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in job_outfits
|
||||
dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in sortList(job_outfits)
|
||||
dresscode = job_outfits[dresscode]
|
||||
if(isnull(dresscode))
|
||||
return
|
||||
|
||||
if (dresscode == "As Plasmaman...")
|
||||
var/list/plasmaman_paths = typesof(/datum/outfit/plasmaman)
|
||||
var/list/plasmaman_outfits = list()
|
||||
for(var/path in plasmaman_paths)
|
||||
var/datum/outfit/O = path
|
||||
plasmaman_outfits[initial(O.name)] = path
|
||||
|
||||
dresscode = input("Select plasmeme equipment", "Robust quick dress shop") as null|anything in sortList(plasmaman_outfits)
|
||||
dresscode = plasmaman_outfits[dresscode]
|
||||
if(isnull(dresscode))
|
||||
return
|
||||
|
||||
if (dresscode == "Custom")
|
||||
var/list/custom_names = list()
|
||||
for(var/datum/outfit/D in GLOB.custom_outfits)
|
||||
custom_names[D.name] = D
|
||||
var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names
|
||||
var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in sortList(custom_names)
|
||||
dresscode = custom_names[selected_name]
|
||||
if(isnull(dresscode))
|
||||
return
|
||||
|
||||
@@ -879,8 +879,6 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] [N.timing ? "activated" : "deactivated"] a nuke at [ADMIN_VERBOSEJMP(N)].")
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Nuke", "[N.timing]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
|
||||
/client/proc/create_outfits()
|
||||
set category = "Debug"
|
||||
set name = "Create Custom Outfit"
|
||||
@@ -1547,6 +1545,55 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
msg += "</UL></BODY></HTML>"
|
||||
src << browse(msg.Join(), "window=Player_playtime_check")
|
||||
|
||||
/obj/effect/temp_visual/fireball
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "fireball"
|
||||
name = "fireball"
|
||||
desc = "Get out of the way!"
|
||||
layer = FLY_LAYER
|
||||
randomdir = FALSE
|
||||
duration = 9
|
||||
pixel_z = 270
|
||||
|
||||
/obj/effect/temp_visual/fireball/Initialize()
|
||||
. = ..()
|
||||
animate(src, pixel_z = 0, time = duration)
|
||||
|
||||
/obj/effect/temp_visual/target
|
||||
icon = 'icons/mob/actions/actions_items.dmi'
|
||||
icon_state = "sniper_zoom"
|
||||
layer = BELOW_MOB_LAYER
|
||||
light_range = 2
|
||||
duration = 9
|
||||
|
||||
/obj/effect/temp_visual/target/ex_act()
|
||||
return
|
||||
|
||||
/obj/effect/temp_visual/target/Initialize(mapload, list/flame_hit)
|
||||
. = ..()
|
||||
INVOKE_ASYNC(src, .proc/fall, flame_hit)
|
||||
|
||||
/obj/effect/temp_visual/target/proc/fall(list/flame_hit)
|
||||
var/turf/T = get_turf(src)
|
||||
playsound(T,'sound/magic/fleshtostone.ogg', 80, 1)
|
||||
new /obj/effect/temp_visual/fireball(T)
|
||||
sleep(duration)
|
||||
if(ismineralturf(T))
|
||||
var/turf/closed/mineral/M = T
|
||||
M.gets_drilled()
|
||||
playsound(T, "explosion", 80, 1)
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700, 50, 1)
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon))
|
||||
continue
|
||||
if(islist(flame_hit) && !flame_hit[L])
|
||||
L.adjustFireLoss(40)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by the drake's fire breath!</span>")
|
||||
flame_hit[L] = TRUE
|
||||
else
|
||||
L.adjustFireLoss(10) //if we've already hit them, do way less damage
|
||||
|
||||
/datum/admins/proc/cmd_show_exp_panel(client/client_to_check)
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/client/proc/cmd_select_equipment(mob/target in GLOB.mob_list)
|
||||
set category = "Admin.Events"
|
||||
set name = "Select equipment"
|
||||
|
||||
|
||||
var/datum/select_equipment/ui = new(usr, target)
|
||||
ui.ui_interact(usr)
|
||||
|
||||
/*
|
||||
* This is the datum housing the select equipment UI.
|
||||
*
|
||||
* You may notice some oddities about the way outfits are passed to the UI and vice versa here.
|
||||
* That's because it handles both outfit typepaths (for normal outfits) *and* outfit objects (for custom outfits).
|
||||
*
|
||||
* Custom outfits need to be objects as they're created in runtime.
|
||||
* "Then just handle the normal outfits as objects too and simplify the handling" - you may say.
|
||||
* There are about 300 outfit types at the time of writing this. Initializing all of these to objects would be a huge waste.
|
||||
*
|
||||
*/
|
||||
|
||||
/datum/select_equipment
|
||||
var/client/user
|
||||
var/mob/target_mob
|
||||
|
||||
var/dummy_key
|
||||
|
||||
//static list to share all the outfit typepaths between all instances of this datum.
|
||||
var/static/list/cached_outfits
|
||||
|
||||
//a typepath if the selected outfit is a normal outfit;
|
||||
//an object if the selected outfit is a custom outfit
|
||||
var/datum/outfit/selected_outfit = /datum/outfit
|
||||
//serializable string for the UI to keep track of which outfit is selected
|
||||
var/selected_identifier = "/datum/outfit"
|
||||
|
||||
/datum/select_equipment/New(_user, mob/target)
|
||||
user = CLIENT_FROM_VAR(_user)
|
||||
|
||||
if(!ishuman(target) && !isobserver(target))
|
||||
alert("Invalid mob")
|
||||
return
|
||||
target_mob = target
|
||||
|
||||
/datum/select_equipment/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "SelectEquipment", "Select Equipment")
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
/datum/select_equipment/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/select_equipment/ui_status(mob/user, datum/ui_state/state)
|
||||
if(QDELETED(target_mob))
|
||||
return UI_CLOSE
|
||||
return ..()
|
||||
|
||||
/datum/select_equipment/ui_close(mob/user)
|
||||
clear_human_dummy(dummy_key)
|
||||
qdel(src)
|
||||
|
||||
/datum/select_equipment/proc/init_dummy()
|
||||
dummy_key = "selectequipmentUI_[target_mob]"
|
||||
generate_dummy_lookalike(dummy_key, target_mob)
|
||||
unset_busy_human_dummy(dummy_key)
|
||||
return
|
||||
|
||||
/**
|
||||
* Packs up data about an outfit as an assoc list to send to the UI as an outfit entry.
|
||||
*
|
||||
* Args:
|
||||
* * category (string) - The tab it will be under
|
||||
*
|
||||
* * identifier (typepath or ref) - This will sent this back to ui_act to preview or spawn in an outfit.
|
||||
* * Must be unique between all entries.
|
||||
*
|
||||
* * name (string) - Will be the text on the button
|
||||
*
|
||||
* * priority (bool)(optional) - If True, the UI will sort the entry to the top, right below favorites.
|
||||
*
|
||||
* * custom_entry (bool)(optional) - Send the identifier with a "ref" keyword instead of "path",
|
||||
* * for the UI to tell apart custom outfits from normal ones.
|
||||
*
|
||||
* Returns (list) An outfit entry
|
||||
*/
|
||||
|
||||
/datum/select_equipment/proc/outfit_entry(category, identifier, name, priority=FALSE, custom_entry=FALSE)
|
||||
if(custom_entry)
|
||||
return list("category" = category, "ref" = identifier, "name" = name, "priority" = priority)
|
||||
return list("category" = category, "path" = identifier, "name" = name, "priority" = priority)
|
||||
|
||||
/datum/select_equipment/proc/make_outfit_entries(category="General", list/outfit_list)
|
||||
var/list/entries = list()
|
||||
for(var/path as anything in outfit_list)
|
||||
var/datum/outfit/outfit = path
|
||||
entries += list(outfit_entry(category, path, initial(outfit.name)))
|
||||
return entries
|
||||
|
||||
//GLOB.custom_outfits lists outfit *objects* so we'll need to do some custom handling for it
|
||||
/datum/select_equipment/proc/make_custom_outfit_entries(list/outfit_list)
|
||||
var/list/entries = list()
|
||||
for(var/datum/outfit/outfit as anything in outfit_list)
|
||||
entries += list(outfit_entry("Custom", REF(outfit), outfit.name, custom_entry=TRUE)) //it's either this or special handling on the UI side
|
||||
return entries
|
||||
|
||||
/datum/select_equipment/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
if(!dummy_key)
|
||||
init_dummy()
|
||||
|
||||
var/icon/dummysprite = get_flat_human_icon(null,
|
||||
dummy_key = dummy_key,
|
||||
outfit_override = selected_outfit,
|
||||
no_anim = TRUE)
|
||||
data["icon64"] = icon2base64(dummysprite)
|
||||
data["name"] = target_mob
|
||||
|
||||
var/datum/preferences/prefs = user?.client?.prefs
|
||||
data["favorites"] = list()
|
||||
if(prefs)
|
||||
data["favorites"] = prefs.favorite_outfits
|
||||
|
||||
var/list/custom
|
||||
custom += make_custom_outfit_entries(GLOB.custom_outfits)
|
||||
data["custom_outfits"] = custom
|
||||
data["current_outfit"] = selected_identifier
|
||||
return data
|
||||
|
||||
|
||||
/datum/select_equipment/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
if(!cached_outfits)
|
||||
cached_outfits = list()
|
||||
cached_outfits += list(outfit_entry("General", /datum/outfit, "Naked", priority=TRUE))
|
||||
cached_outfits += make_outfit_entries("General", subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - typesof(/datum/outfit/plasmaman))
|
||||
cached_outfits += make_outfit_entries("Jobs", typesof(/datum/outfit/job))
|
||||
cached_outfits += make_outfit_entries("Plasmamen Outfits", typesof(/datum/outfit/plasmaman))
|
||||
|
||||
data["outfits"] = cached_outfits
|
||||
return data
|
||||
|
||||
|
||||
/datum/select_equipment/proc/resolve_outfit(text)
|
||||
|
||||
var/path = text2path(text)
|
||||
if(ispath(path, /datum/outfit))
|
||||
return path
|
||||
|
||||
else //don't bail yet - could be a custom outfit
|
||||
var/datum/outfit/custom_outfit = locate(text)
|
||||
if(istype(custom_outfit))
|
||||
return custom_outfit
|
||||
|
||||
|
||||
/datum/select_equipment/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("preview")
|
||||
var/datum/outfit/new_outfit = resolve_outfit(params["path"])
|
||||
|
||||
if(ispath(new_outfit)) //got a typepath - that means we're dealing with a normal outfit
|
||||
selected_identifier = new_outfit //these are keyed by type
|
||||
//by the way, no, they can't be keyed by name because many of them have duplicate names
|
||||
|
||||
else if(istype(new_outfit)) //got an initialized object - means it's a custom outfit
|
||||
selected_identifier = REF(new_outfit) //and the outfit will be keyed by its ref (cause its type will always be /datum/outfit)
|
||||
|
||||
else //we got nothing and should bail
|
||||
return
|
||||
|
||||
selected_outfit = new_outfit
|
||||
|
||||
if("applyoutfit")
|
||||
var/datum/outfit/new_outfit = resolve_outfit(params["path"])
|
||||
if(new_outfit && ispath(new_outfit)) //initialize it
|
||||
new_outfit = new new_outfit
|
||||
if(!istype(new_outfit))
|
||||
return
|
||||
user.admin_apply_outfit(target_mob, new_outfit)
|
||||
|
||||
if("customoutfit")
|
||||
user.outfit_manager()
|
||||
|
||||
if("togglefavorite")
|
||||
var/datum/outfit/outfit_path = resolve_outfit(params["path"])
|
||||
if(!ispath(outfit_path)) //we do *not* want custom outfits (i.e objects) here, they're not even persistent
|
||||
return
|
||||
|
||||
if(user.prefs.favorite_outfits.Find(outfit_path)) //already there, remove it
|
||||
user.prefs.favorite_outfits -= outfit_path
|
||||
else //not there, add it
|
||||
user.prefs.favorite_outfits += outfit_path
|
||||
user.prefs.save_preferences()
|
||||
|
||||
/client/proc/admin_apply_outfit(mob/target, dresscode)
|
||||
if(!ishuman(target) && !isobserver(target))
|
||||
alert("Invalid mob")
|
||||
return
|
||||
|
||||
if(!dresscode)
|
||||
return
|
||||
|
||||
var/delete_pocket
|
||||
var/mob/living/carbon/human/human_target
|
||||
if(isobserver(target))
|
||||
human_target = target.change_mob_type(/mob/living/carbon/human, delete_old_mob = TRUE)
|
||||
else
|
||||
human_target = target
|
||||
if(human_target.l_store || human_target.r_store || human_target.s_store) //saves a lot of time for admins and coders alike
|
||||
if(alert("Drop Items in Pockets? No will delete them.", "Robust quick dress shop", "Yes", "No") == "No")
|
||||
delete_pocket = TRUE
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Select Equipment") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
for(var/obj/item/item in human_target.get_equipped_items(delete_pocket))
|
||||
qdel(item)
|
||||
if(dresscode != "Naked")
|
||||
human_target.equipOutfit(dresscode)
|
||||
|
||||
human_target.regenerate_icons()
|
||||
|
||||
log_admin("[key_name(usr)] changed the equipment of [key_name(human_target)] to [dresscode].")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] changed the equipment of [ADMIN_LOOKUPFLW(human_target)] to [dresscode].</span>")
|
||||
|
||||
return dresscode
|
||||
@@ -111,6 +111,7 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
var/datum/skill_modifier/job/M = GLOB.skill_modifiers[GET_SKILL_MOD_ID(A, type)]
|
||||
if(istype(M))
|
||||
M.name = "[name] Training"
|
||||
owner.AddComponent(/datum/component/activity)
|
||||
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
|
||||
|
||||
/datum/antagonist/proc/is_banned(mob/M)
|
||||
@@ -141,6 +142,7 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
var/datum/team/team = get_team()
|
||||
if(team)
|
||||
team.remove_member(owner)
|
||||
// we don't remove the activity component on purpose--no real point to it
|
||||
qdel(src)
|
||||
|
||||
/datum/antagonist/proc/greet()
|
||||
|
||||
@@ -511,8 +511,7 @@
|
||||
|
||||
/obj/item/abductor/baton/proc/StunAttack(mob/living/L,mob/living/user)
|
||||
|
||||
L.lastattacker = user.real_name
|
||||
L.lastattackerckey = user.ckey
|
||||
L.set_last_attacker(user)
|
||||
|
||||
L.adjustStaminaLoss(35) //because previously it took 5-6 hits to actually "incapacitate" someone for the purposes of the sleep inducement
|
||||
L.DefaultCombatKnockdown(140)
|
||||
|
||||
@@ -385,8 +385,7 @@
|
||||
qdel(src)
|
||||
return
|
||||
log_combat(user, M, "used a cult spell on", source.name, "")
|
||||
M.lastattacker = user.real_name
|
||||
M.lastattackerckey = user.ckey
|
||||
M.set_last_attacker(user)
|
||||
|
||||
/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
. = ..()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//Revenants: based off of wraiths from Goon
|
||||
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invisible
|
||||
//Don't hear deadchat and are NOT normal ghosts
|
||||
//Can hear deadchat, but are NOT normal ghosts and do NOT have x-ray vision
|
||||
//Admin-spawn or random event
|
||||
|
||||
#define INVISIBILITY_REVENANT 50
|
||||
@@ -63,6 +63,7 @@
|
||||
var/essence_regenerating = TRUE //If the revenant regenerates essence or not
|
||||
var/essence_regen_amount = 5 //How much essence regenerates
|
||||
var/essence_accumulated = 0 //How much essence the revenant has stolen
|
||||
var/essence_excess = 0 //How much stolen essence available for unlocks
|
||||
var/revealed = FALSE //If the revenant can take damage from normal sources.
|
||||
var/unreveal_time = 0 //How long the revenant is revealed for, is about 2 seconds times this var.
|
||||
var/unstun_time = 0 //How long the revenant is stunned for, is about 2 seconds times this var.
|
||||
@@ -76,6 +77,7 @@
|
||||
|
||||
/mob/living/simple_animal/revenant/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_SIXTHSENSE, INNATE_TRAIT)
|
||||
AddSpell(new /obj/effect/proc_holder/spell/targeted/night_vision/revenant(null))
|
||||
AddSpell(new /obj/effect/proc_holder/spell/targeted/telepathy/revenant(null))
|
||||
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
|
||||
@@ -138,6 +140,7 @@
|
||||
. = ..()
|
||||
. += "Current essence: [essence]/[essence_regen_cap]E"
|
||||
. += "Stolen essence: [essence_accumulated]E"
|
||||
. += "Unused stolen essence: [essence_excess]E)"
|
||||
. += "Stolen perfect souls: [perfectsouls]"
|
||||
|
||||
/mob/living/simple_animal/revenant/update_health_hud()
|
||||
@@ -304,16 +307,24 @@
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/revenant/proc/unlock(essence_cost)
|
||||
if(essence_excess < essence_cost)
|
||||
return FALSE
|
||||
essence_excess -= essence_cost
|
||||
update_action_buttons_icon()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/revenant/proc/change_essence_amount(essence_amt, silent = FALSE, source = null)
|
||||
if(!src)
|
||||
return
|
||||
if(essence + essence_amt <= 0)
|
||||
if(essence + essence_amt < 0)
|
||||
return
|
||||
essence = max(0, essence+essence_amt)
|
||||
update_action_buttons_icon()
|
||||
update_health_hud()
|
||||
if(essence_amt > 0)
|
||||
essence_accumulated = max(0, essence_accumulated+essence_amt)
|
||||
essence_excess = max(0, essence_excess+essence_amt)
|
||||
update_action_buttons_icon()
|
||||
if(!silent)
|
||||
if(essence_amt > 0)
|
||||
to_chat(src, "<span class='revennotice'>Gained [essence_amt]E[source ? " from [source]":""].</span>")
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
to_chat(src, "<span class='revenminor'>You begin siphoning essence from [target]'s soul.</span>")
|
||||
if(target.stat != DEAD)
|
||||
to_chat(target, "<span class='warning'>You feel a horribly unpleasant draining sensation as your grip on life weakens...</span>")
|
||||
if(target.stat == SOFT_CRIT)
|
||||
target.Stun(46)
|
||||
reveal(46)
|
||||
stun(46)
|
||||
target.visible_message("<span class='warning'>[target] suddenly rises slightly into the air, [target.p_their()] skin turning an ashy gray.</span>")
|
||||
@@ -144,7 +146,7 @@
|
||||
if(user.inhibited)
|
||||
return FALSE
|
||||
if(locked)
|
||||
if(user.essence <= unlock_amount)
|
||||
if(user.essence_excess <= unlock_amount)
|
||||
return FALSE
|
||||
if(user.essence <= cast_amount)
|
||||
return FALSE
|
||||
@@ -158,7 +160,7 @@
|
||||
locked = FALSE
|
||||
return TRUE
|
||||
if(locked)
|
||||
if(!user.castcheck(-unlock_amount))
|
||||
if(!user.unlock(unlock_amount))
|
||||
charge_counter = charge_max
|
||||
return FALSE
|
||||
name = "[initial(name)] ([cast_amount]E)"
|
||||
@@ -185,6 +187,7 @@
|
||||
range = 5
|
||||
stun = 30
|
||||
cast_amount = 40
|
||||
unlock_amount = 25
|
||||
var/shock_range = 2
|
||||
var/shock_damage = 15
|
||||
action_icon_state = "overload_lights"
|
||||
@@ -197,7 +200,7 @@
|
||||
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/overload(turf/T, mob/user)
|
||||
for(var/obj/machinery/light/L in T)
|
||||
if(!L.on)
|
||||
return
|
||||
continue
|
||||
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</span>")
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(4, 0, L)
|
||||
@@ -226,7 +229,7 @@
|
||||
range = 4
|
||||
stun = 20
|
||||
reveal = 40
|
||||
unlock_amount = 75
|
||||
unlock_amount = 10
|
||||
cast_amount = 30
|
||||
action_icon_state = "defile"
|
||||
|
||||
@@ -277,7 +280,7 @@
|
||||
charge_max = 200
|
||||
range = 4
|
||||
cast_amount = 60
|
||||
unlock_amount = 200
|
||||
unlock_amount = 125
|
||||
action_icon_state = "malfunction"
|
||||
|
||||
//A note to future coders: do not replace this with an EMP because it will wreck malf AIs and everyone will hate you.
|
||||
@@ -324,7 +327,7 @@
|
||||
charge_max = 200
|
||||
range = 3
|
||||
cast_amount = 50
|
||||
unlock_amount = 200
|
||||
unlock_amount = 75
|
||||
action_icon_state = "blight"
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
continue
|
||||
if(!SSpersistence.IsValidDebrisLocation(C.loc, allowed_turf_typecache, allowed_z_cache, C.type, FALSE))
|
||||
continue
|
||||
weight += 0.05
|
||||
weight += 0.03
|
||||
return ..()
|
||||
|
||||
/datum/round_event/ghost_role/slaughter
|
||||
|
||||
@@ -1,30 +1,7 @@
|
||||
/datum/traitor_class/human/assassin
|
||||
/datum/traitor_class/human/subterfuge/assassin
|
||||
name = "Donk Co Operative"
|
||||
employer = "Donk Corporation"
|
||||
weight = 0
|
||||
weight = 6
|
||||
chaos = 1
|
||||
threat = 2
|
||||
|
||||
/datum/traitor_class/human/assassin/forge_single_objective(datum/antagonist/traitor/T)
|
||||
.=1
|
||||
var/permakill_prob = 20
|
||||
var/datum/game_mode/dynamic/mode
|
||||
if(istype(SSticker.mode,/datum/game_mode/dynamic))
|
||||
mode = SSticker.mode
|
||||
permakill_prob = max(0,mode.threat_level-50)
|
||||
var/list/active_ais = active_ais()
|
||||
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
|
||||
var/datum/objective/destroy/destroy_objective = new
|
||||
destroy_objective.owner = T.owner
|
||||
destroy_objective.find_target()
|
||||
T.add_objective(destroy_objective)
|
||||
else if(prob(permakill_prob))
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = T.owner
|
||||
kill_objective.find_target()
|
||||
T.add_objective(kill_objective)
|
||||
else
|
||||
var/datum/objective/assassinate/once/kill_objective = new
|
||||
kill_objective.owner = T.owner
|
||||
kill_objective.find_target()
|
||||
T.add_objective(kill_objective)
|
||||
assassin_prob = 70
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/datum/traitor_class/human/freeform
|
||||
name = "Waffle Co Agent"
|
||||
employer = "Waffle Company"
|
||||
weight = 0 // should not spawn in unless admins bus something in the traitor panel with setting traitor classes
|
||||
weight = 5
|
||||
chaos = 0
|
||||
|
||||
/datum/traitor_class/human/freeform/forge_objectives(datum/antagonist/traitor/T)
|
||||
var/datum/objective/escape/O = new
|
||||
O.explanation_text = "You have no explicit goals! While we don't approve of mindless slaughter, you may antagonize nanotrasen any way you wish! Make sure to escape alive and not in custody, though!"
|
||||
var/datum/objective/freedom/O = new
|
||||
O.explanation_text = "You have no explicit goals! While we don't approve of mindless slaughter, you may antagonize nanotrasen any way you wish! Don't get captured or killed, but if you've done nothing, you'll be in trouble!"
|
||||
O.owner = T.owner
|
||||
T.add_objective(O)
|
||||
return
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
T.assign_exchange_role(SSticker.mode.exchange_blue)
|
||||
objective_count += 1 //Exchange counts towards number of objectives
|
||||
var/toa = CONFIG_GET(number/traitor_objectives_amount)
|
||||
var/attempts = 0
|
||||
for(var/i = objective_count, i < toa, i++)
|
||||
forge_single_objective(T)
|
||||
var/success = FALSE
|
||||
while(!success && attempts < max(toa*10, 100))
|
||||
success = forge_single_objective(T)
|
||||
attempts += 1
|
||||
if(!(locate(/datum/objective/escape) in T.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = T.owner
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/datum/traitor_class/human/subterfuge
|
||||
name = "MI13 Operative"
|
||||
employer = "MI13"
|
||||
weight = 36
|
||||
weight = 25
|
||||
chaos = -5
|
||||
var/assassin_prob = 25
|
||||
|
||||
/datum/traitor_class/human/subterfuge/forge_single_objective(datum/antagonist/traitor/T)
|
||||
.=1
|
||||
var/assassin_prob = 30
|
||||
var/datum/game_mode/dynamic/mode
|
||||
if(istype(SSticker.mode,/datum/game_mode/dynamic))
|
||||
mode = SSticker.mode
|
||||
@@ -16,24 +15,31 @@
|
||||
kill_objective.owner = T.owner
|
||||
kill_objective.find_target()
|
||||
T.add_objective(kill_objective)
|
||||
return TRUE
|
||||
else
|
||||
var/list/weights = list()
|
||||
weights["sabo"] = length(subtypesof(/datum/sabotage_objective))
|
||||
weights["steal"] = length(subtypesof(/datum/objective_item/steal))
|
||||
var/datum/objective/sabotage/sabotage_objective = new
|
||||
sabotage_objective.owner = T.owner
|
||||
if(sabotage_objective.find_target())
|
||||
weights["sabo"] = length(subtypesof(/datum/objective_item/steal))
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = T.owner
|
||||
if(steal_objective.find_target())
|
||||
weights["steal"] = length(subtypesof(/datum/objective_item/steal))
|
||||
weights["download"] = !(locate(/datum/objective/download) in T.objectives || (T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
|
||||
switch(pickweight(weights))
|
||||
if("sabo")
|
||||
var/datum/objective/sabotage/sabotage_objective = new
|
||||
sabotage_objective.owner = T.owner
|
||||
sabotage_objective.find_target()
|
||||
T.add_objective(sabotage_objective)
|
||||
qdel(steal_objective)
|
||||
return TRUE
|
||||
if("steal")
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = T.owner
|
||||
steal_objective.find_target()
|
||||
T.add_objective(steal_objective)
|
||||
qdel(sabotage_objective)
|
||||
return TRUE
|
||||
if("download")
|
||||
var/datum/objective/download/download_objective = new
|
||||
download_objective.owner = T.owner
|
||||
download_objective.gen_amount_goal()
|
||||
T.add_objective(download_objective)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -7,7 +7,8 @@ GLOBAL_LIST_EMPTY(traitor_classes)
|
||||
var/chaos = 0
|
||||
var/threat = 0
|
||||
var/TC = 20
|
||||
/// Minimum players for this to randomly roll via get_random_traitor_class().
|
||||
var/processing = FALSE
|
||||
/// Minimum players for this to randomly roll via get_random_traitor_kind().
|
||||
var/min_players = 0
|
||||
var/list/uplink_filters
|
||||
|
||||
@@ -43,4 +44,6 @@ GLOBAL_LIST_EMPTY(traitor_classes)
|
||||
|
||||
/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T)
|
||||
// Any effects that need to be cleaned up if traitor class is being swapped.
|
||||
|
||||
|
||||
/datum/traitor_class/proc/on_process(/datum/antagonist/traitor/T)
|
||||
// only for processing traitor classes; runs once an SSprocessing tick
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
if(traitor_kind)
|
||||
traitor_kind.remove_innate_effects(owner.current)
|
||||
traitor_kind.clean_up_traitor(src)
|
||||
if(traitor_kind.processing)
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
swap_from_old = TRUE
|
||||
traitor_kind = GLOB.traitor_classes[kind]
|
||||
traitor_kind.apply_innate_effects(owner.current)
|
||||
@@ -33,11 +35,16 @@
|
||||
for(var/O in objectives)
|
||||
qdel(O)
|
||||
traitor_kind.forge_objectives(src)
|
||||
if(traitor_kind.processing)
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
if(swap_from_old)
|
||||
traitor_kind.finalize_traitor(src)
|
||||
traitor_kind.greet(src)
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/traitor/process()
|
||||
traitor_kind.on_process(src)
|
||||
|
||||
/proc/get_random_traitor_kind(var/list/blacklist = list())
|
||||
var/chaos_weight = 0
|
||||
if(istype(SSticker.mode,/datum/game_mode/dynamic))
|
||||
|
||||
@@ -345,6 +345,14 @@
|
||||
InsertAll("", each, GLOB.alldirs)
|
||||
..()
|
||||
|
||||
/datum/asset/spritesheet/decals
|
||||
name = "decals"
|
||||
|
||||
/datum/asset/spritesheet/decals/register()
|
||||
for(var/each in list('icons/turf/decals.dmi'))
|
||||
InsertAll("", each, GLOB.alldirs)
|
||||
..()
|
||||
|
||||
/datum/asset/spritesheet/supplypods
|
||||
name = "supplypods"
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
GLOBAL_LIST_EMPTY(bluespace_pipe_networks)
|
||||
/obj/machinery/atmospherics/pipe/bluespace
|
||||
name = "bluespace pipe"
|
||||
desc = "Transmits gas across large distances of space. Developed using bluespace technology."
|
||||
icon = 'icons/obj/atmospherics/pipes/bluespace.dmi'
|
||||
icon_state = "map"
|
||||
pipe_state = "bluespace"
|
||||
dir = SOUTH
|
||||
initialize_directions = SOUTH
|
||||
device_type = UNARY
|
||||
can_buckle = FALSE
|
||||
construction_type = /obj/item/pipe/bluespace
|
||||
var/bluespace_network_name
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/New()
|
||||
icon_state = "pipe"
|
||||
if(bluespace_network_name) // in case someone maps one in for some reason
|
||||
if(!GLOB.bluespace_pipe_networks[bluespace_network_name])
|
||||
GLOB.bluespace_pipe_networks[bluespace_network_name] = list()
|
||||
GLOB.bluespace_pipe_networks[bluespace_network_name] |= src
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/on_construction()
|
||||
. = ..()
|
||||
if(bluespace_network_name)
|
||||
if(!GLOB.bluespace_pipe_networks[bluespace_network_name])
|
||||
GLOB.bluespace_pipe_networks[bluespace_network_name] = list()
|
||||
GLOB.bluespace_pipe_networks[bluespace_network_name] |= src
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/Destroy()
|
||||
if(GLOB.bluespace_pipe_networks[bluespace_network_name])
|
||||
GLOB.bluespace_pipe_networks[bluespace_network_name] -= src
|
||||
for(var/p in GLOB.bluespace_pipe_networks[bluespace_network_name])
|
||||
var/obj/machinery/atmospherics/pipe/bluespace/P = p
|
||||
QDEL_NULL(P.parent)
|
||||
P.build_network()
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/examine(user)
|
||||
. = ..()
|
||||
. += "<span class='notice'>This one is connected to the \"[html_encode(bluespace_network_name)]\" network.</span>"
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/SetInitDirections()
|
||||
initialize_directions = dir
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/pipeline_expansion()
|
||||
return ..() + GLOB.bluespace_pipe_networks[bluespace_network_name] - src
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/hide()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/update_icon(showpipe)
|
||||
underlays.Cut()
|
||||
|
||||
var/turf/T = loc
|
||||
if(level == 2 || !T.intact)
|
||||
showpipe = TRUE
|
||||
plane = GAME_PLANE
|
||||
else
|
||||
showpipe = FALSE
|
||||
plane = FLOOR_PLANE
|
||||
|
||||
if(!showpipe)
|
||||
return //no need to update the pipes if they aren't showing
|
||||
|
||||
var/connected = 0 //Direction bitset
|
||||
|
||||
for(var/i in 1 to device_type) //adds intact pieces
|
||||
if(nodes[i])
|
||||
var/obj/machinery/atmospherics/node = nodes[i]
|
||||
var/image/img = get_pipe_underlay("pipe_intact", get_dir(src, node), node.pipe_color)
|
||||
underlays += img
|
||||
connected |= img.dir
|
||||
|
||||
for(var/direction in GLOB.cardinals)
|
||||
if((initialize_directions & direction) && !(connected & direction))
|
||||
underlays += get_pipe_underlay("pipe_exposed", direction)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/paint()
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/atmospherics/pipe/bluespace/proc/get_pipe_underlay(state, dir, color = null)
|
||||
if(color)
|
||||
. = getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', state, dir, color)
|
||||
else
|
||||
. = getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', state, dir)
|
||||
@@ -4,10 +4,10 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation)
|
||||
GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
|
||||
/**
|
||||
* Corresponds to single entry in gateway control.
|
||||
*
|
||||
* Will NOT be added automatically to GLOB.gateway_destinations list.
|
||||
*/
|
||||
* Corresponds to single entry in gateway control.
|
||||
*
|
||||
* Will NOT be added automatically to GLOB.gateway_destinations list.
|
||||
*/
|
||||
/datum/gateway_destination
|
||||
var/name = "Unknown Destination"
|
||||
var/wait = 0 /// How long after roundstart this destination becomes active
|
||||
@@ -85,7 +85,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
. = "Exit gateway unpowered."
|
||||
|
||||
/datum/gateway_destination/gateway/get_target_turf()
|
||||
return get_step(target_gateway.portal,SOUTH)
|
||||
return get_step(target_gateway, SOUTH)
|
||||
|
||||
/datum/gateway_destination/gateway/post_transfer(atom/movable/AM)
|
||||
. = ..()
|
||||
@@ -144,7 +144,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
name = "gateway"
|
||||
desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations."
|
||||
icon = 'icons/obj/machines/gateway.dmi'
|
||||
icon_state = "off"
|
||||
icon_state = "portal_frame"
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
|
||||
// 3x2 offset by one row
|
||||
@@ -171,10 +171,14 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
var/datum/gateway_destination/target
|
||||
/// bumper object, the thing that starts actual teleport
|
||||
var/obj/effect/gateway_portal_bumper/portal
|
||||
/// Visual object for handling the viscontents
|
||||
var/obj/effect/gateway_portal_effect/portal_visuals
|
||||
|
||||
/obj/machinery/gateway/Initialize()
|
||||
generate_destination()
|
||||
update_icon()
|
||||
portal_visuals = new
|
||||
vis_contents += portal_visuals
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gateway/proc/generate_destination()
|
||||
@@ -191,6 +195,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
if(use_power == ACTIVE_POWER_USE)
|
||||
use_power = IDLE_POWER_USE
|
||||
update_icon()
|
||||
portal_visuals.reset_visuals()
|
||||
|
||||
/obj/machinery/gateway/process()
|
||||
if((stat & (NOPOWER)) && use_power)
|
||||
@@ -198,12 +203,6 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
deactivate()
|
||||
return
|
||||
|
||||
/obj/machinery/gateway/update_icon_state()
|
||||
if(target)
|
||||
icon_state = "on"
|
||||
else
|
||||
icon_state = "off"
|
||||
|
||||
/obj/machinery/gateway/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE)
|
||||
return
|
||||
|
||||
@@ -216,6 +215,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
return
|
||||
target = D
|
||||
target.activate(destination)
|
||||
portal_visuals.setup_visuals(target)
|
||||
generate_bumper()
|
||||
use_power = ACTIVE_POWER_USE
|
||||
update_icon()
|
||||
@@ -307,7 +307,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
try_to_connect(D)
|
||||
return TRUE
|
||||
if("deactivate")
|
||||
if(G && G.target)
|
||||
if(G?.target)
|
||||
G.deactivate()
|
||||
return TRUE
|
||||
|
||||
@@ -324,3 +324,39 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
/obj/item/paper/fluff/gateway
|
||||
info = "Congratulations,<br><br>Your station has been selected to carry out the Gateway Project.<br><br>The equipment will be shipped to you at the start of the next quarter.<br> You are to prepare a secure location to house the equipment as outlined in the attached documents.<br><br>--Nanotrasen Bluespace Research"
|
||||
name = "Confidential Correspondence, Pg 1"
|
||||
|
||||
/obj/effect/gateway_portal_effect
|
||||
appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
vis_flags = VIS_INHERIT_ID
|
||||
layer = GATEWAY_UNDERLAY_LAYER //Slightly lower than gateway itself
|
||||
var/alpha_icon = 'icons/obj/machines/gateway.dmi'
|
||||
var/alpha_icon_state = "portal_mask"
|
||||
var/datum/gateway_destination/our_destination
|
||||
|
||||
|
||||
/obj/effect/gateway_portal_effect/proc/setup_visuals(datum/gateway_destination/D)
|
||||
our_destination = D
|
||||
update_portal_filters()
|
||||
|
||||
/obj/effect/gateway_portal_effect/proc/reset_visuals()
|
||||
our_destination = null
|
||||
update_portal_filters()
|
||||
|
||||
/obj/effect/gateway_portal_effect/proc/update_portal_filters()
|
||||
clear_filters()
|
||||
vis_contents = null
|
||||
|
||||
if(!our_destination)
|
||||
return
|
||||
|
||||
|
||||
add_filter("portal_alpha", 1, list("type" = "alpha", "icon" = icon(alpha_icon, alpha_icon_state), "x" = 32, "y" = 32))
|
||||
add_filter("portal_blur", 1, list("type" = "blur", "size" = 0.5))
|
||||
add_filter("portal_ripple", 1, list("type" = "ripple", "size" = 2, "radius" = 1, "falloff" = 1, "y" = 7))
|
||||
|
||||
animate(get_filter("portal_ripple"), time = 1.3 SECONDS, loop = -1, easing = LINEAR_EASING, radius = 32)
|
||||
|
||||
var/turf/center_turf = our_destination.get_target_turf()
|
||||
|
||||
vis_contents += block(locate(center_turf.x - 1, center_turf.y - 1, center_turf.z), locate(center_turf.x + 1, center_turf.y + 1, center_turf.z))
|
||||
|
||||
@@ -48,3 +48,12 @@
|
||||
price_max = 300
|
||||
stock_max = 3
|
||||
availability_prob = 40
|
||||
|
||||
/datum/blackmarket_item/weapon/derringer
|
||||
name = ".38 Derringer"
|
||||
desc = "Compact safety! Now at a premium!"
|
||||
item = /obj/item/gun/ballistic/derringer
|
||||
price_min = 500
|
||||
price_max = 1500
|
||||
stock_max = 3
|
||||
availability_prob = 30
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
cost = 250
|
||||
unit_name = "heart"
|
||||
export_types = list(/obj/item/organ/heart)
|
||||
exclude_types = list(/obj/item/organ/heart/cursed, /obj/item/organ/heart/cybernetic)
|
||||
exclude_types = list(/obj/item/organ/heart/cursed, /obj/item/organ/heart/cybernetic/tier2, /obj/item/organ/heart/cybernetic/tier3)
|
||||
|
||||
/datum/export/organs/tongue
|
||||
cost = 75
|
||||
@@ -92,29 +92,30 @@
|
||||
cost = 50 //can be replaced
|
||||
unit_name = "stomach"
|
||||
export_types = list(/obj/item/organ/stomach)
|
||||
exclude_types = list(/obj/item/organ/stomach/cybernetic/tier2, /obj/item/organ/stomach/cybernetic/tier3)
|
||||
|
||||
/datum/export/organs/lungs
|
||||
cost = 150
|
||||
unit_name = "lungs"
|
||||
export_types = list(/obj/item/organ/lungs)
|
||||
exclude_types = list(/obj/item/organ/lungs/cybernetic, /obj/item/organ/lungs/cybernetic/upgraded)
|
||||
export_types = list(/obj/item/organ/lungs,)
|
||||
exclude_types = list(/obj/item/organ/lungs/cybernetic/tier2, /obj/item/organ/lungs/cybernetic/tier3)
|
||||
|
||||
/datum/export/organs/liver
|
||||
cost = 175
|
||||
unit_name = "liver"
|
||||
export_types = list(/obj/item/organ/liver)
|
||||
exclude_types = list(/obj/item/organ/liver/cybernetic, /obj/item/organ/liver/cybernetic/upgraded)
|
||||
exclude_types = list(/obj/item/organ/liver/cybernetic/tier2, /obj/item/organ/liver/cybernetic/tier3)
|
||||
|
||||
/datum/export/organs/cybernetic
|
||||
cost = 225
|
||||
unit_name = "cybernetic organ"
|
||||
export_types = list(/obj/item/organ/liver/cybernetic, /obj/item/organ/lungs/cybernetic, /obj/item/organ/eyes/robotic, /obj/item/organ/heart/cybernetic)
|
||||
exclude_types = list(/obj/item/organ/lungs/cybernetic/upgraded, /obj/item/organ/liver/cybernetic/upgraded)
|
||||
export_types = list(/obj/item/organ/liver/cybernetic/tier2, /obj/item/organ/lungs/cybernetic/tier2, /obj/item/organ/eyes/robotic/shield, /obj/item/organ/eyes/robotic/glow, /obj/item/organ/stomach/cybernetic/tier2, /obj/item/organ/heart/cybernetic/tier2)
|
||||
exclude_types = list(/obj/item/organ/liver/cybernetic/tier3, /obj/item/organ/lungs/cybernetic/tier3, /obj/item/organ/eyes/robotic/xray, /obj/item/organ/eyes/robotic/thermals, /obj/item/organ/stomach/cybernetic/tier3, /obj/item/organ/heart/cybernetic/tier3)
|
||||
|
||||
/datum/export/organs/upgraded
|
||||
cost = 275
|
||||
unit_name = "upgraded cybernetic organ"
|
||||
export_types = list(/obj/item/organ/lungs/cybernetic/upgraded, /obj/item/organ/liver/cybernetic/upgraded)
|
||||
export_types = list(/obj/item/organ/liver/cybernetic/tier3, /obj/item/organ/lungs/cybernetic/tier3, /obj/item/organ/eyes/robotic/xray, /obj/item/organ/eyes/robotic/thermals, /obj/item/organ/stomach/cybernetic/tier3, /obj/item/organ/heart/cybernetic/tier3)
|
||||
|
||||
/datum/export/organs/tail // yeah have fun pulling this off someone without catching a bwoink
|
||||
cost = 500
|
||||
|
||||
@@ -15,13 +15,17 @@
|
||||
var/special_enabled = FALSE
|
||||
var/DropPodOnly = FALSE //only usable by the Bluespace Drop Pod via the express cargo console
|
||||
var/admin_spawned = FALSE //Can only an admin spawn this crate?
|
||||
// this might be all in all unnecessary with current code if some changes are made
|
||||
var/goody = PACK_GOODY_NONE //Small items can be grouped into a single crate.They also come in a closet/lockbox instead of a full crate, so the 700 min doesn't apply
|
||||
var/can_private_buy = TRUE //Can it be purchased privately by each crewmember?
|
||||
|
||||
/datum/supply_pack/proc/generate(atom/A, datum/bank_account/paying_account)
|
||||
var/obj/structure/closet/crate/C
|
||||
if(paying_account)
|
||||
C = new /obj/structure/closet/crate/secure/owned(A, paying_account)
|
||||
if(ispath(crate_type, /obj/structure/closet/secure_closet/goodies)) // lets ensure private orders don't come in crates when the original one comes in lockers
|
||||
C = new /obj/structure/closet/secure_closet/goodies/owned(A, paying_account) // that would lead to infinite money exploits
|
||||
else
|
||||
C = new /obj/structure/closet/crate/secure/owned(A, paying_account)
|
||||
C.name = "[crate_name] - Purchased by [paying_account.account_holder]"
|
||||
else
|
||||
C = new crate_type(A)
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
/obj/effect/spawner/bundle/crate/surplusrifle,
|
||||
/obj/item/storage/toolbox/ammo/surplus)
|
||||
crate_name = "surplus military crate"
|
||||
crate_type = /obj/structure/closet/crate/secure/soviet
|
||||
|
||||
/datum/supply_pack/security/armory/russian/fill(obj/structure/closet/crate/C)
|
||||
for(var/i in 1 to 5)
|
||||
@@ -232,3 +233,35 @@
|
||||
desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
|
||||
cost = 1500
|
||||
contains = list(/obj/item/gun/energy/laser/hellgun)
|
||||
|
||||
/datum/supply_pack/security/armory/derringerclassic
|
||||
name = "Holdout Crate"
|
||||
crate_name = "dented crate"
|
||||
desc = "Hey kid.. c'mere. Boss says we need to offload these, to any buyer, no questions asked. You pay us, we give you three of these guns, no strings attached. Locks are to ensure they get to PAYING customers."
|
||||
cost = 2000
|
||||
contraband = TRUE
|
||||
can_private_buy = TRUE
|
||||
contains = list(/obj/item/storage/fancy/cigarettes/derringer/smuggled,
|
||||
/obj/item/storage/fancy/cigarettes/derringer/smuggled,
|
||||
/obj/item/storage/fancy/cigarettes/derringer/smuggled,
|
||||
/obj/item/storage/wallet)
|
||||
|
||||
/datum/supply_pack/security/armory/esoteric_arms
|
||||
name = "Esoteric Armory Shipment"
|
||||
desc = "Well.. you're an agent of taste, I can tell that much. For the right price.. we could see our way clear to send you one of our more... unique weapons."
|
||||
hidden = TRUE
|
||||
cost = 10000
|
||||
can_private_buy = TRUE
|
||||
crate_name = "dusty crate"
|
||||
var/num_contained = 1
|
||||
contains = list(/obj/item/gun/ballistic/shotgun/leveraction,
|
||||
/obj/item/storage/fancy/cigarettes/derringer/gold,
|
||||
/obj/item/gun/ballistic/revolver/nagant,
|
||||
/obj/item/gun/ballistic/automatic/pistol/APS,
|
||||
/obj/item/gun/ballistic/revolver/golden)
|
||||
|
||||
/datum/supply_pack/security/armory/esoteric_arms/fill(obj/structure/closet/crate/C)
|
||||
var/list/L = contains.Copy()
|
||||
for(var/i in 1 to num_contained)
|
||||
var/item = pick_n_take(L)
|
||||
new item(C)
|
||||
|
||||
@@ -81,3 +81,4 @@
|
||||
desc = "Contains one standard epinephrine medipen and one standard emergency first-aid kit medipen. For when you want to prepare for the worst."
|
||||
cost = 500
|
||||
contains = list(/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/hypospray/medipen/ekit)
|
||||
|
||||
|
||||
@@ -14,56 +14,56 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/datum/supply_pack/materials/cardboard50
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "50 Cardboard Sheets"
|
||||
desc = "Create a bunch of boxes."
|
||||
cost = 300 //thrice their export value
|
||||
contains = list(/obj/item/stack/sheet/cardboard/fifty)
|
||||
|
||||
/datum/supply_pack/materials/glass50
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "50 Glass Sheets"
|
||||
desc = "Let some nice light in with fifty glass sheets!"
|
||||
cost = 300 //double their export value
|
||||
contains = list(/obj/item/stack/sheet/glass/fifty)
|
||||
|
||||
/datum/supply_pack/materials/metal50
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "50 Metal Sheets"
|
||||
desc = "Any construction project begins with a good stack of fifty metal sheets!"
|
||||
cost = 300 //double their export value
|
||||
contains = list(/obj/item/stack/sheet/metal/fifty)
|
||||
|
||||
/datum/supply_pack/materials/plasteel20
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "20 Plasteel Sheets"
|
||||
desc = "Reinforce the station's integrity with twenty plasteel sheets!"
|
||||
cost = 4000
|
||||
contains = list(/obj/item/stack/sheet/plasteel/twenty)
|
||||
|
||||
/datum/supply_pack/materials/plastic50
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "50 Plastic Sheets"
|
||||
desc = "Build a limitless amount of toys with fifty plastic sheets!"
|
||||
cost = 200 // double their export
|
||||
contains = list(/obj/item/stack/sheet/plastic/twenty)
|
||||
|
||||
/datum/supply_pack/materials/sandstone30
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "30 Sandstone Blocks"
|
||||
desc = "Neither sandy nor stoney, these thirty blocks will still get the job done."
|
||||
cost = 150 // five times their export
|
||||
contains = list(/obj/item/stack/sheet/mineral/sandstone/thirty)
|
||||
|
||||
/datum/supply_pack/materials/wood20
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "20 Wood Planks"
|
||||
desc = "Turn cargo's boring metal groundwork into beautiful panelled flooring and much more with twenty wooden planks!"
|
||||
cost = 400 // 6-7 planks shy from having equal import/export prices
|
||||
contains = list(/obj/item/stack/sheet/mineral/wood/twenty)
|
||||
|
||||
/datum/supply_pack/materials/rcdammo
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "Large RCD ammo Single-Pack"
|
||||
desc = "A single large compressed RCD matter pack, to help with any holes or projects people might be working on."
|
||||
cost = 600
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/datum/supply_pack/misc/carpet
|
||||
goody = PACK_GOODY_PUBLIC
|
||||
crate_type = /obj/structure/closet/secure_closet/goodies
|
||||
name = "Classic Carpet Single-Pack"
|
||||
desc = "Plasteel floor tiles getting on your nerves? This 50 units stack of extra soft carpet will tie any room together."
|
||||
cost = 200
|
||||
|
||||
@@ -80,15 +80,15 @@
|
||||
|
||||
/datum/supply_pack/security/russianclothing
|
||||
name = "Russian Surplus Clothing"
|
||||
desc = "An old russian crate full of surplus armor that they used to use! Has two sets of bulletproff armor, a few union suits and some warm hats!"
|
||||
desc = "An old russian crate full of surplus armor that they used to use! Has two sets of bulletproof armor, a few union suits and some warm hats!"
|
||||
contraband = TRUE
|
||||
cost = 5750 // Its basicly sec suits, good boots/gloves
|
||||
contains = list(/obj/item/clothing/suit/armor/navyblue/russian,
|
||||
/obj/item/clothing/suit/armor/navyblue/russian,
|
||||
contains = list(/obj/item/clothing/under/syndicate/rus_army,
|
||||
/obj/item/clothing/under/syndicate/rus_army,
|
||||
/obj/item/clothing/shoes/combat,
|
||||
/obj/item/clothing/shoes/combat,
|
||||
/obj/item/clothing/head/ushanka,
|
||||
/obj/item/clothing/head/ushanka,
|
||||
/obj/item/clothing/head/helmet/rus_helmet,
|
||||
/obj/item/clothing/head/helmet/rus_helmet,
|
||||
/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/clothing/head/helmet/alt,
|
||||
@@ -98,23 +98,23 @@
|
||||
/obj/item/clothing/mask/gas,
|
||||
/obj/item/clothing/mask/gas)
|
||||
crate_name = "surplus russian clothing"
|
||||
crate_type = /obj/structure/closet/crate/internals
|
||||
crate_type = /obj/structure/closet/crate/secure/soviet
|
||||
|
||||
/datum/supply_pack/security/russian_partisan
|
||||
name = "Russian Partisan Gear"
|
||||
desc = "An old russian partisan equipment crate, comes with a full russian outfit, a loaded surplus rifle and a second magazine."
|
||||
contraband = TRUE
|
||||
access = FALSE
|
||||
cost = 6500
|
||||
contains = list(/obj/item/clothing/suit/armor/navyblue/russian,
|
||||
/obj/item/clothing/shoes/combat,
|
||||
/obj/item/clothing/head/ushanka,
|
||||
/obj/item/clothing/head/helmet/rus_helmet,
|
||||
/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/clothing/head/helmet/alt,
|
||||
/obj/item/clothing/gloves/tackler/combat/insulated,
|
||||
/obj/item/clothing/under/syndicate/rus_army,
|
||||
/obj/item/clothing/mask/gas)
|
||||
crate_name = "surplus russian gear"
|
||||
crate_type = /obj/structure/closet/crate/internals
|
||||
crate_type = /obj/structure/closet/crate/secure/soviet
|
||||
|
||||
/datum/supply_pack/security/russian_partisan/fill(obj/structure/closet/crate/C)
|
||||
..()
|
||||
@@ -241,7 +241,7 @@
|
||||
access = FALSE
|
||||
access_any = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS)
|
||||
contains = list(/obj/item/ammo_box/c38/dumdum)
|
||||
crate_name = ".38 match crate"
|
||||
crate_name = ".38 dumdum crate"
|
||||
|
||||
/datum/supply_pack/security/match
|
||||
name = ".38 Match Grade Speedloader"
|
||||
|
||||
@@ -227,6 +227,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/persistent_scars = TRUE
|
||||
///If we want to broadcast deadchat connect/disconnect messages
|
||||
var/broadcast_login_logout = TRUE
|
||||
///What outfit typepaths we've favorited in the SelectEquipment menu
|
||||
var/list/favorite_outfits = list()
|
||||
/// We have 5 slots for persistent scars, if enabled we pick a random one to load (empty by default) and scars at the end of the shift if we survived as our original person
|
||||
var/list/scars_list = list("1" = "", "2" = "", "3" = "", "4" = "", "5" = "")
|
||||
/// Which of the 5 persistent scar slots we randomly roll to load for this round, if enabled. Actually rolled in [/datum/preferences/proc/load_character(slot)]
|
||||
|
||||
@@ -385,6 +385,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["auto_ooc"] >> auto_ooc
|
||||
S["no_tetris_storage"] >> no_tetris_storage
|
||||
|
||||
//favorite outfits
|
||||
S["favorite_outfits"] >> favorite_outfits
|
||||
|
||||
var/list/parsed_favs = list()
|
||||
for(var/typetext in favorite_outfits)
|
||||
var/datum/outfit/path = text2path(typetext)
|
||||
if(ispath(path)) //whatever typepath fails this check probably doesn't exist anymore
|
||||
parsed_favs += path
|
||||
favorite_outfits = uniqueList(parsed_favs)
|
||||
|
||||
//try to fix any outdated data if necessary
|
||||
if(needs_update >= 0)
|
||||
@@ -434,6 +443,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
no_tetris_storage = sanitize_integer(no_tetris_storage, 0, 1, initial(no_tetris_storage))
|
||||
key_bindings = sanitize_islist(key_bindings, list())
|
||||
modless_key_bindings = sanitize_islist(modless_key_bindings, list())
|
||||
favorite_outfits = SANITIZE_LIST(favorite_outfits)
|
||||
|
||||
verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped
|
||||
|
||||
@@ -535,6 +545,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["pda_skin"], pda_skin)
|
||||
WRITE_FILE(S["key_bindings"], key_bindings)
|
||||
WRITE_FILE(S["modless_key_bindings"], modless_key_bindings)
|
||||
WRITE_FILE(S["favorite_outfits"], favorite_outfits)
|
||||
|
||||
//citadel code
|
||||
WRITE_FILE(S["screenshake"], screenshake)
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
set desc = "Authorizes your account in the panic bunker of any servers connected to this function."
|
||||
set category = "OOC"
|
||||
|
||||
if(prefs.db_flags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE)
|
||||
to_chat(src, "<span class='danger'>You are not age verified.</span>")
|
||||
return
|
||||
|
||||
if(autobunker_last_try + 5 SECONDS > world.time)
|
||||
to_chat(src, "<span class='danger'>Function on cooldown, try again in 5 seconds.</span>")
|
||||
return
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user