@@ -0,0 +1,504 @@
|
||||
//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/active = FALSE
|
||||
var/powered = FALSE
|
||||
var/fire_delay = 100
|
||||
var/maximum_fire_delay = 100
|
||||
var/minimum_fire_delay = 20
|
||||
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
|
||||
|
||||
|
||||
/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(1, TRUE, src)
|
||||
|
||||
/obj/machinery/power/emitter/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
|
||||
/obj/machinery/power/emitter/RefreshParts()
|
||||
var/max_firedelay = 120
|
||||
var/firedelay = 120
|
||||
var/min_firedelay = 24
|
||||
var/power_usage = 350
|
||||
for(var/obj/item/stock_parts/micro_laser/L in component_parts)
|
||||
max_firedelay -= 20 * L.rating
|
||||
min_firedelay -= 4 * L.rating
|
||||
firedelay -= 20 * L.rating
|
||||
maximum_fire_delay = max_firedelay
|
||||
minimum_fire_delay = min_firedelay
|
||||
fire_delay = firedelay
|
||||
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/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 class='warning'>It is fastened to the floor!</span>")
|
||||
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)
|
||||
QDEL_NULL(sparks)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/emitter/update_icon()
|
||||
if (active && powernet && avail(active_power_usage))
|
||||
icon_state = icon_state_on
|
||||
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 class='warning'>\The [src] isn't connected to a wire!</span>")
|
||||
return TRUE
|
||||
if(!locked && allow_switch_interact)
|
||||
if(active == TRUE)
|
||||
active = FALSE
|
||||
to_chat(user, "<span class='notice'>You turn off [src].</span>")
|
||||
else
|
||||
active = TRUE
|
||||
to_chat(user, "<span class='notice'>You turn on [src].</span>")
|
||||
shot_number = 0
|
||||
fire_delay = maximum_fire_delay
|
||||
|
||||
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)
|
||||
|
||||
update_icon()
|
||||
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The controls are locked!</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] needs to be firmly secured to the floor first!</span>")
|
||||
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 class='warning'>[M] rips [src] free from its moorings!</span>")
|
||||
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)
|
||||
else
|
||||
if(powered)
|
||||
powered = FALSE
|
||||
update_icon()
|
||||
investigate_log("lost power and turned <font color='red'>OFF</font> at [AREACOORD(src)]", INVESTIGATE_SINGULO)
|
||||
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/projectile/P = new projectile_type(get_turf(src))
|
||||
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
|
||||
if(shot_number < 3)
|
||||
fire_delay = 20
|
||||
shot_number ++
|
||||
else
|
||||
fire_delay = rand(minimum_fire_delay,maximum_fire_delay)
|
||||
shot_number = 0
|
||||
return P
|
||||
|
||||
/obj/machinery/power/emitter/can_be_unfasten_wrench(mob/user, silent)
|
||||
if(active)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>Turn \the [src] off first!</span>")
|
||||
return FAILED_UNFASTEN
|
||||
|
||||
else if(state == EMITTER_WELDED)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>[src] is welded to the floor!</span>")
|
||||
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 class='warning'>The [src.name] needs to be wrenched to the floor!</span>")
|
||||
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 class='notice'>You start to weld \the [src] to the floor...</span>", \
|
||||
"<span class='italics'>You hear welding.</span>")
|
||||
if(I.use_tool(src, user, 20, volume=50))
|
||||
state = EMITTER_WELDED
|
||||
to_chat(user, "<span class='notice'>You weld \the [src] to the floor.</span>")
|
||||
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 class='notice'>You start to cut \the [src] free from the floor...</span>", \
|
||||
"<span class='italics'>You hear welding.</span>")
|
||||
if(I.use_tool(src, user, 20, volume=50))
|
||||
state = EMITTER_WRENCHED
|
||||
to_chat(user, "<span class='notice'>You cut \the [src] free from the floor.</span>")
|
||||
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())
|
||||
if(obj_flags & EMAGGED)
|
||||
to_chat(user, "<span class='warning'>The lock seems to be broken!</span>")
|
||||
return
|
||||
if(allowed(user))
|
||||
if(active)
|
||||
locked = !locked
|
||||
to_chat(user, "<span class='notice'>You [src.locked ? "lock" : "unlock"] the controls.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The controls can only be locked when \the [src] is online!</span>")
|
||||
else
|
||||
to_chat(user, "<span class='danger'>Access denied.</span>")
|
||||
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/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
|
||||
user?.visible_message("[user.name] emags [src].","<span class='notice'>You short out the lock.</span>")
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/machinery/power/emitter/prototype
|
||||
name = "Prototype Emitter"
|
||||
icon = 'icons/obj/turrets.dmi'
|
||||
icon_state = "protoemitter"
|
||||
icon_state_on = "protoemitter_+a"
|
||||
can_buckle = TRUE
|
||||
buckle_lying = FALSE
|
||||
var/view_range = 12
|
||||
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.change_view(CONFIG_GET(string/default_view))
|
||||
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.change_view(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
|
||||
@@ -0,0 +1,447 @@
|
||||
|
||||
|
||||
/obj/singularity
|
||||
name = "gravitational singularity"
|
||||
desc = "A gravitational singularity."
|
||||
icon = 'icons/obj/singularity.dmi'
|
||||
icon_state = "singularity_s1"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
layer = MASSIVE_OBJ_LAYER
|
||||
light_range = 6
|
||||
appearance_flags = 0
|
||||
var/current_size = 1
|
||||
var/allowed_size = 1
|
||||
var/contained = 1 //Are we going to move around?
|
||||
var/energy = 100 //How strong are we?
|
||||
var/dissipate = 1 //Do we lose energy over time?
|
||||
var/dissipate_delay = 10
|
||||
var/dissipate_track = 0
|
||||
var/dissipate_strength = 1 //How much energy do we lose?
|
||||
var/move_self = 1 //Do we move on our own?
|
||||
var/grav_pull = 4 //How many tiles out do we pull?
|
||||
var/consume_range = 0 //How many tiles out do we eat
|
||||
var/event_chance = 10 //Prob for event each tick
|
||||
var/target = null //its target. moves towards the target if it has one
|
||||
var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing
|
||||
var/last_warning
|
||||
var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
|
||||
obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION
|
||||
|
||||
/obj/singularity/Initialize(mapload, starting_energy = 50)
|
||||
//CARN: admin-alert for chuckle-fuckery.
|
||||
admin_investigate_setup()
|
||||
|
||||
src.energy = starting_energy
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
GLOB.poi_list |= src
|
||||
GLOB.singularities |= src
|
||||
for(var/obj/machinery/power/singularity_beacon/singubeacon in GLOB.machines)
|
||||
if(singubeacon.active)
|
||||
target = singubeacon
|
||||
break
|
||||
return
|
||||
|
||||
/obj/singularity/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
GLOB.poi_list.Remove(src)
|
||||
GLOB.singularities.Remove(src)
|
||||
return ..()
|
||||
|
||||
/obj/singularity/Move(atom/newloc, direct)
|
||||
if(current_size >= STAGE_FIVE || check_turfs_in(direct))
|
||||
last_failed_movement = 0//Reset this because we moved
|
||||
return ..()
|
||||
else
|
||||
last_failed_movement = direct
|
||||
return 0
|
||||
|
||||
/obj/singularity/attack_hand(mob/user)
|
||||
consume(user)
|
||||
return TRUE
|
||||
|
||||
/obj/singularity/attack_paw(mob/user)
|
||||
consume(user)
|
||||
|
||||
/obj/singularity/attack_alien(mob/user)
|
||||
consume(user)
|
||||
|
||||
/obj/singularity/attack_animal(mob/user)
|
||||
consume(user)
|
||||
|
||||
/obj/singularity/attackby(obj/item/W, mob/user, params)
|
||||
consume(user)
|
||||
return 1
|
||||
|
||||
/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man!
|
||||
return 0
|
||||
|
||||
/obj/singularity/blob_act(obj/structure/blob/B)
|
||||
return
|
||||
|
||||
/obj/singularity/attack_tk(mob/user)
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
log_game("[key_name(C)] has been disintegrated by attempting to telekenetically grab a singularity.</span>")
|
||||
C.visible_message("<span class='danger'>[C]'s head begins to collapse in on itself!</span>", "<span class='userdanger'>Your head feels like it's collapsing in on itself! This was really not a good idea!</span>", "<span class='italics'>You hear something crack and explode in gore.</span>")
|
||||
for(var/i in 1 to 3)
|
||||
C.apply_damage(30, BRUTE, BODY_ZONE_HEAD)
|
||||
C.spawn_gibs()
|
||||
sleep(1)
|
||||
var/obj/item/bodypart/head/rip_u = C.get_bodypart(BODY_ZONE_HEAD)
|
||||
rip_u.dismember(BURN) //nice try jedi
|
||||
qdel(rip_u)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/singularity/ex_act(severity, target)
|
||||
switch(severity)
|
||||
if(1)
|
||||
if(current_size <= STAGE_TWO)
|
||||
investigate_log("has been destroyed by a heavy explosion.", INVESTIGATE_SINGULO)
|
||||
qdel(src)
|
||||
return
|
||||
else
|
||||
energy -= round(((energy+1)/2),1)
|
||||
if(2)
|
||||
energy -= round(((energy+1)/3),1)
|
||||
if(3)
|
||||
energy -= round(((energy+1)/4),1)
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/bullet_act(obj/item/projectile/P)
|
||||
return 0 //Will there be an impact? Who knows. Will we see it? No.
|
||||
|
||||
|
||||
/obj/singularity/Bump(atom/A)
|
||||
consume(A)
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/Bumped(atom/movable/AM)
|
||||
consume(AM)
|
||||
|
||||
|
||||
/obj/singularity/process()
|
||||
if(current_size >= STAGE_TWO)
|
||||
move()
|
||||
radiation_pulse(src, min(5000, (energy*4.5)+1000), RAD_DISTANCE_COEFFICIENT*0.5)
|
||||
if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit
|
||||
event()
|
||||
eat()
|
||||
dissipate()
|
||||
check_energy()
|
||||
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one.
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/admin_investigate_setup()
|
||||
var/turf/T = get_turf(src)
|
||||
last_warning = world.time
|
||||
var/count = locate(/obj/machinery/field/containment) in urange(30, src, 1)
|
||||
if(!count)
|
||||
message_admins("A singulo has been created without containment fields active at [ADMIN_VERBOSEJMP(T)].")
|
||||
investigate_log("was created at [AREACOORD(T)]. [count?"":"<font color='red'>No containment fields were active</font>"]", INVESTIGATE_SINGULO)
|
||||
|
||||
/obj/singularity/proc/dissipate()
|
||||
if(!dissipate)
|
||||
return
|
||||
if(dissipate_track >= dissipate_delay)
|
||||
src.energy -= dissipate_strength
|
||||
dissipate_track = 0
|
||||
else
|
||||
dissipate_track++
|
||||
|
||||
|
||||
/obj/singularity/proc/expand(force_size = 0)
|
||||
var/temp_allowed_size = src.allowed_size
|
||||
if(force_size)
|
||||
temp_allowed_size = force_size
|
||||
if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter)
|
||||
temp_allowed_size = STAGE_FIVE
|
||||
switch(temp_allowed_size)
|
||||
if(STAGE_ONE)
|
||||
current_size = STAGE_ONE
|
||||
icon = 'icons/obj/singularity.dmi'
|
||||
icon_state = "singularity_s1"
|
||||
pixel_x = 0
|
||||
pixel_y = 0
|
||||
grav_pull = 4
|
||||
consume_range = 0
|
||||
dissipate_delay = 10
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 1
|
||||
if(STAGE_TWO)
|
||||
if(check_cardinals_range(1, TRUE))
|
||||
current_size = STAGE_TWO
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "singularity_s3"
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
grav_pull = 6
|
||||
consume_range = 1
|
||||
dissipate_delay = 5
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 5
|
||||
if(STAGE_THREE)
|
||||
if(check_cardinals_range(2, TRUE))
|
||||
current_size = STAGE_THREE
|
||||
icon = 'icons/effects/160x160.dmi'
|
||||
icon_state = "singularity_s5"
|
||||
pixel_x = -64
|
||||
pixel_y = -64
|
||||
grav_pull = 8
|
||||
consume_range = 2
|
||||
dissipate_delay = 4
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 20
|
||||
if(STAGE_FOUR)
|
||||
if(check_cardinals_range(3, TRUE))
|
||||
current_size = STAGE_FOUR
|
||||
icon = 'icons/effects/224x224.dmi'
|
||||
icon_state = "singularity_s7"
|
||||
pixel_x = -96
|
||||
pixel_y = -96
|
||||
grav_pull = 10
|
||||
consume_range = 3
|
||||
dissipate_delay = 10
|
||||
dissipate_track = 0
|
||||
dissipate_strength = 10
|
||||
if(STAGE_FIVE)//this one also lacks a check for gens because it eats everything
|
||||
current_size = STAGE_FIVE
|
||||
icon = 'icons/effects/288x288.dmi'
|
||||
icon_state = "singularity_s9"
|
||||
pixel_x = -128
|
||||
pixel_y = -128
|
||||
grav_pull = 10
|
||||
consume_range = 4
|
||||
dissipate = 0 //It cant go smaller due to e loss
|
||||
if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard.
|
||||
current_size = STAGE_SIX
|
||||
icon = 'icons/effects/352x352.dmi'
|
||||
icon_state = "singularity_s11"
|
||||
pixel_x = -160
|
||||
pixel_y = -160
|
||||
grav_pull = 15
|
||||
consume_range = 5
|
||||
dissipate = 0
|
||||
if(current_size == allowed_size)
|
||||
investigate_log("<font color='red'>grew to size [current_size]</font>", INVESTIGATE_SINGULO)
|
||||
return 1
|
||||
else if(current_size < (--temp_allowed_size))
|
||||
expand(temp_allowed_size)
|
||||
else
|
||||
return 0
|
||||
|
||||
|
||||
/obj/singularity/proc/check_energy()
|
||||
if(energy <= 0)
|
||||
investigate_log("collapsed.", INVESTIGATE_SINGULO)
|
||||
qdel(src)
|
||||
return 0
|
||||
switch(energy)//Some of these numbers might need to be changed up later -Mport
|
||||
if(1 to 199)
|
||||
allowed_size = STAGE_ONE
|
||||
if(200 to 499)
|
||||
allowed_size = STAGE_TWO
|
||||
if(500 to 999)
|
||||
allowed_size = STAGE_THREE
|
||||
if(1000 to 1999)
|
||||
allowed_size = STAGE_FOUR
|
||||
if(2000 to INFINITY)
|
||||
if(energy >= 3000 && consumedSupermatter)
|
||||
allowed_size = STAGE_SIX
|
||||
else
|
||||
allowed_size = STAGE_FIVE
|
||||
if(current_size != allowed_size)
|
||||
expand()
|
||||
return 1
|
||||
|
||||
|
||||
/obj/singularity/proc/eat()
|
||||
for(var/tile in spiral_range_turfs(grav_pull, src))
|
||||
var/turf/T = tile
|
||||
if(!T || !isturf(loc))
|
||||
continue
|
||||
if(get_dist(T, src) > consume_range)
|
||||
T.singularity_pull(src, current_size)
|
||||
else
|
||||
consume(T)
|
||||
for(var/thing in T)
|
||||
if(isturf(loc) && thing != src)
|
||||
var/atom/movable/X = thing
|
||||
if(get_dist(X, src) > consume_range)
|
||||
X.singularity_pull(src, current_size)
|
||||
else
|
||||
consume(X)
|
||||
CHECK_TICK
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/consume(atom/A)
|
||||
var/gain = A.singularity_act(current_size, src)
|
||||
src.energy += gain
|
||||
if(istype(A, /obj/machinery/power/supermatter_crystal) && !consumedSupermatter)
|
||||
desc = "[initial(desc)] It glows fiercely with inner fire."
|
||||
name = "supermatter-charged [initial(name)]"
|
||||
consumedSupermatter = 1
|
||||
set_light(10)
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/move(force_move = 0)
|
||||
if(!move_self)
|
||||
return 0
|
||||
|
||||
var/movement_dir = pick(GLOB.alldirs - last_failed_movement)
|
||||
|
||||
if(force_move)
|
||||
movement_dir = force_move
|
||||
|
||||
if(target && prob(60))
|
||||
movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one
|
||||
|
||||
step(src, movement_dir)
|
||||
|
||||
/obj/singularity/proc/check_cardinals_range(steps, retry_with_move = FALSE)
|
||||
. = length(GLOB.cardinals) //Should be 4.
|
||||
for(var/i in GLOB.cardinals)
|
||||
. -= check_turfs_in(i, steps) //-1 for each working direction
|
||||
if(. && retry_with_move) //If there's still a positive value it means it didn't pass. Retry with move if applicable
|
||||
for(var/i in GLOB.cardinals)
|
||||
if(step(src, i)) //Move in each direction.
|
||||
if(check_cardinals_range(steps, FALSE)) //New location passes, return true.
|
||||
return TRUE
|
||||
. = !.
|
||||
|
||||
/obj/singularity/proc/check_turfs_in(direction = 0, step = 0)
|
||||
if(!direction)
|
||||
return 0
|
||||
var/steps = 0
|
||||
if(!step)
|
||||
switch(current_size)
|
||||
if(STAGE_ONE)
|
||||
steps = 1
|
||||
if(STAGE_TWO)
|
||||
steps = 3//Yes this is right
|
||||
if(STAGE_THREE)
|
||||
steps = 3
|
||||
if(STAGE_FOUR)
|
||||
steps = 4
|
||||
if(STAGE_FIVE)
|
||||
steps = 5
|
||||
else
|
||||
steps = step
|
||||
var/list/turfs = list()
|
||||
var/turf/T = src.loc
|
||||
for(var/i = 1 to steps)
|
||||
T = get_step(T,direction)
|
||||
if(!isturf(T))
|
||||
return 0
|
||||
turfs.Add(T)
|
||||
var/dir2 = 0
|
||||
var/dir3 = 0
|
||||
switch(direction)
|
||||
if(NORTH||SOUTH)
|
||||
dir2 = 4
|
||||
dir3 = 8
|
||||
if(EAST||WEST)
|
||||
dir2 = 1
|
||||
dir3 = 2
|
||||
var/turf/T2 = T
|
||||
for(var/j = 1 to steps-1)
|
||||
T2 = get_step(T2,dir2)
|
||||
if(!isturf(T2))
|
||||
return 0
|
||||
turfs.Add(T2)
|
||||
for(var/k = 1 to steps-1)
|
||||
T = get_step(T,dir3)
|
||||
if(!isturf(T))
|
||||
return 0
|
||||
turfs.Add(T)
|
||||
for(var/turf/T3 in turfs)
|
||||
if(isnull(T3))
|
||||
continue
|
||||
if(!can_move(T3))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/obj/singularity/proc/can_move(turf/T)
|
||||
if(!T)
|
||||
return 0
|
||||
if((locate(/obj/machinery/field/containment) in T)||(locate(/obj/machinery/shieldwall) in T))
|
||||
return 0
|
||||
else if(locate(/obj/machinery/field/generator) in T)
|
||||
var/obj/machinery/field/generator/G = locate(/obj/machinery/field/generator) in T
|
||||
if(G && G.active)
|
||||
return 0
|
||||
else if(locate(/obj/machinery/shieldwallgen) in T)
|
||||
var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T
|
||||
if(S && S.active)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/obj/singularity/proc/event()
|
||||
var/numb = rand(1,4)
|
||||
switch(numb)
|
||||
if(1)//EMP
|
||||
emp_area()
|
||||
if(2)//Stun mobs who lack optic scanners
|
||||
mezzer()
|
||||
if(3,4) //Sets all nearby mobs on fire
|
||||
if(current_size < STAGE_SIX)
|
||||
return 0
|
||||
combust_mobs()
|
||||
else
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/obj/singularity/proc/combust_mobs()
|
||||
for(var/mob/living/carbon/C in urange(20, src, 1))
|
||||
C.visible_message("<span class='warning'>[C]'s skin bursts into flame!</span>", \
|
||||
"<span class='userdanger'>You feel an inner fire as your skin bursts into flames!</span>")
|
||||
C.adjust_fire_stacks(5)
|
||||
C.IgniteMob()
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/mezzer()
|
||||
for(var/mob/living/carbon/M in oviewers(8, src))
|
||||
if(isbrain(M)) //Ignore brains
|
||||
continue
|
||||
|
||||
if(M.stat == CONSCIOUS)
|
||||
if (ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(istype(H.glasses, /obj/item/clothing/glasses/meson))
|
||||
var/obj/item/clothing/glasses/meson/MS = H.glasses
|
||||
if(MS.vision_flags == SEE_TURFS)
|
||||
to_chat(H, "<span class='notice'>You look directly into the [src.name], good thing you had your protective eyewear on!</span>")
|
||||
return
|
||||
|
||||
M.apply_effect(60, EFFECT_STUN)
|
||||
M.visible_message("<span class='danger'>[M] stares blankly at the [src.name]!</span>", \
|
||||
"<span class='userdanger'>You look directly into the [src.name] and feel weak.</span>")
|
||||
return
|
||||
|
||||
|
||||
/obj/singularity/proc/emp_area()
|
||||
empulse(src, 8, 10)
|
||||
return
|
||||
|
||||
/obj/singularity/singularity_act()
|
||||
var/gain = (energy/2)
|
||||
var/dist = max((current_size - 2),1)
|
||||
explosion(src.loc,(dist),(dist*2),(dist*4))
|
||||
qdel(src)
|
||||
return(gain)
|
||||
Reference in New Issue
Block a user