Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2021-07-07 20:10:30 -05:00
20 changed files with 282 additions and 68 deletions

View File

@@ -139,6 +139,7 @@ class World(object):
set_player_attr('treasure_hunt_total', 0)
set_player_attr('potshuffle', False)
set_player_attr('pot_contents', None)
set_player_attr('fakeboots', False)
set_player_attr('shopsanity', False)
set_player_attr('keydropshuffle', False)
@@ -2430,7 +2431,7 @@ access_mode = {"items": 0, "locations": 1, "none": 2}
# byte 6: BSMC BBEE (big, small, maps, compass, bosses, enemies)
boss_mode = {"none": 0, "simple": 1, "full": 2, "random": 3, "chaos": 3}
enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2}
enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2, "legacy": 3}
# byte 7: HHHD DP?? (enemy_health, enemy_dmg, potshuffle, ?)
e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4}

View File

@@ -1,5 +1,9 @@
# Changelog
### 0.1.6.5
- Reduced chance of diagonal flute spot in Balanced
- ~~Merged DR v0.4.0.8 - Boss Indicator / Fake Boots / Quickswap Update / Credits Updates~~
### 0.1.6.4
- Fixed Frogsmith and Stumpy and restored progression in these locations
- Added Blacksmith/Hammer Pegs to OW Tile Swap pool

3
CLI.py
View File

@@ -98,7 +98,7 @@ def parse_cli(argv, no_defaults=False):
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'fakeboots',
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep',
@@ -149,6 +149,7 @@ def parse_settings():
"ow_fluteshuffle": "vanilla",
"shuffle": "vanilla",
"shufflelinks": False,
"fakeboots": False,
"shufflepots": False,
"shuffleenemies": "none",

View File

@@ -1482,14 +1482,12 @@ def link_entrances(world, player):
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
doors.remove(blacksmith_hut)
exit_pool.remove(blacksmith_hut)
# place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
doors.remove(bomb_shop)
exit_pool.remove(bomb_shop)
# handle remaining caves
for cave in caves:

22
Fill.py
View File

@@ -3,7 +3,7 @@ import logging
from BaseClasses import CollectionState
from Items import ItemFactory
from Regions import shop_to_location_table
from Regions import shop_to_location_table, retro_shops
class FillError(RuntimeError):
@@ -496,6 +496,10 @@ def balance_multiworld_progression(world):
new_location = replacement_locations.pop()
new_location.item, old_location.item = old_location.item, new_location.item
if world.shopsanity[new_location.player]:
check_shop_swap(new_location)
if world.shopsanity[old_location.player]:
check_shop_swap(old_location)
new_location.event, old_location.event = True, False
state.collect(new_location.item, True, new_location)
replaced_items = True
@@ -516,6 +520,18 @@ def balance_multiworld_progression(world):
raise RuntimeError('Not all required items reachable. Something went terribly wrong here.')
def check_shop_swap(l):
if l.parent_region.name in shop_to_location_table:
if l.name in shop_to_location_table[l.parent_region.name]:
idx = shop_to_location_table[l.parent_region.name].index(l.name)
inv_slot = l.parent_region.shop.inventory[idx]
inv_slot['item'] = l.item.name
elif l.parent_region in retro_shops:
idx = retro_shops[l.parent_region.name].index(l.name)
inv_slot = l.parent_region.shop.inventory[idx]
inv_slot['item'] = l.item.name
def balance_money_progression(world):
logger = logging.getLogger('')
state = CollectionState(world)
@@ -557,7 +573,6 @@ def balance_money_progression(world):
for player in range(1, world.players+1):
logger.debug(f'Money balance for P{player}: Needed: {total_price[player]} Available: {available_money[player]}')
def get_sphere_locations(sphere_state, locations):
sphere_state.sweep_for_events(key_only=True, locations=locations)
return [loc for loc in locations if sphere_state.can_reach(loc) and sphere_state.not_flooding_a_key(sphere_state.world, loc)]
@@ -672,6 +687,7 @@ def balance_money_progression(world):
logger.debug(f'Upgrading {best_target.item.name} @ {best_target.name} for 300 Rupees')
best_target.item = ItemFactory('Rupees (300)', best_target.item.player)
best_target.item.location = best_target
check_shop_swap(best_target.item.location)
else:
old_item = best_target.item
logger.debug(f'Swapping {best_target.item.name} @ {best_target.name} for {best_swap.item.name} @ {best_swap.name}')
@@ -679,6 +695,8 @@ def balance_money_progression(world):
best_target.item.location = best_target
best_swap.item = old_item
best_swap.item.location = best_swap
check_shop_swap(best_target.item.location)
check_shop_swap(best_swap.item.location)
increase = best_value - old_value
difference -= increase
wallet[target_player] += increase

View File

@@ -28,7 +28,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
from Utils import output_path, parse_player_names
__version__ = '0.4.0.6-u'
__version__ = '0.4.0.8-u'
class EnemizerError(RuntimeError):
@@ -93,6 +93,7 @@ def main(args, seed=None, fish=None):
world.treasure_hunt_count = args.triforce_goal.copy()
world.treasure_hunt_total = args.triforce_pool.copy()
world.shufflelinks = args.shufflelinks.copy()
world.fakeboots = args.fakeboots.copy()
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
@@ -263,12 +264,10 @@ def main(args, seed=None, fish=None):
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
or sprite_random_on_hit)
if use_enemizer:
base_patch = LocalRom(args.rom) # update base2current.json
rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom)
if use_enemizer and (args.enemizercli or not args.jsonout):
base_patch = LocalRom(args.rom) # update base2current.json (side effect)
if args.rom and not(os.path.isfile(args.rom)):
raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom)
if os.path.exists(args.enemizercli):

View File

@@ -150,6 +150,7 @@ def roll_settings(weights):
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off'
ret.shufflelinks = get_choice('shufflelinks') == 'on'
ret.fakeboots = get_choice('fakeboots') == 'on'
ret.shopsanity = get_choice('shopsanity') == 'on'
ret.keydropshuffle = get_choice('keydropshuffle') == 'on'
ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent'
@@ -202,15 +203,17 @@ def roll_settings(weights):
boss_choice = old_style_bosses[boss_choice]
ret.shufflebosses = boss_choice
ret.shuffleenemies = {'none': 'none',
'shuffled': 'shuffled',
'random': 'chaos'
}[get_choice('enemy_shuffle')]
enemy_choice = get_choice('enemy_shuffle')
if enemy_choice == 'chaos':
enemy_choice = 'random'
ret.shuffleenemies = enemy_choice
ret.enemy_damage = {'default': 'default',
'shuffled': 'shuffled',
'random': 'chaos'
}[get_choice('enemy_damage')]
old_style_damage = {'none': 'default',
'chaos': 'random'}
damage_choice = get_choice('enemy_damage')
if damage_choice in old_style_damage:
damage_choice = old_style_damage[damage_choice]
ret.enemy_damage = damage_choice
ret.enemy_health = get_choice('enemy_health')

View File

@@ -1,9 +1,12 @@
import random, logging, copy
from sortedcontainers import SortedList
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot
from OWEdges import OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel
try:
from sortedcontainers import SortedList
except ImportError:
raise Exception('Could not load sortedcontainers module')
__version__ = '0.1.6.4-u'
__version__ = '0.1.6.5-u'
def link_overworld(world, player):
# setup mandatory connections

View File

@@ -14,6 +14,35 @@ Thanks to qadan, cheuer, & compiling
# Bug Fixes and Notes.
* 0.4.0.8
* Ganon jokes added for when silvers aren't available
* Some text updated (Blind jokes, uncle text)
* Fixed some enemizer Mystery settings
* Added a setting that's random enemy shuffle without Unkillable Thieves possible
* Fixed shop spoiler when money balancing/multiworld balancing
* Fixed a problem with insanity
* Fixed an issue with the credit stats specific to DR (e.g. collection rate total)
* More helpful error message when bps is missing?
* Minor generation issues involving enemizer and the link sprite
* Baserom updates (from Bonta, kan, qwertymodo, ardnaxelark)
* Boss icon on dungeon map (if you have a compass)
* Progressive bow sprite replacement
* Quickswap - consecutive special swaps
* Bonk Counter
* One mind
* MSU fix
* Chest turn tracking (not yet in credits)
* Damaged and magic stats in credits (gt bk removed)
* Fix for infinite bombs
* Fake boots option
* Always allowed medallions for swordless (no option yet)
* 0.4.0.7
* Reduce flashing option added
* Sprite author credit added
* Ranged Crystal switch rules tweaked
* Baserom update: includes Credits Speedup, reduced flashing option, msu resume (but turned off by default)
* Create link sprite's zspr from local ROM and no longer attempts to download it from website
* Some minor bug fixes
* 0.4.0.6
* Hints now default to off
* The maiden gives you a hint to the attic if you bring her to the unlit boss room

124
Rom.py
View File

@@ -9,17 +9,20 @@ import random
import struct
import sys
import subprocess
import bps.apply
import bps.io
try:
import bps.apply
import bps.io
except ImportError:
raise Exception('Could not load BPS module')
from BaseClasses import CollectionState, ShopType, Region, Location, OWEdge, Door, DoorType, RegionType, PotItem
from DoorShuffle import compass_data, DROptions, boss_indicator
from Dungeons import dungeon_music_addresses
from KeyDoorShuffle import count_locations_exclude_logic
from Regions import location_table, shop_to_location_table, retro_shops
from RoomData import DoorKind
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
from Text import Uncle_texts, Ganon1_texts, Ganon_Phase_3_No_Silvers_texts, TavernMan_texts, Sahasrahla2_texts
from Text import Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc
from Items import ItemFactory
@@ -28,7 +31,7 @@ from OverworldShuffle import default_flute_connections, flute_data
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '5d2041f4387123c2de98dd41e6e5c4c6'
RANDOMIZERBASEHASH = 'f0a6138148c13414ff4dc89dc0101de6'
class JsonRom(object):
@@ -201,7 +204,7 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, random_sprite_
options = {
'RandomizeEnemies': world.enemy_shuffle[player] != 'none',
'RandomizeEnemiesType': 3,
'RandomizeBushEnemyChance': world.enemy_shuffle[player] == 'random',
'RandomizeBushEnemyChance': world.enemy_shuffle[player] in ['random', 'legacy'],
'RandomizeEnemyHealthRange': world.enemy_health[player] != 'default',
'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[world.enemy_health[player]],
'OHKO': False,
@@ -247,9 +250,9 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, random_sprite_
'SwordGraphics': "sword_gfx/normal.gfx",
'BeeMizer': False,
'BeesLevel': 0,
'RandomizeTileTrapPattern': world.enemy_shuffle[player] == 'random',
'RandomizeTileTrapPattern': world.enemy_shuffle[player] in ['random', 'legacy'],
'RandomizeTileTrapFloorTile': False,
'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'random' else world.enemy_shuffle[player] != 'none',
'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'legacy' else world.enemy_shuffle[player] != 'none',
'RandomizeSpriteOnHit': random_sprite_on_hit,
'DebugMode': False,
'DebugForceEnemy': False,
@@ -891,7 +894,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
write_int16(rom, 0x187010, credits_total) # dynamic credits
if credits_total != 216:
# collection rate address:
cr_address = 0x2391CC
cr_address = 0x2391C4
cr_pc = cr_address - 0x120000 # convert to pc
mid_top, mid_bot = credits_digit((credits_total // 10) % 10)
last_top, last_bot = credits_digit(credits_total % 10)
@@ -902,25 +905,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(cr_pc+0x3a, mid_bot)
rom.write_byte(cr_pc+0x3b, last_bot)
if world.keydropshuffle[player] or world.doorShuffle[player] != 'vanilla':
gt = world.dungeon_layouts[player]['Ganons Tower']
gt_logic = world.key_logic[player]['Ganons Tower']
total = 0
for region in gt.master_sector.regions:
total += count_locations_exclude_logic(region.locations, gt_logic)
# rom.write_byte(0x187012, total) # dynamic credits
# gt big key address:
gtbk_address = 0x2390EE
gtbk_pc = gtbk_address - 0x120000 # convert to pc
mid_top, mid_bot = credits_digit(total // 10)
last_top, last_bot = credits_digit(total % 10)
# top half
rom.write_byte(gtbk_pc+0x1c, mid_top)
rom.write_byte(gtbk_pc+0x1d, last_top)
# bottom half
rom.write_byte(gtbk_pc+0x3a, mid_bot)
rom.write_byte(gtbk_pc+0x3b, last_bot)
# patch medallion requirements
if world.required_medallions[player][0] == 'Bombos':
rom.write_byte(0x180022, 0x00) # requirement
@@ -1264,6 +1248,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x18017E, 0x01) # Fairy fountains only trade in bottles
# Starting equipment
if world.fakeboots[player]:
rom.write_byte(0x18008E, 0x01)
equip = [0] * (0x340 + 0x4F)
equip[0x36C] = 0x18
equip[0x36D] = 0x18
@@ -1724,7 +1711,8 @@ def hud_format_text(text):
def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite,
ow_palettes, uw_palettes, reduce_flashing):
if not os.path.exists("data/sprites/official/001.link.1.zspr"):
if not os.path.exists("data/sprites/official/001.link.1.zspr") and rom.orig_buffer:
dump_zspr(rom.orig_buffer[0x80000:0x87000], rom.orig_buffer[0xdd308:0xdd380],
rom.orig_buffer[0xdedf5:0xdedf9], "data/sprites/official/001.link.1.zspr", "Nintendo", "Link")
@@ -1812,7 +1800,6 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
if reduce_flashing:
rom.write_byte(0x18017f, 1)
default_ow_palettes(rom)
if ow_palettes == 'random':
randomize_ow_palettes(rom)
@@ -1883,6 +1870,61 @@ def dump_zspr(basesprite, basepalette, baseglove, outfilename, author_name, spri
with open('%s' % outfilename, "wb") as zspr_file:
zspr_file.write(write_buffer)
# .zspr file dumping logic copied with permission from SpriteSomething:
# https://github.com/Artheau/SpriteSomething/blob/master/source/meta/classes/spritelib.py#L443 (thanks miketrethewey!)
def dump_zspr(basesprite, basepalette, baseglove, outfilename, author_name, sprite_name):
palettes = basepalette
# Add glove data
palettes.extend(baseglove)
HEADER_STRING = b"ZSPR"
VERSION = 0x01
SPRITE_TYPE = 0x01 # this format has "1" for the player sprite
RESERVED_BYTES = b'\x00\x00\x00\x00\x00\x00'
QUAD_BYTE_NULL_CHAR = b'\x00\x00\x00\x00'
DOUBLE_BYTE_NULL_CHAR = b'\x00\x00'
SINGLE_BYTE_NULL_CHAR = b'\x00'
write_buffer = bytearray()
write_buffer.extend(HEADER_STRING)
write_buffer.extend(struct.pack('B', VERSION)) # as_u8
checksum_start = len(write_buffer)
write_buffer.extend(QUAD_BYTE_NULL_CHAR) # checksum
sprite_sheet_pointer = len(write_buffer)
write_buffer.extend(QUAD_BYTE_NULL_CHAR)
write_buffer.extend(struct.pack('<H', len(basesprite))) # as_u16
palettes_pointer = len(write_buffer)
write_buffer.extend(QUAD_BYTE_NULL_CHAR)
write_buffer.extend(struct.pack('<H', len(palettes))) # as_u16
write_buffer.extend(struct.pack('<H', SPRITE_TYPE)) # as_u16
write_buffer.extend(RESERVED_BYTES)
# sprite.name
write_buffer.extend(sprite_name.encode('utf-16-le'))
write_buffer.extend(DOUBLE_BYTE_NULL_CHAR)
# author.name
write_buffer.extend(author_name.encode('utf-16-le'))
write_buffer.extend(DOUBLE_BYTE_NULL_CHAR)
# author.name-short
write_buffer.extend(author_name.encode('ascii'))
write_buffer.extend(SINGLE_BYTE_NULL_CHAR)
write_buffer[sprite_sheet_pointer:sprite_sheet_pointer +
4] = struct.pack('<L', len(write_buffer)) # as_u32
write_buffer.extend(basesprite)
write_buffer[palettes_pointer:palettes_pointer +
4] = struct.pack('<L', len(write_buffer)) # as_u32
write_buffer.extend(palettes)
checksum = (sum(write_buffer) + 0xFF + 0xFF) % 0x10000
checksum_complement = 0xFFFF - checksum
write_buffer[checksum_start:checksum_start +
2] = struct.pack('<H', checksum) # as_u16
write_buffer[checksum_start + 2:checksum_start +
4] = struct.pack('<H', checksum_complement) # as_u16
with open('%s' % outfilename, "wb") as zspr_file:
zspr_file.write(write_buffer)
def write_sprite(rom, sprite):
if not sprite.valid:
return
@@ -2222,24 +2264,30 @@ def write_strings(rom, world, player, team):
# We still need the older hints of course. Those are done here.
no_silver_text = Ganon_Phase_3_No_Silvers_texts[random.randint(0, len(Ganon_Phase_3_No_Silvers_texts) - 1)]
silverarrows = world.find_items('Silver Arrows', player)
random.shuffle(silverarrows)
silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!'
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
if silverarrows:
hint_phrase = hint_text(silverarrows[0]).replace("Ganon's", "my")
silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?'
else:
silverarrow_hint = no_silver_text
tt['ganon_phase_3_no_silvers'] = silverarrow_hint
tt['ganon_phase_3_no_silvers_alt'] = silverarrow_hint
prog_bow_locs = world.find_items('Progressive Bow', player)
distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None)
progressive_silvers = world.difficulty_requirements[player].progressive_bow_limit >= 2 or world.swords[player] == 'swordless'
if distinguished_prog_bow_loc:
prog_bow_locs.remove(distinguished_prog_bow_loc)
silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!'
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
hint_phrase = hint_text(distinguished_prog_bow_loc).replace("Ganon's", "my")
silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text
tt['ganon_phase_3_no_silvers'] = silverarrow_hint
if any(prog_bow_locs):
silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!'
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
hint_phrase = hint_text(random.choice(prog_bow_locs)).replace("Ganon's", "my")
silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text
tt['ganon_phase_3_no_silvers_alt'] = silverarrow_hint
crystal5 = world.find_items('Crystal 5', player)[0]
crystal6 = world.find_items('Crystal 6', player)[0]

93
Text.py
View File

@@ -21,13 +21,14 @@ text_addresses = {'Pedestal': (0x180300, 256),
Uncle_texts = [
# these ones are er specific
'Good Luck!\nYou will need it.',
'Forward this message to 10 other people or this seed will be awful.',
'I hope you like your seeds bootless and fluteless.',
'10\n9\n8\n7\n6\n5\n4\n3\n2\n1\nGo!',
'I\'m off to visit cousin Fritzl.',
'Don\'t forget to check Antlion Cave.'
] * 2 + [
# these ones are from web randomizer
"We're out of\nWeetabix. To\nthe store!",
"This seed is\nbootless\nuntil boots.",
"Why do we only\nhave one bed?",
@@ -66,15 +67,32 @@ Uncle_texts = [
"Get to the\nchop...\ncastle!",
"Come with me\nif you want\nto live",
"I must go\nmy planet\nneeds me",
"Are we in\ngo mode yet?",
"Darn, I\nthought this\nwas combo.",
"Don't check\nanything I\nwouldn't!",
"I know where\nthe bow is!\n",
"This message\nwill self\ndestruct.",
"Time to cast\nMeteo on\nGanon!",
"I have a\nlong, full\nlife ahead!",
"Why did that\nsoda have a\nskull on it?",
"Something\nrandom just\ncame up.",
"I'm bad at\nthis. Can you\ndo it for me?",
"Link!\n Wake up!\n ... Bye!",
"Text me when\nyou hit\ngo mode.",
"Turn off the\nstove before\nyou leave.",
"It's raining.\nI'm taking\nthe umbrella.",
"Count to 30.\nThen come\nfind me.",
"Gonna shuffle\nall the items\nreal quick."
]
Triforce_texts = [
# these ones are er specific
'Product has Hole in center. Bad seller, 0 out of 5.',
'Who stole the fourth triangle?',
'Trifource?\nMore Like Tritrice, am I right?'
'\n Well Done!',
'You just wasted 2 hours of your life.',
'This was meant to be a trapezoid'
] * 2 + [
'This was meant to be a trapezoid',
# these ones are from web randomizer
"\n G G",
"All your base\nare belong\nto us.",
"You have ended\nthe domination\nof Dr. Wily",
@@ -107,6 +125,13 @@ Triforce_texts = [
"You get one\nwish. Choose\nwisely, hero!",
"Can you please\nbreak us three\nup? Thanks.",
" Pick us up\n before we\n get dizzy!",
"Thank you,\nMikey. Youre\n2 minutes late",
"This was a\n7000 series\ntrain.",
" I'd buy\n that for\n a rupee!",
" Did you like\n that bow\n placement?",
"I promise the\nnext seed will\nbe better.",
"\n Honk.",
"Breakfast\nis served!",
]
BombShop2_texts = ['Bombs!\nBombs!\nBiggest!\nBestest!\nGreatest!\nBoomest!']
Sahasrahla2_texts = ['You already got my item, idiot.', 'Why are you still talking to me?', 'This text won\'t change.', 'Have you met my brother, Hasarahshla?']
@@ -145,6 +170,34 @@ Blind_texts = [
"I tried to\ncatch fog,\nbut I mist.",
"Winter is a\ngreat time\nto chill.",
"Do you think\nthe Ice Rod\nis cool?",
"Pyramids?\nI never saw\nthe point.",
"Stone golems\nare created as\nblank slates.",
"Desert humor\nis often dry.\n",
"Ganon is a\nbacon of\ndespair!",
"Butchering\ncows means\nhigh steaks.",
"I can't search\nthe web...\nToo many links",
"I can whistle\nMost pitches\nbut I can't C",
"The Blinds\nStore is\ncurtain death",
"Dark Aga Rooms\nare not a\nbright idea.",
"Best advice\nfor a Goron?\nBe Boulder.",
"Equestrian\nservices are\na stable job.",
"Do I like\ndrills? Just\na bit.",
"I'd shell out\ngood rupees\nfor a conch.",
"Current\naffairs are\nshocking!",
"A lying Goron\ndeals in\nboulderdash.",
"A bread joke?\nEh, it'd be\nhalf baked.",
"I could take\na stab at a\nsword pun.",
"Gloves open\na handful\nof checks",
"Red mail?\nReturn to\nsender.",
"For sale:\nBaby boots,\nNever found",
"SRL or rtGG?\nI prefer the\nLadder",
"Ladders are\nalways up\nto something",
"Zelda's\nfashion is\nvery chic",
"Zombie geese\nare waterfoul.\n",
"I bought some\ncuccos for a\npoultry sum.",
"The stratus of\nclouds is up\nin the air.",
"Tie two ropes\ntogether?!\nI think knot!",
"Time for you\nto go on a\nBlind date!"
]
Ganon1_texts = [
"Start your day\nsmiling with a\ndelicious\nwhole grain\nbreakfast\ncreated for\nyour\nincredible\ninsides.",
@@ -171,6 +224,40 @@ Ganon1_texts = [
"Life, dreams,\nhope...\nWhere'd they\ncome from? And\nwhere are they\nheaded? These\nthings... I am\ngoing to\ndestroy!",
"My minions all\nfailed to\nguard those\nitems?!\n\nWhy am I\nsurrounded by\nincompetent\nfools?!",
]
Ganon_Phase_3_No_Silvers_texts = [
"Did you find\nthe arrows on\nPlanet Zebes?",
"Did you find\nthe arrows?\nI think not.",
"Silver arrows?\nI have never\nheard of them",
"Did you find\nthe arrows on\nThe Moon?",
"Did you find\nthe arrows\nIn dev null?",
"I have sold\nthe arrows for\na green big 20",
"Did you find\nThe arrows in\nCount Dracula?",
"Error 404\nSilver arrows\nnot found.",
"No arrows for\nYou today,\nSorry",
"No arrows?\nCheck your\njunk mail."
"Careful, all\nthat spinning\nmakes me dizzy",
"Did you find\nthe arrows in\nJabu's belly?",
"Silver is not\nan appropriate\narrow material",
"Did you find\nthe arrows in\nNarnia?",
"Are you ready\nTo spin\nTo win?",
"DID YOU FIND\nTHE ARROWS IN\nKEFKA'S TOWER",
"Did you find\nthe arrows in\nRecycle Bin?",
"Silver Arrows?\n\nLUL",
"Imagine\nfinding the\narrows",
"Did you find\nsilvers in\nscenic Ohio?",
"Did you find\nThe arrows in\n*mumblemumble*",
"\nSpin To Win!\n",
"did you find\nthe arrows in\nthe hourglass?",
"Silver Arrows\nare so v30",
"OH, NO, THEY\nACTUALLY SAID\nSILVER MARROW",
"SURELY THE\nLEFTMOST TILES\nWILL STAY UP",
"Did you find\nthe arrows in\nWorld 4-2?",
"You Spin Me\nRight Round\nLike A Record",
"SILLY HERO,\nSILVER IS FOR\nWEREWOLVES!",
"Did you find\nthe silvers in\nganti's ears",
]
TavernMan_texts = [
"What do you\ncall a blind\ndinosaur?\na doyouthink-\nhesaurus.",
"A blind man\nwalks into\na bar.\nAnd a table.\nAnd a chair.",

View File

@@ -11,6 +11,8 @@ dw 0
;Hooks
org $02a999
jsl OWEdgeTransition : nop #4 ;LDA $02A4E3,X : ORA $7EF3CA
;org $02e238 ;LDX #$9E : - DEX : DEX : CMP $DAEE,X : BNE -
;jsl OWSpecialTransition : nop #5
; flute menu cancel
org $0ab7af ;LDA $F2 : ORA $F0 : AND #$C0
@@ -285,7 +287,8 @@ OWNewDestination:
++ lda $84 : !add 1,s : sta $84 : pla : pla
.adjustMainAxis
LDA $84 : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA $88 ; vram
;LDA $84 : SEC : SBC #$0400 : AND #$0F80 : ASL : XBA : STA $88 ; vram
LDA $84 : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA $88
LDA $84 : SEC : SBC #$0010 : AND #$003E : LSR : STA $86
pla : pla : sep #$10 : ldy $418
@@ -346,7 +349,7 @@ OWNewDestination:
; turn into bunny
lda $5d : cmp #$17 : beq .return
lda #$17 : sta $5d
lda #$01 : sta $02e0
lda #$01 : sta $2e0
bra .return
.nobunny
lda $5d : cmp #$17 : bne .return
@@ -356,6 +359,11 @@ OWNewDestination:
lda $05 : sta $8a
rep #$30 : rts
}
OWSpecialTransition:
{
LDX #$9E
- DEX : DEX : CMP $DAEE,X : BNE -
}
;Data
org $aaa000

Binary file not shown.

View File

@@ -354,6 +354,10 @@
"action": "store_true",
"type": "bool"
},
"fakeboots": {
"action": "store_true",
"type": "bool"
},
"calc_playthrough": {
"action": "store_false",
"type": "bool"
@@ -384,7 +388,8 @@
"choices": [
"none",
"shuffled",
"random"
"random",
"legacy"
]
},
"enemy_health": {

View File

@@ -290,6 +290,7 @@
"Keys are universal, shooting arrows costs rupees,",
"and a few other little things make this more like Zelda-1. (default: %(default)s)"
],
"fakeboots": [ " Players starts with fake boots that allow dashing but no item checks (default: %(default)s"],
"startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ],
"usestartinventory": [ "Toggle usage of Starting Inventory." ],
"custom": [ "Not supported." ],

View File

@@ -2,6 +2,7 @@
"gui": {
"adjust.nobgm": "Disable Music & MSU-1",
"adjust.quickswap": "L/R Quickswapping",
"adjust.reduce_flashing": "Reduce Flashing",
"adjust.heartcolor": "Heart Color",
"adjust.heartcolor.red": "Red",
@@ -86,6 +87,7 @@
"randomizer.enemizer.enemyshuffle.none": "None",
"randomizer.enemizer.enemyshuffle.shuffled": "Shuffled",
"randomizer.enemizer.enemyshuffle.random": "Random",
"randomizer.enemizer.enemyshuffle.legacy": "Random (including Thieves)",
"randomizer.enemizer.bossshuffle": "Boss Shuffle",
"randomizer.enemizer.bossshuffle.none": "None",
@@ -204,6 +206,7 @@
"randomizer.item.hints": "Include Helpful Hints",
"randomizer.item.retro": "Retro mode (universal keys)",
"randomizer.item.fakeboots": "Start with Fake Boots",
"randomizer.item.worldstate": "World State",
"randomizer.item.worldstate.standard": "Standard",

View File

@@ -5,7 +5,8 @@
"options": [
"none",
"shuffled",
"random"
"random",
"legacy"
]
},
"bossshuffle": {

View File

@@ -4,7 +4,8 @@
"shopsanity": { "type": "checkbox" },
"hints": {
"type": "checkbox"
}
},
"fakeboots": { "type": "checkbox" }
},
"leftItemFrame": {
"worldstate": {

View File

@@ -58,6 +58,7 @@ SETTINGSTOPROCESS = {
"hints": "hints",
"retro": "retro",
"shopsanity": "shopsanity",
"fakeboots": "fakeboots",
"worldstate": "mode",
"logiclevel": "logic",
"goal": "goal",

View File

@@ -1,4 +1,4 @@
from tkinter import ttk, Frame, E, W, LEFT, RIGHT
from tkinter import ttk, Frame, E, W, LEFT, RIGHT, Label
import source.gui.widgets as widgets
import json
import os
@@ -17,6 +17,9 @@ def item_page(parent):
self.frames["checkboxes"] = Frame(self)
self.frames["checkboxes"].pack(anchor=W)
various_options = Label(self.frames["checkboxes"], text="")
various_options.pack(side=LEFT)
self.frames["leftItemFrame"] = Frame(self)
self.frames["rightItemFrame"] = Frame(self)
self.frames["leftItemFrame"].pack(side=LEFT)
@@ -34,7 +37,7 @@ def item_page(parent):
self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox":
packAttrs["anchor"] = W
packAttrs["side"] = LEFT
self.widgets[key].pack(packAttrs)
return self