OSRS: Fix UT Integration and Various Gen Failures (#5331)
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import Item, Tutorial, ItemClassification, Region, MultiWorld, CollectionState
|
||||
from Fill import fill_restrictive, FillError
|
||||
from BaseClasses import Item, Tutorial, ItemClassification, Region, MultiWorld
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from Options import OptionError
|
||||
from .Items import OSRSItem, starting_area_dict, chunksanity_starting_chunks, QP_Items, ItemRow, \
|
||||
chunksanity_special_region_names
|
||||
from .Locations import OSRSLocation, LocationRow
|
||||
from .Locations import OSRSLocation, LocationRow, task_types
|
||||
from .Rules import *
|
||||
from .Options import OSRSOptions, StartingArea
|
||||
from .Names import LocationNames, ItemNames, RegionNames
|
||||
@@ -47,6 +47,7 @@ class OSRSWorld(World):
|
||||
base_id = 0x070000
|
||||
data_version = 1
|
||||
explicit_indirect_conditions = False
|
||||
ut_can_gen_without_yaml = True
|
||||
|
||||
item_name_to_id = {item_rows[i].name: 0x070000 + i for i in range(len(item_rows))}
|
||||
location_name_to_id = {location_rows[i].name: 0x070000 + i for i in range(len(location_rows))}
|
||||
@@ -105,6 +106,18 @@ class OSRSWorld(World):
|
||||
|
||||
# Set Starting Chunk
|
||||
self.multiworld.push_precollected(self.create_item(self.starting_area_item))
|
||||
elif hasattr(self.multiworld,"re_gen_passthrough") and self.game in self.multiworld.re_gen_passthrough:
|
||||
re_gen_passthrough = self.multiworld.re_gen_passthrough[self.game] # UT passthrough
|
||||
if "starting_area" in re_gen_passthrough:
|
||||
self.starting_area_item = re_gen_passthrough["starting_area"]
|
||||
for task_type in task_types:
|
||||
if f"max_{task_type}_level" in re_gen_passthrough:
|
||||
getattr(self.options,f"max_{task_type}_level").value = re_gen_passthrough[f"max_{task_type}_level"]
|
||||
max_count = getattr(self.options,f"max_{task_type}_tasks")
|
||||
max_count.value = max_count.range_end
|
||||
self.options.brutal_grinds.value = re_gen_passthrough["brutal_grinds"]
|
||||
|
||||
|
||||
|
||||
"""
|
||||
This function pulls from LogicCSVToPython so that it sends the correct tag of the repository to the client.
|
||||
@@ -115,20 +128,13 @@ class OSRSWorld(World):
|
||||
data = self.options.as_dict("brutal_grinds")
|
||||
data["data_csv_tag"] = data_csv_tag
|
||||
data["starting_area"] = str(self.starting_area_item) #these aren't actually strings, they just play them on tv
|
||||
for task_type in task_types:
|
||||
data[f"max_{task_type}_level"] = getattr(self.options,f"max_{task_type}_level").value
|
||||
return data
|
||||
|
||||
def interpret_slot_data(self, slot_data: typing.Dict[str, typing.Any]) -> None:
|
||||
if "starting_area" in slot_data:
|
||||
self.starting_area_item = slot_data["starting_area"]
|
||||
menu_region = self.multiworld.get_region("Menu",self.player)
|
||||
menu_region.exits.clear() #prevent making extra exits if players just reconnect to a differnet slot
|
||||
if self.starting_area_item in chunksanity_special_region_names:
|
||||
starting_area_region = chunksanity_special_region_names[self.starting_area_item]
|
||||
else:
|
||||
starting_area_region = self.starting_area_item[6:] # len("Area: ")
|
||||
starting_entrance = menu_region.create_exit(f"Start->{starting_area_region}")
|
||||
starting_entrance.access_rule = lambda state: state.has(self.starting_area_item, self.player)
|
||||
starting_entrance.connect(self.region_name_to_data[starting_area_region])
|
||||
@staticmethod
|
||||
def interpret_slot_data(slot_data: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]:
|
||||
return slot_data
|
||||
|
||||
def create_regions(self) -> None:
|
||||
"""
|
||||
@@ -195,6 +201,8 @@ class OSRSWorld(World):
|
||||
generation_is_fake = hasattr(self.multiworld, "generation_is_fake") # UT specific override
|
||||
locations_required = 0
|
||||
for item_row in item_rows:
|
||||
if item_row.name == self.starting_area_item:
|
||||
continue #skip starting area
|
||||
# If it's a filler item, set it aside for later
|
||||
if item_row.progression == ItemClassification.filler:
|
||||
continue
|
||||
@@ -206,15 +214,18 @@ class OSRSWorld(World):
|
||||
locations_required += item_row.amount
|
||||
if self.options.enable_duds: locations_required += self.options.dud_count
|
||||
|
||||
locations_added = 1 # At this point we've already added the starting area, so we start at 1 instead of 0
|
||||
|
||||
locations_added = 0 # Keep track of the number of locations we add so we don't add more the number of items we're going to make
|
||||
# Quests are always added first, before anything else is rolled
|
||||
for i, location_row in enumerate(location_rows):
|
||||
if location_row.category in {"quest", "points", "goal"}:
|
||||
if location_row.category in {"quest"}:
|
||||
if self.task_within_skill_levels(location_row.skills):
|
||||
self.create_and_add_location(i)
|
||||
if location_row.category == "quest":
|
||||
locations_added += 1
|
||||
locations_added += 1
|
||||
elif location_row.category in {"goal"}:
|
||||
if not self.task_within_skill_levels(location_row.skills):
|
||||
raise OptionError(f"Goal location for {self.player_name} not allowed in skill levels") #it doesn't actually have any, but just in case for future
|
||||
self.create_and_add_location(i)
|
||||
|
||||
|
||||
# Build up the weighted Task Pool
|
||||
rnd = self.random
|
||||
@@ -225,18 +236,28 @@ class OSRSWorld(World):
|
||||
rnd.shuffle(general_tasks)
|
||||
else:
|
||||
general_tasks.reverse()
|
||||
for i in range(self.options.minimum_general_tasks):
|
||||
general_tasks_added = 0
|
||||
while general_tasks_added<self.options.minimum_general_tasks and general_tasks:
|
||||
task = general_tasks.pop()
|
||||
self.add_location(task)
|
||||
locations_added += 1
|
||||
if self.task_within_skill_levels(task.skills):
|
||||
self.add_location(task)
|
||||
locations_added += 1
|
||||
general_tasks_added += 1
|
||||
while generation_is_fake and len(general_tasks)>0:
|
||||
task = general_tasks.pop()
|
||||
if self.task_within_skill_levels(task.skills):
|
||||
self.add_location(task)
|
||||
locations_added += 1
|
||||
general_tasks_added += 1
|
||||
if general_tasks_added < self.options.minimum_general_tasks:
|
||||
raise OptionError(f"{self.plyaer_name} doesn't have enough general tasks to create required minimum count"+
|
||||
f", raise maximum skill levels or lower minimum general tasks")
|
||||
|
||||
general_weight = self.options.general_task_weight if len(general_tasks) > 0 else 0
|
||||
general_weight = self.options.general_task_weight.value if len(general_tasks) > 0 else 0
|
||||
|
||||
tasks_per_task_type: typing.Dict[str, typing.List[LocationRow]] = {}
|
||||
weights_per_task_type: typing.Dict[str, int] = {}
|
||||
|
||||
task_types = ["prayer", "magic", "runecraft", "mining", "crafting",
|
||||
"smithing", "fishing", "cooking", "firemaking", "woodcutting", "combat"]
|
||||
|
||||
for task_type in task_types:
|
||||
max_amount_for_task_type = getattr(self.options, f"max_{task_type}_tasks")
|
||||
tasks_for_this_type = [task for task in self.locations_by_category[task_type]
|
||||
@@ -263,10 +284,13 @@ class OSRSWorld(World):
|
||||
all_weights.append(weights_per_task_type[task_type])
|
||||
|
||||
# Even after the initial forced generals, they can still be rolled randomly
|
||||
if general_weight > 0:
|
||||
if general_weight > 0 and len(general_tasks)>0:
|
||||
all_tasks.append(general_tasks)
|
||||
all_weights.append(general_weight)
|
||||
|
||||
if not generation_is_fake and locations_added > locations_required: #due to minimum general tasks we already have more than needed
|
||||
raise OptionError(f"Too many locations created for {self.player_name}, lower the minimum general tasks")
|
||||
|
||||
while locations_added < locations_required or (generation_is_fake and len(all_tasks) > 0):
|
||||
if all_tasks:
|
||||
chosen_task = rnd.choices(all_tasks, all_weights)[0]
|
||||
@@ -282,9 +306,9 @@ class OSRSWorld(World):
|
||||
del all_tasks[index]
|
||||
del all_weights[index]
|
||||
|
||||
else:
|
||||
else: # We can ignore general tasks in UT because they will have been cleared already
|
||||
if len(general_tasks) == 0:
|
||||
raise Exception(f"There are not enough available tasks to fill the remaining pool for OSRS " +
|
||||
raise OptionError(f"There are not enough available tasks to fill the remaining pool for OSRS " +
|
||||
f"Please adjust {self.player_name}'s settings to be less restrictive of tasks.")
|
||||
task = general_tasks.pop()
|
||||
self.add_location(task)
|
||||
@@ -296,7 +320,7 @@ class OSRSWorld(World):
|
||||
self.create_and_add_location(index)
|
||||
|
||||
def create_items(self) -> None:
|
||||
filler_items = []
|
||||
filler_items:list[ItemRow] = []
|
||||
for item_row in item_rows:
|
||||
if item_row.name != self.starting_area_item:
|
||||
# If it's a filler item, set it aside for later
|
||||
@@ -321,7 +345,7 @@ class OSRSWorld(World):
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
if self.options.enable_duds:
|
||||
return self.random.choice([item for item in item_rows if item.progression == ItemClassification.filler])
|
||||
return self.random.choice([item.name for item in item_rows if item.progression == ItemClassification.filler])
|
||||
else:
|
||||
return self.random.choice([ItemNames.Progressive_Weapons, ItemNames.Progressive_Magic,
|
||||
ItemNames.Progressive_Range_Weapon, ItemNames.Progressive_Armor,
|
||||
@@ -388,6 +412,12 @@ class OSRSWorld(World):
|
||||
# Set the access rule for the QP Location
|
||||
add_rule(qp_loc, lambda state, loc=q_loc: (loc.can_reach(state)))
|
||||
|
||||
qp = 0
|
||||
for qp_event in self.available_QP_locations:
|
||||
qp += int(qp_event[0])
|
||||
if qp < self.location_rows_by_name[LocationNames.Q_Dragon_Slayer].qp:
|
||||
raise OptionError(f"{self.player_name} doesn't have enough quests for reach goal, increase maximum skill levels")
|
||||
|
||||
# place "Victory" at "Dragon Slayer" and set collection as win condition
|
||||
self.multiworld.get_location(LocationNames.Q_Dragon_Slayer, self.player) \
|
||||
.place_locked_item(self.create_event("Victory"))
|
||||
|
||||
Reference in New Issue
Block a user