From dcd518d1a8a7e653ad6525db9b48bd99f4cc79b7 Mon Sep 17 00:00:00 2001
From: Drathek <76988376+Drulikar@users.noreply.github.com>
Date: Tue, 5 Jan 2021 13:23:49 -0800
Subject: [PATCH] Borg belly tg UI (#125)
* Added TGUI for Dogborg sleepers again
Added resist chance and help intent struggle messages (to suggest maybe a different intent is required for escape)
Doubled dogborg sleepers cleaning cycle time
Tweaked dogborg sleepers messages and sounds
* Rebuild of TGUI (Discard if this conflicts and just rebuild TGUI)
* Added game_options config ENABLE_DOGBORG_SLEEPERS to determine whether recreational sleepers or normal sleepers should be added to dogborgs
* Moved ENABLE_DOGBORG_SLEEPERS flag to sandstorm config
---
.../configuration/entries/game_options.dm | 2 +
.../objects/items/devices/dogborg_sleeper.dm | 231 ++++++++++++------
.../mob/living/silicon/robot/robot_modules.dm | 33 ++-
config/sandstorm/config.txt | 4 +
.../tgui/interfaces/DogborgSleeper.js | 138 +++++++++++
tgui/public/tgui-common.chunk.js | 2 +-
tgui/public/tgui-panel.bundle.js | 2 +-
tgui/public/tgui.bundle.js | 2 +-
8 files changed, 322 insertions(+), 92 deletions(-)
create mode 100644 tgui/packages/tgui/interfaces/DogborgSleeper.js
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 67e4a97894..efc3ee8b15 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -81,6 +81,8 @@
/datum/config_entry/flag/disable_peaceborg
+/datum/config_entry/flag/enable_dogborg_sleepers // enable normal dogborg sleepers (otherwise recreational)
+
/datum/config_entry/flag/economy //money money money money money money money money money money money money
/datum/config_entry/number/minimum_secborg_alert //Minimum alert level for secborgs to be chosen.
diff --git a/code/game/objects/items/devices/dogborg_sleeper.dm b/code/game/objects/items/devices/dogborg_sleeper.dm
index 1f91196b24..1eb238090c 100644
--- a/code/game/objects/items/devices/dogborg_sleeper.dm
+++ b/code/game/objects/items/devices/dogborg_sleeper.dm
@@ -1,16 +1,18 @@
// Dogborg Sleeper units
/obj/item/dogborg/sleeper
- name = "hound sleeper"
- desc = "nothing should see this."
+ name = "Hound Sleeper"
+ desc = "Equipment for a Medihound unit. A mounted, underslung sleeper, with a medical scanner and chemical injectors."
icon = 'icons/mob/robot_items.dmi'
icon_state = "sleeper"
w_class = WEIGHT_CLASS_TINY
var/mob/living/carbon/patient
var/inject_amount = 10
+ var/inject_cost = 750
var/min_health = -100
var/cleaning = FALSE
- var/cleaning_cycles = 10
+ var/medical_scanner = TRUE
+ var/cleaning_cycles = 20
var/patient_laststat = null
var/list/injection_chems = list(/datum/reagent/medicine/antitoxin, /datum/reagent/medicine/epinephrine,
/datum/reagent/medicine/salbutamol, /datum/reagent/medicine/bicaridine, /datum/reagent/medicine/kelotane)
@@ -18,6 +20,8 @@
var/escape_in_progress = FALSE
var/message_cooldown
var/breakout_time = 150
+ var/escape_chance = 35
+ var/escape_pending = FALSE
var/tmp/last_hearcheck = 0
var/tmp/list/hearing_mobs
var/list/items_preserved = list()
@@ -75,59 +79,82 @@
return
if(!proximity)
return
- if(!iscarbon(target))
+ if(!ishuman(target))
return
+ if(target.buckled)
+ to_chat(user, "The user is buckled and can not be put into your [src.name].")
+ return
+ if(target.anchored)
+ to_chat(user, "The user is anchored and can not be put into your [src.name].")
+ return
+ if(patient)
+ to_chat(user, "Your [src.name] is already occupied.")
+ return
+
+ if (!CHECK_BITFIELD(target.vore_flags,DEVOURABLE))
+ to_chat(user, "The target registers an error code. Unable to insert into [src.name].")
+ return
+
var/voracious = TRUE
if(!target.client || !(target.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
voracious = FALSE
- if(target.buckled)
- to_chat(user, "The user is buckled and can not be put into your [src].")
- return
- if(patient)
- to_chat(user, "Your [src] is already occupied.")
- return
- user.visible_message("[hound.name] is carefully inserting [target.name] into their [src].", "You start placing [target] into your [src]...")
- if(!patient && iscarbon(target) && !target.buckled && do_after (user, 100, target = target))
-
+
+ user.visible_message("[hound.name] is carefully inserting [target.name] into their [src.name].", "You start placing [target] into your [src.name]...")
+ if(do_after (user, 100, target = target) && !target.buckled && !target.anchored && !patient)
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
return //If they moved away, you can't eat them.
- if(patient)
- return //If you try to eat two people at once, you can only eat one.
+ //if(target.buckled)
+ //to_chat(user, "The user is buckled and can not be put into your [src.name].")
+ //return
+ //if(patient)
+ //to_chat(user, "Your [src.name] is already occupied.")
+ //return
- else //If you don't have someone in you, proceed.
- if(!isjellyperson(target) && ("toxin" in injection_chems))
- injection_chems -= "toxin"
- injection_chems += "antitoxin"
- if(isjellyperson(target) && !("toxin" in injection_chems))
- injection_chems -= "antitoxin"
- injection_chems += "toxin"
- target.forceMove(src)
- target.reset_perspective(src)
- target.ExtinguishMob() //The tongue already puts out fire stacks but being put into the sleeper shouldn't allow you to keep burning.
- update_gut(hound)
- user.visible_message("[voracious ? "[hound]'s [src.name] lights up and expands as [target] slips inside into their [src.name]." : "[hound]'s sleeper indicator lights up as [target] is scooped up into [hound.p_their()] [src]."]", \
- "Your [voracious ? "[src.name] lights up as [target] slips into" : "sleeper indicator light shines brightly as [target] is scooped inside"] your [src]. Life support functions engaged.")
- message_admins("[key_name(hound)] has sleeper'd [key_name(patient)] as a dogborg. [ADMIN_JMP(src)]")
- playsound(hound, 'sound/effects/bin_close.ogg', 100, 1)
+ //If you don't have someone in you, proceed.
+ if(!isjellyperson(target) && ("toxin" in injection_chems))
+ injection_chems -= "toxin"
+ injection_chems += "antitoxin"
+ if(isjellyperson(target) && !("toxin" in injection_chems))
+ injection_chems -= "antitoxin"
+ injection_chems += "toxin"
+ target.forceMove(src)
+ target.reset_perspective(src)
+ target.ExtinguishMob() //The tongue already puts out fire stacks but being put into the sleeper shouldn't allow you to keep burning.
+ update_gut(hound)
+ user.visible_message("[voracious ? "[hound]'s [src.name] lights up and expands as [target] slips inside into their [src.name]." : "[hound]'s sleeper indicator lights up as [target] is scooped up into [hound.p_their()] [src.name]."]", \
+ "Your [voracious ? "[src.name] lights up as [target] slips into" : "sleeper indicator light shines brightly as [target] is scooped inside"] your [src.name]. Life support functions engaged.")
+ message_admins("[key_name(hound)] has sleeper'd [key_name(patient)] as a dogborg. [ADMIN_JMP(src)]")
+ playsound(hound, voracious ? 'sound/vore/prey/insertion_01.ogg' : 'sound/effects/bin_close.ogg', 100, 1)
/obj/item/dogborg/sleeper/container_resist(mob/living/user)
var/mob/living/silicon/robot/hound = get_host()
if(!hound)
go_out(user)
return
+
if(user.a_intent == INTENT_HELP)
+ user.visible_message("[user] gently rubs the flexible confines.", \
+ "You gently rub [hound.name]'s flexible confines.")
return
+
var/voracious = TRUE
if(!user.client || !(user.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
voracious = FALSE
- user.visible_message("You see [voracious ? "[user] struggling against the expanded material of [hound]'s gut!" : "and hear [user] pounding against something inside of [hound]'s [src.name]!"]", \
- "[voracious ? "You start struggling inside of [src]'s tight, flexible confines," : "You start pounding against the metallic walls of [src],"] trying to trigger the release... (this will take about [DisplayTimeText(breakout_time)].)", \
- "You hear a [voracious ? "couple of thumps" : "loud banging noise"] coming from within [hound].")
- if(do_after(user, breakout_time, target = src))
- user.visible_message("[user] successfully broke out of [hound.name]!", \
- "You successfully break out of [hound.name]!")
- go_out(user, hound)
+
+ if(prob(escape_chance) && !escape_pending)
+ user.visible_message("You see [voracious ? "[user] struggling against the expanded material of [hound]'s gut!" : "and hear [user] pounding against something inside of [hound]'s [src.name]!"]", \
+ "[voracious ? "You start struggling inside of [src.name]'s tight, flexible confines," : "You start pounding against the metallic walls of [src.name],"] managing to trigger a hidden emergency release... (this will take about [DisplayTimeText(breakout_time)].)", \
+ "You hear a [voracious ? "couple of thumps" : "loud banging noise"] coming from within [hound].")
+ escape_pending = TRUE
+ if(do_after(user, breakout_time, target = src))
+ user.visible_message("[user] successfully broke out of [hound.name]!", \
+ "You successfully break out of [hound.name]!")
+ go_out(user, hound)
+ else
+ user.visible_message("You see [voracious ? "[user] struggling against the expanded material of [hound]'s gut!" : "and hear [user] pounding against something inside of [hound]'s [src.name]!"]", \
+ "[voracious ? "You start struggling inside of [src.name]'s tight, flexible confines," : "You start pounding against the metallic walls of [src.name],"] trying to find the trigger the release...", \
+ "You hear a [voracious ? "couple of thumps" : "loud banging noise"] coming from within [hound].")
/obj/item/dogborg/sleeper/proc/go_out(atom/movable/target, mob/living/silicon/robot/hound)
var/voracious = hound ? TRUE : FALSE
@@ -141,7 +168,7 @@
voracious = FALSE
if(length(targets))
if(hound)
- hound.visible_message("[voracious ? "[hound] empties out [hound.p_their()] contents via [hound.p_their()] release port." : "[hound]'s underside slides open with an audible clunk before [hound.p_their()] [src] flips over, carelessly dumping its contents onto the ground below [hound.p_them()] before closing right back up again."]", \
+ hound.visible_message("[voracious ? "[hound] empties out [hound.p_their()] contents via [hound.p_their()] release port." : "[hound]'s underside slides open with an audible clunk before [hound.p_their()] [src.name] flips over, carelessly dumping its contents onto the ground below [hound.p_them()] before closing right back up again."]", \
"[voracious ? "You empty your contents via your release port." : "You open your sleeper hatch, quickly releasing all of the contents within before closing it again."]")
for(var/a in contents)
var/atom/movable/AM = a
@@ -151,12 +178,13 @@
M.reset_perspective()
playsound(loc, voracious ? 'sound/effects/splat.ogg' : 'sound/effects/bin_close.ogg', 50, 1)
items_preserved.Cut()
+ cleaning_cycles = initial(cleaning_cycles)
cleaning = FALSE
patient = null
+ escape_pending = FALSE
if(hound)
update_gut(hound)
-
/obj/item/dogborg/sleeper/attack_self(mob/user)
. = ..()
if(. || !iscyborg(user))
@@ -169,21 +197,21 @@
/obj/item/dogborg/sleeper/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, "dogborg_sleeper", name, 375, 550) //UI DOES NOT EXIST
+ ui = new(user, src, "DogborgSleeper", name)
ui.open()
/obj/item/dogborg/sleeper/ui_data()
var/list/data = list()
data["occupied"] = patient ? 1 : 0
- if(cleaning && length(contents - items_preserved))
+ if(cleaning)
data["items"] = "Self-cleaning mode active: [length(contents - items_preserved)] object(s) remaining."
data["cleaning"] = cleaning
- if(injection_chems != null)
- data["chem"] = list()
- for(var/chem in injection_chems)
- var/datum/reagent/R = GLOB.chemical_reagents_list[chem]
- data["chem"] += list(list("name" = R.name, "id" = R.type))
+ data["medical_scanner"] = medical_scanner
+ data["chems"] = list()
+ for(var/chem in injection_chems)
+ var/datum/reagent/R = GLOB.chemical_reagents_list[chem]
+ data["chems"] += list(list("name" = R.name, "id" = R.type))
data["occupant"] = list()
var/mob/living/mob_occupant = patient
@@ -211,8 +239,9 @@
data["occupant"]["fireLoss"] = mob_occupant.getFireLoss()
data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss()
data["occupant"]["brainLoss"] = mob_occupant.getOrganLoss(ORGAN_SLOT_BRAIN)
+ data["occupant"]["is_robotic_organism"] = HAS_TRAIT(mob_occupant, TRAIT_ROBOTIC_ORGANISM)
data["occupant"]["reagents"] = list()
- if(mob_occupant.reagents.reagent_list.len)
+ if(mob_occupant.reagents && mob_occupant.reagents.reagent_list.len)
for(var/datum/reagent/R in mob_occupant.reagents.reagent_list)
data["occupant"]["reagents"] += list(list("name" = R.name, "volume" = R.volume))
return data
@@ -233,12 +262,14 @@
inject_chem(chem, usr)
. = TRUE
if("cleaning")
- if(!contents)
- to_chat(src, "Your [src] is already cleaned.")
+ if(!contents || length(contents) == 0)
+ to_chat(usr, "Your [src.name] is already clean.")
return
if(patient)
- to_chat(patient, "[usr.name]'s [src] fills with caustic enzymes around you!")
- to_chat(src, "Cleaning process enabled.")
+ to_chat(patient, "[usr.name]'s [src.name] fills with caustic enzymes around you!")
+ to_chat(usr, "Cleaning process enabled.")
+ playsound(loc, 'sound/machines/click.ogg', 50, 1)
+ cleaning = TRUE
clean_cycle(usr)
. = TRUE
@@ -270,7 +301,7 @@
if(prociconupdate)
hound.update_icons()
//Return original patient
- return(patient)
+ return
//Check for a new patient
else
for(var/mob/living/carbon/human/C in contents)
@@ -288,17 +319,17 @@
hound.sleeper_nv = TRUE
else
hound.sleeper_nv = FALSE
-
+
//Update icon and return new patient
hound.update_icons()
return
//Cleaning looks better with red on, even with nobody in it
- if(cleaning && !patient)
+ if(cleaning)
hound.sleeper_r = 1
hound.sleeper_g = 0
//Couldn't find anyone, and not cleaning
- else if(!cleaning && !patient)
+ else
hound.sleeper_r = 0
hound.sleeper_g = 0
@@ -321,14 +352,13 @@
var/sound/pred_death = sound(get_sfx("death_pred"))
if(cleaning_cycles)
cleaning_cycles--
- cleaning = TRUE
for(var/mob/living/carbon/C in (touchable_items))
if((C.status_flags & GODMODE) || !CHECK_BITFIELD(C.vore_flags, DIGESTABLE))
items_preserved += C
else
C.adjustBruteLoss(2)
C.adjustFireLoss(3)
- if(contents)
+ if(contents && length(touchable_items) > 0)
var/atom/target = pick(touchable_items)
if(iscarbon(target)) //Handle the target being a mob
var/mob/living/carbon/T = target
@@ -360,7 +390,7 @@
if(!T.dropItemToGround(W))
qdel(W)
qdel(T)
- //Handle the target being anything but a mob
+ //Handle the target being anything but a mob
else if(isobj(target))
var/obj/T = target
if(T.type in important_items) //If the object is in the items_preserved global list
@@ -368,18 +398,24 @@
//If the object is not one to preserve
else
qdel(T)
- update_gut()
hound.cell.give(10)
- else
+ else if(contents && length(contents) > 0)
+ // Cycles complete and theres still stuff
cleaning_cycles = initial(cleaning_cycles)
cleaning = FALSE
- to_chat(hound, "Your [src] chimes it ends its self-cleaning cycle.")//Belly is entirely empty
-
- if(!length(contents))
- to_chat(hound, "Your [src] is now clean. Ending self-cleaning cycle.")
+ escape_pending = FALSE
+ to_chat(hound, "Your [src.name] clicks as its self-cleaning cycle ends. NOTE: Foreign objects are still detected. Resume self-cleaning?")
+ playsound(loc, 'sound/machines/click.ogg', 50, 1)
+
+ if(!contents || length(contents) == 0)
+ //Belly is entirely empty
+ to_chat(hound, "Your [src.name] chimes as it completes its self-cleaning cycle.")
+ playsound(loc, 'sound/machines/ding.ogg', 50, 1)
+ cleaning_cycles = initial(cleaning_cycles)
cleaning = FALSE
+ escape_pending = FALSE
-//sound effects
+ //sound effects
if(prob(50))
if((world.time - NORMIE_HEARCHECK) > last_hearcheck)
var/turf/source = get_turf(hound)
@@ -404,18 +440,25 @@
return is_type_in_typecache(I, important_items)
/obj/item/dogborg/sleeper/proc/inject_chem(chem, mob/living/silicon/robot/hound)
- if(!hound)
+ if(!hound || !patient || !patient.reagents)
return
- if(hound.cell.charge <= 800) //This is so borgs don't kill themselves with it. Remember, 750 charge used every injection.
+ if(hound.cell.charge <= inject_cost + 50) //This is so borgs don't kill themselves with it. Remember, 750 charge used every injection.
to_chat(hound, "You don't have enough power to synthesize fluids.")
return
- if(patient.reagents.get_reagent_amount(chem) + 10 >= 20) //Preventing people from accidentally killing themselves by trying to inject too many chemicals!
+ if(!chem_allowed(chem)) //Preventing people from accidentally killing themselves by trying to inject too many chemicals!
to_chat(hound, "Your stomach is currently too full of fluids to secrete more fluids of this kind.")
return
- patient.reagents.add_reagent(chem, 10)
- hound.cell.use(750) //-750 charge per injection
- var/units = round(patient.reagents.get_reagent_amount(chem))
- to_chat(hound, "Injecting [units] unit\s of [chem] into occupant.") //If they were immersed, the reagents wouldn't leave with them.
+ patient.reagents.add_reagent(chem, inject_amount)
+ hound.cell.use(inject_cost) //-750 charge per injection
+ //var/units = round(patient.reagents.get_reagent_amount(chem))
+ to_chat(hound, "Injecting [inject_amount] unit\s of [chem] into occupant.") //If they were immersed, the reagents wouldn't leave with them.
+
+/obj/item/dogborg/sleeper/proc/chem_allowed(chem)
+ if(!patient || !patient.reagents)
+ return
+ var/amount = patient.reagents.get_reagent_amount(chem) + inject_amount <= 20
+ var/occ_health = patient.health > min_health || chem == /datum/reagent/medicine/epinephrine
+ return amount && occ_health
/obj/item/dogborg/sleeper/K9 //The K9 portabrig
name = "Mobile Brig"
@@ -423,30 +466,56 @@
icon_state = "sleeperb"
inject_amount = 0
min_health = -100
- injection_chems = null //So they don't have all the same chems as the medihound!
+ injection_chems = list() //So they don't have all the same chems as the medihound!
breakout_time = 300
+ escape_chance = 15
+ medical_scanner = FALSE
/obj/item/dogborg/sleeper/K9/afterattack(mob/living/carbon/target, mob/living/silicon/user, proximity)
var/mob/living/silicon/robot/hound = get_host()
- if(!hound || !istype(target) || !proximity || target.anchored)
+ if(!hound)
return
- if (!CHECK_BITFIELD(target.vore_flags,DEVOURABLE))
- to_chat(user, "The target registers an error code. Unable to insert into [src].")
+ if(!proximity)
return
- if(patient)
- to_chat(user,"Your [src] is already occupied.")
+ if(!ishuman(target))
return
if(target.buckled)
- to_chat(user,"[target] is buckled and can not be put into your [src].")
+ to_chat(user, "The user is buckled and can not be put into your [src.name].")
return
- user.visible_message("[hound.name] is ingesting [target] into their [src].", "You start ingesting [target] into your [src.name]...")
+ if(target.anchored)
+ to_chat(user, "The user is anchored and can not be put into your [src.name].")
+ return
+ if(patient)
+ to_chat(user, "Your [src.name] is already occupied.")
+ return
+
+ if(!CHECK_BITFIELD(target.vore_flags,DEVOURABLE))
+ to_chat(user, "The target registers an error code. Unable to insert into [src.name].")
+ return
+
+ var/voracious = TRUE
+ if(!target.client || !(target.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
+ voracious = FALSE
+
+ user.visible_message("[hound.name] is ingesting [target] into their [src.name].", "You start ingesting [target] into your [src.name]...")
if(do_after(user, 30, target = target) && !patient && !target.buckled)
+
+ //if(patient)
+ //to_chat(user,"Your [src.name] is already occupied.")
+ //return
+ //if(target.buckled)
+ //to_chat(user,"[target] is buckled and can not be put into your [src.name].")
+ //return
+
target.forceMove(src)
target.reset_perspective(src)
update_gut(hound)
user.visible_message("[hound.name]'s mobile brig clunks in series as [target] slips inside.", "Your mobile brig groans lightly as [target] slips inside.")
- playsound(hound, 'sound/effects/bin_close.ogg', 80, 1) // Really don't need ERP sound effects for robots
+ playsound(hound, voracious ? 'sound/vore/prey/insertion_01.ogg' : 'sound/effects/bin_close.ogg', 80, 1)
/obj/item/dogborg/sleeper/K9/flavour
name = "Recreational Sleeper"
desc = "A mounted, underslung sleeper, intended for holding willing occupants for leisurely purposes."
+ inject_amount = 0
+ injection_chems = list() //So they don't have all the same chems as the medihound!
+ medical_scanner = FALSE
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 101334cd7f..f6c908c446 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -143,25 +143,42 @@
return I
*/ //replaced by the one in sandcode
-//Adds flavoursome dogborg items to dogborg variants without mechanical benefits
+//Adds flavoursome dogborg items to dogborg variants optionally without mechanical benefits
/obj/item/robot_module/proc/dogborg_equip()
has_snowflake_deadsprite = TRUE
cyborg_pixel_offset = -16
hat_offset = INFINITY
basic_modules += new /obj/item/dogborg_nose(src)
basic_modules += new /obj/item/dogborg_tongue(src)
+
var/obj/item/dogborg/sleeper/K9/flavour/I = new(src)
+
+ var/mechanics = CONFIG_GET(flag/enable_dogborg_sleepers)
+ if (mechanics)
+ // Normal sleepers
+ if(istype(src, /obj/item/robot_module/security))
+ basic_modules += new /obj/item/dogborg/sleeper/K9(src)
+ if(istype(src, /obj/item/robot_module/medical))
+ basic_modules += new /obj/item/dogborg/sleeper(src)
+ else
+ // Recreational sleepers
+ if(istype(src, /obj/item/robot_module/security))
+ I.icon_state = "sleeperb"
+ basic_modules += I
+ if(istype(src, /obj/item/robot_module/medical))
+ I.icon_state = "sleeper"
+ basic_modules += I
+
+ // Unimplemented sleepers
if(istype(src, /obj/item/robot_module/engineering))
I.icon_state = "decompiler"
- if(istype(src, /obj/item/robot_module/security))
- I.icon_state = "sleeperb"
- if(istype(src, /obj/item/robot_module/medical))
- I.icon_state = "sleeper"
+ basic_modules += I
if(istype(src, /obj/item/robot_module/butler))
I.icon_state = "servicer"
if(cyborg_base_icon == "scrubpup")
I.icon_state = "compactor"
- basic_modules += I
+ basic_modules += I
+
rebuild_modules()
/obj/item/robot_module/proc/remove_module(obj/item/I, delete_after)
@@ -715,7 +732,7 @@
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
has_snowflake_deadsprite = TRUE
if("Drake")
- cyborg_base_icon = "drakepeace"
+ cyborg_base_icon = "drakepeace"
sleeper_overlay = "drakepeacesleeper"
cyborg_icon_override = 'sandcode/icons/mob/cyborg/drakemech.dmi'
dogborg = TRUE
@@ -858,7 +875,7 @@
"(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"),
"(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"),
"(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"),
- "(Janitor) Drake" = image(icon = 'sandcode/icons/mob/cyborg/drakemech.dmi', icon_state = "drakejanitbox")
+ "(Janitor) Drake" = image(icon = 'sandcode/icons/mob/cyborg/drakemech.dmi', icon_state = "drakejanitbox")
)
var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark",
"(Janitor) Scrubpuppy" = "scrubpup")
diff --git a/config/sandstorm/config.txt b/config/sandstorm/config.txt
index 401708103b..661fd0d9e9 100644
--- a/config/sandstorm/config.txt
+++ b/config/sandstorm/config.txt
@@ -7,3 +7,7 @@
## Roundstart ping
## Won't do anything if unset
#NEW_ROUND_PING
+
+## Dogborg Sleeper Mechanics ###
+## Uncomment to enable regular dogborg sleepers (otherwise will use recreational)
+#ENABLE_DOGBORG_SLEEPERS
diff --git a/tgui/packages/tgui/interfaces/DogborgSleeper.js b/tgui/packages/tgui/interfaces/DogborgSleeper.js
new file mode 100644
index 0000000000..ddad8fe0ef
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/DogborgSleeper.js
@@ -0,0 +1,138 @@
+import { useBackend } from '../backend';
+import { Box, Section, LabeledList, Button, ProgressBar, NoticeBox } from '../components';
+import { Fragment } from 'inferno';
+import { Window } from '../layouts';
+
+const damageTypes = [
+ {
+ label: 'Brute',
+ type: 'bruteLoss',
+ },
+ {
+ label: 'Burn',
+ type: 'fireLoss',
+ },
+ {
+ label: 'Toxin',
+ type: 'toxLoss',
+ },
+ {
+ label: 'Oxygen',
+ type: 'oxyLoss',
+ },
+];
+
+export const DogborgSleeper = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ occupant = {},
+ occupied,
+ cleaning,
+ items,
+ medical_scanner,
+ } = data;
+ const preSortChems = data.chems || [];
+ const chems = preSortChems.sort((a, b) => {
+ const descA = a.name.toLowerCase();
+ const descB = b.name.toLowerCase();
+ if (descA < descB) {
+ return -1;
+ }
+ if (descA > descB) {
+ return 1;
+ }
+ return 0;
+ });
+ return (
+ "+e+"
/gi,"\n").replace(/<\/?[a-z0-9-_]+[^>]*>/gi,"").replace(/&(nbsp|amp|quot|lt|gt|apos);/g,(function(e,n){return t[n]})).replace(/?([0-9]+);/gi,(function(e,t){var n=parseInt(t,10);return String.fromCharCode(n)})).replace(/?([0-9a-f]+);/gi,(function(e,t){var n=parseInt(t,16);return String.fromCharCode(n)}))};t.buildQueryString=function(e){return Object.keys(e).map((function(t){return encodeURIComponent(t)+"="+encodeURIComponent(e[t])})).join("&")}},function(e,t,n){"use strict";var r={}.hasOwnProperty;e.exports=function(e,t){return r.call(e,t)}},function(e,t,n){"use strict";var r=n(53),o=n(66),i=n(18),a=n(12),c=n(72),u=[].push,s=function(e){var t=1==e,n=2==e,s=3==e,l=4==e,f=6==e,d=5==e||f;return function(p,h,g,v){for(var m,y,b=i(p),x=o(b),w=r(h,g,3),_=a(x.length),E=0,k=v||c,S=t?k(p,_):n?k(p,0):undefined;_>E;E++)if((d||E in x)&&(y=w(m=x[E],E,b),e))if(t)S[E]=y;else if(y)switch(e){case 3:return!0;case 5:return m;case 6:return E;case 2:u.call(S,m)}else if(l)return!1;return f?-1:s||l?l:S}};e.exports={forEach:s(0),map:s(1),filter:s(2),some:s(3),every:s(4),find:s(5),findIndex:s(6)}},function(e,t,n){"use strict";t.__esModule=!0,t.useSelector=t.useDispatch=t.createAction=t.combineReducers=t.applyMiddleware=t.createStore=void 0;var r=n(30);function o(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0;return function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nu||n!=n?l*Infinity:l*n}},function(e,t,n){"use strict";var r=n(4),o=Math.hypot,i=Math.abs,a=Math.sqrt;r({target:"Math",stat:!0,forced:!!o&&o(Infinity,NaN)!==Infinity},{hypot:function(e,t){for(var n,r,o=0,c=0,u=arguments.length,s=0;c0?(r=n/s)*r:n;return s===Infinity?Infinity:s*a(o)}})},function(e,t,n){"use strict";var r=n(4),o=n(5),i=Math.imul;r({target:"Math",stat:!0,forced:o((function(){return-5!=i(4294967295,5)||2!=i.length}))},{imul:function(e,t){var n=+e,r=+t,o=65535&n,i=65535&r;return 0|o*i+((65535&n>>>16)*i+o*(65535&r>>>16)<<16>>>0)}})},function(e,t,n){"use strict";var r=n(4),o=Math.log,i=Math.LOG10E;r({target:"Math",stat:!0},{log10:function(e){return o(e)*i}})},function(e,t,n){"use strict";n(4)({target:"Math",stat:!0},{log1p:n(171)})},function(e,t,n){"use strict";var r=n(4),o=Math.log,i=Math.LN2;r({target:"Math",stat:!0},{log2:function(e){return o(e)/i}})},function(e,t,n){"use strict";n(4)({target:"Math",stat:!0},{sign:n(126)})},function(e,t,n){"use strict";var r=n(4),o=n(5),i=n(90),a=Math.abs,c=Math.exp,u=Math.E;r({target:"Math",stat:!0,forced:o((function(){return-2e-17!=Math.sinh(-2e-17)}))},{sinh:function(e){return a(e=+e)<1?(i(e)-i(-e))/2:(c(e-1)-c(-e-1))*(u/2)}})},function(e,t,n){"use strict";var r=n(4),o=n(90),i=Math.exp;r({target:"Math",stat:!0},{tanh:function(e){var t=o(e=+e),n=o(-e);return t==Infinity?1:n==Infinity?-1:(t-n)/(i(e)+i(-e))}})},function(e,t,n){"use strict";n(49)(Math,"Math",!0)},function(e,t,n){"use strict";var r=n(4),o=Math.ceil,i=Math.floor;r({target:"Math",stat:!0},{trunc:function(e){return(e>0?i:o)(e)}})},function(e,t,n){"use strict";var r=n(10),o=n(7),i=n(70),a=n(25),c=n(20),u=n(37),s=n(89),l=n(40),f=n(5),d=n(48),p=n(52).f,h=n(23).f,g=n(16).f,v=n(62).trim,m=o.Number,y=m.prototype,b="Number"==u(d(y)),x=function(e){var t,n,r,o,i,a,c,u,s=l(e,!1);if("string"==typeof s&&s.length>2)if(43===(t=(s=v(s)).charCodeAt(0))||45===t){if(88===(n=s.charCodeAt(2))||120===n)return NaN}else if(48===t){switch(s.charCodeAt(1)){case 66:case 98:r=2,o=49;break;case 79:case 111:r=8,o=55;break;default:return+s}for(a=(i=s.slice(2)).length,c=0;co)return NaN;return parseInt(i,r)}return+s};if(i("Number",!m(" 0o1")||!m("0b1")||m("+0x1"))){for(var w,_=function(e){var t=arguments.length<1?0:e,n=this;return n instanceof _&&(b?f((function(){y.valueOf.call(n)})):"Number"!=u(n))?s(new m(x(t)),n,_):x(t)},E=r?p(m):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),k=0;E.length>k;k++)c(m,w=E[k])&&!c(_,w)&&g(_,w,h(m,w));_.prototype=y,y.constructor=_,a(o,"Number",_)}},function(e,t,n){"use strict";n(4)({target:"Number",stat:!0},{EPSILON:Math.pow(2,-52)})},function(e,t,n){"use strict";n(4)({target:"Number",stat:!0},{isFinite:n(308)})},function(e,t,n){"use strict";var r=n(7).isFinite;e.exports=Number.isFinite||function(e){return"number"==typeof e&&r(e)}},function(e,t,n){"use strict";n(4)({target:"Number",stat:!0},{isInteger:n(172)})},function(e,t,n){"use strict";n(4)({target:"Number",stat:!0},{isNaN:function(e){return e!=e}})},function(e,t,n){"use strict";var r=n(4),o=n(172),i=Math.abs;r({target:"Number",stat:!0},{isSafeInteger:function(e){return o(e)&&i(e)<=9007199254740991}})},function(e,t,n){"use strict";n(4)({target:"Number",stat:!0},{MAX_SAFE_INTEGER:9007199254740991})},function(e,t,n){"use strict";n(4)({target:"Number",stat:!0},{MIN_SAFE_INTEGER:-9007199254740991})},function(e,t,n){"use strict";var r=n(4),o=n(315);r({target:"Number",stat:!0,forced:Number.parseFloat!=o},{parseFloat:o})},function(e,t,n){"use strict";var r=n(7),o=n(62).trim,i=n(91),a=r.parseFloat,c=1/a(i+"-0")!=-Infinity;e.exports=c?function(e){var t=o(String(e)),n=a(t);return 0===n&&"-"==t.charAt(0)?-0:n}:a},function(e,t,n){"use strict";var r=n(4),o=n(173);r({target:"Number",stat:!0,forced:Number.parseInt!=o},{parseInt:o})},function(e,t,n){"use strict";var r=n(4),o=n(38),i=n(318),a=n(125),c=n(5),u=1..toFixed,s=Math.floor,l=function f(e,t,n){return 0===t?n:t%2==1?f(e,t-1,n*e):f(e*e,t/2,n)};r({target:"Number",proto:!0,forced:u&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!c((function(){u.call({})}))},{toFixed:function(e){var t,n,r,c,u=i(this),f=o(e),d=[0,0,0,0,0,0],p="",h="0",g=function(e,t){for(var n=-1,r=t;++n<6;)r+=e*d[n],d[n]=r%1e7,r=s(r/1e7)},v=function(e){for(var t=6,n=0;--t>=0;)n+=d[t],d[t]=s(n/e),n=n%e*1e7},m=function(){for(var e=6,t="";--e>=0;)if(""!==t||0===e||0!==d[e]){var n=String(d[e]);t=""===t?n:t+a.call("0",7-n.length)+n}return t};if(f<0||f>20)throw RangeError("Incorrect fraction digits");if(u!=u)return"NaN";if(u<=-1e21||u>=1e21)return String(u);if(u<0&&(p="-",u=-u),u>1e-21)if(n=(t=function(e){for(var t=0,n=e;n>=4096;)t+=12,n/=4096;for(;n>=2;)t+=1,n/=2;return t}(u*l(2,69,1))-69)<0?u*l(2,-t,1):u/l(2,t,1),n*=4503599627370496,(t=52-t)>0){for(g(0,n),r=f;r>=7;)g(1e7,0),r-=7;for(g(l(10,r,1),0),r=t-1;r>=23;)v(1<<23),r-=23;v(1<
\n":"'+(n?e:Y(e,!0))+"
\n"},t.blockquote=function(e){return""+(n?e:Y(e,!0))+"\n"+e+"
\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
\n":"
\n"},t.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},t.listitem=function(e){return"\n\n"+e+"\n"+t+"
\n"},t.tablerow=function(e){return"\n"+e+" \n"},t.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},t.strong=function(e){return""+e+""},t.em=function(e){return""+e+""},t.codespan=function(e){return""+e+""},t.br=function(){return this.options.xhtml?"
":"
"},t.del=function(e){return""+e+""},t.link=function(e,t,n){if(null===(e=z(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+""},t.image=function(e,t,n){if(null===(e=z(this.options.sanitize,this.options.baseUrl,e)))return n;var r='":">"},t.text=function(e){return e},e}(),$=function(){function e(){}var t=e.prototype;return t.strong=function(e){return e},t.em=function(e){return e},t.codespan=function(e){return e},t.del=function(e){return e},t.html=function(e){return e},t.text=function(e){return e},t.link=function(e,t,n){return""+n},t.image=function(e,t,n){return""+n},t.br=function(){return""},e}(),H=function(){function e(){this.seen={}}return e.prototype.slug=function(e){var t=e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t)){var n=t;do{this.seen[n]++,t=n+"-"+this.seen[n]}while(this.seen.hasOwnProperty(t))}return this.seen[t]=0,t},e}(),W=r.defaults,q=w.unescape,G=function(){function e(e){this.options=e||W,this.options.renderer=this.options.renderer||new U,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new $,this.slugger=new H}e.parse=function(t,n){return new e(n).parse(t)};var t=e.prototype;return t.parse=function(e,t){void 0===t&&(t=!0);var n,r,o,i,a,c,u,s,l,f,d,p,h,g,v,m,y,b,x="",w=e.length;for(n=0;n
"+Q(u.message+"",!0)+"";throw u}}return ne.options=ne.setOptions=function(e){return X(ne.defaults,e),ee(ne.defaults),ne},ne.getDefaults=J,ne.defaults=te,ne.use=function(e){var t=X({},e);if(e.renderer&&function(){var n=ne.defaults.renderer||new U,r=function(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a
'+(n?e:Y(e,!0))+"\n":""+(n?e:Y(e,!0))+"\n"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return t&&(t=""+t+""),""+e+""},t.br=function(){return this.options.xhtml?""+Q(u.message+"",!0)+"";throw u}}return ne.options=ne.setOptions=function(e){return X(ne.defaults,e),ee(ne.defaults),ne},ne.getDefaults=J,ne.defaults=te,ne.use=function(e){var t=X({},e);if(e.renderer&&function(){var n=ne.defaults.renderer||new U,r=function(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a