Refactored random map generator system and added several terrain generators.

Created a global list to track base turfs for explosions/shuttle moves.
Remaps the asteroid to be a moonlet. Tidies up some references to 'asteroid', removes moonbase from the accessible z level list.
This commit is contained in:
Zuhayr
2015-06-03 04:36:19 +09:30
parent d4327658ab
commit ebe62cefd8
47 changed files with 1453 additions and 1175 deletions

View File

@@ -1,173 +1,206 @@
#define ORE_COUNT 1000
/*
This module is used to generate the debris fields/distribution maps/procedural stations.
*/
// Generates cave systems for the asteroid, and places ore tiles.
var/global/list/random_maps = list()
var/global/list/map_count = list()
/datum/random_map
var/descriptor = "asteroid" // Display name.
var/real_size = 246 // Size of each edge (must be square :().
var/cell_range = 2 // Random range for initial cells.
var/iterations = 5 // Number of times to apply the automata rule.
// Strings.
var/name // Set in New()
var/descriptor = "random map" // Display name.
// Locator/value vars.
var/initial_wall_cell = 100 // % Chance that a cell will be seeded as a wall.
var/max_attempts = 5 // Fail if a sane map isn't generated by this point.
var/raw_map_size // Used for creating new maps each iteration. Value must be real_size^2
var/list/map = list() // Actual map.
var/origin_x = 1 // Origin point, left.
var/origin_y = 1 // Origin point, bottom.
var/origin_z = 1 // Target Z-level.
var/limit_x = 256 // Maximum x bound.
var/limit_y = 256 // Maximum y bound.
var/iterate_before_fail = 120 // Infinite loop safeguard.
var/limit_x = 128 // Default x size.
var/limit_y = 128 // Default y size.
var/auto_apply = 1
// Turf paths.
var/wall_type = /turf/simulated/wall
var/floor_type = /turf/simulated/floor
var/target_turf_type
// Storage for the final iteration of the map.
var/list/map = list() // Actual map.
// If set, all sleep(-1) calls will be skipped.
// Test to see if rand_seed() can be used reliably.
var/priority_process
/datum/random_map/New(var/seed, var/tx, var/ty, var/tz, var/tlx, var/tly, var/do_not_apply, var/do_not_announce)
// Store this for debugging.
if(!map_count[descriptor])
map_count[descriptor] = 1
else
map_count[descriptor]++
name = "[descriptor] #[map_count[descriptor]]"
random_maps[name] = src
// Get origins for applying the map later.
origin_x = (!isnull(tx) ? tx : 1)
origin_y = (!isnull(ty) ? ty : 1)
origin_z = (!isnull(tz) ? tz : 1)
if(tlx) limit_x = tlx
if(tly) limit_y = tly
if(do_not_apply)
auto_apply = null
// Initialize map.
set_map_size()
var/start_time = world.timeofday
if(!do_not_announce) admin_notice("<span class='danger'>Generating [name].</span>", R_DEBUG)
sleep(-1)
// Testing needed to see how reliable this is (asynchronous calls, called during worldgen), DM ref is not optimistic
if(seed)
rand_seed(seed)
priority_process = 1
for(var/i = 0;i<max_attempts;i++)
if(generate())
if(!do_not_announce) admin_notice("<span class='danger'>[capitalize(name)] generation completed in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</span>", R_DEBUG)
return
if(!do_not_announce) admin_notice("<span class='danger'>[capitalize(name)] failed to generate ([round(0.1*(world.timeofday-start_time),0.1)] seconds): could not produce sane map.</span>", R_DEBUG)
/datum/random_map/proc/get_map_cell(var/x,var/y)
return ((y-1)*real_size)+x
var/cell = ((y-1)*limit_x)+x
if((cell < 1) || (cell > map.len))
return null
else
return cell
/datum/random_map/proc/get_map_char(var/value)
switch(value)
if(WALL_CHAR)
return "#"
if(FLOOR_CHAR)
return "."
if(DOOR_CHAR)
return "D"
if(ROOM_TEMP_CHAR)
return "+"
if(MONSTER_CHAR)
return "M"
if(ARTIFACT_TURF_CHAR)
return "_"
if(ARTIFACT_CHAR)
return "A"
else
return " "
/datum/random_map/proc/display_map(atom/user)
if(!user)
user = world
for(var/x = 1, x <= real_size, x++)
var/line = ""
for(var/y = 1, y <= real_size, y++)
var/dat = "<code>+------+<br>"
for(var/x = 1, x <= limit_x, x++)
for(var/y = 1, y <= limit_y, y++)
var/current_cell = get_map_cell(x,y)
if(within_bounds(current_cell))
if(map[current_cell] == 2)
line += "#"
else
line += "."
user << line
/datum/random_map/New(var/seed, var/tx, var/ty, var/tz, var/tlx, var/tly)
// Store this for debugging.
random_maps |= src
// Initialize map.
set_map_size()
// Get origins for applying the map later.
if(tx) origin_x = tx
if(ty) origin_y = ty
if(tz) origin_z = tz
if(tlx) limit_x = tlx
if(tly) limit_y = tly
// testing needed to see how reliable this is (asynchronous calls, called during worldgen), DM ref is not optimistic
if(seed) rand_seed(seed)
var/start_time = world.timeofday
admin_notice("<span class='danger'>Generating [descriptor].</span>", R_DEBUG)
for(var/i = 0;i<max_attempts;i++)
if(generate())
admin_notice("<span class='danger'>[capitalize(descriptor)] generation completed in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</span>", R_DEBUG)
return
admin_notice("<span class='danger'>[capitalize(descriptor)] generation failed in [round(0.1*(world.timeofday-start_time),0.1)] seconds: could not produce sane map.</span>", R_DEBUG)
dat += get_map_char(map[current_cell])
dat += "<br>"
user << "[dat]+------+</code>"
/datum/random_map/proc/within_bounds(var/val)
return (val>0) && (val<=raw_map_size)
if(!islist(map))
set_map_size()
return (val>0) && (val<=map.len)
/datum/random_map/proc/set_map_size(var/raw_size)
if(!raw_size)
raw_size = real_size * real_size
raw_map_size = raw_size
map.len = raw_map_size
/datum/random_map/proc/set_map_size()
map = list()
map.len = limit_x * limit_y
/datum/random_map/proc/seed_map()
for(var/x = 1, x <= real_size, x++)
for(var/y = 1, y <= real_size, y++)
for(var/x = 1, x <= limit_x, x++)
for(var/y = 1, y <= limit_y, y++)
var/current_cell = get_map_cell(x,y)
if(prob(55))
map[current_cell] = 2
if(prob(initial_wall_cell))
map[current_cell] = WALL_CHAR
else
map[current_cell] = 1
map[current_cell] = FLOOR_CHAR
/datum/random_map/proc/clear_map()
for(var/x = 1, x <= real_size, x++)
for(var/y = 1, y <= real_size, y++)
for(var/x = 1, x <= limit_x, x++)
for(var/y = 1, y <= limit_y, y++)
map[get_map_cell(x,y)] = 0
/datum/random_map/proc/generate()
seed_map()
for(var/i=1;i<=iterations;i++)
iterate(i)
generate_map()
if(check_map_sanity())
cleanup()
apply_to_map()
if(auto_apply)
apply_to_map()
return 1
return 0
/datum/random_map/proc/iterate(var/iteration)
var/list/next_map[raw_map_size]
for(var/x = 1, x <= real_size, x++)
for(var/y = 1, y <= real_size, y++)
var/current_cell = get_map_cell(x,y)
// Sanity check.
if(!within_bounds(current_cell))
continue
// Copy over original value.
next_map[current_cell] = map[current_cell]
// Check all neighbors.
var/count = 0
for(var/cell in list(current_cell,get_map_cell(x+1,y+1),get_map_cell(x-1,y-1),get_map_cell(x+1,y-1),get_map_cell(x-1,y+1),get_map_cell(x-1,y),get_map_cell(x,y-1),get_map_cell(x+1,y),get_map_cell(x,y+1)))
if(within_bounds(cell) && map[cell] == 2)
count++
if(count>=5)
next_map[current_cell] = 2 // becomes a wall
else
next_map[current_cell] = 1 // becomes a floor
map = next_map
// Unused for basic map.
/datum/random_map/proc/generate_map()
return 1
/datum/random_map/proc/check_map_sanity()
return 1
/datum/random_map/proc/apply_to_map()
for(var/x = 0, x < real_size, x++)
if((origin_x + x) > limit_x) continue
for(var/y = 0, y < real_size, y++)
if((origin_y + y) > limit_y) continue
sleep(-1)
apply_to_turf(origin_x+x,origin_y+y)
/datum/random_map/proc/apply_to_map(var/tx, var/ty, var/tz)
if(!tx) tx = isnull(origin_x) ? 1 : origin_x
if(!ty) ty = isnull(origin_y) ? 1 : origin_y
if(!tz) tz = isnull(origin_z) ? 1 : origin_z
/datum/random_map/proc/apply_to_turf(var/x,var/y)
for(var/x = 1, x <= limit_x, x++)
for(var/y = 1, y <= limit_y, y++)
if(!priority_process) sleep(-1)
apply_to_turf((tx-1)+x,(ty-1)+y,tz)
/datum/random_map/proc/apply_to_turf(var/x,var/y,var/z)
var/current_cell = get_map_cell(x,y)
if(!within_bounds(current_cell))
return
var/turf/T = locate(x,y,origin_z)
if(!T || !istype(T,/turf/unsimulated/mask))
return
switch(map[current_cell])
if(1)
T.ChangeTurf(/turf/simulated/floor/plating/airless/asteroid)
if(2)
T.ChangeTurf(/turf/simulated/mineral)
if(3)
T.ChangeTurf(/turf/simulated/mineral/random)
if(4)
T.ChangeTurf(/turf/simulated/mineral/random/high_chance)
return 0
var/turf/T = locate(x,y,z)
if(!T || (target_turf_type && !istype(T,target_turf_type)))
return 0
var/newpath = get_appropriate_path(map[current_cell])
if(newpath)
T.ChangeTurf(newpath)
get_additional_spawns(map[current_cell],T)
return T
/datum/random_map/proc/get_appropriate_path(var/value)
switch(value)
if(FLOOR_CHAR)
return floor_type
if(WALL_CHAR)
return wall_type
/datum/random_map/proc/get_additional_spawns(var/value, var/turf/T)
if(value == DOOR_CHAR)
new /obj/machinery/door/airlock(T)
/datum/random_map/proc/cleanup()
return
sleep(-1)
// Create ore.
var/ore_count = ORE_COUNT
while(ore_count)
var/check_cell = get_map_cell(rand(1,real_size),rand(1,real_size))
if(!(within_bounds(check_cell)) || map[check_cell] != 2)
continue
if(prob(25))
map[check_cell] = 4
else
map[check_cell] = 3
ore_count--
/datum/random_map/proc/overlay_with(var/datum/random_map/target_map, var/tx, var/ty)
if(!map.len || !istype(target_map))
return
tx-- // Update origin so that x/y index
ty-- // doesn't push it off-kilter by one.
for(var/x = 1, x <= limit_x, x++)
for(var/y = 1, y <= limit_y, y++)
var/current_cell = get_map_cell(x,y)
if(!within_bounds(current_cell))
continue
if(tx+x > target_map.limit_x)
continue
if(ty+y > target_map.limit_y)
continue
target_map.map[target_map.get_map_cell(tx+x,ty+y)] = map[current_cell]
handle_post_overlay_on(target_map,tx,ty)
sleep(-1)
// Place random asteroid rooms.
var/rooms_placed = 0
for(var/i = 0, i < max_secret_rooms, i++)
if(make_mining_asteroid_secret())
rooms_placed++
admin_notice("<span class='danger'>Placed [rooms_placed] secrets.</span>", R_DEBUG)
return 1
/datum/random_map/proc/handle_post_overlay_on(var/datum/random_map/target_map, var/tx, var/ty)
return