Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maybe performances up spawners #3226

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@
///When a gun/mag thing is told to admin reload, signal
#define COMSIG_GUN_MAG_ADMIN_RELOAD "gun_mag_admin_reload"

///simple thing to just see if the thing is still listening to us
#define COMSIG_PING "ping"

#define COMSIG_ATOM_SCREWDRIVER_ACT "atom_screwdriver_act" //from base of atom/screwdriver_act(): (mob/living/user, obj/item/I)
#define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport" //called when teleporting into a protected turf: (channel, turf/origin, turf/destination)
#define COMPONENT_BLOCK_TELEPORT 1
Expand Down
2 changes: 2 additions & 0 deletions code/controllers/subsystem/processing/mob_spawner.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ PROCESSING_SUBSYSTEM_DEF(spawners)
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
wait = 1 SECONDS
priority = FIRE_PRIORITY_SPAWNERS
var/active_duration = 20 SECONDS
var/debug_spawner_turfs = FALSE

/// YEah so this used to be something, but then I messed up and all the mobs just
/// Went into their own spawners, and I liked that, so imma do that k
Expand Down
204 changes: 148 additions & 56 deletions code/datums/components/spawner.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
GLOBAL_VAR_INIT(debug_spawner_turfs, FALSE)
/datum/component/spawner
var/mob_types = list(/mob/living/simple_animal/hostile/carp)
/// List of 'special' mobs to spawn
Expand Down Expand Up @@ -37,7 +38,17 @@
var/am_special = FALSE
/// Is something covering us?
var/datum/weakref/covering_object
/// use the old spawner player-is-close check
var/old_spawner_check = FALSE
/// All our turfs that we're listening to
var/list/my_turfs = list() // its a list of coords
/// All our turfs that somehow got destroyed, and we need to reconnect with
var/list/disconnected = list() // its a list of coords
var/active = FALSE
/// When tripped, when do we stop trying to spawn things?
var/spawn_until = 0
COOLDOWN_DECLARE(spawner_cooldown)
var/covered = FALSE

/datum/component/spawner/Initialize(
_mob_types,
Expand Down Expand Up @@ -98,8 +109,8 @@

RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/nest_destroyed)
RegisterSignal(parent, COMSIG_OBJ_ATTACK_GENERIC, .proc/on_attack_generic)
RegisterSignal(parent, COMSIG_SPAWNER_COVERED, .proc/stop_spawning)
RegisterSignal(parent, COMSIG_SPAWNER_UNCOVERED, .proc/start_spawning)
RegisterSignal(parent, COMSIG_SPAWNER_COVERED, .proc/coverme)
RegisterSignal(parent, COMSIG_SPAWNER_UNCOVERED, .proc/uncoverme)
RegisterSignal(parent, COMSIG_SPAWNER_ABSORB_MOB, .proc/unbirth_mob)
RegisterSignal(parent, COMSIG_SPAWNER_EXISTS, .proc/has_spawner)
if(istype(parent, /obj/structure/nest))
Expand All @@ -109,28 +120,95 @@
if(istype(parent, /obj/structure/nest/special))
am_special = TRUE
RegisterSignal(parent, COMSIG_SPAWNER_SPAWN_NOW, .proc/spawn_mob_special)
if(!delay_start)
start_spawning()
register_turfs()

/datum/component/spawner/proc/register_turfs()
var/atom/dad = parent
if(!dad.loc)
return
var/debug_color = SSspawners.debug_spawner_turfs ? "#[random_color()]" : null
for(var/turf/trip in range(range, dad.loc))
connect_to_turf(trip, debug_color)

/datum/component/spawner/proc/connect_to_turf(turf/trip, debug_color)
my_turfs |= atom2coords(trip)
RegisterSignal(trip, COMSIG_ATOM_ENTERED, .proc/turf_trip)
RegisterSignal(trip, COMSIG_TURF_CHANGE, .proc/turf_changed)
if(SSspawners.debug_spawner_turfs && debug_color)
trip.add_atom_colour(debug_color, ADMIN_COLOUR_PRIORITY)

/datum/component/spawner/proc/turf_changed(turf/changed)
if(!isturf(changed))
return
disconnected |= atom2coords(changed)
start_spawning()

/datum/component/spawner/proc/unregister_turfs()
for(var/coords in my_turfs)
var/turf/trip = coords2turf(coords)
if(!trip)
continue
trip.remove_atom_colour(ADMIN_COLOUR_PRIORITY)
UnregisterSignal(trip, COMSIG_ATOM_ENTERED, COMSIG_TURF_CHANGE)
my_turfs = list()
disconnected = list()

/datum/component/spawner/proc/reconnect()
if(!LAZYLEN(disconnected))
return
var/debug_color = SSspawners.debug_spawner_turfs ? "#[randomColor()]" : null
for(var/coord in disconnected)
var/turf/trip = coords2turf(coord)
if(!trip)
continue
connect_to_turf(trip, debug_color)

/datum/component/spawner/proc/still_there()
return TRUE // hi

/datum/component/spawner/process()
if(should_destroy_spawner())
qdel(parent)
if(old_spawner_check)
try_to_spawn()
return
if(!can_spawn_mob())
if(COOLDOWN_FINISHED(src, spawn_until))
stop_spawning(null, FALSE)
return
spawn_mob()
if(spawn_until && !COOLDOWN_FINISHED(src, spawn_until))
try_to_spawn()
else
reconnect()

/// something entered one of our turfs, check if we should spawn something
/datum/component/spawner/proc/turf_trip(datum/source, atom/movable/arrived)
if(!am_special && spawn_until && !COOLDOWN_FINISHED(src, spawn_until))
COOLDOWN_START(src, spawn_until, SSspawners.active_duration)
return
if(!check_mob(usr)) // could write a proc that searches everything for a mob, buuuuuuut........ dont wanna
return
if(am_special)
spawn_mob_special()
return
COOLDOWN_START(src, spawn_until, SSspawners.active_duration)
start_spawning()

/// Something told us to restart spawning
/datum/component/spawner/proc/start_spawning()
START_PROCESSING(SSspawners, src)
/datum/component/spawner/proc/uncoverme()
covered = FALSE
start_spawning()

/datum/component/spawner/proc/nest_destroyed(datum/source, force, hint)
/// Something told us to restart spawning
/datum/component/spawner/proc/coverme()
covered = TRUE
stop_spawning()
if(!am_special)
GLOB.nest_spawn_points |= atom2coords(parent) // we'll be back, eventually

/datum/component/spawner/proc/stop_spawning(datum/source, force, hint)
/// Something told us to restart spawning
/datum/component/spawner/proc/start_spawning()
START_PROCESSING(SSspawners, src)

/datum/component/spawner/proc/stop_spawning(datum/source, clear_spawned_mobs = TRUE)
STOP_PROCESSING(SSspawners, src)
if(!clear_spawned_mobs)
return
for(var/datum/weakref/mob_ref as anything in spawned_mobs)
var/mob/living/simple_animal/removed_animal = mob_ref.resolve()
if(!removed_animal)
Expand All @@ -139,6 +217,11 @@
removed_animal.nest = null
spawned_mobs = null

/datum/component/spawner/proc/nest_destroyed(datum/source, force, hint)
stop_spawning()
if(!am_special)
GLOB.nest_spawn_points |= atom2coords(parent) // we'll be back, eventually

// Stopping clientless simple mobs' from indiscriminately bashing their own spawners due DestroySurroundings() et similars.
/datum/component/spawner/proc/on_attack_generic(datum/source, mob/user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration)
if(!user.client && ((user.faction & faction) || (WEAKREF(user) in spawned_mobs)))
Expand All @@ -155,10 +238,6 @@
if(thingy.density == TRUE)
return TRUE

/// Do we have any mobs left?
/datum/component/spawner/proc/has_mobs_left()
return counterlist_sum(mob_types) + LAZYLEN(special_mobs)

/// Should the spawner be destroyed?
/datum/component/spawner/proc/should_destroy_spawner()
if(infinite)
Expand All @@ -169,6 +248,57 @@
return FALSE // no more self-destructing ant queens
return TRUE

/// Do we have any mobs left?
/datum/component/spawner/proc/has_mobs_left()
return counterlist_sum(mob_types) + LAZYLEN(special_mobs)

/datum/component/spawner/proc/check_mob(mob/living/check)
if(!isliving(check))
return FALSE
if(!check.client)
return FALSE
return TRUE

/// Basic checks to see if we can spawn something
/datum/component/spawner/proc/try_to_spawn()
if(covered)
return FALSE
if(COOLDOWN_TIMELEFT(src, spawner_cooldown))
return FALSE
if(!check_spawned_mobs())
return FALSE
if(something_covering_us())
return FALSE
if(old_spawner_check && !something_in_range())
return FALSE
spawn_mob()
COOLDOWN_START(src, spawner_cooldown, spawn_time)
if(should_destroy_spawner())
qdel(parent)
return

/datum/component/spawner/proc/something_in_range()
if(!range)
return TRUE
var/atom/P = parent
for(var/mob/living in GLOB.player_list) // client-containing mobs, NOT clients
if(get_dist(P, living) <= range)
return TRUE

/// is something covering us?
/datum/component/spawner/proc/something_covering_us()
if(!coverable_by_dense_things)
return FALSE
var/atom/P = parent
var/turf/our_turf = get_turf(P)
if(!our_turf) // mobs keep spawning in nullspace for some bizarre reason
qdel(P) // and I aint dealing with that shit
return
/// Accounts for anything dense, which includes mobs, mechs, lockers, etc
for(var/atom/movable/maybe_heavy_thing in our_turf.contents)
if(maybe_heavy_thing.density)
return TRUE

/// Check the spawned mob list, prune dead mobs, return TRUE if it isnt full
/datum/component/spawner/proc/check_spawned_mobs()
if(LAZYLEN(spawned_mobs) < max_mobs)
Expand All @@ -183,44 +313,6 @@
if(LAZYLEN(spawned_mobs) < max_mobs)
return TRUE

/// Basic checks to see if we can spawn something
/datum/component/spawner/proc/can_spawn_mob()
if(COOLDOWN_TIMELEFT(src, spawner_cooldown))
return FALSE
if(!check_spawned_mobs())
return FALSE
var/atom/P = parent
if(coverable_by_dense_things)
var/turf/our_turf = get_turf(P)
if(!our_turf) // mobs keep spawning in nullspace for some bizarre reason
qdel(P) // and I aint dealing with that shit
return
var/atom/movable/previous_heavy_thing = covering_object?.resolve()
if(previous_heavy_thing)
if(get_turf(previous_heavy_thing) == our_turf)
return FALSE
else
covering_object = null // mustve wandered off
/// Accounts for anything dense, which includes mobs, mechs, lockers, etc
for(var/atom/movable/maybe_heavy_thing in our_turf.contents)
if(maybe_heavy_thing.density == TRUE)
covering_object = WEAKREF(maybe_heavy_thing)
return FALSE
if(range)
var/is_close_enough = FALSE
for(var/mob/living as anything in SSmobs.clients_by_zlevel[P.z]) // client-containing mobs, NOT clients
if(get_dist(P, living) <= range)
is_close_enough = TRUE
break
if(!is_close_enough)
return FALSE
/* if(overpopulation_range)
var/mobs_in_range
for(var/mob/living/simple_animal/living_mob in range(overpopulation_range, get_turf(P)))
if(mobs_in_range++ >= max_mobs)
return FALSE */
return TRUE

/// spawns a mob, then immediately tries to self-destruct
/datum/component/spawner/proc/spawn_mob_special()
spawn_mob()
Expand Down
4 changes: 3 additions & 1 deletion code/modules/mob/living/simple_animal/hostile/hostile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
/// timer for despawning when lonely
var/lonely_timer_id

/mob/living/simple_animal/hostile/Initialize()
/mob/living/simple_animal/hostile/Initialize(mapload)
. = ..()

if(!targets_from)
Expand All @@ -119,6 +119,8 @@
if(MOB_EMP_DAMAGE in emp_flags)
smoke = new /datum/effect_system/smoke_spread/bad
smoke.attach(src)
if(mapload && despawns_when_lonely)
unbirth_self(TRUE)

/mob/living/simple_animal/hostile/Destroy()
targets_from = null
Expand Down