Merge branch 'master' into upstream-merge-27344
This commit is contained in:
@@ -108,7 +108,11 @@ GLOBAL_PROTECT(AdminProcCallCount)
|
||||
return call(target, procname)(arglist(arguments))
|
||||
|
||||
/proc/IsAdminAdvancedProcCall()
|
||||
#ifdef TESTING
|
||||
return FALSE
|
||||
#else
|
||||
return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey
|
||||
#endif
|
||||
|
||||
/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf)
|
||||
set category = "Debug"
|
||||
|
||||
@@ -153,8 +153,8 @@
|
||||
return
|
||||
var/datum/gas_mixture/air1 = AIR1
|
||||
var/turf/T = get_turf(src)
|
||||
if(isliving(occupant))
|
||||
var/mob/living/mob_occupant
|
||||
if(occupant)
|
||||
var/mob/living/mob_occupant = occupant
|
||||
if(mob_occupant.health >= 100) // Don't bother with fully healed people.
|
||||
on = FALSE
|
||||
update_icon()
|
||||
@@ -176,8 +176,8 @@
|
||||
|
||||
if(beaker)
|
||||
if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic.
|
||||
beaker.reagents.trans_to(mob_occupant, 1, 10 * efficiency) // Transfer reagents, multiplied because cryo magic.
|
||||
beaker.reagents.reaction(mob_occupant, VAPOR)
|
||||
beaker.reagents.trans_to(occupant, 1, 10 * efficiency) // Transfer reagents, multiplied because cryo magic.
|
||||
beaker.reagents.reaction(occupant, VAPOR)
|
||||
air1.gases["o2"][MOLES] -= 2 / efficiency // Lets use gas for this.
|
||||
if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker).
|
||||
reagent_transfer = 0
|
||||
@@ -192,7 +192,7 @@
|
||||
on = FALSE
|
||||
update_icon()
|
||||
return
|
||||
if(isliving(occupant))
|
||||
if(occupant)
|
||||
var/mob/living/mob_occupant = occupant
|
||||
var/cold_protection = 0
|
||||
var/mob/living/carbon/human/H = occupant
|
||||
@@ -244,7 +244,7 @@
|
||||
..()
|
||||
if(occupant)
|
||||
if(on)
|
||||
to_chat(user, "[occupant] is inside [src]!")
|
||||
to_chat(user, "Someone's inside [src]!")
|
||||
else
|
||||
to_chat(user, "You can barely make out a form floating in [src].")
|
||||
else
|
||||
@@ -299,7 +299,7 @@
|
||||
data["autoEject"] = autoeject
|
||||
|
||||
var/list/occupantData = list()
|
||||
if(isliving(occupant))
|
||||
if(occupant)
|
||||
var/mob/living/mob_occupant = occupant
|
||||
occupantData["name"] = mob_occupant.name
|
||||
occupantData["stat"] = mob_occupant.stat
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
|
||||
//Movable and easily code-modified fields! Allows for custom AOE effects that affect movement and anything inside of them, and can do custom turf effects!
|
||||
//Supports automatic recalculation/reset on movement.
|
||||
//If there's any way to make this less CPU intensive than I've managed, gimme a call or do it yourself! - kevinz000
|
||||
|
||||
//Field shapes
|
||||
#define FIELD_NO_SHAPE 0 //Does not update turfs automatically
|
||||
#define FIELD_SHAPE_RADIUS_SQUARE 1 //Uses current_range and square_depth_up/down
|
||||
#define FIELD_SHAPE_CUSTOM_SQUARE 2 //Uses square_height and square_width and square_depth_up/down
|
||||
|
||||
//Proc to make fields. make_field(field_type, field_params_in_associative_list)
|
||||
/proc/make_field(field_type, list/field_params, override_checks = FALSE, start_field = TRUE)
|
||||
var/datum/proximity_monitor/advanced/F = new field_type()
|
||||
if(!F.assume_params(field_params) && !override_checks)
|
||||
QDEL_NULL(F)
|
||||
if(!F.check_variables() && !override_checks)
|
||||
QDEL_NULL(F)
|
||||
if(start_field && (F || override_checks))
|
||||
F.Initialize()
|
||||
return F
|
||||
|
||||
/datum/proximity_monitor/advanced
|
||||
var/name = "\improper Energy Field"
|
||||
//Field setup specifications
|
||||
var/field_shape = FIELD_NO_SHAPE
|
||||
var/square_height = 0
|
||||
var/square_width = 0
|
||||
var/square_depth_up = 0
|
||||
var/square_depth_down = 0
|
||||
//Processing
|
||||
var/requires_processing = FALSE
|
||||
var/process_inner_turfs = FALSE //Don't do this unless it's absolutely necessary
|
||||
var/process_edge_turfs = FALSE //Don't do this either unless it's absolutely necessary, you can just track what things are inside manually or on the initial setup.
|
||||
var/setup_edge_turfs = FALSE //Setup edge turfs/all field turfs. Set either or both to ON when you need it, it's defaulting to off unless you do to save CPU.
|
||||
var/setup_field_turfs = FALSE
|
||||
|
||||
var/list/turf/field_turfs = list()
|
||||
var/list/turf/edge_turfs = list()
|
||||
var/list/turf/field_turfs_new = list()
|
||||
var/list/turf/edge_turfs_new = list()
|
||||
|
||||
/datum/proximity_monitor/advanced/New()
|
||||
SSfields.register_new_field(src)
|
||||
|
||||
/datum/proximity_monitor/advanced/Destroy()
|
||||
SSfields.unregister_field(src)
|
||||
full_cleanup()
|
||||
return ..()
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/assume_params(list/field_params)
|
||||
var/pass_check = TRUE
|
||||
for(var/param in field_params)
|
||||
if(vars[param] || isnull(vars[param]) || (param in vars))
|
||||
vars[param] = field_params[param]
|
||||
else
|
||||
pass_check = FALSE
|
||||
return pass_check
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/check_variables()
|
||||
var/pass = TRUE
|
||||
if(field_shape == FIELD_NO_SHAPE) //If you're going to make a manually updated field you shouldn't be using automatic checks so don't.
|
||||
pass = FALSE
|
||||
if(current_range < 0 || square_height < 0 || square_width < 0 || square_depth_up < 0 || square_depth_down < 0)
|
||||
pass = FALSE
|
||||
if(!istype(host))
|
||||
pass = FALSE
|
||||
return pass
|
||||
|
||||
/datum/proximity_monitor/advanced/process()
|
||||
if(process_inner_turfs)
|
||||
for(var/turf/T in field_turfs)
|
||||
process_inner_turf(T)
|
||||
CHECK_TICK //Really crappy lagchecks, needs improvement once someone starts using processed fields.
|
||||
if(process_edge_turfs)
|
||||
for(var/turf/T in edge_turfs)
|
||||
process_edge_turf(T)
|
||||
CHECK_TICK //Same here.
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/process_inner_turf(turf/T)
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/process_edge_turf(turf/T)
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/Initialize()
|
||||
setup_field()
|
||||
post_setup_field()
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/full_cleanup() //Full cleanup for when you change something that would require complete resetting.
|
||||
for(var/turf/T in edge_turfs)
|
||||
cleanup_edge_turf(T)
|
||||
for(var/turf/T in field_turfs)
|
||||
cleanup_field_turf(T)
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/recalculate_field(ignore_movement_check = FALSE) //Call every time the field moves (done automatically if you use update_center) or a setup specification is changed.
|
||||
if(!(ignore_movement_check || ((host.loc != last_host_loc) && (field_shape != FIELD_NO_SHAPE))))
|
||||
return
|
||||
update_new_turfs()
|
||||
var/list/turf/needs_setup = field_turfs_new.Copy()
|
||||
if(setup_field_turfs)
|
||||
for(var/turf/T in field_turfs)
|
||||
if(!(T in needs_setup))
|
||||
cleanup_field_turf(T)
|
||||
else
|
||||
needs_setup -= T
|
||||
CHECK_TICK
|
||||
for(var/turf/T in needs_setup)
|
||||
setup_field_turf(T)
|
||||
CHECK_TICK
|
||||
if(setup_edge_turfs)
|
||||
for(var/turf/T in edge_turfs)
|
||||
cleanup_edge_turf(T)
|
||||
CHECK_TICK
|
||||
for(var/turf/T in edge_turfs_new)
|
||||
setup_edge_turf(T)
|
||||
CHECK_TICK
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_turf_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F, turf/entering)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_turf_uncross(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_turf_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_turf_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_edge_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F, turf/entering)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_edge_uncross(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_edge_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/field_edge_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F)
|
||||
return TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/HandleMove()
|
||||
var/atom/_host = host
|
||||
var/atom/new_host_loc = _host.loc
|
||||
if(last_host_loc != new_host_loc)
|
||||
recalculate_field()
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/post_setup_field()
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/setup_field()
|
||||
update_new_turfs()
|
||||
if(setup_field_turfs)
|
||||
for(var/turf/T in field_turfs_new)
|
||||
setup_field_turf(T)
|
||||
CHECK_TICK
|
||||
if(setup_edge_turfs)
|
||||
for(var/turf/T in edge_turfs_new)
|
||||
setup_edge_turf(T)
|
||||
CHECK_TICK
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/cleanup_field_turf(turf/T)
|
||||
qdel(field_turfs[T])
|
||||
field_turfs -= T
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/cleanup_edge_turf(turf/T)
|
||||
qdel(edge_turfs[T])
|
||||
edge_turfs -= T
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/setup_field_turf(turf/T)
|
||||
field_turfs[T] = new /obj/effect/abstract/proximity_checker/advanced/field_turf(T, src)
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/setup_edge_turf(turf/T)
|
||||
edge_turfs[T] = new /obj/effect/abstract/proximity_checker/advanced/field_edge(T, src)
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/update_new_turfs()
|
||||
if(!istype(host))
|
||||
return FALSE
|
||||
last_host_loc = host.loc
|
||||
var/turf/center = get_turf(host)
|
||||
field_turfs_new = list()
|
||||
edge_turfs_new = list()
|
||||
switch(field_shape)
|
||||
if(FIELD_NO_SHAPE)
|
||||
return FALSE
|
||||
if(FIELD_SHAPE_RADIUS_SQUARE)
|
||||
for(var/turf/T in block(locate(center.x-current_range,center.y-current_range,center.z-square_depth_down),locate(center.x+current_range, center.y+current_range,center.z+square_depth_up)))
|
||||
field_turfs_new += T
|
||||
edge_turfs_new = field_turfs_new.Copy()
|
||||
if(current_range >= 1)
|
||||
var/list/turf/center_turfs = list()
|
||||
for(var/turf/T in block(locate(center.x-current_range+1,center.y-current_range+1,center.z-square_depth_down),locate(center.x+current_range-1, center.y+current_range-1,center.z+square_depth_up)))
|
||||
center_turfs += T
|
||||
for(var/turf/T in center_turfs)
|
||||
edge_turfs_new -= T
|
||||
if(FIELD_SHAPE_CUSTOM_SQUARE)
|
||||
for(var/turf/T in block(locate(center.x-square_width,center.y-square_height,center.z-square_depth_down),locate(center.x+square_width, center.y+square_height,center.z+square_depth_up)))
|
||||
field_turfs_new += T
|
||||
edge_turfs_new = field_turfs_new.Copy()
|
||||
if(square_height >= 1 && square_width >= 1)
|
||||
var/list/turf/center_turfs = list()
|
||||
for(var/turf/T in block(locate(center.x-square_width+1,center.y-square_height+1,center.z-square_depth_down),locate(center.x+square_width-1, center.y+square_height-1,center.z+square_depth_up)))
|
||||
center_turfs += T
|
||||
for(var/turf/T in center_turfs)
|
||||
edge_turfs_new -= T
|
||||
|
||||
//Gets edge direction/corner, only works with square radius/WDH fields!
|
||||
/datum/proximity_monitor/advanced/proc/get_edgeturf_direction(turf/T, turf/center_override = null)
|
||||
var/turf/checking_from = get_turf(host)
|
||||
if(istype(center_override))
|
||||
checking_from = center_override
|
||||
if(field_shape != FIELD_SHAPE_RADIUS_SQUARE && field_shape != FIELD_SHAPE_CUSTOM_SQUARE)
|
||||
return
|
||||
if(!(T in edge_turfs))
|
||||
return
|
||||
switch(field_shape)
|
||||
if(FIELD_SHAPE_RADIUS_SQUARE)
|
||||
if(((T.x == (checking_from.x + current_range)) || (T.x == (checking_from.x - current_range))) && ((T.y == (checking_from.y + current_range)) || (T.y == (checking_from.y - current_range))))
|
||||
return get_dir(checking_from, T)
|
||||
if(T.x == (checking_from.x + current_range))
|
||||
return EAST
|
||||
if(T.x == (checking_from.x - current_range))
|
||||
return WEST
|
||||
if(T.y == (checking_from.y - current_range))
|
||||
return SOUTH
|
||||
if(T.y == (checking_from.y + current_range))
|
||||
return NORTH
|
||||
if(FIELD_SHAPE_CUSTOM_SQUARE)
|
||||
if(((T.x == (checking_from.x + square_width)) || (T.x == (checking_from.x - square_width))) && ((T.y == (checking_from.y + square_height)) || (T.y == (checking_from.y - square_height))))
|
||||
return get_dir(checking_from, T)
|
||||
if(T.x == (checking_from.x + square_width))
|
||||
return EAST
|
||||
if(T.x == (checking_from.x - square_width))
|
||||
return WEST
|
||||
if(T.y == (checking_from.y - square_height))
|
||||
return SOUTH
|
||||
if(T.y == (checking_from.y + square_height))
|
||||
return NORTH
|
||||
|
||||
//DEBUG FIELDS
|
||||
/datum/proximity_monitor/advanced/debug
|
||||
name = "\improper Color Matrix Field"
|
||||
field_shape = FIELD_SHAPE_RADIUS_SQUARE
|
||||
current_range = 5
|
||||
var/set_fieldturf_color = "#aaffff"
|
||||
var/set_edgeturf_color = "#ffaaff"
|
||||
setup_field_turfs = TRUE
|
||||
setup_edge_turfs = TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/debug/recalculate_field()
|
||||
..()
|
||||
|
||||
/datum/proximity_monitor/advanced/debug/post_setup_field()
|
||||
..()
|
||||
|
||||
/datum/proximity_monitor/advanced/debug/setup_edge_turf(turf/T)
|
||||
T.color = set_edgeturf_color
|
||||
..()
|
||||
|
||||
/datum/proximity_monitor/advanced/debug/cleanup_edge_turf(turf/T)
|
||||
T.color = initial(T.color)
|
||||
..()
|
||||
if(T in field_turfs)
|
||||
T.color = set_fieldturf_color
|
||||
|
||||
/datum/proximity_monitor/advanced/debug/setup_field_turf(turf/T)
|
||||
T.color = set_fieldturf_color
|
||||
..()
|
||||
|
||||
/datum/proximity_monitor/advanced/debug/cleanup_field_turf(turf/T)
|
||||
T.color = initial(T.color)
|
||||
..()
|
||||
|
||||
//DEBUG FIELD ITEM
|
||||
/obj/item/device/multitool/field_debug
|
||||
name = "strange multitool"
|
||||
desc = "Seems to project a colored field!"
|
||||
var/list/field_params = list("field_shape" = FIELD_SHAPE_RADIUS_SQUARE, "current_range" = 5, "set_fieldturf_color" = "#aaffff", "set_edgeturf_color" = "#ffaaff")
|
||||
var/field_type = /datum/proximity_monitor/advanced/debug
|
||||
var/operating = FALSE
|
||||
var/datum/proximity_monitor/advanced/current = null
|
||||
|
||||
/obj/item/device/multitool/field_debug/New()
|
||||
START_PROCESSING(SSobj, src)
|
||||
..()
|
||||
|
||||
/obj/item/device/multitool/field_debug/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
QDEL_NULL(current)
|
||||
..()
|
||||
|
||||
/obj/item/device/multitool/field_debug/proc/setup_debug_field()
|
||||
var/list/new_params = field_params.Copy()
|
||||
new_params["host"] = src
|
||||
current = make_field(field_type, new_params)
|
||||
|
||||
/obj/item/device/multitool/field_debug/attack_self(mob/user)
|
||||
operating = !operating
|
||||
to_chat(user, "You turn the [src] [operating? "on":"off"].")
|
||||
if(!istype(current) && operating)
|
||||
setup_debug_field()
|
||||
else if(!operating)
|
||||
QDEL_NULL(current)
|
||||
|
||||
/obj/item/device/multitool/field_debug/on_mob_move()
|
||||
check_turf(get_turf(src))
|
||||
|
||||
/obj/item/device/multitool/field_debug/process()
|
||||
check_turf(get_turf(src))
|
||||
|
||||
/obj/item/device/multitool/field_debug/proc/check_turf(turf/T)
|
||||
current.HandleMove()
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
//Projectile dampening field that slows projectiles and lowers their damage for an energy cost deducted every 1/5 second.
|
||||
//Only use square radius for this!
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener
|
||||
name = "\improper Hyperkinetic Dampener Field"
|
||||
requires_processing = TRUE
|
||||
setup_edge_turfs = TRUE
|
||||
setup_field_turfs = TRUE
|
||||
field_shape = FIELD_SHAPE_RADIUS_SQUARE
|
||||
var/static/image/edgeturf_south = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_south")
|
||||
var/static/image/edgeturf_north = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_north")
|
||||
var/static/image/edgeturf_west = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_west")
|
||||
var/static/image/edgeturf_east = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_east")
|
||||
var/static/image/northwest_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northwest")
|
||||
var/static/image/southwest_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southwest")
|
||||
var/static/image/northeast_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northeast")
|
||||
var/static/image/southeast_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southeast")
|
||||
var/obj/item/borg/projectile_dampen/projector = null
|
||||
var/list/obj/item/projectile/tracked
|
||||
var/list/obj/item/projectile/staging
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/New()
|
||||
tracked = list()
|
||||
staging = list()
|
||||
..()
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/process()
|
||||
if(!istype(projector))
|
||||
qdel(src)
|
||||
var/list/ranged = list()
|
||||
for(var/obj/item/projectile/P in range(current_range, get_turf(host)))
|
||||
ranged += P
|
||||
for(var/obj/item/projectile/P in tracked)
|
||||
if(!(P in ranged) || !P.loc)
|
||||
release_projectile(P)
|
||||
for(var/mob/living/silicon/robot/R in range(current_range, get_turf(host)))
|
||||
if(R.has_buckled_mobs())
|
||||
for(var/mob/living/L in R.buckled_mobs)
|
||||
L.visible_message("<span class='warning'>[L] is knocked off of [R] by the charge in [R]'s chassis induced by [name]!</span>") //I know it's bad.
|
||||
L.Weaken(3)
|
||||
R.unbuckle_mob(L)
|
||||
do_sparks(5, 0, L)
|
||||
..()
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/setup_edge_turf(turf/T)
|
||||
..()
|
||||
var/image/I = get_edgeturf_overlay(get_edgeturf_direction(T))
|
||||
var/obj/effect/abstract/proximity_checker/advanced/F = edge_turfs[T]
|
||||
F.appearance = I.appearance
|
||||
F.invisibility = 0
|
||||
F.layer = 5
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/cleanup_edge_turf(turf/T)
|
||||
..()
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/proc/get_edgeturf_overlay(direction)
|
||||
switch(direction)
|
||||
if(NORTH)
|
||||
return edgeturf_north
|
||||
if(SOUTH)
|
||||
return edgeturf_south
|
||||
if(EAST)
|
||||
return edgeturf_east
|
||||
if(WEST)
|
||||
return edgeturf_west
|
||||
if(NORTHEAST)
|
||||
return northeast_corner
|
||||
if(NORTHWEST)
|
||||
return northwest_corner
|
||||
if(SOUTHEAST)
|
||||
return southeast_corner
|
||||
if(SOUTHWEST)
|
||||
return southwest_corner
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/proc/capture_projectile(obj/item/projectile/P, track_projectile = TRUE)
|
||||
if(P in tracked)
|
||||
return
|
||||
projector.dampen_projectile(P, track_projectile)
|
||||
if(track_projectile)
|
||||
tracked += P
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/proc/release_projectile(obj/item/projectile/P)
|
||||
projector.restore_projectile(P)
|
||||
tracked -= P
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/field_edge_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F)
|
||||
if(!is_turf_in_field(get_turf(AM), src))
|
||||
if(istype(AM, /obj/item/projectile))
|
||||
if(AM in tracked)
|
||||
release_projectile(AM)
|
||||
else
|
||||
capture_projectile(AM, FALSE)
|
||||
return ..()
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/field_edge_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F)
|
||||
if(istype(AM, /obj/item/projectile) && !(AM in tracked) && staging[AM] && !is_turf_in_field(staging[AM], src))
|
||||
capture_projectile(AM)
|
||||
staging -= AM
|
||||
return ..()
|
||||
|
||||
/datum/proximity_monitor/advanced/peaceborg_dampener/field_edge_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F, turf/entering)
|
||||
if(istype(AM, /obj/item/projectile))
|
||||
staging[AM] = get_turf(AM)
|
||||
. = ..()
|
||||
if(!.)
|
||||
staging -= AM //This one ain't goin' through.
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced
|
||||
name = "field"
|
||||
desc = "Why can you see energy fields?!"
|
||||
icon = null
|
||||
icon_state = null
|
||||
alpha = 0
|
||||
invisibility = INVISIBILITY_ABSTRACT
|
||||
flags = ABSTRACT|ON_BORDER
|
||||
var/datum/proximity_monitor/advanced/parent = null
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/Initialize(mapload, _monitor)
|
||||
if(_monitor)
|
||||
parent = _monitor
|
||||
return ..()
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/center
|
||||
name = "field anchor"
|
||||
desc = "No."
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_turf
|
||||
name = "energy field"
|
||||
desc = "Get off my turf!"
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_turf/CanPass(atom/movable/AM, turf/target, height)
|
||||
if(parent)
|
||||
return parent.field_turf_canpass(AM, src, target)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_turf/Crossed(atom/movable/AM)
|
||||
if(parent)
|
||||
return parent.field_turf_crossed(AM, src)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_turf/Uncross(atom/movable/AM)
|
||||
if(parent)
|
||||
return parent.field_turf_uncross(AM, src)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_turf/Uncrossed(atom/movable/AM)
|
||||
if(parent)
|
||||
return parent.field_turf_uncrossed(AM, src)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_edge
|
||||
name = "energy field edge"
|
||||
desc = "Edgy description here."
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_edge/CanPass(atom/movable/AM, turf/target, height)
|
||||
if(parent)
|
||||
return parent.field_edge_canpass(AM, src, target)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_edge/Crossed(atom/movable/AM)
|
||||
if(parent)
|
||||
return parent.field_edge_crossed(AM, src)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_edge/Uncross(atom/movable/AM)
|
||||
if(parent)
|
||||
return parent.field_edge_uncross(AM, src)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_edge/Uncrossed(atom/movable/AM)
|
||||
if(parent)
|
||||
return parent.field_edge_uncrossed(AM, src)
|
||||
return TRUE
|
||||
|
||||
/proc/is_turf_in_field(turf/T, datum/proximity_monitor/advanced/F) //Looking for ways to optimize this!
|
||||
for(var/obj/effect/abstract/proximity_checker/advanced/O in T)
|
||||
if(istype(O, /obj/effect/abstract/proximity_checker/advanced/field_edge))
|
||||
if(O.parent == F)
|
||||
return FIELD_EDGE
|
||||
if(O.parent == F)
|
||||
return FIELD_TURF
|
||||
return NO_FIELD
|
||||
@@ -135,7 +135,6 @@
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/customizable/initialize_slice(obj/item/weapon/reagent_containers/food/snacks/slice, reagents_per_slice)
|
||||
..()
|
||||
slice.name = "[customname] [initial(slice.name)]"
|
||||
slice.filling_color = filling_color
|
||||
slice.update_overlays(src)
|
||||
|
||||
|
||||
@@ -215,8 +215,9 @@
|
||||
/obj/item/weapon/reagent_containers/food/snacks/proc/initialize_slice(obj/item/weapon/reagent_containers/food/snacks/slice, reagents_per_slice)
|
||||
slice.create_reagents(slice.volume)
|
||||
reagents.trans_to(slice,reagents_per_slice)
|
||||
if( name != initial(name) || desc != initial(desc) )
|
||||
slice.name = "slice of [src]"
|
||||
if(name != initial(name))
|
||||
slice.name = "slice of [name]"
|
||||
if(desc != initial(desc))
|
||||
slice.desc = "[desc]"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/proc/generate_trash(atom/location)
|
||||
|
||||
@@ -174,7 +174,6 @@
|
||||
var/mob/living/carbon/human/gibee = occupant
|
||||
sourcejob = gibee.job
|
||||
var/sourcenutriment = mob_occupant.nutrition / 15
|
||||
var/sourcetotalreagents = mob_occupant.reagents.total_volume
|
||||
var/gibtype = /obj/effect/decal/cleanable/blood/gibs
|
||||
var/typeofmeat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human
|
||||
var/typeofskin = /obj/item/stack/sheet/animalhide/human
|
||||
@@ -207,7 +206,6 @@
|
||||
newmeat.reagents.add_reagent ("nutriment", sourcenutriment / meat_produced) // Thehehe. Fat guys go first
|
||||
if(sourcejob)
|
||||
newmeat.subjectjob = sourcejob
|
||||
src.occupant.reagents.trans_to (newmeat, round (sourcetotalreagents / meat_produced, 1)) // Transfer all the reagents from the
|
||||
allmeat[i] = newmeat
|
||||
allskin = newskin
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
#define BAD_ZLEVEL 1
|
||||
#define BAD_AREA 2
|
||||
#define ZONE_SET 3
|
||||
#define BAD_COORDS 3
|
||||
#define ZONE_SET 4
|
||||
|
||||
/area/shuttle/auxillary_base
|
||||
name = "Auxillary Base"
|
||||
@@ -149,6 +150,9 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
|
||||
if(T.z != ZLEVEL_MINING)
|
||||
return BAD_ZLEVEL
|
||||
var/colony_radius = max(base_dock.width, base_dock.height)*0.5
|
||||
if(T.x - colony_radius < 1 || T.x + colony_radius >= world.maxx || T.y - colony_radius < 1 || T.y + colony_radius >= world.maxx)
|
||||
return BAD_COORDS //Avoid dropping the base too close to map boundaries, as it results in parts of it being left in space
|
||||
|
||||
var/list/area_counter = get_areas_in_range(colony_radius, T)
|
||||
if(area_counter.len > 1) //Avoid smashing ruins unless you are inside a really big one
|
||||
return BAD_AREA
|
||||
@@ -195,6 +199,7 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
|
||||
if(!do_after(user, 50, target = user)) //You get a few seconds to cancel if you do not want to drop there.
|
||||
setting = FALSE
|
||||
return
|
||||
setting = FALSE
|
||||
|
||||
var/turf/T = get_turf(user)
|
||||
var/obj/machinery/computer/auxillary_base/AB
|
||||
@@ -212,6 +217,8 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
|
||||
to_chat(user, "<span class='warning'>This uplink can only be used in a designed mining zone.</span>")
|
||||
if(BAD_AREA)
|
||||
to_chat(user, "<span class='warning'>Unable to acquire a targeting lock. Find an area clear of stuctures or entirely within one.</span>")
|
||||
if(BAD_COORDS)
|
||||
to_chat(user, "<span class='warning'>Location is too close to the edge of the station's scanning range. Move several paces away and try again.</span>")
|
||||
if(ZONE_SET)
|
||||
qdel(src)
|
||||
|
||||
@@ -347,4 +354,5 @@ obj/docking_port/stationary/public_mining_dock/onShuttleMove()
|
||||
|
||||
#undef BAD_ZLEVEL
|
||||
#undef BAD_AREA
|
||||
#undef BAD_COORDS
|
||||
#undef ZONE_SET
|
||||
@@ -1,5 +1,6 @@
|
||||
//The chests dropped by mob spawner tendrils. Also contains associated loot.
|
||||
|
||||
|
||||
/obj/structure/closet/crate/necropolis
|
||||
name = "necropolis chest"
|
||||
desc = "It's watching you closely."
|
||||
@@ -821,6 +822,8 @@
|
||||
if(isliving(target) && chaser_timer <= world.time) //living and chasers off cooldown? fire one!
|
||||
chaser_timer = world.time + chaser_cooldown
|
||||
new /obj/effect/temp_visual/hierophant/chaser(get_turf(user), user, target, chaser_speed, friendly_fire_check)
|
||||
C.damage = 30
|
||||
C.monster_damage_boost = FALSE
|
||||
add_logs(user, target, "fired a chaser at", src)
|
||||
else
|
||||
INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) //otherwise, just do cardinal blast
|
||||
@@ -1005,6 +1008,8 @@
|
||||
if(!J)
|
||||
return
|
||||
new /obj/effect/temp_visual/hierophant/blast(J, user, friendly_fire_check)
|
||||
B.damage = 30
|
||||
B.monster_damage_boost = FALSE
|
||||
previousturf = J
|
||||
J = get_step(previousturf, dir)
|
||||
|
||||
@@ -1016,3 +1021,4 @@
|
||||
sleep(2)
|
||||
for(var/t in RANGE_TURFS(1, T))
|
||||
new /obj/effect/temp_visual/hierophant/blast(t, user, friendly_fire_check)
|
||||
B.damage = 15 //keeps monster damage boost due to lower damage
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
/mob/living/carbon/alien/AdjustStunned(amount, updating = 1, ignore_canstun = 0)
|
||||
. = ..()
|
||||
if(!.)
|
||||
move_delay_add = min(move_delay_add + round(amount / 2), 10)
|
||||
move_delay_add = Clamp(move_delay_add + round(amount/2), 0, 10)
|
||||
|
||||
@@ -823,7 +823,7 @@
|
||||
|
||||
/mob/living/proc/has_bane(banetype)
|
||||
var/datum/antagonist/devil/devilInfo = is_devil(src)
|
||||
return (banetype == devilInfo.bane)
|
||||
return devilInfo && banetype == devilInfo.bane
|
||||
|
||||
/mob/living/proc/check_weakness(obj/item/weapon, mob/living/attacker)
|
||||
if(mind && mind.has_antag_datum(ANTAG_DATUM_DEVIL))
|
||||
|
||||
@@ -1,361 +1,370 @@
|
||||
|
||||
/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = null, soften_text = null, armour_penetration, penetrated_text)
|
||||
var/armor = getarmor(def_zone, attack_flag)
|
||||
|
||||
//the if "armor" check is because this is used for everything on /living, including humans
|
||||
if(armor && armour_penetration)
|
||||
armor = max(0, armor - armour_penetration)
|
||||
if(penetrated_text)
|
||||
to_chat(src, "<span class='userdanger'>[penetrated_text]</span>")
|
||||
else
|
||||
to_chat(src, "<span class='userdanger'>Your armor was penetrated!</span>")
|
||||
else if(armor >= 100)
|
||||
if(absorb_text)
|
||||
to_chat(src, "<span class='userdanger'>[absorb_text]</span>")
|
||||
else
|
||||
to_chat(src, "<span class='userdanger'>Your armor absorbs the blow!</span>")
|
||||
else if(armor > 0)
|
||||
if(soften_text)
|
||||
to_chat(src, "<span class='userdanger'>[soften_text]</span>")
|
||||
else
|
||||
to_chat(src, "<span class='userdanger'>Your armor softens the blow!</span>")
|
||||
return armor
|
||||
|
||||
|
||||
/mob/living/proc/getarmor(def_zone, type)
|
||||
return 0
|
||||
|
||||
//this returns the mob's protection against eye damage (number between -1 and 2)
|
||||
/mob/living/proc/get_eye_protection()
|
||||
return 0
|
||||
|
||||
//this returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears)
|
||||
/mob/living/proc/get_ear_protection()
|
||||
return 0
|
||||
|
||||
/mob/living/proc/on_hit(obj/item/projectile/P)
|
||||
return
|
||||
|
||||
/mob/living/bullet_act(obj/item/projectile/P, def_zone)
|
||||
var/armor = run_armor_check(def_zone, P.flag, "","",P.armour_penetration)
|
||||
if(!P.nodamage)
|
||||
apply_damage(P.damage, P.damage_type, def_zone, armor)
|
||||
if(P.dismemberment)
|
||||
check_projectile_dismemberment(P, def_zone)
|
||||
return P.on_hit(src, armor)
|
||||
|
||||
/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
|
||||
return 0
|
||||
|
||||
/obj/item/proc/get_volume_by_throwforce_and_or_w_class()
|
||||
if(throwforce && w_class)
|
||||
return Clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100
|
||||
else if(w_class)
|
||||
return Clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100
|
||||
else
|
||||
return 0
|
||||
|
||||
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0)
|
||||
if(istype(AM, /obj/item))
|
||||
var/obj/item/I = AM
|
||||
var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest
|
||||
var/dtype = BRUTE
|
||||
var/volume = I.get_volume_by_throwforce_and_or_w_class()
|
||||
if(istype(I,/obj/item/weapon)) //If the item is a weapon...
|
||||
var/obj/item/weapon/W = I
|
||||
dtype = W.damtype
|
||||
|
||||
if (W.throwforce > 0) //If the weapon's throwforce is greater than zero...
|
||||
if (W.throwhitsound) //...and throwhitsound is defined...
|
||||
playsound(loc, W.throwhitsound, volume, 1, -1) //...play the weapon's throwhitsound.
|
||||
else if(W.hitsound) //Otherwise, if the weapon's hitsound is defined...
|
||||
playsound(loc, W.hitsound, volume, 1, -1) //...play the weapon's hitsound.
|
||||
else if(!W.throwhitsound) //Otherwise, if throwhitsound isn't defined...
|
||||
playsound(loc, 'sound/weapons/genhit.ogg',volume, 1, -1) //...play genhit.ogg.
|
||||
|
||||
else if(!I.throwhitsound && I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero...
|
||||
playsound(loc, 'sound/weapons/genhit.ogg', volume, 1, -1)//...play genhit.ogg
|
||||
if(!I.throwforce)// Otherwise, if the item's throwforce is 0...
|
||||
playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg.
|
||||
if(!blocked)
|
||||
visible_message("<span class='danger'>[src] has been hit by [I].</span>", \
|
||||
"<span class='userdanger'>[src] has been hit by [I].</span>")
|
||||
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration)
|
||||
apply_damage(I.throwforce, dtype, zone, armor)
|
||||
if(I.thrownby)
|
||||
add_logs(I.thrownby, src, "hit", I)
|
||||
else
|
||||
return 1
|
||||
else
|
||||
playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/mech_melee_attack(obj/mecha/M)
|
||||
if(M.occupant.a_intent == INTENT_HARM)
|
||||
M.do_attack_animation(src)
|
||||
if(M.damtype == "brute")
|
||||
step_away(src,M,15)
|
||||
switch(M.damtype)
|
||||
if(BRUTE)
|
||||
Paralyse(1)
|
||||
take_overall_damage(rand(M.force/2, M.force))
|
||||
playsound(src, 'sound/weapons/punch4.ogg', 50, 1)
|
||||
if(BURN)
|
||||
take_overall_damage(0, rand(M.force/2, M.force))
|
||||
playsound(src, 'sound/items/Welder.ogg', 50, 1)
|
||||
if(TOX)
|
||||
M.mech_toxin_damage(src)
|
||||
else
|
||||
return
|
||||
updatehealth()
|
||||
visible_message("<span class='danger'>[M.name] has hit [src]!</span>", \
|
||||
"<span class='userdanger'>[M.name] has hit [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
|
||||
else
|
||||
step_away(src,M)
|
||||
add_logs(M.occupant, src, "pushed", M)
|
||||
visible_message("<span class='warning'>[M] pushes [src] out of the way.</span>", null, null, 5)
|
||||
|
||||
/mob/living/fire_act()
|
||||
adjust_fire_stacks(3)
|
||||
IgniteMob()
|
||||
|
||||
/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0)
|
||||
if(user == src || anchored || !isturf(user.loc))
|
||||
return 0
|
||||
if(!user.pulling || user.pulling != src)
|
||||
user.start_pulling(src, supress_message)
|
||||
return
|
||||
|
||||
if(!(status_flags & CANPUSH))
|
||||
to_chat(user, "<span class='warning'>[src] can't be grabbed more aggressively!</span>")
|
||||
return 0
|
||||
grippedby(user)
|
||||
|
||||
//proc to upgrade a simple pull into a more aggressive grab.
|
||||
/mob/living/proc/grippedby(mob/living/carbon/user)
|
||||
if(user.grab_state < GRAB_KILL)
|
||||
user.changeNext_move(CLICK_CD_GRABBING)
|
||||
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
|
||||
if(user.grab_state) //only the first upgrade is instantaneous
|
||||
var/old_grab_state = user.grab_state
|
||||
var/grab_upgrade_time = 30
|
||||
visible_message("<span class='danger'>[user] starts to tighten [user.p_their()] grip on [src]!</span>", \
|
||||
"<span class='userdanger'>[user] starts to tighten [user.p_their()] grip on you!</span>")
|
||||
if(!do_mob(user, src, grab_upgrade_time))
|
||||
return 0
|
||||
if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB)
|
||||
return 0
|
||||
user.grab_state++
|
||||
switch(user.grab_state)
|
||||
if(GRAB_AGGRESSIVE)
|
||||
add_logs(user, src, "grabbed", addition="aggressively")
|
||||
visible_message("<span class='danger'>[user] has grabbed [src] aggressively!</span>", \
|
||||
"<span class='userdanger'>[user] has grabbed [src] aggressively!</span>")
|
||||
drop_all_held_items()
|
||||
stop_pulling()
|
||||
if(GRAB_NECK)
|
||||
visible_message("<span class='danger'>[user] has grabbed [src] by the neck!</span>",\
|
||||
"<span class='userdanger'>[user] has grabbed you by the neck!</span>")
|
||||
update_canmove() //we fall down
|
||||
if(!buckled && !density)
|
||||
Move(user.loc)
|
||||
if(GRAB_KILL)
|
||||
visible_message("<span class='danger'>[user] is strangling [src]!</span>", \
|
||||
"<span class='userdanger'>[user] is strangling you!</span>")
|
||||
update_canmove() //we fall down
|
||||
if(!buckled && !density)
|
||||
Move(user.loc)
|
||||
return 1
|
||||
|
||||
|
||||
/mob/living/attack_slime(mob/living/simple_animal/slime/M)
|
||||
|
||||
/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = null, soften_text = null, armour_penetration, penetrated_text)
|
||||
var/armor = getarmor(def_zone, attack_flag)
|
||||
|
||||
//the if "armor" check is because this is used for everything on /living, including humans
|
||||
if(armor && armour_penetration)
|
||||
armor = max(0, armor - armour_penetration)
|
||||
if(penetrated_text)
|
||||
to_chat(src, "<span class='userdanger'>[penetrated_text]</span>")
|
||||
else
|
||||
to_chat(src, "<span class='userdanger'>Your armor was penetrated!</span>")
|
||||
else if(armor >= 100)
|
||||
if(absorb_text)
|
||||
to_chat(src, "<span class='userdanger'>[absorb_text]</span>")
|
||||
else
|
||||
to_chat(src, "<span class='userdanger'>Your armor absorbs the blow!</span>")
|
||||
else if(armor > 0)
|
||||
if(soften_text)
|
||||
to_chat(src, "<span class='userdanger'>[soften_text]</span>")
|
||||
else
|
||||
to_chat(src, "<span class='userdanger'>Your armor softens the blow!</span>")
|
||||
return armor
|
||||
|
||||
|
||||
/mob/living/proc/getarmor(def_zone, type)
|
||||
return 0
|
||||
|
||||
//this returns the mob's protection against eye damage (number between -1 and 2)
|
||||
/mob/living/proc/get_eye_protection()
|
||||
return 0
|
||||
|
||||
//this returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears)
|
||||
/mob/living/proc/get_ear_protection()
|
||||
return 0
|
||||
|
||||
/mob/living/proc/on_hit(obj/item/projectile/P)
|
||||
return
|
||||
|
||||
/mob/living/bullet_act(obj/item/projectile/P, def_zone)
|
||||
var/armor = run_armor_check(def_zone, P.flag, "","",P.armour_penetration)
|
||||
if(!P.nodamage)
|
||||
apply_damage(P.damage, P.damage_type, def_zone, armor)
|
||||
if(P.dismemberment)
|
||||
check_projectile_dismemberment(P, def_zone)
|
||||
return P.on_hit(src, armor)
|
||||
|
||||
/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
|
||||
return 0
|
||||
|
||||
/obj/item/proc/get_volume_by_throwforce_and_or_w_class()
|
||||
if(throwforce && w_class)
|
||||
return Clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100
|
||||
else if(w_class)
|
||||
return Clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100
|
||||
else
|
||||
return 0
|
||||
|
||||
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0)
|
||||
if(istype(AM, /obj/item))
|
||||
var/obj/item/I = AM
|
||||
var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest
|
||||
var/dtype = BRUTE
|
||||
var/volume = I.get_volume_by_throwforce_and_or_w_class()
|
||||
if(istype(I,/obj/item/weapon)) //If the item is a weapon...
|
||||
var/obj/item/weapon/W = I
|
||||
dtype = W.damtype
|
||||
|
||||
if (W.throwforce > 0) //If the weapon's throwforce is greater than zero...
|
||||
if (W.throwhitsound) //...and throwhitsound is defined...
|
||||
playsound(loc, W.throwhitsound, volume, 1, -1) //...play the weapon's throwhitsound.
|
||||
else if(W.hitsound) //Otherwise, if the weapon's hitsound is defined...
|
||||
playsound(loc, W.hitsound, volume, 1, -1) //...play the weapon's hitsound.
|
||||
else if(!W.throwhitsound) //Otherwise, if throwhitsound isn't defined...
|
||||
playsound(loc, 'sound/weapons/genhit.ogg',volume, 1, -1) //...play genhit.ogg.
|
||||
|
||||
else if(!I.throwhitsound && I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero...
|
||||
playsound(loc, 'sound/weapons/genhit.ogg', volume, 1, -1)//...play genhit.ogg
|
||||
if(!I.throwforce)// Otherwise, if the item's throwforce is 0...
|
||||
playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg.
|
||||
if(!blocked)
|
||||
visible_message("<span class='danger'>[src] has been hit by [I].</span>", \
|
||||
"<span class='userdanger'>[src] has been hit by [I].</span>")
|
||||
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration)
|
||||
apply_damage(I.throwforce, dtype, zone, armor)
|
||||
if(I.thrownby)
|
||||
add_logs(I.thrownby, src, "hit", I)
|
||||
else
|
||||
return 1
|
||||
else
|
||||
playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/mech_melee_attack(obj/mecha/M)
|
||||
if(M.occupant.a_intent == INTENT_HARM)
|
||||
M.do_attack_animation(src)
|
||||
if(M.damtype == "brute")
|
||||
step_away(src,M,15)
|
||||
switch(M.damtype)
|
||||
if(BRUTE)
|
||||
Paralyse(1)
|
||||
take_overall_damage(rand(M.force/2, M.force))
|
||||
playsound(src, 'sound/weapons/punch4.ogg', 50, 1)
|
||||
if(BURN)
|
||||
take_overall_damage(0, rand(M.force/2, M.force))
|
||||
playsound(src, 'sound/items/Welder.ogg', 50, 1)
|
||||
if(TOX)
|
||||
M.mech_toxin_damage(src)
|
||||
else
|
||||
return
|
||||
updatehealth()
|
||||
visible_message("<span class='danger'>[M.name] has hit [src]!</span>", \
|
||||
"<span class='userdanger'>[M.name] has hit [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
|
||||
else
|
||||
step_away(src,M)
|
||||
add_logs(M.occupant, src, "pushed", M)
|
||||
visible_message("<span class='warning'>[M] pushes [src] out of the way.</span>", null, null, 5)
|
||||
|
||||
/mob/living/fire_act()
|
||||
adjust_fire_stacks(3)
|
||||
IgniteMob()
|
||||
|
||||
/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0)
|
||||
if(user == src || anchored || !isturf(user.loc))
|
||||
return 0
|
||||
if(!user.pulling || user.pulling != src)
|
||||
user.start_pulling(src, supress_message)
|
||||
return
|
||||
|
||||
if(!(status_flags & CANPUSH))
|
||||
to_chat(user, "<span class='warning'>[src] can't be grabbed more aggressively!</span>")
|
||||
return 0
|
||||
grippedby(user)
|
||||
|
||||
//proc to upgrade a simple pull into a more aggressive grab.
|
||||
/mob/living/proc/grippedby(mob/living/carbon/user)
|
||||
if(user.grab_state < GRAB_KILL)
|
||||
user.changeNext_move(CLICK_CD_GRABBING)
|
||||
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
|
||||
if(user.grab_state) //only the first upgrade is instantaneous
|
||||
var/old_grab_state = user.grab_state
|
||||
var/grab_upgrade_time = 30
|
||||
visible_message("<span class='danger'>[user] starts to tighten [user.p_their()] grip on [src]!</span>", \
|
||||
"<span class='userdanger'>[user] starts to tighten [user.p_their()] grip on you!</span>")
|
||||
if(!do_mob(user, src, grab_upgrade_time))
|
||||
return 0
|
||||
if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB)
|
||||
return 0
|
||||
user.grab_state++
|
||||
switch(user.grab_state)
|
||||
if(GRAB_AGGRESSIVE)
|
||||
add_logs(user, src, "grabbed", addition="aggressively")
|
||||
visible_message("<span class='danger'>[user] has grabbed [src] aggressively!</span>", \
|
||||
"<span class='userdanger'>[user] has grabbed [src] aggressively!</span>")
|
||||
drop_all_held_items()
|
||||
stop_pulling()
|
||||
if(GRAB_NECK)
|
||||
visible_message("<span class='danger'>[user] has grabbed [src] by the neck!</span>",\
|
||||
"<span class='userdanger'>[user] has grabbed you by the neck!</span>")
|
||||
update_canmove() //we fall down
|
||||
if(!buckled && !density)
|
||||
Move(user.loc)
|
||||
if(GRAB_KILL)
|
||||
visible_message("<span class='danger'>[user] is strangling [src]!</span>", \
|
||||
"<span class='userdanger'>[user] is strangling you!</span>")
|
||||
update_canmove() //we fall down
|
||||
if(!buckled && !density)
|
||||
Move(user.loc)
|
||||
return 1
|
||||
|
||||
|
||||
/mob/living/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(!SSticker.HasRoundStarted())
|
||||
to_chat(M, "You cannot attack people before the game has started.")
|
||||
return
|
||||
|
||||
if(M.buckled)
|
||||
if(M in buckled_mobs)
|
||||
M.Feedstop()
|
||||
return // can't attack while eating!
|
||||
|
||||
if (stat != DEAD)
|
||||
add_logs(M, src, "attacked")
|
||||
M.do_attack_animation(src)
|
||||
visible_message("<span class='danger'>The [M.name] glomps [src]!</span>", \
|
||||
"<span class='userdanger'>The [M.name] glomps [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 1
|
||||
|
||||
/mob/living/attack_animal(mob/living/simple_animal/M)
|
||||
M.face_atom(src)
|
||||
if(M.melee_damage_upper == 0)
|
||||
M.visible_message("<span class='notice'>\The [M] [M.friendly] [src]!</span>")
|
||||
return 0
|
||||
else
|
||||
if(M.attack_sound)
|
||||
playsound(loc, M.attack_sound, 50, 1, 1)
|
||||
M.do_attack_animation(src)
|
||||
visible_message("<span class='danger'>\The [M] [M.attacktext] [src]!</span>", \
|
||||
"<span class='userdanger'>\The [M] [M.attacktext] [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
add_logs(M, src, "attacked")
|
||||
return 1
|
||||
|
||||
|
||||
/mob/living/attack_paw(mob/living/carbon/monkey/M)
|
||||
if(isturf(loc) && istype(loc.loc, /area/start))
|
||||
to_chat(M, "No attacking people at spawn, you jackass.")
|
||||
return 0
|
||||
|
||||
if (M.a_intent == INTENT_HARM)
|
||||
if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH))
|
||||
to_chat(M, "<span class='warning'>You can't bite with your mouth covered!</span>")
|
||||
return 0
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_BITE)
|
||||
if (prob(75))
|
||||
add_logs(M, src, "attacked")
|
||||
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M.name] bites [src]!</span>", \
|
||||
"<span class='userdanger'>[M.name] bites [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 1
|
||||
else
|
||||
visible_message("<span class='danger'>[M.name] has attempted to bite [src]!</span>", \
|
||||
"<span class='userdanger'>[M.name] has attempted to bite [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 0
|
||||
|
||||
/mob/living/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
switch(L.a_intent)
|
||||
if("help")
|
||||
visible_message("<span class='notice'>[L.name] rubs its head against [src].</span>")
|
||||
return 0
|
||||
|
||||
else
|
||||
L.do_attack_animation(src)
|
||||
if(prob(90))
|
||||
add_logs(L, src, "attacked")
|
||||
visible_message("<span class='danger'>[L.name] bites [src]!</span>", \
|
||||
"<span class='userdanger'>[L.name] bites [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
|
||||
return 1
|
||||
else
|
||||
visible_message("<span class='danger'>[L.name] has attempted to bite [src]!</span>", \
|
||||
"<span class='userdanger'>[L.name] has attempted to bite [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 0
|
||||
|
||||
/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
switch(M.a_intent)
|
||||
if ("help")
|
||||
visible_message("<span class='notice'>[M] caresses [src] with its scythe like arm.</span>")
|
||||
return 0
|
||||
|
||||
if ("grab")
|
||||
grabbedby(M)
|
||||
return 0
|
||||
if("harm")
|
||||
M.do_attack_animation(src)
|
||||
return 1
|
||||
if("disarm")
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
return 1
|
||||
|
||||
/mob/living/ex_act(severity, target, origin)
|
||||
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
|
||||
return
|
||||
..()
|
||||
|
||||
//Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs.
|
||||
|
||||
/mob/living/acid_act(acidpwr, acid_volume)
|
||||
take_bodypart_damage(acidpwr * min(1, acid_volume * 0.1))
|
||||
return 1
|
||||
|
||||
/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
|
||||
to_chat(M, "You cannot attack people before the game has started.")
|
||||
return
|
||||
|
||||
if(M.buckled)
|
||||
if(M in buckled_mobs)
|
||||
M.Feedstop()
|
||||
return // can't attack while eating!
|
||||
|
||||
if (stat != DEAD)
|
||||
add_logs(M, src, "attacked")
|
||||
M.do_attack_animation(src)
|
||||
visible_message("<span class='danger'>The [M.name] glomps [src]!</span>", \
|
||||
"<span class='userdanger'>The [M.name] glomps [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 1
|
||||
|
||||
/mob/living/attack_animal(mob/living/simple_animal/M)
|
||||
M.face_atom(src)
|
||||
if(M.melee_damage_upper == 0)
|
||||
M.visible_message("<span class='notice'>\The [M] [M.friendly] [src]!</span>")
|
||||
return 0
|
||||
else
|
||||
if(M.attack_sound)
|
||||
playsound(loc, M.attack_sound, 50, 1, 1)
|
||||
M.do_attack_animation(src)
|
||||
visible_message("<span class='danger'>\The [M] [M.attacktext] [src]!</span>", \
|
||||
"<span class='userdanger'>\The [M] [M.attacktext] [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
add_logs(M, src, "attacked")
|
||||
return 1
|
||||
|
||||
|
||||
/mob/living/attack_paw(mob/living/carbon/monkey/M)
|
||||
if(isturf(loc) && istype(loc.loc, /area/start))
|
||||
to_chat(M, "No attacking people at spawn, you jackass.")
|
||||
return 0
|
||||
|
||||
if (M.a_intent == INTENT_HARM)
|
||||
if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH))
|
||||
to_chat(M, "<span class='warning'>You can't bite with your mouth covered!</span>")
|
||||
return 0
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_BITE)
|
||||
if (prob(75))
|
||||
add_logs(M, src, "attacked")
|
||||
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M.name] bites [src]!</span>", \
|
||||
"<span class='userdanger'>[M.name] bites [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 1
|
||||
else
|
||||
visible_message("<span class='danger'>[M.name] has attempted to bite [src]!</span>", \
|
||||
"<span class='userdanger'>[M.name] has attempted to bite [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 0
|
||||
|
||||
/mob/living/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
switch(L.a_intent)
|
||||
if("help")
|
||||
visible_message("<span class='notice'>[L.name] rubs its head against [src].</span>")
|
||||
return 0
|
||||
|
||||
else
|
||||
L.do_attack_animation(src)
|
||||
if(prob(90))
|
||||
add_logs(L, src, "attacked")
|
||||
visible_message("<span class='danger'>[L.name] bites [src]!</span>", \
|
||||
"<span class='userdanger'>[L.name] bites [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
|
||||
return 1
|
||||
else
|
||||
visible_message("<span class='danger'>[L.name] has attempted to bite [src]!</span>", \
|
||||
"<span class='userdanger'>[L.name] has attempted to bite [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return 0
|
||||
|
||||
/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
switch(M.a_intent)
|
||||
if ("help")
|
||||
visible_message("<span class='notice'>[M] caresses [src] with its scythe like arm.</span>")
|
||||
return 0
|
||||
|
||||
if ("grab")
|
||||
grabbedby(M)
|
||||
return 0
|
||||
if("harm")
|
||||
M.do_attack_animation(src)
|
||||
return 1
|
||||
if("disarm")
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
return 1
|
||||
|
||||
/mob/living/ex_act(severity, target, origin)
|
||||
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
|
||||
return
|
||||
..()
|
||||
|
||||
//Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs.
|
||||
|
||||
/mob/living/acid_act(acidpwr, acid_volume)
|
||||
take_bodypart_damage(acidpwr * min(1, acid_volume * 0.1))
|
||||
return 1
|
||||
|
||||
/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
|
||||
if(tesla_shock && HAS_SECONDARY_FLAG(src, TESLA_IGNORE))
|
||||
return FALSE
|
||||
if(shock_damage > 0)
|
||||
if(!illusion)
|
||||
adjustFireLoss(shock_damage)
|
||||
visible_message(
|
||||
"<span class='danger'>[src] was shocked by \the [source]!</span>", \
|
||||
"<span class='userdanger'>You feel a powerful shock coursing through your body!</span>", \
|
||||
"<span class='italics'>You hear a heavy electrical crack.</span>" \
|
||||
)
|
||||
return shock_damage
|
||||
|
||||
/mob/living/emp_act(severity)
|
||||
var/list/L = src.get_contents()
|
||||
for(var/obj/O in L)
|
||||
O.emp_act(severity)
|
||||
..()
|
||||
|
||||
/mob/living/singularity_act()
|
||||
var/gain = 20
|
||||
investigate_log("([key_name(src)]) has been consumed by the singularity.","singulo") //Oh that's where the clown ended up!
|
||||
gib()
|
||||
return(gain)
|
||||
|
||||
/mob/living/narsie_act()
|
||||
if(status_flags & GODMODE)
|
||||
return
|
||||
|
||||
if(is_servant_of_ratvar(src) && !stat)
|
||||
to_chat(src, "<span class='userdanger'>You resist Nar-Sie's influence... but not all of it. <i>Run!</i></span>")
|
||||
adjustBruteLoss(35)
|
||||
if(src && reagents)
|
||||
reagents.add_reagent("heparin", 5)
|
||||
return FALSE
|
||||
if(client)
|
||||
return FALSE
|
||||
if(shock_damage > 0)
|
||||
if(!illusion)
|
||||
adjustFireLoss(shock_damage)
|
||||
visible_message(
|
||||
"<span class='danger'>[src] was shocked by \the [source]!</span>", \
|
||||
"<span class='userdanger'>You feel a powerful shock coursing through your body!</span>", \
|
||||
"<span class='italics'>You hear a heavy electrical crack.</span>" \
|
||||
)
|
||||
return shock_damage
|
||||
|
||||
/mob/living/emp_act(severity)
|
||||
var/list/L = src.get_contents()
|
||||
for(var/obj/O in L)
|
||||
O.emp_act(severity)
|
||||
..()
|
||||
|
||||
/mob/living/singularity_act()
|
||||
var/gain = 20
|
||||
investigate_log("([key_name(src)]) has been consumed by the singularity.","singulo") //Oh that's where the clown ended up!
|
||||
gib()
|
||||
return(gain)
|
||||
|
||||
/mob/living/narsie_act()
|
||||
if(status_flags & GODMODE)
|
||||
return
|
||||
|
||||
if(is_servant_of_ratvar(src) && !stat)
|
||||
to_chat(src, "<span class='userdanger'>You resist Nar-Sie's influence... but not all of it. <i>Run!</i></span>")
|
||||
adjustBruteLoss(35)
|
||||
if(src && reagents)
|
||||
reagents.add_reagent("heparin", 5)
|
||||
return FALSE
|
||||
if(GLOB.cult_narsie && GLOB.cult_narsie.souls_needed[src])
|
||||
GLOB.cult_narsie.resize(1.1)
|
||||
GLOB.cult_narsie.souls_needed -= src
|
||||
GLOB.cult_narsie.souls += 1
|
||||
if((GLOB.cult_narsie.souls == GLOB.cult_narsie.soul_goal) && (GLOB.cult_narsie.resolved == FALSE))
|
||||
GLOB.cult_narsie.resolved = TRUE
|
||||
world << sound('sound/machines/Alarm.ogg')
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/cult_ending_helper, 1), 120)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/ending_helper), 270)
|
||||
if(client)
|
||||
makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, src, cultoverride = TRUE)
|
||||
else
|
||||
else
|
||||
switch(rand(1, 6))
|
||||
if(1)
|
||||
new /mob/living/simple_animal/hostile/construct/armored/hostile(get_turf(src))
|
||||
if(2)
|
||||
new /mob/living/simple_animal/hostile/construct/wraith/hostile(get_turf(src))
|
||||
if(3 to 6)
|
||||
new /mob/living/simple_animal/hostile/construct/builder/hostile(get_turf(src))
|
||||
spawn_dust()
|
||||
gib()
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/ratvar_act()
|
||||
if(status_flags & GODMODE)
|
||||
return
|
||||
|
||||
if(stat != DEAD && !is_servant_of_ratvar(src))
|
||||
for(var/obj/item/weapon/implant/mindshield/M in implants)
|
||||
qdel(M)
|
||||
if(!add_servant_of_ratvar(src))
|
||||
to_chat(src, "<span class='userdanger'>A blinding light boils you alive! <i>Run!</i></span>")
|
||||
adjustFireLoss(35)
|
||||
if(src)
|
||||
adjust_fire_stacks(1)
|
||||
IgniteMob()
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
//called when the mob receives a bright flash
|
||||
/mob/living/proc/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash)
|
||||
if(get_eye_protection() < intensity && (override_blindness_check || !(disabilities & BLIND)))
|
||||
overlay_fullscreen("flash", type)
|
||||
addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", 25), 25)
|
||||
return 1
|
||||
|
||||
//called when the mob receives a loud bang
|
||||
/mob/living/proc/soundbang_act()
|
||||
return 0
|
||||
|
||||
//to damage the clothes worn by a mob
|
||||
/mob/living/proc/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone)
|
||||
return
|
||||
|
||||
|
||||
/mob/living/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y)
|
||||
if(A != src)
|
||||
end_pixel_y = get_standard_pixel_y_offset(lying)
|
||||
used_item = get_active_held_item()
|
||||
..()
|
||||
floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
|
||||
if(1)
|
||||
new /mob/living/simple_animal/hostile/construct/armored/hostile(get_turf(src))
|
||||
if(2)
|
||||
new /mob/living/simple_animal/hostile/construct/wraith/hostile(get_turf(src))
|
||||
if(3 to 6)
|
||||
new /mob/living/simple_animal/hostile/construct/builder/hostile(get_turf(src))
|
||||
spawn_dust()
|
||||
gib()
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/ratvar_act()
|
||||
if(status_flags & GODMODE)
|
||||
return
|
||||
|
||||
if(stat != DEAD && !is_servant_of_ratvar(src))
|
||||
for(var/obj/item/weapon/implant/mindshield/M in implants)
|
||||
qdel(M)
|
||||
if(!add_servant_of_ratvar(src))
|
||||
to_chat(src, "<span class='userdanger'>A blinding light boils you alive! <i>Run!</i></span>")
|
||||
adjustFireLoss(35)
|
||||
if(src)
|
||||
adjust_fire_stacks(1)
|
||||
IgniteMob()
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
//called when the mob receives a bright flash
|
||||
/mob/living/proc/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash)
|
||||
if(get_eye_protection() < intensity && (override_blindness_check || !(disabilities & BLIND)))
|
||||
overlay_fullscreen("flash", type)
|
||||
addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", 25), 25)
|
||||
return 1
|
||||
|
||||
//called when the mob receives a loud bang
|
||||
/mob/living/proc/soundbang_act()
|
||||
return 0
|
||||
|
||||
//to damage the clothes worn by a mob
|
||||
/mob/living/proc/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone)
|
||||
return
|
||||
|
||||
|
||||
/mob/living/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y)
|
||||
if(A != src)
|
||||
end_pixel_y = get_standard_pixel_y_offset(lying)
|
||||
used_item = get_active_held_item()
|
||||
..()
|
||||
floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
|
||||
@@ -407,7 +407,8 @@
|
||||
/obj/item/weapon/reagent_containers/borghypo/peace,
|
||||
/obj/item/weapon/holosign_creator/cyborg,
|
||||
/obj/item/borg/cyborghug/peacekeeper,
|
||||
/obj/item/weapon/extinguisher)
|
||||
/obj/item/weapon/extinguisher,
|
||||
/obj/item/borg/projectile_dampen)
|
||||
emag_modules = list(/obj/item/weapon/reagent_containers/borghypo/peace/hacked)
|
||||
ratvar_modules = list(
|
||||
/obj/item/clockwork/slab/cyborg/peacekeeper,
|
||||
|
||||
@@ -171,7 +171,30 @@
|
||||
attacktext = "slashes"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
construct_spells = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift)
|
||||
playstyle_string = "<b>You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.</b>"
|
||||
playstyle_string = "<b>You are a Wraith. Though relatively fragile, you are fast, deadly, can phase through walls, and your attacks will lower the cooldown on phasing.</b>"
|
||||
var/attack_refund = 10 //1 second per attack
|
||||
var/crit_refund = 50 //5 seconds when putting a target into critical
|
||||
var/kill_refund = 250 //full refund on kills
|
||||
|
||||
/mob/living/simple_animal/hostile/construct/wraith/AttackingTarget() //refund jaunt cooldown when attacking living targets
|
||||
var/prev_stat
|
||||
if(isliving(target) && !iscultist(target))
|
||||
var/mob/living/L = target
|
||||
prev_stat = L.stat
|
||||
|
||||
. = ..()
|
||||
|
||||
if(. && isnum(prev_stat))
|
||||
var/mob/living/L = target
|
||||
var/refund = 0
|
||||
if(QDELETED(L) || (L.stat == DEAD && prev_stat != DEAD)) //they're dead, you killed them
|
||||
refund += kill_refund
|
||||
else if(L.InCritical() && prev_stat == CONSCIOUS) //you knocked them into critical
|
||||
refund += crit_refund
|
||||
if(L.stat != DEAD && prev_stat != DEAD)
|
||||
refund += attack_refund
|
||||
for(var/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/S in mob_spell_list)
|
||||
S.charge_counter = min(S.charge_counter + refund, S.charge_max)
|
||||
|
||||
/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things
|
||||
AIStatus = AI_ON
|
||||
@@ -265,8 +288,8 @@
|
||||
name = "Harvester"
|
||||
real_name = "Harvester"
|
||||
desc = "A long, thin construct built to herald Nar-Sie's rise. It'll be all over soon."
|
||||
icon_state = "harvester"
|
||||
icon_living = "harvester"
|
||||
icon_state = "chosen"
|
||||
icon_living = "chosen"
|
||||
maxHealth = 60
|
||||
health = 60
|
||||
sight = SEE_MOBS
|
||||
@@ -277,9 +300,10 @@
|
||||
construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/area_conversion,
|
||||
/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall)
|
||||
playstyle_string = "<B>You are a Harvester. You are incapable of directly killing humans, but your attacks will remove their limbs: \
|
||||
Bring those who still cling to this world of illusion back to the Geometer so they may know Truth.</B>"
|
||||
Bring those who still cling to this world of illusion back to the Geometer so they may know Truth. Your form and any you are pulling can pass through runed walls effortlessly.</B>"
|
||||
can_repair_constructs = TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/construct/harvester/Bump(atom/AM)
|
||||
. = ..()
|
||||
if(istype(AM, /turf/closed/wall/mineral/cult) && AM != loc) //we can go through cult walls
|
||||
@@ -306,6 +330,8 @@
|
||||
if(!LAZYLEN(parts))
|
||||
if(undismembermerable_limbs) //they have limbs we can't remove, and no parts we can, attack!
|
||||
return ..()
|
||||
C.Weaken(30)
|
||||
visible_message("<span class='danger'>[src] paralyzes [C]!</span>")
|
||||
to_chat(src, "<span class='cultlarge'>\"Bring [C.p_them()] to me.\"</span>")
|
||||
return FALSE
|
||||
do_attack_animation(C)
|
||||
@@ -314,6 +340,11 @@
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/construct/harvester/Initialize()
|
||||
. = ..()
|
||||
var/datum/action/innate/seek_prey/seek = new()
|
||||
seek.Grant(src)
|
||||
seek.Activate()
|
||||
|
||||
///////////////////////Master-Tracker///////////////////////
|
||||
|
||||
@@ -326,11 +357,14 @@
|
||||
var/tracking = FALSE
|
||||
var/mob/living/simple_animal/hostile/construct/the_construct
|
||||
|
||||
|
||||
/datum/action/innate/seek_master/Grant(var/mob/living/C)
|
||||
the_construct = C
|
||||
..()
|
||||
|
||||
/datum/action/innate/seek_master/Activate()
|
||||
if(!SSticker.mode.eldergod)
|
||||
the_construct.master = GLOB.blood_target
|
||||
if(!the_construct.master)
|
||||
to_chat(the_construct, "<span class='cultitalic'>You have no master to seek!</span>")
|
||||
the_construct.seeking = FALSE
|
||||
@@ -346,6 +380,43 @@
|
||||
to_chat(the_construct, "<span class='cultitalic'>You are now tracking your master.</span>")
|
||||
|
||||
|
||||
/datum/action/innate/seek_prey
|
||||
name = "Seek the Harvest"
|
||||
desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
|
||||
background_icon_state = "bg_demon"
|
||||
buttontooltipstyle = "cult"
|
||||
button_icon_state = "cult_mark"
|
||||
var/tracking = FALSE
|
||||
var/mob/living/simple_animal/hostile/construct/harvester/the_construct
|
||||
|
||||
/datum/action/innate/seek_prey/Grant(var/mob/living/C)
|
||||
the_construct = C
|
||||
..()
|
||||
|
||||
/datum/action/innate/seek_prey/Activate()
|
||||
if(GLOB.cult_narsie == null)
|
||||
return
|
||||
if(tracking)
|
||||
desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
|
||||
button_icon_state = "cult_mark"
|
||||
tracking = FALSE
|
||||
the_construct.seeking = FALSE
|
||||
the_construct.master = GLOB.cult_narsie
|
||||
to_chat(the_construct, "<span class='cultitalic'>You are now tracking Nar'Sie, return to reap the harvest!</span>")
|
||||
return
|
||||
else
|
||||
if(LAZYLEN(GLOB.cult_narsie.souls_needed))
|
||||
the_construct.master = pick(GLOB.cult_narsie.souls_needed)
|
||||
to_chat(the_construct, "<span class='cultitalic'>You are now tracking your prey, [the_construct.master] - harvest them!</span>")
|
||||
else
|
||||
to_chat(the_construct, "<span class='cultitalic'>Nar'Sie has completed her harvest!</span>")
|
||||
return
|
||||
desc = "Activate to track Nar'Sie!"
|
||||
button_icon_state = "sintouch"
|
||||
tracking = TRUE
|
||||
the_construct.seeking = TRUE
|
||||
|
||||
|
||||
/////////////////////////////ui stuff/////////////////////////////
|
||||
|
||||
/mob/living/simple_animal/hostile/construct/update_health_hud()
|
||||
|
||||
@@ -467,6 +467,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
var/used_message = "<span class='holoparasite'>All the cards seem to be blank now.</span>"
|
||||
var/failure_message = "<span class='holoparasitebold'>..And draw a card! It's...blank? Maybe you should try again later.</span>"
|
||||
var/ling_failure = "<span class='holoparasitebold'>The deck refuses to respond to a souless creature such as you.</span>"
|
||||
var/activation_message = "<span class='holoparasite'>The rest of the deck rapidly flashes to ash!</span>"
|
||||
var/list/possible_guardians = list("Assassin", "Chaos", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
var/random = TRUE
|
||||
var/allowmultiple = FALSE
|
||||
@@ -495,6 +496,8 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
if(candidates.len)
|
||||
theghost = pick(candidates)
|
||||
spawn_guardian(user, theghost.key)
|
||||
to_chat(user, "[activation_message]")
|
||||
qdel(src)
|
||||
else
|
||||
to_chat(user, "[failure_message]")
|
||||
used = FALSE
|
||||
@@ -587,6 +590,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
used_message = "<span class='holoparasite'>The injector has already been used.</span>"
|
||||
failure_message = "<span class='holoparasitebold'>...ERROR. BOOT SEQUENCE ABORTED. AI FAILED TO INTIALIZE. PLEASE CONTACT SUPPORT OR TRY AGAIN LATER.</span>"
|
||||
ling_failure = "<span class='holoparasitebold'>The holoparasites recoil in horror. They want nothing to do with a creature like you.</span>"
|
||||
activation_message = "<span class='holoparasite'>The injector self destructs after you inject yourself with it.</span>"
|
||||
|
||||
/obj/item/weapon/guardiancreator/tech/choose/traitor
|
||||
possible_guardians = list("Assassin", "Chaos", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support")
|
||||
@@ -599,7 +603,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
|
||||
/obj/item/weapon/paper/guardian
|
||||
name = "Holoparasite Guide"
|
||||
icon_state = "paper_words"
|
||||
icon_state = "alienpaper_words"
|
||||
info = {"<b>A list of Holoparasite Types</b><br>
|
||||
|
||||
<br>
|
||||
@@ -670,6 +674,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
|
||||
used_message = "<span class='holoparasite'>Someone's already taken a bite out of these fishsticks! Ew.</span>"
|
||||
failure_message = "<span class='holoparasitebold'>You couldn't catch any carp spirits from the seas of Lake Carp. Maybe there are none, maybe you fucked up.</span>"
|
||||
ling_failure = "<span class='holoparasitebold'>Carp'sie is fine with changelings, so you shouldn't be seeing this message.</span>"
|
||||
activation_message = "<span class='holoparasite'>You finish eating the fishsticks! Delicious!>"
|
||||
allowmultiple = TRUE
|
||||
allowling = TRUE
|
||||
random = TRUE
|
||||
|
||||
@@ -478,6 +478,8 @@ Difficulty: Hard
|
||||
var/speed = 3 //how many deciseconds between each step
|
||||
var/currently_seeking = FALSE
|
||||
var/friendly_fire_check = FALSE //if blasts produced apply friendly fire
|
||||
var/monster_damage_boost = TRUE
|
||||
var/damage = 10
|
||||
|
||||
/obj/effect/temp_visual/hierophant/chaser/Initialize(mapload, new_caster, new_target, new_speed, is_friendly_fire)
|
||||
. = ..()
|
||||
@@ -524,6 +526,8 @@ Difficulty: Hard
|
||||
|
||||
/obj/effect/temp_visual/hierophant/chaser/proc/make_blast()
|
||||
new /obj/effect/temp_visual/hierophant/blast(loc, caster, friendly_fire_check)
|
||||
B.damage = damage
|
||||
B.monster_damage_boost = monster_damage_boost
|
||||
|
||||
/obj/effect/temp_visual/hierophant/telegraph
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
@@ -553,6 +557,7 @@ Difficulty: Hard
|
||||
desc = "Get out of the way!"
|
||||
duration = 9
|
||||
var/damage = 10 //how much damage do we do?
|
||||
var/monster_damage_boost = TRUE //do we deal extra damage to monsters? Used by the boss
|
||||
var/list/hit_things = list() //we hit these already, ignore them
|
||||
var/friendly_fire_check = FALSE
|
||||
var/bursting = FALSE //if we're bursting and need to hit anyone crossing us
|
||||
@@ -595,7 +600,7 @@ Difficulty: Hard
|
||||
var/limb_to_hit = L.get_bodypart(pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg"))
|
||||
var/armor = L.run_armor_check(limb_to_hit, "melee", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 50, "Your armor was penetrated by [src]!")
|
||||
L.apply_damage(damage, BURN, limb_to_hit, armor)
|
||||
if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid))
|
||||
if(monster_damage_boost && (ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)))
|
||||
L.adjustBruteLoss(damage)
|
||||
add_logs(caster, L, "struck with a [name]")
|
||||
for(var/obj/mecha/M in T.contents - hit_things) //and mechs.
|
||||
|
||||
@@ -170,8 +170,7 @@
|
||||
|
||||
/obj/item/weapon/paper/proc/updateinfolinks()
|
||||
info_links = info
|
||||
var/i = 0
|
||||
for(i=1,i<=fields,i++)
|
||||
for(var/i in 1 to min(fields, 15))
|
||||
addtofield(i, "<font face=\"[PEN_FONT]\"><A href='?src=\ref[src];write=[i]'>write</A></font>", 1)
|
||||
info_links = info_links + "<font face=\"[PEN_FONT]\"><A href='?src=\ref[src];write=end'>write</A></font>"
|
||||
|
||||
|
||||
@@ -304,16 +304,17 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
|
||||
// Sound the alert if gravity was just enabled or disabled.
|
||||
var/alert = 0
|
||||
var/area/A = get_area(src)
|
||||
if(on && SSticker.IsRoundInProgress()) // If we turned on and the game is live.
|
||||
if(gravity_in_level() == 0)
|
||||
alert = 1
|
||||
investigate_log("was brought online and is now producing gravity for this level.", "gravity")
|
||||
message_admins("The gravity generator was brought online [A][ADMIN_COORDJMP(src)]")
|
||||
else
|
||||
if(gravity_in_level() == 1)
|
||||
alert = 1
|
||||
investigate_log("was brought offline and there is now no gravity for this level.", "gravity")
|
||||
message_admins("The gravity generator was brought offline with no backup generator. [A][ADMIN_COORDJMP(src)]")
|
||||
if(SSticker.IsRoundInProgress())
|
||||
if(on) // If we turned on and the game is live.
|
||||
if(gravity_in_level() == 0)
|
||||
alert = 1
|
||||
investigate_log("was brought online and is now producing gravity for this level.", "gravity")
|
||||
message_admins("The gravity generator was brought online [A][ADMIN_COORDJMP(src)]")
|
||||
else
|
||||
if(gravity_in_level() == 1)
|
||||
alert = 1
|
||||
investigate_log("was brought offline and there is now no gravity for this level.", "gravity")
|
||||
message_admins("The gravity generator was brought offline with no backup generator. [A][ADMIN_COORDJMP(src)]")
|
||||
|
||||
update_icon()
|
||||
update_list()
|
||||
|
||||
@@ -34,13 +34,61 @@
|
||||
|
||||
var/area/A = get_area(src)
|
||||
if(A)
|
||||
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/effects.dmi', "ghostalertsie")
|
||||
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/cult_effects.dmi', "ghostalertsie")
|
||||
notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK)
|
||||
INVOKE_ASYNC(src, .proc/narsie_spawn_animation)
|
||||
|
||||
narsie_spawn_animation()
|
||||
/obj/singularity/narsie/large/cult // For the new cult ending, guaranteed to end the round within 3 minutes
|
||||
var/list/souls_needed = list()
|
||||
var/soul_goal = 0
|
||||
var/souls = 0
|
||||
var/resolved = FALSE
|
||||
|
||||
sleep(19)
|
||||
SSshuttle.emergency.request(null, set_coefficient = 0) //instantly arrives
|
||||
/obj/singularity/narsie/large/cult/proc/resize(var/ratio)
|
||||
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
|
||||
ntransform.Scale(ratio)
|
||||
animate(src, transform = ntransform, time = 40, easing = EASE_IN|EASE_OUT)
|
||||
|
||||
/obj/singularity/narsie/large/cult/Initialize()
|
||||
. = ..()
|
||||
GLOB.cult_narsie = src
|
||||
GLOB.blood_target = src
|
||||
resize(0.6)
|
||||
for(var/datum/mind/cult_mind in SSticker.mode.cult)
|
||||
if(isliving(cult_mind.current))
|
||||
var/mob/living/L = cult_mind.current
|
||||
L.narsie_act()
|
||||
for(var/mob/living/player in GLOB.player_list)
|
||||
if(player.stat != DEAD && player.loc.z == ZLEVEL_STATION && !iscultist(player) && isliving(player))
|
||||
souls_needed[player] = TRUE
|
||||
soul_goal = round(1 + LAZYLEN(souls_needed) * 0.6)
|
||||
INVOKE_ASYNC(src, .proc/begin_the_end)
|
||||
|
||||
/obj/singularity/narsie/large/cult/proc/begin_the_end()
|
||||
sleep(50)
|
||||
priority_announce("An acausal dimensional event has been detected in your sector. Event has been flagged EXTINCTION-CLASS. Directing all available assets toward simulating solutions. SOLUTION ETA: 60 SECONDS.","Central Command Higher Dimensional Affairs", 'sound/misc/airraid.ogg')
|
||||
sleep(550)
|
||||
priority_announce("Simulations on acausal dimensional event complete. Deploying solution package now. Deployment ETA: TWO MINUTES. ","Central Command Higher Dimensional Affairs")
|
||||
sleep(50)
|
||||
set_security_level("delta")
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
SSshuttle.lockdown = TRUE
|
||||
sleep(1150)
|
||||
if(resolved == FALSE)
|
||||
resolved = TRUE
|
||||
world << sound('sound/machines/Alarm.ogg')
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/cult_ending_helper), 120)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/ending_helper), 220)
|
||||
|
||||
/obj/singularity/narsie/large/cult/Destroy()
|
||||
GLOB.cult_narsie = null
|
||||
return ..()
|
||||
|
||||
/proc/ending_helper()
|
||||
SSticker.force_ending = 1
|
||||
|
||||
/proc/cult_ending_helper(var/no_explosion = 0)
|
||||
SSticker.station_explosion_cinematic(no_explosion, "cult", null)
|
||||
|
||||
|
||||
/obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user as mob)
|
||||
@@ -134,7 +182,7 @@
|
||||
return
|
||||
to_chat(target, "<span class='cultsmall'>NAR-SIE HAS LOST INTEREST IN YOU.</span>")
|
||||
target = food
|
||||
if(isliving(target))
|
||||
if(ishuman(target))
|
||||
to_chat(target, "<span class ='cult'>NAR-SIE HUNGERS FOR YOUR SOUL.</span>")
|
||||
else
|
||||
to_chat(target, "<span class ='cult'>NAR-SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL.</span>")
|
||||
|
||||
@@ -64,6 +64,25 @@
|
||||
qdel(S)
|
||||
..()
|
||||
|
||||
/obj/item/ammo_casing/dnainjector
|
||||
name = "rigged syringe gun spring"
|
||||
desc = "A high-power spring that throws DNA injectors."
|
||||
projectile_type = /obj/item/projectile/bullet/dnainjector
|
||||
firing_effect_type = null
|
||||
|
||||
/obj/item/ammo_casing/dnainjector/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
|
||||
if(!BB)
|
||||
return
|
||||
if(istype(loc, /obj/item/weapon/gun/syringe/dna))
|
||||
var/obj/item/weapon/gun/syringe/dna/SG = loc
|
||||
if(!SG.syringes.len)
|
||||
return
|
||||
|
||||
var/obj/item/weapon/dnainjector/S = popleft(SG.syringes)
|
||||
var/obj/item/projectile/bullet/dnainjector/D = BB
|
||||
S.forceMove(D)
|
||||
D.injector = S
|
||||
..()
|
||||
|
||||
/obj/item/ammo_casing/energy/c3dbullet
|
||||
projectile_type = /obj/item/projectile/bullet/midbullet3
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
item_state = null
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
force = 10
|
||||
modifystate = TRUE
|
||||
flags = CONDUCT
|
||||
slot_flags = SLOT_BACK
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser)
|
||||
@@ -39,7 +40,7 @@
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
slot_flags = SLOT_BELT
|
||||
icon_state = "pulse_carbine"
|
||||
item_state = "pulse"
|
||||
item_state = null
|
||||
cell_type = "/obj/item/weapon/stock_parts/cell/pulse/carbine"
|
||||
can_flashlight = 1
|
||||
flight_x_offset = 18
|
||||
|
||||
@@ -49,18 +49,18 @@
|
||||
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = 1)
|
||||
/obj/item/weapon/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
|
||||
if(istype(A, /obj/item/weapon/reagent_containers/syringe))
|
||||
if(syringes.len < max_syringes)
|
||||
if(!user.transferItemToLoc(A, src))
|
||||
return
|
||||
return FALSE
|
||||
to_chat(user, "<span class='notice'>You load [A] into \the [src].</span>")
|
||||
syringes.Add(A)
|
||||
syringes += A
|
||||
recharge_newshot()
|
||||
return 1
|
||||
return TRUE
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>[src] cannot hold more syringes!</span>")
|
||||
return 0
|
||||
to_chat(user, "<span class='warning'>[src] cannot hold more syringes!</span>")
|
||||
return FALSE
|
||||
|
||||
/obj/item/weapon/gun/syringe/rapidsyringe
|
||||
name = "rapid syringe gun"
|
||||
@@ -78,3 +78,29 @@
|
||||
force = 2 //Also very weak because it's smaller
|
||||
suppressed = 1 //Softer fire sound
|
||||
can_unsuppress = 0 //Permanently silenced
|
||||
|
||||
/obj/item/weapon/gun/syringe/dna
|
||||
name = "modified syringe gun"
|
||||
desc = "A syringe gun that has been modified to fit DNA injectors instead of normal syringes."
|
||||
origin_tech = "combat=2;syndicate=2;biotech=3"
|
||||
|
||||
/obj/item/weapon/gun/syringe/dna/Initialize()
|
||||
. = ..()
|
||||
chambered = new /obj/item/ammo_casing/dnainjector(src)
|
||||
|
||||
/obj/item/weapon/gun/syringe/dna/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
|
||||
if(istype(A, /obj/item/weapon/dnainjector))
|
||||
var/obj/item/weapon/dnainjector/D = A
|
||||
if(D.used)
|
||||
to_chat(user, "<span class='warning'>This injector is used up!</span>")
|
||||
return
|
||||
if(syringes.len < max_syringes)
|
||||
if(!user.transferItemToLoc(D, src))
|
||||
return FALSE
|
||||
to_chat(user, "<span class='notice'>You load \the [D] into \the [src].</span>")
|
||||
syringes += D
|
||||
recharge_newshot()
|
||||
return TRUE
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] cannot hold more syringes!</span>")
|
||||
return FALSE
|
||||
@@ -190,7 +190,7 @@
|
||||
name = "dart"
|
||||
icon_state = "cbbolt"
|
||||
damage = 6
|
||||
var/piercing = 0
|
||||
var/piercing = FALSE
|
||||
|
||||
/obj/item/projectile/bullet/dart/New()
|
||||
..()
|
||||
@@ -201,15 +201,15 @@
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/M = target
|
||||
if(blocked != 100) // not completely blocked
|
||||
if(M.can_inject(null, 0, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
|
||||
if(M.can_inject(null, FALSE, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
|
||||
..()
|
||||
reagents.reaction(M, INJECT)
|
||||
reagents.trans_to(M, reagents.total_volume)
|
||||
return 1
|
||||
return TRUE
|
||||
else
|
||||
blocked = 100
|
||||
target.visible_message("<span class='danger'>The [name] was deflected!</span>", \
|
||||
"<span class='userdanger'>You were protected against the [name]!</span>")
|
||||
target.visible_message("<span class='danger'>\The [src] was deflected!</span>", \
|
||||
"<span class='userdanger'>You were protected against \the [src]!</span>")
|
||||
|
||||
..(target, blocked)
|
||||
reagents.set_reacting(TRUE)
|
||||
@@ -240,7 +240,28 @@
|
||||
nodamage = 1
|
||||
. = ..() // Execute the rest of the code.
|
||||
|
||||
|
||||
/obj/item/projectile/bullet/dnainjector
|
||||
name = "\improper DNA injector"
|
||||
icon_state = "syringeproj"
|
||||
var/obj/item/weapon/dnainjector/injector
|
||||
|
||||
/obj/item/projectile/bullet/dnainjector/on_hit(atom/target, blocked = 0)
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/M = target
|
||||
if(blocked != 100)
|
||||
if(M.can_inject(null, FALSE, def_zone, FALSE))
|
||||
if(injector.inject(M, firer))
|
||||
QDEL_NULL(injector)
|
||||
return TRUE
|
||||
else
|
||||
blocked = 100
|
||||
target.visible_message("<span class='danger'>\The [src] was deflected!</span>", \
|
||||
"<span class='userdanger'>You were protected against \the [src]!</span>")
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/bullet/dnainjector/Destroy()
|
||||
QDEL_NULL(injector)
|
||||
return ..()
|
||||
|
||||
//// SNIPER BULLETS
|
||||
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
has_id = 1
|
||||
flavour_text = "<font size=3>You are a syndicate agent, employed in a top secret research facility developing biological weapons. Unfortunately, your hated enemy, Nanotrasen, has begun mining in this sector. <b>Continue your research as best you can, and try to keep a low profile. Do not abandon the base without good cause.</b> The base is rigged with explosives should the worst happen, do not let the base fall into enemy hands!</b>"
|
||||
id_access_list = list(GLOB.access_syndicate)
|
||||
faction = list("syndicate")
|
||||
|
||||
/obj/effect/mob_spawn/human/lavaland_syndicate/comms
|
||||
name = "Syndicate Comms Agent"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
if(charge_type == "recharge")
|
||||
var/refund_percent = current_amount/projectile_amount
|
||||
charge_counter = charge_max * refund_percent
|
||||
start_recharge()
|
||||
remove_ranged_ability(msg)
|
||||
else
|
||||
msg = "<span class='notice'>[active_msg]<B>Left-click to shoot it at a target!</B></span>"
|
||||
|
||||
@@ -1276,6 +1276,14 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
|
||||
restricted_roles = list("Chaplain")
|
||||
surplus = 5 //Very low chance to get it in a surplus crate even without being the chaplain
|
||||
|
||||
/datum/uplink_item/role_restricted/pie_cannon
|
||||
name = "Banana Cream Pie Cannon"
|
||||
desc = "A special pie cannon for a special clown, this gadget can hold up to 20 pies and automatically fabricates one every two seconds!"
|
||||
cost = 10
|
||||
item = /obj/item/weapon/pneumatic_cannon/pie/selfcharge
|
||||
restricted_roles = list("Clown")
|
||||
surplus = 0 //No fun unless you're the clown!
|
||||
|
||||
/datum/uplink_item/role_restricted/ancient_jumpsuit
|
||||
name = "Ancient Jumpsuit"
|
||||
desc = "A tattered old jumpsuit that will provide absolutely no benefit to you. It fills the wearer with a strange compulsion to blurt out 'glorf'."
|
||||
@@ -1291,6 +1299,13 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
|
||||
cost = 2
|
||||
restricted_roles = list("Curator")
|
||||
limited_stock = 1 // please don't spam deadchat
|
||||
|
||||
/datum/uplink_item/role_restricted/modified_syringe_gun
|
||||
name = "Modified Syringe Gun"
|
||||
desc = "A syringe gun that fires DNA injectors instead of normal syringes."
|
||||
item = /obj/item/weapon/gun/syringe/dna
|
||||
cost = 14
|
||||
restricted_roles = list("Geneticist", "Chief Medical Officer")
|
||||
|
||||
// Pointless
|
||||
/datum/uplink_item/badass
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
It requires an organic host as a home base and source of fuel." //This is the description of the actual injector. Feel free to change this for uplink purposes//
|
||||
item = /obj/item/weapon/storage/box/syndie_kit/holoparasite
|
||||
refundable = TRUE
|
||||
cost = 10 //I'm working off the borer. Price subject to change
|
||||
cost = 15 //I'm working off the borer. Price subject to change
|
||||
surplus = 20 //Nobody needs a ton of parasites
|
||||
exclude_modes = list(/datum/game_mode/nuclear)
|
||||
exclude_modes = list(/datum/game_mode/nuclear)
|
||||
refund_path = /obj/item/weapon/guardiancreator/tech/choose/traitor
|
||||
Reference in New Issue
Block a user