mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-04-25 01:21:27 +01:00
210 lines
6.8 KiB
Plaintext
210 lines
6.8 KiB
Plaintext
#define DEFAULT_PADDING 32
|
|
|
|
/datum/ruin_placement
|
|
var/datum/map_template/ruin/ruin
|
|
var/base_padding
|
|
var/padding
|
|
|
|
/datum/ruin_placement/New(datum/map_template/ruin/ruin_, padding_ = DEFAULT_PADDING, base_padding_ = 0)
|
|
. = ..()
|
|
ruin = ruin_
|
|
base_padding = base_padding_
|
|
padding = padding_
|
|
|
|
/datum/ruin_placement/proc/reduce_padding()
|
|
padding = max(floor(padding / 2) - 1, -1)
|
|
|
|
/datum/ruin_placement/proc/try_to_place(zlist_or_zlevel, area_whitelist)
|
|
var/list/z_levels = islist(zlist_or_zlevel) ? zlist_or_zlevel : list(zlist_or_zlevel)
|
|
shuffle_inplace(z_levels)
|
|
|
|
// Our goal is to maximize padding, so we'll perform some number of attempts
|
|
// on one z-level, then the next, until we reach some limit, then reduce the
|
|
// padding and start again.
|
|
padding = DEFAULT_PADDING
|
|
while(padding >= 0)
|
|
var/width_border = base_padding + round(ruin.width / 2) + padding
|
|
var/height_border = base_padding + round(ruin.height / 2) + padding
|
|
|
|
for(var/z_level in z_levels)
|
|
var/blocked_on_level = FALSE
|
|
var/datum/space_level/current_level = GLOB.space_manager.get_zlev(z_level)
|
|
for(var/blocked_ruin_id in ruin.never_spawn_on_the_same_level)
|
|
if(blocked_on_level)
|
|
break
|
|
for(var/ruin_id in current_level.our_ruin_list)
|
|
if(blocked_ruin_id == ruin_id)
|
|
blocked_on_level = TRUE
|
|
break
|
|
if(blocked_on_level)
|
|
continue
|
|
|
|
var/placement_tries = PLACEMENT_TRIES
|
|
while(placement_tries > 0)
|
|
CHECK_TICK
|
|
|
|
placement_tries--
|
|
|
|
var/turf/central_turf = locate(
|
|
rand(width_border, world.maxx - width_border),
|
|
rand(height_border, world.maxy - height_border),
|
|
z_level
|
|
)
|
|
var/valid = TRUE
|
|
|
|
if(!central_turf)
|
|
continue
|
|
|
|
// Expand the original bounds of the ruin with our padding and call
|
|
// that our list of affected turfs.
|
|
var/list/bounds = ruin.get_coordinate_bounds(central_turf, centered = TRUE)
|
|
var/datum/coords/bottom_left = bounds["bottom_left"]
|
|
var/datum/coords/top_right = bounds["top_right"]
|
|
bottom_left.x_pos -= padding
|
|
bottom_left.y_pos -= padding
|
|
top_right.x_pos += padding
|
|
top_right.y_pos += padding
|
|
|
|
// One sanity check just in case
|
|
if(!ruin.fits_in_map_bounds(central_turf, centered = TRUE))
|
|
valid = FALSE
|
|
|
|
for(var/turf/check in block(bottom_left.x_pos, bottom_left.y_pos, z_level, top_right.x_pos, top_right.y_pos, z_level))
|
|
var/area/new_area = get_area(check)
|
|
if(!(istype(new_area, area_whitelist)) || check.flags & NO_RUINS)
|
|
valid = FALSE
|
|
break
|
|
|
|
if(!valid)
|
|
continue
|
|
|
|
var/loaded = ruin.load(central_turf, centered = TRUE)
|
|
if(!loaded)
|
|
stack_trace("ruin [ruin.suffix] failed to load at [COORD(central_turf)] after valid bounds check")
|
|
for(var/turf/T in ruin.get_affected_turfs(central_turf, centered = TRUE)) // Just flag the actual ruin turfs!
|
|
T.flags |= NO_RUINS
|
|
new /obj/effect/landmark/ruin(central_turf, ruin)
|
|
ruin.loaded++
|
|
current_level.our_ruin_list += ruin.id
|
|
|
|
log_world("Ruin \"[ruin.name]\" placed at ([central_turf.x], [central_turf.y], [central_turf.z])")
|
|
|
|
var/map_filename = splittext(ruin.mappath, "/")
|
|
map_filename = map_filename[length(map_filename)]
|
|
SSblackbox.record_feedback("associative", "ruin_placement", 1, list(
|
|
"map" = map_filename,
|
|
"coords" = "[central_turf.x],[central_turf.y],[central_turf.z]"
|
|
))
|
|
|
|
return TRUE
|
|
|
|
// Ran out of placement tries for this z-level/padding, move to the next z-level
|
|
|
|
// Ran out of z-levels to try with this padding, cut it and start again
|
|
reduce_padding()
|
|
|
|
// Ran out of z-levels, we got nowhere to place it
|
|
return FALSE
|
|
|
|
/datum/ruin_placer
|
|
var/ruin_budget
|
|
var/area_whitelist
|
|
var/list/templates
|
|
var/base_padding
|
|
|
|
/datum/ruin_placer/proc/place_ruins(z_levels)
|
|
if(!z_levels || !length(z_levels))
|
|
WARNING("No Z levels provided - Not generating ruins")
|
|
return
|
|
|
|
for(var/zl in z_levels)
|
|
var/turf/T = locate(1, 1, zl)
|
|
if(!T)
|
|
WARNING("Z level [zl] does not exist - Not generating ruins")
|
|
return
|
|
|
|
var/list/ruins = templates.Copy()
|
|
|
|
var/list/forced_ruins = list() // ruins we are required to place
|
|
var/list/ruins_available = list() // ruins we will attempt to place based on budget
|
|
|
|
// Set up the starting ruin lists
|
|
for(var/key in ruins)
|
|
var/datum/map_template/ruin/R = ruins[key]
|
|
if(R.always_place)
|
|
forced_ruins += R
|
|
continue
|
|
if(R.unpickable)
|
|
continue
|
|
if(R.get_cost() > ruin_budget) // Why would you do that
|
|
continue
|
|
ruins_available[R] = R.placement_weight
|
|
|
|
forced_ruins = sortTim(forced_ruins, GLOBAL_PROC_REF(cmp_ruin_placement_cost))
|
|
while(length(forced_ruins))
|
|
var/datum/map_template/ruin/ruin = forced_ruins[length(forced_ruins)]
|
|
var/datum/ruin_placement/placement = new(ruin, base_padding_ = base_padding)
|
|
var/placement_success = placement.try_to_place(z_levels, area_whitelist)
|
|
if(placement_success)
|
|
// this may push us into the negative but always_place means always_place
|
|
ruin_budget -= ruin.get_cost()
|
|
else
|
|
stack_trace("failed to place required ruin [ruin.suffix]")
|
|
|
|
forced_ruins.len--
|
|
CHECK_TICK
|
|
|
|
while(ruin_budget > 0 && length(ruins_available))
|
|
var/datum/map_template/ruin/current_pick = pickweight(ruins_available)
|
|
var/datum/ruin_placement/placement = new(current_pick, base_padding_ = base_padding)
|
|
var/placement_success = placement.try_to_place(z_levels, area_whitelist)
|
|
|
|
if(placement_success)
|
|
ruin_budget -= current_pick.get_cost()
|
|
if(!current_pick.allow_duplicates)
|
|
for(var/datum/map_template/ruin/R in ruins_available)
|
|
if(R.id == current_pick.id)
|
|
ruins_available -= R
|
|
if(current_pick.never_spawn_with)
|
|
for(var/blacklisted_type in current_pick.never_spawn_with)
|
|
for(var/possible_exclusion in ruins_available)
|
|
if(istype(possible_exclusion,blacklisted_type))
|
|
ruins_available -= possible_exclusion
|
|
else
|
|
for(var/datum/map_template/ruin/R in ruins_available)
|
|
if(R.id == current_pick.id)
|
|
ruins_available -= R
|
|
log_debug("failed ruin placement `[current_pick.suffix]` length(z_levels)=[length(z_levels)] budget=[ruin_budget]")
|
|
|
|
//Update the available list
|
|
for(var/datum/map_template/ruin/R in ruins_available)
|
|
if(R.get_cost() > ruin_budget)
|
|
ruins_available -= R
|
|
|
|
CHECK_TICK
|
|
|
|
log_world("Ruin loader finished with [ruin_budget] left to spend.")
|
|
|
|
#undef DEFAULT_PADDING
|
|
|
|
/datum/ruin_placer/space
|
|
area_whitelist = /area/space
|
|
base_padding = TRANSITIONEDGE + SPACERUIN_MAP_EDGE_PAD
|
|
|
|
/datum/ruin_placer/space/New()
|
|
ruin_budget = rand(
|
|
GLOB.configuration.ruins.space_ruin_budget_min,
|
|
GLOB.configuration.ruins.space_ruin_budget_max
|
|
)
|
|
templates = GLOB.space_ruins_templates
|
|
|
|
/datum/ruin_placer/lavaland
|
|
area_whitelist = /area/lavaland/surface/outdoors/unexplored
|
|
|
|
/datum/ruin_placer/lavaland/New()
|
|
ruin_budget = rand(
|
|
GLOB.configuration.ruins.lavaland_ruin_budget_min,
|
|
GLOB.configuration.ruins.lavaland_ruin_budget_max
|
|
)
|
|
templates = GLOB.lava_ruins_templates
|