Files
Polaris/code/modules/maps/tg/map_template.dm
Neerti f429a176e4 Adds Map Template Groups
Adds ability to have many different 'versions' of a PoI, yet only having one show up per round.
An example on how to do this is provided with Radio Free Sif (aka DJOutpost).
Just make sure all the different versions have the same 'template_group' value.
2018-03-30 05:34:11 -04:00

288 lines
10 KiB
Plaintext

var/list/global/map_templates = list()
// Called when the world starts, in world.dm
/proc/load_map_templates()
for(var/T in subtypesof(/datum/map_template))
var/datum/map_template/template = T
if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
continue
template = new T()
map_templates[template.name] = template
return TRUE
/datum/map_template
var/name = "Default Template Name"
var/desc = "Some text should go here. Maybe."
var/template_group = null // If this is set, no more than one template in the same group will be spawned, per submap seeding.
var/width = 0
var/height = 0
var/mappath = null
var/loaded = 0 // Times loaded this round
var/annihilate = FALSE // If true, all (movable) atoms at the location where the map is loaded will be deleted before the map is loaded in.
var/cost = null // The map generator has a set 'budget' it spends to place down different submaps. It will pick available submaps randomly until \
it runs out. The cost of a submap should roughly corrispond with several factors such as size, loot, difficulty, desired scarcity, etc. \
Set to -1 to force the submap to always be made.
var/allow_duplicates = FALSE // If false, only one map template will be spawned by the game. Doesn't affect admins spawning then manually.
var/discard_prob = 0 // If non-zero, there is a chance that the map seeding algorithm will skip this template when selecting potential templates to use.
var/static/dmm_suite/maploader = new
/datum/map_template/New(path = null, rename = null)
if(path)
mappath = path
if(mappath)
spawn(1)
preload_size(mappath)
if(rename)
name = rename
/datum/map_template/proc/preload_size(path)
var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE)
if(bounds)
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
height = bounds[MAP_MAXY]
return bounds
/datum/map_template/proc/initTemplateBounds(var/list/bounds)
if (SSatoms.initialized == INITIALIZATION_INSSATOMS)
return // let proper initialisation handle it later
var/list/atom/atoms = list()
var/list/area/areas = list()
var/list/obj/structure/cable/cables = list()
var/list/obj/machinery/atmospherics/atmos_machines = list()
var/list/turf/turfs = block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]),
locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ]))
for(var/L in turfs)
var/turf/B = L
atoms += B
areas |= B.loc
for(var/A in B)
atoms += A
if(istype(A, /obj/structure/cable))
cables += A
else if(istype(A, /obj/machinery/atmospherics))
atmos_machines += A
atoms |= areas
admin_notice("<span class='danger'>Initializing newly created atom(s) in submap.</span>", R_DEBUG)
SSatoms.InitializeAtoms(atoms)
admin_notice("<span class='danger'>Initializing atmos pipenets and machinery in submap.</span>", R_DEBUG)
SSmachines.setup_atmos_machinery(atmos_machines)
admin_notice("<span class='danger'>Rebuilding powernets due to submap creation.</span>", R_DEBUG)
SSmachines.setup_powernets_for_cables(cables)
// Ensure all machines in loaded areas get notified of power status
for(var/I in areas)
var/area/A = I
A.power_change()
admin_notice("<span class='danger'>Submap initializations finished.</span>", R_DEBUG)
/datum/map_template/proc/load_new_z(var/centered = FALSE)
var/x = 1
var/y = 1
if(centered)
x = round((world.maxx - width)/2)
y = round((world.maxy - height)/2)
var/list/bounds = maploader.load_map(file(mappath), x, y, no_changeturf = TRUE)
if(!bounds)
return FALSE
// repopulate_sorted_areas()
//initialize things that are normally initialized after map load
initTemplateBounds(bounds)
log_game("Z-level [name] loaded at at [x],[y],[world.maxz]")
return TRUE
/datum/map_template/proc/load(turf/T, centered = FALSE)
var/old_T = T
if(centered)
T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
if(!T)
return
if(T.x+width > world.maxx)
return
if(T.y+height > world.maxy)
return
if(annihilate)
annihilate_bounds(old_T, centered)
var/list/bounds = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE)
if(!bounds)
return
// if(!SSmapping.loading_ruins) //Will be done manually during mapping ss init
// repopulate_sorted_areas()
//initialize things that are normally initialized after map load
initTemplateBounds(bounds)
log_game("[name] loaded at at [T.x],[T.y],[T.z]")
loaded++
return TRUE
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
var/turf/placement = T
if(centered)
var/turf/corner = locate(placement.x - round(width/2), placement.y - round(height/2), placement.z)
if(corner)
placement = corner
return block(placement, locate(placement.x+width-1, placement.y+height-1, placement.z))
/datum/map_template/proc/annihilate_bounds(turf/origin, centered = FALSE)
var/deleted_atoms = 0
admin_notice("<span class='danger'>Annihilating objects in submap loading locatation.</span>", R_DEBUG)
var/list/turfs_to_clean = get_affected_turfs(origin, centered)
if(turfs_to_clean.len)
for(var/turf/T in turfs_to_clean)
for(var/atom/movable/AM in T)
++deleted_atoms
qdel(AM)
admin_notice("<span class='danger'>Annihilated [deleted_atoms] objects.</span>", R_DEBUG)
//for your ever biggening badminnery kevinz000
//❤ - Cyberboss
/proc/load_new_z_level(var/file, var/name)
var/datum/map_template/template = new(file, name)
template.load_new_z()
// Very similar to the /tg/ version.
/proc/seed_submaps(var/list/z_levels, var/budget = 0, var/whitelist = /area/space, var/desired_map_template_type = null)
set background = TRUE
if(!z_levels || !z_levels.len)
admin_notice("seed_submaps() was not given any Z-levels.", R_DEBUG)
return
for(var/zl in z_levels)
var/turf/T = locate(1, 1, zl)
if(!T)
admin_notice("Z level [zl] does not exist - Not generating submaps", R_DEBUG)
return
var/overall_sanity = 100 // If the proc fails to place a submap more than this, the whole thing aborts.
var/list/potential_submaps = list() // Submaps we may or may not place.
var/list/priority_submaps = list() // Submaps that will always be placed.
// Lets go find some submaps to make.
for(var/map in map_templates)
var/datum/map_template/MT = map_templates[map]
if(!MT.allow_duplicates && MT.loaded > 0) // This probably won't be an issue but we might as well.
continue
if(!istype(MT, desired_map_template_type)) // Not the type wanted.
continue
if(MT.discard_prob && prob(MT.discard_prob))
continue
if(MT.cost && MT.cost < 0) // Negative costs always get spawned.
priority_submaps += MT
else
potential_submaps += MT
CHECK_TICK
var/list/loaded_submap_names = list()
var/list/template_groups_used = list() // Used to avoid spawning three seperate versions of the same PoI.
// Now lets start choosing some.
while(budget > 0 && overall_sanity > 0)
overall_sanity--
var/datum/map_template/chosen_template = null
if(potential_submaps.len)
if(priority_submaps.len) // Do these first.
chosen_template = pick(priority_submaps)
else
chosen_template = pick(potential_submaps)
else // We're out of submaps.
admin_notice("Submap loader had no submaps to pick from with [budget] left to spend.", R_DEBUG)
break
CHECK_TICK
// Can we afford it?
if(chosen_template.cost > budget)
priority_submaps -= chosen_template
potential_submaps -= chosen_template
continue
// Did we already place down a very similar submap?
if(chosen_template.template_group && chosen_template.template_group in template_groups_used)
priority_submaps -= chosen_template
potential_submaps -= chosen_template
continue
// If so, try to place it.
var/specific_sanity = 100 // A hundred chances to place the chosen submap.
while(specific_sanity > 0)
specific_sanity--
var/width_border = TRANSITIONEDGE + SUBMAP_MAP_EDGE_PAD + round(chosen_template.width / 2)
var/height_border = TRANSITIONEDGE + SUBMAP_MAP_EDGE_PAD + round(chosen_template.height / 2)
var/z_level = pick(z_levels)
var/turf/T = locate(rand(width_border, world.maxx - width_border), rand(height_border, world.maxy - height_border), z_level)
var/valid = TRUE
for(var/turf/check in chosen_template.get_affected_turfs(T,1))
var/area/new_area = get_area(check)
if(!(istype(new_area, whitelist)))
valid = FALSE // Probably overlapping something important.
// world << "Invalid due to overlapping with area [new_area.type], when wanting area [whitelist]."
break
CHECK_TICK
CHECK_TICK
if(!valid)
continue
admin_notice("Submap \"[chosen_template.name]\" placed at ([T.x], [T.y], [T.z])", R_DEBUG)
// Do loading here.
chosen_template.load(T, centered = TRUE) // This is run before the main map's initialization routine, so that can initilize our submaps for us instead.
CHECK_TICK
// For pretty maploading statistics.
if(loaded_submap_names[chosen_template.name])
loaded_submap_names[chosen_template.name] += 1
else
loaded_submap_names[chosen_template.name] = 1
// To avoid two 'related' similar submaps existing at the same time.
if(chosen_template.template_group)
template_groups_used += chosen_template.template_group
// To deduct the cost.
if(chosen_template.cost >= 0)
budget -= chosen_template.cost
// Remove the submap from our options.
if(chosen_template in priority_submaps) // Always remove priority submaps.
priority_submaps -= chosen_template
else if(!chosen_template.allow_duplicates)
potential_submaps -= chosen_template
break // Load the next submap.
var/list/pretty_submap_list = list()
for(var/submap_name in loaded_submap_names)
var/count = loaded_submap_names[submap_name]
if(count > 1)
pretty_submap_list += "[count] <b>[submap_name]</b>"
else
pretty_submap_list += "<b>[submap_name]</b>"
if(!overall_sanity)
admin_notice("Submap loader gave up with [budget] left to spend.", R_DEBUG)
else
admin_notice("Submaps loaded.", R_DEBUG)
admin_notice("Loaded: [english_list(pretty_submap_list)]", R_DEBUG)