Merge branch 'master' into upstream-merge-27344

This commit is contained in:
Poojawa
2017-05-20 23:26:28 -05:00
committed by GitHub
95 changed files with 3681 additions and 1609 deletions
+4
View File
@@ -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
+309
View File
@@ -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()
+106
View File
@@ -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.
+76
View File
@@ -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)
+3 -2
View File
@@ -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
+9 -1
View File
@@ -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)
+1 -1
View File
@@ -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))
+366 -357
View File
@@ -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.
+1 -2
View File
@@ -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>"
+11 -10
View File
@@ -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()
+53 -5
View File
@@ -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
+32 -6
View File
@@ -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
+27 -6
View File
@@ -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
+1
View File
@@ -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"
+1
View File
@@ -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>"
+15
View File
@@ -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
+3 -2
View File
@@ -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