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 |