TFH Futures

This commit is contained in:
Fouton
2021-03-12 16:41:35 -05:00
committed by GitHub
parent fd7728235d
commit 8d933b82de
6 changed files with 76 additions and 92 deletions

View File

@@ -128,6 +128,7 @@ class World(object):
set_player_attr('open_pyramid', False)
set_player_attr('treasure_hunt_icon', 'Triforce Piece')
set_player_attr('treasure_hunt_count', 0)
set_player_attr('treasure_hunt_total', 0)
set_player_attr('potshuffle', False)
set_player_attr('pot_contents', None)
@@ -1377,11 +1378,6 @@ class Door(object):
else:
self.passage = False
def kind(self, world):
if self.roomIndex != -1 and self.doorListPos != -1:
return world.get_room(self.roomIndex, self.player).kind(self)
return None
def __eq__(self, other):
return isinstance(other, self.__class__) and self.name == other.name

106
CLI.py
View File

@@ -6,6 +6,7 @@ import textwrap
import shlex
import sys
import source.classes.constants as CONST
from source.classes.BabelFish import BabelFish
from Utils import update_deprecated_args
@@ -16,7 +17,6 @@ class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
def _get_help_string(self, action):
return textwrap.dedent(action.help)
def parse_cli(argv, no_defaults=False):
def defval(value):
return value if not no_defaults else None
@@ -28,49 +28,44 @@ def parse_cli(argv, no_defaults=False):
fish = BabelFish(lang=lang)
# we need to know how many players we have first
# also if we're loading our own settings file, we should do that now
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--settingsfile', help="input json file of settings", type=str)
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
multiargs, _ = parser.parse_known_args(argv)
if multiargs.settingsfile:
settings = apply_settings_file(settings, multiargs.settingsfile)
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
# get args
args = []
with open(os.path.join("resources", "app", "cli", "args.json")) as argsFile:
args = json.load(argsFile)
for arg in args:
argdata = args[arg]
argname = "--" + arg
argatts = {}
argatts["help"] = "(default: %(default)s)"
if "action" in argdata:
argatts["action"] = argdata["action"]
if "choices" in argdata:
argatts["choices"] = argdata["choices"]
argatts["const"] = argdata["choices"][0]
argatts["default"] = argdata["choices"][0]
argatts["nargs"] = "?"
if arg in settings:
default = settings[arg]
if "type" in argdata and argdata["type"] == "bool":
default = settings[arg] != 0
argatts["default"] = defval(default)
arghelp = fish.translate("cli", "help", arg)
if "help" in argdata and argdata["help"] == "suppress":
argatts["help"] = argparse.SUPPRESS
elif not isinstance(arghelp, str):
argatts["help"] = '\n'.join(arghelp).replace("\\'", "'")
else:
argatts["help"] = arghelp + " " + argatts["help"]
parser.add_argument(argname, **argatts)
with open(os.path.join("resources","app","cli","args.json")) as argsFile:
args = json.load(argsFile)
for arg in args:
argdata = args[arg]
argname = "--" + arg
argatts = {}
argatts["help"] = "(default: %(default)s)"
if "action" in argdata:
argatts["action"] = argdata["action"]
if "choices" in argdata:
argatts["choices"] = argdata["choices"]
argatts["const"] = argdata["choices"][0]
argatts["default"] = argdata["choices"][0]
argatts["nargs"] = "?"
if arg in settings:
default = settings[arg]
if "type" in argdata and argdata["type"] == "bool":
default = settings[arg] != 0
argatts["default"] = defval(default)
arghelp = fish.translate("cli","help",arg)
if "help" in argdata and argdata["help"] == "suppress":
argatts["help"] = argparse.SUPPRESS
elif not isinstance(arghelp,str):
argatts["help"] = '\n'.join(arghelp).replace("\\'","'")
else:
argatts["help"] = arghelp + " " + argatts["help"]
parser.add_argument(argname,**argatts)
parser.add_argument('--seed', default=defval(int(settings["seed"]) if settings["seed"] != "" and settings["seed"] is not None else None), help="\n".join(fish.translate("cli", "help", "seed")), type=int)
parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help="\n".join(fish.translate("cli", "help", "count")), type=int)
parser.add_argument('--seed', default=defval(int(settings["seed"]) if settings["seed"] != "" and settings["seed"] is not None else None), help="\n".join(fish.translate("cli","help","seed")), type=int)
parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help="\n".join(fish.translate("cli","help","count")), type=int)
parser.add_argument('--customitemarray', default={}, help=argparse.SUPPRESS)
# included for backwards compatibility
@@ -78,7 +73,6 @@ def parse_cli(argv, no_defaults=False):
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
parser.add_argument('--securerandom', default=defval(settings["securerandom"]), action='store_true')
parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1))
parser.add_argument('--settingsfile', dest="filename", help="input json file of settings", type=str)
if multiargs.multi:
for player in range(1, multiargs.multi + 1):
@@ -92,11 +86,13 @@ def parse_cli(argv, no_defaults=False):
if multiargs.multi:
defaults = copy.deepcopy(ret)
for player in range(1, multiargs.multi + 1):
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
playerargs = parse_cli(shlex.split(getattr(ret,f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
'triforce_min_difference', 'triforce_goal', 'triforce_pool',
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep',
@@ -110,15 +106,6 @@ def parse_cli(argv, no_defaults=False):
return ret
def apply_settings_file(settings, settings_path):
if os.path.exists(settings_path):
with open(settings_path) as json_file:
data = json.load(json_file)
for k, v in data.items():
settings[k] = v
return settings
def parse_settings():
# set default settings
settings = {
@@ -162,6 +149,15 @@ def parse_settings():
"dungeon_counters": "default",
"mixed_travel": "prevent",
"standardize_palettes": "standardize",
"triforce_pool": 0,
"triforce_goal": 0,
"triforce_pool_min": 0,
"triforce_pool_max": 0,
"triforce_goal_min": 0,
"triforce_goal_max": 0,
"triforce_min_difference": 0,
"code": "",
"multi": 1,
@@ -175,7 +171,7 @@ def parse_settings():
"quickswap": False,
"heartcolor": "red",
"heartbeep": "normal",
"sprite": os.path.join(".", "data", "sprites", "official", "001.link.1.zspr"),
"sprite": os.path.join(".","data","sprites","official","001.link.1.zspr"),
"fastmenu": "normal",
"ow_palettes": "default",
"uw_palettes": "default",
@@ -281,15 +277,17 @@ def parse_settings():
# read saved settings file if it exists and set these
settings_path = os.path.join(".", "resources", "user", "settings.json")
settings = apply_settings_file(settings, settings_path)
if os.path.exists(settings_path):
with(open(settings_path)) as json_file:
data = json.load(json_file)
for k, v in data.items():
settings[k] = v
return settings
# Priority fallback is:
# 1: CLI
# 2: Settings file(s)
# 2: Settings file
# 3: Canned defaults
def get_args_priority(settings_args, gui_args, cli_args):
args = {}
args["settings"] = parse_settings() if settings_args is None else settings_args
@@ -316,7 +314,7 @@ def get_args_priority(settings_args, gui_args, cli_args):
for k in vars(args["cli"]):
load_doesnt_have_key = k not in args["load"]
cli_val = cli[k]
if isinstance(cli_val, dict) and 1 in cli_val:
if isinstance(cli_val,dict) and 1 in cli_val:
cli_val = cli_val[1]
different_val = (k in args["load"] and k in cli) and (str(args["load"][k]) != str(cli_val))
cli_has_empty_dict = k in cli and isinstance(cli_val, dict) and len(cli_val) == 0
@@ -325,9 +323,9 @@ def get_args_priority(settings_args, gui_args, cli_args):
args["load"][k] = cli_val
newArgs = {}
for key in ["settings", "gui", "cli", "load"]:
for key in [ "settings", "gui", "cli", "load" ]:
if args[key]:
if isinstance(args[key], dict):
if isinstance(args[key],dict):
newArgs[key] = argparse.Namespace(**args[key])
else:
newArgs[key] = args[key]

View File

@@ -37,7 +37,7 @@ Difficulty = namedtuple('Difficulty',
['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield',
'basicshield', 'progressivearmor', 'basicarmor', 'swordless',
'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother',
'triforcehunt', 'triforce_pieces_required', 'retro',
'retro',
'extras', 'progressive_sword_limit', 'progressive_shield_limit',
'progressive_armor_limit', 'progressive_bottle_limit',
'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit'])
@@ -60,8 +60,6 @@ difficulties = {
basicbow = ['Bow', 'Silver Arrows'],
timedohko = ['Green Clock'] * 25,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 20,
retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 4,
@@ -87,8 +85,6 @@ difficulties = {
basicbow = ['Bow'] * 2,
timedohko = ['Green Clock'] * 25,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 20,
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 3,
@@ -114,8 +110,6 @@ difficulties = {
basicbow = ['Bow'] * 2,
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 20,
retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15,
extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
progressive_sword_limit = 2,
@@ -262,7 +256,7 @@ def generate_itempool(world, player):
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.customitemarray)
world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999)
else:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.doorShuffle[player])
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.doorShuffle[player])
if player in world.pool_adjustment.keys():
amt = world.pool_adjustment[player]
@@ -320,10 +314,10 @@ def generate_itempool(world, player):
if clock_mode is not None:
world.clock_mode = clock_mode
if treasure_hunt_count is not None:
world.treasure_hunt_count[player] = treasure_hunt_count
if treasure_hunt_icon is not None:
world.treasure_hunt_icon[player] = treasure_hunt_icon
if world.goal[player] == 'triforcehunt':
world.treasure_hunt_icon[player] = 'Triforce Piece'
if world.custom:
world.treasure_hunt_count[player] = treasure_hunt_count
world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player
and ((item.smallkey and world.keyshuffle[player])
@@ -640,13 +634,12 @@ shop_transfer = {'Red Potion': 'Rupees (100)', 'Bee': 'Rupees (5)', 'Blue Potion
}
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, door_shuffle):
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, door_shuffle):
pool = []
placed_items = {}
precollected_items = []
clock_mode = None
treasure_hunt_count = None
treasure_hunt_icon = None
triforcepool = ['Triforce Piece'] * treasure_hunt_total
pool.extend(alwaysitems)
@@ -740,10 +733,8 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r
extraitems -= len(diff.timedohko)
clock_mode = 'countdown-ohko'
if goal == 'triforcehunt':
pool.extend(diff.triforcehunt)
extraitems -= len(diff.triforcehunt)
treasure_hunt_count = diff.triforce_pieces_required
treasure_hunt_icon = 'Triforce Piece'
pool.extend(triforcepool)
extraitems -= len(triforcepool)
for extra in diff.extras:
if extraitems > 0:
@@ -771,7 +762,7 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r
pool.extend(['Small Key (Universal)'])
else:
pool.extend(['Small Key (Universal)'])
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms)
return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms)
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray):
if isinstance(customitemarray,dict) and 1 in customitemarray:

View File

@@ -22,11 +22,11 @@ from RoomData import create_rooms
from Rules import set_rules
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
from Utils import output_path, parse_player_names
__version__ = '0.3.1.5-u'
__version__ = '0.3.1.4-u'
class EnemizerError(RuntimeError):
@@ -86,6 +86,8 @@ def main(args, seed=None, fish=None):
world.keydropshuffle = args.keydropshuffle.copy()
world.mixed_travel = args.mixed_travel.copy()
world.standardize_palettes = args.standardize_palettes.copy()
world.treasure_hunt_count = args.triforce_goal.copy()
world.treasure_hunt_total = args.triforce_pool.copy()
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
@@ -165,9 +167,6 @@ def main(args, seed=None, fish=None):
sell_potions(world, player)
if world.retro[player]:
sell_keys(world, player)
else:
lock_shop_locations(world, player)
logger.info(world.fish.translate("cli","cli","placing.dungeon.prizes"))

View File

@@ -171,9 +171,11 @@ def roll_settings(weights):
ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False
ret.crystals_gt = get_choice('tower_open')
ret.crystals_ganon = get_choice('ganon_open')
if ret.goal == 'triforcehunt':
ret.triforce_goal = random.randint(int(get_choice('triforce_goal_min')),int(get_choice('triforce_goal_max')))
ret.triforce_pool = random.randint(max(int(get_choice('triforce_pool_min')),ret.triforce_goal + int(get_choice('triforce_min_difference'))),int(get_choice('triforce_pool_max')))
ret.mode = get_choice('world_state')
if ret.mode == 'retro':
ret.mode = 'open'
@@ -213,7 +215,8 @@ def roll_settings(weights):
ret.shufflepots = get_choice('pot_shuffle') == 'on'
ret.beemizer = int(get_choice('beemizer')) if 'beemizer' in weights else 0
inventoryweights = weights.get('startinventory', {})
startitems = []
for item in inventoryweights.keys():

7
Rom.py
View File

@@ -16,7 +16,7 @@ from BaseClasses import CollectionState, ShopType, Region, Location, Door, DoorT
from DoorShuffle import compass_data, DROptions, boss_indicator
from Dungeons import dungeon_music_addresses
from KeyDoorShuffle import count_locations_exclude_logic
from Regions import location_table, shop_to_location_table
from Regions import location_table
from RoomData import DoorKind
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
@@ -1568,10 +1568,7 @@ def write_custom_shops(rom, world, player):
break
if world.shopsanity[player] or shop.type == ShopType.TakeAny:
rom.write_byte(0x186560 + shop.sram_address + index, 1)
loc_item = world.get_location(shop_to_location_table[shop.region.name][index], player).item
if not loc_item:
loc_item = ItemFactory(item['item'], player)
item_id = loc_item.code
item_id = ItemFactory(item['item'], player).code
price = int16_as_bytes(item['price'])
replace = ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF
replace_price = int16_as_bytes(item['replacement_price'])