Merge remote-tracking branch 'citadel/master' into combat_rework_experimental

This commit is contained in:
silicons
2020-10-10 22:23:22 -07:00
215 changed files with 7532 additions and 2391 deletions
@@ -468,6 +468,9 @@
/datum/config_entry/string/default_view
config_entry_value = "15x15"
/datum/config_entry/string/default_view_square
config_entry_value = "15x15"
/datum/config_entry/flag/log_pictures
/datum/config_entry/flag/picture_logging_camera
@@ -0,0 +1,144 @@
SUBSYSTEM_DEF(min_spawns)
name = "Minimum Spawns" /// this hot steaming pile of garbage makes sure theres a minimum of tendrils scattered around
init_order = INIT_ORDER_DEFAULT
flags = SS_BACKGROUND | SS_NO_FIRE | SS_ALWAYS_SHOW_STAT
wait = 2
var/where_we_droppin_boys_iterations = 0
var/snaxi_snowflake_check = FALSE
var/list/active_spawns = list() // lavaland, snaxi, etc. primary spawn list
var/list/active_spawns_2 = list() // snaxi underground, etc. secondary spawn list
var/list/valid_mining_turfs = list() // lavaland/snaxi turfs
var/list/valid_mining_turfs_2 = list() // snaxi underground turfs
GLOBAL_LIST_INIT(minimum_lavaland_spawns, list(
/obj/structure/spawner/lavaland,
/obj/structure/spawner/lavaland/goliath,
/obj/structure/spawner/lavaland/legion,
/mob/living/simple_animal/hostile/megafauna/dragon,
/mob/living/simple_animal/hostile/megafauna/colossus,
/mob/living/simple_animal/hostile/megafauna/bubblegum
))
GLOBAL_LIST_INIT(minimum_snow_surface_spawns, list(
/obj/structure/spawner/ice_moon,
/obj/structure/spawner/ice_moon/polarbear
))
GLOBAL_LIST_INIT(minimum_snow_under_spawns, list(
/obj/structure/spawner/ice_moon/demonic_portal,
/obj/structure/spawner/ice_moon/demonic_portal/ice_whelp,
/obj/structure/spawner/ice_moon/demonic_portal/snowlegion
))
// step 1: check for which list(s) to use - done
// step 2: check for caves - done
// step 3: check for mobs - done
// step 4: start throwing shit down - done
// step 5: snaxi support - done?
/datum/controller/subsystem/min_spawns/Initialize(start_timeofday)
var/list/snaxi_zs_list = SSmapping.levels_by_trait(ZTRAIT_ICE_RUINS) // boy if these things arent mutually exclusive
var/list/lavaland_zs_list = SSmapping.levels_by_trait(ZTRAIT_LAVA_RUINS) // i'm gonna fuckin scream
if(snaxi_zs_list.len)
active_spawns = GLOB.minimum_snow_surface_spawns
active_spawns_2 = GLOB.minimum_snow_under_spawns
snaxi_snowflake_check = TRUE
else if(lavaland_zs_list.len) //todo: recognizing maps that aren't lavaland mining but are also not snaxi
active_spawns = GLOB.minimum_lavaland_spawns
else
return ..() // call it a day i guess
// borrowing this from auxbase code - see code\modules\mining\aux_base.dm
if(snaxi_snowflake_check)
for(var/z_level in SSmapping.levels_by_trait(ZTRAIT_ICE_RUINS))
for(var/turf/TT in Z_TURFS(z_level))
if(!isarea(TT.loc))
continue
var/area/A = TT.loc
if(!A.mob_spawn_allowed)
continue
if(!istype(TT, /turf/open/floor/plating/asteroid))
continue
if(typesof(/turf/open/lava) in orange(9, TT))
continue
valid_mining_turfs.Add(TT)
for(var/z_level in SSmapping.levels_by_trait(ZTRAIT_ICE_RUINS_UNDERGROUND))
for(var/turf/TT in Z_TURFS(z_level))
if(!isarea(TT.loc))
continue
var/area/A = TT.loc
if(!A.mob_spawn_allowed)
continue
if(!istype(TT, /turf/open/floor/plating/asteroid))
continue
if(typesof(/turf/open/lava) in orange(9, TT))
continue
valid_mining_turfs_2.Add(TT)
else
for(var/z_level in SSmapping.levels_by_trait(ZTRAIT_LAVA_RUINS))
for(var/turf/TT in Z_TURFS(z_level))
if(!isarea(TT.loc))
continue
var/area/A = TT.loc
if(!A.mob_spawn_allowed)
continue
if(!istype(TT, /turf/open/floor/plating/asteroid))
continue
if(typesof(/turf/open/lava) in orange(9, TT))
continue
valid_mining_turfs.Add(TT)
if(!valid_mining_turfs.len)
return ..() // call it a day i guess
// if we're at this point we might as well fucking hit it
where_we_droppin_boys()
return ..()
/datum/controller/subsystem/min_spawns/proc/where_we_droppin_boys()
while(active_spawns.len)
where_we_droppin_boys_iterations++
CHECK_TICK
if(where_we_droppin_boys_iterations >= 1250)
INIT_ANNOUNCE("Minimum Spawns subsystem stopped early on spawns list 1 - too many iterations!")
break
var/turf/RT = pick_n_take(valid_mining_turfs) //Pick a random mining Z-level turf
var/MS_tospawn = pick_n_take(active_spawns)
for(var/mob/living/simple_animal/hostile/megafauna/H in urange(70,RT)) //prevents mob clumps
if((istype(MS_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT, H) <= 70)
active_spawns.Add(MS_tospawn)
continue //let's try not to dump megas too close to each other?
if((istype(MS_tospawn, /obj/structure/spawner)) && get_dist(RT, H) <= 40)
active_spawns.Add(MS_tospawn)
continue //let's at least /try/ to space these out?
for(var/obj/structure/spawner/LT in urange(70,RT)) //prevents tendril/mega clumps
if((istype(MS_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT, LT) <= 70)
active_spawns.Add(MS_tospawn)
continue //let's try not to dump megas too close to each other?
if((istype(MS_tospawn, /obj/structure/spawner)) && get_dist(RT, LT) <= 40)
active_spawns.Add(MS_tospawn)
continue //let's at least /try/ to space these out?
// man the overhead on this is gonna SUCK
new MS_tospawn(RT)
while(active_spawns_2.len)
where_we_droppin_boys_iterations++
CHECK_TICK
if(where_we_droppin_boys_iterations >= 1250)
INIT_ANNOUNCE("Minimum Spawns subsystem stopped early on active list 2 - too many iterations!")
break
var/turf/RT2 = pick_n_take(valid_mining_turfs_2) //Pick a random mining Z-level turf
var/MS2_tospawn = pick_n_take(active_spawns_2)
for(var/mob/living/simple_animal/hostile/H in urange(70,RT2)) //prevents mob clumps
if((istype(MS2_tospawn, /mob/living/simple_animal/hostile/megafauna) || ismegafauna(H)) && get_dist(RT2, H) <= 70)
active_spawns_2.Add(MS2_tospawn)
continue //let's try not to dump megas too close to each other?
if((istype(MS2_tospawn, /obj/structure/spawner)) && get_dist(RT2, H) <= 40)
active_spawns_2.Add(MS2_tospawn)
continue //let's at least /try/ to space these out?
for(var/obj/structure/spawner/LT in urange(70,RT2)) //prevents tendril/mega clumps
if((istype(MS2_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT2, LT) <= 70)
active_spawns_2.Add(MS2_tospawn)
continue //let's try not to dump megas too close to each other?
if((istype(MS2_tospawn, /obj/structure/spawner)) && get_dist(RT2, LT) <= 40)
active_spawns_2.Add(MS2_tospawn)
continue //let's at least /try/ to space these out?
// man the overhead on this is gonna SUCK
new MS2_tospawn(RT2)
if(!active_spawns.len && !active_spawns_2.len)
return // we're done here
+238
View File
@@ -0,0 +1,238 @@
/// Controls how many buckets should be kept, each representing a tick. (30 seconds worth)
#define BUCKET_LEN (world.fps * 1 * 30)
/// Helper for getting the correct bucket for a given chatmessage
#define BUCKET_POS(scheduled_destruction) (((round((scheduled_destruction - SSrunechat.head_offset) / world.tick_lag) + 1) % BUCKET_LEN) || BUCKET_LEN)
/// Gets the maximum time at which messages will be handled in buckets, used for deferring to secondary queue
#define BUCKET_LIMIT (world.time + TICKS2DS(min(BUCKET_LEN - (SSrunechat.practical_offset - DS2TICKS(world.time - SSrunechat.head_offset)) - 1, BUCKET_LEN - 1)))
/**
* # Runechat Subsystem
*
* Maintains a timer-like system to handle destruction of runechat messages. Much of this code is modeled
* after or adapted from the timer subsystem.
*
* Note that this has the same structure for storing and queueing messages as the timer subsystem does
* for handling timers: the bucket_list is a list of chatmessage datums, each of which are the head
* of a circularly linked list. Any given index in bucket_list could be null, representing an empty bucket.
*/
SUBSYSTEM_DEF(runechat)
name = "Runechat"
flags = SS_TICKER | SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_RUNECHAT
/// world.time of the first entry in the bucket list, effectively the 'start time' of the current buckets
var/head_offset = 0
/// Index of the first non-empty bucket
var/practical_offset = 1
/// world.tick_lag the bucket was designed for
var/bucket_resolution = 0
/// How many messages are in the buckets
var/bucket_count = 0
/// List of buckets, each bucket holds every message that has to be killed that byond tick
var/list/bucket_list = list()
/// Queue used for storing messages that are scheduled for deletion too far in the future for the buckets
var/list/datum/chatmessage/second_queue = list()
/datum/controller/subsystem/runechat/PreInit()
bucket_list.len = BUCKET_LEN
head_offset = world.time
bucket_resolution = world.tick_lag
/datum/controller/subsystem/runechat/stat_entry(msg)
msg = "ActMsgs:[bucket_count] SecQueue:[length(second_queue)]"
return msg
/datum/controller/subsystem/runechat/fire(resumed = FALSE)
// Store local references to datum vars as it is faster to access them this way
var/list/bucket_list = src.bucket_list
if (MC_TICK_CHECK)
return
// Check for when we need to loop the buckets, this occurs when
// the head_offset is approaching BUCKET_LEN ticks in the past
if (practical_offset > BUCKET_LEN)
head_offset += TICKS2DS(BUCKET_LEN)
practical_offset = 1
resumed = FALSE
// Check for when we have to reset buckets, typically from auto-reset
if ((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution))
reset_buckets()
bucket_list = src.bucket_list
resumed = FALSE
// Store a reference to the 'working' chatmessage so that we can resume if the MC
// has us stop mid-way through processing
var/static/datum/chatmessage/cm
if (!resumed)
cm = null
// Iterate through each bucket starting from the practical offset
while (practical_offset <= BUCKET_LEN && head_offset + ((practical_offset - 1) * world.tick_lag) <= world.time)
var/datum/chatmessage/bucket_head = bucket_list[practical_offset]
if (!cm || !bucket_head || cm == bucket_head)
bucket_head = bucket_list[practical_offset]
cm = bucket_head
while (cm)
// If the chatmessage hasn't yet had its life ended then do that now
var/datum/chatmessage/next = cm.next
if (!cm.eol_complete)
cm.end_of_life()
else if (!QDELETED(cm)) // otherwise if we haven't deleted it yet, do so (this is after EOL completion)
qdel(cm)
if (MC_TICK_CHECK)
return
// Break once we've processed the entire bucket
cm = next
if (cm == bucket_head)
break
// Empty the bucket, check if anything in the secondary queue should be shifted to this bucket
bucket_list[practical_offset++] = null
var/i = 0
for (i in 1 to length(second_queue))
cm = second_queue[i]
if (cm.scheduled_destruction >= BUCKET_LIMIT)
i--
break
// Transfer the message into the bucket, performing necessary circular doubly-linked list operations
bucket_count++
var/bucket_pos = max(1, BUCKET_POS(cm.scheduled_destruction))
var/datum/timedevent/head = bucket_list[bucket_pos]
if (!head)
bucket_list[bucket_pos] = cm
cm.next = null
cm.prev = null
continue
if (!head.prev)
head.prev = head
cm.next = head
cm.prev = head.prev
cm.next.prev = cm
cm.prev.next = cm
if (i)
second_queue.Cut(1, i + 1)
cm = null
/datum/controller/subsystem/runechat/Recover()
bucket_list |= SSrunechat.bucket_list
second_queue |= SSrunechat.second_queue
/datum/controller/subsystem/runechat/proc/reset_buckets()
bucket_list.len = BUCKET_LEN
head_offset = world.time
bucket_resolution = world.tick_lag
/**
* Enters the runechat subsystem with this chatmessage, inserting it into the end-of-life queue
*
* This will also account for a chatmessage already being registered, and in which case
* the position will be updated to remove it from the previous location if necessary
*
* Arguments:
* * new_sched_destruction Optional, when provided is used to update an existing message with the new specified time
*/
/datum/chatmessage/proc/enter_subsystem(new_sched_destruction = 0)
// Get local references from subsystem as they are faster to access than the datum references
var/list/bucket_list = SSrunechat.bucket_list
var/list/second_queue = SSrunechat.second_queue
// When necessary, de-list the chatmessage from its previous position
if (new_sched_destruction)
if (scheduled_destruction >= BUCKET_LIMIT)
second_queue -= src
else
SSrunechat.bucket_count--
var/bucket_pos = BUCKET_POS(scheduled_destruction)
if (bucket_pos > 0)
var/datum/chatmessage/bucket_head = bucket_list[bucket_pos]
if (bucket_head == src)
bucket_list[bucket_pos] = next
if (prev != next)
prev.next = next
next.prev = prev
else
prev?.next = null
next?.prev = null
prev = next = null
scheduled_destruction = new_sched_destruction
// Ensure the scheduled destruction time is properly bound to avoid missing a scheduled event
scheduled_destruction = max(CEILING(scheduled_destruction, world.tick_lag), world.time + world.tick_lag)
// Handle insertion into the secondary queue if the required time is outside our tracked amounts
if (scheduled_destruction >= BUCKET_LIMIT)
BINARY_INSERT(src, SSrunechat.second_queue, datum/chatmessage, src, scheduled_destruction, COMPARE_KEY)
return
// Get bucket position and a local reference to the datum var, it's faster to access this way
var/bucket_pos = BUCKET_POS(scheduled_destruction)
// Get the bucket head for that bucket, increment the bucket count
var/datum/chatmessage/bucket_head = bucket_list[bucket_pos]
SSrunechat.bucket_count++
// If there is no existing head of this bucket, we can set this message to be that head
if (!bucket_head)
bucket_list[bucket_pos] = src
return
// Otherwise it's a simple insertion into the circularly doubly-linked list
if (!bucket_head.prev)
bucket_head.prev = bucket_head
next = bucket_head
prev = bucket_head.prev
next.prev = src
prev.next = src
/**
* Removes this chatmessage datum from the runechat subsystem
*/
/datum/chatmessage/proc/leave_subsystem()
// Attempt to find the bucket that contains this chat message
var/bucket_pos = BUCKET_POS(scheduled_destruction)
// Get local references to the subsystem's vars, faster than accessing on the datum
var/list/bucket_list = SSrunechat.bucket_list
var/list/second_queue = SSrunechat.second_queue
// Attempt to get the head of the bucket
var/datum/chatmessage/bucket_head
if (bucket_pos > 0)
bucket_head = bucket_list[bucket_pos]
// Decrement the number of messages in buckets if the message is
// the head of the bucket, or has a SD less than BUCKET_LIMIT implying it fits
// into an existing bucket, or is otherwise not present in the secondary queue
if(bucket_head == src)
bucket_list[bucket_pos] = next
SSrunechat.bucket_count--
else if(scheduled_destruction < BUCKET_LIMIT)
SSrunechat.bucket_count--
else
var/l = length(second_queue)
second_queue -= src
if(l == length(second_queue))
SSrunechat.bucket_count--
// Remove the message from the bucket, ensuring to maintain
// the integrity of the bucket's list if relevant
if(prev != next)
prev.next = next
next.prev = prev
else
prev?.next = null
next?.prev = null
prev = next = null
#undef BUCKET_LEN
#undef BUCKET_POS
#undef BUCKET_LIMIT
+1 -1
View File
@@ -47,7 +47,7 @@ SUBSYSTEM_DEF(shuttle)
var/datum/round_event/shuttle_loan/shuttle_loan
var/shuttle_purchased = FALSE //If the station has purchased a replacement escape shuttle this round
var/shuttle_purchased = SHUTTLEPURCHASE_PURCHASABLE //If the station has purchased a replacement escape shuttle this round
var/list/shuttle_purchase_requirements_met = list() //For keeping track of ingame events that would unlock new shuttles, such as defeating a boss or discovering a secret item
var/lockdown = FALSE //disallow transit after nuke goes off