TUNIC: Expanded hexagon quest options (#4076)

* More hex quest updates

- Implement page ability shuffle for hex quest
- Fix keys behind bosses if hex goal is less than 3
- Added check to fix conflicting hex quest options
- Add option to slot data

* Change option comparison

* Change option checking and fix some stuff

- also keep prayer first on low hex counts

* Update option defaulting

* Update option checking

* Fix option assignment again

* Show player name in option warning

* Add new option to universal tracker stuff

* Update __init__.py

* Make helper method for getting total hexagons in itempool

* Update options.py

* Update option value passthrough

* Change ability shuffle to default on

* Check for hexagons option when writing spoiler
This commit is contained in:
Silent
2025-03-07 19:43:02 -05:00
committed by GitHub
parent 2f0b81e12c
commit bc61221ec6
3 changed files with 103 additions and 25 deletions

View File

@@ -11,7 +11,8 @@ from .er_scripts import create_er_regions
from .grass import grass_location_table, grass_location_name_to_id, grass_location_name_groups, excluded_grass_locations
from .er_data import portal_mapping, RegionInfo, tunic_er_regions
from .options import (TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections,
LaurelsLocation, LogicRules, LaurelsZips, IceGrappling, LadderStorage)
LaurelsLocation, LogicRules, LaurelsZips, IceGrappling, LadderStorage, check_options,
get_hexagons_in_pool, HexagonQuestAbilityUnlockType)
from .combat_logic import area_data, CombatState
from worlds.AutoWorld import WebWorld, World
from Options import PlandoConnection, OptionError
@@ -109,6 +110,8 @@ class TunicWorld(World):
ut_can_gen_without_yaml = True # class var that tells it to ignore the player yaml
def generate_early(self) -> None:
check_options(self)
if self.options.logic_rules >= LogicRules.option_no_major_glitches:
self.options.laurels_zips.value = LaurelsZips.option_true
self.options.ice_grappling.value = IceGrappling.option_medium
@@ -144,6 +147,7 @@ class TunicWorld(World):
self.options.lanternless.value = self.passthrough["lanternless"]
self.options.maskless.value = self.passthrough["maskless"]
self.options.hexagon_quest.value = self.passthrough["hexagon_quest"]
self.options.hexagon_quest_ability_type.value = self.passthrough.get("hexagon_quest_ability_type", 0)
self.options.entrance_rando.value = self.passthrough["entrance_rando"]
self.options.shuffle_ladders.value = self.passthrough["shuffle_ladders"]
self.options.grass_randomizer.value = self.passthrough.get("grass_randomizer", 0)
@@ -261,6 +265,10 @@ class TunicWorld(World):
items_to_create: Dict[str, int] = {item: data.quantity_in_item_pool for item, data in item_table.items()}
# Calculate number of hexagons in item pool
if self.options.hexagon_quest:
items_to_create[gold_hexagon] = get_hexagons_in_pool(self)
for money_fool in fool_tiers[self.options.fool_traps]:
items_to_create["Fool Trap"] += items_to_create[money_fool]
items_to_create[money_fool] = 0
@@ -291,11 +299,21 @@ class TunicWorld(World):
items_to_create["Grass"] -= len(excluded_grass_locations)
if self.options.keys_behind_bosses:
for rgb_hexagon, location in hexagon_locations.items():
hex_item = self.create_item(gold_hexagon if self.options.hexagon_quest else rgb_hexagon)
self.get_location(location).place_locked_item(hex_item)
items_to_create[rgb_hexagon] = 0
items_to_create[gold_hexagon] -= 3
rgb_hexagons = list(hexagon_locations.keys())
# shuffle these in case not all are placed in hex quest
self.random.shuffle(rgb_hexagons)
for rgb_hexagon in rgb_hexagons:
location = hexagon_locations[rgb_hexagon]
if self.options.hexagon_quest:
if items_to_create[gold_hexagon] > 0:
hex_item = self.create_item(gold_hexagon)
items_to_create[gold_hexagon] -= 1
items_to_create[rgb_hexagon] = 0
self.get_location(location).place_locked_item(hex_item)
else:
hex_item = self.create_item(rgb_hexagon)
self.get_location(location).place_locked_item(hex_item)
items_to_create[rgb_hexagon] = 0
# Filler items in the item pool
available_filler: List[str] = [filler for filler in items_to_create if items_to_create[filler] > 0 and
@@ -323,13 +341,11 @@ class TunicWorld(World):
remove_filler(ladder_count)
if self.options.hexagon_quest:
# Calculate number of hexagons in item pool
hexagon_goal = self.options.hexagon_goal
extra_hexagons = self.options.extra_hexagon_percentage
items_to_create[gold_hexagon] += int((Decimal(100 + extra_hexagons) / 100 * hexagon_goal).to_integral_value(rounding=ROUND_HALF_UP))
# Replace pages and normal hexagons with filler
for replaced_item in list(filter(lambda item: "Pages" in item or item in hexagon_locations, items_to_create)):
if replaced_item in item_name_groups["Abilities"] and self.options.ability_shuffling \
and self.options.hexagon_quest_ability_type == "pages":
continue
filler_name = self.get_filler_item_name()
items_to_create[filler_name] += items_to_create[replaced_item]
if items_to_create[filler_name] >= 1 and filler_name not in available_filler:
@@ -441,7 +457,7 @@ class TunicWorld(World):
def create_regions(self) -> None:
self.tunic_portal_pairs = {}
self.er_portal_hints = {}
self.ability_unlocks = randomize_ability_unlocks(self.random, self.options)
self.ability_unlocks = randomize_ability_unlocks(self)
# stuff for universal tracker support, can be ignored for standard gen
if self.using_ut:
@@ -504,7 +520,8 @@ class TunicWorld(World):
return change
def write_spoiler_header(self, spoiler_handle: TextIO):
if self.options.hexagon_quest and self.options.ability_shuffling:
if self.options.hexagon_quest and self.options.ability_shuffling\
and self.options.hexagon_quest_ability_type == HexagonQuestAbilityUnlockType.option_hexagons:
spoiler_handle.write("\nAbility Unlocks (Hexagon Quest):\n")
for ability in self.ability_unlocks:
# Remove parentheses for better readability
@@ -567,6 +584,7 @@ class TunicWorld(World):
"sword_progression": self.options.sword_progression.value,
"ability_shuffling": self.options.ability_shuffling.value,
"hexagon_quest": self.options.hexagon_quest.value,
"hexagon_quest_ability_type": self.options.hexagon_quest_ability_type.value,
"fool_traps": self.options.fool_traps.value,
"laurels_zips": self.options.laurels_zips.value,
"ice_grappling": self.options.ice_grappling.value,