Skip to content

Commit

Permalink
Full Release: Version 6.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
TreZc0 committed Nov 25, 2021
2 parents f0d4447 + 3eaedc5 commit 2b5db17
Show file tree
Hide file tree
Showing 18 changed files with 107 additions and 173 deletions.
31 changes: 13 additions & 18 deletions Fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,37 +388,32 @@ def fill_restrictive(window, worlds, base_search, locations, itempool, count=-1)
max_search.collect_locations()

# perform_access_check checks location reachability
if worlds[0].settings.reachable_locations == 'all':
perform_access_check = True
extra_location_checks = []
elif worlds[0].settings.reachable_locations == 'goals':
# for All Goals Reachable, we have to track whether any goal items have been placed,
# since we then have to start checking their reachability.
perform_access_check = item_to_place.goalitem or not max_search.can_beat_game(scan_for_items=False)
extra_location_checks = [location for world in worlds for location in world.get_filled_locations() if location.item.goalitem]
if worlds[0].check_beatable_only:
if worlds[0].settings.reachable_locations == 'goals':
# If this item is required for a goal, it must be placed somewhere reachable.
# We also need to check to make sure the game is beatable, since custom goals might not imply that.
predicate = lambda state: state.won() and state.has_all_item_goals()
else:
# If the game is not beatable without this item, it must be placed somewhere reachable.
predicate = State.won
perform_access_check = not max_search.can_beat_game(scan_for_items=False, predicate=predicate)
else:
# if any world can not longer be beatable with the remaining items
# then we must check for reachability no matter what.
# This way the reachability test is monotonic. If we were to later
# stop checking, then we could place an item needed in one world
# in an unreachable place in another world.
# scan_for_items would cause an unnecessary copy+collect
perform_access_check = not max_search.can_beat_game(scan_for_items=False)
extra_location_checks = []
# All items must be placed somewhere reachable.
perform_access_check = True

# find a location that the item can be placed. It must be a valid location
# in the world we are placing it (possibly checking for reachability)
spot_to_fill = None
for location in l2cations:
if location.can_fill(max_search.state_list[location.world.id], item_to_place, perform_access_check, extra_location_checks):
if location.can_fill(max_search.state_list[location.world.id], item_to_place, perform_access_check):
# for multiworld, make it so that the location is also reachable
# in the world the item is for. This is to prevent early restrictions
# in one world being placed late in another world. If this is not
# done then one player may be waiting a long time for other players.
if location.world.id != item_to_place.world.id:
try:
source_location = item_to_place.world.get_location(location.name)
if not source_location.can_fill(max_search.state_list[item_to_place.world.id], item_to_place, perform_access_check, extra_location_checks):
if not source_location.can_fill(max_search.state_list[item_to_place.world.id], item_to_place, perform_access_check):
# location wasn't reachable in item's world, so skip it
continue
except KeyError:
Expand Down
5 changes: 4 additions & 1 deletion GUI/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ootr-electron-gui",
"description": "GUI for Ocarina of Time Randomizer",
"version": "6.0.0",
"version": "6.2.0",
"homepage": "https://www.ootrandomizer.com",
"author": "ZeldaSpeedRuns <[email protected]>",
"main": "electron/dist/main.js",
Expand Down Expand Up @@ -82,6 +82,9 @@
"internetEnabled": true
}
},
"engines": {
"node": ">=10 <=15"
},
"dependencies": {
"commander": "2.20.0",
"electron-window-state": "5.0.3",
Expand Down
5 changes: 4 additions & 1 deletion GUI/package_release.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ootr-electron-gui",
"description": "GUI for Ocarina of Time Randomizer",
"version": "6.0.0",
"version": "6.2.0",
"homepage": "https://www.ootrandomizer.com",
"author": "ZeldaSpeedRuns <[email protected]>",
"main": "electron/dist/main.js",
Expand Down Expand Up @@ -83,6 +83,9 @@
"internetEnabled": true
}
},
"engines": {
"node": ">=10 <=15"
},
"dependencies": {
"commander": "2.20.0",
"electron-window-state": "5.0.3",
Expand Down
2 changes: 1 addition & 1 deletion GUI/src/app/@theme/components/footer/footer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class FooterComponent {

promptUpdate() {
this.dialogService.open(ConfirmationWindow, {
autoFocus: true, closeOnBackdropClick: true, closeOnEsc: true, hasBackdrop: true, hasScroll: false, context: { dialogHeader: "New Version Available!", dialogMessage: "You are on version " + this.localVersion + ", and the latest is version " + this.remoteVersion + ". Do you want to download the latest version now?" }
autoFocus: true, closeOnBackdropClick: true, closeOnEsc: true, hasBackdrop: true, hasScroll: false, context: { dialogHeader: "New Version Available!", dialogMessage: "You are using version " + this.localVersion + ", and the latest is version " + this.remoteVersion + ". Do you want to download the latest version now?" + ((this.remoteVersion.includes("Release")) ? "" : " (Note that you are using a development build and therefore will have to redownload and compile the source off GitHub yourself)") }
}).onClose.subscribe(confirmed => {

if (confirmed) {
Expand Down
2 changes: 1 addition & 1 deletion GUI/src/app/providers/GUIGlobal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ export class GUIGlobal {

this.globalEmitter.emit({ name: "local_version_checked", version: res });

let branch = res.includes("Release") ? "master" : "Dev";
let branch = res.includes("Release") ? "release" : "Dev";
var remoteFile = await this.http.get("https://raw.githubusercontent.com/TestRunnerSRL/OoT-Randomizer/" + branch + "/version.py", { responseType: "text" }).toPromise();

let remoteVersion = remoteFile.substr(remoteFile.indexOf("'") + 1);
Expand Down
10 changes: 5 additions & 5 deletions Goals.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self, world, name, hint_text, color, items=None, locations=None, lo
self._item_cache = {}

def copy(self):
new_goal = Goal(self.world, self.name, self.hint_text, self.color, self.items, self.locations, self.lock_locations, self.lock_entrances, self.required_locations)
new_goal = Goal(self.world, self.name, self.hint_text, self.color, self.items, self.locations, self.lock_locations, self.lock_entrances, self.required_locations, True)
return new_goal

def get_item(self, item):
Expand Down Expand Up @@ -98,7 +98,7 @@ def get_goal(self, goal):
def is_beaten(self, search):
# if the category requirements are already satisfied by starting items (such as Links Pocket),
# do not generate hints for other goals in the category
starting_goals = search.can_beat_goals_fast({ self.name: self })
starting_goals = search.beatable_goals_fast({ self.name: self })
return all(map(lambda s: len(starting_goals[self.name]['stateReverse'][s.world.id]) >= self.minimum_goals, search.state_list))


Expand Down Expand Up @@ -184,7 +184,7 @@ def update_goal_items(spoiler):
reachable_goals = {}
# Goals are changed for beatable-only accessibility per-world
category.update_reachable_goals(search, full_search)
reachable_goals = full_search.can_beat_goals_fast({ cat_name: category }, cat_world.id)
reachable_goals = full_search.beatable_goals_fast({ cat_name: category }, cat_world.id)
identified_locations = search_goals({ cat_name: category }, reachable_goals, search, priority_locations, all_locations, item_locations, always_locations, _maybe_set_light_arrows)
# Multiworld can have all goals for one player's bridge entirely
# locked by another player's bridge. Therefore, we can't assume
Expand All @@ -208,7 +208,7 @@ def update_goal_items(spoiler):
full_search.collect_locations()
for cat_name, category in worlds[0].unlocked_goal_categories.items():
category.update_reachable_goals(search, full_search)
reachable_goals = full_search.can_beat_goals_fast(worlds[0].unlocked_goal_categories)
reachable_goals = full_search.beatable_goals_fast(worlds[0].unlocked_goal_categories)
identified_locations = search_goals(worlds[0].unlocked_goal_categories, reachable_goals, search, priority_locations, all_locations, item_locations, always_locations, _maybe_set_light_arrows, search_woth=True)
required_locations.update(identified_locations)
woth_locations = list(required_locations['way of the hero'])
Expand Down Expand Up @@ -300,7 +300,7 @@ def search_goals(categories, reachable_goals, search, priority_locations, all_lo
location.item = None
# copies state! This is very important as we're in the middle of a search
# already, but beneficially, has search it can start from
valid_goals = search.can_beat_goals(categories)
valid_goals = search.beatable_goals(categories)
for cat_name, category in categories.items():
# Exit early if no goals are beatable with category locks
if category.name in reachable_goals and reachable_goals[category.name]:
Expand Down
12 changes: 9 additions & 3 deletions Hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,7 @@ def buildWorldGossipHints(spoiler, world, checkedLocations=None):

if checkedLocations is None:
checkedLocations = set()
checkedAlwaysLocations = set()

stoneIDs = list(gossipLocations.keys())

Expand Down Expand Up @@ -1020,7 +1021,7 @@ def buildWorldGossipHints(spoiler, world, checkedLocations=None):
alwaysLocations = getHintGroup('always', world)
for hint in alwaysLocations:
location = world.get_location(hint.name)
checkedLocations.add(hint.name)
checkedAlwaysLocations.add(hint.name)
if location.item.name in bingoBottlesForHints and world.settings.hint_dist == 'bingo':
always_item = 'Bottle'
else:
Expand Down Expand Up @@ -1059,7 +1060,7 @@ def buildWorldGossipHints(spoiler, world, checkedLocations=None):
raise Exception('User-provided item hints were requested, but copies per named-item hint is zero')
else:
for i in range(0, len(world.named_item_pool)):
hint = get_specific_item_hint(spoiler, world, checkedLocations)
hint = get_specific_item_hint(spoiler, world, checkedLocations | checkedAlwaysLocations)
if hint:
gossip_text, location = hint
place_ok = add_hint(spoiler, world, stoneGroups, gossip_text, hint_dist['named-item'][1], location)
Expand Down Expand Up @@ -1116,7 +1117,12 @@ def buildWorldGossipHints(spoiler, world, checkedLocations=None):
except IndexError:
raise Exception('Not enough valid hints to fill gossip stone locations.')

hint = hint_func[hint_type](spoiler, world, checkedLocations)
allCheckedLocations = checkedLocations | checkedAlwaysLocations
if hint_type == 'barren':
hint = hint_func[hint_type](spoiler, world, checkedLocations)
else:
hint = hint_func[hint_type](spoiler, world, allCheckedLocations)
checkedLocations.update(allCheckedLocations - checkedAlwaysLocations)

if hint == None:
index = hint_types.index(hint_type)
Expand Down
15 changes: 6 additions & 9 deletions Location.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,14 @@ def set_rule(self, lambda_rule):
self.access_rules = [lambda_rule]


def can_fill(self, state, item, check_access=True, extra_location_checks=()):
def can_fill(self, state, item, check_access=True):
if self.minor_only and item.majoritem:
return False
if self.is_disabled() or not self.can_fill_fast(item) or (check_access and not state.search.spot_access(self, 'either')):
return False
if not extra_location_checks:
return True
search_with_this = state.search.copy()
search_with_this.collect(item)
search_with_this.collect_locations(list(chain(search_with_this.progression_locations(), extra_location_checks)))
return all(map(search_with_this.visited, extra_location_checks))
return (
not self.is_disabled() and
self.can_fill_fast(item) and
(not check_access or state.search.spot_access(self, 'either'))
)


def can_fill_fast(self, item, manual=False):
Expand Down
8 changes: 4 additions & 4 deletions LocationList.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def shop_address(shop_id, shelf_id):
("KF Midos Bottom Right Chest", ("Chest", 0x28, 0x03, None, 'Recovery Heart', ("Kokiri Forest", "Forest",))),
("KF Kokiri Sword Chest", ("Chest", 0x55, 0x00, None, 'Kokiri Sword', ("Kokiri Forest", "Forest",))),
("KF Storms Grotto Chest", ("Chest", 0x3E, 0x0C, None, 'Rupees (20)', ("Kokiri Forest", "Forest", "Grottos"))),
("KF Links House Cow", ("NPC", 0x34, 0x15, None, 'Milk', ("KF Links House", "Forest", "Cow", "Minigames"))),
("KF Links House Cow", ("NPC", 0x34, 0x15, None, 'Milk', ("Kokiri Forest", "Forest", "Cow", "Minigames"))),
("KF GS Know It All House", ("GS Token", 0x0C, 0x02, None, 'Gold Skulltula Token', ("Kokiri Forest", "Skulltulas",))),
("KF GS Bean Patch", ("GS Token", 0x0C, 0x01, None, 'Gold Skulltula Token', ("Kokiri Forest", "Skulltulas",))),
("KF GS House of Twins", ("GS Token", 0x0C, 0x04, None, 'Gold Skulltula Token', ("Kokiri Forest", "Skulltulas",))),
Expand Down Expand Up @@ -165,7 +165,7 @@ def shop_address(shop_id, shelf_id):
("HC GS Storms Grotto", ("GS Token", 0x0E, 0x02, None, 'Gold Skulltula Token', ("Hyrule Castle", "Skulltulas", "Grottos"))),

# Lon Lon Ranch
("LLR Talons Chickens", ("NPC", 0x4C, 0x14, None, 'Bottle with Milk', ("Lon Lon Ranch", "Kakariko", "Minigames"))),
("LLR Talons Chickens", ("NPC", 0x4C, 0x14, None, 'Bottle with Milk', ("Lon Lon Ranch", "Minigames"))),
("LLR Freestanding PoH", ("Collectable", 0x4C, 0x01, None, 'Piece of Heart', ("Lon Lon Ranch",))),
("LLR Deku Scrub Grotto Left", ("GrottoNPC", 0xFC, 0x30, None, 'Buy Deku Nut (5)', ("Lon Lon Ranch", "Deku Scrub", "Grottos"))),
("LLR Deku Scrub Grotto Center", ("GrottoNPC", 0xFC, 0x33, None, 'Buy Deku Seeds (30)', ("Lon Lon Ranch", "Deku Scrub", "Grottos"))),
Expand Down Expand Up @@ -368,8 +368,8 @@ def shop_address(shop_id, shelf_id):
("Colossus GS Hill", ("GS Token", 0x15, 0x04, None, 'Gold Skulltula Token', ("Desert Colossus", "Skulltulas",))),

# Outside Ganon's Castle
("OGC Great Fairy Reward", ("Cutscene", 0xFF, 0x15, None, 'Double Defense', ("outside Ganon's Castle", "Market", "Fairies"))),
("OGC GS", ("GS Token", 0x0E, 0x01, None, 'Gold Skulltula Token', ("outside Ganon's Castle", "Skulltulas",))),
("OGC Great Fairy Reward", ("Cutscene", 0xFF, 0x15, None, 'Double Defense', ("Outside Ganon's Castle", "Market", "Fairies"))),
("OGC GS", ("GS Token", 0x0E, 0x01, None, 'Gold Skulltula Token', ("Outside Ganon's Castle", "Skulltulas",))),

## Dungeons
# Deku Tree vanilla
Expand Down
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This is a randomizer for _The Legend of Zelda: Ocarina of Time_ for the Nintendo
* [Settings](#settings)
* [Known Issues](#known-issues)
* [Changelog](#changelog)
* [6.2](#62)
* [6.1](#61)
* [6.0](#60)
* [5.2](#52)
Expand All @@ -24,7 +25,7 @@ https://ootrandomizer.com
If you wish to run the script raw, clone this repository and either run ```Gui.py``` for a
graphical interface or ```OoTRandomizer.py``` for the command line version. They both require Python 3.6+. This will be fully featured,
but the seeds you generate will have different random factors than the bundled release.
To use the GUI, [NodeJS](https://nodejs.org/download/release/v14.15.1/) (v14, with npm) will additionally need to be installed.
To use the GUI, [NodeJS](https://nodejs.org/download/release/v14.15.1/) (v14, with npm) will additionally need to be installed. NodeJS v16+ is currently not supported.
The first time ```Gui.py``` is run it will need to install necessary components, which could take a few minutes. Subsequent instances will run much quicker.
Built-in WAD injection is only supported on the website. To create a WAD from a seed created locally, either use
[gzinject](https://github.com/krimtonz/gzinject/tree/0.2.0) or output a patch file and run that through the website.
Expand Down Expand Up @@ -99,6 +100,32 @@ do that.

## Changelog

### 6.2

#### Bug fixes

* Fix seed generation for multiworld with random trials.
* Fix seed generation for All Goals Reachable.
* Fix a minor optimization for counting needed Skulltula Tokens.
* Fix some erroneous category tags for locations.

#### Other changes
* Allow foolish hints to apply even if an area has an Always hint (but no other types).
* Renamed setting `Enable Useful Cutscenes` to `Enable Specific Glitch-Useful Cutscenes` for clarity.

### 6.1

#### Bug fixes

* Fix seed generation for multiworld with random trials.
* Fix seed generation for All Goals Reachable.
* Fix a minor optimization for counting needed Skulltula Tokens.
* Fix some erroneous category tags for locations.

#### Other changes
* Allow foolish hints to apply even if an area has an Always hint (but no other types).
* Renamed setting `Enable Useful Cutscenes` to `Enable Specific Glitch-Useful Cutscenes` for clarity.

### 6.1

#### New Features
Expand Down
Loading

0 comments on commit 2b5db17

Please sign in to comment.