TGUI Point Defense Mainframe & Advanced Shield Generator

This commit is contained in:
ShadowLarkens
2020-08-18 20:27:03 -07:00
parent 48b630891e
commit 02bb4637eb
8 changed files with 369 additions and 328 deletions
+20 -27
View File
@@ -15,7 +15,6 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense)
anchored = TRUE
circuit = /obj/item/weapon/circuitboard/pointdefense_control
var/list/targets = list() // Targets being engaged by associated batteries
var/ui_template = "pointdefense_control.tmpl"
var/id_tag = null
/obj/machinery/pointdefense_control/Initialize(mapload)
@@ -37,48 +36,42 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense)
if(!id_tag)
. += "[desc_panel_image("multitool")]to set ident tag"
/obj/machinery/pointdefense_control/ui_interact(var/mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(ui_template)
var/list/data = build_ui_data()
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, ui_template, name, 400, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/pointdefense_control/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PointDefenseControl") // 400, 600
ui.open()
/obj/machinery/pointdefense_control/attack_hand(mob/user)
if((. = ..()))
return
if(CanUseTopic(user, global.default_state) > STATUS_CLOSE)
ui_interact(user)
if(..())
return TRUE
tgui_interact(user)
return TRUE
/obj/machinery/pointdefense_control/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
/obj/machinery/pointdefense_control/Topic(var/mob/user, var/href_list)
if((. = ..()))
return
if(href_list["toggle_active"])
var/obj/machinery/power/pointdefense/PD = locate(href_list["toggle_active"])
if(action == "toggle_active")
var/obj/machinery/power/pointdefense/PD = locate(params["target"])
if(!istype(PD))
return TOPIC_NOACTION
return FALSE
//if(!lan || !lan.is_connected(PD))
if(PD.id_tag != id_tag)
return TOPIC_NOACTION
return FALSE
if(!(get_z(PD) in GetConnectedZlevels(get_z(src))))
to_chat(user, "<span class='warning'>[PD] is not within control range.</span>")
return TOPIC_NOACTION
to_chat(usr, "<span class='warning'>[PD] is not within control range.</span>")
return FALSE
if(!PD.Activate()) //Activate() whilst the device is active will return false.
PD.Deactivate()
return TOPIC_REFRESH
return TRUE
/obj/machinery/pointdefense_control/proc/build_ui_data()
/obj/machinery/pointdefense_control/tgui_data(mob/user)
var/list/data = list()
data["id"] = id_tag
data["name"] = name
var/list/turrets = list()
if(id_tag)
var/list/connected_z_levels = GetConnectedZlevels(get_z(src))
+81 -80
View File
@@ -400,8 +400,14 @@
spinup_counter = round(spinup_delay / idle_multiplier)
update_icon()
/obj/machinery/power/shield_generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
/obj/machinery/power/shield_generator/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "OvermapShieldGenerator", name) // 500, 800
ui.open()
/obj/machinery/power/shield_generator/tgui_data(mob/user)
var/list/data = list()
data["running"] = running
data["modes"] = get_flag_descriptions()
@@ -427,12 +433,7 @@
data["idle_valid_values"] = idle_valid_values
data["spinup_counter"] = spinup_counter
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "shield_generator.tmpl", src.name, 500, 800)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
return data
/obj/machinery/power/shield_generator/attack_hand(mob/user)
if((. = ..()))
@@ -440,99 +441,99 @@
if(panel_open && Adjacent(user))
wires.Interact(user)
return
if(CanUseTopic(user, global.default_state) > STATUS_CLOSE)
ui_interact(user)
return TRUE
tgui_interact(user)
/obj/machinery/power/shield_generator/CanUseTopic(var/mob/user)
/obj/machinery/power/shield_generator/tgui_status(mob/user)
if(issilicon(user) && !Adjacent(user) && ai_control_disabled)
return STATUS_UPDATE
if(panel_open)
return min(..(), STATUS_DISABLED)
return ..()
/obj/machinery/power/shield_generator/Topic(href, href_list, datum/topic_state/state = default_state)
if((. = ..()))
return
/obj/machinery/power/shield_generator/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
if(href_list["begin_shutdown"])
if(running < SHIELD_RUNNING) // Discharging or off
return
var/alert = alert(usr, "Are you sure you wish to do this? It will drain the power inside the internal storage rapidly.", "Are you sure?", "Yes", "No")
if(!CanInteract(usr, state))
return
if(running < SHIELD_RUNNING)
return
if(alert == "Yes")
set_idle(TRUE) // do this first to clear the field
running = SHIELD_DISCHARGING
return TOPIC_REFRESH
switch(action)
if("begin_shutdown")
if(running < SHIELD_RUNNING) // Discharging or off
return
var/alert = alert(usr, "Are you sure you wish to do this? It will drain the power inside the internal storage rapidly.", "Are you sure?", "Yes", "No")
if(tgui_status(usr, state) != STATUS_INTERACTIVE)
return
if(running < SHIELD_RUNNING)
return
if(alert == "Yes")
set_idle(TRUE) // do this first to clear the field
running = SHIELD_DISCHARGING
return TRUE
if(href_list["start_generator"])
if(offline_for)
return
set_idle(TRUE)
return TOPIC_REFRESH
if("start_generator")
if(offline_for)
return
set_idle(TRUE)
return TRUE
if(href_list["toggle_idle"])
if(running < SHIELD_RUNNING)
return TOPIC_HANDLED
set_idle(text2num(href_list["toggle_idle"]))
return TOPIC_REFRESH
if("toggle_idle")
if(running < SHIELD_RUNNING)
return TRUE
set_idle(text2num(params["toggle_idle"]))
return TRUE
// Instantly drops the shield, but causes a cooldown before it may be started again. Also carries a risk of EMP at high charge.
if(href_list["emergency_shutdown"])
if(!running)
return TOPIC_HANDLED
// Instantly drops the shield, but causes a cooldown before it may be started again. Also carries a risk of EMP at high charge.
if("emergency_shutdown")
if(!running)
return TRUE
var/choice = input(usr, "Are you sure that you want to initiate an emergency shield shutdown? This will instantly drop the shield, and may result in unstable release of stored electromagnetic energy. Proceed at your own risk.") in list("Yes", "No")
if((choice != "Yes") || !running)
return TOPIC_HANDLED
var/choice = input(usr, "Are you sure that you want to initiate an emergency shield shutdown? This will instantly drop the shield, and may result in unstable release of stored electromagnetic energy. Proceed at your own risk.") in list("Yes", "No")
if((choice != "Yes") || !running)
return TRUE
// If the shield would take 5 minutes to disperse and shut down using regular methods, it will take x1.5 (7 minutes and 30 seconds) of this time to cool down after emergency shutdown
offline_for = round(current_energy / (SHIELD_SHUTDOWN_DISPERSION_RATE / 1.5))
var/old_energy = current_energy
shutdown_field()
log_and_message_admins("has triggered \the [src]'s emergency shutdown!", usr)
spawn()
empulse(src, old_energy / 60000000, old_energy / 32000000, 1) // If shields are charged at 450 MJ, the EMP will be 7.5, 14.0625. 90 MJ, 1.5, 2.8125
old_energy = 0
// If the shield would take 5 minutes to disperse and shut down using regular methods, it will take x1.5 (7 minutes and 30 seconds) of this time to cool down after emergency shutdown
offline_for = round(current_energy / (SHIELD_SHUTDOWN_DISPERSION_RATE / 1.5))
var/old_energy = current_energy
shutdown_field()
log_and_message_admins("has triggered \the [src]'s emergency shutdown!", usr)
spawn()
empulse(src, old_energy / 60000000, old_energy / 32000000, 1) // If shields are charged at 450 MJ, the EMP will be 7.5, 14.0625. 90 MJ, 1.5, 2.8125
old_energy = 0
return TOPIC_REFRESH
return TRUE
if(mode_changes_locked)
return TOPIC_REFRESH
return TRUE
if(href_list["set_range"])
var/new_range = input(usr, "Enter new field range (1-[world.maxx]). Leave blank to cancel.", "Field Radius Control", field_radius) as num
if(!new_range)
return TOPIC_HANDLED
target_radius = between(1, new_range, world.maxx)
return TOPIC_REFRESH
switch(action)
if("set_range")
var/new_range = input(usr, "Enter new field range (1-[world.maxx]). Leave blank to cancel.", "Field Radius Control", field_radius) as num
if(!new_range)
return TRUE
target_radius = between(1, new_range, world.maxx)
return TRUE
if(href_list["set_input_cap"])
var/new_cap = round(input(usr, "Enter new input cap (in kW). Enter 0 or nothing to disable input cap.", "Generator Power Control", round(input_cap / 1000)) as num)
if(!new_cap)
input_cap = 0
return
input_cap = max(0, new_cap) * 1000
return TOPIC_REFRESH
if("set_input_cap")
var/new_cap = round(input(usr, "Enter new input cap (in kW). Enter 0 or nothing to disable input cap.", "Generator Power Control", round(input_cap / 1000)) as num)
if(!new_cap)
input_cap = 0
return
input_cap = max(0, new_cap) * 1000
return TRUE
if(href_list["toggle_mode"])
// Toggling hacked-only modes requires the hacked var to be set to 1
if((text2num(href_list["toggle_mode"]) & (MODEFLAG_BYPASS | MODEFLAG_OVERCHARGE)) && !hacked)
return TOPIC_HANDLED
if("toggle_mode")
// Toggling hacked-only modes requires the hacked var to be set to 1
if((text2num(params["toggle_mode"]) & (MODEFLAG_BYPASS | MODEFLAG_OVERCHARGE)) && !hacked)
return TRUE
toggle_flag(text2num(href_list["toggle_mode"]))
return TOPIC_REFRESH
toggle_flag(text2num(params["toggle_mode"]))
return TRUE
if(href_list["switch_idle"])
if(running == SHIELD_SPINNING_UP)
return TOPIC_REFRESH
var/new_idle = text2num(href_list["switch_idle"])
if(new_idle in idle_valid_values)
idle_multiplier = new_idle
return TOPIC_REFRESH
if("switch_idle")
if(running == SHIELD_SPINNING_UP)
return TRUE
var/new_idle = text2num(params["switch_idle"])
if(new_idle in idle_valid_values)
idle_multiplier = new_idle
return TRUE
/obj/machinery/power/shield_generator/proc/field_integrity()
if(full_shield_strength)
-54
View File
@@ -1,54 +0,0 @@
<h2>Fire Assist Mainframe: {{:data.id || "[no tag]"}}</h2>
{{if !(data.id)}}
<div class="notice">
<span class='bad'>This system has not been assigned an ident tag. Please contact your system administrator or conduct a manual update with a standard multitool.</span>
</div>
{{/if}}
{{for data.turrets}}
<h3>{{:value.id}}</h3>
<div class="item">
<div class="itemLabelWide">
Battery status
</div>
<div class="itemContentMedium">
{{if value.active}}
<span class="fixedLeft good">Online</span> {{:helper.link('Shut down', null, {'toggle_active': value.ref})}}
{{else}}
<span class="fixedLeft average">Offline</span> {{:helper.link('Start up', null, {'toggle_active': value.ref})}}
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabelWide">
Effective range
</div>
<div class="itemContentMedium">
{{:value.effective_range}}
</div>
</div>
<div class="item">
<div class="itemLabelWide">
Reaction wheel delay
</div>
<div class="itemContentMedium">
{{:value.reaction_wheel_delay}}
</div>
</div>
<div class="item">
<div class="itemLabelWide">
Recharge time
</div>
<div class="itemContentMedium">
{{:value.recharge_time}}
</div>
{{empty}}
<div class="item">
<div class="itemLabel">
Error:
</div>
<div class="itemContent">
No weapon systems detected. Please check network connection.
</div>
</div>
<hr/>
{{/for}}
-158
View File
@@ -1,158 +0,0 @@
{{if data.offline_for}}
<h1>EMERGENCY SHUTDOWN</h1>
<b>An emergency shutdown has been initiated - generator cooling down</b><br>
<i>Please wait until the generator cools down before resuming operation. Estimated time left: {{:data.offline_for}} seconds.</i>
{{else}}
<h2>SYSTEM STATUS</h2>
<div class="item">
<div class="itemLabel">
Generator is:
</div>
<div class="itemContent">
{{if data.running == 2}}
{{if data.overloaded}}
Recovering
{{else}}
Active
{{/if}}
{{else data.running == 1}}
Shutting Down
{{else data.running == 3}}
Inactive
{{else data.running == 4}}
Spinning Up&nbsp;
{{if data.target_radius != data.field_radius}}
(Adjusting Radius)
{{else}}
({{:data.spinup_counter * 2}}s)
{{/if}}
{{else}}
Offline
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Energy Storage:
</div>
<div class="itemContent">
{{:data.current_energy}}/{{:data.max_energy}} MJ ({{:data.percentage_energy}}%)
</div>
</div>
<div class="item">
<div class="itemLabel">
Shield Integrity:
</div>
<div class="itemContent">
{{:data.field_integrity}}%
</div>
</div>
<div class="item">
<div class="itemLabel">
Mitigation:
</div>
<div class="itemContent">
{{:data.mitigation_em}}% EM / {{:data.mitigation_physical}}% PH / {{:data.mitigation_heat}}% HE / {{:data.mitigation_max}}% MAX
</div>
</div>
<div class="item">
<div class="itemLabel">
Upkeep Energy Use:
</div>
<div class="itemContent">
{{:data.upkeep_power_usage}} kW
</div>
</div>
<div class="item">
<div class="itemLabel">
Total Energy Use:
</div>
<div class="itemContent">
{{if data.input_cap_kw}}
{{:data.power_usage}} / {{:data.input_cap_kw}} kW
{{else}}
{{:data.power_usage}} kW (No Limit)
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Field Size:
</div>
<div class="itemContent">
{{:data.functional_segments}} / {{:data.total_segments}} m2 (radius {{:data.field_radius}}, target {{:data.target_radius}})
</div>
</div>
<h2>CONTROLS</h2>
<table>
<tr>
{{if (data.running >= 2)}}
<td>{{:helper.link('Turn off', null, {'begin_shutdown' : '1'})}}
{{if (data.running == 3)}}
<td>{{:helper.link('Activate', null, {'toggle_idle' : '0'})}} </td>
{{else}}
<td>{{:helper.link('Deactivate', null, {'toggle_idle' : '1'})}} </td>
{{/if}}
{{else}}
<td>{{:helper.link('Turn on', null, {'start_generator' : '1'})}}
{{/if}}
{{if data.running}}
{{if data.hacked}}
<td>{{:helper.link('EMERGENCY SHUTDOWN', null, {'emergency_shutdown' : '1'}, null, 'redButton')}}
{{/if}}
{{/if}}
<tr>
<td>{{:helper.link('Set Field Range', null, {'set_range' : '1'})}}
<td>{{:helper.link('Set Input Cap', null, {'set_input_cap' : '1'})}}
<tr>
<td>Set inactive power use intensity:</td>
<td>
{{for data.idle_valid_values}}
{{:helper.link(value, null, {'switch_idle' : value}, (value == data.idle_multiplier) ? 'selected' : (data.running == 4 ? 'disabled' : null))}}
{{/for}}
</td>
</table>
<h2>FIELD CALIBRATION</h2>
<hr>
{{for data.modes}}
<div class="item">
<div class="itemLabel">
Mode:
</div>
<div class="itemContent">
{{:value.name}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Description:
</div>
<div class="itemContent">
{{:value.desc}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Status:
</div>
<div class="itemContent">
{{if value.status}}
Enabled
{{else}}
Disabled
{{/if}}
{{:helper.link('Toggle', null, {'toggle_mode' : value.flag})}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Multiplier:
</div>
<div class="itemContent">
{{:value.multiplier}}
</div>
</div>
<hr>
{{/for}}
{{/if}}
@@ -0,0 +1,212 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Table, AnimatedNumber } from "../components";
import { Window } from "../layouts";
export const OvermapShieldGenerator = (props, context) => {
return (
<Window width={500} height={800} resizable>
<Window.Content scrollable>
<OvermapShieldGeneratorContent />
</Window.Content>
</Window>
);
};
const OvermapShieldGeneratorContent = (props, context) => {
const { act, data } = useBackend(context);
const {
modes,
offline_for,
} = data;
if (offline_for) {
return (
<Section title="EMERGENCY SHUTDOWN" color="bad">
An emergency shutdown has been initiated - generator cooling down.
Please wait until the generator cools down before resuming operation.
Estimated time left: {offline_for} seconds.
</Section>
);
}
return (
<Fragment>
<OvermapShieldGeneratorStatus />
<OvermapShieldGeneratorControls />
<Section title="Field Calibration">
{modes.map(mode => (
<Section title={mode.name} level={2} key={mode.name} buttons={
<Button
icon="power-off"
selected={mode.status}
onClick={() => act("toggle_mode", { toggle_mode: mode.flag })}>
{mode.status ? "Enabled" : "Disabled"}
</Button>
}>
<Box color="label">
{mode.desc}
</Box>
<Box mt={0.5}>
Multiplier: {mode.multiplier}
</Box>
</Section>
))}
</Section>
</Fragment>
);
};
const OvermapShieldGeneratorStatus = (props, context) => {
const { act, data } = useBackend(context);
const {
running,
overloaded,
mitigation_max,
mitigation_physical,
mitigation_em,
mitigation_heat,
field_integrity,
max_energy,
current_energy,
percentage_energy,
total_segments,
functional_segments,
field_radius,
target_radius,
input_cap_kw,
upkeep_power_usage,
power_usage,
spinup_counter,
} = data;
return (
<Section title="System Status">
<LabeledList>
<LabeledList.Item label="Generator is">
{running === 1 && (
<Box color="average">Shutting Down</Box>
) || running === 2 && (
overloaded && (
<Box color="bad">Overloaded</Box>
) || (
<Box color="good">Running</Box>
)
) || running === 3 && (
<Box color="average">Inactive</Box>
) || running === 4 && (
<Box color="blue">
Spinning Up&nbsp;
{target_radius !== field_radius && (
<Box inline>(Adjusting Radius)</Box>
) || (
<Box inline>{spinup_counter * 2}s</Box>
)}
</Box>
) || (
<Box color="bad">
Offline
</Box>
)}
</LabeledList.Item>
<LabeledList.Item label="Energy Storage">
<ProgressBar
value={current_energy}
maxValue={max_energy}>
{current_energy} / {max_energy} MJ ({percentage_energy}%)
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Shield Integrity">
<AnimatedNumber value={field_integrity} />%
</LabeledList.Item>
<LabeledList.Item label="Mitigation">
{mitigation_em}% EM / {mitigation_physical}% PH / {mitigation_heat}% HE / {mitigation_max}% MAX
</LabeledList.Item>
<LabeledList.Item label="Upkeep Energy Use">
<AnimatedNumber value={upkeep_power_usage} /> kW
</LabeledList.Item>
<LabeledList.Item label="Total Energy Use">
{input_cap_kw && (
<Box>
<ProgressBar
value={power_usage}
maxValue={input_cap_kw}>
{power_usage} / {input_cap_kw} kW
</ProgressBar>
</Box>
) || (
<Box>
<AnimatedNumber value={power_usage} /> kW (No Limit)
</Box>
)}
</LabeledList.Item>
<LabeledList.Item label="Field Size">
<AnimatedNumber value={functional_segments} />&nbsp;/&nbsp;
<AnimatedNumber value={total_segments} /> m&sup2;
(radius <AnimatedNumber value={field_radius} />, target <AnimatedNumber value={target_radius} />)
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const OvermapShieldGeneratorControls = (props, context) => {
const { act, data } = useBackend(context);
const {
running,
hacked,
idle_multiplier,
idle_valid_values,
} = data;
return (
<Section title="Controls" buttons={
<Fragment>
{running >= 2 && (
<Box>
<Button icon="power-off" onClick={() => act("begin_shutdown")} selected>Turn off</Button>
{running === 3 && (
<Button icon="power-off" onClick={() => act("toggle_idle", { toggle_idle: 0 })}>Activate</Button>
) || (
<Button icon="power-off" onClick={() => act("toggle_idle", { toggle_idle: 1 })} selected>
Deactivate
</Button>
)}
</Box>
) || (
<Button icon="power-off" onClick={() => act("start_generator")}>Turn on</Button>
)}
{running && hacked && (
<Button icon="exclamation-triangle" onClick={() => act("emergency_shutdown")} color="bad">
EMERGENCY SHUTDOWN
</Button>
) || null}
</Fragment>
}>
<Button
icon="expand-arrows-alt"
onClick={() => act("set_range")}>
Set Field Range
</Button>
<Button
icon="bolt"
onClick={() => act("set_input_cap")}>
Set Input Cap
</Button>
<LabeledList>
<LabeledList.Item label="Set inactive power use intensity">
{idle_valid_values.map(val => (
<Button
key={val}
selected={val === idle_multiplier}
disabled={running === 4}
onClick={() => act("switch_idle", { switch_idle: val })}>
{val}
</Button>
))}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
@@ -0,0 +1,47 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Table, AnimatedNumber } from "../components";
import { Window } from "../layouts";
export const PointDefenseControl = (props, context) => {
const { act, data } = useBackend(context);
const {
id,
turrets,
} = data;
return (
<Window width={400} height={600}>
<Window.Content scrollable>
<Section title={"Fire Assist Mainframe: " + (id || "[no tag]")}>
{turrets.length && turrets.map(pd => (
<Section level={2} title={pd.id} key={pd.id} buttons={
<Button
selected={pd.active}
icon="power-off"
onClick={() => act("toggle_active", { target: pd.ref })}>
{pd.active ? "Online" : "Offline"}
</Button>
}>
<LabeledList>
<LabeledList.Item label="Effective range">
{pd.effective_range}
</LabeledList.Item>
<LabeledList.Item label="Reaction wheel delay">
{pd.reaction_wheel_delay}
</LabeledList.Item>
<LabeledList.Item label="Recharge time">
{pd.recharge_time}
</LabeledList.Item>
</LabeledList>
</Section>
)) || (
<Box color="bad">
Error: No weapon systems detected. Please check network connection.
</Box>
)}
</Section>
</Window.Content>
</Window>
);
};
@@ -96,7 +96,7 @@ export const OvermapPanControls = (props, context) => {
<Box>
<Button
disabled={disabled}
selected={selected(1)}
selected={selected(10)}
onClick={() => act(actToDo, { dir: 10 })}
icon="arrow-down"
iconRotation={45} />
File diff suppressed because one or more lines are too long