mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
TUNIC: Shuffle Ladders option (#2919)
This commit is contained in:
@@ -7,6 +7,7 @@ from .rules import set_location_rules, set_region_rules, randomize_ability_unloc
|
||||
from .er_rules import set_er_location_rules
|
||||
from .regions import tunic_regions
|
||||
from .er_scripts import create_er_regions
|
||||
from .er_data import portal_mapping
|
||||
from .options import TunicOptions
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
@@ -44,7 +45,6 @@ class TunicWorld(World):
|
||||
game = "TUNIC"
|
||||
web = TunicWeb()
|
||||
|
||||
data_version = 2
|
||||
options: TunicOptions
|
||||
options_dataclass = TunicOptions
|
||||
item_name_groups = item_name_groups
|
||||
@@ -72,6 +72,7 @@ class TunicWorld(World):
|
||||
self.options.maskless.value = passthrough["maskless"]
|
||||
self.options.hexagon_quest.value = passthrough["hexagon_quest"]
|
||||
self.options.entrance_rando.value = passthrough["entrance_rando"]
|
||||
self.options.shuffle_ladders.value = passthrough["shuffle_ladders"]
|
||||
|
||||
def create_item(self, name: str) -> TunicItem:
|
||||
item_data = item_table[name]
|
||||
@@ -119,27 +120,46 @@ class TunicWorld(World):
|
||||
items_to_create[rgb_hexagon] = 0
|
||||
items_to_create[gold_hexagon] -= 3
|
||||
|
||||
# Filler items in the item pool
|
||||
available_filler: List[str] = [filler for filler in items_to_create if items_to_create[filler] > 0 and
|
||||
item_table[filler].classification == ItemClassification.filler]
|
||||
|
||||
# Remove filler to make room for other items
|
||||
def remove_filler(amount: int):
|
||||
for _ in range(0, amount):
|
||||
if not available_filler:
|
||||
fill = "Fool Trap"
|
||||
else:
|
||||
fill = self.random.choice(available_filler)
|
||||
if items_to_create[fill] == 0:
|
||||
raise Exception("No filler items left to accommodate options selected. Turn down fool trap amount.")
|
||||
items_to_create[fill] -= 1
|
||||
if items_to_create[fill] == 0:
|
||||
available_filler.remove(fill)
|
||||
|
||||
if self.options.shuffle_ladders:
|
||||
ladder_count = 0
|
||||
for item_name, item_data in item_table.items():
|
||||
if item_data.item_group == "ladders":
|
||||
items_to_create[item_name] = 1
|
||||
ladder_count += 1
|
||||
remove_filler(ladder_count)
|
||||
|
||||
if 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)):
|
||||
items_to_create[self.get_filler_item_name()] += items_to_create[replaced_item]
|
||||
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:
|
||||
available_filler.append(filler_name)
|
||||
items_to_create[replaced_item] = 0
|
||||
|
||||
# Filler items that are still in the item pool to swap out
|
||||
available_filler: List[str] = [filler for filler in items_to_create if items_to_create[filler] > 0 and
|
||||
item_table[filler].classification == ItemClassification.filler]
|
||||
|
||||
# Remove filler to make room for extra hexagons
|
||||
for i in range(0, items_to_create[gold_hexagon]):
|
||||
fill = self.random.choice(available_filler)
|
||||
items_to_create[fill] -= 1
|
||||
if items_to_create[fill] == 0:
|
||||
available_filler.remove(fill)
|
||||
remove_filler(items_to_create[gold_hexagon])
|
||||
|
||||
if self.options.maskless:
|
||||
mask_item = TunicItem("Scavenger Mask", ItemClassification.useful, self.item_name_to_id["Scavenger Mask"], self.player)
|
||||
@@ -147,8 +167,8 @@ class TunicWorld(World):
|
||||
items_to_create["Scavenger Mask"] = 0
|
||||
|
||||
if self.options.lanternless:
|
||||
mask_item = TunicItem("Lantern", ItemClassification.useful, self.item_name_to_id["Lantern"], self.player)
|
||||
tunic_items.append(mask_item)
|
||||
lantern_item = TunicItem("Lantern", ItemClassification.useful, self.item_name_to_id["Lantern"], self.player)
|
||||
tunic_items.append(lantern_item)
|
||||
items_to_create["Lantern"] = 0
|
||||
|
||||
for item, quantity in items_to_create.items():
|
||||
@@ -172,15 +192,16 @@ class TunicWorld(World):
|
||||
self.ability_unlocks["Pages 24-25 (Prayer)"] = passthrough["Hexagon Quest Prayer"]
|
||||
self.ability_unlocks["Pages 42-43 (Holy Cross)"] = passthrough["Hexagon Quest Holy Cross"]
|
||||
self.ability_unlocks["Pages 52-53 (Icebolt)"] = passthrough["Hexagon Quest Icebolt"]
|
||||
|
||||
if self.options.entrance_rando:
|
||||
portal_pairs, portal_hints = create_er_regions(self)
|
||||
for portal1, portal2 in portal_pairs.items():
|
||||
self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
|
||||
|
||||
self.er_portal_hints = portal_hints
|
||||
|
||||
# ladder rando uses ER with vanilla connections, so that we're not managing more rules files
|
||||
if self.options.entrance_rando or self.options.shuffle_ladders:
|
||||
portal_pairs = create_er_regions(self)
|
||||
if self.options.entrance_rando:
|
||||
# these get interpreted by the game to tell it which entrances to connect
|
||||
for portal1, portal2 in portal_pairs.items():
|
||||
self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
|
||||
else:
|
||||
# for non-ER, non-ladders
|
||||
for region_name in tunic_regions:
|
||||
region = Region(region_name, self.player, self.multiworld)
|
||||
self.multiworld.regions.append(region)
|
||||
@@ -201,7 +222,7 @@ class TunicWorld(World):
|
||||
victory_region.locations.append(victory_location)
|
||||
|
||||
def set_rules(self) -> None:
|
||||
if self.options.entrance_rando:
|
||||
if self.options.entrance_rando or self.options.shuffle_ladders:
|
||||
set_er_location_rules(self, self.ability_unlocks)
|
||||
else:
|
||||
set_region_rules(self, self.ability_unlocks)
|
||||
@@ -212,7 +233,31 @@ class TunicWorld(World):
|
||||
|
||||
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
|
||||
if self.options.entrance_rando:
|
||||
hint_data[self.player] = self.er_portal_hints
|
||||
hint_data.update({self.player: {}})
|
||||
# all state seems to have efficient paths
|
||||
all_state = self.multiworld.get_all_state(True)
|
||||
all_state.update_reachable_regions(self.player)
|
||||
paths = all_state.path
|
||||
portal_names = [portal.name for portal in portal_mapping]
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
# skipping event locations
|
||||
if not location.address:
|
||||
continue
|
||||
path_to_loc = []
|
||||
previous_name = "placeholder"
|
||||
name, connection = paths[location.parent_region]
|
||||
while connection != ("Menu", None):
|
||||
name, connection = connection
|
||||
# for LS entrances, we just want to give the portal name
|
||||
if "(LS)" in name:
|
||||
name, _ = name.split(" (LS) ")
|
||||
# was getting some cases like Library Grave -> Library Grave -> other place
|
||||
if name in portal_names and name != previous_name:
|
||||
previous_name = name
|
||||
path_to_loc.append(name)
|
||||
hint_text = " -> ".join(reversed(path_to_loc))
|
||||
if hint_text:
|
||||
hint_data[self.player][location.address] = hint_text
|
||||
|
||||
def fill_slot_data(self) -> Dict[str, Any]:
|
||||
slot_data: Dict[str, Any] = {
|
||||
@@ -226,7 +271,8 @@ class TunicWorld(World):
|
||||
"logic_rules": self.options.logic_rules.value,
|
||||
"lanternless": self.options.lanternless.value,
|
||||
"maskless": self.options.maskless.value,
|
||||
"entrance_rando": bool(self.options.entrance_rando.value),
|
||||
"entrance_rando": int(bool(self.options.entrance_rando.value)),
|
||||
"shuffle_ladders": self.options.shuffle_ladders.value,
|
||||
"Hexagon Quest Prayer": self.ability_unlocks["Pages 24-25 (Prayer)"],
|
||||
"Hexagon Quest Holy Cross": self.ability_unlocks["Pages 42-43 (Holy Cross)"],
|
||||
"Hexagon Quest Icebolt": self.ability_unlocks["Pages 52-53 (Icebolt)"],
|
||||
|
||||
Reference in New Issue
Block a user