Ports the disperser beam from Baystation.

This commit is contained in:
Leshana
2020-04-16 20:21:33 -04:00
parent 0f8b8c29cb
commit 64eb2cf552
13 changed files with 539 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
//Most interesting stuff happens in disperser_fire.dm
//This is just basic construction and deconstruction and the like
/obj/machinery/disperser
name = "abstract parent for disperser"
desc = "You should never see one of these, bap your mappers."
icon = 'icons/obj/disperser.dmi'
idle_power_usage = 200
density = TRUE
anchored = TRUE
/obj/machinery/disperser/Initialize()
. = ..()
// TODO - Remove this bit once machines are converted to Initialize
if(ispath(circuit))
circuit = new circuit(src)
default_apply_parts()
/obj/machinery/disperser/examine(mob/user)
. = ..()
if(panel_open)
to_chat(user, "The maintenance panel is open.")
/obj/machinery/disperser/attackby(obj/item/I, mob/user)
if(I && I.is_wrench())
if(panel_open)
user.visible_message("<span class='notice'>\The [user] rotates \the [src] with \the [I].</span>",
"<span class='notice'>You rotate \the [src] with \the [I].</span>")
set_dir(turn(dir, 90))
playsound(src, 'sound/items/jaws_pry.ogg', 50, 1)
else
to_chat(user, "<span class='notice'>The maintenance panel must be screwed open for this!</span>")
return
if(default_deconstruction_screwdriver(user, I))
return
if(default_deconstruction_crowbar(user, I))
return
if(default_part_replacement(user, I))
return
return ..()
/obj/machinery/disperser/front
name = "obstruction removal ballista beam generator"
desc = "A complex machine which shoots concentrated material beams.\
<br>A sign on it reads: <i>STAY CLEAR! DO NOT BLOCK!</i>"
icon_state = "front"
circuit = /obj/item/weapon/circuitboard/disperserfront
/obj/machinery/disperser/middle
name = "obstruction removal ballista fusor"
desc = "A complex machine which transmits immense amount of data \
from the material deconstructor to the particle beam generator.\
<br>A sign on it reads: <i>EXPLOSIVE! DO NOT OVERHEAT!</i>"
icon_state = "middle"
circuit = /obj/item/weapon/circuitboard/dispersermiddle
// maximum_component_parts = list(/obj/item/weapon/stock_parts = 15)
/obj/machinery/disperser/back
name = "obstruction removal ballista material deconstructor"
desc = "A prototype machine which can deconstruct materials atom by atom.\
<br>A sign on it reads: <i>KEEP AWAY FROM LIVING MATERIAL!</i>"
icon_state = "back"
circuit = /obj/item/weapon/circuitboard/disperserback
density = FALSE
layer = UNDER_JUNK_LAYER //So the charges go above us.

View File

@@ -0,0 +1,51 @@
/obj/structure/ship_munition/disperser_charge
name = "unknown disperser charge"
desc = "A charge to power the obstruction removal ballista with. It looks impossibly round and shiny. This charge does not have a defined purpose."
icon = 'icons/obj/munitions.dmi'
icon_state = "slug"
w_class = ITEMSIZE_HUGE
density = TRUE
// atom_flags = ATOM_FLAG_NO_TEMP_CHANGE | ATOM_FLAG_CLIMBABLE
var/chargetype
var/chargedesc
var/static/list/move_sounds = list( // some nasty sounds to make when moving the board
'sound/effects/metalscrape1.ogg',
'sound/effects/metalscrape2.ogg',
'sound/effects/metalscrape3.ogg'
)
// make a screeching noise to drive people mad
/obj/structure/ship_munition/disperser_charge/Move(atom/newloc, direct = 0)
if((. = ..()) && prob(50))
var/turf/T = get_turf(src)
if(!isspace(T) && !istype(T, /turf/simulated/floor/carpet))
playsound(T, pick(move_sounds), 50, 1)
/obj/structure/ship_munition/disperser_charge/fire
name = "FR1-ENFER charge"
color = "#b95a00"
desc = "A charge to power the obstruction removal ballista with. It looks impossibly round and shiny. This charge is designed to release a localised fire on impact."
chargetype = OVERMAP_WEAKNESS_FIRE
chargedesc = "ENFER"
/obj/structure/ship_munition/disperser_charge/emp
name = "EM2-QUASAR charge"
color = "#6a97b0"
desc = "A charge to power the obstruction removal ballista with. It looks impossibly round and shiny. This charge is designed to release a blast of electromagnetic pulse on impact."
chargetype = OVERMAP_WEAKNESS_EMP
chargedesc = "QUASAR"
/obj/structure/ship_munition/disperser_charge/mining
name = "MN3-BERGBAU charge"
color = "#cfcf55"
desc = "A charge to power the obstruction removal ballista with. It looks impossibly round and shiny. This charge is designed to mine ores on impact."
chargetype = OVERMAP_WEAKNESS_MINING
chargedesc = "BERGBAU"
/obj/structure/ship_munition/disperser_charge/explosive
name = "XP4-INDARRA charge"
color = "#aa5f61"
desc = "A charge to power the obstruction removal ballista with. It looks impossibly round and shiny. This charge is designed to explode on impact."
chargetype = OVERMAP_WEAKNESS_EXPLOSIVE
chargedesc = "INDARRA"

View File

@@ -0,0 +1,35 @@
#ifndef T_BOARD
#error T_BOARD macro is not defined but we need it!
#endif
/obj/item/weapon/circuitboard/disperser
name = T_BOARD("obstruction removal ballista control")
build_path = /obj/machinery/computer/ship/disperser
origin_tech = list(TECH_ENGINEERING = 2, TECH_COMBAT = 2, TECH_BLUESPACE = 2)
/obj/item/weapon/circuitboard/disperserfront
name = T_BOARD("obstruction removal ballista beam generator")
build_path = /obj/machinery/disperser/front
board_type = new /datum/frame/frame_types/machine
origin_tech = list(TECH_ENGINEERING = 2, TECH_COMBAT = 2, TECH_BLUESPACE = 2)
req_components = list (
/obj/item/weapon/stock_parts/manipulator/pico = 5
)
/obj/item/weapon/circuitboard/dispersermiddle
name = T_BOARD("obstruction removal ballista fusor")
build_path = /obj/machinery/disperser/middle
board_type = new /datum/frame/frame_types/machine
origin_tech = list(TECH_ENGINEERING = 2, TECH_COMBAT = 2, TECH_BLUESPACE = 2)
req_components = list (
/obj/item/weapon/stock_parts/subspace/crystal = 10
)
/obj/item/weapon/circuitboard/disperserback
name = T_BOARD("obstruction removal ballista material deconstructor")
build_path = /obj/machinery/disperser/back
board_type = new /datum/frame/frame_types/machine
origin_tech = list(TECH_ENGINEERING = 2, TECH_COMBAT = 2, TECH_BLUESPACE = 2)
req_components = list (
/obj/item/weapon/stock_parts/capacitor/super = 5
)

View File

@@ -0,0 +1,192 @@
//Amazing disperser from Bxil(tm). Some icons, sounds, and some code shamelessly stolen from ParadiseSS13.
/obj/machinery/computer/ship/disperser
name = "obstruction removal ballista control"
icon = 'icons/obj/computer.dmi'
icon_state = "computer"
circuit = /obj/item/weapon/circuitboard/disperser
core_skill = /datum/skill/pilot
var/skill_offset = SKILL_ADEPT - 1 //After which skill level it starts to matter. -1, because we have to index from zero
icon_keyboard = "rd_key"
icon_screen = "teleport"
var/obj/machinery/disperser/front/front
var/obj/machinery/disperser/middle/middle
var/obj/machinery/disperser/back/back
var/const/link_range = 16 //How far can the above stuff be maximum before we start complaining
var/overmapdir = 0
var/caldigit = 4 //number of digits that needs calibration
var/list/calibration //what it is
var/list/calexpected //what is should be
var/range = 1 //range of the explosion
var/strength = 1 //strength of the explosion
var/next_shot = 0 //round time where the next shot can start from
var/const/coolinterval = 2 MINUTES //time to wait between safe shots in deciseconds
/obj/machinery/computer/ship/disperser/Initialize()
. = ..()
link_parts()
reset_calibration()
/obj/machinery/computer/ship/disperser/Destroy()
release_links()
. = ..()
/obj/machinery/computer/ship/disperser/proc/link_parts()
if(is_valid_setup())
return TRUE
for(var/obj/machinery/disperser/front/F in global.machines)
if(get_dist(src, F) >= link_range)
continue
var/backwards = turn(F.dir, 180)
var/obj/machinery/disperser/middle/M = locate() in get_step(F, backwards)
if(!M || get_dist(src, M) >= link_range)
continue
var/obj/machinery/disperser/back/B = locate() in get_step(M, backwards)
if(!B || get_dist(src, B) >= link_range)
continue
front = F
middle = M
back = B
if(is_valid_setup())
GLOB.destroyed_event.register(F, src, .proc/release_links)
GLOB.destroyed_event.register(M, src, .proc/release_links)
GLOB.destroyed_event.register(B, src, .proc/release_links)
return TRUE
return FALSE
obj/machinery/computer/ship/disperser/proc/is_valid_setup()
if(front && middle && back)
var/everything_in_range = (get_dist(src, front) < link_range) && (get_dist(src, middle) < link_range) && (get_dist(src, back) < link_range)
var/everything_in_order = (middle.Adjacent(front) && middle.Adjacent(back)) && (front.dir == middle.dir && middle.dir == back.dir)
return everything_in_order && everything_in_range
return FALSE
/obj/machinery/computer/ship/disperser/proc/release_links()
GLOB.destroyed_event.unregister(front, src, .proc/release_links)
GLOB.destroyed_event.unregister(middle, src, .proc/release_links)
GLOB.destroyed_event.unregister(back, src, .proc/release_links)
front = null
middle = null
back = null
/obj/machinery/computer/ship/disperser/proc/get_calibration()
var/list/calresult[caldigit]
for(var/i = 1 to caldigit)
if(calibration[i] == calexpected[i])
calresult[i] = 2
else if(calibration[i] in calexpected)
calresult[i] = 1
else
calresult[i] = 0
return calresult
/obj/machinery/computer/ship/disperser/proc/reset_calibration()
calexpected = new /list(caldigit)
calibration = new /list(caldigit)
for(var/i = 1 to caldigit)
calexpected[i] = rand(0,9)
calibration[i] = 0
/obj/machinery/computer/ship/disperser/proc/cal_accuracy()
var/top = 0
var/divisor = caldigit * 2 //maximum possible value, aka 100% accuracy
for(var/i in get_calibration())
top += i
return round(top * 100 / divisor)
/obj/machinery/computer/ship/disperser/proc/get_next_shot_seconds()
return max(0, (next_shot - world.time) / 10)
/obj/machinery/computer/ship/disperser/proc/cool_failchance()
return get_next_shot_seconds() * 1000 / coolinterval
/obj/machinery/computer/ship/disperser/proc/get_charge_type()
var/obj/structure/ship_munition/disperser_charge/B = locate() in get_turf(back)
if(B)
return B.chargetype
return OVERMAP_WEAKNESS_NONE
/obj/machinery/computer/ship/disperser/proc/get_charge()
var/obj/structure/ship_munition/disperser_charge/B = locate() in get_turf(back)
if(B)
return B
/obj/machinery/computer/ship/disperser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
if(!linked)
display_reconnect_dialog(user, "disperser synchronization")
return
var/data[0]
if (!link_parts())
data["faillink"] = TRUE
else
data["calibration"] = calibration
data["overmapdir"] = overmapdir
data["cal_accuracy"] = cal_accuracy()
data["strength"] = strength
data["range"] = range
data["next_shot"] = round(get_next_shot_seconds())
data["nopower"] = !data["faillink"] && (!front.powered() || !middle.powered() || !back.powered())
data["skill"] = user.get_skill_value(core_skill) > skill_offset
var/charge = "<b>UNKNOWN ERROR</b>"
if(get_charge_type() == OVERMAP_WEAKNESS_NONE)
charge = "<b>ERROR</b>: No valid charge detected."
else
var/obj/structure/ship_munition/disperser_charge/B = get_charge()
charge = B.chargedesc
data["chargeload"] = charge
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "disperser.tmpl", "[linked.name] ORB control", 400, 550)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/ship/disperser/OnTopic(mob/user, list/href_list, state)
. = ..()
if(.)
return
if(!linked)
return TOPIC_HANDLED
if (href_list["choose"])
overmapdir = sanitize_integer(text2num(href_list["choose"]), 0, 9, 0)
reset_calibration()
if(href_list["calibration"])
var/input = input("0-9", "disperser calibration", 0) as num|null
if(!isnull(input)) //can be zero so we explicitly check for null
var/calnum = sanitize_integer(text2num(href_list["calibration"]), 0, caldigit)//sanitiiiiize
calibration[calnum + 1] = sanitize_integer(input, 0, 9, 0)//must add 1 because nanoui indexes from 0
if(href_list["skill_calibration"])
for(var/i = 1 to min(caldigit, user.get_skill_value(core_skill) - skill_offset))
calibration[i] = calexpected[i]
if(href_list["strength"])
var/input = input("1-5", "disperser strength", 1) as num|null
if(input && CanInteract(user, state))
strength = sanitize_integer(input, 1, 5, 1)
middle.idle_power_usage = strength * range * 100
if(href_list["range"])
var/input = input("1-5", "disperser radius", 1) as num|null
if(input && CanInteract(user, state))
range = sanitize_integer(input, 1, 5, 1)
middle.idle_power_usage = strength * range * 100
if(href_list["fire"])
fire(user)
return TOPIC_REFRESH

View File

@@ -0,0 +1,100 @@
/obj/machinery/computer/ship/disperser/proc/fire(mob/user)
log_and_message_admins("attempted to launch a disperser beam.")
if(!link_parts())
return FALSE //no disperser, no service
if(!front.powered() || !middle.powered() || !back.powered())
return FALSE //no power, no boom boom
var/chargetype = get_charge_type()
if(chargetype <= 0)
return FALSE //no dear, you cannot fire the captain out of a cannon... unless you put him in a box of course
var/atom/movable/atomcharge = get_charge()
var/turf/start = front
var/direction = front.dir
var/distance = 0
for(var/turf/T in getline(get_step(front,front.dir), get_target_turf(start, direction)))
distance++
if(T.density)
if(distance < 7)
explosion(T,1,2,3)
continue
else
T.ex_act(1)
for(var/atom/A in T)
if(A.density)
if(distance < 7)
explosion(A,1,2,3)
break
else
A.ex_act(1)
var/list/relevant_z = GetConnectedZlevels(start.z)
for(var/mob/M in global.player_list)
var/turf/T = get_turf(M)
if(!T || !(T.z in relevant_z))
continue
shake_camera(M, 25)
if(!isdeaf(M))
M << sound('sound/effects/explosionfar.ogg', volume=10)
if(front) //Meanwhile front might have exploded
front.layer = ABOVE_JUNK_LAYER //So the beam goes below us. Looks a lot better
playsound(start, 'sound/machines/disperser_fire.ogg', 100, 1)
handle_beam(start, direction)
handle_overbeam()
qdel(atomcharge)
//Some moron disregarded the cooldown warning. Let's blow in their face.
if(prob(cool_failchance()))
explosion(middle,rand(1,2),rand(2,3),rand(3,4))
next_shot = coolinterval + world.time
//Success, but we missed.
if(prob(100 - cal_accuracy()))
return TRUE
reset_calibration()
var/list/candidates = list()
for(var/obj/effect/overmap/event/O in get_step(linked, overmapdir))
candidates += O
//Way to waste a charge
if(!length(candidates))
return TRUE
var/obj/effect/overmap/event/finaltarget = pick(candidates)
log_and_message_admins("A type [chargetype] disperser beam was launched at [finaltarget].", location=finaltarget)
fire_at_event(finaltarget, chargetype)
return TRUE
/obj/machinery/computer/ship/disperser/proc/fire_at_event(obj/effect/overmap/event/finaltarget, chargetype)
if(chargetype & finaltarget.weaknesses)
var/turf/T = finaltarget.loc
qdel(finaltarget)
GLOB.overmap_event_handler.update_hazards(T)
/obj/machinery/computer/ship/disperser/proc/handle_beam(turf/start, direction)
set waitfor = FALSE
start.Beam(get_target_turf(start, direction), "bsa_beam", time = 50, maxdistance = world.maxx)
if(front)
front.layer = initial(front.layer)
/obj/machinery/computer/ship/disperser/proc/handle_overbeam()
set waitfor = FALSE
linked.Beam(get_step(linked, overmapdir), "bsa_beam", time = 150, maxdistance = world.maxx)
/obj/machinery/computer/ship/disperser/proc/get_target_turf(turf/start, direction)
switch(direction)
if(NORTH)
return locate(start.x,world.maxy,start.z)
if(SOUTH)
return locate(start.x,1,start.z)
if(WEST)
return locate(1,start.y,start.z)
if(EAST)
return locate(world.maxx,start.y,start.z)