diff --git a/code/__defines/_planes+layers_vr.dm b/code/__defines/_planes+layers_vr.dm index 125f492cd7a..a9266aa701d 100644 --- a/code/__defines/_planes+layers_vr.dm +++ b/code/__defines/_planes+layers_vr.dm @@ -3,5 +3,6 @@ #define PLANE_CH_STATUS_R 27 //Right-side status icon #define PLANE_CH_BACKUP 28 //Backup implant #define PLANE_CH_VANTAG 29 //Vore Antag hud +#define PLANE_CH_STOMACH -11 //Stomach Plane #define PLANE_AUGMENTED 40 //Augmented-reality plane diff --git a/code/__defines/chemistry_vr.dm b/code/__defines/chemistry_vr.dm index 0058985adcb..3ce868c5b82 100644 --- a/code/__defines/chemistry_vr.dm +++ b/code/__defines/chemistry_vr.dm @@ -1,4 +1,5 @@ // More for our custom races #define IS_CHIMERA 12 #define IS_SHADEKIN 13 -#define IS_ALRAUNE 14 \ No newline at end of file +#define IS_ALRAUNE 14 +#define IS_LLEILL 15 \ No newline at end of file diff --git a/code/__defines/is_helpers.dm b/code/__defines/is_helpers.dm index dfb8b9246a8..b4e180deb09 100644 --- a/code/__defines/is_helpers.dm +++ b/code/__defines/is_helpers.dm @@ -51,6 +51,7 @@ #define issilicon(A) istype(A, /mob/living/silicon) #define isAI(A) istype(A, /mob/living/silicon/ai) #define isrobot(A) istype(A, /mob/living/silicon/robot) +#define isshell(A) istype(A, /mob/living/silicon/robot/ai_shell) #define ispAI(A) istype(A, /mob/living/silicon/pai) #define isbot(A) istype(A, /mob/living/bot) diff --git a/code/__defines/mobs_vr.dm b/code/__defines/mobs_vr.dm index 183921ecde7..ae87b1c624e 100644 --- a/code/__defines/mobs_vr.dm +++ b/code/__defines/mobs_vr.dm @@ -7,7 +7,9 @@ #define VIS_AUGMENTED 32 -#define VIS_COUNT 32 +#define VIS_CH_STOMACH 33 + +#define VIS_COUNT 33 //Protean organs #define O_ORCH "orchestrator" @@ -33,6 +35,7 @@ #define SPECIES_ZORREN_HIGH "Zorren" #define SPECIES_CUSTOM "Custom Species" #define SPECIES_TAJARAN "Tajara" +#define SPECIES_LLEILL "Lleill" //monkey species #define SPECIES_MONKEY_AKULA "Sobaka" #define SPECIES_MONKEY_NEVREAN "Sparra" diff --git a/code/__defines/species_languages_vr.dm b/code/__defines/species_languages_vr.dm index ec9165653d7..146fcb86ab9 100644 --- a/code/__defines/species_languages_vr.dm +++ b/code/__defines/species_languages_vr.dm @@ -17,3 +17,4 @@ #define LANGUAGE_MOUSE "Mouse" #define LANGUAGE_SHADEKIN "Shadekin Empathy" +#define LANGUAGE_LLEILL "Glamour Speak" diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 20c38938a59..8a722ac7676 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1580,6 +1580,7 @@ GLOBAL_REAL_VAR(list/stack_trace_storage) . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH_VR} //Health bar but transparent at 100 . += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status . += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags + . += new /obj/screen/plane_master{plane = PLANE_CH_STOMACH} //Stomachs . += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality //VOREStation Add End /proc/CallAsync(datum/source, proctype, list/arguments) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index edfe9059ff1..da18b6c5706 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -1015,6 +1015,12 @@ var/list/gamemode_cache = list() if("asset_cdn_url") config.asset_cdn_url = value + if("allow_robot_recolor") + config.allow_robot_recolor = TRUE + + if("allow_simple_mob_recolor") + config.allow_simple_mob_recolor = TRUE + else log_misc("Unknown setting in configuration: '[name]'") @@ -1083,10 +1089,6 @@ var/list/gamemode_cache = list() if("loadout_whitelist") config.loadout_whitelist = text2num(value) - if("allow_robot_recolor") - config.allow_robot_recolor = 1 - if("allow_simple_mob_recolor") - config.allow_simple_mob_recolor = 1 else log_misc("Unknown setting in configuration: '[name]'") diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 17d8fb17edc..7c37c10f520 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -486,7 +486,6 @@ var/global/list/PDA_Manifest = list() R.fields["ma_crim"] = "None" R.fields["ma_crim_d"] = "No major crime convictions." R.fields["notes"] = "No notes." - R.fields["notes"] = "No notes." if(hidden) hidden_security += R else diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index b73df5ca3ab..87d4146259d 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -205,6 +205,11 @@ occupantData["bodyTempF"] = (((H.bodytemperature-T0C) * 1.8) + 32) occupantData["hasBorer"] = H.has_brain_worms() + occupantData["colourblind"] = null + for(var/datum/modifier/M in H.modifiers) + if(!isnull(M.wire_colors_replace)) + occupantData["colourblind"] = LAZYLEN(M.wire_colors_replace) + break var/bloodData[0] if(H.vessel) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 38b48ee76a4..49dcdbafe76 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -86,7 +86,7 @@ /obj/machinery/computer/cryopod/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "CryoStorageVr", storage_name) // VOREStation Edit - Use our own template for our custom data + ui = new(user, src, "CryoStorage", storage_name) // VOREStation Edit - Use our own template for our custom data ui.open() /obj/machinery/computer/cryopod/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index fe41bc658ff..ace8fde6360 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -62,6 +62,7 @@ /obj/item/borg/upgrade/utility/rename/action(var/mob/living/silicon/robot/R) if(..()) return 0 + if(isshell(R)) return 0 R.notify_ai(ROBOT_NOTIFICATION_NEW_NAME, R.name, heldname) R.name = heldname R.custom_name = heldname diff --git a/code/game/objects/random/maintenance.dm b/code/game/objects/random/maintenance.dm index 56366d7cdd9..25f70920591 100644 --- a/code/game/objects/random/maintenance.dm +++ b/code/game/objects/random/maintenance.dm @@ -112,7 +112,8 @@ something, make sure it's not in one of the other lists.*/ prob(2);/obj/item/toy/tennis/blue, prob(2);/obj/item/toy/tennis/purple, prob(1);/obj/item/toy/baseball, - prob(1);/obj/item/pizzavoucher + prob(1);/obj/item/pizzavoucher, + prob(2);/obj/item/weapon/cracker /* VOREStation Edit End */ ) diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm index 3f0f80593ca..a02ac5de5a1 100644 --- a/code/game/objects/random/misc.dm +++ b/code/game/objects/random/misc.dm @@ -1132,5 +1132,13 @@ prob(8);/obj/item/capture_crystal/random, prob(10);/obj/item/weapon/bluespace_harpoon, prob(10);/obj/item/weapon/bluespace_crystal, - prob(1);/obj/item/clothing/glasses/graviton + prob(1);/obj/item/clothing/glasses/graviton, + prob(10);/obj/item/weapon/cracker, + prob(1);/obj/item/weapon/cracker/shrinking, + prob(1);/obj/item/weapon/cracker/growing, + prob(1);/obj/item/weapon/cracker/invisibility, + prob(1);/obj/item/weapon/cracker/drugged, + prob(1);/obj/item/weapon/cracker/knockover, + prob(1);/obj/item/weapon/cracker/vore, + prob(1);/obj/item/weapon/cracker/money ) \ No newline at end of file diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index d4b679e742a..fb18083562e 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -227,8 +227,7 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) if (!( A.anchored )) A.forceMove(src) playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - //src.connected = null - qdel(src.connected) + qdel_null(connected) else if (src.locked == 0) playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) src.connected = new /obj/structure/m_tray/c_tray( src.loc ) @@ -242,8 +241,7 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) A.forceMove(src.connected.loc) src.connected.icon_state = "cremat" else - //src.connected = null - qdel(src.connected) + qdel_null(connected) src.add_fingerprint(user) update() diff --git a/code/game/objects/structures/props/rocks.dm b/code/game/objects/structures/props/rocks.dm index 220bc832150..253a36bda55 100644 --- a/code/game/objects/structures/props/rocks.dm +++ b/code/game/objects/structures/props/rocks.dm @@ -80,3 +80,28 @@ /obj/structure/prop/rock/ice/small/alt icon_state = "icesmall2" + +// glamour things for whitespace +/obj/structure/prop/rock/glamour + name = "glamour" + icon = 'icons/obj/glamour.dmi' + icon_state = "pillar_1" + +/obj/structure/prop/rock/glamour/double + icon_state = "pillar_3" + +/obj/structure/prop/rock/glamour/triple + icon_state = "pillar_2" + +/obj/structure/prop/rock/glamour/small + icon_state = "gems_1" + density = FALSE + +/obj/structure/prop/rock/glamour/small/alt1 + icon_state = "gems_2" + +/obj/structure/prop/rock/glamour/small/alt2 + icon_state = "gems_3" + +/obj/structure/prop/rock/glamour/small/alt3 + icon_state = "gems_4" \ No newline at end of file diff --git a/code/game/objects/structures/trash_pile_vr.dm b/code/game/objects/structures/trash_pile_vr.dm index 4fcfc0619a7..535b0198a86 100644 --- a/code/game/objects/structures/trash_pile_vr.dm +++ b/code/game/objects/structures/trash_pile_vr.dm @@ -281,6 +281,7 @@ prob(4);/obj/item/weapon/gun/energy/sizegun, prob(4);/obj/item/device/slow_sizegun, prob(4);/obj/item/clothing/accessory/collar/shock/bluespace, + prob(3);/obj/item/weapon/cracker, prob(3);/obj/item/weapon/material/butterfly, prob(3);/obj/item/weapon/material/butterfly/switchblade, prob(3);/obj/item/clothing/gloves/knuckledusters, @@ -313,7 +314,8 @@ prob(1);/obj/item/device/perfect_tele/one_beacon, prob(1);/obj/item/clothing/gloves/bluespace, prob(1);/obj/item/weapon/gun/energy/mouseray, - prob(1);/obj/item/clothing/accessory/collar/shock/bluespace/modified) + prob(1);/obj/item/clothing/accessory/collar/shock/bluespace/modified, + prob(1);/obj/item/weapon/gun/energy/sizegun/backfire) var/obj/item/I = new path() return I diff --git a/code/game/turfs/simulated/water.dm b/code/game/turfs/simulated/water.dm index ae136ec0e62..fc6f5b261e4 100644 --- a/code/game/turfs/simulated/water.dm +++ b/code/game/turfs/simulated/water.dm @@ -263,3 +263,14 @@ var/list/shoreline_icon_cache = list() to_chat(L, "You get drenched in blood from entering \the [src]!") AM.water_act(5) ..() + +/turf/simulated/floor/water/glamour + name = "glamour" + desc = "A body of glamour. It seems shallow enough to walk through, if needed." + icon = 'icons/turf/flooring/glamour.dmi' + icon_state = "water" + water_icon = 'icons/turf/flooring/glamour.dmi' + water_state = "water" + under_state = "glamour" + reagent_type = "water" + diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index 61603361f59..21e6602863e 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -155,17 +155,17 @@ var/list/gear_datums = list() . += "
" for(var/gear_name in LC.gear) var/datum/gear/G = LC.gear[gear_name] - //VOREStation Edit Start if(preference_mob && preference_mob.client) if(G.ckeywhitelist && !(preference_mob.ckey in G.ckeywhitelist)) continue if(G.character_name && !(preference_mob.client.prefs.real_name in G.character_name)) continue - //VOREStation Edit End var/ticked = (G.display_name in pref.gear) . += "[G.display_name]" . += "[G.cost]" . += "[G.description]" + if(G.show_roles && G.allowed_roles) + . += "        Restricted to: [list2text(G.allowed_roles,", ")]" if(ticked) . += "" for(var/datum/gear_tweak/tweak in G.gear_tweaks) @@ -252,6 +252,7 @@ var/list/gear_datums = list() var/cost = 1 //Number of points used. Items in general cost 1 point, storage/armor/gloves/special use costs 2 points. var/slot //Slot to equip to. var/list/allowed_roles //Roles that can spawn with this item. + var/show_roles = TRUE //Show the role restrictions on this item? var/whitelisted //Term to check the whitelist for.. var/sort_category = "General" var/list/gear_tweaks = list() //List of datums which will alter the item after it has been spawned. diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm index a971c1f17a7..762cca238e1 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm @@ -75,7 +75,7 @@ gear_tweaks += new/datum/gear_tweak/path(sortAssoc(wcoats)) /datum/gear/accessory/holster - display_name = "holster selection (Security, CD, HoP)" + display_name = "holster selection" path = /obj/item/clothing/accessory/holster allowed_roles = list("Site Manager", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective") diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm index 71245acf0ef..beb7ccdeada 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm @@ -57,35 +57,35 @@ path = /obj/item/clothing/accessory/collar/holo/indigestible /datum/gear/accessory/holster - display_name = "holster selection (Security, SM, HoP)" + display_name = "holster selection" allowed_roles = list("Site Manager", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Talon Captain","Talon Guard") /datum/gear/accessory/brown_vest - display_name = "webbing, brown (Eng, Sec, Med, Miner)" + display_name = "webbing, brown" allowed_roles = list("Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Shaft Miner","Talon Captain","Talon Doctor","Talon Engineer","Talon Guard", "Talon Miner") /datum/gear/accessory/black_vest - display_name = "webbing, black (Eng, Sec, Med, Miner)" + display_name = "webbing, black" allowed_roles = list("Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Shaft Miner","Talon Captain","Talon Doctor","Talon Engineer","Talon Guard", "Talon Miner") /datum/gear/accessory/white_vest - display_name = "webbing, white (Medical)" + display_name = "webbing, white" allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Talon Doctor") /datum/gear/accessory/brown_drop_pouches - display_name = "drop pouches, brown (Eng, Sec, Med, Miner)" + display_name = "drop pouches, brown" allowed_roles = list("Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Shaft Miner","Talon Captain","Talon Doctor","Talon Engineer","Talon Guard", "Talon Miner") /datum/gear/accessory/black_drop_pouches - display_name = "drop pouches, black (Eng, Sec, Med, Miner)" + display_name = "drop pouches, black" allowed_roles = list("Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Shaft Miner","Talon Captain","Talon Doctor","Talon Engineer","Talon Guard", "Talon Miner") /datum/gear/accessory/white_drop_pouches - display_name = "drop pouches, white (Medical)" + display_name = "drop pouches, white" allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Talon Doctor") /datum/gear/accessory/bluespace - display_name = "bluespace badge (Eng, Sec, Med, Miner, Pilot)" + display_name = "bluespace badge" path = /obj/item/clothing/accessory/storage/bluespace allowed_roles = list("Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Shaft Miner","Talon Captain","Talon Doctor","Talon Engineer","Talon Guard","Talon Miner","Pilot") cost = 2 diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes.dm b/code/modules/client/preference_setup/loadout/loadout_eyes.dm index 101ba17368b..6e9f81a9d5b 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes.dm @@ -68,76 +68,76 @@ path = /obj/item/clothing/glasses/science /datum/gear/eyes/security - display_name = "Security HUD (Security)" + display_name = "Security HUD" path = /obj/item/clothing/glasses/hud/security allowed_roles = list("Security Officer","Head of Security","Warden", "Detective") /datum/gear/eyes/security/prescriptionsec - display_name = "Security HUD, prescription (Security)" + display_name = "Security HUD, prescription" path = /obj/item/clothing/glasses/hud/security/prescription /datum/gear/eyes/security/sunglasshud - display_name = "Security HUD, sunglasses (Security)" + display_name = "Security HUD, sunglasses" path = /obj/item/clothing/glasses/sunglasses/sechud /datum/gear/eyes/security/aviator - display_name = "Security HUD Aviators (Security)" + display_name = "Security HUD Aviators" path = /obj/item/clothing/glasses/sunglasses/sechud/aviator /datum/gear/eyes/security/aviator/prescription - display_name = "Security HUD Aviators, prescription (Security)" + display_name = "Security HUD Aviators, prescription" path = /obj/item/clothing/glasses/sunglasses/sechud/aviator/prescription /datum/gear/eyes/medical - display_name = "Medical HUD (Medical)" + display_name = "Medical HUD" path = /obj/item/clothing/glasses/hud/health allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Search and Rescue") /datum/gear/eyes/medical/prescriptionmed - display_name = "Medical HUD, prescription (Medical)" + display_name = "Medical HUD, prescription" path = /obj/item/clothing/glasses/hud/health/prescription /datum/gear/eyes/medical/aviator - display_name = "Medical HUD Aviators (Medical)" + display_name = "Medical HUD Aviators" path = /obj/item/clothing/glasses/hud/health/aviator /datum/gear/eyes/medical/aviator/prescription - display_name = "Medical HUD Aviators, prescription (Medical)" + display_name = "Medical HUD Aviators, prescription" path = /obj/item/clothing/glasses/hud/health/aviator/prescription /datum/gear/eyes/janitor - display_name = "Contaminant HUD (Janitor)" + display_name = "Contaminant HUD" path = /obj/item/clothing/glasses/hud/janitor allowed_roles = list("Janitor") /datum/gear/eyes/janitor/prescriptionjan - display_name = "Contaminant HUD, prescription (Janitor)" + display_name = "Contaminant HUD, prescription" path = /obj/item/clothing/glasses/hud/janitor/prescription /datum/gear/eyes/meson - display_name = "Optical Meson Scanners (Engineering, Science, Mining)" + display_name = "Optical Meson Scanners" path = /obj/item/clothing/glasses/meson allowed_roles = list("Engineer","Chief Engineer","Atmospheric Technician", "Scientist", "Research Director", "Shaft Miner") /datum/gear/eyes/meson/prescription - display_name = "Optical Meson Scanners, prescription (Engineering, Science, Mining)" + display_name = "Optical Meson Scanners, prescription" path = /obj/item/clothing/glasses/meson/prescription /datum/gear/eyes/material - display_name = "Optical Material Scanners (Mining)" + display_name = "Optical Material Scanners" path = /obj/item/clothing/glasses/material allowed_roles = list("Shaft Miner","Quartermaster") /datum/gear/eyes/material/prescription - display_name = "Prescription Optical Material Scanners (Mining)" + display_name = "Prescription Optical Material Scanners" path = /obj/item/clothing/glasses/material/prescription /datum/gear/eyes/meson/aviator - display_name = "Optical Meson Aviators, (Engineering, Science, Mining)" + display_name = "Optical Meson Aviators" path = /obj/item/clothing/glasses/meson/aviator /datum/gear/eyes/meson/aviator/prescription - display_name = "Optical Meson Aviators, prescription (Engineering, Science, Mining)" + display_name = "Optical Meson Aviators, prescription" path = /obj/item/clothing/glasses/meson/aviator/prescription /datum/gear/eyes/glasses/fakesun @@ -149,16 +149,16 @@ path = /obj/item/clothing/glasses/fakesunglasses/aviator /datum/gear/eyes/sun - display_name = "Sunglasses (Security/Command)" + display_name = "sunglasses (Security/Command)" path = /obj/item/clothing/glasses/sunglasses allowed_roles = list("Security Officer","Head of Security","Warden","Site Manager","Head of Personnel","Quartermaster","Internal Affairs Agent","Detective") /datum/gear/eyes/sun/shades - display_name = "Sunglasses, fat (Security/Command)" + display_name = "sunglasses, fat (Security/Command)" path = /obj/item/clothing/glasses/sunglasses/big /datum/gear/eyes/sun/aviators - display_name = "Sunglasses, aviators (Security/Command)" + display_name = "sunglasses, aviators (Security/Command)" path = /obj/item/clothing/glasses/sunglasses/aviator /datum/gear/eyes/sun/prescriptionsun @@ -170,17 +170,17 @@ path = /obj/item/clothing/glasses/circuitry /datum/gear/eyes/glasses/rimless - display_name = "Glasses, rimless" + display_name = "glasses, rimless" path = /obj/item/clothing/glasses/rimless /datum/gear/eyes/glasses/prescriptionrimless - display_name = "Glasses, prescription rimless" + display_name = "glasses, prescription rimless" path = /obj/item/clothing/glasses/regular/rimless /datum/gear/eyes/glasses/thin - display_name = "Glasses, thin frame" + display_name = "glasses, thin frame" path = /obj/item/clothing/glasses/thin /datum/gear/eyes/glasses/prescriptionthin - display_name = "Glasses, prescription thin frame" + display_name = "glasses, prescription thin frame" path = /obj/item/clothing/glasses/regular/thin diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm b/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm index bc545291def..6bc9e9b1377 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm @@ -21,27 +21,27 @@ path = /obj/item/clothing/glasses/omnihud/prescription /datum/gear/eyes/arglasses/sec - display_name = "AR-S glasses (Sec)" + display_name = "AR-Security glasses" path = /obj/item/clothing/glasses/omnihud/sec allowed_roles = list("Security Officer","Head of Security","Warden","Detective") /datum/gear/eyes/arglasses/sci - display_name = "AR-R glasses (Sci)" + display_name = "AR-Research glasses" path = /obj/item/clothing/glasses/omnihud/rnd allowed_roles = list("Research Director","Scientist","Xenobiologist","Xenobotanist","Roboticist") /datum/gear/eyes/arglasses/eng - display_name = "AR-E glasses (Eng)" + display_name = "AR-Engineering glasses" path = /obj/item/clothing/glasses/omnihud/eng allowed_roles = list("Engineer","Chief Engineer","Atmospheric Technician") /datum/gear/eyes/arglasses/med - display_name = "AR-M glasses (Medical)" + display_name = "AR-Medical glasses" path = /obj/item/clothing/glasses/omnihud/med allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist") /datum/gear/eyes/arglasses/all - display_name = "AR-B glasses (SM, HoP)" + display_name = "AR-Command glasses" path = /obj/item/clothing/glasses/omnihud/all cost = 2 allowed_roles = list("Site Manager","Head of Personnel") @@ -55,7 +55,7 @@ path = /obj/item/clothing/glasses/fluff/science_proper /datum/gear/eyes/meson/retinal - display_name = "retinal projector, meson (Eng, Sci, Explo)" + display_name = "retinal projector, meson" path = /obj/item/clothing/glasses/omnihud/eng/meson /datum/gear/eyes/security/secpatch @@ -67,7 +67,7 @@ path = /obj/item/clothing/glasses/hud/security/eyepatch2 /datum/gear/eyes/security/tac_sec_visor - display_name = "Tactical AR visor (Security)" + display_name = "Tactical AR visor" path = /obj/item/clothing/glasses/sunglasses/sechud/tactical_sec_vis /datum/gear/eyes/medical/medpatch diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm index 1720d503153..1cc15975e27 100644 --- a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm @@ -10,16 +10,16 @@ ckeywhitelist = list("This entry should never be choosable with this variable set.") //If it does, then that means somebody fucked up the whitelist system pretty hard character_name = list("This entry should never be choosable with this variable set.") cost = 0 + /* /datum/gear/fluff/testhorn path = /obj/item/weapon/bikehorn display_name = "Airhorn - Example Item" description = "An example item that you probably shouldn't see!" - ckeywhitelist = list("mewchild") + ckeywhitelist = list("your_ckey_here") allowed_roles = list("Engineer") */ - /datum/gear/fluff/collar //Use this as a base path for collars if you'd like to set tags in loadout. Make sure you don't use apostrophes in the display name or this breaks! slot = slot_tie diff --git a/code/modules/client/preference_setup/loadout/loadout_gloves.dm b/code/modules/client/preference_setup/loadout/loadout_gloves.dm index 6530edf3215..35711424481 100644 --- a/code/modules/client/preference_setup/loadout/loadout_gloves.dm +++ b/code/modules/client/preference_setup/loadout/loadout_gloves.dm @@ -52,7 +52,7 @@ cost = 3 /datum/gear/gloves/forensic - display_name = "gloves, forensic (Detective)" + display_name = "gloves, forensic" path = /obj/item/clothing/gloves/forensic allowed_roles = list("Detective") diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index a2bde3aaa93..3d5793acaee 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -144,6 +144,7 @@ display_name = "labcoat selection, cmo" path = /obj/item/clothing/suit/storage/toggle/labcoat/cmo allowed_roles = list("Chief Medical Officer") + show_roles = FALSE /datum/gear/suit/labcoat_cmo/New() ..() @@ -163,6 +164,7 @@ display_name = "labcoat, research director" path = /obj/item/clothing/suit/storage/toggle/labcoat/rd allowed_roles = list("Research Director") + show_roles = FALSE /datum/gear/suit/miscellaneous/labcoat display_name = "plague doctor's coat" @@ -254,42 +256,49 @@ display_name = "cloak, head of security" path = /obj/item/clothing/accessory/poncho/roles/cloak/hos allowed_roles = list("Head of Security") + show_roles = FALSE cost = 1 /datum/gear/suit/roles/cloak_cmo display_name = "cloak, chief medical officer" path = /obj/item/clothing/accessory/poncho/roles/cloak/cmo allowed_roles = list("Chief Medical Officer") + show_roles = FALSE cost = 1 /datum/gear/suit/roles/cloak_ce display_name = "cloak, chief engineer" path = /obj/item/clothing/accessory/poncho/roles/cloak/ce allowed_roles = list("Chief Engineer") + show_roles = FALSE cost = 1 /datum/gear/suit/roles/cloak_rd display_name = "cloak, research director" path = /obj/item/clothing/accessory/poncho/roles/cloak/rd allowed_roles = list("Research Director") + show_roles = FALSE cost = 1 /datum/gear/suit/roles/cloak_qm display_name = "cloak, quartermaster" path = /obj/item/clothing/accessory/poncho/roles/cloak/qm allowed_roles = list("Quartermaster") + show_roles = FALSE cost = 1 /datum/gear/suit/roles/cloak_captain display_name = "cloak, site manager" path = /obj/item/clothing/accessory/poncho/roles/cloak/captain allowed_roles = list("Site Manager") + show_roles = FALSE cost = 1 /datum/gear/suit/roles/cloak_hop display_name = "cloak, head of personnel" path = /obj/item/clothing/accessory/poncho/roles/cloak/hop allowed_roles = list("Head of Personnel") + show_roles = FALSE cost = 1 /datum/gear/suit/cloak_custom //A colorable cloak @@ -344,7 +353,7 @@ path = /obj/item/clothing/suit/suspenders /datum/gear/suit/forensics - display_name = "forensics uniform selection (Detective)" + display_name = "forensics uniform selection" path = /obj/item/clothing/suit/storage/forensics/red/long allowed_roles = list("Detective") @@ -362,11 +371,13 @@ display_name = "coat, quartermaster" path = /obj/item/clothing/suit/storage/qm allowed_roles = list("Quartermaster") + show_roles = FALSE /datum/gear/suit/cargo_coat display_name = "coat, cargo tech" path = /obj/item/clothing/suit/storage/cargo allowed_roles = list("Quartermaster","Shaft Miner","Cargo Technician","Head of Personnel") + show_roles = FALSE // winter coats go here /datum/gear/suit/wintercoat @@ -377,116 +388,139 @@ display_name = "winter coat, site manager" path = /obj/item/clothing/suit/storage/hooded/wintercoat/captain allowed_roles = list("Site Manager") + show_roles = FALSE /datum/gear/suit/wintercoat/hop display_name = "winter coat, head of personnel" path = /obj/item/clothing/suit/storage/hooded/wintercoat/hop allowed_roles = list("Head of Personnel") + show_roles = FALSE /datum/gear/suit/wintercoat/security display_name = "winter coat, security" path = /obj/item/clothing/suit/storage/hooded/wintercoat/security allowed_roles = list("Security Officer", "Head of Security", "Warden", "Detective") + show_roles = FALSE /datum/gear/suit/wintercoat/security/hos display_name = "winter coat, head of security" path = /obj/item/clothing/suit/storage/hooded/wintercoat/security/hos allowed_roles = list("Head of Security") + show_roles = FALSE /datum/gear/suit/wintercoat/medical display_name = "winter coat, medical" path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist") + show_roles = FALSE /datum/gear/suit/wintercoat/medical/alt display_name = "winter coat, medical alt" path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/alt allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist") + show_roles = FALSE /datum/gear/suit/wintercoat/medical/viro display_name = "winter coat, virologist" path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/viro allowed_roles = list("Medical Doctor","Chief Medical Officer") + show_roles = FALSE /datum/gear/suit/wintercoat/medical/para display_name = "winter coat, paramedic" path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/para allowed_roles = list("Medical Doctor","Chief Medical Officer","Paramedic") + show_roles = FALSE /datum/gear/suit/wintercoat/medical/chemist display_name = "winter coat, chemist" path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/chemist allowed_roles = list("Chief Medical Officer","Chemist") + show_roles = FALSE /datum/gear/suit/wintercoat/medical/cmo display_name = "winter coat, chief medical officer" path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/cmo allowed_roles = list("Chief Medical Officer") + show_roles = FALSE /datum/gear/suit/wintercoat/medical/sar display_name = "winter coat, search and rescue" path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar allowed_roles = list("Chief Medical Officer","Paramedic") + show_roles = FALSE /datum/gear/suit/wintercoat/science display_name = "winter coat, science" path = /obj/item/clothing/suit/storage/hooded/wintercoat/science allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist", "Xenobotanist") + show_roles = FALSE /datum/gear/suit/wintercoat/science/robotics display_name = "winter coat, robotics" path = /obj/item/clothing/suit/storage/hooded/wintercoat/science/robotics allowed_roles = list("Research Director", "Roboticist") + show_roles = FALSE /datum/gear/suit/wintercoat/science/rd display_name = "winter coat, research director" path = /obj/item/clothing/suit/storage/hooded/wintercoat/science/rd allowed_roles = list("Research Director") + show_roles = FALSE /datum/gear/suit/wintercoat/engineering display_name = "winter coat, engineering" path = /obj/item/clothing/suit/storage/hooded/wintercoat/engineering allowed_roles = list("Chief Engineer","Atmospheric Technician", "Engineer") + show_roles = FALSE /datum/gear/suit/wintercoat/engineering/atmos display_name = "winter coat, atmospherics" path = /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos allowed_roles = list("Chief Engineer", "Atmospheric Technician") + show_roles = FALSE /datum/gear/suit/wintercoat/engineering/ce display_name = "winter coat, chief engineer" path = /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/ce allowed_roles = list("Chief Engineer") + show_roles = FALSE /datum/gear/suit/wintercoat/hydro display_name = "winter coat, hydroponics" path = /obj/item/clothing/suit/storage/hooded/wintercoat/hydro allowed_roles = list("Botanist", "Xenobotanist") + show_roles = FALSE /datum/gear/suit/wintercoat/cargo display_name = "winter coat, cargo" path = /obj/item/clothing/suit/storage/hooded/wintercoat/cargo allowed_roles = list("Quartermaster","Cargo Technician") + show_roles = FALSE /datum/gear/suit/wintercoat/miner display_name = "winter coat, mining" path = /obj/item/clothing/suit/storage/hooded/wintercoat/miner allowed_roles = list("Shaft Miner") + show_roles = FALSE /datum/gear/suit/wintercoat/cargo/qm display_name = "winter coat, quartermaster" path = /obj/item/clothing/suit/storage/hooded/wintercoat/cargo/qm allowed_roles = list("Quartermaster") + show_roles = FALSE /datum/gear/suit/wintercoat/bar display_name = "winter coat, bartender" path = /obj/item/clothing/suit/storage/hooded/wintercoat/bar allowed_roles = list("Bartender") + show_roles = FALSE /datum/gear/suit/wintercoat/janitor display_name = "winter coat, janitor" path = /obj/item/clothing/suit/storage/hooded/wintercoat/janitor allowed_roles = list("Janitor") + show_roles = FALSE /datum/gear/suit/wintercoat/aformal display_name = "winter coat, assistant formal" @@ -613,31 +647,37 @@ display_name = "snowsuit, command" path = /obj/item/clothing/suit/storage/snowsuit/command allowed_roles = list("Site Manager","Research Director","Head of Personnel","Head of Security","Chief Engineer","Command Secretary") + show_roles = FALSE /datum/gear/suit/snowsuit/security display_name = "snowsuit, security" path = /obj/item/clothing/suit/storage/snowsuit/security allowed_roles = list("Security Officer", "Head of Security", "Warden", "Detective") + show_roles = FALSE /datum/gear/suit/snowsuit/medical display_name = "snowsuit, medical" path = /obj/item/clothing/suit/storage/snowsuit/medical allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Search and Rescue") + show_roles = FALSE /datum/gear/suit/snowsuit/science display_name = "snowsuit, science" path = /obj/item/clothing/suit/storage/snowsuit/science allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist") + show_roles = FALSE /datum/gear/suit/snowsuit/engineering display_name = "snowsuit, engineering" path = /obj/item/clothing/suit/storage/snowsuit/engineering allowed_roles = list("Chief Engineer","Atmospheric Technician", "Engineer") + show_roles = FALSE /datum/gear/suit/snowsuit/cargo display_name = "snowsuit, supply" path = /obj/item/clothing/suit/storage/snowsuit/cargo allowed_roles = list("Quartermaster","Shaft Miner","Cargo Technician","Head of Personnel") + show_roles = FALSE /datum/gear/suit/miscellaneous/cardigan display_name = "cardigan, colorable" @@ -651,6 +691,7 @@ display_name = "command dress jacket" path = /obj/item/clothing/suit/storage/toggle/cmddressjacket allowed_roles = list("Site Manager", "Head of Personnel", "Command Secretary") + show_roles = FALSE /datum/gear/suit/miscellaneous/kimono display_name = "traditional kimono, colorable" diff --git a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm index 26f079d653f..74a2f3f08a7 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm @@ -55,7 +55,7 @@ //Detective alternative /datum/gear/suit/detective_alt - display_name = "sleek modern coat selection, detective" + display_name = "sleek modern coat selection" path = /obj/item/clothing/suit/storage/det_trench/alt allowed_roles = list("Head of Security", "Detective") @@ -112,7 +112,7 @@ Talon winter coat /datum/gear/suit/armor/combat/crusader_explo display_name = "knight, explo" path = /obj/item/clothing/suit/armor/combat/crusader_explo - allowed_roles = list("Explorer","Pathfinder") + //allowed_roles = list("Explorer","Pathfinder") /datum/gear/suit/armor/combat/crusader_explo/FM display_name = "knight, Field Medic" @@ -232,11 +232,13 @@ Talon winter coat display_name = "boat cloak, site manager" path = /obj/item/clothing/accessory/poncho/roles/cloak/boat/cap allowed_roles = list("Site Manager") + show_roles = FALSE /datum/gear/suit/roles/hopboatcloak display_name = "boat cloak, head of personnel" path = /obj/item/clothing/accessory/poncho/roles/cloak/boat/hop allowed_roles = list("Head of Personnel") + show_roles = FALSE /datum/gear/suit/roles/boatcloaks display_name = "boat cloak selection" @@ -268,11 +270,13 @@ Talon winter coat display_name = "shroud, site manager" path = /obj/item/clothing/accessory/poncho/roles/cloak/shroud/cap allowed_roles = list("Site Manager") + show_roles = FALSE /datum/gear/suit/roles/hopshroud display_name = "shroud, head of personnel" path = /obj/item/clothing/accessory/poncho/roles/cloak/shroud/hop allowed_roles = list("Head of Personnel") + show_roles = FALSE /datum/gear/suit/roles/shrouds display_name = "shroud selection" diff --git a/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm b/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm index bcd32eb9c45..624ab0692fa 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm @@ -5,6 +5,7 @@ display_name = "DEPT - BLANK's Uniforms" description = "Select from a range of outfits available to all BLANK personnel." allowed_roles = list("") + show_roles = FALSE path = slot = slot_w_uniform sort_category = "Uniforms" @@ -23,6 +24,7 @@ display_name = "Command - Site Manager's Uniforms" description = "Select from a range of outfits available to all Site Managers." allowed_roles = list("Site Manager") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_captain sort_category = "Uniforms" cost = 1 @@ -52,6 +54,7 @@ display_name = "Command - Head of Personnel's Uniforms" description = "Select from a range of outfits available to all Heads of Personnel." allowed_roles = list("Head of Personnel") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_hop sort_category = "Uniforms" cost = 1 @@ -83,6 +86,7 @@ display_name = "Civilian - Pilot's Uniforms" description = "Select from a range of outfits available to all Pilots." allowed_roles = list("Pilot") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_pilot sort_category = "Uniforms" cost = 1 @@ -101,6 +105,7 @@ display_name = "Civilian - Janitor's Uniforms" description = "Select from a range of outfits available to all Janitorial personnel." allowed_roles = list("Janitor") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_janitor sort_category = "Uniforms" cost = 1 @@ -184,6 +189,7 @@ display_name = "Security - Basic Uniforms" description = "Select from a range of outfits available to all Security personnel." allowed_roles = list("Head of Security", "Warden", "Detective", "Security Officer") + show_roles = FALSE path = /obj/item/clothing/under/rank/security/corp sort_category = "Uniforms" cost = 1 @@ -229,6 +235,7 @@ display_name = "Security - Warden's Uniforms" description = "Select from a range of outfits available to Wardens." allowed_roles = list("Head of Security","Warden") + show_roles = FALSE path = /obj/item/clothing/under/rank/warden/corp sort_category = "Uniforms" cost = 1 @@ -250,6 +257,7 @@ display_name = "Security - Detective's Uniforms" description = "Select from a range of outfits available to all Detectives." allowed_roles = list("Head of Security","Detective") + show_roles = FALSE path = /obj/item/clothing/under/det/corporate sort_category = "Uniforms" cost = 1 @@ -266,6 +274,7 @@ display_name = "Security - Head's Uniforms" description = "Select from a range of outfits available to all Heads of Security." allowed_roles = list("Head of Security") + show_roles = FALSE path = /obj/item/clothing/under/rank/head_of_security/corp sort_category = "Uniforms" cost = 1 @@ -305,6 +314,7 @@ display_name = "Cargo - Quartermaster's Uniforms" description = "Select from a range of outfits available to all Quartermasters." allowed_roles = list("Quartermaster") + show_roles = FALSE path = /obj/item/clothing/under/rank/cargo/jeans sort_category = "Uniforms" cost = 1 @@ -336,6 +346,7 @@ display_name = "Cargo - Basic Uniforms" description = "Select from a range of outfits available to all Cargo personnel." allowed_roles = list("Cargo Technician","Shaft Miner","Quartermaster") + show_roles = FALSE path = /obj/item/clothing/under/rank/cargotech/jeans sort_category = "Uniforms" cost = 1 @@ -364,6 +375,7 @@ display_name = "Cargo - Miner's Uniforms" description = "Select from a range of outfits available to all Mining personnel." allowed_roles = list("Shaft Miner","Quartermaster") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_miner sort_category = "Uniforms" cost = 1 @@ -382,6 +394,7 @@ display_name = "Engineering - Chief Engineer's Uniforms" description = "Select from a range of outfits available to all Chief Engineers." allowed_roles = list("Chief Engineer") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_chiefengi sort_category = "Uniforms" cost = 1 @@ -406,6 +419,7 @@ display_name = "Engineering - Basic Uniforms" description = "Select from a range of outfits available to all Engineering personnel." allowed_roles = list("Chief Engineer","Engineer","Atmospheric Technician") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_engi sort_category = "Uniforms" cost = 1 @@ -433,6 +447,7 @@ display_name = "Engineering - Atmos Tech's Uniforms" description = "Select from a range of outfits available to all Atmospherics Technicians." allowed_roles = list("Chief Engineer","Atmospheric Technician") + show_roles = FALSE path = /obj/item/clothing/under/rank/atmospheric_technician/skirt sort_category = "Uniforms" cost = 1 @@ -452,6 +467,7 @@ display_name = "Medical - Basic Uniforms" description = "Select from a range of outfits available to all Medical personnel." allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_med sort_category = "Uniforms" cost = 1 @@ -484,6 +500,7 @@ display_name = "Medical - Chemist's Uniforms" description = "Select from a range of outfits available to all Chemists." allowed_roles = list("Chief Medical Officer","Chemist") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_chem sort_category = "Uniforms" cost = 1 @@ -503,6 +520,7 @@ display_name = "Medical - Paramedic's Uniforms" description = "Select from a range of outfits available to all Paramedics." allowed_roles = list("Medical Doctor","Chief Medical Officer","Paramedic") + show_roles = FALSE path = /obj/item/clothing/under/rank/paramedunidark sort_category = "Uniforms" cost = 1 @@ -525,6 +543,7 @@ display_name = "Medical - Chief Medical Officer's Uniforms" description = "Select from a range of outfits available to all Chief Medical Officers." allowed_roles = list("Chief Medical Officer") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_cmo sort_category = "Uniforms" cost = 2 @@ -553,6 +572,7 @@ display_name = "Science - Research Director's Uniforms" description = "Select from a range of outfits available to all Research Directors." allowed_roles = list("Research Director") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_rd_suit sort_category = "Uniforms" cost = 1 @@ -578,6 +598,7 @@ display_name = "Science - Basic Uniforms" description = "Select from a range of outfits available to all Science personnel." allowed_roles = list("Scientist","Research Director","Roboticist","Xenobiologist","Xenobotanist") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_science sort_category = "Uniforms" cost = 1 @@ -602,6 +623,7 @@ display_name = "Science - Roboticist's Uniforms" description = "Select from a range of outfits available to all Roboticists." allowed_roles = list("Research Director","Roboticist") + show_roles = FALSE path = /obj/item/clothing/under/rank/neo_robo sort_category = "Uniforms" cost = 1 diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno.dm b/code/modules/client/preference_setup/loadout/loadout_xeno.dm index 3c46d70eeca..317aa09d602 100644 --- a/code/modules/client/preference_setup/loadout/loadout_xeno.dm +++ b/code/modules/client/preference_setup/loadout/loadout_xeno.dm @@ -554,16 +554,19 @@ display_name = "warden belted cloak (Teshari)" path = /obj/item/clothing/suit/storage/teshari/beltcloak/jobs/wrdn allowed_roles = list("Head of Security","Warden") + sort_category = "Xenowear" /datum/gear/suit/dept/beltcloak/jani display_name = "janitor belted cloak (Teshari)" path = /obj/item/clothing/suit/storage/teshari/beltcloak/jobs/jani allowed_roles = list("Janitor") + sort_category = "Xenowear" /datum/gear/suit/dept/beltcloak/cmd display_name = "command belted cloak (Teshari)" path = /obj/item/clothing/suit/storage/teshari/beltcloak/jobs/command allowed_roles = list("Site Manager","Head of Personnel","Head of Security","Chief Engineer","Chief Medical Officer","Research Director") + sort_category = "Xenowear" /datum/gear/suit/cloak_hood display_name = "hooded cloak selection (Teshari)" diff --git a/code/modules/client/preference_setup/vore/09_misc.dm b/code/modules/client/preference_setup/vore/09_misc.dm index bde733d4c88..963354baea4 100644 --- a/code/modules/client/preference_setup/vore/09_misc.dm +++ b/code/modules/client/preference_setup/vore/09_misc.dm @@ -11,6 +11,7 @@ S["capture_crystal"] >> pref.capture_crystal S["auto_backup_implant"] >> pref.auto_backup_implant S["borg_petting"] >> pref.borg_petting + S["stomach_vision"] >> pref.stomach_vision /datum/category_item/player_setup_item/vore/misc/save_character(var/savefile/S) S["show_in_directory"] << pref.show_in_directory @@ -21,12 +22,21 @@ S["capture_crystal"] << pref.capture_crystal S["auto_backup_implant"] << pref.auto_backup_implant S["borg_petting"] << pref.borg_petting + S["stomach_vision"] << pref.stomach_vision /datum/category_item/player_setup_item/vore/misc/copy_to_mob(var/mob/living/carbon/human/character) if(pref.sensorpref > 5 || pref.sensorpref < 1) pref.sensorpref = 5 character.sensorpref = pref.sensorpref character.capture_crystal = pref.capture_crystal + //Vore Stomach Sprite Preference + character.stomach_vision = pref.stomach_vision + if((character && !istype(character,/mob/living/carbon/human/dummy)) && character.stomach_vision && !(VIS_CH_STOMACH in character.vis_enabled)) + character.plane_holder.set_vis(VIS_CH_STOMACH,TRUE) + character.vis_enabled += VIS_CH_STOMACH + else if((character && !istype(character,/mob/living/carbon/human/dummy)) && !character.stomach_vision && (VIS_CH_STOMACH in character.vis_enabled)) + character.plane_holder.set_vis(VIS_CH_STOMACH,FALSE) + character.vis_enabled -= VIS_CH_STOMACH /datum/category_item/player_setup_item/vore/misc/sanitize_character() pref.show_in_directory = sanitize_integer(pref.show_in_directory, 0, 1, initial(pref.show_in_directory)) @@ -36,6 +46,7 @@ pref.capture_crystal = sanitize_integer(pref.capture_crystal, 0, 1, initial(pref.capture_crystal)) pref.auto_backup_implant = sanitize_integer(pref.auto_backup_implant, 0, 1, initial(pref.auto_backup_implant)) pref.borg_petting = sanitize_integer(pref.borg_petting, 0, 1, initial(pref.borg_petting)) + pref.stomach_vision = sanitize_integer(pref.stomach_vision, 0, 1, initial(pref.stomach_vision)) /datum/category_item/player_setup_item/vore/misc/content(var/mob/user) . += "
" @@ -47,6 +58,7 @@ . += "Capture Crystal Preference: [pref.capture_crystal ? "Yes" : "No"]
" . += "Spawn With Backup Implant: [pref.auto_backup_implant ? "Yes" : "No"]
" . += "Allow petting as robot: [pref.borg_petting ? "Yes" : "No"]
" + . += "Enable Stomach Sprites: [pref.stomach_vision ? "Yes" : "No"]
" /datum/category_item/player_setup_item/vore/misc/OnTopic(var/href, var/list/href_list, var/mob/user) if(href_list["toggle_show_in_directory"]) @@ -84,4 +96,7 @@ else if(href_list["toggle_borg_petting"]) pref.borg_petting = pref.borg_petting ? 0 : 1; return TOPIC_REFRESH + else if(href_list["toggle_stomach_vision"]) + pref.stomach_vision = pref.stomach_vision ? 0 : 1; + return TOPIC_REFRESH return ..(); diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm index ee3363dff59..f1cfc4413ab 100644 --- a/code/modules/client/preferences_vr.dm +++ b/code/modules/client/preferences_vr.dm @@ -7,6 +7,7 @@ var/capture_crystal = 1 //Whether or not someone is able to be caught with capture crystals var/auto_backup_implant = FALSE //Whether someone starts with a backup implant or not. var/borg_petting = TRUE //Whether someone can be petted as a borg or not. + var/stomach_vision = TRUE //Whether or not someone can view stomach sprites var/job_talon_high = 0 var/job_talon_med = 0 diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index ebf17c84b61..cd26d6da781 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -210,6 +210,13 @@ if(Adjacent(user) && src.loc == user) . += "You are currently facing [dir2text(user.dir)]. The micro beacon is [gps.tracking ? "on" : "off"]." var/TB = src.loc.loc + if(istype(TB, /turf/)) //no point returning light level if we're not on a turf (might be *in* someone!) + var/turf/TL = TB + var/light_level = TL.get_lumcount() + if(light_level) + . += "Light Level: [TL.get_lumcount()]" + else + . += "It's too dark to see the light level!" if(istype(TB, /turf/simulated)) //no point returning atmospheric data from unsimulated tiles (they don't track pressure anyway, only temperature) var/turf/simulated/T = TB var/datum/gas_mixture/env = T.return_air() diff --git a/code/modules/clothing/spacesuits/void/ert_vr.dm b/code/modules/clothing/spacesuits/void/ert_vr.dm index 8b147a4dc33..9d98e3273cb 100644 --- a/code/modules/clothing/spacesuits/void/ert_vr.dm +++ b/code/modules/clothing/spacesuits/void/ert_vr.dm @@ -171,6 +171,7 @@ sprite_sheets = list( SPECIES_HUMAN = 'icons/inventory/suit/mob_vr.dmi', SPECIES_TAJ = 'icons/inventory/suit/mob_vr_tajaran.dmi', + SPECIES_LLEILL = 'icons/inventory/suit/mob_vr_tajaran.dmi', SPECIES_SKRELL = 'icons/inventory/suit/mob_vr_skrell.dmi', SPECIES_UNATHI = 'icons/inventory/suit/mob_vr_unathi.dmi', SPECIES_XENOHYBRID = 'icons/inventory/suit/mob_vr_unathi.dmi', @@ -201,13 +202,15 @@ SPECIES_NEVREAN = 'icons/inventory/suit/item_vr.dmi', SPECIES_RAPALA = 'icons/inventory/suit/item_vr.dmi', SPECIES_ALRAUNE = 'icons/inventory/suit/item_vr.dmi', - SPECIES_ZADDAT = 'icons/inventory/suit/item_vr.dmi' + SPECIES_ZADDAT = 'icons/inventory/suit/item_vr.dmi', + SPECIES_LLEILL = 'icons/inventory/suit/item_vr.dmi' ) /obj/item/clothing/head/helmet/space/void/responseteam sprite_sheets = list( SPECIES_HUMAN = 'icons/inventory/head/mob_vr.dmi', SPECIES_TAJ = 'icons/inventory/head/mob_vr_tajaran.dmi', + SPECIES_LLEILL = 'icons/inventory/suit/mob_vr_tajaran.dmi', SPECIES_SKRELL = 'icons/inventory/head/mob_vr_skrell.dmi', SPECIES_UNATHI = 'icons/inventory/head/mob_vr_unathi.dmi', SPECIES_XENOHYBRID = 'icons/inventory/head/mob_vr_unathi.dmi', @@ -238,5 +241,6 @@ SPECIES_NEVREAN = 'icons/inventory/head/item_vr.dmi', SPECIES_RAPALA = 'icons/inventory/head/item_vr.dmi', SPECIES_ALRAUNE = 'icons/inventory/head/item_vr.dmi', - SPECIES_ZADDAT = 'icons/inventory/head/item_vr.dmi' + SPECIES_ZADDAT = 'icons/inventory/head/item_vr.dmi', + SPECIES_LLEILL = 'icons/inventory/suit/item_vr.dmi' ) diff --git a/code/modules/examine/descriptions/armor.dm b/code/modules/examine/descriptions/armor.dm index b1cf96af6ed..e83c739473d 100644 --- a/code/modules/examine/descriptions/armor.dm +++ b/code/modules/examine/descriptions/armor.dm @@ -43,7 +43,6 @@ else return "It's difficult to tell how much it'll influence your speed." - /obj/item/clothing/get_description_info() var/armor_stats = description_info + "\
" @@ -70,28 +69,18 @@ if(flags & AIRTIGHT) armor_stats += "It is airtight. \n" - if(min_pressure_protection == 0 && max_pressure_protection >= WARNING_HIGH_PRESSURE) //0 to 325 - armor_stats += "Wearing this will protect you from the vacuum of space and from high pressures. \n" - else if(min_pressure_protection <= WARNING_LOW_PRESSURE && max_pressure_protection >= WARNING_HIGH_PRESSURE) //50 to 325 - armor_stats += "Wearing this will protect you from both low and high pressures, but not the vacuum of space. \n" - else if(min_pressure_protection == 0) - armor_stats += "Wearing this will protect you from the vacuum of space. \n" - else if(min_pressure_protection <= WARNING_LOW_PRESSURE) //50 or below - armor_stats += "Wearing this will protect you from low pressures, but not the vacuum of space. \n" - else if(max_pressure_protection >= WARNING_HIGH_PRESSURE) //325 or higher - armor_stats += "Wearing this will protect you from high pressures. \n" + if(min_pressure_protection != null) + armor_stats += "It is rated for pressures as low as [min_pressure_protection] kPa. \n" + if(max_pressure_protection) + armor_stats += "It is rated for pressures as high as [max_pressure_protection] kPa. \n" if(flags & THICKMATERIAL) //stops syringes armor_stats += "The material is exceptionally thick. \n" - if(max_heat_protection_temperature >= FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE && min_cold_protection_temperature <= SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE) //30000 or higher and as low as 2 - armor_stats += "It provides exceptional protection from extremely high and low temperatures alike. \n" - else if(max_heat_protection_temperature >= SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE && min_cold_protection_temperature <= SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE) //5000 or above, but less than 30000 - armor_stats += "It provides very good protection against hazardous temperatures at both extremes, but may not be sufficient for very high-intensity situations. \n" - else if(max_heat_protection_temperature >= FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE) //30000 or above - armor_stats += "It provides exceptional protection from extremely high temperatures. \n" - else if(min_cold_protection_temperature <= SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE) //2 or less - armor_stats += "It provides exceptional protection against very low temperatures. \n" + if(min_cold_protection_temperature) + armor_stats += "It is rated for temperatures as low as [min_cold_protection_temperature] Kelvin. \n" + if(max_heat_protection_temperature) + armor_stats += "It is rated for temperatures as high as [max_heat_protection_temperature] Kelvin. \n" var/list/covers = list() var/list/slots = list() diff --git a/code/modules/food/food/drinks.dm b/code/modules/food/food/drinks.dm index 84a651ba3c1..9c2944df265 100644 --- a/code/modules/food/food/drinks.dm +++ b/code/modules/food/food/drinks.dm @@ -179,7 +179,16 @@ return ..() /obj/item/weapon/reagent_containers/food/drinks/self_feed_message(var/mob/user) - to_chat(user, "You swallow a gulp from \the [src].") + if(amount_per_transfer_from_this == volume) //I wanted to use a switch, but switch statements can't use vars and the maximum volume of containers varies + to_chat(user, "You knock back the entire [src] in one go!") + else if(amount_per_transfer_from_this <= 4) //below the standard 5 + to_chat(user, "You take a modest sip from \the [src].") + else if(amount_per_transfer_from_this <= 10) //the standard five to a bit more + to_chat(user, "You swallow a gulp from \the [src].") + else if(amount_per_transfer_from_this <= 30) + to_chat(user, "You take a long drag from \the [src].") + else //default message as a fallback + to_chat(user, "You swallow a gulp from \the [src].") /obj/item/weapon/reagent_containers/food/drinks/feed_sound(var/mob/user) playsound(src, 'sound/items/drink.ogg', rand(10, 50), 1) diff --git a/code/modules/food/food/drinks/bottle.dm b/code/modules/food/food/drinks/bottle.dm index 2b25369016b..5c2889c5a96 100644 --- a/code/modules/food/food/drinks/bottle.dm +++ b/code/modules/food/food/drinks/bottle.dm @@ -180,6 +180,25 @@ var/obj/item/weapon/broken_bottle/B = smash(target.loc, target) user.put_in_active_hand(B) +/obj/item/weapon/reagent_containers/food/drinks/bottle/verb/spin_bottle() + set name = "Spin The Bottle" + set category = "Object" + set src in view(1) + + if(isobserver(usr) || usr.stat) + return + + if(!isturf(src.loc)) + to_chat(usr, "\The [src] needs to be on the floor to spin.") + return + + var/spin_rotation = (rand(0,359)) + usr.visible_message("\The [usr] spins \the [src]!","You spin \the [src]!") + SpinAnimation(3,10) + spawn(30) + icon_rotation = spin_rotation + update_transform() + //Keeping this here for now, I'll ask if I should keep it here. /obj/item/weapon/broken_bottle name = "Broken Bottle" diff --git a/code/modules/hydroponics/seedtypes/flowers.dm b/code/modules/hydroponics/seedtypes/flowers.dm index e71e707e636..e6c2e86f597 100644 --- a/code/modules/hydroponics/seedtypes/flowers.dm +++ b/code/modules/hydroponics/seedtypes/flowers.dm @@ -65,7 +65,7 @@ set_trait(TRAIT_MATURATION,7) set_trait(TRAIT_PRODUCTION,5) set_trait(TRAIT_YIELD,5) - set_trait(TRAIT_PRODUCT_ICON,"lavender") + set_trait(TRAIT_PRODUCT_ICON,"flower6") set_trait(TRAIT_PRODUCT_COLOUR,"#B57EDC") set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") set_trait(TRAIT_PLANT_ICON,"flower4") diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm index f38420fd376..2927b0ddbaf 100644 --- a/code/modules/instruments/songs/editor.dm +++ b/code/modules/instruments/songs/editor.dm @@ -191,7 +191,7 @@ stop_playing() else if(href_list["setlinearfalloff"]) - var/amount = tgui_input_number(usr, "Set linear sustain duration in seconds", "Linear Sustain Duration") + var/amount = tgui_input_number(usr, "Set linear sustain duration in seconds", "Linear Sustain Duration", round_value=FALSE) if(!isnull(amount)) set_linear_falloff_duration(round(amount * 10, world.tick_lag)) diff --git a/code/modules/media/mediamanager.dm b/code/modules/media/mediamanager.dm index a03cfaf35de..adc522546f6 100644 --- a/code/modules/media/mediamanager.dm +++ b/code/modules/media/mediamanager.dm @@ -59,7 +59,7 @@ set_new_volume(usr) /client/proc/set_new_volume(var/mob/user) - if(!QDELETED(src.media) || !istype(src.media)) + if(QDELETED(src.media) || !istype(src.media)) to_chat(user, "You have no media datum to change, if you're not in the lobby tell an admin.") return var/value = input(usr, "Choose your Jukebox volume.", "Jukebox volume", media.volume) diff --git a/code/modules/mob/language/station_vr.dm b/code/modules/mob/language/station_vr.dm index df525dbe382..797e5778c16 100644 --- a/code/modules/mob/language/station_vr.dm +++ b/code/modules/mob/language/station_vr.dm @@ -143,6 +143,21 @@ /datum/language/echosong/scramble(var/input, var/list/known_languages) return stars(input) +/datum/language/lleill + name = LANGUAGE_LLEILL + desc = "An ancient, gutteral language involving a lot of spitting." + speech_verb = "speaks" + ask_verb = "ponders" + exclaim_verb = "calls" + colour = "echosong" + key = "L" + syllables = list( + "llyn", "bren", "gwyn", "gwyr", "ddys", "dath", "llio", "cym", "ddrai", "ffyr", "lle", "dy", "eto", "uno", "dydno", "llego", "bryth", "ffair", + "ynys", "ed", "fore", "oe", "hen", "wladd", "ty", "nha", "dwy", "mae", "dros", "pob", "ia", "wyll", "gwdd", "fi" + ) + machine_understands = FALSE + flags = WHITELISTED + /datum/language/echosong/broadcast(var/mob/living/speaker, var/message, var/speaker_mask) log_say("(INAUDIBLE) [message]", speaker) speaker.say_signlang(format_message(message), pick(signlang_verb), pick(signlang_verb_understood), src, 2) diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index ffbd20a6f12..7c9870a79ca 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -15,6 +15,7 @@ var/cleaning = 0 var/wet_floors = 0 var/spray_blood = 0 + var/blood = 1 var/list/target_types = list() /mob/living/bot/cleanbot/New() @@ -138,7 +139,8 @@ if(prob(20)) custom_emote(2, "begins to clean up \the [loc]") if(do_after(src, cleantime * cTimeMult)) - clean_blood() + if(blood) + clean_blood() if(istype(loc, /turf/simulated)) var/turf/simulated/T = loc T.dirt = 0 @@ -186,6 +188,7 @@ data["on"] = on data["open"] = open data["locked"] = locked + data["blood"] = blood data["patrol"] = will_patrol data["vocal"] = vocal @@ -207,6 +210,9 @@ else turn_on() . = TRUE + if("blood") + blood = !blood + . = TRUE if("patrol") will_patrol = !will_patrol patrol_path = null diff --git a/code/modules/mob/living/bot/edCLNbot.dm b/code/modules/mob/living/bot/edCLNbot.dm index 3a8265f3521..0d976fef89f 100644 --- a/code/modules/mob/living/bot/edCLNbot.dm +++ b/code/modules/mob/living/bot/edCLNbot.dm @@ -213,4 +213,4 @@ user.drop_item() qdel(W) user.drop_from_inventory(src) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 38427453e3a..712f549626e 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -7,6 +7,17 @@ has_huds = TRUE //We do have HUDs (like health, wanted, status, not inventory slots) + + var/vore_capacity = 3 + var/vore_capacity_ex = list("stomach" = 3, "taur belly" = 3) + var/vore_fullness_ex = list("stomach" = 0, "taur belly" = 0) + var/vore_icon_bellies = list("stomach", "taur belly") + var/struggle_anim_stomach = FALSE + var/struggle_anim_taur = FALSE + var/vore_sprite_color = list("stomach" = "#FFFFFF", "taur belly" = "#FFFFFF") + var/vore_sprite_multiply = list("stomach" = TRUE, "taur belly" = TRUE) + var/vore_fullness = 0 + var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us. var/obj/item/weapon/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call. var/last_push_time //For human_attackhand.dm, keeps track of the last use of disarm @@ -19,6 +30,7 @@ var/can_defib = 1 //Horrible damage (like beheadings) will prevent defibbing organics. var/active_regen = FALSE //Used for the regenerate proc in human_powers.dm var/active_regen_delay = 300 + var/list/teleporters = list() //Used for lleill abilities /mob/living/carbon/human/Initialize(mapload, var/new_species = null) if(!dna) @@ -1777,3 +1789,19 @@ /mob/living/carbon/human/get_mob_riding_slots() return list(back, head, wear_suit) + +/mob/living/carbon/human/proc/update_fullness() + var/list/new_fullness = list() + vore_fullness = 0 + for(var/belly_class in vore_icon_bellies) + new_fullness[belly_class] = 0 + for(var/obj/belly/B as anything in vore_organs) + new_fullness[B.belly_sprite_to_affect] += B.GetFullnessFromBelly() + for(var/belly_class in vore_icon_bellies) + new_fullness[belly_class] /= size_multiplier //Divided by pred's size so a macro mob won't get macro belly from a regular prey. + new_fullness[belly_class] = round(new_fullness[belly_class], 1) // Because intervals of 0.25 are going to make sprite artists cry. + vore_fullness_ex[belly_class] = min(vore_capacity_ex[belly_class], new_fullness[belly_class]) + vore_fullness += new_fullness[belly_class] + vore_fullness = min(vore_capacity, vore_fullness) + update_vore_belly_sprite() + update_vore_tail_sprite() diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 692f121ba82..c531ef5086f 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -192,6 +192,15 @@ compiled_vis |= VIS_CH_VANTAG //VOREStation Add End + //Vore Stomach addition start. This goes here. + if(stomach_vision && !(VIS_CH_STOMACH in vis_enabled)) + plane_holder.set_vis(VIS_CH_STOMACH,TRUE) + compiled_vis += VIS_CH_STOMACH + else if(!stomach_vision && (VIS_CH_STOMACH in vis_enabled)) + plane_holder.set_vis(VIS_CH_STOMACH,FALSE) + compiled_vis -= VIS_CH_STOMACH + //Vore Stomach addition end + if(!compiled_vis.len && !vis_enabled.len) return //Nothin' doin'. diff --git a/code/modules/mob/living/carbon/human/human_species_vr.dm b/code/modules/mob/living/carbon/human/human_species_vr.dm index a747280df8d..dd3134420a6 100644 --- a/code/modules/mob/living/carbon/human/human_species_vr.dm +++ b/code/modules/mob/living/carbon/human/human_species_vr.dm @@ -31,3 +31,6 @@ /mob/living/carbon/human/altevian/New(var/new_loc) ..(new_loc, SPECIES_ALTEVIAN) + +/mob/living/carbon/human/lleill/New(var/new_loc) + ..(new_loc, SPECIES_LLEILL) diff --git a/code/modules/mob/living/carbon/human/species/lleill/lleill.dm b/code/modules/mob/living/carbon/human/species/lleill/lleill.dm new file mode 100644 index 00000000000..48146df8e8a --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/lleill/lleill.dm @@ -0,0 +1,178 @@ +/datum/species/lleill + + name = SPECIES_LLEILL + name_plural = "Lleill" + icobase = 'icons/mob/human_races/r_lleill.dmi' + deform = 'icons/mob/human_races/r_lleill.dmi' + color_mult = 1 + tail = "tail" + icobase_tail = 1 + blurb = "A species that appears to originate somewhere in redspace. Their forms are not consistent, \ + they do not care for consistency, and are working under a constant never-ending drive to improve themselves \ + and the world around them. With little care for whether the world itself wants to change." //PLACEHOLDER + + blood_color = "#FFFFFF" + blood_name = "glamour" + flesh_color = "#FFFFFF" + reagent_tag = IS_LLEILL + + num_alternate_languages = 3 + species_language = LANGUAGE_LLEILL + language = LANGUAGE_LLEILL + name_language = LANGUAGE_LLEILL + + flags = NO_SCAN | NO_MINOR_CUT | NO_INFECT | NO_HALLUCINATION + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_WHITELIST_SELECTABLE + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_SKIN_COLOR | HAS_EYE_COLOR | HAS_UNDERWEAR + + max_age = 200 + + economic_modifier = 15 + + digi_allowed = TRUE + + //Specific abilities + + darksight = 10 //Can see in dark + + burn_mod = 0.25 //Very resistant to fire + pain_mod = 0.25 //Whilst not resistant to brute or stunning, they are quite resistant to pain, making them tanky in their own way. + + warning_low_pressure = 50 + hazard_low_pressure = -1 + warning_high_pressure = 300 + hazard_high_pressure = 10000 //Can be killed by pressure but you're going to need a hell of a lot + + cold_level_1 = -1 //Safe in space + cold_level_2 = -1 + cold_level_3 = -1 + + heat_level_1 = 1500 //Very resiliant to heat + heat_level_2 = 2500 + heat_level_3 = 5000 + + can_space_freemove = TRUE //Have no issue moving through space. + can_zero_g_move = TRUE + + chem_strength_alcohol = 0 //Can't get drunk + + oxy_mod = 0.25 //Suffocates very slowly, but does ultimately need to breathe, will outpace heal most oxygen damage. + poison_type = null //Not harmed by phoron. + water_breather = TRUE + + var/list/valid_transform_species = list( + "Human", "Unathi", "Tajara", "Skrell", + "Diona", "Teshari", "Monkey","Sergal", + "Akula","Nevrean","Zorren", + "Fennec", "Vulpkanin", "Vasilissan", + "Rapala", "Neaera", "Stok", "Farwa", "Sobaka", + "Wolpin", "Saru", "Sparra", "Lleill") + + // Looks like a lot but the majority of these are just to change their appearance. + inherent_verbs = list( + /mob/living/carbon/human/proc/lleill_select_colour, + /mob/living/carbon/human/proc/lleill_select_shape, + /mob/living/carbon/human/proc/shapeshifter_select_hair, + /mob/living/carbon/human/proc/shapeshifter_select_hair_colors, + /mob/living/carbon/human/proc/shapeshifter_select_gender, + /mob/living/carbon/human/proc/shapeshifter_select_wings, + /mob/living/carbon/human/proc/shapeshifter_select_tail, + /mob/living/carbon/human/proc/shapeshifter_select_ears, + /mob/living/proc/set_size, + /mob/living/carbon/human/proc/lleill_invisibility, + /mob/living/carbon/human/proc/lleill_transmute, + /mob/living/carbon/human/proc/lleill_rings) + + //organs, going with just the basics for now + + has_organ = list( + O_HEART = /obj/item/organ/internal/heart, + O_LUNGS = /obj/item/organ/internal/lungs, + O_VOICE = /obj/item/organ/internal/voicebox, + O_LIVER = /obj/item/organ/internal/liver, + O_KIDNEYS = /obj/item/organ/internal/kidneys, + O_BRAIN = /obj/item/organ/internal/brain, + O_APPENDIX = /obj/item/organ/internal/appendix, + O_SPLEEN = /obj/item/organ/internal/spleen, + O_EYES = /obj/item/organ/internal/eyes, + O_STOMACH = /obj/item/organ/internal/stomach, + O_INTESTINE = /obj/item/organ/internal/intestine + ) + + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/lleill), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + + base_species = SPECIES_LLEILL + +// Shapeshifters have some behaviour that doesn't play well with this species so I have taken the main parts needed for here. + +/datum/species/lleill/get_valid_shapeshifter_forms(var/mob/living/carbon/human/H) + return valid_transform_species + +/datum/species/lleill/get_icobase(var/mob/living/carbon/human/H, var/get_deform) + if(!H) return ..(null, get_deform) + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!S || S.type == src.type) return ..(H, get_deform) + return S.get_icobase(H,get_deform) + +/datum/species/lleill/get_race_key(var/mob/living/carbon/human/H) + return "[..()]-[wrapped_species_by_ref["\ref[H]"]]" + +/datum/species/lleill/get_bodytype(var/mob/living/carbon/human/H) + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!H || !S) return ..() + if(S.type == src.type) return ..(H) + return S.get_bodytype(H) + +/datum/species/lleill/get_blood_mask(var/mob/living/carbon/human/H) + if(!H) return ..() + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!S || S.name == src.name) + return ..() + return S?.get_blood_mask(H) + +/datum/species/lleill/get_damage_mask(var/mob/living/carbon/human/H) + if(!H) return ..() + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!S || S.name == src.name) + return ..() + return S?.get_damage_mask(H) + +/datum/species/lleill/get_damage_overlays(var/mob/living/carbon/human/H) + if(!H) return ..() + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!S || S.name == src.name) + return ..() + return S?.get_damage_overlays(H) + +/datum/species/lleill/get_tail(var/mob/living/carbon/human/H) + if(!H) return ..() + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!S || S.name == src.name) + return ..() + return S?.get_tail(H) + +/datum/species/lleill/get_tail_animation(var/mob/living/carbon/human/H) + if(!H) return ..() + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!S || S.name == src.name) + return ..() + return S?.get_tail_animation(H) + +/datum/species/lleill/get_tail_hair(var/mob/living/carbon/human/H) + if(!H) return ..() + var/datum/species/S = GLOB.all_species[wrapped_species_by_ref["\ref[H]"]] + if(!S || S.name == src.name) + return ..() + return S?.get_tail_hair(H) diff --git a/code/modules/mob/living/carbon/human/species/lleill/lleill_abilities.dm b/code/modules/mob/living/carbon/human/species/lleill/lleill_abilities.dm new file mode 100644 index 00000000000..97ee036c8a7 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/lleill/lleill_abilities.dm @@ -0,0 +1,179 @@ +/datum/power/lleill + +// Simple ability to become invisible. Does not phase you out of the world, you can still interact with things and can not pass through walls. +// Essentially the same as traitor cloaking, using the same proc for it. + +/datum/power/lleill/invisibility + name = "Invisibility" + desc = "Change your appearance to match your surroundings, becoming completely invisible to the naked eye." + verbpath = /mob/living/carbon/human/proc/lleill_invisibility + +/mob/living/carbon/human/proc/lleill_invisibility() + set name = "Invisibility" + set desc = "Change your appearance to match your surroundings, becoming completely invisible to the naked eye." + set category = "Abilities" + + if(stat) + to_chat(src, "You can't go invisible when weakened like this.") + return + + if(!cloaked) + cloak() + to_chat(src, "Your fur shimmers and shifts around you, hiding you from the naked eye.") + else + uncloak() + to_chat(src, "The brustling of your fur settles down and you become visible once again.") + +/mob/living/carbon/human/proc/lleill_select_shape() + + set name = "Select Body Shape" + set category = "Abilities" + + if(stat || world.time < last_special) + return + + last_special = world.time + 50 + + var/new_species = null + new_species = tgui_input_list(usr, "Please select a species to emulate.", "Shapeshifter Body", species.get_valid_shapeshifter_forms(src)) + + if(!new_species || !GLOB.all_species[new_species] || wrapped_species_by_ref["\ref[src]"] == new_species) + return + lleill_change_shape(new_species) + +/mob/living/carbon/human/proc/lleill_change_shape(var/new_species = null) + if(!new_species) + return + + wrapped_species_by_ref["\ref[src]"] = new_species + dna.base_species = new_species + species.base_species = new_species + visible_message("\The [src] shifts and contorts, taking the form of \a [new_species]!") + regenerate_icons() + +/mob/living/carbon/human/proc/lleill_select_colour() + + set name = "Select Body Colour" + set category = "Abilities" + + if(stat || world.time < last_special) + return + + last_special = world.time + 50 + + var/new_skin = input(usr, "Please select a new body color.", "Shapeshifter Colour", rgb(r_skin, g_skin, b_skin)) as null|color + if(!new_skin) + return + lleill_set_colour(new_skin) + +/mob/living/carbon/human/proc/lleill_set_colour(var/new_skin) + + r_skin = hex2num(copytext(new_skin, 2, 4)) + g_skin = hex2num(copytext(new_skin, 4, 6)) + b_skin = hex2num(copytext(new_skin, 6, 8)) + r_synth = r_skin + g_synth = g_skin + b_synth = b_skin + + for(var/obj/item/organ/external/E in organs) + E.sync_colour_to_human(src) + + regenerate_icons() + +/mob/living/carbon/human/proc/lleill_transmute() + set name = "Transmute Object" + set desc = "Convert an object into a piece of glamour." + set category = "Abilities" + + var/list/transmute_list = list( + /obj/item/weapon/potion_material/glamour_transparent, + /obj/item/weapon/potion_material/glamour_shrinking, + /obj/item/weapon/potion_material/glamour_twinkling, + /obj/item/weapon/potion_material/glamour_shard, + /obj/item/capture_crystal/glamour, + /obj/item/glamour_face, + /obj/item/device/universal_translator/glamour + ) + + if(stat) + to_chat(src, "You can't go do that when weakened like this.") + return + + var/obj/item/I = get_active_hand() + if(!I) + to_chat(src, "You have no item in your active hand.") + return + + var/obj/item/transmute_product = tgui_input_list(src, "Choose an glamour to transmute the item into:", "Transmutation", transmute_list) + if(!get_active_hand(I)) + to_chat(src, "The item is no longer in your hands.") + return + else + visible_message("\The [src] begins to change the form of \the [I].") + if(!do_after(usr, 10 SECONDS, I, exclusive = TASK_USER_EXCLUSIVE)) + visible_message("\The [src] leaves \the [I] in its original form.") + return 0 + visible_message("\The [src] transmutes \the [I] into a \the [transmute_product.name].") + drop_item(I) + qdel(I) + var/spawnloc = get_turf(usr) + var/obj/item/N = new transmute_product(spawnloc) + put_in_active_hand(N) + +/mob/living/carbon/human/proc/lleill_rings() + set name = "Place/Use Rings" + set desc = "Place or teleport to a glamour ring." + set category = "Abilities" + + if(stat) + to_chat(src, "You can't go do that when weakened like this.") + return + if(buckled) + to_chat(src,"You can't do that when restrained.") + + var/r_action = tgui_alert(src, "What would you like to do with your rings?", "Actions", list("Spawn New Ring", "Teleport to Ring", "Cancel")) + if(r_action == "Cancel") + return + if(r_action == "Spawn New Ring") + if(!do_after(src, 10 SECONDS, src, exclusive = TASK_USER_EXCLUSIVE)) + src.visible_message("\The [src] begins to form white rings on the ground.") + return 0 + to_chat(src, "You place a new glamour ring at your feet.") + var/spawnloc = get_turf(src) + var/obj/structure/glamour_ring/R = new(spawnloc) + src.teleporters |= R + if(r_action == "Teleport to Ring") + if(!src.teleporters.len) + to_chat(src, "You need to place rings to teleport to them.") + return + else + var/obj/structure/glamour_ring/R = tgui_input_list(src, "Where do you wish to teleport?", "Teleport", src.teleporters) + + var/datum/effect/effect/system/spark_spread/spk + spk = new(src) + + var/T = get_turf(src) + spk.set_up(5, 0, src) + spk.attach(src) + playsound(T, "sparks", 50, 1) + anim(T,src,'icons/mob/mob.dmi',,"phaseout",,src.dir) + + var/S = get_turf(R) + src.forceMove(S) + + spk.start() + playsound(S, 'sound/effects/phasein.ogg', 25, 1) + playsound(S, 'sound/effects/sparks2.ogg', 50, 1) + anim(S,src,'icons/mob/mob.dmi',,"phasein",,src.dir) + spk.set_up(5, 0, src) + spk.attach(src) + + //Would be fun to eat people standing on your ring... + if(can_be_drop_pred && vore_selected) + var/list/target_list = src.living_mobs(0) + if(target_list.len) + for(var/mob/living/M in target_list) + if(M.devourable && M.can_be_drop_prey) + M.forceMove(vore_selected) + to_chat(M,"In a bright flash of white light, you suddenly find yourself trapped in \the [src]'s [vore_selected.name]!") + diff --git a/code/modules/mob/living/carbon/human/species/lleill/lleill_items.dm b/code/modules/mob/living/carbon/human/species/lleill/lleill_items.dm new file mode 100644 index 00000000000..8b724fe8b6b --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/lleill/lleill_items.dm @@ -0,0 +1,198 @@ +//Transparent Glamour (invisibility potion) + +/obj/item/weapon/potion_material/glamour_transparent + name = "transparent glamour" + desc = "A shard of hardened white crystal that is clearly translucent." + icon = 'icons/obj/glamour.dmi' + icon_state = "transparent" + base_reagent = /obj/item/weapon/potion_base/aqua_regia + product_potion = /obj/item/weapon/reagent_containers/glass/bottle/potion/invisibility + +/obj/item/weapon/reagent_containers/glass/bottle/potion/invisibility + name = "transparent potion" + desc = "A small white potion, the clear liquid inside can barely be seen at all." + prefill = list("transparent glamour" = 1) + +/datum/reagent/glamour_transparent + name = "Clear Glamour" + id = "transparent glamour" + description = "This material is from somewhere else, it can barely be seen by the naked eye." + taste_description = "nothingness" + reagent_state = LIQUID + color = "#ffffff" + scannable = 1 + +/datum/reagent/glamour_transparent/affect_blood(var/mob/living/carbon/target, var/removed) + if(!target.cloaked) + target.visible_message("\The [target] vanishes from sight.") + target.cloak() + target.bloodstr.clear_reagents() //instantly clears reagents afterwards + target.ingested.clear_reagents() + target.touching.clear_reagents() + spawn(600) + if(target.cloaked) + target.uncloak() + target.visible_message("\The [target] appears as if from thin air.") + +//Shrinking Glamour (scaling potion) + +/obj/item/weapon/potion_material/glamour_shrinking + name = "shrinking glamour" + desc = "A soft clump of white material that seems to shrink at your touch." + icon = 'icons/obj/glamour.dmi' + icon_state = "shrinking" + base_reagent = /obj/item/weapon/potion_base/aqua_regia + product_potion = /obj/item/weapon/reagent_containers/glass/bottle/potion/scaling + +/obj/item/weapon/reagent_containers/glass/bottle/potion/scaling + name = "scaling potion" + desc = "A small white potion, the clear liquid inside can barely be seen at all." + prefill = list("scaling glamour" = 1) + +/datum/reagent/glamour_scaling + name = "Scaling Glamour" + id = "scaling glamour" + description = "This material is from somewhere else, it appears to change volumes readily at a glance." + taste_description = "difficult to discern" + reagent_state = LIQUID + color = "#ffffff" + scannable = 1 + +/datum/reagent/glamour_scaling/affect_blood(var/mob/living/carbon/target, var/removed) + if(!(/mob/living/proc/set_size in target.verbs)) + to_chat(target, "You feel as though you could change size at any moment.") + target.verbs |= /mob/living/proc/set_size + target.bloodstr.clear_reagents() //instantly clears reagents afterwards + target.ingested.clear_reagents() + target.touching.clear_reagents() + +//Twinkling Glamour (Sparkling potion - Gives darksight) + +/obj/item/weapon/potion_material/glamour_twinkling + name = "twinkling glamour" + desc = "A sheet of white material that twinkles on its own accord." + icon = 'icons/obj/glamour.dmi' + icon_state = "twinkling" + base_reagent = /obj/item/weapon/potion_base/aqua_regia + product_potion = /obj/item/weapon/reagent_containers/glass/bottle/potion/darksight + +/obj/item/weapon/reagent_containers/glass/bottle/potion/darksight + name = "twinling potion" + desc = "A small white potion, the thin white liquid inside twinkles brightly." + prefill = list("twinkling glamour" = 1) + +/datum/reagent/glamour_twinkling + name = "Twinkling Glamour" + id = "twinkling glamour" + description = "This material is from somewhere else, it appears to be twinkling." + taste_description = "bright" + reagent_state = LIQUID + color = "#ffffff" + scannable = 1 + +/datum/reagent/glamour_twinkling/affect_blood(var/mob/living/carbon/human/target, var/removed) + if(target.species.darksight < 10) + to_chat(target, "You can suddenly see much better than before.") + target.species.darksight = 10 + if(target.disabilities & NEARSIGHTED) + target.disabilities &= ~NEARSIGHTED + to_chat(target, "Everything is much less blurry.") + target.bloodstr.clear_reagents() //instantly clears reagents afterwards + target.ingested.clear_reagents() + target.touching.clear_reagents() + +//Glamour Cell (variant of capture crystal) + +/obj/item/capture_crystal/glamour + name = "glamour cell" + desc = "A large but light round ball of glamour that glows from somewhere within." + icon = 'icons/obj/glamour.dmi' + +/obj/item/capture_crystal/glamour/animate_action(atom/thing) + var/image/coolanimation = image('icons/obj/glamour.dmi', null, "animation") + coolanimation.plane = PLANE_LIGHTING_ABOVE + thing.overlays += coolanimation + sleep(14) + thing.overlays -= coolanimation + +//Face of Glamour (creates a clone of a target) + +/obj/item/glamour_face + name = "face of glamour" + desc = "A piece of glamour that is formed vaguely into the shape of a face." + icon = 'icons/obj/glamour.dmi' + icon_state = "face" + var/mob/living/homunculus = 0 + +/obj/item/glamour_face/attack_self(var/mob/user) + if(!homunculus) + var/list/targets = list() + for(var/mob/living/carbon/human/M in mob_list) + if(M.z != user.z || get_dist(user,M) > 10) + continue + if(istype(M) && M.resleeve_lock && M.ckey != M.resleeve_lock) + continue + targets |= M + + if(!targets) + to_chat(user, "There are no appropriate targets in range.") + return + + var/mob/living/carbon/human/chosen_target = tgui_input_list(user, "Which target do you wish to create a homunculus of?", "homunculus", targets) + + var/spawnloc = get_turf(user) + var/mob/living/simple_mob/homunculus/H = new(spawnloc) + H.name = chosen_target.name + H.desc = chosen_target.desc + H.icon = chosen_target.icon + H.icon_state = chosen_target.icon_state + H.copy_overlays(chosen_target, TRUE) + H.resize(chosen_target.size_multiplier, ignore_prefs = TRUE) + homunculus = H + H.owner = src + return + if(homunculus) + var/mob/living/simple_mob/homunculus/H = homunculus + var/h_action = tgui_alert(user, "What would you like to do with your homunculus?", "Actions", list("Recall", "Speak Through", "Cancel")) + if(h_action == "Cancel") + return + if(h_action == "Recall") + H.visible_message("\The [H] returns to the face.") + qdel(H) + homunculus = 0 + return + if(h_action == "Speak Through") + var/words_to_say = tgui_input_text(user, "What should the homunculus say:", "Speak Through") + H.say(words_to_say) + return + + +//Speaking Glamour (universal translator) + +/obj/item/device/universal_translator/glamour + name = "speaking glamour" + desc = "A shard of glamour that translates all known language for the user." + icon = 'icons/obj/glamour.dmi' + icon_state = "translator" + +//Teleporter ring + +/obj/structure/glamour_ring + name = "glamour ring" + desc = "A ring of glowing white, oddly reflective material." + icon = 'icons/obj/glamour.dmi' + icon_state = "ring" + density = 0 + anchored = 1 + + var/connected_mob + var/area_name + +/obj/structure/glamour_ring/Initialize() + . = ..() + var/area/A = get_area(src) + area_name = A.name + name = "[area_name] glamour ring" + +//Glamour Floor +//Glamour Wall \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 9f68957ceeb..3b471a84a3f 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -108,7 +108,7 @@ var/chem_strength_heal = 1 // Multiplier to most beneficial chem strength var/chem_strength_pain = 1 // Multiplier to painkiller strength (could be used in a negative trait to simulate long-term addiction reducing effects, etc.) var/chem_strength_tox = 1 // Multiplier to toxic chem strength (inc. chloral/sopo/mindbreaker/etc. thresholds) - var/chem_strength_alcohol = 1 // Multiplier to alcohol strength; 0.5 = half, 0 = no effect at all, 2 = double, etc. + var/chem_strength_alcohol = 1 // Multiplier to alcohol effect thresholds; higher means more is needed to reach a given effect tier var/chemOD_threshold = 1 // Multiplier to overdose threshold; lower = easier overdosing var/chemOD_mod = 1 // Damage modifier for overdose; higher = more damage from ODs @@ -222,6 +222,8 @@ var/rarity_value = 1 // Relative rarity/collector value for this species. var/economic_modifier = 2 // How much money this species makes + var/vore_belly_default_variant = "H" + // Determines the organs that the species spawns with and var/list/has_organ = list( // which required-organ checks are conducted. O_HEART = /obj/item/organ/internal/heart, diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans.dm b/code/modules/mob/living/carbon/human/species/station/prometheans.dm index 7e5f579592a..06ef4270d29 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans.dm @@ -61,7 +61,7 @@ var/datum/species/shapeshifter/promethean/prometheans item_slowdown_mod = 1.33 throwforce_absorb_threshold = 10 - chem_strength_alcohol = 2 + chem_strength_alcohol = 0.5 cloning_modifier = /datum/modifier/cloning_sickness/promethean diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index 6fda9ef6804..43b199f96ae 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -71,7 +71,7 @@ name_language = LANGUAGE_UNATHI species_language = LANGUAGE_UNATHI health_hud_intensity = 2.5 - chem_strength_alcohol = 0.75 + chem_strength_alcohol = 1.25 throwforce_absorb_threshold = 10 digi_allowed = TRUE @@ -198,7 +198,7 @@ name_language = LANGUAGE_SIIK species_language = LANGUAGE_SIIK health_hud_intensity = 2.5 - chem_strength_alcohol = 1.25 + chem_strength_alcohol = 0.75 digi_allowed = TRUE min_age = 17 @@ -303,7 +303,7 @@ species_language = LANGUAGE_SKRELLIAN assisted_langs = list(LANGUAGE_EAL, LANGUAGE_ROOTLOCAL, LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX, LANGUAGE_PROMETHEAN) health_hud_intensity = 2 - chem_strength_alcohol = 5 + chem_strength_alcohol = 0.2 water_movement = -3 @@ -522,7 +522,7 @@ show_ssd = "completely quiescent" health_hud_intensity = 2.5 item_slowdown_mod = 0.1 - chem_strength_alcohol = 0 + chem_strength_alcohol = 10000 //a little hacky, maybe? but whatever. nobody plays diona anyway. throwforce_absorb_threshold = 5 num_alternate_languages = 3 diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm index f4b80235620..93897421cac 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm @@ -1529,3 +1529,145 @@ if(Adjacent(target)) //We leapt at them but we didn't manage to hit them, let's see if we're next to them target.Weaken(2) //get knocked down, idiot + + +/mob/living/proc/injection() // Allows the user to inject reagents into others somehow, like stinging, or biting. + set name = "Injection" + set category = "Abilities" + set desc = "Inject another being with something!" + + if(stat || paralysis || weakened || stunned || world.time < last_special) //Epic copypasta from tongue grabbing. + to_chat(src, "You can't do that in your current state.") + return + + last_special = world.time + 10 //Anti-spam. + + var/list/choices = list("Inject") + + if(trait_injection_reagents.len > 1) //Should never happen, but who knows! + choices += "Change reagent" + else if(!trait_injection_selected) + trait_injection_selected = trait_injection_reagents[1] + + choices += "Change amount" + choices += "Change verb" + choices += "Chemical Refresher" + + var/choice = tgui_alert(src, "Do you wish to inject somebody, or adjust settings?", "Selection List", choices) + + if(choice == "Change reagent") + var/reagent_choice = tgui_input_list(usr, "Choose which reagent to inject!", "Select reagent", trait_injection_reagents) + if(reagent_choice) + trait_injection_selected = reagent_choice + to_chat(src, "You prepare to inject [trait_injection_amount] units of [trait_injection_selected ? "[trait_injection_selected]" : "...nothing. Select a reagent before trying to inject anything."]") + return + if(choice == "Change amount") + var/amount_choice = tgui_input_number(usr, "How much of the reagent do you want to inject? (Up to 5 units) (Can select 0 for a bite that doesn't inject venom!)", "How much?", trait_injection_amount, 5, 0) + if(amount_choice >= 0) + trait_injection_amount = amount_choice + to_chat(src, "You prepare to inject [trait_injection_amount] units of [trait_injection_selected ? "[trait_injection_selected]" : "...nothing. Select a reagent before trying to inject anything."]") + return + if(choice == "Change verb") + var/verb_choice = tgui_input_text(usr, "Choose the percieved manner of injection, such as 'bites' or 'stings', don't be misleading or abusive. This will show up in game as ('X' 'Verb' 'Y'. Example: X bites Y.)", "How are you injecting?", trait_injection_verb, max_length = 60) //Whoaa there cowboy don't put a novel in there. + if(verb_choice) + trait_injection_verb = verb_choice + to_chat(src, "You will [trait_injection_verb] your targets.") + return + if(choice == "Chemical Refresher") + var/output = {"Chemical Refresher!
+ Options for venoms
+
+ Size Chemicals
+ Microcillin: Will make someone shrink.
+ Macrocillin: Will make someone grow.
+ Normalcillin: Will make someone normal size.
+ Note: 1 unit = 100% size diff. 0.01 unit = 1% size diff.
+ Note: Normacillin stops at 100% size.
+
+ Gender Chemicals
+ Androrovir: Will transform someone's sex to male.
+ Gynorovir: Will transform someone's sex to female.
+ Androgynorovir: Will transform someone's sex to plural.
+
+ Special Chemicals
+ Stoxin: Will make someone drowsy.
+ Rainbow Toxin: Will make someone see rainbows.
+ Paralysis Toxin: Will make someone paralyzed.
+ Numbing Enzyme: Will make someone unable to feel pain.
+ Pain Enzyme: Will make someone feel amplified pain.
+
+ Side Notes
+ You can select a value of 0 to inject nothing!
+ Overdose threshold for most chemicals is 30 units.
+ Exceptions to OD is: (Numbing Enzyme:20)
+ You can also bite synthetics, but due to how synths work, they won't have anything injected into them. +
+ "} + usr << browse(output,"window=chemicalrefresher") + return + else + var/list/targets = list() //IF IT IS NOT BROKEN. DO NOT FIX IT. AND KEEP COPYPASTING IT (Pointing Rick Dalton: "That's my code!" ~CL) + + for(var/mob/living/carbon/L in living_mobs(1, TRUE)) //Noncarbons don't even process reagents so don't bother listing others. + if(!istype(L, /mob/living/carbon)) + continue + if(L == src) //no getting high off your own supply, get a nif or something, nerd. + continue + if(!L.resizable && (trait_injection_selected == "macrocillin" || trait_injection_selected == "microcillin" || trait_injection_selected == "normalcillin")) // If you're using a size reagent, ignore those with pref conflicts. + continue + if(!L.allow_spontaneous_tf && (trait_injection_selected == "androrovir" || trait_injection_selected == "gynorovir" || trait_injection_selected == "androgynorovir")) // If you're using a TF reagent, ignore those with pref conflicts. + continue + targets += L + + if(!(targets.len)) + to_chat(src, "No eligible targets found.") + return + + var/mob/living/target = tgui_input_list(src, "Please select a target.", "Victim", targets) + + if(!target) + return + + if(!istype(target, /mob/living/carbon)) //Safety. + to_chat(src, "That won't work on that kind of creature! (Only works on crew/monkeys)") + return + + + var/synth = 0 + if(target.isSynthetic()) + synth = 1 + + if(!trait_injection_selected) + to_chat(src, "You need to select a reagent.") + return + + if(!trait_injection_verb) + to_chat(src, "Somehow, you forgot your means of injecting. (Select a verb!)") + return + + if(do_after(src, 50, target)) //A decent enough timer. + add_attack_logs(src,target,"Injection trait ([trait_injection_selected], [trait_injection_amount])") + if(target.reagents && (trait_injection_amount > 0) && !synth) + target.reagents.add_reagent(trait_injection_selected, trait_injection_amount) + var/ourmsg = "[usr] [trait_injection_verb] [target] " + switch(zone_sel.selecting) + if(BP_HEAD) + ourmsg += "on the head!" + if(BP_TORSO) + ourmsg += "on the chest!" + if(BP_GROIN) + ourmsg += "on the groin!" + if(BP_R_ARM, BP_L_ARM) + ourmsg += "on the arm!" + if(BP_R_HAND, BP_L_HAND) + ourmsg += "on the hand!" + if(BP_R_LEG, BP_L_LEG) + ourmsg += "on the leg!" + if(BP_R_FOOT, BP_L_FOOT) + ourmsg += "on the foot!" + if("mouth") + ourmsg += "on the mouth!" + if("eyes") + ourmsg += "on the eyes!" + ourmsg += "" + visible_message(ourmsg) diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm index 89a17e9a91c..2575d52dcfa 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm @@ -143,6 +143,45 @@ H.verbs |= /mob/living/carbon/human/proc/succubus_drain_finalize H.verbs |= /mob/living/carbon/human/proc/succubus_drain_lethal +/datum/trait/neutral/venom_bite + name = "Venomous Injection" + desc = "Allows for injecting prey through one method or another to inject them with a variety of chemicals with varying effects!" + tutorial = "This trait allows you to bite prey with varying effects!
\ + Options for venoms:
\ + =====Size Chemicals =====
\ + Microcillin: Will make someone shrink. (This is 1% per 0.01 units. So 1 unit = 100% size change)
\ + Macrocillin: Will make someone grow. (This is 1% per 0.01 units. So 1 unit = 100% size change)
\ + Normalcillin: Will make someone normal size. (This is 1% per 0.01 units. So 1 unit = 100% size change) Stops at 100% size.
\ + ===== Gender Chemicals =====
\ + Androrovir: Will transform someone's sex to male.
\ + Gynorovir: Will transform someone's sex to female.
\ + Androgynorovir: Will transform someone's sex to pleural.
\ + ===== Special Chemicals =====
\ + Stoxin: Will make someone drowsy.
\ + Rainbow Toxin: Will make someone see rainbows.
\ + Paralysis Toxin: Will make someone paralyzed.
\ + Numbing Enzyme: Will make someone unable to feel pain.
\ + Pain Enzyme: Will make someone feel pain, amplifieed
\ + ===== Side Notes =====
\ + You aren't required to inject anything if you prefer to just use it as a normal bite!" + cost = 0 + custom_only = FALSE + +/datum/trait/neutral/venom_bite/apply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.verbs |= /mob/living/proc/injection + H.trait_injection_reagents += "microcillin" // get small + H.trait_injection_reagents += "macrocillin" // get BIG + H.trait_injection_reagents += "normalcillin" // normal + H.trait_injection_reagents += "numbenzyme" // no feelings + H.trait_injection_reagents += "androrovir" // -> MALE + H.trait_injection_reagents += "gynorovir" // -> FEMALE + H.trait_injection_reagents += "androgynorovir" // -> PLURAL + H.trait_injection_reagents += "stoxin" // night night chem + H.trait_injection_reagents += "rainbowtoxin" // Funny flashing lights. + H.trait_injection_reagents += "paralysistoxin" // Paralysis! + H.trait_injection_reagents += "painenzyme" // Pain INCREASER + /datum/trait/neutral/long_vore name = "Long Predatorial Reach" desc = "Makes you able to use an unspecified appendage to grab creatures." @@ -482,21 +521,21 @@ desc = "The only way you can hold a drink is if it's in your own two hands, and even then you'd best not inhale too deeply near it. Alcohol hits you three times as hard as they do other people." cost = 0 custom_only = FALSE - var_changes = list("chem_strength_alcohol" = 3) + var_changes = list("chem_strength_alcohol" = 0.33) /datum/trait/neutral/alcohol_intolerance_basic name = "Liver of Lilies" desc = "You have a hard time with alcohol. Maybe you just never took to it, or maybe it doesn't agree with your system... either way, alcohol hits you twice as hard." cost = 0 custom_only = FALSE - var_changes = list("chem_strength_alcohol" = 2) + var_changes = list("chem_strength_alcohol" = 0.5) /datum/trait/neutral/alcohol_intolerance_slight name = "Liver of Tulips" desc = "You are what some might call 'a bit of a lightweight', but you can still keep your drinks down... most of the time. Alcohol hits you fifty percent harder." cost = 0 custom_only = FALSE - var_changes = list("chem_strength_alcohol" = 1.5) + var_changes = list("chem_strength_alcohol" = 0.75) /datum/trait/neutral/alcohol_tolerance_reset name = "Liver of Unremarkableness" @@ -511,21 +550,21 @@ desc = "You can hold drinks much better than those lily-livered land-lubbers! Arr! Alcohol's effects on you are reduced by about a quarter." cost = 0 custom_only = FALSE - var_changes = list("chem_strength_alcohol" = 0.75) + var_changes = list("chem_strength_alcohol" = 1.25) /datum/trait/neutral/alcohol_tolerance_advanced name = "Liver of Steel" desc = "Drinks tremble before your might! You can hold your alcohol twice as well as those blue-bellied barnacle boilers! Alcohol has just half the effect on you as it does on others." cost = 0 custom_only = FALSE - var_changes = list("chem_strength_alcohol" = 0.5) + var_changes = list("chem_strength_alcohol" = 2) /datum/trait/neutral/alcohol_immunity name = "Liver of Durasteel" desc = "You've drunk so much that most booze doesn't even faze you. It takes something like a Pan-Galactic or a pint of Deathbell for you to even get slightly buzzed." cost = 0 custom_only = FALSE - var_changes = list("chem_strength_alcohol" = 0.25) + var_changes = list("chem_strength_alcohol" = 4) // Alcohol Traits End Here. /datum/trait/neutral/colorblind/mono diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 804d9ad78bd..3a1441f334a 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -97,7 +97,10 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() #define FIRE_LAYER 35 //'Mob on fire' overlay layer // # define MOB_WATER_LAYER 36 //'Mob submerged' overlay layer // Moved to global defines #define TARGETED_LAYER 37 //'Aimed at' overlay layer -#define TOTAL_LAYERS 37 //VOREStation edit. <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list. +#define VORE_BELLY_LAYER 38 +#define VORE_TAIL_LAYER 39 + +#define TOTAL_LAYERS 39 //VOREStation edit. <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list. ////////////////////////////////// /mob/living/carbon/human @@ -421,6 +424,8 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() //tail update_tail_showing() update_wing_showing() + update_vore_belly_sprite() + update_vore_tail_sprite() /mob/living/carbon/human/proc/update_skin() if(QDESTROYING(src)) @@ -1060,7 +1065,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() apply_layer(tail_layer) return - var/species_tail = species.get_tail(src) // Species tail icon_state prefix. + var/species_tail = species?.get_tail(src) // Species tail icon_state prefix. //This one is actually not that bad I guess. if(species_tail && !(wear_suit && wear_suit.flags_inv & HIDETAIL)) @@ -1393,6 +1398,74 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() if((. = ..())) update_wing_showing() +/mob/living/carbon/human/proc/update_vore_belly_sprite() + if(QDESTROYING(src)) + return + + remove_layer(VORE_BELLY_LAYER) + + var/image/vore_belly_image = get_vore_belly_image() + if(vore_belly_image) + vore_belly_image.layer = BODY_LAYER+VORE_BELLY_LAYER + overlays_standing[VORE_BELLY_LAYER] = vore_belly_image + vore_belly_image.plane = PLANE_CH_STOMACH //This one line of code. This ONE LINE OF CODE TOOK 6 HOURS TO FIGURE OUT. THANK YOU REDCAT. + + apply_layer(VORE_BELLY_LAYER) + +/mob/living/carbon/human/proc/get_vore_belly_image() + if(!(wear_suit && wear_suit.flags_inv & HIDETAIL)) + var/vs_fullness = vore_fullness_ex["stomach"] + var/icon/vorebelly_s = new/icon(icon = 'icons/mob/vore/Bellies.dmi', icon_state = "[species.vore_belly_default_variant]Belly[vs_fullness][struggle_anim_stomach ? "" : " idle"]") + vorebelly_s.Blend(vore_sprite_color["stomach"], vore_sprite_multiply["stomach"] ? ICON_MULTIPLY : ICON_ADD) + var/image/working = image(vorebelly_s) + working.overlays += em_block_image_generic(working) + return working + return null + +/mob/living/carbon/human/proc/vore_belly_animation() + if(!struggle_anim_stomach) + struggle_anim_stomach = TRUE + update_vore_belly_sprite() + spawn(12) + struggle_anim_stomach = FALSE + update_vore_belly_sprite() + +/mob/living/carbon/human/proc/update_vore_tail_sprite() + if(QDESTROYING(src)) + return + + remove_layer(VORE_TAIL_LAYER) + + var/image/vore_tail_image = get_vore_tail_image() + if(vore_tail_image) + vore_tail_image.layer = BODY_LAYER+VORE_TAIL_LAYER + overlays_standing[VORE_TAIL_LAYER] = vore_tail_image + vore_tail_image.plane = PLANE_CH_STOMACH //This one line of code. This ONE LINE OF CODE TOOK 6 HOURS TO FIGURE OUT. THANK YOU REDCAT. + + apply_layer(VORE_TAIL_LAYER) + +/mob/living/carbon/human/proc/get_vore_tail_image() + if(tail_style && istaurtail(tail_style) && tail_style:vore_tail_sprite_variant) + var/vs_fullness = vore_fullness_ex["taur belly"] + var/loaf_alt = lying && tail_style:belly_variant_when_loaf + var/fullness_icons = min(tail_style.fullness_icons, vs_fullness) + var/icon/vorebelly_s = new/icon(icon = tail_style.bellies_icon_path, icon_state = "Taur[tail_style:vore_tail_sprite_variant]-Belly-[fullness_icons][loaf_alt ? " loaf" : (struggle_anim_taur ? "" : " idle")]") + vorebelly_s.Blend(vore_sprite_color["taur belly"], vore_sprite_multiply["taur belly"] ? ICON_MULTIPLY : ICON_ADD) + var/image/working = image(vorebelly_s) + working.pixel_x = -16 + if(tail_style.em_block) + working.overlays += em_block_image_generic(working) + return working + return null + +/mob/living/carbon/human/proc/vore_tail_animation() + if(tail_style.struggle_anim && !struggle_anim_taur) + struggle_anim_taur = TRUE + update_vore_tail_sprite() + spawn(12) + struggle_anim_taur = FALSE + update_vore_tail_sprite() + //Human Overlays Indexes///////// #undef MUTATIONS_LAYER #undef SKIN_LAYER diff --git a/code/modules/mob/living/silicon/robot/analyzer.dm b/code/modules/mob/living/silicon/robot/analyzer.dm index 9e6fbdb2d2c..c7476f2ab71 100644 --- a/code/modules/mob/living/silicon/robot/analyzer.dm +++ b/code/modules/mob/living/silicon/robot/analyzer.dm @@ -56,8 +56,8 @@ user.show_message("\t Damage Specifics: [span_orange("[BU]")] - [span_red("[BR]")]") if(M.tod && M.stat == DEAD) user.show_message(span_blue("Time of Disable: [M.tod]")) - var/mob/living/silicon/robot/H = M - var/obj/item/weapon/cell/cell = H.get_cell() + var/mob/living/silicon/robot/R = M + var/obj/item/weapon/cell/cell = R.get_cell() if(cell) var/cell_charge = round(cell.percent()) var/cell_text @@ -72,7 +72,7 @@ else cell_text = span_red("[cell_charge]") user.show_message("\t Power Cell Status: [span_blue("[capitalize(cell.name)]")] at [cell_text]% charge") - var/list/damaged = H.get_damaged_components(1,1,1) + var/list/damaged = R.get_damaged_components(1,1,1) user.show_message(span_blue("Localized Damage:"),1) if(length(damaged)>0) for(var/datum/robot_component/org in damaged) @@ -85,12 +85,12 @@ (org.powered) ? "Power ON" : "[span_red("Power OFF")]")),1) else user.show_message(span_blue("\t Components are OK."),1) - if(H.emagged && prob(5)) + if(R.emagged && prob(5)) user.show_message(span_red("\t ERROR: INTERNAL SYSTEMS COMPROMISED"),1) user.show_message(span_blue("Operating Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)"), 1) else - var/mob/living/silicon/robot/H = M - var/obj/item/weapon/cell/cell = H.get_cell() + var/mob/living/silicon/robot/R = M + var/obj/item/weapon/cell/cell = R.get_cell() user.show_message(span_blue("Upgrade Analyzing Results for [M]:")) if(cell) user.show_message("\t Power Cell Details: [span_blue("[capitalize(cell.name)]")] with a capacity of [cell.maxcharge] at [round(cell.percent())]% charge") @@ -98,12 +98,12 @@ for(var/datum/design/item/prosfab/robot_upgrade/utility/upgrade) var/obj/item/borg/upgrade/utility/upgrade_type = initial(upgrade.build_path) var/needs_module = initial(upgrade_type.require_module) - if((!H.module && needs_module) || !initial(upgrade.name) || (H.stat != DEAD && initial(upgrade.name) == "Emergency Restart Module")) + if((!R.module && needs_module) || !initial(upgrade.name) || (R.stat != DEAD && (upgrade_type == /obj/item/borg/upgrade/utility/restart)) || (isshell(R) && (upgrade_type == /obj/item/borg/upgrade/utility/rename))) continue if(show_title) user.show_message("\t Utility Modules, used for modifying purposes:") show_title = FALSE - if(H.stat == DEAD) + if(R.stat == DEAD) if(initial(upgrade.name) == "Emergency Restart Module") user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_green("Usable")]")) else @@ -112,55 +112,55 @@ for(var/datum/design/item/prosfab/robot_upgrade/basic/upgrade) var/obj/item/borg/upgrade/basic/upgrade_type = initial(upgrade.build_path) var/needs_module = initial(upgrade_type.require_module) - if((!H.module && needs_module) || !initial(upgrade.name) || H.stat == DEAD) + if((!R.module && needs_module) || !initial(upgrade.name) || R.stat == DEAD) continue if(show_title) user.show_message("\t Basic Modules, used for direct upgrade purposes:") show_title = FALSE - if(H.has_basic_upgrade(initial(upgrade.build_path)) == "") + if(R.has_basic_upgrade(initial(upgrade.build_path)) == "") user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) else - user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_basic_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [R.has_basic_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) show_title = TRUE for(var/datum/design/item/prosfab/robot_upgrade/advanced/upgrade) var/obj/item/borg/upgrade/advanced/upgrade_type = initial(upgrade.build_path) var/needs_module = initial(upgrade_type.require_module) - if((!H.module && needs_module) || !initial(upgrade.name) || H.stat == DEAD) + if((!R.module && needs_module) || !initial(upgrade.name) || R.stat == DEAD) continue if(show_title) user.show_message("\t Advanced Modules, used for module upgrade purposes:") show_title = FALSE - if(H.has_advanced_upgrade(initial(upgrade.build_path)) == "") + if(R.has_advanced_upgrade(initial(upgrade.build_path)) == "") user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) else - user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_advanced_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [R.has_advanced_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) show_title = TRUE for(var/datum/design/item/prosfab/robot_upgrade/restricted/upgrade) var/obj/item/borg/upgrade/restricted/upgrade_type = initial(upgrade.build_path) var/needs_module = initial(upgrade_type.require_module) - if((!H.module && needs_module) || !initial(upgrade.name) || !H.supports_upgrade(initial(upgrade.build_path)) || H.stat == DEAD) + if((!R.module && needs_module) || !initial(upgrade.name) || !R.supports_upgrade(initial(upgrade.build_path)) || R.stat == DEAD) continue if(show_title) user.show_message("\t Restricted Modules, used for module upgrade purposes on specific chassis:") show_title = FALSE - if(H.has_restricted_upgrade(initial(upgrade.build_path)) == "") + if(R.has_restricted_upgrade(initial(upgrade.build_path)) == "") user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) else - user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_restricted_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [R.has_restricted_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) show_title = TRUE for(var/datum/design/item/prosfab/robot_upgrade/no_prod/upgrade) var/obj/item/borg/upgrade/no_prod/upgrade_type = initial(upgrade.build_path) var/needs_module = initial(upgrade_type.require_module) var/hidden = initial(upgrade_type.hidden_from_scan) - if((!H.module && needs_module) || !initial(upgrade.name) || hidden || H.stat == DEAD) + if((!R.module && needs_module) || !initial(upgrade.name) || hidden || R.stat == DEAD) continue if(show_title) user.show_message("\t Special Modules, used for recreation purposes:") show_title = FALSE - if(H.has_no_prod_upgrade(initial(upgrade.build_path)) == "") + if(R.has_no_prod_upgrade(initial(upgrade.build_path)) == "") user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) else - user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_no_prod_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [R.has_no_prod_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) if("prosthetics") diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index 2b8d37c7ae5..fd092f30039 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -42,7 +42,7 @@ //VOREStation Edit - multiz lol if(D.foreign_droid) continue - + drones.Add(list(list( "name" = D.real_name, "active" = D.stat != 2, @@ -56,7 +56,10 @@ data["fabricator"] = dronefab data["fabPower"] = dronefab?.produce_drones - data["areas"] = GLOB.tagger_locations + var/list/areas = list() + for(var/area in GLOB.tagger_locations) + areas += area + data["areas"] = areas data["selected_area"] = "[drone_call_area]" return data diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 695a3dd6310..6d850186ba6 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -427,7 +427,7 @@ set category = "Robot Commands" if(custom_name) - to_chat(usr, "You can't pick another custom name. Go ask for a name change.") + to_chat(usr, "You can't pick another custom name. [isshell(src) ? "" : "Go ask for a name change."]") return 0 spawn(0) diff --git a/code/modules/mob/living/simple_mob/subtypes/glamour/homunculus.dm b/code/modules/mob/living/simple_mob/subtypes/glamour/homunculus.dm new file mode 100644 index 00000000000..3a65edf7b6f --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/glamour/homunculus.dm @@ -0,0 +1,35 @@ +/mob/living/simple_mob/homunculus + name = "homunculus" + desc = "A strange misshapen humanoid creature made purely from glamour!" + + icon_dead = "homunculus" + icon_living = "homunculus" + icon_state = "homunculus" + icon = 'icons/mob/vore.dmi' + + ai_holder_type = /datum/ai_holder/simple_mob/passive + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + var/owner + +/mob/living/simple_mob/homunculus/death() + if(owner) + var/obj/item/glamour_face/O = owner + O.homunculus = 0 + qdel(src) + +/mob/living/simple_mob/homunculus/update_icon() + return + +/mob/living/simple_mob/homunculus/update_icons() + return + diff --git a/code/modules/mob/mob_defines_vr.dm b/code/modules/mob/mob_defines_vr.dm index 619cfe59403..0eaed29131d 100644 --- a/code/modules/mob/mob_defines_vr.dm +++ b/code/modules/mob/mob_defines_vr.dm @@ -1,5 +1,6 @@ /mob var/vantag_hud = 0 // Do I have the HUD enabled? + var/stomach_vision = 1 // By default, you will see stomachs. var/mob/temporary_form // For holding onto a temporary form var/disconnect_time = null //Time of client loss, set by Logout(), for timekeeping diff --git a/code/modules/mob/mob_helpers_vr.dm b/code/modules/mob/mob_helpers_vr.dm index 663cfc7f4a2..81ae7da305f 100644 --- a/code/modules/mob/mob_helpers_vr.dm +++ b/code/modules/mob/mob_helpers_vr.dm @@ -1,5 +1,13 @@ /mob/recalculate_vis() . = ..() + + if(stomach_vision && !(VIS_CH_STOMACH in vis_enabled)) + plane_holder.set_vis(VIS_CH_STOMACH,TRUE) + vis_enabled += VIS_CH_STOMACH + else if(!stomach_vision && (VIS_CH_STOMACH in vis_enabled)) + plane_holder.set_vis(VIS_CH_STOMACH,FALSE) + vis_enabled -= VIS_CH_STOMACH + if(!plane_holder || !vis_enabled) return @@ -12,3 +20,33 @@ plane_holder.set_vis(VIS_CH_VANTAG,FALSE) vis_enabled -= VIS_CH_VANTAG return + + +/mob/verb/toggle_stomach_vision() + set name = "Toggle Stomach Sprites" + set category = "Preferences" + set desc = "Toggle the ability to see stomachs or not" + + var/toggle + toggle = tgui_alert(src, "Would you like to see visible stomachs?", "Visible Tummy?", list("Yes", "No")) + if(toggle =="Yes") + stomach_vision = 1 //Simple! Easy! + if(!(VIS_CH_STOMACH in vis_enabled)) + plane_holder.set_vis(VIS_CH_STOMACH,TRUE) + vis_enabled += VIS_CH_STOMACH + to_chat("You can now see stomachs!") + else + stomach_vision = 0 + if(VIS_CH_STOMACH in vis_enabled) + plane_holder.set_vis(VIS_CH_STOMACH,FALSE) + vis_enabled -= VIS_CH_STOMACH + to_chat("You will no longer see stomachs!") + +/* //Leaving this in as an example of 'how to properly enable a plane to hide/show itself' for future PRs. +if(stomach_vision && !(VIS_CH_STOMACH in vis_enabled)) + plane_holder.set_vis(VIS_CH_STOMACH,TRUE) + vis_enabled += VIS_CH_STOMACH +else if(!stomach_vision && (VIS_CH_STOMACH in vis_enabled)) + plane_holder.set_vis(VIS_CH_STOMACH,FALSE) + vis_enabled -= VIS_CH_STOMACH +*/ diff --git a/code/modules/mob/mob_planes_vr.dm b/code/modules/mob/mob_planes_vr.dm index b23067733f3..9f5b30f5793 100644 --- a/code/modules/mob/mob_planes_vr.dm +++ b/code/modules/mob/mob_planes_vr.dm @@ -4,6 +4,7 @@ plane_masters[VIS_CH_HEALTH_VR] = new /obj/screen/plane_master{plane = PLANE_CH_HEALTH_VR} //Health bar but transparent at 100 plane_masters[VIS_CH_BACKUP] = new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status plane_masters[VIS_CH_VANTAG] = new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags + plane_masters[VIS_CH_STOMACH] = new /obj/screen/plane_master{plane = PLANE_CH_STOMACH} //Stomach plane_masters[VIS_AUGMENTED] = new /obj/screen/plane_master/augmented(M = my_mob) //Augmented reality ..() diff --git a/code/modules/mob/new_player/sprite_accessories_ear_vr.dm b/code/modules/mob/new_player/sprite_accessories_ear_vr.dm index 4f62456cd20..d2932c0b258 100644 --- a/code/modules/mob/new_player/sprite_accessories_ear_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_ear_vr.dm @@ -9,7 +9,7 @@ name = "You should not see this..." icon = 'icons/mob/vore/ears_vr.dmi' do_colouration = 0 // Set to 1 to blend (ICON_ADD) hair color - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN) //This lets all races use + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL) //This lets all races use color_blend_mode = ICON_ADD // Only appliciable if do_coloration = 1 // Species-unique ears @@ -30,6 +30,17 @@ do_colouration = 1 extra_overlay = "shadekin-round-inner" +/datum/sprite_accessory/ears/lleill + name = "Lleill Ears, colorable" + desc = "" + icon = 'icons/mob/vore/ears_32x64.dmi' + icon_state = "lleill" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY + extra_overlay = "lleill-antlers" + species_allowed = list(SPECIES_LLEILL) + + // Ears avaliable to anyone /datum/sprite_accessory/ears/alien_slug diff --git a/code/modules/mob/new_player/sprite_accessories_tail.dm b/code/modules/mob/new_player/sprite_accessories_tail.dm index a4f1e457f5a..b1f6a930a73 100644 --- a/code/modules/mob/new_player/sprite_accessories_tail.dm +++ b/code/modules/mob/new_player/sprite_accessories_tail.dm @@ -1058,3 +1058,12 @@ extra_overlay2 = "zorgoia_fluff_top" do_colouration = 1 color_blend_mode = ICON_MULTIPLY + +/datum/sprite_accessory/tail/lleill + name = "Lleill tail" + desc = "" + icon = 'icons/mob/species/lleill/tail.dmi' + icon_state = "tail" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY + extra_overlay = "tail_marking" diff --git a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm index b71cede427b..67f07f57b55 100644 --- a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm @@ -13,7 +13,7 @@ var/mob_offset_x = 0 var/mob_offset_y = 0 do_colouration = 0 //Set to 1 to enable coloration using the tail color. - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN) //This lets all races use + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL) //This lets all races use var/list/lower_layer_dirs = list(SOUTH) var/icon_loaf = null diff --git a/code/modules/mob/new_player/sprite_accessories_taur.dm b/code/modules/mob/new_player/sprite_accessories_taur.dm index 0e052d9c50a..89a264c82e4 100644 --- a/code/modules/mob/new_player/sprite_accessories_taur.dm +++ b/code/modules/mob/new_player/sprite_accessories_taur.dm @@ -1,3 +1,10 @@ +/datum/sprite_accessory/tail + var/vore_tail_sprite_variant = "" + var/belly_variant_when_loaf = FALSE + var/fullness_icons = 0 + var/struggle_anim = FALSE + var/bellies_icon_path = 'icons/mob/vore/Taur_Bellies.dmi' + /datum/riding/taur keytype = /obj/item/weapon/material/twohanded/riding_crop // Crack! nonhuman_key_exemption = FALSE // If true, nonhumans who can't hold keys don't need them, like borgs and simplemobs. @@ -355,4 +362,70 @@ /datum/sprite_accessory/tail/taur/zorgoia/fat name = "Zorgoia (Fat Taur)" - extra_overlay = "zorgoia_fat" \ No newline at end of file + extra_overlay = "zorgoia_fat" + + +/datum/sprite_accessory/tail/taur/wolf + vore_tail_sprite_variant = "N" + fullness_icons = 3 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/naga/naga_2c + vore_tail_sprite_variant = "Naga" + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/horse + vore_tail_sprite_variant = "Horse" + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/cow + vore_tail_sprite_variant = "Cow" + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/lizard + vore_tail_sprite_variant = "Lizard" + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/lizard/synthlizard + vore_tail_sprite_variant = "SynthLiz" + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/feline + vore_tail_sprite_variant = "Feline" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/slug + vore_tail_sprite_variant = "Slug" + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/drake + vore_tail_sprite_variant = "Drake" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/otie + vore_tail_sprite_variant = "Otie" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/deer + vore_tail_sprite_variant = "Deer" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/skunk + vore_tail_sprite_variant = "Skunk" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE diff --git a/code/modules/mob/new_player/sprite_accessories_taur_vr.dm b/code/modules/mob/new_player/sprite_accessories_taur_vr.dm index 17e716fd4e5..2a85dda15f4 100644 --- a/code/modules/mob/new_player/sprite_accessories_taur_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_taur_vr.dm @@ -60,6 +60,9 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 4 + vore_tail_sprite_variant = "N" + fullness_icons = 3 + struggle_anim = TRUE /datum/sprite_accessory/tail/taur/wolf/fatwolf name = "Fat Wolf (Taur)" @@ -131,6 +134,10 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 3 + vore_tail_sprite_variant = "Skunk" //Sadly there appears to be no sprites... For now! + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE /datum/sprite_accessory/tail/taur/naga name = "Naga (Taur)" @@ -165,34 +172,43 @@ msg_prey_stepunder = "You jump over %prey's thick tail." msg_owner_stepunder = "%owner bounds over your tail." -/datum/sprite_accessory/tail/taur/naga/naga_2c +/datum/sprite_accessory/tail/taur/naga/vore_compatable + name = "Naga (Taur) (Vore Compatable)" + vore_tail_sprite_variant = "Naga" + fullness_icons = 1 + struggle_anim = TRUE + +/datum/sprite_accessory/tail/taur/naga/vore_compatable/naga_2c name = "Naga dual-color (Taur)" icon_state = "naga_s" extra_overlay = "naga_markings" //icon_sprite_tag = "naga2c" + vore_tail_sprite_variant = "Naga" + fullness_icons = 1 + struggle_anim = TRUE -/datum/sprite_accessory/tail/taur/naga/alt_2c +/datum/sprite_accessory/tail/taur/naga/vore_compatable/alt_2c name = "Naga alt style dual-color (Taur)" suit_sprites = 'icons/mob/taursuits_naga_alt_vr.dmi' icon_state = "altnaga_s" extra_overlay = "altnaga_markings" //icon_sprite_tag = "altnaga2c" -/datum/sprite_accessory/tail/taur/naga/alt_3c +/datum/sprite_accessory/tail/taur/naga/vore_compatable/alt_3c name = "Naga alt style tri-color (Taur)" suit_sprites = 'icons/mob/taursuits_naga_alt_vr.dmi' icon_state = "altnaga_s" extra_overlay = "altnaga_markings" extra_overlay2 = "altnaga_stripes" -/datum/sprite_accessory/tail/taur/naga/alt_3c_rattler +/datum/sprite_accessory/tail/taur/naga/vore_compatable/alt_3c_rattler name = "Naga alt style tri-color, rattler (Taur)" suit_sprites = 'icons/mob/taursuits_naga_alt_vr.dmi' icon_state = "altnaga_s" extra_overlay = "altnaga_markings" extra_overlay2 = "altnaga_rattler" -/datum/sprite_accessory/tail/taur/naga/alt_3c_tailmaw +/datum/sprite_accessory/tail/taur/naga/vore_compatable/alt_3c_tailmaw name = "Naga alt style tri-color, tailmaw (Taur)" suit_sprites = 'icons/mob/taursuits_naga_alt_vr.dmi' icon_state = "altnagatailmaw_s" @@ -208,6 +224,9 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 4 + vore_tail_sprite_variant = "Horse" + fullness_icons = 1 + struggle_anim = TRUE msg_owner_disarm_run = "You quickly push %prey to the ground with your hoof!" msg_prey_disarm_run = "%owner pushes you down to the ground with their hoof!" @@ -242,6 +261,9 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 3 + vore_tail_sprite_variant = "Cow" + fullness_icons = 1 + struggle_anim = TRUE msg_owner_disarm_run = "You quickly push %prey to the ground with your hoof!" msg_prey_disarm_run = "%owner pushes you down to the ground with their hoof!" @@ -267,6 +289,10 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 7 + vore_tail_sprite_variant = "Deer" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE msg_owner_disarm_run = "You quickly push %prey to the ground with your hoof!" msg_prey_disarm_run = "%owner pushes you down to the ground with their hoof!" @@ -303,6 +329,9 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 5 + vore_tail_sprite_variant = "Lizard" + fullness_icons = 1 + struggle_anim = TRUE /datum/sprite_accessory/tail/taur/lizard/fatlizard name = "Fat Lizard (Taur)" @@ -349,6 +378,9 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 3 + vore_tail_sprite_variant = "SynthLiz" + fullness_icons = 1 + struggle_anim = TRUE /datum/sprite_accessory/tail/taur/lizard/fatsynthlizard name = "Fat SynthLizard dual-color (Taur)" @@ -424,6 +456,10 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 5 + vore_tail_sprite_variant = "Feline" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE /datum/sprite_accessory/tail/taur/fatfeline name = "Fat Feline (Taur)" @@ -529,6 +565,9 @@ icon_state = "slug_s" suit_sprites = 'icons/mob/taursuits_slug_vr.dmi' icon_sprite_tag = "slug" + vore_tail_sprite_variant = "Slug" + fullness_icons = 1 + struggle_anim = TRUE msg_owner_help_walk = "You carefully slither around %prey." msg_prey_help_walk = "%owner's huge tail slithers past beside you!" @@ -599,6 +638,10 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 6 + vore_tail_sprite_variant = "Drake" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE /datum/sprite_accessory/tail/taur/drake/fat name = "Fat Drake (Taur)" @@ -626,6 +669,10 @@ can_loaf = TRUE icon_loaf = 'icons/mob/vore/taurs_vr_loaf.dmi' loaf_offset = 5 + vore_tail_sprite_variant = "Otie" + belly_variant_when_loaf = TRUE + fullness_icons = 1 + struggle_anim = TRUE /datum/sprite_accessory/tail/taur/alraune/alraune_2c name = "Alraune (dual color)" @@ -1071,4 +1118,4 @@ offset_x = -32 offset_y = -11 mob_offset_y = 11 -*/ \ No newline at end of file +*/ diff --git a/code/modules/mob/new_player/sprite_accessories_vr.dm b/code/modules/mob/new_player/sprite_accessories_vr.dm index d6e61062bfc..ac03b0692d4 100644 --- a/code/modules/mob/new_player/sprite_accessories_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_vr.dm @@ -6,7 +6,7 @@ /datum/sprite_accessory/hair - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN) //This lets all races use the default hairstyles. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL) //This lets all races use the default hairstyles. /datum/sprite_accessory/hair/astolfo name = "Astolfo" @@ -485,20 +485,20 @@ /datum/sprite_accessory/facial_hair icon = 'icons/mob/human_face_or_vr.dmi' color_blend_mode = ICON_MULTIPLY - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN) //This lets all races use the facial hair styles. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL) //This lets all races use the facial hair styles. /datum/sprite_accessory/facial_hair/shaved name = "Shaved" icon_state = "bald" gender = NEUTER - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN) //This needed to be manually defined, apparantly. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL) //This needed to be manually defined, apparantly. /datum/sprite_accessory/facial_hair/neck_fluff name = "Neck Fluff" icon = 'icons/mob/human_face_or_vr.dmi' icon_state = "facial_neckfluff" gender = NEUTER - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN) + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL) /datum/sprite_accessory/facial_hair/vulp_none name = "None" diff --git a/code/modules/mob/new_player/sprite_accessories_wing_vr.dm b/code/modules/mob/new_player/sprite_accessories_wing_vr.dm index 1e8f6d49191..8bd27ee7460 100644 --- a/code/modules/mob/new_player/sprite_accessories_wing_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_wing_vr.dm @@ -9,7 +9,7 @@ name = "You should not see this..." icon = 'icons/mob/vore/wings_vr.dmi' do_colouration = 0 //Set to 1 to enable coloration using the tail color. - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN) //This lets all races use + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL) //This lets all races use color_blend_mode = ICON_ADD // Only appliciable if do_coloration = 1 var/wing_offset = 0 var/multi_dir = FALSE // Does it use different sprites at different layers? _front will be added for sprites on low layer, _back to high layer diff --git a/code/modules/multiz/portals_vr.dm b/code/modules/multiz/portals_vr.dm index 4f6756ad496..a7a4c9c75bd 100644 --- a/code/modules/multiz/portals_vr.dm +++ b/code/modules/multiz/portals_vr.dm @@ -93,7 +93,7 @@ if(portal_type == "Weird Green") portal_icon_selection = "type-b-portal" if(portal_type == "Pulsing") - var/portal_subtype = tgui_alert(user, "Which subtype would you prefer?", "Subtype Selection", list("Blue","Red","Blue/Red Mix", "Yellow")) + var/portal_subtype = tgui_alert(user, "Which subtype would you prefer?", "Subtype Selection", list("Blue","Red","Blue/Red Mix", "Yellow", "White")) if(portal_subtype == "Blue") portal_icon_selection = "type-c-blue-portal" if(portal_subtype == "Red") @@ -102,6 +102,8 @@ portal_icon_selection = "type-c-mix-portal" if(portal_subtype == "Yellow") portal_icon_selection = "type-c-yellow-portal" + if(portal_subtype == "White") + portal_icon_selection = "type-c-white-portal" return portal_icon_selection /obj/structure/portal_event/proc/teleport(atom/movable/M as mob|obj) diff --git a/code/modules/nifsoft/nif.dm b/code/modules/nifsoft/nif.dm index 13c37fd1c47..fc3884d0753 100644 --- a/code/modules/nifsoft/nif.dm +++ b/code/modules/nifsoft/nif.dm @@ -293,7 +293,7 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable notify("Adjoining optic [human.isSynthetic() ? "interface" : "nerve"], please be patient.",TRUE) else notify("You are not an authorized user for this device. Please contact [owner].",TRUE) - unimplant() + unimplant(human) stat = NIF_TEMPFAIL return FALSE diff --git a/code/modules/organs/subtypes/lleill.dm b/code/modules/organs/subtypes/lleill.dm new file mode 100644 index 00000000000..bd50b6ebff6 --- /dev/null +++ b/code/modules/organs/subtypes/lleill.dm @@ -0,0 +1,3 @@ +/obj/item/organ/external/head/lleill + eye_icon_location = 'icons/mob/human_face_vr.dmi' + eye_icon = "eyes_lleill" diff --git a/code/modules/pda/cart_apps.dm b/code/modules/pda/cart_apps.dm index c0f24ed9df6..eaf0db739de 100644 --- a/code/modules/pda/cart_apps.dm +++ b/code/modules/pda/cart_apps.dm @@ -229,7 +229,7 @@ for(var/datum/supply_order/SO as anything in SSsupply.shoppinglist) supplyOrderCount++ - supplyOrderData[++supplyOrderData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "ApprovedBy" = SO.ordered_by, "Comment" = html_encode(SO.comment)) + supplyOrderData[++supplyOrderData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "ApprovedBy" = SO.approved_by, "Comment" = html_encode(SO.comment)) supplyData["approved"] = supplyOrderData supplyData["approved_count"] = supplyOrderCount diff --git a/code/modules/pda/core_apps.dm b/code/modules/pda/core_apps.dm index 94c30cea652..6e6ab500761 100644 --- a/code/modules/pda/core_apps.dm +++ b/code/modules/pda/core_apps.dm @@ -314,7 +314,9 @@ // Compile all the newscasts for(var/datum/feed_channel/channel in news_network.network_channels) if(!channel.censored) + var/index = 0 for(var/datum/feed_message/FM in channel.messages) + index++ var/body = replacetext(FM.body, "\n", "
") news[++news.len] = list( "channel" = channel.channel_name, @@ -324,7 +326,8 @@ "time_stamp" = FM.time_stamp, "has_image" = (FM.img != null), "caption" = FM.caption, - "time" = FM.post_time + "time" = FM.post_time, + "index" = index ) // Cut out all but the youngest three diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 4af59cedb8a..3c509574c67 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -805,7 +805,7 @@ GLOBAL_LIST_EMPTY(apcs) "emagged" = emagged, "isOperating" = operating, "externalPower" = main_status, - "powerCellStatus" = cell ? cell.percent() : null, + "powerCellStatus" = cell ? cell.percent() : 0, "chargeMode" = chargemode, "chargingStatus" = charging, "totalLoad" = round(lastused_total), diff --git a/code/modules/reagents/machinery/dispenser/dispenser2.dm b/code/modules/reagents/machinery/dispenser/dispenser2.dm index 43a19e7ac01..be5090e2e70 100644 --- a/code/modules/reagents/machinery/dispenser/dispenser2.dm +++ b/code/modules/reagents/machinery/dispenser/dispenser2.dm @@ -152,7 +152,7 @@ var/chemicals[0] for(var/label in cartridges) var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label] - chemicals.Add(list(list("title" = label, "id" = label, "amount" = C.reagents.total_volume))) // list in a list because Byond merges the first list... + chemicals.Add(list(list("name" = label, "id" = label, "volume" = C.reagents.total_volume))) // list in a list because Byond merges the first list... data["chemicals"] = chemicals return data diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm index 007669286e0..e7ed52ea846 100644 --- a/code/modules/reagents/reagent_containers/patch.dm +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -40,7 +40,7 @@ to_chat(H, "\The [src] is placed on your [affecting].") M.drop_from_inventory(src) //icon update if(reagents.total_volume) - reagents.trans_to_mob(M, reagents.total_volume, CHEM_TOUCH) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_BLOOD) //CHEM_TOUCH qdel(src) return 1 @@ -74,7 +74,7 @@ M.drop_from_inventory(src) //icon update if(reagents.total_volume) - reagents.trans_to_mob(M, reagents.total_volume, CHEM_TOUCH) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_BLOOD) //CHEM_TOUCH qdel(src) return 1 diff --git a/code/modules/reagents/reagents/dispenser.dm b/code/modules/reagents/reagents/dispenser.dm index f69d7d451ee..84beb5266e3 100644 --- a/code/modules/reagents/reagents/dispenser.dm +++ b/code/modules/reagents/reagents/dispenser.dm @@ -113,7 +113,7 @@ if(issmall(M)) removed *= 2 - var/strength_mod = 3 * M.species.chem_strength_alcohol //Alcohol is 3x stronger when injected into the veins. + var/strength_mod = 3 //Alcohol is 3x stronger when injected into the veins. if(!strength_mod) return @@ -121,19 +121,19 @@ M.add_chemical_effect(CE_ALCOHOL, 1) var/effective_dose = dose * strength_mod * (1 + volume/60) //drinking a LOT will make you go down faster - if(effective_dose >= strength) // Early warning + if(effective_dose >= (strength * M.species.chem_strength_alcohol)) // Early warning M.make_dizzy(18) // It is decreased at the speed of 3 per tick - if(effective_dose >= strength * 2) // Slurring + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 2) // Slurring M.slurring = max(M.slurring, 90) - if(effective_dose >= strength * 3) // Confusion - walking in random directions + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 3) // Confusion - walking in random directions M.Confuse(60) - if(effective_dose >= strength * 4) // Blurry vision + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 4) // Blurry vision M.eye_blurry = max(M.eye_blurry, 30) - if(effective_dose >= strength * 5) // Drowsyness - periodically falling asleep + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 5) // Drowsyness - periodically falling asleep M.drowsyness = max(M.drowsyness, 60) - if(effective_dose >= strength * 6) // Toxic dose + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 6) // Toxic dose M.add_chemical_effect(CE_ALCOHOL_TOXIC, toxicity*3) - if(effective_dose >= strength * 7) // Pass out + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 7) // Pass out M.Paralyse(60) M.Sleeping(90) @@ -160,26 +160,26 @@ if(M.isSynthetic() && M.nutrition < 500 && M.species.robo_ethanol_proc) M.adjust_nutrition(round(max(0,ep_base_power - strength) * removed)/ep_final_mod) //the stronger it is, the more juice you gain - var/effective_dose = dose * M.species.chem_strength_alcohol + var/effective_dose = dose if(!effective_dose) return if(M.species.robo_ethanol_drunk || !(M.isSynthetic())) M.add_chemical_effect(CE_ALCOHOL, 1) - if(effective_dose >= strength) // Early warning + if(effective_dose >= (strength * M.species.chem_strength_alcohol)) // Early warning M.make_dizzy(6) // It is decreased at the speed of 3 per tick - if(effective_dose >= strength * 2) // Slurring + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 2) // Slurring M.slurring = max(M.slurring, 30) - if(effective_dose >= strength * 3) // Confusion - walking in random directions + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 3) // Confusion - walking in random directions M.Confuse(20) - if(effective_dose >= strength * 4) // Blurry vision + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 4) // Blurry vision M.eye_blurry = max(M.eye_blurry, 10) - if(effective_dose >= strength * 5) // Drowsyness - periodically falling asleep + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 5) // Drowsyness - periodically falling asleep M.drowsyness = max(M.drowsyness, 20) - if(effective_dose >= strength * 6) // Toxic dose + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 6) // Toxic dose M.add_chemical_effect(CE_ALCOHOL_TOXIC, toxicity) - if(effective_dose >= strength * 7) // Pass out + if(effective_dose >= (strength * M.species.chem_strength_alcohol) * 7) // Pass out M.Paralyse(20) M.Sleeping(30) diff --git a/code/modules/reagents/reagents/vore_vr.dm b/code/modules/reagents/reagents/vore_vr.dm index 42e39065628..4b56d19fc9b 100644 --- a/code/modules/reagents/reagents/vore_vr.dm +++ b/code/modules/reagents/reagents/vore_vr.dm @@ -186,3 +186,59 @@ H.change_gender_identity(PLURAL) H.visible_message("[H] suddenly twitches as some of their features seem to contort and reshape, adjusting... In the end, it seems they are now of mixed gender.", "Your body suddenly contorts, feeling very different in various ways... By the time the rushing feeling is over it seems you just became of mixed gender.") + + +////////////////////////// Misc Drugs ////////////////////////// + +/datum/reagent/drugs/rainbow_toxin /// Replaces Space Drugs. + name = "Rainbow Toxin" + id = "rainbowtoxin" + description = "Known for providing a euphoric high, this psychoactive drug is often injected into unknowing prey by serpents and other fanged beasts. Highly valuable and frequently sought after by hypno-enthusiasts and party-goers." + taste_description = "mixed euphoria" + taste_mult = 0.8 //You ARE going to taste this! + scannable = 1 //Sure! If you manage to milk a snake for some of this, go ahead and scan it and mass produce it. Your local club will love you! + +/datum/reagent/drugs/rainbow_toxin/affect_blood(mob/living/carbon/M, var/alien, var/removed) + ..() + var/drug_strength = 20 + M.druggy = max(M.druggy, drug_strength) + +/datum/reagent/drugs/bliss/overdose(var/mob/living/M as mob) + if(prob_proc == TRUE && prob(20)) + M.hallucination = max(M.hallucination, 5) + prob_proc = FALSE + M.adjustBrainLoss(0.25*REM) //Too much isn't good for your long term health... + M.adjustToxLoss(0.01*REM) //Enough that it'll make your HUD dummy update, but not enough that you'll vomit mid scene. (Sorry emetophiliacs!) + ..() + +/datum/reagent/paralysis_toxin + name = "Tetrodotoxin" + id = "paralysistoxin" + description = "A potent toxin commonly found in a plethora of species. When exposed to the toxin, causes extreme, paralysis for a prolonged period, with only essential functions of the body being unhindered. Commonly used by covert operatives and used as a crowd control tool." + taste_description = "bitterness" + reagent_state = LIQUID + color = "#37007f" + metabolism = REM * 0.25 + overdose = REAGENTS_OVERDOSE + scannable = 0 //YOU ARE NOT SCANNING THE FUNNY PARALYSIS TOXIN. NO. BAD. STAY AWAY. + +/datum/reagent/paralysis_toxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(M.weakened < 50) //Let's not leave them PERMA stuck, after all. + M.AdjustWeakened(5) //Stand in for paralyze so you can still talk/emote/see + +/datum/reagent/pain_enzyme + name = "Pain Enzyme" + id = "painenzyme" + description = "An enzyme found in a variety of species. When exposed to the toxin, will cause severe, agonizing pain. The effects can last for hours depending on the dose. Only known cure is an equally strong painkiller or dialysis." + taste_description = "sourness" + reagent_state = LIQUID + color = "#04b8fa" //Light blue in honor of Perry. + metabolism = 0.1 //Lasts up to 50 seconds if you give 5 units. + mrate_static = TRUE + overdose = 100 //There is no OD. You already are taking the worst of it. + scannable = 0 //Let's not have medical mechs able to make an extremely strong 'I hit you you fall down in agony' chem. + +/datum/reagent/pain_enzyme/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + M.add_chemical_effect(CE_PAINKILLER, -200) + if(prob(0.01)) //1 in 10000 chance per tick. Extremely rare. + to_chat(M,"Your body feels as though it's on fire!") diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index cd6753ba648..5bc3241e008 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -1265,7 +1265,7 @@ /obj/structure/disposalpipe/tagger/New() . = ..() dpdir = dir | turn(dir, 180) - if(sort_tag) GLOB.tagger_locations |= sort_tag + if(sort_tag) GLOB.tagger_locations |= list("[sort_tag]" = get_z(src)) updatename() updatedesc() update() @@ -1331,7 +1331,7 @@ /obj/structure/disposalpipe/sortjunction/New() . = ..() - if(sortType) GLOB.tagger_locations |= sortType + if(sortType) GLOB.tagger_locations |= list("[sortType]" = get_z(src)) updatedir() updatename() diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index b8b091444ef..a341e073368 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -348,11 +348,23 @@ ui = new(user, src, "DestinationTagger", name) ui.open() +/obj/item/device/destTagger/tgui_static_data(mob/user) + var/list/data = ..() + var/list/taggers = list() + var/list/tagger_levels = list() + for(var/tag in GLOB.tagger_locations) + var/z_level = GLOB.tagger_locations[tag] + taggers += list(list("tag" = tag, "level" = z_level)) + tagger_levels += list(list("z" = z_level, "location" = using_map.get_zlevel_name(z_level))) + data["taggerLevels"] = tagger_levels + data["taggerLocs"] = taggers + + return data + /obj/item/device/destTagger/tgui_data(mob/user, datum/tgui/ui) var/list/data = ..() data["currTag"] = currTag - data["taggerLocs"] = GLOB.tagger_locations return data diff --git a/code/modules/shuttles/escape_pods.dm b/code/modules/shuttles/escape_pods.dm index 7490887c20e..aa966b582a8 100644 --- a/code/modules/shuttles/escape_pods.dm +++ b/code/modules/shuttles/escape_pods.dm @@ -55,9 +55,9 @@ . = list( "docking_status" = docking_program.get_docking_status(), "override_enabled" = docking_program.override_enabled, - "exterior_status" = docking_program.memory["door_status"], - "can_force" = pod.can_force() || (emergency_shuttle.departed && pod.can_launch()), //allow players to manually launch ahead of time if the shuttle leaves - "armed" = pod.arming_controller.armed, + "exterior_status" = docking_program.memory["door_status"], // TGUI DATA fails silently when there's no linked pod, leading to UI crashes + "can_force" = pod?.can_force() || (emergency_shuttle.departed && pod?.can_launch()), //allow players to manually launch ahead of time if the shuttle leaves + "armed" = pod?.arming_controller.armed, "internalTemplateName" = "EscapePodConsole", ) diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index e55296c902f..9eeb4de6641 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -1,4 +1,3 @@ - /datum/tgui_module/appearance_changer name = "Appearance Editor" tgui_id = "AppearanceChanger" @@ -63,6 +62,9 @@ cam_background.del_on_map_removal = FALSE update_active_camera_screen() + if(customize_usr) + if(ishuman(usr)) + H = usr owner = H if(owner) owner.AddComponent(/datum/component/recursive_move) @@ -184,8 +186,8 @@ instance = null if(!istype(instance) && !params["clear"]) return FALSE - owner.ear_style = instance - owner.update_hair() + target.ear_style = instance + target.update_hair() update_dna() changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE @@ -197,7 +199,7 @@ target.g_ears = hex2num(copytext(new_hair, 4, 6)) target.b_ears = hex2num(copytext(new_hair, 6, 8)) update_dna() - owner.update_hair() + target.update_hair() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("ears2_color") @@ -208,7 +210,7 @@ target.g_ears2 = hex2num(copytext(new_hair, 4, 6)) target.b_ears2 = hex2num(copytext(new_hair, 6, 8)) update_dna() - owner.update_hair() + target.update_hair() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("tail") @@ -218,8 +220,8 @@ instance = null if(!istype(instance) && !params["clear"]) return FALSE - owner.tail_style = instance - owner.update_tail_showing() + target.tail_style = instance + target.update_tail_showing() update_dna() changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE @@ -231,7 +233,7 @@ target.g_tail = hex2num(copytext(new_hair, 4, 6)) target.b_tail = hex2num(copytext(new_hair, 6, 8)) update_dna() - owner.update_tail_showing() + target.update_tail_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("tail2_color") @@ -242,7 +244,7 @@ target.g_tail2 = hex2num(copytext(new_hair, 4, 6)) target.b_tail2 = hex2num(copytext(new_hair, 6, 8)) update_dna() - owner.update_tail_showing() + target.update_tail_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("wing") @@ -252,8 +254,8 @@ instance = null if(!istype(instance) && !params["clear"]) return FALSE - owner.wing_style = instance - owner.update_wing_showing() + target.wing_style = instance + target.update_wing_showing() update_dna() changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE @@ -265,7 +267,7 @@ target.g_wing = hex2num(copytext(new_hair, 4, 6)) target.b_wing = hex2num(copytext(new_hair, 6, 8)) update_dna() - owner.update_wing_showing() + target.update_wing_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("wing2_color") @@ -276,7 +278,7 @@ target.g_wing2 = hex2num(copytext(new_hair, 4, 6)) target.b_wing2 = hex2num(copytext(new_hair, 6, 8)) update_dna() - owner.update_wing_showing() + target.update_wing_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("marking") diff --git a/code/modules/tgui_panel/tgui_panel.dm b/code/modules/tgui_panel/tgui_panel.dm index 5dd06e3c36c..e9f6429f25e 100644 --- a/code/modules/tgui_panel/tgui_panel.dm +++ b/code/modules/tgui_panel/tgui_panel.dm @@ -48,13 +48,13 @@ assets = list( get_asset_datum(/datum/asset/simple/tgui_panel), )) - window.reinitialize() // Workaround for an early init fail... window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome)) window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/tgfont)) window.send_asset(get_asset_datum(/datum/asset/spritesheet/chat)) // Other setup request_telemetry() addtimer(CALLBACK(src, PROC_REF(on_initialize_timed_out)), 5 SECONDS) + window.send_message("testTelemetryCommand") /** * private diff --git a/code/modules/virus2/diseasesplicer.dm b/code/modules/virus2/diseasesplicer.dm index afc875a76e6..cdb7948bdd0 100644 --- a/code/modules/virus2/diseasesplicer.dm +++ b/code/modules/virus2/diseasesplicer.dm @@ -82,7 +82,7 @@ if(dish.growth >= 50) var/list/effects[0] for (var/datum/disease2/effectholder/e in dish.virus2.effects) - effects.Add(list(list("name" = (dish.analysed ? e.effect.name : "Unknown"), "stage" = (e.stage), "reference" = "\ref[e]"))) + effects.Add(list(list("name" = (dish.analysed ? e.effect.name : "Unknown"), "stage" = (e.stage), "reference" = "\ref[e]"), "badness" = e.effect.badness)) data["effects"] = effects else data["info"] = "Insufficient cell growth for gene splicing." @@ -191,4 +191,3 @@ if("disk") burning = 10 . = TRUE - diff --git a/code/modules/vore/eating/belly_obj_vr.dm b/code/modules/vore/eating/belly_obj_vr.dm index 21602a1c27f..23b07967893 100644 --- a/code/modules/vore/eating/belly_obj_vr.dm +++ b/code/modules/vore/eating/belly_obj_vr.dm @@ -8,6 +8,12 @@ // // Parent type of all the various "belly" varieties. // + +#define DM_FLAG_VORESPRITE_TAIL 0x2 +#define DM_FLAG_VORESPRITE_MARKING 0x4 +#define DM_FLAG_VORESPRITE_ARTICLE 0x8 + + /obj/belly name = "belly" // Name of this location desc = "It's a belly! You're in it!" // Flavor text description of inside sight/sound/smells/feels. @@ -60,6 +66,30 @@ var/belly_item_mult = 1 //Multiplier for how filling items are in borg borg bellies. Items are also weighted on item size var/belly_overall_mult = 1 //Multiplier applied ontop of any other specific multipliers + + var/vore_sprite_flags = DM_FLAG_VORESPRITE_ARTICLE + var/tmp/static/list/vore_sprite_flag_list= list( + "Normal Belly Sprite" = DM_FLAG_VORESPRITE_ARTICLE, + //"Tail adjustment" = DM_FLAG_VORESPRITE_TAIL, + //"Marking addition" = DM_FLAG_VORESPRITE_MARKING + ) + var/affects_vore_sprites = FALSE + var/count_absorbed_prey_for_sprite = TRUE + var/absorbed_multiplier = 1 + var/count_liquid_for_sprite = FALSE + var/liquid_multiplier = 1 + var/count_items_for_sprite = FALSE + var/item_multiplier = 1 + var/health_impacts_size = TRUE + var/resist_triggers_animation = TRUE + var/size_factor_for_sprite = 1 + var/belly_sprite_to_affect = "stomach" + var/datum/sprite_accessory/tail/tail_to_change_to = FALSE + var/tail_colouration = FALSE + var/tail_extra_overlay = FALSE + var/tail_extra_overlay2 = FALSE + var/undergarment_chosen = "Underwear, bottom" + // Generally just used by AI var/autotransferchance = 0 // % Chance of prey being autotransferred to transfer location var/autotransferwait = 10 // Time between trying to transfer. @@ -345,6 +375,15 @@ "belly_item_mult", "belly_overall_mult", "drainmode", + "vore_sprite_flags", + "affects_vore_sprites", + "count_absorbed_prey_for_sprite", + "resist_triggers_animation", + "size_factor_for_sprite", + "belly_sprite_to_affect", + "health_impacts_size", + "count_items_for_sprite", + "item_multiplier" ) if (save_digest_mode == 1) @@ -426,6 +465,10 @@ if(M.ai_holder) M.ai_holder.handle_eaten() + if (istype(owner, /mob/living/carbon/human)) + var/mob/living/carbon/human/hum = owner + hum.update_fullness() + // Intended for simple mobs if(!owner.client && autotransferlocation && autotransferchance > 0) addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/belly, check_autotransfer), thing, autotransferlocation), autotransferwait) @@ -444,6 +487,10 @@ L.toggle_hud_vis() if((L.stat != DEAD) && L.ai_holder) L.ai_holder.go_wake() + if (istype(owner, /mob/living/carbon/human)) + var/mob/living/carbon/human/hum = owner + hum.update_fullness() + /obj/belly/proc/vore_fx(mob/living/L) if(!istype(L)) @@ -1757,6 +1804,15 @@ dupe.belly_mob_mult = belly_mob_mult dupe.belly_item_mult = belly_item_mult dupe.belly_overall_mult = belly_overall_mult + dupe.vore_sprite_flags = vore_sprite_flags + dupe.affects_vore_sprites = affects_vore_sprites + dupe.count_absorbed_prey_for_sprite = count_absorbed_prey_for_sprite + dupe.resist_triggers_animation = resist_triggers_animation + dupe.size_factor_for_sprite = size_factor_for_sprite + dupe.belly_sprite_to_affect = belly_sprite_to_affect + dupe.health_impacts_size = health_impacts_size + dupe.count_items_for_sprite = count_items_for_sprite + dupe.item_multiplier = item_multiplier //// Object-holding variables //struggle_messages_outside - strings @@ -1955,3 +2011,38 @@ /obj/belly/container_resist(mob/M) return relay_resist(M) + +/obj/belly/proc/GetFullnessFromBelly() + if(!affects_vore_sprites) + return 0 + var/belly_fullness = 0 + for(var/mob/living/M in src) + if(count_absorbed_prey_for_sprite || !M.absorbed) + var/fullness_to_add = M.size_multiplier + fullness_to_add *= M.mob_size / 20 + if(M.absorbed) + fullness_to_add *= absorbed_multiplier + if(health_impacts_size) + fullness_to_add *= M.health / M.getMaxHealth() + belly_fullness += fullness_to_add + if(count_liquid_for_sprite) + belly_fullness += (reagents.total_volume / 100) * liquid_multiplier + if(count_items_for_sprite) + for(var/obj/item/I in src) + var/fullness_to_add = 0 + if(I.w_class == ITEMSIZE_TINY) + fullness_to_add = ITEMSIZE_COST_TINY + else if(I.w_class == ITEMSIZE_SMALL) + fullness_to_add = ITEMSIZE_COST_SMALL + else if(I.w_class == ITEMSIZE_NORMAL) + fullness_to_add = ITEMSIZE_COST_NORMAL + else if(I.w_class == ITEMSIZE_LARGE) + fullness_to_add = ITEMSIZE_COST_LARGE + else if(I.w_class == ITEMSIZE_HUGE) + fullness_to_add = ITEMSIZE_COST_HUGE + else + fullness_to_add = ITEMSIZE_COST_NO_CONTAINER + fullness_to_add /= 32 + belly_fullness += fullness_to_add * item_multiplier + belly_fullness *= size_factor_for_sprite + return belly_fullness diff --git a/code/modules/vore/eating/bellymodes_datum_vr.dm b/code/modules/vore/eating/bellymodes_datum_vr.dm index b372dc2ce95..9c49d8f93f0 100644 --- a/code/modules/vore/eating/bellymodes_datum_vr.dm +++ b/code/modules/vore/eating/bellymodes_datum_vr.dm @@ -35,6 +35,10 @@ GLOBAL_LIST_INIT(digest_modes, list()) else SEND_SOUND(L, sound(get_sfx("fancy_death_prey"))) B.handle_digestion_death(L) + if(!L) + if (istype(B.owner, /mob/living/carbon/human)) + var/mob/living/carbon/human/howner = B.owner + howner.update_fullness() if(!B.fancy_vore) return list("to_update" = TRUE, "soundToPlay" = sound(get_sfx("classic_death_sounds"))) return list("to_update" = TRUE, "soundToPlay" = sound(get_sfx("fancy_death_pred"))) @@ -61,6 +65,11 @@ GLOBAL_LIST_INIT(digest_modes, list()) var/offset = (1 + ((L.weight - 137) / 137)) // 130 pounds = .95 140 pounds = 1.02 var/difference = B.owner.size_multiplier / L.size_multiplier + if(B.health_impacts_size) + if (istype(B.owner, /mob/living/carbon/human)) + var/mob/living/carbon/human/howner = B.owner + howner.update_fullness() + consider_healthbar(L, old_health, B.owner) if(isrobot(B.owner)) diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index f4bf8733160..9e78aebd3d2 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -34,6 +34,10 @@ 'sound/effects/mob_effects/xenochimera/regen_5.ogg' ) var/trash_catching = FALSE //Toggle for trash throw vore from chompstation + var/list/trait_injection_reagents = list() //List of all the reagents allowed to be used for injection via venom bite + var/trait_injection_selected = null //RSEdit: What trait reagent you're injecting. + var/trait_injection_amount = 5 //RSEdit: How much you're injecting with traits. + var/trait_injection_verb = "bites" //RSEdit: Which fluffy manner you're doing the injecting. // // Hook for generic creation of stuff on new creatures @@ -256,6 +260,8 @@ P.weight_message_visible = src.weight_message_visible P.weight_messages = src.weight_messages + P.vore_sprite_color = istype(src, /mob/living/carbon/human) ? src:vore_sprite_color : null + var/list/serialized = list() for(var/obj/belly/B as anything in src.vore_organs) serialized += list(B.serialize()) //Can't add a list as an object to another list in Byond. Thanks. @@ -306,6 +312,10 @@ weight_message_visible = P.weight_message_visible weight_messages = P.weight_messages + + if (istype(src, /mob/living/carbon/human)) + src:vore_sprite_color = P.vore_sprite_color + if(bellies) if(isliving(src)) var/mob/living/L = src diff --git a/code/modules/vore/eating/vore_vr.dm b/code/modules/vore/eating/vore_vr.dm index 7888cdb010a..d7fe76ed5a2 100644 --- a/code/modules/vore/eating/vore_vr.dm +++ b/code/modules/vore/eating/vore_vr.dm @@ -69,6 +69,8 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE var/step_mechanics_pref = FALSE var/pickup_pref = TRUE + var/vore_sprite_color = list("stomach" = "#000", "taur belly" = "#000") + var/list/belly_prefs = list() var/vore_taste = "nothing in particular" var/vore_smell = "nothing in particular" @@ -193,6 +195,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE weight_message_visible = json_from_file["weight_message_visible"] weight_messages = json_from_file["weight_messages"] eating_privacy_global = json_from_file["eating_privacy_global"] + vore_sprite_color = json_from_file["vore_sprite_color"] //Quick sanitize if(isnull(digestable)) @@ -279,7 +282,8 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE else if(weight_messages.len < 10) while(weight_messages.len < 10) weight_messages.Add("") - + if(isnull(vore_sprite_color)) + vore_sprite_color = list("stomach" = "#000", "taur belly" = "#000") return TRUE /datum/vore_preferences/proc/save_vore() @@ -320,6 +324,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE "weight_message_visible" = weight_message_visible, "weight_messages" = weight_messages, "eating_privacy_global" = eating_privacy_global, + "vore_sprite_color" = vore_sprite_color, ) //List to JSON diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm index bb59b7c7adb..d2286f78b59 100644 --- a/code/modules/vore/eating/vorepanel_vr.dm +++ b/code/modules/vore/eating/vorepanel_vr.dm @@ -227,7 +227,23 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono", "belly_item_mult" = selected.belly_item_mult, "belly_overall_mult" = selected.belly_overall_mult, "drainmode" = selected.drainmode, - + "affects_voresprite" = selected.affects_vore_sprites, + "absorbed_voresprite" = selected.count_absorbed_prey_for_sprite, + "absorbed_multiplier" = selected.absorbed_multiplier, + "liquid_voresprite" = selected.count_liquid_for_sprite, + "liquid_multiplier" = selected.liquid_multiplier, + "item_voresprite" = selected.count_items_for_sprite, + "item_multiplier" = selected.item_multiplier, + "health_voresprite" = selected.health_impacts_size, + "resist_animation" = selected.resist_triggers_animation, + "voresprite_size_factor" = selected.size_factor_for_sprite, + "belly_sprite_to_affect" = selected.belly_sprite_to_affect, + "belly_sprite_option_shown" = istype(host, /mob/living/carbon/human) ? (LAZYLEN(host:vore_icon_bellies) >= 1 ? TRUE : FALSE) : FALSE, // TODO: FIX THIS (It won't be fixed) + "tail_option_shown" = istype(host, /mob/living/carbon/human), + "tail_to_change_to" = selected.tail_to_change_to, + "tail_colouration" = selected.tail_colouration, + "tail_extra_overlay" = selected.tail_extra_overlay, + "tail_extra_overlay2" = selected.tail_extra_overlay2 ) var/list/addons = list() @@ -236,6 +252,13 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono", addons.Add(flag_name) selected_list["addons"] = addons + var/list/vs_flags = list() + for(var/flag_name in selected.vore_sprite_flag_list) + if(selected.vore_sprite_flags & selected.vore_sprite_flag_list[flag_name]) + vs_flags.Add(flag_name) + selected_list["vore_sprite_flags"] = vs_flags + + selected_list["egg_type"] = selected.egg_type selected_list["contaminates"] = selected.contaminates selected_list["contaminate_flavor"] = null @@ -609,6 +632,20 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono", host.weight_message_visible = !host.weight_message_visible unsaved_changes = TRUE return TRUE + if("set_vs_color") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/belly_choice = tgui_input_list(usr, "Which vore sprite are you going to edit the color of?", "Vore Sprite Color", hhost.vore_icon_bellies) + var/newcolor = input(usr, "Choose a color.", "", hhost.vore_sprite_color[belly_choice]) as color|null + if(newcolor) + hhost.vore_sprite_color[belly_choice] = newcolor + var/multiply = tgui_input_list(usr, "Set the color to be applied multiplicatively or additively? Currently in [hhost.vore_sprite_multiply[belly_choice] ? "Multiply" : "Add"]", "Vore Sprite Color", list("Multiply", "Add")) + if(multiply == "Multiply") + hhost.vore_sprite_multiply[belly_choice] = TRUE + else if(multiply == "Add") + hhost.vore_sprite_multiply[belly_choice] = FALSE + hhost.update_icons_body() + return TRUE /datum/vore_look/proc/pick_from_inside(mob/user, params) var/atom/movable/target = locate(params["pick"]) @@ -1680,6 +1717,98 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono", qdel(host.vore_selected) host.vore_selected = host.vore_organs[1] . = TRUE - + if("b_belly_sprite_to_affect") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/belly_choice = tgui_input_list(usr, "Which belly sprite do you want your [lowertext(hhost.vore_selected.name)] to affect?","Select Region", hhost.vore_icon_bellies) + if(!belly_choice) //They cancelled, no changes + return FALSE + else + hhost.vore_selected.belly_sprite_to_affect = belly_choice + hhost.update_fullness() + . = TRUE + if("b_affects_vore_sprites") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + hhost.vore_selected.affects_vore_sprites = !hhost.vore_selected.affects_vore_sprites + hhost.update_fullness() + . = TRUE + if("b_count_absorbed_prey_for_sprites") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + hhost.vore_selected.count_absorbed_prey_for_sprite = !hhost.vore_selected.count_absorbed_prey_for_sprite + hhost.update_fullness() + . = TRUE + if("b_absorbed_multiplier") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/absorbed_multiplier_input = input(user, "Set the impact absorbed prey's size have on your vore sprite. 1 means no scaling, 0.5 means absorbed prey count half as much, 2 means absorbed prey count double. (Range from 0.1 - 3)", "Absorbed Multiplier") as num|null + if(!isnull(absorbed_multiplier_input)) + hhost.vore_selected.absorbed_multiplier = CLAMP(absorbed_multiplier_input, 0.1, 3) + hhost.update_fullness() + . = TRUE + if("b_count_items_for_sprites") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + hhost.vore_selected.count_items_for_sprite = !hhost.vore_selected.count_items_for_sprite + hhost.update_fullness() + . = TRUE + if("b_item_multiplier") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/item_multiplier_input = input(user, "Set the impact items will have on your vore sprite. 1 means a belly with 8 normal-sized items will count as 1 normal sized prey-thing's worth, 0.5 means items count half as much, 2 means items count double. (Range from 0.1 - 10)", "Item Multiplier") as num|null + if(!isnull(item_multiplier_input)) + hhost.vore_selected.item_multiplier = CLAMP(item_multiplier_input, 0.1, 10) + hhost.update_fullness() + . = TRUE + if("b_health_impacts_size") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + hhost.vore_selected.health_impacts_size = !hhost.vore_selected.health_impacts_size + hhost.update_fullness() + . = TRUE + if("b_resist_animation") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + hhost.vore_selected.resist_triggers_animation = !hhost.vore_selected.resist_triggers_animation + . = TRUE + if("b_size_factor_sprites") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/size_factor_input = input(user, "Set the impact all belly content's collective size has on your vore sprite. 1 means no scaling, 0.5 means content counts half as much, 2 means contents count double. (Range from 0.1 - 3)", "Size Factor") as num|null + if(!isnull(size_factor_input)) + hhost.vore_selected.size_factor_for_sprite = CLAMP(size_factor_input, 0.1, 3) + hhost.update_fullness() + . = TRUE + if("b_tail_to_change_to") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/tail_choice = tgui_input_list(usr, "Which tail sprite do you want to use when your [lowertext(host.vore_selected.name)] is filled?","Select Sprite", global.tail_styles_list) + if(!tail_choice) //They cancelled, no changes + return FALSE + else + hhost.vore_selected.tail_to_change_to = tail_choice + . = TRUE + if("b_tail_color") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/newcolor = input(usr, "Choose tail color.", "", hhost.vore_selected.tail_colouration) as color|null + if(newcolor) + hhost.vore_selected.tail_colouration = newcolor + . = TRUE + if("b_tail_color2") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/newcolor = input(usr, "Choose tail secondary color.", "", hhost.vore_selected.tail_extra_overlay) as color|null + if(newcolor) + hhost.vore_selected.tail_extra_overlay = newcolor + . = TRUE + if("b_tail_color3") + if (istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/hhost = host + var/newcolor = input(usr, "Choose tail tertiary color.", "", hhost.vore_selected.tail_extra_overlay2) as color|null + if(newcolor) + hhost.vore_selected.tail_extra_overlay2 = newcolor + . = TRUE if(.) unsaved_changes = TRUE diff --git a/code/modules/vore/resizing/crackers.dm b/code/modules/vore/resizing/crackers.dm new file mode 100644 index 00000000000..b88a87cb2a7 --- /dev/null +++ b/code/modules/vore/resizing/crackers.dm @@ -0,0 +1,177 @@ +//A bluespace cracker item that can be pulled between two characters. +//The winner of the pull has an effect applied to them. +//Crackers do already exist, but these ones are a more memey scene item. + +/obj/item/weapon/cracker + name = "bluespace cracker" //I have no idea why this was called shrink ray when this increased and decreased size. + desc = "A celebratory little game with a bluespace twist! Pull it between two people until it snaps, and the person who recieves the larger end gets a prize!" + icon = 'icons/obj/crackers.dmi' + icon_state = "blue" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_cracker.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_cracker.dmi', + ) + item_state = "blue" + var/rigged = 0 //So that they can be rigged by varedits to go one way or the other. positive values mean holder always wins, negative values mean target always wins. + var/list/prizes = list("shrinking","growing","drugged","invisibility","knocked over","teleport","wealth") + var/list/jokes = list( + "When is a boat just like snow? When it’s adrift.", + "What happens to naughty elves? Santa gives them the sack.", + "What do you call an old snowman? Water.", + "Why has Santa been banned from sooty chimneys? Carbon footprints.", + "What goes down but doesn't come up? A yo.", + "What's green and fuzzy, has four legs and would kill you if it fell from a tree? A pool table.", + "Why did the blind man fall into the well? Because he couldn't see that well.", + "What did the pirate get on his report card? Seven Cs", + "What do you call a fish with no eyes? Fsh", + "How do you make an egg roll? You push it.", + "What do you call a deer with no eyes? NO EYED DEER!", + "What's red, and smells like blue paint? Red paint.", + "Where do cows go to dance? A meat ball.", + "What do you call a person who steals all your toenail clippings? A cliptoemaniac.", + "What’s brown and sticky? A stick.", + "What's the best way to kill a circus? Go for the juggler.", + "What do you call a cow with no legs? Ground Beef.", + "Why'd the scarecrow win the Nobel prize? He was outstanding in his field.") + +/obj/item/weapon/cracker/attack(atom/A, mob/living/user, adjacent, params) + var/mob/living/carbon/human/target = A + if(!istype(target)) + return + if(target.stat) + return + if(target == user) + to_chat(user, "You can't pull \the [src] by yourself, that would just be sad!") + return + var/check_pull = tgui_alert(target, "\The [user] is offering to pull \the [src] with you, do you want to pull it?", "Pull Cracker", list("Yes", "No")) + if(check_pull == "No") + to_chat(user, "\The [target] chose not to pull \the [src]!") + return + if(!adjacent) + to_chat(user, "\The [target] is not standing close enough to pull \the [src]!") + return + var/prize = pick(prizes) + var/joke = pick(jokes) + var/mob/living/carbon/human/winner + var/mob/living/carbon/human/loser + if(!rigged) + if(prob(50)) + winner = user + loser = target + else + winner = target + loser = user + else + if(rigged > 0) + winner = user + loser = target + else + winner = target + loser = user + + var/spawnloc = get_turf(winner) + + winner.visible_message("\The [winner] wins the cracker prize!","You win the cracker prize!") + if(prize == "shrinking") + winner.resize(0.25) + winner.visible_message("\The [winner] shrinks suddenly!") + if(prize == "growing") + winner.resize(2) + winner.visible_message("\The [winner] grows in height suddenly.") + if(prize == "drugged") + winner.druggy = max(winner.druggy, 50) + if(prize == "invisibility") + if(!winner.cloaked) + winner.visible_message("\The [winner] vanishes from sight.") + winner.cloak() + spawn(600) + if(winner.cloaked) + winner.uncloak() + winner.visible_message("\The [winner] appears as if from thin air.") + if(prize == "knocked over") + winner.visible_message("\The [winner] is suddenly knocked to the ground.") + winner.weakened = max(winner.weakened,50) + if(prize == "teleport") + if(loser.can_be_drop_pred && loser.vore_selected) + if(winner.devourable && winner.can_be_drop_prey) + winner.visible_message("\The [winner] is teleported to somewhere nearby...") + var/datum/effect/effect/system/spark_spread/spk + spk = new(winner) + + var/T = get_turf(winner) + spk.set_up(5, 0, winner) + spk.attach(winner) + playsound(T, "sparks", 50, 1) + anim(T,winner,'icons/mob/mob.dmi',,"phaseout",,winner.dir) + winner.forceMove(loser.vore_selected) + if(prize == "wealth") + new /obj/random/cash/huge(spawnloc) + new /obj/random/cash/huge(spawnloc) + winner.visible_message("\The [winner] has a whole load of cash fall at their feet!") + + playsound(user, 'sound/effects/snap.ogg', 50, 1) + user.drop_item(src) + new /obj/random/toy(spawnloc) + new /obj/item/clothing/head/paper_crown(spawnloc) + var/obj/item/weapon/paper/cracker_joke/J = new(spawnloc) + J.info = joke + qdel(src) + +/obj/item/weapon/cracker/Initialize() + var/list/styles = list("blue","green","yellow","red","heart","hazard") + var/style = pick(styles) + icon_state = style + item_state = style + ..() + +/obj/item/weapon/cracker/shrinking + name = "shrinking bluespace cracker" + prizes = list("shrinking") + +/obj/item/weapon/cracker/growing + name = "growing bluespace cracker" + prizes = list("growing") + +/obj/item/weapon/cracker/invisibility + name = "cloaking bluespace cracker" + prizes = list("invisibility") + +/obj/item/weapon/cracker/drugged + name = "psychedelic bluespace cracker" + prizes = list("drugged") + +/obj/item/weapon/cracker/knockover + name = "forceful bluespace cracker" + prizes = list("knocked over") + +/obj/item/weapon/cracker/vore + name = "teleporting bluespace cracker" + prizes = list("teleport") + +/obj/item/weapon/cracker/money + name = "fortuitous bluespace cracker" + prizes = list("wealth") + +/obj/item/clothing/head/paper_crown + name = "paper crown" + icon_state = "paper_crown_blue" + item_state = "paper_crown_blue" + item_icons = list(slot_head_str = 'icons/inventory/head/mob.dmi') + desc = "A paper crown so thin that you can see through it." + flags_inv = 0 + body_parts_covered = 0 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/head/paper_crown/Initialize() + var/list/styles = list("paper_crown_blue","paper_crown_green","paper_crown_yellow","paper_crown_red","paper_crown_pink") + var/style = pick(styles) + icon_state = style + item_state = style + ..() + +/obj/item/weapon/paper/cracker_joke + name = "joke" + icon_state = "joke" + +/obj/item/weapon/paper/cracker_joke/update_icon() + return diff --git a/code/modules/vore/resizing/sizegun_vr.dm b/code/modules/vore/resizing/sizegun_vr.dm index 4bbaaff0433..2bbd6010354 100644 --- a/code/modules/vore/resizing/sizegun_vr.dm +++ b/code/modules/vore/resizing/sizegun_vr.dm @@ -15,6 +15,7 @@ origin_tech = list(TECH_BLUESPACE = 4) modifystate = "sizegun-shrink" battery_lock = 1 + var/backfire = 0 var/size_set_to = 1 firemodes = list( list(mode_name = "select size", @@ -133,6 +134,54 @@ size_set_to = clamp((size_select/100), 0, 1000) //eheh to_chat(usr, "You set the size to [size_select]%") +/obj/item/weapon/gun/energy/sizegun/afterattack(atom/A, mob/living/user, adjacent, params) + if(adjacent) return //A is adjacent, is the user, or is on the user's person + + if(backfire) + if(prob(50)) + to_chat(user, "\The [src] backfires and consumes its entire charge!") + Fire(user, user) + power_supply.charge = 0 + var/mob/living/M = loc // TGMC Ammo HUD + if(istype(M)) // TGMC Ammo HUD + M?.hud_used.update_ammo_hud(M, src) + return + else + return ..() + else + return ..() + +/obj/item/weapon/gun/energy/sizegun/attack(atom/A, mob/living/user, adjacent, params) + if(backfire) + if(prob(50)) + to_chat(user, "\The [src] backfires and consumes its entire charge!") + Fire(user, user) + power_supply.charge = 0 + var/mob/living/M = loc // TGMC Ammo HUD + if(istype(M)) // TGMC Ammo HUD + M?.hud_used.update_ammo_hud(M, src) + return + else + return ..() + else + return ..() + + +/obj/item/weapon/gun/energy/sizegun/attackby(var/obj/item/A as obj, mob/user as mob) + if(A.has_tool_quality(TOOL_WIRECUTTER)) + if(backfire) + to_chat(user, "You repair the damage to the \the [src].") + backfire = 0 + name = "size gun" + else + to_chat(user, "You snip a wire on \the [src], making it less reliable.") + backfire = 1 + name = "unstable size gun" + ..() + +/obj/item/weapon/gun/energy/sizegun/backfire + name = "unstable size gun" + backfire = 1 /obj/item/weapon/gun/energy/sizegun/mounted name = "mounted size gun" diff --git a/code/modules/xenobio2/machinery/injector_computer.dm b/code/modules/xenobio2/machinery/injector_computer.dm index 196543a2560..e4ac65403a4 100644 --- a/code/modules/xenobio2/machinery/injector_computer.dm +++ b/code/modules/xenobio2/machinery/injector_computer.dm @@ -46,6 +46,7 @@ ..() +//Is missing a tgui interface? /obj/machinery/computer/xenobio2/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!user) return diff --git a/icons/inventory/head/item.dmi b/icons/inventory/head/item.dmi index 538c3a57ee2..7897ed22072 100644 Binary files a/icons/inventory/head/item.dmi and b/icons/inventory/head/item.dmi differ diff --git a/icons/inventory/head/mob.dmi b/icons/inventory/head/mob.dmi index 635188fbada..8f7e232b3d2 100644 Binary files a/icons/inventory/head/mob.dmi and b/icons/inventory/head/mob.dmi differ diff --git a/icons/mob/human_face_vr.dmi b/icons/mob/human_face_vr.dmi index ea687def736..5da8720d000 100644 Binary files a/icons/mob/human_face_vr.dmi and b/icons/mob/human_face_vr.dmi differ diff --git a/icons/mob/human_races/r_lleill.dmi b/icons/mob/human_races/r_lleill.dmi new file mode 100644 index 00000000000..a3f6d1429b5 Binary files /dev/null and b/icons/mob/human_races/r_lleill.dmi differ diff --git a/icons/mob/items/lefthand_cracker.dmi b/icons/mob/items/lefthand_cracker.dmi new file mode 100644 index 00000000000..6e2d5776fce Binary files /dev/null and b/icons/mob/items/lefthand_cracker.dmi differ diff --git a/icons/mob/items/righthand_cracker.dmi b/icons/mob/items/righthand_cracker.dmi new file mode 100644 index 00000000000..0818ed3ebc6 Binary files /dev/null and b/icons/mob/items/righthand_cracker.dmi differ diff --git a/icons/mob/species/lleill/tail.dmi b/icons/mob/species/lleill/tail.dmi new file mode 100644 index 00000000000..3ff90038904 Binary files /dev/null and b/icons/mob/species/lleill/tail.dmi differ diff --git a/icons/mob/vore.dmi b/icons/mob/vore.dmi index 2aabf0438b7..e11ef54c8d7 100644 Binary files a/icons/mob/vore.dmi and b/icons/mob/vore.dmi differ diff --git a/icons/mob/vore/Bellies.dmi b/icons/mob/vore/Bellies.dmi new file mode 100644 index 00000000000..4f0d94f0083 Binary files /dev/null and b/icons/mob/vore/Bellies.dmi differ diff --git a/icons/mob/vore/Taur_Bellies.dmi b/icons/mob/vore/Taur_Bellies.dmi new file mode 100644 index 00000000000..e606c64d183 Binary files /dev/null and b/icons/mob/vore/Taur_Bellies.dmi differ diff --git a/icons/mob/vore/ears_32x64.dmi b/icons/mob/vore/ears_32x64.dmi index cfae3acbcb1..1e73072d946 100644 Binary files a/icons/mob/vore/ears_32x64.dmi and b/icons/mob/vore/ears_32x64.dmi differ diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi index 5c0531f5b7d..61365297d24 100644 Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ diff --git a/icons/obj/crackers.dmi b/icons/obj/crackers.dmi new file mode 100644 index 00000000000..ac657f46f92 Binary files /dev/null and b/icons/obj/crackers.dmi differ diff --git a/icons/obj/glamour.dmi b/icons/obj/glamour.dmi new file mode 100644 index 00000000000..2682d7badcf Binary files /dev/null and b/icons/obj/glamour.dmi differ diff --git a/icons/obj/seeds.dmi b/icons/obj/seeds.dmi index e181299f135..6b5dc5dab58 100644 Binary files a/icons/obj/seeds.dmi and b/icons/obj/seeds.dmi differ diff --git a/icons/obj/stationobjs_vr.dmi b/icons/obj/stationobjs_vr.dmi index aa6df06395e..8ce777ec974 100644 Binary files a/icons/obj/stationobjs_vr.dmi and b/icons/obj/stationobjs_vr.dmi differ diff --git a/icons/turf/flooring/glamour.dmi b/icons/turf/flooring/glamour.dmi index 74ee6430659..8f60748a652 100644 Binary files a/icons/turf/flooring/glamour.dmi and b/icons/turf/flooring/glamour.dmi differ diff --git a/maps/redgate/fantasy_dungeon.dmm b/maps/redgate/fantasy_dungeon.dmm index 02cbdea6c88..96aef51a37d 100644 --- a/maps/redgate/fantasy_dungeon.dmm +++ b/maps/redgate/fantasy_dungeon.dmm @@ -559,10 +559,7 @@ /turf/simulated/floor/wmarble, /area/redgate/fantasy/dungeon) "me" = ( -/mob/living/simple_mob/vore/demon/engorge{ - ai_holder_type = /datum/ai_holder/simple_mob/vore; - vore_pounce_chance = 50 - }, +/mob/living/simple_mob/vore/sonadile, /turf/simulated/floor/cult, /area/redgate/fantasy/dungeon) "ml" = ( @@ -1625,14 +1622,7 @@ /turf/simulated/floor/concrete, /area/redgate/fantasy/ratbasement) "Hn" = ( -/mob/living/simple_mob/vore/demon{ - ai_holder_type = /datum/ai_holder/simple_mob/vore; - harm_intent_damage = 8; - health = 300; - melee_damage_lower = 5; - melee_damage_upper = 2; - vore_pounce_chance = 50 - }, +/mob/living/simple_mob/vore/devil, /turf/simulated/floor/cult, /area/redgate/fantasy/dungeon) "Hx" = ( @@ -11776,7 +11766,7 @@ kn jI ah ah -Hn +ah ah Xp hR @@ -12346,7 +12336,7 @@ ah ah ah ah -Hn +ah ah kn kn diff --git a/maps/redgate/jungle.dmm b/maps/redgate/jungle.dmm index 4c5e0f0f1e3..9308753680f 100644 --- a/maps/redgate/jungle.dmm +++ b/maps/redgate/jungle.dmm @@ -728,14 +728,7 @@ /turf/simulated/shuttle/wall, /area/redgate/jungle/westcaves) "ik" = ( -/mob/living/simple_mob/vore/demon{ - ai_holder_type = /datum/ai_holder/simple_mob/vore; - harm_intent_damage = 8; - health = 300; - melee_damage_lower = 5; - melee_damage_upper = 2; - vore_pounce_chance = 50 - }, +/mob/living/simple_mob/vore/devil, /turf/simulated/floor/gorefloor, /area/redgate/jungle/temple) "il" = ( @@ -3510,13 +3503,6 @@ }, /turf/simulated/floor/outdoors/grass/forest, /area/redgate/jungle/aboveground) -"PG" = ( -/mob/living/simple_mob/vore/demon/engorge{ - ai_holder_type = /datum/ai_holder/simple_mob/vore; - vore_pounce_chance = 50 - }, -/turf/simulated/floor/gorefloor2, -/area/redgate/jungle/temple) "PK" = ( /obj/item/weapon/bedsheet/pillow, /turf/simulated/mineral/floor/cave, @@ -9402,7 +9388,7 @@ Ji Ji pL GT -ik +GT GT GT rI @@ -9545,7 +9531,7 @@ Ji ue GT GT -GT +ik GT rI rI @@ -9689,7 +9675,7 @@ GT GT GT rI -PG +rI rI pL pL diff --git a/tgui/packages/common/type-utils.ts b/tgui/packages/common/type-utils.ts new file mode 100644 index 00000000000..a73c0c1d595 --- /dev/null +++ b/tgui/packages/common/type-utils.ts @@ -0,0 +1,41 @@ +/** + * Helps visualize highly complex ui data on the fly. + * @example + * ```tsx + * const { data } = useBackend(); + * logger.log(getShallowTypes(data)); + * ``` + */ +export function getShallowTypes( + data: Record, +): Record { + const output = {}; + + for (const key in data) { + if (Array.isArray(data[key])) { + const arr: any[] = data[key]; + + // Return the first array item if it exists + if (data[key].length > 0) { + output[key] = arr[0]; + continue; + } + + output[key] = 'emptyarray'; + } else if (typeof data[key] === 'object' && data[key] !== null) { + // Please inspect it further and make a new type for it + output[key] = 'object (inspect) || Record'; + } else if (typeof data[key] === 'number') { + const num = Number(data[key]); + + // 0 and 1 could be booleans from byond + if (num === 1 || num === 0) { + output[key] = `${num}, BooleanLike?`; + continue; + } + output[key] = data[key]; + } + } + + return output; +} diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json index 5f1b5be3e54..3a92ec35c95 100644 --- a/tgui/packages/tgui-dev-server/package.json +++ b/tgui/packages/tgui-dev-server/package.json @@ -8,6 +8,6 @@ "glob": "^7.2.3", "source-map": "^0.7.4", "stacktrace-parser": "^0.1.10", - "ws": "^8.16.0" + "ws": "^8.17.1" } } diff --git a/tgui/packages/tgui-panel/chat/middleware.js b/tgui/packages/tgui-panel/chat/middleware.js index 57dab303b5f..f1745b353bf 100644 --- a/tgui/packages/tgui-panel/chat/middleware.js +++ b/tgui/packages/tgui-panel/chat/middleware.js @@ -170,7 +170,7 @@ export const chatMiddleware = (store) => { settings.interleaveColor, ); // Load the chat once settings are loaded - if (!initialized && settings.initialized) { + if (!initialized && (settings.initialized || settings.firstLoad)) { initialized = true; setInterval(() => { saveChatToStorage(store); diff --git a/tgui/packages/tgui-panel/settings/reducer.js b/tgui/packages/tgui-panel/settings/reducer.js index cf5467831cd..c34ccf554a1 100644 --- a/tgui/packages/tgui-panel/settings/reducer.js +++ b/tgui/packages/tgui-panel/settings/reducer.js @@ -57,6 +57,7 @@ const initialState = { exportEnd: 0, lastId: null, initialized: false, + firstLoad: false, storedTypes: {}, hideImportantInAdminTab: false, interleave: false, @@ -82,6 +83,7 @@ export const settingsReducer = (state = initialState, action) => { if (type === loadSettings.type) { // Validate version and/or migrate state if (!payload?.version) { + state.firstLoad = true; return state; } diff --git a/tgui/packages/tgui-panel/telemetry.js b/tgui/packages/tgui-panel/telemetry.js index 3b1eb9a9500..581d32f3069 100644 --- a/tgui/packages/tgui-panel/telemetry.js +++ b/tgui/packages/tgui-panel/telemetry.js @@ -36,6 +36,14 @@ export const telemetryMiddleware = (store) => { Byond.sendMessage('telemetry', { connections }); return; } + // For whatever reason we didn't get the telemetry, re-request + if (type === 'testTelemetryCommand') { + setTimeout(() => { + if (!telemetry) { + Byond.sendMessage('ready'); + } + }, 500); + } // Keep telemetry up to date if (type === 'backend/update') { next(action); diff --git a/tgui/packages/tgui/backend.ts b/tgui/packages/tgui/backend.ts index 4af2e7418c2..9d10d3f41f3 100644 --- a/tgui/packages/tgui/backend.ts +++ b/tgui/packages/tgui/backend.ts @@ -262,6 +262,8 @@ type BackendState = { status: number; interface: string; refreshing: boolean; + map: string; // Vorestation Add + mapZLevel: number; // Vorestation Add window: { key: string; size: [number, number]; diff --git a/tgui/packages/tgui/components/Collapsible.tsx b/tgui/packages/tgui/components/Collapsible.tsx index b470ed5ce6d..8f53e7d92b7 100644 --- a/tgui/packages/tgui/components/Collapsible.tsx +++ b/tgui/packages/tgui/components/Collapsible.tsx @@ -13,11 +13,12 @@ type Props = Partial<{ buttons: ReactNode; open: boolean; title: ReactNode; + child_mt: number; // Vorestation Add }> & BoxProps; export function Collapsible(props: Props) { - const { children, color, title, buttons, ...rest } = props; + const { children, color, title, buttons, child_mt = 1, ...rest } = props; const [open, setOpen] = useState(props.open); return ( @@ -38,7 +39,7 @@ export function Collapsible(props: Props) {
{buttons}
)} - {open && {children}} + {open && {children}} ); } diff --git a/tgui/packages/tgui/components/NoticeBox.tsx b/tgui/packages/tgui/components/NoticeBox.tsx index db4f821d6b6..73abc743223 100644 --- a/tgui/packages/tgui/components/NoticeBox.tsx +++ b/tgui/packages/tgui/components/NoticeBox.tsx @@ -11,7 +11,7 @@ import { Box, BoxProps } from './Box'; type Props = ExclusiveProps & BoxProps; /** You MUST use only one or none */ -type NoticeType = 'info' | 'success' | 'danger'; +type NoticeType = 'info' | 'success' | 'warning' | 'danger'; type None = { [K in NoticeType]?: undefined; @@ -25,12 +25,15 @@ type ExclusiveProps = | (Omit & { success: boolean; }) + | (Omit & { + warning: boolean; + }) | (Omit & { danger: boolean; }); export function NoticeBox(props: Props) { - const { className, color, info, success, danger, ...rest } = props; + const { className, color, info, success, danger, warning, ...rest } = props; return ( void; rightSlot: ReactNode; @@ -57,6 +58,7 @@ const Tab = (props: TabProps) => { selected, color, icon, + iconSpin, // Vorestation Add leftSlot, rightSlot, children, @@ -87,7 +89,7 @@ const Tab = (props: TabProps) => { {(canRender(leftSlot) &&
{leftSlot}
) || (!!icon && (
- +
))}
{children}
diff --git a/tgui/packages/tgui/interfaces/AICard.jsx b/tgui/packages/tgui/interfaces/AICard.tsx similarity index 86% rename from tgui/packages/tgui/interfaces/AICard.jsx rename to tgui/packages/tgui/interfaces/AICard.tsx index 1c2125e5d7e..2d4dc59ad79 100644 --- a/tgui/packages/tgui/interfaces/AICard.jsx +++ b/tgui/packages/tgui/interfaces/AICard.tsx @@ -1,11 +1,26 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Button, LabeledList, ProgressBar, Section } from '../components'; import { Window } from '../layouts'; +type Data = { + name: string; + has_ai: BooleanLike; + integrity: number; + backup_capacitor: number; + flushing: BooleanLike; + has_laws: BooleanLike; + laws: string[]; + wireless: BooleanLike; + radio: BooleanLike; +}; + export const AICard = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { + name, has_ai, integrity, backup_capacitor, @@ -16,7 +31,7 @@ export const AICard = (props) => { radio, } = data; - if (has_ai === 0) { + if (!has_ai) { return ( @@ -29,7 +44,7 @@ export const AICard = (props) => { ); } else { - let integrityColor = null; // Handles changing color of the integrity bar + let integrityColor: string | undefined; // Handles changing color of the integrity bar if (integrity >= 75) { integrityColor = 'green'; } else if (integrity >= 25) { @@ -38,7 +53,7 @@ export const AICard = (props) => { integrityColor = 'red'; } - let powerColor = null; + let powerColor: string | undefined; if (backup_capacitor >= 75) { powerColor = 'green'; } @@ -52,7 +67,7 @@ export const AICard = (props) => {
- +

{name}

@@ -77,7 +92,7 @@ export const AICard = (props) => { {(!!has_laws && ( {laws.map((value, key) => ( - + {value} ))} diff --git a/tgui/packages/tgui/interfaces/APC.jsx b/tgui/packages/tgui/interfaces/APC.tsx similarity index 67% rename from tgui/packages/tgui/interfaces/APC.jsx rename to tgui/packages/tgui/interfaces/APC.tsx index 032e5fb8bbc..9df33fc68a9 100644 --- a/tgui/packages/tgui/interfaces/APC.jsx +++ b/tgui/packages/tgui/interfaces/APC.tsx @@ -1,3 +1,5 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, @@ -12,14 +14,46 @@ import { Window } from '../layouts'; import { FullscreenNotice } from './common/FullscreenNotice'; import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; +type Data = { + gridCheck: BooleanLike; + failTime: number; + locked: BooleanLike; + normallyLocked: BooleanLike; + siliconUser: BooleanLike; + externalPower; + chargingStatus; + powerChannels: { + title: string; + powerLoad: number; + status: number; + topicParams: { + auto: Record; + on: Record; + off: Record; + }[]; + }; + powerCellStatus: number; + emagged: BooleanLike; + isOperating: BooleanLike; + chargeMode: BooleanLike; + totalCharging: number; + totalLoad: number; + coverLocked: BooleanLike; + nightshiftLights: BooleanLike; + nightshiftSetting: number; + emergencyLights: boolean; +}; + export const APC = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); + + const { gridCheck, failTime } = data; let body = ; - if (data.gridCheck) { + if (gridCheck) { body = ; - } else if (data.failTime) { + } else if (failTime) { body = ; } @@ -30,7 +64,13 @@ export const APC = (props) => { ); }; -const powerStatusMap = { +type powerStatus = { + color: string; + externalPowerText: string; + chargingText: string; +}; + +const powerStatusMap: Record = { 2: { color: 'good', externalPowerText: 'External Power', @@ -48,7 +88,10 @@ const powerStatusMap = { }, }; -const malfMap = { +const malfMap: Record< + number, + { icon: string; content: string; action: string } +> = { 1: { icon: 'terminal', content: 'Override Programming', @@ -72,21 +115,38 @@ const malfMap = { }; const ApcContent = (props) => { - const { act, data } = useBackend(); - const locked = data.locked && !data.siliconUser; - const normallyLocked = data.normallyLocked; - const externalPowerStatus = - powerStatusMap[data.externalPower] || powerStatusMap[0]; - const chargingStatus = - powerStatusMap[data.chargingStatus] || powerStatusMap[0]; - const channelArray = data.powerChannels || []; + const { act, data } = useBackend(); + + const { + locked, + siliconUser, + externalPower, + chargingStatus, + powerChannels, + powerCellStatus, + emagged, + isOperating, + chargeMode, + totalCharging, + totalLoad, + coverLocked, + nightshiftSetting, + emergencyLights, + } = data; + + const is_locked: BooleanLike = locked && !siliconUser; + const externalPowerStatus: powerStatus = + powerStatusMap[externalPower] || powerStatusMap[0]; + const chargingPowerStatus: powerStatus = + powerStatusMap[chargingStatus] || powerStatusMap[0]; + const channelArray: any = powerChannels || []; // const malfStatus = malfMap[data.malfStatus] || null; - const adjustedCellChange = data.powerCellStatus / 100; + const adjustedCellChange: number = powerCellStatus / 100; return ( <> @@ -103,13 +163,13 @@ const ApcContent = (props) => { color={externalPowerStatus.color} buttons={ } > @@ -120,19 +180,19 @@ const ApcContent = (props) => { act('charge')} > - {data.chargeMode ? 'Auto' : 'Off'} + {chargeMode ? 'Auto' : 'Off'} } > - [ {chargingStatus.chargingText} ] + [ {chargingPowerStatus.chargingText} ]
@@ -156,26 +216,26 @@ const ApcContent = (props) => { } /> @@ -228,7 +288,7 @@ const ApcContent = (props) => { <> } /> @@ -299,7 +359,9 @@ const GridCheck = (props) => { }; const ApcFailure = (props) => { - const { data, act } = useBackend(); + const { data, act } = useBackend(); + + const { locked, siliconUser, failTime } = data; let rebootOptions = ( ); - if (data.locked && !data.siliconUser) { + if (locked && !siliconUser) { rebootOptions = Swipe an ID card for manual reboot.; } @@ -321,7 +383,7 @@ const ApcFailure = (props) => { I/O regulators malfunction detected! Waiting for system reboot...
- Automatic reboot in {data.failTime} seconds... + Automatic reboot in {failTime} seconds... {rebootOptions} ); diff --git a/tgui/packages/tgui/interfaces/AccountsTerminal.jsx b/tgui/packages/tgui/interfaces/AccountsTerminal.tsx similarity index 82% rename from tgui/packages/tgui/interfaces/AccountsTerminal.jsx rename to tgui/packages/tgui/interfaces/AccountsTerminal.tsx index 9831a560639..ba2fd43bb53 100644 --- a/tgui/packages/tgui/interfaces/AccountsTerminal.jsx +++ b/tgui/packages/tgui/interfaces/AccountsTerminal.tsx @@ -1,3 +1,5 @@ +import { BooleanLike } from 'common/react'; + import { useBackend, useSharedState } from '../backend'; import { Box, @@ -10,8 +12,36 @@ import { } from '../components'; import { Window } from '../layouts'; +type Data = { + id_inserted: BooleanLike; + id_card: string; + access_level: number; + machine_id: string; + creating_new_account: BooleanLike; + detailed_account_view: boolean; + station_account_number: number; + account_number: number | null; + owner_name: string | null; + money: number | null; + suspended: BooleanLike; + transactions: { + date: string; + time: string; + target_name: string; + purpose: string; + amount: number; + source_terminal: string; + }[]; + accounts: { + account_number: number; + owner_name: string; + suspended: string; + account_index: number; + }[]; +}; + export const AccountsTerminal = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { id_inserted, id_card, access_level, machine_id } = data; @@ -41,7 +71,7 @@ export const AccountsTerminal = (props) => { }; const AccountTerminalContent = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { creating_new_account, detailed_account_view } = data; @@ -56,18 +86,14 @@ const AccountTerminalContent = (props) => { Home act('create_account')} > New Account {!creating_new_account ? ( - act('print')} - > + act('print')}> Print ) : ( @@ -81,13 +107,13 @@ const AccountTerminalContent = (props) => { }; const NewAccountView = (props) => { - const { act } = useBackend(); + const { act } = useBackend(); const [holder, setHolder] = useSharedState('holder', ''); const [newMoney, setMoney] = useSharedState('money', ''); return ( -
+
setHolder(val)} /> @@ -115,7 +141,7 @@ const NewAccountView = (props) => { }; const DetailedView = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { access_level, @@ -130,7 +156,6 @@ const DetailedView = (props) => { return (
{ {suspended ? 'SUSPENDED' : 'Active'} -
+
{
{access_level >= 2 && ( -
+
@@ -178,7 +203,7 @@ const DetailedView = (props) => {
)} -
+
Timestamp @@ -205,18 +230,18 @@ const DetailedView = (props) => { }; const ListView = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { accounts } = data; return ( -
+
{(accounts.length && ( {accounts.map((acc) => ( )} diff --git a/tgui/packages/tgui/interfaces/AiAirlock.jsx b/tgui/packages/tgui/interfaces/AiAirlock.tsx similarity index 54% rename from tgui/packages/tgui/interfaces/AiAirlock.jsx rename to tgui/packages/tgui/interfaces/AiAirlock.tsx index e13cf1331b6..169a0ea8977 100644 --- a/tgui/packages/tgui/interfaces/AiAirlock.jsx +++ b/tgui/packages/tgui/interfaces/AiAirlock.tsx @@ -1,8 +1,40 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Button, LabeledList, Section } from '../components'; import { Window } from '../layouts'; -const dangerMap = { +type Data = { + power: { + main: number; + main_timeleft: number; + backup: number; + backup_timeleft: number; + }; + wires: { + main_1: BooleanLike; + main_2: BooleanLike; + backup_1: BooleanLike; + backup_2: BooleanLike; + shock: BooleanLike; + id_scanner: BooleanLike; + bolts: BooleanLike; + lights: BooleanLike; + safe: BooleanLike; + timing: BooleanLike; + }; + shock: number; + shock_timeleft: number; + id_scanner: BooleanLike; + lights: BooleanLike; + locked: BooleanLike; + safe: BooleanLike; + speed: BooleanLike; + opened: BooleanLike; + welded: BooleanLike; +}; + +const dangerMap: Record = { 2: { color: 'good', localStatusText: 'Optimal', @@ -18,10 +50,25 @@ const dangerMap = { }; export const AiAirlock = (props) => { - const { act, data } = useBackend(); - const statusMain = dangerMap[data.power.main] || dangerMap[0]; - const statusBackup = dangerMap[data.power.backup] || dangerMap[0]; - const statusElectrify = dangerMap[data.shock] || dangerMap[0]; + const { act, data } = useBackend(); + + const { + power, + wires, + shock, + shock_timeleft, + id_scanner, + lights, + locked, + safe, + speed, + opened, + welded, + } = data; + + const statusMain = dangerMap[power.main] || dangerMap[0]; + const statusBackup = dangerMap[power.backup] || dangerMap[0]; + const statusElectrify = dangerMap[shock] || dangerMap[0]; return ( @@ -33,18 +80,16 @@ export const AiAirlock = (props) => { buttons={ } > - {data.power.main ? 'Online' : 'Offline'}{' '} - {((!data.wires.main_1 || !data.wires.main_2) && - '[Wires have been cut!]') || - (data.power.main_timeleft > 0 && - `[${data.power.main_timeleft}s]`)} + {power.main ? 'Online' : 'Offline'}{' '} + {((!wires.main_1 || !wires.main_2) && '[Wires have been cut!]') || + (power.main_timeleft > 0 && `[${power.main_timeleft}s]`)} { buttons={ } > - {data.power.backup ? 'Online' : 'Offline'}{' '} - {((!data.wires.backup_1 || !data.wires.backup_2) && + {power.backup ? 'Online' : 'Offline'}{' '} + {((!wires.backup_1 || !wires.backup_2) && '[Wires have been cut!]') || - (data.power.backup_timeleft > 0 && - `[${data.power.backup_timeleft}s]`)} + (power.backup_timeleft > 0 && `[${power.backup_timeleft}s]`)} { <>
@@ -108,16 +152,16 @@ export const AiAirlock = (props) => { color="bad" buttons={ } > - {!data.wires.id_scanner && '[Wires have been cut!]'} + {!wires.id_scanner && '[Wires have been cut!]'} { color="bad" buttons={ } > - {!data.wires.bolts && '[Wires have been cut!]'} + {!wires.bolts && '[Wires have been cut!]'} act('light-toggle')} > - {data.lights ? 'Enabled' : 'Disabled'} + {lights ? 'Enabled' : 'Disabled'} } > - {!data.wires.lights && '[Wires have been cut!]'} + {!wires.lights && '[Wires have been cut!]'} act('safe-toggle')} > - {data.safe ? 'Enabled' : 'Disabled'} + {safe ? 'Enabled' : 'Disabled'} } > - {!data.wires.safe && '[Wires have been cut!]'} + {!wires.safe && '[Wires have been cut!]'} act('speed-toggle')} > - {data.speed ? 'Enabled' : 'Disabled'} + {speed ? 'Enabled' : 'Disabled'} } > - {!data.wires.timing && '[Wires have been cut!]'} + {!wires.timing && '[Wires have been cut!]'} { color="bad" buttons={ } > - {!!(data.locked || data.welded) && ( + {!!(locked || welded) && ( - [Door is {data.locked ? 'bolted' : ''} - {data.locked && data.welded ? ' and ' : ''} - {data.welded ? 'welded' : ''}!] + [Door is {locked ? 'bolted' : ''} + {locked && welded ? ' and ' : ''} + {welded ? 'welded' : ''}!] )} diff --git a/tgui/packages/tgui/interfaces/AiRestorer.jsx b/tgui/packages/tgui/interfaces/AiRestorer.tsx similarity index 87% rename from tgui/packages/tgui/interfaces/AiRestorer.jsx rename to tgui/packages/tgui/interfaces/AiRestorer.tsx index c6b4c81cca5..7db847a58c8 100644 --- a/tgui/packages/tgui/interfaces/AiRestorer.jsx +++ b/tgui/packages/tgui/interfaces/AiRestorer.tsx @@ -1,3 +1,5 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, @@ -9,6 +11,17 @@ import { } from '../components'; import { Window } from '../layouts'; +type Data = { + AI_present: boolean; + error: string | null; + name: string; + laws: string[]; + isDead: boolean; + restoring: BooleanLike; + health: number; + ejectable: boolean; +}; + export const AiRestorer = () => { return ( @@ -20,7 +33,7 @@ export const AiRestorer = () => { }; export const AiRestorerContent = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { AI_present, error, @@ -81,7 +94,7 @@ export const AiRestorerContent = (props) => { > Begin Reconstruction -
+
{laws.map((law) => ( {law} diff --git a/tgui/packages/tgui/interfaces/AiSupermatter.jsx b/tgui/packages/tgui/interfaces/AiSupermatter.tsx similarity index 87% rename from tgui/packages/tgui/interfaces/AiSupermatter.jsx rename to tgui/packages/tgui/interfaces/AiSupermatter.tsx index 0e8105c65cc..bf469604460 100644 --- a/tgui/packages/tgui/interfaces/AiSupermatter.jsx +++ b/tgui/packages/tgui/interfaces/AiSupermatter.tsx @@ -1,13 +1,21 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Icon, LabeledList, ProgressBar, Section } from '../components'; import { Window } from '../layouts'; import { FullscreenNotice } from './common/FullscreenNotice'; -export const AiSupermatter = (props) => { - const { data } = useBackend(); +type Data = { + detonating: BooleanLike; + integrity_percentage: number; + ambient_temp: number; + ambient_pressure: number; +}; - const { integrity_percentage, ambient_temp, ambient_pressure, detonating } = - data; +export const AiSupermatter = (props) => { + const { data } = useBackend(); + + const { detonating } = data; let body = ; if (detonating) { @@ -38,7 +46,7 @@ const AiSupermatterDetonation = (props) => ( ); const AiSupermatterContent = (props) => { - const { data } = useBackend(); + const { data } = useBackend(); const { integrity_percentage, ambient_temp, ambient_pressure } = data; diff --git a/tgui/packages/tgui/interfaces/AirAlarm.jsx b/tgui/packages/tgui/interfaces/AirAlarm.tsx similarity index 78% rename from tgui/packages/tgui/interfaces/AirAlarm.jsx rename to tgui/packages/tgui/interfaces/AirAlarm.tsx index 451c1c1b612..ebdbdaf3316 100644 --- a/tgui/packages/tgui/interfaces/AirAlarm.jsx +++ b/tgui/packages/tgui/interfaces/AirAlarm.tsx @@ -1,4 +1,5 @@ import { toFixed } from 'common/math'; +import { BooleanLike } from 'common/react'; import { Fragment, useState } from 'react'; import { useBackend } from '../backend'; @@ -6,10 +7,57 @@ import { Box, Button, LabeledList, Section } from '../components'; import { getGasColor, getGasLabel } from '../constants'; import { Window } from '../layouts'; import { Scrubber, Vent } from './common/AtmosControls'; +import { single_scrubber, single_vent } from './common/CommonTypes'; import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; +type Data = { + locked: BooleanLike; + siliconUser: BooleanLike; + remoteUser: BooleanLike; + environment_data: { + name: string; + value: number; + unit: string; + danger_level: number; + }[]; + danger_level: number; + target_temperature: string; + rcon: number; + atmos_alarm: BooleanLike; + fire_alarm: BooleanLike; + emagged: BooleanLike; // Seems unused + mode: number; + thresholds: thresholds[]; + scrubbers: single_scrubber[]; + vents: single_vent[]; + modes: { + name: string; + mode: number; + selected: BooleanLike; + danger: BooleanLike; + }[]; +}; + +type thresholds = { + name: string; + settings: { + env: string; + val: number; + selected: { + oxygen: number[]; + carbon_dioxide: number; + phoron: number; + other: number; + pressure: number; + temperature: number; + }; + }[]; +}; + export const AirAlarm = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); + + const { locked, siliconUser, remoteUser } = data; const [screen, setScreen] = useState(''); @@ -17,14 +65,14 @@ export const AirAlarm = (props) => { setScreen(value); } - const locked = data.locked && !data.siliconUser && !data.remoteUser; + const is_locked: BooleanLike = locked && !siliconUser && !remoteUser; return ( - {!locked && ( + {!is_locked && ( )} @@ -33,24 +81,28 @@ export const AirAlarm = (props) => { }; const AirAlarmStatus = (props) => { - const { data } = useBackend(); - const entries = (data.environment_data || []).filter( + const { data } = useBackend(); + + const { environment_data, atmos_alarm, fire_alarm, emagged } = data; + + const entries = (environment_data || []).filter( (entry) => entry.value >= 0.01, ); - const dangerMap = { - 0: { - color: 'good', - localStatusText: 'Optimal', - }, - 1: { - color: 'average', - localStatusText: 'Caution', - }, - 2: { - color: 'bad', - localStatusText: 'Danger (Internals Required)', - }, - }; + const dangerMap: Record = + { + 0: { + color: 'good', + localStatusText: 'Optimal', + }, + 1: { + color: 'average', + localStatusText: 'Caution', + }, + 2: { + color: 'bad', + localStatusText: 'Danger (Internals Required)', + }, + }; const localStatus = dangerMap[data.danger_level] || dangerMap[0]; return (
@@ -75,10 +127,10 @@ const AirAlarmStatus = (props) => { - {(data.atmos_alarm && 'Atmosphere Alarm') || - (data.fire_alarm && 'Fire Alarm') || + {(atmos_alarm && 'Atmosphere Alarm') || + (fire_alarm && 'Fire Alarm') || 'Nominal'} @@ -87,7 +139,7 @@ const AirAlarmStatus = (props) => { Cannot obtain air sample for analysis. )} - {!!data.emagged && ( + {!!emagged && ( Safety measures offline. Device may exhibit abnormal behavior. @@ -98,7 +150,7 @@ const AirAlarmStatus = (props) => { }; const AirAlarmUnlockedControl = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { target_temperature, rcon } = data; return (
@@ -179,7 +231,7 @@ const AirAlarmControl = (props) => { // -------------------------------------------------------- const AirAlarmControlHome = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { mode, atmos_alarm } = data; return ( <> @@ -226,7 +278,7 @@ const AirAlarmControlHome = (props) => { // -------------------------------------------------------- const AirAlarmControlVents = (props) => { - const { data } = useBackend(); + const { data } = useBackend(); const { vents } = data; if (!vents || vents.length === 0) { return 'Nothing to show'; @@ -238,7 +290,7 @@ const AirAlarmControlVents = (props) => { // -------------------------------------------------------- const AirAlarmControlScrubbers = (props) => { - const { data } = useBackend(); + const { data } = useBackend(); const { scrubbers } = data; if (!scrubbers || scrubbers.length === 0) { return 'Nothing to show'; @@ -252,7 +304,7 @@ const AirAlarmControlScrubbers = (props) => { // -------------------------------------------------------- const AirAlarmControlModes = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { modes } = data; if (!modes || modes.length === 0) { return 'Nothing to show'; @@ -276,7 +328,7 @@ const AirAlarmControlModes = (props) => { // -------------------------------------------------------- const AirAlarmControlThresholds = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { thresholds } = data; return (
diff --git a/tgui/packages/tgui/interfaces/AlgaeFarm.jsx b/tgui/packages/tgui/interfaces/AlgaeFarm.tsx similarity index 88% rename from tgui/packages/tgui/interfaces/AlgaeFarm.jsx rename to tgui/packages/tgui/interfaces/AlgaeFarm.tsx index a8a219f82d1..3774fdd5ce8 100644 --- a/tgui/packages/tgui/interfaces/AlgaeFarm.jsx +++ b/tgui/packages/tgui/interfaces/AlgaeFarm.tsx @@ -12,8 +12,28 @@ import { } from '../components'; import { Window } from '../layouts'; +type Data = { + usePower: number; + materials: { + name: string; + display: string; + qty: number; + max: number; + percent: number; + }[]; + last_flow_rate: number; + last_power_draw: number; + inputDir: string; + outputDir: string; + input: gas; + output: gas; + errorText: string | null; +}; + +type gas = { pressure: number; name: string; percent: number; moles: number }; + export const AlgaeFarm = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { usePower, materials, @@ -31,7 +51,7 @@ export const AlgaeFarm = (props) => { {errorText && ( - + {errorText} diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger.jsx b/tgui/packages/tgui/interfaces/AppearanceChanger.jsx deleted file mode 100644 index 9a7d0fb83ba..00000000000 --- a/tgui/packages/tgui/interfaces/AppearanceChanger.jsx +++ /dev/null @@ -1,523 +0,0 @@ -import { sortBy } from 'common/collections'; -import { capitalize, decodeHtmlEntities } from 'common/string'; -import { useState } from 'react'; - -import { useBackend } from '../backend'; -import { - Box, - Button, - ByondUi, - ColorBox, - Flex, - LabeledList, - Section, - Tabs, -} from '../components'; -import { Window } from '../layouts'; - -export const AppearanceChanger = (props) => { - const { act, config, data } = useBackend(); - - const { - name, - specimen, - gender, - gender_id, - hair_style, - facial_hair_style, - ear_style, - tail_style, - wing_style, - markings, - change_race, - change_gender, - change_eye_color, - change_skin_tone, - change_skin_color, - change_hair_color, - change_facial_hair_color, - change_hair, - change_facial_hair, - mapRef, - } = data; - - const { title } = config; - - const change_color = - change_eye_color || - change_skin_tone || - change_skin_color || - change_hair_color || - change_facial_hair_color; - - let firstAccesibleTab = -1; - if (change_race) { - firstAccesibleTab = 0; - } else if (change_gender) { - firstAccesibleTab = 1; - } else if (change_color) { - firstAccesibleTab = 2; - } else if (change_hair) { - firstAccesibleTab = 4; - } else if (change_facial_hair) { - firstAccesibleTab = 5; - } - - const [tabIndex, setTabIndex] = useState(firstAccesibleTab); - - return ( - - -
- - - - {name} - - {specimen} - - - {gender ? capitalize(gender) : 'Not Set'} - - - {gender_id ? capitalize(gender_id) : 'Not Set'} - - - {hair_style ? capitalize(hair_style) : 'Not Set'} - - - {facial_hair_style - ? capitalize(facial_hair_style) - : 'Not Set'} - - - {ear_style ? capitalize(ear_style) : 'Not Set'} - - - {tail_style ? capitalize(tail_style) : 'Not Set'} - - - {wing_style ? capitalize(wing_style) : 'Not Set'} - - - - - - - -
- - {change_race ? ( - setTabIndex(0)}> - Race - - ) : null} - {change_gender ? ( - setTabIndex(1)}> - Gender & Sex - - ) : null} - {change_color ? ( - setTabIndex(2)}> - Colors - - ) : null} - {change_hair ? ( - <> - setTabIndex(3)} - > - Hair - - setTabIndex(5)} - > - Ear - - setTabIndex(6)} - > - Tail - - setTabIndex(7)} - > - Wing - - setTabIndex(8)} - > - Markings - - - ) : null} - {change_facial_hair ? ( - setTabIndex(4)}> - Facial Hair - - ) : null} - - - {change_race && tabIndex === 0 ? : null} - {change_gender && tabIndex === 1 ? : null} - {change_color && tabIndex === 2 ? : null} - {change_hair && tabIndex === 3 ? : null} - {change_facial_hair && tabIndex === 4 ? ( - - ) : null} - {change_hair && tabIndex === 5 ? : null} - {change_hair && tabIndex === 6 ? : null} - {change_hair && tabIndex === 7 ? : null} - {change_hair && tabIndex === 8 ? : null} - -
-
- ); -}; - -const AppearanceChangerSpecies = (props) => { - const { act, data } = useBackend(); - const { species, specimen } = data; - - const sortedSpecies = sortBy((val) => val.specimen)(species || []); - - return ( -
- {sortedSpecies.map((spec) => ( - - ))} -
- ); -}; - -const AppearanceChangerGender = (props) => { - const { act, data } = useBackend(); - - const { gender, gender_id, genders, id_genders } = data; - - return ( -
- - - {genders.map((g) => ( - - ))} - - - {id_genders.map((g) => ( - - ))} - - -
- ); -}; - -const AppearanceChangerColors = (props) => { - const { act, data } = useBackend(); - - const { - change_eye_color, - change_skin_tone, - change_skin_color, - change_hair_color, - change_facial_hair_color, - eye_color, - skin_color, - hair_color, - facial_hair_color, - ears_color, - ears2_color, - tail_color, - tail2_color, - wing_color, - wing2_color, - } = data; - - return ( -
- {change_eye_color ? ( - - - - - ) : null} - {change_skin_tone ? ( - - - - ) : null} - {change_skin_color ? ( - - - - - ) : null} - {change_hair_color ? ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) : null} - {change_facial_hair_color ? ( - - - - - ) : null} -
- ); -}; - -const AppearanceChangerHair = (props) => { - const { act, data } = useBackend(); - - const { hair_style, hair_styles } = data; - - return ( -
- {hair_styles.map((hair) => ( - - ))} -
- ); -}; - -const AppearanceChangerFacialHair = (props) => { - const { act, data } = useBackend(); - - const { facial_hair_style, facial_hair_styles } = data; - - return ( -
- {facial_hair_styles.map((hair) => ( - - ))} -
- ); -}; - -const AppearanceChangerEars = (props) => { - const { act, data } = useBackend(); - - const { ear_style, ear_styles } = data; - - return ( -
- - {sortBy((e) => e.name.toLowerCase())(ear_styles).map((ear) => ( - - ))} -
- ); -}; - -const AppearanceChangerTails = (props) => { - const { act, data } = useBackend(); - - const { tail_style, tail_styles } = data; - - return ( -
- - {sortBy((e) => e.name.toLowerCase())(tail_styles).map((tail) => ( - - ))} -
- ); -}; - -const AppearanceChangerWings = (props) => { - const { act, data } = useBackend(); - - const { wing_style, wing_styles } = data; - - return ( -
- - {sortBy((e) => e.name.toLowerCase())(wing_styles).map((wing) => ( - - ))} -
- ); -}; - -const AppearanceChangerMarkings = (props) => { - const { act, data } = useBackend(); - - const { markings } = data; - - return ( -
- - - - - {markings.map((m) => ( - - - - - - - - ))} - -
- ); -}; diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerBody.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerBody.tsx new file mode 100644 index 00000000000..66cfcbb8866 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerBody.tsx @@ -0,0 +1,139 @@ +import { sortBy } from 'common/collections'; + +import { useBackend } from '../../backend'; +import { Button, LabeledList, Section } from '../../components'; +import { Data, species, styles } from './types'; + +export const AppearanceChangerSpecies = (props) => { + const { act, data } = useBackend(); + const { species, specimen } = data; + + const sortedSpecies = sortBy((val: species) => val.specimen)(species || []); + + return ( +
+ {sortedSpecies.map((spec) => ( + + ))} +
+ ); +}; + +export const AppearanceChangerGender = (props) => { + const { act, data } = useBackend(); + + const { gender, gender_id, genders, id_genders } = data; + + return ( +
+ + + {genders.map((g) => ( + + ))} + + + {id_genders.map((g) => ( + + ))} + + +
+ ); +}; + +export const AppearanceChangerEars = (props) => { + const { act, data } = useBackend(); + + const { ear_style, ear_styles } = data; + + return ( +
+ + {sortBy((e: styles) => e.name.toLowerCase())(ear_styles).map((ear) => ( + + ))} +
+ ); +}; + +export const AppearanceChangerTails = (props) => { + const { act, data } = useBackend(); + + const { tail_style, tail_styles } = data; + + return ( +
+ + {sortBy((e: styles) => e.name.toLowerCase())(tail_styles).map((tail) => ( + + ))} +
+ ); +}; + +export const AppearanceChangerWings = (props) => { + const { act, data } = useBackend(); + + const { wing_style, wing_styles } = data; + + return ( +
+ + {sortBy((e: styles) => e.name.toLowerCase())(wing_styles).map((wing) => ( + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerDetails.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerDetails.tsx new file mode 100644 index 00000000000..c5d2f403b7b --- /dev/null +++ b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerDetails.tsx @@ -0,0 +1,142 @@ +import { useBackend } from '../../backend'; +import { Box, Button, ColorBox, LabeledList, Section } from '../../components'; +import { Data } from './types'; + +export const AppearanceChangerColors = (props) => { + const { act, data } = useBackend(); + + const { + change_eye_color, + change_skin_tone, + change_skin_color, + change_hair_color, + change_facial_hair_color, + eye_color, + skin_color, + hair_color, + facial_hair_color, + ears_color, + ears2_color, + tail_color, + tail2_color, + wing_color, + wing2_color, + } = data; + + return ( +
+ {change_eye_color ? ( + + + + + ) : ( + '' + )} + {change_skin_tone ? ( + + + + ) : ( + '' + )} + {change_skin_color ? ( + + + + + ) : ( + '' + )} + {change_hair_color ? ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) : null} + {change_facial_hair_color ? ( + + + + + ) : null} +
+ ); +}; + +export const AppearanceChangerMarkings = (props) => { + const { act, data } = useBackend(); + + const { markings } = data; + + return ( +
+ + + + + {markings.map((m) => ( + + + + + + + + ))} + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerHairs.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerHairs.tsx new file mode 100644 index 00000000000..9e61a2c1a76 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerHairs.tsx @@ -0,0 +1,45 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; +import { Data } from './types'; + +export const AppearanceChangerHair = (props) => { + const { act, data } = useBackend(); + + const { hair_style, hair_styles } = data; + + return ( +
+ {hair_styles.map((hair) => ( + + ))} +
+ ); +}; + +export const AppearanceChangerFacialHair = (props) => { + const { act, data } = useBackend(); + + const { facial_hair_style, facial_hair_styles } = data; + + return ( +
+ {facial_hair_styles.map((hair) => ( + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/index.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/index.tsx new file mode 100644 index 00000000000..48935191fa6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AppearanceChanger/index.tsx @@ -0,0 +1,269 @@ +import { capitalize, decodeHtmlEntities } from 'common/string'; +import { useState } from 'react'; + +import { useBackend } from '../../backend'; +import { + Box, + ByondUi, + Flex, + LabeledList, + Section, + Tabs, +} from '../../components'; +import { Window } from '../../layouts'; +import { + AppearanceChangerEars, + AppearanceChangerGender, + AppearanceChangerSpecies, + AppearanceChangerTails, + AppearanceChangerWings, +} from './AppearanceChangerBody'; +import { + AppearanceChangerColors, + AppearanceChangerMarkings, +} from './AppearanceChangerDetails'; +import { + AppearanceChangerFacialHair, + AppearanceChangerHair, +} from './AppearanceChangerHairs'; +import { Data } from './types'; + +export const AppearanceChanger = (props) => { + const { act, config, data } = useBackend(); + + const { + name, + specimen, + gender, + gender_id, + hair_style, + facial_hair_style, + ear_style, + tail_style, + wing_style, + change_race, + change_gender, + change_eye_color, + change_skin_tone, + change_skin_color, + change_hair_color, + change_facial_hair_color, + change_hair, + change_facial_hair, + mapRef, + } = data; + + const { title } = config; + + const tab: React.JSX.Element[] = []; + + const change_color = + change_eye_color || + change_skin_tone || + change_skin_color || + change_hair_color || + change_facial_hair_color; + + const disabled = ; + + tab[-1] = ; + tab[0] = change_race ? ( + + ) : ( + + ); + tab[1] = change_gender ? ( + + ) : ( + + ); + tab[2] = change_color ? ( + + ) : ( + + ); + tab[3] = change_hair ? ( + + ) : ( + + ); + tab[4] = change_facial_hair ? ( + + ) : ( + + ); + tab[5] = change_hair ? ( + + ) : ( + + ); + tab[6] = change_hair ? ( + + ) : ( + + ); + tab[7] = change_hair ? ( + + ) : ( + + ); + tab[8] = change_hair ? ( + + ) : ( + + ); + + let firstAccesibleTab = -1; + if (change_race) { + firstAccesibleTab = 0; + } else if (change_gender) { + firstAccesibleTab = 1; + } else if (change_color) { + firstAccesibleTab = 2; + } else if (change_hair) { + firstAccesibleTab = 4; + } else if (change_facial_hair) { + firstAccesibleTab = 5; + } + + const [tabIndex, setTabIndex] = useState(firstAccesibleTab); + + return ( + + +
+ + + + {name} + + {specimen} + + + {gender ? capitalize(gender) : 'Not Set'} + + + {gender_id ? capitalize(gender_id) : 'Not Set'} + + + {hair_style ? capitalize(hair_style) : 'Not Set'} + + + {facial_hair_style + ? capitalize(facial_hair_style) + : 'Not Set'} + + + {ear_style ? capitalize(ear_style) : 'Not Set'} + + + {tail_style ? capitalize(tail_style) : 'Not Set'} + + + {wing_style ? capitalize(wing_style) : 'Not Set'} + + + + + + + +
+ + {change_race ? ( + setTabIndex(0)}> + Race + + ) : null} + {change_gender ? ( + setTabIndex(1)}> + Gender & Sex + + ) : null} + {change_color ? ( + setTabIndex(2)}> + Colors + + ) : null} + {change_hair ? ( + <> + setTabIndex(3)} + > + Hair + + setTabIndex(5)} + > + Ear + + setTabIndex(6)} + > + Tail + + setTabIndex(7)} + > + Wing + + setTabIndex(8)} + > + Markings + + + ) : null} + {change_facial_hair ? ( + setTabIndex(4)}> + Facial Hair + + ) : null} + + {tab[tabIndex]} +
+
+ ); +}; + +export const AppearanceChangerDefaultError = (props) => { + return Disabled; +}; diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts b/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts new file mode 100644 index 00000000000..4d62e131771 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts @@ -0,0 +1,53 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + name: string; + specimen: string; + species: species[]; + gender: string; + gender_id: string; + hair_style: string; + facial_hair_style: string; + ear_style: string; + ear_styles: styles[]; + wing_style: string; + wing_styles: styles[]; + tail_style: string; + tail_styles: styles[]; + markings: { marking_name: string; marking_color: string }[]; + change_race: BooleanLike; + change_gender: BooleanLike; + genders: genders; + id_genders: genders; + change_eye_color: BooleanLike; + change_skin_tone: BooleanLike; + change_skin_color: BooleanLike; + change_hair_color: BooleanLike; + change_facial_hair_color: BooleanLike; + change_hair: BooleanLike; + change_facial_hair: BooleanLike; + mapRef: string; + eye_color: string; + skin_color: string; + hair_color: string; + facial_hair_color: string; + ears_color: string; + ears2_color: string; + tail_color: string; + tail2_color: string; + wing_color: string; + wing2_color: string; + facial_hair_styles: { facialhairstyle: string }[]; + hair_styles: { hairstyle: string }[]; +}; + +type genders = { gender_name: string; gender_key: string }[]; + +export type styles = { + name: string; + instance: string; + color: boolean; + second_color: boolean; +}; + +export type species = { specimen: string }; diff --git a/tgui/packages/tgui/interfaces/ArcadeBattle.jsx b/tgui/packages/tgui/interfaces/ArcadeBattle.tsx similarity index 93% rename from tgui/packages/tgui/interfaces/ArcadeBattle.jsx rename to tgui/packages/tgui/interfaces/ArcadeBattle.tsx index 9ffead3baf6..f1300c73380 100644 --- a/tgui/packages/tgui/interfaces/ArcadeBattle.jsx +++ b/tgui/packages/tgui/interfaces/ArcadeBattle.tsx @@ -1,3 +1,5 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, @@ -9,18 +11,27 @@ import { } from '../components'; import { Window } from '../layouts'; +type Data = { + name: string; + temp: string; + enemyAction: string; + enemyName: string; + playerHP: number; + playerMP: number; + enemyHP: number; + gameOver: BooleanLike; +}; + export const ArcadeBattle = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { - name, temp, enemyAction, enemyName, playerHP, playerMP, enemyHP, - enemyMP, gameOver, } = data; diff --git a/tgui/packages/tgui/interfaces/AreaScrubberControl.jsx b/tgui/packages/tgui/interfaces/AreaScrubberControl.tsx similarity index 88% rename from tgui/packages/tgui/interfaces/AreaScrubberControl.jsx rename to tgui/packages/tgui/interfaces/AreaScrubberControl.tsx index e9aa25cc8ab..22e404dc84a 100644 --- a/tgui/packages/tgui/interfaces/AreaScrubberControl.jsx +++ b/tgui/packages/tgui/interfaces/AreaScrubberControl.tsx @@ -1,3 +1,4 @@ +import { BooleanLike } from 'common/react'; import { toTitleCase } from 'common/string'; import { useState } from 'react'; @@ -5,10 +6,24 @@ import { useBackend } from '../backend'; import { Box, Button, Flex, LabeledList, Section } from '../components'; import { Window } from '../layouts'; -export const AreaScrubberControl = (props) => { - const { act, data } = useBackend(); +type scrubber = { + id: string; + name: string; + on: BooleanLike; + pressure: number; + flow_rate: number; + load: number; + area: string; +}; - const [showArea, setShowArea] = useState(false); +type Data = { + scrubbers: scrubber[]; +}; + +export const AreaScrubberControl = (props) => { + const { act, data } = useBackend(); + + const [showArea, setShowArea] = useState(false); const { scrubbers } = data; @@ -83,7 +98,7 @@ export const AreaScrubberControl = (props) => { ); }; -const BigScrubber = (props) => { +const BigScrubber = (props: { scrubber: scrubber; showArea: boolean }) => { const { act } = useBackend(); const { scrubber, showArea } = props; diff --git a/tgui/packages/tgui/interfaces/AssemblyProx.jsx b/tgui/packages/tgui/interfaces/AssemblyProx.tsx similarity index 81% rename from tgui/packages/tgui/interfaces/AssemblyProx.jsx rename to tgui/packages/tgui/interfaces/AssemblyProx.tsx index 7e1761bef72..a08e6f768a4 100644 --- a/tgui/packages/tgui/interfaces/AssemblyProx.jsx +++ b/tgui/packages/tgui/interfaces/AssemblyProx.tsx @@ -1,12 +1,21 @@ import { round } from 'common/math'; +import { BooleanLike } from 'common/react'; import { useBackend } from '../backend'; import { Button, LabeledList, NumberInput, Section } from '../components'; import { formatTime } from '../format'; import { Window } from '../layouts'; +type Data = { + timing: number; + time: number; + range: number; + maxRange: number; + scanning: BooleanLike; +}; + export const AssemblyProx = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { timing, time, range, maxRange, scanning } = data; return ( @@ -31,8 +40,8 @@ export const AssemblyProx = (props) => { value={time} minValue={0} maxValue={600} - format={(val) => formatTime(round(val * 10))} - onDrag={(e, val) => act('set_time', { time: val })} + format={(val: number) => formatTime(round(val * 10, 0))} + onDrag={(e, val: string) => act('set_time', { time: val })} /> @@ -44,7 +53,7 @@ export const AssemblyProx = (props) => { minValue={1} value={range} maxValue={maxRange} - onDrag={(e, val) => act('range', { range: val })} + onDrag={(e, val: string) => act('range', { range: val })} /> diff --git a/tgui/packages/tgui/interfaces/AssemblyTimer.jsx b/tgui/packages/tgui/interfaces/AssemblyTimer.tsx similarity index 81% rename from tgui/packages/tgui/interfaces/AssemblyTimer.jsx rename to tgui/packages/tgui/interfaces/AssemblyTimer.tsx index 02d6f8b0e66..9fd6c8fb94e 100644 --- a/tgui/packages/tgui/interfaces/AssemblyTimer.jsx +++ b/tgui/packages/tgui/interfaces/AssemblyTimer.tsx @@ -5,8 +5,10 @@ import { Button, LabeledList, NumberInput, Section } from '../components'; import { formatTime } from '../format'; import { Window } from '../layouts'; +type Data = { timing: number; time: number }; + export const AssemblyTimer = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { timing, time } = data; return ( @@ -31,8 +33,8 @@ export const AssemblyTimer = (props) => { value={time} minValue={0} maxValue={600} - format={(val) => formatTime(round(val * 10))} - onDrag={(e, val) => act('set_time', { time: val })} + format={(val: number) => formatTime(round(val * 10, 0))} + onDrag={(e, val: string) => act('set_time', { time: val })} /> diff --git a/tgui/packages/tgui/interfaces/AtmosAlertConsole.jsx b/tgui/packages/tgui/interfaces/AtmosAlertConsole.tsx similarity index 74% rename from tgui/packages/tgui/interfaces/AtmosAlertConsole.jsx rename to tgui/packages/tgui/interfaces/AtmosAlertConsole.tsx index c722c954c0c..b2658aa66b4 100644 --- a/tgui/packages/tgui/interfaces/AtmosAlertConsole.jsx +++ b/tgui/packages/tgui/interfaces/AtmosAlertConsole.tsx @@ -2,19 +2,24 @@ import { useBackend } from '../backend'; import { Button, Section } from '../components'; import { Window } from '../layouts'; +type alarm = { name: string; ref: string }; + +type Data = { priority_alarms: alarm[]; minor_alarms: alarm[] }; + export const AtmosAlertConsole = (props) => { - const { act, data } = useBackend(); - const priorityAlerts = data.priority_alarms || []; - const minorAlerts = data.minor_alarms || []; + const { act, data } = useBackend(); + + const { priority_alarms = [], minor_alarms = [] } = data; + return (
    - {priorityAlerts.length === 0 && ( + {priority_alarms.length === 0 && (
  • No Priority Alerts
  • )} - {priorityAlerts.map((alert) => ( + {priority_alarms.map((alert) => (
  • ))} - {minorAlerts.length === 0 && ( + {minor_alarms.length === 0 && (
  • No Minor Alerts
  • )} - {minorAlerts.map((alert) => ( + {minor_alarms.map((alert) => (
  • val + ' L/s'} /> + onDrag={(e, value: number) => act('rate', { rate: value, }) @@ -49,7 +61,7 @@ export const AtmosFilter = (props) => { - {filterTypes.map((filter) => ( + {filter_types.map((filter) => ( + onChange={(e, value: number) => act('pressure', { pressure: value, }) @@ -36,7 +59,7 @@ export const AtmosMixer = (props) => { { + const { act } = useBackend(); + const { bodyrecords } = props; + return ( +
    act('menu', { menu: 'Main' })}> + Back + + } + > + {bodyrecords + ? bodyrecords.map((record) => ( + + )) + : ''} +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerMain.tsx b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerMain.tsx new file mode 100644 index 00000000000..5da0f605165 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerMain.tsx @@ -0,0 +1,16 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; + +export const BodyDesignerMain = (props) => { + const { act } = useBackend(); + return ( +
    + + +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerOOCNotes.tsx b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerOOCNotes.tsx new file mode 100644 index 00000000000..f57b2f868cf --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerOOCNotes.tsx @@ -0,0 +1,29 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; +import { activeBodyRecord } from './types'; + +export const BodyDesignerOOCNotes = (props: { + activeBodyRecord: activeBodyRecord; +}) => { + const { act } = useBackend(); + const { activeBodyRecord } = props; + return ( +
    act('menu', { menu: 'Specific Record' })} + > + Back + + } + style={{ wordBreak: 'break-all' }} + > + {(activeBodyRecord && activeBodyRecord.booc) || + 'ERROR: Body record not found!'} +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyDesigner.jsx b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerSpecificRecord.tsx similarity index 66% rename from tgui/packages/tgui/interfaces/BodyDesigner.jsx rename to tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerSpecificRecord.tsx index 3a9fcd4483e..b6fd5e67f44 100644 --- a/tgui/packages/tgui/interfaces/BodyDesigner.jsx +++ b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerSpecificRecord.tsx @@ -1,6 +1,6 @@ import { capitalize } from 'common/string'; -import { useBackend } from '../backend'; +import { useBackend } from '../../backend'; import { Box, Button, @@ -9,115 +9,15 @@ import { Flex, LabeledList, Section, -} from '../components'; -import { Window } from '../layouts'; +} from '../../components'; +import { activeBodyRecord } from './types'; -export const BodyDesigner = (props) => { - const { act, data } = useBackend(); - - const { menu, disk, diskStored, activeBodyRecord } = data; - - let body = MenuToTemplate[menu]; - - return ( - - - {disk ? ( - - - - - - ) : null} - {body} - - - ); -}; - -const BodyDesignerMain = (props) => { - const { act, data } = useBackend(); - return ( -
    - - -
    - ); -}; - -const BodyDesignerBodyRecords = (props) => { - const { act, data } = useBackend(); - const { bodyrecords } = data; - return ( -
    act('menu', { menu: 'Main' })}> - Back - - } - > - {bodyrecords - ? bodyrecords.map((record) => ( - - )) - : ''} -
    - ); -}; - -const BodyDesignerStockRecords = (props) => { - const { act, data } = useBackend(); - const { stock_bodyrecords } = data; - return ( -
    act('menu', { menu: 'Main' })}> - Back - - } - > - {stock_bodyrecords.map((record) => ( - - ))} -
    - ); -}; - -const BodyDesignerSpecificRecord = (props) => { - const { act, data } = useBackend(); - const { activeBodyRecord, mapRef } = data; +export const BodyDesignerSpecificRecord = (props: { + activeBodyRecord: activeBodyRecord; + mapRef: string; +}) => { + const { act } = useBackend(); + const { activeBodyRecord, mapRef } = props; return activeBodyRecord ? ( @@ -325,35 +225,3 @@ const BodyDesignerSpecificRecord = (props) => { ERROR: Record Not Found! ); }; - -const BodyDesignerOOCNotes = (props) => { - const { act, data } = useBackend(); - const { activeBodyRecord } = data; - return ( -
    act('menu', { menu: 'Specific Record' })} - > - Back - - } - style={{ 'word-break': 'break-all' }} - > - {(activeBodyRecord && activeBodyRecord.booc) || - 'ERROR: Body record not found!'} -
    - ); -}; - -const MenuToTemplate = { - Main: , - 'Body Records': , - 'Stock Records': , - 'Specific Record': , - 'OOC Notes': , -}; diff --git a/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerStockRecords.tsx b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerStockRecords.tsx new file mode 100644 index 00000000000..71195d4f6dd --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyDesigner/BodyDesignerStockRecords.tsx @@ -0,0 +1,29 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; + +export const BodyDesignerStockRecords = (props: { + stock_bodyrecords: string[]; +}) => { + const { act } = useBackend(); + const { stock_bodyrecords } = props; + return ( +
    act('menu', { menu: 'Main' })}> + Back + + } + > + {stock_bodyrecords.map((record) => ( + + ))} +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyDesigner/index.tsx b/tgui/packages/tgui/interfaces/BodyDesigner/index.tsx new file mode 100644 index 00000000000..be2178696f7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyDesigner/index.tsx @@ -0,0 +1,71 @@ +import { useBackend } from '../../backend'; +import { Box, Button } from '../../components'; +import { Window } from '../../layouts'; +import { BodyDesignerBodyRecords } from './BodyDesignerBodyRecords'; +import { BodyDesignerMain } from './BodyDesignerMain'; +import { BodyDesignerOOCNotes } from './BodyDesignerOOCNotes'; +import { BodyDesignerSpecificRecord } from './BodyDesignerSpecificRecord'; +import { BodyDesignerStockRecords } from './BodyDesignerStockRecords'; +import { Data } from './types'; + +export const BodyDesigner = (props) => { + const { act, data } = useBackend(); + + const { + menu, + disk, + diskStored, + activeBodyRecord, + stock_bodyrecords, + bodyrecords, + mapRef, + } = data; + + const MenuToTemplate = { + Main: , + 'Body Records': , + 'Stock Records': ( + + ), + 'Specific Record': ( + + ), + 'OOC Notes': , + }; + + let body = MenuToTemplate[menu]; + + return ( + + + {disk ? ( + + + + + + ) : ( + '' + )} + {body} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyDesigner/types.ts b/tgui/packages/tgui/interfaces/BodyDesigner/types.ts new file mode 100644 index 00000000000..3ec032ee1e5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyDesigner/types.ts @@ -0,0 +1,63 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + mapRef: string; + bodyrecords: bodyrecord[]; + stock_bodyrecords: string[]; + activeBodyRecord: activeBodyRecord; + menu: string; + temp: { + styleHref: string; + style: string; + color: string | undefined; + colorHref: string | undefined; + color2?: string | undefined; + colorHref2?: string | undefined; + }; + disk: BooleanLike; + diskStored: BooleanLike; +}; + +export type bodyrecord = { name: string; recref: string }; + +export type activeBodyRecord = { + real_name: string; + speciesname: string; + gender: string; + synthetic: string; + locked: string; + scale: string; + booc: string; + styles: { + Ears: colourableStyle; + Tail: colourableStyle; + Wing: colourableStyle; + Hair: simpleStyle; + Facial: simpleStyle; + Eyes: colourStyle; + 'Body Color': colourStyle; + Bodytype: { styleHref: string; style: string }; + }; + markings: { name: Record }; // Record entries match BP regions +}; + +type colourableStyle = { + styleHref: string; + style: string; + color: string | undefined; + colorHref: string | undefined; + color2: string | undefined; + colorHref2: string | undefined; +}; + +type simpleStyle = { + styleHref: string; + style: string; + colorHref: string; + color: string; +}; + +type colourStyle = { + colorHref: string; + color: string; +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner.jsx b/tgui/packages/tgui/interfaces/BodyScanner.jsx deleted file mode 100644 index 9afdb718231..00000000000 --- a/tgui/packages/tgui/interfaces/BodyScanner.jsx +++ /dev/null @@ -1,500 +0,0 @@ -import { round } from 'common/math'; - -import { useBackend } from '../backend'; -import { - AnimatedNumber, - Box, - Button, - Flex, - Icon, - LabeledList, - ProgressBar, - Section, - Table, - Tooltip, -} from '../components'; -import { Window } from '../layouts'; - -const stats = [ - ['good', 'Alive'], - ['average', 'Unconscious'], - ['bad', 'DEAD'], -]; - -const abnormalities = [ - [ - 'hasBorer', - 'bad', - (occupant) => - 'Large growth detected in frontal lobe,' + - ' possibly cancerous. Surgical removal is recommended.', - ], - ['hasVirus', 'bad', (occupant) => 'Viral pathogen detected in blood stream.'], - ['blind', 'average', (occupant) => 'Cataracts detected.'], - [ - 'colourblind', - 'average', - (occupant) => 'Photoreceptor abnormalities detected.', - ], - ['nearsighted', 'average', (occupant) => 'Retinal misalignment detected.'], - /* VOREStation Add */ - [ - 'humanPrey', - 'average', - (occupant) => { - return 'Foreign Humanoid(s) detected: ' + occupant.humanPrey; - }, - ], - [ - 'livingPrey', - 'average', - (occupant) => { - return 'Foreign Creature(s) detected: ' + occupant.livingPrey; - }, - ], - [ - 'objectPrey', - 'average', - (occupant) => { - return 'Foreign Object(s) detected: ' + occupant.objectPrey; - }, - ], - /* VOREStation Add End */ -]; - -const damages = [ - ['Respiratory', 'oxyLoss'], - ['Brain', 'brainLoss'], - ['Toxin', 'toxLoss'], - ['Radiation', 'radLoss'], - ['Brute', 'bruteLoss'], - ['Genetic', 'cloneLoss'], - ['Burn', 'fireLoss'], - ['Paralysis', 'paralysis'], -]; - -const damageRange = { - average: [0.25, 0.5], - bad: [0.5, Infinity], -}; - -const mapTwoByTwo = (a, c) => { - let result = []; - for (let i = 0; i < a.length; i += 2) { - result.push(c(a[i], a[i + 1], i)); - } - return result; -}; - -const reduceOrganStatus = (A) => { - return A.length > 0 - ? A.reduce((a, s) => - a === null ? ( - s - ) : ( - <> - {a} - {!!s && {s}} - - ), - ) - : null; -}; - -const germStatus = (i) => { - if (i > 100) { - if (i < 300) { - return 'mild infection'; - } - if (i < 400) { - return 'mild infection+'; - } - if (i < 500) { - return 'mild infection++'; - } - if (i < 700) { - return 'acute infection'; - } - if (i < 800) { - return 'acute infection+'; - } - if (i < 900) { - return 'acute infection++'; - } - if (i >= 900) { - return 'septic'; - } - } - - return ''; -}; - -export const BodyScanner = (props) => { - const { data } = useBackend(); - const { occupied, occupant = {} } = data; - const body = occupied ? ( - - ) : ( - - ); - return ( - - - {body} - - - ); -}; - -const BodyScannerMain = (props) => { - const { occupant } = props; - return ( - - - - - - - - - ); -}; - -const BodyScannerMainOccupant = (props) => { - const { act, data } = useBackend(); - const { occupant } = data; - return ( -
    - - - - } - > - - {occupant.name} - - - - - {stats[occupant.stat][1]} - - - - °C,  - - °F - - - {' '} - units ( - - %) - - {/* VOREStation Add */} - - {round(data.occupant.weight) + - 'lbs, ' + - round(data.occupant.weight / 2.20463) + - 'kgs'} - - {/* VOREStation Add End */} - -
    - ); -}; - -const BodyScannerMainReagents = (props) => { - const { occupant } = props; - - return ( - <> -
    - {occupant.reagents ? ( -
- - Reagent - Amount - - {occupant.reagents.map((reagent) => ( - - {reagent.name} - - {reagent.amount} Units{' '} - {reagent.overdose ? OVERDOSING : null} - - - ))} -
- ) : ( - No Blood Reagents Detected - )} -
-
- {occupant.ingested ? ( - - - Reagent - Amount - - {occupant.ingested.map((reagent) => ( - - {reagent.name} - - {reagent.amount} Units{' '} - {reagent.overdose ? OVERDOSING : null} - - - ))} -
- ) : ( - No Stomach Reagents Detected - )} -
- - ); -}; - -const BodyScannerMainAbnormalities = (props) => { - const { occupant } = props; - - let hasAbnormalities = - occupant.hasBorer || - occupant.blind || - occupant.colourblind || - occupant.nearsighted || - occupant.hasVirus; - - /* VOREStation Add */ - hasAbnormalities = - hasAbnormalities || - occupant.humanPrey || - occupant.livingPrey || - occupant.objectPrey; - /* VOREStation Add End */ - - if (!hasAbnormalities) { - return ( -
- No abnormalities found. -
- ); - } - - return ( -
- {abnormalities.map((a, i) => { - if (occupant[a[0]]) { - return ( - - {a[2](occupant)} - - ); - } - })} -
- ); -}; - -const BodyScannerMainDamage = (props) => { - const { occupant } = props; - return ( -
- - {mapTwoByTwo(damages, (d1, d2, i) => ( - <> - - {d1[0]}: - {!!d2 && d2[0] + ':'} - - - - - - - {!!d2 && } - - - - ))} -
-
- ); -}; - -const BodyScannerMainDamageBar = (props) => { - return ( - - {round(props.value, 0)} - - ); -}; - -const BodyScannerMainOrgansExternal = (props) => { - if (props.organs.length === 0) { - return ( -
- N/A -
- ); - } - - return ( -
- - - Name - Damage - Injuries - - {props.organs.map((o, i) => ( - - {o.name} - - 0 && '0.5rem'} - value={o.totalLoss / 100} - ranges={damageRange} - > - - {!!o.bruteLoss && ( - - - {round(o.bruteLoss, 0)}  - - - )} - {!!o.fireLoss && ( - - - {round(o.fireLoss, 0)} - - - )} - - {round(o.totalLoss, 0)} - - - - - {reduceOrganStatus([ - o.internalBleeding && 'Internal bleeding', - !!o.status.bleeding && 'External bleeding', - o.lungRuptured && 'Ruptured lung', - o.destroyed && 'Destroyed', - !!o.status.broken && o.status.broken, - germStatus(o.germ_level), - !!o.open && 'Open incision', - ])} - - - {reduceOrganStatus([ - !!o.status.splinted && 'Splinted', - !!o.status.robotic && 'Robotic', - !!o.status.dead && DEAD, - ])} - {reduceOrganStatus( - o.implants.map((s) => (s.known ? s.name : 'Unknown object')), - )} - - - - ))} -
-
- ); -}; - -const BodyScannerMainOrgansInternal = (props) => { - if (props.organs.length === 0) { - return ( -
- N/A -
- ); - } - - return ( -
- - - Name - Damage - Injuries - - {props.organs.map((o, i) => ( - - {o.name} - - 0 && '0.5rem'} - ranges={damageRange} - > - {round(o.damage, 0)} - - - - - {reduceOrganStatus([ - germStatus(o.germ_level), - !!o.inflamed && 'Appendicitis detected.', - ])} - - - {reduceOrganStatus([ - o.robotic === 1 && 'Robotic', - o.robotic === 2 && 'Assisted', - !!o.dead && DEAD, - ])} - - - - ))} -
-
- ); -}; - -const BodyScannerEmpty = () => { - return ( -
- - - -
- No occupant detected. -
-
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerEmpty.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerEmpty.tsx new file mode 100644 index 00000000000..e6f0f4bae97 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerEmpty.tsx @@ -0,0 +1,15 @@ +import { Flex, Icon, Section } from '../../components'; + +export const BodyScannerEmpty = () => { + return ( +
+ + + +
+ No occupant detected. +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMain.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMain.tsx new file mode 100644 index 00000000000..28237b0b04f --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMain.tsx @@ -0,0 +1,22 @@ +import { Box } from '../../components'; +import { BodyScannerMainAbnormalities } from './BodyScannerMainAbnormalities'; +import { BodyScannerMainDamage } from './BodyScannerMainDamage'; +import { BodyScannerMainOccupant } from './BodyScannerMainOccupant'; +import { BodyScannerMainOrgansExternal } from './BodyScannerMainOrgansExternal'; +import { BodyScannerMainOrgansInternal } from './BodyScannerMainOrgansInternal'; +import { BodyScannerMainReagents } from './BodyScannerMainReagents'; +import { occupant } from './types'; + +export const BodyScannerMain = (props: { occupant: occupant }) => { + const { occupant } = props; + return ( + + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainAbnormalities.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainAbnormalities.tsx new file mode 100644 index 00000000000..e68604d909b --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainAbnormalities.tsx @@ -0,0 +1,42 @@ +import { Box, Section } from '../../components'; +import { abnormalities } from './constants'; +import { occupant } from './types'; + +export const BodyScannerMainAbnormalities = (props: { occupant: occupant }) => { + const { occupant } = props; + + let hasAbnormalities = + occupant.hasBorer || + occupant.blind || + occupant.colourblind || + occupant.nearsighted || + occupant.hasVirus; + + hasAbnormalities = + hasAbnormalities || + occupant.humanPrey || + occupant.livingPrey || + occupant.objectPrey; + + if (!hasAbnormalities) { + return ( +
+ No abnormalities found. +
+ ); + } + + return ( +
+ {abnormalities.map((a, i) => { + if (occupant[a[0] as string]) { + return ( + + {(a[2] as (occupant: occupant) => string)(occupant)} + + ); + } + })} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainDamage.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainDamage.tsx new file mode 100644 index 00000000000..6b84636138a --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainDamage.tsx @@ -0,0 +1,54 @@ +import { toFixed } from 'common/math'; + +import { ProgressBar, Section, Table } from '../../components'; +import { damageRange, damages } from './constants'; +import { mapTwoByTwo } from './functions'; +import { occupant } from './types'; + +export const BodyScannerMainDamage = (props: { occupant: occupant }) => { + const { occupant } = props; + return ( +
+ + {mapTwoByTwo(damages, (d1: string[], d2: string[], i: number) => ( + <> + + {d1[0]}: + {!!d2 && d2[0] + ':'} + + + + + + + {!!d2 && } + + + + ))} +
+
+ ); +}; + +const BodyScannerMainDamageBar = (props: { + value: number; + marginBottom?: boolean; +}) => { + const { value, marginBottom } = props; + return ( + + {toFixed(value)} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOccupant.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOccupant.tsx new file mode 100644 index 00000000000..95f54a5cd97 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOccupant.tsx @@ -0,0 +1,81 @@ +import { toFixed } from 'common/math'; + +import { useBackend } from '../../backend'; +import { + AnimatedNumber, + Button, + LabeledList, + ProgressBar, + Section, +} from '../../components'; +import { stats } from './constants'; +import { occupant } from './types'; + +export const BodyScannerMainOccupant = (props: { occupant: occupant }) => { + const { act } = useBackend(); + const { occupant } = props; + return ( +
+ + + + } + > + + {occupant.name} + + + + + {stats[occupant.stat][1]} + + + toFixed(value)} + /> + °C,  + toFixed(value)} + /> + °F + + + toFixed(value)} + /> + units ( + toFixed(value)} + /> + %) + + + {toFixed(occupant.weight) + + 'lbs, ' + + toFixed(occupant.weight / 2.20463) + + 'kgs'} + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansExternal.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansExternal.tsx new file mode 100644 index 00000000000..cd32c84a4a8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansExternal.tsx @@ -0,0 +1,99 @@ +import { toFixed } from 'common/math'; + +import { + Box, + Icon, + ProgressBar, + Section, + Table, + Tooltip, +} from '../../components'; +import { damageRange } from './constants'; +import { germStatus, reduceOrganStatus } from './functions'; +import { externalOrgan } from './types'; + +export const BodyScannerMainOrgansExternal = (props: { + organs: externalOrgan[]; +}) => { + const { organs } = props; + + if (organs.length === 0) { + return ( +
+ N/A +
+ ); + } + + return ( +
+ + + Name + Damage + Injuries + + {organs.map((o, i) => ( + + {o.name} + + 0 && '0.5rem'} + value={o.totalLoss / 100} + ranges={damageRange} + > + + {!!o.bruteLoss && ( + + + {toFixed(o.bruteLoss)}  + + + )} + {!!o.fireLoss && ( + + + {toFixed(o.fireLoss)} + + + )} + + {toFixed(o.totalLoss)} + + + + + {reduceOrganStatus([ + o.internalBleeding && 'Internal bleeding', + !!o.status.bleeding && 'External bleeding', + o.lungRuptured && 'Ruptured lung', + o.status.destroyed && 'Destroyed', + !!o.status.broken && o.status.broken, + germStatus(o.germ_level), + !!o.open && 'Open incision', + ])} + + + {reduceOrganStatus([ + !!o.status.splinted && 'Splinted', + !!o.status.robotic && 'Robotic', + !!o.status.dead && DEAD, + ])} + {reduceOrganStatus( + o.implants.map((s) => (s.known ? s.name : 'Unknown object')), + )} + + + + ))} +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansInternal.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansInternal.tsx new file mode 100644 index 00000000000..af44217f2d6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansInternal.tsx @@ -0,0 +1,63 @@ +import { toFixed } from 'common/math'; + +import { Box, ProgressBar, Section, Table } from '../../components'; +import { damageRange } from './constants'; +import { germStatus, reduceOrganStatus } from './functions'; +import { internalOrgan } from './types'; + +export const BodyScannerMainOrgansInternal = (props: { + organs: internalOrgan[]; +}) => { + const { organs } = props; + + if (organs.length === 0) { + return ( +
+ N/A +
+ ); + } + + return ( +
+ + + Name + Damage + Injuries + + {organs.map((o, i) => ( + + {o.name} + + 0 && '0.5rem'} + ranges={damageRange} + > + {toFixed(o.damage)} + + + + + {reduceOrganStatus([ + germStatus(o.germ_level), + !!o.inflamed && 'Appendicitis detected.', + ])} + + + {reduceOrganStatus([ + o.robotic === 1 && 'Robotic', + o.robotic === 2 && 'Assisted', + !!o.dead && DEAD, + ])} + + + + ))} +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainReagents.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainReagents.tsx new file mode 100644 index 00000000000..922cb4059ad --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainReagents.tsx @@ -0,0 +1,53 @@ +import { Box, Section, Table } from '../../components'; +import { occupant } from './types'; + +export const BodyScannerMainReagents = (props: { occupant: occupant }) => { + const { occupant } = props; + + return ( + <> +
+ {occupant.reagents ? ( + + + Reagent + Amount + + {occupant.reagents.map((reagent) => ( + + {reagent.name} + + {reagent.amount} Units{' '} + {reagent.overdose ? OVERDOSING : null} + + + ))} +
+ ) : ( + No Blood Reagents Detected + )} +
+
+ {occupant.ingested ? ( + + + Reagent + Amount + + {occupant.ingested.map((reagent) => ( + + {reagent.name} + + {reagent.amount} Units{' '} + {reagent.overdose ? OVERDOSING : null} + + + ))} +
+ ) : ( + No Stomach Reagents Detected + )} +
+ + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/constants.ts b/tgui/packages/tgui/interfaces/BodyScanner/constants.ts new file mode 100644 index 00000000000..14dfe607709 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/constants.ts @@ -0,0 +1,62 @@ +import { occupant } from './types'; + +export const stats: string[][] = [ + ['good', 'Alive'], + ['average', 'Unconscious'], + ['bad', 'DEAD'], +]; + +export const abnormalities: (string | ((occupant: occupant) => string))[][] = [ + [ + 'hasBorer', + 'bad', + (occupant) => + 'Large growth detected in frontal lobe,' + + ' possibly cancerous. Surgical removal is recommended.', + ], + ['hasVirus', 'bad', (occupant) => 'Viral pathogen detected in blood stream.'], + ['blind', 'average', (occupant) => 'Cataracts detected.'], + [ + 'colourblind', + 'average', + (occupant) => 'Photoreceptor abnormalities detected.', + ], + ['nearsighted', 'average', (occupant) => 'Retinal misalignment detected.'], + [ + 'humanPrey', + 'average', + (occupant) => { + return 'Foreign Humanoid(s) detected: ' + occupant.humanPrey; + }, + ], + [ + 'livingPrey', + 'average', + (occupant) => { + return 'Foreign Creature(s) detected: ' + occupant.livingPrey; + }, + ], + [ + 'objectPrey', + 'average', + (occupant) => { + return 'Foreign Object(s) detected: ' + occupant.objectPrey; + }, + ], +]; + +export const damages: string[][] = [ + ['Respiratory', 'oxyLoss'], + ['Brain', 'brainLoss'], + ['Toxin', 'toxLoss'], + ['Radiation', 'radLoss'], + ['Brute', 'bruteLoss'], + ['Genetic', 'cloneLoss'], + ['Burn', 'fireLoss'], + ['Paralysis', 'paralysis'], +]; + +export const damageRange: Record = { + average: [0.25, 0.5], + bad: [0.5, Infinity], +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/functions.tsx b/tgui/packages/tgui/interfaces/BodyScanner/functions.tsx new file mode 100644 index 00000000000..3c43d892f08 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/functions.tsx @@ -0,0 +1,58 @@ +import { BooleanLike } from 'common/react'; + +import { Box } from '../../components'; + +/* + */ +export function mapTwoByTwo(a: any[][], c: any) { + let result: any[] = []; + for (let i = 0; i < a.length; i += 2) { + result.push(c(a[i], a[i + 1], i)); + } + return result; +} + +export function reduceOrganStatus( + A: (string | BooleanLike | React.ReactElement)[], +) { + return A.length > 0 + ? A.reduce((a, s) => + a === null ? ( + s + ) : ( + <> + {a} + {!!s && {s}} + + ), + ) + : null; +} + +export function germStatus(i: number): string { + if (i > 100) { + if (i < 300) { + return 'mild infection'; + } + if (i < 400) { + return 'mild infection+'; + } + if (i < 500) { + return 'mild infection++'; + } + if (i < 700) { + return 'acute infection'; + } + if (i < 800) { + return 'acute infection+'; + } + if (i < 900) { + return 'acute infection++'; + } + if (i >= 900) { + return 'septic'; + } + } + + return ''; +} diff --git a/tgui/packages/tgui/interfaces/BodyScanner/index.tsx b/tgui/packages/tgui/interfaces/BodyScanner/index.tsx new file mode 100644 index 00000000000..d44bc0d78bf --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/index.tsx @@ -0,0 +1,23 @@ +import { useBackend } from '../../backend'; +import { Window } from '../../layouts'; +import { BodyScannerEmpty } from './BodyScannerEmpty'; +import { BodyScannerMain } from './BodyScannerMain'; +import { Data, occupant } from './types'; + +export const BodyScanner = (props) => { + const { data } = useBackend(); + const { occupied, occupant = {} as occupant } = data; + const body = occupied ? ( + + ) : ( + + ); + return ( + + + {body} + + + ); +}; +6; diff --git a/tgui/packages/tgui/interfaces/BodyScanner/types.ts b/tgui/packages/tgui/interfaces/BodyScanner/types.ts new file mode 100644 index 00000000000..45aacd61ce9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner/types.ts @@ -0,0 +1,77 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + occupied: BooleanLike; + occupant: occupant; +}; + +export type occupant = { + name: string; + stat: number; + health: number; + maxHealth: number; + hasVirus: number; + bruteLoss: number; + oxyLoss: number; + toxLoss: number; + fireLoss: number; + radLoss: number; + cloneLoss: number; + brainLoss: number; + paralysis: number; + paralysisSeconds: number; + bodyTempC: number; + bodyTempF: number; + hasBorer: BooleanLike; + colourblind: BooleanLike; + blood: { volume: number; percent: number }; + reagents: reagent[]; + ingested: reagent[]; + extOrgan: externalOrgan[]; + intOrgan: internalOrgan[]; + blind: BooleanLike; + nearsighted: BooleanLike; + livingPrey: number; + humanPrey: number; + objectPrey: number; + weight: number; +}; + +type reagent = { name: string; amount: number; overdose: BooleanLike }; + +export type internalOrgan = { + name: string; + desc: string | null; + germ_level: number; + damage: number; + maxHealth: number; + bruised: number; + broken: number; + robotic: BooleanLike; + dead: BooleanLike; + inflamed: BooleanLike; +}; + +export type externalOrgan = { + name: string; + open: BooleanLike; + germ_level: number; + bruteLoss: number; + fireLoss: number; + totalLoss: number; + maxHealth: number; + bruised: number; + broken: number; + implants: { name: string; known: BooleanLike }[]; + implants_len: number; + status: { + destroyed: BooleanLike; + broken: string; + robotic: BooleanLike; + splinted: BooleanLike; + bleeding: BooleanLike; + dead: BooleanLike; + }; + lungRuptured: BooleanLike; + internalBleeding: BooleanLike; +}; diff --git a/tgui/packages/tgui/interfaces/BombTester.jsx b/tgui/packages/tgui/interfaces/BombTester.tsx similarity index 88% rename from tgui/packages/tgui/interfaces/BombTester.jsx rename to tgui/packages/tgui/interfaces/BombTester.tsx index c1e1a2afe9d..1fe140210f5 100644 --- a/tgui/packages/tgui/interfaces/BombTester.jsx +++ b/tgui/packages/tgui/interfaces/BombTester.tsx @@ -1,11 +1,23 @@ +import { BooleanLike } from 'common/react'; import { Component } from 'react'; import { useBackend } from '../backend'; import { Box, Button, Icon, LabeledList, Section, Slider } from '../components'; import { Window } from '../layouts'; +type Data = { + simulating: BooleanLike; + mode: number; + tank1: string; + tank1ref: string; + tank2: string; + tank2ref: string; + canister: string | null; + sim_canister_output: number; +}; + export const BombTester = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { simulating, @@ -96,7 +108,7 @@ export const BombTester = (props) => { minValue={0} value={sim_canister_output} maxValue={1013.25} - onDrag={(e, val) => + onDrag={(e, val: number) => act('set_can_pressure', { pressure: val }) } /> @@ -120,16 +132,26 @@ export const BombTester = (props) => { ); }; +type stateType = { + reverseX: boolean; + reverseY: boolean; + x: number; + y: number; +}; + class BombTesterSimulation extends Component { + process: NodeJS.Timeout; + state: stateType; + constructor(props) { super(props); - const BOUND_X = 340; - const BOUND_Y = 205; - const MOVEMENT_SPEED = 2; + const BOUND_X: number = 340; + const BOUND_Y: number = 205; + const MOVEMENT_SPEED: number = 2; - let startRight = Math.random() > 0.5; - let startBottom = Math.random() > 0.5; + let startRight: boolean = Math.random() > 0.5; + let startBottom: boolean = Math.random() > 0.5; this.state = { x: startRight ? BOUND_X : 0, @@ -139,7 +161,7 @@ class BombTesterSimulation extends Component { }; this.process = setInterval(() => { - this.setState((prevState) => { + this.setState((prevState: stateType) => { const state = { ...prevState }; if (state.reverseX) { if (state.x - MOVEMENT_SPEED < -5) { diff --git a/tgui/packages/tgui/interfaces/BotanyEditor.jsx b/tgui/packages/tgui/interfaces/BotanyEditor.tsx similarity index 88% rename from tgui/packages/tgui/interfaces/BotanyEditor.jsx rename to tgui/packages/tgui/interfaces/BotanyEditor.tsx index 5210e31dd79..b8709812896 100644 --- a/tgui/packages/tgui/interfaces/BotanyEditor.jsx +++ b/tgui/packages/tgui/interfaces/BotanyEditor.tsx @@ -1,9 +1,20 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Button, LabeledList, NoticeBox, Section } from '../components'; import { Window } from '../layouts'; +type Data = { + activity: BooleanLike; + degradation: number; + disk: BooleanLike; + loaded: string | number; + sourceName: string; + locus: string[]; +}; + export const BotanyEditor = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { activity, degradation, disk, sourceName, locus, loaded } = data; diff --git a/tgui/packages/tgui/interfaces/BotanyIsolator.jsx b/tgui/packages/tgui/interfaces/BotanyIsolator.tsx similarity index 91% rename from tgui/packages/tgui/interfaces/BotanyIsolator.jsx rename to tgui/packages/tgui/interfaces/BotanyIsolator.tsx index b46e434268a..12c8e45e2c2 100644 --- a/tgui/packages/tgui/interfaces/BotanyIsolator.jsx +++ b/tgui/packages/tgui/interfaces/BotanyIsolator.tsx @@ -1,9 +1,21 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Button, LabeledList, NoticeBox, Section } from '../components'; import { Window } from '../layouts'; +type Data = { + geneMasks: { tag: string; mask: string }[]; + activity: BooleanLike; + degradation: number; + disk: BooleanLike; + loaded: string | number; + hasGenetics: BooleanLike; + sourceName: string; +}; + export const BotanyIsolator = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { geneMasks, diff --git a/tgui/packages/tgui/interfaces/BrigTimer.jsx b/tgui/packages/tgui/interfaces/BrigTimer.tsx similarity index 60% rename from tgui/packages/tgui/interfaces/BrigTimer.jsx rename to tgui/packages/tgui/interfaces/BrigTimer.tsx index 44e693a6093..a0ab78e35d7 100644 --- a/tgui/packages/tgui/interfaces/BrigTimer.jsx +++ b/tgui/packages/tgui/interfaces/BrigTimer.tsx @@ -1,12 +1,36 @@ import { round } from 'common/math'; +import { BooleanLike } from 'common/react'; import { useBackend } from '../backend'; import { Button, Flex, NumberInput, Section } from '../components'; import { formatTime } from '../format'; import { Window } from '../layouts'; +type Data = { + time_left: number; + max_time_left: number; + timing: BooleanLike; + flash_found: BooleanLike; + flash_charging: BooleanLike; + preset_short: number; + preset_medium: number; + preset_long: number; +}; + export const BrigTimer = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); + + const { + time_left, + max_time_left, + timing, + flash_found, + flash_charging, + preset_short, + preset_medium, + preset_long, + } = data; + return ( @@ -16,18 +40,18 @@ export const BrigTimer = (props) => { <> - {(data.flash_found && ( + {(flash_found && ( )) || null} @@ -37,11 +61,11 @@ export const BrigTimer = (props) => { formatTime(round(val * 10))} - onDrag={(e, val) => act('time', { time: val })} + maxValue={max_time_left / 10} + format={(val: number) => formatTime(round(val * 10, 0))} + onDrag={(e, val: number) => act('time', { time: val })} /> @@ -50,7 +74,7 @@ export const BrigTimer = (props) => { icon="hourglass-start" onClick={() => act('preset', { preset: 'short' })} > - {'Add ' + formatTime(data.preset_short)} + {'Add ' + formatTime(preset_short)} @@ -59,7 +83,7 @@ export const BrigTimer = (props) => { icon="hourglass-start" onClick={() => act('preset', { preset: 'medium' })} > - {'Add ' + formatTime(data.preset_medium)} + {'Add ' + formatTime(preset_medium)} @@ -68,7 +92,7 @@ export const BrigTimer = (props) => { icon="hourglass-start" onClick={() => act('preset', { preset: 'long' })} > - {'Add ' + formatTime(data.preset_long)} + {'Add ' + formatTime(preset_long)} diff --git a/tgui/packages/tgui/interfaces/CameraConsole.jsx b/tgui/packages/tgui/interfaces/CameraConsole.tsx similarity index 74% rename from tgui/packages/tgui/interfaces/CameraConsole.jsx rename to tgui/packages/tgui/interfaces/CameraConsole.tsx index 29ee68a38aa..f558de6398f 100644 --- a/tgui/packages/tgui/interfaces/CameraConsole.jsx +++ b/tgui/packages/tgui/interfaces/CameraConsole.tsx @@ -1,6 +1,6 @@ import { filter, sortBy } from 'common/collections'; import { flow } from 'common/fp'; -import { classes } from 'common/react'; +import { BooleanLike, classes } from 'common/react'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -8,11 +8,25 @@ import { useBackend } from '../backend'; import { Button, ByondUi, Dropdown, Flex, Input, Section } from '../components'; import { Window } from '../layouts'; +type activeCamera = { name: string; status: BooleanLike } | null; + +type camera = { name: string; networks: string[] }; + +export type Data = { + activeCamera: activeCamera; + mapRef: string; + cameras: camera[]; + allNetworks: string[]; +}; + /** * Returns previous and next camera names relative to the currently * active camera. */ -export const prevNextCamera = (cameras, activeCamera) => { +export const prevNextCamera = ( + cameras: camera[], + activeCamera: activeCamera, +) => { if (!activeCamera) { return []; } @@ -22,32 +36,43 @@ export const prevNextCamera = (cameras, activeCamera) => { return [cameras[index - 1]?.name, cameras[index + 1]?.name]; }; +function notEmpty(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; +} + /** * Camera selector. * * Filters cameras, applies search terms and sorts the alphabetically. */ -export const selectCameras = (cameras, searchText = '', networkFilter = '') => { - const testSearch = createSearch(searchText, (camera) => camera.name); +export const selectCameras = ( + cameras: camera[], + searchText: string = '', + networkFilter: string = '', +) => { + const testSearch = createSearch(searchText, (camera: camera) => camera.name); return flow([ // Null camera filter - filter((camera) => camera?.name), + filter((camera: camera) => notEmpty(camera?.name)), // Optional search term searchText && filter(testSearch), // Optional network filter networkFilter && - filter((camera) => camera.networks.includes(networkFilter)), + filter((camera: camera) => camera.networks.includes(networkFilter)), // Slightly expensive, but way better than sorting in BYOND - sortBy((camera) => camera.name), + sortBy((camera: camera) => camera.name), ])(cameras); }; export const CameraConsole = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { mapRef, activeCamera } = data; - const cameras = selectCameras(data.cameras); - const [prevCameraName, nextCameraName] = prevNextCamera( - cameras, + + const { cameras } = data; + + const selected_cameras: camera[] = selectCameras(cameras); + const [prevCameraName, nextCameraName]: string[] = prevNextCamera( + selected_cameras, activeCamera, ); return ( @@ -101,12 +126,16 @@ export const CameraConsole = (props) => { }; export const CameraConsoleContent = (props) => { - const { act, data } = useBackend(); - const [searchText, setSearchText] = useState(''); - const [networkFilter, setNetworkFilter] = useState(''); - const { activeCamera, allNetworks } = data; + const { act, data } = useBackend(); + const [searchText, setSearchText] = useState(''); + const [networkFilter, setNetworkFilter] = useState(''); + const { activeCamera, allNetworks, cameras } = data; allNetworks.sort(); - const cameras = selectCameras(data.cameras, searchText, networkFilter); + const selected_cameras: camera[] = selectCameras( + cameras, + searchText, + networkFilter, + ); return ( @@ -115,7 +144,7 @@ export const CameraConsoleContent = (props) => { fluid mt={1} placeholder="Search for a camera" - onInput={(e, value) => setSearchText(value)} + onInput={(e, value: string) => setSearchText(value)} /> @@ -147,7 +176,7 @@ export const CameraConsoleContent = (props) => {
- {cameras.map((camera) => ( + {selected_cameras.map((camera) => ( // We're not using the component here because performance // would be absolutely abysmal (50+ ms for each re-render).
{ - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { connected, can_relabel, @@ -66,7 +79,7 @@ export const Canister = (props) => { minValue={minReleasePressure} maxValue={maxReleasePressure} stepPixelSize={1} - onDrag={(e, value) => + onDrag={(e, value: number) => act('pressure', { pressure: value, }) diff --git a/tgui/packages/tgui/interfaces/Canvas.jsx b/tgui/packages/tgui/interfaces/Canvas.tsx similarity index 69% rename from tgui/packages/tgui/interfaces/Canvas.jsx rename to tgui/packages/tgui/interfaces/Canvas.tsx index 4d5d7015ee3..d54eb7e66cd 100644 --- a/tgui/packages/tgui/interfaces/Canvas.jsx +++ b/tgui/packages/tgui/interfaces/Canvas.tsx @@ -1,4 +1,5 @@ -import { Component, createRef } from 'react'; +import { BooleanLike } from 'common/react'; +import { Component, createRef, RefObject } from 'react'; import { useBackend } from '../backend'; import { Box, Button } from '../components'; @@ -6,7 +7,17 @@ import { Window } from '../layouts'; const PX_PER_UNIT = 24; -class PaintCanvas extends Component { +type PaintCanvasProps = Partial<{ + onCanvasClick: (x: number, y: number) => void; + value: string[][]; + dotsize: number; + res: number; +}>; + +class PaintCanvas extends Component { + canvasRef: RefObject; + onCVClick: (x: number, y: number) => void; + constructor(props) { super(props); this.canvasRef = createRef(); @@ -21,16 +32,20 @@ class PaintCanvas extends Component { this.drawCanvas(this.props); } - drawCanvas(propSource) { - const ctx = this.canvasRef.current.getContext('2d'); + drawCanvas(propSource: PaintCanvasProps) { + const canvas = this.canvasRef.current!; + const ctx = canvas.getContext('2d')!; const grid = propSource.value; + if (!grid) { + return; + } const x_size = grid.length; if (!x_size) { return; } const y_size = grid[0].length; - const x_scale = Math.round(this.canvasRef.current.width / x_size); - const y_scale = Math.round(this.canvasRef.current.height / y_size); + const x_scale = Math.round(canvas.width / x_size); + const y_scale = Math.round(canvas.height / y_size); ctx.save(); ctx.scale(x_scale, y_scale); for (let x = 0; x < grid.length; x++) { @@ -44,14 +59,19 @@ class PaintCanvas extends Component { ctx.restore(); } - clickwrapper(event) { - const x_size = this.props.value.length; + clickwrapper(event: React.MouseEvent) { + const value = this.props.value; + if (!value) { + return; + } + const x_size = value.length; if (!x_size) { return; } const y_size = this.props.value[0].length; - const x_scale = this.canvasRef.current.width / x_size; - const y_scale = this.canvasRef.current.height / y_size; + const canvas = this.canvasRef.current!; + const x_scale = canvas.width / x_size; + const y_scale = canvas.height / y_size; const x = Math.floor(event.nativeEvent.offsetX / x_scale) + 1; const y = Math.floor(event.nativeEvent.offsetY / y_scale) + 1; this.onCVClick(x, y); @@ -80,8 +100,15 @@ const getImageSize = (value) => { return [width, height]; }; +type Data = { + grid: string[][]; + name: string; + finalized: BooleanLike; +}; + export const Canvas = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); + const dotsize = PX_PER_UNIT; const [width, height] = getImageSize(data.grid); return ( diff --git a/tgui/packages/tgui/interfaces/CasinoPrizeDispenser.jsx b/tgui/packages/tgui/interfaces/CasinoPrizeDispenser.tsx similarity index 81% rename from tgui/packages/tgui/interfaces/CasinoPrizeDispenser.jsx rename to tgui/packages/tgui/interfaces/CasinoPrizeDispenser.tsx index 8e03666a517..6c13f446db2 100644 --- a/tgui/packages/tgui/interfaces/CasinoPrizeDispenser.jsx +++ b/tgui/packages/tgui/interfaces/CasinoPrizeDispenser.tsx @@ -13,26 +13,36 @@ import { } from '../components'; import { Window } from '../layouts'; +type Data = { + items: Record; +}; + +type sortable = { + name: string; + affordable: number; + price: number; + restriction: string; +}; + const sortTypes = { - Alphabetical: (a, b) => a.name > b.name, - 'By availability': (a, b) => -(a.affordable - b.affordable), - 'By price': (a, b) => a.price - b.price, + Alphabetical: (a: sortable, b: sortable) => a.name > b.name, + 'By price': (a: sortable, b: sortable) => a.price - b.price, }; export const CasinoPrizeDispenser = () => { - const [searchText, setSearchText] = useState(''); - const [sortOrder, setSortOrder] = useState('Alphabetical'); - const [descending, setDescending] = useState(false); + const [searchText, setSearchText] = useState(''); + const [sortOrder, setSortOrder] = useState('Alphabetical'); + const [descending, setDescending] = useState(false); - function handleSearchText(value) { + function handleSearchText(value: string) { setSearchText(value); } - function handleSortOrder(value) { + function handleSortOrder(value: string) { setSortOrder(value); } - function handleDescending(value) { + function handleDescending(value: boolean) { setDescending(value); } @@ -52,9 +62,6 @@ export const CasinoPrizeDispenser = () => { searchText={searchText} sortOrder={sortOrder} descending={descending} - onSearchText={handleSearchText} - onSortOrder={handleSortOrder} - onDescending={handleDescending} /> @@ -98,19 +105,18 @@ const CasinoPrizeDispenserSearch = (props) => { }; const CasinoPrizeDispenserItems = (props) => { - const { act, data } = useBackend(); - const { points, items } = data; + const { act, data } = useBackend(); + const { items } = data; // Search thingies const searcher = createSearch(props.searchText, (item) => { return item[0]; }); let has_contents = false; - let contents = Object.entries(items).map((kv, _i) => { + let contents = Object.entries(items).map((kv) => { let items_in_cat = Object.entries(kv[1]) .filter(searcher) .map((kv2) => { - kv2[1].affordable = points >= kv2[1].price; return kv2[1]; }) .sort(sortTypes[props.sortOrder]); @@ -143,15 +149,19 @@ const CasinoPrizeDispenserItems = (props) => { ); }; -const CasinoPrizeDispenserItemsCategory = (props) => { - const { act, data } = useBackend(); +const CasinoPrizeDispenserItemsCategory = (props: { + key: string; + title: string; + items: sortable[]; +}) => { + const { act } = useBackend(); const { title, items, ...rest } = props; return ( {items.map((item) => ( { +const getTagColor = (tag: string) => { switch (tag) { case 'Unset': return 'label'; @@ -23,19 +24,36 @@ const getTagColor = (tag) => { } }; +type Data = { + personalVisibility: BooleanLike; + personalTag: string; + personalErpTag: string; + directory: character[]; +}; + +type character = { + name: string; + species: string; + ooc_notes: string; + tag: string; + erptag: string; + character_ad: string; + flavor_text: string; +}; + export const CharacterDirectory = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { personalVisibility, personalTag, personalErpTag } = data; - const [overlay, setOverlay] = useState(null); - const [overwritePrefs, setOverwritePrefs] = useState(false); + const [overlay, setOverlay] = useState(null); + const [overwritePrefs, setOverwritePrefs] = useState(false); - function handleOverlay(value) { + function handleOverlay(value: character | null) { setOverlay(value); } return ( - + {(overlay && ( @@ -119,29 +137,29 @@ const ViewCharacter = (props) => { } > -
+
{props.overlay.species}
-
+
{props.overlay.tag}
-
+
{props.overlay.erptag}
-
- +
+ {props.overlay.character_ad || 'Unset.'}
-
- +
+ {props.overlay.ooc_notes || 'Unset.'}
-
- +
+ {props.overlay.flavor_text || 'Unset.'}
@@ -150,17 +168,17 @@ const ViewCharacter = (props) => { }; const CharacterDirectoryList = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { directory } = data; - const [sortId, setSortId] = useState('name'); - const [sortOrder, setSortOrder] = useState('name'); + const [sortId, setSortId] = useState('name'); + const [sortOrder, setSortOrder] = useState('name'); - function handleSortId(value) { + function handleSortId(value: string) { setSortId(value); } - function handleSortOrder(value) { + function handleSortOrder(value: string) { setSortOrder(value); } @@ -243,9 +261,14 @@ const CharacterDirectoryList = (props) => { ); }; -const SortButton = (props) => { - const { act, data } = useBackend(); - +const SortButton = (props: { + id: string; + sortId: string; + sortOrder: string; + onSortId: Function; + onSortOrder: Function; + children: ReactNode | string; +}) => { const { id, children } = props; // Hey, same keys mean same data~ diff --git a/tgui/packages/tgui/interfaces/CheckboxInput.tsx b/tgui/packages/tgui/interfaces/CheckboxInput.tsx index 30ed63d08b6..de6fe94eff9 100644 --- a/tgui/packages/tgui/interfaces/CheckboxInput.tsx +++ b/tgui/packages/tgui/interfaces/CheckboxInput.tsx @@ -97,7 +97,7 @@ export const CheckboxInput = (props) => { setSearchQuery(value)} + onInput={(e, value: string) => setSearchQuery(value)} /> diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.jsx b/tgui/packages/tgui/interfaces/ChemDispenser.jsx deleted file mode 100644 index 801fe33dd38..00000000000 --- a/tgui/packages/tgui/interfaces/ChemDispenser.jsx +++ /dev/null @@ -1,175 +0,0 @@ -import { useBackend } from '../backend'; -import { Box, Button, Flex, LabeledList, Section, Slider } from '../components'; -import { BeakerContents } from '../interfaces/common/BeakerContents'; -import { Window } from '../layouts'; - -const dispenseAmounts = [5, 10, 20, 30, 40, 60]; -const removeAmounts = [1, 5, 10]; - -export const ChemDispenser = (props) => { - return ( - - - - - - - - ); -}; - -const ChemDispenserSettings = (properties) => { - const { act, data } = useBackend(); - const { amount } = data; - return ( -
- - - {dispenseAmounts.map((a, i) => ( - - ))} - - - - act('amount', { - amount: value, - }) - } - /> - - -
- ); -}; - -const ChemDispenserChemicals = (properties) => { - const { act, data } = useBackend(); - const { chemicals = [] } = data; - const flexFillers = []; - for (let i = 0; i < (chemicals.length + 1) % 3; i++) { - flexFillers.push(true); - } - return ( -
- - {chemicals.map((c, i) => ( - - - - ))} - {flexFillers.map((_, i) => ( - - ))} - -
- ); -}; - -const ChemDispenserBeaker = (properties) => { - const { act, data } = useBackend(); - const { - isBeakerLoaded, - beakerCurrentVolume, - beakerMaxVolume, - beakerContents = [], - } = data; - return ( -
- {!!isBeakerLoaded && ( - - {beakerCurrentVolume} / {beakerMaxVolume} units - - )} - - - } - > - ( - <> - - {removeAmounts.map((a, i) => ( - - ))} - - - )} - /> -
- ); -}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserBeaker.tsx b/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserBeaker.tsx new file mode 100644 index 00000000000..b9946802ac4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserBeaker.tsx @@ -0,0 +1,81 @@ +import { useBackend } from '../../backend'; +import { Box, Button, Section } from '../../components'; +import { BeakerContents } from '.././common/BeakerContents'; +import { removeAmounts } from './constants'; +import { Data } from './types'; + +export const ChemDispenserBeaker = (props) => { + const { act, data } = useBackend(); + const { + isBeakerLoaded, + beakerCurrentVolume, + beakerMaxVolume, + beakerContents = [], + } = data; + return ( +
+ {!!isBeakerLoaded && ( + + {beakerCurrentVolume} / {beakerMaxVolume} units + + )} + + + } + > + ( + <> + + {removeAmounts.map((a, i) => ( + + ))} + + + )} + /> +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserChemicals.tsx b/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserChemicals.tsx new file mode 100644 index 00000000000..06160da65f3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserChemicals.tsx @@ -0,0 +1,41 @@ +import { useBackend } from '../../backend'; +import { Button, Flex, Section } from '../../components'; +import { Data } from './types'; + +export const ChemDispenserChemicals = (props) => { + const { act, data } = useBackend(); + const { chemicals = [] } = data; + const flexFillers: boolean[] = []; + for (let i = 0; i < (chemicals.length + 1) % 3; i++) { + flexFillers.push(true); + } + return ( +
+ + {chemicals.map((c, i) => ( + + + + ))} + {flexFillers.map((_, i) => ( + + ))} + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserSettings.tsx b/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserSettings.tsx new file mode 100644 index 00000000000..2a55775822c --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser/ChemDispenserSettings.tsx @@ -0,0 +1,46 @@ +import { useBackend } from '../../backend'; +import { Button, LabeledList, Section, Slider } from '../../components'; +import { dispenseAmounts } from './constants'; +import { Data } from './types'; + +export const ChemDispenserSettings = (props) => { + const { act, data } = useBackend(); + const { amount } = data; + return ( +
+ + + {dispenseAmounts.map((a, i) => ( + + ))} + + + + act('amount', { + amount: value, + }) + } + /> + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser/constants.ts b/tgui/packages/tgui/interfaces/ChemDispenser/constants.ts new file mode 100644 index 00000000000..8555508f50e --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser/constants.ts @@ -0,0 +1,2 @@ +export const dispenseAmounts: number[] = [5, 10, 20, 30, 40, 60]; +export const removeAmounts: number[] = [1, 5, 10]; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser/index.tsx b/tgui/packages/tgui/interfaces/ChemDispenser/index.tsx new file mode 100644 index 00000000000..9e47296c17f --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser/index.tsx @@ -0,0 +1,16 @@ +import { Window } from '../../layouts'; +import { ChemDispenserBeaker } from './ChemDispenserBeaker'; +import { ChemDispenserChemicals } from './ChemDispenserChemicals'; +import { ChemDispenserSettings } from './ChemDispenserSettings'; + +export const ChemDispenser = (props) => { + return ( + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser/types.ts b/tgui/packages/tgui/interfaces/ChemDispenser/types.ts new file mode 100644 index 00000000000..f558816663e --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser/types.ts @@ -0,0 +1,13 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + amount: number; + isBeakerLoaded: BooleanLike; + glass: BooleanLike; + beakerContents: reagent[]; + beakerCurrentVolume: number | null; + beakerMaxVolume: number | null; + chemicals: reagent[]; +}; + +type reagent = { name: string; id: string; volume: number }; diff --git a/tgui/packages/tgui/interfaces/ChemMaster.jsx b/tgui/packages/tgui/interfaces/ChemMaster.jsx deleted file mode 100644 index 8e154bf0b9b..00000000000 --- a/tgui/packages/tgui/interfaces/ChemMaster.jsx +++ /dev/null @@ -1,496 +0,0 @@ -import { classes } from '../.././common/react'; -import { useBackend } from '../backend'; -import { Box, Button, Flex, Icon, LabeledList, Section } from '../components'; -import { Window } from '../layouts'; -import { BeakerContents } from './common/BeakerContents'; -import { - ComplexModal, - modalOpen, - modalRegisterBodyOverride, -} from './common/ComplexModal'; - -const transferAmounts = [1, 5, 10, 30, 60]; -const bottleStyles = [ - 'bottle.png', - 'small_bottle.png', - 'wide_bottle.png', - 'round_bottle.png', - 'reagent_bottle.png', -]; - -const analyzeModalBodyOverride = (modal) => { - const { act, data } = useBackend(); - const result = modal.args.analysis; - return ( -
- - - {result.name} - - {(result.desc || '').length > 0 ? result.desc : 'N/A'} - - {result.blood_type && ( - <> - - {result.blood_type} - - - {result.blood_dna} - - - )} - {!data.condi && ( - - )} - - -
- ); -}; - -export const ChemMaster = (props) => { - const { data } = useBackend(); - const { - condi, - beaker, - beaker_reagents = [], - buffer_reagents = [], - mode, - } = data; - return ( - - - - 0} - /> - - 0} - /> - {/* */} - - - ); -}; - -const ChemMasterBeaker = (props) => { - const { act, data } = useBackend(); - const { beaker, beakerReagents, bufferNonEmpty } = props; - - let headerButton = bufferNonEmpty ? ( - act('eject')} - > - Eject and Clear Buffer - - ) : ( - - ); - - return ( -
- {beaker ? ( - ( - - - {transferAmounts.map((am, j) => ( - - ))} - - - - )} - /> - ) : ( - No beaker loaded. - )} -
- ); -}; - -const ChemMasterBuffer = (props) => { - const { act } = useBackend(); - const { mode, bufferReagents = [] } = props; - return ( -
- Transferring to  - - - } - > - {bufferReagents.length > 0 ? ( - ( - - - {transferAmounts.map((am, i) => ( - - ))} - - - - )} - /> - ) : ( - Buffer is empty. - )} -
- ); -}; - -const ChemMasterProduction = (props) => { - const { act, data } = useBackend(); - if (!props.bufferNonEmpty) { - return ( -
act('ejectp')} - > - {data.loaded_pill_bottle - ? data.loaded_pill_bottle_name + - ' (' + - data.loaded_pill_bottle_contents_len + - '/' + - data.loaded_pill_bottle_storage_slots + - ')' - : 'No pill bottle loaded'} - - } - > - - - -
- Buffer is empty. -
-
-
- ); - } - - return ( -
act('ejectp')} - > - {data.loaded_pill_bottle - ? data.loaded_pill_bottle_name + - ' (' + - data.loaded_pill_bottle_contents_len + - '/' + - data.loaded_pill_bottle_storage_slots + - ')' - : 'No pill bottle loaded'} - - } - > - {!props.isCondiment ? ( - - ) : ( - - )} -
- ); -}; - -const ChemMasterProductionChemical = (props) => { - const { act, data } = useBackend(); - return ( - - - - -
- -
- - - - - - - -
- -
-
- ); -}; - -const ChemMasterProductionCondiment = (props) => { - const { act } = useBackend(); - return ( - <> - -
- - - ); -}; - -// const ChemMasterCustomization = (props) => { -// const { act, data } = useBackend(); -// if (!data.loaded_pill_bottle) { -// return ( -//
-// -// None loaded. -// -//
-// ); -// } - -// return ( -//
-// -//
-// ); -// }; - -modalRegisterBodyOverride('analyze', analyzeModalBodyOverride); diff --git a/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterAnalyzeModalBodyOverride.tsx b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterAnalyzeModalBodyOverride.tsx new file mode 100644 index 00000000000..f51100d3e1b --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterAnalyzeModalBodyOverride.tsx @@ -0,0 +1,53 @@ +import { useBackend } from '../../backend'; +import { Box, Button, LabeledList, Section } from '../../components'; +import { Data, modalData } from './types'; + +export const analyzeModalBodyOverride = (modal: modalData) => { + const { act, data } = useBackend(); + const result = modal.args.analysis; + return ( +
+ + + {result.name} + + {(result.desc || '').length > 0 ? result.desc : 'N/A'} + + {result.blood_type && ( + <> + + {result.blood_type} + + + {result.blood_dna} + + + )} + {!data.condi && ( + + )} + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterBeaker.tsx b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterBeaker.tsx new file mode 100644 index 00000000000..e42b4a32073 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterBeaker.tsx @@ -0,0 +1,94 @@ +import { BooleanLike } from 'common/react'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section } from '../../components'; +import { BeakerContents } from '.././common/BeakerContents'; +import { modalOpen } from '.././common/ComplexModal'; +import { transferAmounts } from './constants'; +import { reagent } from './types'; + +export const ChemMasterBeaker = (props: { + beaker: BooleanLike; + beakerReagents: reagent[]; + bufferNonEmpty: BooleanLike; +}) => { + const { act } = useBackend(); + const { beaker, beakerReagents, bufferNonEmpty } = props; + + let headerButton = bufferNonEmpty ? ( + act('eject')} + > + Eject and Clear Buffer + + ) : ( + + ); + + return ( +
+ {beaker ? ( + ( + + + {transferAmounts.map((am, j) => ( + + ))} + + + + )} + /> + ) : ( + No beaker loaded. + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterBuffer.tsx b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterBuffer.tsx new file mode 100644 index 00000000000..3bd477b6855 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterBuffer.tsx @@ -0,0 +1,92 @@ +import { BooleanLike } from 'common/react'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section } from '../../components'; +import { BeakerContents } from '.././common/BeakerContents'; +import { modalOpen } from '.././common/ComplexModal'; +import { transferAmounts } from './constants'; +import { reagent } from './types'; + +export const ChemMasterBuffer = (props: { + mode: BooleanLike; + bufferReagents: reagent[]; +}) => { + const { act } = useBackend(); + const { mode, bufferReagents = [] } = props; + return ( +
+ Transferring to  + + + } + > + {bufferReagents.length > 0 ? ( + ( + + + {transferAmounts.map((am, i) => ( + + ))} + + + + )} + /> + ) : ( + Buffer is empty. + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterCustomization.tsx b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterCustomization.tsx new file mode 100644 index 00000000000..3e70da5f8e1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterCustomization.tsx @@ -0,0 +1,55 @@ +import { BooleanLike } from 'common/react'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section } from '../../components'; +import { modalOpen } from '.././common/ComplexModal'; + +export const ChemMasterCustomization = (props: { + loaded_pill_bottle: BooleanLike; + loaded_pill_bottle_name: string; + loaded_pill_bottle_contents_len: number; + loaded_pill_bottle_storage_slots: number; +}) => { + const { act } = useBackend(); + + const { + loaded_pill_bottle, + loaded_pill_bottle_name, + loaded_pill_bottle_contents_len, + loaded_pill_bottle_storage_slots, + } = props; + + if (!loaded_pill_bottle) { + return ( +
+ None loaded. +
+ ); + } + + return ( +
+ + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProduction.tsx b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProduction.tsx new file mode 100644 index 00000000000..59f9619c645 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProduction.tsx @@ -0,0 +1,97 @@ +import { BooleanLike } from 'common/react'; + +import { useBackend } from '../../backend'; +import { Button, Flex, Icon, Section } from '../../components'; +import { ChemMasterProductionChemical } from './ChemMasterProductionChemical'; +import { ChemMasterProductionCondiment } from './ChemMasterProductionCondiment'; + +export const ChemMasterProduction = (props: { + bufferNonEmpty: boolean; + isCondiment: BooleanLike; + loaded_pill_bottle: BooleanLike; + loaded_pill_bottle_name: string; + loaded_pill_bottle_contents_len: number; + loaded_pill_bottle_storage_slots: number; + pillsprite: string; + bottlesprite: string; +}) => { + const { act } = useBackend(); + + const { + bufferNonEmpty, + isCondiment, + loaded_pill_bottle, + loaded_pill_bottle_name, + loaded_pill_bottle_contents_len, + loaded_pill_bottle_storage_slots, + pillsprite, + bottlesprite, + } = props; + + if (!bufferNonEmpty) { + return ( +
act('ejectp')} + > + {loaded_pill_bottle + ? loaded_pill_bottle_name + + ' (' + + loaded_pill_bottle_contents_len + + '/' + + loaded_pill_bottle_storage_slots + + ')' + : 'No pill bottle loaded'} + + } + > + + + +
+ Buffer is empty. +
+
+
+ ); + } + + return ( +
act('ejectp')} + > + {loaded_pill_bottle + ? loaded_pill_bottle_name + + ' (' + + loaded_pill_bottle_contents_len + + '/' + + loaded_pill_bottle_storage_slots + + ')' + : 'No pill bottle loaded'} + + } + > + {!isCondiment ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProductionChemical.tsx b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProductionChemical.tsx new file mode 100644 index 00000000000..3b7a4c2e136 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProductionChemical.tsx @@ -0,0 +1,111 @@ +import { classes } from '../../.././common/react'; +import { Box, Button, LabeledList } from '../../components'; +import { modalOpen } from '.././common/ComplexModal'; + +export const ChemMasterProductionChemical = (props: { + pillsprite: string; + bottlesprite: string; +}) => { + const { pillsprite, bottlesprite } = props; + + return ( + + + + +
+ +
+ + + + + + + +
+ +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProductionCondiment.tsx b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProductionCondiment.tsx new file mode 100644 index 00000000000..fd58ec8228c --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/ChemMasterProductionCondiment.tsx @@ -0,0 +1,26 @@ +import { useBackend } from '../../backend'; +import { Button } from '../../components'; +import { modalOpen } from '.././common/ComplexModal'; + +export const ChemMasterProductionCondiment = (props) => { + const { act } = useBackend(); + return ( + <> + +
+ + + ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/constants.ts b/tgui/packages/tgui/interfaces/ChemMaster/constants.ts new file mode 100644 index 00000000000..8fa1d18fa32 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/constants.ts @@ -0,0 +1,8 @@ +export const transferAmounts: number[] = [1, 5, 10, 30, 60]; +export const bottleStyles: string[] = [ + 'bottle.png', + 'small_bottle.png', + 'wide_bottle.png', + 'round_bottle.png', + 'reagent_bottle.png', +]; diff --git a/tgui/packages/tgui/interfaces/ChemMaster/index.tsx b/tgui/packages/tgui/interfaces/ChemMaster/index.tsx new file mode 100644 index 00000000000..658ffc8f3c0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/index.tsx @@ -0,0 +1,65 @@ +import { useBackend } from '../../backend'; +import { Window } from '../../layouts'; +import { + ComplexModal, + modalRegisterBodyOverride, +} from '.././common/ComplexModal'; +import { analyzeModalBodyOverride } from './ChemMasterAnalyzeModalBodyOverride'; +import { ChemMasterBeaker } from './ChemMasterBeaker'; +import { ChemMasterBuffer } from './ChemMasterBuffer'; +import { ChemMasterProduction } from './ChemMasterProduction'; +import { Data } from './types'; + +export const ChemMaster = (props) => { + const { data } = useBackend(); + const { + condi, + beaker, + beaker_reagents = [], + buffer_reagents = [], + mode, + loaded_pill_bottle, + loaded_pill_bottle_name, + loaded_pill_bottle_contents_len, + loaded_pill_bottle_storage_slots, + pillsprite, + bottlesprite, + } = data; + return ( + + + + 0} + /> + + 0} + loaded_pill_bottle={loaded_pill_bottle} + loaded_pill_bottle_name={loaded_pill_bottle_name || ''} + loaded_pill_bottle_contents_len={loaded_pill_bottle_contents_len || 0} + loaded_pill_bottle_storage_slots={ + loaded_pill_bottle_storage_slots || 0 + } + pillsprite={pillsprite} + bottlesprite={bottlesprite} + /> + {/* Vorestation Remove + + */} + + + ); +}; + +modalRegisterBodyOverride('analyze', analyzeModalBodyOverride); diff --git a/tgui/packages/tgui/interfaces/ChemMaster/types.ts b/tgui/packages/tgui/interfaces/ChemMaster/types.ts new file mode 100644 index 00000000000..a5b6dcdca09 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster/types.ts @@ -0,0 +1,40 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + condi: BooleanLike; + loaded_pill_bottle: BooleanLike; + loaded_pill_bottle_name: string | undefined; + loaded_pill_bottle_contents_len: number | undefined; + loaded_pill_bottle_storage_slots: number | undefined; + beaker: BooleanLike; + beaker_reagents: reagent[] | undefined; + buffer_reagents: reagent[] | undefined; + pillsprite: string; + bottlesprite: string; + mode: BooleanLike; + printing: BooleanLike; + modal: modalData; +}; + +export type reagent = { + name: string; + volume: number; + description: string; + id: string; +}; + +export type modalData = { + id: string; + text: string; + args: { + analysis: { + idx: string; + name: string; + desc: string; + blood_type: string | null; + blood_dna: string | null; + }; + beaker: number; + }; + type: string; +}; diff --git a/tgui/packages/tgui/interfaces/ClawMachine.jsx b/tgui/packages/tgui/interfaces/ClawMachine.tsx similarity index 91% rename from tgui/packages/tgui/interfaces/ClawMachine.jsx rename to tgui/packages/tgui/interfaces/ClawMachine.tsx index a25e9e0395e..3b43c19352a 100644 --- a/tgui/packages/tgui/interfaces/ClawMachine.jsx +++ b/tgui/packages/tgui/interfaces/ClawMachine.tsx @@ -2,8 +2,15 @@ import { useBackend } from '../backend'; import { Box, Button, LabeledList, ProgressBar } from '../components'; import { Window } from '../layouts'; +type Data = { + wintick: number; + instructions: string; + gameStatus: string; + winscreen: string; +}; + export const ClawMachine = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { wintick, instructions, gameStatus, winscreen } = data; let body; @@ -39,7 +46,7 @@ export const ClawMachine = (props) => { average: [1, 7], good: [8, Infinity], }} - value={data.wintick} + value={wintick} minValue={0} maxValue={10} /> diff --git a/tgui/packages/tgui/interfaces/Cleanbot.jsx b/tgui/packages/tgui/interfaces/Cleanbot.tsx similarity index 91% rename from tgui/packages/tgui/interfaces/Cleanbot.jsx rename to tgui/packages/tgui/interfaces/Cleanbot.tsx index 09632130d36..e66624a11ee 100644 --- a/tgui/packages/tgui/interfaces/Cleanbot.jsx +++ b/tgui/packages/tgui/interfaces/Cleanbot.tsx @@ -1,9 +1,27 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Button, LabeledList, Section } from '../components'; import { Window } from '../layouts'; +type Data = { + on: BooleanLike; + open: BooleanLike; + locked: BooleanLike; + patrol: BooleanLike; + vocal: BooleanLike; + wet_floors: BooleanLike; + spray_blood: BooleanLike; + version: string; + blood: BooleanLike; + rgbpanel: BooleanLike; + red_switch: BooleanLike; + green_switch: BooleanLike; + blue_switch: BooleanLike; +}; + export const Cleanbot = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { on, @@ -11,7 +29,6 @@ export const Cleanbot = (props) => { locked, version, blood, - patrol, vocal, wet_floors, spray_blood, diff --git a/tgui/packages/tgui/interfaces/CloningConsole.jsx b/tgui/packages/tgui/interfaces/CloningConsole.jsx deleted file mode 100644 index 99d10942eff..00000000000 --- a/tgui/packages/tgui/interfaces/CloningConsole.jsx +++ /dev/null @@ -1,458 +0,0 @@ -import { round } from 'common/math'; - -import { resolveAsset } from '../assets'; -import { useBackend } from '../backend'; -import { - Box, - Button, - Flex, - Icon, - LabeledList, - NoticeBox, - ProgressBar, - Section, - Tabs, -} from '../components'; -import { COLORS } from '../constants'; -import { - ComplexModal, - modalRegisterBodyOverride, -} from '../interfaces/common/ComplexModal'; -import { Window } from '../layouts'; - -const viewRecordModalBodyOverride = (modal) => { - const { act, data } = useBackend(); - const { activerecord, realname, health, unidentity, strucenzymes } = - modal.args; - const damages = health.split(' - '); - return ( -
- - {realname} - - {damages.length > 1 ? ( - <> - - {damages[0]} - -  |  - - {damages[2]} - -  |  - - {damages[3]} - -  |  - - {damages[1]} - - - ) : ( - Unknown - )} - - - {unidentity} - - - {strucenzymes} - - - - act('disk', { - option: 'load', - }) - } - > - Import - - - - - - - - - - -
- ); -}; - -export const CloningConsole = (props) => { - const { act, data } = useBackend(); - const { menu } = data; - modalRegisterBodyOverride('view_rec', viewRecordModalBodyOverride); - return ( - - - - - - -
- -
-
-
- ); -}; - -const CloningConsoleNavigation = (props) => { - const { act, data } = useBackend(); - const { menu } = data; - return ( - - - act('menu', { - num: 1, - }) - } - > - Main - - - act('menu', { - num: 2, - }) - } - > - Records - - - ); -}; - -const CloningConsoleBody = (props) => { - const { data } = useBackend(); - const { menu } = data; - let body; - if (menu === 1) { - body = ; - } else if (menu === 2) { - body = ; - } - return body; -}; - -const CloningConsoleMain = (props) => { - const { act, data } = useBackend(); - const { - loading, - scantemp, - occupant, - locked, - can_brainscan, - scan_mode, - numberofpods, - pods, - selected_pod, - } = data; - const isLocked = locked && !!occupant; - return ( - <> -
- - Scanner Lock:  - - - - - } - > - - - {loading ? ( - - -   Scanning... - - ) : ( - {scantemp.text} - )} - - {!!can_brainscan && ( - - - - )} - - -
-
- {numberofpods ? ( - pods.map((pod, i) => { - let podAction; - if (pod.status === 'cloning') { - podAction = ( - - {round(pod.progress, 0) + '%'} - - ); - } else if (pod.status === 'mess') { - podAction = ( - - ERROR - - ); - } else { - podAction = ( - - ); - } - - return ( - - - Pod #{i + 1} - = 150 ? 'good' : 'bad'} inline> - = 150 ? 'circle' : 'circle-o'} /> -   - {pod.biomass} - - {podAction} - - ); - }) - ) : ( - No pods detected. Unable to clone. - )} -
- - ); -}; - -const CloningConsoleRecords = (props) => { - const { act, data } = useBackend(); - const { records } = data; - if (!records.length) { - return ( - - - -
- No records found. -
-
- ); - } - return ( - - {records.map((record, i) => ( - - ))} - - ); -}; - -const CloningConsoleTemp = (props) => { - const { act, data } = useBackend(); - const { temp } = data; - if (!temp || !temp.text || temp.text.length <= 0) { - return; - } - - const tempProp = { [temp.style]: true }; - return ( - - - {temp.text} - - - - )} - - - } - > - - - {scanner ? ( - Connected - ) : ( - Not connected! - )} - - - {numberofpods ? ( - {numberofpods} connected - ) : ( - None connected! - )} - - -
- ); -}; diff --git a/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleBodyOverride.tsx b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleBodyOverride.tsx new file mode 100644 index 00000000000..fb6a6491523 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleBodyOverride.tsx @@ -0,0 +1,115 @@ +import { useBackend } from '../../backend'; +import { Box, Button, LabeledList, Section } from '../../components'; +import { COLORS } from '../../constants'; +import { Data, modalData } from './types'; + +export const viewRecordModalBodyOverride = (modal: modalData) => { + const { act, data } = useBackend(); + + const { disk, podready } = data; + + const { activerecord, realname, health, unidentity, strucenzymes } = + modal.args; + const damages = health.split(' - '); + return ( +
+ + {realname} + + {damages.length > 1 ? ( + <> + + {damages[0]} + +  |  + + {damages[2]} + +  |  + + {damages[3]} + +  |  + + {damages[1]} + + + ) : ( + Unknown + )} + + + {unidentity} + + + {strucenzymes} + + + + act('disk', { + option: 'load', + }) + } + > + Import + + + + + + + + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleNavigation.tsx b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleNavigation.tsx new file mode 100644 index 00000000000..d06866ca50f --- /dev/null +++ b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleNavigation.tsx @@ -0,0 +1,34 @@ +import { useBackend } from '../../backend'; +import { Tabs } from '../../components'; +import { Data } from './types'; + +export const CloningConsoleNavigation = (props) => { + const { act, data } = useBackend(); + const { menu } = data; + return ( + + + act('menu', { + num: 1, + }) + } + > + Main + + + act('menu', { + num: 2, + }) + } + > + Records + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleStatus.tsx b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleStatus.tsx new file mode 100644 index 00000000000..4034d3fed63 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleStatus.tsx @@ -0,0 +1,92 @@ +import { useBackend } from '../../backend'; +import { Box, Button, LabeledList, NoticeBox, Section } from '../../components'; +import { Data } from './types'; + +export const CloningConsoleTemp = (props) => { + const { act, data } = useBackend(); + const { temp } = data; + if (!temp || !temp.text || temp.text.length <= 0) { + return; + } + + const tempProp = { [temp.style]: true }; + return ( + + + {temp.text} + + + + )} + + + } + > + + + {scanner ? ( + Connected + ) : ( + Not connected! + )} + + + {numberofpods ? ( + {numberofpods} connected + ) : ( + None connected! + )} + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleTabs.tsx b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleTabs.tsx new file mode 100644 index 00000000000..4399d2a3b53 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CloningConsole/CloningConsoleTabs.tsx @@ -0,0 +1,192 @@ +import { toFixed } from 'common/math'; + +import { resolveAsset } from '../../assets'; +import { useBackend } from '../../backend'; +import { + Box, + Button, + Flex, + Icon, + Image, + LabeledList, + ProgressBar, + Section, +} from '../../components'; +import { Data } from './types'; + +export const CloningConsoleMain = (props) => { + const { act, data } = useBackend(); + const { + loading, + scantemp, + occupant, + locked, + can_brainscan, + scan_mode, + numberofpods, + pods, + selected_pod, + } = data; + const isLocked = locked && !!occupant; + return ( + <> +
+ + Scanner Lock:  + + + + + } + > + + + {loading ? ( + + +   Scanning... + + ) : ( + {scantemp.text} + )} + + {!!can_brainscan && ( + + + + )} + + +
+
+ {numberofpods ? ( + pods.map((pod, i) => { + let podAction; + if (pod.status === 'cloning') { + podAction = ( + + {toFixed(pod.progress) + '%'} + + ); + } else if (pod.status === 'mess') { + podAction = ( + + ERROR + + ); + } else { + podAction = ( + + ); + } + + return ( + + + Pod #{i + 1} + = 150 ? 'good' : 'bad'} inline> + = 150 ? 'circle' : 'circle-o'} /> +   + {pod.biomass} + + {podAction} + + ); + }) + ) : ( + No pods detected. Unable to clone. + )} +
+ + ); +}; + +export const CloningConsoleRecords = (props) => { + const { act, data } = useBackend(); + const { records } = data; + if (!records.length) { + return ( + + + +
+ No records found. +
+
+ ); + } + return ( + + {records.map((record, i) => ( + + ))} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CloningConsole/index.tsx b/tgui/packages/tgui/interfaces/CloningConsole/index.tsx new file mode 100644 index 00000000000..7957732fd7d --- /dev/null +++ b/tgui/packages/tgui/interfaces/CloningConsole/index.tsx @@ -0,0 +1,44 @@ +import { useBackend } from '../../backend'; +import { Box, Section } from '../../components'; +import { + ComplexModal, + modalRegisterBodyOverride, +} from '../../interfaces/common/ComplexModal'; +import { Window } from '../../layouts'; +import { viewRecordModalBodyOverride } from './CloningConsoleBodyOverride'; +import { CloningConsoleNavigation } from './CloningConsoleNavigation'; +import { + CloningConsoleStatus, + CloningConsoleTemp, +} from './CloningConsoleStatus'; +import { + CloningConsoleMain, + CloningConsoleRecords, +} from './CloningConsoleTabs'; +import { Data } from './types'; + +export const CloningConsole = (props) => { + const { data } = useBackend(); + + const { menu } = data; + + const tab: React.JSX.Element[] = []; + + tab[1] = ; + tab[2] = ; + + modalRegisterBodyOverride('view_rec', viewRecordModalBodyOverride); + return ( + + + + + + +
+ {tab[menu] || Error} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CloningConsole/types.ts b/tgui/packages/tgui/interfaces/CloningConsole/types.ts new file mode 100644 index 00000000000..5621fd1055c --- /dev/null +++ b/tgui/packages/tgui/interfaces/CloningConsole/types.ts @@ -0,0 +1,41 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + menu: number; + scanner: string; + numberofpods: number | null; + pods: { + pod: string; + name: string; + biomass: number; + status: string; + progress: number; + }[]; + loading: BooleanLike; + autoprocess: BooleanLike; + can_brainscan: BooleanLike; + scan_mode: BooleanLike; + autoallowed: BooleanLike; + occupant: string; + locked: BooleanLike; + temp: { text: string; style: string }; + scantemp: { text: string; color: string }; + disk: string | null; + selected_pod: string; + records: { record: string; realname: string }[]; + podready: BooleanLike; + modal: modalData; +}; + +export type modalData = { + id: string; + text: string; + args: { + activerecord: string; + realname: string; + health: string; + unidentity: string; + strucenzymes: string; + }; + modal_type: string; +}; diff --git a/tgui/packages/tgui/interfaces/ColorMate.jsx b/tgui/packages/tgui/interfaces/ColorMate.jsx deleted file mode 100644 index dab5b5dd378..00000000000 --- a/tgui/packages/tgui/interfaces/ColorMate.jsx +++ /dev/null @@ -1,401 +0,0 @@ -import { useBackend } from '../backend'; -import { - Button, - Icon, - NoticeBox, - NumberInput, - Section, - Slider, - Table, - Tabs, -} from '../components'; -import { Window } from '../layouts'; - -export const ColorMate = (props, context) => { - const { act, data } = useBackend(context); - const { activemode, temp } = data; - const item = data.item || []; - return ( - - -
- {temp ? {temp} : null} - {Object.keys(item).length ? ( - <> - - -
-
Item:
- -
-
- -
-
Preview:
- -
-
-
- - - act('switch_modes', { - mode: 1, - }) - } - > - Tint coloring (Simple) - - - act('switch_modes', { - mode: 2, - }) - } - > - HSV coloring (Normal) - - - act('switch_modes', { - mode: 3, - }) - } - > - Matrix coloring (Advanced) - - -
Coloring: {item.name}
- - - - - - - - {activemode === 1 ? ( - - ) : activemode === 2 ? ( - - ) : ( - - )} - -
- - ) : ( -
No item inserted.
- )} -
-
-
- ); -}; - -export const ColorMateTint = (props, context) => { - const { act, data } = useBackend(context); - return ( - - ); -}; - -export const ColorMateMatrix = (props, context) => { - const { act, data } = useBackend(context); - const matrixcolors = data.matrixcolors || []; - return ( - - - - RR:{' '} - - act('set_matrix_color', { - color: 1, - value, - }) - } - /> - - - GR:{' '} - - act('set_matrix_color', { - color: 4, - value, - }) - } - /> - - - BR:{' '} - - act('set_matrix_color', { - color: 7, - value, - }) - } - /> - - - - - RG:{' '} - - act('set_matrix_color', { - color: 2, - value, - }) - } - /> - - - GG:{' '} - - act('set_matrix_color', { - color: 5, - value, - }) - } - /> - - - BG:{' '} - - act('set_matrix_color', { - color: 8, - value, - }) - } - /> - - - - - RB:{' '} - - act('set_matrix_color', { - color: 3, - value, - }) - } - /> - - - GB:{' '} - - act('set_matrix_color', { - color: 6, - value, - }) - } - /> - - - BB:{' '} - - act('set_matrix_color', { - color: 9, - value, - }) - } - /> - - - - - CR:{' '} - - act('set_matrix_color', { - color: 10, - value, - }) - } - /> - - - CG:{' '} - - act('set_matrix_color', { - color: 11, - value, - }) - } - /> - - - CB:{' '} - - act('set_matrix_color', { - color: 12, - value, - }) - } - /> - - - - RG means red will become - this much green. -
- CR means this much red will - be added. -
-
- ); -}; - -export const ColorMateHSV = (props, context) => { - const { act, data } = useBackend(context); - const { buildhue, buildsat, buildval } = data; - return ( - - -
Hue:
- - - act('set_hue', { - buildhue: value, - }) - } - /> - -
- -
Saturation:
- - - act('set_sat', { - buildsat: value, - }) - } - /> - -
- -
Value:
- - - act('set_val', { - buildval: value, - }) - } - /> - -
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/ColorMate/ColorMateColor.tsx b/tgui/packages/tgui/interfaces/ColorMate/ColorMateColor.tsx new file mode 100644 index 00000000000..a5efd4024ae --- /dev/null +++ b/tgui/packages/tgui/interfaces/ColorMate/ColorMateColor.tsx @@ -0,0 +1,76 @@ +import { toFixed } from 'common/math'; + +import { useBackend } from '../../backend'; +import { Button, Slider, Table } from '../../components'; +import { Data } from './types'; + +export const ColorMateTint = (props) => { + const { act } = useBackend(); + + return ( + + ); +}; + +export const ColorMateHSV = (props) => { + const { act, data } = useBackend(); + + const { buildhue, buildsat, buildval } = data; + return ( + + +
Hue:
+ + toFixed(value)} + onDrag={(e, value: number) => + act('set_hue', { + buildhue: value, + }) + } + /> + +
+ +
Saturation:
+ + toFixed(value, 2)} + onDrag={(e, value: number) => + act('set_sat', { + buildsat: value, + }) + } + /> + +
+ +
Value:
+ + toFixed(value, 2)} + onDrag={(e, value: number) => + act('set_val', { + buildval: value, + }) + } + /> + +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ColorMate/ColorMateMatrix.tsx b/tgui/packages/tgui/interfaces/ColorMate/ColorMateMatrix.tsx new file mode 100644 index 00000000000..c9c0c5fc0d1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ColorMate/ColorMateMatrix.tsx @@ -0,0 +1,235 @@ +import { toFixed } from 'common/math'; + +import { useBackend } from '../../backend'; +import { Icon, NumberInput, Table } from '../../components'; +import { Data } from './types'; + +export const ColorMateMatrix = (props) => { + const { act, data } = useBackend(); + + const { matrixcolors } = data; + + return ( + + + + RR: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 1, + value, + }) + } + /> + + + GR: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 4, + value, + }) + } + /> + + + BR: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 7, + value, + }) + } + /> + + + + + RG: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 2, + value, + }) + } + /> + + + GG: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 5, + value, + }) + } + /> + + + BG: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 8, + value, + }) + } + /> + + + + + RB: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 3, + value, + }) + } + /> + + + GB: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 6, + value, + }) + } + /> + + + BB: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 9, + value, + }) + } + /> + + + + + CR: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 10, + value, + }) + } + /> + + + CG: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 11, + value, + }) + } + /> + + + CB: + toFixed(value, 2)} + onChange={(e, value: number) => + act('set_matrix_color', { + color: 12, + value, + }) + } + /> + + + + RG means red will become + this much green. +
+ CR means this much red will + be added. +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ColorMate/index.tsx b/tgui/packages/tgui/interfaces/ColorMate/index.tsx new file mode 100644 index 00000000000..8bbe62e7052 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ColorMate/index.tsx @@ -0,0 +1,120 @@ +import { useBackend } from '../../backend'; +import { + Box, + Button, + Image, + NoticeBox, + Section, + Table, + Tabs, +} from '../../components'; +import { Window } from '../../layouts'; +import { ColorMateHSV, ColorMateTint } from './ColorMateColor'; +import { ColorMateMatrix } from './ColorMateMatrix'; +import { Data } from './types'; + +export const ColorMate = (props) => { + const { act, data } = useBackend(); + + const { activemode, temp, item } = data; + + const tab: React.JSX.Element[] = []; + + tab[1] = ; + tab[2] = ; + tab[3] = ; + + return ( + + +
+ {temp ? {temp} : null} + {item && Object.keys(item).length ? ( + <> + + +
+
Item:
+ +
+
+ +
+
Preview:
+ +
+
+
+ + + act('switch_modes', { + mode: 1, + }) + } + > + Tint coloring (Simple) + + + act('switch_modes', { + mode: 2, + }) + } + > + HSV coloring (Normal) + + + act('switch_modes', { + mode: 3, + }) + } + > + Matrix coloring (Advanced) + + +
Coloring: {item.name}
+ + + + + + + + {tab[activemode] || Error} + +
+ + ) : ( +
No item inserted.
+ )} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ColorMate/types.ts b/tgui/packages/tgui/interfaces/ColorMate/types.ts new file mode 100644 index 00000000000..a56bab9c7b1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ColorMate/types.ts @@ -0,0 +1,22 @@ +export type Data = { + activemode: number; + matrixcolors: { + rr: number; + rg: number; + rb: number; + gr: number; + gg: number; + gb: number; + br: number; + bg: number; + bb: number; + cr: number; + cg: number; + cb: number; + }; + buildhue: number; + buildsat: number; + buildval: number; + temp: string | null; + item: { name: string; sprite: string; preview: string } | null; +}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx deleted file mode 100644 index 857364e55d1..00000000000 --- a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx +++ /dev/null @@ -1,364 +0,0 @@ -import { useBackend } from '../backend'; -import { Box, Button, LabeledList, Section } from '../components'; -import { Window } from '../layouts'; - -export const CommunicationsConsole = (props) => { - return ( - - - - - - ); -}; - -export const CommunicationsConsoleContent = (props) => { - const { act, data } = useBackend(); - - const { menu_state } = data; - - let mainTemplate = ( - - ERRROR. Unknown menu_state: {menu_state} - Please report this to NT Technical Support. - - ); - - // 1 = main screen - if (menu_state === 1) { - mainTemplate = ; - } else if (menu_state === 2) { - // 2 = status screen - mainTemplate = ; - } else if (menu_state === 3) { - // 3 = messages screen - mainTemplate = ; - } - - return ( - <> - - {mainTemplate} - - ); -}; - -const CommunicationsConsoleMain = (props) => { - const { act, data } = useBackend(); - - const { - messages, - msg_cooldown, - emagged, - cc_cooldown, - str_security_level, - levels, - authmax, - security_level, - security_level_color, - authenticated, - atcsquelch, - boss_short, - } = data; - - let reportText = 'View (' + messages.length + ')'; - let announceText = 'Make Priority Announcement'; - if (msg_cooldown > 0) { - announceText += ' (' + msg_cooldown + 's)'; - } - let ccMessageText = emagged ? 'Message [UNKNOWN]' : 'Message ' + boss_short; - if (cc_cooldown > 0) { - ccMessageText += ' (' + cc_cooldown + 's)'; - } - - let alertLevelText = str_security_level; - let alertLevelButtons = levels.map((slevel) => { - return ( - - ); - }); - - return ( - <> -
- - - - - {(!!emagged && ( - - - - - )) || ( - - - - )} - -
-
- - - {alertLevelText} - - - {alertLevelButtons} - - - - - - - - - - - -
- - ); -}; - -const CommunicationsConsoleAuth = (props) => { - const { act, data } = useBackend(); - - const { authenticated, is_ai, esc_status, esc_callable, esc_recallable } = - data; - - let authReadable; - if (!authenticated) { - authReadable = 'Not Logged In'; - } else if (is_ai) { - authReadable = 'AI'; - } else if (authenticated === 1) { - authReadable = 'Command'; - } else if (authenticated === 2) { - authReadable = 'Site Director'; - } else { - authReadable = 'ERROR: Report This Bug!'; - } - - return ( - <> -
- - {(is_ai && ( - AI - )) || ( - - - - )} - -
-
- - {!!esc_status && ( - {esc_status} - )} - {!!esc_callable && ( - - - - )} - {!!esc_recallable && ( - - - - )} - -
- - ); -}; - -const CommunicationsConsoleMessage = (props) => { - const { act, data } = useBackend(); - - const { message_current, message_deletion_allowed, authenticated, messages } = - data; - - if (message_current) { - return ( -
act('messagelist')} - > - Return To Message List - - } - > - {message_current.contents} -
- ); - } - - let messageRows = messages.map((m) => { - return ( - - - - - ); - }); - - return ( -
act('main')}> - Back To Main Menu - - } - > - - {(messages.length && messageRows) || ( - - No messages. - - )} - -
- ); -}; - -const CommunicationsConsoleStatusDisplay = (props) => { - const { act, data } = useBackend(); - - const { stat_display, authenticated } = data; - - let presetButtons = stat_display['presets'].map((pb) => { - return ( - - ); - }); - return ( -
act('main')}> - Back To Main Menu - - } - > - - {presetButtons} - - - - - - - -
- ); -}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleAuth.tsx b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleAuth.tsx new file mode 100644 index 00000000000..24b814ebee5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleAuth.tsx @@ -0,0 +1,74 @@ +import { useBackend } from '../../backend'; +import { Button, LabeledList, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicationsConsoleAuth = (props) => { + const { act, data } = useBackend(); + + const { authenticated, is_ai, esc_status, esc_callable, esc_recallable } = + data; + + let authReadable; + if (!authenticated) { + authReadable = 'Not Logged In'; + } else if (is_ai) { + authReadable = 'AI'; + } else if (authenticated === 1) { + authReadable = 'Command'; + } else if (authenticated === 2) { + authReadable = 'Site Director'; + } else { + authReadable = 'ERROR: Report This Bug!'; + } + + return ( + <> +
+ + {(is_ai && ( + AI + )) || ( + + + + )} + +
+
+ + {!!esc_status && ( + {esc_status} + )} + {!!esc_callable && ( + + + + )} + {!!esc_recallable && ( + + + + )} + +
+ + ); +}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleContent.tsx b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleContent.tsx new file mode 100644 index 00000000000..57826b88f34 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleContent.tsx @@ -0,0 +1,37 @@ +import { useBackend } from '../../backend'; +import { Box } from '../../components'; +import { CommunicationsConsoleAuth } from './CommunicationsConsoleAuth'; +import { CommunicationsConsoleMain } from './CommunicationsConsoleMain'; +import { CommunicationsConsoleMessage } from './CommunicationsConsoleMessage'; +import { CommunicationsConsoleStatusDisplay } from './CommunicationsConsoleStatusDisplay'; +import { Data } from './types'; + +export const CommunicationsConsoleContent = (props) => { + const { data } = useBackend(); + + const { menu_state } = data; + + const tab: React.JSX.Element[] = []; + + tab[1] = ; + tab[2] = ; + tab[3] = ; + + return ( + <> + + {tab[menu_state] || } + + ); +}; + +const DefaultError = (props: { menu_state: string }) => { + const { menu_state } = props; + + return ( + + ERRROR. Unknown menu_state: {menu_state} + Please report this to NT Technical Support. + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleMain.tsx b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleMain.tsx new file mode 100644 index 00000000000..caa937b1fcc --- /dev/null +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleMain.tsx @@ -0,0 +1,132 @@ +import { useBackend } from '../../backend'; +import { Button, LabeledList, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicationsConsoleMain = (props) => { + const { act, data } = useBackend(); + + const { + messages, + msg_cooldown, + emagged, + cc_cooldown, + str_security_level, + levels, + authmax, + security_level, + security_level_color, + authenticated, + atcsquelch, + boss_short, + } = data; + + const reportText = 'View (' + messages.length + ')'; + let announceText = 'Make Priority Announcement'; + if (msg_cooldown > 0) { + announceText += ' (' + msg_cooldown + 's)'; + } + let ccMessageText = emagged ? 'Message [UNKNOWN]' : 'Message ' + boss_short; + if (cc_cooldown > 0) { + ccMessageText += ' (' + cc_cooldown + 's)'; + } + + const alertLevelText = str_security_level; + const alertLevelButtons = levels.map((slevel) => { + return ( + + ); + }); + + return ( + <> +
+ + + + + {(!!emagged && ( + + + + + )) || ( + + + + )} + +
+
+ + + {alertLevelText} + + + {alertLevelButtons} + + + + + + + + + + + +
+ + ); +}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleMessage.tsx b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleMessage.tsx new file mode 100644 index 00000000000..e70a61bae5f --- /dev/null +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleMessage.tsx @@ -0,0 +1,69 @@ +import { useBackend } from '../../backend'; +import { Box, Button, LabeledList, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicationsConsoleMessage = (props) => { + const { act, data } = useBackend(); + + const { message_current, message_deletion_allowed, authenticated, messages } = + data; + + if (message_current) { + return ( +
act('messagelist')} + > + Return To Message List + + } + > + {message_current.contents} +
+ ); + } + + let messageRows = messages.map((m) => { + return ( + + + + + ); + }); + + return ( +
act('main')}> + Back To Main Menu + + } + > + + {(messages.length && messageRows) || ( + + No messages. + + )} + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleStatusDisplay.tsx b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleStatusDisplay.tsx new file mode 100644 index 00000000000..6ea5a7c3197 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole/CommunicationsConsoleStatusDisplay.tsx @@ -0,0 +1,54 @@ +import { useBackend } from '../../backend'; +import { Button, LabeledList, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicationsConsoleStatusDisplay = (props) => { + const { act, data } = useBackend(); + + const { stat_display, authenticated } = data; + + let presetButtons = stat_display['presets'].map((pb) => { + return ( + + ); + }); + return ( +
act('main')}> + Back To Main Menu + + } + > + + {presetButtons} + + + + + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole/index.tsx b/tgui/packages/tgui/interfaces/CommunicationsConsole/index.tsx new file mode 100644 index 00000000000..891947d2783 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole/index.tsx @@ -0,0 +1,12 @@ +import { Window } from '../../layouts'; +import { CommunicationsConsoleContent } from './CommunicationsConsoleContent'; + +export const CommunicationsConsole = (props) => { + return ( + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole/types.ts b/tgui/packages/tgui/interfaces/CommunicationsConsole/types.ts new file mode 100644 index 00000000000..3b3d1ac327a --- /dev/null +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole/types.ts @@ -0,0 +1,33 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + is_ai: BooleanLike; + menu_state: string; + emagged: BooleanLike; + authenticated: BooleanLike; + authmax: BooleanLike; + atcsquelch: BooleanLike; + boss_short: string; + stat_display: { + type: string; + line_1: string; + line_2: string; + presets: { name: string; label: string; desc: string }[]; + }; + security_level: number; + security_level_color: string; + str_security_level: string; + levels: { id: number; name: string; icon: string }[]; + messages: message[]; + message_deletion_allowed: BooleanLike; + message_current_id: number; + message_current: message; + current_viewing_message: number; + msg_cooldown: number; + cc_cooldown: number; + esc_callable: BooleanLike; + esc_recallable: BooleanLike; + esc_status: BooleanLike | string; +}; + +type message = { id: string; title: String; contents: string }; diff --git a/tgui/packages/tgui/interfaces/Communicator.tsx b/tgui/packages/tgui/interfaces/Communicator.tsx deleted file mode 100644 index 0bd2dd4d6bb..00000000000 --- a/tgui/packages/tgui/interfaces/Communicator.tsx +++ /dev/null @@ -1,1307 +0,0 @@ -import { filter } from 'common/collections'; -import { BooleanLike } from 'common/react'; -import { decodeHtmlEntities, toTitleCase } from 'common/string'; -import { useState } from 'react'; - -import { useBackend } from '../backend'; -import { - Box, - Button, - ByondUi, - Flex, - Icon, - Input, - LabeledList, - Section, - Table, -} from '../components'; -import { Window } from '../layouts'; -import { CrewManifestContent } from './CrewManifest'; - -const HOMETAB = 1; -const PHONTAB = 2; -const CONTTAB = 3; -const MESSTAB = 4; -const MESSSUBTAB = 40; -const NEWSTAB = 5; -const NOTETAB = 6; -const WTHRTAB = 7; -const MANITAB = 8; -const SETTTAB = 9; - -let TABS = [ - HOMETAB, - PHONTAB, - CONTTAB, - MESSTAB, - MESSSUBTAB, - NEWSTAB, - NOTETAB, - WTHRTAB, - MANITAB, - SETTTAB, -]; - -let TabToTemplate = {}; // Populated under each template - -type Data = { - // GENERAL - currentTab: number; - video_comm: BooleanLike; - mapRef: string; - - // FOOTER - time: string; - connectionStatus: BooleanLike; - owner: string; - occupation: string; - - // HEADER - flashlight: BooleanLike; - - // HOMETAB - homeScreen: { number: number; module: string; icon: string }[]; - - // PHONETAB - targetAddress: string; - voice_mobs: { name: string; true_name: string; ref: string }[]; - communicating: { - address: string; - name: string; - true_name: string; - ref: string; - }[]; - requestsReceived: { address: string; name: string; ref: string }[]; - invitesSent: { address: string; name: string }[]; - phone_video_comm: string; - selfie_mode: BooleanLike; - - // MESSAGING - imContacts: { address: string; name: string }[]; - targetAddressName: string; - imList: { address: string; to_address: string; im: string }[]; - - // SETTINGS - address: string; - visible: BooleanLike; - ring: BooleanLike; - - // NEWSTAB - feeds: { index: number; name: string }[]; - target_feed: { name: string; author: string; messages: NewsMessage[] }; - latest_news: NewsMessage[]; - - // NOTETAB - note: string; -}; - -type NewsMessage = { - ref: string; - body: string; - img: string; - caption: string; - message_type: string; - author: string; - time_stamp: string; - - index: number; - channel: string; -}; - -function notFound(val) { - return TABS.includes(val); -} - -export const Communicator = (props) => { - const { act, data } = useBackend(); - - const { - currentTab, - video_comm, - owner, - occupation, - connectionStatus, - address, - visible, - ring, - selfie_mode, - homeScreen, - targetAddress, - voice_mobs, - phone_video_comm, - communicating, - requestsReceived, - invitesSent, - imContacts, - targetAddressName, - imList, - feeds, - target_feed, - latest_news, - note, - } = data; - - const validCharacters = [ - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - 'A', - 'B', - 'C', - 'D', - 'E', - 'F', - ]; - - let buttonArray = validCharacters.map((char) => ( - - )); - - let finalArray: any[] = []; - - for (let i = 0; i < buttonArray.length; i += 4) { - finalArray.push( - - {buttonArray[i]} - {buttonArray[i + 1]} - {buttonArray[i + 2]} - {buttonArray[i + 3]} - , - ); - } - /* 0: Fullscreen Video - * 1: Popup Video - * 2: Minimized Video - */ - const [videoSetting, setVideoSetting] = useState(0); - const [clipboardMode, setClipboardMode] = useState(false); - - return ( - - - {video_comm && ( - - )} - {(!video_comm || videoSetting !== 0) && ( - <> - - - {(currentTab === HOMETAB && ( - - {homeScreen.map((app) => ( - - - {app.module} - - ))} - - )) || - (currentTab === PHONTAB && ( -
- - - - - - act('write_target_address', { val: val }) - } - /> - - - - Dial - - {/* Message */} - - - Message - - {/* Hang Up */} - - - Hang Up - - - - -
- - - - - -
- {(!!voice_mobs.length && ( - - {voice_mobs.map((mob) => ( - - - - ))} - - )) || No connections} -
-
- {(!!communicating.length && ( - - {communicating.map((comm) => ( - - - {decodeHtmlEntities(comm.name)} - - - - {(video_comm === null && ( - - )) || - (phone_video_comm === comm.ref && ( - - ))} - - - ))} -
- )) || No connections} -
-
- {(!!requestsReceived.length && ( - - {requestsReceived.map((request) => ( - - {decodeHtmlEntities(request.address)} - - - - - - ))} - - )) || No requests received.} -
-
- {(!!invitesSent.length && ( - - {invitesSent.map((invite) => ( - - {decodeHtmlEntities(invite.address)} - - - - - ))} - - )) || No invites sent.} -
-
-
- )) || - (currentTab === CONTTAB && ) || - (currentTab === MESSTAB && ( -
- {(imContacts.length && ( - - {imContacts.map((device) => ( - - - {decodeHtmlEntities(device.name)}: - - - {device.address} - - - - - - ))} -
- )) || ( - - You haven't sent any messages yet. - - - )} -
- )) || - (currentTab === MESSSUBTAB && - (clipboardMode ? ( -
- {enforceLengthLimit( - 'Conversation with ', - decodeHtmlEntities(targetAddressName), - 30, - )} - - } - buttons={ - -
- ) : ( -
- {enforceLengthLimit( - 'Conversation with ', - decodeHtmlEntities(targetAddressName), - 30, - )} - - } - buttons={ - -
- ))) || - (currentTab === NEWSTAB && ( -
- {(!feeds.length && ( - - Error: No newsfeeds available. Please try again later. - - )) || - (target_feed && ( -
- act('newsfeed', { newsfeed: null }) - } - > - Back - - } - > - {target_feed.messages.map((message) => ( -
- - {decodeHtmlEntities(message.body)} - {!!message.img && ( - - - {decodeHtmlEntities(message.caption) || null} - - )} - - [{message.message_type} by{' '} - {decodeHtmlEntities(message.author)} -{' '} - {message.time_stamp}] - -
- ))} -
- )) || ( - <> -
-
- {latest_news.map((news) => ( - -
- {decodeHtmlEntities(news.channel)} - -
- - {decodeHtmlEntities(news.body)} - {!!news.img && ( - - [image omitted, view story for more - details] - {news.caption || null} - - )} - - [{news.message_type} by{' '} - - {news.author} - {' '} - - {news.time_stamp}] - -
- ))} -
-
-
- {feeds.map((feed) => ( - - ))} -
- - )} -
- )) || - (currentTab === NOTETAB && ( -
act('edit')}> - Edit Notes - - } - > -
- {note} -
-
- )) || - (currentTab === WTHRTAB && ) || - (currentTab === MANITAB && ) || - (currentTab === SETTTAB && ( -
- - - - - - - - - {decodeHtmlEntities(occupation)} - - - {connectionStatus === 1 ? ( - Connected - ) : ( - Disconnected - )} - - - {address} - - - act('toggle_visibility')} - > - {visible - ? 'This device can be seen by other devices.' - : 'This device is invisible to other devices.'} - - - - act('toggle_ringer')} - > - {ring ? 'Ringer on.' : 'Ringer off.'} - - - - -
- )) || - (notFound(currentTab) && )} -
- - - )} -
-
- ); -}; - -const VideoComm = (props) => { - const { act, data } = useBackend(); - - const { video_comm, mapRef } = data; - - const { videoSetting, setVideoSetting } = props; - - if (videoSetting === 0) { - return ( - - - - -
- - - ); - } - return null; -}; - -const TemplateError = (props) => { - const { act, data } = useBackend(); - - const { currentTab } = data; - - return ( -
- You tried to access tab #{currentTab}, but there was no template defined! -
- ); -}; - -const CommunicatorHeader = (props) => { - const { act, data } = useBackend(); - - const { time, connectionStatus, owner, occupation } = data; - - return ( -
- - {time} - - - - {decodeHtmlEntities(owner)} - {decodeHtmlEntities(occupation)} - -
- ); -}; - -const CommunicatorFooter = (props) => { - const { act, data } = useBackend(); - - const { flashlight } = data; - - const { videoSetting, setVideoSetting } = props; - - return ( - - -
+ + + ); + } + return null; +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorHomeTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorHomeTab.tsx new file mode 100644 index 00000000000..9e8809cc77f --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorHomeTab.tsx @@ -0,0 +1,67 @@ +import { useBackend } from '../../backend'; +import { Box, Button, Flex, Icon } from '../../components'; +import { Data } from './types'; + +export const CommunicatorHomeTab = (props) => { + const { act, data } = useBackend(); + + const { homeScreen } = data; + + return ( + + {homeScreen.map((app) => ( + + + {app.module} + + ))} + + ); +}; + +/* Helper for notifications (yes this is a mess, but whatever, it works) */ +const hasNotifications = (app: string | null) => { + const { data } = useBackend(); + + const { + /* Phone Notifications */ + voice_mobs, + communicating, + requestsReceived, + invitesSent, + video_comm, + } = data; + + if (app === 'Phone') { + if ( + voice_mobs.length || + communicating.length || + requestsReceived.length || + invitesSent.length || + video_comm + ) { + return true; + } + } + + return false; +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorMessageSubTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorMessageSubTab.tsx new file mode 100644 index 00000000000..5e15ccfb36b --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorMessageSubTab.tsx @@ -0,0 +1,190 @@ +import { decodeHtmlEntities } from 'common/string'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicatorMessageSubTab = (props: { + clipboardMode: boolean; + onClipboardMode: Function; +}) => { + const { act, data } = useBackend(); + + const { clipboardMode, onClipboardMode } = props; + + const { targetAddress, targetAddressName, imList } = data; + + return clipboardMode ? ( +
+ {enforceLengthLimit( + 'Conversation with ', + decodeHtmlEntities(targetAddressName), + 30, + )} + + } + buttons={ + +
+ ) : ( +
+ {enforceLengthLimit( + 'Conversation with ', + decodeHtmlEntities(targetAddressName), + 30, + )} + + } + buttons={ + +
+ ); +}; + +/* Actual messaging conversation */ +const IsIMOurs = ( + im: { address: string; to_address: string; im: string }, + targetAddress: string, +) => { + return im.address !== targetAddress; +}; + +const enforceLengthLimit = (prefix: string, name: string, length: number) => { + if ((prefix + name).length > length) { + if (name.length > length) { + return name.slice(0, length) + '...'; + } + return name; + } + return prefix + name; +}; + +const findClassMessage = ( + im: { address: string; to_address: string; im: string }, + targetAddress: string, + lastIndex: number, + filterArray: { + address: string; + to_address: string; + im: string; + }[], +) => { + if (lastIndex < 0 || lastIndex > filterArray.length) { + return IsIMOurs(im, targetAddress) + ? 'TinderMessage_First_Sent' + : 'TinderMessage_First_Received'; + } + + let thisSent = IsIMOurs(im, targetAddress); + let lastSent = IsIMOurs(filterArray[lastIndex], targetAddress); + if (thisSent && lastSent) { + return 'TinderMessage_Subsequent_Sent'; + } else if (!thisSent && !lastSent) { + return 'TinderMessage_Subsequent_Received'; + } + return thisSent ? 'TinderMessage_First_Sent' : 'TinderMessage_First_Received'; +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorMessageTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorMessageTab.tsx new file mode 100644 index 00000000000..6fe344719ae --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorMessageTab.tsx @@ -0,0 +1,63 @@ +import { decodeHtmlEntities } from 'common/string'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section, Table } from '../../components'; +import { CONTTAB, MESSSUBTAB } from './constants'; +import { Data } from './types'; + +export const CommunicatorMessageTab = (props) => { + const { act, data } = useBackend(); + + const { imContacts } = data; + + return ( +
+ {(imContacts.length && ( + + {imContacts.map((device) => ( + + + {decodeHtmlEntities(device.name)}: + + + {device.address} + + + + + + ))} +
+ )) || ( + + You haven't sent any messages yet. + + + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorNewsTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorNewsTab.tsx new file mode 100644 index 00000000000..5ed6049bfd9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorNewsTab.tsx @@ -0,0 +1,105 @@ +import { decodeHtmlEntities } from 'common/string'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicatorNewsTab = (props) => { + const { act, data } = useBackend(); + + const { feeds, target_feed, latest_news } = data; + + return ( +
+ {(!feeds.length && ( + + Error: No newsfeeds available. Please try again later. + + )) || + (target_feed && ( +
act('newsfeed', { newsfeed: null })} + > + Back + + } + > + {target_feed.messages.map((message) => ( +
+ - {decodeHtmlEntities(message.body)} + {!!message.img && ( + + + {decodeHtmlEntities(message.caption) || null} + + )} + + [{message.message_type} by{' '} + {decodeHtmlEntities(message.author)} - {message.time_stamp}] + +
+ ))} +
+ )) || ( + <> +
+
+ {latest_news.map((news) => ( + +
+ {decodeHtmlEntities(news.channel)} + +
+ - {decodeHtmlEntities(news.body)} + {!!news.img && ( + + [image omitted, view story for more details] + {news.caption || null} + + )} + + [{news.message_type} by{' '} + + {news.author} + {' '} + - {news.time_stamp}] + +
+ ))} +
+
+
+ {feeds.map((feed) => ( + + ))} +
+ + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorNoteTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorNoteTab.tsx new file mode 100644 index 00000000000..35edfc6a61c --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorNoteTab.tsx @@ -0,0 +1,34 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicatorNoteTab = (props) => { + const { act, data } = useBackend(); + + const { note } = data; + + return ( +
act('edit')}> + Edit Notes + + } + > +
+ {note} +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorPhoneTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorPhoneTab.tsx new file mode 100644 index 00000000000..4e50303131b --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorPhoneTab.tsx @@ -0,0 +1,328 @@ +import { decodeHtmlEntities } from 'common/string'; + +import { useBackend } from '../../backend'; +import { + Box, + Button, + Flex, + Icon, + Input, + LabeledList, + Section, + Table, +} from '../../components'; +import { MESSTAB } from './constants'; +import { Data } from './types'; + +export const CommunicatorPhoneTab = (props) => { + const { act, data } = useBackend(); + + const { selfie_mode, targetAddress } = data; + + const validCharacters = [ + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + ]; + + const buttonArray = validCharacters.map((char) => ( + + )); + + let finalArray: React.JSX.Element[] = []; + + for (let i = 0; i < buttonArray.length; i += 4) { + finalArray.push( + + {buttonArray[i]} + {buttonArray[i + 1]} + {buttonArray[i + 2]} + {buttonArray[i + 3]} + , + ); + } + + return ( +
+ + + + + act('write_target_address', { val: val })} + /> + + + + Dial + + {/* Message */} + + + Message + + {/* Hang Up */} + + + Hang Up + + + + +
+ + + + + + + + + +
+
+ ); +}; + +const CommunicatorPhoneTabExternal = (props) => { + const { act, data } = useBackend(); + + const { voice_mobs } = data; + + return ( +
+ {(!!voice_mobs.length && ( + + {voice_mobs.map((mob) => ( + + + + ))} + + )) || No connections} +
+ ); +}; + +const CommunicatorPhoneTabInternal = (props) => { + const { act, data } = useBackend(); + + const { communicating, video_comm, phone_video_comm } = data; + + return ( +
+ {(!!communicating.length && ( + + {communicating.map((comm) => ( + + + {decodeHtmlEntities(comm.name)} + + + + {(video_comm === null && ( + + )) || + (phone_video_comm === comm.ref && ( + + ))} + + + ))} +
+ )) || No connections} +
+ ); +}; + +const CommunicatorPhoneTabRequest = (props) => { + const { act, data } = useBackend(); + + const { requestsReceived } = data; + + return ( +
+ {(!!requestsReceived.length && ( + + {requestsReceived.map((request) => ( + + {decodeHtmlEntities(request.address)} + + + + + + ))} + + )) || No requests received.} +
+ ); +}; + +const CommunicatorPhoneTabInvite = (props) => { + const { act, data } = useBackend(); + + const { invitesSent } = data; + + return ( +
+ {(!!invitesSent.length && ( + + {invitesSent.map((invite) => ( + + {decodeHtmlEntities(invite.address)} + + + + + ))} + + )) || No invites sent.} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorSettingsTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorSettingsTab.tsx new file mode 100644 index 00000000000..3cfcb217b11 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorSettingsTab.tsx @@ -0,0 +1,74 @@ +import { decodeHtmlEntities } from 'common/string'; + +import { useBackend } from '../../backend'; +import { Box, Button, LabeledList, Section } from '../../components'; +import { Data } from './types'; + +export const CommunicatorSettingsTab = (props) => { + const { act, data } = useBackend(); + + const { + address, + selfie_mode, + owner, + occupation, + connectionStatus, + visible, + ring, + } = data; + + return ( +
+ + + + + + + + + {decodeHtmlEntities(occupation)} + + + {connectionStatus === 1 ? ( + Connected + ) : ( + Disconnected + )} + + + {address} + + + act('toggle_visibility')} + > + {visible + ? 'This device can be seen by other devices.' + : 'This device is invisible to other devices.'} + + + + act('toggle_ringer')} + > + {ring ? 'Ringer on.' : 'Ringer off.'} + + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/CommunicatorWeatherTab.tsx b/tgui/packages/tgui/interfaces/Communicator/CommunicatorWeatherTab.tsx new file mode 100644 index 00000000000..5c7412253da --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/CommunicatorWeatherTab.tsx @@ -0,0 +1,97 @@ +import { filter } from 'common/collections'; +import { decodeHtmlEntities, toTitleCase } from 'common/string'; + +import { useBackend } from '../../backend'; +import { Box, LabeledList, Section } from '../../components'; +import { AirContent, WeatherTabData } from './types'; + +export const CommunicatorWeatherTab = (props) => { + const { act, data } = useBackend(); + + const { aircontents, weather } = data; + + const deg: string = '\u00B0'; + + return ( +
+
+ + {filter( + (i: AirContent) => + i.val !== '0' || + i.entry === 'Pressure' || + i.entry === 'Temperature', + )(aircontents).map((item: AirContent) => ( + + {item.val} + {decodeHtmlEntities(item.units)} + + ))} + +
+
+ {(!!weather.length && ( + + {weather.map((wr) => ( + + + {wr.Time} + + {toTitleCase(wr.Weather)} + + + Current: {wr.Temperature.toFixed()} {deg}C | High:{' '} + {wr.High.toFixed()} {deg}C | Low: {wr.Low.toFixed()} {deg}C + + + {wr.WindDir} + + + {wr.WindSpeed} + + + {decodeHtmlEntities(wr.Forecast)} + + + + ))} + + )) || ( + + No weather reports available. Please check back later. + + )} +
+
+ ); +}; + +/* Weather App */ +const getItemColor = ( + value: number, + min2: number, + min1: number, + max1: number, + max2: number, +) => { + if (value < min2) { + return 'bad'; + } else if (value < min1) { + return 'average'; + } else if (value > max1) { + return 'average'; + } else if (value > max2) { + return 'bad'; + } + return 'good'; +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/constants.ts b/tgui/packages/tgui/interfaces/Communicator/constants.ts new file mode 100644 index 00000000000..f37d974320a --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/constants.ts @@ -0,0 +1,27 @@ +export const HOMETAB = 1; +export const PHONTAB = 2; +export const CONTTAB = 3; +export const MESSTAB = 4; +export const MESSSUBTAB = 40; +export const NEWSTAB = 5; +export const NOTETAB = 6; +export const WTHRTAB = 7; +export const MANITAB = 8; +export const SETTTAB = 9; + +export const tabs = [ + HOMETAB, + PHONTAB, + CONTTAB, + MESSTAB, + MESSSUBTAB, + NEWSTAB, + NOTETAB, + WTHRTAB, + MANITAB, + SETTTAB, +]; + +export function notFound(val) { + return tabs.includes(val); +} diff --git a/tgui/packages/tgui/interfaces/Communicator/index.tsx b/tgui/packages/tgui/interfaces/Communicator/index.tsx new file mode 100644 index 00000000000..01941690715 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/index.tsx @@ -0,0 +1,88 @@ +import { useState } from 'react'; + +import { useBackend } from '../../backend'; +import { Box } from '../../components'; +import { Window } from '../../layouts'; +import { CrewManifestContent } from '../CrewManifest'; +import { CommunicatorContactTab } from './CommunicatorContactTab'; +import { + CommunicatorFooter, + CommunicatorHeader, + TemplateError, + VideoComm, +} from './CommunicatorGeneral'; +import { CommunicatorHomeTab } from './CommunicatorHomeTab'; +import { CommunicatorMessageSubTab } from './CommunicatorMessageSubTab'; +import { CommunicatorMessageTab } from './CommunicatorMessageTab'; +import { CommunicatorNewsTab } from './CommunicatorNewsTab'; +import { CommunicatorNoteTab } from './CommunicatorNoteTab'; +import { CommunicatorPhoneTab } from './CommunicatorPhoneTab'; +import { CommunicatorSettingsTab } from './CommunicatorSettingsTab'; +import { CommunicatorWeatherTab } from './CommunicatorWeatherTab'; +import { notFound, tabs } from './constants'; +import { Data } from './types'; + +export const Communicator = () => { + const { act, data } = useBackend(); + + const { currentTab, video_comm } = data; + + const tab: React.JSX.Element[] = []; + + const [videoSetting, setVideoSetting] = useState(0); + const [clipboardMode, setClipboardMode] = useState(false); + + function handleClipboardMode(value: boolean) { + setClipboardMode(value); + } + + tab[tabs[0]] = ; + tab[tabs[1]] = ; + tab[tabs[2]] = ; + tab[tabs[3]] = ; + tab[tabs[4]] = ( + + ); + tab[tabs[5]] = ; + tab[tabs[6]] = ; + tab[tabs[7]] = ; + tab[tabs[8]] = ; + tab[tabs[9]] = ; + + return ( + + + {video_comm && ( + + )} + {(!video_comm || videoSetting !== 0) && ( + <> + + + {tab[currentTab] || + (notFound(currentTab) && ( + + ))} + + + + )} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Communicator/types.ts b/tgui/packages/tgui/interfaces/Communicator/types.ts new file mode 100644 index 00000000000..7d32b0e34b0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Communicator/types.ts @@ -0,0 +1,96 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + // GENERAL + currentTab: number; + video_comm: BooleanLike; + mapRef: string; + + // FOOTER + time: string; + connectionStatus: BooleanLike; + owner: string; + occupation: string; + + // HEADER + flashlight: BooleanLike; + + // HOMETAB + homeScreen: { number: number; module: string; icon: string }[]; + + // PHONETAB + targetAddress: string; + voice_mobs: { name: string; true_name: string; ref: string }[]; + communicating: { + address: string; + name: string; + true_name: string; + ref: string; + }[]; + requestsReceived: { address: string; name: string; ref: string }[]; + invitesSent: { address: string; name: string }[]; + phone_video_comm: string; + selfie_mode: BooleanLike; + + // MESSAGING + imContacts: { address: string; name: string }[]; + targetAddressName: string; + imList: { address: string; to_address: string; im: string }[]; + + // SETTINGS + address: string; + visible: BooleanLike; + ring: BooleanLike; + + // NEWSTAB + feeds: { index: number; name: string }[]; + target_feed: { name: string; author: string; messages: NewsMessage[] } | null; + latest_news: NewsMessage[]; + + // NOTETAB + note: string; +}; + +type NewsMessage = { + ref: string; + body: string; + img: string; + caption: string; + message_type: string; + author: string; + time_stamp: string; + + index: number; + channel: string; +}; + +export type ContactsTabData = { + knownDevices: { address: string; name: string }[]; +}; + +export type WeatherTabData = { + aircontents: AirContent[]; + weather: Weather[]; +}; + +export type AirContent = { + entry: string; + val; + bad_low: number; + poor_low: number; + poor_high: number; + bad_high: number; + units; +}; + +type Weather = { + Planet: string; + Time: string; + Weather: string; + Temperature; + High; + Low; + WindDir; + WindSpeed; + Forecast: string; +}; diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep1.tsx b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep1.tsx new file mode 100644 index 00000000000..e950bdad7c3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep1.tsx @@ -0,0 +1,53 @@ +import { useBackend } from '../../backend'; +import { Box, Button, Section, Table } from '../../components'; + +// This had a pretty gross backend so this was unfortunately one of the +// best ways of doing it. +export const CfStep1 = (props) => { + const { act } = useBackend(); + return ( +
+ + Choose your Device + + + + + + + + + + + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator.jsx b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep2.tsx similarity index 67% rename from tgui/packages/tgui/interfaces/ComputerFabricator.jsx rename to tgui/packages/tgui/interfaces/ComputerFabricator/CfStep2.tsx index 619f5c0feb4..6975129674a 100644 --- a/tgui/packages/tgui/interfaces/ComputerFabricator.jsx +++ b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep2.tsx @@ -1,91 +1,31 @@ import { multiline } from 'common/string'; -import { useBackend } from '../backend'; -import { Box, Button, Section, Table, Tooltip } from '../components'; -import { Window } from '../layouts'; +import { useBackend } from '../../backend'; +import { Box, Button, Section, Table, Tooltip } from '../../components'; +import { Data } from './types'; -export const ComputerFabricator = (props) => { - const { act, data } = useBackend(); - return ( - - -
- Your perfect device, only three steps away... -
- {data.state !== 0 && ( - - )} - {data.state === 0 && } - {data.state === 1 && } - {data.state === 2 && } - {data.state === 3 && } -
-
- ); -}; +export const CfStep2 = (props) => { + const { act, data } = useBackend(); -// This had a pretty gross backend so this was unfortunately one of the -// best ways of doing it. -const CfStep1 = (props) => { - const { act, data } = useBackend(); - return ( -
- - Choose your Device - - - - - - - - - - - -
-
-
- ); -}; + const { + totalprice, + hw_battery, + hw_disk, + hw_netcard, + hw_nanoprint, + hw_card, + devtype, + hw_cpu, + hw_tesla, + } = data; -const CfStep2 = (props) => { - const { act, data } = useBackend(); return (
- {data.totalprice}â‚® + {totalprice}â‚® } > @@ -103,7 +43,7 @@ const CfStep2 = (props) => { - {data.devtype !== 2 && ( + {devtype !== 2 && ( Processor Unit: @@ -329,7 +269,7 @@ const CfStep2 = (props) => {
); }; - -const CfStep3 = (props) => { - const { act, data } = useBackend(); - return ( -
- - Your device is ready for fabrication... - - - Please swipe your ID now to authorize payment of: -   - - {data.totalprice}â‚® - - -
- ); -}; - -const CfStep4 = (props) => { - return ( -
- - Thank you for your purchase! - - - If you experience any difficulties with your new device, please contact - your local network administrator. - -
- ); -}; diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep3.tsx b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep3.tsx new file mode 100644 index 00000000000..668b739f29f --- /dev/null +++ b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep3.tsx @@ -0,0 +1,19 @@ +import { Box, Section } from '../../components'; + +export const CfStep3 = (props: { totalprice: number }) => { + const { totalprice } = props; + return ( +
+ + Your device is ready for fabrication... + + + Please swipe your ID now to authorize payment of: +   + + {totalprice}â‚® + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep4.tsx b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep4.tsx new file mode 100644 index 00000000000..62fb59fa851 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ComputerFabricator/CfStep4.tsx @@ -0,0 +1,15 @@ +import { Box, Section } from '../../components'; + +export const CfStep4 = (props) => { + return ( +
+ + Thank you for your purchase! + + + If you experience any difficulties with your new device, please contact + your local network administrator. + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator/index.tsx b/tgui/packages/tgui/interfaces/ComputerFabricator/index.tsx new file mode 100644 index 00000000000..b3919e0d0ac --- /dev/null +++ b/tgui/packages/tgui/interfaces/ComputerFabricator/index.tsx @@ -0,0 +1,37 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; +import { Window } from '../../layouts'; +import { CfStep1 } from './CfStep1'; +import { CfStep2 } from './CfStep2'; +import { CfStep3 } from './CfStep3'; +import { CfStep4 } from './CfStep4'; +import { Data } from './types'; + +export const ComputerFabricator = (props) => { + const { act, data } = useBackend(); + + const { state, totalprice } = data; + + const tab: React.JSX.Element[] = []; + + tab[0] = ; + tab[1] = ; + tab[2] = ; + tab[3] = ; + + return ( + + +
+ Your perfect device, only three steps away... +
+ {state !== 0 && ( + + )} + {tab[state]} +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ComputerFabricator/types.ts b/tgui/packages/tgui/interfaces/ComputerFabricator/types.ts new file mode 100644 index 00000000000..405a1007bd2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ComputerFabricator/types.ts @@ -0,0 +1,14 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + state: number; + devtype: number | undefined; + hw_battery: number | undefined; + hw_disk: number | undefined; + hw_netcard: number | undefined; + hw_tesla: BooleanLike; + hw_nanoprint: BooleanLike; + hw_card: BooleanLike; + hw_cpu: number | undefined; + totalprice: number | undefined; +}; diff --git a/tgui/packages/tgui/interfaces/CookingAppliance.jsx b/tgui/packages/tgui/interfaces/CookingAppliance.tsx similarity index 87% rename from tgui/packages/tgui/interfaces/CookingAppliance.jsx rename to tgui/packages/tgui/interfaces/CookingAppliance.tsx index 4efa443a6a0..7b951cd5db1 100644 --- a/tgui/packages/tgui/interfaces/CookingAppliance.jsx +++ b/tgui/packages/tgui/interfaces/CookingAppliance.tsx @@ -1,3 +1,5 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { AnimatedNumber, @@ -9,8 +11,22 @@ import { } from '../components'; import { Window } from '../layouts'; +type Data = { + temperature: number; + optimalTemp: number; + temperatureEnough: BooleanLike; + efficiency: number; + containersRemovable: BooleanLike; + our_contents: { + empty: BooleanLike; + progress: number; + progressText: string; + container: string | null; + }[]; +}; + export const CookingAppliance = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { temperature, diff --git a/tgui/packages/tgui/interfaces/CrewMonitor.jsx b/tgui/packages/tgui/interfaces/CrewMonitor.jsx deleted file mode 100644 index 0dfb9c3955e..00000000000 --- a/tgui/packages/tgui/interfaces/CrewMonitor.jsx +++ /dev/null @@ -1,188 +0,0 @@ -import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; -import { useState } from 'react'; - -import { useBackend } from '../backend'; -import { Box, Button, Icon, NanoMap, Table, Tabs } from '../components'; -import { Window } from '../layouts'; - -const getStatText = (cm) => { - if (cm.dead) { - return 'Deceased'; - } - if (parseInt(cm.stat, 10) === 1) { - // Unconscious - return 'Unconscious'; - } - return 'Living'; -}; - -const getStatColor = (cm) => { - if (cm.dead) { - return 'red'; - } - if (parseInt(cm.stat, 10) === 1) { - // Unconscious - return 'orange'; - } - return 'green'; -}; - -export const CrewMonitor = () => { - const [tabIndex, setTabIndex] = useState(0); - const [zoom, setZoom] = useState(1); - - function handleTabIndex(value) { - setTabIndex(value); - } - - function handleZoom(value) { - setZoom(value); - } - - return ( - - - - - - ); -}; - -export const CrewMonitorContent = (props) => { - const { act, data, config } = useBackend(); - - const crew = flow([ - sortBy((cm) => cm.name), - sortBy((cm) => cm?.x), - sortBy((cm) => cm?.y), - sortBy((cm) => cm?.realZ), - ])(data.crewmembers || []); - - let body; - // Data view - if (props.tabIndex === 0) { - body = ( - - - Name - Status - Location - - {crew.map((cm) => ( - - - {cm.name} ({cm.assignment}) - - - - {getStatText(cm)} - - {cm.sensor_type >= 2 ? ( - - {'('} - - {cm.brute} - - {'|'} - - {cm.fire} - - {'|'} - - {cm.tox} - - {'|'} - - {cm.oxy} - - {')'} - - ) : null} - - - {cm.sensor_type === 3 ? ( - data.isAI ? ( - - ) : ( - cm.area + ' (' + cm.x + ', ' + cm.y + ', ' + cm.z + ')' - ) - ) : ( - 'Not Available' - )} - - - ))} -
- ); - } else if (props.tabIndex === 1) { - // Please note, if you ever change the zoom values, - // you MUST update styles/components/Tooltip.scss - // and change the @for scss to match. - body = ; - } else { - body = 'ERROR'; - } - - return ( - <> - - props.onTabIndex(0)} - > - Data View - - props.onTabIndex(1)} - > - Map View - - - {body} - - ); -}; - -const CrewMonitorMapView = (props) => { - const { act, config, data } = useBackend(); - return ( - - props.onZoom(v)}> - {data.crewmembers - .filter( - (x) => x.sensor_type === 3 && ~~x.realZ === ~~config.mapZLevel, - ) - .map((cm) => ( - - ))} - - - ); -}; diff --git a/tgui/packages/tgui/interfaces/CrewMonitor.tsx b/tgui/packages/tgui/interfaces/CrewMonitor.tsx new file mode 100644 index 00000000000..385f158dd83 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CrewMonitor.tsx @@ -0,0 +1,231 @@ +import { sortBy } from 'common/collections'; +import { flow } from 'common/fp'; +import { BooleanLike } from 'common/react'; +import { useState } from 'react'; + +import { useBackend } from '../backend'; +import { Box, Button, Icon, NanoMap, Table, Tabs } from '../components'; +import { Window } from '../layouts'; + +type Data = { + zoomScale: number; + isAI: BooleanLike; + map_levels: number[]; + crewmembers: crewmember[]; +}; + +type crewmember = { + sensor_type: number; + name: string; + rank: string; + assignment: string; + dead: BooleanLike; + stat: number; + oxy: number; + tox: number; + fire: number; + brute: number; + area: string; + x: number; + y: number; + realZ: number; + z: number; + ref: string; +}; + +const getStatText = (cm: crewmember) => { + if (cm.dead) { + return 'Deceased'; + } + if (cm.stat === 1) { + // Unconscious + return 'Unconscious'; + } + return 'Living'; +}; + +const getStatColor = (cm: crewmember) => { + if (cm.dead) { + return 'red'; + } + if (cm.stat === 1) { + // Unconscious + return 'orange'; + } + return 'green'; +}; + +export const CrewMonitor = () => { + const [tabIndex, setTabIndex] = useState(0); + const [zoom, setZoom] = useState(1); + + function handleTabIndex(value: number) { + setTabIndex(value); + } + + function handleZoom(value: number) { + setZoom(value); + } + + return ( + + + + + + ); +}; + +export const CrewMonitorContent = (props: { + tabIndex: number; + zoom: number; + onTabIndex: Function; + onZoom: Function; +}) => { + const { data } = useBackend(); + + const { crewmembers = [] } = data; + + const crew: crewmember[] = flow([ + sortBy((cm: crewmember) => cm.name), + sortBy((cm: crewmember) => cm?.x), + sortBy((cm: crewmember) => cm?.y), + sortBy((cm: crewmember) => cm?.realZ), + ])(crewmembers); + + const tab: React.JSX.Element[] = []; + // Data view + // Please note, if you ever change the zoom values, + // you MUST update styles/components/Tooltip.scss + // and change the @for scss to match. + tab[0] = ; + + tab[1] = ; + + return ( + <> + + props.onTabIndex(0)} + > + Data View + + props.onTabIndex(1)} + > + Map View + + + {tab[props.tabIndex] || ERROR} + + ); +}; + +const CrewMonitorCrew = (props: { crew: crewmember[] }) => { + const { act, data } = useBackend(); + + const { crew } = props; + + const { isAI } = data; + + return ( + + + Name + Status + Location + + {crew.map((cm) => ( + + + {cm.name} ({cm.assignment}) + + + + {getStatText(cm)} + + {cm.sensor_type >= 2 ? ( + + {'('} + + {cm.brute} + + {'|'} + + {cm.fire} + + {'|'} + + {cm.tox} + + {'|'} + + {cm.oxy} + + {')'} + + ) : null} + + + {cm.sensor_type === 3 ? ( + isAI ? ( + + ) : ( + cm.area + ' (' + cm.x + ', ' + cm.y + ', ' + cm.z + ')' + ) + ) : ( + 'Not Available' + )} + + + ))} +
+ ); +}; + +const CrewMonitorMapView = (props: { zoom: number; onZoom: Function }) => { + const { config, data } = useBackend(); + + const { zoomScale, crewmembers } = data; + + return ( + + props.onZoom(v)}> + {crewmembers + .filter( + (x) => x.sensor_type === 3 && ~~x.realZ === ~~config.mapZLevel, + ) + .map((cm) => ( + + ))} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Cryo.jsx b/tgui/packages/tgui/interfaces/Cryo/CryoContent.tsx similarity index 70% rename from tgui/packages/tgui/interfaces/Cryo.jsx rename to tgui/packages/tgui/interfaces/Cryo/CryoContent.tsx index 5ec9994bb38..9ac1a8dff4b 100644 --- a/tgui/packages/tgui/interfaces/Cryo.jsx +++ b/tgui/packages/tgui/interfaces/Cryo/CryoContent.tsx @@ -1,4 +1,6 @@ -import { useBackend } from '../backend'; +import { toFixed } from 'common/math'; + +import { useBackend } from '../../backend'; import { AnimatedNumber, Box, @@ -8,50 +10,16 @@ import { LabeledList, ProgressBar, Section, -} from '../components'; -import { Window } from '../layouts'; +} from '../../components'; +import { damageTypes, statNames } from './constants'; +import { Data } from './types'; -const damageTypes = [ - { - label: 'Resp.', - type: 'oxyLoss', - }, - { - label: 'Toxin', - type: 'toxLoss', - }, - { - label: 'Brute', - type: 'bruteLoss', - }, - { - label: 'Burn', - type: 'fireLoss', - }, -]; - -const statNames = [ - ['good', 'Conscious'], - ['average', 'Unconscious'], - ['bad', 'DEAD'], -]; - -export const Cryo = (props) => { - return ( - - - - - - ); -}; - -const CryoContent = (props) => { - const { act, data } = useBackend(); +export const CryoContent = (props) => { + const { act, data } = useBackend(); const { isOperating, hasOccupant, - occupant = [], + occupant, cellTemperature, cellTemperatureStatus, isBeakerLoaded, @@ -60,7 +28,7 @@ const CryoContent = (props) => { <>
{ 0 ? 'good' : 'average'} > - + toFixed(value)} + /> { {statNames[occupant.stat][1]} - - {' K'} + toFixed(value) + ' K'} + /> - {damageTypes.map((damageType) => ( - + {damageTypes.map((damageType, i) => ( + toFixed(value)} /> @@ -113,7 +87,7 @@ const CryoContent = (props) => { ) : ( - +
No occupant detected.
@@ -155,7 +129,7 @@ const CryoContent = (props) => { }; const CryoBeaker = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { isBeakerLoaded, beakerLabel, beakerVolume } = data; if (isBeakerLoaded) { return ( @@ -165,7 +139,7 @@ const CryoBeaker = (props) => { {beakerVolume ? ( Math.round(v) + ' units remaining'} + format={(v) => toFixed(v) + ' units remaining'} /> ) : ( 'Beaker is empty' diff --git a/tgui/packages/tgui/interfaces/Cryo/constants.ts b/tgui/packages/tgui/interfaces/Cryo/constants.ts new file mode 100644 index 00000000000..b300a5f9822 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Cryo/constants.ts @@ -0,0 +1,24 @@ +export const damageTypes: { label: string; type: string }[] = [ + { + label: 'Resp.', + type: 'oxyLoss', + }, + { + label: 'Toxin', + type: 'toxLoss', + }, + { + label: 'Brute', + type: 'bruteLoss', + }, + { + label: 'Burn', + type: 'fireLoss', + }, +]; + +export const statNames: string[][] = [ + ['good', 'Conscious'], + ['average', 'Unconscious'], + ['bad', 'DEAD'], +]; diff --git a/tgui/packages/tgui/interfaces/Cryo/index.tsx b/tgui/packages/tgui/interfaces/Cryo/index.tsx new file mode 100644 index 00000000000..481740ce719 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Cryo/index.tsx @@ -0,0 +1,12 @@ +import { Window } from '../../layouts'; +import { CryoContent } from './CryoContent'; + +export const Cryo = (props) => { + return ( + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Cryo/types.ts b/tgui/packages/tgui/interfaces/Cryo/types.ts new file mode 100644 index 00000000000..49d2d2e3031 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Cryo/types.ts @@ -0,0 +1,23 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + isOperating: BooleanLike; + hasOccupant: BooleanLike; + occupant: { + name: string; + stat: number; + health: number; + maxHealth: number; + minHealth: number; + bruteLoss: number; + oxyLoss: number; + toxLoss: number; + fireLoss: number; + bodyTemperature: number; + }; + cellTemperature: number; + cellTemperatureStatus: string; + isBeakerLoaded: BooleanLike; + beakerLabel: string | null; + beakerVolume: number; +}; diff --git a/tgui/packages/tgui/interfaces/CryoStorage.jsx b/tgui/packages/tgui/interfaces/CryoStorage.tsx similarity index 61% rename from tgui/packages/tgui/interfaces/CryoStorage.jsx rename to tgui/packages/tgui/interfaces/CryoStorage.tsx index d947810e38e..b564e95578b 100644 --- a/tgui/packages/tgui/interfaces/CryoStorage.jsx +++ b/tgui/packages/tgui/interfaces/CryoStorage.tsx @@ -1,16 +1,29 @@ +import { BooleanLike } from 'common/react'; import { useState } from 'react'; import { useBackend } from '../backend'; -import { Box, Button, NoticeBox, Section, Tabs } from '../components'; +import { Box, NoticeBox, Section, Tabs } from '../components'; import { Window } from '../layouts'; +export type Data = { + real_name: string; + allow_items: BooleanLike; + crew: string[]; + items: string[]; +}; + export const CryoStorage = (props) => { - const { act, data } = useBackend(); + const { data } = useBackend(); const { real_name, allow_items } = data; const [tab, setTab] = useState(0); + const tabs: React.JSX.Element[] = []; + + tabs[0] = ; + tabs[1] = allow_items ? : ; + return ( @@ -25,15 +38,14 @@ export const CryoStorage = (props) => { )} Welcome, {real_name}. - {tab === 0 && } - {!!allow_items && tab === 1 && } + {tabs[tab]} ); }; export const CryoStorageCrew = (props) => { - const { act, data } = useBackend(); + const { data } = useBackend(); const { crew } = data; @@ -50,7 +62,29 @@ export const CryoStorageCrew = (props) => { }; export const CryoStorageItems = (props) => { - const { act, data } = useBackend(); + const { data } = useBackend(); + + const { items } = data; + + return ( +
+ {(items.length && + items.map((item) => ( + + {item} + + ))) || No items stored.} +
+ ); +}; + +export const CryoStorageDefaultError = (props) => { + return Disabled; +}; + +/* Unused here +export const CryoStorageItems = (props) => { + const { act, data } = useBackend(); const { items } = data; @@ -76,3 +110,4 @@ export const CryoStorageItems = (props) => {
); }; +*/ diff --git a/tgui/packages/tgui/interfaces/CryoStorageVr.jsx b/tgui/packages/tgui/interfaces/CryoStorageVr.jsx deleted file mode 100644 index 173842a7a8a..00000000000 --- a/tgui/packages/tgui/interfaces/CryoStorageVr.jsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useState } from 'react'; - -import { useBackend } from '../backend'; -import { Box, NoticeBox, Section, Tabs } from '../components'; -import { Window } from '../layouts'; -import { CryoStorageCrew } from './CryoStorage'; - -export const CryoStorageVr = (props) => { - const { act, data } = useBackend(); - - const { real_name, allow_items } = data; - - const [tab, setTab] = useState(0); - - return ( - - - - setTab(0)}> - Crew - - {!!allow_items && ( - setTab(1)}> - Items - - )} - - Welcome, {real_name}. - {tab === 0 && } - {!!allow_items && tab === 1 && } - - - ); -}; - -export const CryoStorageItemsVr = (props) => { - const { act, data } = useBackend(); - - const { items } = data; - - return ( -
- {(items.length && - items.map((item) => ( - - {item} - - ))) || No items stored.} -
- ); -}; diff --git a/tgui/packages/tgui/interfaces/DNAForensics.jsx b/tgui/packages/tgui/interfaces/DNAForensics.tsx similarity index 89% rename from tgui/packages/tgui/interfaces/DNAForensics.jsx rename to tgui/packages/tgui/interfaces/DNAForensics.tsx index b1dc785183c..ab160dc8ef5 100644 --- a/tgui/packages/tgui/interfaces/DNAForensics.jsx +++ b/tgui/packages/tgui/interfaces/DNAForensics.tsx @@ -1,9 +1,18 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Button, LabeledList, ProgressBar, Section } from '../components'; import { Window } from '../layouts'; +type Data = { + scan_progress: number; + scanning: BooleanLike; + bloodsamp: string; + bloodsamp_desc: string; +}; + export const DNAForensics = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { scan_progress, scanning, bloodsamp, bloodsamp_desc } = data; return ( diff --git a/tgui/packages/tgui/interfaces/DNAModifier.jsx b/tgui/packages/tgui/interfaces/DNAModifier.jsx deleted file mode 100644 index 72560e14705..00000000000 --- a/tgui/packages/tgui/interfaces/DNAModifier.jsx +++ /dev/null @@ -1,701 +0,0 @@ -import { useBackend } from '../backend'; -import { - Box, - Button, - Dimmer, - Flex, - Icon, - Knob, - LabeledList, - ProgressBar, - Section, - Tabs, -} from '../components'; -import { Window } from '../layouts'; -import { ComplexModal } from './common/ComplexModal'; - -const stats = [ - ['good', 'Alive'], - ['average', 'Unconscious'], - ['bad', 'DEAD'], -]; - -const operations = [ - ['ui', 'Modify U.I.', 'dna'], - ['se', 'Modify S.E.', 'dna'], - ['buffer', 'Transfer Buffers', 'syringe'], - ['rejuvenators', 'Rejuvenators', 'flask'], -]; - -const rejuvenatorsDoses = [5, 10, 20, 30, 50]; - -export const DNAModifier = (props) => { - const { act, data } = useBackend(); - const { irradiating, dnaBlockSize, occupant } = data; - const isDNAInvalid = - !occupant.isViableSubject || - !occupant.uniqueIdentity || - !occupant.structuralEnzymes; - let radiatingModal; - if (irradiating) { - radiatingModal = ; - } - return ( - - - {radiatingModal} - - - - - - ); -}; - -const DNAModifierOccupant = (props) => { - const { act, data } = useBackend(); - const { locked, hasOccupant, occupant } = data; - return ( -
- - Door Lock: - - - - - } - > - {hasOccupant ? ( - <> - - - {occupant.name} - - - - - {stats[occupant.stat][1]} - - - - - {props.isDNAInvalid ? ( - - -   The occupant's DNA structure is ruined beyond - recognition, please insert a subject with an intact DNA structure. - - ) : ( - - - - - - {data.occupant.uniqueEnzymes ? ( - data.occupant.uniqueEnzymes - ) : ( - - -   Unknown - - )} - - - )} - - ) : ( - Cell unoccupied. - )} -
- ); -}; - -const DNAModifierMain = (props) => { - const { act, data } = useBackend(); - const { selectedMenuKey, hasOccupant, occupant } = data; - if (!hasOccupant) { - return ( -
- - - -
- No occupant in DNA modifier. -
-
-
- ); - } else if (props.isDNAInvalid) { - return ( -
- - - -
- No operation possible on this subject. -
-
-
- ); - } - let body; - if (selectedMenuKey === 'ui') { - body = ( - <> - - - - ); - } else if (selectedMenuKey === 'se') { - body = ( - <> - - - - ); - } else if (selectedMenuKey === 'buffer') { - body = ; - } else if (selectedMenuKey === 'rejuvenators') { - body = ; - } - return ( -
- - {operations.map((op, i) => ( - act('selectMenuKey', { key: op[0] })} - > - - {op[1]} - - ))} - - {body} -
- ); -}; - -const DNAModifierMainUI = (props) => { - const { act, data } = useBackend(); - const { - selectedUIBlock, - selectedUISubBlock, - selectedUITarget, - dnaBlockSize, - occupant, - } = data; - return ( -
- - - - value.toString(16).toUpperCase()} - ml="0" - onChange={(e, val) => act('changeUITarget', { value: val })} - /> - - - -
- ); -}; - -const DNAModifierMainSE = (props) => { - const { act, data } = useBackend(); - const { selectedSEBlock, selectedSESubBlock, dnaBlockSize, occupant } = data; - return ( -
- - -
- ); -}; - -const DNAModifierMainRadiationEmitter = (props) => { - const { act, data } = useBackend(); - const { radiationIntensity, radiationDuration } = data; - return ( -
- - - act('radiationIntensity', { value: val })} - /> - - - act('radiationDuration', { value: val })} - /> - - - -
- ); -}; - -const DNAModifierMainBuffers = (props) => { - const { act, data } = useBackend(); - const { buffers } = data; - let bufferElements = buffers.map((buffer, i) => ( - - )); - return ( - <> -
- {bufferElements} -
- - - ); -}; - -const DNAModifierMainBuffersElement = (props) => { - const { act, data } = useBackend(); - const { id, name, buffer } = props; - const isInjectorReady = data.isInjectorReady; - const realName = name + (buffer.data ? ' - ' + buffer.label : ''); - return ( - -
- - act('bufferOption', { - option: 'clear', - id: id, - }) - } - > - Clear - - - - - } - > - - - - - - - - {!!buffer.data && ( - <> - - {buffer.owner || Unknown} - - - {buffer.type === 'ui' - ? 'Unique Identifiers' - : 'Structural Enzymes'} - {!!buffer.ue && ' and Unique Enzymes'} - - - - - - - - )} - - {!buffer.data && ( - - This buffer is empty. - - )} -
-
- ); -}; - -const DNAModifierMainBuffersDisk = (props) => { - const { act, data } = useBackend(); - const { hasDisk, disk } = data; - return ( -
- act('wipeDisk')} - > - Wipe - - - - } - > - {hasDisk ? ( - disk.data ? ( - - - {disk.label ? disk.label : 'No label'} - - - {disk.owner ? disk.owner : Unknown} - - - {disk.type === 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'} - {!!disk.ue && ' and Unique Enzymes'} - - - ) : ( - Disk is blank. - ) - ) : ( - - -
- No disk inserted. -
- )} -
- ); -}; - -const DNAModifierMainRejuvenators = (props) => { - const { act, data } = useBackend(); - const { isBeakerLoaded, beakerVolume, beakerLabel } = data; - return ( -
act('ejectBeaker')} - > - Eject - - } - > - {isBeakerLoaded ? ( - - - {rejuvenatorsDoses.map((a, i) => ( - - ))} - - - - {beakerLabel ? beakerLabel : 'No label'} - {beakerVolume ? ( - - {beakerVolume} unit{beakerVolume === 1 ? '' : 's'} remaining - - ) : ( - Empty - )} - - - ) : ( - - -
- No beaker loaded. -
- )} -
- ); -}; - -const DNAModifierIrradiating = (props) => { - return ( - - -
- -

- -  Irradiating occupant  - -

-
- -

- For {props.duration} second{props.duration === 1 ? '' : 's'} -

-
-
- ); -}; - -const DNAModifierBlocks = (props) => { - const { act, data } = useBackend(); - const { dnaString, selectedBlock, selectedSubblock, blockSize, action } = - props; - - const characters = dnaString.split(''); - let curBlock = 0; - let dnaBlocks = []; - for (let block = 0; block < characters.length; block += blockSize) { - const realBlock = block / blockSize + 1; - let subBlocks = []; - for (let subblock = 0; subblock < blockSize; subblock++) { - const realSubblock = subblock + 1; - subBlocks.push( - , - ); - } - dnaBlocks.push( - - - {realBlock} - - {subBlocks} - , - ); - } - return {dnaBlocks}; -}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierBlocks.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierBlocks.tsx new file mode 100644 index 00000000000..1d465d3e5a2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierBlocks.tsx @@ -0,0 +1,59 @@ +import { useBackend } from '../../backend'; +import { Box, Button, Flex } from '../../components'; + +export const DNAModifierBlocks = (props: { + dnaString: string; + selectedBlock: number; + selectedSubblock: number; + blockSize: number; + action: string; +}) => { + const { act } = useBackend(); + + const { dnaString, selectedBlock, selectedSubblock, blockSize, action } = + props; + + const characters: string[] = dnaString.split(''); + let dnaBlocks: React.JSX.Element[] = []; + for (let block = 0; block < characters.length; block += blockSize) { + const realBlock: number = block / blockSize + 1; + let subBlocks: React.JSX.Element[] = []; + for (let subblock = 0; subblock < blockSize; subblock++) { + const realSubblock: number = subblock + 1; + subBlocks.push( + , + ); + } + dnaBlocks.push( + + + {realBlock} + + {subBlocks} + , + ); + } + return {dnaBlocks}; +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierIrradiating.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierIrradiating.tsx new file mode 100644 index 00000000000..1b6c04d76eb --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierIrradiating.tsx @@ -0,0 +1,22 @@ +import { Box, Dimmer, Icon } from '../../components'; + +export const DNAModifierIrradiating = (props: { duration: number }) => { + return ( + + +
+ +

+ +  Irradiating occupant  + +

+
+ +

+ For {props.duration} second{props.duration === 1 ? '' : 's'} +

+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMain.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMain.tsx new file mode 100644 index 00000000000..aa08e16013f --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMain.tsx @@ -0,0 +1,265 @@ +import { BooleanLike } from 'common/react'; + +import { useBackend } from '../../backend'; +import { + Box, + Button, + Flex, + Icon, + Knob, + LabeledList, + Section, + Tabs, +} from '../../components'; +import { operations, rejuvenatorsDoses } from './constants'; +import { DNAModifierBlocks } from './DNAModifierBlocks'; +import { DNAModifierMainBuffers } from './DNAModifierMainBuffers'; +import { Data } from './types'; + +export const DNAModifierMain = (props: { isDNAInvalid: BooleanLike }) => { + const { act, data } = useBackend(); + + const { selectedMenuKey, hasOccupant } = data; + + if (!hasOccupant) { + return ( +
+ + + +
+ No occupant in DNA modifier. +
+
+
+ ); + } else if (props.isDNAInvalid) { + return ( +
+ + + +
+ No operation possible on this subject. +
+
+
+ ); + } + let body; + if (selectedMenuKey === 'ui') { + body = ( + <> + + + + ); + } else if (selectedMenuKey === 'se') { + body = ( + <> + + + + ); + } else if (selectedMenuKey === 'buffer') { + body = ; + } else if (selectedMenuKey === 'rejuvenators') { + body = ; + } + return ( +
+ + {operations.map((op, i) => ( + act('selectMenuKey', { key: op[0] })} + > + + {op[1]} + + ))} + + {body} +
+ ); +}; + +const DNAModifierMainUI = (props) => { + const { act, data } = useBackend(); + + const { + selectedUIBlock, + selectedUISubBlock, + selectedUITarget, + dnaBlockSize, + occupant, + } = data; + + return ( +
+ + + + value.toString(16).toUpperCase()} + ml="0" + onChange={(e, val) => act('changeUITarget', { value: val })} + /> + + + +
+ ); +}; + +const DNAModifierMainSE = (props) => { + const { act, data } = useBackend(); + + const { selectedSEBlock, selectedSESubBlock, dnaBlockSize, occupant } = data; + + return ( +
+ + +
+ ); +}; + +const DNAModifierMainRadiationEmitter = (props) => { + const { act, data } = useBackend(); + + const { radiationIntensity, radiationDuration } = data; + + return ( +
+ + + act('radiationIntensity', { value: val })} + /> + + + act('radiationDuration', { value: val })} + /> + + + +
+ ); +}; + +const DNAModifierMainRejuvenators = (props) => { + const { act, data } = useBackend(); + + const { isBeakerLoaded, beakerVolume, beakerLabel } = data; + + return ( +
act('ejectBeaker')} + > + Eject + + } + > + {isBeakerLoaded ? ( + + + {rejuvenatorsDoses.map((a, i) => ( + + ))} + + + + {beakerLabel ? beakerLabel : 'No label'} + {beakerVolume ? ( + + {beakerVolume} unit{beakerVolume === 1 ? '' : 's'} remaining + + ) : ( + Empty + )} + + + ) : ( + + +
+ No beaker loaded. +
+ )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMainBuffers.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMainBuffers.tsx new file mode 100644 index 00000000000..d3efe7678d7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMainBuffers.tsx @@ -0,0 +1,254 @@ +import { useBackend } from '../../backend'; +import { Box, Button, Icon, LabeledList, Section } from '../../components'; +import { buffData, Data } from './types'; + +export const DNAModifierMainBuffers = (props) => { + const { data } = useBackend(); + + const { buffers } = data; + + let bufferElements = buffers.map((buffer, i) => ( + + )); + return ( + <> +
{bufferElements}
+ + + ); +}; + +const DNAModifierMainBuffersElement = (props: { + id: number; + name: string; + buffer: buffData; +}) => { + const { act, data } = useBackend(); + const { id, name, buffer } = props; + const { isInjectorReady } = data; + const realName: string = name + (buffer.data ? ' - ' + buffer.label : ''); + return ( + +
+ + act('bufferOption', { + option: 'clear', + id: id, + }) + } + > + Clear + + + + + } + > + + + + + + + + {!!buffer.data && ( + <> + + {buffer.owner || Unknown} + + + {buffer.type === 'ui' + ? 'Unique Identifiers' + : 'Structural Enzymes'} + {!!buffer.ue && ' and Unique Enzymes'} + + + + + + + + )} + + {!buffer.data && ( + + This buffer is empty. + + )} +
+
+ ); +}; + +const DNAModifierMainBuffersDisk = (props) => { + const { act, data } = useBackend(); + const { hasDisk, disk } = data; + return ( +
+ act('wipeDisk')} + > + Wipe + + + + } + > + {hasDisk ? ( + disk.data ? ( + + + {disk.label ? disk.label : 'No label'} + + + {disk.owner ? disk.owner : Unknown} + + + {disk.type === 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'} + {!!disk.ue && ' and Unique Enzymes'} + + + ) : ( + Disk is blank. + ) + ) : ( + + +
+ No disk inserted. +
+ )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierOccupant.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierOccupant.tsx new file mode 100644 index 00000000000..b8fd28b5ce8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierOccupant.tsx @@ -0,0 +1,103 @@ +import { BooleanLike } from 'common/react'; + +import { useBackend } from '../../backend'; +import { + Box, + Button, + Icon, + LabeledList, + ProgressBar, + Section, +} from '../../components'; +import { stats } from './constants'; +import { Data } from './types'; + +export const DNAModifierOccupant = (props: { isDNAInvalid: BooleanLike }) => { + const { act, data } = useBackend(); + + const { locked, hasOccupant, occupant } = data; + + return ( +
+ + Door Lock: + + + + + } + > + {hasOccupant ? ( + <> + + + {occupant.name} + + + + + {stats[occupant.stat!][1]} + + + + + {props.isDNAInvalid ? ( + + +   The occupant's DNA structure is ruined beyond + recognition, please insert a subject with an intact DNA structure. + + ) : ( + + + + + + {data.occupant.uniqueEnzymes ? ( + data.occupant.uniqueEnzymes + ) : ( + + +   Unknown + + )} + + + )} + + ) : ( + Cell unoccupied. + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/constants.ts b/tgui/packages/tgui/interfaces/DNAModifier/constants.ts new file mode 100644 index 00000000000..d247c8a51ac --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/constants.ts @@ -0,0 +1,14 @@ +export const stats: string[][] = [ + ['good', 'Alive'], + ['average', 'Unconscious'], + ['bad', 'DEAD'], +]; + +export const operations: string[][] = [ + ['ui', 'Modify U.I.', 'dna'], + ['se', 'Modify S.E.', 'dna'], + ['buffer', 'Transfer Buffers', 'syringe'], + ['rejuvenators', 'Rejuvenators', 'flask'], +]; + +export const rejuvenatorsDoses: number[] = [5, 10, 20, 30, 50]; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/index.tsx b/tgui/packages/tgui/interfaces/DNAModifier/index.tsx new file mode 100644 index 00000000000..220f72de667 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/index.tsx @@ -0,0 +1,29 @@ +import { useBackend } from '../../backend'; +import { Window } from '../../layouts'; +import { ComplexModal } from '../common/ComplexModal'; +import { DNAModifierIrradiating } from './DNAModifierIrradiating'; +import { DNAModifierMain } from './DNAModifierMain'; +import { DNAModifierOccupant } from './DNAModifierOccupant'; +import { Data } from './types'; + +export const DNAModifier = (props) => { + const { data } = useBackend(); + + const { irradiating, occupant } = data; + + const isDNAInvalid: boolean = + !occupant.isViableSubject || + !occupant.uniqueIdentity || + !occupant.structuralEnzymes; + + return ( + + + {irradiating && } + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/types.ts b/tgui/packages/tgui/interfaces/DNAModifier/types.ts new file mode 100644 index 00000000000..73907fe9e70 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/types.ts @@ -0,0 +1,54 @@ +import { BooleanLike } from 'common/react'; + +export type Data = { + selectedMenuKey: string; + locked: BooleanLike; + hasOccupant: BooleanLike; + isInjectorReady: BooleanLike; + hasDisk: BooleanLike; + disk: buffData; + buffers: buffData[]; + radiationIntensity: number; + radiationDuration: number; + irradiating: number; + dnaBlockSize: number; + selectedUIBlock: number; + selectedUISubBlock: number; + selectedSEBlock: number; + selectedSESubBlock: number; + selectedUITarget: number; + selectedUITargetHex: string; + occupant: { + name: string | null; + stat: number | null; + isViableSubject: BooleanLike | null; + health: number | null; + maxHealth: number | null; + minHealth: number | null; + uniqueEnzymes: string | null; + uniqueIdentity: string | null; + structuralEnzymes: string | null; + radiationLevel: number | null; + }; + isBeakerLoaded: BooleanLike; + beakerLabel: string | null; + beakerVolume: number; + modal: modalData; +}; + +type modalData = { + id: string; + text: string; + args: { + id: string; + }; + modal_type: string; +}; + +export type buffData = { + data: number[] | null; + owner: string | null; + label: string | null; + type: string | null; + ue: BooleanLike; +}; diff --git a/tgui/packages/tgui/interfaces/DestinationTagger.jsx b/tgui/packages/tgui/interfaces/DestinationTagger.jsx deleted file mode 100644 index ee46aa1fba8..00000000000 --- a/tgui/packages/tgui/interfaces/DestinationTagger.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useBackend } from '../backend'; -import { Button, Flex, Section } from '../components'; -import { Window } from '../layouts'; - -export const DestinationTagger = (props) => { - const { act, data } = useBackend(); - - const { currTag, taggerLocs } = data; - - return ( - - -
- - {taggerLocs.sort().map((tag) => ( - - - - ))} - -
-
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/DestinationTagger.tsx b/tgui/packages/tgui/interfaces/DestinationTagger.tsx new file mode 100644 index 00000000000..a55eef5fc48 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DestinationTagger.tsx @@ -0,0 +1,50 @@ +import { useBackend } from '../backend'; +import { Button, Flex, Section } from '../components'; +import { Window } from '../layouts'; + +type Data = { + currTag: string; + taggerLevels: { z: number; location: string }[]; + taggerLocs: { tag: string; level: number }[]; +}; + +export const DestinationTagger = (props) => { + const { act, data } = useBackend(); + + const { currTag, taggerLevels = [], taggerLocs } = data; + + const unique_levels = taggerLevels.filter((obj, index) => { + return index === taggerLevels.findIndex((o) => obj.location === o.location); + }); + + return ( + + +
+ {unique_levels.map((level) => ( +
+ + {taggerLocs.map( + (tag) => + level.z === tag.level && ( + + + + ), + )} + +
+ ))} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DiseaseSplicer.jsx b/tgui/packages/tgui/interfaces/DiseaseSplicer.tsx similarity index 76% rename from tgui/packages/tgui/interfaces/DiseaseSplicer.jsx rename to tgui/packages/tgui/interfaces/DiseaseSplicer.tsx index 4db3567cffe..f962aa1628e 100644 --- a/tgui/packages/tgui/interfaces/DiseaseSplicer.jsx +++ b/tgui/packages/tgui/interfaces/DiseaseSplicer.tsx @@ -1,9 +1,24 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Button, LabeledList, ProgressBar, Section } from '../components'; import { Window } from '../layouts'; +type Data = { + dish_inserted: BooleanLike; + buffer: { name: string; stage: number } | null; + species_buffer: string | null; + busy: string | null; + growth: number; + effects: + | { name: string; stage: number; reference: string; badness: number }[] + | null; + info: string; + affected_species: string[] | null; +}; + export const DiseaseSplicer = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { busy } = data; @@ -28,7 +43,7 @@ export const DiseaseSplicer = (props) => { }; const DiseaseSplicerVirusDish = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); const { dish_inserted, effects, info, growth, affected_species } = data; @@ -60,12 +75,12 @@ const DiseaseSplicerVirusDish = (props) => { {info ? ( -
+
{info}
) : ( <> -
+
{(effects && effects.map((effect) => ( @@ -74,23 +89,25 @@ const DiseaseSplicerVirusDish = (props) => { ))) || No virus sample loaded.}
-
- {!affected_species || !affected_species.length ? 'None' : null} - {affected_species.sort().join(', ')} +
+ {!affected_species || !affected_species.length + ? 'None' + : affected_species.sort().join(', ')}
-
+
CAUTION: Reverse engineering will destroy the viral sample. - {effects.map((e) => ( - - ))} + {effects && + effects.map((e) => ( + + ))} @@ -102,18 +119,9 @@ const DiseaseSplicerVirusDish = (props) => { }; const DiseaseSplicerStorage = (props) => { - const { act, data } = useBackend(); + const { act, data } = useBackend(); - const { - dish_inserted, - buffer, - species_buffer, - effects, - info, - growth, - affected_species, - busy, - } = data; + const { buffer, species_buffer, info } = data; return (
@@ -173,7 +181,7 @@ const DiseaseSplicerStorage = (props) => {
act('set_dcall_area', { area: val })} diff --git a/tgui/packages/tgui/interfaces/EmbeddedController.jsx b/tgui/packages/tgui/interfaces/EmbeddedController.jsx deleted file mode 100644 index 8c52e3514e6..00000000000 --- a/tgui/packages/tgui/interfaces/EmbeddedController.jsx +++ /dev/null @@ -1,757 +0,0 @@ -import { useBackend } from '../backend'; -import { - Box, - Button, - Flex, - Icon, - LabeledList, - ProgressBar, - Section, -} from '../components'; -import { Window } from '../layouts'; -import { createLogger } from '../logging'; -const logger = createLogger('fuck'); - -// This UI uses an internal routing system for the many different variants of -// embedded controllers in use. -let primaryRoutes = {}; - -/** - * This is an all-in-one replacement for the following NanoUI Templates: - * - advanced_airlock_console.tmpl - * - docking_airlock_console.tmpl - * - door_access_console.tmpl - * - escape_pod_console.tmpl - * - escape_pod_berth_console.tmpl - * - multi_docking_console.tmpl - * - phoron_airlock_console.tmpl - * - simple_airlock_console.tmpl - * - simple_docking_console.tmpl - * - simple_docking_console_pod.tmpl -- Funny enough, wasn't used anywhere. - */ - -/** - * Let's cover all of the attributes of `data` for this UI right here. - * For those unfamiliar with JSDoc syntax, [param] indicates - * an optional parameter. - */ - -/** - * Interior/Exterior Door Status - * @typedef {Object} doorStatus - * @property {('open'|'closed')} state - * @property {('locked'|'unlocked')} lock - */ - -/** - * Dock Status - * @typedef {('undocked'|'undocking'|'docking'|'docked')} dockStatus - */ - -/** - * All possible data attributes. - * @typedef {Object} Data - * @property {string} internalTemplateName -