Various Item pool fixes
* Pedestal goal always left a spare item in the pool, unless vanilla swords was also selected * extra items for the pool can now be given dynamically based on items still needed. * easy item pool + swordless gets 4 bows, not 2 (weird combo, but ok) * add some item pool unittests * add easy item pool to CLI and GUI
This commit is contained in:
parent
8759ab83bc
commit
01ace95c32
|
@ -91,9 +91,10 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
type=lambda value: min(max(int(value), 1), 90),
|
type=lambda value: min(max(int(value), 1), 90),
|
||||||
help='''Set Triforce Pieces required to win a Triforce Hunt''')
|
help='''Set Triforce Pieces required to win a Triforce Hunt''')
|
||||||
parser.add_argument('--difficulty', default=defval('normal'), const='normal', nargs='?',
|
parser.add_argument('--difficulty', default=defval('normal'), const='normal', nargs='?',
|
||||||
choices=['normal', 'hard', 'expert'],
|
choices=['easy', 'normal', 'hard', 'expert'],
|
||||||
help='''\
|
help='''\
|
||||||
Select game difficulty. Affects available itempool. (default: %(default)s)
|
Select game difficulty. Affects available itempool. (default: %(default)s)
|
||||||
|
Easy: An easier setting with some equipment duplicated and increased health.
|
||||||
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.
|
||||||
|
|
2
Gui.py
2
Gui.py
|
@ -272,7 +272,7 @@ def guiMain(args=None):
|
||||||
difficultyFrame = Frame(drowDownFrame)
|
difficultyFrame = Frame(drowDownFrame)
|
||||||
difficultyVar = StringVar()
|
difficultyVar = StringVar()
|
||||||
difficultyVar.set('normal')
|
difficultyVar.set('normal')
|
||||||
difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'normal', 'hard', 'expert')
|
difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'easy', 'normal', 'hard', 'expert')
|
||||||
difficultyOptionMenu.pack(side=RIGHT)
|
difficultyOptionMenu.pack(side=RIGHT)
|
||||||
difficultyLabel = Label(difficultyFrame, text='Difficulty: item pool')
|
difficultyLabel = Label(difficultyFrame, text='Difficulty: item pool')
|
||||||
difficultyLabel.pack(side=LEFT)
|
difficultyLabel.pack(side=LEFT)
|
||||||
|
|
56
ItemList.py
56
ItemList.py
|
@ -23,12 +23,12 @@ normalbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bott
|
||||||
hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)',
|
hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)',
|
||||||
'Bottle (Good Bee)']
|
'Bottle (Good Bee)']
|
||||||
|
|
||||||
easybaseitems = (['Sanctuary Heart Container', "Lamp"] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 +
|
easybaseitems = (['Sanctuary Heart Container', "Lamp"] + ['Rupees (300)'] * 5 + ['Magic Upgrade (1/2)'] * 2 +
|
||||||
['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12)
|
['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24)
|
||||||
easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)']
|
easyfirst15extra = ['Piece of Heart'] * 12 + ['Rupees (300)'] * 3
|
||||||
easyfirst15extra = ['Rupees (100)'] + ['Arrows (10)'] * 7 + ['Bombs (3)'] * 7
|
easysecond15extra = ['Rupees (100)'] + ['Arrows (10)'] * 7 + ['Bombs (3)'] * 7
|
||||||
easysecond10extra = ['Bombs (3)'] * 7 + ['Rupee (1)', 'Rupees (50)', 'Bombs (10)']
|
easythird10extra = ['Bombs (3)'] * 7 + ['Rupee (1)', 'Rupees (50)', 'Bombs (10)']
|
||||||
easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)']
|
easyfourth5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)']
|
||||||
easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2
|
easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2
|
||||||
|
|
||||||
normalbaseitems = (['Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrows (10)', 'Bombs (10)'] +
|
normalbaseitems = (['Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrows (10)', 'Bombs (10)'] +
|
||||||
|
@ -66,18 +66,17 @@ difficulties = {
|
||||||
progressivebow=["Progressive Bow"] * 2,
|
progressivebow=["Progressive Bow"] * 2,
|
||||||
basicbow=['Bow', 'Silver Bow'] * 2,
|
basicbow=['Bow', 'Silver Bow'] * 2,
|
||||||
timedohko=['Green Clock'] * 25,
|
timedohko=['Green Clock'] * 25,
|
||||||
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5,
|
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||||
# +5 more Red Clocks if there is room
|
|
||||||
triforcehunt=['Triforce Piece'] * 30,
|
triforcehunt=['Triforce Piece'] * 30,
|
||||||
retro=['Small Key (Universal)'] * 27,
|
retro=['Small Key (Universal)'] * 28,
|
||||||
extras=[easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra],
|
extras=[easyfirst15extra, easysecond15extra, easythird10extra, easyfourth5extra, easyfinal25extra],
|
||||||
progressive_sword_limit=8,
|
progressive_sword_limit=8,
|
||||||
progressive_shield_limit=6,
|
progressive_shield_limit=6,
|
||||||
progressive_armor_limit=2,
|
progressive_armor_limit=4,
|
||||||
progressive_bow_limit=4,
|
progressive_bow_limit=4,
|
||||||
progressive_bottle_limit=8,
|
progressive_bottle_limit=8,
|
||||||
boss_heart_container_limit=10,
|
boss_heart_container_limit=10,
|
||||||
heart_piece_limit=24,
|
heart_piece_limit=36,
|
||||||
),
|
),
|
||||||
'normal': Difficulty(
|
'normal': Difficulty(
|
||||||
baseitems=normalbaseitems,
|
baseitems=normalbaseitems,
|
||||||
|
@ -157,13 +156,14 @@ difficulties = {
|
||||||
progressive_shield_limit=1,
|
progressive_shield_limit=1,
|
||||||
progressive_armor_limit=0,
|
progressive_armor_limit=0,
|
||||||
progressive_bow_limit=1,
|
progressive_bow_limit=1,
|
||||||
progressive_bottle_limit = 4,
|
progressive_bottle_limit=4,
|
||||||
boss_heart_container_limit = 2,
|
boss_heart_container_limit=2,
|
||||||
heart_piece_limit = 8,
|
heart_piece_limit=8,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_itempool(world, player):
|
|
||||||
|
def generate_itempool(world, player: int):
|
||||||
if world.difficulty[player] not in difficulties:
|
if world.difficulty[player] not in difficulties:
|
||||||
raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
|
raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
|
||||||
if world.goal[player] not in {'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'localtriforcehunt',
|
if world.goal[player] not in {'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'localtriforcehunt',
|
||||||
|
@ -465,14 +465,14 @@ def get_pool_core(world, player: int):
|
||||||
if logic in {'owglitches', 'nologic'} and world.glitch_boots[player]:
|
if logic in {'owglitches', 'nologic'} and world.glitch_boots[player]:
|
||||||
precollected_items.append('Pegasus Boots')
|
precollected_items.append('Pegasus Boots')
|
||||||
pool.remove('Pegasus Boots')
|
pool.remove('Pegasus Boots')
|
||||||
pool.extend(['Rupees (20)'])
|
pool.append('Rupees (20)')
|
||||||
|
|
||||||
if want_progressives():
|
if want_progressives():
|
||||||
pool.extend(progressivegloves)
|
pool.extend(progressivegloves)
|
||||||
else:
|
else:
|
||||||
pool.extend(basicgloves)
|
pool.extend(basicgloves)
|
||||||
|
|
||||||
# insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start
|
# insanity legacy shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start
|
||||||
if shuffle == 'insanity_legacy':
|
if shuffle == 'insanity_legacy':
|
||||||
place_item('Link\'s House', 'Magic Mirror')
|
place_item('Link\'s House', 'Magic Mirror')
|
||||||
place_item('Sanctuary', 'Moon Pearl')
|
place_item('Sanctuary', 'Moon Pearl')
|
||||||
|
@ -511,7 +511,10 @@ def get_pool_core(world, player: int):
|
||||||
elif swords != 'swordless':
|
elif swords != 'swordless':
|
||||||
pool.extend(diff.basicbow)
|
pool.extend(diff.basicbow)
|
||||||
else:
|
else:
|
||||||
pool.extend(['Bow', 'Silver Bow'])
|
swordless_bows = ['Bow', 'Silver Bow']
|
||||||
|
if difficulty == "easy":
|
||||||
|
swordless_bows *= 2
|
||||||
|
pool.extend(swordless_bows)
|
||||||
|
|
||||||
if swords == 'swordless':
|
if swords == 'swordless':
|
||||||
pool.extend(diff.swordless)
|
pool.extend(diff.swordless)
|
||||||
|
@ -557,17 +560,22 @@ def get_pool_core(world, player: int):
|
||||||
treasure_hunt_icon = 'Triforce Piece'
|
treasure_hunt_icon = 'Triforce Piece'
|
||||||
|
|
||||||
for extra in diff.extras:
|
for extra in diff.extras:
|
||||||
if extraitems > 0:
|
if extraitems >= len(extra):
|
||||||
pool.extend(extra)
|
pool.extend(extra)
|
||||||
extraitems -= len(extra)
|
extraitems -= len(extra)
|
||||||
|
elif extraitems > 0:
|
||||||
|
pool.extend(world.random.sample(extra, extraitems))
|
||||||
|
break
|
||||||
|
|
||||||
if goal == 'pedestal' and swords != 'vanilla':
|
if goal == 'pedestal' and swords != 'vanilla':
|
||||||
place_item('Master Sword Pedestal', 'Triforce')
|
place_item('Master Sword Pedestal', 'Triforce')
|
||||||
|
pool.remove("Rupees (20)")
|
||||||
|
|
||||||
if retro:
|
if retro:
|
||||||
pool = [item.replace('Single Arrow','Rupees (5)') for item in pool]
|
pool = [item.replace('Single Arrow', 'Rupees (5)') for item in pool]
|
||||||
pool = [item.replace('Arrows (10)','Rupees (5)') for item in pool]
|
pool = [item.replace('Arrows (10)', 'Rupees (5)') for item in pool]
|
||||||
pool = [item.replace('Arrow Upgrade (+5)','Rupees (5)') for item in pool]
|
pool = [item.replace('Arrow Upgrade (+5)', 'Rupees (5)') for item in pool]
|
||||||
pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool]
|
pool = [item.replace('Arrow Upgrade (+10)', 'Rupees (5)') for item in pool]
|
||||||
pool.extend(diff.retro)
|
pool.extend(diff.retro)
|
||||||
if mode == 'standard':
|
if mode == 'standard':
|
||||||
key_location = world.random.choice(
|
key_location = world.random.choice(
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
from ItemList import difficulties
|
||||||
|
from test.TestBase import TestBase
|
||||||
|
|
||||||
|
base_items = 43
|
||||||
|
extra_counts = (15, 15, 10, 5, 25)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDifficulty(TestBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def build_difficulty_test(difficulty):
|
||||||
|
# binds difficulty to definition local scope
|
||||||
|
def build_for(function):
|
||||||
|
def wrapped(self, *args):
|
||||||
|
return function(self, difficulty, *args)
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
return build_for
|
||||||
|
|
||||||
|
|
||||||
|
def build_dynamic_tests():
|
||||||
|
for name, difficulty in difficulties.items():
|
||||||
|
|
||||||
|
@build_difficulty_test(difficulty)
|
||||||
|
def test_dyn_difficulty(self, difficulty):
|
||||||
|
base = len(difficulty.baseitems)
|
||||||
|
self.assertEqual(base, base_items)
|
||||||
|
|
||||||
|
setattr(TestDifficulty, f"testCountBase{name}", test_dyn_difficulty)
|
||||||
|
|
||||||
|
@build_difficulty_test(difficulty)
|
||||||
|
def test_dyn_difficulty(self, difficulty):
|
||||||
|
self.assertEqual(len(extra_counts), len(difficulty.extras))
|
||||||
|
|
||||||
|
setattr(TestDifficulty, f"testCountExtra{name}", test_dyn_difficulty)
|
||||||
|
|
||||||
|
@build_difficulty_test(difficulty)
|
||||||
|
def test_dyn_difficulty(self, difficulty):
|
||||||
|
for i, extras in enumerate(extra_counts):
|
||||||
|
self.assertEqual(extras, len(difficulty.extras[i]))
|
||||||
|
|
||||||
|
setattr(TestDifficulty, f"testCountExtras{name}", test_dyn_difficulty)
|
||||||
|
|
||||||
|
|
||||||
|
build_dynamic_tests()
|
Loading…
Reference in New Issue