Merge branch 'main' into breaking_changes
# Conflicts: # Main.py # Utils.py
This commit is contained in:
commit
f7dc21ddcc
15
Main.py
15
Main.py
|
@ -39,11 +39,12 @@ def get_seed(seed=None):
|
||||||
return seed
|
return seed
|
||||||
|
|
||||||
|
|
||||||
seeds: Dict[tuple, str] = dict()
|
|
||||||
def get_same_seed(world: MultiWorld, seed_def: tuple) -> str:
|
def get_same_seed(world: MultiWorld, seed_def: tuple) -> str:
|
||||||
|
seeds: Dict[tuple, str] = getattr(world, "__named_seeds", {})
|
||||||
if seed_def in seeds:
|
if seed_def in seeds:
|
||||||
return seeds[seed_def]
|
return seeds[seed_def]
|
||||||
seeds[seed_def] = str(world.random.randint(0, 2 ** 64))
|
seeds[seed_def] = str(world.random.randint(0, 2 ** 64))
|
||||||
|
world.__named_seeds = seeds
|
||||||
return seeds[seed_def]
|
return seeds[seed_def]
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,9 +122,15 @@ def main(args, seed=None):
|
||||||
world.shuffle[player] = shuffle
|
world.shuffle[player] = shuffle
|
||||||
if shuffle == "vanilla":
|
if shuffle == "vanilla":
|
||||||
world.er_seeds[player] = "vanilla"
|
world.er_seeds[player] = "vanilla"
|
||||||
elif seed.startswith("team-"):
|
elif seed.startswith("group-"): # renamed from team to group to not confuse with existing team name use
|
||||||
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
||||||
else:
|
elif seed.startswith("team-"): # TODO: remove on breaking_changes
|
||||||
|
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
||||||
|
elif not args.race:
|
||||||
|
world.er_seeds[player] = seed
|
||||||
|
elif seed: # race but with a set seed, ignore set seed and use group logic instead
|
||||||
|
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
||||||
|
else: # race but without a set seed
|
||||||
world.er_seeds[player] = seed
|
world.er_seeds[player] = seed
|
||||||
elif world.shuffle[player] == "vanilla":
|
elif world.shuffle[player] == "vanilla":
|
||||||
world.er_seeds[player] = "vanilla"
|
world.er_seeds[player] = "vanilla"
|
||||||
|
@ -307,7 +314,7 @@ def main(args, seed=None):
|
||||||
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
|
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
|
||||||
args.fastmenu[player], args.disablemusic[player], args.sprite[player],
|
args.fastmenu[player], args.disablemusic[player], args.sprite[player],
|
||||||
palettes_options, world, player, True,
|
palettes_options, world, player, True,
|
||||||
reduceflashing=args.reduceflashing[player] if not args.race else True,
|
reduceflashing=args.reduceflashing[player] or args.race,
|
||||||
triforcehud=args.triforcehud[player])
|
triforcehud=args.triforcehud[player])
|
||||||
|
|
||||||
mcsb_name = ''
|
mcsb_name = ''
|
||||||
|
|
73
Mystery.py
73
Mystery.py
|
@ -294,6 +294,8 @@ def prefer_int(input_data: str) -> typing.Union[str, int]:
|
||||||
|
|
||||||
available_boss_names: typing.Set[str] = {boss.lower() for boss in Bosses.boss_table if boss not in
|
available_boss_names: typing.Set[str] = {boss.lower() for boss in Bosses.boss_table if boss not in
|
||||||
{'Agahnim', 'Agahnim2', 'Ganon'}}
|
{'Agahnim', 'Agahnim2', 'Ganon'}}
|
||||||
|
available_boss_locations: typing.Set[str] = {f"{loc.lower()}{f' {level}' if level else ''}" for loc, level in
|
||||||
|
Bosses.boss_location_table}
|
||||||
|
|
||||||
boss_shuffle_options = {None: 'none',
|
boss_shuffle_options = {None: 'none',
|
||||||
'none': 'none',
|
'none': 'none',
|
||||||
|
@ -370,6 +372,42 @@ def roll_triggers(weights: dict) -> dict:
|
||||||
f"Please fix your triggers.") from e
|
f"Please fix your triggers.") from e
|
||||||
return weights
|
return weights
|
||||||
|
|
||||||
|
|
||||||
|
def get_plando_bosses(boss_shuffle: str, plando_options: typing.Set[str]) -> str:
|
||||||
|
if boss_shuffle in boss_shuffle_options:
|
||||||
|
return boss_shuffle_options[boss_shuffle]
|
||||||
|
elif "bosses" in plando_options:
|
||||||
|
options = boss_shuffle.lower().split(";")
|
||||||
|
remainder_shuffle = "none" # vanilla
|
||||||
|
bosses = []
|
||||||
|
for boss in options:
|
||||||
|
if boss in boss_shuffle_options:
|
||||||
|
remainder_shuffle = boss_shuffle_options[boss]
|
||||||
|
elif "-" in boss:
|
||||||
|
loc, boss_name = boss.split("-")
|
||||||
|
if boss_name not in available_boss_names:
|
||||||
|
raise ValueError(f"Unknown Boss name {boss_name}")
|
||||||
|
if loc not in available_boss_locations:
|
||||||
|
raise ValueError(f"Unknown Boss Location {loc}")
|
||||||
|
level = ''
|
||||||
|
if loc.split(" ")[-1] in {"top", "middle", "bottom"}:
|
||||||
|
# split off level
|
||||||
|
loc = loc.split(" ")
|
||||||
|
level = f" {loc[-1]}"
|
||||||
|
loc = " ".join(loc[:-1])
|
||||||
|
loc = loc.title().replace("Of", "of")
|
||||||
|
if not Bosses.can_place_boss(boss_name.title(), loc, level):
|
||||||
|
raise ValueError(f"Cannot place {boss_name} at {loc}{level}")
|
||||||
|
bosses.append(boss)
|
||||||
|
elif boss not in available_boss_names:
|
||||||
|
raise ValueError(f"Unknown Boss name or Boss shuffle option {boss}.")
|
||||||
|
else:
|
||||||
|
bosses.append(boss)
|
||||||
|
return ";".join(bosses + [remainder_shuffle])
|
||||||
|
else:
|
||||||
|
raise Exception(f"Boss Shuffle {boss_shuffle} is unknown and boss plando is turned off.")
|
||||||
|
|
||||||
|
|
||||||
def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("bosses"))):
|
def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("bosses"))):
|
||||||
if "pre_rolled" in weights:
|
if "pre_rolled" in weights:
|
||||||
pre_rolled = weights["pre_rolled"]
|
pre_rolled = weights["pre_rolled"]
|
||||||
|
@ -380,10 +418,25 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
||||||
item["world"],
|
item["world"],
|
||||||
item["from_pool"],
|
item["from_pool"],
|
||||||
item["force"]) for item in pre_rolled["plando_items"]]
|
item["force"]) for item in pre_rolled["plando_items"]]
|
||||||
|
if "items" not in plando_options and pre_rolled["plando_items"]:
|
||||||
|
raise Exception("Item Plando is turned off. Reusing this pre-rolled setting not permitted.")
|
||||||
|
|
||||||
if "plando_connections" in pre_rolled:
|
if "plando_connections" in pre_rolled:
|
||||||
pre_rolled["plando_connections"] = [PlandoConnection(connection["entrance"],
|
pre_rolled["plando_connections"] = [PlandoConnection(connection["entrance"],
|
||||||
connection["exit"],
|
connection["exit"],
|
||||||
connection["direction"]) for connection in pre_rolled["plando_connections"]]
|
connection["direction"]) for connection in pre_rolled["plando_connections"]]
|
||||||
|
if "connections" not in plando_options and pre_rolled["plando_connections"]:
|
||||||
|
raise Exception("Connection Plando is turned off. Reusing this pre-rolled setting not permitted.")
|
||||||
|
|
||||||
|
if "bosses" not in plando_options:
|
||||||
|
try:
|
||||||
|
pre_rolled["shufflebosses"] = get_plando_bosses(pre_rolled["shufflebosses"], plando_options)
|
||||||
|
except Exception as ex:
|
||||||
|
raise Exception("Boss Plando is turned off. Reusing this pre-rolled setting not permitted.") from ex
|
||||||
|
|
||||||
|
if pre_rolled.get("plando_texts") and "texts" not in plando_options:
|
||||||
|
raise Exception("Text Plando is turned off. Reusing this pre-rolled setting not permitted.")
|
||||||
|
|
||||||
return argparse.Namespace(**pre_rolled)
|
return argparse.Namespace(**pre_rolled)
|
||||||
|
|
||||||
if "linked_options" in weights:
|
if "linked_options" in weights:
|
||||||
|
@ -514,23 +567,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
||||||
ret.item_functionality = get_choice('item_functionality', weights)
|
ret.item_functionality = get_choice('item_functionality', weights)
|
||||||
|
|
||||||
boss_shuffle = get_choice('boss_shuffle', weights)
|
boss_shuffle = get_choice('boss_shuffle', weights)
|
||||||
|
ret.shufflebosses = get_plando_bosses(boss_shuffle, plando_options)
|
||||||
if boss_shuffle in boss_shuffle_options:
|
|
||||||
ret.shufflebosses = boss_shuffle_options[boss_shuffle]
|
|
||||||
elif "bosses" in plando_options:
|
|
||||||
options = boss_shuffle.lower().split(";")
|
|
||||||
remainder_shuffle = "none" # vanilla
|
|
||||||
bosses = []
|
|
||||||
for boss in options:
|
|
||||||
if boss in boss_shuffle_options:
|
|
||||||
remainder_shuffle = boss_shuffle_options[boss]
|
|
||||||
elif boss not in available_boss_names and not "-" in boss:
|
|
||||||
raise ValueError(f"Unknown Boss name or Boss shuffle option {boss}.")
|
|
||||||
else:
|
|
||||||
bosses.append(boss)
|
|
||||||
ret.shufflebosses = ";".join(bosses + [remainder_shuffle])
|
|
||||||
else:
|
|
||||||
raise Exception(f"Boss Shuffle {boss_shuffle} is unknown and boss plando is turned off.")
|
|
||||||
|
|
||||||
ret.enemy_shuffle = {'none': False,
|
ret.enemy_shuffle = {'none': False,
|
||||||
'shuffled': 'shuffled',
|
'shuffled': 'shuffled',
|
||||||
|
@ -601,7 +638,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
||||||
ret.required_medallions[index] = {"ether": "Ether", "quake": "Quake", "bombos": "Bombos", "random": "random"}\
|
ret.required_medallions[index] = {"ether": "Ether", "quake": "Quake", "bombos": "Bombos", "random": "random"}\
|
||||||
.get(medallion.lower(), None)
|
.get(medallion.lower(), None)
|
||||||
if not ret.required_medallions[index]:
|
if not ret.required_medallions[index]:
|
||||||
raise Exception(f"unknown Medallion {medallion}")
|
raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}")
|
||||||
|
|
||||||
inventoryweights = weights.get('startinventory', {})
|
inventoryweights = weights.get('startinventory', {})
|
||||||
startitems = []
|
startitems = []
|
||||||
|
|
5
Utils.py
5
Utils.py
|
@ -52,7 +52,10 @@ def snes_to_pc(value):
|
||||||
def parse_player_names(names, players, teams):
|
def parse_player_names(names, players, teams):
|
||||||
names = tuple(n for n in (n.strip() for n in names.split(",")) if n)
|
names = tuple(n for n in (n.strip() for n in names.split(",")) if n)
|
||||||
if len(names) != len(set(names)):
|
if len(names) != len(set(names)):
|
||||||
raise ValueError("Duplicate Player names is not supported.")
|
import collections
|
||||||
|
name_counter = collections.Counter(names)
|
||||||
|
raise ValueError(f"Duplicate Player names is not supported, "
|
||||||
|
f'found multiple "{name_counter.most_common(1)[0][0]}".')
|
||||||
ret = []
|
ret = []
|
||||||
while names or len(ret) < teams:
|
while names or len(ret) < teams:
|
||||||
team = [n[:16] for n in names[:players]]
|
team = [n[:16] for n in names[:players]]
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
border-top-left-radius: 4px;
|
border-top-left-radius: 4px;
|
||||||
border-top-right-radius: 4px;
|
border-top-right-radius: 4px;
|
||||||
padding: 3px 3px 10px;
|
padding: 3px 3px 10px;
|
||||||
width: 260px;
|
width: 284px;
|
||||||
background-color: #42b149;
|
background-color: #42b149;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#location-table{
|
#location-table{
|
||||||
width: 260px;
|
width: 284px;
|
||||||
border-left: 2px solid #000000;
|
border-left: 2px solid #000000;
|
||||||
border-right: 2px solid #000000;
|
border-right: 2px solid #000000;
|
||||||
border-bottom: 2px solid #000000;
|
border-bottom: 2px solid #000000;
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#location-table td.counter{
|
#location-table td.counter{
|
||||||
padding-right: 10px;
|
padding-right: 8px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,12 @@ html{
|
||||||
width: calc(100% - 1rem);
|
width: calc(100% - 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tracker-wrapper a{
|
||||||
|
color: #234ae4;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.table-wrapper{
|
.table-wrapper{
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|
|
@ -44,7 +44,8 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{%- for player, items in players.items() -%}
|
{%- for player, items in players.items() -%}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ room.tracker|suuid }}/{{ team + 1 }}/{{ player }}">{{ loop.index }}</a></td>
|
<td><a href="{{ url_for("getPlayerTracker", tracker=room.tracker,
|
||||||
|
tracked_team=team, tracked_player=player)}}">{{ loop.index }}</a></td>
|
||||||
{%- if (team, loop.index) in video -%}
|
{%- if (team, loop.index) in video -%}
|
||||||
{%- if video[(team, loop.index)][0] == "Twitch" -%}
|
{%- if video[(team, loop.index)][0] == "Twitch" -%}
|
||||||
<td>
|
<td>
|
||||||
|
@ -120,7 +121,8 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{%- for player, checks in players.items() -%}
|
{%- for player, checks in players.items() -%}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ room.tracker|suuid }}/{{ team + 1 }}/{{ player }}">{{ loop.index }}</a></td>
|
<td><a href="{{ url_for("getPlayerTracker", tracker=room.tracker,
|
||||||
|
tracked_team=team, tracked_player=player)}}">{{ loop.index }}</a></td>
|
||||||
<td>{{ player_names[(team, loop.index)]|e }}</td>
|
<td>{{ player_names[(team, loop.index)]|e }}</td>
|
||||||
{%- for area in ordered_areas -%}
|
{%- for area in ordered_areas -%}
|
||||||
{%- set checks_done = checks[area] -%}
|
{%- set checks_done = checks[area] -%}
|
||||||
|
|
|
@ -164,7 +164,7 @@ tracking_names = ["Progressive Sword", "Progressive Bow", "Book of Mudora", "Ham
|
||||||
"Red Boomerang", "Bug Catching Net", "Cape", "Shovel", "Lamp",
|
"Red Boomerang", "Bug Catching Net", "Cape", "Shovel", "Lamp",
|
||||||
"Mushroom", "Magic Powder",
|
"Mushroom", "Magic Powder",
|
||||||
"Cane of Somaria", "Cane of Byrna", "Fire Rod", "Ice Rod", "Bombos", "Ether", "Quake",
|
"Cane of Somaria", "Cane of Byrna", "Fire Rod", "Ice Rod", "Bombos", "Ether", "Quake",
|
||||||
"Bottle", "Triforce"] # TODO make sure this list has what we need and sort it better
|
"Bottle", "Triforce"]
|
||||||
|
|
||||||
default_locations = {
|
default_locations = {
|
||||||
'Light World': {1572864, 1572865, 60034, 1572867, 1572868, 60037, 1572869, 1572866, 60040, 59788, 60046, 60175,
|
'Light World': {1572864, 1572865, 60034, 1572867, 1572868, 60037, 1572869, 1572866, 60040, 59788, 60046, 60175,
|
||||||
|
@ -240,14 +240,18 @@ for item in tracking_names:
|
||||||
|
|
||||||
small_key_ids = {}
|
small_key_ids = {}
|
||||||
big_key_ids = {}
|
big_key_ids = {}
|
||||||
|
ids_small_key = {}
|
||||||
|
ids_big_key = {}
|
||||||
|
|
||||||
for item_name, data in Items.item_table.items():
|
for item_name, data in Items.item_table.items():
|
||||||
if "Key" in item_name:
|
if "Key" in item_name:
|
||||||
area = item_name.split("(")[1][:-1]
|
area = item_name.split("(")[1][:-1]
|
||||||
if "Small" in item_name:
|
if "Small" in item_name:
|
||||||
small_key_ids[area] = data[2]
|
small_key_ids[area] = data[2]
|
||||||
|
ids_small_key[data[2]] = area
|
||||||
else:
|
else:
|
||||||
big_key_ids[area] = data[2]
|
big_key_ids[area] = data[2]
|
||||||
|
ids_big_key[data[2]] = area
|
||||||
|
|
||||||
from MultiServer import get_item_name_from_id
|
from MultiServer import get_item_name_from_id
|
||||||
|
|
||||||
|
@ -318,16 +322,25 @@ def get_static_room_data(room: Room):
|
||||||
for playernumber in range(1, len(names[0]) + 1)}
|
for playernumber in range(1, len(names[0]) + 1)}
|
||||||
player_location_to_area = {playernumber: get_location_table(multidata["checks_in_area"][f'{playernumber}'])
|
player_location_to_area = {playernumber: get_location_table(multidata["checks_in_area"][f'{playernumber}'])
|
||||||
for playernumber in range(1, len(names[0]) + 1)}
|
for playernumber in range(1, len(names[0]) + 1)}
|
||||||
result = locations, names, use_door_tracker, player_checks_in_area, player_location_to_area
|
|
||||||
|
player_big_key_locations = {playernumber: set() for playernumber in range(1, len(names[0]) + 1)}
|
||||||
|
player_small_key_locations = {playernumber: set() for playernumber in range(1, len(names[0]) + 1)}
|
||||||
|
for _, (item_id, item_player) in multidata["locations"]:
|
||||||
|
if item_id in ids_big_key:
|
||||||
|
player_big_key_locations[item_player].add(ids_big_key[item_id])
|
||||||
|
if item_id in ids_small_key:
|
||||||
|
player_small_key_locations[item_player].add(ids_small_key[item_id])
|
||||||
|
|
||||||
|
result = locations, names, use_door_tracker, player_checks_in_area, player_location_to_area, player_big_key_locations, player_small_key_locations
|
||||||
_multidata_cache[room.seed.id] = result
|
_multidata_cache[room.seed.id] = result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tracker/<suuid:tracker>/<int:team>/<int:player>')
|
@app.route('/tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>')
|
||||||
@cache.memoize(timeout=15)
|
@cache.memoize(timeout=15)
|
||||||
def getPlayerTracker(tracker: UUID, team: int, player: int):
|
def getPlayerTracker(tracker: UUID, tracked_team: int, tracked_player: int):
|
||||||
# Team and player must be positive and greater than zero
|
# Team and player must be positive and greater than zero
|
||||||
if team < 1 or player < 1:
|
if tracked_team < 0 or tracked_player < 1:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
room = Room.get(tracker=tracker)
|
room = Room.get(tracker=tracker)
|
||||||
|
@ -335,15 +348,15 @@ def getPlayerTracker(tracker: UUID, team: int, player: int):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
# Collect seed information and pare it down to a single player
|
# Collect seed information and pare it down to a single player
|
||||||
locations, names, use_door_tracker, seed_checks_in_area, player_location_to_area = get_static_room_data(room)
|
locations, names, use_door_tracker, seed_checks_in_area, player_location_to_area, player_big_key_locations, player_small_key_locations = get_static_room_data(room)
|
||||||
player_name = names[team - 1][player - 1]
|
player_name = names[tracked_team][tracked_player - 1]
|
||||||
seed_checks_in_area = seed_checks_in_area[player]
|
seed_checks_in_area = seed_checks_in_area[tracked_player]
|
||||||
player_location_to_area = player_location_to_area[player]
|
location_to_area = player_location_to_area[tracked_player]
|
||||||
inventory = collections.Counter()
|
inventory = collections.Counter()
|
||||||
checks_done = {loc_name: 0 for loc_name in default_locations}
|
checks_done = {loc_name: 0 for loc_name in default_locations}
|
||||||
|
|
||||||
# Add starting items to inventory
|
# Add starting items to inventory
|
||||||
starting_items = room.seed.multidata.get("precollected_items", None)[player - 1]
|
starting_items = room.seed.multidata.get("precollected_items", None)[tracked_player - 1]
|
||||||
if starting_items:
|
if starting_items:
|
||||||
for item_id in starting_items:
|
for item_id in starting_items:
|
||||||
attribute_item_solo(inventory, item_id)
|
attribute_item_solo(inventory, item_id)
|
||||||
|
@ -352,26 +365,24 @@ def getPlayerTracker(tracker: UUID, team: int, player: int):
|
||||||
for (ms_team, ms_player), locations_checked in room.multisave.get("location_checks", {}):
|
for (ms_team, ms_player), locations_checked in room.multisave.get("location_checks", {}):
|
||||||
# logging.info(f"{ms_team}, {ms_player}, {locations_checked}")
|
# logging.info(f"{ms_team}, {ms_player}, {locations_checked}")
|
||||||
# Skip teams and players not matching the request
|
# Skip teams and players not matching the request
|
||||||
if ms_team != (team - 1):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If the player does not have the item, do nothing
|
if ms_team == tracked_team:
|
||||||
for location in locations_checked:
|
# If the player does not have the item, do nothing
|
||||||
if (location, ms_player) not in locations or location not in player_location_to_area:
|
for location in locations_checked:
|
||||||
continue
|
if (location, ms_player) not in locations:
|
||||||
|
continue
|
||||||
|
|
||||||
item, recipient = locations[location, ms_player]
|
item, recipient = locations[location, ms_player]
|
||||||
if recipient == player:
|
if recipient == tracked_player: # a check done for the tracked player
|
||||||
attribute_item_solo(inventory, item)
|
attribute_item_solo(inventory, item)
|
||||||
if ms_player != player:
|
if ms_player == tracked_player: # a check done by the tracked player
|
||||||
continue
|
checks_done[location_to_area[location]] += 1
|
||||||
checks_done[player_location_to_area[location]] += 1
|
checks_done["Total"] += 1
|
||||||
checks_done["Total"] += 1
|
|
||||||
|
|
||||||
# Note the presence of the triforce item
|
# Note the presence of the triforce item
|
||||||
for (ms_team, ms_player), game_state in room.multisave.get("client_game_state", []):
|
for (ms_team, ms_player), game_state in room.multisave.get("client_game_state", []):
|
||||||
# Skip teams and players not matching the request
|
# Skip teams and players not matching the request
|
||||||
if ms_team != (team - 1) or ms_player != player:
|
if ms_team != tracked_team or ms_player != tracked_player:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if game_state:
|
if game_state:
|
||||||
|
@ -462,8 +473,9 @@ def getPlayerTracker(tracker: UUID, team: int, player: int):
|
||||||
sword_url=sword_url, sword_acquired=sword_acquired, gloves_url=gloves_url,
|
sword_url=sword_url, sword_acquired=sword_acquired, gloves_url=gloves_url,
|
||||||
gloves_acquired=gloves_acquired, bow_url=bow_url, bow_acquired=bow_acquired,
|
gloves_acquired=gloves_acquired, bow_url=bow_url, bow_acquired=bow_acquired,
|
||||||
small_key_ids=small_key_ids, big_key_ids=big_key_ids, sp_areas=sp_areas,
|
small_key_ids=small_key_ids, big_key_ids=big_key_ids, sp_areas=sp_areas,
|
||||||
key_locations=key_locations, big_key_locations=big_key_locations, mail_url=mail_url,
|
key_locations=player_small_key_locations[tracked_player],
|
||||||
shield_url=shield_url, shield_acquired=shield_acquired)
|
big_key_locations=player_big_key_locations[tracked_player],
|
||||||
|
mail_url=mail_url, shield_url=shield_url, shield_acquired=shield_acquired)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tracker/<suuid:tracker>')
|
@app.route('/tracker/<suuid:tracker>')
|
||||||
|
@ -472,7 +484,7 @@ def getTracker(tracker: UUID):
|
||||||
room = Room.get(tracker=tracker)
|
room = Room.get(tracker=tracker)
|
||||||
if not room:
|
if not room:
|
||||||
abort(404)
|
abort(404)
|
||||||
locations, names, use_door_tracker, seed_checks_in_area, player_location_to_area = get_static_room_data(room)
|
locations, names, use_door_tracker, seed_checks_in_area, player_location_to_area, player_big_key_locations, player_small_key_locations = get_static_room_data(room)
|
||||||
|
|
||||||
inventory = {teamnumber: {playernumber: collections.Counter() for playernumber in range(1, len(team) + 1)}
|
inventory = {teamnumber: {playernumber: collections.Counter() for playernumber in range(1, len(team) + 1)}
|
||||||
for teamnumber, team in enumerate(names)}
|
for teamnumber, team in enumerate(names)}
|
||||||
|
|
|
@ -90,6 +90,7 @@ entrance_shuffle:
|
||||||
# you can also define entrance shuffle seed, like so:
|
# you can also define entrance shuffle seed, like so:
|
||||||
crossed-1000: 0 # using this method, you can have the same layout as another player and share entrance information
|
crossed-1000: 0 # using this method, you can have the same layout as another player and share entrance information
|
||||||
# however, many other settings like logic, world state, retro etc. may affect the shuffle result as well.
|
# however, many other settings like logic, world state, retro etc. may affect the shuffle result as well.
|
||||||
|
crossed-group-myfriends: 0 # using this method, everyone with "group-myfriends" will share the same seed
|
||||||
goals:
|
goals:
|
||||||
ganon: 50 # Climb GT, defeat Agahnim 2, and then kill Ganon
|
ganon: 50 # Climb GT, defeat Agahnim 2, and then kill Ganon
|
||||||
fast_ganon: 0 # Only killing Ganon is required. However, items may still be placed in GT
|
fast_ganon: 0 # Only killing Ganon is required. However, items may still be placed in GT
|
||||||
|
|
|
@ -147,6 +147,22 @@ boss_table = {
|
||||||
'Agahnim2': ('Agahnim2', AgahnimDefeatRule)
|
'Agahnim2': ('Agahnim2', AgahnimDefeatRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boss_location_table = [
|
||||||
|
['Ganons Tower', 'top'],
|
||||||
|
['Tower of Hera', None],
|
||||||
|
['Skull Woods', None],
|
||||||
|
['Ganons Tower', 'middle'],
|
||||||
|
['Eastern Palace', None],
|
||||||
|
['Desert Palace', None],
|
||||||
|
['Palace of Darkness', None],
|
||||||
|
['Swamp Palace', None],
|
||||||
|
['Thieves Town', None],
|
||||||
|
['Ice Palace', None],
|
||||||
|
['Misery Mire', None],
|
||||||
|
['Turtle Rock', None],
|
||||||
|
['Ganons Tower', 'bottom'],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def can_place_boss(boss: str, dungeon_name: str, level: Optional[str] = None) -> bool:
|
def can_place_boss(boss: str, dungeon_name: str, level: Optional[str] = None) -> bool:
|
||||||
# blacklist approach
|
# blacklist approach
|
||||||
|
@ -183,21 +199,7 @@ def place_bosses(world, player: int):
|
||||||
if world.boss_shuffle[player] == 'none':
|
if world.boss_shuffle[player] == 'none':
|
||||||
return
|
return
|
||||||
# Most to least restrictive order
|
# Most to least restrictive order
|
||||||
boss_locations = [
|
boss_locations = boss_location_table.copy()
|
||||||
['Ganons Tower', 'top'],
|
|
||||||
['Tower of Hera', None],
|
|
||||||
['Skull Woods', None],
|
|
||||||
['Ganons Tower', 'middle'],
|
|
||||||
['Eastern Palace', None],
|
|
||||||
['Desert Palace', None],
|
|
||||||
['Palace of Darkness', None],
|
|
||||||
['Swamp Palace', None],
|
|
||||||
['Thieves Town', None],
|
|
||||||
['Ice Palace', None],
|
|
||||||
['Misery Mire', None],
|
|
||||||
['Turtle Rock', None],
|
|
||||||
['Ganons Tower', 'bottom'],
|
|
||||||
]
|
|
||||||
|
|
||||||
all_bosses = sorted(boss_table.keys()) # sorted to be deterministic on older pythons
|
all_bosses = sorted(boss_table.keys()) # sorted to be deterministic on older pythons
|
||||||
placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']]
|
placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']]
|
||||||
|
@ -217,13 +219,13 @@ def place_bosses(world, player: int):
|
||||||
loc = loc.split(" ")
|
loc = loc.split(" ")
|
||||||
level = loc[-1]
|
level = loc[-1]
|
||||||
loc = " ".join(loc[:-1])
|
loc = " ".join(loc[:-1])
|
||||||
loc = loc.title()
|
loc = loc.title().replace("Of", "of")
|
||||||
if can_place_boss(boss, loc, level) and [loc, level] in boss_locations:
|
if can_place_boss(boss, loc, level) and [loc, level] in boss_locations:
|
||||||
place_boss(world, player, boss, loc, level)
|
place_boss(world, player, boss, loc, level)
|
||||||
already_placed_bosses.append(boss)
|
already_placed_bosses.append(boss)
|
||||||
boss_locations.remove([loc, level])
|
boss_locations.remove([loc, level])
|
||||||
else:
|
else:
|
||||||
Exception("Cannot place", boss, "at", loc, level, "for player", player)
|
raise Exception("Cannot place", boss, "at", loc, level, "for player", player)
|
||||||
else:
|
else:
|
||||||
boss = boss.title()
|
boss = boss.title()
|
||||||
boss_locations, already_placed_bosses = place_where_possible(world, player, boss, boss_locations)
|
boss_locations, already_placed_bosses = place_where_possible(world, player, boss, boss_locations)
|
||||||
|
|
Loading…
Reference in New Issue