Merge remote-tracking branch 'upstream/master' into rp-markings-attempt-two

This commit is contained in:
Timothy Teakettle
2021-05-02 22:20:57 +01:00
70 changed files with 2593 additions and 711 deletions
+66 -30
View File
@@ -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
+10 -7
View File
@@ -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
+8 -5
View File
@@ -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
+35 -27
View File
@@ -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
+1
View File
@@ -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.
+3
View File
@@ -136,3 +136,6 @@
// paintings
#define VV_HK_REMOVE_PAINTING "remove_painting"
//outfits
#define VV_HK_TO_OUTFIT_EDITOR "outfit_editor"
+1
View File
@@ -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))
+3 -4
View File
@@ -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
+2
View File
@@ -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)
+6
View File
@@ -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
+6 -1
View File
@@ -49,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,
+2 -2
View File
@@ -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))
+1 -1
View File
@@ -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)
+3 -2
View File
@@ -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)
+1 -1
View File
@@ -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]
+294 -57
View File
@@ -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)
+234 -195
View File
@@ -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!")
+7 -1
View File
@@ -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()
-15
View File
@@ -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
..()
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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,
+196
View File
@@ -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
+73
View File
@@ -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)
+32
View File
@@ -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)
+22 -44
View File
@@ -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
-2
View File
@@ -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"
+227
View File
@@ -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
@@ -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"
@@ -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
+8 -10
View File
@@ -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,21 @@
/obj/item/clothing/mask/gas,
/obj/item/clothing/mask/gas)
crate_name = "surplus russian clothing"
crate_type = /obj/structure/closet/crate/internals
/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
/datum/supply_pack/security/russian_partisan/fill(obj/structure/closet/crate/C)
..()
@@ -241,7 +239,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"
+2
View File
@@ -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)]
@@ -406,6 +406,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)
@@ -455,6 +464,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
@@ -556,6 +566,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)
+1 -1
View File
@@ -3,7 +3,7 @@
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))
if(prefs.db_flags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE)
to_chat(src, "<span class='danger'>You are not age verified.</span>")
return
+1 -2
View File
@@ -83,8 +83,7 @@
standard_outfit_options = list()
for(var/path in subtypesof(/datum/outfit/job))
var/datum/outfit/O = path
if(initial(O.can_be_admin_equipped))
standard_outfit_options[initial(O.name)] = path
standard_outfit_options[initial(O.name)] = path
sortTim(standard_outfit_options, /proc/cmp_text_asc)
outfit_options = standard_outfit_options
@@ -358,6 +358,12 @@
..()
user.cure_blind("blindfold_[REF(src)]")
/obj/item/clothing/glasses/fakeblindfold
name = "thin blindfold"
desc = "Covers the eyes, but not thick enough to obscure vision. Mostly for aesthetic."
icon_state = "blindfoldwhite"
item_state = "blindfoldwhite"
/obj/item/clothing/glasses/sunglasses/blindfold/white
name = "blind personnel blindfold"
desc = "Indicates that the wearer suffers from blindness."
+1 -1
View File
@@ -145,7 +145,7 @@
/obj/item/clothing/suit/armor/riot
name = "riot suit"
desc = "A suit of semi-flexible polycarbonate body armor with heavy padding to protect against melee attacks. Helps the wearer resist shoving in close quarters."
icon_state = "riot"
icon_state = "swat"
item_state = "swat_suit"
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
+44 -4
View File
@@ -24,7 +24,12 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
/mob/living/carbon/human/dummy/proc/wipe_state()
delete_equipment()
icon_render_key = null
cut_overlays()
cut_overlays(TRUE)
/mob/living/carbon/human/dummy/setup_human_dna()
create_dna(src)
randomize_human(src)
dna.initialize_dna(skip_index = TRUE) //Skip stuff that requires full round init.
//Inefficient pooling/caching way.
GLOBAL_LIST_EMPTY(human_dummy_list)
@@ -42,13 +47,48 @@ GLOBAL_LIST_EMPTY(dummy_mob_list)
D = new
GLOB.human_dummy_list[slotkey] = D
GLOB.dummy_mob_list += D
else
D.regenerate_icons() //they were cut in wipe_state()
D.in_use = TRUE
return D
/proc/unset_busy_human_dummy(slotnumber)
if(!slotnumber)
/proc/generate_dummy_lookalike(slotkey, mob/target)
if(!istype(target))
return generate_or_wait_for_human_dummy(slotkey)
var/mob/living/carbon/human/dummy/copycat = generate_or_wait_for_human_dummy(slotkey)
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
carbon_target.dna.transfer_identity(copycat, transfer_SE = TRUE)
if(ishuman(target))
var/mob/living/carbon/human/human_target = target
human_target.copy_clothing_prefs(copycat)
copycat.updateappearance(icon_update=TRUE, mutcolor_update=TRUE, mutations_overlay_update=TRUE)
else
//even if target isn't a carbon, if they have a client we can make the
//dummy look like what their human would look like based on their prefs
target?.client?.prefs?.copy_to(copycat, icon_updates=TRUE, roundstart_checks=FALSE)
return copycat
/proc/unset_busy_human_dummy(slotkey)
if(!slotkey)
return
var/mob/living/carbon/human/dummy/D = GLOB.human_dummy_list[slotnumber]
var/mob/living/carbon/human/dummy/D = GLOB.human_dummy_list[slotkey]
if(istype(D))
D.wipe_state()
D.in_use = FALSE
/proc/clear_human_dummy(slotkey)
if(!slotkey)
return
var/mob/living/carbon/human/dummy/dummy = GLOB.human_dummy_list[slotkey]
GLOB.human_dummy_list -= slotkey
if(istype(dummy))
GLOB.dummy_mob_list -= dummy
qdel(dummy)
@@ -14,10 +14,7 @@
//initialize limbs first
create_bodyparts()
//initialize dna. for spawned humans; overwritten by other code
create_dna(src)
randomize_human(src)
dna.initialize_dna()
setup_human_dna()
if(dna.species)
set_species(dna.species.type)
@@ -36,6 +33,11 @@
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /atom.proc/clean_blood)
GLOB.human_list += src
/mob/living/carbon/human/proc/setup_human_dna()
//initialize dna. for spawned humans; overwritten by other code
create_dna(src)
randomize_human(src)
dna.initialize_dna()
/mob/living/carbon/human/ComponentInitialize()
. = ..()
@@ -176,3 +176,9 @@
/mob/living/carbon/human/get_biological_state()
return dna.species.get_biological_state()
///copies over clothing preferences like underwear to another human
/mob/living/carbon/human/proc/copy_clothing_prefs(mob/living/carbon/human/destination)
destination.underwear = underwear
destination.undershirt = undershirt
destination.socks = socks
@@ -23,7 +23,7 @@ Difficulty: Hard
*/
/mob/living/simple_animal/hostile/megafauna/bubblegum
name = "bubblegum"
name = "Bubblegum"
desc = "In what passes for a hierarchy among slaughter demons, this one is king."
health = 2500
maxHealth = 2500
@@ -443,7 +443,7 @@ Difficulty: Hard
charge(chargeat, delay, chargepast)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination
name = "bubblegum's hallucination"
name = "Bubblegum's hallucination"
desc = "Is that really just a hallucination?"
health = 1
maxHealth = 1
@@ -64,7 +64,6 @@
friendly_verb_simple = "groom"
mob_size = MOB_SIZE_SMALL
movement_type = FLYING
gold_core_spawnable = FRIENDLY_SPAWN
var/parrot_damage_upper = 10
var/parrot_state = PARROT_WANDER //Hunt for a perch when created
@@ -25,7 +25,7 @@
/obj/item/ammo_casing/chemgun
name = "dart synthesiser"
desc = "A high-power spring, linked to an energy-based dart synthesiser."
projectile_type = /obj/item/projectile/bullet/dart
projectile_type = /obj/item/projectile/bullet/dart/piercing
firing_effect_type = null
/obj/item/ammo_casing/chemgun/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
@@ -35,7 +35,7 @@
var/obj/item/gun/chem/CG = loc
if(CG.syringes_left <= 0)
return
CG.reagents.trans_to(BB, 15)
CG.reagents.trans_to(BB, 10)
BB.name = "chemical dart"
CG.syringes_left--
..()
@@ -407,11 +407,11 @@
fire_sound = 'sound/weapons/rifleshot.ogg'
weapon_weight = WEAPON_HEAVY
mag_type = /obj/item/ammo_box/magazine/m10mm/rifle
fire_delay = 30
fire_delay = 10
burst_size = 1
can_unsuppress = TRUE
can_suppress = TRUE
w_class = WEIGHT_CLASS_HUGE
w_class = WEIGHT_CLASS_BULKY
slot_flags = ITEM_SLOT_BACK
automatic_burst_overlay = FALSE
actions_types = list()
@@ -13,9 +13,9 @@
custom_materials = list(/datum/material/iron=2000)
clumsy_check = FALSE
fire_sound = 'sound/items/syringeproj.ogg'
var/time_per_syringe = 250
var/syringes_left = 4
var/max_syringes = 4
var/time_per_syringe = 300
var/syringes_left = 5
var/max_syringes = 5
var/last_synth = 0
/obj/item/gun/chem/Initialize()
@@ -644,66 +644,109 @@
//Cybernetic organs
/datum/design/cybernetic_liver
name = "Cybernetic Liver"
desc = "A cybernetic liver"
name = "Basic Cybernetic Liver"
desc = "A basic cybernetic liver."
id = "cybernetic_liver"
build_type = PROTOLATHE | MECHFAB
construction_time = 40
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/liver/cybernetic
category = list("Misc","Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/cybernetic_heart
name = "Cybernetic Heart"
desc = "A cybernetic heart"
id = "cybernetic_heart"
build_type = PROTOLATHE | MECHFAB
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/heart/cybernetic
category = list("Misc","Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/cybernetic_heart_u
name = "Upgraded Cybernetic Heart"
desc = "An upgraded cybernetic heart"
id = "cybernetic_heart_u"
build_type = PROTOLATHE | MECHFAB
construction_time = 50
materials = list(/datum/material/iron = 500, /datum/material/glass = 500, /datum/material/silver = 500)
build_path = /obj/item/organ/heart/cybernetic/upgraded
category = list("Misc", "Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/cybernetic_liver_u
name = "Upgraded Cybernetic Liver"
desc = "An upgraded cybernetic liver"
id = "cybernetic_liver_u"
build_type = PROTOLATHE | MECHFAB
/datum/design/cybernetic_liver/tier2
name = "Cybernetic Liver"
desc = "A cybernetic liver."
id = "cybernetic_liver_tier2"
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/liver/cybernetic/upgraded
category = list("Misc","Medical Designs")
build_path = /obj/item/organ/liver/cybernetic/tier2
/datum/design/cybernetic_liver/tier3
name = "Upgraded Cybernetic Liver"
desc = "An upgraded cybernetic liver."
id = "cybernetic_liver_tier3"
construction_time = 50
materials = list(/datum/material/iron = 500, /datum/material/glass = 500, /datum/material/silver = 600, /datum/material/gold = 600, /datum/material/plasma = 1000, /datum/material/diamond = 2000)
build_path = /obj/item/organ/liver/cybernetic/tier3
/datum/design/cybernetic_heart
name = "Basic Cybernetic Heart"
desc = "A basic cybernetic heart."
id = "cybernetic_heart"
build_type = PROTOLATHE | MECHFAB
construction_time = 40
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/heart/cybernetic
category = list("Misc", "Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/cybernetic_heart/tier2
name = "Cybernetic Heart"
desc = "A cybernetic heart."
id = "cybernetic_heart_tier2"
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/heart/cybernetic/tier2
/datum/design/cybernetic_heart/tier3
name = "Upgraded Cybernetic Heart"
desc = "An upgraded cybernetic heart."
id = "cybernetic_heart_tier3"
construction_time = 50
materials = list(/datum/material/iron = 500, /datum/material/glass = 500, /datum/material/silver = 600, /datum/material/gold = 600, /datum/material/plasma = 1000, /datum/material/diamond = 2000)
build_path = /obj/item/organ/heart/cybernetic/tier3
/datum/design/cybernetic_lungs
name = "Cybernetic Lungs"
desc = "A pair of cybernetic lungs."
name = "Basic Cybernetic Lungs"
desc = "A basic pair of cybernetic lungs."
id = "cybernetic_lungs"
build_type = PROTOLATHE | MECHFAB
construction_time = 40
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/lungs/cybernetic
category = list("Misc","Medical Designs")
category = list("Misc", "Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/cybernetic_lungs_u
/datum/design/cybernetic_lungs/tier2
name = "Cybernetic Lungs"
desc = "A pair of cybernetic lungs."
id = "cybernetic_lungs_tier2"
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/lungs/cybernetic/tier2
/datum/design/cybernetic_lungs/tier3
name = "Upgraded Cybernetic Lungs"
desc = "A pair of upgraded cybernetic lungs."
id = "cybernetic_lungs_u"
id = "cybernetic_lungs_tier3"
construction_time = 50
materials = list(/datum/material/iron = 500, /datum/material/glass = 500, /datum/material/silver = 600, /datum/material/gold = 600, /datum/material/plasma = 1000, /datum/material/diamond = 2000)
build_path = /obj/item/organ/lungs/cybernetic/tier3
/datum/design/cybernetic_stomach
name = "Basic Cybernetic Stomach"
desc = "A basic cybernetic stomach."
id = "cybernetic_stomach"
build_type = PROTOLATHE | MECHFAB
materials = list(/datum/material/iron = 500, /datum/material/glass = 500, /datum/material/silver = 500)
build_path = /obj/item/organ/lungs/cybernetic/upgraded
category = list("Misc","Medical Designs")
construction_time = 40
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/stomach/cybernetic
category = list("Misc", "Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/cybernetic_stomach/tier2
name = "Cybernetic Stomach"
desc = "A cybernetic stomach."
id = "cybernetic_stomach_tier2"
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/stomach/cybernetic/tier2
/datum/design/cybernetic_stomach/tier3
name = "Upgraded Cybernetic Stomach"
desc = "An upgraded cybernetic stomach."
id = "cybernetic_stomach_tier3"
construction_time = 50
materials = list(/datum/material/iron = 500, /datum/material/glass = 500, /datum/material/silver = 600, /datum/material/gold = 600, /datum/material/plasma = 1000, /datum/material/diamond = 2000)
build_path = /obj/item/organ/stomach/cybernetic/tier3
/datum/design/cybernetic_tongue
name = "Cybernetic tongue"
desc = "A fancy cybernetic tongue."
@@ -711,7 +754,7 @@
build_type = PROTOLATHE | MECHFAB
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/organ/tongue/cybernetic
category = list("Misc","Medical Designs")
category = list("Misc", "Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/cybernetic_ears
@@ -67,20 +67,27 @@
design_ids = list("implanter", "implantcase", "implant_chem", "implant_tracking", "locator", "c38_trac")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/basic_cyber_organs
id = "basic_cyber_organs"
starting_node = TRUE
display_name = "Basic Cybernetic Organs"
description = "We have the techinology to force him to live a disgusting halflife."
design_ids = list("cybernetic_liver", "cybernetic_heart", "cybernetic_lungs", "cybernetic_stomach")
/datum/techweb_node/cyber_organs
id = "cyber_organs"
display_name = "Cybernetic Organs"
description = "We have the technology to rebuild him."
prereq_ids = list("adv_biotech")
design_ids = list("cybernetic_ears", "cybernetic_heart", "cybernetic_liver", "cybernetic_lungs", "cybernetic_tongue")
prereq_ids = list("biotech")
design_ids = list("cybernetic_ears", "cybernetic_heart_tier2", "cybernetic_liver_tier2", "cybernetic_lungs_tier2", "cybernetic_stomach_tier2")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
/datum/techweb_node/cyber_organs_upgraded
id = "cyber_organs_upgraded"
display_name = "Upgraded Cybernetic Organs"
description = "We have the technology to upgrade him."
prereq_ids = list("cyber_organs")
design_ids = list("cybernetic_ears_u", "cybernetic_heart_u", "cybernetic_liver_u", "cybernetic_lungs_u", "ipc_stomach")
prereq_ids = list("adv_biotech", "cyber_organs")
design_ids = list("cybernetic_ears_u", "cybernetic_heart_tier3", "cybernetic_liver_tier3", "cybernetic_lungs_tier3", "cybernetic_stomach_tier3")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
/datum/techweb_node/cyber_implants
@@ -21,7 +21,7 @@
id = "advanced_illegal_ballistics"
display_name = "Advanced Non-Standard Ballistics"
description = "Ballistic ammunition for non-standard firearms. Usually the ones you don't have nor want to be involved with."
design_ids = list("10mm","10mmap","10mminc","10mmhp","sl357","sl357ap","pistolm9mm","m45","bolt_clip")
design_ids = list("10mm","10mmap","10mminc","10mmhp","sl357","sl357ap","pistolm9mm","m45","bolt_clip","m10apbox","m10firebox","m10hpbox")
prereq_ids = list("ballistic_weapons","syndicate_basic","explosive_weapons")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 25000) //This gives sec lethal mags/clips for guns from traitors, space, or anything in between.
+48 -26
View File
@@ -197,45 +197,67 @@
colour = "red"
/obj/item/organ/heart/cybernetic
name = "cybernetic heart"
desc = "An electronic device designed to mimic the functions of an organic human heart. Offers no benefit over an organic heart other than being easy to make."
name = "basic cybernetic heart"
desc = "A basic electronic device designed to mimic the functions of an organic human heart."
icon_state = "heart-c"
organ_flags = ORGAN_SYNTHETIC
maxHealth = STANDARD_ORGAN_THRESHOLD*0.75 //This also hits defib timer, so a bit higher than its less important counterparts
var/dose_available = FALSE
var/rid = /datum/reagent/medicine/epinephrine
var/ramount = 10
var/emp_vulnerability = 1 //The value the severity of emps are divided by to determine the likelihood of permanent damage.
/obj/item/organ/heart/cybernetic/tier2
name = "cybernetic heart"
desc = "An electronic device designed to mimic the functions of an organic human heart. Also holds an emergency dose of epinephrine, used automatically after facing severe trauma."
icon_state = "heart-c-u"
maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD
dose_available = TRUE
emp_vulnerability = 2
/obj/item/organ/heart/cybernetic/tier3
name = "upgraded cybernetic heart"
desc = "An electronic device designed to mimic the functions of an organic human heart. Also holds an emergency dose of epinephrine, used automatically after facing severe trauma. This upgraded model can regenerate its dose after use."
icon_state = "heart-c-u2"
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
dose_available = TRUE
rid = /datum/reagent/medicine/atropine
ramount = 5
emp_vulnerability = 3
/obj/item/organ/heart/cybernetic/emp_act(severity)
. = ..()
// If the owner doesn't need a heart, we don't need to do anything with it.
if(!owner.needs_heart())
return
if(. & EMP_PROTECT_SELF)
return
Stop()
addtimer(CALLBACK(src, .proc/Restart), 0.2*severity SECONDS)
damage += severity
if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people.
owner.Dizzy(10)
owner.losebreath += 10
COOLDOWN_START(src, severe_cooldown, 20 SECONDS)
if(prob(severity/emp_vulnerability)) //Chance of permanent effects
organ_flags |= ORGAN_SYNTHETIC_EMP //Starts organ faliure - gonna need replacing soon.
Stop()
owner.visible_message("<span class='danger'>[owner] clutches at [owner.p_their()] chest as if [owner.p_their()] heart is stopping!</span>", \
"<span class='userdanger'>You feel a terrible pain in your chest, as if your heart has stopped!</span>")
addtimer(CALLBACK(src, .proc/Restart), 10 SECONDS)
/obj/item/organ/heart/cybernetic/upgraded
name = "upgraded cybernetic heart"
desc = "An electronic device designed to mimic the functions of an organic human heart. Also holds an emergency dose of epinephrine, used automatically after facing severe trauma. This upgraded model can regenerate its dose after use."
icon_state = "heart-c-u"
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
//I put it on upgraded for now.
var/dose_available = TRUE
var/rid = /datum/reagent/medicine/epinephrine
var/ramount = 10
/obj/item/organ/heart/cybernetic/upgraded/on_life()
/obj/item/organ/heart/cybernetic/on_life(delta_time, times_fired)
. = ..()
if(!.)
return
if(dose_available && owner.health <= owner.crit_threshold && !owner.reagents.has_reagent(rid))
owner.reagents.add_reagent(rid, ramount)
used_dose()
if(ramount < 10) //eats your nutrition to regen epinephrine
var/regen_amount = owner.nutrition/2000
owner.adjust_nutrition(-regen_amount)
ramount += regen_amount
/obj/item/organ/heart/cybernetic/upgraded/proc/used_dose()
/obj/item/organ/heart/cybernetic/proc/used_dose()
owner.reagents.add_reagent(rid, ramount)
dose_available = FALSE
/obj/item/organ/heart/cybernetic/tier3/used_dose()
. = ..()
addtimer(VARSET_CALLBACK(src, dose_available, TRUE), 5 MINUTES)
ramount = 0
/obj/item/organ/heart/ipc
name = "IPC heart"
+27 -9
View File
@@ -98,23 +98,41 @@
icon_state = "liver-c"
/obj/item/organ/liver/cybernetic
name = "cybernetic liver"
name = "basic cybernetic liver"
icon_state = "liver-c"
desc = "An electronic device designed to mimic the functions of a human liver. It has no benefits over an organic liver, but is easy to produce."
desc = "A very basic device designed to mimic the functions of a human liver. Handles toxins slightly worse than an organic liver."
organ_flags = ORGAN_SYNTHETIC
maxHealth = 1.1 * STANDARD_ORGAN_THRESHOLD
toxTolerance = 0.3 * LIVER_DEFAULT_TOX_TOLERANCE //little less than 1u of toxin purging
toxLethality = 1.1 * LIVER_DEFAULT_TOX_LETHALITY
maxHealth = STANDARD_ORGAN_THRESHOLD*0.5
/obj/item/organ/liver/cybernetic/upgraded
name = "upgraded cybernetic liver"
var/emp_vulnerability = 1 //The value the severity of emps are divided by to determine the likelihood of permanent damage.
/obj/item/organ/liver/cybernetic/tier2
name = "cybernetic liver"
icon_state = "liver-c-u"
desc = "An upgraded version of the cybernetic liver, designed to improve upon organic livers. It is resistant to alcohol poisoning and is very robust at filtering toxins."
desc = "An electronic device designed to mimic the functions of a human liver. Handles toxins slightly better than an organic liver."
maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD
toxTolerance = 2 * LIVER_DEFAULT_TOX_TOLERANCE //6 units of toxin purging
toxLethality = 0.8 * LIVER_DEFAULT_TOX_LETHALITY //20% less damage than a normal liver
emp_vulnerability = 2
/obj/item/organ/liver/cybernetic/tier3
name = "upgraded cybernetic liver"
icon_state = "liver-c-u2"
desc = "An upgraded version of the cybernetic liver, designed to improve further upon organic livers. It is resistant to alcohol poisoning and is very robust at filtering toxins."
alcohol_tolerance = 0.001
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
toxTolerance = 15 //can shrug off up to 15u of toxins
toxLethality = 0.008 //20% less damage than a normal liver
toxTolerance = 5 * LIVER_DEFAULT_TOX_TOLERANCE //15 units of toxin purging
toxLethality = 0.4 * LIVER_DEFAULT_TOX_LETHALITY //60% less damage than a normal liver
emp_vulnerability = 3
/obj/item/organ/liver/cybernetic/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
damage += severity
if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people.
owner.adjustToxLoss(10)
COOLDOWN_START(src, severe_cooldown, 10 SECONDS)
if(prob(severity/emp_vulnerability)) //Chance of permanent effects
organ_flags |= ORGAN_SYNTHETIC_EMP //Starts organ faliure - gonna need replacing soon.
+35 -16
View File
@@ -547,33 +547,52 @@
maxHealth = INFINITY//I don't understand how plamamen work, so I'm not going to try t give them special lungs atm
/obj/item/organ/lungs/cybernetic
name = "cybernetic lungs"
desc = "A cybernetic version of the lungs found in traditional humanoid entities. It functions the same as an organic lung and is merely meant as a replacement."
name = "basic cybernetic lungs"
desc = "A basic cybernetic version of the lungs found in traditional humanoid entities."
icon_state = "lungs-c"
organ_flags = ORGAN_SYNTHETIC
maxHealth = 400
safe_oxygen_min = 13
maxHealth = STANDARD_ORGAN_THRESHOLD * 0.5
/obj/item/organ/lungs/cybernetic/emp_act()
. = ..()
if(. & EMP_PROTECT_SELF)
return
owner.losebreath = 20
owner.adjustOrganLoss(ORGAN_SLOT_LUNGS, 25)
var/emp_vulnerability = 1 //The value the severity of emps are divided by to determine the likelihood of permanent damage.
/obj/item/organ/lungs/cybernetic/upgraded
name = "upgraded cybernetic lungs"
desc = "A more advanced version of the stock cybernetic lungs. They are capable of filtering out lower levels of toxins and carbon dioxide."
/obj/item/organ/lungs/cybernetic/tier2
name = "cybernetic lungs"
desc = "A cybernetic version of the lungs found in traditional humanoid entities. Allows for greater intakes of oxygen than organic lungs, requiring slightly less pressure."
icon_state = "lungs-c-u"
safe_toxins_max = 20
safe_co2_max = 20
safe_oxygen_max = 250
maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD
safe_oxygen_min = 13
safe_oxygen_max = 100
emp_vulnerability = 2
/obj/item/organ/lungs/cybernetic/tier3
name = "upgraded cybernetic lungs"
desc = "A more advanced version of the stock cybernetic lungs. Features the ability to filter out various airbourne toxins and carbon dioxide even at heavy levels."
icon_state = "lungs-c-u2"
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
safe_oxygen_min = 4 //You could literally be breathing the thinnest amount of oxygen and be fine
safe_oxygen_max = 250 //Or be in an enriched oxygen room for that matter
safe_toxins_max = 30
safe_co2_max = 30
SA_para_min = 30
SA_sleep_min = 50
BZ_trip_balls_min = 30
emp_vulnerability = 3
cold_level_1_threshold = 200
cold_level_2_threshold = 140
cold_level_3_threshold = 100
maxHealth = 550
/obj/item/organ/lungs/cybernetic/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people.
owner.losebreath += 20
COOLDOWN_START(src, severe_cooldown, 30 SECONDS)
if(prob(severity/emp_vulnerability)) //Chance of permanent effects
organ_flags |= ORGAN_SYNTHETIC_EMP //Starts organ faliure - gonna need replacing soon.
/obj/item/organ/lungs/ashwalker
name = "ash lungs"
desc = "blackened lungs identical from specimens recovered from lavaland, unsuited to higher air pressures."
@@ -16,6 +16,7 @@
var/decay_factor = 0 //same as above but when without a living owner, set to 0 for generic organs
var/high_threshold = STANDARD_ORGAN_THRESHOLD * 0.45 //when severe organ damage occurs
var/low_threshold = STANDARD_ORGAN_THRESHOLD * 0.1 //when minor organ damage occurs
var/severe_cooldown //cooldown for severe effects, used for synthetic organ emp effects.
///Organ variables for determining what we alert the owner with when they pass/clear the damage thresholds
var/prev_damage = 0
@@ -153,6 +154,9 @@
/obj/item/organ/proc/on_life() //repair organ damage if the organ is not failing or synthetic
if(organ_flags & ORGAN_FAILING || !owner)
return FALSE
if(organ_flags & ORGAN_SYNTHETIC_EMP) //Synthetic organ has been emped, is now failing.
applyOrganDamage(maxHealth * decay_factor)
return
if(!is_cold() && damage)
///Damage decrements by a percent of its maxhealth
var/healing_amount = -(maxHealth * healing_factor)
+34
View File
@@ -93,6 +93,40 @@
icon_state = "stomach-p"
desc = "A strange crystal that is responsible for metabolizing the unseen energy force that feeds plasmamen."
/obj/item/organ/stomach/cybernetic
name = "basic cybernetic stomach"
icon_state = "stomach-c"
desc = "A basic device designed to mimic the functions of a human stomach"
organ_flags = ORGAN_SYNTHETIC
maxHealth = STANDARD_ORGAN_THRESHOLD * 0.5
var/emp_vulnerability = 1 //The value the severity of emps are divided by to determine the likelihood of permanent damage.
/obj/item/organ/stomach/cybernetic/tier2
name = "cybernetic stomach"
icon_state = "stomach-c-u"
desc = "An electronic device designed to mimic the functions of a human stomach. Handles disgusting food a bit better."
maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD
disgust_metabolism = 2
emp_vulnerability = 2
/obj/item/organ/stomach/cybernetic/tier3
name = "upgraded cybernetic stomach"
icon_state = "stomach-c-u2"
desc = "An upgraded version of the cybernetic stomach, designed to improve further upon organic stomachs. Handles disgusting food very well."
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
disgust_metabolism = 3
emp_vulnerability = 3
/obj/item/organ/stomach/cybernetic/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people.
owner.vomit(stun = FALSE)
COOLDOWN_START(src, severe_cooldown, 10 SECONDS)
if(prob(severity/emp_vulnerability)) //Chance of permanent effects
organ_flags |= ORGAN_SYNTHETIC_EMP //Starts organ faliure - gonna need replacing soon.
/obj/item/organ/stomach/ipc
name = "ipc cell"
icon_state = "stomach-ipc"
@@ -208,9 +208,10 @@
/datum/uplink_item/role_restricted/chemical_gun
name = "Reagent Dartgun"
desc = "A heavily modified syringe gun which is capable of synthesizing its own chemical darts using input reagents. Can hold 100u of reagents."
desc = "A heavily modified syringe gun which is capable of synthesizing its own chemical darts using input reagents. \
Synthesizes one piercing 10 unit dart every 30 seconds up to a maximum of five. Can hold 100u of reagents."
item = /obj/item/gun/chem
cost = 12
cost = 10
restricted_roles = list("Chemist", "Chief Medical Officer")
/datum/uplink_item/role_restricted/reverse_bear_trap
@@ -257,4 +258,4 @@
item = /obj/item/storage/toolbox/emergency/turret
cost = 11
restricted_roles = list("Station Engineer")
+71 -159
View File
@@ -50,6 +50,77 @@
-->
<div class="commit sansserif">
<h2 class="date">01 May 2021</h2>
<h3 class="author">qweq12yt updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Restores the sprite for the Riot Suit.</li>
</ul>
<h2 class="date">30 April 2021</h2>
<h3 class="author">DrPainis updated:</h3>
<ul class="changes bgimages16">
<li class="spellcheck">Bubblegum's hallucinations are capitalized.</li>
</ul>
<h3 class="author">Melbert, SandPoot updated:</h3>
<ul class="changes bgimages16">
<li class="refactor">TGUI Limbgrower</li>
<li class="refactor">Refactored the limbgrower to modernize the code and allow for more types of designs.</li>
<li class="rscadd">The limbgrower now supports plumbing ducts.</li>
<li class="bugfix">Fixes genitals not actually getting data from disks.</li>
<li class="code_imp">Adds two special helpers.</li>
</ul>
<h3 class="author">SandPoot updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">The decal painter now has visible previews for your tile painting funs.</li>
<li class="bugfix">Fixes decal painter painting in the opposite direction.</li>
</ul>
<h3 class="author">TheObserver-sys updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Restores the access lock on crates that should have them, given the goods inside.</li>
<li class="bugfix">Makes the 10MM Surplus Rifle a less awful thing to use.</li>
<li class="bugfix">replaces unarmored things with their armored versions.</li>
<li class="balance">Illegal Tech Ammo actually is fucking reasonable, now.</li>
<li class="balance">Expensive Illegal Tech Ammo Boxes are now constructible, with actually justifiable prices.</li>
</ul>
<h3 class="author">WanderingFox95 updated:</h3>
<ul class="changes bgimages16">
<li class="balance">There's finally a reason for the reagent dart gun to exist and be used!</li>
</ul>
<h3 class="author">akada updated:</h3>
<ul class="changes bgimages16">
<li class="imageadd">Changes the space adaptation sprite to something less intrusive and more subtle.</li>
</ul>
<h3 class="author">necromanceranne updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Basic cybernetic organs: they're worse than organic! Basic stomachs, hearts, lungs and livers! For when you hate someone enough to not bother harvesting organs from a monkeyhuman!</li>
<li class="rscadd">Cybernetic organs have been adjusted into three tiers: 1 (basic), 2 (standard, better than organic) and 3 (absolutely better than organic but expensive to print)</li>
<li class="rscadd">Cybernetic organs that are emp'd instead suffer different effects based on the severity of the emp. The bigger the emp, the worse the effect is.</li>
<li class="rscadd">Rather than outright bricking, severely emp'd cyberorgans degrade over time very quickly, requiring replacement in the near future.</li>
<li class="rscadd">Fake blindfolds in the loadout. They don't obscure vision, for better or worse.</li>
</ul>
<h2 class="date">29 April 2021</h2>
<h3 class="author">Putnam3145 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Fixed a couple runtimes in activity (threat) tracking</li>
</ul>
<h3 class="author">keronshb updated:</h3>
<ul class="changes bgimages16">
<li class="balance">Removes the Reinforcement Chromosome from Genetics.</li>
</ul>
<h2 class="date">26 April 2021</h2>
<h3 class="author">Trigg, stylemistake and SandPoot updated:</h3>
<ul class="changes bgimages16">
<li class="admin">Admins just got a new TGUI Select Equipment menu tweak: Prevents the window from creating sprites for any animated version there might be. (this guarantees consistant sprite size/amount)</li>
</ul>
<h2 class="date">25 April 2021</h2>
<h3 class="author">DrPainis updated:</h3>
<ul class="changes bgimages16">
<li class="spellcheck">Bubblegum is now capitalized.</li>
</ul>
<h2 class="date">22 April 2021</h2>
<h3 class="author">Whoneedspacee updated:</h3>
<ul class="changes bgimages16">
@@ -722,165 +793,6 @@
<ul class="changes bgimages16">
<li class="bugfix">cursed rice hat right in front of the jungle gateway's entrance is now removed from this dimensional plane</li>
</ul>
<h2 class="date">28 February 2021</h2>
<h3 class="author">Putnam3145 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Polychromic windbreaker's alt-click message is now coherent</li>
<li class="code_imp">Toggleable suits now have an on_toggle proc to be overridden.</li>
</ul>
<h3 class="author">R3dtail updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">doubled max belly name length and quadrupled belly description length</li>
</ul>
<h3 class="author">SandPoot updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">Body rejuvenation surgery will loop until the patient is completely healed.</li>
</ul>
<h3 class="author">dzahlus updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">fixes toxinlovers dying from heretic stuff that should heal them instead</li>
</ul>
<h2 class="date">27 February 2021</h2>
<h3 class="author">Hatterhat updated:</h3>
<ul class="changes bgimages16">
<li class="balance">Lingfists (trait_mauler) now deal no stam damage and lost their 15(!!!) armor penetration.</li>
</ul>
<h3 class="author">Putnam3145 updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">Tablets now protect their contents from rads.</li>
</ul>
<h3 class="author">TheObserver-sys updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Chems that should have been usable are now usable, try some cryoxadone on a plant today!!!</li>
</ul>
<h3 class="author">kappa-sama updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">cards and card binders are now small-class items</li>
</ul>
<h3 class="author">keronshb updated:</h3>
<ul class="changes bgimages16">
<li class="balance">16 > 10 unlock cost for stronger abilities</li>
<li class="balance">Made nearly all other abilities for free.</li>
</ul>
<h3 class="author">kiwedespars updated:</h3>
<ul class="changes bgimages16">
<li class="balance">reverted the pr that absolutely gutted pugilism and made it worse than base unarmed, also gives it a second long stagger</li>
<li class="balance">removed the ability to parry while horizontal, because that's dumb and makes it easy to just time the parries right.</li>
</ul>
<h3 class="author">silicons updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">chaplain arrythmic knives can no longer be abused for infinite speed.</li>
</ul>
<h2 class="date">26 February 2021</h2>
<h3 class="author">DeltaFire15 updated:</h3>
<ul class="changes bgimages16">
<li class="code_imp">All machine-frame based tool-use actions now have state-checking callbacks.</li>
</ul>
<h2 class="date">25 February 2021</h2>
<h3 class="author">DeltaFire15 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Traitor / Ling objective amount should now be correct again.</li>
</ul>
<h2 class="date">24 February 2021</h2>
<h3 class="author">SandPoot updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Regular crowbars no longer open powered airlocks.</li>
</ul>
<h3 class="author">silicons updated:</h3>
<ul class="changes bgimages16">
<li class="balance">xeno cube makes hostile xenos now, and drops a sentinel instead of a drone.</li>
</ul>
<h2 class="date">23 February 2021</h2>
<h3 class="author">keronshb updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Hyperblade to uplink with poplock</li>
<li class="balance">Removes combination of two Dragon Tooth Swords while keeping it for regular eutactics.</li>
</ul>
<h3 class="author">timothyteakettle updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">banning panel prioritises mobs with clients now when trying to find them if they're in the game</li>
</ul>
<h2 class="date">22 February 2021</h2>
<h3 class="author">Putnam3145 updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">(Hexa)crocin</li>
<li class="rscadd">(Hexa)camphor</li>
<li class="rscadd">Nymphomaniac quirk</li>
<li class="admin">All climaxes and arousals are now logged, as well as genital exposure.</li>
</ul>
<h3 class="author">SandPoot updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Cyborg tablets and it's special app for self-management.</li>
<li class="bugfix">In the case of a doomsday device being created outside of an AI it will delete itself.</li>
<li class="imageadd">Some sprites for it have been added and the borg's hud light toggles been changed to only on-off (made by yours truly)</li>
<li class="refactor">A lot of borg code was changed</li>
<li class="refactor">Tools no longer use istype checks and actually check for their behavior.</li>
</ul>
<h3 class="author">Vynzill updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">cursed rice hat that's hard to find and obtain, along with a couple other hats</li>
<li class="rscadd">a replacement toy gun for donksoft lmg</li>
<li class="rscadd">gorillas to the jungle gateway, friendly, even when attacked.</li>
<li class="bugfix">couple mapping errors I noticed, most importantly a missing window in the chapel.</li>
<li class="balance">shotgun and donksoft lmg removed, captain coat nerfed armor values.</li>
<li class="balance">leaper healthpool from 450 to 550 hopefully making it more of a struggle, and gives it a name.</li>
<li class="tweak">leaper pit is more wider. The hidden room south is now more obvious to find</li>
</ul>
<h3 class="author">dzahlus updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Added pain emote to getting wounded</li>
<li class="soundadd">added a new pain emote sounds</li>
</ul>
<h2 class="date">21 February 2021</h2>
<h3 class="author">Hatterhat updated:</h3>
<ul class="changes bgimages16">
<li class="balance">Anomaly announcements and brand intelligence now always announce instead of having some ham-fisted chance of being a command report.</li>
</ul>
<h3 class="author">IronEleven updated:</h3>
<ul class="changes bgimages16">
<li class="balance">Raises Space Vine Population Requirement from 10 to 20</li>
</ul>
<h3 class="author">MrJWhit updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">Removes an unnecessary % on the seed extractor.</li>
</ul>
<h3 class="author">timothyteakettle updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">the query for checking mentors now gets properly deleted</li>
<li class="rscadd">vampires no longer burn in the chapel if they signed up as the chaplain</li>
</ul>
<h2 class="date">20 February 2021</h2>
<h3 class="author">Adelphon updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">polychromic pants</li>
<li class="tweak">urban coat made polychromic</li>
</ul>
<h3 class="author">Chiirno updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">Synthflesh now unhusks with 100u instead of requiring 101u.</li>
</ul>
<h3 class="author">SmArtKar updated:</h3>
<ul class="changes bgimages16">
<li class="tweak">Added some QoL changes to TCG</li>
<li class="bugfix">Fixed TCG cards not saving</li>
</ul>
<h3 class="author">TyrianTyrell updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">fixed the signed language so that you can actually use it, and that it's unusable when it's meant to be.</li>
</ul>
<h3 class="author">timothyteakettle updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">stops people using Message All on PDAs when their cartridge doesn't allow it</li>
</ul>
</div>
<b>GoonStation 13 Development Team</b>
+55
View File
@@ -29164,3 +29164,58 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- balance: triple fire breath for the lava swoop only happens below half health
now
- bugfix: The arena attack not making safespots when you fight it in a mech
2021-04-25:
DrPainis:
- spellcheck: Bubblegum is now capitalized.
2021-04-26:
Trigg, stylemistake and SandPoot:
- admin: 'Admins just got a new TGUI Select Equipment menu tweak: Prevents the window
from creating sprites for any animated version there might be. (this guarantees
consistant sprite size/amount)'
2021-04-29:
Putnam3145:
- bugfix: Fixed a couple runtimes in activity (threat) tracking
keronshb:
- balance: Removes the Reinforcement Chromosome from Genetics.
2021-04-30:
DrPainis:
- spellcheck: Bubblegum's hallucinations are capitalized.
Melbert, SandPoot:
- refactor: TGUI Limbgrower
- refactor: Refactored the limbgrower to modernize the code and allow for more types
of designs.
- rscadd: The limbgrower now supports plumbing ducts.
- bugfix: Fixes genitals not actually getting data from disks.
- code_imp: Adds two special helpers.
SandPoot:
- rscadd: The decal painter now has visible previews for your tile painting funs.
- bugfix: Fixes decal painter painting in the opposite direction.
TheObserver-sys:
- bugfix: Restores the access lock on crates that should have them, given the goods
inside.
- bugfix: Makes the 10MM Surplus Rifle a less awful thing to use.
- bugfix: replaces unarmored things with their armored versions.
- balance: Illegal Tech Ammo actually is fucking reasonable, now.
- balance: Expensive Illegal Tech Ammo Boxes are now constructible, with actually
justifiable prices.
WanderingFox95:
- balance: There's finally a reason for the reagent dart gun to exist and be used!
akada:
- imageadd: Changes the space adaptation sprite to something less intrusive and
more subtle.
necromanceranne:
- rscadd: 'Basic cybernetic organs: they''re worse than organic! Basic stomachs,
hearts, lungs and livers! For when you hate someone enough to not bother harvesting
organs from a monkeyhuman!'
- rscadd: 'Cybernetic organs have been adjusted into three tiers: 1 (basic), 2 (standard,
better than organic) and 3 (absolutely better than organic but expensive to
print)'
- rscadd: Cybernetic organs that are emp'd instead suffer different effects based
on the severity of the emp. The bigger the emp, the worse the effect is.
- rscadd: Rather than outright bricking, severely emp'd cyberorgans degrade over
time very quickly, requiring replacement in the near future.
- rscadd: Fake blindfolds in the loadout. They don't obscure vision, for better
or worse.
2021-05-01:
qweq12yt:
- bugfix: Restores the sprite for the Riot Suit.
@@ -0,0 +1,10 @@
author: "TripleShades"
delete-after: True
changes:
- rscadd: "Added two air alarms to Pubby Security, one in the evidence locker room and one in the main equipment back room"
- rscadd: "pAI Card back to outside Research in Meta Station"
- bugfix: "Pubby Disposals now shunts to space"
- bugfix: "Maintinence Areas being not applied to certain airlocks as well as stealing minor walls"
- bugfix: "Box Surgery Storage camera is now renamed to be on the network"
- bugfix: "Box Paramedic Station camera is now renamed to be on the network, and no longer steals the Morgue's cam
tweak: Box Surgery Storage is now it's own proper room"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

@@ -6,6 +6,10 @@
name = "Blindfold"
path = /obj/item/clothing/glasses/sunglasses/blindfold
/datum/gear/glasses/fakeblindfold
name = "Fake Blindfold"
path = /obj/item/clothing/glasses/fakeblindfold
/datum/gear/glasses/cold
name = "Cold goggles"
path = /obj/item/clothing/glasses/cold
@@ -3,10 +3,9 @@
desc = "A gun magazine. Loaded with rounds which ignite the target.."
id = "10mminc"
build_type = PROTOLATHE
materials = list(/datum/material/plasma = 50000, /datum/material/iron = 18000)
reagents_list = list(/datum/reagent/toxin/plasma = 120, /datum/reagent/napalm = 240)
materials = list(/datum/material/plasma = 5000, /datum/material/iron = 7500)
build_path = /obj/item/ammo_box/magazine/m10mm/fire
category = list("Weapons")
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/m10mm
@@ -14,7 +13,7 @@
desc = "A gun magazine."
id = "10mm"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 55000)
materials = list(/datum/material/iron = 6000)
build_path = /obj/item/ammo_box/magazine/m10mm
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
@@ -24,8 +23,7 @@
desc = "A gun magazine. Loaded with hollow-point rounds, extremely effective against unarmored targets, but nearly useless against protective clothing."
id = "10mmhp"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 40000, /datum/material/glass = 50000)
reagents_list = list(/datum/reagent/sonic_powder = 280)
materials = list(/datum/material/iron = 7500, /datum/material/glass = 5000)
build_path = /obj/item/ammo_box/magazine/m10mm/hp
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
@@ -35,7 +33,7 @@
desc = "A gun magazine. Loaded with rounds which penetrate armour, but are less effective against normal targets."
id = "10mmap"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 40000, /datum/material/titanium = 60000)
materials = list(/datum/material/iron = 7500, /datum/material/titanium = 6500)
build_path = /obj/item/ammo_box/magazine/m10mm/ap
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
@@ -54,7 +52,7 @@
name = "handgun magazine (.45)"
id = "m45"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 80000)
materials = list(/datum/material/iron = 8000)
build_path = /obj/item/ammo_box/magazine/m45
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
@@ -64,7 +62,7 @@
desc = "A gun magazine."
id = "pistolm9mm"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 80000)
materials = list(/datum/material/iron = 7500)
build_path = /obj/item/ammo_box/magazine/pistolm9mm
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
@@ -84,7 +82,37 @@
desc = "A revolver speedloader. Cuts through like a hot knife through butter."
id = "sl357ap"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 30000, /datum/material/titanium = 45000)
materials = list(/datum/material/iron = 30000, /datum/material/titanium = 5000)
build_path = /obj/item/ammo_box/a357/ap
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/m10apbox
name = "ammo box (10mm Armour Piercing)"
desc = "A box of ammo containing 20 rounds designed to penetrate armor, at the cost of raw damage."
id = "m10apbox"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 30000, /datum/material/titanium = 6000)
build_path = /obj/item/ammo_box/c10mm/ap
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/m10firebox
name = "ammo box (10mm Incendiary)"
desc = "A box of ammo containing 20 rounds designed to set people ablaze, at the cost of raw damage."
id = "m10firebox"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 30000, /datum/material/plasma = 6000)
build_path = /obj/item/ammo_box/c10mm/fire
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/m10hpbox
name = "ammo box (10mm Hollowpoint)"
desc = "A box of ammo containing 20 rounds designed to tear through unarmored opponents, while being completely ineffective against armor."
id = "m10hpbox"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 30000, /datum/material/glass = 6000)
build_path = /obj/item/ammo_box/c10mm/hp
category = list("Ammo")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+4
View File
@@ -1416,6 +1416,9 @@
#include "code\modules\admin\ipintel.dm"
#include "code\modules\admin\IsBanned.dm"
#include "code\modules\admin\NewBan.dm"
#include "code\modules\admin\outfit_editor.dm"
#include "code\modules\admin\outfit_manager.dm"
#include "code\modules\admin\outfits.dm"
#include "code\modules\admin\permissionedit.dm"
#include "code\modules\admin\player_panel.dm"
#include "code\modules\admin\sound_emitter.dm"
@@ -1456,6 +1459,7 @@
#include "code\modules\admin\verbs\randomverbs.dm"
#include "code\modules\admin\verbs\reestablish_db_connection.dm"
#include "code\modules\admin\verbs\secrets.dm"
#include "code\modules\admin\verbs\selectequipment.dm"
#include "code\modules\admin\verbs\shuttlepanel.dm"
#include "code\modules\admin\verbs\spawnobjasmob.dm"
#include "code\modules\admin\verbs\tripAI.dm"
+4
View File
@@ -171,6 +171,8 @@ export const sortBy = (...iterateeFns) => array => {
return mappedArray;
};
export const sort = sortBy();
/**
* A fast implementation of reduce.
*/
@@ -235,6 +237,8 @@ export const uniqBy = iterateeFn => array => {
return result;
};
export const uniq = uniqBy();
/**
* Creates an array of grouped elements, the first of which contains
* the first elements of the given arrays, the second of which contains
+57 -7
View File
@@ -1,6 +1,7 @@
import { useBackend } from '../backend';
import { Button, Section } from '../components';
import { Box, Button, Section } from '../components';
import { Window } from '../layouts';
import { classes } from 'common/react';
export const DecalPainter = (props, context) => {
const { act, data } = useBackend(context);
@@ -16,11 +17,25 @@ export const DecalPainter = (props, context) => {
{decal_list.map(decal => (
<Button
key={decal.decal}
content={decal.name}
style={{
width: '48px',
height: '48px',
padding: 0,
}}
selected={decal.decal === data.decal_style}
tooltip={decal.name}
onClick={() => act('select decal', {
decals: decal.decal,
})} />
})}>
<Box
className={classes([
'decals32x32',
data.decal_dir_text + '-' + decal.decal + (data.decal_color ? '_' + data.decal_color : ''),
])}
style={{
transform: 'scale(1.5) translate(17%, 17%)',
}} />
</Button>
))}
</Section>
<Section title="Decal Color">
@@ -28,7 +43,12 @@ export const DecalPainter = (props, context) => {
return (
<Button
key={color.colors}
content={color.colors === "red"
style={{
width: '48px',
height: '48px',
padding: 0,
}}
tooltip={color.colors === "red"
? "Red"
: color.colors === "white"
? "White"
@@ -36,7 +56,16 @@ export const DecalPainter = (props, context) => {
selected={color.colors === data.decal_color}
onClick={() => act('select color', {
colors: color.colors,
})} />
})}>
<Box
className={classes([
'decals32x32',
data.decal_dir_text + '-' + data.decal_style + (color.colors ? '_' + color.colors : ''),
])}
style={{
transform: 'scale(1.5) translate(17%, 17%)',
}} />
</Button>
);
})}
</Section>
@@ -45,7 +74,12 @@ export const DecalPainter = (props, context) => {
return (
<Button
key={dir.dirs}
content={dir.dirs === 1
style={{
width: '48px',
height: '48px',
padding: 0,
}}
tooltip={dir.dirs === 1
? "North"
: dir.dirs === 2
? "South"
@@ -55,7 +89,23 @@ export const DecalPainter = (props, context) => {
selected={dir.dirs === data.decal_direction}
onClick={() => act('selected direction', {
dirs: dir.dirs,
})} />
})}>
<Box
className={classes([
'decals32x32',
(dir.dirs === 1
? "north"
: dir.dirs === 2
? "south"
: dir.dirs === 4
? "east"
: "west")
+ '-' + data.decal_style + (data.decal_color ? '_' + data.decal_color : ''),
])}
style={{
transform: 'scale(1.5) translate(17%, 17%)',
}} />
</Button>
);
})}
</Section>
+109
View File
@@ -0,0 +1,109 @@
import { useBackend, useSharedState } from '../backend';
import { Box, Button, Dimmer, Icon, LabeledList, Section, Tabs } from '../components';
import { Window } from '../layouts';
export const Limbgrower = (props, context) => {
const { act, data } = useBackend(context);
const {
reagents = [],
total_reagents,
max_reagents,
categories = [],
busy,
disk = [],
} = data;
const [tab, setTab] = useSharedState(
context, 'category', categories[0]?.name);
const designList = categories
.find(category => category.name === tab)
?.designs;
return (
<Window
title="Limb Grower"
width={500}
height={550}>
{!!busy && (
<Dimmer fontSize="32px">
<Icon name="cog" spin={1} />
{' Building...'}
</Dimmer>
)}
<Window.Content scrollable>
<Section title="Data Disk" buttons={
<Button
content="Eject Disk"
icon="eject"
onClick={() => act('eject_disk')}
disabled={!disk['disk']}
/>
}>
{disk['name'] ? (
<div>
Containing data for {disk['name']},<br />
Attempting to create genitalia will use the disk&apos;s data.
</div>
) : disk['disk'] ? "No data." : "No disk."}
</Section>
<Section title="Reagents">
<Box mb={1}>
{total_reagents} / {max_reagents} reagent capacity used.
</Box>
<LabeledList>
{reagents.map(reagent => (
<LabeledList.Item
key={reagent.reagent_name}
label={reagent.reagent_name}
buttons={(
<Button.Confirm
textAlign="center"
content="Remove Reagent"
icon="fill-drip"
color="bad"
onClick={() => act('empty_reagent', {
reagent_type: reagent.reagent_type,
})} />
)}>
{reagent.reagent_amount}u
</LabeledList.Item>
))}
</LabeledList>
</Section>
<Section title="Designs">
<Tabs>
{categories.map(category => (
<Tabs.Tab
fluid
key={category.name}
selected={tab === category.name}
onClick={() => setTab(category.name)}>
{category.name}
</Tabs.Tab>
))}
</Tabs>
<LabeledList>
{designList.map(design => (
<LabeledList.Item
key={design.name}
label={design.name}
buttons={(
<Button
content="Make"
onClick={() => act('make_limb', {
design_id: design.id,
active_tab: design.parent_category,
})} />
)}>
{design.needed_reagents.map(reagent => (
<Box key={reagent.name}>
{reagent.name}: {reagent.amount}u
</Box>
))}
</LabeledList.Item>
))}
</LabeledList>
</Section>
</Window.Content>
</Window>
);
};
+206
View File
@@ -0,0 +1,206 @@
/**
* @file
* @copyright 2020 watermelon914 (https://github.com/watermelon914)
* @license MIT
*/
import { clamp01 } from 'common/math';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Section, Input, Stack } from '../components';
import { Window } from '../layouts';
const ARROW_KEY_UP = 38;
const ARROW_KEY_DOWN = 40;
let lastScrollTime = 0;
export const ListInput = (props, context) => {
const { act, data } = useBackend(context);
const {
title,
message,
buttons,
timeout,
} = data;
// Search
const [showSearchBar, setShowSearchBar] = useLocalState(
context, 'search_bar', false);
const [displayedArray, setDisplayedArray] = useLocalState(
context, 'displayed_array', buttons);
// KeyPress
const [searchArray, setSearchArray] = useLocalState(
context, 'search_array', []);
const [searchIndex, setSearchIndex] = useLocalState(
context, 'search_index', 0);
const [lastCharCode, setLastCharCode] = useLocalState(
context, 'last_char_code', null);
// Selected Button
const [selectedButton, setSelectedButton] = useLocalState(
context, 'selected_button', buttons[0]);
const handleKeyDown = e => {
e.preventDefault();
if (lastScrollTime > performance.now()) {
return;
}
lastScrollTime = performance.now() + 125;
if (e.keyCode === ARROW_KEY_UP || e.keyCode === ARROW_KEY_DOWN) {
let direction = 1;
if (e.keyCode === ARROW_KEY_UP) direction = -1;
let index = 0;
for (index; index < buttons.length; index++) {
if (buttons[index] === selectedButton) break;
}
index += direction;
if (index < 0) index = buttons.length - 1;
else if (index >= buttons.length) index = 0;
setSelectedButton(buttons[index]);
setLastCharCode(null);
document.getElementById(buttons[index]).focus();
return;
}
const charCode = String.fromCharCode(e.keyCode).toLowerCase();
if (!charCode) return;
let foundValue;
if (charCode === lastCharCode && searchArray.length > 0) {
const nextIndex = searchIndex + 1;
if (nextIndex < searchArray.length) {
foundValue = searchArray[nextIndex];
setSearchIndex(nextIndex);
}
else {
foundValue = searchArray[0];
setSearchIndex(0);
}
}
else {
const resultArray = displayedArray.filter(value =>
value.substring(0, 1).toLowerCase() === charCode
);
if (resultArray.length > 0) {
setSearchArray(resultArray);
setSearchIndex(0);
foundValue = resultArray[0];
}
}
if (foundValue) {
setLastCharCode(charCode);
setSelectedButton(foundValue);
document.getElementById(foundValue).focus();
}
};
return (
<Window
title={title}
width={325}
height={325}>
{timeout !== undefined && <Loader value={timeout} />}
<Window.Content>
<Stack fill vertical>
<Stack.Item grow>
<Section
fill
scrollable
className="ListInput__Section"
title={message}
tabIndex={0}
onKeyDown={handleKeyDown}
buttons={(
<Button
compact
icon="search"
color="transparent"
selected={showSearchBar}
tooltip="Search Bar"
tooltipPosition="left"
onClick={() => {
setShowSearchBar(!showSearchBar);
setDisplayedArray(buttons);
}}
/>
)}>
{displayedArray.map(button => (
<Button
key={button}
fluid
color="transparent"
id={button}
selected={selectedButton === button}
onClick={() => {
if (selectedButton === button) {
act("choose", { choice: button });
}
else {
setSelectedButton(button);
}
setLastCharCode(null);
}}>
{button}
</Button>
))}
</Section>
</Stack.Item>
{showSearchBar && (
<Stack.Item>
<Input
fluid
onInput={(e, value) => setDisplayedArray(
buttons.filter(val => (
val.toLowerCase().search(value.toLowerCase()) !== -1
))
)}
/>
</Stack.Item>
)}
<Stack.Item>
<Stack textAlign="center">
<Stack.Item grow basis={0}>
<Button
fluid
color="good"
lineHeight={2}
content="Confirm"
disabled={selectedButton === null}
onClick={() => act("choose", { choice: selectedButton })}
/>
</Stack.Item>
<Stack.Item grow basis={0}>
<Button
fluid
color="bad"
lineHeight={2}
content="Cancel"
onClick={() => act("cancel")}
/>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
export const Loader = props => {
const { value } = props;
return (
<div className="ListInput__Loader">
<Box
className="ListInput__LoaderProgress"
style={{
width: clamp01(value) * 100 + '%',
}} />
</div>
);
};
@@ -0,0 +1,146 @@
import { useBackend } from '../backend';
import { Box, Button, Icon, Section, Stack } from '../components';
import { Window } from '../layouts';
export const OutfitEditor = (props, context) => {
const { act, data } = useBackend(context);
const { outfit, saveable, dummy64 } = data;
return (
<Window
width={380}
height={600}>
<Window.Content>
<Box
as="img"
fillPositionedParent
width="100%"
height="100%"
opacity={0.5}
py={3}
src={`data:image/jpeg;base64,${dummy64}`}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
}} />
<Section
fill
title={
<Stack>
<Stack.Item grow={1}
style={{
'overflow': 'hidden',
'white-space': 'nowrap',
'text-overflow': 'ellipsis',
}}>
<Button
ml={0.5}
color="transparent"
icon="pencil-alt"
title="Rename this outfit"
onClick={() => act("rename", {})} />
{outfit.name}
</Stack.Item>
<Stack.Item align="end" shrink={0}>
<Button
color="transparent"
icon="info"
tooltip="Ctrl-click a button to select *any* item instead of what will probably fit in that slot."
tooltipPosition="bottom-left" />
<Button
icon="code"
tooltip="Edit this outfit on a VV window"
tooltipPosition="bottom-left"
onClick={() => act("vv")} />
<Button
color={!saveable && "bad"}
icon={saveable ? "save" : "trash-alt"}
tooltip={saveable
? "Save this outfit to the custom outfit list"
: "Remove this outfit from the custom outfit list"}
tooltipPosition="bottom-left"
onClick={() => act(saveable ? "save" : "delete")} />
</Stack.Item>
</Stack>
}>
<Box textAlign="center">
<Stack mb={2}>
<OutfitSlot name="Headgear" icon="hard-hat" slot="head" />
<OutfitSlot name="Glasses" icon="glasses" slot="glasses" />
<OutfitSlot name="Ears" icon="headphones-alt" slot="ears" />
</Stack>
<Stack mb={2}>
<OutfitSlot name="Neck" icon="stethoscope" slot="neck" />
<OutfitSlot name="Mask" icon="theater-masks" slot="mask" />
</Stack>
<Stack mb={2}>
<OutfitSlot name="Uniform" icon="tshirt" slot="uniform" />
<OutfitSlot name="Suit" icon="user-tie" slot="suit" />
<OutfitSlot name="Gloves" icon="mitten" slot="gloves" />
</Stack>
<Stack mb={2}>
<OutfitSlot name="Suit Storage" icon="briefcase-medical" slot="suit_store" />
<OutfitSlot name="Back" icon="shopping-bag" slot="back" />
<OutfitSlot name="ID" icon="id-card-o" slot="id" />
</Stack>
<Stack mb={2}>
<OutfitSlot name="Belt" icon="band-aid" slot="belt" />
<OutfitSlot name="Left Hand" icon="hand-paper" slot="l_hand" />
<OutfitSlot name="Right Hand" icon="hand-paper" slot="r_hand" />
</Stack>
<Stack mb={2}>
<OutfitSlot name="Shoes" icon="socks" slot="shoes" />
<OutfitSlot name="Left Pocket" icon="envelope-open-o" iconRot={180} slot="l_pocket" />
<OutfitSlot name="Right Pocket" icon="envelope-open-o" iconRot={180} slot="r_pocket" />
</Stack>
</Box>
</Section>
</Window.Content>
</Window>
);
};
const OutfitSlot = (props, context) => {
const { act, data } = useBackend(context);
const { name, icon, iconRot, slot } = props;
const { outfit } = data;
const currItem = outfit[slot];
return (
<Stack.Item grow={1} basis={0}>
<Button fluid height={2}
bold
// todo: intuitive way to clear items
onClick={e => act(e.ctrlKey ? "ctrlClick" : "click", { slot })} >
<Icon name={icon} rotation={iconRot} />
{name}
</Button>
<Box height="32px">
{currItem?.sprite && (
<>
<Box
as="img"
src={`data:image/jpeg;base64,${currItem?.sprite}`}
title={currItem?.desc}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
}} />
<Icon
position="absolute"
name="times"
color="label"
style={{ cursor: 'pointer' }}
onClick={() => act("clear", { slot })} />
</>
)}
</Box>
<Box
color="label"
style={{
'overflow': 'hidden',
'white-space': 'nowrap',
'text-overflow': 'ellipsis',
}}
title={currItem?.path}>
{currItem?.name || "Empty"}
</Box>
</Stack.Item>
);
};
@@ -0,0 +1,81 @@
import { useBackend } from '../backend';
import { Button, Section, Stack } from '../components';
import { Window } from '../layouts';
export const OutfitManager = (props, context) => {
const { act, data } = useBackend(context);
const { outfits } = data;
return (
<Window
title="Outfit Manager"
width={300}
height={300}>
<Window.Content>
<Section
fill
scrollable
title="Custom Outfit Manager"
buttons={
<>
<Button
icon="file-upload"
tooltip="Load an outfit from a file"
tooltipPosition="left"
onClick={() => act("load")} />
<Button
icon="copy"
tooltip="Copy an already existing outfit"
tooltipPosition="left"
onClick={() => act("copy")} />
<Button
icon="plus"
tooltip="Create a new outfit"
tooltipPosition="left"
onClick={() => act("new")} />
</>
}>
<Stack vertical>
{outfits?.map(outfit => (
<Stack.Item key={outfit.ref}>
<Stack>
<Stack.Item grow={1} shrink={1}
style={{
'overflow': 'hidden',
'white-space': 'nowrap',
'text-overflow': 'ellipsis',
}}>
<Button
fluid
style={{
'overflow': 'hidden',
'white-space': 'nowrap',
'text-overflow': 'ellipsis',
}}
content={outfit.name}
onClick={() => act("edit", { outfit: outfit.ref })} />
</Stack.Item>
<Stack.Item ml={0.5}>
<Button
icon="save"
tooltip="Save this outfit to a file"
tooltipPosition="left"
onClick={() => act("save", { outfit: outfit.ref })} />
</Stack.Item>
<Stack.Item ml={0.5}>
<Button
color="bad"
icon="trash-alt"
tooltip="Delete this outfit"
tooltipPosition="left"
onClick={() => act("delete", { outfit: outfit.ref })} />
</Stack.Item>
</Stack>
</Stack.Item>
))}
</Stack>
</Section>
</Window.Content>
</Window>
);
};
@@ -0,0 +1,214 @@
import { filter, map, sortBy, uniq } from 'common/collections';
import { flow } from 'common/fp';
import { createSearch } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Icon, Input, Section, Stack, Tabs } from '../components';
import { Window } from '../layouts';
// here's an important mental define:
// custom outfits give a ref keyword instead of path
const getOutfitKey = outfit => outfit.path || outfit.ref;
const useOutfitTabs = (context, categories) => {
return useLocalState(context, 'selected-tab', categories[0]);
};
export const SelectEquipment = (props, context) => {
const { act, data } = useBackend(context);
const {
name,
icon64,
current_outfit,
favorites,
} = data;
const isFavorited = entry => favorites?.includes(entry.path);
const outfits = map(entry => ({
...entry,
favorite: isFavorited(entry),
}))([
...data.outfits,
...data.custom_outfits,
]);
// even if no custom outfits were sent, we still want to make sure there's
// at least a 'Custom' tab so the button to create a new one pops up
const categories = uniq([
...outfits.map(entry => entry.category),
'Custom',
]);
const [tab] = useOutfitTabs(context, categories);
const [searchText, setSearchText] = useLocalState(
context, 'searchText', '');
const searchFilter = createSearch(searchText, entry => (
entry.name + entry.path
));
const visibleOutfits = flow([
filter(entry => entry.category === tab),
filter(searchFilter),
sortBy(
entry => !entry.favorite,
entry => !entry.priority,
entry => entry.name
),
])(outfits);
const getOutfitEntry = current_outfit => outfits.find(outfit => (
getOutfitKey(outfit) === current_outfit
));
const currentOutfitEntry = getOutfitEntry(current_outfit);
return (
<Window
width={650}
height={415}>
<Window.Content>
<Stack fill>
<Stack.Item>
<Stack fill vertical>
<Stack.Item>
<Input
fluid
autoFocus
placeholder="Search"
value={searchText}
onInput={(e, value) => setSearchText(value)} />
</Stack.Item>
<Stack.Item>
<DisplayTabs categories={categories} />
</Stack.Item>
<Stack.Item mt={0} grow={1} basis={0}>
<OutfitDisplay
entries={visibleOutfits}
currentTab={tab} />
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item grow={1} basis={0}>
<Stack fill vertical>
<Stack.Item>
<Section>
<CurrentlySelectedDisplay entry={currentOutfitEntry} />
</Section>
</Stack.Item>
<Stack.Item grow={1}>
<Section
fill
title={name}
textAlign="center">
<Box
as="img"
m={0}
src={`data:image/jpeg;base64,${icon64}`}
height="100%"
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
}} />
</Section>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
const DisplayTabs = (props, context) => {
const { categories } = props;
const [tab, setTab] = useOutfitTabs(context, categories);
return (
<Tabs textAlign="center">
{categories.map(category => (
<Tabs.Tab
key={category}
selected={tab === category}
onClick={() => setTab(category)}>
{category}
</Tabs.Tab>
))}
</Tabs>
);
};
const OutfitDisplay = (props, context) => {
const { act, data } = useBackend(context);
const { current_outfit } = data;
const { entries, currentTab } = props;
return (
<Section fill scrollable>
{entries.map(entry => (
<Button
key={getOutfitKey(entry)}
fluid
ellipsis
icon={entry.favorite && 'star'}
iconColor="gold"
content={entry.name}
title={entry.path || entry.name}
selected={getOutfitKey(entry) === current_outfit}
onClick={() => act('preview', { path: getOutfitKey(entry) })} />
))}
{currentTab === "Custom" && (
<Button
color="transparent"
icon="plus"
fluid
onClick={() => act('customoutfit')}>
Create a custom outfit...
</Button>
)}
</Section>
);
};
const CurrentlySelectedDisplay = (props, context) => {
const { act, data } = useBackend(context);
const { current_outfit } = data;
const { entry } = props;
return (
<Stack align="center">
{entry?.path && (
<Stack.Item>
<Icon
size={1.6}
name={entry.favorite ? 'star' : 'star-o'}
color="gold"
style={{ cursor: 'pointer' }}
onClick={() => act('togglefavorite', {
path: entry.path,
})} />
</Stack.Item>
)}
<Stack.Item grow={1} basis={0}>
<Box color="label">
Currently selected:
</Box>
<Box
title={entry?.path}
style={{
'overflow': 'hidden',
'white-space': 'nowrap',
'text-overflow': 'ellipsis',
}}>
{entry?.name}
</Box>
</Stack.Item>
<Stack.Item>
<Button
mr={0.8}
lineHeight={2}
color="green"
onClick={() => act('applyoutfit', {
path: current_outfit,
})}>
Confirm
</Button>
</Stack.Item>
</Stack>
);
};