From 031ec9983f0b10c286146eb1f0ea472bbfa60a59 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 25 Mar 2022 14:54:28 -0600 Subject: [PATCH] Forfeit multiworld option fix for shops and pots GT junk fill reworked for pottery lottery --- Fill.py | 42 +++++++++++++++++++++++++++++++++++++----- MultiServer.py | 7 +++---- RELEASENOTES.md | 2 ++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/Fill.py b/Fill.py index 3645e5d9..6171344b 100644 --- a/Fill.py +++ b/Fill.py @@ -2,6 +2,7 @@ import RaceRandom as random import collections import itertools import logging +import math from BaseClasses import CollectionState, FillError, LocationType from Items import ItemFactory @@ -380,12 +381,21 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # fill in gtower locations with trash first for player in range(1, world.players + 1): - if not gftower_trash or not world.ganonstower_vanilla[player] or world.doorShuffle[player] == 'crossed' or world.logic[player] in ['owglitches', 'nologic']: + if (not gftower_trash or not world.ganonstower_vanilla[player] + or world.logic[player] in ['owglitches', 'nologic']): continue - max_trash = 8 if world.algorithm == 'dungeon_only' else 15 - gftower_trash_count = (random.randint(15, 50) if world.goal[player] == 'triforcehunt' else random.randint(0, max_trash)) + gt_count, total_count = calc_trash_locations(world, player) + scale_factor = .75 * (world.crystals_needed_for_gt[player] / 7) + if world.algorithm == 'dungeon_only': + reserved_space = sum(1 for i in progitempool+prioitempool if i.player == player) + max_trash = max(0, min(gt_count, total_count - reserved_space)) + else: + max_trash = gt_count + scaled_trash = math.floor(max_trash * scale_factor) + gftower_trash_count = (random.randint(scaled_trash, max_trash) if world.goal[player] == 'triforcehunt' else random.randint(0, scaled_trash)) - gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player] + gtower_locations = [location for location in fill_locations if location.parent_region.dungeon + and location.parent_region.dungeon.name == 'Ganons Tower' and location.player == player] random.shuffle(gtower_locations) trashcnt = 0 while gtower_locations and restitempool and trashcnt < gftower_trash_count: @@ -449,19 +459,41 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None ensure_good_pots(world) +def calc_trash_locations(world, player): + total_count, gt_count = 0, 0 + for loc in world.get_locations(): + if (loc.player == player and loc.item is None + and (loc.type not in {LocationType.Pot, LocationType.Drop, LocationType.Normal} or not loc.forced_item) + and (loc.type != LocationType.Shop or world.shopsanity[player]) + and loc.parent_region.dungeon): + total_count += 1 + if loc.parent_region.dungeon.name == 'Ganons Tower': + gt_count += 1 + return gt_count, total_count + + def ensure_good_pots(world, write_skips=False): for loc in world.get_locations(): # convert Arrows 5 and Nothing when necessary if (loc.item.name in {'Arrows (5)', 'Nothing'} and (loc.type != LocationType.Pot or loc.item.player != loc.player)): loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player) + # can be placed here by multiworld balancing or shop balancing + # change it to something normal for the player it got swapped to + elif (loc.item.name in {'Chicken', 'Big Magic'} + and (loc.type != LocationType.Pot or loc.item.player != loc.player)): + if loc.type == LocationType.Pot: + loc.item.player = loc.player + else: + loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) # don't write out all pots to spoiler if write_skips: if loc.type == LocationType.Pot and loc.item.name in valid_pot_items: loc.skip = True -invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)'} +invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)', + 'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic'} def fast_fill_helper(world, item_pool, fill_locations): diff --git a/MultiServer.py b/MultiServer.py index b15390dd..c4e8224b 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -159,8 +159,7 @@ def send_new_items(ctx : Context): client.send_index = len(items) def forfeit_player(ctx : Context, team, slot): - all_locations = {values[0] for values in Regions.location_table.values() if type(values[0]) is int} - all_locations.update({values[1] for values in Regions.key_drop_data.values()}) + all_locations = set(ctx.lookup_id_to_name.keys()) notify_all(ctx, "%s (Team #%d) has forfeited" % (ctx.player_names[(team, slot)], team + 1)) register_location_checks(ctx, team, slot, all_locations) @@ -347,8 +346,8 @@ async def console(ctx : Context): def init_lookups(ctx): - ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()} - ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()} + ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()} + ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()} for location, datum in PotShuffle.key_drop_data.items(): type = datum[0] if type == 'Drop': diff --git a/RELEASENOTES.md b/RELEASENOTES.md index eb6428de..07c498f2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -148,6 +148,8 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o #### Volatile * 1.0.1.12 + * Fix for Multiworld forfeits, shops and pot items now included + * Reworked GT Trash Fill. Base rate is 0-75% of locations fill with 7 crystals entrance requirements. Triforce hunt is 75%-100% of locations. The 75% number will decrease based on the crystal entrance requirement. Dungeon_only algorithm caps it based on how many items need to be placed in dungeons. Cross dungeon shuffle will now work with the trash fill. * Inverted bug * Fix for hammerdashing pots, if sprite limit is reached, items won't spawn, error beep won't play either because of other SFX * Killing enemies freeze + hammer results in the droppable item instead of the freeze prize