241 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import typing
 | |
| import random
 | |
| 
 | |
| 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 raft_options
 | |
| 
 | |
| 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
 | |
|     option_definitions = raft_options
 | |
| 
 | |
|     data_version = 2
 | |
|     required_client_version = (0, 3, 4)
 | |
| 
 | |
|     def create_items(self):
 | |
|         minRPSpecified = self.multiworld.minimum_resource_pack_amount[self.player].value
 | |
|         maxRPSpecified = self.multiworld.maximum_resource_pack_amount[self.player].value
 | |
|         minimumResourcePackAmount = min(minRPSpecified, maxRPSpecified)
 | |
|         maximumResourcePackAmount = max(minRPSpecified, maxRPSpecified)
 | |
|         isFillingFrequencies = self.multiworld.island_frequency_locations[self.player].value <= 3
 | |
|         # 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.multiworld.filler_item_types[self.player].value != 1): # Use resource packs
 | |
|                 for packItem in resourcePackItems:
 | |
|                     for i in range(minimumResourcePackAmount, maximumResourcePackAmount + 1):
 | |
|                         extraItemNamePool.append(createResourcePackName(i, packItem))
 | |
| 
 | |
|             if self.multiworld.filler_item_types[self.player].value != 0: # Use duplicate items
 | |
|                 dupeItemPool = item_table.copy()
 | |
|                 # Remove frequencies if necessary
 | |
|                 if self.multiworld.island_frequency_locations[self.player].value != 5: # 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.multiworld.island_frequency_locations[self.player].value == 4:
 | |
|                         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.multiworld.duplicate_items[self.player].value == 0): # Progression only
 | |
|                     dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == True)
 | |
|                 elif (self.multiworld.duplicate_items[self.player].value == 1): # 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 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.multiworld.island_frequency_locations[self.player] in [0, 1, 2, 3]:
 | |
|             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 = ((isFrequency and self.multiworld.island_frequency_locations[self.player].value == 4)
 | |
|             or (not isFrequency and self.multiworld.progressive_items[self.player].value))
 | |
|         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.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.multiworld.island_frequency_locations[self.player] == 0: # 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.multiworld.island_frequency_locations[self.player] == 1: # 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.multiworld.island_frequency_locations[self.player] in [2, 3]:
 | |
|             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[random.randint(0, len(availableLocationList) - 2)]
 | |
|                 else:
 | |
|                     currentLocation = availableLocationList[0] # Utopia (only one left in list)
 | |
|                 availableLocationList.remove(currentLocation)
 | |
|                 if self.multiworld.island_frequency_locations[self.player] == 2: # Random island order
 | |
|                     self.setLocationItem(locationToVanillaFrequencyLocationMap[previousLocation], locationToFrequencyItemMap[currentLocation])
 | |
|                 elif self.multiworld.island_frequency_locations[self.player] == 3: # 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 = 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.multiworld.island_generation_distance[self.player].value,
 | |
|             "ExpensiveResearch": bool(self.multiworld.expensive_research[self.player].value),
 | |
|             "DeathLink": bool(self.multiworld.death_link[self.player].value)
 | |
|         }
 | |
| 
 | |
| 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"
 | 
