 81a239325d
			
		
	
	81a239325d
	
	
	
		
			
			Adds Link's Awakening: DX. Fully imports and forks LADXR, with permission - https://github.com/daid/LADXR
		
			
				
	
	
		
			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
 |