move ALTTP to its own world folder
This commit is contained in:
parent
4f8c737eec
commit
1d58f54101
|
@ -6,7 +6,7 @@ import textwrap
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from AdjusterMain import adjust
|
from AdjusterMain import adjust
|
||||||
from Rom import get_sprite_from_name
|
from worlds.alttp.Rom import get_sprite_from_name
|
||||||
|
|
||||||
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
|
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from Utils import output_path
|
from Utils import output_path
|
||||||
from Rom import LocalRom, apply_rom_settings
|
from worlds.alttp.Rom import LocalRom, apply_rom_settings
|
||||||
|
|
||||||
|
|
||||||
def adjust(args):
|
def adjust(args):
|
||||||
|
|
|
@ -5,16 +5,20 @@ from enum import Enum, unique
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from collections import OrderedDict, Counter, deque
|
from collections import OrderedDict, Counter, deque
|
||||||
from typing import Union, Optional, List, Set, Dict
|
from typing import Union, Optional, List, Dict
|
||||||
import secrets
|
import secrets
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from EntranceShuffle import door_addresses, indirect_connections
|
import worlds.alttp
|
||||||
|
from worlds.alttp.EntranceShuffle import door_addresses, indirect_connections
|
||||||
from Utils import int16_as_bytes
|
from Utils import int16_as_bytes
|
||||||
from Items import item_name_groups
|
from worlds.alttp.Items import item_name_groups
|
||||||
|
|
||||||
|
|
||||||
class World(object):
|
class World():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MultiWorld():
|
||||||
debug_types = False
|
debug_types = False
|
||||||
player_names: list
|
player_names: list
|
||||||
_region_cache: dict
|
_region_cache: dict
|
||||||
|
@ -26,15 +30,7 @@ class World(object):
|
||||||
def __init__(self, players: int, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer,
|
def __init__(self, players: int, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer,
|
||||||
progressive,
|
progressive,
|
||||||
goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
|
goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
|
||||||
if self.debug_types:
|
|
||||||
import inspect
|
|
||||||
methods = inspect.getmembers(self, predicate=inspect.ismethod)
|
|
||||||
|
|
||||||
for name, method in methods:
|
|
||||||
if name.startswith("_debug_"):
|
|
||||||
setattr(self, name[7:], method)
|
|
||||||
logging.debug(f"Set {self}.{name[7:]} to {method}")
|
|
||||||
self.get_location = self._debug_get_location
|
|
||||||
self.random = random.Random() # world-local random state is saved in case of future use a
|
self.random = random.Random() # world-local random state is saved in case of future use a
|
||||||
# persistently running program with multiple worlds rolling concurrently
|
# persistently running program with multiple worlds rolling concurrently
|
||||||
self.players = players
|
self.players = players
|
||||||
|
@ -131,6 +127,10 @@ class World(object):
|
||||||
set_player_attr('dark_room_logic', "lamp")
|
set_player_attr('dark_room_logic', "lamp")
|
||||||
set_player_attr('restrict_dungeon_item_on_boss', False)
|
set_player_attr('restrict_dungeon_item_on_boss', False)
|
||||||
|
|
||||||
|
self.worlds = []
|
||||||
|
#for i in range(players):
|
||||||
|
# self.worlds.append(worlds.alttp.ALTTPWorld({}, i))
|
||||||
|
|
||||||
def secure(self):
|
def secure(self):
|
||||||
self.random = secrets.SystemRandom()
|
self.random = secrets.SystemRandom()
|
||||||
|
|
||||||
|
@ -170,18 +170,6 @@ class World(object):
|
||||||
self._recache()
|
self._recache()
|
||||||
return self._region_cache[player][regionname]
|
return self._region_cache[player][regionname]
|
||||||
|
|
||||||
def _debug_get_region(self, regionname: str, player: int) -> Region:
|
|
||||||
if type(regionname) != str:
|
|
||||||
raise TypeError(f"expected str, got {type(regionname)} instead")
|
|
||||||
try:
|
|
||||||
return self._region_cache[player][regionname]
|
|
||||||
except KeyError:
|
|
||||||
for region in self.regions:
|
|
||||||
if region.name == regionname and region.player == player:
|
|
||||||
assert not region.world # this should only happen before initialization
|
|
||||||
self._region_cache[player][regionname] = region
|
|
||||||
return region
|
|
||||||
raise KeyError('No such region %s for player %d' % (regionname, player))
|
|
||||||
|
|
||||||
def get_entrance(self, entrance: str, player: int) -> Entrance:
|
def get_entrance(self, entrance: str, player: int) -> Entrance:
|
||||||
try:
|
try:
|
||||||
|
@ -190,19 +178,6 @@ class World(object):
|
||||||
self._recache()
|
self._recache()
|
||||||
return self._entrance_cache[entrance, player]
|
return self._entrance_cache[entrance, player]
|
||||||
|
|
||||||
def _debug_get_entrance(self, entrance: str, player: int) -> Entrance:
|
|
||||||
if type(entrance) != str:
|
|
||||||
raise TypeError(f"expected str, got {type(entrance)} instead")
|
|
||||||
try:
|
|
||||||
return self._entrance_cache[(entrance, player)]
|
|
||||||
except KeyError:
|
|
||||||
for region in self.regions:
|
|
||||||
for exit in region.exits:
|
|
||||||
if exit.name == entrance and exit.player == player:
|
|
||||||
self._entrance_cache[(entrance, player)] = exit
|
|
||||||
return exit
|
|
||||||
|
|
||||||
raise KeyError('No such entrance %s for player %d' % (entrance, player))
|
|
||||||
|
|
||||||
def get_location(self, location: str, player: int) -> Location:
|
def get_location(self, location: str, player: int) -> Location:
|
||||||
try:
|
try:
|
||||||
|
@ -211,19 +186,6 @@ class World(object):
|
||||||
self._recache()
|
self._recache()
|
||||||
return self._location_cache[location, player]
|
return self._location_cache[location, player]
|
||||||
|
|
||||||
def _debug_get_location(self, location: str, player: int) -> Location:
|
|
||||||
if type(location) != str:
|
|
||||||
raise TypeError(f"expected str, got {type(location)} instead")
|
|
||||||
try:
|
|
||||||
return self._location_cache[(location, player)]
|
|
||||||
except KeyError:
|
|
||||||
for region in self.regions:
|
|
||||||
for r_location in region.locations:
|
|
||||||
if r_location.name == location and r_location.player == player:
|
|
||||||
self._location_cache[(location, player)] = r_location
|
|
||||||
return r_location
|
|
||||||
|
|
||||||
raise KeyError('No such location %s for player %d' % (location, player))
|
|
||||||
|
|
||||||
def get_dungeon(self, dungeonname: str, player: int) -> Dungeon:
|
def get_dungeon(self, dungeonname: str, player: int) -> Dungeon:
|
||||||
for dungeon in self.dungeons:
|
for dungeon in self.dungeons:
|
||||||
|
@ -231,13 +193,6 @@ class World(object):
|
||||||
return dungeon
|
return dungeon
|
||||||
raise KeyError('No such dungeon %s for player %d' % (dungeonname, player))
|
raise KeyError('No such dungeon %s for player %d' % (dungeonname, player))
|
||||||
|
|
||||||
def _debug_get_dungeon(self, dungeonname: str, player: int) -> Dungeon:
|
|
||||||
if type(dungeonname) != str:
|
|
||||||
raise TypeError(f"expected str, got {type(dungeonname)} instead")
|
|
||||||
for dungeon in self.dungeons:
|
|
||||||
if dungeon.name == dungeonname and dungeon.player == player:
|
|
||||||
return dungeon
|
|
||||||
raise KeyError('No such dungeon %s for player %d' % (dungeonname, player))
|
|
||||||
|
|
||||||
def get_all_state(self, keys=False) -> CollectionState:
|
def get_all_state(self, keys=False) -> CollectionState:
|
||||||
ret = CollectionState(self)
|
ret = CollectionState(self)
|
||||||
|
@ -291,7 +246,7 @@ class World(object):
|
||||||
|
|
||||||
if keys:
|
if keys:
|
||||||
for p in range(1, self.players + 1):
|
for p in range(1, self.players + 1):
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
for item in ItemFactory(
|
for item in ItemFactory(
|
||||||
['Small Key (Hyrule Castle)', 'Big Key (Eastern Palace)', 'Big Key (Desert Palace)',
|
['Small Key (Hyrule Castle)', 'Big Key (Eastern Palace)', 'Big Key (Desert Palace)',
|
||||||
'Small Key (Desert Palace)', 'Big Key (Tower of Hera)', 'Small Key (Tower of Hera)',
|
'Small Key (Desert Palace)', 'Big Key (Tower of Hera)', 'Small Key (Tower of Hera)',
|
||||||
|
@ -432,7 +387,7 @@ class World(object):
|
||||||
|
|
||||||
class CollectionState(object):
|
class CollectionState(object):
|
||||||
|
|
||||||
def __init__(self, parent: World):
|
def __init__(self, parent: MultiWorld):
|
||||||
self.prog_items = Counter()
|
self.prog_items = Counter()
|
||||||
self.world = parent
|
self.world = parent
|
||||||
self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
|
self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
|
||||||
|
@ -1164,7 +1119,7 @@ class UpgradeShop(Shop):
|
||||||
|
|
||||||
|
|
||||||
class Spoiler(object):
|
class Spoiler(object):
|
||||||
world: World
|
world: MultiWorld
|
||||||
|
|
||||||
def __init__(self, world):
|
def __init__(self, world):
|
||||||
self.world = world
|
self.world = world
|
||||||
|
|
6
Gui.py
6
Gui.py
|
@ -15,10 +15,10 @@ import ModuleUpdate
|
||||||
ModuleUpdate.update()
|
ModuleUpdate.update()
|
||||||
|
|
||||||
from AdjusterMain import adjust
|
from AdjusterMain import adjust
|
||||||
from EntranceRandomizer import parse_arguments
|
from worlds.alttp.EntranceRandomizer import parse_arguments
|
||||||
from GuiUtils import ToolTips, set_icon, BackgroundTaskProgress
|
from GuiUtils import ToolTips, set_icon, BackgroundTaskProgress
|
||||||
from Main import main, get_seed, __version__ as MWVersion
|
from worlds.alttp.Main import main, get_seed, __version__ as MWVersion
|
||||||
from Rom import Sprite
|
from worlds.alttp.Rom import Sprite
|
||||||
from Utils import is_bundled, local_path, output_path, open_file
|
from Utils import is_bundled, local_path, output_path, open_file
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ from prompt_toolkit.patch_stdout import patch_stdout
|
||||||
from NetUtils import Endpoint
|
from NetUtils import Endpoint
|
||||||
import WebUI
|
import WebUI
|
||||||
|
|
||||||
import Regions
|
from worlds.alttp import Regions
|
||||||
import Utils
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,7 @@ import prompt_toolkit
|
||||||
from prompt_toolkit.patch_stdout import patch_stdout
|
from prompt_toolkit.patch_stdout import patch_stdout
|
||||||
from fuzzywuzzy import process as fuzzy_process
|
from fuzzywuzzy import process as fuzzy_process
|
||||||
|
|
||||||
import Items
|
from worlds.alttp import Items, Regions
|
||||||
import Regions
|
|
||||||
import Utils
|
import Utils
|
||||||
from Utils import get_item_name_from_id, get_location_name_from_address, \
|
from Utils import get_item_name_from_id, get_location_name_from_address, \
|
||||||
ReceivedItem, _version_tuple, restricted_loads
|
ReceivedItem, _version_tuple, restricted_loads
|
||||||
|
|
10
Mystery.py
10
Mystery.py
|
@ -11,11 +11,11 @@ import ModuleUpdate
|
||||||
ModuleUpdate.update()
|
ModuleUpdate.update()
|
||||||
|
|
||||||
from Utils import parse_yaml
|
from Utils import parse_yaml
|
||||||
from Rom import get_sprite_from_name
|
from worlds.alttp.Rom import get_sprite_from_name
|
||||||
from EntranceRandomizer import parse_arguments
|
from worlds.alttp.EntranceRandomizer import parse_arguments
|
||||||
from Main import main as ERmain
|
from worlds.alttp.Main import main as ERmain
|
||||||
from Main import get_seed, seeddigits
|
from worlds.alttp.Main import get_seed, seeddigits
|
||||||
from Items import item_name_groups, item_table
|
from worlds.alttp.Items import item_name_groups, item_table
|
||||||
|
|
||||||
|
|
||||||
def mystery_argparse():
|
def mystery_argparse():
|
||||||
|
|
9
Patch.py
9
Patch.py
|
@ -10,8 +10,9 @@ import sys
|
||||||
from typing import Tuple, Optional
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
import Utils
|
import Utils
|
||||||
from Rom import JAP10HASH
|
from worlds.alttp.Rom import JAP10HASH
|
||||||
|
|
||||||
|
current_patch_version = 1
|
||||||
|
|
||||||
def get_base_rom_path(file_name: str = "") -> str:
|
def get_base_rom_path(file_name: str = "") -> str:
|
||||||
options = Utils.get_options()
|
options = Utils.get_options()
|
||||||
|
@ -23,7 +24,7 @@ def get_base_rom_path(file_name: str = "") -> str:
|
||||||
|
|
||||||
|
|
||||||
def get_base_rom_bytes(file_name: str = "") -> bytes:
|
def get_base_rom_bytes(file_name: str = "") -> bytes:
|
||||||
from Rom import read_rom
|
from worlds.alttp.Rom import read_rom
|
||||||
base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
|
base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
|
||||||
if not base_rom_bytes:
|
if not base_rom_bytes:
|
||||||
file_name = get_base_rom_path(file_name)
|
file_name = get_base_rom_path(file_name)
|
||||||
|
@ -42,6 +43,8 @@ def generate_yaml(patch: bytes, metadata: Optional[dict] = None) -> bytes:
|
||||||
patch = yaml.dump({"meta": metadata,
|
patch = yaml.dump({"meta": metadata,
|
||||||
"patch": patch,
|
"patch": patch,
|
||||||
"game": "alttp",
|
"game": "alttp",
|
||||||
|
"compatible_version": 1, # minimum version of patch system expected for patching to be successful
|
||||||
|
"version": current_patch_version,
|
||||||
"base_checksum": JAP10HASH})
|
"base_checksum": JAP10HASH})
|
||||||
return patch.encode(encoding="utf-8-sig")
|
return patch.encode(encoding="utf-8-sig")
|
||||||
|
|
||||||
|
@ -63,6 +66,8 @@ def create_patch_file(rom_file_to_patch: str, server: str = "", destination: str
|
||||||
|
|
||||||
def create_rom_bytes(patch_file: str) -> Tuple[dict, str, bytearray]:
|
def create_rom_bytes(patch_file: str) -> Tuple[dict, str, bytearray]:
|
||||||
data = Utils.parse_yaml(lzma.decompress(load_bytes(patch_file)).decode("utf-8-sig"))
|
data = Utils.parse_yaml(lzma.decompress(load_bytes(patch_file)).decode("utf-8-sig"))
|
||||||
|
if data["compatible_version"] > current_patch_version:
|
||||||
|
raise RuntimeError("Patch file is incompatible with this patcher, likely an update is required.")
|
||||||
patched_data = bsdiff4.patch(get_base_rom_bytes(), data["patch"])
|
patched_data = bsdiff4.patch(get_base_rom_bytes(), data["patch"])
|
||||||
rom_hash = patched_data[int(0x7FC0):int(0x7FD5)]
|
rom_hash = patched_data[int(0x7FC0):int(0x7FD5)]
|
||||||
data["meta"]["hash"] = "".join(chr(x) for x in rom_hash)
|
data["meta"]["hash"] = "".join(chr(x) for x in rom_hash)
|
||||||
|
|
4
Utils.py
4
Utils.py
|
@ -181,12 +181,12 @@ def get_options() -> dict:
|
||||||
|
|
||||||
|
|
||||||
def get_item_name_from_id(code):
|
def get_item_name_from_id(code):
|
||||||
import Items
|
from worlds.alttp import Items
|
||||||
return Items.lookup_id_to_name.get(code, f'Unknown item (ID:{code})')
|
return Items.lookup_id_to_name.get(code, f'Unknown item (ID:{code})')
|
||||||
|
|
||||||
|
|
||||||
def get_location_name_from_address(address):
|
def get_location_name_from_address(address):
|
||||||
import Regions
|
from worlds.alttp import Regions
|
||||||
return Regions.lookup_id_to_name.get(address, f'Unknown location (ID:{address})')
|
return Regions.lookup_id_to_name.get(address, f'Unknown location (ID:{address})')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ import json
|
||||||
|
|
||||||
from flask import request, flash, redirect, url_for, session, render_template
|
from flask import request, flash, redirect, url_for, session, render_template
|
||||||
|
|
||||||
from EntranceRandomizer import parse_arguments
|
from worlds.alttp.EntranceRandomizer import parse_arguments
|
||||||
from Main import main as ERmain
|
from worlds.alttp.Main import main as ERmain
|
||||||
from Main import get_seed, seeddigits
|
from worlds.alttp.Main import get_seed, seeddigits
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
|
@ -3,11 +3,9 @@ import collections
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from werkzeug.exceptions import abort
|
from werkzeug.exceptions import abort
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import Items
|
from worlds.alttp import Items, Regions
|
||||||
import Regions
|
|
||||||
from WebHostLib import app, cache, Room
|
from WebHostLib import app, cache, Room
|
||||||
from Utils import Hint
|
from Utils import Hint
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import argparse
|
||||||
import json
|
import json
|
||||||
from os import listdir
|
from os import listdir
|
||||||
from os.path import isfile, join
|
from os.path import isfile, join
|
||||||
from Rom import Sprite
|
from worlds.alttp.Rom import Sprite
|
||||||
from Gui import get_image_for_sprite
|
from Gui import get_image_for_sprite
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Dump sprite data and .png files to a directory.')
|
parser = argparse.ArgumentParser(description='Dump sprite data and .png files to a directory.')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from BaseClasses import CollectionState
|
from BaseClasses import CollectionState
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
|
|
||||||
|
|
||||||
class TestBase(unittest.TestCase):
|
class TestBase(unittest.TestCase):
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from BaseClasses import World, CollectionState
|
from BaseClasses import MultiWorld, CollectionState
|
||||||
from Dungeons import create_dungeons, get_dungeon_item_pool
|
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
|
||||||
from EntranceShuffle import mandatory_connections, connect_simple
|
from worlds.alttp.EntranceShuffle import mandatory_connections, connect_simple
|
||||||
from ItemPool import difficulties, generate_itempool
|
from worlds.alttp.ItemPool import difficulties, generate_itempool
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from Regions import create_regions, create_shops
|
from worlds.alttp.Regions import create_regions, create_shops
|
||||||
from Rules import set_rules
|
from worlds.alttp.Rules import set_rules
|
||||||
|
|
||||||
|
|
||||||
class TestDungeon(unittest.TestCase):
|
class TestDungeon(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'open'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
|
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'open'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
|
||||||
True, {1:False}, False, None, {1:False})
|
True, {1:False}, False, None, {1:False})
|
||||||
self.starting_regions = [] # Where to start exploring
|
self.starting_regions = [] # Where to start exploring
|
||||||
self.remove_exits = [] # Block dungeon exits
|
self.remove_exits = [] # Block dungeon exits
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
from BaseClasses import World
|
from BaseClasses import MultiWorld
|
||||||
from Dungeons import create_dungeons, get_dungeon_item_pool
|
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
|
||||||
from EntranceShuffle import link_inverted_entrances
|
from worlds.alttp.EntranceShuffle import link_inverted_entrances
|
||||||
from InvertedRegions import create_inverted_regions
|
from worlds.alttp.InvertedRegions import create_inverted_regions
|
||||||
from ItemPool import generate_itempool, difficulties
|
from worlds.alttp.ItemPool import generate_itempool, difficulties
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from Regions import mark_light_world_regions, create_shops
|
from worlds.alttp.Regions import mark_light_world_regions, create_shops
|
||||||
from Rules import set_rules
|
from worlds.alttp.Rules import set_rules
|
||||||
from test.TestBase import TestBase
|
from test.TestBase import TestBase
|
||||||
|
|
||||||
|
|
||||||
class TestInverted(TestBase):
|
class TestInverted(TestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'inverted'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
|
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'inverted'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
|
||||||
True, {1:False}, False, None, {1:False})
|
True, {1:False}, False, None, {1:False})
|
||||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||||
create_inverted_regions(self.world, 1)
|
create_inverted_regions(self.world, 1)
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from BaseClasses import World
|
from BaseClasses import MultiWorld
|
||||||
from Dungeons import create_dungeons
|
from worlds.alttp.Dungeons import create_dungeons
|
||||||
from EntranceShuffle import connect_entrance, Inverted_LW_Entrances, Inverted_LW_Dungeon_Entrances, Inverted_LW_Single_Cave_Doors, Inverted_Old_Man_Entrances, Inverted_DW_Entrances, Inverted_DW_Dungeon_Entrances, Inverted_DW_Single_Cave_Doors, \
|
from worlds.alttp.EntranceShuffle import connect_entrance, Inverted_LW_Entrances, Inverted_LW_Dungeon_Entrances, Inverted_LW_Single_Cave_Doors, Inverted_Old_Man_Entrances, Inverted_DW_Entrances, Inverted_DW_Dungeon_Entrances, Inverted_DW_Single_Cave_Doors, \
|
||||||
Inverted_LW_Entrances_Must_Exit, Inverted_LW_Dungeon_Entrances_Must_Exit, Inverted_Bomb_Shop_Multi_Cave_Doors, Inverted_Bomb_Shop_Single_Cave_Doors, Blacksmith_Single_Cave_Doors, Inverted_Blacksmith_Multi_Cave_Doors
|
Inverted_LW_Entrances_Must_Exit, Inverted_LW_Dungeon_Entrances_Must_Exit, Inverted_Bomb_Shop_Multi_Cave_Doors, Inverted_Bomb_Shop_Single_Cave_Doors, Blacksmith_Single_Cave_Doors, Inverted_Blacksmith_Multi_Cave_Doors
|
||||||
from InvertedRegions import create_inverted_regions
|
from worlds.alttp.InvertedRegions import create_inverted_regions
|
||||||
from ItemPool import difficulties
|
from worlds.alttp.ItemPool import difficulties
|
||||||
from Rules import set_inverted_big_bomb_rules
|
from worlds.alttp.Rules import set_inverted_big_bomb_rules
|
||||||
|
|
||||||
|
|
||||||
class TestInvertedBombRules(unittest.TestCase):
|
class TestInvertedBombRules(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'inverted'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
|
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'inverted'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
|
||||||
True, {1:False}, False, None, {1:False})
|
True, {1:False}, False, None, {1:False})
|
||||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||||
create_inverted_regions(self.world, 1)
|
create_inverted_regions(self.world, 1)
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
from BaseClasses import World
|
from BaseClasses import MultiWorld
|
||||||
from Dungeons import create_dungeons, get_dungeon_item_pool
|
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
|
||||||
from EntranceShuffle import link_inverted_entrances
|
from worlds.alttp.EntranceShuffle import link_inverted_entrances
|
||||||
from InvertedRegions import create_inverted_regions
|
from worlds.alttp.InvertedRegions import create_inverted_regions
|
||||||
from ItemPool import generate_itempool, difficulties
|
from worlds.alttp.ItemPool import generate_itempool, difficulties
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from Regions import mark_light_world_regions, create_shops
|
from worlds.alttp.Regions import mark_light_world_regions, create_shops
|
||||||
from Rules import set_rules
|
from worlds.alttp.Rules import set_rules
|
||||||
from test.TestBase import TestBase
|
from test.TestBase import TestBase
|
||||||
|
|
||||||
|
|
||||||
class TestInvertedOWG(TestBase):
|
class TestInvertedOWG(TestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.world = World(1, {1:'vanilla'}, {1:'owglitches'}, {1:'inverted'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
|
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'owglitches'}, {1: 'inverted'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
|
||||||
True, {1:False}, False, None, {1:False})
|
True, {1:False}, False, None, {1:False})
|
||||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||||
create_inverted_regions(self.world, 1)
|
create_inverted_regions(self.world, 1)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from ItemPool import difficulties
|
from worlds.alttp.ItemPool import difficulties
|
||||||
from test.TestBase import TestBase
|
from test.TestBase import TestBase
|
||||||
|
|
||||||
base_items = 41
|
base_items = 41
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
from BaseClasses import World
|
from BaseClasses import MultiWorld
|
||||||
from Dungeons import create_dungeons, get_dungeon_item_pool
|
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
|
||||||
from EntranceShuffle import link_entrances
|
from worlds.alttp.EntranceShuffle import link_entrances
|
||||||
from InvertedRegions import mark_dark_world_regions
|
from worlds.alttp.InvertedRegions import mark_dark_world_regions
|
||||||
from ItemPool import difficulties, generate_itempool
|
from worlds.alttp.ItemPool import difficulties, generate_itempool
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from Regions import create_regions, create_shops
|
from worlds.alttp.Regions import create_regions, create_shops
|
||||||
from Rules import set_rules
|
from worlds.alttp.Rules import set_rules
|
||||||
from test.TestBase import TestBase
|
from test.TestBase import TestBase
|
||||||
|
|
||||||
|
|
||||||
class TestVanillaOWG(TestBase):
|
class TestVanillaOWG(TestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.world = World(1, {1:'vanilla'}, {1:'owglitches'}, {1:'open'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
|
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'owglitches'}, {1: 'open'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
|
||||||
True, {1:False}, False, None, {1:False})
|
True, {1:False}, False, None, {1:False})
|
||||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||||
create_regions(self.world, 1)
|
create_regions(self.world, 1)
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
from BaseClasses import World
|
from BaseClasses import MultiWorld
|
||||||
from Dungeons import create_dungeons, get_dungeon_item_pool
|
from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool
|
||||||
from EntranceShuffle import link_entrances
|
from worlds.alttp.EntranceShuffle import link_entrances
|
||||||
from InvertedRegions import mark_dark_world_regions
|
from worlds.alttp.InvertedRegions import mark_dark_world_regions
|
||||||
from ItemPool import difficulties, generate_itempool
|
from worlds.alttp.ItemPool import difficulties, generate_itempool
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from Regions import create_regions, create_shops
|
from worlds.alttp.Regions import create_regions, create_shops
|
||||||
from Rules import set_rules
|
from worlds.alttp.Rules import set_rules
|
||||||
from test.TestBase import TestBase
|
from test.TestBase import TestBase
|
||||||
|
|
||||||
|
|
||||||
class TestVanilla(TestBase):
|
class TestVanilla(TestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.world = World(1, {1:'vanilla'}, {1:'noglitches'}, {1:'open'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'},
|
self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'noglitches'}, {1: 'open'}, {1: 'random'}, {1: 'normal'}, {1: 'normal'}, {1:False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'},
|
||||||
True, {1:False}, False, None, {1:False})
|
True, {1:False}, False, None, {1:False})
|
||||||
self.world.difficulty_requirements[1] = difficulties['normal']
|
self.world.difficulty_requirements[1] = difficulties['normal']
|
||||||
create_regions(self.world, 1)
|
create_regions(self.world, 1)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from BaseClasses import Dungeon
|
from BaseClasses import Dungeon
|
||||||
from Bosses import BossFactory
|
from worlds.alttp.Bosses import BossFactory
|
||||||
from Fill import fill_restrictive
|
from Fill import fill_restrictive
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from Regions import lookup_boss_drops
|
from worlds.alttp.Regions import lookup_boss_drops
|
||||||
|
|
||||||
|
|
||||||
def create_dungeons(world, player):
|
def create_dungeons(world, player):
|
|
@ -7,8 +7,8 @@ import textwrap
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from Main import main, get_seed
|
from worlds.alttp.Main import main, get_seed
|
||||||
from Rom import get_sprite_from_name
|
from worlds.alttp.Rom import get_sprite_from_name
|
||||||
from Utils import is_bundled, close_console
|
from Utils import is_bundled, close_console
|
||||||
|
|
||||||
|
|
||||||
|
@ -376,44 +376,3 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
getattr(ret, name)[player] = value
|
getattr(ret, name)[player] = value
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def start():
|
|
||||||
args = parse_arguments(None)
|
|
||||||
|
|
||||||
if is_bundled() and len(sys.argv) == 1:
|
|
||||||
# for the bundled builds, if we have no arguments, the user
|
|
||||||
# probably wants the gui. Users of the bundled build who want the command line
|
|
||||||
# interface shouuld specify at least one option, possibly setting a value to a
|
|
||||||
# default if they like all the defaults
|
|
||||||
from Gui import guiMain
|
|
||||||
close_console()
|
|
||||||
guiMain()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# ToDo: Validate files further than mere existance
|
|
||||||
if not os.path.isfile(args.rom):
|
|
||||||
input(
|
|
||||||
'Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom)
|
|
||||||
sys.exit(1)
|
|
||||||
if any([sprite is not None and not os.path.isfile(sprite) and not get_sprite_from_name(sprite) for sprite in
|
|
||||||
args.sprite.values()]):
|
|
||||||
input('Could not find link sprite sheet at given location. \nPress Enter to exit.')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# set up logger
|
|
||||||
loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[args.loglevel]
|
|
||||||
logging.basicConfig(format='%(message)s', level=loglevel)
|
|
||||||
|
|
||||||
if args.gui:
|
|
||||||
from Gui import guiMain
|
|
||||||
guiMain(args)
|
|
||||||
elif args.count is not None:
|
|
||||||
seed = args.seed
|
|
||||||
for _ in range(args.count):
|
|
||||||
main(seed=seed, args=args)
|
|
||||||
seed = get_seed()
|
|
||||||
else:
|
|
||||||
main(seed=args.seed, args=args)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
start()
|
|
|
@ -1941,7 +1941,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
|
||||||
invalid_cave_connections = defaultdict(set)
|
invalid_cave_connections = defaultdict(set)
|
||||||
|
|
||||||
if world.logic[player] in ['owglitches', 'nologic']:
|
if world.logic[player] in ['owglitches', 'nologic']:
|
||||||
import OverworldGlitchRules
|
from worlds.alttp import OverworldGlitchRules
|
||||||
for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'):
|
for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'):
|
||||||
invalid_connections[entrance] = set()
|
invalid_connections[entrance] = set()
|
||||||
if entrance in must_be_exits:
|
if entrance in must_be_exits:
|
|
@ -1,6 +1,6 @@
|
||||||
import collections
|
import collections
|
||||||
from BaseClasses import RegionType
|
from BaseClasses import RegionType
|
||||||
from Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
|
from worlds.alttp.Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
|
||||||
|
|
||||||
|
|
||||||
def create_inverted_regions(world, player):
|
def create_inverted_regions(world, player):
|
|
@ -2,11 +2,11 @@ from collections import namedtuple
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from BaseClasses import Region, RegionType, ShopType, Location, TakeAny
|
from BaseClasses import Region, RegionType, ShopType, Location, TakeAny
|
||||||
from Bosses import place_bosses
|
from worlds.alttp.Bosses import place_bosses
|
||||||
from Dungeons import get_dungeon_item_pool
|
from worlds.alttp.Dungeons import get_dungeon_item_pool
|
||||||
from EntranceShuffle import connect_entrance
|
from worlds.alttp.EntranceShuffle import connect_entrance
|
||||||
from Fill import FillError, fill_restrictive
|
from Fill import FillError, fill_restrictive
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
|
|
||||||
# This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
|
# This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
|
||||||
# Some basic items that various modes require are placed here, including pendants and crystals. Medallion requirements for the two relevant entrances are also decided.
|
# Some basic items that various modes require are placed here, including pendants and crystals. Medallion requirements for the two relevant entrances are also decided.
|
|
@ -8,16 +8,16 @@ import time
|
||||||
import zlib
|
import zlib
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
|
|
||||||
from BaseClasses import World, CollectionState, Item, Region, Location, Shop
|
from BaseClasses import MultiWorld, CollectionState, Item, Region, Location
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from Regions import create_regions, create_shops, mark_light_world_regions, lookup_vanilla_location_to_entrance
|
from worlds.alttp.Regions import create_regions, create_shops, mark_light_world_regions, lookup_vanilla_location_to_entrance
|
||||||
from InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
from worlds.alttp.InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
||||||
from EntranceShuffle import link_entrances, link_inverted_entrances
|
from worlds.alttp.EntranceShuffle import link_entrances, link_inverted_entrances
|
||||||
from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, get_hash_string
|
from worlds.alttp.Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, get_hash_string
|
||||||
from Rules import set_rules
|
from worlds.alttp.Rules import set_rules
|
||||||
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
from worlds.alttp.Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
||||||
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression
|
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression
|
||||||
from ItemPool import generate_itempool, difficulties, fill_prizes
|
from worlds.alttp.ItemPool import generate_itempool, difficulties, fill_prizes
|
||||||
from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple
|
from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple
|
||||||
import Patch
|
import Patch
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ def main(args, seed=None):
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
|
|
||||||
# initialize the world
|
# initialize the world
|
||||||
world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty,
|
world = MultiWorld(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty,
|
||||||
args.item_functionality, args.timer, args.progressive.copy(), args.goal, args.algorithm,
|
args.item_functionality, args.timer, args.progressive.copy(), args.goal, args.algorithm,
|
||||||
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
|
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
|
||||||
|
|
||||||
|
@ -278,7 +278,7 @@ def main(args, seed=None):
|
||||||
|
|
||||||
# collect ER hint info
|
# collect ER hint info
|
||||||
er_hint_data = {player: {} for player in range(1, world.players + 1) if world.shuffle[player] != "vanilla"}
|
er_hint_data = {player: {} for player in range(1, world.players + 1) if world.shuffle[player] != "vanilla"}
|
||||||
from Regions import RegionType
|
from worlds.alttp.Regions import RegionType
|
||||||
for region in world.regions:
|
for region in world.regions:
|
||||||
if region.player in er_hint_data and region.locations:
|
if region.player in er_hint_data and region.locations:
|
||||||
main_entrance = get_entrance_to_region(region)
|
main_entrance = get_entrance_to_region(region)
|
||||||
|
@ -333,7 +333,7 @@ def main(args, seed=None):
|
||||||
|
|
||||||
def copy_world(world):
|
def copy_world(world):
|
||||||
# ToDo: Not good yet
|
# ToDo: Not good yet
|
||||||
ret = World(world.players, world.shuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
|
ret = MultiWorld(world.players, world.shuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
|
||||||
ret.teams = world.teams
|
ret.teams = world.teams
|
||||||
ret.player_names = copy.deepcopy(world.player_names)
|
ret.player_names = copy.deepcopy(world.player_names)
|
||||||
ret.remote_items = world.remote_items.copy()
|
ret.remote_items = world.remote_items.copy()
|
|
@ -15,17 +15,17 @@ import concurrent.futures
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from BaseClasses import CollectionState, ShopType, Region, Location
|
from BaseClasses import CollectionState, ShopType, Region, Location
|
||||||
from Dungeons import dungeon_music_addresses
|
from worlds.alttp.Dungeons import dungeon_music_addresses
|
||||||
from Regions import location_table
|
from worlds.alttp.Regions import location_table
|
||||||
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
|
from worlds.alttp.Text import MultiByteTextMapper, text_addresses, Credits, TextTable
|
||||||
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
|
from worlds.alttp.Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
|
||||||
|
|
||||||
from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, \
|
from worlds.alttp.Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, \
|
||||||
LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, \
|
LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, \
|
||||||
SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
|
SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
|
||||||
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc, is_bundled
|
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc, is_bundled
|
||||||
from Items import ItemFactory
|
from worlds.alttp.Items import ItemFactory
|
||||||
from EntranceShuffle import door_addresses
|
from worlds.alttp.EntranceShuffle import door_addresses
|
||||||
import Patch
|
import Patch
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -455,7 +455,7 @@ class Sprite(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default_link_sprite():
|
def default_link_sprite():
|
||||||
return Sprite(local_path('data', 'default.zspr'))
|
return Sprite(local_path('../../data', 'default.zspr'))
|
||||||
|
|
||||||
def decode8(self, pos):
|
def decode8(self, pos):
|
||||||
arr = [[0 for _ in range(8)] for _ in range(8)]
|
arr = [[0 for _ in range(8)] for _ in range(8)]
|
||||||
|
@ -1397,7 +1397,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||||
|
|
||||||
# set rom name
|
# set rom name
|
||||||
# 21 bytes
|
# 21 bytes
|
||||||
from Main import __version__
|
from worlds.alttp.Main import __version__
|
||||||
# TODO: Adjust Enemizer to accept AP and AD
|
# TODO: Adjust Enemizer to accept AP and AD
|
||||||
rom.name = bytearray(f'BM{__version__.replace(".", "")[0:3]}_{team + 1}_{player}_{world.seed:09}\0', 'utf8')[:21]
|
rom.name = bytearray(f'BM{__version__.replace(".", "")[0:3]}_{team + 1}_{player}_{world.seed:09}\0', 'utf8')[:21]
|
||||||
rom.name.extend([0] * (21 - len(rom.name)))
|
rom.name.extend([0] * (21 - len(rom.name)))
|
||||||
|
@ -1544,7 +1544,7 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
|
||||||
"randomize_overworld": ow_palettes == 'random'
|
"randomize_overworld": ow_palettes == 'random'
|
||||||
}
|
}
|
||||||
if any(options.values()):
|
if any(options.values()):
|
||||||
data_dir = local_path("data") if is_bundled() else None
|
data_dir = local_path("../../data") if is_bundled() else None
|
||||||
offsets_array = build_offset_collections(options, data_dir)
|
offsets_array = build_offset_collections(options, data_dir)
|
||||||
|
|
||||||
ColorF = z3pr.ColorF
|
ColorF = z3pr.ColorF
|
|
@ -1,10 +1,10 @@
|
||||||
import collections
|
import collections
|
||||||
import logging
|
import logging
|
||||||
import OverworldGlitchRules
|
from worlds.alttp import OverworldGlitchRules
|
||||||
from BaseClasses import RegionType, World, Entrance
|
from BaseClasses import RegionType, MultiWorld, Entrance
|
||||||
from Items import ItemFactory, progression_items, item_name_groups
|
from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups
|
||||||
from OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
|
from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
|
||||||
from Bosses import GanonDefeatRule
|
from worlds.alttp.Bosses import GanonDefeatRule
|
||||||
|
|
||||||
|
|
||||||
def set_rules(world, player):
|
def set_rules(world, player):
|
||||||
|
@ -124,7 +124,7 @@ def add_rule(spot, rule, combine='and'):
|
||||||
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
||||||
|
|
||||||
|
|
||||||
def add_lamp_requirement(world: World, spot, player: int, has_accessible_torch: bool = False):
|
def add_lamp_requirement(world: MultiWorld, spot, player: int, has_accessible_torch: bool = False):
|
||||||
if world.dark_room_logic[player] == "lamp":
|
if world.dark_room_logic[player] == "lamp":
|
||||||
add_rule(spot, lambda state: state.has('Lamp', player))
|
add_rule(spot, lambda state: state.has('Lamp', player))
|
||||||
elif world.dark_room_logic[player] == "torches": # implicitly lamp as well
|
elif world.dark_room_logic[player] == "torches": # implicitly lamp as well
|
||||||
|
@ -1371,7 +1371,7 @@ def set_inverted_big_bomb_rules(world, player):
|
||||||
raise Exception('No logic found for routing from %s to the pyramid.' % bombshop_entrance.name)
|
raise Exception('No logic found for routing from %s to the pyramid.' % bombshop_entrance.name)
|
||||||
|
|
||||||
|
|
||||||
def set_bunny_rules(world: World, player: int, inverted: bool):
|
def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
|
||||||
|
|
||||||
# regions for the exits of multi-entrance caves/drops that bunny cannot pass
|
# regions for the exits of multi-entrance caves/drops that bunny cannot pass
|
||||||
# Note spiral cave and two brothers house are passable in superbunny state for glitch logic with extra requirements.
|
# Note spiral cave and two brothers house are passable in superbunny state for glitch logic with extra requirements.
|
|
@ -0,0 +1,109 @@
|
||||||
|
from BaseClasses import World
|
||||||
|
|
||||||
|
|
||||||
|
class ALTTPWorld(World):
|
||||||
|
def __init__(self, options, slot: int):
|
||||||
|
self._region_cache = {}
|
||||||
|
self.slot = slot
|
||||||
|
self.shuffle = shuffle
|
||||||
|
self.logic = logic
|
||||||
|
self.mode = mode
|
||||||
|
self.swords = swords
|
||||||
|
self.difficulty = difficulty
|
||||||
|
self.difficulty_adjustments = difficulty_adjustments
|
||||||
|
self.timer = timer
|
||||||
|
self.progressive = progressive
|
||||||
|
self.goal = goal
|
||||||
|
self.dungeons = []
|
||||||
|
self.regions = []
|
||||||
|
self.shops = []
|
||||||
|
self.itempool = []
|
||||||
|
self.seed = None
|
||||||
|
self.precollected_items = []
|
||||||
|
self.state = CollectionState(self)
|
||||||
|
self._cached_entrances = None
|
||||||
|
self._cached_locations = None
|
||||||
|
self._entrance_cache = {}
|
||||||
|
self._location_cache = {}
|
||||||
|
self.required_locations = []
|
||||||
|
self.light_world_light_cone = False
|
||||||
|
self.dark_world_light_cone = False
|
||||||
|
self.rupoor_cost = 10
|
||||||
|
self.aga_randomness = True
|
||||||
|
self.lock_aga_door_in_escape = False
|
||||||
|
self.save_and_quit_from_boss = True
|
||||||
|
self.accessibility = accessibility
|
||||||
|
self.shuffle_ganon = shuffle_ganon
|
||||||
|
self.fix_gtower_exit = self.shuffle_ganon
|
||||||
|
self.retro = retro
|
||||||
|
self.custom = custom
|
||||||
|
self.customitemarray: List[int] = customitemarray
|
||||||
|
self.hints = hints
|
||||||
|
self.dynamic_regions = []
|
||||||
|
self.dynamic_locations = []
|
||||||
|
|
||||||
|
|
||||||
|
self.remote_items = False
|
||||||
|
self.required_medallions = ['Ether', 'Quake']
|
||||||
|
self.swamp_patch_required = False
|
||||||
|
self.powder_patch_required = False
|
||||||
|
self.ganon_at_pyramid = True
|
||||||
|
self.ganonstower_vanilla = True
|
||||||
|
|
||||||
|
|
||||||
|
self.can_access_trock_eyebridge = None
|
||||||
|
self.can_access_trock_front = None
|
||||||
|
self.can_access_trock_big_chest = None
|
||||||
|
self.can_access_trock_middle = None
|
||||||
|
self.fix_fake_world = True
|
||||||
|
self.mapshuffle = False
|
||||||
|
self.compassshuffle = False
|
||||||
|
self.keyshuffle = False
|
||||||
|
self.bigkeyshuffle = False
|
||||||
|
self.difficulty_requirements = None
|
||||||
|
self.boss_shuffle = 'none'
|
||||||
|
self.enemy_shuffle = False
|
||||||
|
self.enemy_health = 'default'
|
||||||
|
self.enemy_damage = 'default'
|
||||||
|
self.killable_thieves = False
|
||||||
|
self.tile_shuffle = False
|
||||||
|
self.bush_shuffle = False
|
||||||
|
self.beemizer = 0
|
||||||
|
self.escape_assist = []
|
||||||
|
self.crystals_needed_for_ganon = 7
|
||||||
|
self.crystals_needed_for_gt = 7
|
||||||
|
self.open_pyramid = False
|
||||||
|
self.treasure_hunt_icon = 'Triforce Piece'
|
||||||
|
self.treasure_hunt_count = 0
|
||||||
|
self.clock_mode = False
|
||||||
|
self.can_take_damage = True
|
||||||
|
self.glitch_boots = True
|
||||||
|
self.progression_balancing = True
|
||||||
|
self.local_items = set()
|
||||||
|
self.triforce_pieces_available = 30
|
||||||
|
self.triforce_pieces_required = 20
|
||||||
|
self.shop_shuffle = 'off'
|
||||||
|
self.shuffle_prizes = "g"
|
||||||
|
self.sprite_pool = []
|
||||||
|
self.dark_room_logic = "lamp"
|
||||||
|
self.restrict_dungeon_item_on_boss = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sewer_light_cone(self):
|
||||||
|
return self.mode == "standard"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fix_trock_doors(self):
|
||||||
|
return self.shuffle != 'vanilla' or self.mode == 'inverted'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fix_skullwoods_exit(self):
|
||||||
|
return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fix_palaceofdarkness_exit(self):
|
||||||
|
return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fix_trock_exit(self):
|
||||||
|
return self.shuffle not in {'vanilla', 'simple', 'restricted', 'dungeonssimple'}
|
Loading…
Reference in New Issue