mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
541 lines
16 KiB
Plaintext
541 lines
16 KiB
Plaintext
//emitter construction defines
|
|
#define EMITTER_UNWRENCHED 0
|
|
#define EMITTER_WRENCHED 1
|
|
#define EMITTER_WELDED 2
|
|
|
|
/obj/machinery/power/emitter
|
|
name = "emitter"
|
|
desc = "A heavy-duty industrial laser, often used in containment fields and power generation."
|
|
icon = 'icons/obj/singularity.dmi'
|
|
icon_state = "emitter"
|
|
|
|
anchored = FALSE
|
|
density = TRUE
|
|
req_access = list(ACCESS_ENGINE_EQUIP)
|
|
circuit = /obj/item/circuitboard/machine/emitter
|
|
|
|
use_power = NO_POWER_USE
|
|
idle_power_usage = 10
|
|
active_power_usage = 300
|
|
|
|
var/icon_state_on = "emitter_+a"
|
|
var/icon_state_underpowered = "emitter_+u"
|
|
var/active = FALSE
|
|
var/powered = FALSE
|
|
var/fire_delay = 10 SECONDS //emitters always start with a 10 second starting time
|
|
var/maximum_reload_time = 10 SECONDS
|
|
var/minimum_reload_time = 2 SECONDS
|
|
var/shots_before_reload = 4 //how many times we shoot before "reloading"
|
|
var/delay_between_shots = 2 SECONDS //the time in seconds between regular shots, ie not "reloading"
|
|
var/last_shot = 0
|
|
var/shot_number = 0
|
|
var/state = EMITTER_UNWRENCHED
|
|
var/locked = FALSE
|
|
var/allow_switch_interact = TRUE
|
|
|
|
var/projectile_type = /obj/item/projectile/beam/emitter
|
|
var/projectile_sound = 'sound/weapons/emitter.ogg'
|
|
var/datum/effect_system/spark_spread/sparks
|
|
|
|
var/obj/item/gun/energy/gun
|
|
var/list/gun_properties
|
|
var/mode = 0
|
|
|
|
// The following 3 vars are mostly for the prototype
|
|
var/manual = FALSE
|
|
var/charge = 0
|
|
var/last_projectile_params
|
|
|
|
var/throwing_range = 5
|
|
var/throwing_speed = 3
|
|
|
|
|
|
/obj/machinery/power/emitter/anchored
|
|
anchored = TRUE
|
|
|
|
/obj/machinery/power/emitter/ctf
|
|
name = "Energy Cannon"
|
|
active = TRUE
|
|
active_power_usage = FALSE
|
|
idle_power_usage = FALSE
|
|
locked = TRUE
|
|
req_access_txt = "100"
|
|
state = EMITTER_WELDED
|
|
use_power = FALSE
|
|
|
|
/obj/machinery/power/emitter/Initialize()
|
|
. = ..()
|
|
RefreshParts()
|
|
wires = new /datum/wires/emitter(src)
|
|
if(state == EMITTER_WELDED && anchored)
|
|
connect_to_network()
|
|
|
|
sparks = new
|
|
sparks.attach(src)
|
|
sparks.set_up(5, TRUE, src)
|
|
|
|
/obj/machinery/power/emitter/ComponentInitialize()
|
|
. = ..()
|
|
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
|
|
|
/obj/machinery/power/emitter/RefreshParts()
|
|
var/max_reload = initial(maximum_reload_time) + 20
|
|
var/min_reload = initial(minimum_reload_time) + 4
|
|
var/power_usage = initial(active_power_usage) + 50
|
|
var/shot_delay = initial(delay_between_shots) + 2
|
|
var/shot_reload = initial(shots_before_reload) - 1
|
|
for(var/obj/item/stock_parts/micro_laser/L in component_parts)
|
|
max_reload -= 20 * L.rating
|
|
min_reload -= 4 * L.rating
|
|
maximum_reload_time = max_reload
|
|
minimum_reload_time = min_reload
|
|
for(var/obj/item/stock_parts/capacitor/C in component_parts)
|
|
shot_delay -= 2 * C.rating
|
|
shot_reload += C.rating
|
|
fire_delay = maximum_reload_time
|
|
delay_between_shots = shot_delay
|
|
shots_before_reload = shot_reload
|
|
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
|
power_usage -= 50 * M.rating
|
|
active_power_usage = power_usage
|
|
|
|
/obj/machinery/power/emitter/examine(mob/user)
|
|
. = ..()
|
|
if(in_range(user, src) || isobserver(user))
|
|
. += "<span class='notice'>The status display reads: Emitting one beam each <b>[fire_delay*0.1]</b> seconds, shooting [shots_before_reload] times before recharging. <br>Power consumption at <b>[active_power_usage]W</b>.<span>"
|
|
|
|
/obj/machinery/power/emitter/ComponentInitialize()
|
|
. = ..()
|
|
AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))
|
|
|
|
/obj/machinery/power/emitter/proc/can_be_rotated(mob/user,rotation_type)
|
|
if (anchored)
|
|
to_chat(user, span_warning("It is fastened to the floor!"))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/machinery/power/emitter/Destroy()
|
|
if(SSticker.IsRoundInProgress())
|
|
var/turf/T = get_turf(src)
|
|
message_admins("Emitter deleted at [ADMIN_VERBOSEJMP(T)]")
|
|
log_game("Emitter deleted at [AREACOORD(T)]")
|
|
investigate_log("<font color='red'>deleted</font> at [AREACOORD(T)]", INVESTIGATE_SINGULO)
|
|
investigate_log("<font color='red'>deleted</font> at [AREACOORD(T)]", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful
|
|
QDEL_NULL(sparks)
|
|
return ..()
|
|
|
|
/obj/machinery/power/emitter/update_icon()
|
|
if(active && powernet)
|
|
icon_state = avail(active_power_usage) ? icon_state_on : icon_state_underpowered
|
|
else
|
|
icon_state = initial(icon_state)
|
|
|
|
/obj/machinery/power/emitter/interact(mob/user)
|
|
add_fingerprint(user)
|
|
if(state == EMITTER_WELDED)
|
|
if(!powernet)
|
|
to_chat(user, span_warning("\The [src] isn't connected to a wire!"))
|
|
return TRUE
|
|
if(!locked && allow_switch_interact)
|
|
if(active == TRUE)
|
|
active = FALSE
|
|
to_chat(user, span_notice("You turn off [src]."))
|
|
else
|
|
active = TRUE
|
|
to_chat(user, span_notice("You turn on [src]."))
|
|
shot_number = 0
|
|
fire_delay = maximum_reload_time
|
|
|
|
message_admins("Emitter turned [active ? "ON" : "OFF"] by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(src)]")
|
|
log_game("Emitter turned [active ? "ON" : "OFF"] by [key_name(user)] in [AREACOORD(src)]")
|
|
investigate_log("turned [active ? "<font color='green'>ON</font>" : "<font color='red'>OFF</font>"] by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SINGULO)
|
|
investigate_log("turned [active ? "<font color='green'>ON</font>" : "<font color='red'>OFF</font>"] by [key_name(user)] at [AREACOORD(src)]", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful
|
|
update_icon()
|
|
|
|
else
|
|
to_chat(user, span_warning("The controls are locked!"))
|
|
else
|
|
to_chat(user, span_warning("[src] needs to be firmly secured to the floor first!"))
|
|
return TRUE
|
|
|
|
/obj/machinery/power/emitter/attack_animal(mob/living/simple_animal/M)
|
|
if(ismegafauna(M) && anchored)
|
|
state = EMITTER_UNWRENCHED
|
|
anchored = FALSE
|
|
M.visible_message(span_warning("[M] rips [src] free from its moorings!"))
|
|
else
|
|
..()
|
|
if(!anchored)
|
|
step(src, get_dir(M, src))
|
|
|
|
/obj/machinery/power/emitter/process()
|
|
if(stat & (BROKEN))
|
|
return
|
|
if(state != EMITTER_WELDED || (!powernet && active_power_usage))
|
|
active = FALSE
|
|
update_icon()
|
|
return
|
|
if(active == TRUE)
|
|
if(!active_power_usage || surplus() >= active_power_usage)
|
|
add_load(active_power_usage)
|
|
if(!powered)
|
|
powered = TRUE
|
|
update_icon()
|
|
investigate_log("regained power and turned <font color='green'>ON</font> at [AREACOORD(src)]", INVESTIGATE_SINGULO)
|
|
investigate_log("regained power and turned <font color='green'>ON</font> at [AREACOORD(src)]", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful
|
|
else
|
|
if(powered)
|
|
powered = FALSE
|
|
update_icon()
|
|
investigate_log("lost power and turned <font color='red'>OFF</font> at [AREACOORD(src)]", INVESTIGATE_SINGULO)
|
|
investigate_log("lost power and turned <font color='red'>OFF</font> at [AREACOORD(src)]", INVESTIGATE_SUPERMATTER) // yogs - so supermatter investigate is useful
|
|
log_game("Emitter lost power in [AREACOORD(src)]")
|
|
return
|
|
if(charge <= 80)
|
|
charge += 5
|
|
if(!check_delay() || manual == TRUE)
|
|
return FALSE
|
|
fire_beam()
|
|
|
|
/obj/machinery/power/emitter/proc/check_delay()
|
|
if((src.last_shot + src.fire_delay) <= world.time)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/obj/machinery/power/emitter/proc/fire_beam_pulse()
|
|
if(!check_delay())
|
|
return FALSE
|
|
if(state != EMITTER_WELDED)
|
|
return FALSE
|
|
if(surplus() >= active_power_usage)
|
|
add_load(active_power_usage)
|
|
fire_beam()
|
|
|
|
/obj/machinery/power/emitter/proc/fire_beam(mob/user)
|
|
var/obj/item/K = new projectile_type(get_turf(src))
|
|
|
|
/// If it isn't a projectile, throw it
|
|
if(!istype(K, /obj/item/projectile))
|
|
if(istype(K, /obj/item/grenade))
|
|
var/obj/item/grenade/I = K
|
|
I.preprime()
|
|
K.throw_at(get_edge_target_turf(src, dir), throwing_range, throwing_speed)
|
|
playsound(get_turf(src), projectile_sound, 50, TRUE)
|
|
if(prob(35))
|
|
sparks.start()
|
|
return K
|
|
|
|
var/obj/item/projectile/P = K
|
|
playsound(get_turf(src), projectile_sound, 50, TRUE)
|
|
if(prob(35))
|
|
sparks.start()
|
|
P.firer = user ? user : src
|
|
P.fired_from = src
|
|
if(last_projectile_params)
|
|
P.p_x = last_projectile_params[2]
|
|
P.p_y = last_projectile_params[3]
|
|
P.fire(last_projectile_params[1])
|
|
else
|
|
P.fire(dir2angle(dir))
|
|
if(!manual)
|
|
last_shot = world.time
|
|
shot_number++
|
|
if(shot_number < shots_before_reload)
|
|
fire_delay = delay_between_shots
|
|
else
|
|
fire_delay = rand(minimum_reload_time,maximum_reload_time)
|
|
shot_number = 0
|
|
return P
|
|
|
|
/obj/machinery/power/emitter/can_be_unfasten_wrench(mob/user, silent)
|
|
if(active)
|
|
if(!silent)
|
|
to_chat(user, span_warning("Turn \the [src] off first!"))
|
|
return FAILED_UNFASTEN
|
|
|
|
else if(state == EMITTER_WELDED)
|
|
if(!silent)
|
|
to_chat(user, span_warning("[src] is welded to the floor!"))
|
|
return FAILED_UNFASTEN
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/power/emitter/default_unfasten_wrench(mob/user, obj/item/I, time = 20)
|
|
. = ..()
|
|
if(. == SUCCESSFUL_UNFASTEN)
|
|
if(anchored)
|
|
state = EMITTER_WRENCHED
|
|
else
|
|
state = EMITTER_UNWRENCHED
|
|
|
|
/obj/machinery/power/emitter/wrench_act(mob/living/user, obj/item/I)
|
|
default_unfasten_wrench(user, I)
|
|
return TRUE
|
|
|
|
/obj/machinery/power/emitter/welder_act(mob/living/user, obj/item/I)
|
|
if(active)
|
|
to_chat(user, "Turn \the [src] off first.")
|
|
return TRUE
|
|
|
|
switch(state)
|
|
if(EMITTER_UNWRENCHED)
|
|
to_chat(user, span_warning("The [src.name] needs to be wrenched to the floor!"))
|
|
if(EMITTER_WRENCHED)
|
|
if(!I.tool_start_check(user, amount=0))
|
|
return TRUE
|
|
user.visible_message("[user.name] starts to weld the [name] to the floor.", \
|
|
span_notice("You start to weld \the [src] to the floor..."), \
|
|
span_italics("You hear welding."))
|
|
if(I.use_tool(src, user, 20, volume=50) && state == EMITTER_WRENCHED)
|
|
state = EMITTER_WELDED
|
|
to_chat(user, span_notice("You weld \the [src] to the floor."))
|
|
connect_to_network()
|
|
if(EMITTER_WELDED)
|
|
if(!I.tool_start_check(user, amount=0))
|
|
return TRUE
|
|
user.visible_message("[user.name] starts to cut the [name] free from the floor.", \
|
|
span_notice("You start to cut \the [src] free from the floor..."), \
|
|
span_italics("You hear welding."))
|
|
if(I.use_tool(src, user, 20, volume=50) && state == EMITTER_WELDED)
|
|
state = EMITTER_WRENCHED
|
|
to_chat(user, span_notice("You cut \the [src] free from the floor."))
|
|
disconnect_from_network()
|
|
|
|
return TRUE
|
|
|
|
/obj/machinery/power/emitter/crowbar_act(mob/living/user, obj/item/I)
|
|
if(panel_open && gun)
|
|
return remove_gun(user)
|
|
default_deconstruction_crowbar(I)
|
|
return TRUE
|
|
|
|
/obj/machinery/power/emitter/screwdriver_act(mob/living/user, obj/item/I)
|
|
if(..())
|
|
return TRUE
|
|
default_deconstruction_screwdriver(user, "emitter_open", "emitter", I)
|
|
return TRUE
|
|
|
|
|
|
/obj/machinery/power/emitter/attackby(obj/item/I, mob/user, params)
|
|
if(I.GetID())
|
|
togglelock(user)
|
|
return
|
|
else if(is_wire_tool(I) && panel_open)
|
|
wires.interact(user)
|
|
return
|
|
else if(panel_open && !gun && istype(I,/obj/item/gun/energy))
|
|
if(integrate(I,user))
|
|
return
|
|
return ..()
|
|
|
|
/obj/machinery/power/emitter/proc/togglelock(mob/user)
|
|
if(!user.canUseTopic(src, !issilicon(user)))
|
|
return
|
|
if(obj_flags & EMAGGED)
|
|
to_chat(user, span_warning("The lock seems to be broken!"))
|
|
return
|
|
if(allowed(user))
|
|
if(active)
|
|
locked = !locked
|
|
to_chat(user, span_notice("You [src.locked ? "lock" : "unlock"] the controls."))
|
|
else
|
|
to_chat(user, span_warning("The controls can only be locked when \the [src] is online!"))
|
|
else
|
|
to_chat(user, span_danger("Access denied."))
|
|
return
|
|
|
|
/obj/machinery/power/emitter/proc/integrate(obj/item/gun/energy/E,mob/user)
|
|
if(istype(E, /obj/item/gun/energy))
|
|
if(!user.transferItemToLoc(E, src))
|
|
return
|
|
gun = E
|
|
gun_properties = gun.get_turret_properties()
|
|
set_projectile()
|
|
return TRUE
|
|
|
|
/obj/machinery/power/emitter/proc/remove_gun(mob/user)
|
|
if(!gun)
|
|
return
|
|
user.put_in_hands(gun)
|
|
gun = null
|
|
playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
|
|
gun_properties = list()
|
|
set_projectile()
|
|
return TRUE
|
|
|
|
/obj/machinery/power/emitter/proc/set_projectile()
|
|
if(LAZYLEN(gun_properties))
|
|
if(mode || !gun_properties["lethal_projectile"])
|
|
projectile_type = gun_properties["stun_projectile"]
|
|
projectile_sound = gun_properties["stun_projectile_sound"]
|
|
else
|
|
projectile_type = gun_properties["lethal_projectile"]
|
|
projectile_sound = gun_properties["lethal_projectile_sound"]
|
|
return
|
|
projectile_type = initial(projectile_type)
|
|
projectile_sound = initial(projectile_sound)
|
|
|
|
/obj/machinery/power/emitter/emag_act(mob/user)
|
|
if(obj_flags & EMAGGED)
|
|
return
|
|
locked = FALSE
|
|
obj_flags |= EMAGGED
|
|
if(user)
|
|
user.visible_message("[user.name] emags [src].",span_notice("You short out the lock."))
|
|
|
|
|
|
/obj/machinery/power/emitter/prototype
|
|
name = "Prototype Emitter"
|
|
icon = 'icons/obj/turrets.dmi'
|
|
icon_state = "protoemitter"
|
|
icon_state_on = "protoemitter_+a"
|
|
icon_state_underpowered = "protoemitter_+u"
|
|
can_buckle = TRUE
|
|
buckle_lying = FALSE
|
|
var/view_range = 4.5
|
|
var/datum/action/innate/protoemitter/firing/auto
|
|
|
|
//BUCKLE HOOKS
|
|
|
|
/obj/machinery/power/emitter/prototype/unbuckle_mob(mob/living/buckled_mob,force = 0)
|
|
playsound(src,'sound/mecha/mechmove01.ogg', 50, TRUE)
|
|
manual = FALSE
|
|
for(var/obj/item/I in buckled_mob.held_items)
|
|
if(istype(I, /obj/item/turret_control))
|
|
qdel(I)
|
|
if(istype(buckled_mob))
|
|
buckled_mob.pixel_x = 0
|
|
buckled_mob.pixel_y = 0
|
|
if(buckled_mob.client)
|
|
buckled_mob.client.view_size.resetToDefault()
|
|
auto.Remove(buckled_mob)
|
|
. = ..()
|
|
|
|
/obj/machinery/power/emitter/prototype/user_buckle_mob(mob/living/M, mob/living/carbon/user)
|
|
if(user.incapacitated() || !istype(user))
|
|
return
|
|
for(var/atom/movable/A in get_turf(src))
|
|
if(A.density && (A != src && A != M))
|
|
return
|
|
M.forceMove(get_turf(src))
|
|
..()
|
|
playsound(src,'sound/mecha/mechmove01.ogg', 50, TRUE)
|
|
M.pixel_y = 14
|
|
layer = 4.1
|
|
if(M.client)
|
|
M.client.view_size.setTo(view_range)
|
|
if(!auto)
|
|
auto = new()
|
|
auto.Grant(M, src)
|
|
|
|
/datum/action/innate/protoemitter
|
|
check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUN | AB_CHECK_CONSCIOUS
|
|
var/obj/machinery/power/emitter/prototype/PE
|
|
var/mob/living/carbon/U
|
|
|
|
|
|
/datum/action/innate/protoemitter/Grant(mob/living/carbon/L, obj/machinery/power/emitter/prototype/proto)
|
|
PE = proto
|
|
U = L
|
|
. = ..()
|
|
|
|
/datum/action/innate/protoemitter/firing
|
|
name = "Switch to Manual Firing"
|
|
desc = "The emitter will only fire on your command and at your designated target"
|
|
button_icon_state = "mech_zoom_on"
|
|
|
|
/datum/action/innate/protoemitter/firing/Activate()
|
|
if(PE.manual)
|
|
playsound(PE,'sound/mecha/mechmove01.ogg', 50, TRUE)
|
|
PE.manual = FALSE
|
|
name = "Switch to Manual Firing"
|
|
desc = "The emitter will only fire on your command and at your designated target"
|
|
button_icon_state = "mech_zoom_on"
|
|
for(var/obj/item/I in U.held_items)
|
|
if(istype(I, /obj/item/turret_control))
|
|
qdel(I)
|
|
UpdateButtonIcon()
|
|
return
|
|
else
|
|
playsound(PE,'sound/mecha/mechmove01.ogg', 50, TRUE)
|
|
name = "Switch to Automatic Firing"
|
|
desc = "Emitters will switch to periodic firing at your last target"
|
|
button_icon_state = "mech_zoom_off"
|
|
PE.manual = TRUE
|
|
for(var/V in U.held_items)
|
|
var/obj/item/I = V
|
|
if(istype(I))
|
|
if(U.dropItemToGround(I))
|
|
var/obj/item/turret_control/TC = new /obj/item/turret_control()
|
|
U.put_in_hands(TC)
|
|
else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand
|
|
var/obj/item/turret_control/TC = new /obj/item/turret_control()
|
|
U.put_in_hands(TC)
|
|
UpdateButtonIcon()
|
|
|
|
|
|
/obj/item/turret_control
|
|
name = "turret controls"
|
|
icon_state = "offhand"
|
|
w_class = WEIGHT_CLASS_HUGE
|
|
item_flags = ABSTRACT | NOBLUDGEON
|
|
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
|
var/delay = 0
|
|
|
|
/obj/item/turret_control/Initialize()
|
|
. = ..()
|
|
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
|
|
|
|
/obj/item/turret_control/afterattack(atom/targeted_atom, mob/user, proxflag, clickparams)
|
|
. = ..()
|
|
var/obj/machinery/power/emitter/E = user.buckled
|
|
E.setDir(get_dir(E,targeted_atom))
|
|
user.setDir(E.dir)
|
|
switch(E.dir)
|
|
if(NORTH)
|
|
E.layer = 3.9
|
|
user.pixel_x = 0
|
|
user.pixel_y = -14
|
|
if(NORTHEAST)
|
|
E.layer = 3.9
|
|
user.pixel_x = -8
|
|
user.pixel_y = -12
|
|
if(EAST)
|
|
E.layer = 4.1
|
|
user.pixel_x = -14
|
|
user.pixel_y = 0
|
|
if(SOUTHEAST)
|
|
E.layer = 3.9
|
|
user.pixel_x = -8
|
|
user.pixel_y = 12
|
|
if(SOUTH)
|
|
E.layer = 4.1
|
|
user.pixel_x = 0
|
|
user.pixel_y = 14
|
|
if(SOUTHWEST)
|
|
E.layer = 3.9
|
|
user.pixel_x = 8
|
|
user.pixel_y = 12
|
|
if(WEST)
|
|
E.layer = 4.1
|
|
user.pixel_x = 14
|
|
user.pixel_y = 0
|
|
if(NORTHWEST)
|
|
E.layer = 3.9
|
|
user.pixel_x = 8
|
|
user.pixel_y = -12
|
|
|
|
E.last_projectile_params = calculate_projectile_angle_and_pixel_offsets(user, clickparams)
|
|
|
|
if(E.charge >= 10 && world.time > delay)
|
|
E.charge -= 10
|
|
E.fire_beam(user)
|
|
delay = world.time + 10
|
|
else if (E.charge < 10)
|
|
playsound(src,'sound/machines/buzz-sigh.ogg', 50, TRUE)
|
|
|
|
|
|
#undef EMITTER_UNWRENCHED
|
|
#undef EMITTER_WRENCHED
|
|
#undef EMITTER_WELDED
|