Archipelago/worlds/raft/__init__.py

245 lines
13 KiB
Python

import typing
from .Locations import location_table, lookup_name_to_id as locations_lookup_name_to_id
from .Items import (createResourcePackName, item_table, progressive_table, progressive_item_list,
lookup_name_to_item, resourcepack_items as resourcePackItems, lookup_name_to_id as items_lookup_name_to_id)
from .Regions import create_regions, getConnectionName
from .Rules import set_rules
from .Options import RaftOptions
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, ItemClassification, Tutorial
from ..AutoWorld import World, WebWorld
class RaftWeb(WebWorld):
theme = "ocean"
tutorials = [Tutorial(
"Multiworld Setup Guide",
"A guide to setting up Raft integration for Archipelago multiworld games.",
"English",
"setup_en.md",
"setup/en",
["SunnyBat", "Awareqwx"]
)]
class RaftWorld(World):
"""
Raft is a flooded world exploration game. You're stranded on a small raft in the middle of the
ocean, and you must survive on trash floating by you on the top of the water and around/on any
islands that you come across.
"""
game: str = "Raft"
web = RaftWeb()
item_name_to_id = items_lookup_name_to_id.copy()
lastItemId = max(filter(lambda val: val is not None, item_name_to_id.values()))
location_name_to_id = locations_lookup_name_to_id
options_dataclass = RaftOptions
options: RaftOptions
required_client_version = (0, 3, 4)
def create_items(self):
minRPSpecified = self.options.minimum_resource_pack_amount.value
maxRPSpecified = self.options.maximum_resource_pack_amount.value
minimumResourcePackAmount = min(minRPSpecified, maxRPSpecified)
maximumResourcePackAmount = max(minRPSpecified, maxRPSpecified)
isFillingFrequencies = self.options.island_frequency_locations.is_filling_frequencies_in_world()
# Generate item pool
pool = []
frequencyItems = []
for item in item_table:
raft_item = self.create_item_replaceAsNecessary(item["name"])
if isFillingFrequencies and "Frequency" in item["name"]:
frequencyItems.append(raft_item)
else:
pool.append(raft_item)
if isFillingFrequencies:
if not hasattr(self.multiworld, "raft_frequencyItemsPerPlayer"):
self.multiworld.raft_frequencyItemsPerPlayer = {}
self.multiworld.raft_frequencyItemsPerPlayer[self.player] = frequencyItems
extraItemNamePool = []
extras = len(location_table) - len(item_table) - 1 # Victory takes up 1 unaccounted-for slot
if extras > 0:
if (self.options.filler_item_types != self.options.filler_item_types.option_duplicates): # Use resource packs
for packItem in resourcePackItems:
for i in range(minimumResourcePackAmount, maximumResourcePackAmount + 1):
extraItemNamePool.append(createResourcePackName(i, packItem))
if self.options.filler_item_types != self.options.filler_item_types.option_resource_packs: # Use duplicate items
dupeItemPool = item_table.copy()
# Remove frequencies if necessary
if self.options.island_frequency_locations != self.options.island_frequency_locations.option_anywhere: # Not completely random locations
# If we let frequencies stay in with progressive-frequencies, the progressive-frequency item
# will be included 7 times. This is a massive flood of progressive-frequency items, so we
# instead add progressive-frequency as its own item a smaller amount of times to prevent
# flooding the duplicate item pool with them.
if self.options.island_frequency_locations == self.options.island_frequency_locations.option_progressive:
for _ in range(2):
# Progressives are not in item_pool, need to create faux item for duplicate item pool
# This can still be filtered out later by duplicate_items setting
dupeItemPool.append({ "name": "progressive-frequency", "progression": True }) # Progressive frequencies need to be included
# Always remove non-progressive Frequency items
dupeItemPool = (itm for itm in dupeItemPool if "Frequency" not in itm["name"])
# Remove progression or non-progression items if necessary
if (self.options.duplicate_items == self.options.duplicate_items.option_progression): # Progression only
dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == True)
elif (self.options.duplicate_items == self.options.duplicate_items.option_non_progression): # Non-progression only
dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == False)
dupeItemPool = list(dupeItemPool)
# Finally, add items as necessary
if len(dupeItemPool) > 0:
for item in dupeItemPool:
extraItemNamePool.append(item["name"])
if (len(extraItemNamePool) > 0):
for randomItem in self.random.choices(extraItemNamePool, k=extras):
raft_item = self.create_item_replaceAsNecessary(randomItem)
pool.append(raft_item)
self.multiworld.itempool += pool
# Victory item
self.multiworld.get_location("Utopia Complete", self.player).place_locked_item(
RaftItem("Victory", ItemClassification.progression, None, player=self.player))
def set_rules(self):
set_rules(self.multiworld, self.player)
def create_regions(self):
create_regions(self.multiworld, self.player)
def get_pre_fill_items(self):
if self.options.island_frequency_locations.is_filling_frequencies_in_world():
return [loc.item for loc in self.multiworld.get_filled_locations()]
return []
def create_item_replaceAsNecessary(self, name: str) -> Item:
isFrequency = "Frequency" in name
shouldUseProgressive = bool((isFrequency and self.options.island_frequency_locations == self.options.island_frequency_locations.option_progressive)
or (not isFrequency and self.options.progressive_items))
if shouldUseProgressive and name in progressive_table:
name = progressive_table[name]
return self.create_item(name)
def create_item(self, name: str) -> Item:
item = lookup_name_to_item[name]
return RaftItem(name, ItemClassification.progression if item["progression"] else ItemClassification.filler,
self.item_name_to_id[name], player=self.player)
def create_resourcePack(self, rpName: str) -> Item:
return RaftItem(rpName, ItemClassification.filler, self.item_name_to_id[rpName], player=self.player)
def collect_item(self, state, item, remove=False):
if item.advancement is False:
return None
if item.name in progressive_item_list:
prog_table = progressive_item_list[item.name]
if remove:
for item_name in reversed(prog_table):
if state.has(item_name, item.player):
return item_name
else:
for item_name in prog_table:
if not state.has(item_name, item.player):
return item_name
return super(RaftWorld, self).collect_item(state, item, remove)
def pre_fill(self):
if self.options.island_frequency_locations == self.options.island_frequency_locations.option_vanilla:
self.setLocationItem("Radio Tower Frequency to Vasagatan", "Vasagatan Frequency")
self.setLocationItem("Vasagatan Frequency to Balboa", "Balboa Island Frequency")
self.setLocationItem("Relay Station quest", "Caravan Island Frequency")
self.setLocationItem("Caravan Island Frequency to Tangaroa", "Tangaroa Frequency")
self.setLocationItem("Tangaroa Frequency to Varuna Point", "Varuna Point Frequency")
self.setLocationItem("Varuna Point Frequency to Temperance", "Temperance Frequency")
self.setLocationItem("Temperance Frequency to Utopia", "Utopia Frequency")
elif self.options.island_frequency_locations == self.options.island_frequency_locations.option_random_on_island:
self.setLocationItemFromRegion("RadioTower", "Vasagatan Frequency")
self.setLocationItemFromRegion("Vasagatan", "Balboa Island Frequency")
self.setLocationItemFromRegion("BalboaIsland", "Caravan Island Frequency")
self.setLocationItemFromRegion("CaravanIsland", "Tangaroa Frequency")
self.setLocationItemFromRegion("Tangaroa", "Varuna Point Frequency")
self.setLocationItemFromRegion("Varuna Point", "Temperance Frequency")
self.setLocationItemFromRegion("Temperance", "Utopia Frequency")
elif self.options.island_frequency_locations in [
self.options.island_frequency_locations.option_random_island_order,
self.options.island_frequency_locations.option_random_on_island_random_order
]:
locationToFrequencyItemMap = {
"Vasagatan": "Vasagatan Frequency",
"BalboaIsland": "Balboa Island Frequency",
"CaravanIsland": "Caravan Island Frequency",
"Tangaroa": "Tangaroa Frequency",
"Varuna Point": "Varuna Point Frequency",
"Temperance": "Temperance Frequency",
"Utopia": "Utopia Frequency"
}
locationToVanillaFrequencyLocationMap = {
"RadioTower": "Radio Tower Frequency to Vasagatan",
"Vasagatan": "Vasagatan Frequency to Balboa",
"BalboaIsland": "Relay Station quest",
"CaravanIsland": "Caravan Island Frequency to Tangaroa",
"Tangaroa": "Tangaroa Frequency to Varuna Point",
"Varuna Point": "Varuna Point Frequency to Temperance",
"Temperance": "Temperance Frequency to Utopia"
}
# Utopia is never chosen until the end, otherwise these are chosen randomly
availableLocationList = ["Vasagatan", "BalboaIsland", "CaravanIsland", "Tangaroa", "Varuna Point", "Temperance", "Utopia"]
previousLocation = "RadioTower"
while (len(availableLocationList) > 0):
if (len(availableLocationList) > 1):
currentLocation = availableLocationList[self.random.randint(0, len(availableLocationList) - 2)]
else:
currentLocation = availableLocationList[0] # Utopia (only one left in list)
availableLocationList.remove(currentLocation)
if self.options.island_frequency_locations == self.options.island_frequency_locations.option_random_island_order:
self.setLocationItem(locationToVanillaFrequencyLocationMap[previousLocation], locationToFrequencyItemMap[currentLocation])
elif self.options.island_frequency_locations == self.options.island_frequency_locations.option_random_on_island_random_order:
self.setLocationItemFromRegion(previousLocation, locationToFrequencyItemMap[currentLocation])
previousLocation = currentLocation
def setLocationItem(self, location: str, itemName: str):
itemToUse = next(filter(lambda itm: itm.name == itemName, self.multiworld.raft_frequencyItemsPerPlayer[self.player]))
self.multiworld.raft_frequencyItemsPerPlayer[self.player].remove(itemToUse)
self.multiworld.get_location(location, self.player).place_locked_item(itemToUse)
def setLocationItemFromRegion(self, region: str, itemName: str):
itemToUse = next(filter(lambda itm: itm.name == itemName, self.multiworld.raft_frequencyItemsPerPlayer[self.player]))
self.multiworld.raft_frequencyItemsPerPlayer[self.player].remove(itemToUse)
location = self.random.choice(list(loc for loc in location_table if loc["region"] == region))
self.multiworld.get_location(location["name"], self.player).place_locked_item(itemToUse)
def fill_slot_data(self):
return {
"IslandGenerationDistance": self.options.island_generation_distance.value,
"ExpensiveResearch": bool(self.options.expensive_research),
"DeathLink": bool(self.options.death_link)
}
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
ret = Region(name, player, world)
if locations:
for location in locations:
loc_id = locations_lookup_name_to_id.get(location, 0)
locationObj = RaftLocation(player, location, loc_id, ret)
ret.locations.append(locationObj)
if exits:
for exit in exits:
ret.exits.append(Entrance(player, getConnectionName(name, exit), ret))
return ret
class RaftLocation(Location):
game = "Raft"
class RaftItem(Item):
game = "Raft"