Refactor bottle randomization

Bottle randomization refactored to be done on generation of itempool, not with the ItemFactory function. This allows difficulty settings to use different bottle pools more easily as well as allows the pedestal, credits, and spoiler logs to report bottle contents. Expert and insane difficulties deliberately set the same bottle content to all four bottles since the bottle limit is one in those modes so this only guarantees that all racers regardless of play sequence will get the same one free bottle content.
This commit is contained in:
AmazingAmpharos 2017-11-11 20:22:44 -06:00 committed by GitHub
parent cde90cbc84
commit 10d96bfa6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 17 deletions

View File

@ -336,6 +336,9 @@ class CollectionState(object):
def can_lift_rocks(self):
return self.has('Power Glove') or self.has('Titans Mitts')
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')
def can_lift_heavy_rocks(self):
return self.has('Titans Mitts')

View File

@ -10,7 +10,10 @@ alwaysitems = ['Bombos', 'Book of Mudora', 'Bow', 'Cane of Somaria', 'Ether', 'F
progressivegloves = ['Progressive Glove'] * 2
basicgloves = ['Power Glove', 'Titans Mitts']
normalbaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows', 'Magic Upgrade (1/2)'] + ['Bottle'] * 4 + ['Rupees (300)'] * 4 +
normalbottles = ['Bottle', 'BottleRedPotion', 'BottleGreenPotion', 'BottleBluePotion', 'BottleFairy', 'BottleBee', 'BottleGoodBee']
hardbottles = ['Bottle', 'BottleRedPotion', 'BottleGreenPotion', 'BottleBluePotion', 'BottleBee', 'BottleGoodBee']
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)
normalfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6
normalsecond15extra = ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)']
@ -28,7 +31,7 @@ normalbasicshield = ['Blue Shield', 'Red Shield', 'Mirror Shield']
normalprogressivearmor = ['Progressive Armor'] * 2
normalbasicarmor = ['Blue Mail', 'Red Mail']
easybaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows'] + ['Bottle'] * 8 + ['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)
easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)']
easylimitedextra = ['Boss Heart Container'] * 3
@ -48,7 +51,7 @@ easybasicshield = ['Blue Shield', 'Blue Shield', 'Red Shield', 'Red Shield', 'Mi
easyprogressivearmor = ['Progressive Armor'] * 4
easybasicarmor = ['Blue Mail', 'Blue Mail', 'Red Mail', 'Red Mail']
hardbaseitems = (['Silver Arrows', 'Single Arrow'] + ['Bottle'] * 4 + ['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)
hardfirst20extra = ['Bombs (3)'] * 4 + ['Single Bomb'] * 4 + ['Rupees (5)'] * 5 + ['Rupee (1)'] * 2 + ['Rupees (100)'] + ['Rupees (50)'] * 4
hardsecond20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 10 + ['Rupees (20)'] * 2 + ['Rupee (1)'] * 3 + ['Arrows (10)']
@ -65,7 +68,7 @@ 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 +
['Bottle'] * 4 + ['Rupees (20)'] + ['Single Bomb'] * 2 + ['Piece of Heart'] * 24)
['Rupees (20)'] + ['Single Bomb'] * 2 + ['Piece of Heart'] * 24)
expertfirst15extra = ['Single Bomb'] * 13 + ['Rupees (20)'] * 2
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
@ -78,7 +81,7 @@ 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 +
['Bottle'] * 4 + ['Rupee (1)'] * 4 + ['Single Bomb'] * 4)
['Rupee (1)'] * 4 + ['Single Bomb'] * 4)
insanefirst15extra = ['Single Bomb'] * 4 + ['Single Arrow'] * 4 + ['Rupee (1)'] * 4 + ['Rupees (300)'] + ['Rupees (100)'] + ['Rupees (50)']
insanesecond25extra = ['Single Bomb'] * 7 + ['Single Arrow'] * 7 + ['Rupee (1)'] * 7 + ['Rupees (20)'] * 4
insanethird10extra = ['Single Bomb'] * 3 + ['Single Arrow'] * 3 + ['Rupee (1)'] * 3 + ['Rupees (20)']
@ -130,6 +133,9 @@ def generate_itempool(world):
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))
@ -214,6 +220,9 @@ def generate_itempool(world):
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))
@ -303,6 +312,9 @@ def generate_itempool(world):
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))
@ -379,6 +391,9 @@ def generate_itempool(world):
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))
@ -444,6 +459,9 @@ def generate_itempool(world):
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))

View File

@ -1,4 +1,4 @@
from BaseClasses import Item
from BaseClasses import World, Item
import random
import logging
@ -12,12 +12,6 @@ def ItemFactory(items):
for item in items:
if item in item_table:
advancement, priority, type, code, pedestal_hint, pedestal_credit, sickkid_credit, zora_credit, witch_credit, fluteboy_credit = item_table[item]
if item == 'Bottle':
# randomly fill bottle
if world.difficulty in ['hard', 'expert', insane']:
code = [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)]
else:
code = [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)]
ret.append(Item(item, advancement, priority, type, code, pedestal_hint, pedestal_credit, sickkid_credit, zora_credit, witch_credit, fluteboy_credit))
else:
logging.getLogger('').warning('Unknown Item: %s' % item)
@ -52,7 +46,13 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
'Ether': (True, False, None, 0x10, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-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'),
'Bottle': (True, False, None, 0xFF, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'boy stores things again'), # specific content written on creation
'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'),
'BottleGreenPotion': (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'),
'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'),
'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'),
'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'),
'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'),
'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

@ -100,7 +100,7 @@ def global_rules(world):
set_rule(world.get_entrance('Waterfall of Wishing'), lambda state: state.has('Flippers')) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. Can be improved in future Todo
set_rule(world.get_location('Blacksmith'), lambda state: state.can_lift_heavy_rocks() and state.can_reach('West Dark World')) # Can S&Q with smith
set_rule(world.get_location('Magic Bat'), lambda state: state.has('Magic Powder'))
set_rule(world.get_location('Sick Kid'), lambda state: state.has('Bottle'))
set_rule(world.get_location('Sick Kid'), lambda state: state.has_bottle())
set_rule(world.get_location('Library'), lambda state: state.has_Boots())
set_rule(world.get_location('Potion Shop'), lambda state: state.has('Mushroom'))
set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks'), lambda state: state.can_lift_rocks())
@ -177,7 +177,7 @@ def global_rules(world):
set_rule(world.get_entrance('Superbunny Cave (Bottom)'), lambda state: state.has_Pearl())
set_rule(world.get_entrance('Cave Shop (Dark Death Mountain)'), lambda state: state.has_Pearl()) # just for save bunny algo for now
set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)'), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling
set_rule(world.get_location('Spike Cave'), lambda state: state.has('Hammer') and state.can_lift_rocks() and (state.has('Cane of Byrna') or state.has('Cape')) and (state.has('Bottle') or state.has('Half Magic') or state.has('Quarter Magic')))
set_rule(world.get_location('Spike Cave'), lambda state: state.has('Hammer') and state.can_lift_rocks() and (state.has('Cane of Byrna') or state.has('Cape')) and (state.has_bottle() or state.has('Half Magic') or state.has('Quarter Magic')))
set_rule(world.get_location('Hookshot Cave - Top Right'), lambda state: state.has('Hookshot'))
set_rule(world.get_location('Hookshot Cave - Top Left'), lambda state: state.has('Hookshot'))
set_rule(world.get_location('Hookshot Cave - Bottom Right'), lambda state: state.has('Hookshot') or state.has('Pegasus Boots'))
@ -291,7 +291,7 @@ def global_rules(world):
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield'))
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield'))
set_rule(world.get_entrance('Turtle Rock (Trinexx)'), lambda state: state.has('Small Key (Turtle Rock)', 4) and state.has('Big Key (Turtle Rock)') and state.has('Cane of Somaria') and state.has('Fire Rod') and state.has('Ice Rod') and
(state.has('Hammer') or state.has_beam_sword() or state.has('Bottle') or state.has('Half Magic') or state.has('Quarter Magic')))
(state.has('Hammer') or state.has_beam_sword() or state.has_bottle() or state.has('Half Magic') or state.has('Quarter Magic')))
set_trock_key_rules(world)
set_rule(world.get_entrance('Palace of Darkness Bonk Wall'), lambda state: state.has('Bow'))
@ -364,7 +364,7 @@ def global_rules(world):
set_rule(world.get_location('Ganon'), lambda state: state.has_beam_sword() and state.has_fire_source() and state.has('Crystal 1') and state.has('Crystal 2')
and state.has('Crystal 3') and state.has('Crystal 4') and state.has('Crystal 5') and state.has('Crystal 6') and state.has('Crystal 7')
and (state.has('Tempered Sword') or state.has('Golden Sword') or (state.has('Silver Arrows') and state.has('Bow')) or state.has('Lamp') or state.has('Bottle') or state.has('Half Magic') or state.has('Quarter Magic'))) # need to light torch a sufficient amount of times
and (state.has('Tempered Sword') or state.has('Golden Sword') or (state.has('Silver Arrows') and state.has('Bow')) or state.has('Lamp') or state.has_bottle() or state.has('Half Magic') or state.has('Quarter Magic'))) # need to light torch a sufficient amount of times
set_rule(world.get_entrance('Ganon Drop'), lambda state: state.has_beam_sword()) # need to damage ganon to get tiles to drop