Xenoarch analysis machines NanoUI + hotkeys. (#16412)

* Gives the xenoarch anomaly machines a nicer UI, alt and ctrl click.

* Bunsen burners get an alt-click

* Rework basically everything. New Hyperspectral sprite.

* More sanity.

* Fix code review issues
This commit is contained in:
Pieter-Jan Briers
2017-11-10 21:28:44 +01:00
committed by Shadowmech88
parent d0e5d936d8
commit 9cd53f5b6a
5 changed files with 229 additions and 116 deletions

View File

@@ -9,6 +9,10 @@
//How many joules of electrical energy produce how many joules of heat energy? //How many joules of electrical energy produce how many joules of heat energy?
#define XENOARCH_HEAT_COEFFICIENT 3 #define XENOARCH_HEAT_COEFFICIENT 3
#define XENOARCH_SAFETY_TEMP 350
#define XENOARCH_MAX_TEMP 400
// I literally don't even know why this one is different from XENOARCH_MAX_TEMP.
#define XENOARCH_MAX_HEAT_INCREASE_TEMP 450
/obj/machinery/anomaly /obj/machinery/anomaly
name = "Analysis machine" name = "Analysis machine"
@@ -24,68 +28,65 @@
machine_flags = SCREWTOGGLE | CROWDESTROY | WRENCHMOVE | FIXED2WORK machine_flags = SCREWTOGGLE | CROWDESTROY | WRENCHMOVE | FIXED2WORK
var/obj/item/weapon/reagent_containers/glass/held_container var/obj/item/weapon/reagent_containers/glass/held_container
var/obj/item/weapon/tank/fuel_container
var/target_scan_ticks = 30 var/target_scan_ticks = 30
var/report_num = 0 var/report_num = 0
// How far into a scan we are.
// If it's zero we're not scanning.
var/scan_process = 0 var/scan_process = 0
var/temperature = 273 //measured in kelvin, if this exceeds 1200, the machine is damaged and requires repairs
//if this exceeds 600 and safety is enabled it will shutdown
//temp greater than 600 also requires a safety prompt to initiate scanning
var/max_temp = 450
/obj/machinery/anomaly/New() //measured in kelvin, if this exceeds 1200, the machine is damaged and requires repairs
..() //if this exceeds 600 and safety is enabled it will shutdown
//temp greater than 600 also requires a safety prompt to initiate scanning
var/temperature = T0C
//for analysis debugging
/*var/obj/item/weapon/reagent_containers/glass/solution_tray/S = new(src.loc)
var/turf/unsimulated/mineral/diamond/D
for(var/turf/unsimulated/mineral/diamond/M in world)
D = M
break
S.reagents.add_reagent(ANALYSIS_SAMPLE, 1, D.geological_data)
S.reagents.add_reagent(CHLORINE, 1, null)*/
/obj/machinery/anomaly/RefreshParts() /obj/machinery/anomaly/RefreshParts()
var/scancount = 0 var/scancount = 0
for(var/obj/item/weapon/stock_parts/SP in component_parts) for(var/obj/item/weapon/stock_parts/scanning_module/SP in component_parts)
if(istype(SP, /obj/item/weapon/stock_parts/scanning_module)) scancount += SP.rating-1
scancount += SP.rating-1
target_scan_ticks = initial(target_scan_ticks) - scancount*4 target_scan_ticks = initial(target_scan_ticks) - scancount*4
/obj/machinery/anomaly/power_change()
..()
if (stat & NOPOWER && scan_process)
stop()
else
update_icon()
/obj/machinery/anomaly/process() /obj/machinery/anomaly/process()
//not sure if everything needs to heat up, or just the GLPC //not sure if everything needs to heat up, or just the GLPC
var/datum/gas_mixture/env = loc.return_air() var/datum/gas_mixture/env = loc.return_air()
var/environmental_temp = env.temperature var/environmental_temp = env.temperature
if(scan_process) if(scan_process)
// Shouldn't be reachable, still can't hurt.
if(stat & NOPOWER)
stop()
if(scan_process++ > target_scan_ticks) if(scan_process++ > target_scan_ticks)
FinishScan() FinishScan()
else if(temperature > 400) else if(temperature > XENOARCH_MAX_TEMP)
src.visible_message("<span class='notice'>[bicon(src)] shuts down from the heat!</span>", 2) visible_message("<span class='notice'>[bicon(src)] shuts down from the heat!</span>")
scan_process = 0 scan_process = 0
else if(temperature > 350 && prob(10)) else if(temperature > XENOARCH_SAFETY_TEMP && prob(10))
src.visible_message("<span class='notice'>[bicon(src)] bleets plaintively.</span>", 2) visible_message("<span class='notice'>[bicon(src)] bleets plaintively.</span>")
if(temperature > 400)
scan_process = 0
//show we're busy //show we're busy
if(prob(5)) if(prob(5))
src.visible_message("<span class='notice'>[bicon(src)] [pick("whirrs","chuffs","clicks")][pick(" quietly"," softly"," sadly"," excitedly"," energetically"," angrily"," plaintively")].</span>", 2) visible_message("<span class='notice'>[bicon(src)] [pick("whirrs","chuffs","clicks")][pick(" quietly"," softly"," sadly"," excitedly"," energetically"," angrily"," plaintively")].</span>")
use_power = 2 use_power = 2
icon_state = "analyser_processing"
else else
use_power = 1 use_power = 1
icon_state = "analyser"
if(prob(10))
flick(src, "analyser_processing")
//Add 3000 joules when active. This is about 0.6 degrees per tick. //Add 3000 joules when active. This is about 0.6 degrees per tick.
//May need adjustment //May need adjustment
if(use_power == 1) if(use_power == 1)
var/heat_added = active_power_usage *XENOARCH_HEAT_COEFFICIENT var/heat_added = active_power_usage *XENOARCH_HEAT_COEFFICIENT
if(temperature < max_temp) if(temperature < XENOARCH_MAX_HEAT_INCREASE_TEMP)
temperature += heat_added/XENOARCH_HEAT_CAPACITY temperature += heat_added/XENOARCH_HEAT_CAPACITY
var/temperature_difference = abs(environmental_temp-temperature) var/temperature_difference = abs(environmental_temp-temperature)
@@ -100,7 +101,7 @@
removed.temperature = max(TCMB, removed.temperature + heat_added/heat_capacity) removed.temperature = max(TCMB, removed.temperature + heat_added/heat_capacity)
if(temperature_difference > 10 && prob(5)) if(temperature_difference > 10 && prob(5))
src.visible_message("<span class='notice'>[bicon(src)] hisses softly.</span>", 2) visible_message("<span class='notice'>[bicon(src)] hisses softly.</span>", "You hear a soft hiss.")
else else
//heat up to match the air //heat up to match the air
@@ -108,107 +109,112 @@
removed.temperature = max(TCMB, removed.temperature - heat_added/heat_capacity) removed.temperature = max(TCMB, removed.temperature - heat_added/heat_capacity)
if(temperature_difference > 10 && prob(5)) if(temperature_difference > 10 && prob(5))
src.visible_message("<span class='notice'>[bicon(src)] plinks quietly.</span>", 2) visible_message("<span class='notice'>[bicon(src)] plinks quietly.</span>", "You hear a soft hiss.")
env.merge(removed) env.merge(removed)
nanomanager.update_uis(src)
//this proc should be overriden by each individual machine /obj/machinery/anomaly/attack_hand(var/mob/user)
/obj/machinery/anomaly/attack_hand(var/mob/user as mob) ui_interact(user)
if(..())
return
if(stat & (NOPOWER|BROKEN))
return
user.machine = src
var/dat = "<B>[src.name]</B><BR>"
dat += {"Module heat level: [temperature] kelvin<br> obj/machinery/anomaly/attackby(obj/item/weapon/W, mob/living/user)
Safeties set at 350k, shielding failure at 400k. Failure to maintain safe heat levels may result in equipment damage.<br>
<hr>"}
if(scan_process)
dat += "Scan in progress<br><br><br>"
else
dat += "[held_container ? "<A href='?src=\ref[src];eject_beaker=1'>Eject beaker</a>" : "No beaker inserted."]<br>"
//dat += "[fuel_container ? "<A href='?src=\ref[src];eject_fuel=1'>Eject fuel tank</a>" : "No fuel tank inserted."]<br>"
dat += "[held_container ? "<A href='?src=\ref[src];begin=1'>Begin scanning</a>" : ""]"
dat += {"<hr>
<A href='?src=\ref[src];refresh=1'>Refresh</a><BR>
<A href='?src=\ref[src];close=1'>Close</a><BR>"}
user << browse(dat, "window=anomaly;size=450x500")
onclose(user, "anomaly")
obj/machinery/anomaly/attackby(obj/item/weapon/W as obj, mob/living/user as mob)
if(istype(W, /obj/item/weapon/reagent_containers/glass)) if(istype(W, /obj/item/weapon/reagent_containers/glass))
//var/obj/item/weapon/reagent_containers/glass/G = W
if(held_container) if(held_container)
to_chat(user, "<span class='warning'>You must remove the [held_container] first.</span>") to_chat(user, "<span class='warning'>You must remove \the [held_container] first.</span>")
else return TRUE
if(user.drop_item(W, src))
to_chat(user, "<span class='notice'>You put the [W] into the [src].</span>")
held_container = W if(user.drop_item(W, src))
updateDialog() to_chat(user, "<span class='notice'>You put \the [W] into the [src].</span>")
return 1 // avoid afterattack() being called held_container = W
/*else if(istype(W, /obj/item/weapon/tank)) nanomanager.update_uis(src)
//var/obj/item/weapon/reagent_containers/glass/G = W
if(fuel_container)
to_chat(user, "<span class='warning'>You must remove the [fuel_container] first.</span>")
else
to_chat(user, "<span class='notice'>You put the [fuel_container] into the [src].</span>")
user.drop_item(W, src)
fuel_container.forceMove(src)
fuel_container = W
updateDialog()*/
else
return ..()
obj/machinery/anomaly/proc/ScanResults() return TRUE
//instantiate in children to produce unique scan behaviour
return ..()
/obj/machinery/anomaly/proc/ScanResults()
// Override in children to produce unique scan behaviour.
return "<span class='warning'>Error initialising scanning components.</span>" return "<span class='warning'>Error initialising scanning components.</span>"
obj/machinery/anomaly/proc/FinishScan() /obj/machinery/anomaly/proc/FinishScan()
scan_process = 0 stop()
updateDialog()
//determine the results and print a report //determine the results and print a report
if(held_container) if(held_container)
src.visible_message("<span class='notice'>[bicon(src)] makes an insistent chime.</span>", 2) src.visible_message("<span class='notice'>[bicon(src)] makes an insistent chime.</span>", "You hear an insistent chime.")
var/obj/item/weapon/paper/P = new(src.loc) var/obj/item/weapon/paper/P = new(loc)
P.name = "[src] report #[++report_num]" P.name = "[src] report #[++report_num]"
P.info = "<b>[src] analysis report #[report_num]</b><br><br>" + ScanResults() P.info = "<b>[src] analysis report #[report_num]</b><br><br>" + ScanResults()
P.stamped = list(/obj/item/weapon/stamp) P.stamped = list(/obj/item/weapon/stamp)
P.overlays = list("paper_stamp-qm") P.overlays += "paper_stamp-qm"
else else
src.visible_message("<span class='notice'>[bicon(src)] makes a low buzzing noise.</span>", 2) visible_message("<span class='notice'>[bicon(src)] makes a low buzzing noise.</span>", "You hear a low buzz.")
obj/machinery/anomaly/Topic(href, href_list) obj/machinery/anomaly/Topic(href, href_list)
if(..()) . = ..()
if (.)
return return
usr.set_machine(src)
if(href_list["close"])
usr << browse(null, "window=anomaly")
usr.machine = null
if(href_list["eject_beaker"])
held_container.forceMove(src.loc)
held_container = null
if(href_list["eject_fuel"])
fuel_container.forceMove(src.loc)
fuel_container = null
if(href_list["begin"])
if(temperature >= 350)
var/proceed = input("Unsafe internal temperature detected, enter YES below to continue.","Warning")
if(proceed == "YES" && !..()) //call parent again to run distance and power checks again.
scan_process = 1
else
scan_process = 1
updateUsrDialog() if (href_list["eject"] && held_container && !scan_process)
eject()
. = 1
if (href_list["begin"] && !scan_process && held_container)
start(usr)
. = 1
if (href_list["stop"] && scan_process)
stop()
. = 1
/obj/machinery/anomaly/proc/eject()
held_container.forceMove(loc)
held_container = null
nanomanager.update_uis(src)
/obj/machinery/anomaly/proc/start(var/mob/user)
if (temperature >= XENOARCH_SAFETY_TEMP)
var/proceed = input("Unsafe internal temperature detected, enter YES below to continue.","Warning")
if (proceed != "YES" || user.incapacitated() || !user.Adjacent(src))
return FALSE
scan_process = 1
update_icon()
nanomanager.update_uis(src)
/obj/machinery/anomaly/proc/stop()
scan_process = 0
update_icon()
nanomanager.update_uis(src)
/obj/machinery/anomaly/update_icon()
if (scan_process)
icon_state = "analyser_processing"
else
icon_state = "analyser"
/obj/machinery/anomaly/AltClick(var/mob/user)
if (user.incapacitated() || !user.Adjacent(src) || scan_process || !held_container || stat & NOPOWER)
return
eject()
/obj/machinery/anomaly/CtrlClick(var/mob/user)
if (!anchored)
return ..()
if (user.incapacitated() || !user.Adjacent(src) || scan_process || !held_container || stat & NOPOWER)
return
start(user)
//whether the carrier sample matches the possible finds //whether the carrier sample matches the possible finds
//results greater than a threshold of 0.6 means a positive result //results greater than a threshold of 0.6 means a positive result
obj/machinery/anomaly/proc/GetResultSpecifity(var/datum/geosample/scanned_sample, var/carrier_name) /obj/machinery/anomaly/proc/GetResultSpecifity(var/datum/geosample/scanned_sample, var/carrier_name)
var/specifity = 0 var/specifity = 0
if(scanned_sample && carrier_name) if(scanned_sample && carrier_name)
@@ -218,3 +224,37 @@ obj/machinery/anomaly/proc/GetResultSpecifity(var/datum/geosample/scanned_sample
specifity = rand(0, 0.5) specifity = rand(0, 0.5)
return specifity return specifity
/obj/machinery/anomaly/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = NANOUI_FOCUS)
if (stat & NOPOWER)
return
var/list/data[0]
data["max_temperature"] = XENOARCH_MAX_TEMP
data["safety_temperature"] = XENOARCH_SAFETY_TEMP
data["temperature"] = temperature
data["target_ticks"] = target_scan_ticks
data["scan_process"] = scan_process
data["beaker"] = !!held_container
if (held_container)
data["beaker_name"] = held_container.name
var/list/beaker_contents[0]
for(var/datum/reagent/R in held_container.reagents.reagent_list)
beaker_contents[++beaker_contents.len] = list(
"name" = R.name,
"volume" = R.volume
)
data["beaker_contents"] = beaker_contents
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "xenoarch_analysis.tmpl", name, 480, 400)
// when the ui is first opened this is the data it will use
ui.set_initial_data(data)
// open the new ui window
ui.open()

View File

@@ -1,7 +1,9 @@
/obj/machinery/anomaly/hyperspectral /obj/machinery/anomaly/hyperspectral
name = "Hyperspectral Imager" name = "Hyperspectral Imager"
icon = 'icons/obj/xenoarchaeology.dmi' icon = 'icons/obj/xenoarchaeology.dmi'
icon_state = "scanner" icon_state = "hyperspectral"
light_power = 0.75
light_color = LIGHT_COLOR_GREEN
/obj/machinery/anomaly/hyperspectral/New() /obj/machinery/anomaly/hyperspectral/New()
. = ..() . = ..()
@@ -15,15 +17,26 @@
RefreshParts() RefreshParts()
/obj/machinery/anomaly/hyperspectral/process() /obj/machinery/anomaly/hyperspectral/update_icon()
..() overlays.Cut()
if(scan_process) if (stat & (NOPOWER | BROKEN))
icon_state = "scanner_active" return
else
icon_state = "scanner"
if(prob(10))
flick(src, "scanner_active")
overlays += "hyperspectral_on"
if (scan_process)
overlays += "hyperspectral_active"
if (panel_open)
overlays += "hyperspectral_panel"
/obj/machinery/anomaly/hyperspectral/start(var/mob/user)
..()
set_light(2)
/obj/machinery/anomaly/hyperspectral/stop()
..()
set_light(0)
/obj/machinery/anomaly/hyperspectral/ScanResults() /obj/machinery/anomaly/hyperspectral/ScanResults()
var/results = "The scan was inconclusive. Check sample integrity and carrier consistency." var/results = "The scan was inconclusive. Check sample integrity and carrier consistency."

View File

@@ -47,6 +47,7 @@
if(held_container) if(held_container)
held_container.forceMove(get_turf(src)) held_container.forceMove(get_turf(src))
held_container = null held_container = null
processing_objects.Remove(src)
..() ..()
/obj/machinery/bunsen_burner/attackby(obj/item/weapon/W, mob/user) /obj/machinery/bunsen_burner/attackby(obj/item/weapon/W, mob/user)
@@ -128,14 +129,23 @@
else else
toggle() toggle()
/obj/machinery/bunsen_burner/verb/toggle() /obj/machinery/bunsen_burner/verb/verb_toggle()
set src in view(1) set src in view(1)
set name = "Toggle bunsen burner" set name = "Toggle bunsen burner"
set category = "Object" set category = "Object"
if (!usr.Adjacent(src) || usr.incapacitated())
return
toggle()
/obj/machinery/bunsen_burner/proc/toggle()
heating = !heating heating = !heating
update_icon() update_icon()
if(heating) if(heating)
processing_objects.Add(src) processing_objects.Add(src)
else else
processing_objects.Remove(src) processing_objects.Remove(src)
/obj/machinery/bunsen_burner/AltClick()
toggle()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -0,0 +1,50 @@
<div class="item">
<div class="itemLabel">
Temperature:
</div>
{{:helper.displayBar(data.temperature, 273.15, data.max_temperature, data.temperature > data.safety_temperature ? "bad" : "good")}}
<div class="itemContent" style="width: 60px">
{{:helper.round(data.temperature)}} K
</div>
</div>
<div class="item">
<div class="itemLabel">
Scan Status:
</div>
<div class="itemContent">
{{:helper.displayBar(data.scan_process-1, 0, data.target_ticks)}}
<div style="clear: both; padding-top: 4px;">
{{:helper.link("start", "check", {"begin": 1}, (data.scan_process || !data.beaker) ? "disabled" : null)}}
{{:helper.link("abort", "cancel", {"stop": 1}, data.scan_process ? null : "disabled", data.scan_process ? "redBackground" : null)}}
</div>
</div>
</div>
<h3>Holding Container Status</h3>
{{if data.beaker}}
<div class="item">
<div class="itemLabel">
Container:
</div>
<div class="itemContent">
<div style="float: left; width: 180px;">{{:data.beaker_name}}</div> {{:helper.link('Eject', 'eject', {'eject' : 1}, data.scan_process ? "disabled" : null)}}
</div>
</div>
<div class="statusDisplay" style="min-height: 180px;">
<div class="item">
<div class="itemContent" style="width: 100%;">
{{for data.beaker_contents}}
<span class="highlight">{{:value.volume}} units of {{:value.name}}</span><br>
{{empty}}
<span class="bad">
Container is empty.
</span>
{{/for}}
</div>
</div>
</div>
{{else}}
<div class="item"><span class="average"><i>No container inserted.</i></span></div>
{{/if}}