Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into changelogs-tgui
This commit is contained in:
@@ -81,6 +81,7 @@
|
||||
#define SYNTH_ACTIVE_COOLING_MIN_ADJUSTMENT 5 //What is the minimum amount of temp you move towards the target point, even if it would be less with default calculations?
|
||||
#define SYNTH_INTEGRATION_COOLANT_PENALTY 0.4 //Integrating coolant is multiplied with this for calculation of impact on passive cooling.
|
||||
#define SYNTH_INTEGRATION_COOLANT_CAP 0.25 //Integrating coolant is capped at counting as current_blood * this number. This is so you can't just run on salglu or whatever.
|
||||
#define SYNTH_COLD_OFFSET -125 //How much colder temps Synths can tolerate. Used in their species.
|
||||
|
||||
#define BODYTEMP_NORMAL 310.15 //The natural temperature for a body
|
||||
#define BODYTEMP_AUTORECOVERY_DIVISOR 11 //This is the divisor which handles how much of the temperature difference between the current body temperature and 310.15K (optimal temperature) humans auto-regenerate each tick. The higher the number, the slower the recovery. This is applied each tick, so long as the mob is alive.
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
#define TRAIT_RESISTCOLD "resist_cold"
|
||||
#define TRAIT_RESISTHIGHPRESSURE "resist_high_pressure"
|
||||
#define TRAIT_RESISTLOWPRESSURE "resist_low_pressure"
|
||||
#define TRAIT_LOWPRESSURECOOLING "low_pressure_cooling"
|
||||
#define TRAIT_BOMBIMMUNE "bomb_immunity"
|
||||
#define TRAIT_RADIMMUNE "rad_immunity"
|
||||
#define TRAIT_GENELESS "geneless"
|
||||
|
||||
@@ -38,6 +38,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_RESISTCOLD" = TRAIT_RESISTCOLD,
|
||||
"TRAIT_RESISTHIGHPRESSURE" = TRAIT_RESISTHIGHPRESSURE,
|
||||
"TRAIT_RESISTLOWPRESSURE" = TRAIT_RESISTLOWPRESSURE,
|
||||
"TRAIT_LOWPRESSURECOOLING" = TRAIT_LOWPRESSURECOOLING,
|
||||
"TRAIT_BOMBIMMUNE" = TRAIT_BOMBIMMUNE,
|
||||
"TRAIT_RADIMMUNE" = TRAIT_RADIMMUNE,
|
||||
"TRAIT_GENELESS" = TRAIT_GENELESS,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
/atom/movable/screen
|
||||
name = ""
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
layer = HUD_LAYER
|
||||
plane = HUD_PLANE
|
||||
animate_movement = SLIDE_STEPS
|
||||
speech_span = SPAN_ROBOT
|
||||
|
||||
@@ -294,3 +294,8 @@
|
||||
description = "<span class='boldwarning'>I hate when my shoes come untied!</span>\n"
|
||||
mood_change = -3
|
||||
timeout = 1 MINUTES
|
||||
|
||||
/datum/mood_event/sacrifice_bad
|
||||
description = "<span class='warning'>Those darn savages!</span>\n"
|
||||
mood_change = -5
|
||||
timeout = 2 MINUTES
|
||||
|
||||
@@ -205,3 +205,8 @@
|
||||
/datum/mood_event/cleared_stomach
|
||||
description = "<span class='nicegreen'>Feels nice to get that out of the way!</span>\n"
|
||||
mood_change = 3
|
||||
|
||||
/datum/mood_event/sacrifice_good
|
||||
description = "<span class='nicegreen'>The gods are pleased with this offering!</span>\n"
|
||||
mood_change = 5
|
||||
timeout = 3 MINUTES
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
return
|
||||
ADD_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
|
||||
ADD_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
|
||||
ADD_TRAIT(owner, TRAIT_LOWPRESSURECOOLING, "cold_resistance")
|
||||
owner.add_filter("space_glow", 2, list("type" = "outline", "color" = "#ffe46bd8", "size" = 1))
|
||||
addtimer(CALLBACK(src, .proc/glow_loop, owner), rand(1,19))
|
||||
|
||||
@@ -27,5 +28,6 @@
|
||||
return
|
||||
REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
|
||||
REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
|
||||
REMOVE_TRAIT(owner, TRAIT_LOWPRESSURECOOLING, "cold_resistance")
|
||||
owner.remove_filter("space_glow")
|
||||
|
||||
|
||||
@@ -778,6 +778,24 @@ GLOBAL_LIST_EMPTY(possible_items_special)
|
||||
target_amount = count
|
||||
update_explanation_text()
|
||||
*/
|
||||
/datum/objective/protect_object
|
||||
name = "protect object"
|
||||
var/obj/protect_target
|
||||
|
||||
/datum/objective/protect_object/proc/set_target(obj/O)
|
||||
protect_target = O
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/protect_object/update_explanation_text()
|
||||
. = ..()
|
||||
if(protect_target)
|
||||
explanation_text = "Protect \the [protect_target] at all costs."
|
||||
else
|
||||
explanation_text = "Free objective."
|
||||
|
||||
/datum/objective/protect_object/check_completion()
|
||||
return !QDELETED(protect_target)
|
||||
|
||||
//Changeling Objectives
|
||||
|
||||
/datum/objective/absorb
|
||||
|
||||
+208
-217
@@ -1,7 +1,3 @@
|
||||
#define AUTOLATHE_MAIN_MENU 1
|
||||
#define AUTOLATHE_CATEGORY_MENU 2
|
||||
#define AUTOLATHE_SEARCH_MENU 3
|
||||
|
||||
/obj/machinery/autolathe
|
||||
name = "autolathe"
|
||||
desc = "It produces items using metal and glass."
|
||||
@@ -24,13 +20,14 @@
|
||||
var/shock_wire
|
||||
|
||||
var/busy = FALSE
|
||||
var/prod_coeff = 1
|
||||
|
||||
///the multiplier for how much materials the created object takes from this machines stored materials
|
||||
var/creation_efficiency = 1.6
|
||||
|
||||
var/datum/design/being_built
|
||||
var/datum/techweb/stored_research
|
||||
var/list/datum/design/matching_designs
|
||||
var/selected_category
|
||||
var/screen = 1
|
||||
var/selected_category = "None"
|
||||
var/base_price = 25
|
||||
var/hacked_price = 50
|
||||
|
||||
@@ -60,7 +57,7 @@
|
||||
QDEL_NULL(wires)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/autolathe/ui_interact(mob/user)
|
||||
/obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui)
|
||||
. = ..()
|
||||
if(!is_operational())
|
||||
return
|
||||
@@ -68,101 +65,129 @@
|
||||
if(shocked && !(stat & NOPOWER))
|
||||
shock(user,50)
|
||||
|
||||
var/dat
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Autolathe", capitalize(src.name))
|
||||
ui.open()
|
||||
|
||||
switch(screen)
|
||||
if(AUTOLATHE_MAIN_MENU)
|
||||
dat = main_win(user)
|
||||
if(AUTOLATHE_CATEGORY_MENU)
|
||||
dat = category_win(user,selected_category)
|
||||
if(AUTOLATHE_SEARCH_MENU)
|
||||
dat = search_win(user)
|
||||
|
||||
var/datum/browser/popup = new(user, "autolathe", name, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/autolathe/on_deconstruction()
|
||||
/obj/machinery/autolathe/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["materials"] = list()
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
materials.retrieve_all()
|
||||
data["materialtotal"] = materials.total_amount
|
||||
data["materialsmax"] = materials.max_amount
|
||||
data["categories"] = categories
|
||||
data["designs"] = list()
|
||||
data["active"] = busy
|
||||
|
||||
/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params)
|
||||
if (busy)
|
||||
to_chat(user, "<span class=\"alert\">The autolathe is busy. Please wait for completion of previous operation.</span>")
|
||||
return TRUE
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O))
|
||||
updateUsrDialog()
|
||||
return TRUE
|
||||
|
||||
if(default_deconstruction_crowbar(O))
|
||||
return TRUE
|
||||
|
||||
if(panel_open && is_wire_tool(O))
|
||||
wires.interact(user)
|
||||
return TRUE
|
||||
|
||||
if(user.a_intent == INTENT_HARM) //so we can hit the machine
|
||||
return ..()
|
||||
|
||||
if(stat)
|
||||
return TRUE
|
||||
|
||||
if(istype(O, /obj/item/disk/design_disk))
|
||||
user.visible_message("<span class='notice'>[user] begins to load \the [O] in \the [src]...</span>",
|
||||
"<span class='notice'>You begin to load a design from \the [O]...</span>",
|
||||
"<span class='hear'>You hear the chatter of a floppy drive.</span>")
|
||||
busy = TRUE
|
||||
var/obj/item/disk/design_disk/D = O
|
||||
if(do_after(user, 14.4, target = src))
|
||||
for(var/B in D.blueprints)
|
||||
if(B)
|
||||
stored_research.add_design(B)
|
||||
busy = FALSE
|
||||
return TRUE
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/machinery/autolathe/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted)
|
||||
if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
|
||||
use_power(MINERAL_MATERIAL_AMOUNT / 10)
|
||||
else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
|
||||
flick("autolathe_r",src)//plays glass insertion animation by default otherwise
|
||||
for(var/mat_id in materials.materials)
|
||||
var/datum/material/M = mat_id
|
||||
var/mineral_count = materials.materials[mat_id]
|
||||
var/list/material_data = list(
|
||||
name = M.name,
|
||||
mineral_amount = mineral_count,
|
||||
matcolour = M.color,
|
||||
)
|
||||
data["materials"] += list(material_data)
|
||||
if(selected_category != "None" && !length(matching_designs))
|
||||
data["designs"] = handle_designs(stored_research.researched_designs, TRUE)
|
||||
else
|
||||
flick("autolathe_o",src)//plays metal insertion animation
|
||||
data["designs"] = handle_designs(matching_designs, FALSE)
|
||||
return data
|
||||
|
||||
/obj/machinery/autolathe/proc/handle_designs(list/designs, categorycheck)
|
||||
var/list/output = list()
|
||||
for(var/v in designs)
|
||||
var/datum/design/D = categorycheck ? SSresearch.techweb_design_by_id(v) : v
|
||||
if(categorycheck)
|
||||
if(!(selected_category in D.category))
|
||||
continue
|
||||
var/unbuildable = FALSE // we can't build the design currently
|
||||
var/m10 = FALSE // 10x mult
|
||||
var/m25 = FALSE // 25x mult
|
||||
var/m50 = FALSE // 50x mult
|
||||
var/m5 = FALSE // 5x mult
|
||||
var/sheets = FALSE // sheets or no?
|
||||
if(disabled || !can_build(D))
|
||||
unbuildable = TRUE
|
||||
var/max_multiplier = unbuildable ? 0 : 1
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
sheets = TRUE
|
||||
if(!unbuildable)
|
||||
var/datum/component/material_container/mats = GetComponent(/datum/component/material_container)
|
||||
for(var/datum/material/mat in D.materials)
|
||||
max_multiplier = min(D.maxstack, round(mats.get_material_amount(mat)/D.materials[mat]))
|
||||
if (max_multiplier>10 && !disabled)
|
||||
m10 = TRUE
|
||||
if (max_multiplier>25 && !disabled)
|
||||
m25 = TRUE
|
||||
else
|
||||
if(!unbuildable)
|
||||
if(!disabled && can_build(D, 5))
|
||||
m5 = TRUE
|
||||
if(!disabled && can_build(D, 10))
|
||||
m10 = TRUE
|
||||
var/datum/component/material_container/mats = GetComponent(/datum/component/material_container)
|
||||
for(var/datum/material/mat in D.materials)
|
||||
max_multiplier = min(50, round(mats.get_material_amount(mat)/(D.materials[mat] * creation_efficiency)))
|
||||
|
||||
use_power(min(1000, amount_inserted / 100))
|
||||
updateUsrDialog()
|
||||
var/list/design = list(
|
||||
name = D.name,
|
||||
id = D.id,
|
||||
ref = REF(src),
|
||||
cost = get_design_cost(D),
|
||||
buildable = unbuildable,
|
||||
mult5 = m5,
|
||||
mult10 = m10,
|
||||
mult25 = m25,
|
||||
mult50 = m50,
|
||||
sheet = sheets,
|
||||
maxmult = max_multiplier,
|
||||
)
|
||||
output += list(design)
|
||||
return output
|
||||
|
||||
/obj/machinery/autolathe/Topic(href, href_list)
|
||||
if(..())
|
||||
/obj/machinery/autolathe/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if (!busy)
|
||||
if(href_list["menu"])
|
||||
screen = text2num(href_list["menu"])
|
||||
updateUsrDialog()
|
||||
if(action == "menu")
|
||||
selected_category = null
|
||||
matching_designs.Cut()
|
||||
. = TRUE
|
||||
|
||||
if(href_list["category"])
|
||||
selected_category = href_list["category"]
|
||||
updateUsrDialog()
|
||||
if(action == "category")
|
||||
selected_category = params["selectedCategory"]
|
||||
matching_designs.Cut()
|
||||
. = TRUE
|
||||
|
||||
if(href_list["make"])
|
||||
if(action == "search")
|
||||
matching_designs.Cut()
|
||||
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(v)
|
||||
if(findtext(D.name,params["to_search"]))
|
||||
matching_designs.Add(D)
|
||||
. = TRUE
|
||||
|
||||
if(action == "make")
|
||||
if (!busy)
|
||||
/////////////////
|
||||
//href protection
|
||||
being_built = stored_research.isDesignResearchedID(href_list["make"])
|
||||
being_built = stored_research.isDesignResearchedID(params["id"])
|
||||
if(!being_built)
|
||||
return
|
||||
|
||||
var/multiplier = text2num(href_list["multiplier"])
|
||||
var/multiplier = text2num(params["multiplier"])
|
||||
if(!multiplier)
|
||||
to_chat(usr, span_alert("[src] only accepts a numerical multiplier!"))
|
||||
return
|
||||
var/is_stack = ispath(being_built.build_path, /obj/item/stack)
|
||||
multiplier = clamp(multiplier,1,50)
|
||||
multiplier = clamp(round(multiplier),1,50)
|
||||
|
||||
/////////////////
|
||||
|
||||
var/coeff = (is_stack ? 1 : prod_coeff) //stacks are unaffected by production coefficient
|
||||
var/coeff = (is_stack ? 1 : creation_efficiency) //stacks are unaffected by production coefficient
|
||||
var/total_amount = 0
|
||||
|
||||
for(var/MAT in being_built.materials)
|
||||
@@ -184,8 +209,8 @@
|
||||
if(materials.materials[i] > 0)
|
||||
list_to_show += i
|
||||
|
||||
used_material = input("Choose [used_material]", "Custom Material") as null|anything in sortList(list_to_show, /proc/cmp_typepaths_asc)
|
||||
if(!used_material)
|
||||
used_material = tgui_input_list(usr, "Choose [used_material]", "Custom Material", sortList(list_to_show, /proc/cmp_typepaths_asc))
|
||||
if(isnull(used_material))
|
||||
return //Didn't pick any material, so you can't build shit either.
|
||||
custom_materials[used_material] += amount_needed
|
||||
|
||||
@@ -193,27 +218,95 @@
|
||||
|
||||
if(materials.has_materials(materials_used))
|
||||
busy = TRUE
|
||||
to_chat(usr, span_notice("You print [multiplier] item(s) from the [src]"))
|
||||
use_power(power)
|
||||
icon_state = "autolathe_n"
|
||||
var/time = is_stack ? 32 : (32 * coeff * multiplier) ** 0.8
|
||||
addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack, usr), time)
|
||||
. = TRUE
|
||||
else
|
||||
to_chat(usr, "<span class=\"alert\">Not enough materials for this operation.</span>")
|
||||
to_chat(usr, span_alert("Not enough materials for this operation."))
|
||||
else
|
||||
to_chat(usr, span_alert("The autolathe is busy. Please wait for completion of previous operation."))
|
||||
|
||||
if(href_list["search"])
|
||||
matching_designs.Cut()
|
||||
/obj/machinery/autolathe/on_deconstruction()
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
materials.retrieve_all()
|
||||
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(v)
|
||||
if(findtext(D.name,href_list["to_search"]))
|
||||
matching_designs.Add(D)
|
||||
updateUsrDialog()
|
||||
/obj/machinery/autolathe/attackby(obj/item/O, mob/living/user, params)
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return TRUE
|
||||
|
||||
if(user.a_intent == INTENT_HARM) //so we can hit the machine
|
||||
return ..()
|
||||
|
||||
if(stat)
|
||||
return TRUE
|
||||
|
||||
if(istype(O, /obj/item/disk/design_disk))
|
||||
user.visible_message(span_notice("[user] begins to load \the [O] in \the [src]..."),
|
||||
balloon_alert(user, "uploading design..."),
|
||||
span_hear("You hear the chatter of a floppy drive."))
|
||||
busy = TRUE
|
||||
if(do_after(user, 14.4, target = src))
|
||||
var/obj/item/disk/design_disk/disky = O
|
||||
var/list/not_imported
|
||||
for(var/datum/design/blueprint as anything in disky.blueprints)
|
||||
if(!blueprint)
|
||||
continue
|
||||
if(blueprint.build_type & AUTOLATHE)
|
||||
stored_research.add_design(blueprint)
|
||||
else
|
||||
LAZYADD(not_imported, blueprint.name)
|
||||
if(not_imported)
|
||||
to_chat(user, span_warning("The following design[length(not_imported) > 1 ? "s" : ""] couldn't be imported: [english_list(not_imported)]"))
|
||||
busy = FALSE
|
||||
return TRUE
|
||||
|
||||
if(panel_open)
|
||||
balloon_alert(user, "close the panel first!")
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/machinery/autolathe/screwdriver_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I))
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
/obj/machinery/autolathe/crowbar_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
if(default_deconstruction_crowbar(I))
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
/obj/machinery/autolathe/multitool_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
if(panel_open)
|
||||
wires.interact(user)
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted)
|
||||
if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
|
||||
use_power(MINERAL_MATERIAL_AMOUNT / 10)
|
||||
else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
|
||||
flick("autolathe_r", src)//plays glass insertion animation by default otherwise
|
||||
else
|
||||
to_chat(usr, "<span class=\"alert\">The autolathe is busy. Please wait for completion of previous operation.</span>")
|
||||
flick("autolathe_o", src)//plays metal insertion animation
|
||||
|
||||
updateUsrDialog()
|
||||
|
||||
return
|
||||
use_power(min(1000, amount_inserted / 100))
|
||||
|
||||
/obj/machinery/autolathe/proc/make_item(power, list/materials_used, list/picked_materials, multiplier, coeff, is_stack, mob/user)
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
@@ -223,11 +316,11 @@
|
||||
materials.use_materials(materials_used)
|
||||
|
||||
if(is_stack)
|
||||
var/obj/item/stack/N = new being_built.build_path(A, multiplier)
|
||||
N.update_icon()
|
||||
var/obj/item/stack/N = new being_built.build_path(A, multiplier, FALSE)
|
||||
N.update_appearance()
|
||||
N.autolathe_crafted(src)
|
||||
else
|
||||
for(var/i=1, i<=multiplier, i++)
|
||||
for(var/i in 1 to multiplier)
|
||||
var/obj/item/new_item = new being_built.build_path(A)
|
||||
new_item.autolathe_crafted(src)
|
||||
|
||||
@@ -241,132 +334,30 @@
|
||||
|
||||
icon_state = "autolathe"
|
||||
busy = FALSE
|
||||
updateDialog()
|
||||
|
||||
/obj/machinery/autolathe/RefreshParts()
|
||||
var/T = 0
|
||||
for(var/obj/item/stock_parts/matter_bin/MB in component_parts)
|
||||
T += MB.rating*75000
|
||||
var/mat_capacity = 0
|
||||
for(var/obj/item/stock_parts/matter_bin/new_matter_bin in component_parts)
|
||||
mat_capacity += new_matter_bin.rating*75000
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
materials.max_amount = T
|
||||
T=1.2
|
||||
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
||||
T -= M.rating*0.2
|
||||
prod_coeff = min(1,max(0,T)) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4
|
||||
materials.max_amount = mat_capacity
|
||||
|
||||
var/efficiency=1.2
|
||||
for(var/obj/item/stock_parts/manipulator/new_manipulator in component_parts)
|
||||
efficiency -= new_manipulator.rating*0.2
|
||||
creation_efficiency = max(1,efficiency) // creation_efficiency goes 1 -> 0,8 -> 0,6 -> 0,4 per level of manipulator efficiency
|
||||
|
||||
/obj/machinery/autolathe/examine(mob/user)
|
||||
. += ..()
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
if(in_range(user, src) || isobserver(user))
|
||||
. += "<span class='notice'>The status display reads: Storing up to <b>[materials.max_amount]</b> material units.<br>Material consumption at <b>[prod_coeff*100]%</b>.</span>"
|
||||
|
||||
/obj/machinery/autolathe/proc/main_win(mob/user)
|
||||
var/dat = "<div class='statusDisplay'><h3>Autolathe Menu:</h3><br>"
|
||||
dat += materials_printout()
|
||||
|
||||
dat += "<form name='search' action='?src=[REF(src)]'>\
|
||||
<input type='hidden' name='src' value='[REF(src)]'>\
|
||||
<input type='hidden' name='search' value='to_search'>\
|
||||
<input type='hidden' name='menu' value='[AUTOLATHE_SEARCH_MENU]'>\
|
||||
<input type='text' name='to_search'>\
|
||||
<input type='submit' value='Search'>\
|
||||
</form><hr>"
|
||||
|
||||
var/line_length = 1
|
||||
dat += "<table style='width:100%' align='center'><tr>"
|
||||
|
||||
for(var/C in categories)
|
||||
if(line_length > 2)
|
||||
dat += "</tr><tr>"
|
||||
line_length = 1
|
||||
|
||||
dat += "<td><A href='?src=[REF(src)];category=[C];menu=[AUTOLATHE_CATEGORY_MENU]'>[C]</A></td>"
|
||||
line_length++
|
||||
|
||||
dat += "</tr></table></div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/autolathe/proc/category_win(mob/user,selected_category)
|
||||
var/dat = "<A href='?src=[REF(src)];menu=[AUTOLATHE_MAIN_MENU]'>Return to main menu</A>"
|
||||
dat += "<div class='statusDisplay'><h3>Browsing [selected_category]:</h3><br>"
|
||||
dat += materials_printout()
|
||||
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(v)
|
||||
if(!(selected_category in D.category))
|
||||
continue
|
||||
|
||||
if(disabled || !can_build(D))
|
||||
dat += "<span class='linkOff'>[D.name]</span>"
|
||||
else
|
||||
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
|
||||
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
var/max_multiplier
|
||||
for(var/datum/material/mat in D.materials)
|
||||
max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat]))
|
||||
if (max_multiplier>10 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
|
||||
if (max_multiplier>25 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
|
||||
if(max_multiplier > 0 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
|
||||
else
|
||||
if(!disabled && can_build(D, 5))
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=5'>x5</a>"
|
||||
if(!disabled && can_build(D, 10))
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
|
||||
|
||||
dat += "[get_design_cost(D)]<br>"
|
||||
|
||||
dat += "</div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/autolathe/proc/search_win(mob/user)
|
||||
var/dat = "<A href='?src=[REF(src)];menu=[AUTOLATHE_MAIN_MENU]'>Return to main menu</A>"
|
||||
dat += "<div class='statusDisplay'><h3>Search results:</h3><br>"
|
||||
dat += materials_printout()
|
||||
|
||||
for(var/v in matching_designs)
|
||||
var/datum/design/D = v
|
||||
if(disabled || !can_build(D))
|
||||
dat += "<span class='linkOff'>[D.name]</span>"
|
||||
else
|
||||
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
|
||||
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
var/max_multiplier
|
||||
for(var/datum/material/mat in D.materials)
|
||||
max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat]))
|
||||
if (max_multiplier>10 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
|
||||
if (max_multiplier>25 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
|
||||
if(max_multiplier > 0 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
|
||||
|
||||
dat += "[get_design_cost(D)]<br>"
|
||||
|
||||
dat += "</div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/autolathe/proc/materials_printout()
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
var/dat = "<b>Total amount:</b> [materials.total_amount] / [materials.max_amount] cm<sup>3</sup><br>"
|
||||
for(var/mat_id in materials.materials)
|
||||
var/datum/material/M = mat_id
|
||||
var/mineral_amount = materials.materials[mat_id]
|
||||
if(mineral_amount > 0)
|
||||
dat += "<b>[M.name] amount:</b> [mineral_amount] cm<sup>3</sup><br>"
|
||||
return dat
|
||||
. += span_notice("The status display reads: Storing up to <b>[materials.max_amount]</b> material units.<br>Material consumption at <b>[creation_efficiency*100]%</b>.")
|
||||
|
||||
/obj/machinery/autolathe/proc/can_build(datum/design/D, amount = 1)
|
||||
if(D.make_reagents.len)
|
||||
if(length(D.make_reagents))
|
||||
return FALSE
|
||||
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : creation_efficiency)
|
||||
|
||||
var/list/required_materials = list()
|
||||
|
||||
@@ -379,7 +370,7 @@
|
||||
|
||||
|
||||
/obj/machinery/autolathe/proc/get_design_cost(datum/design/D)
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : creation_efficiency)
|
||||
var/dat
|
||||
for(var/i in D.materials)
|
||||
if(istext(i)) //Category handling
|
||||
@@ -402,7 +393,7 @@
|
||||
disabled = FALSE
|
||||
|
||||
/obj/machinery/autolathe/proc/shock(mob/user, prb)
|
||||
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
|
||||
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
|
||||
return FALSE
|
||||
if(!prob(prb))
|
||||
return FALSE
|
||||
@@ -424,7 +415,7 @@
|
||||
else
|
||||
stored_research.remove_design(D)
|
||||
|
||||
/obj/machinery/autolathe/hacked/Initialize()
|
||||
/obj/machinery/autolathe/hacked/Initialize(mapload)
|
||||
. = ..()
|
||||
adjust_hacked(TRUE)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Objects that spawn ghosts in as a certain role when they click on it, i.e. away mission bartenders.
|
||||
|
||||
#define spawnOverride TRUE
|
||||
//Preserved terrarium/seed vault: Spawns in seed vault structures in lavaland. Ghosts become plantpeople and are advised to begin growing plants in the room near them.
|
||||
/obj/effect/mob_spawn/human/seed_vault
|
||||
name = "preserved terrarium"
|
||||
@@ -36,6 +36,44 @@
|
||||
|
||||
//Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers.
|
||||
|
||||
/obj/structure/ash_walker_eggshell
|
||||
name = "ash walker egg"
|
||||
desc = "A man-sized yellow egg, spawned from some unfathomable creature. A humanoid silhouette lurks within. The egg shell looks resistant to temperature but otherwise rather brittle."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "large_egg"
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | FREEZE_PROOF
|
||||
max_integrity = 80
|
||||
var/obj/effect/mob_spawn/human/ash_walker/egg
|
||||
|
||||
/obj/structure/ash_walker_eggshell/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) //lifted from xeno eggs
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(damage_amount)
|
||||
playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE)
|
||||
else
|
||||
playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
|
||||
if(BURN)
|
||||
if(damage_amount)
|
||||
playsound(loc, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/ash_walker_eggshell/attack_ghost(mob/user) //Pass on ghost clicks to the mob spawner
|
||||
if(egg)
|
||||
egg.attack_ghost(user)
|
||||
. = ..()
|
||||
|
||||
/obj/structure/ash_walker_eggshell/Destroy()
|
||||
if(!egg)
|
||||
return ..()
|
||||
var/mob/living/carbon/human/yolk = new /mob/living/carbon/human/(get_turf(src))
|
||||
yolk.fully_replace_character_name(null,random_unique_lizard_name(gender))
|
||||
yolk.set_species(/datum/species/lizard/ashwalker)
|
||||
yolk.underwear = "Nude"
|
||||
yolk.equipOutfit(/datum/outfit/ashwalker)//this is an authentic mess we're making
|
||||
yolk.update_body()
|
||||
yolk.gib()
|
||||
QDEL_NULL(egg)
|
||||
return ..()
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker
|
||||
name = "ash walker egg"
|
||||
desc = "A man-sized yellow egg, spawned from some unfathomable creature. A humanoid silhouette lurks within."
|
||||
@@ -55,12 +93,25 @@
|
||||
You have seen lights in the distance... they foreshadow the arrival of outsiders to your domain. \
|
||||
Ensure your nest remains protected at all costs."
|
||||
assignedrole = "Ash Walker"
|
||||
var/datum/team/ashwalkers/team
|
||||
var/obj/structure/ash_walker_eggshell/eggshell
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/Destroy()
|
||||
eggshell = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/allow_spawn(mob/user, silent = FALSE)
|
||||
if(!(user.key in team.players_spawned) || spawnOverride)//one per person unless you get a bonus spawn
|
||||
return TRUE
|
||||
to_chat(user, span_warning("<b>You have exhausted your usefulness to the Necropolis</b>."))
|
||||
return FALSE
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn)
|
||||
new_spawn.real_name = random_unique_lizard_name(gender)
|
||||
if(is_mining_level(z))
|
||||
to_chat(new_spawn, "<b>Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Glory to the Necropolis!</b>")
|
||||
to_chat(new_spawn, "<b>You can expand the weather proof area provided by your shelters by using the 'New Area' key near the bottom right of your HUD.</b>")
|
||||
to_chat(new_spawn, "<b>Dragging injured ashwalkers to the tentacle or using the sleep verb next to it youself causes the body to remade whole after a short delay!</b>")
|
||||
else
|
||||
to_chat(new_spawn, "<span class='userdanger'>You have been born outside of your natural home! Whether you decide to return home, or make due with your new home is your own decision.</span>")
|
||||
|
||||
@@ -72,10 +123,18 @@
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.update_body()
|
||||
new_spawn.mind.add_antag_datum(/datum/antagonist/ashwalker, team)
|
||||
team.players_spawned += (new_spawn.key)
|
||||
eggshell.egg = null
|
||||
QDEL_NULL(eggshell)
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/Initialize(mapload)
|
||||
/obj/effect/mob_spawn/human/ash_walker/Initialize(mapload, datum/team/ashwalkers/ashteam)
|
||||
. = ..()
|
||||
var/area/A = get_area(src)
|
||||
team = ashteam
|
||||
eggshell = new /obj/structure/ash_walker_eggshell(get_turf(loc))
|
||||
eggshell.egg = src
|
||||
src.forceMove(eggshell)
|
||||
if(A)
|
||||
notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_ASHWALKER, ignore_dnr_observers = TRUE)
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
remove_innate_effects(old_body)
|
||||
if(!soft_antag && old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
|
||||
if(!soft_antag && old_body && old_body.stat != DEAD && !length(old_body.mind?.antag_datums))
|
||||
old_body.remove_from_current_living_antags()
|
||||
apply_innate_effects(new_body)
|
||||
if(!soft_antag && new_body.stat != DEAD)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/datum/team/ashwalkers
|
||||
name = "Ashwalkers"
|
||||
show_roundend_report = FALSE
|
||||
var/list/players_spawned = new
|
||||
|
||||
/datum/antagonist/ashwalker
|
||||
name = "\improper Ash Walker"
|
||||
job_rank = ROLE_LAVALAND
|
||||
show_in_antagpanel = FALSE
|
||||
show_to_ghosts = TRUE
|
||||
antagpanel_category = "Ash Walkers"
|
||||
var/datum/team/ashwalkers/ashie_team
|
||||
|
||||
/datum/antagonist/ashwalker/create_team(datum/team/team)
|
||||
if(team)
|
||||
ashie_team = team
|
||||
objectives |= ashie_team.objectives
|
||||
else
|
||||
ashie_team = new
|
||||
|
||||
/datum/antagonist/ashwalker/get_team()
|
||||
return ashie_team
|
||||
|
||||
/datum/antagonist/ashwalker/on_body_transfer(mob/living/old_body, mob/living/new_body)
|
||||
. = ..()
|
||||
RegisterSignal(new_body, COMSIG_MOB_EXAMINATE, .proc/on_examinate)
|
||||
|
||||
/datum/antagonist/ashwalker/on_gain()
|
||||
. = ..()
|
||||
RegisterSignal(owner.current, COMSIG_MOB_EXAMINATE, .proc/on_examinate)
|
||||
|
||||
/datum/antagonist/ashwalker/on_removal()
|
||||
. = ..()
|
||||
UnregisterSignal(owner.current, COMSIG_MOB_EXAMINATE)
|
||||
|
||||
/datum/antagonist/ashwalker/proc/on_examinate(datum/source, atom/A)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(istype(A, /obj/structure/headpike))
|
||||
SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, "headspear", /datum/mood_event/sacrifice_good)
|
||||
@@ -33,6 +33,10 @@
|
||||
var/ghost_usable = TRUE
|
||||
var/skip_reentry_check = FALSE //Skips the ghost role blacklist time for people who ghost/suicide/cryo
|
||||
|
||||
///override this to add special spawn conditions to a ghost role
|
||||
/obj/effect/mob_spawn/proc/allow_spawn(mob/user, silent = FALSE)
|
||||
return TRUE
|
||||
|
||||
//ATTACK GHOST IGNORING PARENT RETURN VALUE
|
||||
/obj/effect/mob_spawn/attack_ghost(mob/user, latejoinercalling)
|
||||
if(!SSticker.HasRoundStarted() || !loc || !ghost_usable)
|
||||
@@ -43,6 +47,8 @@
|
||||
if(jobban_isbanned(user, banType))
|
||||
to_chat(user, "<span class='warning'>You are jobanned!</span>")
|
||||
return
|
||||
if(!allow_spawn(user, silent = FALSE))
|
||||
return
|
||||
if(QDELETED(src) || QDELETED(user))
|
||||
return
|
||||
if(isobserver(user))
|
||||
|
||||
@@ -36,3 +36,25 @@
|
||||
/datum/keybinding/living/toggle_resting/down(client/user)
|
||||
var/mob/living/L = user.mob
|
||||
L.lay_down()
|
||||
|
||||
/datum/keybinding/living/cancel_action
|
||||
hotkey_keys = list("Unbound")
|
||||
name = "cancel_action"
|
||||
full_name = "Cancel Action"
|
||||
description = "Cancel the current action."
|
||||
|
||||
/// Technically you shouldn't be doing any actions if you were sleeping either but...
|
||||
/datum/keybinding/living/can_use(client/user)
|
||||
. = ..()
|
||||
var/mob/living/mob = user.mob
|
||||
return . && (mob.stat == CONSCIOUS)
|
||||
|
||||
/datum/keybinding/living/cancel_action/down(client/user)
|
||||
var/mob/M = user.mob
|
||||
if(length(M.do_afters))
|
||||
var/atom/target = M.do_afters[M.do_afters.len]
|
||||
to_chat(M, "<span class='notice'>You stop interacting with \the [target].</span>")
|
||||
LAZYREMOVE(M.do_afters, target)
|
||||
else
|
||||
to_chat(M, "<span class='notice'>There's nothing that you can cancel right now.</span>")
|
||||
return TRUE
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
coldmod = 0.5
|
||||
heatmod = 1.2
|
||||
cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
|
||||
cold_offset = SYNTH_COLD_OFFSET //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
|
||||
blacklisted_quirks = list(/datum/quirk/coldblooded)
|
||||
balance_point_values = TRUE
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE,HAIR,ROBOTIC_LIMBS)
|
||||
hair_alpha = 210
|
||||
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
|
||||
mutant_bodyparts = list("ipc_screen" = "Blank", "ipc_antenna" = "None")
|
||||
mutant_bodyparts = list("ipc_screen" = "Blank", "deco_wings" = "None", "ipc_antenna" = "None")
|
||||
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
|
||||
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
|
||||
|
||||
coldmod = 0.5
|
||||
heatmod = 1.2
|
||||
cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
|
||||
cold_offset = SYNTH_COLD_OFFSET //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
|
||||
blacklisted_quirks = list(/datum/quirk/coldblooded)
|
||||
balance_point_values = TRUE
|
||||
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
default_color = "00FFFF"
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,HAS_FLESH)
|
||||
inherent_traits = list(TRAIT_TOXINLOVER)
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_tail" = "None", "mam_ears" = "None", "mam_body_markings" = "Plain", "mam_snouts" = "None", "taur" = "None", "legs" = "Plantigrade")
|
||||
mutant_bodyparts = list("mcolor" = "FFFFFF", "mcolor2" = "FFFFFF","mcolor3" = "FFFFFF", "mam_tail" = "None", "mam_ears" = "None", "mam_body_markings" = "Plain", "mam_snouts" = "None", "taur" = "None", "deco_wings" = "None", "legs" = "Plantigrade")
|
||||
say_mod = "says"
|
||||
hair_color = "mutcolor"
|
||||
hair_alpha = 160 //a notch brighter so it blends better.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
coldmod = 0.5
|
||||
heatmod = 1.2
|
||||
cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
|
||||
cold_offset = SYNTH_COLD_OFFSET //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
|
||||
blacklisted_quirks = list(/datum/quirk/coldblooded)
|
||||
balance_point_values = TRUE
|
||||
|
||||
|
||||
@@ -699,6 +699,9 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
|
||||
var/heat_efficiency = clamp(1 + ((bodytemperature - heat) * SYNTH_HEAT_EFFICIENCY_COEFF), 0, SYNTH_SINGLE_INFLUENCE_COOLING_EFFECT_CAP)
|
||||
var/pressure_efficiency = clamp(pressure / ONE_ATMOSPHERE, 0, SYNTH_SINGLE_INFLUENCE_COOLING_EFFECT_CAP)
|
||||
|
||||
if(HAS_TRAIT(src, TRAIT_LOWPRESSURECOOLING))
|
||||
pressure_efficiency = max(pressure_efficiency, 1) //Space adaptation nulls drawbacks of low-pressure cooling.
|
||||
|
||||
var/total_environment_efficiency = min(heat_efficiency * pressure_efficiency, SYNTH_TOTAL_ENVIRONMENT_EFFECT_CAP) //At best, you can get 200% total
|
||||
return total_environment_efficiency
|
||||
|
||||
|
||||
@@ -292,18 +292,21 @@
|
||||
|
||||
/mob/living/simple_animal/bot/on_attack_hand(mob/living/carbon/human/H)
|
||||
if(H.a_intent == INTENT_HELP)
|
||||
interact(H)
|
||||
ui_interact(H)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/attack_ai(mob/user)
|
||||
if(!topic_denied(user))
|
||||
interact(user)
|
||||
ui_interact(user)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src]'s interface is not responding!</span>")
|
||||
|
||||
/mob/living/simple_animal/bot/interact(mob/user)
|
||||
show_controls(user)
|
||||
/mob/living/simple_animal/bot/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "SimpleBot", name)
|
||||
ui.open()
|
||||
|
||||
/mob/living/simple_animal/bot/attackby(obj/item/W, mob/user, params)
|
||||
if(W.tool_behaviour == TOOL_SCREWDRIVER)
|
||||
@@ -795,56 +798,48 @@ Pass a positive integer as an argument to override a bot's default speed.
|
||||
else // no path, so calculate new one
|
||||
calc_summon_path()
|
||||
|
||||
/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened!
|
||||
// Variables sent to TGUI
|
||||
/mob/living/simple_animal/bot/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["can_hack"] = (issilicon(user) || IsAdminGhost(user))
|
||||
data["custom_controls"] = list()
|
||||
data["emagged"] = emagged
|
||||
data["locked"] = locked
|
||||
data["pai"] = list()
|
||||
data["settings"] = list()
|
||||
if(!locked || issilicon(user) || IsAdminGhost(user))
|
||||
data["pai"]["allow_pai"] = allow_pai
|
||||
data["pai"]["card_inserted"] = paicard
|
||||
data["settings"]["airplane_mode"] = remote_disabled
|
||||
data["settings"]["maintenance_lock"] = !open
|
||||
data["settings"]["power"] = on
|
||||
data["settings"]["patrol_station"] = auto_patrol
|
||||
return data
|
||||
|
||||
// Actions received from TGUI
|
||||
/mob/living/simple_animal/bot/ui_act(action, params)
|
||||
. = ..()
|
||||
if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(access_card)))
|
||||
var/obj/machinery/door/D = M
|
||||
if(D.check_access(access_card))
|
||||
D.open()
|
||||
frustration = 0
|
||||
|
||||
/mob/living/simple_animal/bot/proc/show_controls(mob/M)
|
||||
users |= M
|
||||
var/dat = ""
|
||||
dat = get_controls(M)
|
||||
var/datum/browser/popup = new(M,window_id,window_name,350,600)
|
||||
popup.set_content(dat)
|
||||
popup.open(use_onclose = 0)
|
||||
onclose(M,window_id,ref=src)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/proc/update_controls()
|
||||
for(var/mob/M in users)
|
||||
show_controls(M)
|
||||
|
||||
/mob/living/simple_animal/bot/proc/get_controls(mob/M)
|
||||
return "PROTOBOT - NOT FOR USE"
|
||||
|
||||
/mob/living/simple_animal/bot/Topic(href, href_list)
|
||||
//No ..() to prevent strip panel showing up - Todo: make that saner
|
||||
if(href_list["close"])// HUE HUE
|
||||
if(usr in users)
|
||||
users.Remove(usr)
|
||||
return TRUE
|
||||
|
||||
if(topic_denied(usr))
|
||||
to_chat(usr, "<span class='warning'>[src]'s interface is not responding!</span>")
|
||||
return TRUE
|
||||
add_fingerprint(usr)
|
||||
|
||||
if((href_list["power"]) && (bot_core.allowed(usr) || !locked))
|
||||
if(on)
|
||||
turn_off()
|
||||
else
|
||||
turn_on()
|
||||
|
||||
switch(href_list["operation"])
|
||||
if(.)
|
||||
return
|
||||
if(!hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
to_chat(usr, span_warning("Access denied."))
|
||||
return
|
||||
if(action == "lock")
|
||||
locked = !locked
|
||||
if(locked && !(issilicon(usr) || IsAdminGhost(usr)))
|
||||
return
|
||||
switch(action)
|
||||
if("power")
|
||||
on = !on
|
||||
update_appearance()
|
||||
if("maintenance")
|
||||
open = !open
|
||||
if("patrol")
|
||||
if(!hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
return TRUE
|
||||
auto_patrol = !auto_patrol
|
||||
bot_reset()
|
||||
if("remote")
|
||||
if("airplane")
|
||||
remote_disabled = !remote_disabled
|
||||
if("hack")
|
||||
if(!hasSiliconAccessInArea(usr) && !IsAdminGhost(usr))
|
||||
@@ -865,11 +860,11 @@ Pass a positive integer as an argument to override a bot's default speed.
|
||||
hacked = FALSE
|
||||
to_chat(usr, "<span class='notice'>[text_dehack]</span>")
|
||||
bot_reset()
|
||||
if("ejectpai")
|
||||
if("eject_pai")
|
||||
if(paicard && (!locked || hasSiliconAccessInArea(usr) || IsAdminGhost(usr)))
|
||||
to_chat(usr, "<span class='notice'>You eject [paicard] from [bot_name]</span>")
|
||||
ejectpai(usr)
|
||||
update_controls()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/update_icon_state()
|
||||
icon_state = "[initial(icon_state)][on]"
|
||||
@@ -897,33 +892,6 @@ Pass a positive integer as an argument to override a bot's default speed.
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/bot/proc/hack(mob/user)
|
||||
var/hack
|
||||
if(hasSiliconAccessInArea(user) || IsAdminGhost(user)) //Allows silicons or admins to toggle the emag status of a bot.
|
||||
hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]<BR>"
|
||||
hack += "Harm Prevention Safety System: <A href='?src=[REF(src)];operation=hack'>[emagged ? "<span class='bad'>DANGER</span>" : "Engaged"]</A><BR>"
|
||||
else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel and PDA control.
|
||||
hack += "Remote network control radio: <A href='?src=[REF(src)];operation=remote'>[remote_disabled ? "Disconnected" : "Connected"]</A><BR>"
|
||||
return hack
|
||||
|
||||
/mob/living/simple_animal/bot/proc/showpai(mob/user)
|
||||
var/eject = ""
|
||||
if((!locked || hasSiliconAccessInArea(usr) || IsAdminGhost(usr)))
|
||||
if(paicard || allow_pai)
|
||||
eject += "Personality card status: "
|
||||
if(paicard)
|
||||
if(client)
|
||||
eject += "<A href='?src=[REF(src)];operation=ejectpai'>Active</A>"
|
||||
else
|
||||
eject += "<A href='?src=[REF(src)];operation=ejectpai'>Inactive</A>"
|
||||
else if(!allow_pai || key)
|
||||
eject += "Unavailable"
|
||||
else
|
||||
eject += "Not inserted"
|
||||
eject += "<BR>"
|
||||
eject += "<BR>"
|
||||
return eject
|
||||
|
||||
/mob/living/simple_animal/bot/proc/insertpai(mob/user, obj/item/paicard/card)
|
||||
if(paicard)
|
||||
to_chat(user, "<span class='warning'>A [paicard] is already inserted!</span>")
|
||||
|
||||
@@ -443,37 +443,33 @@
|
||||
/obj/machinery/bot_core/cleanbot
|
||||
req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS)
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += text({"
|
||||
Status: <A href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</A><BR>
|
||||
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
|
||||
Maintenance panel panel is [open ? "opened" : "closed"]"})
|
||||
if(!locked || issilicon(user)|| IsAdminGhost(user))
|
||||
dat += "<BR>Clean Blood: <A href='?src=[REF(src)];operation=blood'>[blood ? "Yes" : "No"]</A>"
|
||||
dat += "<BR>Clean Trash: <A href='?src=[REF(src)];operation=trash'>[trash ? "Yes" : "No"]</A>"
|
||||
dat += "<BR>Clean Graffiti: <A href='?src=[REF(src)];operation=drawn'>[drawn ? "Yes" : "No"]</A>"
|
||||
dat += "<BR>Exterminate Pests: <A href='?src=[REF(src)];operation=pests'>[pests ? "Yes" : "No"]</A>"
|
||||
dat += "<BR><BR>Patrol Station: <A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A>"
|
||||
return dat
|
||||
// Variables sent to TGUI
|
||||
/mob/living/simple_animal/bot/cleanbot/ui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["operation"])
|
||||
switch(href_list["operation"])
|
||||
if("blood")
|
||||
blood = !blood
|
||||
if("pests")
|
||||
pests = !pests
|
||||
if("trash")
|
||||
trash = !trash
|
||||
if("drawn")
|
||||
drawn = !drawn
|
||||
get_targets()
|
||||
update_controls()
|
||||
if(!locked || issilicon(user)|| IsAdminGhost(user))
|
||||
data["custom_controls"]["clean_blood"] = blood
|
||||
data["custom_controls"]["clean_trash"] = trash
|
||||
data["custom_controls"]["clean_graffiti"] = drawn
|
||||
data["custom_controls"]["pest_control"] = pests
|
||||
return data
|
||||
|
||||
// Actions received from TGUI
|
||||
/mob/living/simple_animal/bot/cleanbot/ui_act(action, params)
|
||||
. = ..()
|
||||
if(. || !hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
return TRUE
|
||||
switch(action)
|
||||
if("clean_blood")
|
||||
blood = !blood
|
||||
if("pest_control")
|
||||
pests = !pests
|
||||
if("clean_trash")
|
||||
trash = !trash
|
||||
if("clean_graffiti")
|
||||
drawn = !drawn
|
||||
get_targets()
|
||||
return
|
||||
|
||||
/obj/machinery/bot_core/cleanbot/medbay
|
||||
req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS, ACCESS_MEDICAL)
|
||||
|
||||
@@ -96,64 +96,45 @@
|
||||
text_dehack = "You restore [name]'s combat inhibitor."
|
||||
text_dehack_fail = "[name] ignores your attempts to restrict him!"
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += text({"
|
||||
<TT><B>Security Unit v2.6 controls</B></TT><BR><BR>
|
||||
Status: []<BR>
|
||||
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
|
||||
Maintenance panel panel is [open ? "opened" : "closed"]<BR>"},
|
||||
|
||||
"<A href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</A>" )
|
||||
|
||||
// Variables sent to TGUI
|
||||
/mob/living/simple_animal/bot/ed209/ui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
if(!locked || hasSiliconAccessInArea(user)|| IsAdminGhost(user))
|
||||
if(!lasercolor)
|
||||
dat += text({"<BR>
|
||||
Arrest Unidentifiable Persons: []<BR>
|
||||
Arrest for Unauthorized Weapons: []<BR>
|
||||
Arrest for Warrant: []<BR>
|
||||
<BR>
|
||||
Operating Mode: []<BR>
|
||||
Report Arrests[]<BR>
|
||||
Auto Patrol[]"},
|
||||
data["custom_controls"]["check_id"] = idcheck
|
||||
data["custom_controls"]["check_weapons"] = weaponscheck
|
||||
data["custom_controls"]["check_warrants"] = check_records
|
||||
data["custom_controls"]["handcuff_targets"] = !arrest_type
|
||||
data["custom_controls"]["arrest_alert"] = declare_arrests
|
||||
return data
|
||||
|
||||
"<A href='?src=[REF(src)];operation=idcheck'>[idcheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=weaponscheck'>[weaponscheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=ignorerec'>[check_records ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=switchmode'>[arrest_type ? "Detain" : "Arrest"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=declarearrests'>[declare_arrests ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>" )
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/Topic(href, href_list)
|
||||
// Actions received from TGUI
|
||||
/mob/living/simple_animal/bot/ed209/ui_act(action, params)
|
||||
if(lasercolor && ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
if((lasercolor == "b") && (istype(H.wear_suit, /obj/item/clothing/suit/redtag)))//Opposing team cannot operate it
|
||||
return
|
||||
else if((lasercolor == "r") && (istype(H.wear_suit, /obj/item/clothing/suit/bluetag)))
|
||||
return
|
||||
if(..())
|
||||
return 1
|
||||
switch(lasercolor) //Opposing team cannot operate it
|
||||
if("b")
|
||||
if(istype(H.wear_suit, /obj/item/clothing/suit/redtag))
|
||||
return TRUE
|
||||
if("r")
|
||||
if(istype(H.wear_suit, /obj/item/clothing/suit/bluetag))
|
||||
return TRUE
|
||||
. = ..()
|
||||
if(. || !hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
return TRUE
|
||||
|
||||
switch(href_list["operation"])
|
||||
if("idcheck")
|
||||
switch(action)
|
||||
if("check_id")
|
||||
idcheck = !idcheck
|
||||
update_controls()
|
||||
if("weaponscheck")
|
||||
if("check_weapons")
|
||||
weaponscheck = !weaponscheck
|
||||
update_controls()
|
||||
if("ignorerec")
|
||||
if("check_warrants")
|
||||
check_records = !check_records
|
||||
update_controls()
|
||||
if("switchmode")
|
||||
if("handcuff_targets")
|
||||
arrest_type = !arrest_type
|
||||
update_controls()
|
||||
if("declarearrests")
|
||||
if("arrest_alert")
|
||||
declare_arrests = !declare_arrests
|
||||
update_controls()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/judgement_criteria()
|
||||
var/final = FALSE
|
||||
|
||||
@@ -102,23 +102,6 @@
|
||||
text_dehack = "You detect errors in [name] and reset his programming."
|
||||
text_dehack_fail = "[name] is not responding to reset commands!"
|
||||
|
||||
/mob/living/simple_animal/bot/firebot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += "<TT><B>Mobile Fire Extinguisher v1.0</B></TT><BR><BR>"
|
||||
dat += "Status: <A href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</A><BR>"
|
||||
dat += "Maintenance panel panel is [open ? "opened" : "closed"]<BR>"
|
||||
|
||||
dat += "Behaviour controls are [locked ? "locked" : "unlocked"]<BR>"
|
||||
if(!locked || hasSiliconAccessInArea(user) || IsAdminGhost(user))
|
||||
dat += "Extinguish Fires: <A href='?src=[REF(src)];operation=extinguish_fires'>[extinguish_fires ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Extinguish People: <A href='?src=[REF(src)];operation=extinguish_people'>[extinguish_people ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Patrol Station: <A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Stationary Mode: <a href='?src=[REF(src)];operation=stationary_mode'>[stationary_mode ? "Yes" : "No"]</a><br>"
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/firebot/emag_act(mob/user)
|
||||
. = ..()
|
||||
if(emagged == 1)
|
||||
@@ -138,20 +121,29 @@
|
||||
internal_ext.max_water = INFINITY
|
||||
internal_ext.refill()
|
||||
|
||||
/mob/living/simple_animal/bot/firebot/Topic(href, href_list)
|
||||
if(..())
|
||||
return TRUE
|
||||
// Variables sent to TGUI
|
||||
/mob/living/simple_animal/bot/firebot/ui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
if(!locked || issilicon(user) || IsAdminGhost(user))
|
||||
data["custom_controls"]["extinguish_fires"] = extinguish_fires
|
||||
data["custom_controls"]["extinguish_people"] = extinguish_people
|
||||
data["custom_controls"]["stationary_mode"] = stationary_mode
|
||||
return data
|
||||
|
||||
switch(href_list["operation"])
|
||||
// Actions received from TGUI
|
||||
/mob/living/simple_animal/bot/firebot/ui_act(action, params)
|
||||
. = ..()
|
||||
if(. || !hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
return TRUE
|
||||
switch(action)
|
||||
if("extinguish_fires")
|
||||
extinguish_fires = !extinguish_fires
|
||||
if("extinguish_people")
|
||||
extinguish_people = !extinguish_people
|
||||
if("stationary_mode")
|
||||
stationary_mode = !stationary_mode
|
||||
|
||||
update_controls()
|
||||
update_icon()
|
||||
update_appearance()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/firebot/proc/is_burning(atom/target)
|
||||
if(ismob(target))
|
||||
|
||||
@@ -71,36 +71,6 @@
|
||||
text_dehack = "You detect errors in [name] and reset his programming."
|
||||
text_dehack_fail = "[name] is not responding to reset commands!"
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += "<TT><B>Floor Repairer Controls v1.1</B></TT><BR><BR>"
|
||||
dat += "Status: <A href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</A><BR>"
|
||||
dat += "Maintenance panel panel is [open ? "opened" : "closed"]<BR>"
|
||||
dat += "Special tiles: "
|
||||
if(specialtiles)
|
||||
dat += "<A href='?src=[REF(src)];operation=eject'>Loaded \[[specialtiles]/[maxtiles]\]</a><BR>"
|
||||
else
|
||||
dat += "None Loaded<BR>"
|
||||
|
||||
dat += "Behaviour controls are [locked ? "locked" : "unlocked"]<BR>"
|
||||
if(!locked || hasSiliconAccessInArea(user) || IsAdminGhost(user))
|
||||
dat += "Add tiles to new hull plating: <A href='?src=[REF(src)];operation=autotile'>[autotile ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Place floor tiles: <A href='?src=[REF(src)];operation=place'>[placetiles ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Replace existing floor tiles with custom tiles: <A href='?src=[REF(src)];operation=replace'>[replacetiles ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Repair damaged tiles and platings: <A href='?src=[REF(src)];operation=fix'>[fixfloors ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Traction Magnets: <A href='?src=[REF(src)];operation=anchor'>[anchored ? "Engaged" : "Disengaged"]</A><BR>"
|
||||
dat += "Patrol Station: <A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A><BR>"
|
||||
var/bmode
|
||||
if(targetdirection)
|
||||
bmode = dir2text(targetdirection)
|
||||
else
|
||||
bmode = "disabled"
|
||||
dat += "Line Mode : <A href='?src=[REF(src)];operation=linemode'>[bmode]</A><BR>"
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/attackby(obj/item/W , mob/user, params)
|
||||
if(istype(W, /obj/item/stack/tile/plasteel))
|
||||
to_chat(user, "<span class='notice'>The floorbot can produce normal tiles itself.</span>")
|
||||
@@ -161,26 +131,44 @@
|
||||
if(user)
|
||||
to_chat(user, "<span class='danger'>[src] buzzes and beeps.</span>")
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
// Variables sent to TGUI
|
||||
/mob/living/simple_animal/bot/floorbot/ui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
if(!locked || issilicon(user) || IsAdminGhost(user))
|
||||
data["custom_controls"]["tile_hull"] = autotile
|
||||
data["custom_controls"]["place_tiles"] = placetiles
|
||||
data["custom_controls"]["place_custom"] = replacetiles
|
||||
data["custom_controls"]["repair_damage"] = fixfloors
|
||||
data["custom_controls"]["traction_magnets"] = anchored
|
||||
data["custom_controls"]["tile_stack"] = 0
|
||||
data["custom_controls"]["line_mode"] = FALSE
|
||||
if(specialtiles)
|
||||
data["custom_controls"]["tile_stack"] = specialtiles
|
||||
if(targetdirection)
|
||||
data["custom_controls"]["line_mode"] = dir2text(targetdirection)
|
||||
return data
|
||||
|
||||
switch(href_list["operation"])
|
||||
if("replace")
|
||||
// Actions received from TGUI
|
||||
/mob/living/simple_animal/bot/floorbot/ui_act(action, params)
|
||||
. = ..()
|
||||
if(. || !hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
return TRUE
|
||||
switch(action)
|
||||
if("place_custom")
|
||||
replacetiles = !replacetiles
|
||||
if("place")
|
||||
if("place_tiles")
|
||||
placetiles = !placetiles
|
||||
if("fix")
|
||||
if("repair_damage")
|
||||
fixfloors = !fixfloors
|
||||
if("autotile")
|
||||
if("tile_hull")
|
||||
autotile = !autotile
|
||||
if("anchor")
|
||||
if("traction_magnets")
|
||||
anchored = !anchored
|
||||
if("eject")
|
||||
if("eject_tiles")
|
||||
if(specialtiles && tiletype != null)
|
||||
empty_tiles()
|
||||
|
||||
if("linemode")
|
||||
if("line_mode")
|
||||
var/setdir = input("Select construction direction:") as null|anything in list("north","east","south","west","disable")
|
||||
switch(setdir)
|
||||
if("north")
|
||||
@@ -193,7 +181,7 @@
|
||||
targetdirection = 8
|
||||
if("disable")
|
||||
targetdirection = null
|
||||
update_controls()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/proc/empty_tiles()
|
||||
new tiletype(drop_location(), specialtiles)
|
||||
|
||||
@@ -78,24 +78,6 @@
|
||||
text_dehack = "You reboot [name] and restore the sound control system."
|
||||
text_dehack_fail = "[name] refuses to accept your authority!"
|
||||
|
||||
/mob/living/simple_animal/bot/honkbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += text({"
|
||||
<TT><B>Honkomatic Bike Horn Unit v1.0.7 controls</B></TT><BR><BR>
|
||||
Status: []<BR>
|
||||
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
|
||||
Maintenance panel panel is [open ? "opened" : "closed"]"},
|
||||
|
||||
"<A href='?src=[REF(src)];power=[TRUE]'>[on ? "On" : "Off"]</A>" )
|
||||
|
||||
if(!locked || hasSiliconAccessInArea(user) || IsAdminGhost(user))
|
||||
dat += text({"<BR> Auto Patrol: []"},
|
||||
|
||||
"<A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>" )
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/honkbot/proc/judgement_criteria()
|
||||
var/final = NONE
|
||||
if(check_records)
|
||||
|
||||
@@ -155,87 +155,63 @@
|
||||
/mob/living/simple_animal/bot/medbot/attack_paw(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += "<TT><B>Medical Unit Controls v1.1</B></TT><BR><BR>"
|
||||
dat += "Status: <A href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</A><BR>"
|
||||
dat += "Maintenance panel panel is [open ? "opened" : "closed"]<BR>"
|
||||
dat += "Beaker: "
|
||||
// Variables sent to TGUI
|
||||
/mob/living/simple_animal/bot/medbot/ui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
if(reagent_glass)
|
||||
dat += "<A href='?src=[REF(src)];eject=1'>Loaded \[[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]\]</a>"
|
||||
else
|
||||
dat += "None Loaded"
|
||||
dat += "<br>Behaviour controls are [locked ? "locked" : "unlocked"]<hr>"
|
||||
data["custom_controls"]["beaker"] = reagent_glass
|
||||
data["custom_contrlos"]["reagents"] = "[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]"
|
||||
if(!locked || hasSiliconAccessInArea(user) || IsAdminGhost(user))
|
||||
dat += "<TT>Healing Threshold: "
|
||||
dat += "<a href='?src=[REF(src)];adj_threshold=-10'>--</a> "
|
||||
dat += "<a href='?src=[REF(src)];adj_threshold=-5'>-</a> "
|
||||
dat += "[heal_threshold] "
|
||||
dat += "<a href='?src=[REF(src)];adj_threshold=5'>+</a> "
|
||||
dat += "<a href='?src=[REF(src)];adj_threshold=10'>++</a>"
|
||||
dat += "</TT><br>"
|
||||
data["custom_controls"]["injection_amount"] = injection_amount
|
||||
data["custom_controls"]["use_beaker"] = use_beaker
|
||||
data["custom_controls"]["treat_virus"] = treat_virus
|
||||
data["custom_controls"]["heal_threshold"] = heal_threshold
|
||||
data["custom_controls"]["speaker"] = !shut_up
|
||||
data["custom_controls"]["crit_alerts"] = declare_crit
|
||||
data["custom_controls"]["stationary_mode"] = stationary_mode
|
||||
return data
|
||||
|
||||
dat += "<TT>Injection Level: "
|
||||
dat += "<a href='?src=[REF(src)];adj_inject=-5'>-</a> "
|
||||
dat += "[injection_amount] "
|
||||
dat += "<a href='?src=[REF(src)];adj_inject=5'>+</a> "
|
||||
dat += "</TT><br>"
|
||||
// Actions received from TGUI
|
||||
/mob/living/simple_animal/bot/medbot/ui_act(action, params)
|
||||
. = ..()
|
||||
if(. || !hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
return TRUE
|
||||
switch(action)
|
||||
if("heal_threshold")
|
||||
var/adjust_num = round(text2num(params["threshold"]))
|
||||
heal_threshold = adjust_num
|
||||
if(heal_threshold < 5)
|
||||
heal_threshold = 5
|
||||
if(heal_threshold > 75)
|
||||
heal_threshold = 75
|
||||
|
||||
dat += "Reagent Source: "
|
||||
dat += "<a href='?src=[REF(src)];use_beaker=1'>[use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]</a><br>"
|
||||
if("injection_amount")
|
||||
var/adjust_num = round(text2num(params["amount"]))
|
||||
injection_amount = adjust_num
|
||||
if(injection_amount < 1)
|
||||
injection_amount = 1
|
||||
if(injection_amount > 15)
|
||||
injection_amount = 15
|
||||
|
||||
dat += "Treat Viral Infections: <a href='?src=[REF(src)];virus=1'>[treat_virus ? "Yes" : "No"]</a><br>"
|
||||
dat += "The speaker switch is [shut_up ? "off" : "on"]. <a href='?src=[REF(src)];togglevoice=[1]'>Toggle</a><br>"
|
||||
dat += "Critical Patient Alerts: <a href='?src=[REF(src)];critalerts=1'>[declare_crit ? "Yes" : "No"]</a><br>"
|
||||
dat += "Patrol Station: <a href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "Yes" : "No"]</a><br>"
|
||||
dat += "Stationary Mode: <a href='?src=[REF(src)];stationary=1'>[stationary_mode ? "Yes" : "No"]</a><br>"
|
||||
if("use_beaker")
|
||||
use_beaker = !use_beaker
|
||||
|
||||
return dat
|
||||
if("eject")
|
||||
if(!isnull(reagent_glass))
|
||||
reagent_glass.forceMove(drop_location())
|
||||
reagent_glass = null
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if("speaker")
|
||||
shut_up = !shut_up
|
||||
if("crit_alerts")
|
||||
declare_crit = !declare_crit
|
||||
if("stationary_mode")
|
||||
stationary_mode = !stationary_mode
|
||||
path = list()
|
||||
update_appearance()
|
||||
|
||||
if(href_list["adj_threshold"])
|
||||
var/adjust_num = text2num(href_list["adj_threshold"])
|
||||
heal_threshold += adjust_num
|
||||
if(heal_threshold < 5)
|
||||
heal_threshold = 5
|
||||
if(heal_threshold > 75)
|
||||
heal_threshold = 75
|
||||
|
||||
else if(href_list["adj_inject"])
|
||||
var/adjust_num = text2num(href_list["adj_inject"])
|
||||
injection_amount += adjust_num
|
||||
if(injection_amount < 5)
|
||||
injection_amount = 5
|
||||
if(injection_amount > 15)
|
||||
injection_amount = 15
|
||||
|
||||
else if(href_list["use_beaker"])
|
||||
use_beaker = !use_beaker
|
||||
|
||||
else if(href_list["eject"] && (!isnull(reagent_glass)))
|
||||
reagent_glass.forceMove(drop_location())
|
||||
reagent_glass = null
|
||||
|
||||
else if(href_list["togglevoice"])
|
||||
shut_up = !shut_up
|
||||
|
||||
else if(href_list["critalerts"])
|
||||
declare_crit = !declare_crit
|
||||
|
||||
else if(href_list["stationary"])
|
||||
stationary_mode = !stationary_mode
|
||||
path = list()
|
||||
update_icon()
|
||||
|
||||
else if(href_list["virus"])
|
||||
treat_virus = !treat_virus
|
||||
|
||||
update_controls()
|
||||
if("virus")
|
||||
treat_virus = !treat_virus
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/attackby(obj/item/W as obj, mob/user as mob, params)
|
||||
@@ -251,7 +227,6 @@
|
||||
|
||||
reagent_glass = W
|
||||
to_chat(user, "<span class='notice'>You insert [W].</span>")
|
||||
show_controls(user)
|
||||
|
||||
else if(istype(W, /obj/item/reagent_containers/syringe/piercing))
|
||||
if(bot_core.allowed(user) && open && !(upgrades & UPGRADE_MEDICAL_PIERERCING))
|
||||
|
||||
@@ -276,56 +276,6 @@
|
||||
if("ejectpai")
|
||||
ejectpairemote(user)
|
||||
|
||||
// TODO: remove this; PDAs currently depend on it
|
||||
/mob/living/simple_animal/bot/mulebot/get_controls(mob/user)
|
||||
var/ai = hasSiliconAccessInArea(user)
|
||||
var/dat
|
||||
dat += "<h3>Multiple Utility Load Effector Mk. V</h3>"
|
||||
dat += "<b>ID:</b> [id]<BR>"
|
||||
dat += "<b>Power:</b> [on ? "On" : "Off"]<BR>"
|
||||
dat += "<h3>Status</h3>"
|
||||
dat += "<div class='statusDisplay'>"
|
||||
switch(mode)
|
||||
if(BOT_IDLE)
|
||||
dat += "<span class='good'>Ready</span>"
|
||||
if(BOT_DELIVER)
|
||||
dat += "<span class='good'>[mode_name[BOT_DELIVER]]</span>"
|
||||
if(BOT_GO_HOME)
|
||||
dat += "<span class='good'>[mode_name[BOT_GO_HOME]]</span>"
|
||||
if(BOT_BLOCKED)
|
||||
dat += "<span class='average'>[mode_name[BOT_BLOCKED]]</span>"
|
||||
if(BOT_NAV,BOT_WAIT_FOR_NAV)
|
||||
dat += "<span class='average'>[mode_name[BOT_NAV]]</span>"
|
||||
if(BOT_NO_ROUTE)
|
||||
dat += "<span class='bad'>[mode_name[BOT_NO_ROUTE]]</span>"
|
||||
dat += "</div>"
|
||||
|
||||
dat += "<b>Current Load:</b> [load ? load.name : "<i>none</i>"]<BR>"
|
||||
dat += "<b>Destination:</b> [!destination ? "<i>none</i>" : destination]<BR>"
|
||||
dat += "<b>Power level:</b> [cell ? cell.percent() : 0]%"
|
||||
|
||||
if(locked && !ai && !IsAdminGhost(user))
|
||||
dat += " <br /><div class='notice'>Controls are locked</div><A href='byond://?src=[REF(src)];op=unlock'>Unlock Controls</A>"
|
||||
else
|
||||
dat += " <br /><div class='notice'>Controls are unlocked</div><A href='byond://?src=[REF(src)];op=lock'>Lock Controls</A><BR><BR>"
|
||||
|
||||
dat += "<A href='byond://?src=[REF(src)];op=power'>Toggle Power</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=stop'>Stop</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=go'>Proceed</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=home'>Return to Home</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=destination'>Set Destination</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=setid'>Set Bot ID</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=sethome'>Set Home</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=autoret'>Toggle Auto Return Home</A> ([auto_return ? "On":"Off"])<BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=autopick'>Toggle Auto Pickup Crate</A> ([auto_pickup ? "On":"Off"])<BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];op=report'>Toggle Delivery Reporting</A> ([report_delivery ? "On" : "Off"])<BR>"
|
||||
if(load)
|
||||
dat += "<A href='byond://?src=[REF(src)];op=unload'>Unload Now</A><BR>"
|
||||
dat += "<div class='notice'>The maintenance hatch is closed.</div>"
|
||||
|
||||
return dat
|
||||
|
||||
|
||||
// returns true if the bot has power
|
||||
/mob/living/simple_animal/bot/mulebot/proc/has_power()
|
||||
return !open && cell && cell.charge > 0 && (!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
|
||||
|
||||
@@ -209,57 +209,34 @@
|
||||
text_dehack = "You reboot [name] and restore the target identification."
|
||||
text_dehack_fail = "[name] refuses to accept your authority!"
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += text({"
|
||||
<TT><B>Securitron v1.6 controls</B></TT><BR><BR>
|
||||
Status: []<BR>
|
||||
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
|
||||
Maintenance panel panel is [open ? "opened" : "closed"]"},
|
||||
|
||||
"<A href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</A>" )
|
||||
|
||||
// Variables sent to TGUI
|
||||
/mob/living/simple_animal/bot/secbot/ui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
if(!locked || hasSiliconAccessInArea(user) || IsAdminGhost(user))
|
||||
dat += text({"<BR>
|
||||
Arrest Unidentifiable Persons: []<BR>
|
||||
Arrest for Unauthorized Weapons: []<BR>
|
||||
Arrest for Warrant: []<BR>
|
||||
Operating Mode: []<BR>
|
||||
Report Arrests[]<BR>
|
||||
Auto Patrol: []"},
|
||||
data["custom_controls"]["check_id"] = idcheck
|
||||
data["custom_controls"]["check_weapons"] = weaponscheck
|
||||
data["custom_controls"]["check_warrants"] = check_records
|
||||
data["custom_controls"]["handcuff_targets"] = !arrest_type
|
||||
data["custom_controls"]["arrest_alert"] = declare_arrests
|
||||
return data
|
||||
|
||||
"<A href='?src=[REF(src)];operation=idcheck'>[idcheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=weaponscheck'>[weaponscheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=ignorerec'>[check_records ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=switchmode'>[arrest_type ? "Detain" : "Arrest"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=declarearrests'>[declare_arrests ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>" )
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(!hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
// Actions received from TGUI
|
||||
/mob/living/simple_animal/bot/secbot/ui_act(action, params)
|
||||
. = ..()
|
||||
if(. || !hasSiliconAccessInArea(usr) && !IsAdminGhost(usr) && !(bot_core.allowed(usr) || !locked))
|
||||
return TRUE
|
||||
switch(href_list["operation"])
|
||||
if("idcheck")
|
||||
switch(action)
|
||||
if("check_id")
|
||||
idcheck = !idcheck
|
||||
update_controls()
|
||||
if("weaponscheck")
|
||||
if("check_weapons")
|
||||
weaponscheck = !weaponscheck
|
||||
update_controls()
|
||||
if("ignorerec")
|
||||
if("check_warrants")
|
||||
check_records = !check_records
|
||||
update_controls()
|
||||
if("switchmode")
|
||||
if("handcuff_targets")
|
||||
arrest_type = !arrest_type
|
||||
update_controls()
|
||||
if("declarearrests")
|
||||
if("arrest_alert")
|
||||
declare_arrests = !declare_arrests
|
||||
update_controls()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/proc/retaliate(mob/living/carbon/human/H)
|
||||
var/judgement_criteria = judgement_criteria()
|
||||
|
||||
@@ -308,10 +308,15 @@
|
||||
adjustHealth(unsuitable_atmos_damage)
|
||||
|
||||
/mob/living/simple_animal/gib(no_brain, no_organs, no_bodyparts, datum/explosion/was_explosion)
|
||||
if(butcher_results)
|
||||
if(butcher_results || guaranteed_butcher_results)
|
||||
var/list/butcher = list()
|
||||
if(butcher_results)
|
||||
butcher += butcher_results
|
||||
if(guaranteed_butcher_results)
|
||||
butcher += guaranteed_butcher_results
|
||||
var/atom/Tsec = drop_location()
|
||||
for(var/path in butcher_results)
|
||||
for(var/i in 1 to butcher_results[path])
|
||||
for(var/path in butcher)
|
||||
for(var/i in 1 to butcher[path])
|
||||
new path(Tsec)
|
||||
..()
|
||||
|
||||
|
||||
@@ -495,7 +495,11 @@
|
||||
|
||||
/datum/status_effect/stabilized/orange/tick()
|
||||
var/body_temperature_difference = BODYTEMP_NORMAL - owner.bodytemperature
|
||||
owner.adjust_bodytemperature(min(5,body_temperature_difference))
|
||||
var/cooling_cap = -5
|
||||
if(HAS_TRAIT(owner, TRAIT_ROBOTIC_ORGANISM))
|
||||
cooling_cap *= 0.5 //Only cools by half as much (which is 5 per life tick since this ticks twice as much as life) so it isn't true spaceproofness..
|
||||
body_temperature_difference += SYNTH_COLD_OFFSET //.. But also cools towards a cold temp, provided there is nothing that counters it.
|
||||
owner.adjust_bodytemperature(clamp(body_temperature_difference, cooling_cap, 5))
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/stabilized/purple
|
||||
|
||||
@@ -12,14 +12,29 @@
|
||||
max_integrity = 200
|
||||
var/faction = list("ashwalker")
|
||||
var/meat_counter = 6
|
||||
var/datum/team/ashwalkers/ashies
|
||||
var/datum/linked_objective
|
||||
|
||||
/obj/structure/lavaland/ash_walker/Initialize()
|
||||
/obj/structure/lavaland/ash_walker/Initialize(mapload)
|
||||
.=..()
|
||||
ashies = new /datum/team/ashwalkers()
|
||||
var/datum/objective/protect_object/objective = new
|
||||
objective.set_target(src)
|
||||
linked_objective = objective
|
||||
ashies.objectives += objective
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
|
||||
/obj/structure/lavaland/ash_walker/Destroy()
|
||||
ashies.objectives -= linked_objective
|
||||
ashies = null
|
||||
QDEL_NULL(linked_objective)
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/lavaland/ash_walker/deconstruct(disassembled)
|
||||
new /obj/item/assembly/signaler/anomaly (get_step(loc, pick(GLOB.alldirs)))
|
||||
new /obj/effect/collapse(loc)
|
||||
return ..()
|
||||
|
||||
/obj/structure/lavaland/ash_walker/process()
|
||||
consume()
|
||||
@@ -28,20 +43,64 @@
|
||||
/obj/structure/lavaland/ash_walker/proc/consume()
|
||||
for(var/mob/living/H in view(src, 1)) //Only for corpse right next to/on same tile
|
||||
if(H.stat)
|
||||
visible_message("<span class='warning'>Serrated tendrils eagerly pull [H] to [src], tearing the body apart as its blood seeps over the eggs.</span>")
|
||||
playsound(get_turf(src),'sound/magic/demon_consume.ogg', 100, 1)
|
||||
for(var/obj/item/W in H)
|
||||
if(!H.dropItemToGround(W))
|
||||
qdel(W)
|
||||
if(issilicon(H)) //no advantage to sacrificing borgs...
|
||||
H.gib()
|
||||
visible_message(span_notice("Serrated tendrils eagerly pull [H] apart, but find nothing of interest."))
|
||||
return
|
||||
|
||||
if(H.mind?.has_antag_datum(/datum/antagonist/ashwalker) && (H.key || H.get_ghost(FALSE, TRUE))) //special interactions for dead lava lizards with ghosts attached
|
||||
visible_message(span_warning("Serrated tendrils carefully pull [H] to [src], absorbing the body and creating it anew."))
|
||||
var/datum/mind/deadmind
|
||||
if(H.key)
|
||||
deadmind = H
|
||||
else
|
||||
deadmind = H.get_ghost(FALSE, TRUE)
|
||||
to_chat(deadmind, "Your body has been returned to the nest. You are being remade anew, and will awaken shortly. </br><b>Your memories will remain intact in your new body, as your soul is being salvaged</b>")
|
||||
SEND_SOUND(deadmind, sound('sound/magic/enter_blood.ogg',volume=100))
|
||||
addtimer(CALLBACK(src, .proc/remake_walker, H.mind, H.real_name), 20 SECONDS)
|
||||
new /obj/effect/gibspawner/generic(get_turf(H))
|
||||
qdel(H)
|
||||
return
|
||||
|
||||
if(ismegafauna(H))
|
||||
meat_counter += 20
|
||||
else
|
||||
meat_counter++
|
||||
visible_message(span_warning("Serrated tendrils eagerly pull [H] to [src], tearing the body apart as its blood seeps over the eggs."))
|
||||
playsound(get_turf(src),'sound/magic/demon_consume.ogg', 100, TRUE)
|
||||
var/deliverykey = H.fingerprintslast //key of whoever brought the body
|
||||
var/mob/living/deliverymob = get_mob_by_key(deliverykey) //mob of said key
|
||||
//there is a 40% chance that the Lava Lizard unlocks their respawn with each sacrifice
|
||||
if(deliverymob && (deliverymob.mind?.has_antag_datum(/datum/antagonist/ashwalker)) && (deliverykey in ashies.players_spawned) && (prob(40)))
|
||||
to_chat(deliverymob, span_warning("<b>The Necropolis is pleased with your sacrifice. You feel confident your existence after death is secure.</b>"))
|
||||
ashies.players_spawned -= deliverykey
|
||||
H.gib()
|
||||
obj_integrity = min(obj_integrity + max_integrity*0.05,max_integrity)//restores 5% hp of tendril
|
||||
for(var/mob/living/L in view(src, 5))
|
||||
if(L.mind?.has_antag_datum(/datum/antagonist/ashwalker))
|
||||
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "headspear", /datum/mood_event/sacrifice_good)
|
||||
else
|
||||
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "headspear", /datum/mood_event/sacrifice_bad)
|
||||
|
||||
/obj/structure/lavaland/ash_walker/proc/remake_walker(datum/mind/oldmind, oldname)
|
||||
var/mob/living/carbon/human/M = new /mob/living/carbon/human(get_step(loc, pick(GLOB.alldirs)))
|
||||
M.set_species(/datum/species/lizard/ashwalker)
|
||||
M.real_name = oldname
|
||||
M.underwear = "Nude"
|
||||
M.undershirt = "Nude"
|
||||
M.socks = "Nude"
|
||||
M.update_body()
|
||||
M.remove_language(/datum/language/common)
|
||||
oldmind.transfer_to(M)
|
||||
M.mind.grab_ghost()
|
||||
to_chat(M, "<b>You have been pulled back from beyond the grave, with a new body and renewed purpose. Glory to the Necropolis!</b>")
|
||||
playsound(get_turf(M),'sound/magic/exit_blood.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/lavaland/ash_walker/proc/spawn_mob()
|
||||
if(meat_counter >= ASH_WALKER_SPAWN_THRESHOLD)
|
||||
new /obj/effect/mob_spawn/human/ash_walker(get_step(loc, pick(GLOB.alldirs)))
|
||||
new /obj/effect/mob_spawn/human/ash_walker(get_step(loc, pick(GLOB.alldirs)), ashies)
|
||||
visible_message("<span class='danger'>One of the eggs swells to an unnatural size and tumbles free. It's ready to hatch!</span>")
|
||||
meat_counter -= ASH_WALKER_SPAWN_THRESHOLD
|
||||
|
||||
@@ -35,67 +35,63 @@
|
||||
. = ..()
|
||||
. += distance_multiplier < 1 ? "Bluespace shortcut module installed. Route is [distance_multiplier]x the original length." : ""
|
||||
|
||||
/obj/machinery/computer/custom_shuttle/ui_interact(mob/user)
|
||||
/obj/machinery/computer/custom_shuttle/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "CustomShuttleConsole", name)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/computer/custom_shuttle/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
var/list/options = params2list(possible_destinations)
|
||||
var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
|
||||
var/dat = "[M ? "Current Location : [M.getStatusText()]" : "Shuttle link required."]<br><br>"
|
||||
data["docked_location"] = M ? M.get_status_text_tgui() : null
|
||||
if(M)
|
||||
dat += "<A href='?src=[REF(src)];calculate=1'>Run Flight Calculations</A><br>"
|
||||
dat += "<b>Shuttle Data</b><hr>"
|
||||
dat += "Shuttle Mass: [calculated_mass/10]tons<br>"
|
||||
dat += "Engine Force: [calculated_dforce]kN ([calculated_engine_count] engines)<br>"
|
||||
dat += "Sublight Speed: [calculated_speed]ms<sup>-1</sup><br>"
|
||||
dat += calculated_speed < 1 ? "<b>INSUFFICIENT ENGINE POWER</b><br>" : ""
|
||||
dat += calculated_non_operational_thrusters > 0 ? "<b>Warning: [calculated_non_operational_thrusters] thrusters offline.</b><br>" : ""
|
||||
dat += "Fuel Consumption: [calculated_consumption]units per distance<br>"
|
||||
dat += "Engine Cooldown: [calculated_cooldown]s<hr>"
|
||||
var/destination_found
|
||||
calculateStats(FALSE, 0, TRUE)
|
||||
data["ship_name"] = M.area_type ? M.area_type:name : "ERROR"
|
||||
data["shuttle_mass"] = calculated_mass/10
|
||||
data["engine_force"] = calculated_dforce
|
||||
data["engines"] = calculated_engine_count
|
||||
data["calculated_speed"] = calculated_speed
|
||||
data["damaged_engines"] = calculated_non_operational_thrusters
|
||||
data["calculated_consumption"] = calculated_consumption
|
||||
data["calculated_cooldown"] = calculated_cooldown
|
||||
data["locations"] = list()
|
||||
for(var/obj/docking_port/stationary/S in SSshuttle.stationary)
|
||||
if(!options.Find(S.id))
|
||||
continue
|
||||
if(!M.check_dock(S, silent=TRUE))
|
||||
if(!M.check_dock(S, silent = TRUE))
|
||||
continue
|
||||
if(calculated_speed == 0)
|
||||
break
|
||||
destination_found = TRUE
|
||||
var/dist = round(calculateDistance(S))
|
||||
dat += "<A href='?src=[REF(src)];setloc=[S.id]'>Target [S.name] (Dist: [dist] | Fuel Cost: [round(dist * calculated_consumption)] | Time: [round(dist / calculated_speed)])</A><br>"
|
||||
if(!destination_found)
|
||||
dat += "<B>No valid destinations</B><br>"
|
||||
dat += "<hr>[targetLocation ? "Target Location : [targetLocation]" : "No Target Location"]"
|
||||
dat += "<hr><A href='?src=[REF(src)];fly=1'>Initate Flight</A><br>"
|
||||
dat += "<A href='?src=[REF(user)];mach_close=computer'>Close</a>"
|
||||
var/list/location_data = list(
|
||||
id = S.id,
|
||||
name = S.name,
|
||||
dist = round(calculateDistance(S))
|
||||
)
|
||||
data["locations"] += list(location_data)
|
||||
data["destination"] = targetLocation
|
||||
return data
|
||||
|
||||
popup = new(user, "computer", M ? M.name : "shuttle", 350, 450)
|
||||
popup.set_content("<center>[dat]</center>")
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/custom_shuttle/Topic(href, href_list)
|
||||
if(..())
|
||||
/obj/machinery/computer/custom_shuttle/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
usr.set_machine(src)
|
||||
src.add_fingerprint(usr)
|
||||
if(!allowed(usr))
|
||||
to_chat(usr, "<span class='danger'>Access denied.</span>")
|
||||
return
|
||||
|
||||
if(href_list["calculate"])
|
||||
calculateStats()
|
||||
ui_interact(usr)
|
||||
return
|
||||
var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
|
||||
if(!M)
|
||||
to_chat(usr, "<span class='danger'>Shuttle Link Required.</span>")
|
||||
return
|
||||
if(M.launch_status == ENDGAME_LAUNCHED)
|
||||
return
|
||||
if(href_list["setloc"])
|
||||
SetTargetLocation(href_list["setloc"])
|
||||
ui_interact(usr)
|
||||
return
|
||||
else if(href_list["fly"])
|
||||
Fly()
|
||||
ui_interact(usr)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("setloc")
|
||||
SetTargetLocation(params["setloc"])
|
||||
if("fly")
|
||||
Fly()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/custom_shuttle/proc/calculateDistance(var/obj/docking_port/stationary/port)
|
||||
var/deltaX = port.x - x
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "LiteralMushroom"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Slimes can have wings now."
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "CoreFlare"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Humanity is... Ok. Blood is fuel. IPCs have wings."
|
||||
@@ -0,0 +1,7 @@
|
||||
author: "SakuraOran"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Ashwalkers can now heal, even from death, by being incapacitated (asleep/dead) near the tendril"
|
||||
- rscadd: "Added the ability for antags to have the \"protect object\" objective, which the ashwalkers now have to protect the tendril. This is determined by if the object is broken or not, and can be set to any object or structure that can reasonably be destroyed"
|
||||
- balance: "Ashwalkers get one life, and have a 40% chance to earn an additional life when they sacrifice to the tendril."
|
||||
- bugfix: "fixed mobs not dropping guaranteed_butcher_results when gibbed"
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "SandPoot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Adds a keybinding where you can cancel actions."
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "DeltaFire15"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "The Space Adaptation mutation when applied to Synthetics now nullifies the penalty for passive cooling if in low-pressure environments."
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "SandPoot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "You can once again see your previews for picking target bodyparts on your hud."
|
||||
@@ -0,0 +1,5 @@
|
||||
author: "DeltaFire15"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "Stabilized orange extracts can only cool by -5 degrees per tick base instead of by as much as the difference is."
|
||||
- balance: "Stabilized orange extracts only have halved cooling effects on Synthetics (2.5 per status tick / 5 per life), but also cool them towards ~-100°C instead of base bodytemp."
|
||||
@@ -0,0 +1,6 @@
|
||||
author: "SandPoot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "TGUI autolathe
|
||||
tweak: You can now input a custom amount to print(hard limit at 50 or maximum stack size)
|
||||
tweak: Can now search and change categories while the autolathe is busy, line up those designs!"
|
||||
@@ -0,0 +1,5 @@
|
||||
author: "SandPoot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "All of your favorite simple bot friends aboard the station now have a much better interface in TGUI"
|
||||
- qol: "You can now lock and unlock bots, including their maintenance hatches, via the UI."
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "SandPoot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Made a cool UI for the custom shuttles."
|
||||
@@ -1562,6 +1562,7 @@
|
||||
#include "code\modules\antagonists\abductor\machinery\dispenser.dm"
|
||||
#include "code\modules\antagonists\abductor\machinery\experiment.dm"
|
||||
#include "code\modules\antagonists\abductor\machinery\pad.dm"
|
||||
#include "code\modules\antagonists\ashwalker\ashwalker.dm"
|
||||
#include "code\modules\antagonists\blob\blob.dm"
|
||||
#include "code\modules\antagonists\blob\blob\blob_report.dm"
|
||||
#include "code\modules\antagonists\blob\blob\overmind.dm"
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, LabeledList, Section, ProgressBar, Flex, Box, Table, Collapsible, Input, Dimmer, Icon } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { capitalize } from "common/string";
|
||||
|
||||
export const Autolathe = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
// Extract `health` and `color` variables from the `data` object.
|
||||
const {
|
||||
materialtotal,
|
||||
materialsmax,
|
||||
materials = [],
|
||||
categories = [],
|
||||
designs = [],
|
||||
active,
|
||||
} = data;
|
||||
const [
|
||||
current_category,
|
||||
setCategory,
|
||||
] = useLocalState(context, 'current_category', "None");
|
||||
const filteredmaterials = materials.filter(material =>
|
||||
material.mineral_amount > 0);
|
||||
return (
|
||||
<Window
|
||||
width={600}
|
||||
height={600}>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Total Materials">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Total Materials">
|
||||
<ProgressBar
|
||||
value={materialtotal}
|
||||
minValue={0}
|
||||
maxValue={materialsmax}
|
||||
ranges={{
|
||||
"good": [materialsmax * 0.85, materialsmax],
|
||||
"average": [materialsmax * 0.25, materialsmax * 0.85],
|
||||
"bad": [0, materialsmax * 0.25],
|
||||
}}>
|
||||
{materialtotal + '/' + materialsmax + ' cm³'}
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item>
|
||||
{filteredmaterials.length > 0 && (
|
||||
<Collapsible title="Materials">
|
||||
<LabeledList>
|
||||
{filteredmaterials.map(filteredmaterial => (
|
||||
<LabeledList.Item
|
||||
key={filteredmaterial.id}
|
||||
label={capitalize(filteredmaterial.name)}>
|
||||
<ProgressBar
|
||||
style={{
|
||||
transform: 'scaleX(-1) scaleY(1)',
|
||||
}}
|
||||
value={materialsmax - filteredmaterial.mineral_amount}
|
||||
maxValue={materialsmax}
|
||||
color="black"
|
||||
backgroundColor={filteredmaterial.matcolour}>
|
||||
<div style={{ transform: 'scaleX(-1)' }}>{filteredmaterial.mineral_amount + ' cm³'}</div>
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
</Collapsible>)}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section
|
||||
title="Search">
|
||||
<Input fluid
|
||||
placeholder="Search Recipes..."
|
||||
selfClear
|
||||
onChange={(e, value) => {
|
||||
if (value.length) {
|
||||
act('search', {
|
||||
to_search: value,
|
||||
});
|
||||
setCategory('results for "' + value + '"');
|
||||
}
|
||||
}} />
|
||||
</Section>
|
||||
<Section title="Categories">
|
||||
<Box>
|
||||
{categories.map(category => (
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<Button
|
||||
selected={current_category === category}
|
||||
content={category}
|
||||
onClick={() => {
|
||||
act('category', {
|
||||
selectedCategory: category,
|
||||
});
|
||||
setCategory(category);
|
||||
}} />
|
||||
))}
|
||||
</Box>
|
||||
</Section>
|
||||
{current_category.toString() !== "None" && (
|
||||
<Section
|
||||
title={'Displaying ' + current_category.toString()}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="times"
|
||||
content="Close Category"
|
||||
onClick={() => {
|
||||
act('menu');
|
||||
setCategory("None");
|
||||
}} />
|
||||
)}>
|
||||
{active === 1 && (
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="cog" spin />
|
||||
{'Building items...'}
|
||||
</Dimmer>
|
||||
)}
|
||||
<Flex direction="row" wrap="nowrap">
|
||||
<Table>
|
||||
{designs.length
|
||||
&& (designs.map(design => (
|
||||
<Table.Row
|
||||
key={design.id}>
|
||||
<Flex.Item>
|
||||
<Button
|
||||
content={design.name}
|
||||
disabled={design.buildable}
|
||||
onClick={() => act('make', {
|
||||
id: design.id,
|
||||
multiplier: '1',
|
||||
})} />
|
||||
</Flex.Item>
|
||||
{design.sheet ? (
|
||||
<Table.Cell>
|
||||
<Flex.Item grow={1}>
|
||||
<Button
|
||||
icon="hammer"
|
||||
content="10"
|
||||
disabled={!design.mult10}
|
||||
onClick={() => act('make', {
|
||||
id: design.id,
|
||||
multiplier: '10',
|
||||
})} />
|
||||
<Button
|
||||
icon="hammer"
|
||||
content="25"
|
||||
disabled={!design.mult25}
|
||||
onClick={() => act('make', {
|
||||
id: design.id,
|
||||
multiplier: '25',
|
||||
})} />
|
||||
</Flex.Item>
|
||||
</Table.Cell>
|
||||
) : (
|
||||
<Table.Cell>
|
||||
<Flex.Item grow={3}>
|
||||
<Button
|
||||
icon="hammer"
|
||||
content="5"
|
||||
disabled={!design.mult5}
|
||||
onClick={() => act('make', {
|
||||
id: design.id,
|
||||
multiplier: '5',
|
||||
})} />
|
||||
<Button
|
||||
icon="hammer"
|
||||
content="10"
|
||||
disabled={!design.mult10}
|
||||
onClick={() => act('make', {
|
||||
id: design.id,
|
||||
multiplier: '10',
|
||||
})} />
|
||||
</Flex.Item>
|
||||
</Table.Cell>
|
||||
)}
|
||||
<Table.Cell>
|
||||
<Button.Input
|
||||
content={"[Max:" + design.maxmult + ']'}
|
||||
maxValue={design.maxmult}
|
||||
disabled={design.buildable}
|
||||
backgroundColor={design.buildable ? '#999999' : 'default'}
|
||||
onCommit={(e, value) => act('make', {
|
||||
id: design.id,
|
||||
multiplier: value,
|
||||
})} />
|
||||
</Table.Cell>
|
||||
{design.cost}
|
||||
</Table.Row>
|
||||
))) || (
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
{"No designs found."}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</Table>
|
||||
</Flex>
|
||||
</Section>
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
/* eslint-disable react/jsx-closing-tag-location */
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Flex, Icon, LabeledList, Modal, Section, Table, Tooltip } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const CustomShuttleConsole = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
docked_location,
|
||||
ship_name = "ERROR",
|
||||
shuttle_mass = 0,
|
||||
engine_force = 0,
|
||||
engines = 0,
|
||||
calculated_speed = 0,
|
||||
calculated_non_operational_thrusters = 0,
|
||||
calculated_consumption = 0,
|
||||
calculated_cooldown = 0,
|
||||
locations = [],
|
||||
destination = null,
|
||||
} = data;
|
||||
return (
|
||||
<Window
|
||||
width={385}
|
||||
height={475}>
|
||||
<Window.Content overflow="auto">
|
||||
<Section fluid fill grow={1}>
|
||||
{!docked_location ? (
|
||||
<Modal
|
||||
ml={1}
|
||||
mt={1}
|
||||
width={26}
|
||||
height={12}
|
||||
fontSize="28px"
|
||||
fontFamily="monospace"
|
||||
textAlign="center">
|
||||
<Flex>
|
||||
<Flex.Item mt={8} ml={3}>
|
||||
<Icon
|
||||
name="minus-circle" />
|
||||
</Flex.Item>
|
||||
<Flex.Item
|
||||
mt={5}
|
||||
ml={2}
|
||||
color="yellow">
|
||||
{"Shuttle Link Required."}
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Modal>
|
||||
) : (
|
||||
<>
|
||||
<font size="+3"><center>{ship_name}</center></font>
|
||||
<Section title="Info">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Current Location">
|
||||
{docked_location}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Shuttle Mass">
|
||||
{shuttle_mass / 10}ton{shuttle_mass !== 1 ? "s" : null}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Engine Force">
|
||||
{engine_force}Kn ({engines} engine{engines !== 1 ? "s" : null})
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Sublight speed">
|
||||
{calculated_speed}ms{<sup>-1</sup>} {calculated_speed < 1 ? <Tooltip content="INSUFFICIENT ENGINE POWER"><Icon name="exclamation-triangle" color="yellow" /></Tooltip> : null}
|
||||
</LabeledList.Item>
|
||||
{calculated_non_operational_thrusters.len
|
||||
? <LabeledList.Item label="Warning">
|
||||
{calculated_non_operational_thrusters} thruster{calculated_non_operational_thrusters !== 1 ? "s are" : " is"} not operational.
|
||||
</LabeledList.Item>
|
||||
: null}
|
||||
<LabeledList.Item label="Fuel Consumption">
|
||||
{calculated_consumption} unit{calculated_consumption !== 1 ? "s" : null} per travel
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Engine Cooldown">
|
||||
{calculated_cooldown}s
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Destinations">
|
||||
{locations.length===0 && (
|
||||
<Box
|
||||
mb={1.7}
|
||||
color="bad">
|
||||
No valid destinations
|
||||
</Box>
|
||||
) || (
|
||||
<Table>
|
||||
{locations.map(location => (
|
||||
<tr class="Table__row candystripe" key={location.id}>
|
||||
{location.name} <td>({location.dist}m)</td>
|
||||
<td>
|
||||
<Button
|
||||
icon="crosshairs"
|
||||
selected={location.id === destination}
|
||||
onClick={() => act("setloc", {
|
||||
setloc: location.id,
|
||||
})} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</Table>
|
||||
)}
|
||||
</Section>
|
||||
<Section>
|
||||
<Button
|
||||
content="Initiate Flight"
|
||||
icon="rocket"
|
||||
disabled={calculated_speed < 1 || !destination}
|
||||
onClick={() => act('fly')}
|
||||
fluid />
|
||||
</Section>
|
||||
</>
|
||||
)}
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const getLocationNameById = (locations, id) => {
|
||||
return locations?.find(location => location.id === id)?.name;
|
||||
};
|
||||
|
||||
const getLocationIdByName = (locations, name) => {
|
||||
return locations?.find(location => location.name === name)?.id;
|
||||
};
|
||||
|
||||
const STATUS_COLOR_KEYS = {
|
||||
"In Transit": "good",
|
||||
"Idle": "average",
|
||||
"Igniting": "average",
|
||||
"Recharging": "average",
|
||||
"Missing": "bad",
|
||||
"Unauthorized Access": "bad",
|
||||
"Locked": "bad",
|
||||
};
|
||||
@@ -0,0 +1,351 @@
|
||||
import { multiline } from '../../common/string';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, Icon, LabeledControls, NoticeBox, Section, Slider, Stack, Tooltip } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
type SimpleBotContext = {
|
||||
can_hack: number;
|
||||
locked: number;
|
||||
emagged: number;
|
||||
pai: Pai;
|
||||
settings: Settings;
|
||||
custom_controls: Controls;
|
||||
};
|
||||
|
||||
type Pai = {
|
||||
allow_pai: number;
|
||||
card_inserted: number;
|
||||
};
|
||||
|
||||
type Settings = {
|
||||
power: number;
|
||||
airplane_mode: number;
|
||||
maintenance_lock: number;
|
||||
patrol_station: number;
|
||||
};
|
||||
|
||||
type Controls = {
|
||||
[Control: string]: [Value: number];
|
||||
};
|
||||
|
||||
export const SimpleBot = (_, context) => {
|
||||
const { data } = useBackend<SimpleBotContext>(context);
|
||||
const { can_hack, locked } = data;
|
||||
const access = (!locked || can_hack);
|
||||
|
||||
return (
|
||||
<Window width={450} height={300}>
|
||||
<Window.Content>
|
||||
<Stack fill vertical>
|
||||
<Stack.Item>
|
||||
<Section title="Settings" buttons={<TabDisplay />}>
|
||||
{!access
|
||||
? (<NoticeBox>Locked!</NoticeBox>)
|
||||
: (<SettingsDisplay />)}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
{access && (
|
||||
<Stack.Item grow>
|
||||
<Section fill scrollable title="Controls">
|
||||
<ControlsDisplay />
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
)}
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
/** Creates a lock button at the top of the controls */
|
||||
const TabDisplay = (_, context) => {
|
||||
const { act, data } = useBackend<SimpleBotContext>(context);
|
||||
const { can_hack, locked, pai } = data;
|
||||
const { allow_pai } = pai;
|
||||
|
||||
return (
|
||||
<>
|
||||
{!!can_hack && <HackButton />}
|
||||
{!!allow_pai && <PaiButton />}
|
||||
<Button
|
||||
color="transparent"
|
||||
icon={locked ? 'lock' : 'lock-open'}
|
||||
onClick={() => act('lock')}
|
||||
selected={locked}
|
||||
tooltip={`${locked ? 'Unlock' : 'Lock'} the control panel.`}>
|
||||
Controls Lock
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/** If user is a bad silicon, they can press this button to hack the bot */
|
||||
const HackButton = (_, context) => {
|
||||
const { act, data } = useBackend<SimpleBotContext>(context);
|
||||
const { can_hack, emagged } = data;
|
||||
|
||||
return (
|
||||
<Button
|
||||
color="danger"
|
||||
disabled={!can_hack}
|
||||
icon={emagged ? 'bug' : 'lock'}
|
||||
onClick={() => act('hack')}
|
||||
selected={!emagged}
|
||||
tooltip={
|
||||
!emagged
|
||||
? 'Unlocks the safety protocols.'
|
||||
: 'Resets the bot operating system.'
|
||||
}>
|
||||
{emagged ? 'Malfunctional' : 'Safety Lock'}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
/** Creates a button indicating PAI status and offers the eject action */
|
||||
const PaiButton = (_, context) => {
|
||||
const { act, data } = useBackend<SimpleBotContext>(context);
|
||||
const { card_inserted } = data.pai;
|
||||
|
||||
if (!card_inserted) {
|
||||
return (
|
||||
<Button
|
||||
color="transparent"
|
||||
icon="robot"
|
||||
tooltip={multiline`Insert an active PAI card to control this device.`}>
|
||||
No PAI Inserted
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Button
|
||||
disabled={!card_inserted}
|
||||
icon="eject"
|
||||
onClick={() => act('eject_pai')}
|
||||
tooltip={multiline`Ejects the current PAI.`}>
|
||||
Eject PAI
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** Displays the bot's standard settings: Power, patrol, etc. */
|
||||
const SettingsDisplay = (_, context) => {
|
||||
const { act, data } = useBackend<SimpleBotContext>(context);
|
||||
const { settings } = data;
|
||||
const { airplane_mode, patrol_station, power, maintenance_lock } = settings;
|
||||
|
||||
return (
|
||||
<LabeledControls>
|
||||
<LabeledControls.Item label="Power">
|
||||
<Tooltip content={`Powers ${power ? 'off' : 'on'} the bot.`}>
|
||||
<Icon
|
||||
size={2}
|
||||
name="power-off"
|
||||
color={power ? 'good' : 'gray'}
|
||||
onClick={() => act('power')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</LabeledControls.Item>
|
||||
<LabeledControls.Item label="Airplane Mode">
|
||||
<Tooltip
|
||||
content={`${
|
||||
!airplane_mode ? 'Disables' : 'Enables'
|
||||
} remote access via console.`}>
|
||||
<Icon
|
||||
size={2}
|
||||
name="plane"
|
||||
color={airplane_mode ? 'yellow' : 'gray'}
|
||||
onClick={() => act('airplane')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</LabeledControls.Item>
|
||||
<LabeledControls.Item label="Patrol Station">
|
||||
<Tooltip
|
||||
content={`${
|
||||
patrol_station ? 'Disables' : 'Enables'
|
||||
} automatic station patrol.`}>
|
||||
<Icon
|
||||
size={2}
|
||||
name="map-signs"
|
||||
color={patrol_station ? 'good' : 'gray'}
|
||||
onClick={() => act('patrol')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</LabeledControls.Item>
|
||||
<LabeledControls.Item label="Maintenance Lock">
|
||||
<Tooltip
|
||||
content={
|
||||
maintenance_lock
|
||||
? 'Opens the maintenance hatch for repairs.'
|
||||
: 'Closes the maintenance hatch.'
|
||||
}>
|
||||
<Icon
|
||||
size={2}
|
||||
name="toolbox"
|
||||
color={maintenance_lock ? 'yellow' : 'gray'}
|
||||
onClick={() => act('maintenance')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</LabeledControls.Item>
|
||||
</LabeledControls>
|
||||
);
|
||||
};
|
||||
|
||||
/** Iterates over custom controls.
|
||||
* Calls the helper to identify which button to use.
|
||||
*/
|
||||
const ControlsDisplay = (_, context) => {
|
||||
const { data } = useBackend<SimpleBotContext>(context);
|
||||
const { custom_controls } = data;
|
||||
|
||||
return (
|
||||
<LabeledControls wrap>
|
||||
{Object.entries(custom_controls).map((control) => {
|
||||
return (
|
||||
<LabeledControls.Item
|
||||
pb={2}
|
||||
key={control[0]}
|
||||
label={control[0]
|
||||
.replace('_', ' ')
|
||||
.replace(/(^\w{1})|(\s+\w{1})/g, (letter) =>
|
||||
letter.toUpperCase())}>
|
||||
<ControlHelper control={control} />
|
||||
</LabeledControls.Item>
|
||||
);
|
||||
})}
|
||||
</LabeledControls>
|
||||
);
|
||||
};
|
||||
|
||||
/** Helper function which identifies which button to create.
|
||||
* Might need some fine tuning if you are using more advanced controls.
|
||||
*/
|
||||
const ControlHelper = (props, context) => {
|
||||
const { act } = useBackend<SimpleBotContext>(context);
|
||||
const { control } = props;
|
||||
if (control[0] === 'sync_tech') {
|
||||
/** Control is for sync - this is medbot specific */
|
||||
return <MedbotSync />;
|
||||
} else if (control[0] === 'heal_threshold') {
|
||||
/** Control is a threshold - this is medbot specific */
|
||||
return <MedbotThreshold control={control} />;
|
||||
} else if (control[0] === 'injection_amount') {
|
||||
/** Control is for injection - this is medbot specific */
|
||||
return <InjectionThreshold control={control} />;
|
||||
} else if (control[0] === 'tile_stack') {
|
||||
return <FloorbotTiles control={control} />;
|
||||
} else if (control[0] === 'line_mode') {
|
||||
return <FloorbotLine control={control} />;
|
||||
} else {
|
||||
/** Control is a boolean of some type */
|
||||
return (
|
||||
<Icon
|
||||
color={control[1] ? 'good' : 'gray'}
|
||||
name={control[1] ? 'toggle-on' : 'toggle-off'}
|
||||
size={2}
|
||||
onClick={() => act(control[0])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** Small button to sync medbots with research. */
|
||||
const MedbotSync = (_, context) => {
|
||||
const { act } = useBackend<SimpleBotContext>(context);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content={multiline`Synchronize surgical data with research network.
|
||||
Improves Tending Efficiency.`}>
|
||||
<Icon
|
||||
color="purple"
|
||||
name="cloud-download-alt"
|
||||
size={2}
|
||||
onClick={() => act('sync_tech')}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
/** Slider button for medbot injection thresholds */
|
||||
const InjectionThreshold = (props, context) => {
|
||||
const { act } = useBackend<SimpleBotContext>(context);
|
||||
const { control } = props;
|
||||
|
||||
return (
|
||||
<Tooltip content="Adjusts the amount for using when injecting.">
|
||||
<Slider
|
||||
minValue={1}
|
||||
maxValue={15}
|
||||
ranges={{
|
||||
bad: [-Infinity, 5],
|
||||
average: [6, 10],
|
||||
good: [11, Infinity],
|
||||
}}
|
||||
step={2}
|
||||
unit="u"
|
||||
value={control[1]}
|
||||
onChange={(_, value) => act(control[0], { amount: value })}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
/** Slider button for medbot healing thresholds */
|
||||
const MedbotThreshold = (props, context) => {
|
||||
const { act } = useBackend<SimpleBotContext>(context);
|
||||
const { control } = props;
|
||||
|
||||
return (
|
||||
<Tooltip content="Adjusts the sensitivity for damage treatment.">
|
||||
<Slider
|
||||
minValue={5}
|
||||
maxValue={75}
|
||||
ranges={{
|
||||
good: [-Infinity, 15],
|
||||
average: [15, 55],
|
||||
bad: [55, Infinity],
|
||||
}}
|
||||
step={5}
|
||||
unit="%"
|
||||
value={control[1]}
|
||||
onChange={(_, value) => act(control[0], { threshold: value })}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
/** Tile stacks for floorbots - shows number and eject button */
|
||||
const FloorbotTiles = (props, context) => {
|
||||
const { act } = useBackend<SimpleBotContext>(context);
|
||||
const { control } = props;
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={!control[1]}
|
||||
icon={control[1] ? 'eject' : ''}
|
||||
onClick={() => act('eject_tiles')}
|
||||
tooltip="Number of floor tiles contained in the bot.">
|
||||
{control[1] ? `${control[1]}` : 'Empty'}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
/** Direction indicator for floorbot when line mode is chosen. */
|
||||
const FloorbotLine = (props, context) => {
|
||||
const { act } = useBackend<SimpleBotContext>(context);
|
||||
const { control } = props;
|
||||
|
||||
return (
|
||||
<Tooltip content="Enables straight line tiling mode.">
|
||||
<Icon
|
||||
color={control[1] ? 'good' : 'gray'}
|
||||
name={control[1] ? 'compass' : 'toggle-off'}
|
||||
onClick={() => act('line_mode')}
|
||||
size={!control[1] ? 2 : 1.5}>
|
||||
{' '}
|
||||
{control[1] ? control[1].toString().charAt(0).toUpperCase() : ''}
|
||||
</Icon>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user