mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 18:22:39 +00:00
Ports the disperser beam from Baystation.
This commit is contained in:
65
code/modules/overmap/disperser/disperser.dm
Normal file
65
code/modules/overmap/disperser/disperser.dm
Normal 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.
|
||||
51
code/modules/overmap/disperser/disperser_charge.dm
Normal file
51
code/modules/overmap/disperser/disperser_charge.dm
Normal 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"
|
||||
35
code/modules/overmap/disperser/disperser_circuit.dm
Normal file
35
code/modules/overmap/disperser/disperser_circuit.dm
Normal 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
|
||||
)
|
||||
192
code/modules/overmap/disperser/disperser_console.dm
Normal file
192
code/modules/overmap/disperser/disperser_console.dm
Normal 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
|
||||
100
code/modules/overmap/disperser/disperser_fire.dm
Normal file
100
code/modules/overmap/disperser/disperser_fire.dm
Normal 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)
|
||||
Reference in New Issue
Block a user