diff --git a/playerSettings.yaml b/playerSettings.yaml index 15375cf8..bfec9c15 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -29,7 +29,6 @@ game: # Pick a game to play Minecraft: 0 Subnautica: 0 Slay the Spire: 0 - Terraria: 0 requires: version: 0.1.6 # Version of Archipelago required for this yaml to work as expected. # Shared Options supported by all games: @@ -280,18 +279,6 @@ Minecraft: send_defeated_mobs: # Send killed mobs to other Minecraft worlds which have this option enabled. on: 0 off: 1 -Terraria: - include_hardmode_achievements: # Junk-fills achievements which can only be obtained after defeating the Wall of Flesh. - on: 0 - off: 1 - # Junk-fills extremely difficult advancements; this is primarily for achievements which require completing large parts of the game - # (such as gelatin world tour or Real Estate Agent) as well as the more advanced fishing quests. - include_insane_achievements: - on: 0 - off: 1 - include_postgame_achievements: # Some achievements require defeating the Moon Lord first; this will junk-fill them so you won't have to finish to send some items. - on: 0 - off: 1 A Link to the Past: ### Logic Section ### glitches_required: # Determine the logic required to complete the seed diff --git a/worlds/terraria/Items.py b/worlds/terraria/Items.py deleted file mode 100644 index 6e3e965c..00000000 --- a/worlds/terraria/Items.py +++ /dev/null @@ -1,26 +0,0 @@ -from BaseClasses import Item -import typing - - -class ItemData(typing.NamedTuple): - code: typing.Optional[int] - progression: bool - - -class TerrariaItem(Item): - game: str = "Terraria" - - -item_table = { - "Copper Shortsword": ItemData(73001, False), - - "Victory": ItemData(73000, True) -} - -# If not listed here then has frequency 1 -item_frequencies = { - "Copper Shortsword": 87, - -} - -lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code} diff --git a/worlds/terraria/Locations.py b/worlds/terraria/Locations.py deleted file mode 100644 index 01c3ebce..00000000 --- a/worlds/terraria/Locations.py +++ /dev/null @@ -1,166 +0,0 @@ -from BaseClasses import Location -import typing - - -class AchieveData(typing.NamedTuple): - id: typing.Optional[int] - region: str - - -class TerrariaAchievement(Location): - game: str = "Terraria" - - def __init__(self, player: int, name: str, address: typing.Optional[int], parent): - super().__init__(player, name, address, parent) - self.event = not address - - -achievement_table = { - "Timber!!": AchieveData(0, "Overworld"), - "No Hobo": AchieveData(1, "Overworld"), - "Stop! Hammer Time!": AchieveData(2, "Overworld"), - "Ooo! Shiny!": AchieveData(3, "Overworld"), - "Heart Breaker": AchieveData(4, "Overworld"), - "Heavy Metal": AchieveData(5, "Overworld"), - "I Am Loot!": AchieveData(6, "Overworld"), - "Star Power": AchieveData(7, "Overworld"), - "Hold on Tight!": AchieveData(8, "Overworld"), - "Eye on You": AchieveData(9, "Overworld"), - "Smashing, Poppet!": AchieveData(10, "Overworld"), - "Worm Fodder": AchieveData(11, "Corruption"), - "Mastermind": AchieveData(12, "Crimson"), - "Where's My Honey?": AchieveData(13, "Jungle"), - "Sting Operation": AchieveData(14, "Jungle"), - "Boned": AchieveData(15, "Overworld"), - "Dungeon Heist": AchieveData(16, "Dungeon"), - "It's Getting Hot in Here": AchieveData(17, "Underworld"), - "Miner for Fire": AchieveData(18, "Underworld"), - "Still Hungry": AchieveData(19, "Underworld"), - "It's Hard!": AchieveData(20, "Underworld"), - "Begone, Evil!": AchieveData(21, "Hardmode"), - "Extra Shiny!": AchieveData(22, "Hardmode"), - "Head in the Clouds": AchieveData(23, "Hardmode"), - "Like a Boss": AchieveData(24, "Overworld"), - "Buckets of Bolts": AchieveData(25, "Hardmode"), - "Drax Attax": AchieveData(26, "Hardmode"), - "Photosynthesis": AchieveData(27, "Hardmode Jungle"), - "Get a Life": AchieveData(28, "Hardmode Jungle"), - "The Great Southern Plantkill": AchieveData(29, "Hardmode Jungle"), - "Temple Raider": AchieveData(30, "Post-Plantera"), - "Lihzahrdian Idol": AchieveData(31, "Post-Plantera"), - "Robbing the Grave": AchieveData(32, "Post-Plantera"), - "Big Booty": AchieveData(33, "Post-Plantera"), - "Fish Out of Water": AchieveData(34, "Overworld"), - "Obsessive Devotion": AchieveData(35, "Post-Golem"), - "Star Destroyer": AchieveData(36, "Post-Golem"), - "Champion of Terraria": AchieveData(37, "Post-Golem"), - "Bloodbath": AchieveData(38, "Overworld"), - "Slippery Shinobi": AchieveData(39, "Overworld"), - "Goblin Punter": AchieveData(40, "Overworld"), - "Walk the Plank": AchieveData(41, "Hardmode"), - "Kill the Sun": AchieveData(42, "Hardmode"), - "Do You Want to Slay a Snowman?": AchieveData(43, "Hardmode"), - "Tin-Foil Hatter": AchieveData(44, "Post-Golem"), - "Baleful Harvest": AchieveData(45, "Post-Plantera"), - "Ice Scream": AchieveData(46, "Post-Plantera"), - "Sticky Situation": AchieveData(47, "Overworld"), - "Real Estate Agent": AchieveData(48, "Postgame"), - "Not the Bees!": AchieveData(49, "Jungle"), - "Jeepers Creepers": AchieveData(50, "Overworld"), - "Funkytown": AchieveData(51, "Overworld"), - "Into Orbit": AchieveData(52, "Overworld"), - "Rock Bottom": AchieveData(53, "Underworld"), - "Mecha Mayhem": AchieveData(54, "Hardmode"), - "Gelatin World Tour": AchieveData(55, "Postgame"), - "Fashion Statement": AchieveData(56, "Overworld"), - "Vehicular Manslaughter": AchieveData(57, "Overworld"), - "Bulldozer": AchieveData(58, "Overworld"), - "There are Some Who Call Him...": AchieveData(59, "Overworld"), - "Deceiver of Fools": AchieveData(60, "Overworld"), - "Sword of the Hero": AchieveData(61, "Hardmode"), - "Lucky Break": AchieveData(62, "Overworld"), - "Throwing Lines": AchieveData(63, "Overworld"), - "Dye Hard": AchieveData(64, "Overworld"), - "Sick Throw": AchieveData(65, "Postgame"), - "The Frequent Flyer": AchieveData(66, "Overworld"), - "The Cavalry": AchieveData(67, "Overworld"), - "Completely Awesome": AchieveData(68, "Overworld"), - "Til Death...": AchieveData(69, "Overworld"), - "Archaeologist": AchieveData(70, "Jungle"), - "Pretty in Pink": AchieveData(71, "Overworld"), - "Rainbows and Unicorns": AchieveData(72, "Hardmode"), - "You and What Army?": AchieveData(73, "Hardmode"), - "Prismancer": AchieveData(74, "Hardmode"), - "It Can Talk?!": AchieveData(75, "Hardmode"), - "Watch Your Step!": AchieveData(76, "Overworld"), - "Marathon Medalist": AchieveData(77, "Overworld"), - "Glorious Golden Pole": AchieveData(78, "Overworld"), - "Servant-in-Training": AchieveData(79, "Overworld"), - "Good Little Slave": AchieveData(80, "Overworld"), - "Trout Monkey": AchieveData(81, "Overworld"), - "Fast and Fishious": AchieveData(82, "Overworld"), - "Supreme Helper Minion!": AchieveData(83, "Overworld"), - "Topped Off": AchieveData(84, "Hardmode"), - "Slayer of Worlds": AchieveData(85, "Postgame"), - "You Can Do It!": AchieveData(86, "Overworld"), - "Matching Attire": AchieveData(87, "Overworld"), -} - -exclusion_table = { - "hardmode": { - "It's Hard!", - "Extra Shiny!", - "Head in the Clouds", - "Buckets of Bolts", - "Drax Attax", - "Photosynthesis", - "Get a Life", - "The Great Southern Plantkill", - "Temple Raider", - "Lihzahrdian Idol", - "Robbing the Grave", - "Big Booty", - "Fish Out of Water", - "Obsessive Devotion", - "Star Destroyer", - "Champion of Terraria", - "Walk the Plank", - "Kill the Sun", - "Do You Want to Slay a Snowman?", - "Tin-Foil Hatter", - "Baleful Harvest", - "Ice Scream", - "Real Estate Agent", - "Mecha Mayhem", - "Gelatin World Tour", - "Sword of the Hero", - "Sick Throw", - "Rainbows and Unicorns", - "You and What Army?", - "Prismancer", - "It Can Talk?!", - "Topped Off", - "Slayer of Worlds", - }, - "insane": { - "Gelatin World Tour", - "Fast and Fishious", - "Supreme Helper Minion!", - "Real Estate Agent", - "Mecha Mayhem", - "Bulldozer", - "Marathon Medalist", - "Slayer of Worlds", - }, - "postgame": { - "Slayer of Worlds", - "Sick Throw", - } -} - -events_table = { - "Still Hungry": "Victory" -} - -lookup_id_to_name: typing.Dict[int, str] = {loc_data.id: loc_name for loc_name, loc_data in achievement_table.items() if - loc_data.id} diff --git a/worlds/terraria/Options.py b/worlds/terraria/Options.py deleted file mode 100644 index 967607cd..00000000 --- a/worlds/terraria/Options.py +++ /dev/null @@ -1,8 +0,0 @@ -import typing -from Options import Choice, Option, Toggle, Range - -terraria_options: typing.Dict[str, type(Option)] = { - "include_hardmode_achievements": Toggle, - "include_insane_achievements": Toggle, - "include_postgame_achievements": Toggle, -} \ No newline at end of file diff --git a/worlds/terraria/Regions.py b/worlds/terraria/Regions.py deleted file mode 100644 index 413502dc..00000000 --- a/worlds/terraria/Regions.py +++ /dev/null @@ -1,85 +0,0 @@ - -def link_terraria_structures(world, player): - - # Link mandatory connections first - for (exit, region) in mandatory_connections: - world.get_entrance(exit, player).connect(world.get_region(region, player)) - - # Get all unpaired exits and all regions without entrances (except the Menu) - # This function is destructive on these lists. - exits = [exit.name for r in world.regions if r.player == player for exit in r.exits if exit.connected_region == None] - structs = [r.name for r in world.regions if r.player == player and r.entrances == [] and r.name != 'Menu'] - exits_spoiler = exits[:] # copy the original order for the spoiler log - #try: - # assert len(exits) == len(structs) - #except AssertionError as e: # this should never happen - # raise Exception(f"Could not obtain equal numbers of Minecraft exits and structures for player {player} ({world.player_names[player]})") - - pairs = {} - - def set_pair(exit, struct): - if (exit in exits) and (struct in structs) and (exit not in illegal_connections.get(struct, [])): - pairs[exit] = struct - exits.remove(exit) - structs.remove(struct) - else: - raise Exception(f"Invalid connection: {exit} => {struct} for player {player} ({world.player_names[player]})") - - - for (exit, struct) in default_connections: - if exit in exits: - set_pair(exit, struct) - - # Make sure we actually paired everything; might fail if plando - try: - assert len(exits) == len(structs) == 0 - except AssertionError: - raise Exception(f"Failed to connect all Minecraft structures for player {player} ({world.player_names[player]})") - - for exit in exits_spoiler: - world.get_entrance(exit, player).connect(world.get_region(pairs[exit], player)) - if world.shuffle_structures[player] or world.plando_connections[player]: - world.spoiler.set_entrance(exit, pairs[exit], 'entrance', player) - - - -# (Region name, list of exits) -terraria_regions = [ - ('Menu', ['New World']), - ('Overworld', ['Descend to Underworld', 'Go to Jungle', 'Go to Dungeon', 'Go to Corruption', 'Go to Crimson']), - ('Underworld', ['Kill WoF']), - ('Jungle', []), - ('Corruption', []), - ('Crimson', []), - ('Dungeon', []), - ('Hardmode Jungle', []), - ('Hardmode', ['Kill Plantera', 'Go to Hardmode Jungle']), - ('Post-Plantera', ['Kill Golem']), - ('Post-Golem', ['Kill Moon Lord']), - ('Postgame', []) -] - -# (Entrance, region pointed to) -mandatory_connections = [ - ('New World', 'Overworld'), - ('Descend to Underworld', 'Underworld'), - ('Go to Jungle', 'Jungle'), - ('Go to Corruption', 'Corruption'), - ('Go to Crimson', 'Crimson'), - ('Go to Hardmode Jungle', 'Hardmode Jungle'), - ('Go to Dungeon', 'Dungeon'), - ('Kill WoF', 'Hardmode'), - ('Kill Plantera', 'Post-Plantera'), - ('Kill Golem', 'Post-Golem'), - ('Kill Moon Lord', 'Postgame'), -] - -default_connections = { - -} - -# Structure: illegal locations -illegal_connections = { - -} - diff --git a/worlds/terraria/Rules.py b/worlds/terraria/Rules.py deleted file mode 100644 index 015d4944..00000000 --- a/worlds/terraria/Rules.py +++ /dev/null @@ -1,43 +0,0 @@ -from ..generic.Rules import set_rule -from .Locations import exclusion_table, events_table -from BaseClasses import MultiWorld -from ..AutoWorld import LogicMixin - - -class TerrariaLogic(LogicMixin): - # Defs here - - def temp(self, player: int): - pass - -def set_rules(world: MultiWorld, player: int): - def reachable_locations(state): - postgame_advancements = exclusion_table['postgame'].copy() - for event in events_table.keys(): - postgame_advancements.add(event) - return [location for location in world.get_locations() if - location.player == player and - location.name not in postgame_advancements and - location.can_reach(state)] - - # 88 total achievements. Goal is to defeat Wall of Flesh. - goal = 20#int(world.achievement_goal[player].value) - can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('Hardmode', 'Region', player) - - if world.logic[player] != 'nologic': - world.completion_condition[player] = lambda state: state.has('Victory', player) - - set_rule(world.get_entrance("Kill WoF", player), lambda state: True) - set_rule(world.get_entrance("Kill Plantera", player), lambda state: True) - set_rule(world.get_entrance("Kill Golem", player), lambda state: True) - set_rule(world.get_entrance("Kill Moon Lord", player), lambda state: True) - set_rule(world.get_entrance("Descend to Underworld", player), lambda state: True) - set_rule(world.get_entrance("Go to Corruption", player), lambda state: True) - set_rule(world.get_entrance("Go to Crimson", player), lambda state: True) - set_rule(world.get_entrance("Go to Dungeon", player), lambda state: True) - set_rule(world.get_entrance("Go to Jungle", player), lambda state: True) - set_rule(world.get_entrance("Go to Hardmode Jungle", player), lambda state: True) - - set_rule(world.get_location("Still Hungry", player), lambda state: can_complete(state)) - - \ No newline at end of file diff --git a/worlds/terraria/__init__.py b/worlds/terraria/__init__.py deleted file mode 100644 index 3d94c0c8..00000000 --- a/worlds/terraria/__init__.py +++ /dev/null @@ -1,99 +0,0 @@ -import os - - -from .Items import TerrariaItem, item_table, item_frequencies -from .Locations import TerrariaAchievement, achievement_table, exclusion_table, events_table -from .Regions import terraria_regions, link_terraria_structures, default_connections -from .Rules import set_rules -from worlds.generic.Rules import exclusion_rules - -from BaseClasses import Region, Entrance, Item -from .Options import terraria_options -from ..AutoWorld import World - -client_version = 5 - -class TerrariaWorld(World): - game: str = "Terraria" - options = terraria_options - topology_present = True - - item_name_to_id = {name: data.code for name, data in item_table.items()} - location_name_to_id = {name: data.id for name, data in achievement_table.items()} - - data_version = 2 - - def _get_terraria_data(self): - exits = [connection[0] for connection in default_connections] - return { - 'world_seed': self.world.slot_seeds[self.player].getrandbits(32), - # consistent and doesn't interfere with other generation - 'seed_name': self.world.seed_name, - 'player_name': self.world.get_player_name(self.player), - 'player_id': self.player, - 'client_version': client_version, - 'race': self.world.is_race - } - - def generate_basic(self): - - # Generate item pool - itempool = [] - pool_counts = item_frequencies.copy() - for item_name in item_table: - for count in range(pool_counts.get(item_name, 1)): - itempool.append(self.create_item(item_name)) - - # Choose locations to automatically exclude based on settings - exclusion_pool = set() - exclusion_types = ['hardmode', 'insane', 'postgame'] - for key in exclusion_types: - if not getattr(self.world, f"include_{key}_achievements")[self.player]: - exclusion_pool.update(exclusion_table[key]) - exclusion_rules(self.world, self.player, exclusion_pool) - - # Prefill the Wall of Flesh with the completion condition - completion = self.create_item("Victory") - self.world.get_location("Still Hungry", self.player).place_locked_item(completion) - itempool.remove(completion) - self.world.itempool += itempool - - def set_rules(self): - set_rules(self.world, self.player) - - def create_regions(self): - def TerrariaRegion(region_name: str, exits=[]): - ret = Region(region_name, None, region_name, self.player, self.world) - ret.locations = [TerrariaAchievement(self.player, loc_name, loc_data.id, ret) - for loc_name, loc_data in achievement_table.items() - if loc_data.region == region_name] - for exit in exits: - ret.exits.append(Entrance(self.player, exit, ret)) - return ret - - self.world.regions += [TerrariaRegion(*r) for r in terraria_regions] - link_terraria_structures(self.world, self.player) - - def generate_output(self, output_directory: str): - import json - from base64 import b64encode - - data = self._get_terraria_data() - filename = f"AP_{self.world.seed_name}_P{self.player}_{self.world.get_player_name(self.player)}.apterra" - with open(os.path.join(output_directory, filename), 'wb') as f: - f.write(b64encode(bytes(json.dumps(data), 'utf-8'))) - - def fill_slot_data(self): - slot_data = self._get_terraria_data() - for option_name in terraria_options: - option = getattr(self.world, option_name)[self.player] - slot_data[option_name] = int(option.value) - return slot_data - - def create_item(self, name: str) -> Item: - item_data = item_table[name] - item = TerrariaItem(name, item_data.progression, item_data.code, self.player) - nonexcluded_items = [] - if name in nonexcluded_items: # prevent books from going on excluded locations - item.never_exclude = True - return item