Merge branch 'master' into spooder
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
name = "Initializing..."
|
||||
var/target
|
||||
|
||||
/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical
|
||||
INITIALIZE_IMMEDIATE(/obj/effect/statclick) //it's new, but rebranded.
|
||||
|
||||
/obj/effect/statclick/Initialize(mapload, text, target) //Don't port this to Initialize it's too critical
|
||||
. = ..()
|
||||
name = text
|
||||
src.target = target
|
||||
|
||||
@@ -230,14 +230,6 @@
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed
|
||||
key_mode = KEY_MODE_TYPE
|
||||
value_mode = VALUE_MODE_NUM
|
||||
config_entry_value = list( //DEFAULTS
|
||||
/mob/living/simple_animal = 1,
|
||||
/mob/living/silicon/pai = 1,
|
||||
/mob/living/carbon/alien/humanoid/sentinel = 0.25,
|
||||
/mob/living/carbon/alien/humanoid/drone = 0.5,
|
||||
/mob/living/carbon/alien/humanoid/royal/praetorian = 1,
|
||||
/mob/living/carbon/alien/humanoid/royal/queen = 3
|
||||
)
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed/ValidateAndSet()
|
||||
. = ..()
|
||||
@@ -249,6 +241,24 @@
|
||||
if(. && (var_name == NAMEOF(src, config_entry_value)))
|
||||
update_config_movespeed_type_lookup(TRUE)
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed/normal
|
||||
config_entry_value = list( //DEFAULTS
|
||||
/mob/living/simple_animal = 1,
|
||||
/mob/living/silicon/pai = 1,
|
||||
/mob/living/carbon/alien/humanoid/sentinel = 0.25,
|
||||
/mob/living/carbon/alien/humanoid/drone = 0.5,
|
||||
/mob/living/carbon/alien/humanoid/royal/praetorian = 1,
|
||||
/mob/living/carbon/alien/humanoid/royal/queen = 3
|
||||
)
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed/floating
|
||||
config_entry_value = list(
|
||||
/mob/living = 0,
|
||||
/mob/living/carbon/alien/humanoid = 0,
|
||||
/mob/living/carbon/alien/humanoid/royal/praetorian = 0,
|
||||
/mob/living/carbon/alien/humanoid/royal/queen = 2
|
||||
)
|
||||
|
||||
/datum/config_entry/number/movedelay //Used for modifying movement speed for mobs.
|
||||
abstract_type = /datum/config_entry/number/movedelay
|
||||
integer = FALSE
|
||||
@@ -280,6 +290,12 @@
|
||||
/datum/config_entry/number/movedelay/sprint_speed_increase
|
||||
config_entry_value = 1
|
||||
|
||||
/datum/config_entry/number/movedelay/sprint_max_tiles_increase
|
||||
config_entry_value = 5
|
||||
|
||||
/datum/config_entry/number/movedelay/sprint_absolute_max_tiles
|
||||
config_entry_value = 13
|
||||
|
||||
/datum/config_entry/number/movedelay/sprint_buffer_max
|
||||
config_entry_value = 24
|
||||
|
||||
@@ -291,7 +307,7 @@
|
||||
|
||||
/////////////////////////////////////////////////Outdated move delay
|
||||
/datum/config_entry/number/outdated_movedelay
|
||||
deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed
|
||||
deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed/normal
|
||||
abstract_type = /datum/config_entry/number/outdated_movedelay
|
||||
|
||||
var/movedelay_type
|
||||
|
||||
@@ -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,35 @@
|
||||
/datum/config_entry/number/stamina_combat
|
||||
integer = FALSE
|
||||
abstract_type = /datum/config_entry/number/stamina_combat
|
||||
|
||||
/// Maximum stamina buffer
|
||||
/datum/config_entry/number/stamina_combat/buffer_max
|
||||
config_entry_value = 25
|
||||
|
||||
/// Seconds until percent_regeneration_out_of_combat kicks in
|
||||
/datum/config_entry/number/stamina_combat/out_of_combat_timer
|
||||
config_entry_value = 15
|
||||
|
||||
/// Base regeneration per second
|
||||
/datum/config_entry/number/stamina_combat/base_regeneration
|
||||
config_entry_value = 0.5
|
||||
|
||||
/// Combat mode regeneration per second
|
||||
/datum/config_entry/number/stamina_combat/combat_regeneration
|
||||
config_entry_value = 5
|
||||
|
||||
/// After out_of_combat_timer elapses, additionally regenerate this percent of total stamina per second. Unaffected by combat mode.
|
||||
/datum/config_entry/number/stamina_combat/percent_regeneration_out_of_combat
|
||||
config_entry_value = 30
|
||||
|
||||
/// Seconds after an action for which your regeneration is penalized
|
||||
/datum/config_entry/number/stamina_combat/post_action_penalty_delay
|
||||
config_entry_value = 5
|
||||
|
||||
/// Factor to multiply by for penalizing post-action-stamina-regen
|
||||
/datum/config_entry/number/stamina_combat/post_action_penalty_factor
|
||||
config_entry_value = 0.25
|
||||
|
||||
/// Factor to multiply by for stamina usage past buffer into health
|
||||
/datum/config_entry/number/stamina_combat/overdraw_penalty_factor
|
||||
config_entry_value = 1.5
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Failsafe
|
||||
*
|
||||
* Pretty much pokes the MC to make sure it's still alive.
|
||||
@@ -31,7 +31,7 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
Initialize()
|
||||
|
||||
/datum/controller/failsafe/Initialize()
|
||||
set waitfor = 0
|
||||
set waitfor = FALSE
|
||||
Failsafe.Loop()
|
||||
if(!QDELETED(src))
|
||||
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
|
||||
@@ -97,4 +97,4 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
|
||||
/datum/controller/failsafe/stat_entry(msg)
|
||||
msg = "Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"
|
||||
return msg
|
||||
return msg
|
||||
@@ -20,8 +20,9 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
|
||||
|
||||
Initialize()
|
||||
|
||||
/datum/controller/global_vars/Destroy()
|
||||
//fuck off kevinz
|
||||
/datum/controller/global_vars/Destroy(force)
|
||||
// This is done to prevent an exploit where admins can get around protected vars
|
||||
SHOULD_CALL_PARENT(FALSE)
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
/datum/controller/global_vars/stat_entry(msg)
|
||||
|
||||
@@ -97,9 +97,9 @@
|
||||
return
|
||||
|
||||
//This is used so the mc knows when the subsystem sleeps. do not override.
|
||||
/datum/controller/subsystem/proc/ignite(resumed = 0)
|
||||
/datum/controller/subsystem/proc/ignite(resumed = FALSE)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
set waitfor = 0
|
||||
set waitfor = FALSE
|
||||
. = SS_SLEEPING
|
||||
fire(resumed)
|
||||
. = state
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(dcs)
|
||||
name = "Datum Component System"
|
||||
flags = SS_NO_INIT
|
||||
wait = 1 SECONDS
|
||||
|
||||
var/list/elements_by_type = list()
|
||||
|
||||
|
||||
@@ -60,9 +60,9 @@ SUBSYSTEM_DEF(economy)
|
||||
secmedsrv_payout() // Payout based on crew safety, health, and mood.
|
||||
civ_payout() // Payout based on ??? Profit
|
||||
car_payout() // Cargo's natural gain in the cash moneys.
|
||||
for(var/A in bank_accounts)
|
||||
var/datum/bank_account/B = A
|
||||
B.payday(1)
|
||||
for(var/account in bank_accounts)
|
||||
var/datum/bank_account/bank_account = account
|
||||
bank_account.payday(1)
|
||||
|
||||
|
||||
/datum/controller/subsystem/economy/proc/get_dep_account(dep_id)
|
||||
|
||||
@@ -100,7 +100,7 @@ SUBSYSTEM_DEF(events)
|
||||
// REEEEEEEEE
|
||||
/client/proc/forceEvent()
|
||||
set name = "Trigger Event"
|
||||
set category = "Fun"
|
||||
set category = "Admin.Events"
|
||||
|
||||
if(!holder ||!check_rights(R_FUN))
|
||||
return
|
||||
|
||||
@@ -64,10 +64,10 @@ SUBSYSTEM_DEF(input)
|
||||
// Misc
|
||||
macroset_classic_input["Tab"] = "\".winset \\\"mainwindow.macro=[SKIN_MACROSET_CLASSIC_HOTKEYS] map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\""
|
||||
macroset_classic_input["Escape"] = "\".winset \\\"input.text=\\\"\\\"\\\"\""
|
||||
|
||||
|
||||
// FINALLY, WE CAN DO SOMETHING MORE NORMAL FOR THE SNOWFLAKE-BUT-LESS KEYSET.
|
||||
|
||||
// HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when
|
||||
|
||||
// HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when
|
||||
// conflicts occur with something like say indicator vs say), we're going to snowflake this anyways
|
||||
var/list/hard_binds = list(
|
||||
"O" = "ooc",
|
||||
@@ -80,7 +80,7 @@ SUBSYSTEM_DEF(input)
|
||||
for(var/key in hard_binds)
|
||||
for(var/modifier in anti_collision_modifiers)
|
||||
hard_bind_anti_collision["[modifier]+[key]"] = ".NONSENSICAL_VERB_THAT_DOES_NOTHING"
|
||||
|
||||
|
||||
macroset_classic_hotkey = list(
|
||||
"Any" = "\"KeyDown \[\[*\]\]\"",
|
||||
"Any+UP" = "\"KeyUp \[\[*\]\]\"",
|
||||
@@ -88,7 +88,7 @@ SUBSYSTEM_DEF(input)
|
||||
"Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
|
||||
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
|
||||
)
|
||||
|
||||
|
||||
macroset_classic_hotkey |= hard_binds
|
||||
macroset_classic_hotkey |= hard_bind_anti_collision
|
||||
|
||||
@@ -100,7 +100,7 @@ SUBSYSTEM_DEF(input)
|
||||
"Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
|
||||
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
|
||||
)
|
||||
|
||||
|
||||
macroset_hotkey |= hard_binds
|
||||
macroset_hotkey |= hard_bind_anti_collision
|
||||
|
||||
|
||||
@@ -55,9 +55,10 @@ SUBSYSTEM_DEF(job)
|
||||
continue
|
||||
if(!job.config_check())
|
||||
continue
|
||||
if(!job.map_check()) //Even though we initialize before mapping, this is fine because the config is loaded at new
|
||||
if(!job.map_check(SSmapping.config)) //Even though we initialize before mapping, this is fine because the config is loaded at new
|
||||
testing("Removed [job.type] due to map config");
|
||||
continue
|
||||
job.process_map_overrides(SSmapping.config)
|
||||
occupations += job
|
||||
name_occupations[job.title] = job
|
||||
type_occupations[J] = job
|
||||
@@ -665,7 +666,7 @@ SUBSYSTEM_DEF(job)
|
||||
message_admins(msg)
|
||||
CRASH(msg)
|
||||
|
||||
/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff, bypass_prereqs = FALSE)
|
||||
/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff, bypass_prereqs = FALSE, can_drop = TRUE)
|
||||
var/mob/the_mob = N
|
||||
if(!the_mob)
|
||||
the_mob = M // cause this doesn't get assigned if player is a latejoiner
|
||||
@@ -694,9 +695,15 @@ SUBSYSTEM_DEF(job)
|
||||
var/mob/living/carbon/C = M
|
||||
var/obj/item/storage/backpack/B = C.back
|
||||
if(!B || !SEND_SIGNAL(B, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE)) // Otherwise, try to put it in the backpack, for carbons.
|
||||
I.forceMove(get_turf(C))
|
||||
if(can_drop)
|
||||
I.forceMove(get_turf(C))
|
||||
else
|
||||
qdel(I)
|
||||
else if(!M.equip_to_slot_if_possible(I, SLOT_IN_BACKPACK, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // Otherwise, try to put it in the backpack
|
||||
I.forceMove(get_turf(M)) // If everything fails, just put it on the floor under the mob.
|
||||
if(can_drop)
|
||||
I.forceMove(get_turf(M)) // If everything fails, just put it on the floor under the mob.
|
||||
else
|
||||
qdel(I)
|
||||
|
||||
/datum/controller/subsystem/job/proc/FreeRole(rank)
|
||||
if(!rank)
|
||||
|
||||
@@ -447,7 +447,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
|
||||
//Manual loading of away missions.
|
||||
/client/proc/admin_away()
|
||||
set name = "Load Away Mission / Virtual Reality"
|
||||
set category = "Fun"
|
||||
set category = "Admin.Events"
|
||||
|
||||
if(!holder ||!check_rights(R_FUN))
|
||||
return
|
||||
|
||||
@@ -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
|
||||
@@ -5,6 +5,10 @@ SUBSYSTEM_DEF(npcpool)
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
var/list/currentrun = list()
|
||||
/// catches sleeping
|
||||
var/invoking = FALSE
|
||||
/// Invoke start time
|
||||
var/invoke_start = 0
|
||||
|
||||
/datum/controller/subsystem/npcpool/stat_entry(msg)
|
||||
var/list/activelist = GLOB.simple_animals[AI_ON]
|
||||
@@ -12,7 +16,6 @@ SUBSYSTEM_DEF(npcpool)
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/npcpool/fire(resumed = FALSE)
|
||||
|
||||
if (!resumed)
|
||||
var/list/activelist = GLOB.simple_animals[AI_ON]
|
||||
src.currentrun = activelist.Copy()
|
||||
@@ -24,12 +27,22 @@ SUBSYSTEM_DEF(npcpool)
|
||||
var/mob/living/simple_animal/SA = currentrun[currentrun.len]
|
||||
--currentrun.len
|
||||
|
||||
if(!SA.ckey && !SA.mob_transforming)
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_movement()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_action()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_speech()
|
||||
invoking = TRUE
|
||||
invoke_start = world.time
|
||||
INVOKE_ASYNC(src, .proc/invoke_process, SA)
|
||||
if(invoking)
|
||||
stack_trace("WARNING: [SA] ([SA.type]) slept during NPCPool processing.")
|
||||
invoking = FALSE
|
||||
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/npcpool/proc/invoke_process(mob/living/simple_animal/SA)
|
||||
if(!SA.ckey && !SA.mob_transforming)
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_movement()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_action()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_speech()
|
||||
invoking = FALSE
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -27,28 +27,6 @@ SUBSYSTEM_DEF(statpanels)
|
||||
var/ETA = SSshuttle.emergency.getModeStr()
|
||||
if(ETA)
|
||||
global_data += "[ETA] [SSshuttle.emergency.getTimerStr()]"
|
||||
/* Please fix
|
||||
if(SSvote.mode)
|
||||
var/static/list/supported = list(PLURALITY_VOTING, APPROVAL_VOTING)
|
||||
global_data += "Vote active!, There is currently a vote running. Question: [SSvote.question]"
|
||||
if(!(SSvote.vote_system in supported))
|
||||
global_data += "<STATPANEL VOTING DISABLED>, The current vote system is not supported by statpanel rendering. Please vote manually by opening the vote popup using the action button or chat link."
|
||||
return
|
||||
global_data += "Time Left:, [round(SSvote.end_time - world.time)] seconds"
|
||||
|
||||
global_data += "Choices:"
|
||||
for(var/i in 1 to SSvote.choice_statclicks.len)
|
||||
var/choice = SSvote.choice_statclicks[i]
|
||||
var/ivotedforthis = FALSE
|
||||
if(usr.ckey)
|
||||
switch(SSvote.vote_system)
|
||||
if(APPROVAL_VOTING)
|
||||
ivotedforthis = SSvote.voted[usr.ckey] && (i in SSvote.voted[usr.ckey])
|
||||
if(PLURALITY_VOTING)
|
||||
ivotedforthis = SSvote.voted[usr.ckey] == i
|
||||
|
||||
global_data += (ivotedforthis? "\[X\]" : "\[ \]", SSvote.choice_statclicks[choice])
|
||||
*/
|
||||
|
||||
encoded_global_data = url_encode(json_encode(global_data))
|
||||
src.currentrun = GLOB.clients.Copy()
|
||||
@@ -63,6 +41,31 @@ SUBSYSTEM_DEF(statpanels)
|
||||
var/ping_str = url_encode("Ping: [round(target.lastping, 1)]ms (Average: [round(target.avgping, 1)]ms)")
|
||||
var/other_str = url_encode(json_encode(target.mob.get_status_tab_items()))
|
||||
target << output("[encoded_global_data];[ping_str];[other_str]", "statbrowser:update")
|
||||
if(SSvote.mode)
|
||||
var/list/vote_arry = list(
|
||||
list("Vote active!", "There is currently a vote running. Question: [SSvote.question]")
|
||||
) //see the MC on how this works.
|
||||
if(!(SSvote.vote_system in list(PLURALITY_VOTING, APPROVAL_VOTING)))
|
||||
vote_arry[++vote_arry.len] += list("STATPANEL VOTING DISABLED!", "The current vote system is not supported by statpanel rendering. Please vote manually by opening the vote popup using the action button or chat link.", "disabled")
|
||||
//does not return.
|
||||
else
|
||||
vote_arry[++vote_arry.len] += list("Time Left:", " [round(SSvote.end_time - world.time)] seconds")
|
||||
vote_arry[++vote_arry.len] += list("Choices:", "")
|
||||
for(var/choice in SSvote.choice_statclicks)
|
||||
var/choice_id = SSvote.choice_statclicks[choice]
|
||||
var/ivotedforthis = FALSE
|
||||
if(target.ckey)
|
||||
switch(SSvote.vote_system)
|
||||
if(APPROVAL_VOTING)
|
||||
ivotedforthis = SSvote.voted[target.ckey] && (text2num(choice_id) in SSvote.voted[usr.ckey])
|
||||
if(PLURALITY_VOTING)
|
||||
ivotedforthis = (SSvote.voted[target.ckey] == text2num(choice_id))
|
||||
vote_arry[++vote_arry.len] += list(ivotedforthis ? "\[X\]" : "\[ \]", choice, "[REF(SSvote)];vote=[choice_id];statpannel=1")
|
||||
var/vote_str = url_encode(json_encode(vote_arry))
|
||||
target << output("[vote_str]", "statbrowser:update_voting")
|
||||
else
|
||||
var/null_bullet = url_encode(json_encode(list(list(null))))
|
||||
target << output("[null_bullet]", "statbrowser:update_voting")
|
||||
if(!target.holder)
|
||||
target << output("", "statbrowser:remove_admin_tabs")
|
||||
else
|
||||
|
||||
@@ -16,6 +16,7 @@ SUBSYSTEM_DEF(vote)
|
||||
var/question = null
|
||||
var/list/choices = list()
|
||||
/// List of choice = object for statclick objects for statpanel voting
|
||||
/// statclick rework? 2: list("name"="id")
|
||||
var/list/choice_statclicks = list()
|
||||
var/list/scores = list()
|
||||
var/list/choice_descs = list() // optional descriptions
|
||||
@@ -49,34 +50,6 @@ SUBSYSTEM_DEF(vote)
|
||||
client_popup.open(0)
|
||||
next_pop = world.time+VOTE_COOLDOWN
|
||||
|
||||
/**
|
||||
* Renders a statpanel. Directly uses statpanel/stat calls since this is called from base of mob/Stat().
|
||||
*/
|
||||
/datum/controller/subsystem/vote/proc/render_statpanel(mob/M)
|
||||
if(!mode) // check if vote is running
|
||||
return
|
||||
if(!statpanel("Status")) // don't bother if they're not focused on this panel
|
||||
return
|
||||
var/static/list/supported = list(PLURALITY_VOTING, APPROVAL_VOTING)
|
||||
stat("Vote active!", "There is currently a vote running. Question: [question]")
|
||||
if(!(vote_system in supported))
|
||||
stat("<STATPANEL VOTING DISABLED>", "The current vote system is not supported by statpanel rendering. Please vote manually by opening the vote popup using the action button or chat link.")
|
||||
return
|
||||
stat("Time Left:", "[round(end_time - world.time)] seconds")
|
||||
stat(null, null)
|
||||
stat("Choices:", null)
|
||||
stat(null, null)
|
||||
for(var/i in 1 to choice_statclicks.len)
|
||||
var/choice = choice_statclicks[i]
|
||||
var/ivotedforthis = FALSE
|
||||
switch(vote_system)
|
||||
if(APPROVAL_VOTING)
|
||||
ivotedforthis = voted[usr.ckey] && (i in voted[usr.ckey])
|
||||
if(PLURALITY_VOTING)
|
||||
ivotedforthis = voted[usr.ckey] == i
|
||||
stat(ivotedforthis? "\[X\]" : "\[ \]", choice_statclicks[choice])
|
||||
stat(null, null)
|
||||
|
||||
/datum/controller/subsystem/vote/proc/reset()
|
||||
initiator = null
|
||||
end_time = 0
|
||||
@@ -87,26 +60,10 @@ SUBSYSTEM_DEF(vote)
|
||||
voted.Cut()
|
||||
voting.Cut()
|
||||
scores.Cut()
|
||||
cleanup_statclicks()
|
||||
choice_statclicks = list()
|
||||
display_votes = initial(display_votes) //CIT CHANGE - obfuscated votes
|
||||
remove_action_buttons()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/cleanup_statclicks()
|
||||
for(var/choice in choice_statclicks)
|
||||
qdel(choice_statclicks[choice])
|
||||
choice_statclicks = list()
|
||||
|
||||
/obj/effect/statclick/vote
|
||||
name = "ERROR"
|
||||
var/choice
|
||||
|
||||
/obj/effect/statclick/vote/Click()
|
||||
SSvote.submit_vote(choice)
|
||||
|
||||
/obj/effect/statclick/vote/New(loc, choice, name)
|
||||
src.choice = choice
|
||||
src.name = name
|
||||
|
||||
/datum/controller/subsystem/vote/proc/get_result()
|
||||
//get the highest number of votes
|
||||
var/greatest_votes = 0
|
||||
@@ -582,10 +539,10 @@ SUBSYSTEM_DEF(vote)
|
||||
to_chat(world, "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=[REF(src)]'>here</a> to place your votes.\nYou have [DisplayTimeText(vp)] to vote.</font>")
|
||||
end_time = started_time+vp
|
||||
// generate statclick list
|
||||
cleanup_statclicks()
|
||||
choice_statclicks = list()
|
||||
for(var/i in 1 to choices.len)
|
||||
var/choice = choices[i]
|
||||
choice_statclicks[choice] = new /obj/effect/statclick/vote(null, i, choice)
|
||||
choice_statclicks[choice] = "[i]"
|
||||
//
|
||||
for(var/c in GLOB.clients)
|
||||
SEND_SOUND(c, sound('sound/misc/server-ready.ogg'))
|
||||
@@ -785,7 +742,8 @@ SUBSYSTEM_DEF(vote)
|
||||
submit_vote(round(text2num(href_list["vote"])),round(text2num(href_list["score"])))
|
||||
else
|
||||
submit_vote(round(text2num(href_list["vote"])))
|
||||
usr.vote()
|
||||
if(!href_list["statpannel"])
|
||||
usr.vote()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/remove_action_buttons()
|
||||
for(var/v in generated_actions)
|
||||
|
||||
Reference in New Issue
Block a user