Merge pull request #2 from KevinCathcart/Dev

Dev
This commit is contained in:
AmazingAmpharos 2017-11-24 18:42:58 -06:00 committed by GitHub
commit 595e2a20b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 367 additions and 566 deletions

View File

@ -185,7 +185,7 @@ class World(object):
return True return True
return False return False
def has_beaten_game(self, state): def has_beaten_game(self, state):
if state.has('Triforce'): return True if state.has('Triforce'): return True
if self.goal in ['triforcehunt']: if self.goal in ['triforcehunt']:
@ -232,7 +232,7 @@ class World(object):
goal = ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'].index(self.goal) goal = ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'].index(self.goal)
shuffle = ['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'].index(self.shuffle) shuffle = ['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'].index(self.shuffle)
difficulty = ['easy', 'normal', 'hard', 'expert', 'insane'].index(self.difficulty) difficulty = ['easy', 'normal', 'hard', 'expert', 'insane'].index(self.difficulty)
timer = ['none', 'display', 'timed', 'timed-ohko', 'timed-countdown'].index(self.timer) timer = ['none', 'display', 'timed', 'timed-ohko', 'timed-countdown','ohko'].index(self.timer)
progressive = ['on', 'off', 'random'].index(self.progressive) progressive = ['on', 'off', 'random'].index(self.progressive)
algorithm = ['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'].index(self.algorithm) algorithm = ['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'].index(self.algorithm)
beatableonly = 1 if self.check_beatable_only else 0 beatableonly = 1 if self.check_beatable_only else 0
@ -329,7 +329,7 @@ class CollectionState(object):
return item in self.prog_items return item in self.prog_items
else: else:
return self.item_count(item) >= count return self.item_count(item) >= count
def item_count(self, item): def item_count(self, item):
return len([pritem for pritem in self.prog_items if pritem == item]) return len([pritem for pritem in self.prog_items if pritem == item])
@ -337,7 +337,7 @@ class CollectionState(object):
return self.has('Power Glove') or self.has('Titans Mitts') return self.has('Power Glove') or self.has('Titans Mitts')
def has_bottle(self): def has_bottle(self):
return self.has('Bottle') or self.has('BottleRedPotion') or self.has('BottleGreenPotion') or self.has('BottleBluePotion') or self.has('BottleFairy') or self.has('BottleBee') or self.has('BottleGoodBee') return self.has('Bottle') or self.has('Bottle (Red Potion)') or self.has('Bottle (Green Potion)') or self.has('Bottle (Blue Potion)') or self.has('Bottle (Fairy)') or self.has('Bottle (Bee)') or self.has('Bottle (Good Bee)')
def can_lift_heavy_rocks(self): def can_lift_heavy_rocks(self):
return self.has('Titans Mitts') return self.has('Titans Mitts')

View File

@ -3,6 +3,7 @@ import os
import logging import logging
import random import random
import textwrap import textwrap
import sys
from Main import main from Main import main
from Gui import guiMain from Gui import guiMain
@ -20,9 +21,9 @@ if __name__ == '__main__':
parser.add_argument('--logic', default='noglitches', const='noglitches', nargs='?', choices=['noglitches', 'minorglitches'], parser.add_argument('--logic', default='noglitches', const='noglitches', nargs='?', choices=['noglitches', 'minorglitches'],
help='''\ help='''\
Select Enforcement of Item Requirements. (default: %(default)s) Select Enforcement of Item Requirements. (default: %(default)s)
No Glitches: No Glitches:
Minor Glitches: May require Fake Flippers, Bunny Revival Minor Glitches: May require Fake Flippers, Bunny Revival
and Dark Room Navigation. and Dark Room Navigation.
''') ''')
parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless'], parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless'],
help='''\ help='''\
@ -37,19 +38,19 @@ if __name__ == '__main__':
Agahnim\'s Tower barrier can be destroyed with Agahnim\'s Tower barrier can be destroyed with
hammer. Misery Mire and Turtle Rock can be opened hammer. Misery Mire and Turtle Rock can be opened
without a sword. Hammer damages Ganon. Ether and without a sword. Hammer damages Ganon. Ether and
Bombos Tablet can be activated with Hammer (and Book). Bombos Tablet can be activated with Hammer (and Book).
''') ''')
parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'],
help='''\ help='''\
Select completion goal. (default: %(default)s) Select completion goal. (default: %(default)s)
Ganon: Collect all crystals, beat Agahnim 2 then Ganon: Collect all crystals, beat Agahnim 2 then
defeat Ganon. defeat Ganon.
Crystals: Collect all crystals then defeat Ganon. Crystals: Collect all crystals then defeat Ganon.
Pedestal: Places the Triforce at the Master Sword Pedestal. Pedestal: Places the Triforce at the Master Sword Pedestal.
All Dungeons: Collect all crystals, pendants, beat both All Dungeons: Collect all crystals, pendants, beat both
Agahnim fights and then defeat Ganon. Agahnim fights and then defeat Ganon.
Triforce Hunt: Places 30 Triforce Pieces in the world, collect Triforce Hunt: Places 30 Triforce Pieces in the world, collect
20 of them to beat the game. 20 of them to beat the game.
''') ''')
parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['easy', 'normal', 'hard', 'expert', 'insane'], parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['easy', 'normal', 'hard', 'expert', 'insane'],
help='''\ help='''\
@ -58,9 +59,9 @@ if __name__ == '__main__':
Normal: Normal difficulty. Normal: Normal difficulty.
Hard: A harder setting with less equipment and reduced health. Hard: A harder setting with less equipment and reduced health.
Expert: A harder yet setting with minimum equipment and health. Expert: A harder yet setting with minimum equipment and health.
Insane: A setting with the absolute minimum in equipment and no extra health. Insane: A setting with the absolute minimum in equipment and no extra health.
''') ''')
parser.add_argument('--timer', default='none', const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'timed-countdown'], parser.add_argument('--timer', default='none', const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'],
help='''\ help='''\
Select game timer setting. Affects available itempool. (default: %(default)s) Select game timer setting. Affects available itempool. (default: %(default)s)
None: No timer. None: No timer.
@ -74,9 +75,11 @@ if __name__ == '__main__':
Timed OHKO: Starts clock at 10 minutes. Green Clocks add Timed OHKO: Starts clock at 10 minutes. Green Clocks add
5 minutes (Total: 25). As long as clock is at 0, 5 minutes (Total: 25). As long as clock is at 0,
Link will die in one hit. Link will die in one hit.
OHKO: Like Timed OHKO, but no clock items are present
and the clock is permenantly at zero.
Timed Countdown: Starts with clock at 40 minutes. Same clocks as Timed Countdown: Starts with clock at 40 minutes. Same clocks as
Timed mode. If time runs out, you lose (but can Timed mode. If time runs out, you lose (but can
still keep playing). still keep playing).
''') ''')
parser.add_argument('--progressive', default='on', const='normal', nargs='?', choices=['on', 'off', 'random'], parser.add_argument('--progressive', default='on', const='normal', nargs='?', choices=['on', 'off', 'random'],
help='''\ help='''\
@ -90,7 +93,7 @@ if __name__ == '__main__':
be found at any time. Downgrades are not possible. be found at any time. Downgrades are not possible.
Random: Swords, Shields, Armor, and Gloves will, per Random: Swords, Shields, Armor, and Gloves will, per
category, be randomly progressive or not. category, be randomly progressive or not.
Link will die in one hit. Link will die in one hit.
''') ''')
parser.add_argument('--algorithm', default='balanced', const='balanced', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'], parser.add_argument('--algorithm', default='balanced', const='balanced', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'],
help='''\ help='''\
@ -113,7 +116,7 @@ if __name__ == '__main__':
them the more often they were found unreachable. them the more often they were found unreachable.
Flood: Push out items starting from Link\'s House and Flood: Push out items starting from Link\'s House and
slightly biased to placing progression items with slightly biased to placing progression items with
less restrictions. less restrictions.
''') ''')
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'], parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'],
help='''\ help='''\
@ -135,7 +138,7 @@ if __name__ == '__main__':
discretion. discretion.
Experimental. Experimental.
The dungeon variants only mix up dungeons and keep the rest of The dungeon variants only mix up dungeons and keep the rest of
the overworld vanilla. the overworld vanilla.
''') ''')
parser.add_argument('--rom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.') parser.add_argument('--rom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.')
parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.') parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.')
@ -145,7 +148,7 @@ if __name__ == '__main__':
If --seed is provided, it will be used for the first seed, then If --seed is provided, it will be used for the first seed, then
used to derive the next seed (i.e. generating 10 seeds with used to derive the next seed (i.e. generating 10 seeds with
--seed given will produce the same 10 (different) roms each --seed given will produce the same 10 (different) roms each
time). time).
''', type=int) ''', type=int)
parser.add_argument('--fastmenu', help='Enable instant menu', action='store_true') parser.add_argument('--fastmenu', help='Enable instant menu', action='store_true')
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
@ -155,33 +158,33 @@ if __name__ == '__main__':
''', action='store_true') ''', action='store_true')
parser.add_argument('--nodungeonitems', help='''\ parser.add_argument('--nodungeonitems', help='''\
Remove Maps and Compasses from Itempool, replacing them by Remove Maps and Compasses from Itempool, replacing them by
empty slots. empty slots.
''', action='store_true') ''', action='store_true')
parser.add_argument('--beatableonly', help='''\ parser.add_argument('--beatableonly', help='''\
Only check if the game is beatable with placement. Do not Only check if the game is beatable with placement. Do not
ensure all locations are reachable. This only has an effect ensure all locations are reachable. This only has an effect
on the restrictive algorithm currently. on the restrictive algorithm currently.
''', action='store_true') ''', action='store_true')
parser.add_argument('--shuffleganon', help='''\ parser.add_argument('--shuffleganon', help='''\
If set, include the Pyramid Hole and Ganon's Tower in the If set, include the Pyramid Hole and Ganon's Tower in the
entrance shuffle pool. entrance shuffle pool.
''', action='store_true') ''', action='store_true')
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'], parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
help='''\ help='''\
Select the rate at which the heart beep sound is played at Select the rate at which the heart beep sound is played at
low health. (default: %(default)s) low health. (default: %(default)s)
''') ''')
parser.add_argument('--sprite', help='''\ parser.add_argument('--sprite', help='''\
Path to a sprite sheet to use for Link. Needs to be in Path to a sprite sheet to use for Link. Needs to be in
binary format and have a length of 0x7000 (28672) bytes, binary format and have a length of 0x7000 (28672) bytes,
or 0x7078 (28792) bytes including palette data. or 0x7078 (28792) bytes including palette data.
Alternatively, can be a ALttP Rom patched with a Link Alternatively, can be a ALttP Rom patched with a Link
sprite that will be extracted. sprite that will be extracted.
''') ''')
parser.add_argument('--suppress_rom', help='Do not create an output rom file.', action='store_true') parser.add_argument('--suppress_rom', help='Do not create an output rom file.', action='store_true')
parser.add_argument('--gui', help='Launch the GUI', action='store_true') parser.add_argument('--gui', help='Launch the GUI', action='store_true')
parser.add_argument('--jsonout', action='store_true', help='''\ parser.add_argument('--jsonout', action='store_true', help='''\
Output .json patch to stdout instead of a patched rom. Used Output .json patch to stdout instead of a patched rom. Used
for VT site integration, do not use otherwise. for VT site integration, do not use otherwise.
''') ''')
args = parser.parse_args() args = parser.parse_args()
@ -189,11 +192,11 @@ if __name__ == '__main__':
# ToDo: Validate files further than mere existance # ToDo: Validate files further than mere existance
if not args.jsonout and not os.path.isfile(args.rom): if not args.jsonout and not os.path.isfile(args.rom):
input('Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom) input('Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom)
exit(1) sys.exit(1)
if args.sprite is not None and not os.path.isfile(args.sprite): if args.sprite is not None and not os.path.isfile(args.sprite):
if not args.jsonout: if not args.jsonout:
input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite) input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite)
exit(1) sys.exit(1)
else: else:
raise IOError('Cannot find sprite file at %s' % args.sprite) raise IOError('Cannot find sprite file at %s' % args.sprite)

View File

@ -167,7 +167,7 @@ def fill_restrictive(world, base_state, locations, itempool):
while itempool and locations: while itempool and locations:
item_to_place = itempool.pop() item_to_place = itempool.pop()
maximum_exploration_state = sweep_from_pool() maximum_exploration_state = sweep_from_pool()
if world.check_beatable_only: if world.check_beatable_only:
can_beat_without = world.has_beaten_game(maximum_exploration_state) can_beat_without = world.has_beaten_game(maximum_exploration_state)

2
Gui.py
View File

@ -113,7 +113,7 @@ def guiMain(args=None):
timerFrame = Frame(drowDownFrame) timerFrame = Frame(drowDownFrame)
timerVar = StringVar() timerVar = StringVar()
timerVar.set('none') timerVar.set('none')
timerOptionMenu = OptionMenu(timerFrame, timerVar, 'none', 'display', 'timed', 'timed-ohko', 'timed-countdown') timerOptionMenu = OptionMenu(timerFrame, timerVar, 'none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown')
timerOptionMenu.pack(side=RIGHT) timerOptionMenu.pack(side=RIGHT)
timerLabel = Label(timerFrame, text='Timer setting') timerLabel = Label(timerFrame, text='Timer setting')
timerLabel.pack(side=LEFT) timerLabel.pack(side=LEFT)

View File

@ -1,5 +1,6 @@
from Items import ItemFactory from Items import ItemFactory
from Fill import fill_restrictive from Fill import fill_restrictive
from collections import namedtuple
import random import random
#This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space. #This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
@ -10,8 +11,8 @@ alwaysitems = ['Bombos', 'Book of Mudora', 'Bow', 'Cane of Somaria', 'Ether', 'F
progressivegloves = ['Progressive Glove'] * 2 progressivegloves = ['Progressive Glove'] * 2
basicgloves = ['Power Glove', 'Titans Mitts'] basicgloves = ['Power Glove', 'Titans Mitts']
normalbottles = ['Bottle', 'BottleRedPotion', 'BottleGreenPotion', 'BottleBluePotion', 'BottleFairy', 'BottleBee', 'BottleGoodBee'] normalbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)']
hardbottles = ['Bottle', 'BottleRedPotion', 'BottleGreenPotion', 'BottleBluePotion', 'BottleBee', 'BottleGoodBee'] hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)', 'Bottle (Good Bee)']
normalbaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows', 'Magic Upgrade (1/2)'] + ['Rupees (300)'] * 4 + normalbaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows', 'Magic Upgrade (1/2)'] + ['Rupees (300)'] * 4 +
['Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24) ['Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24)
@ -20,36 +21,17 @@ normalsecond15extra = ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)']
normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', 'Rupee (1)', 'Rupees (5)'] normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', 'Rupee (1)', 'Rupees (5)']
normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)'] normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)']
normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2
normaltimedohko = ['Green Clock'] * 25
normaltimedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10
normaltriforcehunt = ['Triforce Piece'] * 30
normalprogressivesword = ['Progressive Sword'] * 3
normalbasicsword = ['Master Sword', 'Tempered Sword', 'Golden Sword']
normalswordless = ['Rupees (20)'] * 4
normalprogressiveshield = ['Progressive Shield'] * 3
normalbasicshield = ['Blue Shield', 'Red Shield', 'Mirror Shield']
normalprogressivearmor = ['Progressive Armor'] * 2
normalbasicarmor = ['Blue Mail', 'Red Mail']
easybaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 + easybaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 +
['Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12) ['Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12)
easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)'] easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)']
easylimitedextra = ['Boss Heart Container'] * 3 easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 peices of heart
easyfirst15extra = ['Rupees (100)'] + ['Rupees (50)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 + ['Bombs (3)'] easyfirst15extra = ['Rupees (100)'] + ['Rupees (50)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 + ['Bombs (3)']
easysecond10extra = ['Bombs (3)'] * 9 + ['Rupee (1)'] easysecond10extra = ['Bombs (3)'] * 9 + ['Rupee (1)']
easythird5extra = ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (5)'] easythird5extra = ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (5)']
easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 3 + ['Rupees (5)'] * 3 easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 3 + ['Rupees (5)'] * 3
easytimedohko = ['Green Clock'] * 25
easytimedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5
easytimedotherextra = ['Red Clock'] * 5 easytimedotherextra = ['Red Clock'] * 5
easytriforcehunt = ['Triforce Piece'] * 30
easyprogressivesword = ['Progressive Sword'] * 7
easybasicsword = ['Fighter Sword', 'Master Sword', 'Master Sword', 'Tempered Sword', 'Tempered Sword', 'Golden Sword', 'Golden Sword']
easyswordless = ['Rupees (20)'] * 8
easyprogressiveshield = ['Progressive Shield'] * 6
easybasicshield = ['Blue Shield', 'Blue Shield', 'Red Shield', 'Red Shield', 'Mirror Shield', 'Mirror Shield']
easyprogressivearmor = ['Progressive Armor'] * 4
easybasicarmor = ['Blue Mail', 'Blue Mail', 'Red Mail', 'Red Mail']
hardbaseitems = (['Silver Arrows', 'Single Arrow'] + ['Rupees (300)'] + ['Rupees (100)'] * 2 + ['Rupees (50)'] + ['Bombs (3)'] + hardbaseitems = (['Silver Arrows', 'Single Arrow'] + ['Rupees (300)'] + ['Rupees (100)'] * 2 + ['Rupees (50)'] + ['Bombs (3)'] +
['Boss Heart Container'] * 5 + ['Piece of Heart'] * 24) ['Boss Heart Container'] * 5 + ['Piece of Heart'] * 24)
@ -57,15 +39,6 @@ hardfirst20extra = ['Bombs (3)'] * 4 + ['Single Bomb'] * 4 + ['Rupees (5)'] * 5
hardsecond20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 10 + ['Rupees (20)'] * 2 + ['Rupee (1)'] * 3 + ['Arrows (10)'] hardsecond20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 10 + ['Rupees (20)'] * 2 + ['Rupee (1)'] * 3 + ['Arrows (10)']
hardthird20extra = ['Arrows (10)'] * 4 + ['Rupees (20)'] * 3 + ['Rupees (5)'] * 3 + ['Single Bomb'] * 5 + ['Single Arrow'] * 5 hardthird20extra = ['Arrows (10)'] * 4 + ['Rupees (20)'] * 3 + ['Rupees (5)'] * 3 + ['Single Bomb'] * 5 + ['Single Arrow'] * 5
hardfinal20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 2 + ['Single Arrow'] * 14 hardfinal20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 2 + ['Single Arrow'] * 14
hardtimedohko = ['Green Clock'] * 20
hardtimedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10
hardtriforcehunt = ['Triforce Piece'] * 40
hardprogressivesword = ['Progressive Sword'] * 3
hardbasicsword = ['Master Sword', 'Master Sword', 'Tempered Sword']
hardswordless = ['Rupees (20)'] * 4
hardprogressiveshield = ['Progressive Shield'] * 3
hardbasicshield = ['Blue Shield', 'Red Shield', 'Red Shield']
hardarmor = ['Progressive Armor', 'Progressive Armor']
expertbaseitems = (['Single Arrow', 'Rupees (300)', 'Rupees (100)', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 4 + ['Rupees (5)'] * 5 + expertbaseitems = (['Single Arrow', 'Rupees (300)', 'Rupees (100)', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 4 + ['Rupees (5)'] * 5 +
['Rupees (20)'] + ['Single Bomb'] * 2 + ['Piece of Heart'] * 24) ['Rupees (20)'] + ['Single Bomb'] * 2 + ['Piece of Heart'] * 24)
@ -73,12 +46,6 @@ expertfirst15extra = ['Single Bomb'] * 13 + ['Rupees (20)'] * 2
expertsecond25extra = ['Single Bomb'] * 8 + ['Single Arrow'] * 9 + ['Rupees (20)'] * 3 + ['Rupee (1)'] * 5 expertsecond25extra = ['Single Bomb'] * 8 + ['Single Arrow'] * 9 + ['Rupees (20)'] * 3 + ['Rupee (1)'] * 5
expertthird15extra = ['Rupees (5)'] * 5 + ['Single Bomb'] * 3 + ['Rupees (20)'] * 2 + ['Single Arrow'] * 5 expertthird15extra = ['Rupees (5)'] * 5 + ['Single Bomb'] * 3 + ['Rupees (20)'] * 2 + ['Single Arrow'] * 5
expertfinal25extra = ['Single Bomb'] * 4 + ['Rupees (20)'] * 3 + ['Single Arrow'] * 18 expertfinal25extra = ['Single Bomb'] * 4 + ['Rupees (20)'] * 3 + ['Single Arrow'] * 18
experttimedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5
experttimedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10
experttriforcehunt = ['Triforce Piece'] * 40
expertprogressivesword = ['Progressive Sword'] * 3
expertbasicsword = ['Fighter Sword', 'Master Sword', 'Master Sword']
expertswordless = ['Rupees (20)'] * 3 + ['Silver Arrows']
insanebaseitems = (['Single Arrow', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 3 + ['Rupees (5)'] * 10 + ['Rupees (300)'] * 4 + ['Rupees (100)'] * 3 + insanebaseitems = (['Single Arrow', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 3 + ['Rupees (5)'] * 10 + ['Rupees (300)'] * 4 + ['Rupees (100)'] * 3 +
['Rupee (1)'] * 4 + ['Single Bomb'] * 4) ['Rupee (1)'] * 4 + ['Single Bomb'] * 4)
@ -87,16 +54,131 @@ insanesecond25extra = ['Single Bomb'] * 7 + ['Single Arrow'] * 7 + ['Rupee (1)']
insanethird10extra = ['Single Bomb'] * 3 + ['Single Arrow'] * 3 + ['Rupee (1)'] * 3 + ['Rupees (20)'] insanethird10extra = ['Single Bomb'] * 3 + ['Single Arrow'] * 3 + ['Rupee (1)'] * 3 + ['Rupees (20)']
insanefourth15extra = ['Single Bomb'] * 5 + ['Single Arrow'] * 5 + ['Rupee (1)'] * 5 insanefourth15extra = ['Single Bomb'] * 5 + ['Single Arrow'] * 5 + ['Rupee (1)'] * 5
insanefinal25extra = ['Single Bomb'] * 2 + ['Single Arrow'] * 10 + ['Rupee (1)'] * 7 + ['Rupees (20)'] * 6 insanefinal25extra = ['Single Bomb'] * 2 + ['Single Arrow'] * 10 + ['Rupee (1)'] * 7 + ['Rupees (20)'] * 6
insanetimedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5
insanetimedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10 Difficulty = namedtuple('Difficulty',
insanetriforcehunt = ['Triforce Piece'] * 50 ['baseitems', 'bottles', 'bottle_count','same_bottle', 'progressiveshield',
insaneprogressivesword = ['Progressive Sword'] * 3 'basicshield', 'progressivearmor', 'basicarmor', 'swordless',
insanebasicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'] 'progressivesword', 'basicsword', 'timedohko', 'timedother',
insaneswordless = ['Rupees (20)'] * 3 + ['Silver Arrows'] 'triforcehunt', 'triforce_pieces_required', 'conditional_extras',
'extras'])
TotalItemsToPlace = 153
def easy_conditional_extras(timer, goal, mode, pool, placed_items):
extraitems = TotalItemsToPlace - len(pool) - len(placed_items)
if extraitems < len(easyextra):
return easylimitedextra
if timer in ['timed', 'timed-countdown']:
return easytimedotherextra
return []
def no_conditonal_extras(*args):
return []
#def Difficulty(**kwargs):
# protodifficulty._replace(**kwargs)
difficulties= {
'normal': Difficulty(
baseitems = normalbaseitems,
bottles = normalbottles,
bottle_count = 4,
same_bottle = False,
progressiveshield = ['Progressive Shield'] * 3,
basicshield = ['Blue Shield', 'Red Shield', 'Mirror Shield'],
progressivearmor = ['Progressive Armor'] * 2,
basicarmor = ['Blue Mail', 'Red Mail'],
swordless = ['Rupees (20)'] * 4,
progressivesword = ['Progressive Sword'] * 3,
basicsword = ['Master Sword', 'Tempered Sword', 'Golden Sword'],
timedohko = ['Green Clock'] * 25,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 20,
conditional_extras = no_conditonal_extras,
extras = [normalfirst15extra,normalsecond15extra,normalthird10extra,normalfourth5extra,normalfinal25extra]
),
'easy': Difficulty(
baseitems = easybaseitems,
bottles = normalbottles,
bottle_count = 8,
same_bottle = False,
progressiveshield = ['Progressive Shield'] * 6,
basicshield = ['Blue Shield', 'Red Shield', 'Mirror Shield'] * 2,
progressivearmor = ['Progressive Armor'] * 4,
basicarmor = ['Blue Mail', 'Red Mail'] * 2,
swordless = ['Rupees (20)'] * 8,
progressivesword = ['Progressive Sword'] * 7,
basicsword = ['Master Sword', 'Tempered Sword', 'Golden Sword'] *2 + ['Fighter Sword'],
timedohko = ['Green Clock'] * 25,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5, # +5 more Red Clocks if there is room
triforcehunt = ['Triforce Piece'] * 30,
triforce_pieces_required = 10,
conditional_extras = easy_conditional_extras,
extras = [easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra],
),
'hard': Difficulty(
baseitems = hardbaseitems,
bottles = hardbottles,
bottle_count = 4,
same_bottle = False,
progressiveshield = ['Progressive Shield'] * 3,
basicshield = ['Blue Shield', 'Red Shield', 'Red Shield'],
progressivearmor = ['Progressive Armor'] * 2,
basicarmor = ['Progressive Armor'] * 2, #only the first one will upgrade, making this equivlent to two blue mail
swordless = ['Rupees (20)'] * 4,
progressivesword = ['Progressive Sword'] * 3,
basicsword = ['Master Sword', 'Master Sword', 'Tempered Sword'],
timedohko = ['Green Clock'] * 20,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 40,
triforce_pieces_required = 30,
conditional_extras = no_conditonal_extras,
extras = [hardfirst20extra, hardsecond20extra, hardthird20extra, hardfinal20extra],
),
'expert': Difficulty(
baseitems = expertbaseitems,
bottles = hardbottles,
bottle_count = 4,
same_bottle = True,
progressiveshield = [],
basicshield = [],
progressivearmor = [],
basicarmor = [],
swordless = ['Rupees (20)'] * 3 + ['Silver Arrows'],
progressivesword = ['Progressive Sword'] * 3,
basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'],
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 40,
triforce_pieces_required = 40,
conditional_extras = no_conditonal_extras,
extras = [expertfirst15extra, expertsecond25extra, expertthird15extra, expertfinal25extra],
),
'insane': Difficulty(
baseitems = insanebaseitems,
bottles = hardbottles,
bottle_count = 4,
same_bottle = True,
progressiveshield = [],
basicshield = [],
progressivearmor = [],
basicarmor = [],
swordless = ['Rupees (20)'] * 3 + ['Silver Arrows'],
progressivesword = ['Progressive Sword'] * 3,
basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'],
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
triforcehunt = ['Triforce Piece'] * 50,
triforce_pieces_required = 50,
conditional_extras = no_conditonal_extras,
extras = [insanefirst15extra, insanesecond25extra, insanethird10extra, insanefourth15extra, insanefinal25extra],
),
}
def generate_itempool(world): def generate_itempool(world):
if (world.difficulty not in ['easy', 'normal', 'hard', 'expert', 'insane'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'] if (world.difficulty not in ['easy', 'normal', 'hard', 'expert', 'insane'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals']
or world.mode not in ['open', 'standard', 'swordless'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']): or world.mode not in ['open', 'standard', 'swordless'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
raise NotImplementedError('Not supported yet') raise NotImplementedError('Not supported yet')
world.push_item('Ganon', ItemFactory('Triforce'), False) world.push_item('Ganon', ItemFactory('Triforce'), False)
@ -107,431 +189,17 @@ def generate_itempool(world):
world.get_location('Agahnim 2').event = True world.get_location('Agahnim 2').event = True
# set up item pool # set up item pool
world.itempool = ItemFactory(alwaysitems) (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode)
if world.progressive == 'on': world.itempool = ItemFactory(pool)
world.itempool.extend(ItemFactory(progressivegloves)) for (location, item) in placed_items:
elif world.progressive == 'off': world.push_item(location, ItemFactory(item), False)
world.itempool.extend(ItemFactory(basicgloves)) world.get_location(location).event = True
else: if clock_mode is not None:
randvalue = random.randint(0, 1) world.clock_mode = clock_mode
if (randvalue == 0): if treasure_hunt_count is not None:
world.itempool.extend(ItemFactory(progressivegloves)) world.treasure_hunt_count = treasure_hunt_count
else: if treasure_hunt_icon is not None:
world.itempool.extend(ItemFactory(basicgloves)) wrold.treasure_hunt_icon = treasure_hunt_icon
# insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start
if world.shuffle == 'insanity':
world.push_item('Link\'s House', ItemFactory('Magic Mirror'), False)
world.get_location('Link\'s House').event = True
world.push_item('Sanctuary', ItemFactory('Moon Pearl'), False)
world.get_location('Sanctuary').event = True
else:
world.itempool.extend(ItemFactory(['Magic Mirror', 'Moon Pearl']))
if world.timer == 'display':
world.clock_mode = 'stopwatch'
if world.difficulty == 'normal':
world.itempool.extend(ItemFactory(normalbaseitems))
for i in range (0, 4):
thisbottle = normalbottles[random.randint(0, 6)]
world.itempool.append(ItemFactory(thisbottle))
extraitems = 70
if world.timer in ['timed', 'timed-countdown']:
world.itempool.extend(ItemFactory(normaltimedother))
extraitems = extraitems - 40
world.clock_mode = 'stopwatch' if world.timer == 'timed' else 'countdown'
elif world.timer == 'timed-ohko':
world.itempool.extend(ItemFactory(normaltimedohko))
extraitems = extraitems - 25
world.clock_mode = 'ohko'
if world.goal == 'triforcehunt':
world.itempool.extend(ItemFactory(normaltriforcehunt))
extraitems = extraitems - 30
world.treasure_hunt_count = 20
world.treasure_hunt_icon = 'Triforce Piece'
if extraitems > 0:
world.itempool.extend(ItemFactory(normalfirst15extra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(normalsecond15extra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(normalthird10extra))
extraitems = extraitems - 10
if extraitems > 0:
world.itempool.extend(ItemFactory(normalfourth5extra))
extraitems = extraitems - 5
if extraitems > 0:
world.itempool.extend(ItemFactory(normalfinal25extra))
extraitems = extraitems - 25
if world.progressive == 'on':
world.itempool.extend(ItemFactory(normalprogressiveshield))
world.itempool.extend(ItemFactory(normalprogressivearmor))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(normalbasicshield))
world.itempool.extend(ItemFactory(normalbasicarmor))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(normalprogressiveshield))
else:
world.itempool.extend(ItemFactory(normalbasicshield))
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(normalprogressivearmor))
else:
world.itempool.extend(ItemFactory(normalbasicarmor))
if world.mode == 'swordless':
world.itempool.extend(ItemFactory(normalswordless))
elif world.mode == 'standard':
if world.progressive == 'on':
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(normalprogressivesword))
elif world.progressive == 'off':
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(normalbasicsword))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(normalprogressivesword))
else:
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(normalbasicsword))
else:
if world.progressive == 'on':
world.itempool.extend(ItemFactory(normalprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(normalbasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(normalprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
else:
world.itempool.extend(ItemFactory(normalbasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
elif world.difficulty == 'easy':
world.itempool.extend(ItemFactory(easybaseitems))
for i in range (0, 8):
thisbottle = normalbottles[random.randint(0, 6)]
world.itempool.append(ItemFactory(thisbottle))
extraitems = 70
if world.timer in ['timed', 'timed-countdown']:
world.itempool.extend(ItemFactory(easytimedother))
extraitems = extraitems - 40
world.clock_mode = 'stopwatch' if world.timer == 'timed' else 'countdown'
elif world.timer == 'timed-ohko':
world.itempool.extend(ItemFactory(easytimedohko))
extraitems = extraitems - 25
world.clock_mode = 'ohko'
if world.goal == 'triforcehunt':
world.itempool.extend(ItemFactory(easytriforcehunt))
extraitems = extraitems - 30
world.treasure_hunt_count = 10
world.treasure_hunt_icon = 'Triforce Piece'
if extraitems == 0:
world.itempool.extend(ItemFactory(easylimitedextra))
else:
world.itempool.extend(ItemFactory(easyextra))
if world.timer in ['timed', 'timed-countdown']:
world.itempool.extend(ItemFactory(easytimedotherextra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(easyfirst15extra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(easysecond10extra))
extraitems = extraitems - 10
if extraitems > 0:
world.itempool.extend(ItemFactory(easythird5extra))
extraitems = extraitems - 5
if extraitems > 0:
world.itempool.extend(ItemFactory(easyfinal25extra))
extraitems = extraitems - 25
if world.progressive == 'on':
world.itempool.extend(ItemFactory(easyprogressiveshield))
world.itempool.extend(ItemFactory(easyprogressivearmor))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(easybasicshield))
world.itempool.extend(ItemFactory(easybasicarmor))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(easyprogressiveshield))
else:
world.itempool.extend(ItemFactory(easybasicshield))
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(easyprogressivearmor))
else:
world.itempool.extend(ItemFactory(easybasicarmor))
if world.mode == 'swordless':
world.itempool.extend(ItemFactory(easyswordless))
elif world.mode == 'standard':
if world.progressive == 'on':
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(easyprogressivesword))
elif world.progressive == 'off':
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(easybasicsword))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(easyprogressivesword))
else:
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(easybasicsword))
else:
if world.progressive == 'on':
world.itempool.extend(ItemFactory(easyprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(easybasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(easyprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
else:
world.itempool.extend(ItemFactory(easybasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
elif world.difficulty == 'hard':
world.itempool.extend(ItemFactory(hardbaseitems))
for i in range (0, 4):
thisbottle = hardbottles[random.randint(0, 5)]
world.itempool.append(ItemFactory(thisbottle))
extraitems = 80
if world.timer in ['timed', 'timed-countdown']:
world.itempool.extend(ItemFactory(hardtimedother))
extraitems = extraitems - 40
world.clock_mode = 'stopwatch' if world.timer == 'timed' else 'countdown'
elif world.timer == 'timed-ohko':
world.itempool.extend(ItemFactory(hardtimedohko))
extraitems = extraitems - 25
world.clock_mode = 'ohko'
if world.goal == 'triforcehunt':
world.itempool.extend(ItemFactory(hardtriforcehunt))
extraitems = extraitems - 40
world.treasure_hunt_count = 30
world.treasure_hunt_icon = 'Triforce Piece'
if extraitems > 0:
world.itempool.extend(ItemFactory(hardfirst20extra))
extraitems = extraitems - 20
if extraitems > 0:
world.itempool.extend(ItemFactory(hardsecond20extra))
extraitems = extraitems - 20
if extraitems > 0:
world.itempool.extend(ItemFactory(hardthird20extra))
extraitems = extraitems - 20
if extraitems > 0:
world.itempool.extend(ItemFactory(hardfinal20extra))
extraitems = extraitems - 20
world.itempool.extend(ItemFactory(hardarmor))
if world.progressive == 'on':
world.itempool.extend(ItemFactory(hardprogressiveshield))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(hardbasicshield))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(hardprogressiveshield))
else:
world.itempool.extend(ItemFactory(hardbasicshield))
if world.mode == 'swordless':
world.itempool.extend(ItemFactory(hardswordless))
elif world.mode == 'standard':
if world.progressive == 'on':
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(hardprogressivesword))
elif world.progressive == 'off':
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(hardbasicsword))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(hardprogressivesword))
else:
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(hardbasicsword))
else:
if world.progressive == 'on':
world.itempool.extend(ItemFactory(hardprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(hardbasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(hardprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
else:
world.itempool.extend(ItemFactory(hardbasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
elif world.difficulty == 'expert':
world.itempool.extend(ItemFactory(expertbaseitems))
thisbottle = hardbottles[random.randint(0, 5)]
for i in range (0, 4):
world.itempool.append(ItemFactory(thisbottle))
extraitems = 80
if world.timer in ['timed', 'timed-countdown']:
world.itempool.extend(ItemFactory(experttimedother))
extraitems = extraitems - 40
world.clock_mode = 'stopwatch' if world.timer == 'timed' else 'countdown'
elif world.timer == 'timed-ohko':
world.itempool.extend(ItemFactory(experttimedohko))
extraitems = extraitems - 25
world.clock_mode = 'ohko'
if world.goal == 'triforcehunt':
world.itempool.extend(ItemFactory(experttriforcehunt))
extraitems = extraitems - 40
world.treasure_hunt_count = 40
world.treasure_hunt_icon = 'Triforce Piece'
if extraitems > 0:
world.itempool.extend(ItemFactory(expertfirst15extra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(expertsecond25extra))
extraitems = extraitems - 25
if extraitems > 0:
world.itempool.extend(ItemFactory(expertthird15extra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(expertfinal25extra))
extraitems = extraitems - 25
if world.mode == 'swordless':
world.itempool.extend(ItemFactory(expertswordless))
elif world.mode == 'standard':
if world.progressive == 'on':
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(expertprogressivesword))
elif world.progressive == 'off':
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(expertbasicsword))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(expertprogressivesword))
else:
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(expertbasicsword))
else:
if world.progressive == 'on':
world.itempool.extend(ItemFactory(expertprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(expertbasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(expertprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
else:
world.itempool.extend(ItemFactory(expertbasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
elif world.difficulty == 'insane':
world.itempool.extend(ItemFactory(insanebaseitems))
thisbottle = hardbottles[random.randint(0, 5)]
for i in range (0, 4):
world.itempool.append(ItemFactory(thisbottle))
extraitems = 90
if world.timer in ['timed', 'timed-countdown']:
world.itempool.extend(ItemFactory(insanetimedother))
extraitems = extraitems - 40
world.clock_mode = 'stopwatch' if world.timer == 'timed' else 'countdown'
elif world.timer == 'timed-ohko':
world.itempool.extend(ItemFactory(insanetimedohko))
extraitems = extraitems - 25
world.clock_mode = 'ohko'
if world.goal == 'triforcehunt':
world.itempool.extend(ItemFactory(insanetriforcehunt))
extraitems = extraitems - 50
world.treasure_hunt_count = 50
world.treasure_hunt_icon = 'Triforce Piece'
if extraitems > 0:
world.itempool.extend(ItemFactory(insanefirst15extra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(insanesecond25extra))
extraitems = extraitems - 25
if extraitems > 0:
world.itempool.extend(ItemFactory(insanethird10extra))
extraitems = extraitems - 10
if extraitems > 0:
world.itempool.extend(ItemFactory(insanefourth15extra))
extraitems = extraitems - 15
if extraitems > 0:
world.itempool.extend(ItemFactory(insanefinal25extra))
extraitems = extraitems - 25
if world.mode == 'swordless':
world.itempool.extend(ItemFactory(insaneswordless))
elif world.mode == 'standard':
if world.progressive == 'on':
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(insaneprogressivesword))
elif world.progressive == 'off':
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(insanebasicsword))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.push_item('Link\'s Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(insaneprogressivesword))
else:
world.push_item('Link\'s Uncle', ItemFactory('Fighter Sword'), False)
world.get_location('Link\'s Uncle').event = True
world.itempool.extend(ItemFactory(insanebasicsword))
else:
if world.progressive == 'on':
world.itempool.extend(ItemFactory(insaneprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
elif world.progressive == 'off':
world.itempool.extend(ItemFactory(insanebasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
else:
randvalue = random.randint(0, 1)
if (randvalue == 0):
world.itempool.extend(ItemFactory(insaneprogressivesword))
world.itempool.extend(ItemFactory(['Progressive Sword']))
else:
world.itempool.extend(ItemFactory(insanebasicsword))
world.itempool.extend(ItemFactory(['Fighter Sword']))
if world.goal == 'pedestal':
world.push_item('Master Sword Pedestal', ItemFactory('Triforce'), False)
world.get_location('Master Sword Pedestal').event = True
# shuffle medallions # shuffle medallions
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
@ -546,4 +214,120 @@ def generate_itempool(world):
random.shuffle(crystal_locations) random.shuffle(crystal_locations)
fill_restrictive(world, world.get_all_state(keys=True), crystal_locations, crystals) fill_restrictive(world, world.get_all_state(keys=True), crystal_locations, crystals)
def get_pool_core(progressive,shuffle,difficulty,timer, goal, mode):
pool=[]
placed_items=[]
clock_mode=None
treasure_hunt_count=None
treasure_hunt_icon=None
pool.extend(alwaysitems)
def wantProgressives():
return random.choice([True, False]) if progressive == 'random' else progressive=='on'
if wantProgressives():
pool.extend(progressivegloves)
else:
pool.extend(basicgloves)
# insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start
if shuffle == 'insanity':
placed_items.append(('Link\'s House', 'Magic Mirror'))
placed_items.append(('Sanctuary', 'Moon Pearl'))
else:
pool.extend(['Magic Mirror', 'Moon Pearl'])
if timer == 'display':
clock_mode = 'stopwatch'
elif timer == 'ohko':
clock_mode = 'ohko'
diff = difficulties[difficulty]
pool.extend(diff.baseitems)
# expert+ difficulties produce the same contents for
# all bottles, since only one bottle is available
if diff.same_bottle:
thisbottle = random.choice(diff.bottles)
for i in range (diff.bottle_count):
if not diff.same_bottle:
thisbottle = random.choice(diff.bottles)
pool.append(thisbottle)
if wantProgressives():
pool.extend(diff.progressiveshield)
else:
pool.extend(diff.basicshield)
if wantProgressives():
pool.extend(diff.progressivearmor)
else:
pool.extend(diff.basicarmor)
if mode == 'swordless':
pool.extend(diff.swordless)
elif mode == 'standard':
if wantProgressives():
placed_items.append(('Link\'s Uncle', 'Progressive Sword'))
pool.extend(diff.progressivesword)
else:
placed_items.append(('Link\'s Uncle', 'Fighter Sword'))
pool.extend(diff.basicsword)
else:
if wantProgressives():
pool.extend(diff.progressivesword)
pool.extend(['Progressive Sword'])
else:
pool.extend(diff.basicsword)
pool.extend(['Fighter Sword'])
extraitems = TotalItemsToPlace - len(pool) - len(placed_items)
if timer in ['timed', 'timed-countdown']:
pool.extend(diff.timedother)
extraitems -= len(diff.timedother)
clock_mode = 'stopwatch' if timer == 'timed' else 'countdown'
elif timer == 'timed-ohko':
pool.extend(diff.timedohko)
extraitems -= len(diff.timedohko)
clock_mode = 'countdown-ohko'
if goal == 'triforcehunt':
pool.extend(diff.triforcehunt)
extraitems -= len(diff.triforcehunt)
treasure_hunt_count = diff.triforce_pieces_required
treasure_hunt_icon = 'Triforce Piece'
cond_extras = diff.conditional_extras(timer, goal, mode, pool, placed_items)
pool.extend(cond_extras)
extraitems -= len(cond_extras)
for extra in diff.extras:
if(extraitems > 0):
pool.extend(extra )
extraitems -= len(extra)
if goal == 'pedestal':
placed_items.append(('Master Sword Pedestal', 'Triforce'))
return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon)
# A quick test to ensure all combinations generate the correct amount of items.
if __name__ == '__main__':
for difficulty in ['easy', 'normal', 'hard', 'expert', 'insane']:
for goal in ['ganon', 'triforcehunt', 'pedestal']:
for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
for mode in ['open', 'standard', 'swordless']:
for progressive in ['on','off']:
for shuffle in ['full','insane']:
out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode)
count = len(out[0]) + len(out[1])
correct_count = TotalItemsToPlace
if goal in ['pedestal']:
# pedestal goals generate one extra item
correct_count += 1
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode))

View File

@ -47,12 +47,12 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
'Bombos': (True, False, None, 0x0F, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'boy hides coin again'), 'Bombos': (True, False, None, 0x0F, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'boy hides coin again'),
'Quake': (True, False, None, 0x11, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'boy hides coin again'), 'Quake': (True, False, None, 0x11, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'boy hides coin again'),
'Bottle': (True, False, None, 0x16, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'boy stores things again'), 'Bottle': (True, False, None, 0x16, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'boy stores things again'),
'BottleRedPotion': (True, False, None, 0x2B, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'boy drinks again'), 'Bottle (Red Potion)': (True, False, None, 0x2B, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'boy drinks again'),
'BottleGreenPotion': (True, False, None, 0x2C, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'boy drinks again'), 'Bottle (Green Potion)': (True, False, None, 0x2C, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'boy drinks again'),
'BottleBluePotion': (True, False, None, 0x2D, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'boy stores drinks again'), 'Bottle (Blue Potion)': (True, False, None, 0x2D, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'boy stores drinks again'),
'BottleFairy': (True, False, None, 0x3D, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'boy revives again'), 'Bottle (Fairy)': (True, False, None, 0x3D, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'boy revives again'),
'BottleBee': (True, False, None, 0x3C, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'boy is stung again'), 'Bottle (Bee)': (True, False, None, 0x3C, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'boy is stung again'),
'BottleGoodBee': (True, False, None, 0x48, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'boy is stung again'), 'Bottle (Good Bee)': (True, False, None, 0x48, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'boy is stung again'),
'Master Sword': (True, False, None, 0x50, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'boy fights again'), 'Master Sword': (True, False, None, 0x50, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'boy fights again'),
'Tempered Sword': (True, False, None, 0x02, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'boy fights again'), 'Tempered Sword': (True, False, None, 0x02, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'boy fights again'),
'Fighter Sword': (True, False, None, 0x49, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'boy fights again'), 'Fighter Sword': (True, False, None, 0x49, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'boy fights again'),

View File

@ -228,4 +228,4 @@ def create_playthrough(world):
old_world.required_locations = [location.name for sphere in collection_spheres for location in sphere] old_world.required_locations = [location.name for sphere in collection_spheres for location in sphere]
# we can finally output our playthrough # we can finally output our playthrough
old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)]) old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)])

View File

@ -1,7 +1,7 @@
from BaseClasses import World from BaseClasses import World
from Regions import create_regions from Regions import create_regions
from EntranceShuffle import link_entrances, connect_entrance, connect_two_way, connect_exit from EntranceShuffle import link_entrances, connect_entrance, connect_two_way, connect_exit
from Rom import patch_rom, LocalRom, write_string_to_rom, write_credits_string_to_rom from Rom import patch_rom, LocalRom, write_string_to_rom
from Rules import set_rules from Rules import set_rules
from Dungeons import create_dungeons from Dungeons import create_dungeons
from Items import ItemFactory from Items import ItemFactory
@ -12,6 +12,7 @@ import logging
import argparse import argparse
import os import os
import hashlib import hashlib
import sys
__version__ = '0.2-dev' __version__ = '0.2-dev'
@ -161,9 +162,10 @@ def fill_world(world, plando, text_patches):
elif line.startswith('!text_'): elif line.startswith('!text_'):
textname, text = line.split(':', 1) textname, text = line.split(':', 1)
text_patches.append([textname.lstrip('!text_').strip(), 'text', text.strip()]) text_patches.append([textname.lstrip('!text_').strip(), 'text', text.strip()])
elif line.startswith('!credits_'): #temporarilly removed. New credits system not ready to handle this.
textname, text = line.split(':', 1) #elif line.startswith('!credits_'):
text_patches.append([textname.lstrip('!credits_').strip(), 'credits', text.strip()]) # textname, text = line.split(':', 1)
# text_patches.append([textname.lstrip('!credits_').strip(), 'credits', text.strip()])
continue continue
locationstr, itemstr = line.split(':', 1) locationstr, itemstr = line.split(':', 1)
@ -214,13 +216,13 @@ if __name__ == '__main__':
# ToDo: Validate files further than mere existance # ToDo: Validate files further than mere existance
if not os.path.isfile(args.rom): if not os.path.isfile(args.rom):
input('Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom) input('Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom)
exit(1) sys.exit(1)
if not os.path.isfile(args.plando): if not os.path.isfile(args.plando):
input('Could not find Plandomizer distribution at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.plando) input('Could not find Plandomizer distribution at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.plando)
exit(1) sys.exit(1)
if args.sprite is not None and not os.path.isfile(args.rom): if args.sprite is not None and not os.path.isfile(args.rom):
input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite) input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite)
exit(1) sys.exit(1)
# set up logger # set up logger
loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[args.loglevel] loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[args.loglevel]

View File

@ -30,13 +30,13 @@ Output a Spoiler File (default: False)
--logic [{noglitches,minorglitches}] --logic [{noglitches,minorglitches}]
``` ```
Select Enforcement of Item Requirements. Select Enforcement of Item Requirements.
### No Glitches ### No Glitches
The game can be completed without knowing how to perform glitches of any kind. The game can be completed without knowing how to perform glitches of any kind.
### Minor Glitches ### Minor Glitches
May require Fake Flippers, Bunny Revival. (default: noglitches) May require Fake Flippers, Bunny Revival. (default: noglitches)
@ -54,7 +54,7 @@ Gives lightcone in Hyrule Castle Sewers even without the Lamp.
### Open ### Open
This mode starts with the option to start in your house or the sanctuary, you are free to explore. This mode starts with the option to start in your house or the sanctuary, you are free to explore.
Special notes: Special notes:
@ -84,11 +84,11 @@ Select completion goal.
Standard game completion requiring you to collect the 7 crystals, defeat Agahnim 2 and then beat Ganon. Standard game completion requiring you to collect the 7 crystals, defeat Agahnim 2 and then beat Ganon.
### Pedestal ### Pedestal
Places the Triforce at the Master Sword Pedestal. Ganon cannot be damaged. Places the Triforce at the Master Sword Pedestal. Ganon cannot be damaged.
### All Dungeons ### All Dungeons
Ganon cannot be damaged until all dungeons (including Hyrule Castle Tower and Ganons Tower) are cleared. Ganon cannot be damaged until all dungeons (including Hyrule Castle Tower and Ganons Tower) are cleared.
@ -143,7 +143,7 @@ This setting is a modest step up from Expert. The main difference is that the pl
additional health. additional health.
``` ```
--timer [{none,display,timed,timed-ohko,timed-countdown}] --timer [{none,display,timed,timed-ohko,ohko,timed-countdown}]
``` ```
Select the timer setting. Select the timer setting.
@ -168,10 +168,16 @@ Displays a countdown timer on screen that, when it hits zero, will put the playe
knockout state until more time is added to the clock via some of the Green Clocks that will be added knockout state until more time is added to the clock via some of the Green Clocks that will be added
to the itempool. to the itempool.
### OHKO
The player into a one hit state the entire game. This is the same as Timed-OHKO,
except that the clock starts at zero, and there are no Clock items, so it will
always stay at zero, resulting in a permanent one hit knockout state.
### Timed-countdown ### Timed-countdown
Displays a countdown timer on screen that can be increased with Green Clocks and Blue Clocks or Displays a countdown timer on screen that can be increased with Green Clocks and Blue Clocks or
increased with Red Clocks found in chests that will be added to the itempool. The goal of this mode decreased with Red Clocks found in chests that will be added to the itempool. The goal of this mode
is to finish the game without the timer reaching zero, but the game will continue uninterrupted if is to finish the game without the timer reaching zero, but the game will continue uninterrupted if
the player runs out of time. the player runs out of time.
@ -203,7 +209,7 @@ randomized.
--algorithm [{freshness,flood,vt21,vt22,vt25,vt26,balanced}] --algorithm [{freshness,flood,vt21,vt22,vt25,vt26,balanced}]
``` ```
Select item filling algorithm. Select item filling algorithm.
### Balanced (Default) ### Balanced (Default)
This is a variation of vt26 that aims to strike a balance between the overworld heavy vt25 and the dungeon heavy vt26 algorithm. This is a variation of vt26 that aims to strike a balance between the overworld heavy vt25 and the dungeon heavy vt26 algorithm.
@ -214,7 +220,7 @@ Items and locations are shuffled like in VT25, and dungeon items are now placed
shuffled it includes a slight deliberate bias against having too many desireable items in Ganon's Tower to help counterbalance shuffled it includes a slight deliberate bias against having too many desireable items in Ganon's Tower to help counterbalance
the sheer number of chests in that single location. the sheer number of chests in that single location.
### VT25 ### VT25
Items and locations are shuffled and placed from the top of the lists. The only thing preventing an item from being placed into a spot Items and locations are shuffled and placed from the top of the lists. The only thing preventing an item from being placed into a spot
is if is absolutely impossible to be there given the previous made placement choices. Leads to very uniform but guaranteed solvable distributions. is if is absolutely impossible to be there given the previous made placement choices. Leads to very uniform but guaranteed solvable distributions.
@ -237,7 +243,7 @@ staleness, decreasing the likelihood of receiving a progress item.
--shuffle [{default,simple,restricted,full,madness,insanity,dungeonsfull,dungeonssimple}] --shuffle [{default,simple,restricted,full,madness,insanity,dungeonsfull,dungeonssimple}]
``` ```
Select Entrance Shuffling Algorithm. Select Entrance Shuffling Algorithm.
### Default ### Default
@ -250,7 +256,7 @@ on the overworld. On Death Mountain, entrances are connected more freely.
### Full (Default) ### Full (Default)
Mixes cave and dungeon entrances freely. Mixes cave and dungeon entrances freely.
### Restricted ### Restricted
@ -290,7 +296,7 @@ Define seed number to generate. (default: None) Using the same seed with same se
--count COUNT --count COUNT
``` ```
Use to batch generate multiple seeds with same settings. Use to batch generate multiple seeds with same settings.
If --seed is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with --seed given will produce the same 10 (different) roms each time). (default: None) If --seed is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with --seed given will produce the same 10 (different) roms each time). (default: None)
``` ```

52
Rom.py
View File

@ -21,10 +21,10 @@ class JsonRom(object):
def write_bytes(self, startaddress, values): def write_bytes(self, startaddress, values):
self.patches[str(startaddress)] = list(values) self.patches[str(startaddress)] = list(values)
def write_int16_to_rom(self, address, value): def write_int16_to_rom(self, address, value):
self.write_bytes(address, int16_as_bytes(value)) self.write_bytes(address, int16_as_bytes(value))
def write_int32_to_rom(self, address, value): def write_int32_to_rom(self, address, value):
self.write_bytes(address, int32_as_bytes(value)) self.write_bytes(address, int32_as_bytes(value))
@ -47,7 +47,7 @@ class LocalRom(object):
def write_int16_to_rom(self, address, value): def write_int16_to_rom(self, address, value):
self.write_bytes(address, int16_as_bytes(value)) self.write_bytes(address, int16_as_bytes(value))
def write_int32_to_rom(self, address, value): def write_int32_to_rom(self, address, value):
self.write_bytes(address, int32_as_bytes(value)) self.write_bytes(address, int32_as_bytes(value))
@ -66,7 +66,7 @@ class LocalRom(object):
self.buffer.extend(bytearray([0x00] * (2097152 - len(self.buffer)))) self.buffer.extend(bytearray([0x00] * (2097152 - len(self.buffer))))
# load randomizer patches # load randomizer patches
patches = json.load(open('base2current.json', 'r')) patches = json.load(open('data/base2current.json', 'r'))
for patch in patches: for patch in patches:
if isinstance(patch, dict): if isinstance(patch, dict):
for baseaddress, values in patch.items(): for baseaddress, values in patch.items():
@ -86,7 +86,7 @@ class LocalRom(object):
def int16_as_bytes(value): def int16_as_bytes(value):
value = value & 0xFFFF value = value & 0xFFFF
return [value & 0xFF, (value >> 8) & 0xFF] return [value & 0xFF, (value >> 8) & 0xFF]
def int32_as_bytes(value): def int32_as_bytes(value):
value = value & 0xFFFFFFFF value = value & 0xFFFFFFFF
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF] return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
@ -391,33 +391,39 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
# set up clocks for timed modes # set up clocks for timed modes
if world.clock_mode == 'off': if world.clock_mode == 'off':
rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode
rom.write_bytes(0x180200, [0x00, 0x00, 0x00, 0x00]) # red clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180200, 0) # red clock adjustment time (in frames, sint32)
rom.write_bytes(0x180204, [0x00, 0x00, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180204, 0) # blue clock adjustment time (in frames, sint32)
rom.write_bytes(0x180208, [0x00, 0x00, 0x00, 0x00]) # green clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180208, 0) # green clock adjustment time (in frames, sint32)
rom.write_bytes(0x18020C, [0x00, 0x00, 0x00, 0x00]) # starting time (in frames, sint32) rom.write_int32_to_rom(0x18020C, 0) # starting time (in frames, sint32)
elif world.clock_mode == 'ohko': elif world.clock_mode == 'ohko':
rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality
rom.write_bytes(0x180200, [0x00, 0x00, 0x00, 0x81]) # red clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180200, 0) # red clock adjustment time (in frames, sint32)
rom.write_bytes(0x180204, [0x20, 0x1C, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180204, 0) # blue clock adjustment time (in frames, sint32)
rom.write_bytes(0x180208, [0x40, 0x38, 0x00, 0x00]) # green clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180208, 0) # green clock adjustment time (in frames, sint32)
rom.write_int32_to_rom(0x18020C, 0) # starting time (in frames, sint32)
elif world.clock_mode == 'countdown-ohko':
rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality
rom.write_int32_to_rom(0x180200, -100 * 60 * 60 * 60) # red clock adjustment time (in frames, sint32)
rom.write_int32_to_rom(0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32)
rom.write_int32_to_rom(0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32)
if world.difficulty == 'easy': if world.difficulty == 'easy':
rom.write_bytes(0x18020C, [0x40, 0x19, 0x01, 0x00]) # starting time (in frames, sint32) rom.write_int32_to_rom(0x18020C, 20 * 60 * 60) # starting time (in frames, sint32)
elif world.difficulty == 'normal': elif world.difficulty == 'normal':
rom.write_bytes(0x18020C, [0xA0, 0x8C, 0x00, 0x00]) # starting time (in frames, sint32) rom.write_int32_to_rom(0x18020C, 10 * 60 * 60) # starting time (in frames, sint32)
else: else:
rom.write_bytes(0x18020C, [0x50, 0x46, 0x00, 0x00]) # starting time (in frames, sint32) rom.write_int32_to_rom(0x18020C, 5 * 60 * 60) # starting time (in frames, sint32)
if world.clock_mode == 'stopwatch': if world.clock_mode == 'stopwatch':
rom.write_bytes(0x180190, [0x02, 0x01, 0x00]) # set stopwatch mode rom.write_bytes(0x180190, [0x02, 0x01, 0x00]) # set stopwatch mode
rom.write_bytes(0x180200, [0xE0, 0xE3, 0xFF, 0xFF]) # red clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180200, -2 * 60 * 60) # red clock adjustment time (in frames, sint32)
rom.write_bytes(0x180204, [0x20, 0x1C, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32)
rom.write_bytes(0x180208, [0x40, 0x38, 0x00, 0x00]) # green clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32)
rom.write_bytes(0x18020C, [0x00, 0x00, 0x00, 0x00]) # starting time (in frames, sint32) rom.write_int32_to_rom(0x18020C, 0) # starting time (in frames, sint32)
if world.clock_mode == 'countdown': if world.clock_mode == 'countdown':
rom.write_bytes(0x180190, [0x01, 0x01, 0x00]) # set countdown, with no reset available rom.write_bytes(0x180190, [0x01, 0x01, 0x00]) # set countdown, with no reset available
rom.write_bytes(0x180200, [0xE0, 0xE3, 0xFF, 0xFF]) # red clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180200, -2 * 60 * 60) # red clock adjustment time (in frames, sint32)
rom.write_bytes(0x180204, [0x20, 0x1C, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32)
rom.write_bytes(0x180208, [0x40, 0x38, 0x00, 0x00]) # green clock adjustment time (in frames, sint32) rom.write_int32_to_rom(0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32)
rom.write_bytes(0x18020C, [0x80, 0x32, 0x02, 0x00]) # starting time (in frames, sint32) rom.write_int32_to_rom(0x18020C, 40 * 60 * 60) # starting time (in frames, sint32)
# set up goals for treasure hunt # set up goals for treasure hunt
rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon == 'Triforce Piece' else [0x0D, 0x28]) rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon == 'Triforce Piece' else [0x0D, 0x28])

View File

@ -301,7 +301,7 @@ def global_rules(world):
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)']))) set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
set_rule(world.get_entrance('Palace of Darkness (North)'), lambda state: state.has('Small Key (Palace of Darkness)', 4)) set_rule(world.get_entrance('Palace of Darkness (North)'), lambda state: state.has('Small Key (Palace of Darkness)', 4))
set_rule(world.get_location('Palace of Darkness - Big Chest'), lambda state: state.has('Big Key (Palace of Darkness)')) set_rule(world.get_location('Palace of Darkness - Big Chest'), lambda state: state.has('Big Key (Palace of Darkness)'))
if world.keysanity: if world.keysanity:
set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)']))) set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)'])))
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)']))) set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
@ -310,7 +310,7 @@ def global_rules(world):
set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)']))) set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)'])))
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)']))) set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
set_rule(world.get_entrance('Palace of Darkness Maze Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5)) set_rule(world.get_entrance('Palace of Darkness Maze Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5))
for location in ['Palace of Darkness - Big Chest', 'Palace of Darkness - Helmasaur']: for location in ['Palace of Darkness - Big Chest', 'Palace of Darkness - Helmasaur']:
forbid_item(world.get_location(location), 'Big Key (Palace of Darkness)') forbid_item(world.get_location(location), 'Big Key (Palace of Darkness)')
@ -320,7 +320,7 @@ def global_rules(world):
# these key rules are conservative, you might be able to get away with more lenient rules # these key rules are conservative, you might be able to get away with more lenient rules
randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'] randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right']
compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'] compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right']
set_rule(world.get_location('Ganons Tower - Bob\'s Torch'), lambda state: state.has_Boots()) set_rule(world.get_location('Ganons Tower - Bob\'s Torch'), lambda state: state.has_Boots())
set_rule(world.get_entrance('Ganons Tower (Tile Room)'), lambda state: state.has('Cane of Somaria')) set_rule(world.get_entrance('Ganons Tower (Tile Room)'), lambda state: state.has('Cane of Somaria'))
set_rule(world.get_entrance('Ganons Tower (Hookshot Room)'), lambda state: state.has('Hammer')) set_rule(world.get_entrance('Ganons Tower (Hookshot Room)'), lambda state: state.has('Hammer'))
@ -328,24 +328,24 @@ def global_rules(world):
set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 4) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Big Key (Ganons Tower)' and state.has('Small Key (Ganons Tower)', 3)) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)')) set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 4) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Big Key (Ganons Tower)' and state.has('Small Key (Ganons Tower)', 3)) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)'))
else: else:
set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)')) set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)'))
# It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere We reflect this in the chest requirements. # It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere We reflect this in the chest requirements.
# However we need to leave these at the lower values derive that with 3 keys it is always possible to reach Bob and Ice Armos. # However we need to leave these at the lower values derive that with 3 keys it is always possible to reach Bob and Ice Armos.
set_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has('Small Key (Ganons Tower)', 2)) set_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has('Small Key (Ganons Tower)', 2))
# It is possible to need more than 3 keys .... # It is possible to need more than 3 keys ....
set_rule(world.get_entrance('Ganons Tower (Firesnake Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3)) set_rule(world.get_entrance('Ganons Tower (Firesnake Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3))
#The actual requirements for these rooms to avoid key-lock #The actual requirements for these rooms to avoid key-lock
set_rule(world.get_location('Ganons Tower - Firesnake Room'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 2))) set_rule(world.get_location('Ganons Tower - Firesnake Room'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 2)))
for location in randomizer_room_chests: for location in randomizer_room_chests:
set_rule(world.get_location(location), lambda state: state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 3))) set_rule(world.get_location(location), lambda state: state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 3)))
# Once again it is possible to need more than 3 keys... # Once again it is possible to need more than 3 keys...
set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door'), lambda state: state.has('Small Key (Ganons Tower)', 3) and state.has('Fire Rod')) set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door'), lambda state: state.has('Small Key (Ganons Tower)', 3) and state.has('Fire Rod'))
# Actual requirements # Actual requirements
for location in compass_room_chests: for location in compass_room_chests:
set_rule(world.get_location(location), lambda state: state.has('Fire Rod') and (state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', compass_room_chests) and state.has('Small Key (Ganons Tower)', 3)))) set_rule(world.get_location(location), lambda state: state.has('Fire Rod') and (state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', compass_room_chests) and state.has('Small Key (Ganons Tower)', 3))))
set_rule(world.get_location('Ganons Tower - Big Chest'), lambda state: state.has('Big Key (Ganons Tower)')) set_rule(world.get_location('Ganons Tower - Big Chest'), lambda state: state.has('Big Key (Ganons Tower)'))
set_rule(world.get_location('Ganons Tower - Big Key Room - Left'), lambda state: state.has('Bow') or state.has_blunt_weapon()) set_rule(world.get_location('Ganons Tower - Big Key Room - Left'), lambda state: state.has('Bow') or state.has_blunt_weapon())
set_rule(world.get_location('Ganons Tower - Big Key Chest'), lambda state: state.has('Bow') or state.has_blunt_weapon()) set_rule(world.get_location('Ganons Tower - Big Key Chest'), lambda state: state.has('Bow') or state.has_blunt_weapon())
@ -429,7 +429,7 @@ def open_rules(world):
# softlock protection as you can reach the sewers small key door with a guard drop key # softlock protection as you can reach the sewers small key door with a guard drop key
forbid_item(world.get_location('Hyrule Castle - Boomerang Chest'), 'Small Key (Escape)') forbid_item(world.get_location('Hyrule Castle - Boomerang Chest'), 'Small Key (Escape)')
forbid_item(world.get_location('Hyrule Castle - Zelda\'s Chest'), 'Small Key (Escape)') forbid_item(world.get_location('Hyrule Castle - Zelda\'s Chest'), 'Small Key (Escape)')
# to prevent key-lock in keysanity we need to prevent these chests from having an item that # to prevent key-lock in keysanity we need to prevent these chests from having an item that
# blocks the small key # blocks the small key
if (world.keysanity): if (world.keysanity):
@ -443,7 +443,7 @@ def swordless_rules(world):
# should there ever be fixes that apply to open mode but not swordless, this # should there ever be fixes that apply to open mode but not swordless, this
# can be revisited. # can be revisited.
open_rules(world) open_rules(world)
set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has('Hammer') or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has('Hammer') or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle
set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has('Hammer') or state.has('Bug Catching Net') and state.has('Small Key (Agahnims Tower)', 2)) set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has('Hammer') or state.has('Bug Catching Net') and state.has('Small Key (Agahnims Tower)', 2))
set_rule(world.get_location('Ether Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer')) set_rule(world.get_location('Ether Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer'))
@ -479,7 +479,7 @@ def set_trock_key_rules(world):
# if we have backdoor access we can waste a key on the trinexx door, then have no lamp to reverse traverse the maze room. We simply require an additional key just to be super safe then. The backdoor access to the chest is otherwise free # if we have backdoor access we can waste a key on the trinexx door, then have no lamp to reverse traverse the maze room. We simply require an additional key just to be super safe then. The backdoor access to the chest is otherwise free
if not can_reach_back: if not can_reach_back:
set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 1)) set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 1))
else: else:
set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 2)) set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 2))
@ -496,8 +496,8 @@ def set_trock_key_rules(world):
# however in keysanity being able to reach all other chests while only having three keys does not imply this contains # however in keysanity being able to reach all other chests while only having three keys does not imply this contains
# a key, so we again need all four keys unless it contains the big key # a key, so we again need all four keys unless it contains the big key
if can_reach_back: if can_reach_back:
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)']))) set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
elif world.keysanity: elif world.keysanity:
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)']))) set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
else: else:
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 3) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)']))) set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 3) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))

10
Text.py
View File

@ -141,11 +141,11 @@ class Credits(object):
class CreditLine(object): class CreditLine(object):
"""Base class of credit lines""" """Base class of credit lines"""
def __init__(self, text, align='center'): def __init__(self, text, align='center'):
self.text = text self.text = text
self.align = align self.align = align
@property @property
def x(self): def x(self):
x = 0 x = 0
@ -157,7 +157,7 @@ class CreditLine(object):
x = (32 - len(self.text)) // 2 x = (32 - len(self.text)) // 2
return x return x
class SceneCreditLine(CreditLine): class SceneCreditLine(CreditLine):
"""Base class for credit lines for the scene portion of the credits""" """Base class for credit lines for the scene portion of the credits"""
def __init__(self, y, text, align='center'): def __init__(self, y, text, align='center'):
@ -214,7 +214,7 @@ class SceneLargeCreditLine(SceneCreditLine):
buf += LargeCreditBottomMapper.convert(self.text) buf += LargeCreditBottomMapper.convert(self.text)
return buf return buf
def string_to_alttp_text(s, maxbytes=256): def string_to_alttp_text(s, maxbytes=256):
lines = s.upper().split('\n') lines = s.upper().split('\n')
outbuf = bytearray() outbuf = bytearray()
@ -489,7 +489,7 @@ class GreenCreditMapper(TextMapper):
char_map = {' ': 0x9F, char_map = {' ': 0x9F,
'.': 0x52} '.': 0x52}
alpha_offset = -0x29 alpha_offset = -0x29
class RedCreditMapper(TextMapper): class RedCreditMapper(TextMapper):
char_map = {' ': 0x9F} #fixme char_map = {' ': 0x9F} #fixme
alpha_offset= -0x61 alpha_offset= -0x61