diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm index b0623b8d7fc88..8fb377e1b77f9 100644 --- a/code/game/objects/effects/effect_system/effects_sparks.dm +++ b/code/game/objects/effects/effect_system/effects_sparks.dm @@ -28,7 +28,7 @@ return INITIALIZE_HINT_LATELOAD /obj/effect/particle_effect/sparks/LateInitialize() - flick("sparks", src) // replay the animation + flick(icon_state, src) // replay the animation playsound(src, "sparks", 100, TRUE) var/turf/T = loc if(isturf(T)) @@ -59,3 +59,17 @@ /datum/effect_system/lightning_spread effect_type = /obj/effect/particle_effect/sparks/electricity + +// shield sparks +/obj/effect/particle_effect/sparks/shield + name = "shield sparks" + icon_state = "shieldsparkles" + +/obj/effect/particle_effect/sparks/shield/Initialize(mapload) + . = ..() + // every particle has a little different color + var/generator/gen_color = generator("color", LIGHT_COLOR_WHITE, LIGHT_COLOR_ELECTRIC_CYAN) + var/rand_color = gen_color.Rand() + color = rand_color + set_light_color(rand_color) + diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 0a3799447ca66..687f9da811192 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -131,8 +131,8 @@ update_appearance() // connect the machine to a powernet if a node cable is present on the turf -/obj/machinery/power/proc/connect_to_network() - var/turf/T = src.loc +/obj/machinery/power/proc/connect_to_network(var/turf/turf = loc) + var/turf/T = turf if(!T || !istype(T)) return FALSE diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index 9c47ddb47c5d5..a36f29624d4a5 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -65,3 +65,7 @@ /obj/machinery/power/terminal/wirecutter_act(mob/living/user, obj/item/I) dismantle(user, I) return TRUE + +/obj/machinery/power/terminal/invisible + name = "" + icon_state = "" diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index 4fb1b79746a18..fa168fb5d3b29 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -25,7 +25,7 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) /datum/station_goal/bluespace_cannon/check_completion() if(..()) return TRUE - var/obj/machinery/bsa/full/B = locate() + var/obj/machinery/power/bsa/full/B = locate() if(B && !B.machine_stat) return TRUE return FALSE @@ -129,26 +129,51 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) return WEST -/obj/machinery/bsa/full +/obj/machinery/power/bsa/full name = "Bluespace Artillery" desc = "Long range bluespace artillery." icon = 'icons/obj/lavaland/cannon.dmi' icon_state = "cannon_west" + var/base_battery_icon_state = "bsa_west_capacitor" var/static/mutable_appearance/top_layer var/ex_power = 3 - var/power_used_per_shot = 2000000 //enough to kil standard apc - todo : make this use wires instead and scale explosion power with it var/ready + + var/power_used_per_shot = 5000000 + var/obj/item/stock_parts/cell/cell + var/obj/machinery/power/terminal/invisible/terminal + use_power = NO_POWER_USE + idle_power_usage = 50 // when idle + active_power_usage = INFINITY // how much you can charge at once + var/charge_efficiency = 0.6 // 60% of power is stored in the cell + pixel_y = -32 pixel_x = -192 bound_width = 352 bound_x = -192 + density = TRUE appearance_flags = NONE //Removes default TILE_BOUND resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF -/obj/machinery/bsa/full/wrench_act(mob/living/user, obj/item/I) + var/sound/select_sound = 'sound/machines/bsa/bsa_charge.ogg' + var/select_sound_length = 17 SECONDS + + var/sound/fire_sound = 'sound/machines/bsa/bsa_fire.ogg' + var/winding_up = FALSE // if true, sparks will be generated in the bullseye + + var/last_charge_quarter = 0 + + + +/obj/machinery/power/bsa/full/wrench_act(mob/living/user, obj/item/I) return FALSE -/obj/machinery/bsa/full/proc/get_front_turf() +/obj/machinery/power/bsa/full/Destroy() + . = ..() + QDEL_NULL(cell) + QDEL_NULL(terminal) + +/obj/machinery/power/bsa/full/proc/get_front_turf() switch(dir) if(WEST) return locate(x - 7,y,z) @@ -156,7 +181,7 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) return locate(x + 4,y,z) return get_turf(src) -/obj/machinery/bsa/full/proc/get_back_turf() +/obj/machinery/power/bsa/full/proc/get_back_turf() switch(dir) if(WEST) return locate(x + 4,y,z) @@ -164,7 +189,7 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) return locate(x - 6,y,z) return get_turf(src) -/obj/machinery/bsa/full/proc/get_target_turf() +/obj/machinery/power/bsa/full/proc/get_target_turf() switch(dir) if(WEST) return locate(1,y,z) @@ -172,25 +197,73 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) return locate(world.maxx,y,z) return get_turf(src) -/obj/machinery/bsa/full/Initialize(mapload, cannon_direction = WEST) +/obj/machinery/power/bsa/full/proc/make_terminal(turf/T) + // create a terminal object at the same position as original turf loc + // wires will attach to this + terminal = new /obj/machinery/power/terminal/invisible(T) + terminal.master = src + +/obj/machinery/power/bsa/full/Initialize(mapload, cannon_direction = WEST) . = ..() + cell = new /obj/item/stock_parts/cell(src, 5000000) + cell.charge = 0 top_layer = top_layer || mutable_appearance(icon, layer = ABOVE_MOB_LAYER) switch(cannon_direction) if(WEST) setDir(WEST) pixel_x = -192 - top_layer.icon_state = "top_west" - icon_state = "cannon_west" if(EAST) setDir(EAST) - top_layer.icon_state = "top_east" - icon_state = "cannon_east" - add_overlay(top_layer) - reload() + update_icon_state() + make_terminal(get_back_turf()) + + +/obj/machinery/power/bsa/full/update_icon_state() + . = ..() + icon_state = "cannon_[dir2text(dir)]" + base_battery_icon_state = "bsa_[dir2text(dir)]_capacitor" -/obj/machinery/bsa/full/proc/fire(mob/user, turf/bullseye) - reload() +/obj/machinery/power/bsa/full/update_overlays() + . = ..() + cut_overlays() + add_overlay(top_layer) + top_layer.icon_state = "top_[dir2text(dir)]" + + var/charge_quarter = FLOOR(cell.percent() / 25, 1) + var/charge_sound = 'sound/machines/apc/PowerSwitch_Off.ogg' + if(charge_quarter >= 1) + add_overlay("[base_battery_icon_state]_25") + if(charge_quarter >= 2) + add_overlay("[base_battery_icon_state]_50") + if(charge_quarter >= 3) + add_overlay("[base_battery_icon_state]_75") + if(charge_quarter >= 4) + add_overlay("[base_battery_icon_state]_100") + charge_sound = 'sound/machines/apc/PowerUp_001.ogg' + if(charge_quarter > last_charge_quarter) + playsound(get_turf(src), charge_sound, 25, 1) + + +/obj/machinery/power/bsa/full/proc/charge_up(mob/user, turf/bullseye) + if(!cell.use(power_used_per_shot)) + return FALSE + var/sound/charge_up = sound(select_sound) + playsound(get_turf(src), charge_up, 50, 1) + var/timerid = addtimer(CALLBACK(src, PROC_REF(fire), user, bullseye), select_sound_length, TIMER_STOPPABLE) + winding_up = TRUE + var/list/turfs = spiral_range_turfs(ex_power * 2, bullseye) + var/base_cooldown = 2 SECONDS + var/cooldown = base_cooldown + while(winding_up) + if(QDELETED(src)) + break + new /obj/effect/particle_effect/sparks/shield(pick(turfs)) + cooldown = base_cooldown * ((timeleft(timerid)) / select_sound_length) + sleep(cooldown) +/obj/machinery/power/bsa/full/proc/fire(mob/user, turf/bullseye) + winding_up = FALSE + playsound(get_turf(src), fire_sound, 50, 1, world.maxx) var/turf/point = get_front_turf() var/turf/target = get_target_turf() var/atom/movable/blocker @@ -222,16 +295,26 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) log_game("[key_name(user)] has launched an artillery strike targeting [AREACOORD(bullseye)] but it was blocked by [blocker] at [AREACOORD(target)].") -/obj/machinery/bsa/full/proc/reload() +/obj/machinery/power/bsa/full/proc/reload() ready = FALSE - use_power(power_used_per_shot) ui_update() addtimer(CALLBACK(src,"ready_cannon"),600) -/obj/machinery/bsa/full/proc/ready_cannon() +/obj/machinery/power/bsa/full/proc/ready_cannon() ready = TRUE ui_update() +/obj/machinery/power/bsa/full/process(delta_time) + if(cell.percent() >= 100 || terminal.surplus() < 1) + return + terminal.add_load(idle_power_usage) + var/charge = clamp(terminal.surplus() * delta_time, 0, active_power_usage) + terminal.add_load(charge) + cell.give(charge * charge_efficiency) + update_appearance(UPDATE_OVERLAYS) + last_charge_quarter = FLOOR(cell.percent() / 25, 1) + ui_update() + /obj/structure/filler name = "big machinery part" density = TRUE @@ -254,7 +337,6 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) canSmoothWith = null - var/datum/weakref/cannon_ref var/notice var/target @@ -272,12 +354,14 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) //Missing updates for: target GPS name changes /obj/machinery/computer/bsa_control/ui_data() - var/obj/machinery/bsa/full/cannon = cannon_ref?.resolve() + var/obj/machinery/power/bsa/full/cannon = cannon_ref?.resolve() var/list/data = list() data["ready"] = cannon ? cannon.ready : FALSE data["connected"] = cannon data["notice"] = notice data["unlocked"] = GLOB.bsa_unlock + data["charge"] = cannon ? cannon.cell.charge : 0 + data["max_charge"] = cannon ? cannon.cell.maxcharge : 0 if(target) data["target"] = get_target_name() else @@ -303,20 +387,20 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) /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 + var/list/targets = list() + // Find all active GPS + for(var/datum/component/gps/G in GLOB.GPS_list) if(G.tracking) - gps_locators[G.gpstag] = G + targets[G.gpstag] = G - var/list/options = gps_locators if(area_aim) - options += GLOB.teleportlocs - var/victim = tgui_input_list(user, "Select target", "Artillery Targeting", options) + targets += GLOB.teleportlocs + var/victim = tgui_input_list(user, "Select target", "Artillery Targeting", targets) if(isnull(victim)) return - if(isnull(options[victim])) + if(isnull(targets[victim])) return - target = options[victim] + target = targets[victim] var/datum/component/gps/log_target = target log_game("[key_name(user)] has aimed the bluespace artillery strike (BSA) at [get_area_name(log_target.parent)].") @@ -335,19 +419,20 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) var/datum/component/gps/G = target return get_turf(G.parent) + /obj/machinery/computer/bsa_control/proc/fire(mob/user) - var/obj/machinery/bsa/full/cannon = cannon_ref?.resolve() + var/obj/machinery/power/bsa/full/cannon = cannon_ref?.resolve() if(!cannon) notice = "No Cannon Exists!" return - if(cannon.machine_stat) - notice = "Cannon unpowered!" + if(cannon.cell.percent() < 100) + notice = "Cannon doesn't have enough charge!" return notice = null - cannon.fire(user, get_impact_turf()) + cannon.charge_up(user, get_impact_turf()) /obj/machinery/computer/bsa_control/proc/deploy(force=FALSE) - var/obj/machinery/bsa/full/prebuilt = locate() in range(7) //In case of adminspawn + var/obj/machinery/power/bsa/full/prebuilt = locate() in range(7) //In case of adminspawn if(prebuilt) return prebuilt @@ -362,7 +447,7 @@ DEFINE_BUFFER_HANDLER(/obj/machinery/bsa/middle) var/datum/effect_system/smoke_spread/s = new s.set_up(4,get_turf(centerpiece)) s.start() - var/obj/machinery/bsa/full/cannon = new(get_turf(centerpiece),centerpiece.get_cannon_direction()) + var/obj/machinery/power/bsa/full/cannon = new(get_turf(centerpiece),centerpiece.get_cannon_direction()) QDEL_NULL(centerpiece.front_ref) QDEL_NULL(centerpiece.back_ref) qdel(centerpiece) diff --git a/icons/obj/lavaland/cannon.dmi b/icons/obj/lavaland/cannon.dmi index c434be513b1d7..3006ba03f3c2c 100644 Binary files a/icons/obj/lavaland/cannon.dmi and b/icons/obj/lavaland/cannon.dmi differ diff --git a/sound/machines/bsa/bsa_charge.ogg b/sound/machines/bsa/bsa_charge.ogg new file mode 100644 index 0000000000000..2c255d11c846b Binary files /dev/null and b/sound/machines/bsa/bsa_charge.ogg differ diff --git a/sound/machines/bsa/bsa_fire.ogg b/sound/machines/bsa/bsa_fire.ogg new file mode 100644 index 0000000000000..70610b800138b Binary files /dev/null and b/sound/machines/bsa/bsa_fire.ogg differ diff --git a/tgui/packages/tgui/interfaces/BluespaceArtillery.js b/tgui/packages/tgui/interfaces/BluespaceArtillery.js index e2bf47d7ba2be..763b51fa30e4b 100644 --- a/tgui/packages/tgui/interfaces/BluespaceArtillery.js +++ b/tgui/packages/tgui/interfaces/BluespaceArtillery.js @@ -1,16 +1,26 @@ import { useBackend } from '../backend'; -import { Box, Button, LabeledList, NoticeBox, Section } from '../components'; +import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components'; import { Window } from '../layouts'; export const BluespaceArtillery = (props, context) => { const { act, data } = useBackend(context); - const { notice, connected, unlocked, target } = data; + const { notice, connected, unlocked, target, charge, max_charge } = data; return ( - + {!!notice && {notice}} {connected ? ( <> +
+ +
act('recalibrate')} />}>