614 lines
25 KiB
GDScript
614 lines
25 KiB
GDScript
extends Node
|
|
# Portability for GLES2 vs GLES3 stuff
|
|
const INDEX_FORMAT := globals.INDEX_FORMAT
|
|
|
|
const snes_graphics := preload('res://scripts/loaders/snes/graphics.gd')
|
|
const snes_battle_bgs := preload('res://scripts/loaders/snes/battle_bgs.gd')
|
|
const gba_graphics := preload('res://scripts/loaders/gba/graphics.gd')
|
|
var shader_material = load('res://palette_mat.tres')
|
|
const TILE_RECT := Rect2(0, 0, 8, 8)
|
|
const GLYPH_RECT := Rect2(0, 0, 16, 12)
|
|
|
|
# Offsets
|
|
|
|
# Glyphs
|
|
const offset_glyphs_small := 0x1FF000 # 0x100 entries, 2bpp
|
|
const offset_glyphs_dialog := 0x03E800 # 0x100 entries, 1bpp
|
|
const offset_glyphs_kanji := 0x1BD000 # 0x1AA entries, 1bpp
|
|
|
|
# Images
|
|
const offset_title_ffv := 0x031243 # Type 02 headed compression, expands to 0x1000 bytes
|
|
const offset_title_dragon := 0x032D22 # Type 02 compression, expands to 0x1200 bytes
|
|
const offset_title_dragon_OAM := 0x033342 # Type 02 headed compression, expands to 0x0120 bytes
|
|
const num_Character_Battle_Sprite_Layouts := 11
|
|
|
|
# Arrays to store sprites in
|
|
var worldmap_palette_imgs = []
|
|
var worldmap_palette_textures = []
|
|
var worldmap_tile_individual_imgs = []
|
|
var worldmap_tile_atlas_textures = []
|
|
var worldmap_block_tile_ids = []
|
|
var worldmap_block_individual_imgs = []
|
|
var worldmap_block_individual_textures = []
|
|
var worldmap_block_atlas_imgs = []
|
|
var worldmap_block_atlas_textures = []
|
|
# var worldmap_tile_atlas_textures = []
|
|
|
|
#var character_battle_sprite_tiles = []
|
|
#var character_battle_sprite_palette_imgs = []
|
|
var character_battle_sprite_palette_textures = []
|
|
var character_battle_sprite_palette_disabled_texture: ImageTexture
|
|
var character_battle_sprite_palette_stone_texture: ImageTexture
|
|
var weapon_textures = {}
|
|
var font_atlas_image: Image
|
|
var font_atlas_texture: ImageTexture
|
|
var font: BitmapFont
|
|
|
|
var character_status_palette_replacements = {
|
|
'poison': {4: '$7EDB = #DEB5FF', 8: '$4DD3 = #9C739C'}, # Also kneel
|
|
'zombie': {3: '$7FFF = #FFFFFF', 4: '$3AF5 = #ADBD73', 8: '$3210 = #848463'}, # Also kneel in menu
|
|
# Darkness None
|
|
'old': {5: '$5294 = #A5A5A5', 9: '$4210 = #848484'},
|
|
# Sleep None, Paralyze None, Charm None
|
|
'berserk': {4: '$013F = #FF4A00', 8: '$001F = #FF0000'},
|
|
# Mute None, Image None
|
|
'wall': {1: '$6A60 = #009CD6'}, # Strobe between normal ($1084 = #212121) and this aqua at 30Hz (2 frames each)
|
|
'armor': {1: '$031F = #FFC600'}, # Strobe between normal ($1084 = #212121) and this orange at 30Hz (2 frames each)
|
|
'shell': {1: '$0B64 = #21DE10'}, # Strobe between normal ($1084 = #212121) and this green 30Hz (2 frames each)
|
|
'stop': {1: '$001A = #D60000'}, # Strobe between normal ($1084 = #212121) and this aqua at 30Hz (2 frames each)
|
|
'haste': {1: '$017F = #FF5A00'}, # Strobe between normal ($1084 = #212121) and this orange at 30Hz (2 frames each)
|
|
'slow': {1: '$7FFF = #FFFFFF'}, # Strobe between normal ($1084 = #212121) and this green 30Hz (2 frames each)
|
|
}
|
|
|
|
static func ByteArray(size: int) -> PoolByteArray:
|
|
var arr := PoolByteArray()
|
|
arr.resize(size)
|
|
return arr
|
|
|
|
static func texture_from_image(image: Image, flags: int = 0) -> ImageTexture:
|
|
var tex = ImageTexture.new()
|
|
tex.create_from_image(image, flags)
|
|
return tex
|
|
|
|
static func bgr555_to_color(short: int) -> Color:
|
|
var color = Color()
|
|
color.a = 1
|
|
color.r = ((short & 0x1F) / 31.0)
|
|
color.g = (((short >> 5) & 0x1F) / 31.0)
|
|
color.b = (((short >> 10) & 0x1F) / 31.0)
|
|
return color
|
|
|
|
static func generate_palette_rgbf(rom: File, offset: int, length: int = 16) -> Image:
|
|
rom.seek(offset)
|
|
var img := Image.new()
|
|
var rows := length/16
|
|
img.create(16, rows, false, Image.FORMAT_RGBF)
|
|
img.lock()
|
|
for y in rows:
|
|
for x in 16:
|
|
var color = bgr555_to_color(rom.get_16())
|
|
img.set_pixel(x, y, color)
|
|
img.unlock()
|
|
return img
|
|
|
|
static func generate_palette_rgb8(rom: File, offset: int, length: int = 16) -> Image:
|
|
# Safe for GLES2 only!
|
|
# Implicit sRGB -> linear conversion on ImageTexture creation from RGB8 Image ruins this in GLES3 mode
|
|
rom.seek(offset)
|
|
var data = ByteArray(length*3)
|
|
# img.lock()
|
|
for i in length:
|
|
var color := bgr555_to_color(rom.get_16())
|
|
var j: int = i*3
|
|
data[j] = color.r8
|
|
data[j+1] = color.g8
|
|
data[j+2] = color.b8
|
|
var img := Image.new()
|
|
img.create_from_data(16, length/16, false, Image.FORMAT_RGB8, data)
|
|
return img
|
|
|
|
static func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image:
|
|
var data = ByteArray(length*2)
|
|
rom.seek(offset)
|
|
for i in range(length):
|
|
var bgr555 := rom.get_16()
|
|
var r := bgr555 & 0x1F
|
|
var g := (bgr555 >> 5) & 0x1F
|
|
var b := (bgr555 >> 10) & 0x1F
|
|
var rgb5_a1 := (r << 11) | (g << 6) | (b << 1) | 1
|
|
data[i*2] = rgb5_a1 & 0xFF
|
|
data[(i*2)+1] = rgb5_a1 >> 8
|
|
var img := Image.new()
|
|
img.create_from_data(16, length/16, false, Image.FORMAT_RGBA5551, data)
|
|
return img
|
|
|
|
static func generate_palette(rom: File, offset: int, length: int = 16) -> Image:
|
|
if length < 16:
|
|
length = 16
|
|
return generate_palette_rgb8(rom, offset, length)
|
|
|
|
static func generate_palette_from_colorarray(colors: PoolColorArray, format:=Image.FORMAT_RGBF) -> Image:
|
|
var img := Image.new()
|
|
var rows := len(colors)/16
|
|
img.create(16, rows, false, format)
|
|
img.lock()
|
|
var i := 0
|
|
for y in rows:
|
|
for x in 16:
|
|
img.set_pixel(x, y, colors[i])
|
|
i += 1
|
|
img.unlock()
|
|
return img
|
|
|
|
static func make_tile_atlas(tile_images) -> Image:
|
|
var image = Image.new()
|
|
image.create(128, 128, false, INDEX_FORMAT)
|
|
for tile in len(tile_images):
|
|
image.blit_rect(tile_images[tile], TILE_RECT, Vector2((tile%16)*8, (tile/16)*8))
|
|
return image
|
|
|
|
static func make_font_glyph_atlas(small_glyph_indexed_images, dialog_glyph_images, kanji_glyph_images) -> Image:
|
|
# Pack 256 small glyphs, 256 dialog glyphs, 426 kanji glyphs into a 512x512 atlas
|
|
var atlas = Image.new()
|
|
atlas.create(512, 512, false, Image.FORMAT_LA8)
|
|
for i in 256:
|
|
# First, convert indexed to LA8
|
|
var indexed: Image = small_glyph_indexed_images[i]
|
|
var data := PoolByteArray()
|
|
for pixel in indexed.get_data():
|
|
match pixel:
|
|
0:
|
|
data.append(0)
|
|
data.append(0xFF)
|
|
1:
|
|
data.append(0)
|
|
data.append(0)
|
|
2:
|
|
data.append(0x80)
|
|
data.append(0xFF)
|
|
3:
|
|
data.append(0xFF)
|
|
data.append(0xFF)
|
|
var tile = Image.new()
|
|
tile.create_from_data(8, 8, false, Image.FORMAT_LA8, data)
|
|
atlas.blit_rect(tile, TILE_RECT, Vector2((i%64)*8, (i/64)*8))
|
|
for i in 256:
|
|
atlas.blit_rect(dialog_glyph_images[i], GLYPH_RECT, Vector2((i%32)*16, (i/32)*12 + 32))
|
|
for i in 426:
|
|
atlas.blit_rect(kanji_glyph_images[i], GLYPH_RECT, Vector2((i%32)*16, (i/32)*12 + 128))
|
|
return atlas
|
|
|
|
# func snes_load_worldmap(rom: File):
|
|
# # Load Worldmap Graphics
|
|
# var worldmap_tile_counts = [256, 256, 128] # Only 128 underwater tiles
|
|
# for world_ts in 3: # Bartz/Combined World, Galuf World, Underwater (world tilesets, not to be confused with the 5 world maps)
|
|
# var tile_count: int = worldmap_tile_counts[world_ts]
|
|
# var image := generate_palette(rom, Common.SNES_PSX_addresses['worldmap_palettes']['SNES'] + (world_ts*0x100), 0x100)
|
|
# worldmap_palette_imgs.append(image)
|
|
# worldmap_palette_textures.append(texture_from_image(image))
|
|
|
|
# var tile_palettes = []
|
|
# rom.seek(Common.SNES_PSX_addresses['worldmap_tiles.bias']['SNES'] + (world_ts*0x100))
|
|
# for pal in 256:
|
|
# tile_palettes.append(rom.get_8())
|
|
|
|
# var tile_images = []
|
|
# # var tile_textures = []
|
|
# rom.seek(Common.SNES_PSX_addresses['worldmap_tiles']['SNES'] + (world_ts*0x2000))
|
|
# for tile in tile_count:
|
|
# var tiledata := rom.get_buffer(32)
|
|
# image = snes_graphics.mode7_compressed_to_tile(tiledata, tile_palettes[tile])
|
|
# tile_images.append(image)
|
|
# # tile_textures.append(texture_from_image(image))
|
|
# if world_ts == 0: # Waterfall hack: lay it out vertically, pushing out dummy tiles
|
|
# tile_images[0x97] = tile_images[0x88]
|
|
# tile_images[0x98] = tile_images[0x87]
|
|
# worldmap_tile_individual_imgs.append(tile_images)
|
|
# worldmap_tile_atlas_textures.append(texture_from_image(make_tile_atlas(tile_images)))
|
|
|
|
|
|
# # Block definitions
|
|
# var block_images = []
|
|
# var block_textures = []
|
|
# var block_bank_start: int = Common.SNES_PSX_addresses['worldmap_blocks']['SNES'] + (world_ts*0x300)
|
|
# var block_tile_ids := PoolByteArray()
|
|
# for block in 0xC0: # 192 blocks per world tileset
|
|
# image = Image.new()
|
|
# image.create(16, 16, false, INDEX_FORMAT)
|
|
# for tile in 4:
|
|
# rom.seek(block_bank_start + block + (tile * 0xC0)) # Horrible interleaving scheme
|
|
# var src_idx := rom.get_8()
|
|
# block_tile_ids.append(src_idx)
|
|
# if src_idx < tile_count:
|
|
# image.blit_rect(tile_images[src_idx], Rect2(0, 0, 8, 8), Vector2((tile%2)*8, (tile/2)*8))
|
|
# block_images.append(image)
|
|
# block_textures.append(texture_from_image(image))
|
|
# worldmap_block_individual_imgs.append(block_images)
|
|
# worldmap_block_individual_textures.append(block_textures)
|
|
# worldmap_block_tile_ids.append(block_tile_ids)
|
|
# # Make block atlas
|
|
# image = Image.new()
|
|
# image.create(16*16, 16*12, false, INDEX_FORMAT)
|
|
# for block in 0xC0:
|
|
# image.blit_rect(block_images[block], Rect2(0, 0, 16, 16), Vector2((block%16)*16, (block/16)*16))
|
|
# worldmap_block_atlas_imgs.append(image)
|
|
# worldmap_block_atlas_textures.append(texture_from_image(image))
|
|
|
|
# # # DEBUG: Make tile atlas
|
|
# # image = Image.new()
|
|
# # image.create(16*8, (tile_count/16)*8, false, INDEX_FORMAT)
|
|
# # for tile in tile_count:
|
|
# # 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))
|
|
|
|
# MapLoader.update_worldmap_block_tile_ids(worldmap_block_tile_ids)
|
|
|
|
|
|
# func snes_load_battle_sprites(rom: File):
|
|
# # Load Battle sprites
|
|
# rom.seek(Common.SNES_PSX_addresses['character_battle_sprite_layouts']['SNES'])
|
|
# var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6)
|
|
# # Character Battle Sprite Tiles
|
|
# for strip in range(0, 22*5):
|
|
# var tiles = []
|
|
# for i in range(0, 32*48, 32):
|
|
# tiles.append(snes_graphics.get_tile(rom, Common.SNES_PSX_addresses['character_battle_sprite_tiles']['SNES'] + (strip*32*48) + i, 32))
|
|
# var strip_image = Image.new()
|
|
# strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT)
|
|
# for i in range(6 * num_Character_Battle_Sprite_Layouts):
|
|
# strip_image.blit_rect(tiles[battle_strip_layouts[i]], Rect2(0, 0, 8, 8), Vector2((i%2) * 8, (i/2) * 8))
|
|
# strip_images.append(strip_image)
|
|
# strip_textures.append(texture_from_image(strip_image))
|
|
|
|
# # Character Battle Sprite Palettes
|
|
# for palette in range(0, 22*5):
|
|
# character_battle_sprite_palette_textures.append(texture_from_image(generate_palette(rom, Common.SNES_PSX_addresses['character_battle_sprite_palettes']['SNES'] + (palette*32))))
|
|
# character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette(rom, Common.SNES_PSX_addresses['character_battle_sprite_disabled_palette']['SNES']))
|
|
# character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, Common.SNES_PSX_addresses['character_battle_sprite_stone_palette']['SNES']))
|
|
|
|
# weapon_textures['Fist'] = texture_from_image(snes_graphics.get_tile(rom, Common.SNES_PSX_addresses['tiles_fist']['SNES'], 24))
|
|
|
|
|
|
# var json_sprite_blocks: Dictionary = Common.load_json('res://data/sprite_blocks.json') # This needs error handling later
|
|
# var sprite_blocks := {}
|
|
# func snes_load_map_sprites(rom: File):
|
|
# # Main palettes
|
|
# var map_palettes = []
|
|
# for i in 4:
|
|
# map_palettes.append(generate_palette(rom, 0x1FFC00 + 32*i))
|
|
|
|
# for k in json_sprite_blocks.keys():
|
|
# var v = json_sprite_blocks[k]
|
|
# var start: int = v['start'].hex_to_int()
|
|
# var bpp := int(v['bpp'])
|
|
# var definitions = v['definitions']
|
|
# var total_tiles := 0
|
|
# for defn in definitions:
|
|
# total_tiles += defn[0] * defn[1] * defn[2]
|
|
# print_debug('Processing sprite block "%s" - starts at $%06X with %d %dbpp tiles' % [k, start, total_tiles, bpp])
|
|
# rom.seek(start)
|
|
# var tiles := []
|
|
# match bpp:
|
|
# 1:
|
|
# for i in total_tiles:
|
|
# tiles.append(snes_graphics._1plane_to_tile(rom.get_buffer(8)))
|
|
# 2:
|
|
# for i in total_tiles:
|
|
# tiles.append(snes_graphics._2plane_to_tile(rom.get_buffer(16)))
|
|
# 3:
|
|
# for i in total_tiles:
|
|
# tiles.append(snes_graphics._3plane_to_tile(rom.get_buffer(24)))
|
|
# 4:
|
|
# for i in total_tiles:
|
|
# tiles.append(snes_graphics._4plane_to_tile(rom.get_buffer(32)))
|
|
# _:
|
|
# print_debug('Invalid bpp "%s" in sprite blocks json' % bpp)
|
|
# sprite_blocks[k] = tiles
|
|
|
|
# func load_snes_rom(rom: File):
|
|
# snes_load_battle_sprites(rom)
|
|
# snes_load_worldmap(rom)
|
|
# # snes_load_map_sprites(rom)
|
|
|
|
# Cache these as they are shared between monsters
|
|
var monster_battle_sprite_palettes := {} # offset -> 16 colour palette image
|
|
var monster_battle_sprite_palette_textures := {} # offset -> 16 colour palette texture
|
|
var monster_battle_sprite_images := {} # "tiles_offset:layout_id:large:bpp" -> tiles laid out in image
|
|
var monster_battle_sprite_textures := {} # "tiles_offset:layout_id:large:bpp" -> tiles laid out in texture
|
|
func load_enemy_battle_sprites(data: Dictionary, buffer: StreamPeerBuffer):
|
|
var PAL: int = Common.SNES_PSX_addresses.enemy_battle_sprite_palettes.SNES
|
|
var TILES: int = Common.SNES_PSX_addresses.enemy_battle_sprite_tiles.SNES
|
|
var Palette16Of555 = RomLoader.structdefs.Palette16Of555
|
|
data.monster_battle_sprites = []
|
|
for monster in data.enemy_battle_sprite_data:
|
|
var entry = {}
|
|
# Fetch or generate+cache palette
|
|
var palette_offset = (monster.palette_offset_hi << 12) + (monster.palette_offset_lo << 4)
|
|
if not (palette_offset in monster_battle_sprite_palettes):
|
|
buffer.seek(palette_offset + PAL)
|
|
var colors = PoolColorArray()
|
|
for i in 16:
|
|
colors.append(snes_graphics.bgr555_to_color(buffer.get_u16()))
|
|
var image := generate_palette_from_colorarray(colors)
|
|
monster_battle_sprite_palettes[palette_offset] = image
|
|
monster_battle_sprite_palette_textures[palette_offset] = texture_from_image(image)
|
|
entry['palette'] = monster_battle_sprite_palette_textures[palette_offset]
|
|
# Fetch or generate+cache tiles and layout
|
|
var bpp := 3 if monster.is_3bpp else 4
|
|
var tiles_offset = (monster.tileset_offset_hi << 11) + (monster.tileset_offset_lo << 3)
|
|
var key: String
|
|
if monster.is_large_layout:
|
|
var layout = data.enemy_battle_sprite_layouts_large[monster.layout_id]
|
|
key = '%04X:%02X:1:%d' % [tiles_offset, monster.layout_id, bpp]
|
|
if not (key in monster_battle_sprite_images):
|
|
buffer.seek(tiles_offset + TILES)
|
|
var image := Image.new()
|
|
image.create(128, 128, false, INDEX_FORMAT)
|
|
var max_col := 0
|
|
var max_row := 0
|
|
if bpp == 3:
|
|
for y in 16:
|
|
for x in 16:
|
|
if (layout[y] & (0x8000 >> x)):
|
|
image.blit_rect(snes_graphics._3plane_to_tile(buffer.get_data(24)[1]), TILE_RECT, Vector2(x, y)*8)
|
|
if max_row < y:
|
|
max_row = y
|
|
if max_col < x:
|
|
max_col = x
|
|
else:
|
|
for y in 16:
|
|
for x in 16:
|
|
if (layout[y] & (0x8000 >> x)):
|
|
image.blit_rect(snes_graphics._4plane_to_tile(buffer.get_data(32)[1]), TILE_RECT, Vector2(x, y)*8)
|
|
if max_row < y:
|
|
max_row = y
|
|
if max_col < x:
|
|
max_col = x
|
|
image.crop(8*(max_col+1), 8*(max_row+1))
|
|
monster_battle_sprite_images[key] = image
|
|
monster_battle_sprite_textures[key] = texture_from_image(image)
|
|
else:
|
|
var layout = data.enemy_battle_sprite_layouts_small[monster.layout_id]
|
|
key = '%04X:%02X:0:%d' % [tiles_offset, monster.layout_id, bpp]
|
|
if not (key in monster_battle_sprite_images):
|
|
buffer.seek(tiles_offset + TILES)
|
|
var image := Image.new()
|
|
image.create(64, 64, false, INDEX_FORMAT)
|
|
var max_col := 0
|
|
var max_row := 0
|
|
if bpp == 3:
|
|
for y in 8:
|
|
for x in 8:
|
|
if (layout[y] & (0x80 >> x)):
|
|
image.blit_rect(snes_graphics._3plane_to_tile(buffer.get_data(24)[1]), TILE_RECT, Vector2(x, y)*8)
|
|
if max_row < y:
|
|
max_row = y
|
|
if max_col < x:
|
|
max_col = x
|
|
else:
|
|
for y in 8:
|
|
for x in 8:
|
|
if (layout[y] & (0x80 >> x)):
|
|
image.blit_rect(snes_graphics._4plane_to_tile(buffer.get_data(32)[1]), TILE_RECT, Vector2(x, y)*8)
|
|
if max_row < y:
|
|
max_row = y
|
|
if max_col < x:
|
|
max_col = x
|
|
image.crop(8*(max_col+1), 8*(max_row+1))
|
|
monster_battle_sprite_images[key] = image
|
|
monster_battle_sprite_textures[key] = texture_from_image(image)
|
|
entry['sprite'] = monster_battle_sprite_textures[key]
|
|
# Save monster
|
|
data.monster_battle_sprites.append(entry)
|
|
|
|
|
|
class BattleBackground:
|
|
var tilemap_image: Image
|
|
var tilemap_tex: Texture
|
|
var palette_images: Array
|
|
var palette_texs: Array
|
|
var tile_atlas_images: Array
|
|
var tile_atlas_texs: Array
|
|
|
|
var battle_backgrounds := []
|
|
func load_battle_bgs(data: Dictionary, buffer: StreamPeerBuffer):
|
|
var bg_palettes = data.battle_background_palettes
|
|
for map in snes_battle_bgs.get_all_battle_background_tilemaps(buffer, data):
|
|
var bg := BattleBackground.new()
|
|
bg.tilemap_image = map.tilemap_image
|
|
bg.tilemap_tex = texture_from_image(bg.tilemap_image)
|
|
var palette_images = []
|
|
var palette_texs = []
|
|
for ids in map.palette_ids:
|
|
var pal_image := generate_palette_from_colorarray(bg_palettes[ids[0]] + bg_palettes[ids[1]])
|
|
palette_images.append(pal_image)
|
|
palette_texs.append(texture_from_image(pal_image))
|
|
bg.palette_images = palette_images
|
|
bg.palette_texs = palette_texs
|
|
var tiles := []
|
|
var ts: PoolByteArray = map.tileset
|
|
var ts_l = len(ts)
|
|
for i in 128:
|
|
var start: int = i*32
|
|
var end: int = start+31 # inclusive...
|
|
if end >= ts_l:
|
|
break
|
|
tiles.append(snes_graphics._4plane_to_tile(ts.subarray(start, end)))
|
|
if 'animated_tiles' in map:
|
|
bg.tile_atlas_images = []
|
|
bg.tile_atlas_texs = []
|
|
var frames = []
|
|
for frame in 4:
|
|
frames.append(tiles.duplicate())
|
|
for pair in map.animated_tiles:
|
|
var frame = pair[0] >> 6
|
|
var tile_dst = pair[0] & 0x3F
|
|
var tile_src = pair[1]
|
|
frames[frame][tile_dst] = tiles[tile_src]
|
|
for frame in 4:
|
|
var atlas_image = make_tile_atlas(frames[frame])
|
|
bg.tile_atlas_images.append(atlas_image)
|
|
bg.tile_atlas_texs.append(texture_from_image(atlas_image))
|
|
else:
|
|
var atlas_image = make_tile_atlas(tiles)
|
|
bg.tile_atlas_images = [atlas_image]
|
|
bg.tile_atlas_texs = [texture_from_image(atlas_image)]
|
|
battle_backgrounds.append(bg)
|
|
|
|
static func bias_tile(unbiased: PoolByteArray, bias: int) -> Image:
|
|
var image := Image.new()
|
|
var biased = ByteArray(64)
|
|
for i in 64:
|
|
biased[i] = unbiased[i] + bias
|
|
image.create_from_data(8, 8, false, INDEX_FORMAT, biased)
|
|
return image
|
|
|
|
func load_from_structs(data: Dictionary):
|
|
# Load Battle sprites
|
|
for character_tiles in data.character_battle_sprite_tiles:
|
|
for job_tiles in character_tiles:
|
|
var strip_image = Image.new()
|
|
# Should probably refactor later
|
|
strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT)
|
|
for sprite in num_Character_Battle_Sprite_Layouts:
|
|
var sprite_tiles: PoolByteArray = data.character_battle_sprite_layouts[sprite]
|
|
for i in 6:
|
|
strip_image.blit_rect(job_tiles[sprite_tiles[i]], TILE_RECT, Vector2((i%2) * 8, ((i/2) * 8) + (sprite * 24)))
|
|
strip_images.append(strip_image)
|
|
strip_textures.append(texture_from_image(strip_image))
|
|
# Character Battle Sprite Palettes
|
|
for character_palettes in data.character_battle_sprite_palettes:
|
|
for job_palette in character_palettes:
|
|
character_battle_sprite_palette_textures.append(texture_from_image(generate_palette_from_colorarray(job_palette)))
|
|
character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette_from_colorarray(data.character_battle_sprite_disabled_palette))
|
|
character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette_from_colorarray(data.character_battle_sprite_stone_palette))
|
|
weapon_textures['Fist'] = texture_from_image(data.tiles_fist)
|
|
# Load World Map
|
|
# Load Worldmap Graphics
|
|
var worldmap_tile_counts = [256, 256, 128] # Only 128 underwater tiles
|
|
for world_ts in 3: # Bartz/Combined World, Galuf World, Underwater (world tilesets, not to be confused with the 5 world maps)
|
|
var tile_count: int = worldmap_tile_counts[world_ts]
|
|
var image := generate_palette_from_colorarray(data.worldmap_palettes[world_ts])
|
|
worldmap_palette_imgs.append(image)
|
|
worldmap_palette_textures.append(texture_from_image(image))
|
|
var tile_biases: PoolByteArray = data.worldmap_tiles.bias[world_ts]
|
|
var tile_images_unbiased = data.worldmap_tiles[world_ts]
|
|
var tile_images := []
|
|
for i in tile_count:
|
|
tile_images.append(bias_tile(tile_images_unbiased[i].get_data(), tile_biases[i]))
|
|
if world_ts == 0: # Waterfall hack: lay it out vertically, pushing out dummy tiles
|
|
tile_images[0x97] = tile_images[0x88]
|
|
tile_images[0x98] = tile_images[0x87]
|
|
worldmap_tile_individual_imgs.append(tile_images)
|
|
worldmap_tile_atlas_textures.append(texture_from_image(make_tile_atlas(tile_images)))
|
|
# Block definitions
|
|
var block_images = []
|
|
var block_textures = []
|
|
var block_tile_ids := PoolByteArray()
|
|
for block in 0xC0: # 192 blocks per world tileset
|
|
image = Image.new()
|
|
image.create(16, 16, false, INDEX_FORMAT)
|
|
for i in 4:
|
|
var src_idx: int = data.worldmap_blocks[world_ts][i][block]
|
|
block_tile_ids.append(src_idx)
|
|
if src_idx < tile_count:
|
|
image.blit_rect(tile_images[src_idx], TILE_RECT, Vector2((i%2)*8, (i/2)*8))
|
|
block_images.append(image)
|
|
block_textures.append(texture_from_image(image))
|
|
worldmap_block_individual_imgs.append(block_images)
|
|
worldmap_block_individual_textures.append(block_textures)
|
|
worldmap_block_tile_ids.append(block_tile_ids)
|
|
# Make block atlas
|
|
# image = Image.new()
|
|
# image.create(16*16, 16*12, false, INDEX_FORMAT)
|
|
# for block in 0xC0:
|
|
# image.blit_rect(block_images[block], Rect2(0, 0, 16, 16), Vector2((block%16)*16, (block/16)*16))
|
|
# worldmap_block_atlas_imgs.append(image)
|
|
# worldmap_block_atlas_textures.append(texture_from_image(image))
|
|
|
|
# # DEBUG: Make tile atlas
|
|
# image = Image.new()
|
|
# image.create(16*8, (tile_count/16)*8, false, INDEX_FORMAT)
|
|
# for tile in tile_count:
|
|
# image.blit_rect(tile_images[tile], TILE_RECT, Vector2((tile%16)*8, (tile/16)*8))
|
|
# worldmap_tile_atlas_textures.append(texture_from_image(image))
|
|
|
|
MapLoader.update_worldmap_block_tile_ids(worldmap_block_tile_ids)
|
|
|
|
# Make font atlas
|
|
self.font_atlas_image = make_font_glyph_atlas(data.font_glyphs_small, data.font_glyphs_dialogue, data.font_glyphs_kanji)
|
|
self.font_atlas_texture = texture_from_image(self.font_atlas_image)
|
|
self.font = BitmapFont.new()
|
|
self.font.add_texture(self.font_atlas_texture)
|
|
# self.font.extra_spacing_top = -2
|
|
# self.font.extra_spacing_bottom = -1
|
|
# self.font.extra_spacing_char = -1
|
|
# self.font.extra_spacing_space = 1
|
|
var glyph_table: PoolStringArray = StringLoader.glyph_tables.RPGe_dialog
|
|
var advance_table: PoolByteArray = data.RPGe_font_character_widths
|
|
for i in 256:
|
|
var s := glyph_table[i]
|
|
if len(s) == 1: # ignore icons etc. for now
|
|
var c := s.ord_at(0)
|
|
self.font.add_char(c, 0, Rect2((i%32)*16, (i/32)*12 + 32, advance_table[i], 12))
|
|
if c == 0x2019: # RIGHT SINGLE QUOTATION MARK
|
|
# Add for ' as well
|
|
self.font.add_char(0x27, 0, Rect2((i%32)*16, (i/32)*12 + 32, advance_table[i], 12))
|
|
# ThemeManager.theme.default_font = self.font
|
|
|
|
const gba_marker := 'FINAL FANTASY V ADVANCE SYGMAB'
|
|
const gba_marker_pos_US := 0x12FE10
|
|
const gba_marker_pos_EU := 0x131294
|
|
const gba_offset_battle_sprite_tiles := 504
|
|
const gba_offset_battle_sprite_palettes := 1064
|
|
const gba_offset_battle_sprite_layouts := 9435
|
|
func load_gba_rom(filename: String):
|
|
var rom := File.new()
|
|
var offset_marker: int
|
|
var result = rom.open(filename, File.READ)
|
|
if result != 0:
|
|
print_debug('ROM failed to open: ', result)
|
|
return
|
|
|
|
rom.seek(gba_marker_pos_US)
|
|
if rom.get_buffer(len(gba_marker)).get_string_from_ascii() == gba_marker:
|
|
offset_marker = gba_marker_pos_US
|
|
else:
|
|
rom.seek(gba_marker_pos_EU)
|
|
if rom.get_buffer(len(gba_marker)).get_string_from_ascii() == gba_marker:
|
|
offset_marker = gba_marker_pos_EU
|
|
else:
|
|
print_debug('ROM does not match known US/EU ROMs, aborting')
|
|
return
|
|
|
|
# Battle Sprite Tiles
|
|
rom.seek(offset_marker + gba_offset_battle_sprite_layouts)
|
|
var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6)
|
|
for strip in range(0, 26*5):
|
|
var tiles = []
|
|
rom.seek(offset_marker + gba_offset_battle_sprite_tiles + strip*4)
|
|
var address = rom.get_32() - 0x08000000
|
|
var tiledata = CommonGBA.LZ77_decompress(rom, address)
|
|
tiledata.append(0) # Slicing to end of array causes an engine crash. For shame!
|
|
for i in range(8, 57*32+8, 32):
|
|
tiles.append(gba_graphics._4bpp_to_tile(tiledata.subarray(i, i+32)))
|
|
var strip_image = Image.new()
|
|
strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT)
|
|
for i in range(6 * num_Character_Battle_Sprite_Layouts):
|
|
strip_image.blit_rect(tiles[battle_strip_layouts[i]], TILE_RECT, Vector2((i%2) * 8, (i/2) * 8))
|
|
strip_images.append(strip_image)
|
|
strip_textures.append(texture_from_image(strip_image))
|
|
|
|
# Character Battle Sprite Palettes
|
|
for palette in range(0, 26*5):
|
|
rom.seek(offset_marker + gba_offset_battle_sprite_palettes + palette*4)
|
|
character_battle_sprite_palette_textures.append(texture_from_image(generate_palette(rom, rom.get_32() - 0x08000000 + 8)))
|
|
# character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Disabled_Palette))
|
|
# character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Stone_Palette))
|
|
|
|
|
|
var strip_images = []
|
|
var strip_textures = []
|
|
# func _ready():
|
|
#pass
|