Add WorldMap dynamic regional changes
This commit is contained in:
parent
9a5435f01f
commit
334545fcc4
|
@ -36,7 +36,7 @@ I have mostly solved parsing of SNES menus in a sister project, however there ar
|
||||||
#### World Maps (Fields?)
|
#### World Maps (Fields?)
|
||||||
- [x] Tiles
|
- [x] Tiles
|
||||||
- [x] Tilemaps
|
- [x] Tilemaps
|
||||||
- [ ] Dynamic changes (e.g. meteors, breaking seals, sinking island, pirate cave, voids) (might hardcode these later)
|
- [x] Dynamic changes (e.g. meteors, breaking seals, sinking island, pirate cave, voids) (didn't have to hardcode it! just need to hook it into the script system later)
|
||||||
- [ ] Pathing (Data is in place, just needs moving around with collisions)
|
- [ ] Pathing (Data is in place, just needs moving around with collisions)
|
||||||
- [ ] Mode 7 Effects (...might hardcode these later)
|
- [ ] Mode 7 Effects (...might hardcode these later)
|
||||||
#### Dungeon/Town/Zone Maps (idk what accepted terminology is)
|
#### Dungeon/Town/Zone Maps (idk what accepted terminology is)
|
||||||
|
@ -64,7 +64,7 @@ This will be an interpreter, I am not hardcoding the thousands of scripts.
|
||||||
|
|
||||||
### Sound System
|
### Sound System
|
||||||
- [x] Instrument samples
|
- [x] Instrument samples
|
||||||
- [x] Basic Music playing (currently loops correctly, but tuning is a bit off)
|
- [x] Basic Music playing (currently loops correctly, but tuning is a bit off on looped samples, possible Godot bug to investigate)
|
||||||
- [ ] DSP stuff including ADSR envelopes
|
- [ ] DSP stuff including ADSR envelopes
|
||||||
|
|
||||||
# What's different?
|
# What's different?
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
Label SNES PSX_file PSX_offset format Comment
|
Label SNES PSX_file PSX_offset format Comment
|
||||||
|
ptrs_worldmap_event_replacements 0x006ABD 6 of u16 5 worlds + the end address
|
||||||
|
worldmap_event_replacements.0 0x00726C 61 of WorldMapEventReplacement hardcoded of above
|
||||||
|
worldmap_event_replacements.1 0x0073DA 53 of WorldMapEventReplacement hardcoded of above
|
||||||
|
worldmap_event_replacements.2 0x007518 106 of WorldMapEventReplacement hardcoded of above
|
||||||
|
worldmap_event_replacements.3 0x007794 1 of WorldMapEventReplacement hardcoded of above
|
||||||
|
worldmap_event_replacements.4 0x00779A 98 of WorldMapEventReplacement hardcoded of above
|
||||||
character_battle_sprite_stone_palette 0x00F807 N/A N/A 16 of ColorBGR555 Also 0x199835
|
character_battle_sprite_stone_palette 0x00F807 N/A N/A 16 of ColorBGR555 Also 0x199835
|
||||||
character_battle_sprite_disabled_palette 0x00F867 /mnu/memsave.bin 0x000034 16 of ColorBGR555
|
character_battle_sprite_disabled_palette 0x00F867 /mnu/memsave.bin 0x000034 16 of ColorBGR555
|
||||||
locations_bg_palettes 0x03BB00 /nar/ff5_binx.bin 0x03BF80 43 of 128 of ColorBGR555
|
locations_bg_palettes 0x03BB00 /nar/ff5_binx.bin 0x03BF80 43 of 128 of ColorBGR555
|
||||||
|
@ -19,7 +25,7 @@ worldmap_compressed_tilesets 0x070000 tilesets 0 up to 0x434
|
||||||
worldmap_compressed_tilesets2 0x080000 tilesets 0x434 up to 0x500
|
worldmap_compressed_tilesets2 0x080000 tilesets 0x434 up to 0x500
|
||||||
ptrs_jp_speech 0x082220 2160 of u16
|
ptrs_jp_speech 0x082220 2160 of u16
|
||||||
ptrs_extended_event_data 0x083320 1940 of u24
|
ptrs_extended_event_data 0x083320 1940 of u24
|
||||||
extended_event_data 0x0849DF See above for addresses
|
extended_event_data 0x0849DC See above for addresses
|
||||||
jp_speech 0x0A0000 See 0x082220 for offsets
|
jp_speech 0x0A0000 See 0x082220 for offsets
|
||||||
ptrs_tilemaps 0x0B0000 328 of u16
|
ptrs_tilemaps 0x0B0000 328 of u16
|
||||||
tilemaps 0x0B0290 See above for offsets
|
tilemaps 0x0B0290 See above for offsets
|
||||||
|
|
|
|
@ -197,3 +197,10 @@ u8 palette_id
|
||||||
u8 23
|
u8 23
|
||||||
u8 24
|
u8 24
|
||||||
u8 music_id
|
u8 music_id
|
||||||
|
|
||||||
|
struct WorldMapEventReplacement
|
||||||
|
u8 y
|
||||||
|
u8 x
|
||||||
|
u8 num_bytes
|
||||||
|
u8 event_flag # Add 0x1D0 to this for actual event flag
|
||||||
|
u16 ptr_bytes # Read num_bytes from this address (0xC0 bank implied, so just from the start of the ROM)
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 43.
|
|
@ -66,8 +66,32 @@ class WorldMap:
|
||||||
const tile_width := block_width * 2
|
const tile_width := block_width * 2
|
||||||
const tile_height := block_height * 2
|
const tile_height := block_height * 2
|
||||||
var blockmap: PoolByteArray
|
var blockmap: PoolByteArray
|
||||||
|
var blockmap_original: PoolByteArray
|
||||||
var block_tile_ids: PoolByteArray
|
var block_tile_ids: PoolByteArray
|
||||||
var block_pathing: PoolIntArray
|
var block_pathing: PoolIntArray
|
||||||
|
var event_replacements: Dictionary # Dictionary[Array[EventReplacementRegion]]
|
||||||
|
|
||||||
|
class EventReplacementRegion:
|
||||||
|
var start_y: int
|
||||||
|
var rows: Array # Array[Array[int, PoolByteArray]]
|
||||||
|
func _init() -> void:
|
||||||
|
self.rows = []
|
||||||
|
|
||||||
|
func get_min_x() -> int:
|
||||||
|
var min_x = block_width
|
||||||
|
for row in rows:
|
||||||
|
var x = row[0]
|
||||||
|
if x < min_x:
|
||||||
|
min_x = x
|
||||||
|
return min_x
|
||||||
|
|
||||||
|
func get_max_x() -> int:
|
||||||
|
var max_x = 0
|
||||||
|
for row in rows:
|
||||||
|
var x = row[0]
|
||||||
|
if x > max_x:
|
||||||
|
max_x = x
|
||||||
|
return max_x
|
||||||
|
|
||||||
func get_block_tiles(id: int) -> PoolByteArray:
|
func get_block_tiles(id: int) -> PoolByteArray:
|
||||||
var i = id * 4
|
var i = id * 4
|
||||||
|
@ -92,6 +116,59 @@ class WorldMap:
|
||||||
image.create_from_data(tile_width, tile_height, false, SpriteLoader.INDEX_FORMAT, data)
|
image.create_from_data(tile_width, tile_height, false, SpriteLoader.INDEX_FORMAT, data)
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
func apply_event_region_replacement(region: WorldMap.EventReplacementRegion):
|
||||||
|
# Apply a single event region replacement
|
||||||
|
var y := region.start_y
|
||||||
|
var y_offset = y * block_width
|
||||||
|
for row in region.rows:
|
||||||
|
var x: int = row[0]
|
||||||
|
var blocks: PoolByteArray = row[1]
|
||||||
|
var offset = y_offset + x
|
||||||
|
var new_blockmap := blocks
|
||||||
|
# A simple array splice shows the API weakness of GDScript's PoolByteArrays, sadly
|
||||||
|
if offset > 0: # Prepend behind if non-empty (weakness of PoolXArray::subarray)
|
||||||
|
new_blockmap = self.blockmap.subarray(0, offset-1) + new_blockmap
|
||||||
|
if len(new_blockmap) < len(self.blockmap): # Append behind if non-empty (weakness of PoolXArray::subarray)
|
||||||
|
new_blockmap = new_blockmap + self.blockmap.subarray(len(new_blockmap), -1)
|
||||||
|
self.blockmap = new_blockmap
|
||||||
|
y_offset += block_width
|
||||||
|
|
||||||
|
func apply_event_replacements(event_flags): # Any integer array is fine
|
||||||
|
for event_flag in event_flags:
|
||||||
|
if self.event_replacements.has(event_flag):
|
||||||
|
for region in self.event_replacements[event_flag]:
|
||||||
|
self.apply_event_region_replacement(region)
|
||||||
|
|
||||||
|
func init_event_replacements(_data: Dictionary, buffer: StreamPeerBuffer, worldmap_event_replacements: Array):
|
||||||
|
# Turn deserialized WorldMapEventReplacement structs into EventReplacementRegions
|
||||||
|
self.event_replacements = {}
|
||||||
|
var last_event_flag: int = -1
|
||||||
|
var last_y: int = -1
|
||||||
|
var region := WorldMap.EventReplacementRegion.new()
|
||||||
|
|
||||||
|
for entry in worldmap_event_replacements:
|
||||||
|
var event_flag = entry.event_flag + 0x1D0
|
||||||
|
var y = entry.y
|
||||||
|
var x = entry.x
|
||||||
|
buffer.seek(entry.ptr_bytes)
|
||||||
|
var blocks = PoolByteArray(buffer.get_data(entry.num_bytes)[1])
|
||||||
|
|
||||||
|
if last_event_flag == -1: # Finish initializing the initial region
|
||||||
|
region.start_y = y
|
||||||
|
elif last_event_flag != event_flag or last_y != y-1:
|
||||||
|
# Save last region and start a new one
|
||||||
|
self.event_replacements.get_or_add(last_event_flag, []).append(region)
|
||||||
|
# Start a new region
|
||||||
|
region = WorldMap.EventReplacementRegion.new()
|
||||||
|
region.start_y = y
|
||||||
|
# Keep building existing region
|
||||||
|
last_event_flag = event_flag
|
||||||
|
last_y = y
|
||||||
|
region.rows.append([x, blocks])
|
||||||
|
# Save final region
|
||||||
|
self.event_replacements.get_or_add(last_event_flag, []).append(region)
|
||||||
|
|
||||||
|
|
||||||
var worldmaps = [WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new()]
|
var worldmaps = [WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new()]
|
||||||
|
|
||||||
var worldmap_block_properties = []
|
var worldmap_block_properties = []
|
||||||
|
@ -150,6 +227,8 @@ func load_worldmaps(data: Dictionary, buffer: StreamPeerBuffer):
|
||||||
blockmap.append(b+2)
|
blockmap.append(b+2)
|
||||||
chunk_size += 2
|
chunk_size += 2
|
||||||
worldmaps[worldmap_id].blockmap = blockmap
|
worldmaps[worldmap_id].blockmap = blockmap
|
||||||
|
worldmaps[worldmap_id].blockmap_original = blockmap
|
||||||
|
worldmaps[worldmap_id].init_event_replacements(data, buffer, data.worldmap_event_replacements[worldmap_id])
|
||||||
|
|
||||||
func update_worldmap_block_tile_ids(worldmap_block_tile_ids: Array):
|
func update_worldmap_block_tile_ids(worldmap_block_tile_ids: Array):
|
||||||
# Called by SpriteLoader
|
# Called by SpriteLoader
|
||||||
|
|
|
@ -183,7 +183,7 @@ static func get_structarraytype(type: String, existing_structs: Dictionary):
|
||||||
'of':
|
'of':
|
||||||
i -= 1
|
i -= 1
|
||||||
var l1 = int(tokens[i])
|
var l1 = int(tokens[i])
|
||||||
if l1 > 1:
|
if l1 >= 0: # 0-of and 1-of may seem nonsensical, but they may help in generic array-of-array parsing
|
||||||
inner_type = StructArrayType.new(l1, inner_type) # Might be worth caching these later on if we use them more
|
inner_type = StructArrayType.new(l1, inner_type) # Might be worth caching these later on if we use them more
|
||||||
i -= 1
|
i -= 1
|
||||||
var k:
|
var k:
|
||||||
|
|
|
@ -5,8 +5,10 @@ var worldmap_shader_mat := preload('res://worldmap_palette_mat.tres')
|
||||||
onready var minimap := $tr_minimap
|
onready var minimap := $tr_minimap
|
||||||
|
|
||||||
var current_map_id := 0
|
var current_map_id := 0
|
||||||
var map_images := []
|
var map_images := [null, null, null, null, null]
|
||||||
var map_textures := []
|
var map_textures := [null, null, null, null, null]
|
||||||
|
var map_regional_replacement_amounts := [0, 0, 0, 0, 0]
|
||||||
|
var map_regional_replacement_lists := [[], [], [], [], []]
|
||||||
var current_texture: Texture
|
var current_texture: Texture
|
||||||
var minimap_mode := 0
|
var minimap_mode := 0
|
||||||
var minimap_tween := 0.0
|
var minimap_tween := 0.0
|
||||||
|
@ -15,13 +17,20 @@ var minimap_tween := 0.0
|
||||||
const map_tilesets = [0, 1, 0, 2, 2]
|
const map_tilesets = [0, 1, 0, 2, 2]
|
||||||
const waterfall_scrolls = [true, false, true, false, false]
|
const waterfall_scrolls = [true, false, true, false, false]
|
||||||
const sea_scrolls = [true, true, true, false, false]
|
const sea_scrolls = [true, true, true, false, false]
|
||||||
|
|
||||||
|
func _create_worldmap_texture(id: int) -> void:
|
||||||
|
var tileset = map_tilesets[id]
|
||||||
|
var image = MapLoader.worldmaps[id].make_tile_map()
|
||||||
|
self.map_images[id] = image
|
||||||
|
var tex := SpriteLoader.texture_from_image(image, Texture.FLAG_REPEAT)
|
||||||
|
self.map_textures[id] = tex
|
||||||
|
|
||||||
func _create_worldmap_textures() -> void:
|
func _create_worldmap_textures() -> void:
|
||||||
for i in 5:
|
for id in 5:
|
||||||
var tileset = map_tilesets[i]
|
self._create_worldmap_texture(id)
|
||||||
var image = MapLoader.worldmaps[i].make_tile_map()
|
for flag_block in MapLoader.worldmaps[id].event_replacements.values():
|
||||||
self.map_images.append(image)
|
for region in flag_block:
|
||||||
var tex := SpriteLoader.texture_from_image(image, Texture.FLAG_REPEAT)
|
self.map_regional_replacement_lists[id].append(region)
|
||||||
self.map_textures.append(tex)
|
|
||||||
|
|
||||||
func _set_map(id: int) -> void:
|
func _set_map(id: int) -> void:
|
||||||
if id < 0 or id >= len(map_images):
|
if id < 0 or id >= len(map_images):
|
||||||
|
@ -41,6 +50,46 @@ func _set_map(id: int) -> void:
|
||||||
minimap.material.set_shader_param('enable_sea_scroll', false)
|
minimap.material.set_shader_param('enable_sea_scroll', false)
|
||||||
minimap.material.set_shader_param('enable_tile_globbing', true)
|
minimap.material.set_shader_param('enable_tile_globbing', true)
|
||||||
|
|
||||||
|
func _jump_to_regional_replacement(region) -> void:
|
||||||
|
var y_min = region.start_y
|
||||||
|
var y_max = y_min + len(region.rows)
|
||||||
|
var x_min = region.get_min_x()
|
||||||
|
var x_max = region.get_max_x()
|
||||||
|
self.pos = Vector2((x_max+x_min)/2, (y_max+y_min)/2)
|
||||||
|
print_debug('Jumping to event position ', self.pos.x, self.pos.y)
|
||||||
|
|
||||||
|
var delay_map_update: float = -1.0
|
||||||
|
func _perform_regional_replacement(amount: int) -> void:
|
||||||
|
var map = MapLoader.worldmaps[current_map_id]
|
||||||
|
var last_region
|
||||||
|
match amount:
|
||||||
|
0:
|
||||||
|
map.blockmap = map.blockmap_original
|
||||||
|
self.map_regional_replacement_amounts[current_map_id] = 0
|
||||||
|
self.delay_map_update = 0.0001
|
||||||
|
1:
|
||||||
|
if self.map_regional_replacement_amounts[current_map_id] >= len(self.map_regional_replacement_lists[current_map_id]):
|
||||||
|
return
|
||||||
|
last_region = self.map_regional_replacement_lists[current_map_id][self.map_regional_replacement_amounts[current_map_id]]
|
||||||
|
map.apply_event_region_replacement(last_region)
|
||||||
|
self._jump_to_regional_replacement(last_region)
|
||||||
|
self.map_regional_replacement_amounts[current_map_id] += 1
|
||||||
|
self.delay_map_update = 1.0
|
||||||
|
-1:
|
||||||
|
if self.map_regional_replacement_amounts[current_map_id] < 1:
|
||||||
|
return
|
||||||
|
self.map_regional_replacement_amounts[current_map_id] -= 1
|
||||||
|
# Reset and reapply the stack
|
||||||
|
map.blockmap = map.blockmap_original
|
||||||
|
for i in self.map_regional_replacement_amounts[current_map_id]:
|
||||||
|
last_region = self.map_regional_replacement_lists[current_map_id][i]
|
||||||
|
map.apply_event_region_replacement(last_region)
|
||||||
|
# self._jump_to_regional_replacement(last_region)
|
||||||
|
self.delay_map_update = 0.0001
|
||||||
|
# self._create_worldmap_texture(current_map_id)
|
||||||
|
# self._set_map(current_map_id) # refresh shader etc.
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# Only create this after MapLoader and SpriteLoader have loaded!
|
# Only create this after MapLoader and SpriteLoader have loaded!
|
||||||
|
@ -80,6 +129,14 @@ func _process(delta: float) -> void:
|
||||||
# minimap.material.set_shader_param('uv_scale', 64 * minimap_scale)
|
# minimap.material.set_shader_param('uv_scale', 64 * minimap_scale)
|
||||||
minimap.material.set_shader_param('uv_scale', lerp(512, 1<<5, l))
|
minimap.material.set_shader_param('uv_scale', lerp(512, 1<<5, l))
|
||||||
# minimap.material.set_shader_param('uv_scale', 512/16)
|
# minimap.material.set_shader_param('uv_scale', 512/16)
|
||||||
|
|
||||||
|
# Hack to do a presentation of map region replacements with a nice before and after
|
||||||
|
if self.delay_map_update > 0.000:
|
||||||
|
self.delay_map_update -= delta
|
||||||
|
if self.delay_map_update <= 0.000:
|
||||||
|
self._create_worldmap_texture(current_map_id)
|
||||||
|
self._set_map(current_map_id) # refresh shader etc.
|
||||||
|
|
||||||
update()
|
update()
|
||||||
|
|
||||||
var pos := Vector2(0, 0)
|
var pos := Vector2(0, 0)
|
||||||
|
@ -111,5 +168,11 @@ func _input(event: InputEvent) -> void:
|
||||||
_set_map(4)
|
_set_map(4)
|
||||||
KEY_0:
|
KEY_0:
|
||||||
self.minimap_mode = 1 - minimap_mode
|
self.minimap_mode = 1 - minimap_mode
|
||||||
|
KEY_7:
|
||||||
|
self._perform_regional_replacement(0)
|
||||||
|
KEY_8:
|
||||||
|
self._perform_regional_replacement(-1)
|
||||||
|
KEY_9:
|
||||||
|
self._perform_regional_replacement(1)
|
||||||
KEY_H:
|
KEY_H:
|
||||||
$lbl_help.visible = !$lbl_help.visible
|
$lbl_help.visible = !$lbl_help.visible
|
||||||
|
|
|
@ -17,4 +17,5 @@ text = "H: Toggle this help
|
||||||
1-5: Change world
|
1-5: Change world
|
||||||
0: Toggle minimap size
|
0: Toggle minimap size
|
||||||
Arrow keys: Move around
|
Arrow keys: Move around
|
||||||
|
7-9: Reset, Decrement, or Increment regional replacements
|
||||||
Backspace: Return to debug menu"
|
Backspace: Return to debug menu"
|
||||||
|
|
Loading…
Reference in New Issue