Ocarina of Time (#64)
* first commit (not including OoT data files yet)
* added some basic options
* rule parser works now at least
* make sure to commit everything this time
* temporary change to BaseClasses for oot
* overworld location graph builds mostly correctly
* adding oot data files
* commenting out world options until later since they only existed to make the RuleParser work
* conversion functions between AP ids and OOT ids
* world graph outputs
* set scrub prices
* itempool generates, entrances connected, way too many options added
* fixed set_rules and set_shop_rules
* temp baseclasses changes
* Reaches the fill step now, old event-based system retained in case the new way breaks
* Song placements and misc fixes everywhere
* temporary changes to make oot work
* changed root exits for AP fill framework
* prevent infinite recursion due to OoT sharing usage of the address field
* age reachability works hopefully, songs are broken again
* working spoiler log generation on beatable-only
* Logic tricks implemented
* need this for logic tricks
* fixed map/compass being placed on Serenade location
* kill unreachable events before filling the world
* add a bunch of utility functions to prepare for rom patching
* move OptionList into generic options
* fixed some silly bugs with OptionList
* properly seed all random behavior (so far)
* ROM generation working
* fix hints trying to get alttp dungeon hint texts
* continue fixing hints
* add oot to network data package
* change item and location IDs to 66000 and 67000 range respectively
* push removed items to precollected items
* fixed various issues with cross-contamination with multiple world generation
* reenable glitched logic (hopefully)
* glitched world files age-check fix
* cleaned up some get_locations calls
* added token shuffle and scrub shuffle, modified some options slightly to make the parsing work
* reenable MQ dungeons
* fix forest mq exception
* made targeting style an option for now, will be cosmetic later
* reminder to move targeting to cosmetics
* some oot option maintenance
* enabled starting time of day
* fixed issue breaking shop slots in multiworld generation
* added "off" option for text shuffle and hints
* shopsanity functionality restored
* change patch file extension
* remove unnecessary utility functions + imports
* update MIT license
* change option to "patch_uncompressed_rom" instead of "compress_rom"
* compliance with new AutoWorld systems
* Kill only internal events, remove non-internal big poe event in code
* re-add the big poe event and handle it correctly
* remove extra method in Range option
* fix typo
* Starting items, starting with consumables option
* do not remove nonexistent item
* move set_shop_rules to after shop items are placed
* some cleanup
* add retries for song placement
* flagged Skull Mask and Mask of Truth as advancement items
* update OoT to use LogicMixin
* Fixed trying to assign starting items from the wrong players
* fixed song retry step
* improved option handling, comments, and starting item replacements
* DefaultOnToggle writes Yes or No to spoiler
* enable compression of output if Compress executable is present
* clean up compression
* check whether (de)compressor exists before running the process
* allow specification of rom path in host.yaml
* check if decompressed file already exists before decompressing again
* fix triforce hunt generation
* rename all the oot state functions with prefix
* OoT: mark triforce pieces as completion goal for triforce hunt
* added overworld and any-dungeon shuffle for dungeon items
* Hide most unshuffled locations and events from the list of locations in spoiler
* build oot option ranges with a generic function instead of defining each separately
* move oot output-type control to host.yaml instead of individual yamls
* implement dungeon song shuffle
* minor improvements to overworld dungeon item shuffle
* remove random ice trap names in shops, mostly to avoid maintaining a massive censor list
* always output patch file to folder, remove option to generate ROM in preparation for removal
* re-add the fix for infinite recursion due to not being light or dark world
* change AP-sendable to Ocarina of Time model, since the triforce piece has some extra code apparently
* oot: remove item_names and location_names
* oot: minor fixes
* oot: comment out ROM patching
* oot: only add CollectionState objects on creation if actually needed
* main entrance shuffle method and entrances-based rules
* fix entrances based rules
* disable master quest and big poe count options for client compatibility
* use get_player_name instead of get_player_names
* fix OptionList
* fix oot options for new option system
* new coop section in oot rom: expand player names to 16 bytes, write AP_PLAYER_NAME at end of PLAYER_NAMES
* fill AP player name in oot rom with 0 instead of 0xDF
* encode player name with ASCII for fixed-width
* revert oot player name array to 8 bytes per name
* remove Pierre location if fast scarecrow is on
* check player name length
* "free_scarecrow" not "fast_scarecrow"
* OoT locations now properly store the AP ID instead of the oot internal ID
* oot __version__ updates in lockstep with AP version
* pull in unmodified oot cosmetic files
* also grab JSONDump since it's needed apparently
* gather extra needed methods, modify imports
* delete cosmetics log, replace all instances of SettingsList with OOTWorld
* cosmetic options working, except for sound effects (due to ear-safe issues)
* SFX, Music, and Fanfare randomization reenabled
* move OoT data files into the worlds folder
* move Compress and Decompress into oot data folder
* Replace get_all_state with custom method to avoid the cache
* OoT ROM: increment item counter before setting incoming item/player values to 0, preventing desync issues
* set data_version to 0
* make Kokiri Sword shuffle off by default
* reenable "Random Choice" for various cosmetic options
* kill Ruto's Letter turnin if open fountain
also fix for shopsanity
* place Buy Goron/Zora Tunic first in shop shuffle
* make ice traps appear as other items instead of breaking generation
* managed to break ice traps on non-major-only
* only handle ice traps if they are on
* fix shopsanity for non-oot games, and write player name instead of player number
* light arrows hint uses player name instead of player number
* Reenable "skip child zelda" option
* fix entrances_based_rules
* fix ganondorf hint if starting with light arrows
* fix dungeonitem shuffle and shopsanity interaction
* remove has_all_of, has_any_of, count_of in BaseClasses, replace usage with has_all, has_any, has_group
* force local giveable item on ZL if skip_child_zelda and shuffle_song_items is any
* keep bosses and bombchu bowling chus out of data package
* revert workaround for infinite recursion and fix it properly
* fix shared shop id caches during patching process
* fix shop text box overflows, as much as possible
* add default oot host.yaml option
* add .apz5, .n64, .z64 to gitignore
* Properly document and name all (functioning) OOT options
* clean up some imports
* remove unnecessary files from oot's data
* fix typo in gitignore
* readd the Compress and Decompress utilities, since they are needed for generation
* cleanup of imports and some minor optimizations
* increase shop offset for item IDs to 0xCB
* remove shop item AP ids entirely
* prevent triforce pieces for other players from being received by yourself
* add "excluded" property to Location
* Hint system adapted and reenabled; hints still unseeded
* make hints deterministic with lists instead of sets
* do not allow hints to point to Light Arrows on non-vanilla bridge
* foreign locations hint as their full name in OoT rather than their region
* checkedLocations now stores hint names by player ID, so that the same location in different worlds can have hints associated
* consolidate versioning in Utils
* ice traps appear as major items rather than any progression item
* set prescription and claim check as defaults for adult trade item settings
* add oot options to playerSettings
* allow case-insensitive logic tricks in yaml
* fix oot shopsanity option formatting
* Write OoT override info even if local item, enabling local checks to show up immediately in the client
* implement CollectionState.can_live_dmg for oot glitched logic
* filter item names for invalid characters when patching shops
* make ice traps appear according to the settings of the world they are shuffled into, rather than the original world
* set hidden-spoiler items and locations with Shop items to events
* make GF carpenters, Gerudo Card, Malon, ZL, and Impa events if the relevant settings are enabled, preventing them from appearing in the client on game start
* Fix oot Glitched and No Logic generation
* fix indenting
* Greatly reduce displayed cosmetic options
* Change oot data version to 1
* add apz5 distribution to webhost
* print player name if an ALttP dungeon contains a good item for OoT world
* delete unneeded commented code
* remove OcarinaSongs import to satisfy lint
2021-09-02 12:35:05 +00:00
import io
import hashlib
import logging
import os
import struct
import random
from collections import OrderedDict
import urllib . request
from urllib . error import URLError , HTTPError
import json
from enum import Enum
from . HintList import getHint , getHintGroup , Hint , hintExclusions
2021-11-20 15:49:33 +00:00
from . Messages import COLOR_MAP , update_message_by_id
Ocarina of Time (#64)
* first commit (not including OoT data files yet)
* added some basic options
* rule parser works now at least
* make sure to commit everything this time
* temporary change to BaseClasses for oot
* overworld location graph builds mostly correctly
* adding oot data files
* commenting out world options until later since they only existed to make the RuleParser work
* conversion functions between AP ids and OOT ids
* world graph outputs
* set scrub prices
* itempool generates, entrances connected, way too many options added
* fixed set_rules and set_shop_rules
* temp baseclasses changes
* Reaches the fill step now, old event-based system retained in case the new way breaks
* Song placements and misc fixes everywhere
* temporary changes to make oot work
* changed root exits for AP fill framework
* prevent infinite recursion due to OoT sharing usage of the address field
* age reachability works hopefully, songs are broken again
* working spoiler log generation on beatable-only
* Logic tricks implemented
* need this for logic tricks
* fixed map/compass being placed on Serenade location
* kill unreachable events before filling the world
* add a bunch of utility functions to prepare for rom patching
* move OptionList into generic options
* fixed some silly bugs with OptionList
* properly seed all random behavior (so far)
* ROM generation working
* fix hints trying to get alttp dungeon hint texts
* continue fixing hints
* add oot to network data package
* change item and location IDs to 66000 and 67000 range respectively
* push removed items to precollected items
* fixed various issues with cross-contamination with multiple world generation
* reenable glitched logic (hopefully)
* glitched world files age-check fix
* cleaned up some get_locations calls
* added token shuffle and scrub shuffle, modified some options slightly to make the parsing work
* reenable MQ dungeons
* fix forest mq exception
* made targeting style an option for now, will be cosmetic later
* reminder to move targeting to cosmetics
* some oot option maintenance
* enabled starting time of day
* fixed issue breaking shop slots in multiworld generation
* added "off" option for text shuffle and hints
* shopsanity functionality restored
* change patch file extension
* remove unnecessary utility functions + imports
* update MIT license
* change option to "patch_uncompressed_rom" instead of "compress_rom"
* compliance with new AutoWorld systems
* Kill only internal events, remove non-internal big poe event in code
* re-add the big poe event and handle it correctly
* remove extra method in Range option
* fix typo
* Starting items, starting with consumables option
* do not remove nonexistent item
* move set_shop_rules to after shop items are placed
* some cleanup
* add retries for song placement
* flagged Skull Mask and Mask of Truth as advancement items
* update OoT to use LogicMixin
* Fixed trying to assign starting items from the wrong players
* fixed song retry step
* improved option handling, comments, and starting item replacements
* DefaultOnToggle writes Yes or No to spoiler
* enable compression of output if Compress executable is present
* clean up compression
* check whether (de)compressor exists before running the process
* allow specification of rom path in host.yaml
* check if decompressed file already exists before decompressing again
* fix triforce hunt generation
* rename all the oot state functions with prefix
* OoT: mark triforce pieces as completion goal for triforce hunt
* added overworld and any-dungeon shuffle for dungeon items
* Hide most unshuffled locations and events from the list of locations in spoiler
* build oot option ranges with a generic function instead of defining each separately
* move oot output-type control to host.yaml instead of individual yamls
* implement dungeon song shuffle
* minor improvements to overworld dungeon item shuffle
* remove random ice trap names in shops, mostly to avoid maintaining a massive censor list
* always output patch file to folder, remove option to generate ROM in preparation for removal
* re-add the fix for infinite recursion due to not being light or dark world
* change AP-sendable to Ocarina of Time model, since the triforce piece has some extra code apparently
* oot: remove item_names and location_names
* oot: minor fixes
* oot: comment out ROM patching
* oot: only add CollectionState objects on creation if actually needed
* main entrance shuffle method and entrances-based rules
* fix entrances based rules
* disable master quest and big poe count options for client compatibility
* use get_player_name instead of get_player_names
* fix OptionList
* fix oot options for new option system
* new coop section in oot rom: expand player names to 16 bytes, write AP_PLAYER_NAME at end of PLAYER_NAMES
* fill AP player name in oot rom with 0 instead of 0xDF
* encode player name with ASCII for fixed-width
* revert oot player name array to 8 bytes per name
* remove Pierre location if fast scarecrow is on
* check player name length
* "free_scarecrow" not "fast_scarecrow"
* OoT locations now properly store the AP ID instead of the oot internal ID
* oot __version__ updates in lockstep with AP version
* pull in unmodified oot cosmetic files
* also grab JSONDump since it's needed apparently
* gather extra needed methods, modify imports
* delete cosmetics log, replace all instances of SettingsList with OOTWorld
* cosmetic options working, except for sound effects (due to ear-safe issues)
* SFX, Music, and Fanfare randomization reenabled
* move OoT data files into the worlds folder
* move Compress and Decompress into oot data folder
* Replace get_all_state with custom method to avoid the cache
* OoT ROM: increment item counter before setting incoming item/player values to 0, preventing desync issues
* set data_version to 0
* make Kokiri Sword shuffle off by default
* reenable "Random Choice" for various cosmetic options
* kill Ruto's Letter turnin if open fountain
also fix for shopsanity
* place Buy Goron/Zora Tunic first in shop shuffle
* make ice traps appear as other items instead of breaking generation
* managed to break ice traps on non-major-only
* only handle ice traps if they are on
* fix shopsanity for non-oot games, and write player name instead of player number
* light arrows hint uses player name instead of player number
* Reenable "skip child zelda" option
* fix entrances_based_rules
* fix ganondorf hint if starting with light arrows
* fix dungeonitem shuffle and shopsanity interaction
* remove has_all_of, has_any_of, count_of in BaseClasses, replace usage with has_all, has_any, has_group
* force local giveable item on ZL if skip_child_zelda and shuffle_song_items is any
* keep bosses and bombchu bowling chus out of data package
* revert workaround for infinite recursion and fix it properly
* fix shared shop id caches during patching process
* fix shop text box overflows, as much as possible
* add default oot host.yaml option
* add .apz5, .n64, .z64 to gitignore
* Properly document and name all (functioning) OOT options
* clean up some imports
* remove unnecessary files from oot's data
* fix typo in gitignore
* readd the Compress and Decompress utilities, since they are needed for generation
* cleanup of imports and some minor optimizations
* increase shop offset for item IDs to 0xCB
* remove shop item AP ids entirely
* prevent triforce pieces for other players from being received by yourself
* add "excluded" property to Location
* Hint system adapted and reenabled; hints still unseeded
* make hints deterministic with lists instead of sets
* do not allow hints to point to Light Arrows on non-vanilla bridge
* foreign locations hint as their full name in OoT rather than their region
* checkedLocations now stores hint names by player ID, so that the same location in different worlds can have hints associated
* consolidate versioning in Utils
* ice traps appear as major items rather than any progression item
* set prescription and claim check as defaults for adult trade item settings
* add oot options to playerSettings
* allow case-insensitive logic tricks in yaml
* fix oot shopsanity option formatting
* Write OoT override info even if local item, enabling local checks to show up immediately in the client
* implement CollectionState.can_live_dmg for oot glitched logic
* filter item names for invalid characters when patching shops
* make ice traps appear according to the settings of the world they are shuffled into, rather than the original world
* set hidden-spoiler items and locations with Shop items to events
* make GF carpenters, Gerudo Card, Malon, ZL, and Impa events if the relevant settings are enabled, preventing them from appearing in the client on game start
* Fix oot Glitched and No Logic generation
* fix indenting
* Greatly reduce displayed cosmetic options
* Change oot data version to 1
* add apz5 distribution to webhost
* print player name if an ALttP dungeon contains a good item for OoT world
* delete unneeded commented code
* remove OcarinaSongs import to satisfy lint
2021-09-02 12:35:05 +00:00
from . TextBox import line_wrap
from . Utils import data_path , read_json
bingoBottlesForHints = (
" Bottle " , " Bottle with Red Potion " , " Bottle with Green Potion " , " Bottle with Blue Potion " ,
" Bottle with Fairy " , " Bottle with Fish " , " Bottle with Blue Fire " , " Bottle with Bugs " ,
" Bottle with Big Poe " , " Bottle with Poe " ,
)
defaultHintDists = [
' balanced.json ' , ' bingo.json ' , ' ddr.json ' , ' scrubs.json ' , ' strong.json ' , ' tournament.json ' , ' useless.json ' , ' very_strong.json '
]
class RegionRestriction ( Enum ) :
NONE = 0 ,
DUNGEON = 1 ,
OVERWORLD = 2 ,
class GossipStone ( ) :
def __init__ ( self , name , location ) :
self . name = name
self . location = location
self . reachable = True
class GossipText ( ) :
def __init__ ( self , text , colors = None , prefix = " They say that " ) :
text = prefix + text
text = text [ : 1 ] . upper ( ) + text [ 1 : ]
self . text = text
self . colors = colors
def to_json ( self ) :
return { ' text ' : self . text , ' colors ' : self . colors }
def __str__ ( self ) :
return get_raw_text ( line_wrap ( colorText ( self ) ) )
# Abbreviations
# DMC Death Mountain Crater
# DMT Death Mountain Trail
# GC Goron City
# GV Gerudo Valley
# HC Hyrule Castle
# HF Hyrule Field
# KF Kokiri Forest
# LH Lake Hylia
# LW Lost Woods
# SFM Sacred Forest Meadow
# ToT Temple of Time
# ZD Zora's Domain
# ZF Zora's Fountain
# ZR Zora's River
gossipLocations = {
0x0405 : GossipStone ( ' DMC (Bombable Wall) ' , ' DMC Gossip Stone ' ) ,
0x0404 : GossipStone ( ' DMT (Biggoron) ' , ' DMT Gossip Stone ' ) ,
0x041A : GossipStone ( ' Colossus (Spirit Temple) ' , ' Colossus Gossip Stone ' ) ,
0x0414 : GossipStone ( ' Dodongos Cavern (Bombable Wall) ' , ' Dodongos Cavern Gossip Stone ' ) ,
0x0411 : GossipStone ( ' GV (Waterfall) ' , ' GV Gossip Stone ' ) ,
0x0415 : GossipStone ( ' GC (Maze) ' , ' GC Maze Gossip Stone ' ) ,
0x0419 : GossipStone ( ' GC (Medigoron) ' , ' GC Medigoron Gossip Stone ' ) ,
0x040A : GossipStone ( ' Graveyard (Shadow Temple) ' , ' Graveyard Gossip Stone ' ) ,
0x0412 : GossipStone ( ' HC (Malon) ' , ' HC Malon Gossip Stone ' ) ,
0x040B : GossipStone ( ' HC (Rock Wall) ' , ' HC Rock Wall Gossip Stone ' ) ,
0x0413 : GossipStone ( ' HC (Storms Grotto) ' , ' HC Storms Grotto Gossip Stone ' ) ,
0x041F : GossipStone ( ' KF (Deku Tree Left) ' , ' KF Deku Tree Gossip Stone (Left) ' ) ,
0x0420 : GossipStone ( ' KF (Deku Tree Right) ' , ' KF Deku Tree Gossip Stone (Right) ' ) ,
0x041E : GossipStone ( ' KF (Outside Storms) ' , ' KF Gossip Stone ' ) ,
0x0403 : GossipStone ( ' LH (Lab) ' , ' LH Lab Gossip Stone ' ) ,
0x040F : GossipStone ( ' LH (Southeast Corner) ' , ' LH Gossip Stone (Southeast) ' ) ,
0x0408 : GossipStone ( ' LH (Southwest Corner) ' , ' LH Gossip Stone (Southwest) ' ) ,
0x041D : GossipStone ( ' LW (Bridge) ' , ' LW Gossip Stone ' ) ,
0x0416 : GossipStone ( ' SFM (Maze Lower) ' , ' SFM Maze Gossip Stone (Lower) ' ) ,
0x0417 : GossipStone ( ' SFM (Maze Upper) ' , ' SFM Maze Gossip Stone (Upper) ' ) ,
0x041C : GossipStone ( ' SFM (Saria) ' , ' SFM Saria Gossip Stone ' ) ,
0x0406 : GossipStone ( ' ToT (Left) ' , ' ToT Gossip Stone (Left) ' ) ,
0x0407 : GossipStone ( ' ToT (Left-Center) ' , ' ToT Gossip Stone (Left-Center) ' ) ,
0x0410 : GossipStone ( ' ToT (Right) ' , ' ToT Gossip Stone (Right) ' ) ,
0x040E : GossipStone ( ' ToT (Right-Center) ' , ' ToT Gossip Stone (Right-Center) ' ) ,
0x0409 : GossipStone ( ' ZD (Mweep) ' , ' ZD Gossip Stone ' ) ,
0x0401 : GossipStone ( ' ZF (Fairy) ' , ' ZF Fairy Gossip Stone ' ) ,
0x0402 : GossipStone ( ' ZF (Jabu) ' , ' ZF Jabu Gossip Stone ' ) ,
0x040D : GossipStone ( ' ZR (Near Grottos) ' , ' ZR Near Grottos Gossip Stone ' ) ,
0x040C : GossipStone ( ' ZR (Near Domain) ' , ' ZR Near Domain Gossip Stone ' ) ,
0x041B : GossipStone ( ' HF (Cow Grotto) ' , ' HF Cow Grotto Gossip Stone ' ) ,
0x0430 : GossipStone ( ' HF (Near Market Grotto) ' , ' HF Near Market Grotto Gossip Stone ' ) ,
0x0432 : GossipStone ( ' HF (Southeast Grotto) ' , ' HF Southeast Grotto Gossip Stone ' ) ,
0x0433 : GossipStone ( ' HF (Open Grotto) ' , ' HF Open Grotto Gossip Stone ' ) ,
0x0438 : GossipStone ( ' Kak (Open Grotto) ' , ' Kak Open Grotto Gossip Stone ' ) ,
0x0439 : GossipStone ( ' ZR (Open Grotto) ' , ' ZR Open Grotto Gossip Stone ' ) ,
0x043C : GossipStone ( ' KF (Storms Grotto) ' , ' KF Storms Grotto Gossip Stone ' ) ,
0x0444 : GossipStone ( ' LW (Near Shortcuts Grotto) ' , ' LW Near Shortcuts Grotto Gossip Stone ' ) ,
0x0447 : GossipStone ( ' DMT (Storms Grotto) ' , ' DMT Storms Grotto Gossip Stone ' ) ,
0x044A : GossipStone ( ' DMC (Upper Grotto) ' , ' DMC Upper Grotto Gossip Stone ' ) ,
}
gossipLocations_reversemap = {
stone . name : stone_id for stone_id , stone in gossipLocations . items ( )
}
def getItemGenericName ( item ) :
if item . game != " Ocarina of Time " :
return item . name
elif item . dungeonitem :
return item . type
else :
return item . name
def isRestrictedDungeonItem ( dungeon , item ) :
if ( item . map or item . compass ) and dungeon . world . shuffle_mapcompass == ' dungeon ' :
return item in dungeon . dungeon_items
if item . type == ' SmallKey ' and dungeon . world . shuffle_smallkeys == ' dungeon ' :
return item in dungeon . small_keys
if item . type == ' BossKey ' and dungeon . world . shuffle_bosskeys == ' dungeon ' :
return item in dungeon . boss_key
if item . type == ' GanonBossKey ' and dungeon . world . shuffle_ganon_bosskey == ' dungeon ' :
return item in dungeon . boss_key
return False
# Attach a player name to the item or location text.
# If the associated player of the item/location and the world are the same, does nothing.
# Otherwise, attaches the object's player's name to the string.
def attach_name ( text , hinted_object , world ) :
if hinted_object . player == world . player :
return text
return f " { text } for { world . world . get_player_name ( hinted_object . player ) } "
def add_hint ( world , groups , gossip_text , count , location = None , force_reachable = False ) :
world . hint_rng . shuffle ( groups )
skipped_groups = [ ]
duplicates = [ ]
first = True
success = True
# early failure if not enough
if len ( groups ) < int ( count ) :
return False
# Randomly round up, if we have enough groups left
total = int ( world . hint_rng . random ( ) + count ) if len ( groups ) > count else int ( count )
while total :
if groups :
group = groups . pop ( 0 )
if any ( map ( lambda id : gossipLocations [ id ] . reachable , group ) ) :
stone_names = [ gossipLocations [ id ] . location for id in group ]
# stone_locations = [world.get_location(stone_name) for stone_name in stone_names]
# Taking out all checks on gossip stone reachability and hint logic
if not first or True : # or any(map(lambda stone_location: can_reach_hint(worlds, stone_location, location), stone_locations)):
# if first and location:
# # just name the event item after the gossip stone directly
# event_item = None
# for i, stone_name in enumerate(stone_names):
# # place the same event item in each location in the group
# if event_item is None:
# event_item = MakeEventItem(stone_name, stone_locations[i], event_item)
# else:
# MakeEventItem(stone_name, stone_locations[i], event_item)
# # This mostly guarantees that we don't lock the player out of an item hint
# # by establishing a (hint -> item) -> hint -> item -> (first hint) loop
# location.add_rule(world.parser.parse_rule(repr(event_item.name)))
total - = 1
first = False
for id in group :
world . gossip_hints [ id ] = gossip_text
# Immediately start choosing duplicates from stones we passed up earlier
while duplicates and total :
group = duplicates . pop ( 0 )
total - = 1
for id in group :
world . gossip_hints [ id ] = gossip_text
else :
# Temporarily skip this stone but consider it for duplicates
duplicates . append ( group )
else :
if not force_reachable :
# The stones are not readable at all in logic, so we ignore any kind of logic here
if not first :
total - = 1
for id in group :
world . gossip_hints [ id ] = gossip_text
else :
# Temporarily skip this stone but consider it for duplicates
duplicates . append ( group )
else :
# If flagged to guarantee reachable, then skip
# If no stones are reachable, then this will place nothing
skipped_groups . append ( group )
else :
# Out of groups
if not force_reachable and len ( duplicates ) > = total :
# Didn't find any appropriate stones for this hint, but maybe enough completely unreachable ones.
# We'd rather not use reachable stones for this.
unr = [ group for group in duplicates if all ( map ( lambda id : not gossipLocations [ id ] . reachable , group ) ) ]
if len ( unr ) > = total :
duplicates = [ group for group in duplicates if group not in unr [ : total ] ]
for group in unr [ : total ] :
for id in group :
world . gossip_hints [ id ] = gossip_text
# Success
break
# Failure
success = False
break
groups . extend ( duplicates )
groups . extend ( skipped_groups )
return success
def writeGossipStoneHints ( world , messages ) :
for id , gossip_text in world . gossip_hints . items ( ) :
update_message_by_id ( messages , id , str ( gossip_text ) , 0x23 )
def filterTrailingSpace ( text ) :
if text . endswith ( ' & ' ) :
return text [ : - 1 ]
else :
return text
hintPrefixes = [
' a few ' ,
' some ' ,
' plenty of ' ,
' a ' ,
' an ' ,
' the ' ,
' ' ,
]
def getSimpleHintNoPrefix ( item ) :
hint = getHint ( item . name , True ) . text
for prefix in hintPrefixes :
if hint . startswith ( prefix ) :
# return without the prefix
return hint [ len ( prefix ) : ]
# no prefex
return hint
def colorText ( gossip_text ) :
text = gossip_text . text
colors = list ( gossip_text . colors ) if gossip_text . colors is not None else [ ]
color = ' White '
while ' # ' in text :
splitText = text . split ( ' # ' , 2 )
if len ( colors ) > 0 :
color = colors . pop ( )
for prefix in hintPrefixes :
if splitText [ 1 ] . startswith ( prefix ) :
splitText [ 0 ] + = splitText [ 1 ] [ : len ( prefix ) ]
splitText [ 1 ] = splitText [ 1 ] [ len ( prefix ) : ]
break
2021-11-20 15:49:33 +00:00
splitText [ 1 ] = ' \x05 ' + COLOR_MAP [ color ] + splitText [ 1 ] + ' \x05 \x40 '
Ocarina of Time (#64)
* first commit (not including OoT data files yet)
* added some basic options
* rule parser works now at least
* make sure to commit everything this time
* temporary change to BaseClasses for oot
* overworld location graph builds mostly correctly
* adding oot data files
* commenting out world options until later since they only existed to make the RuleParser work
* conversion functions between AP ids and OOT ids
* world graph outputs
* set scrub prices
* itempool generates, entrances connected, way too many options added
* fixed set_rules and set_shop_rules
* temp baseclasses changes
* Reaches the fill step now, old event-based system retained in case the new way breaks
* Song placements and misc fixes everywhere
* temporary changes to make oot work
* changed root exits for AP fill framework
* prevent infinite recursion due to OoT sharing usage of the address field
* age reachability works hopefully, songs are broken again
* working spoiler log generation on beatable-only
* Logic tricks implemented
* need this for logic tricks
* fixed map/compass being placed on Serenade location
* kill unreachable events before filling the world
* add a bunch of utility functions to prepare for rom patching
* move OptionList into generic options
* fixed some silly bugs with OptionList
* properly seed all random behavior (so far)
* ROM generation working
* fix hints trying to get alttp dungeon hint texts
* continue fixing hints
* add oot to network data package
* change item and location IDs to 66000 and 67000 range respectively
* push removed items to precollected items
* fixed various issues with cross-contamination with multiple world generation
* reenable glitched logic (hopefully)
* glitched world files age-check fix
* cleaned up some get_locations calls
* added token shuffle and scrub shuffle, modified some options slightly to make the parsing work
* reenable MQ dungeons
* fix forest mq exception
* made targeting style an option for now, will be cosmetic later
* reminder to move targeting to cosmetics
* some oot option maintenance
* enabled starting time of day
* fixed issue breaking shop slots in multiworld generation
* added "off" option for text shuffle and hints
* shopsanity functionality restored
* change patch file extension
* remove unnecessary utility functions + imports
* update MIT license
* change option to "patch_uncompressed_rom" instead of "compress_rom"
* compliance with new AutoWorld systems
* Kill only internal events, remove non-internal big poe event in code
* re-add the big poe event and handle it correctly
* remove extra method in Range option
* fix typo
* Starting items, starting with consumables option
* do not remove nonexistent item
* move set_shop_rules to after shop items are placed
* some cleanup
* add retries for song placement
* flagged Skull Mask and Mask of Truth as advancement items
* update OoT to use LogicMixin
* Fixed trying to assign starting items from the wrong players
* fixed song retry step
* improved option handling, comments, and starting item replacements
* DefaultOnToggle writes Yes or No to spoiler
* enable compression of output if Compress executable is present
* clean up compression
* check whether (de)compressor exists before running the process
* allow specification of rom path in host.yaml
* check if decompressed file already exists before decompressing again
* fix triforce hunt generation
* rename all the oot state functions with prefix
* OoT: mark triforce pieces as completion goal for triforce hunt
* added overworld and any-dungeon shuffle for dungeon items
* Hide most unshuffled locations and events from the list of locations in spoiler
* build oot option ranges with a generic function instead of defining each separately
* move oot output-type control to host.yaml instead of individual yamls
* implement dungeon song shuffle
* minor improvements to overworld dungeon item shuffle
* remove random ice trap names in shops, mostly to avoid maintaining a massive censor list
* always output patch file to folder, remove option to generate ROM in preparation for removal
* re-add the fix for infinite recursion due to not being light or dark world
* change AP-sendable to Ocarina of Time model, since the triforce piece has some extra code apparently
* oot: remove item_names and location_names
* oot: minor fixes
* oot: comment out ROM patching
* oot: only add CollectionState objects on creation if actually needed
* main entrance shuffle method and entrances-based rules
* fix entrances based rules
* disable master quest and big poe count options for client compatibility
* use get_player_name instead of get_player_names
* fix OptionList
* fix oot options for new option system
* new coop section in oot rom: expand player names to 16 bytes, write AP_PLAYER_NAME at end of PLAYER_NAMES
* fill AP player name in oot rom with 0 instead of 0xDF
* encode player name with ASCII for fixed-width
* revert oot player name array to 8 bytes per name
* remove Pierre location if fast scarecrow is on
* check player name length
* "free_scarecrow" not "fast_scarecrow"
* OoT locations now properly store the AP ID instead of the oot internal ID
* oot __version__ updates in lockstep with AP version
* pull in unmodified oot cosmetic files
* also grab JSONDump since it's needed apparently
* gather extra needed methods, modify imports
* delete cosmetics log, replace all instances of SettingsList with OOTWorld
* cosmetic options working, except for sound effects (due to ear-safe issues)
* SFX, Music, and Fanfare randomization reenabled
* move OoT data files into the worlds folder
* move Compress and Decompress into oot data folder
* Replace get_all_state with custom method to avoid the cache
* OoT ROM: increment item counter before setting incoming item/player values to 0, preventing desync issues
* set data_version to 0
* make Kokiri Sword shuffle off by default
* reenable "Random Choice" for various cosmetic options
* kill Ruto's Letter turnin if open fountain
also fix for shopsanity
* place Buy Goron/Zora Tunic first in shop shuffle
* make ice traps appear as other items instead of breaking generation
* managed to break ice traps on non-major-only
* only handle ice traps if they are on
* fix shopsanity for non-oot games, and write player name instead of player number
* light arrows hint uses player name instead of player number
* Reenable "skip child zelda" option
* fix entrances_based_rules
* fix ganondorf hint if starting with light arrows
* fix dungeonitem shuffle and shopsanity interaction
* remove has_all_of, has_any_of, count_of in BaseClasses, replace usage with has_all, has_any, has_group
* force local giveable item on ZL if skip_child_zelda and shuffle_song_items is any
* keep bosses and bombchu bowling chus out of data package
* revert workaround for infinite recursion and fix it properly
* fix shared shop id caches during patching process
* fix shop text box overflows, as much as possible
* add default oot host.yaml option
* add .apz5, .n64, .z64 to gitignore
* Properly document and name all (functioning) OOT options
* clean up some imports
* remove unnecessary files from oot's data
* fix typo in gitignore
* readd the Compress and Decompress utilities, since they are needed for generation
* cleanup of imports and some minor optimizations
* increase shop offset for item IDs to 0xCB
* remove shop item AP ids entirely
* prevent triforce pieces for other players from being received by yourself
* add "excluded" property to Location
* Hint system adapted and reenabled; hints still unseeded
* make hints deterministic with lists instead of sets
* do not allow hints to point to Light Arrows on non-vanilla bridge
* foreign locations hint as their full name in OoT rather than their region
* checkedLocations now stores hint names by player ID, so that the same location in different worlds can have hints associated
* consolidate versioning in Utils
* ice traps appear as major items rather than any progression item
* set prescription and claim check as defaults for adult trade item settings
* add oot options to playerSettings
* allow case-insensitive logic tricks in yaml
* fix oot shopsanity option formatting
* Write OoT override info even if local item, enabling local checks to show up immediately in the client
* implement CollectionState.can_live_dmg for oot glitched logic
* filter item names for invalid characters when patching shops
* make ice traps appear according to the settings of the world they are shuffled into, rather than the original world
* set hidden-spoiler items and locations with Shop items to events
* make GF carpenters, Gerudo Card, Malon, ZL, and Impa events if the relevant settings are enabled, preventing them from appearing in the client on game start
* Fix oot Glitched and No Logic generation
* fix indenting
* Greatly reduce displayed cosmetic options
* Change oot data version to 1
* add apz5 distribution to webhost
* print player name if an ALttP dungeon contains a good item for OoT world
* delete unneeded commented code
* remove OcarinaSongs import to satisfy lint
2021-09-02 12:35:05 +00:00
text = ' ' . join ( splitText )
return text
class HintAreaNotFound ( RuntimeError ) :
pass
# Peforms a breadth first search to find the closest hint area from a given spot (location or entrance)
# May fail to find a hint if the given spot is only accessible from the root and not from any other region with a hint area
# Returns the name of the location if the spot is not in OoT
def get_hint_area ( spot ) :
if spot . game == ' Ocarina of Time ' :
already_checked = [ ]
spot_queue = [ spot ]
while spot_queue :
current_spot = spot_queue . pop ( 0 )
already_checked . append ( current_spot )
parent_region = current_spot . parent_region
if parent_region . dungeon :
return parent_region . dungeon . hint_text
elif parent_region . hint_text and ( spot . parent_region . name == ' Root ' or parent_region . name != ' Root ' ) :
return parent_region . hint_text
spot_queue . extend ( list ( filter ( lambda ent : ent not in already_checked , parent_region . entrances ) ) )
2022-01-04 16:16:09 +00:00
raise HintAreaNotFound ( ' No hint area could be found for %s [World %d ] ' % ( spot , spot . player ) )
Ocarina of Time (#64)
* first commit (not including OoT data files yet)
* added some basic options
* rule parser works now at least
* make sure to commit everything this time
* temporary change to BaseClasses for oot
* overworld location graph builds mostly correctly
* adding oot data files
* commenting out world options until later since they only existed to make the RuleParser work
* conversion functions between AP ids and OOT ids
* world graph outputs
* set scrub prices
* itempool generates, entrances connected, way too many options added
* fixed set_rules and set_shop_rules
* temp baseclasses changes
* Reaches the fill step now, old event-based system retained in case the new way breaks
* Song placements and misc fixes everywhere
* temporary changes to make oot work
* changed root exits for AP fill framework
* prevent infinite recursion due to OoT sharing usage of the address field
* age reachability works hopefully, songs are broken again
* working spoiler log generation on beatable-only
* Logic tricks implemented
* need this for logic tricks
* fixed map/compass being placed on Serenade location
* kill unreachable events before filling the world
* add a bunch of utility functions to prepare for rom patching
* move OptionList into generic options
* fixed some silly bugs with OptionList
* properly seed all random behavior (so far)
* ROM generation working
* fix hints trying to get alttp dungeon hint texts
* continue fixing hints
* add oot to network data package
* change item and location IDs to 66000 and 67000 range respectively
* push removed items to precollected items
* fixed various issues with cross-contamination with multiple world generation
* reenable glitched logic (hopefully)
* glitched world files age-check fix
* cleaned up some get_locations calls
* added token shuffle and scrub shuffle, modified some options slightly to make the parsing work
* reenable MQ dungeons
* fix forest mq exception
* made targeting style an option for now, will be cosmetic later
* reminder to move targeting to cosmetics
* some oot option maintenance
* enabled starting time of day
* fixed issue breaking shop slots in multiworld generation
* added "off" option for text shuffle and hints
* shopsanity functionality restored
* change patch file extension
* remove unnecessary utility functions + imports
* update MIT license
* change option to "patch_uncompressed_rom" instead of "compress_rom"
* compliance with new AutoWorld systems
* Kill only internal events, remove non-internal big poe event in code
* re-add the big poe event and handle it correctly
* remove extra method in Range option
* fix typo
* Starting items, starting with consumables option
* do not remove nonexistent item
* move set_shop_rules to after shop items are placed
* some cleanup
* add retries for song placement
* flagged Skull Mask and Mask of Truth as advancement items
* update OoT to use LogicMixin
* Fixed trying to assign starting items from the wrong players
* fixed song retry step
* improved option handling, comments, and starting item replacements
* DefaultOnToggle writes Yes or No to spoiler
* enable compression of output if Compress executable is present
* clean up compression
* check whether (de)compressor exists before running the process
* allow specification of rom path in host.yaml
* check if decompressed file already exists before decompressing again
* fix triforce hunt generation
* rename all the oot state functions with prefix
* OoT: mark triforce pieces as completion goal for triforce hunt
* added overworld and any-dungeon shuffle for dungeon items
* Hide most unshuffled locations and events from the list of locations in spoiler
* build oot option ranges with a generic function instead of defining each separately
* move oot output-type control to host.yaml instead of individual yamls
* implement dungeon song shuffle
* minor improvements to overworld dungeon item shuffle
* remove random ice trap names in shops, mostly to avoid maintaining a massive censor list
* always output patch file to folder, remove option to generate ROM in preparation for removal
* re-add the fix for infinite recursion due to not being light or dark world
* change AP-sendable to Ocarina of Time model, since the triforce piece has some extra code apparently
* oot: remove item_names and location_names
* oot: minor fixes
* oot: comment out ROM patching
* oot: only add CollectionState objects on creation if actually needed
* main entrance shuffle method and entrances-based rules
* fix entrances based rules
* disable master quest and big poe count options for client compatibility
* use get_player_name instead of get_player_names
* fix OptionList
* fix oot options for new option system
* new coop section in oot rom: expand player names to 16 bytes, write AP_PLAYER_NAME at end of PLAYER_NAMES
* fill AP player name in oot rom with 0 instead of 0xDF
* encode player name with ASCII for fixed-width
* revert oot player name array to 8 bytes per name
* remove Pierre location if fast scarecrow is on
* check player name length
* "free_scarecrow" not "fast_scarecrow"
* OoT locations now properly store the AP ID instead of the oot internal ID
* oot __version__ updates in lockstep with AP version
* pull in unmodified oot cosmetic files
* also grab JSONDump since it's needed apparently
* gather extra needed methods, modify imports
* delete cosmetics log, replace all instances of SettingsList with OOTWorld
* cosmetic options working, except for sound effects (due to ear-safe issues)
* SFX, Music, and Fanfare randomization reenabled
* move OoT data files into the worlds folder
* move Compress and Decompress into oot data folder
* Replace get_all_state with custom method to avoid the cache
* OoT ROM: increment item counter before setting incoming item/player values to 0, preventing desync issues
* set data_version to 0
* make Kokiri Sword shuffle off by default
* reenable "Random Choice" for various cosmetic options
* kill Ruto's Letter turnin if open fountain
also fix for shopsanity
* place Buy Goron/Zora Tunic first in shop shuffle
* make ice traps appear as other items instead of breaking generation
* managed to break ice traps on non-major-only
* only handle ice traps if they are on
* fix shopsanity for non-oot games, and write player name instead of player number
* light arrows hint uses player name instead of player number
* Reenable "skip child zelda" option
* fix entrances_based_rules
* fix ganondorf hint if starting with light arrows
* fix dungeonitem shuffle and shopsanity interaction
* remove has_all_of, has_any_of, count_of in BaseClasses, replace usage with has_all, has_any, has_group
* force local giveable item on ZL if skip_child_zelda and shuffle_song_items is any
* keep bosses and bombchu bowling chus out of data package
* revert workaround for infinite recursion and fix it properly
* fix shared shop id caches during patching process
* fix shop text box overflows, as much as possible
* add default oot host.yaml option
* add .apz5, .n64, .z64 to gitignore
* Properly document and name all (functioning) OOT options
* clean up some imports
* remove unnecessary files from oot's data
* fix typo in gitignore
* readd the Compress and Decompress utilities, since they are needed for generation
* cleanup of imports and some minor optimizations
* increase shop offset for item IDs to 0xCB
* remove shop item AP ids entirely
* prevent triforce pieces for other players from being received by yourself
* add "excluded" property to Location
* Hint system adapted and reenabled; hints still unseeded
* make hints deterministic with lists instead of sets
* do not allow hints to point to Light Arrows on non-vanilla bridge
* foreign locations hint as their full name in OoT rather than their region
* checkedLocations now stores hint names by player ID, so that the same location in different worlds can have hints associated
* consolidate versioning in Utils
* ice traps appear as major items rather than any progression item
* set prescription and claim check as defaults for adult trade item settings
* add oot options to playerSettings
* allow case-insensitive logic tricks in yaml
* fix oot shopsanity option formatting
* Write OoT override info even if local item, enabling local checks to show up immediately in the client
* implement CollectionState.can_live_dmg for oot glitched logic
* filter item names for invalid characters when patching shops
* make ice traps appear according to the settings of the world they are shuffled into, rather than the original world
* set hidden-spoiler items and locations with Shop items to events
* make GF carpenters, Gerudo Card, Malon, ZL, and Impa events if the relevant settings are enabled, preventing them from appearing in the client on game start
* Fix oot Glitched and No Logic generation
* fix indenting
* Greatly reduce displayed cosmetic options
* Change oot data version to 1
* add apz5 distribution to webhost
* print player name if an ALttP dungeon contains a good item for OoT world
* delete unneeded commented code
* remove OcarinaSongs import to satisfy lint
2021-09-02 12:35:05 +00:00
else :
return spot . name
def get_woth_hint ( world , checked ) :
locations = world . required_locations
locations = list ( filter ( lambda location :
location . name not in checked [ location . player ]
and not ( world . woth_dungeon > = world . hint_dist_user [ ' dungeons_woth_limit ' ] and location . parent_region . dungeon )
and location . name not in world . hint_exclusions
and location . name not in world . hint_type_overrides [ ' woth ' ]
and location . item . name not in world . item_hint_type_overrides [ ' woth ' ] ,
locations ) )
if not locations :
return None
location = world . hint_rng . choice ( locations )
checked [ location . player ] . add ( location . name )
if location . parent_region . dungeon :
world . woth_dungeon + = 1
location_text = getHint ( location . parent_region . dungeon . name , world . clearer_hints ) . text
else :
location_text = get_hint_area ( location )
if world . triforce_hunt :
return ( GossipText ( ' # %s # is on the path of gold. ' % location_text , [ ' Light Blue ' ] ) , location )
else :
return ( GossipText ( ' # %s # is on the way of the hero. ' % location_text , [ ' Light Blue ' ] ) , location )
def get_barren_hint ( world , checked ) :
if not hasattr ( world , ' get_barren_hint_prev ' ) :
world . get_barren_hint_prev = RegionRestriction . NONE
areas = list ( filter ( lambda area :
area not in checked [ world . player ]
and area not in world . hint_type_overrides [ ' barren ' ]
and not ( world . barren_dungeon > = world . hint_dist_user [ ' dungeons_barren_limit ' ] and world . empty_areas [ area ] [ ' dungeon ' ] ) ,
world . empty_areas . keys ( ) ) )
if not areas :
return None
# Randomly choose between overworld or dungeon
dungeon_areas = list ( filter ( lambda area : world . empty_areas [ area ] [ ' dungeon ' ] , areas ) )
overworld_areas = list ( filter ( lambda area : not world . empty_areas [ area ] [ ' dungeon ' ] , areas ) )
if not dungeon_areas :
# no dungeons left, default to overworld
world . get_barren_hint_prev = RegionRestriction . OVERWORLD
elif not overworld_areas :
# no overworld left, default to dungeons
world . get_barren_hint_prev = RegionRestriction . DUNGEON
else :
if world . get_barren_hint_prev == RegionRestriction . NONE :
# 50/50 draw on the first hint
world . get_barren_hint_prev = world . hint_rng . choices ( [ RegionRestriction . DUNGEON , RegionRestriction . OVERWORLD ] , [ 0.5 , 0.5 ] ) [ 0 ]
elif world . get_barren_hint_prev == RegionRestriction . DUNGEON :
# weights 75% against drawing dungeon again
world . get_barren_hint_prev = world . hint_rng . choices ( [ RegionRestriction . DUNGEON , RegionRestriction . OVERWORLD ] , [ 0.25 , 0.75 ] ) [ 0 ]
elif world . get_barren_hint_prev == RegionRestriction . OVERWORLD :
# weights 75% against drawing overworld again
world . get_barren_hint_prev = world . hint_rng . choices ( [ RegionRestriction . DUNGEON , RegionRestriction . OVERWORLD ] , [ 0.75 , 0.25 ] ) [ 0 ]
if world . get_barren_hint_prev == RegionRestriction . DUNGEON :
areas = dungeon_areas
else :
areas = overworld_areas
if not areas :
return None
area_weights = [ world . empty_areas [ area ] [ ' weight ' ] for area in areas ]
2021-11-11 09:42:08 +00:00
if not any ( area_weights ) :
return None
Ocarina of Time (#64)
* first commit (not including OoT data files yet)
* added some basic options
* rule parser works now at least
* make sure to commit everything this time
* temporary change to BaseClasses for oot
* overworld location graph builds mostly correctly
* adding oot data files
* commenting out world options until later since they only existed to make the RuleParser work
* conversion functions between AP ids and OOT ids
* world graph outputs
* set scrub prices
* itempool generates, entrances connected, way too many options added
* fixed set_rules and set_shop_rules
* temp baseclasses changes
* Reaches the fill step now, old event-based system retained in case the new way breaks
* Song placements and misc fixes everywhere
* temporary changes to make oot work
* changed root exits for AP fill framework
* prevent infinite recursion due to OoT sharing usage of the address field
* age reachability works hopefully, songs are broken again
* working spoiler log generation on beatable-only
* Logic tricks implemented
* need this for logic tricks
* fixed map/compass being placed on Serenade location
* kill unreachable events before filling the world
* add a bunch of utility functions to prepare for rom patching
* move OptionList into generic options
* fixed some silly bugs with OptionList
* properly seed all random behavior (so far)
* ROM generation working
* fix hints trying to get alttp dungeon hint texts
* continue fixing hints
* add oot to network data package
* change item and location IDs to 66000 and 67000 range respectively
* push removed items to precollected items
* fixed various issues with cross-contamination with multiple world generation
* reenable glitched logic (hopefully)
* glitched world files age-check fix
* cleaned up some get_locations calls
* added token shuffle and scrub shuffle, modified some options slightly to make the parsing work
* reenable MQ dungeons
* fix forest mq exception
* made targeting style an option for now, will be cosmetic later
* reminder to move targeting to cosmetics
* some oot option maintenance
* enabled starting time of day
* fixed issue breaking shop slots in multiworld generation
* added "off" option for text shuffle and hints
* shopsanity functionality restored
* change patch file extension
* remove unnecessary utility functions + imports
* update MIT license
* change option to "patch_uncompressed_rom" instead of "compress_rom"
* compliance with new AutoWorld systems
* Kill only internal events, remove non-internal big poe event in code
* re-add the big poe event and handle it correctly
* remove extra method in Range option
* fix typo
* Starting items, starting with consumables option
* do not remove nonexistent item
* move set_shop_rules to after shop items are placed
* some cleanup
* add retries for song placement
* flagged Skull Mask and Mask of Truth as advancement items
* update OoT to use LogicMixin
* Fixed trying to assign starting items from the wrong players
* fixed song retry step
* improved option handling, comments, and starting item replacements
* DefaultOnToggle writes Yes or No to spoiler
* enable compression of output if Compress executable is present
* clean up compression
* check whether (de)compressor exists before running the process
* allow specification of rom path in host.yaml
* check if decompressed file already exists before decompressing again
* fix triforce hunt generation
* rename all the oot state functions with prefix
* OoT: mark triforce pieces as completion goal for triforce hunt
* added overworld and any-dungeon shuffle for dungeon items
* Hide most unshuffled locations and events from the list of locations in spoiler
* build oot option ranges with a generic function instead of defining each separately
* move oot output-type control to host.yaml instead of individual yamls
* implement dungeon song shuffle
* minor improvements to overworld dungeon item shuffle
* remove random ice trap names in shops, mostly to avoid maintaining a massive censor list
* always output patch file to folder, remove option to generate ROM in preparation for removal
* re-add the fix for infinite recursion due to not being light or dark world
* change AP-sendable to Ocarina of Time model, since the triforce piece has some extra code apparently
* oot: remove item_names and location_names
* oot: minor fixes
* oot: comment out ROM patching
* oot: only add CollectionState objects on creation if actually needed
* main entrance shuffle method and entrances-based rules
* fix entrances based rules
* disable master quest and big poe count options for client compatibility
* use get_player_name instead of get_player_names
* fix OptionList
* fix oot options for new option system
* new coop section in oot rom: expand player names to 16 bytes, write AP_PLAYER_NAME at end of PLAYER_NAMES
* fill AP player name in oot rom with 0 instead of 0xDF
* encode player name with ASCII for fixed-width
* revert oot player name array to 8 bytes per name
* remove Pierre location if fast scarecrow is on
* check player name length
* "free_scarecrow" not "fast_scarecrow"
* OoT locations now properly store the AP ID instead of the oot internal ID
* oot __version__ updates in lockstep with AP version
* pull in unmodified oot cosmetic files
* also grab JSONDump since it's needed apparently
* gather extra needed methods, modify imports
* delete cosmetics log, replace all instances of SettingsList with OOTWorld
* cosmetic options working, except for sound effects (due to ear-safe issues)
* SFX, Music, and Fanfare randomization reenabled
* move OoT data files into the worlds folder
* move Compress and Decompress into oot data folder
* Replace get_all_state with custom method to avoid the cache
* OoT ROM: increment item counter before setting incoming item/player values to 0, preventing desync issues
* set data_version to 0
* make Kokiri Sword shuffle off by default
* reenable "Random Choice" for various cosmetic options
* kill Ruto's Letter turnin if open fountain
also fix for shopsanity
* place Buy Goron/Zora Tunic first in shop shuffle
* make ice traps appear as other items instead of breaking generation
* managed to break ice traps on non-major-only
* only handle ice traps if they are on
* fix shopsanity for non-oot games, and write player name instead of player number
* light arrows hint uses player name instead of player number
* Reenable "skip child zelda" option
* fix entrances_based_rules
* fix ganondorf hint if starting with light arrows
* fix dungeonitem shuffle and shopsanity interaction
* remove has_all_of, has_any_of, count_of in BaseClasses, replace usage with has_all, has_any, has_group
* force local giveable item on ZL if skip_child_zelda and shuffle_song_items is any
* keep bosses and bombchu bowling chus out of data package
* revert workaround for infinite recursion and fix it properly
* fix shared shop id caches during patching process
* fix shop text box overflows, as much as possible
* add default oot host.yaml option
* add .apz5, .n64, .z64 to gitignore
* Properly document and name all (functioning) OOT options
* clean up some imports
* remove unnecessary files from oot's data
* fix typo in gitignore
* readd the Compress and Decompress utilities, since they are needed for generation
* cleanup of imports and some minor optimizations
* increase shop offset for item IDs to 0xCB
* remove shop item AP ids entirely
* prevent triforce pieces for other players from being received by yourself
* add "excluded" property to Location
* Hint system adapted and reenabled; hints still unseeded
* make hints deterministic with lists instead of sets
* do not allow hints to point to Light Arrows on non-vanilla bridge
* foreign locations hint as their full name in OoT rather than their region
* checkedLocations now stores hint names by player ID, so that the same location in different worlds can have hints associated
* consolidate versioning in Utils
* ice traps appear as major items rather than any progression item
* set prescription and claim check as defaults for adult trade item settings
* add oot options to playerSettings
* allow case-insensitive logic tricks in yaml
* fix oot shopsanity option formatting
* Write OoT override info even if local item, enabling local checks to show up immediately in the client
* implement CollectionState.can_live_dmg for oot glitched logic
* filter item names for invalid characters when patching shops
* make ice traps appear according to the settings of the world they are shuffled into, rather than the original world
* set hidden-spoiler items and locations with Shop items to events
* make GF carpenters, Gerudo Card, Malon, ZL, and Impa events if the relevant settings are enabled, preventing them from appearing in the client on game start
* Fix oot Glitched and No Logic generation
* fix indenting
* Greatly reduce displayed cosmetic options
* Change oot data version to 1
* add apz5 distribution to webhost
* print player name if an ALttP dungeon contains a good item for OoT world
* delete unneeded commented code
* remove OcarinaSongs import to satisfy lint
2021-09-02 12:35:05 +00:00
area = world . hint_rng . choices ( areas , weights = area_weights ) [ 0 ]
if world . empty_areas [ area ] [ ' dungeon ' ] :
world . barren_dungeon + = 1
checked [ world . player ] . add ( area )
return ( GossipText ( " plundering # %s # is a foolish choice. " % area , [ ' Pink ' ] ) , None )
def is_not_checked ( location , checked ) :
return not ( location . name in checked [ location . player ] or get_hint_area ( location ) in checked )
def get_good_item_hint ( world , checked ) :
locations = list ( filter ( lambda location :
is_not_checked ( location , checked )
and not location . locked
and location . name not in world . hint_exclusions
and location . name not in world . hint_type_overrides [ ' item ' ]
and location . item . name not in world . item_hint_type_overrides [ ' item ' ] ,
world . major_item_locations ) )
if not locations :
return None
location = world . hint_rng . choice ( locations )
checked [ location . player ] . add ( location . name )
item_text = getHint ( getItemGenericName ( location . item ) , world . clearer_hints ) . text
if location . parent_region . dungeon :
location_text = getHint ( location . parent_region . dungeon . name , world . clearer_hints ) . text
return ( GossipText ( ' # %s # hoards # %s #. ' % ( attach_name ( location_text , location , world ) , attach_name ( item_text , location . item , world ) ) ,
[ ' Green ' , ' Red ' ] ) , location )
else :
location_text = get_hint_area ( location )
return ( GossipText ( ' # %s # can be found at # %s #. ' % ( attach_name ( item_text , location . item , world ) , attach_name ( location_text , location , world ) ) ,
[ ' Red ' , ' Green ' ] ) , location )
def get_specific_item_hint ( world , checked ) :
if len ( world . named_item_pool ) == 0 :
logger = logging . getLogger ( ' ' )
logger . info ( " Named item hint requested, but pool is empty. " )
return None
while True :
itemname = world . named_item_pool . pop ( 0 )
if itemname == " Bottle " and world . hint_dist == " bingo " :
locations = [
location for location in world . world . get_filled_locations ( )
if ( is_not_checked ( location , checked )
and location . name not in world . hint_exclusions
and location . item . name in bingoBottlesForHints
and not location . locked
and location . name not in world . hint_type_overrides [ ' named-item ' ] )
]
else :
locations = [
location for location in world . world . get_filled_locations ( )
if ( is_not_checked ( location , checked )
and location . name not in world . hint_exclusions
and location . item . name == itemname
and not location . locked
and location . name not in world . hint_type_overrides [ ' named-item ' ] )
]
if len ( locations ) > 0 :
break
if len ( world . named_item_pool ) == 0 :
return None
location = world . hint_rng . choice ( locations )
checked [ location . player ] . add ( location . name )
item_text = getHint ( getItemGenericName ( location . item ) , world . clearer_hints ) . text
if location . parent_region . dungeon :
location_text = getHint ( location . parent_region . dungeon . name , world . clearer_hints ) . text
if world . hint_dist_user . get ( ' vague_named_items ' , False ) :
return ( GossipText ( ' # %s # may be on the hero \' s path. ' % ( location_text ) , [ ' Green ' ] ) , location )
else :
return ( GossipText ( ' # %s # hoards # %s #. ' % ( attach_name ( location_text , location , world ) , attach_name ( item_text , location . item , world ) ) ,
[ ' Green ' , ' Red ' ] ) , location )
else :
location_text = get_hint_area ( location )
if world . hint_dist_user . get ( ' vague_named_items ' , False ) :
return ( GossipText ( ' # %s # may be on the hero \' s path. ' % ( location_text ) , [ ' Green ' ] ) , location )
else :
return ( GossipText ( ' # %s # can be found at # %s #. ' % ( attach_name ( item_text , location . item , world ) , attach_name ( location_text , location , world ) ) ,
[ ' Red ' , ' Green ' ] ) , location )
def get_random_location_hint ( world , checked ) :
locations = list ( filter ( lambda location :
is_not_checked ( location , checked )
and location . item . type not in ( ' Drop ' , ' Event ' , ' Shop ' , ' DungeonReward ' )
# and not (location.parent_region.dungeon and isRestrictedDungeonItem(location.parent_region.dungeon, location.item)) # AP already locks dungeon items
and not location . locked
and location . name not in world . hint_exclusions
and location . name not in world . hint_type_overrides [ ' item ' ]
and location . item . name not in world . item_hint_type_overrides [ ' item ' ] ,
world . world . get_filled_locations ( world . player ) ) )
if not locations :
return None
location = world . hint_rng . choice ( locations )
checked [ location . player ] . add ( location . name )
dungeon = location . parent_region . dungeon
item_text = getHint ( getItemGenericName ( location . item ) , world . clearer_hints ) . text
if dungeon :
location_text = getHint ( dungeon . name , world . clearer_hints ) . text
return ( GossipText ( ' # %s # hoards # %s #. ' % ( attach_name ( location_text , location , world ) , attach_name ( item_text , location . item , world ) ) ,
[ ' Green ' , ' Red ' ] ) , location )
else :
location_text = get_hint_area ( location )
return ( GossipText ( ' # %s # can be found at # %s #. ' % ( attach_name ( item_text , location . item , world ) , attach_name ( location_text , location , world ) ) ,
[ ' Red ' , ' Green ' ] ) , location )
def get_specific_hint ( world , checked , type ) :
hintGroup = getHintGroup ( type , world )
hintGroup = list ( filter ( lambda hint : is_not_checked ( world . get_location ( hint . name ) , checked ) , hintGroup ) )
if not hintGroup :
return None
hint = world . hint_rng . choice ( hintGroup )
location = world . get_location ( hint . name )
checked [ location . player ] . add ( location . name )
if location . name in world . hint_text_overrides :
location_text = world . hint_text_overrides [ location . name ]
else :
location_text = hint . text
if ' # ' not in location_text :
location_text = ' # %s # ' % location_text
item_text = getHint ( getItemGenericName ( location . item ) , world . clearer_hints ) . text
return ( GossipText ( ' %s # %s #. ' % ( attach_name ( location_text , location , world ) , attach_name ( item_text , location . item , world ) ) ,
[ ' Green ' , ' Red ' ] ) , location )
def get_sometimes_hint ( world , checked ) :
return get_specific_hint ( world , checked , ' sometimes ' )
def get_song_hint ( world , checked ) :
return get_specific_hint ( world , checked , ' song ' )
def get_overworld_hint ( world , checked ) :
return get_specific_hint ( world , checked , ' overworld ' )
def get_dungeon_hint ( world , checked ) :
return get_specific_hint ( world , checked , ' dungeon ' )
# probably broken
def get_entrance_hint ( world , checked ) :
if not world . entrance_shuffle :
return None
entrance_hints = list ( filter ( lambda hint : hint . name not in checked [ world . player ] , getHintGroup ( ' entrance ' , world ) ) )
shuffled_entrance_hints = list ( filter ( lambda entrance_hint : world . get_entrance ( entrance_hint . name ) . shuffled , entrance_hints ) )
regions_with_hint = [ hint . name for hint in getHintGroup ( ' region ' , world ) ]
valid_entrance_hints = list ( filter ( lambda entrance_hint :
( world . get_entrance ( entrance_hint . name ) . connected_region . name in regions_with_hint or
world . get_entrance ( entrance_hint . name ) . connected_region . dungeon ) , shuffled_entrance_hints ) )
if not valid_entrance_hints :
return None
entrance_hint = world . hint_rng . choice ( valid_entrance_hints )
entrance = world . get_entrance ( entrance_hint . name )
checked [ world . player ] . add ( entrance . name )
entrance_text = entrance_hint . text
if ' # ' not in entrance_text :
entrance_text = ' # %s # ' % entrance_text
connected_region = entrance . connected_region
if connected_region . dungeon :
region_text = getHint ( connected_region . dungeon . name , world . clearer_hints ) . text
else :
region_text = getHint ( connected_region . name , world . clearer_hints ) . text
if ' # ' not in region_text :
region_text = ' # %s # ' % region_text
return ( GossipText ( ' %s %s . ' % ( entrance_text , region_text ) , [ ' Light Blue ' , ' Green ' ] ) , None )
def get_junk_hint ( world , checked ) :
hints = getHintGroup ( ' junk ' , world )
hints = list ( filter ( lambda hint : hint . name not in checked [ world . player ] , hints ) )
if not hints :
return None
hint = world . hint_rng . choice ( hints )
checked [ world . player ] . add ( hint . name )
return ( GossipText ( hint . text , prefix = ' ' ) , None )
hint_func = {
' trial ' : lambda world , checked : None ,
' always ' : lambda world , checked : None ,
' woth ' : get_woth_hint ,
' barren ' : get_barren_hint ,
' item ' : get_good_item_hint ,
' sometimes ' : get_sometimes_hint ,
' song ' : get_song_hint ,
' overworld ' : get_overworld_hint ,
' dungeon ' : get_dungeon_hint ,
' entrance ' : get_entrance_hint ,
' random ' : get_random_location_hint ,
' junk ' : get_junk_hint ,
' named-item ' : get_specific_item_hint
}
hint_dist_keys = {
' trial ' ,
' always ' ,
' woth ' ,
' barren ' ,
' item ' ,
' song ' ,
' overworld ' ,
' dungeon ' ,
' entrance ' ,
' sometimes ' ,
' random ' ,
' junk ' ,
' named-item '
}
# builds out general hints based on location and whether an item is required or not
def buildWorldGossipHints ( world , checkedLocations = None ) :
# rebuild hint exclusion list
hintExclusions ( world , clear_cache = True )
world . barren_dungeon = 0
world . woth_dungeon = 0
if checkedLocations is None :
checkedLocations = { player : set ( ) for player in world . world . player_ids }
2021-11-15 14:38:32 +00:00
# If Ganondorf hints Light Arrows and is reachable without them, add to checkedLocations to prevent extra hinting
2021-09-12 21:05:21 +00:00
# Can only be forced with vanilla bridge or trials
2021-11-15 14:38:32 +00:00
if world . bridge != ' vanilla ' and world . trials == 0 and world . misc_hints :
Ocarina of Time (#64)
* first commit (not including OoT data files yet)
* added some basic options
* rule parser works now at least
* make sure to commit everything this time
* temporary change to BaseClasses for oot
* overworld location graph builds mostly correctly
* adding oot data files
* commenting out world options until later since they only existed to make the RuleParser work
* conversion functions between AP ids and OOT ids
* world graph outputs
* set scrub prices
* itempool generates, entrances connected, way too many options added
* fixed set_rules and set_shop_rules
* temp baseclasses changes
* Reaches the fill step now, old event-based system retained in case the new way breaks
* Song placements and misc fixes everywhere
* temporary changes to make oot work
* changed root exits for AP fill framework
* prevent infinite recursion due to OoT sharing usage of the address field
* age reachability works hopefully, songs are broken again
* working spoiler log generation on beatable-only
* Logic tricks implemented
* need this for logic tricks
* fixed map/compass being placed on Serenade location
* kill unreachable events before filling the world
* add a bunch of utility functions to prepare for rom patching
* move OptionList into generic options
* fixed some silly bugs with OptionList
* properly seed all random behavior (so far)
* ROM generation working
* fix hints trying to get alttp dungeon hint texts
* continue fixing hints
* add oot to network data package
* change item and location IDs to 66000 and 67000 range respectively
* push removed items to precollected items
* fixed various issues with cross-contamination with multiple world generation
* reenable glitched logic (hopefully)
* glitched world files age-check fix
* cleaned up some get_locations calls
* added token shuffle and scrub shuffle, modified some options slightly to make the parsing work
* reenable MQ dungeons
* fix forest mq exception
* made targeting style an option for now, will be cosmetic later
* reminder to move targeting to cosmetics
* some oot option maintenance
* enabled starting time of day
* fixed issue breaking shop slots in multiworld generation
* added "off" option for text shuffle and hints
* shopsanity functionality restored
* change patch file extension
* remove unnecessary utility functions + imports
* update MIT license
* change option to "patch_uncompressed_rom" instead of "compress_rom"
* compliance with new AutoWorld systems
* Kill only internal events, remove non-internal big poe event in code
* re-add the big poe event and handle it correctly
* remove extra method in Range option
* fix typo
* Starting items, starting with consumables option
* do not remove nonexistent item
* move set_shop_rules to after shop items are placed
* some cleanup
* add retries for song placement
* flagged Skull Mask and Mask of Truth as advancement items
* update OoT to use LogicMixin
* Fixed trying to assign starting items from the wrong players
* fixed song retry step
* improved option handling, comments, and starting item replacements
* DefaultOnToggle writes Yes or No to spoiler
* enable compression of output if Compress executable is present
* clean up compression
* check whether (de)compressor exists before running the process
* allow specification of rom path in host.yaml
* check if decompressed file already exists before decompressing again
* fix triforce hunt generation
* rename all the oot state functions with prefix
* OoT: mark triforce pieces as completion goal for triforce hunt
* added overworld and any-dungeon shuffle for dungeon items
* Hide most unshuffled locations and events from the list of locations in spoiler
* build oot option ranges with a generic function instead of defining each separately
* move oot output-type control to host.yaml instead of individual yamls
* implement dungeon song shuffle
* minor improvements to overworld dungeon item shuffle
* remove random ice trap names in shops, mostly to avoid maintaining a massive censor list
* always output patch file to folder, remove option to generate ROM in preparation for removal
* re-add the fix for infinite recursion due to not being light or dark world
* change AP-sendable to Ocarina of Time model, since the triforce piece has some extra code apparently
* oot: remove item_names and location_names
* oot: minor fixes
* oot: comment out ROM patching
* oot: only add CollectionState objects on creation if actually needed
* main entrance shuffle method and entrances-based rules
* fix entrances based rules
* disable master quest and big poe count options for client compatibility
* use get_player_name instead of get_player_names
* fix OptionList
* fix oot options for new option system
* new coop section in oot rom: expand player names to 16 bytes, write AP_PLAYER_NAME at end of PLAYER_NAMES
* fill AP player name in oot rom with 0 instead of 0xDF
* encode player name with ASCII for fixed-width
* revert oot player name array to 8 bytes per name
* remove Pierre location if fast scarecrow is on
* check player name length
* "free_scarecrow" not "fast_scarecrow"
* OoT locations now properly store the AP ID instead of the oot internal ID
* oot __version__ updates in lockstep with AP version
* pull in unmodified oot cosmetic files
* also grab JSONDump since it's needed apparently
* gather extra needed methods, modify imports
* delete cosmetics log, replace all instances of SettingsList with OOTWorld
* cosmetic options working, except for sound effects (due to ear-safe issues)
* SFX, Music, and Fanfare randomization reenabled
* move OoT data files into the worlds folder
* move Compress and Decompress into oot data folder
* Replace get_all_state with custom method to avoid the cache
* OoT ROM: increment item counter before setting incoming item/player values to 0, preventing desync issues
* set data_version to 0
* make Kokiri Sword shuffle off by default
* reenable "Random Choice" for various cosmetic options
* kill Ruto's Letter turnin if open fountain
also fix for shopsanity
* place Buy Goron/Zora Tunic first in shop shuffle
* make ice traps appear as other items instead of breaking generation
* managed to break ice traps on non-major-only
* only handle ice traps if they are on
* fix shopsanity for non-oot games, and write player name instead of player number
* light arrows hint uses player name instead of player number
* Reenable "skip child zelda" option
* fix entrances_based_rules
* fix ganondorf hint if starting with light arrows
* fix dungeonitem shuffle and shopsanity interaction
* remove has_all_of, has_any_of, count_of in BaseClasses, replace usage with has_all, has_any, has_group
* force local giveable item on ZL if skip_child_zelda and shuffle_song_items is any
* keep bosses and bombchu bowling chus out of data package
* revert workaround for infinite recursion and fix it properly
* fix shared shop id caches during patching process
* fix shop text box overflows, as much as possible
* add default oot host.yaml option
* add .apz5, .n64, .z64 to gitignore
* Properly document and name all (functioning) OOT options
* clean up some imports
* remove unnecessary files from oot's data
* fix typo in gitignore
* readd the Compress and Decompress utilities, since they are needed for generation
* cleanup of imports and some minor optimizations
* increase shop offset for item IDs to 0xCB
* remove shop item AP ids entirely
* prevent triforce pieces for other players from being received by yourself
* add "excluded" property to Location
* Hint system adapted and reenabled; hints still unseeded
* make hints deterministic with lists instead of sets
* do not allow hints to point to Light Arrows on non-vanilla bridge
* foreign locations hint as their full name in OoT rather than their region
* checkedLocations now stores hint names by player ID, so that the same location in different worlds can have hints associated
* consolidate versioning in Utils
* ice traps appear as major items rather than any progression item
* set prescription and claim check as defaults for adult trade item settings
* add oot options to playerSettings
* allow case-insensitive logic tricks in yaml
* fix oot shopsanity option formatting
* Write OoT override info even if local item, enabling local checks to show up immediately in the client
* implement CollectionState.can_live_dmg for oot glitched logic
* filter item names for invalid characters when patching shops
* make ice traps appear according to the settings of the world they are shuffled into, rather than the original world
* set hidden-spoiler items and locations with Shop items to events
* make GF carpenters, Gerudo Card, Malon, ZL, and Impa events if the relevant settings are enabled, preventing them from appearing in the client on game start
* Fix oot Glitched and No Logic generation
* fix indenting
* Greatly reduce displayed cosmetic options
* Change oot data version to 1
* add apz5 distribution to webhost
* print player name if an ALttP dungeon contains a good item for OoT world
* delete unneeded commented code
* remove OcarinaSongs import to satisfy lint
2021-09-02 12:35:05 +00:00
try :
light_arrow_location = world . world . find_item ( " Light Arrows " , world . player )
checkedLocations [ light_arrow_location . player ] . add ( light_arrow_location . name )
except StopIteration : # start with them
pass
stoneIDs = list ( gossipLocations . keys ( ) )
if ' disabled ' in world . hint_dist_user :
for stone_name in world . hint_dist_user [ ' disabled ' ] :
try :
stone_id = gossipLocations_reversemap [ stone_name ]
except KeyError :
raise ValueError ( f ' Gossip stone location " { stone_name } " is not valid ' )
stoneIDs . remove ( stone_id )
( gossip_text , _ ) = get_junk_hint ( world , checkedLocations )
world . gossip_hints [ stone_id ] = gossip_text
stoneGroups = [ ]
if ' groups ' in world . hint_dist_user :
for group_names in world . hint_dist_user [ ' groups ' ] :
group = [ ]
for stone_name in group_names :
try :
stone_id = gossipLocations_reversemap [ stone_name ]
except KeyError :
raise ValueError ( f ' Gossip stone location " { stone_name } " is not valid ' )
stoneIDs . remove ( stone_id )
group . append ( stone_id )
stoneGroups . append ( group )
# put the remaining locations into singleton groups
stoneGroups . extend ( [ [ id ] for id in stoneIDs ] )
world . hint_rng . shuffle ( stoneGroups )
# Load hint distro from distribution file or pre-defined settings
#
# 'fixed' key is used to mimic the tournament distribution, creating a list of fixed hint types to fill
# Once the fixed hint type list is exhausted, weighted random choices are taken like all non-tournament sets
# This diverges from the tournament distribution where leftover stones are filled with sometimes hints (or random if no sometimes locations remain to be hinted)
sorted_dist = { }
type_count = 1
hint_dist = OrderedDict ( { } )
fixed_hint_types = [ ]
max_order = 0
for hint_type in world . hint_dist_user [ ' distribution ' ] :
if world . hint_dist_user [ ' distribution ' ] [ hint_type ] [ ' order ' ] > 0 :
hint_order = int ( world . hint_dist_user [ ' distribution ' ] [ hint_type ] [ ' order ' ] )
sorted_dist [ hint_order ] = hint_type
if max_order < hint_order :
max_order = hint_order
type_count = type_count + 1
if ( type_count - 1 ) < max_order :
raise Exception ( " There are gaps in the custom hint orders. Please revise your plando file to remove them. " )
for i in range ( 1 , type_count ) :
hint_type = sorted_dist [ i ]
if world . hint_dist_user [ ' distribution ' ] [ hint_type ] [ ' copies ' ] > 0 :
fixed_num = world . hint_dist_user [ ' distribution ' ] [ hint_type ] [ ' fixed ' ]
hint_weight = world . hint_dist_user [ ' distribution ' ] [ hint_type ] [ ' weight ' ]
else :
fixed_num = 0
hint_weight = 0
hint_dist [ hint_type ] = ( hint_weight , world . hint_dist_user [ ' distribution ' ] [ hint_type ] [ ' copies ' ] )
hint_dist . move_to_end ( hint_type )
fixed_hint_types . extend ( [ hint_type ] * int ( fixed_num ) )
hint_types , hint_prob = zip ( * hint_dist . items ( ) )
hint_prob , _ = zip ( * hint_prob )
# Add required location hints, only if hint copies > 0
if hint_dist [ ' always ' ] [ 1 ] > 0 :
alwaysLocations = getHintGroup ( ' always ' , world )
for hint in alwaysLocations :
location = world . get_location ( hint . name )
checkedLocations [ location . player ] . add ( hint . name )
if location . item . name in bingoBottlesForHints and world . hint_dist == ' bingo ' :
always_item = ' Bottle '
else :
always_item = location . item . name
if always_item in world . named_item_pool :
world . named_item_pool . remove ( always_item )
if location . name in world . hint_text_overrides :
location_text = world . hint_text_overrides [ location . name ]
else :
location_text = getHint ( location . name , world . clearer_hints ) . text
if ' # ' not in location_text :
location_text = ' # %s # ' % location_text
item_text = getHint ( getItemGenericName ( location . item ) , world . clearer_hints ) . text
add_hint ( world , stoneGroups , GossipText ( ' %s # %s #. ' % ( attach_name ( location_text , location , world ) , attach_name ( item_text , location . item , world ) ) ,
[ ' Green ' , ' Red ' ] ) , hint_dist [ ' always ' ] [ 1 ] , location , force_reachable = True )
logging . getLogger ( ' ' ) . debug ( ' Placed always hint for %s . ' , location . name )
# Add trial hints, only if hint copies > 0
if hint_dist [ ' trial ' ] [ 1 ] > 0 :
if world . trials == 6 :
add_hint ( world , stoneGroups , GossipText ( " #Ganon ' s Tower# is protected by a powerful barrier. " , [ ' Pink ' ] ) , hint_dist [ ' trial ' ] [ 1 ] , force_reachable = True )
elif world . trials == 0 :
add_hint ( world , stoneGroups , GossipText ( " Sheik dispelled the barrier around #Ganon ' s Tower#. " , [ ' Yellow ' ] ) , hint_dist [ ' trial ' ] [ 1 ] , force_reachable = True )
elif world . trials < 6 and world . trials > 3 :
for trial , skipped in world . skipped_trials . items ( ) :
if skipped :
add_hint ( world , stoneGroups , GossipText ( " the # %s Trial# was dispelled by Sheik. " % trial , [ ' Yellow ' ] ) , hint_dist [ ' trial ' ] [ 1 ] , force_reachable = True )
elif world . trials < = 3 and world . trials > 0 :
for trial , skipped in world . skipped_trials . items ( ) :
if not skipped :
add_hint ( world , stoneGroups , GossipText ( " the # %s Trial# protects Ganon ' s Tower. " % trial , [ ' Pink ' ] ) , hint_dist [ ' trial ' ] [ 1 ] , force_reachable = True )
# Add user-specified hinted item locations if using a built-in hint distribution
# Raise error if hint copies is zero
if len ( world . named_item_pool ) > 0 and world . hint_dist_user [ ' named_items_required ' ] :
if hint_dist [ ' named-item ' ] [ 1 ] == 0 :
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 ( world , checkedLocations )
if hint == None :
raise Exception ( ' No valid hints for user-provided item ' )
else :
gossip_text , location = hint
place_ok = add_hint ( world , stoneGroups , gossip_text , hint_dist [ ' named-item ' ] [ 1 ] , location )
if not place_ok :
raise Exception ( ' Not enough gossip stones for user-provided item hints ' )
# Shuffle named items hints
# When all items are not required to be hinted, this allows for
# opportunity-style hints to be drawn at random from the defined list.
world . hint_rng . shuffle ( world . named_item_pool )
hint_types = list ( hint_types )
hint_prob = list ( hint_prob )
hint_counts = { }
custom_fixed = True
while stoneGroups :
if fixed_hint_types :
hint_type = fixed_hint_types . pop ( 0 )
copies = hint_dist [ hint_type ] [ 1 ]
if copies > len ( stoneGroups ) :
# Quiet to avoid leaking information.
logging . getLogger ( ' ' ) . debug ( f ' Not enough gossip stone locations ( { len ( stoneGroups ) } groups) for fixed hint type { hint_type } with { copies } copies, proceeding with available stones. ' )
copies = len ( stoneGroups )
else :
custom_fixed = False
# Make sure there are enough stones left for each hint type
num_types = len ( hint_types )
hint_types = list ( filter ( lambda htype : hint_dist [ htype ] [ 1 ] < = len ( stoneGroups ) , hint_types ) )
new_num_types = len ( hint_types )
if new_num_types == 0 :
raise Exception ( ' Not enough gossip stone locations for remaining weighted hint types. ' )
elif new_num_types < num_types :
hint_prob = [ ]
for htype in hint_types :
hint_prob . append ( hint_dist [ htype ] [ 0 ] )
try :
# Weight the probabilities such that hints that are over the expected proportion
# will be drawn less, and hints that are under will be drawn more.
# This tightens the variance quite a bit. The variance can be adjusted via the power
weighted_hint_prob = [ ]
for w1_type , w1_prob in zip ( hint_types , hint_prob ) :
p = w1_prob
if p != 0 : # If the base prob is 0, then it's 0
for w2_type , w2_prob in zip ( hint_types , hint_prob ) :
if w2_prob != 0 : # If the other prob is 0, then it has no effect
# Raising this term to a power greater than 1 will decrease variance
# Conversely, a power less than 1 will increase variance
p = p * ( ( ( hint_counts . get ( w2_type , 0 ) / w2_prob ) + 1 ) / ( ( hint_counts . get ( w1_type , 0 ) / w1_prob ) + 1 ) )
weighted_hint_prob . append ( p )
hint_type = world . hint_rng . choices ( hint_types , weights = weighted_hint_prob ) [ 0 ]
copies = hint_dist [ hint_type ] [ 1 ]
except IndexError :
raise Exception ( ' Not enough valid hints to fill gossip stone locations. ' )
hint = hint_func [ hint_type ] ( world , checkedLocations )
if hint == None :
index = hint_types . index ( hint_type )
hint_prob [ index ] = 0
# Zero out the probability in the base distribution in case the probability list is modified
# to fit hint types in remaining gossip stones
hint_dist [ hint_type ] = ( 0.0 , copies )
else :
gossip_text , location = hint
place_ok = add_hint ( world , stoneGroups , gossip_text , copies , location )
if place_ok :
hint_counts [ hint_type ] = hint_counts . get ( hint_type , 0 ) + 1
if location is None :
logging . getLogger ( ' ' ) . debug ( ' Placed %s hint. ' , hint_type )
else :
logging . getLogger ( ' ' ) . debug ( ' Placed %s hint for %s . ' , hint_type , location . name )
if not place_ok and custom_fixed :
logging . getLogger ( ' ' ) . debug ( ' Failed to place %s fixed hint for %s . ' , hint_type , location . name )
fixed_hint_types . insert ( 0 , hint_type )
# builds text that is displayed at the temple of time altar for child and adult, rewards pulled based off of item in a fixed order.
def buildAltarHints ( world , messages , include_rewards = True , include_wincons = True ) :
# text that appears at altar as a child.
child_text = ' \x08 '
if include_rewards :
bossRewardsSpiritualStones = [
( ' Kokiri Emerald ' , ' Green ' ) ,
( ' Goron Ruby ' , ' Red ' ) ,
( ' Zora Sapphire ' , ' Blue ' ) ,
]
child_text + = getHint ( ' Spiritual Stone Text Start ' , world . clearer_hints ) . text + ' \x04 '
for ( reward , color ) in bossRewardsSpiritualStones :
child_text + = buildBossString ( reward , color , world )
child_text + = getHint ( ' Child Altar Text End ' , world . clearer_hints ) . text
child_text + = ' \x0B '
update_message_by_id ( messages , 0x707A , get_raw_text ( child_text ) , 0x20 )
# text that appears at altar as an adult.
adult_text = ' \x08 '
adult_text + = getHint ( ' Adult Altar Text Start ' , world . clearer_hints ) . text + ' \x04 '
if include_rewards :
bossRewardsMedallions = [
( ' Light Medallion ' , ' Light Blue ' ) ,
( ' Forest Medallion ' , ' Green ' ) ,
( ' Fire Medallion ' , ' Red ' ) ,
( ' Water Medallion ' , ' Blue ' ) ,
( ' Shadow Medallion ' , ' Pink ' ) ,
( ' Spirit Medallion ' , ' Yellow ' ) ,
]
for ( reward , color ) in bossRewardsMedallions :
adult_text + = buildBossString ( reward , color , world )
if include_wincons :
adult_text + = buildBridgeReqsString ( world )
adult_text + = ' \x04 '
adult_text + = buildGanonBossKeyString ( world )
else :
adult_text + = getHint ( ' Adult Altar Text End ' , world . clearer_hints ) . text
adult_text + = ' \x0B '
update_message_by_id ( messages , 0x7057 , get_raw_text ( adult_text ) , 0x20 )
# pulls text string from hintlist for reward after sending the location to hintlist.
def buildBossString ( reward , color , world ) :
for location in world . world . get_filled_locations ( world . player ) :
if location . item . name == reward :
item_icon = chr ( location . item . special [ ' item_id ' ] )
location_text = getHint ( location . name , world . clearer_hints ) . text
return str ( GossipText ( " \x08 \x13 %s %s " % ( item_icon , location_text ) , [ color ] , prefix = ' ' ) ) + ' \x04 '
return ' '
def buildBridgeReqsString ( world ) :
string = " \x13 \x12 " # Light Arrow Icon
if world . bridge == ' open ' :
string + = " The awakened ones will have #already created a bridge# to the castle where the evil dwells. "
else :
item_req_string = getHint ( ' bridge_ ' + world . bridge , world . clearer_hints ) . text
if world . bridge == ' medallions ' :
item_req_string = str ( world . bridge_medallions ) + ' ' + item_req_string
elif world . bridge == ' stones ' :
item_req_string = str ( world . bridge_stones ) + ' ' + item_req_string
elif world . bridge == ' dungeons ' :
item_req_string = str ( world . bridge_rewards ) + ' ' + item_req_string
elif world . bridge == ' tokens ' :
item_req_string = str ( world . bridge_tokens ) + ' ' + item_req_string
if ' # ' not in item_req_string :
item_req_string = ' # %s # ' % item_req_string
string + = " The awakened ones will await for the Hero to collect %s . " % item_req_string
return str ( GossipText ( string , [ ' Green ' ] , prefix = ' ' ) )
def buildGanonBossKeyString ( world ) :
string = " \x13 \x74 " # Boss Key Icon
if world . shuffle_ganon_bosskey == ' remove ' :
string + = " And the door to the \x05 \x41 evil one \x05 \x40 ' s chamber will be left #unlocked#. "
else :
if world . shuffle_ganon_bosskey == ' on_lacs ' :
item_req_string = getHint ( ' lacs_ ' + world . lacs_condition , world . clearer_hints ) . text
if world . lacs_condition == ' medallions ' :
item_req_string = str ( world . lacs_medallions ) + ' ' + item_req_string
elif world . lacs_condition == ' stones ' :
item_req_string = str ( world . lacs_stones ) + ' ' + item_req_string
elif world . lacs_condition == ' dungeons ' :
item_req_string = str ( world . lacs_rewards ) + ' ' + item_req_string
elif world . lacs_condition == ' tokens ' :
item_req_string = str ( world . lacs_tokens ) + ' ' + item_req_string
if ' # ' not in item_req_string :
item_req_string = ' # %s # ' % item_req_string
bk_location_string = " provided by Zelda once %s are retrieved " % item_req_string
else :
bk_location_string = getHint ( ' ganonBK_ ' + world . shuffle_ganon_bosskey , world . clearer_hints ) . text
string + = " And the \x05 \x41 evil one \x05 \x40 ' s key will be %s . " % bk_location_string
return str ( GossipText ( string , [ ' Yellow ' ] , prefix = ' ' ) )
# fun new lines for Ganon during the final battle
def buildGanonText ( world , messages ) :
# empty now unused messages to make space for ganon lines
update_message_by_id ( messages , 0x70C8 , " " )
update_message_by_id ( messages , 0x70C9 , " " )
update_message_by_id ( messages , 0x70CA , " " )
# lines before battle
ganonLines = getHintGroup ( ' ganonLine ' , world )
world . hint_rng . shuffle ( ganonLines )
text = get_raw_text ( ganonLines . pop ( ) . text )
update_message_by_id ( messages , 0x70CB , text )
# light arrow hint or validation chest item
if world . starting_items [ ' Light Arrows ' ] > 0 :
text = get_raw_text ( getHint ( ' Light Arrow Location ' , world . clearer_hints ) . text )
text + = " \x05 \x42 your pocket \x05 \x40 "
else :
try :
find_light_arrows = world . world . find_item ( ' Light Arrows ' , world . player )
text = get_raw_text ( getHint ( ' Light Arrow Location ' , world . clearer_hints ) . text )
location = find_light_arrows
location_hint = get_hint_area ( location )
if world . player != location . player :
text + = " \x05 \x42 %s ' s \x05 \x40 %s " % ( world . world . get_player_name ( location . player ) , get_raw_text ( location_hint ) )
else :
location_hint = location_hint . replace ( ' Ganon \' s Castle ' , ' my castle ' )
text + = get_raw_text ( location_hint )
except StopIteration :
text = get_raw_text ( getHint ( ' Validation Line ' , world . clearer_hints ) . text )
for location in world . world . get_filled_locations ( world . player ) :
if location . name == ' Ganons Tower Boss Key Chest ' :
text + = get_raw_text ( getHint ( getItemGenericName ( location . item ) , world . clearer_hints ) . text )
break
text + = ' ! '
update_message_by_id ( messages , 0x70CC , text )
def get_raw_text ( string ) :
text = ' '
for char in string :
if char == ' ^ ' :
text + = ' \x04 ' # box break
elif char == ' & ' :
text + = ' \x01 ' # new line
elif char == ' @ ' :
text + = ' \x0F ' # print player name
elif char == ' # ' :
text + = ' \x05 \x40 ' # sets color to white
else :
text + = char
return text
def HintDistFiles ( ) :
return [ os . path . join ( data_path ( ' Hints/ ' ) , d ) for d in defaultHintDists ] + [
os . path . join ( data_path ( ' Hints/ ' ) , d )
for d in sorted ( os . listdir ( data_path ( ' Hints/ ' ) ) )
if d . endswith ( ' .json ' ) and d not in defaultHintDists ]
def HintDistList ( ) :
dists = { }
for d in HintDistFiles ( ) :
dist = read_json ( d )
dist_name = dist [ ' name ' ]
gui_name = dist [ ' gui_name ' ]
dists . update ( { dist_name : gui_name } )
return dists
def HintDistTips ( ) :
tips = " "
first_dist = True
line_char_limit = 33
for d in HintDistFiles ( ) :
if not first_dist :
tips = tips + " \n "
else :
first_dist = False
dist = read_json ( d )
gui_name = dist [ ' gui_name ' ]
desc = dist [ ' description ' ]
i = 0
end_of_line = False
tips = tips + " <b> "
for c in gui_name :
if c == " " and end_of_line :
tips = tips + " \n "
end_of_line = False
else :
tips = tips + c
i = i + 1
if i > line_char_limit :
end_of_line = True
i = 0
tips = tips + " </b>: "
i = i + 2
for c in desc :
if c == " " and end_of_line :
tips = tips + " \n "
end_of_line = False
else :
tips = tips + c
i = i + 1
if i > line_char_limit :
end_of_line = True
i = 0
tips = tips + " \n "
return tips