From 1e9cdf0528aba685df9c0c15d23ae74dc087d35d Mon Sep 17 00:00:00 2001 From: Gandalf <9026500+Gandalf2k15@users.noreply.github.com> Date: Sun, 2 Apr 2023 16:33:46 +0100 Subject: [PATCH] Bluespace Artillery Overhaul(v2.0) (#16680) * wew * e * Delete packs.dm * e * review stuff * e * Update bsa_cannon.dm * Toggle BSA admin command * Update admin_verb.dm * Update admin_verb.dm * Update bsa_cannon.dm * E --- code/modules/admin/admin_verbs.dm | 1 + code/modules/cargo/packs/engineering.dm | 1 - .../modules/bsa_overhaul/code/admin_verb.dm | 10 + .../modules/bsa_overhaul/code/bsa_cannon.dm | 423 +++++++++--------- .../modules/bsa_overhaul/code/bsa_computer.dm | 137 +++--- .../modules/bsa_overhaul/code/station_goal.dm | 30 ++ tgstation.dme | 2 + .../interfaces/BluespaceArtillerySkyrat.tsx | 136 ++++++ 8 files changed, 470 insertions(+), 270 deletions(-) create mode 100644 modular_skyrat/modules/bsa_overhaul/code/admin_verb.dm create mode 100644 modular_skyrat/modules/bsa_overhaul/code/station_goal.dm create mode 100644 tgui/packages/tgui/interfaces/BluespaceArtillerySkyrat.tsx diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index ce125511bf7..7754b2d40e7 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -150,6 +150,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/spawn_mob_spawner, /*SKYRAT EDIT ADDITION*/ /client/proc/spawn_pollution, /*SKYRAT EDIT ADDITION*/ /client/proc/spawn_sunbeam, /*SKYRAT EDIT ADDITION*/ + /client/proc/toggle_bsa, /*SKYRAT EDIT ADDITION*/ )) GLOBAL_PROTECT(admin_verbs_fun) GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character, /datum/admins/proc/beaker_panel)) diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm index 9ed69514af2..b49482766d2 100644 --- a/code/modules/cargo/packs/engineering.dm +++ b/code/modules/cargo/packs/engineering.dm @@ -162,7 +162,6 @@ contains = list(/obj/item/circuitboard/machine/bsa/front, /obj/item/circuitboard/machine/bsa/middle, /obj/item/circuitboard/machine/bsa/back, - /obj/item/circuitboard/machine/bsa/powercore, //SKYRAT EDIT ADDITION /obj/item/circuitboard/computer/bsa_control, ) crate_name= "bluespace artillery parts crate" diff --git a/modular_skyrat/modules/bsa_overhaul/code/admin_verb.dm b/modular_skyrat/modules/bsa_overhaul/code/admin_verb.dm new file mode 100644 index 00000000000..aa0082ad244 --- /dev/null +++ b/modular_skyrat/modules/bsa_overhaul/code/admin_verb.dm @@ -0,0 +1,10 @@ +/client/proc/toggle_bsa() + set category = "Admin.Fun" + set name = "Toggle BSA Control" + set desc = "Toggles the BSA control lock on and off." + + GLOB.bsa_unlock = !GLOB.bsa_unlock + minor_announce("Bluespace Artillery firing protocols have been [GLOB.bsa_unlock? "unlocked" : "locked"]", "Weapons Systems Update:") + + message_admins("[ADMIN_LOOKUPFLW(usr)] [GLOB.bsa_unlock? "unlocked" : "locked"] BSA firing protocols.") + log_admin("[key_name(usr)] [GLOB.bsa_unlock? "unlocked" : "locked"] BSA firing protocols.") diff --git a/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm b/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm index 628864bfad8..ce05af34a25 100644 --- a/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm +++ b/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm @@ -4,41 +4,22 @@ * Requires high amount of power * Requires high level stock parts */ +#define BSA_SYSTEM_READY "SYSTEM READY" +#define BSA_SYSTEM_PREFIRE "! SYSTEM PREFIRING !" +#define BSA_SYSTEM_FIRING "SYSTEM FIRING" +#define BSA_SYSTEM_RELOADING "SYSTEM RELOADING" +#define BSA_SYSTEM_LOW_POWER "SYSTEM POWER LOW" +#define BSA_SYSTEM_CHARGE_CAPACITORS "SYSTEM CHARGING CAPACITORS" -#define SYSTEM_OFFLINE "SYSTEM OFFLINE" -#define SYSTEM_READY "SYSTEM READY" -#define SYSTEM_PREFIRE "SYSTEM PREFIRING" -#define SYSTEM_FIRING "SYSTEM FIRING" -#define SYSTEM_RELOADING "SYSTEM RELOADING" -#define SYSTEM_INTERRUPTED "SYSTEM INTERRUPTED" +#define BSA_RELOAD_TIME 20 SECONDS +#define BSA_FIRE_POWER_THRESHOLD 1000000 // 1 MW -#define SUPERWEAPON_RELOAD_TIME (30 SECONDS) - -/datum/station_goal/bluespace_cannon - name = "Bluespace Artillery" - -/datum/station_goal/bluespace_cannon/get_report() - return list( - "
Our military presence is inadequate in your sector.", - "We need you to construct BSA-[rand(1,99)] Artillery position aboard your station.", - "", - "Base parts are available for shipping via cargo.", - "-Nanotrasen Naval Command", - ).Join("\n") - -/datum/station_goal/bluespace_cannon/on_report() - //Unlock BSA parts - var/datum/supply_pack/engineering/bsa/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/bsa] - P.special_enabled = TRUE - -/datum/station_goal/bluespace_cannon/check_completion() - if(..()) - return TRUE - var/obj/machinery/bsa/full/B = locate() - if(B && !B.machine_stat) - return TRUE - return FALSE +/** + * BSA parts + * + * The parts are only used to build the full machine and are discarded after completion. + */ /obj/machinery/bsa icon = 'icons/obj/machines/particle_accelerator.dmi' density = TRUE @@ -54,12 +35,12 @@ desc = "Generates cannon pulse. Needs to be linked with a fusor." icon_state = "power_box" -/obj/machinery/bsa/back/multitool_act(mob/living/user, obj/item/I) - if(!multitool_check_buffer(user, I)) //make sure it has a data buffer +/obj/machinery/bsa/back/multitool_act(mob/living/user, obj/item/tool) + if(!multitool_check_buffer(user, tool)) //make sure it has a data buffer return - var/obj/item/multitool/M = I - M.buffer = src - to_chat(user, span_notice("You store linkage information in [I]'s buffer.")) + var/obj/item/multitool/multitool = tool + multitool.buffer = src + to_chat(user, span_notice("You store linkage information in [multitool]'s buffer.")) return TRUE /obj/machinery/bsa/front @@ -67,44 +48,48 @@ desc = "Do not stand in front of cannon during operation. Needs to be linked with a fusor." icon_state = "emitter_center" -/obj/machinery/bsa/front/multitool_act(mob/living/user, obj/item/I) - if(!multitool_check_buffer(user, I)) //make sure it has a data buffer +/obj/machinery/bsa/front/multitool_act(mob/living/user, obj/item/tool) + if(!multitool_check_buffer(user, tool)) //make sure it has a data buffer return - var/obj/item/multitool/M = I - M.buffer = src - to_chat(user, span_notice("You store linkage information in [I]'s buffer.")) + var/obj/item/multitool/multitool = tool + multitool.buffer = src + to_chat(user, span_notice("You store linkage information in [multitool]'s buffer.")) return TRUE /obj/machinery/bsa/middle name = "Bluespace Artillery Fusor" desc = "Contents classified by Nanotrasen Naval Command. Needs to be linked with the other BSA parts using a multitool." icon_state = "fuel_chamber" - var/obj/machinery/bsa/back/back - var/obj/machinery/bsa/front/front + /// Our linked back piece + var/datum/weakref/back_piece + /// Our linked front piece + var/datum/weakref/front_piece -/obj/machinery/bsa/middle/multitool_act(mob/living/user, obj/item/I) - if(!multitool_check_buffer(user, I)) +/obj/machinery/bsa/middle/multitool_act(mob/living/user, obj/item/tool) + if(!multitool_check_buffer(user, tool)) return - var/obj/item/multitool/M = I - if(M.buffer) - if(istype(M.buffer, /obj/machinery/bsa/back)) - back = M.buffer - M.buffer = null - to_chat(user, span_notice("You link [src] with [back].")) - else if(istype(M.buffer, /obj/machinery/bsa/front)) - front = M.buffer - M.buffer = null - to_chat(user, span_notice("You link [src] with [front].")) + var/obj/item/multitool/multitool = tool + if(multitool.buffer) + if(istype(multitool.buffer, /obj/machinery/bsa/back)) + back_piece = WEAKREF(multitool.buffer) + to_chat(user, span_notice("You link [src] with [multitool.buffer].")) + multitool.buffer = null + else if(istype(multitool.buffer, /obj/machinery/bsa/front)) + front_piece = multitool.buffer + to_chat(user, span_notice("You link [src] with [multitool.buffer].")) + multitool.buffer = null else - to_chat(user, span_warning("[I]'s data buffer is empty!")) + to_chat(user, span_warning("[multitool]'s data buffer is empty!")) return TRUE /obj/machinery/bsa/middle/proc/check_completion() - if(!front || !back) - return "No linked parts detected!" - if(!front.anchored || !back.anchored || !anchored) + var/obj/machinery/bsa/back/back_part = back_piece.resolve() + var/obj/machinery/bsa/front/front_part = front_piece.resolve() + if(!front_part || !back_part) + return "Some parts are missing!" + if(!front_part.anchored || !back_part.anchored || !anchored) return "Linked parts unwrenched!" - if(front.y != y || back.y != y || !(front.x > x && back.x < x || front.x < x && back.x > x) || front.z != z || back.z != z) + if(front_part.y != y || back_part.y != y || !(front_part.x > x && back_part.x < x || front_part.x < x && back_part.x > x) || front_part.z != z || back_part.z != z) return "Parts misaligned!" if(!has_space()) return "Not enough free space!" @@ -121,35 +106,77 @@ x_min = x + 4 x_max = x - 6 - for(var/turf/T in block(locate(x_min,y-1,z),locate(x_max,y+1,z))) - if(T.density || isspaceturf(T)) + for(var/turf/our_turf in block(locate(x_min, y - 1, z),locate(x_max, y + 1, z))) + if(our_turf.density || isspaceturf(our_turf)) return FALSE return TRUE /obj/machinery/bsa/middle/proc/get_cannon_direction() - if(front.x > x && back.x < x) + var/obj/machinery/bsa/back/back_part = back_piece.resolve() + var/obj/machinery/bsa/front/front_part = front_piece.resolve() + if(!back_part || !front_part) + return FALSE + if(front_part.x > x && back_part.x < x) return EAST - else if(front.x < x && back.x > x) + else if(front_part.x < x && back_part.x > x) return WEST + +/** + * The full BSA cannon + * + * This operates by charging a "capacitor" bank which is then discharged in the beam. + * The capacitor bank is charged during the power up phase, it essentially drains the connected powernet until it reaches it's target power, and then fires. + */ /obj/machinery/bsa/full name = "Bluespace Artillery" desc = "Long range bluespace artillery." icon = 'icons/obj/lavaland/cannon.dmi' icon_state = "cannon_west" - var/static/mutable_appearance/top_layer - use_power = NO_POWER_USE - var/ex_power = 5 - var/power_used_per_shot = 20000000 //enough to kil standard apc - todo : make this use wires instead and scale explosion power with it - var/ready - var/system_state = SYSTEM_OFFLINE - max_integrity = 2000 - var/obj/machinery/computer/bsa_control/control_unit + use_power = NO_POWER_USE // We use power when we're fired. pixel_y = -32 pixel_x = -192 bound_width = 352 bound_x = -192 appearance_flags = NONE //Removes default TILE_BOUND + max_integrity = 2000 + + /// A reference to our topmost mutable layer + var/static/mutable_appearance/top_layer + /// The total collected power, this is calculated in the "power up" phase of the superweapon. + var/capacitor_power = 0 + /// The cap on how much power we can suck from the powernet per cycle + var/power_suck_cap = 1000000 // 1 MEGAWATT + /// The set target power + var/target_power = 1000000 + /// How much charge our capacitors can hold + var/max_charge = 100000000 + /// The current system state + var/system_state = BSA_SYSTEM_READY + /// Our connected control computer + var/obj/machinery/computer/bsa_control/control_computer + +/obj/machinery/bsa/full/Initialize(mapload, cannon_direction = WEST) + . = ..() + top_layer = top_layer || mutable_appearance(icon, layer = ABOVE_MOB_LAYER) + switch(cannon_direction) + if(WEST) + setDir(WEST) + top_layer.icon_state = "top_west" + icon_state = "cannon_west" + if(EAST) + setDir(EAST) + pixel_x = -128 + bound_x = -128 + top_layer.icon_state = "top_east" + icon_state = "cannon_east" + add_overlay(top_layer) + reload() + START_PROCESSING(SSobj, src) + +/obj/machinery/bsa/full/Destroy() + control_computer = null + return ..() /obj/machinery/bsa/full/wrench_act(mob/living/user, obj/item/I) return FALSE @@ -178,89 +205,111 @@ return locate(world.maxx,y,z) return get_turf(src) -/obj/machinery/bsa/full/Initialize(mapload, cannon_direction = WEST) - . = ..() - top_layer = top_layer || mutable_appearance(icon, layer = ABOVE_MOB_LAYER) - switch(cannon_direction) - if(WEST) - setDir(WEST) - top_layer.icon_state = "top_west" - icon_state = "cannon_west" - if(EAST) - setDir(EAST) - pixel_x = -128 - bound_x = -128 - top_layer.icon_state = "top_east" - icon_state = "cannon_east" - add_overlay(top_layer) - reload() +// Firing procs -/obj/machinery/bsa/full/proc/pre_fire(mob/user, turf/bullseye) - if(system_state == SYSTEM_READY) - priority_announce("BLUESPACE TARGETING PARAMETERS SET, PREIGNITION STARTING... FIRING IN T-20 SECONDS!", "BLUESPACE ARTILLERY", ANNOUNCER_BLUESPACEARTY) - alert_sound_to_playing('modular_skyrat/modules/bsa_overhaul/sound/superlaser_prefire.ogg', override_volume = TRUE) - system_state = SYSTEM_PREFIRE - message_admins("[user] has started the fire cycle of [src]! Firing at: [ADMIN_VERBOSEJMP(bullseye)]") - set_light(5, 5, COLOR_BLUE_LIGHT) - addtimer(CALLBACK(src, PROC_REF(fire), user, bullseye), 20 SECONDS, TIMER_CLIENT_TIME) - START_PROCESSING(SSobj, src) - return system_state +/** + * Collect charge + * + * Cycles the powernet and sucks power from it and shoves it into the capacitors. + */ +/obj/machinery/bsa/full/proc/charge_capacitors() + if(capacitor_power >= target_power) + if(capacitor_power < BSA_FIRE_POWER_THRESHOLD) + system_state = BSA_SYSTEM_LOW_POWER + else + system_state = BSA_SYSTEM_READY + return + var/area/our_area = get_area(src) + if(!our_area) //wtf + return + var/obj/machinery/power/apc/our_apc = our_area.apc + if(!our_apc) + return + var/obj/machinery/power/terminal/our_terminal = our_apc.terminal + if(!our_terminal) + return + var/datum/powernet/our_powernet = our_terminal.powernet + if(!our_powernet) + return + + var/charge_to_pull = power_suck_cap + + var/charge_to_full = max_charge - capacitor_power + + if(charge_to_full < power_suck_cap) + charge_to_pull = charge_to_full + + var/max_power_draw = charge_to_pull > our_powernet.avail ? our_powernet.avail : charge_to_pull + + our_powernet.avail -= max_power_draw + + capacitor_power += max_power_draw + +/** + * Gets the currently available power cap + */ +/obj/machinery/bsa/full/proc/get_available_powercap() + var/area/our_area = get_area(src) + if(!our_area) //wtf + return + var/obj/machinery/power/apc/our_apc = our_area.apc + if(!our_apc) + return + var/obj/machinery/power/terminal/our_terminal = our_apc.terminal + if(!our_terminal) + return + var/datum/powernet/our_powernet = our_terminal.powernet + if(!our_powernet) + return + + return our_powernet.avail /obj/machinery/bsa/full/process() - if(system_state == SYSTEM_PREFIRE) //We drain this while either action is being performed. - if(isnull(control_unit.core)) - system_state = SYSTEM_INTERRUPTED - STOP_PROCESSING(SSobj, src) - var/obj/structure/cable/attached = control_unit.core.attached - var/datum/powernet/PN = attached.powernet - if(PN) - // found a powernet, so drain up to max power from it - var/drained = min(power_used_per_shot, attached.newavail()) - attached.add_delayedload(drained) - // if tried to drain more than available on powernet - // now look for APCs and drain their cells - if(drained < power_used_per_shot) - for(var/obj/machinery/power/terminal/T in PN.nodes) - if(istype(T.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = T.master - if(A.operating && A.cell) - A.cell.charge = max(0, A.cell.charge - 50) - if(A.charging == 2) // If the cell was full - A.charging = 1 // It's no longer full - if(drained >= power_used_per_shot) - break - else - system_state = SYSTEM_INTERRUPTED - STOP_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) + if(system_state == BSA_SYSTEM_CHARGE_CAPACITORS) + charge_capacitors() + if(capacitor_power >= target_power) + return -/obj/machinery/bsa/full/proc/fire(mob/user, turf/bullseye) - if(!system_state == SYSTEM_PREFIRE || !control_unit.core || machine_stat) - minor_announce("BLUESPACE ARTILLERY FIRE FAILURE!", "BLUESPACE ARTILLERY", TRUE) - system_state = SYSTEM_READY +/obj/machinery/bsa/full/proc/pre_fire(mob/user, turf/bullseye) + if(system_state != BSA_SYSTEM_READY) return - system_state = SYSTEM_FIRING + system_state = BSA_SYSTEM_PREFIRE + priority_announce("BLUESPACE TARGETING PARAMETERS SET, PREIGNITION STARTING... CAPACITOR CHARGE AT [round(capacitor_power / 1000000, 0.1)] MW, FIRING IN T-20 SECONDS!", "BLUESPACE ARTILLERY", ANNOUNCER_BLUESPACEARTY) + alert_sound_to_playing('modular_skyrat/modules/bsa_overhaul/sound/superlaser_prefire.ogg', override_volume = TRUE) + message_admins("[user] has started the fire cycle of [src]! Firing at: [ADMIN_VERBOSEJMP(bullseye)]") + set_light(5, 5, COLOR_BLUE_LIGHT) + addtimer(CALLBACK(src, PROC_REF(fire), user, bullseye), 20 SECONDS, TIMER_CLIENT_TIME) + +/// Initiates the fire sequence of the gun providing it has the required power. +/obj/machinery/bsa/full/proc/fire(mob/user, turf/bullseye) + // Check if the system is already firing or if the machine is broken + if(system_state != BSA_SYSTEM_PREFIRE || machine_stat) + minor_announce("BLUESPACE ARTILLERY FIRE FAILURE!", "BLUESPACE ARTILLERY", TRUE) + system_state = BSA_SYSTEM_READY + return + system_state = BSA_SYSTEM_FIRING + // We need to reload the chamber so it's ready to fire another one reload() - use_power(power_used_per_shot) + // The turf directly in front of the gun. var/turf/point = get_front_turf() + // the target turf var/turf/target = get_target_turf() + // Anything that blocks the BSA beam, if it's blocked, it hits that thing var/atom/movable/blocker - for(var/T in get_line(get_step(point, dir), target)) - var/turf/tile = T - if(SEND_SIGNAL(tile, COMSIG_ATOM_BSA_BEAM) & COMSIG_ATOM_BLOCKS_BSA_BEAM) - blocker = tile + // Now we absolutely destroy everything in the beams path. + for(var/turf/iterating_turf as anything in get_line(get_step(point, dir), target)) + if(SEND_SIGNAL(iterating_turf, COMSIG_ATOM_BSA_BEAM) & COMSIG_ATOM_BLOCKS_BSA_BEAM) + blocker = iterating_turf else - for(var/AM in tile) - var/atom/movable/stuff = AM - if(SEND_SIGNAL(stuff, COMSIG_ATOM_BSA_BEAM) & COMSIG_ATOM_BLOCKS_BSA_BEAM) - blocker = stuff + for(var/atom/movable/iterating_atom in iterating_turf) + if(SEND_SIGNAL(iterating_atom, COMSIG_ATOM_BSA_BEAM) & COMSIG_ATOM_BLOCKS_BSA_BEAM) + blocker = iterating_atom break if(blocker) - target = tile + target = iterating_turf break else - SSexplosions.highturf += tile //also fucks everything else on the turf + SSexplosions.highturf += iterating_turf //also fucks everything else on the turf point.Beam(target, icon_state = "bsa_beam", time = 5 SECONDS, maxdistance = world.maxx) //ZZZAP new /obj/effect/temp_visual/bsa_splash(point, dir) @@ -268,29 +317,32 @@ message_admins("[ADMIN_LOOKUPFLW(user)] has launched an artillery strike targeting [ADMIN_VERBOSEJMP(bullseye)].") log_game("[key_name(user)] has launched an artillery strike targeting [AREACOORD(bullseye)].") minor_announce("BLUESPACE ARTILLERY FIRE SUCCESSFUL! DIRECT HIT!", "BLUESPACE ARTILLERY", TRUE) - explosion(bullseye, ex_power, ex_power*2, ex_power*4) + create_calculated_explosion(bullseye) alert_sound_to_playing('modular_skyrat/modules/bsa_overhaul/sound/superlaser_firing.ogg', override_volume = TRUE) + capacitor_power = 0 else message_admins("[ADMIN_LOOKUPFLW(user)] has launched an artillery strike targeting [ADMIN_VERBOSEJMP(bullseye)] but it was blocked by [blocker] at [ADMIN_VERBOSEJMP(target)].") log_game("[key_name(user)] has launched an artillery strike targeting [AREACOORD(bullseye)] but it was blocked by [blocker] at [AREACOORD(target)].") - minor_announce("BLUESPACE ARTILLERY FIRE FAILURE!", "BLUESPACE ARTILLERY", TRUE) + minor_announce("BLUESPACE ARTILLERY MALFUNCTION!", "BLUESPACE ARTILLERY", TRUE) +/// Reloads the BSA. /obj/machinery/bsa/full/proc/reload() - system_state = SYSTEM_RELOADING + system_state = BSA_SYSTEM_RELOADING set_light(0) STOP_PROCESSING(SSobj, src) - addtimer(CALLBACK(src, PROC_REF(ready_cannon)), SUPERWEAPON_RELOAD_TIME) + addtimer(CALLBACK(src, .proc/ready_cannon), BSA_RELOAD_TIME) /obj/machinery/bsa/full/proc/ready_cannon() - system_state = SYSTEM_READY + if(capacitor_power < BSA_FIRE_POWER_THRESHOLD) + system_state = BSA_SYSTEM_LOW_POWER + else + system_state = BSA_SYSTEM_READY STOP_PROCESSING(SSobj, src) -/obj/machinery/bsa/full/Destroy() - if(control_unit) - if(control_unit.cannon) - control_unit.cannon = null - control_unit = null - . = ..() +/obj/machinery/bsa/full/proc/create_calculated_explosion(atom/target) + var/calculated_explosion_power = capacitor_power / 10000000 // Maximum explosion range of 10, 20, 40 + + explosion(target, calculated_explosion_power, calculated_explosion_power * 2, calculated_explosion_power * 4, ignorecap = TRUE) // This should ignore cap so we can achieve our maximum potential /obj/structure/filler name = "big machinery part" @@ -306,70 +358,5 @@ parent = null . = ..() -/obj/machinery/bsa_powercore - name = "artillery powercore" - desc = "A core that transfers power from the local power net and transfers it to nearby artillery pieces." - icon = 'modular_skyrat/modules/bsa_overhaul/icons/bsa_parts.dmi' - icon_state = "powercore_off" - density = TRUE - ///The attached power cable that the power core will SUCC energy from. - var/obj/machinery/computer/bsa_control/control_unit - var/obj/structure/cable/attached -/obj/machinery/bsa_powercore/multitool_act(mob/living/user, obj/item/I) - if(!multitool_check_buffer(user, I)) //make sure it has a data buffer - return - var/obj/item/multitool/M = I - M.buffer = src - to_chat(user, span_notice("You store linkage information in [I]'s buffer.")) - return TRUE -/obj/machinery/bsa_powercore/wrench_act(mob/living/user, obj/item/I) - ..() - default_unfasten_wrench(user, I, 10) - if(anchored) - var/turf/T = loc - if(isturf(T) && T.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE) - attached = locate() in T - if(!attached) - set_light(0) - to_chat(user, span_warning("[src] must be placed over an exposed, powered cable node!")) - else - set_light(5) - to_chat(user, span_notice("You attach [src] to the cable below.")) - else - attached = null - update_appearance() - return TRUE - -/obj/machinery/bsa_powercore/Destroy() - if(control_unit) - control_unit.core = null - control_unit = null - attached = null - . = ..() - -/obj/machinery/bsa_powercore/update_icon(updates) - . = ..() - if(attached) - icon_state = "powercore_on" - else - icon_state = "powercore_off" - -/obj/item/circuitboard/machine/bsa/powercore - name = "Bluespace Artillery Powercore (Machine Board)" - greyscale_colors = CIRCUIT_COLOR_COMMAND - build_path = /obj/machinery/bsa_powercore - req_components = list( - /datum/stock_part/capacitor/tier4 = 5, - /obj/item/stock_parts/cell/bluespace = 1, - /obj/item/stack/cable_coil = 2, - ) - -#undef SYSTEM_OFFLINE -#undef SYSTEM_READY -#undef SYSTEM_PREFIRE -#undef SYSTEM_FIRING -#undef SYSTEM_RELOADING -#undef SYSTEM_INTERRUPTED -#undef SUPERWEAPON_RELOAD_TIME diff --git a/modular_skyrat/modules/bsa_overhaul/code/bsa_computer.dm b/modular_skyrat/modules/bsa_overhaul/code/bsa_computer.dm index e7a3f84de50..4de076b893f 100644 --- a/modular_skyrat/modules/bsa_overhaul/code/bsa_computer.dm +++ b/modular_skyrat/modules/bsa_overhaul/code/bsa_computer.dm @@ -1,4 +1,8 @@ -//CONTROL COMPUTER +/** + * The control computer + * + * Responsible for cannon firing protocols. + */ /obj/machinery/computer/bsa_control name = "bluespace artillery control" @@ -6,33 +10,17 @@ circuit = /obj/item/circuitboard/computer/bsa_control icon = 'modular_skyrat/modules/fixing_missing_icons/particle_accelerator.dmi' icon_state = "control_boxp" - var/obj/machinery/bsa_powercore/core //The moveable power core link - var/obj/machinery/bsa/full/cannon + /// A weakref to our cannon + var/datum/weakref/connected_cannon + /// The current system status of the gun var/notice - var/target + /// Our target... WHY NOT WEEKREFF + var/atom/target + /// Are we allowing the gun to target areas? var/area_aim = FALSE //should also show areas for targeting connectable = FALSE //connecting_computer change: since icon_state is not a typical console, it cannot be connectable. -/obj/machinery/computer/bsa_control/multitool_act(mob/living/user, obj/item/I) - if(!multitool_check_buffer(user, I)) - return - var/obj/item/multitool/M = I - if(M.buffer) - if(istype(M.buffer, /obj/machinery/bsa_powercore)) - if(!cannon) - to_chat(user, span_warning("There is no cannon linked to this control unit!")) - return FALSE - if(core) - to_chat(user, span_warning("There is already a core linked to this control unit!")) - return FALSE - core = M.buffer - core.control_unit = src - M.buffer = null - to_chat(user, span_notice("You link [src] with [core].")) - else - to_chat(user, span_warning("[I]'s data buffer is empty!")) - return TRUE /obj/machinery/computer/bsa_control/ui_state(mob/user) return GLOB.physical_state @@ -41,15 +29,22 @@ . = ..() ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "BluespaceArtillery", name) + ui = new(user, src, "BluespaceArtillerySkyrat", name) ui.open() /obj/machinery/computer/bsa_control/ui_data() var/list/data = list() - data["ready"] = cannon ? cannon.ready : FALSE + var/obj/machinery/bsa/full/cannon = connected_cannon?.resolve() + data["connected"] = cannon data["notice"] = notice data["unlocked"] = GLOB.bsa_unlock + data["powernet_power"] = cannon?.get_available_powercap() + data["power_suck_cap"] = cannon?.power_suck_cap + data["status"] = cannon?.system_state + data["capacitor_charge"] = cannon?.capacitor_power + data["target_capacitor_charge"] = cannon?.target_power + data["max_capacitor_charge"] = cannon?.max_charge if(target) data["target"] = get_target_name() return data @@ -61,7 +56,7 @@ switch(action) if("build") - cannon = deploy() + deploy() . = TRUE if("fire") fire(usr) @@ -69,15 +64,42 @@ if("recalibrate") calibrate(usr) . = TRUE + if("charge") + charge() + if("capacitor_target_change") + change_capacitor_target(params["capacitor_target"]) update_appearance() +/** + * Changes the target charge for the internal capacitors + */ +/obj/machinery/computer/bsa_control/proc/change_capacitor_target(new_target) + var/obj/machinery/bsa/full/cannon = connected_cannon?.resolve() + if(!cannon) + return + cannon.target_power = new_target + +/** + * Takes power from the powernet and inserts it into the gun. + */ +/obj/machinery/computer/bsa_control/proc/charge() + var/obj/machinery/bsa/full/cannon = connected_cannon?.resolve() + if(!cannon) + return + if(cannon.system_state != BSA_SYSTEM_READY && cannon.system_state != BSA_SYSTEM_LOW_POWER) + return + cannon.system_state = BSA_SYSTEM_CHARGE_CAPACITORS + +/** + * Sets a target for the gun to use. + */ /obj/machinery/computer/bsa_control/proc/calibrate(mob/user) if(!GLOB.bsa_unlock) return var/list/gps_locators = list() - for(var/datum/component/gps/G in GLOB.GPS_list) //nulls on the list somehow - if(G.tracking) - gps_locators[G.gpstag] = G + for(var/datum/component/gps/iterating_gps in GLOB.GPS_list) //nulls on the list somehow + if(iterating_gps.tracking) + gps_locators[iterating_gps.gpstag] = iterating_gps var/list/options = gps_locators if(area_aim) @@ -89,23 +111,33 @@ return target = options[victim] +/** + * Returns the targets name, simple. + */ /obj/machinery/computer/bsa_control/proc/get_target_name() if(istype(target, /area)) return get_area_name(target, TRUE) else if(istype(target, /datum/component/gps)) - var/datum/component/gps/G = target - return G.gpstag + var/datum/component/gps/gps = target + return gps.gpstag +/** + * Locates the impact turf based off of if it's an area or a GPS. + */ /obj/machinery/computer/bsa_control/proc/get_impact_turf() if(istype(target, /area)) return pick(get_area_turfs(target)) else if(istype(target, /datum/component/gps)) - var/datum/component/gps/G = target - return get_turf(G.parent) + var/datum/component/gps/gps = target + return get_turf(gps.parent) +/** + * Initiates the cannon fire protocol + */ /obj/machinery/computer/bsa_control/proc/fire(mob/user) - if(!core) - notice = "Core not detected!" + var/obj/machinery/bsa/full/cannon = connected_cannon?.resolve() + if(!cannon) + notice = "System error" return if((cannon.machine_stat & BROKEN)) notice = "Cannon integrity failure!" @@ -115,11 +147,17 @@ return notice = cannon.pre_fire(user, get_impact_turf()) +/** + * Deploy + * + * Deploys the cannon and deletes the old parts. + */ /obj/machinery/computer/bsa_control/proc/deploy(force=FALSE) var/obj/machinery/bsa/full/prebuilt = locate() in range(7) //In case of adminspawn if(prebuilt) - prebuilt.control_unit = src - return prebuilt + prebuilt.control_computer = src + connected_cannon = WEAKREF(prebuilt) + return var/obj/machinery/bsa/middle/centerpiece = locate() in range(7) if(!centerpiece) @@ -129,19 +167,16 @@ if(notice) return null //Totally nanite construction system not an immersion breaking spawning - var/datum/effect_system/fluid_spread/smoke/s = new - s.set_up(4, location = get_turf(centerpiece)) - s.start() - var/obj/machinery/bsa/full/cannon = new(get_turf(centerpiece),centerpiece.get_cannon_direction()) - cannon.control_unit = src - qdel(centerpiece.front) - qdel(centerpiece.back) + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.set_up(4, location = get_turf(centerpiece)) + smoke.start() + var/obj/machinery/bsa/full/cannon = new(get_turf(centerpiece), centerpiece.get_cannon_direction()) + cannon.control_computer = src + if(centerpiece.front_piece) + qdel(centerpiece.front_piece.resolve()) + if(centerpiece.back_piece) + qdel(centerpiece.back_piece.resolve()) qdel(centerpiece) - return cannon + connected_cannon = WEAKREF(cannon) + -/obj/machinery/computer/bsa_control/Destroy() - if(cannon) - cannon.control_unit = null - cannon = null - core = null - . = ..() diff --git a/modular_skyrat/modules/bsa_overhaul/code/station_goal.dm b/modular_skyrat/modules/bsa_overhaul/code/station_goal.dm new file mode 100644 index 00000000000..370318f21ec --- /dev/null +++ b/modular_skyrat/modules/bsa_overhaul/code/station_goal.dm @@ -0,0 +1,30 @@ +/** + * Bluespace artillery goal + * + * Requires the crew to construct an artillery piece by shift end. + */ + +/datum/station_goal/bluespace_cannon + name = "Bluespace Artillery" + +/datum/station_goal/bluespace_cannon/get_report() + return list( + "
Our military presence is inadequate in your sector.", + "We need you to construct BSA-[rand(1,99)] Artillery position aboard your station.", + "", + "Base parts are available for shipping via cargo.", + "-Nanotrasen Naval Command", + ).Join("\n") + +/datum/station_goal/bluespace_cannon/on_report() + //Unlock BSA parts + var/datum/supply_pack/engineering/bsa/parts = SSshuttle.supply_packs[/datum/supply_pack/engineering/bsa] + parts.special_enabled = TRUE + +/datum/station_goal/bluespace_cannon/check_completion() + if(..()) + return TRUE + var/obj/machinery/bsa/full/built_cannon = locate() + if(built_cannon && !built_cannon.machine_stat) + return TRUE + return FALSE diff --git a/tgstation.dme b/tgstation.dme index 28d7c546dba..8b472b5f34e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5719,8 +5719,10 @@ #include "modular_skyrat\modules\bongs\code\bong.dm" #include "modular_skyrat\modules\borg_buffs\code\robot.dm" #include "modular_skyrat\modules\borg_buffs\code\snack_dispensor.dm" +#include "modular_skyrat\modules\bsa_overhaul\code\admin_verb.dm" #include "modular_skyrat\modules\bsa_overhaul\code\bsa_cannon.dm" #include "modular_skyrat\modules\bsa_overhaul\code\bsa_computer.dm" +#include "modular_skyrat\modules\bsa_overhaul\code\station_goal.dm" #include "modular_skyrat\modules\bsrpd\code\bsrpd.dm" #include "modular_skyrat\modules\bulletrebalance\code\sniper.dm" #include "modular_skyrat\modules\cargo\code\goodies.dm" diff --git a/tgui/packages/tgui/interfaces/BluespaceArtillerySkyrat.tsx b/tgui/packages/tgui/interfaces/BluespaceArtillerySkyrat.tsx new file mode 100644 index 00000000000..05b09287a67 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BluespaceArtillerySkyrat.tsx @@ -0,0 +1,136 @@ +import { BooleanLike } from 'common/react'; +import { useBackend } from '../backend'; +import { Box, Button, LabeledList, NoticeBox, Section, Slider } from '../components'; +import { Window } from '../layouts'; +import { formatPower } from '../format'; + +type Data = { + connected: BooleanLike; + notice: string; + unlocked: BooleanLike; + target: string; + powernet_power: number; + capacitor_charge: number; + target_capacitor_charge: number; + max_capacitor_charge: number; + status: string; +}; + +export const BluespaceArtillerySkyrat = (props, context) => { + const { act, data } = useBackend(context); + const { + notice, + connected, + unlocked, + target, + powernet_power, + capacitor_charge, + target_capacitor_charge, + max_capacitor_charge, + status, + } = data; + + return ( +