WorldMap plumbing

This commit is contained in:
Luke Hubmayer-Werner 2023-07-27 19:38:53 +09:30
parent daf4c54cd5
commit 17fd68bbbb
4 changed files with 160 additions and 6 deletions

View File

@ -45,11 +45,118 @@ extends Node
# Probably going to shader this effect instead of storing hundreds of frames # Probably going to shader this effect instead of storing hundreds of frames
# No palette cycling # No palette cycling
enum FlagsBlockPathing {
PATHABLE_FOOT = 0x001,
PATHABLE_CHOCOBO = 0x002,
PATHABLE_BLACK_CHOCOBO = 0x004,
PATHABLE_HIRYUU = 0x008,
PATHABLE_SUBMARINE = 0x010,
PATHABLE_SHIP = 0x020,
PATHABLE_AIRSHIP = 0x040,
SURFACEABLE = 0x080,
LANDABLE_CHOCOBO = 0x100,
LANDABLE_BLACK_CHOCOBO = 0x200,
LANDABLE_HIRYUU = 0x400,
LANDABLE_AIRSHIP = 0x800,
}
class WorldMap:
const block_width := 256
const block_height := 256
const tile_width := block_width * 2
const tile_height := block_height * 2
var blockmap: PoolByteArray
var block_tile_ids: PoolByteArray
var block_pathing: PoolIntArray
func get_block_tiles(id: int) -> PoolByteArray:
var i = id * 4
return self.block_tile_ids.subarray(i, i+4)
func get_block_pathing_flags(id: int) -> int:
return block_pathing[id]
func make_tile_atlas() -> Image:
var image := Image.new()
var data := PoolByteArray()
data.resize(tile_width*tile_height)
var block_tile := 0
for y_off in range(0, tile_height*tile_width, tile_width):
for x_off in range(0, tile_width, 2):
data[y_off + x_off] = self.block_tile_ids[block_tile]
block_tile += 1
data[y_off + x_off + 1] = self.block_tile_ids[block_tile]
block_tile += 1
data[y_off + tile_width + x_off] = self.block_tile_ids[block_tile]
block_tile += 1
data[y_off + tile_width + x_off + 1] = self.block_tile_ids[block_tile]
block_tile += 1
return image
var worldmaps = [WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new()]
var worldmap_block_properties = [] var worldmap_block_properties = []
var worldmap_block_pathings = []
func load_worldmap_block_properties(rom: File): func load_worldmap_block_properties(rom: File):
rom.seek(0x0FEA00) rom.seek(0x0FEA00)
for world_ts in 3: for world_ts in 3:
var ts_properties = PoolIntArray() var ts_properties = PoolIntArray()
var ts_pathings = PoolIntArray()
for block in 0xC0: for block in 0xC0:
ts_properties.append(rom.get_16() + (rom.get_8() << 16)) var properties := rom.get_16() + (rom.get_8() << 16)
ts_properties.append(properties)
var pathings := properties >> 16 # First 8 pathable flags map directly
pathings |= (((properties >> 12) & 0xF) ^ 0xF) << 8 # Next 4 flags (can land) are taken from high bits of second byte and inverted
ts_pathings.append(pathings)
worldmap_block_properties.append(ts_properties) worldmap_block_properties.append(ts_properties)
worldmap_block_pathings.append(ts_pathings)
worldmaps[0].block_pathing = worldmap_block_pathings[0]
worldmaps[1].block_pathing = worldmap_block_pathings[1]
worldmaps[2].block_pathing = worldmap_block_pathings[0]
worldmaps[3].block_pathing = worldmap_block_pathings[2]
worldmaps[4].block_pathing = worldmap_block_pathings[2]
func load_worldmaps(rom: File):
var chunk_addresses = PoolIntArray()
chunk_addresses.resize(0x500) # 5 worldmaps * 256 chunks
rom.seek(0x0FE000)
for id in range(0, 0x434):
chunk_addresses[id] = rom.get_16() + 0x070000
for id in range(0x434, 0x500):
chunk_addresses[id] = rom.get_16() + 0x080000
for worldmap_id in 5: # Bartz World, Galuf World, Combined World, Underwater Galuf World, Underwater Combined World
# Worldmap chunks have a basic compression.
# Repeated blocks along a row are run-length-encoded (RLE)
# Certain blocks (mountains) expand to 1x3
var blockmap = PoolByteArray()
blockmap.resize(WorldMap.block_height * WorldMap.block_width)
for chunk_id in range(worldmap_id*0x100, (worldmap_id+1)*0x100):
rom.seek(chunk_addresses[chunk_id])
while blockmap.size() < 256:
var b := rom.get_8()
if b >= 0xC0: # RLE
var count := b-0xBF
var block = rom.get_8()
for i in count:
blockmap.append(block)
else:
blockmap.append(b)
if b == 0x0C or b == 0x1C or b == 0x2C:
# Mountain blocks expand to a 1x3
blockmap.append(b+1)
blockmap.append(b+2)
worldmaps[worldmap_id].blockmap = blockmap
func update_worldmap_block_tile_ids(worldmap_block_tile_ids: Array):
# Called by SpriteLoader
worldmaps[0].block_tile_ids = worldmap_block_tile_ids[0]
worldmaps[1].block_tile_ids = worldmap_block_tile_ids[1]
worldmaps[2].block_tile_ids = worldmap_block_tile_ids[0]
worldmaps[3].block_tile_ids = worldmap_block_tile_ids[2]
worldmaps[4].block_tile_ids = worldmap_block_tile_ids[2]
func load_snes_rom(rom: File):
load_worldmap_block_properties(rom)
load_worldmaps(rom)

View File

@ -10,7 +10,7 @@ func load_snes_rom(filename: String):
var error := rom_snes.open(filename, File.READ) var error := rom_snes.open(filename, File.READ)
if error == OK: if error == OK:
SpriteLoader.load_snes_rom(rom_snes) SpriteLoader.load_snes_rom(rom_snes)
MapLoader.load_worldmap_block_properties(rom_snes) MapLoader.load_snes_rom(rom_snes)
var _thread_error = thread.start(SoundLoader, 'parse_rom', rom_snes) var _thread_error = thread.start(SoundLoader, 'parse_rom', rom_snes)
func _ready(): func _ready():

View File

@ -33,7 +33,9 @@ const offset_Tiles_Fist := 0x11D710 # 3bpp tile
# Arrays to store sprites in # Arrays to store sprites in
var worldmap_palette_imgs = [] var worldmap_palette_imgs = []
var worldmap_palette_textures = [] var worldmap_palette_textures = []
var worldmap_tile_imgs = [] var worldmap_tile_individual_imgs = []
var worldmap_tile_atlas_textures = []
var worldmap_block_tile_ids = []
var worldmap_block_individual_imgs = [] var worldmap_block_individual_imgs = []
var worldmap_block_individual_textures = [] var worldmap_block_individual_textures = []
var worldmap_block_atlas_imgs = [] var worldmap_block_atlas_imgs = []
@ -193,6 +195,18 @@ func snes_get_tile(rom: File, offset: int, length: int) -> Image:
return snes_1plane_to_tile(data) return snes_1plane_to_tile(data)
func make_tile_atlas(tile_images) -> Image:
var r := Rect2(0, 0, 8, 8)
var image = Image.new()
image.create(256, 256, false, Image.FORMAT_R8)
var tile = 0
for y in tile_images.size()/16:
for x in 16:
image.blit_rect(tile_images[tile], r, Vector2(x, y)*8)
tile += 1
return image
func snes_load_worldmap(rom: File): func snes_load_worldmap(rom: File):
# Load Worldmap Graphics # Load Worldmap Graphics
var worldmap_tile_counts = [256, 256, 128] # Only 128 underwater tiles var worldmap_tile_counts = [256, 256, 128] # Only 128 underwater tiles
@ -215,24 +229,29 @@ func snes_load_worldmap(rom: File):
image = snes_mode7_compressed_to_tile(tiledata, tile_palettes[tile]) image = snes_mode7_compressed_to_tile(tiledata, tile_palettes[tile])
tile_images.append(image) tile_images.append(image)
# tile_textures.append(texture_from_image(image)) # tile_textures.append(texture_from_image(image))
worldmap_tile_imgs.append(tile_images) worldmap_tile_individual_imgs.append(tile_images)
worldmap_tile_atlas_textures.append(texture_from_image(make_tile_atlas(tile_images)))
# Block definitions # Block definitions
var block_images = [] var block_images = []
var block_textures = [] var block_textures = []
var block_bank_start: int = offset_worldmap_blocks + (world_ts*0x300) var block_bank_start: int = offset_worldmap_blocks + (world_ts*0x300)
var block_tile_ids := PoolByteArray()
for block in 0xC0: # 192 blocks per world tileset for block in 0xC0: # 192 blocks per world tileset
image = Image.new() image = Image.new()
image.create(16, 16, false, Image.FORMAT_R8) image.create(16, 16, false, Image.FORMAT_R8)
for tile in 4: for tile in 4:
rom.seek(block_bank_start + block + (tile * 0xC0)) # Horrible interleaving scheme rom.seek(block_bank_start + block + (tile * 0xC0)) # Horrible interleaving scheme
var src_idx := rom.get_8() var src_idx := rom.get_8()
block_tile_ids.append(src_idx)
if src_idx < tile_count: if src_idx < tile_count:
image.blit_rect(tile_images[src_idx], Rect2(0, 0, 8, 8), Vector2((tile%2)*8, (tile/2)*8)) image.blit_rect(tile_images[src_idx], Rect2(0, 0, 8, 8), Vector2((tile%2)*8, (tile/2)*8))
block_images.append(image) block_images.append(image)
block_textures.append(texture_from_image(image)) block_textures.append(texture_from_image(image))
worldmap_block_individual_imgs.append(block_images) worldmap_block_individual_imgs.append(block_images)
worldmap_block_individual_textures.append(block_textures) worldmap_block_individual_textures.append(block_textures)
worldmap_block_tile_ids.append(block_tile_ids)
# Make block atlas # Make block atlas
image = Image.new() image = Image.new()
image.create(16*16, 16*12, false, Image.FORMAT_R8) image.create(16*16, 16*12, false, Image.FORMAT_R8)
@ -248,8 +267,8 @@ func snes_load_worldmap(rom: File):
# image.blit_rect(tile_images[tile], Rect2(0, 0, 8, 8), Vector2((tile%16)*8, (tile/16)*8)) # image.blit_rect(tile_images[tile], Rect2(0, 0, 8, 8), Vector2((tile%16)*8, (tile/16)*8))
# worldmap_tile_atlas_textures.append(texture_from_image(image)) # worldmap_tile_atlas_textures.append(texture_from_image(image))
for world_map in 5: # Bartz World, Galuf World, Combined World, Underwater Galuf World, Underwater Combined World MapLoader.update_worldmap_block_tile_ids(worldmap_block_tile_ids)
pass
func load_snes_rom(rom: File): func load_snes_rom(rom: File):
# Load Battle sprites # Load Battle sprites

View File

@ -0,0 +1,28 @@
shader_type canvas_item;
uniform sampler2D tiles;
uniform sampler2D palette;
// uniform float tile_width = 8.0;
uniform float tilemap_width = 256.0; // Require square tilemap for now
const float index_scale = 255.0 / 16.0;
// This shader maps from tileID texels to Tiles, and then applies palette.
// tiles hardcoded to 16x16 tiles for now
// palette hardcoded to 16x16 colors for now
void fragment() {
// GLES2
float tile_idx = texture(TEXTURE, UV).a * index_scale;
vec2 tile_uv = vec2(fract(tile_idx), trunc(tile_idx) / 16.0);
vec2 sub_tile_uv = fract(UV / tilemap_width);
// TODO: add sea HScroll, waterfall VScroll tile UV modulation
// TODO: move cycling palette to a sampler2DArray or sampler3D rather than rebinding
float color_idx = texture(tiles, tile_uv + (sub_tile_uv/16.0)).a * index_scale;
float palette_uv = vec2(fract(color_idx), trunc(color_idx) / 16.0);
COLOR = texture(palette, palette_uv);
if (color_idx == 0.0)
COLOR.a = 0.0;
}