-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
API: Spawn pools, a distributed loot manager. (#27199)
* api: Spawn pools, a distributed loot manager. * meh * documentation and cleanups * how do numbers work * word wrapping * fixes found from prototyping
- Loading branch information
1 parent
cb1e9df
commit c4e4487
Showing
8 changed files
with
420 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
code/game/objects/effects/spawners/random/pool/pool_spawner.dm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/// A random spawner managed by a [/datum/spawn_pool]. | ||
/obj/effect/spawner/random/pool | ||
icon = 'icons/effects/random_spawners.dmi' | ||
icon_state = "loot" | ||
|
||
/// How much this spawner will subtract from the available budget if it | ||
/// spawns. A value of `INFINITY` (i.e., not setting the value on a subtype) | ||
/// does not attempt to subtract from the budget. This is useful for | ||
/// spawners which themselves spawn other spawners. | ||
var/point_value = INFINITY | ||
/// Whether non-spawner items should be removed from the shared loot pool | ||
/// after spawning. | ||
var/unique_picks = FALSE | ||
/// Guaranteed spawners will always proc, and always proc first. | ||
var/guaranteed = FALSE | ||
/// The ID of the spawn pool. Must match the pool's [/datum/spawn_pool/var/id]. | ||
var/spawn_pool_id | ||
|
||
/obj/effect/spawner/random/pool/Initialize(mapload) | ||
// short-circuit atom init machinery since we won't be around long | ||
if(initialized) | ||
stack_trace("Warning: [src]([type]) initialized multiple times!") | ||
initialized = TRUE | ||
|
||
if(!spawn_pool_id) | ||
stack_trace("No spawn pool ID provided to [src]([type])") | ||
|
||
if(GLOB.spawn_pool_manager.finalized) | ||
// We've already gotten through SSlate_mapping, so someone probably spawned this manually. | ||
// Skip all the shit and just spawn it. | ||
spawn_loot() | ||
qdel(src) | ||
return | ||
|
||
var/datum/spawn_pool/pool = GLOB.spawn_pool_manager.get(spawn_pool_id) | ||
if(!pool) | ||
stack_trace("Could not find spawn pool with ID [spawn_pool_id]") | ||
|
||
if(unique_picks && !(type in pool.unique_spawners)) | ||
pool.unique_spawners[type] = loot.Copy() | ||
|
||
if(guaranteed) | ||
pool.guaranteed_spawners |= src | ||
else | ||
pool.known_spawners |= src | ||
|
||
/obj/effect/spawner/random/pool/generate_loot_list() | ||
var/datum/spawn_pool/pool = GLOB.spawn_pool_manager.get(spawn_pool_id) | ||
if(!pool) | ||
stack_trace("Could not find spawn pool with ID [spawn_pool_id]") | ||
|
||
if(unique_picks) | ||
var/list/unique_loot = pool.unique_spawners[type] | ||
return unique_loot.Copy() | ||
|
||
return ..() | ||
|
||
/obj/effect/spawner/random/pool/check_safe(type_path_to_make) | ||
// TODO: Spawners with `spawn_all_loot` set will subtract the | ||
// point value for each item spawned. This needs to change so | ||
// that the budget is only checked once initially, and then | ||
// all of the loot is spawned after. | ||
if(!..()) | ||
return FALSE | ||
|
||
var/is_safe = FALSE | ||
var/deduct_points = TRUE | ||
var/datum/spawn_pool/pool = GLOB.spawn_pool_manager.get(spawn_pool_id) | ||
if(!pool) | ||
stack_trace("Could not find spawn pool with ID [spawn_pool_id]") | ||
|
||
if(ispath(type_path_to_make, /obj/effect/spawner/random/pool)) | ||
return TRUE | ||
|
||
// If we're past SSlate_mapping, we're safe and don't have a pool | ||
// to deduct points from | ||
if(GLOB.spawn_pool_manager.finalized) | ||
is_safe = TRUE | ||
deduct_points = FALSE | ||
|
||
// If we don't have a sane point value, don't deduct points | ||
if(point_value == INFINITY) | ||
deduct_points = FALSE | ||
|
||
// If we deduct points, we need to check affordability | ||
if(deduct_points) | ||
if(pool.can_afford(point_value)) | ||
is_safe = TRUE | ||
else | ||
is_safe = TRUE | ||
|
||
// Early breakout if we're not safe | ||
if(!is_safe) | ||
return FALSE | ||
|
||
if(deduct_points) | ||
pool.consume(point_value) | ||
|
||
if(pool && unique_picks) | ||
// We may have multiple instances of a given type so just remove the first instance we find | ||
var/list/unique_spawners = pool.unique_spawners[type] | ||
unique_spawners.Remove(type_path_to_make) | ||
|
||
return TRUE |
59 changes: 59 additions & 0 deletions
59
code/game/objects/effects/spawners/random/pool/spawn_pool.dm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/// Keeps track of the available points for a given pool, as well as any | ||
/// spawners that need to keep track globally of the number of any specific item | ||
/// that they spawn. | ||
/datum/spawn_pool | ||
/// The ID of the spawn pool. All spawners registered to this pool must use this ID. | ||
var/id | ||
/// The number of points left for the spawner to use. Starts at its initial value. | ||
var/available_points = 0 | ||
/// A list of all spawners registered to this pool. | ||
var/list/known_spawners = list() | ||
/// A key-value list of spawners with TRUE `unique_picks` to a shared copy of their | ||
/// loot pool. When items from one of these spawners are spawned, it is removed | ||
/// from the shared loot pool so it never spawns again. | ||
var/list/unique_spawners = list() | ||
/// A list of spawners whose `guaranteed` is `TRUE`. These spawners will | ||
/// always spawn, and always before anything else, | ||
var/list/guaranteed_spawners = list() | ||
|
||
/datum/spawn_pool/proc/can_afford(points) | ||
if(available_points >= points) | ||
return TRUE | ||
|
||
return FALSE | ||
|
||
/datum/spawn_pool/proc/consume(points) | ||
available_points -= points | ||
|
||
/datum/spawn_pool/proc/process_guaranteed_spawners() | ||
while(length(guaranteed_spawners)) | ||
var/obj/effect/spawner/random/pool/spawner = guaranteed_spawners[length(guaranteed_spawners)] | ||
guaranteed_spawners.len-- | ||
spawner.spawn_loot() | ||
qdel(spawner) | ||
|
||
QDEL_LIST_CONTENTS(guaranteed_spawners) | ||
|
||
/datum/spawn_pool/proc/process_spawners() | ||
process_guaranteed_spawners() | ||
|
||
shuffle_inplace(known_spawners) | ||
while(length(known_spawners)) | ||
if(available_points <= 0) | ||
break | ||
|
||
var/obj/effect/spawner/random/pool/spawner = known_spawners[length(known_spawners)] | ||
known_spawners.len-- | ||
if(spawner.point_value != INFINITY && available_points < spawner.point_value) | ||
qdel(spawner) | ||
continue | ||
|
||
spawner.spawn_loot() | ||
if(length(guaranteed_spawners)) | ||
WARNING("non-guaranteed spawner [spawner.type] spawned a guaranteed spawner, this should be avoided") | ||
process_guaranteed_spawners() | ||
|
||
qdel(spawner) | ||
|
||
|
||
QDEL_LIST_CONTENTS(known_spawners) |
27 changes: 27 additions & 0 deletions
27
code/game/objects/effects/spawners/random/pool/spawn_pool_manager.dm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
GLOBAL_DATUM_INIT(spawn_pool_manager, /datum/spawn_pool_manager, new) | ||
|
||
/// The singleton which keeps track of all spawn pools. | ||
/// All known [/datum/spawn_pool] subtypes are registered | ||
/// to it and are processed in SSlate_mapping. | ||
/datum/spawn_pool_manager | ||
var/list/spawn_pools = list() | ||
var/finalized = FALSE | ||
|
||
/datum/spawn_pool_manager/New() | ||
for(var/spawn_pool_type in subtypesof(/datum/spawn_pool)) | ||
var/datum/spawn_pool/pool = new spawn_pool_type | ||
spawn_pools[pool.id] = pool | ||
|
||
/datum/spawn_pool_manager/proc/get(id) | ||
return spawn_pools[id] | ||
|
||
/datum/spawn_pool_manager/proc/process_pools() | ||
for(var/pool_id in spawn_pools) | ||
var/datum/spawn_pool/pool = spawn_pools[pool_id] | ||
pool.process_spawners() | ||
|
||
finalized = TRUE | ||
|
||
/datum/spawn_pool_manager/Destroy() | ||
QDEL_LIST_CONTENTS(spawn_pools) | ||
return ..() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.