148 lines
6.1 KiB
Python
148 lines
6.1 KiB
Python
from ..romTables import ROMWithTables
|
|
from ..roomEditor import RoomEditor, ObjectWarp
|
|
from ..patches import overworld, core
|
|
from .tileset import loadTileInfo
|
|
from .map import Map, MazeGen
|
|
from .wfc import WFCMap, ContradictionException
|
|
from .roomgen import setup_room_types
|
|
from .imagegenerator import ImageGen
|
|
from .util import xyrange
|
|
from .locations.entrance import DummyEntrance
|
|
from .locationgen import LocationGenerator
|
|
from .logic import LogicGenerator
|
|
from .enemygen import generate_enemies
|
|
from ..assembler import ASM
|
|
|
|
|
|
def store_map(rom, the_map: Map):
|
|
# Move all exceptions to room FF
|
|
# Dig seashells
|
|
rom.patch(0x03, 0x220F, ASM("cp $DA"), ASM("cp $FF"))
|
|
rom.patch(0x03, 0x2213, ASM("cp $A5"), ASM("cp $FF"))
|
|
rom.patch(0x03, 0x2217, ASM("cp $74"), ASM("cp $FF"))
|
|
rom.patch(0x03, 0x221B, ASM("cp $3A"), ASM("cp $FF"))
|
|
rom.patch(0x03, 0x221F, ASM("cp $A8"), ASM("cp $FF"))
|
|
rom.patch(0x03, 0x2223, ASM("cp $B2"), ASM("cp $FF"))
|
|
# Force tile 04 under bushes and rocks, instead of conditionally tile 3, else seashells won't spawn.
|
|
rom.patch(0x14, 0x1655, 0x1677, "", fill_nop=True)
|
|
# Bonk trees
|
|
rom.patch(0x03, 0x0F03, ASM("cp $A4"), ASM("cp $FF"))
|
|
rom.patch(0x03, 0x0F07, ASM("cp $D2"), ASM("cp $FF"))
|
|
# Stairs under rocks
|
|
rom.patch(0x14, 0x1638, ASM("cp $52"), ASM("cp $FF"))
|
|
rom.patch(0x14, 0x163C, ASM("cp $04"), ASM("cp $FF"))
|
|
|
|
# Patch D6 raft game exit, just remove the exit.
|
|
re = RoomEditor(rom, 0x1B0)
|
|
re.removeObject(7, 0)
|
|
re.store(rom)
|
|
# Patch D8 back entrance, remove the outside part
|
|
re = RoomEditor(rom, 0x23A)
|
|
re.objects = [obj for obj in re.objects if not isinstance(obj, ObjectWarp)] + [ObjectWarp(1, 7, 0x23D, 0x58, 0x10)]
|
|
re.store(rom)
|
|
re = RoomEditor(rom, 0x23D)
|
|
re.objects = [obj for obj in re.objects if not isinstance(obj, ObjectWarp)] + [ObjectWarp(1, 7, 0x23A, 0x58, 0x10)]
|
|
re.store(rom)
|
|
|
|
for room in the_map:
|
|
for location in room.locations:
|
|
location.prepare(rom)
|
|
for n in range(0x00, 0x100):
|
|
sx = n & 0x0F
|
|
sy = ((n >> 4) & 0x0F)
|
|
if sx < the_map.w and sy < the_map.h:
|
|
tiles = the_map.get(sx, sy).tiles
|
|
else:
|
|
tiles = [4] * 80
|
|
tiles[44] = 0xC6
|
|
|
|
re = RoomEditor(rom, n)
|
|
# tiles = re.getTileArray()
|
|
re.objects = []
|
|
re.entities = []
|
|
room = the_map.get(sx, sy) if sx < the_map.w and sy < the_map.h else None
|
|
|
|
tileset = the_map.tilesets[room.tileset_id] if room else None
|
|
rom.banks[0x3F][0x3F00 + n] = tileset.main_id if tileset else 0x0F
|
|
rom.banks[0x21][0x02EF + n] = tileset.palette_id if tileset and tileset.palette_id is not None else 0x03
|
|
rom.banks[0x1A][0x2476 + n] = tileset.attr_bank if tileset and tileset.attr_bank else 0x22
|
|
rom.banks[0x1A][0x1E76 + n * 2] = (tileset.attr_addr & 0xFF) if tileset and tileset.attr_addr else 0x00
|
|
rom.banks[0x1A][0x1E77 + n * 2] = (tileset.attr_addr >> 8) if tileset and tileset.attr_addr else 0x60
|
|
re.animation_id = tileset.animation_id if tileset and tileset.animation_id is not None else 0x03
|
|
|
|
re.buildObjectList(tiles)
|
|
if room:
|
|
for idx, tile_id in enumerate(tiles):
|
|
if tile_id == 0x61: # Fix issues with the well being used as chimney as well and causing wrong warps
|
|
DummyEntrance(room, idx % 10, idx // 10)
|
|
re.entities += room.entities
|
|
room.locations.sort(key=lambda loc: (loc.y, loc.x, id(loc)))
|
|
for location in room.locations:
|
|
location.update_room(rom, re)
|
|
else:
|
|
re.objects.append(ObjectWarp(0x01, 0x10, 0x2A3, 0x50, 0x7C))
|
|
re.store(rom)
|
|
|
|
rom.banks[0x21][0x00BF:0x00BF+3] = [0, 0, 0] # Patch out the "load palette on screen transition" exception code.
|
|
|
|
# Fix some tile attribute issues
|
|
def change_attr(tileset, index, a, b, c, d):
|
|
rom.banks[the_map.tilesets[tileset].attr_bank][the_map.tilesets[tileset].attr_addr - 0x4000 + index * 4 + 0] = a
|
|
rom.banks[the_map.tilesets[tileset].attr_bank][the_map.tilesets[tileset].attr_addr - 0x4000 + index * 4 + 1] = b
|
|
rom.banks[the_map.tilesets[tileset].attr_bank][the_map.tilesets[tileset].attr_addr - 0x4000 + index * 4 + 2] = c
|
|
rom.banks[the_map.tilesets[tileset].attr_bank][the_map.tilesets[tileset].attr_addr - 0x4000 + index * 4 + 3] = d
|
|
change_attr("mountains", 0x04, 6, 6, 6, 6)
|
|
change_attr("mountains", 0x27, 6, 6, 3, 3)
|
|
change_attr("mountains", 0x28, 6, 6, 3, 3)
|
|
change_attr("mountains", 0x6E, 1, 1, 1, 1)
|
|
change_attr("town", 0x59, 2, 2, 2, 2) # Roof tile wrong color
|
|
|
|
|
|
def generate(rom_filename, w, h):
|
|
rom = ROMWithTables(rom_filename)
|
|
overworld.patchOverworldTilesets(rom)
|
|
core.cleanup(rom)
|
|
tilesets = loadTileInfo(rom)
|
|
|
|
the_map = Map(w, h, tilesets)
|
|
setup_room_types(the_map)
|
|
|
|
MazeGen(the_map)
|
|
imggen = ImageGen(tilesets, the_map, rom)
|
|
imggen.enabled = False
|
|
wfcmap = WFCMap(the_map, tilesets) #, step_callback=imggen.on_step)
|
|
try:
|
|
wfcmap.initialize()
|
|
except ContradictionException as e:
|
|
print(f"Failed on setup {e.x // 10} {e.y // 8} {e.x % 10} {e.y % 8}")
|
|
imggen.on_step(wfcmap, err=(e.x, e.y))
|
|
return
|
|
imggen.on_step(wfcmap)
|
|
for x, y in xyrange(w, h):
|
|
for n in range(50):
|
|
try:
|
|
wfcmap.build(x * 10, y * 8, 10, 8)
|
|
imggen.on_step(wfcmap)
|
|
break
|
|
except ContradictionException as e:
|
|
print(f"Failed {x} {y} {e.x%10} {e.y%8} {n}")
|
|
imggen.on_step(wfcmap, err=(e.x, e.y))
|
|
wfcmap.clear()
|
|
if n == 49:
|
|
raise RuntimeError("Failed to fill chunk")
|
|
print(f"Done {x} {y}")
|
|
imggen.on_step(wfcmap)
|
|
wfcmap.store_tile_data(the_map)
|
|
|
|
LocationGenerator(the_map)
|
|
|
|
for room in the_map:
|
|
generate_enemies(room)
|
|
|
|
if imggen.enabled:
|
|
store_map(rom, the_map)
|
|
from mapexport import MapExport
|
|
MapExport(rom).export_all(w, h, dungeons=False)
|
|
rom.save("test.gbc")
|
|
return the_map
|