mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-01-01 13:12:23 +00:00
158 lines
5.6 KiB
Plaintext
158 lines
5.6 KiB
Plaintext
/**
|
|
* Base class for all random spawners.
|
|
*/
|
|
/obj/effect/spawner/random
|
|
icon = 'icons/effects/spawner_icons.dmi'
|
|
icon_state = "loot"
|
|
layer = OBJ_LAYER
|
|
/// Stops persistent lootdrop spawns from being shoved into lockers
|
|
anchored = TRUE
|
|
/// A list of possible items to spawn e.g. list(/obj/item, /obj/structure, /obj/effect)
|
|
var/list/loot
|
|
/// The subtypes AND type to combine with the loot list
|
|
var/loot_type_path
|
|
/// The subtypes (this excludes the provided path) to combine with the loot list
|
|
var/loot_subtype_path
|
|
/// How many items will be spawned
|
|
var/spawn_loot_count = 1
|
|
/// If the same item can be spawned twice
|
|
var/spawn_loot_double = TRUE
|
|
/// Whether the items should be distributed to offsets 0,1,-1,2,-2,3,-3.. This overrides pixel_x/y on the spawner itself
|
|
var/spawn_loot_split = FALSE
|
|
/// The pixel x/y divider offsets for spawn_loot_split (spaced 1 pixel apart by default)
|
|
var/spawn_loot_split_pixel_offsets = 2
|
|
/// Whether the spawner should spawn all the loot in the list
|
|
var/spawn_all_loot = FALSE
|
|
/// The chance for the spawner to create loot (ignores spawn_loot_count)
|
|
var/spawn_loot_chance = 100
|
|
/// Determines how big of a range (in tiles) we should scatter things in.
|
|
var/spawn_scatter_radius = 0
|
|
/// Whether the items should have a random pixel_x/y offset (maximum offset distance is ± spawn_random_offset_max_pixels for x/y)
|
|
var/spawn_random_offset = FALSE
|
|
/// Maximum offset distance for random pixel offsets.
|
|
var/spawn_random_offset_max_pixels = 16
|
|
/// Whether the spawned items should be rotated randomly.
|
|
var/spawn_random_angle = FALSE
|
|
/// Whether blackbox should record when the spawner spawns.
|
|
var/record_spawn = FALSE
|
|
/// Where do we want to spawn an item (closet, safe etc.)
|
|
var/spawn_inside
|
|
|
|
// Brief explanation:
|
|
// Rather then setting up and then deleting spawners, we block all atomlike setup
|
|
// and do the absolute bare minimum
|
|
// This is with the intent of optimizing mapload
|
|
// TODO: Bring this optimization up one level if possible
|
|
/obj/effect/spawner/random/Initialize(mapload)
|
|
SHOULD_CALL_PARENT(FALSE)
|
|
if(initialized)
|
|
stack_trace("Warning: [src]([type]) initialized multiple times!")
|
|
initialized = TRUE
|
|
spawn_loot()
|
|
return INITIALIZE_HINT_QDEL
|
|
|
|
///If the spawner has any loot defined, randomly picks some and spawns it. Does not cleanup the spawner.
|
|
/obj/effect/spawner/random/proc/spawn_loot(lootcount_override)
|
|
if(!prob(spawn_loot_chance))
|
|
return
|
|
|
|
var/list/spawn_locations = get_spawn_locations(spawn_scatter_radius)
|
|
var/spawn_loot_count = isnull(lootcount_override) ? src.spawn_loot_count : lootcount_override
|
|
|
|
if(spawn_inside)
|
|
new spawn_inside(loc)
|
|
|
|
if(spawn_all_loot)
|
|
spawn_loot_count = INFINITY
|
|
spawn_loot_double = FALSE
|
|
|
|
if(loot_type_path)
|
|
loot += typesof(loot_type_path)
|
|
|
|
if(loot_subtype_path)
|
|
loot += subtypesof(loot_subtype_path)
|
|
|
|
if(length(loot))
|
|
var/loot_spawned = 0
|
|
var/pixel_divider = FLOOR(spawn_random_offset_max_pixels / spawn_loot_split_pixel_offsets, 1)
|
|
while((spawn_loot_count-loot_spawned) && length(loot))
|
|
loot_spawned++
|
|
|
|
var/lootspawn = pick_weight_recursive(loot)
|
|
if(!spawn_loot_double)
|
|
loot.Remove(lootspawn)
|
|
if(lootspawn)
|
|
var/turf/spawn_loc = loc
|
|
if(spawn_scatter_radius > 0 && length(spawn_locations))
|
|
spawn_loc = pick(spawn_locations)
|
|
|
|
if(ispath(lootspawn, /turf))
|
|
spawn_loc.ChangeTurf(lootspawn)
|
|
continue
|
|
|
|
var/atom/movable/spawned_loot = make_item(spawn_loc, lootspawn)
|
|
spawned_loot.setDir(dir)
|
|
|
|
if(!spawn_loot_split && !spawn_random_offset)
|
|
if(pixel_x != 0)
|
|
spawned_loot.pixel_x = pixel_x
|
|
if(pixel_y != 0)
|
|
spawned_loot.pixel_y = pixel_y
|
|
else if(spawn_random_offset)
|
|
spawned_loot.pixel_x = rand(-spawn_random_offset_max_pixels, spawn_random_offset_max_pixels)
|
|
spawned_loot.pixel_y = rand(-spawn_random_offset_max_pixels, spawn_random_offset_max_pixels)
|
|
else if(spawn_loot_split)
|
|
if(loot_spawned)
|
|
var/column = FLOOR(loot_spawned / pixel_divider, 1)
|
|
spawned_loot.pixel_x = spawn_loot_split_pixel_offsets * (loot_spawned % pixel_divider) + (column * spawn_loot_split_pixel_offsets)
|
|
spawned_loot.pixel_y = spawn_loot_split_pixel_offsets * (loot_spawned % pixel_divider)
|
|
|
|
/**
|
|
* Makes the actual item related to our spawner. If `record_spawn` is `TRUE`,
|
|
* this is when the items spawned are recorded to blackbox (except for `/obj/effect`s).
|
|
*
|
|
* spawn_loc - where are we spawning it?
|
|
* type_path_to_make - what are we spawning?
|
|
**/
|
|
/obj/effect/spawner/random/proc/make_item(spawn_loc, type_path_to_make)
|
|
var/result = new type_path_to_make(spawn_loc)
|
|
|
|
if(record_spawn)
|
|
record_item(type_path_to_make)
|
|
|
|
var/atom/item = result
|
|
if(spawn_random_angle && istype(item))
|
|
item.transform = turn(item.transform, rand(0, 360))
|
|
|
|
return result
|
|
|
|
/obj/effect/spawner/random/proc/record_item(type_path_to_make)
|
|
if(ispath(type_path_to_make, /obj/effect))
|
|
return
|
|
|
|
SSblackbox.record_feedback("tally", "random_spawners", 1, "[type_path_to_make]")
|
|
|
|
///If the spawner has a spawn_scatter_radius set, this creates a list of nearby turfs available that are in view and have an unblocked line to them.
|
|
/obj/effect/spawner/random/proc/get_spawn_locations(radius)
|
|
var/list/scatter_locations = list()
|
|
|
|
if(!radius)
|
|
return scatter_locations
|
|
|
|
for(var/turf/turf_in_view in view(radius, get_turf(src)))
|
|
if(!isfloorturf(turf_in_view))
|
|
continue
|
|
if(!has_unblocked_line(turf_in_view))
|
|
continue
|
|
|
|
scatter_locations += turf_in_view
|
|
|
|
return scatter_locations
|
|
|
|
/obj/effect/spawner/random/proc/has_unblocked_line(destination)
|
|
for(var/turf/potential_blockage as anything in get_line(get_turf(src), destination))
|
|
if(!is_blocked_turf(potential_blockage, exclude_mobs = TRUE))
|
|
continue
|
|
return FALSE
|
|
return TRUE
|