Add Terraria Support
But it works this time. Hopefully. Client still needs to be caught up.
This commit is contained in:
parent
61b875256f
commit
3aacaffe6b
|
@ -29,6 +29,7 @@ game: # Pick a game to play
|
||||||
Minecraft: 0
|
Minecraft: 0
|
||||||
Subnautica: 0
|
Subnautica: 0
|
||||||
Slay the Spire: 0
|
Slay the Spire: 0
|
||||||
|
Terraria: 0
|
||||||
requires:
|
requires:
|
||||||
version: 0.1.6 # Version of Archipelago required for this yaml to work as expected.
|
version: 0.1.6 # Version of Archipelago required for this yaml to work as expected.
|
||||||
# Shared Options supported by all games:
|
# Shared Options supported by all games:
|
||||||
|
@ -279,6 +280,18 @@ Minecraft:
|
||||||
send_defeated_mobs: # Send killed mobs to other Minecraft worlds which have this option enabled.
|
send_defeated_mobs: # Send killed mobs to other Minecraft worlds which have this option enabled.
|
||||||
on: 0
|
on: 0
|
||||||
off: 1
|
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:
|
A Link to the Past:
|
||||||
### Logic Section ###
|
### Logic Section ###
|
||||||
glitches_required: # Determine the logic required to complete the seed
|
glitches_required: # Determine the logic required to complete the seed
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
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}
|
|
@ -0,0 +1,166 @@
|
||||||
|
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}
|
|
@ -0,0 +1,8 @@
|
||||||
|
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,
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
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
|
Loading…
Reference in New Issue