a_pleasant_stroll/world.lua

202 lines
5.9 KiB
Lua
Raw Normal View History

--- Procedural generation methods, and other aspects of generating the
--- environment
2019-12-04 23:48:04 +00:00
function init_world()
2019-12-07 21:30:41 +00:00
-- Metadata for different biomes
-- tile_frequencies tuples are {frequency, sprite_index}, see index_map.md
-- frequencies by convention add up to 1000, but this is arbitrary, the
-- only concern is the space consumed by the resulting tables.
2019-12-07 21:30:41 +00:00
biome_data = {
meadow = {
biome_frequency = 20,
tile_frequencies = { {525, 2}, {200, 5}, {200, 6}, {55, 18}, {19, 3}, {1, 4} }
},
2019-12-07 21:30:41 +00:00
grassland = {
biome_frequency = 55,
tile_frequencies = { {500, 2}, {345, 18}, {4, 3}, {1, 4}, {100, 5}, {50, 6} }
2019-12-07 21:30:41 +00:00
},
forest = {
biome_frequency = 20,
tile_frequencies = { {600, 2}, {200, 4}, {50, 3}, {50, 5}, {40, 7}, {59, 6}, {1, 8} }
2019-12-07 21:30:41 +00:00
},
desert = {
biome_frequency = 5,
tile_frequencies = { {800, 9}, {109, 11}, {60, 13}, {30, 12}, {1, 10} },
2019-12-07 21:30:41 +00:00
}
}
2019-12-09 02:40:00 +00:00
-- Why is this hard-coded separately from the biome_data? glad you asked.
-- Lua's pairs() function appears not to guarantee a consistent return order,
-- and we want our world to be deterministically generated,
-- so the biome_metadata array needs to have its entries appear consistently.
biome_list = {"grassland", "meadow", "forest", "desert"}
2019-12-09 02:40:00 +00:00
2019-12-07 21:30:41 +00:00
-- this is the frequency list for the biomes themselves
biome_metadata = {}
2019-12-09 02:40:00 +00:00
for i=1,#biome_list do
local biome = biome_list[i]
2019-12-07 21:30:41 +00:00
-- add the biome's name N times to the biome metadata 'hat'
for i=1,biome_data[biome].biome_frequency do
2019-12-09 02:40:00 +00:00
add(biome_metadata, biome)
2019-12-07 21:30:41 +00:00
end
2019-12-09 02:40:00 +00:00
build_biome(biome, biome_data[biome])
2019-12-04 23:48:04 +00:00
end
-- the indices here are sprite numbers.
object_interaction_map = {
-- bush
[3] = {
replacement = 17,
sfx = 13,
drop = 68
},
-- tree
[4] = {
replacement = 14,
sfx = 11,
drop = 64
},
-- big mushroom
[8] = {
replacement = 16,
sfx = 12,
drop = 65
},
-- cactus w/ flower
[10] = {
replacement = 15,
sfx = 12,
drop = 67
},
-- cactus
[13] = {
replacement = 15,
sfx = 12,
drop = 66
}
}
-- initialize a ring buffer of changed positions. In use, this will be keyed
-- using strings of the form mod_buffer["x:y"], using absolute world
-- coordinates. this is to flatten the buffer so that #mod_cache is useful
-- for checking against the number of allowed entries
mod_buffer = {}
mod_queue = {}
end
-- draw the sprites for this part of the world to the screen
-- this calculates everything about the world fresh every frame,
-- but pico-8 handles this just fine!
function draw_world(start_x, start_y)
for x=0,15 do
for y=0,15 do
spr(get_tile(start_x-8+x, start_y-8+y), x*8, y*8)
end
end
2019-12-07 21:30:41 +00:00
end
2019-12-04 23:48:04 +00:00
2019-12-07 21:30:41 +00:00
-- build the lookup table for a given biome, based on the biome_meta data for
-- that string.
function build_biome(biome_name, data)
local meta_frequencies = data.tile_frequencies
2019-12-07 21:30:41 +00:00
local tile_lookup = {}
for i=1,#meta_frequencies do
local tuple = meta_frequencies[i]
2019-12-04 23:48:04 +00:00
for j=1,tuple[1] do
2019-12-07 21:30:41 +00:00
add(tile_lookup, tuple[2])
2019-12-04 23:48:04 +00:00
end
end
2019-12-07 21:30:41 +00:00
data.tile_lookup = tile_lookup
2019-12-04 23:48:04 +00:00
end
-- generates a unique identifier for a position
-- uses srand() and rand() to get an unpredictable value (is this too slow?)
-- the two seed values are randomly chosen. Change them and change the world.
function generate_uid(pos_x, pos_y)
srand((pos_x + 2229) * (pos_y + 12295))
return flr(rnd(0xffff))
2019-12-04 23:48:04 +00:00
end
2019-12-09 02:40:00 +00:00
-- given an {x,y} position, calculates the aligned starting position for the
-- biome that position is in.
-- biomes are currently defined to be 128x128
function calculate_biome_pos(pos_x, pos_y)
return flr(pos_x / 128), flr(pos_y / 128)
2019-12-09 02:40:00 +00:00
end
2019-12-07 21:30:41 +00:00
-- determines which biome a given world map position should be,
-- returns the object out of the biome_data table
function get_biome_name(pos_x, pos_y)
local biome_pos_x, biome_pos_y = calculate_biome_pos(pos_x, pos_y)
local uid = generate_uid(biome_pos_x, biome_pos_y)
return biome_metadata[(uid % #biome_metadata) + 1]
2019-12-07 21:30:41 +00:00
end
-- determine what sprite to render for a given position.
-- pos_x and pos_y are global coordinates.
function get_tile(pos_x, pos_y)
-- lookup changes in the change buffer
local modded_sprite = mod_buffer[get_mod_key(pos_x, pos_y)]
if (modded_sprite) return modded_sprite
local biome_name = get_biome_name(pos_x, pos_y)
local biome = biome_data[biome_name]
local uid = generate_uid(pos_x, pos_y)
2019-12-07 21:30:41 +00:00
return biome.tile_lookup[(uid % #biome.tile_lookup) + 1]
2019-12-04 23:48:04 +00:00
end
---
--- mod buffer functions - these handle locations on the world map that have
--- changed from their 'default' state
---
-- x and y are global coords
function get_mod_key(x, y)
return tostr(x) .. ":" .. tostr(y)
end
-- x and y are map-local coords
function write_map_change(new_sprite, x, y, perm)
local key = get_mod_key(x, y)
mod_buffer[key] = new_sprite
-- the queue gives us a time-ordered list of items to delete.
-- anything that should persist is simply not added to the queue,
-- making it un-deletable. It also doesn't count against the maximum
-- size of the mod queue before deletion, meaning it permanently inflates
-- the size of ram.
-- obviously if we end up with a *very large number* of persistent
-- objects we can run into trouble, but this is functionally a design
-- limitation. We also only save 32 of these on exit, further limiting
-- the possible size of the 'memory leak' pool, as it were.
if not perm then
add(mod_queue, key)
end
if #mod_queue >= 8192 then
cull_mod_buffer()
end
end
function cull_mod_buffer()
-- we cull 512 entries at a time.
local count = 0
for i=1,511 do
local key = mod_queue[1]
if (not key) return -- check that we're not out of items for some reason
mod_buffer[key] = nil
del(mod_queue, key)
end
2019-12-04 23:48:04 +00:00
end