Terraria: Implement New Game (#1405)

Co-authored-by: Zach Parks <zach@alliware.com>
Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
This commit is contained in:
Seldom 2023-07-18 19:37:26 -07:00 committed by GitHub
parent f318ca8886
commit 83387da6a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1934 additions and 0 deletions

View File

@ -50,6 +50,7 @@ Currently, the following games are supported:
* Mega Man Battle Network 3: Blue Version
* Muse Dash
* DOOM 1993
* Terraria
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled

736
worlds/terraria/Checks.py Normal file
View File

@ -0,0 +1,736 @@
from BaseClasses import Item, Location
from typing import Tuple, Union, Set, List, Dict
import string
import pkgutil
class TerrariaItem(Item):
game = "Terraria"
class TerrariaLocation(Location):
game = "Terraria"
def add_token(
tokens: List[Tuple[int, int, Union[str, int, None]]],
token: Union[str, int],
token_index: int,
):
if token == "":
return
if type(token) == str:
tokens.append((token_index, 0, token.rstrip()))
elif type(token) == int:
tokens.append((token_index, 1, token))
IDENT = 0
NUM = 1
SEMI = 2
HASH = 3
AT = 4
NOT = 5
AND = 6
OR = 7
LPAREN = 8
RPAREN = 9
END_OF_LINE = 10
CHAR_TO_TOKEN_ID = {
";": SEMI,
"#": HASH,
"@": AT,
"~": NOT,
"&": AND,
"|": OR,
"(": LPAREN,
")": RPAREN,
}
TOKEN_ID_TO_CHAR = {id: char for char, id in CHAR_TO_TOKEN_ID.items()}
def tokens(rule: str) -> List[Tuple[int, int, Union[str, int, None]]]:
token_list = []
token = ""
token_index = 0
escaped = False
for index, char in enumerate(rule):
if escaped:
if token == "":
token_index = index
token += char
escaped = False
elif char == "\\":
if type(token) == int:
add_token(token_list, token, token_index)
token = ""
escaped = True
elif char == "/" and token.endswith("/"):
add_token(token_list, token[:-1], token_index)
return token_list
elif token == "" and char.isspace():
pass
elif token == "" and char.isdigit():
token_index = index
token = int(char)
elif type(token) == int and char.isdigit():
token = token * 10 + int(char)
elif (id := CHAR_TO_TOKEN_ID.get(char)) != None:
add_token(token_list, token, token_index)
token_list.append((index, id, None))
token = ""
else:
if token == "":
token_index = index
token += char
add_token(token_list, token, token_index)
return token_list
NAME = 0
NAME_SEMI = 1
FLAG_OR_SEMI = 2
POST_FLAG = 3
FLAG = 4
FLAG_ARG = 5
FLAG_ARG_END = 6
COND_OR_SEMI = 7
POST_COND = 8
COND = 9
LOC = 10
FN = 11
POST_FN = 12
FN_ARG = 13
FN_ARG_END = 14
END = 15
GOAL = 16
POS_FMT = [
"name or `#`",
"`;`",
"flag or `;`",
"`;`, `|`, or `(`",
"flag",
"text or number",
"`)`",
"name, `#`, `@`, `~`, `(`, or `;`",
"`;`, `&`, `|`, or `)`",
"name, `#`, `@`, `~`, or `(`",
"name",
"name",
"`(`, `;`, `&`, `|`, or `)`",
"text or number",
"`)`",
"end of line",
"goal",
]
RWD_NAME = 0
RWD_NAME_SEMI = 1
RWD_FLAG = 2
RWD_FLAG_SEMI = 3
RWD_END = 4
RWD_LABEL = 5
RWD_POS_FMT = ["name or `#`", "`;`", "flag", "`;`", "end of line", "name"]
def unexpected(line: int, char: int, id: int, token, pos, pos_fmt, file):
if id == IDENT or id == NUM:
token_fmt = f"`{token}`"
elif id == END_OF_LINE:
token_fmt = "end of line"
else:
token_fmt = f"`{TOKEN_ID_TO_CHAR[id]}`"
raise Exception(
f"in `{file}`, found {token_fmt} at {line + 1}:{char + 1}; expected {pos_fmt[pos]}"
)
COND_ITEM = 0
COND_LOC = 1
COND_FN = 2
COND_GROUP = 3
def validate_conditions(
rule: str,
rule_indices: dict,
conditions: List[
Tuple[
bool, int, Union[str, Tuple[Union[bool, None], list]], Union[str, int, None]
]
],
):
for _, type, condition, _ in conditions:
if type == COND_ITEM:
if condition not in rule_indices:
raise Exception(f"item `{condition}` in `{rule}` is not defined")
elif type == COND_LOC:
if condition not in rule_indices:
raise Exception(f"location `{condition}` in `{rule}` is not defined")
elif type == COND_FN:
if condition not in {
"npc",
"calamity",
"pickaxe",
"hammer",
"mech_boss",
"minions",
}:
raise Exception(f"function `{condition}` in `{rule}` is not defined")
elif type == COND_GROUP:
_, conditions = condition
validate_conditions(rule, rule_indices, conditions)
def mark_progression(
conditions: List[
Tuple[
bool, int, Union[str, tuple[Union[bool, None], list]], Union[str, int, None]
]
],
progression: Set[str],
rules: list,
rule_indices: dict,
loc_to_item: dict,
):
for _, type, condition, _ in conditions:
if type == COND_ITEM:
prog = condition in progression
progression.add(loc_to_item[condition])
_, flags, _, conditions = rules[rule_indices[condition]]
if (
not prog
and "Achievement" not in flags
and "Location" not in flags
and "Item" not in flags
):
mark_progression(
conditions, progression, rules, rule_indices, loc_to_item
)
elif type == COND_LOC:
_, _, _, conditions = rules[rule_indices[condition]]
mark_progression(conditions, progression, rules, rule_indices, loc_to_item)
elif type == COND_GROUP:
_, conditions = condition
mark_progression(conditions, progression, rules, rule_indices, loc_to_item)
def read_data() -> (
Tuple[
# Goal to rule index that ends that goal's range and the locations required
List[Tuple[int, Set[str]]],
# Rules
List[
Tuple[
# Rule
str,
# Flag to flag arg
Dict[str, Union[str, int, None]],
# True = or, False = and, None = N/A
Union[bool, None],
# Conditions
List[
Tuple[
# True = positive, False = negative
bool,
# Condition type
int,
# Condition name or list (True = or, False = and, None = N/A) (list shares type with outer)
Union[str, Tuple[Union[bool, None], List]],
# Condition arg
Union[str, int, None],
]
],
]
],
# Rule to rule index
Dict[str, int],
# Label to rewards
Dict[str, List[str]],
# Reward to flags
Dict[str, Set[str]],
# Item name to ID
Dict[str, int],
# Location name to ID
Dict[str, int],
# NPCs
List[str],
# Pickaxe to pick power
Dict[str, int],
# Hammer to hammer power
Dict[str, int],
# Mechanical bosses
List[str],
# Calamity final bosses
List[str],
# Progression rules
Set[str],
# Armor to minion count,
Dict[str, int],
# Accessory to minion count,
Dict[str, int],
]
):
next_id = 0x7E0000
item_name_to_id = {}
goals = []
goal_indices = {}
rules = []
rule_indices = {}
loc_to_item = {}
npcs = []
pickaxes = {}
hammers = {}
mech_boss_loc = []
mech_bosses = []
final_boss_loc = []
final_bosses = []
armor_minions = {}
accessory_minions = {}
progression = set()
for line, rule in enumerate(
pkgutil.get_data(__name__, "Rules.dsv").decode().splitlines()
):
goal = None
name = None
flags = {}
sign = True
operator = None
outer = []
conditions = []
pos = NAME
for char, id, token in tokens(rule):
if pos == NAME:
if id == IDENT:
name = token
pos = NAME_SEMI
elif id == HASH:
pos = GOAL
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == NAME_SEMI:
if id == SEMI:
pos = FLAG_OR_SEMI
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == FLAG_OR_SEMI:
if id == IDENT:
flag = token
pos = POST_FLAG
elif id == SEMI:
pos = COND_OR_SEMI
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == POST_FLAG:
if id == SEMI:
if flag is not None:
if flag in flags:
raise Exception(
f"set flag `{flag}` at {line + 1}:{char + 1} that was already set"
)
flags[flag] = None
flag = None
pos = COND_OR_SEMI
elif id == OR:
pos = FLAG
elif id == LPAREN:
pos = FLAG_ARG
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == FLAG:
if id == IDENT:
if flag is not None:
if flag in flags:
raise Exception(
f"set flag `{flag}` at {line + 1}:{char + 1} that was already set"
)
flags[flag] = None
flag = None
flag = token
pos = POST_FLAG
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == FLAG_ARG:
if id == IDENT or id == NUM:
if flag in flags:
raise Exception(
f"set flag `{flag}` at {line + 1}:{char + 1} that was already set"
)
flags[flag] = token
flag = None
pos = FLAG_ARG_END
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == FLAG_ARG_END:
if id == RPAREN:
pos = POST_FLAG
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == COND_OR_SEMI:
if id == IDENT:
conditions.append((sign, COND_ITEM, token, None))
sign = True
pos = POST_COND
elif id == HASH:
pos = LOC
elif id == AT:
pos = FN
elif id == NOT:
sign = not sign
pos = COND
elif id == LPAREN:
outer.append((sign, None, conditions))
conditions = []
sign = True
pos = COND
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == POST_COND:
if id == SEMI:
if outer:
raise Exception(
f"found `;` at {line + 1}:{char + 1} after unclosed `(`"
)
pos = END
elif id == AND:
if operator is True:
raise Exception(
f"found `&` at {line + 1}:{char + 1} in group containing `|`"
)
operator = False
pos = COND
elif id == OR:
if operator is False:
raise Exception(
f"found `|` at {line + 1}:{char + 1} in group containing `&`"
)
operator = True
pos = COND
elif id == RPAREN:
if not outer:
raise Exception(
f"found `)` at {line + 1}:{char + 1} without matching `(`"
)
condition = operator, conditions
sign, operator, conditions = outer.pop()
conditions.append((sign, COND_GROUP, condition, None))
sign = True
pos = POST_COND
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == COND:
if id == IDENT:
conditions.append((sign, COND_ITEM, token, None))
sign = True
pos = POST_COND
elif id == HASH:
pos = LOC
elif id == AT:
pos = FN
elif id == NOT:
sign = not sign
elif id == LPAREN:
outer.append((sign, operator, conditions))
conditions = []
sign = True
operator = None
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == LOC:
if id == IDENT:
conditions.append((sign, COND_LOC, token, None))
sign = True
pos = POST_COND
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == FN:
if id == IDENT:
function = token
pos = POST_FN
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == POST_FN:
if id == LPAREN:
pos = FN_ARG
elif id == SEMI:
conditions.append((sign, COND_FN, function, None))
pos = END
elif id == AND:
conditions.append((sign, COND_FN, function, None))
sign = True
if operator is True:
raise Exception(
f"found `&` at {line + 1}:{char + 1} in group containing `|`"
)
operator = False
pos = COND
elif id == OR:
conditions.append((sign, COND_FN, function, None))
sign = True
if operator is False:
raise Exception(
f"found `|` at {line + 1}:{char + 1} in group containing `&`"
)
operator = True
pos = COND
elif id == RPAREN:
conditions.append((sign, COND_FN, function, None))
if not outer:
raise Exception(
f"found `)` at {line + 1}:{char + 1} without matching `(`"
)
condition = operator, conditions
sign, operator, conditions = outer.pop()
conditions.append((sign, COND_GROUP, condition, None))
sign = True
pos = POST_COND
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == FN_ARG:
if id == IDENT or id == NUM:
conditions.append((sign, COND_FN, function, token))
sign = True
pos = FN_ARG_END
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == FN_ARG_END:
if id == RPAREN:
pos = POST_COND
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
elif pos == END:
unexpected(line, char, id, token, pos)
elif pos == GOAL:
if id == IDENT:
goal = token
pos = END
else:
unexpected(line, char, id, token, pos, POS_FMT, "Rules.dsv")
if pos != NAME and pos != FLAG_OR_SEMI and pos != COND_OR_SEMI and pos != END:
unexpected(line, char + 1, END_OF_LINE, None, pos, POS_FMT, "Rules.dsv")
if name:
if name in rule_indices:
raise Exception(
f"rule `{name}` on line `{line + 1}` shadows a previous rule"
)
rule_indices[name] = len(rules)
rules.append((name, flags, operator, conditions))
if "Item" in flags:
item_name = flags["Item"] or f"Post-{name}"
if item_name in item_name_to_id:
raise Exception(
f"item `{item_name}` on line `{line + 1}` shadows a previous item"
)
item_name_to_id[item_name] = next_id
next_id += 1
loc_to_item[name] = item_name
else:
loc_to_item[name] = name
if "Npc" in flags:
npcs.append(name)
if (power := flags.get("Pickaxe")) is not None:
pickaxes[name] = power
if (power := flags.get("Hammer")) is not None:
hammers[name] = power
if "Mech Boss" in flags:
mech_bosses.append(flags["Item"] or f"Post-{name}")
mech_boss_loc.append(name)
if "Final Boss" in flags:
final_bosses.append(flags["Item"] or f"Post-{name}")
final_boss_loc.append(name)
if (minions := flags.get("ArmorMinions")) is not None:
armor_minions[name] = minions
if (minions := flags.get("Minions")) is not None:
accessory_minions[name] = minions
if goal:
if goal in goal_indices:
raise Exception(
f"goal `{goal}` on line `{line + 1}` shadows a previous goal"
)
goal_indices[goal] = len(goals)
goals.append((len(rules), set()))
for name, flags, _, _ in rules:
if "Goal" in flags:
_, items = goals[
goal_indices[
name.translate(str.maketrans("", "", string.punctuation))
.replace(" ", "_")
.lower()
]
]
items.add(name)
_, mech_boss_items = goals[goal_indices["mechanical_bosses"]]
mech_boss_items.update(mech_boss_loc)
_, final_boss_items = goals[goal_indices["calamity_final_bosses"]]
final_boss_items.update(final_boss_loc)
for name, _, _, conditions in rules:
validate_conditions(name, rule_indices, conditions)
for name, flags, _, conditions in rules:
prog = False
if (
"Npc" in flags
or "Goal" in flags
or "Pickaxe" in flags
or "Hammer" in flags
or "Mech Boss" in flags
or "Minions" in flags
or "ArmorMinions" in flags
):
progression.add(loc_to_item[name])
prog = True
if prog or "Location" in flags or "Achievement" in flags:
mark_progression(conditions, progression, rules, rule_indices, loc_to_item)
# Will be randomized via `slot_randoms` / `self.multiworld.random`
label = None
labels = {}
rewards = {}
for line in pkgutil.get_data(__name__, "Rewards.dsv").decode().splitlines():
reward = None
flags = set()
pos = RWD_NAME
for char, id, token in tokens(line):
if pos == RWD_NAME:
if id == IDENT:
reward = f"Reward: {token}"
pos = RWD_NAME_SEMI
elif id == HASH:
pos = RWD_LABEL
else:
unexpected(line, char, id, token, pos, RWD_POS_FMT, "Rewards.dsv")
elif pos == RWD_NAME_SEMI:
if id == SEMI:
pos = RWD_FLAG
else:
unexpected(line, char, id, token, pos, RWD_POS_FMT, "Rewards.dsv")
elif pos == RWD_FLAG:
if id == IDENT:
if token in flags:
raise Exception(
f"set flag `{token}` at {line + 1}:{char + 1} that was already set"
)
flags.add(token)
pos = RWD_FLAG_SEMI
else:
unexpected(line, char, id, token, pos, RWD_POS_FMT, "Rewards.dsv")
elif pos == RWD_FLAG_SEMI:
if id == SEMI:
pos = RWD_END
else:
unexpected(line, char, id, token, pos, RWD_POS_FMT, "Rewards.dsv")
elif pos == RWD_END:
unexpected(line, char, id, token, pos, RWD_POS_FMT, "Rewards.dsv")
elif pos == RWD_LABEL:
if id == IDENT:
label = token
if label in labels:
raise Exception(
f"started label `{label}` at {line + 1}:{char + 1} that was already used"
)
labels[label] = []
pos = RWD_END
else:
unexpected(line, char, id, token, pos, RWD_POS_FMT, "Rewards.dsv")
if pos != RWD_NAME and pos != RWD_FLAG and pos != RWD_END:
unexpected(line, char + 1, END_OF_LINE, None, pos)
if reward:
if reward in rewards:
raise Exception(
f"reward `{reward}` on line `{line + 1}` shadows a previous reward"
)
rewards[reward] = flags
if not label:
raise Exception(
f"reward `{reward}` on line `{line + 1}` is not labeled"
)
labels[label].append(reward)
if reward in item_name_to_id:
raise Exception(
f"item `{reward}` on line `{line + 1}` shadows a previous item"
)
item_name_to_id[reward] = next_id
next_id += 1
item_name_to_id["Reward: Coins"] = next_id
item_name_to_id["Victory"] = next_id + 1
next_id += 2
location_name_to_id = {}
for name, flags, _, _ in rules:
if "Location" in flags or "Achievement" in flags:
if name in location_name_to_id:
raise Exception(f"location `{name}` shadows a previous location")
location_name_to_id[name] = next_id
next_id += 1
return (
goals,
rules,
rule_indices,
labels,
rewards,
item_name_to_id,
location_name_to_id,
npcs,
pickaxes,
hammers,
mech_bosses,
final_bosses,
progression,
armor_minions,
accessory_minions,
)
(
goals,
rules,
rule_indices,
labels,
rewards,
item_name_to_id,
location_name_to_id,
npcs,
pickaxes,
hammers,
mech_bosses,
final_bosses,
progression,
armor_minions,
accessory_minions,
) = read_data()

View File

@ -0,0 +1,57 @@
from Options import Choice, Option, Toggle, DeathLink
import typing
class Goal(Choice):
"""The victory condition for your run. Stuff after the goal will not be shuffled."""
display_name = "Goal"
option_mechanical_bosses = 0
# option_calamitas_clone = 1
option_plantera = 2
option_golem = 3
option_empress_of_light = 4
option_lunatic_cultist = 5
# option_astrum_deus = 6
option_moon_lord = 7
# option_providence_the_profaned_goddess = 8
# option_devourer_of_gods = 9
# option_yharon_dragon_of_rebirth = 10
option_zenith = 11
# option_calamity_final_bosses = 12
# option_adult_eidolon_wyrm = 13
default = 0
class Achievements(Choice):
"""
Adds checks upon collecting achievements. Achievements for clearing bosses and events are excluded.
"Exclude Grindy" also excludes fishing achievements.
"""
display_name = "Achievements"
option_none = 0
option_exclude_grindy = 1
option_exclude_fishing = 2
option_all = 3
default = 1
class FillExtraChecksWith(Choice):
"""
Applies if you have achievements enabled. "Useful Items" helps to make the early game less grindy.
Items are rewarded to all players in your Terraria world.
"""
display_name = "Fill Extra Checks With"
option_coins = 0
option_useful_items = 1
default = 1
options: typing.Dict[str, type(Option)] = { # type: ignore
"goal": Goal,
"achievements": Achievements,
"fill_extra_checks_with": FillExtraChecksWith,
"death_link": DeathLink,
}

154
worlds/terraria/Rewards.dsv Normal file
View File

@ -0,0 +1,154 @@
# ordered
Hermes Boots;
Magic Mirror;
Demon Conch;
Magic Conch;
Cosmolight; Calamity;
Grappling Hook;
Cloud in a Bottle;
Climbing Claws;
Ancient Chisel;
Fledgling Wings;
Rod of Discord;
# random
// Movement
Aglet;
Anklet of the Wind;
Ice Skates;
Lava Charm;
Water Walking Boots;
Flipper;
Frog Leg;
Shoe Spikes;
Tabi;
Black Belt;
Flying Carpet;
Moon Charm;
Neptune's Shell;
// Informational (Cell Phone)
Compass;
Depth Meter;
Radar;
Tally Counter;
Lifeform Analyzer;
DPS Meter;
Stopwatch;
Metal Detector;
Fisherman's Pocket Guide;
Weather Radio;
Sextant;
// Health and Mana
Band of Regeneration;
Celestial Magnet;
Nature's Gift;
Philosopher's Stone;
// Combat
Cobalt Shield;
Armor Polish;
Vitamins;
Bezoar;
Adhesive Bandage;
Megaphone;
Nazar;
Fast Clock;
Trifold Map;
Blindfold;
Pocket Mirror;
Paladin's Shield;
Frozen Turtle Shell;
Flesh Knuckles;
Putrid Scent;
Feral Claws;
Cross Necklace;
Star Cloak;
Titan Glove;
Obsidian Rose;
Magma Stone;
Shark Tooth Necklace;
Magic Quiver;
Rifle Scope;
// Construction (Hand of Creation)
Brick Layer;
Extendo Grip;
Paint Sprayer;
Portable Cement Mixer;
Treasure Magnet;
Step Stool;
// Money (Greedy Ring)
Discount Card;
Gold Ring;
Lucky Coin;
// Fishing (Lavaproof Tackle Bag)
High Test Fishing Line;
Angler Earring;
Tackle Box;
Lavaproof Fishing Hook;
// Yoyo Stuff
Red Counterweight;
Yoyo Glove;
// Vanilla Forced to Calamity
Diving Helmet; Calamity;
Jellyfish Necklace; Calamity;
// Calamity Accessories
Corrupt Flask; Calamity;
Crimson Flask; Calamity;
Craw Carapace; Calamity;
Giant Shell; Calamity;
Fungal Carapace; Calamity;
Life Jelly; Calamity;
Vital Jelly; Calamity;
Mana Jelly; Calamity;
Giant Tortoise Shell; Calamity;
Coin of Deceit; Calamity;
Ink Bomb; Calamity;
Voltaic Jelly; Calamity;
Wulfrum Battery; Calamity;
Luxor's Gift; Calamity;
Raider's Talisman; Calamity;
Rotten Dogtooth; Calamity;
Scuttler's Jewel; Calamity;
Unstable Granite Core; Calamity;
Amidias' Spark; Calamity;
Ursa Sergeant; Calamity;
Trinket of Chi; Calamity;
The Transformer; Calamity;
Rover Drive; Calamity;
Marnite Repulsion Shield; Calamity;
Frost Barrier; Calamity;
Ancient Fossil; Calamity;
Spelunker's Amulet; Calamity;
Fungal Symbiote; Calamity;
Gladiator's Locket; Calamity;
Wulfrum Acrobatics Pack; Calamity;
Depths Charm; Calamity;
Anechoic Plating; Calamity;
Iron Boots; Calamity;
Sprit Glyph; Calamity;
Abyssal Amulet; Calamity;

573
worlds/terraria/Rules.dsv Normal file
View File

@ -0,0 +1,573 @@
// TODO Calamity minion armor
// Starting gear
Copper Shortsword;
Guide; Npc;
// Immediately accessible
Timber!!; Achievement;
Benched; Achievement;
Stop! Hammer Time!; Achievement;
Matching Attire; Achievement;
Fashion Statement; Achievement;
Ooo! Shiny!; Achievement;
No Hobo; Achievement;
Merchant; Npc;
Bug Net; ; @calamity | Merchant;
Heavy Metal; Achievement;
Nurse; Npc; Merchant;
The Frequent Flyer; Achievement; Nurse;
Demolitionist; Npc; Merchant;
Dye Trader; Npc; @npc(4);
Dye Hard; Achievement; Dye Trader;
Lucky Break; Achievement;
Star Power; Achievement;
You Can Do It!; Achievement;
// Surface exploration
Aglet;
Heliophobia; Achievement;
Blighted Gel; Calamity;
Archaeologist; Achievement | Grindy;
Zoologist; Npc;
Cat; Npc; Zoologist;
Feeling Petty; Achievement; Cat | Dog;
Dog; Npc; Zoologist;
A Rather Blustery Day; Achievement | Grindy;
Enchanted Sword;
Pretty in Pink; Achievement | Grindy;
Marathon Medalist; Achievement | Grindy;
Angler; Npc;
Servant-in-Training; Achievement | Fishing; Angler;
\10 Fishing Quests; Achievement | Fishing; Angler;
Trout Monkey; Achievement | Fishing; Angler;
Glorious Golden Pole; Achievement | Fishing; Angler;
Fast and Fishious; Achievement | Fishing; Angler;
Supreme Helper Minion!; Achievement | Fishing; Angler;
Water Walking Boots;
Painter; Npc; @npc(8);
// Sky exploration
Into Orbit; Achievement;
Mysterious Circuitry; Calamity;
Dubious Plating; Calamity;
Charging Station; Calamity;
Codebreaker Base; Calamity;
Starfury;
// Underground
Watch Your Step!; Achievement;
Throwing Lines; Achievement;
Torch God; Location | Item(Reward: Torch God's Favor);
Vehicular Manslaughter; Achievement;
Hey! Listen!; Achievement;
I Am Loot!; Achievement;
Heart Breaker; Achievement;
Hold on Tight!; Achievement;
Feller of Evergreens; Calamity;
Gold Hammer; Hammer(55);
Gold Pickaxe; Pickaxe(55);
Like a Boss; Achievement;
Hermes Boots;
Jeepers Creepers; Achievement;
Stylist; Npc;
Funkytown; Achievement;
Deceiver of Fools; Achievement | Grindy;
Dead Men Tell No Tales; Achievement;
Bulldozer; Achievement | Grindy;
// Cavern
Obsidian;
Obsidian Skull; ; Obsidian;
There are Some Who Call Him...; Achievement | Grindy;
Lava Charm;
Demonite Ore;
Demonite Bar; ; Demonite Ore | (@calamity & #Calamity Evil Boss);
Evil Sword; ; Demonite Bar;
// Underground Ice
Ice Skates;
Flinx Fur Coat; ArmorMinions(1);
// Underground Desert
Golfer; Npc;
// Sunken Sea
Sea Prism; Calamity;
Navyplate; Calamity; Sea Prism & Obsidian;
// Underground Jungle
Anklet of the Wind;
Stinger;
Jungle Spores;
Vine;
Blade of Grass; ; Stinger & Jungle Spores & Vine;
Summoning Potion; Minions(1);
// Underworld
It's Getting Hot in Here; Achievement;
Rock Bottom; Achievement;
Obsidian Rose;
Havocplate; Calamity;
// Evil
Smashing, Poppet!; Achievement;
Arms Dealer; Npc;
Leading Landlord; Achievement; Nurse & Arms Dealer; // The logic is way more complex, but that doesn't affect anything
Completely Awesome; Achievement; Arms Dealer;
// King Slime
King Slime; Location | Item;
Sticky Situation; Achievement | Grindy;
The Cavalry; Achievement;
Solidifier; ; #King Slime;
// Desert Scourge
Desert Scourge; Calamity | Location | Item;
Pearl Shard; Calamity; #Desert Scourge;
Sea Remains; Calamity; Pearl Shard;
Reefclaw Hamaxe; Calamity | Hammer(60); Sea Remains;
Sandstorm; ; ~@calamity | Desert Scourge;
Voltaic Jelly; Calamity | Minions(1); Desert Scourge; // Jelly-Charged Battery doesn't stack. This is the case for all Calamity minion accessory upgrades.
// Giant Clam
Giant Clam; Calamity | Location | Item; Desert Scourge;
Amidias; Calamity; Desert Scourge & #Giant Clam;
// Blood Moon
Bloodbath; Achievement | Grindy;
Til Death...; Achievement | Grindy;
Quiet Neighborhood; Achievement;
// Eye of Cthulhu
Eye of Cthulhu; Location | Item;
Dryad; Npc; Eye of Cthulhu | Evil Boss | Skeletron;
Pumpkin Seeds; ; Dryad;
Pumpkin; ; Pumpkin Seeds;
Purification Powder; ; Dryad; // Shimmered from Evil Powder in 1.4.4. Not bought from Dryad in get fixed boi.
Party Girl; Npc; @npc(14);
Jolly Jamboree; Achievement | Grindy; Party Girl;
Acid Rain Tier 1; Calamity | Location | Item; Eye of Cthulhu;
// Crabulon
Crabulon; Calamity | Location | Item;
// Evil Boss
Evil Boss; Location | Item;
Evil Boss Part; ; #Evil Boss;
Evil Pickaxe; Pickaxe(65); Evil Boss Part;
Obsidian Armor; ArmorMinions(1); Obsidian & Evil Boss Part;
Tavernkeep; Npc; Evil Boss;
Old One's Army Tier 1; Location | Item; Tavernkeep;
Meteorite; ; Evil Boss;
Meteorite Bar; ; Meteorite;
Meteor Hamaxe; Hammer(60); Meteorite Bar;
Hellforge; ; @pickaxe(60);
Hellstone; ; @pickaxe(65) | Wall of Flesh;
Hellstone Bar; ; Hellstone;
Fiery Greatsword; ; Hellstone Bar;
Molten Hamaxe; Hammer(70); Hellstone Bar;
Molten Pickaxe; Pickaxe(100); Hellstone Bar;
Miner for Fire; Achievement; Molten Pickaxe;
Hot Reels!; Achievement; Hellstone Bar & Bug Net; // TODO Calamity
Brimstone Slag; Calamity; @pickaxe(100);
// Goblin Army
Goblin Army; Location | Item;
Goblin Tinkerer; Npc; Goblin Army;
Tinkerer's Workshop; ; Goblin Tinkerer;
Rocket Boots; ; Goblin Tinkerer;
Spectre Boots; ; Tinkerer's Workshop & Hermes Boots & Rocket Boots;
Lightning Boots; ; Tinkerer's Workshop & Spectre Boots & Anklet of the Wind & Aglet;
Frostspark Boots; ; Tinkerer's Workshop & Lightning Boots & Ice Skates;
Lava Waders; ; Tinkerer's Workshop & Obsidian Skull & Lava Charm & Obsidian Rose & Water Walking Boots;
Terraspark Boots; ; Tinkerer's Workshop & Frostspark Boots & Lava Waders;
Boots of the Hero; Achievement | Grindy; Terraspark Boots;
// Queen Bee
Where's My Honey?; Achievement;
Queen Bee; Location | Item;
Bee Keeper; ; #Queen Bee;
Bee Wax; ; #Queen Bee;
Bee Armor; ArmorMinions(2); Bee Wax;
Not the Bees!; Achievement; #Queen Bee & Bee Armor;
Witch Doctor; Npc; Queen Bee;
Pygmy Necklace; Minions(1); Witch Doctor;
// Calamity Evil Boss
Calamity Evil Boss; Calamity | Location | Item;
Aerialite Ore; Calamity; Calamity Evil Boss & @pickaxe(65);
Aerialite Bar; Calamity; Aerialite Ore;
Aerial Hamaxe; Calamity | Hammer(70); Aerialite Bar;
Skyfringe Pickaxe; Calamity | Pickaxe(75); Aerialite Bar;
// Skeletron
Skeletron; Location | Item;
Clothier; Npc; Skeletron;
Dungeon; ; Skeletron;
Dungeon Heist; Achievement; Dungeon;
Bone; ; Dungeon | (@calamity & #Skeletron);
Bewitching Table; Minions(1); Dungeon;
Mechanic; ; Dungeon;
Wire; ; Mechanic;
Decryption Computer; Calamity; Mysterious Circuitry & Dubious Plating & Wire;
Actuator; ; Mechanic;
Muramasa; ; Dungeon;
// Deerclops
Deerclops; Location | Item;
// The Slime God
The Slime God; Calamity | Location | Item; Blighted Gel;
Purified Gel; Calamity; #The Slime God;
Static Refiner; Calamity; Purified Gel & Solidifier;
Gelpick; Calamity | Pickaxe(100); Static Refiner & Purified Gel & Blighted Gel;
Night's Edge; ; Evil Sword & Muramasa & Blade of Grass & Fiery Greatsword & (~@calamity | Purified Gel);
// Wall of Flesh
Wall of Flesh; Location | Item(Hardmode); Guide;
Pwnhammer; Hammer(80); #Wall of Flesh;
Wizard; Npc; Wall of Flesh;
Tax Collector; Npc; Purification Powder & Wall of Flesh;
Spider Fangs; ; Wall of Flesh;
Spider Armor; ArmorMinions(3); Spider Fangs;
Cross Necklace; ; Wall of Flesh;
Altar; ; Wall of Flesh & @hammer(80);
Begone, Evil!; Achievement; Altar;
Cobalt Ore; ; ((~@calamity & Altar) | (@calamity & Wall of Flesh)) & @pickaxe(100);
Extra Shiny!; Achievement; Cobalt Ore | Mythril Ore | Adamantite Ore | Chlorophyte Ore;
Cobalt Bar; ; Cobalt Ore;
Cobalt Pickaxe; Pickaxe(110); Cobalt Bar;
Soul of Night; ; Wall of Flesh | (@calamity & Altar);
Hallow; ; Wall of Flesh;
Pixie Dust; ; Hallow;
Unicorn Horn; ; Hallow;
Crystal Shard; ; Hallow;
Axe of Purity; Calamity; Feller of Evergreens & Purification Powder & Pixie Dust & Crystal Shard;
Soul of Light; ; Hallow | (@calamity & #Queen Slime);
Blessed Apple; ; Hallow;
Rod of Discord; ; Hallow;
Gelatin World Tour; Achievement | Grindy; Dungeon & Wall of Flesh & Hallow & #King Slime;
Soul of Flight; ; Wall of Flesh;
Head in the Clouds; Achievement; (Soul of Flight & ((Hardmode Anvil & (Soul of Light | Soul of Night | Pixie Dust | Wall of Flesh | Solar Eclipse | @mech_boss(1) | Plantera | Spectre Bar | #Golem)) | (Shroomite Bar & Autohammer) | #Mourning Wood | #Pumpking)) | Steampunker | (Wall of Flesh & Witch Doctor) | (Solar Eclipse & Plantera) | #Everscream | #Old One's Army Tier 3 | #Empress of Light | #Duke Fishron | (Fragment & Luminite Bar & Ancient Manipulator); // Leaf Wings are Post-Plantera in 1.4.4
Bunny; Npc; Zoologist & Wall of Flesh; // Extremely simplified
Forbidden Fragment; ; Sandstorm & Wall of Flesh;
Astral Infection; Calamity; Wall of Flesh;
Stardust; Calamity; Astral Infection | #Astrum Aureus | #Astrum Deus;
Trapper Bulb; Calamity; Wall of Flesh;
Titan Heart; Calamity; Astral Infection;
Essence of Sunlight; Calamity; Wall of Flesh | Golem;
Essence of Eleum; Calamity; Wall of Flesh | Cryogen | #Cryogen; // TODO Check
Essence of Havoc; Calamity; Wall of Flesh | #Calamitas Clone | #Brimstone Elemental;
Don't Dread on Me; Achievement; Wall of Flesh;
Earth Elemental; Calamity | Location | Item; Wall of Flesh;
Cloud Elemental; Calamity | Location | Item; Wall of Flesh;
Truffle; Npc; Wall of Flesh;
It Can Talk?!; Achievement; Truffle;
The First Shadowflame; Calamity | Minions(1); Goblin Army | Wall of Flesh;
// Pirate Invasion
Pirate Invasion; Location | Item; Wall of Flesh;
Pirate; Npc; Pirate Invasion;
// Queen Slime
Queen Slime; Location | Item; Hallow;
// Aquatic Scourge
Mythril Ore; ; ((~@calamity & Altar) | (@calamity & @mech_boss(1))) & @pickaxe(110);
Mythril Bar; ; Mythril Ore;
Hardmode Anvil; ; Mythril Bar;
Mythril Pickaxe; Pickaxe(150); Hardmode Anvil & Mythril Bar;
Adamantite Ore; ; ((~@calamity & Altar) | (@calamity & @mech_boss(2))) & @pickaxe(150);
Hardmode Forge; ; Hardmode Anvil & Adamantite Ore & Hellforge;
Adamantite Bar; ; Hardmode Forge & Adamantite Ore;
Adamantite Pickaxe; Pickaxe(180); Hardmode Anvil & Adamantite Bar;
Forbidden Armor; ArmorMinions(2); Hardmode Anvil & Adamantite Bar & Forbidden Fragment;
Aquatic Scourge; Calamity | Location | Item;
The Twins; Location | Item | Mech Boss; (@calamity | Hardmode Anvil) & Soul of Light;
Brimstone Elemental; Calamity | Location | Item; Soul of Night & Essence of Havoc & Unholy Core;
The Destroyer; Location | Item | Mech Boss; (@calamity | Hardmode Anvil) & Soul of Night;
Cryogen; Calamity | Location | Item; Soul of Night & Soul of Light & Essence of Eleum;
Skeletron Prime; Location | Item | Mech Boss; (@calamity | Hardmode Anvil) & Soul of Night & Soul of Light & Bone;
# mechanical_bosses
Cragmaw Mire; Calamity | Location | Item; #Acid Rain Tier 2;
Nuclear Rod; Calamity | Minions(1); #Cragmaw Mire;
Acid Rain Tier 2; Calamity | Location | Item; #Acid Rain Tier 1 & Aquatic Scourge;
// The Twins
Soul of Sight; ; The Twins;
Steampunker; Npc; @mech_boss(1);
Hammush; ; Truffle & @mech_boss(1);
Rainbow Rod; ; Hardmode Anvil & Crystal Shard & Unicorn Horn & Pixie Dust & Soul of Light & Soul of Sight;
Prismancer; Achievement; Rainbow Rod;
Long Ranged Sensor Array; Calamity; Hardmode Anvil & Mysterious Circuitry & Dubious Plating & Mythril Bar & Wire & Decryption Computer & Codebreaker Base;
Hydraulic Volt Crusher; Calamity; Hardmode Anvil & Mysterious Circuitry & Dubious Plating & Mythril Bar & Soul of Sight;
Life Fruit; ; (@mech_boss(1) & Wall of Flesh) | (@calamity & (Living Shard | Wall of Flesh));
Get a Life; Achievement; Life Fruit;
Topped Off; Achievement; Life Fruit;
Old One's Army Tier 2; Location | Item; #Old One's Army Tier 1 & (@mech_boss(1) | #Old One's Army Tier 3);
// Brimstone Elemental
Infernal Suevite; Calamity; @pickaxe(150) | Brimstone Elemental;
Unholy Core; Calamity; Infernal Suevite & Hellstone;
// The Destroyer
Soul of Might; ; #The Destroyer;
// Cryogen
Cryonic Ore; Calamity; Cryogen & (@pickaxe(180) | @mech_boss(2));
Cryonic Bar; Calamity; (Hardmode Forge & Cryonic Ore) | Fleshy Geode | Necromantic Geode;
Abyssal Warhammer; Calamity | Hammer(88); Hardmode Anvil & Cryonic Bar;
Shardlight Pickaxe; Calamity | Pickaxe(180); Hardmode Anvil & Cryonic Bar;
// Skeletron Prime
Soul of Fright; ; #Skeletron Prime;
Inferna Cutter; Calamity; Hardmode Anvil & Axe of Purity & Soul of Fright & Essence of Havoc;
Buckets of Bolts; Achievement; #The Twins & #The Destroyer & #Skeletron Prime;
Mecha Mayhem; Achievement; #The Twins & #The Destroyer & #Skeletron Prime;
Hallowed Bar; ; (#The Twins | #The Destroyer | #Skeletron Prime) & (~@calamity | @mech_boss(3)); // Can't count on Hallowed Ore, since the player may be in prehardmode (TODO Check this)
Hallowed Armor; ArmorMinions(3); Hardmode Anvil & Hallowed Bar;
Excalibur; ; Hardmode Anvil & Hallowed Bar;
Pickaxe Axe; Pickaxe(200); Hardmode Anvil & Hallowed Bar & Soul of Fright & Soul of Might & Soul of Sight;
Drax Attax; Achievement; Pickaxe Axe;
True Night's Edge; ; Hardmode Anvil & Night's Edge & Soul of Fright & Soul of Might & Soul of Sight;
Chlorophyte Ore; ; Wall of Flesh & @pickaxe(200);
Photosynthesis; Achievement; Chlorophyte Ore;
Chlorophyte Bar; ; Hardmode Forge & Chlorophyte Ore;
True Excalibur; ; Hardmode Anvil & Excalibur & Chlorophyte Bar;
Chlorophyte Pickaxe; Pickaxe(200); Hardmode Anvil & Chlorophyte Bar;
Chlorophyte Warhammer; Hammer(90); Hardmode Anvil & Chlorophyte Bar;
// Calamitas Clone
Calamitas Clone; Calamity | Location | Item | Goal; Hardmode Anvil & Hellstone Bar & Essence of Havoc;
Plantera; Location | Item | Goal; Wall of Flesh & (@mech_boss(3) | (@calamity & Hardmode Anvil & Trapper Bulb));
# calamitas_clone
# plantera
Ashes of Calamity; Calamity; #Calamitas Clone;
// Plantera
The Axe; Hammer(100); #Plantera;
Seedler; ; #Plantera;
Living Shard; Calamity; #Plantera;
Tiki Armor; ArmorMinions(4); Witch Doctor & Wall of Flesh & Plantera;
Hercules Beetle; ; Witch Doctor & Wall of Flesh & Plantera;
You and What Army?; Achievement; @minions(8);
Cyborg; Npc; Plantera;
Autohammer; ; Truffle & Plantera;
Shroomite Bar; ; Autohammer & Chlorophyte Bar;
Shroomite Digging Claw; Pickaxe(200); Hardmode Anvil & Shroomite Bar;
Princess; Npc; Guide & Merchant & Nurse & Demolitionist & Dye Trader & Zoologist & Angler & Painter & Stylist & Golfer & Arms Dealer & Dryad & Party Girl & Tavernkeep & Goblin Tinkerer & Witch Doctor & Clothier & Wizard & Truffle & Tax Collector & Pirate & Steampunker & Cyborg;
Real Estate Agent; Achievement; Princess;
Ectoplasm; ; ((Dungeon & Wall of Flesh) | @calamity) & Plantera;
Paladin's Shield; ; Dungeon & Wall of Flesh & Plantera;
Core of Sunlight; Calamity; (Hardmode Anvil & Essence of Sunlight & Ectoplasm) | Fleshy Geode | Necromantic Geode;
Core of Eleum; Calamity; (Hardmode Anvil & Essence of Eleum & Ectoplasm) | Fleshy Geode | Necromantic Geode;
Core of Havoc; Calamity; (Hardmode Anvil & Essence of Havoc & Ectoplasm) | Fleshy Geode | Necromantic Geode;
Core of Calamity; Calamity; (Hardmode Anvil & Core of Sunlight & Core of Eleum & Core of Havoc & Ashes of Calamity) | Necromantic Geode;
Spectre Bar; ; Hardmode Forge & Chlorophyte Bar & Ectoplasm;
Spectre Pickaxe; Pickaxe(200); Hardmode Anvil & Spectre Bar;
Spectre Hamaxe; Hammer(90); Hardmode Anvil & Spectre Bar;
Robbing the Grave; Achievement; Dungeon & Plantera;
Evil Key; ; Plantera | (@calamity & #Wall of Flesh);
Frozen Key; ; Plantera | (@calamity & #Cryogen);
Jungle Key; ; Plantera | (@calamity & #Plantera);
Hallowed Key; ; (Plantera & Hallow) | (@calamity & #Queen Slime);
Desert Key; ; Plantera | (@calamity & #Great Sand Shark);
Big Booty; Achievement; Dungeon & Wall of Flesh & Plantera & (Evil Key | Frozen Key | Jungle Key | Hallowed Key | Desert Key);
Rainbow Gun; ; Dungeon & Wall of Flesh & Plantera & Hallowed Key;
Rainbows and Unicorns; Achievement; Blessed Apple & Rainbow Gun;
Perennial Ore; Calamity; Plantera;
Perennial Bar; Calamity; Hardmode Forge & Perennial Ore;
Beastial Pickaxe; Calamity | Pickaxe(200); Hardmode Anvil & Perennial Bar;
Armored Digger; Calamity | Location | Item; Plantera; // TODO Check
// Solar Eclipse
Temple Raider; Achievement; #Plantera;
Lihzahrd Temple; ; #Plantera | (Plantera & Actuator) | @pickaxe(210) | (@calamity & Hardmode Anvil & Soul of Light & Soul of Night);
Solar Eclipse; ; Lihzahrd Temple & Wall of Flesh;
Broken Hero Sword; ; (Solar Eclipse & Plantera) | (@calamity & #Calamitas Clone);
Terra Blade; ; Hardmode Anvil & True Night's Edge & True Excalibur & Broken Hero Sword & (~@calamity | Living Shard);
Sword of the Hero; Achievement; Terra Blade;
Kill the Sun; Achievement; Solar Eclipse;
// Great Sand Shark
Great Sand Shark; Calamity | Location | Item; Hardmode Anvil & Forbidden Fragment & Core of Sunlight;
// Leviathan and Anahita
Leviathan and Anahita; Calamity | Location | Item;
// Astrum Aureus
Astrum Aureus; Calamity | Location | Item; Hardmode Anvil & Stardust & Astral Infection;
Starbuster Core; Calamity | Minions(1); Astrum Aureus & Astral Infection;
// Golem
Golem; Location | Item | Goal; (Wall of Flesh & Plantera & Lihzahrd Temple) | (@calamity & Hardmode Anvil & Lihzahrd Temple & Essence of Sunlight);
# golem
Picksaw; Pickaxe(210); #Golem;
Lihzahrd Brick; ; @pickaxe(210);
Scoria Ore; Calamity; Golem | @pickaxe(210);
Scoria Bar; Calamity; Hardmode Forge & Scoria Ore;
Seismic Hampick; Calamity | Pickaxe(210) | Hammer(95); Hardmode Anvil & Scoria Bar;
Life Alloy; Calamity; (Hardmode Anvil & Cryonic Bar & Perennial Bar & Scoria Bar) | Necromantic Geode;
Advanced Display; Calamity; Hardmode Anvil & Mysterious Circuitry & Dubious Plating & Life Alloy & Long Ranged Sensor Array;
Old One's Army Tier 3; Location | Item; #Old One's Army Tier 1 & Golem;
// Martian Madness
Martian Madness; Location | Item; Wall of Flesh & Golem;
Influx Waver; ; #Martian Madness;
// The Plaguebringer Goliath
Plague Cell Canister; Calamity; Golem;
Plaguebringer; Calamity | Location | Item; Golem;
The Plaguebringer Goliath; Calamity | Location | Item; Hardmode Anvil & Plague Cell Canister;
// Duke Fishron
Duke Fishron; Location | Item; Bug Net & Wall of Flesh;
// Pumpkin Moon
Pumpkin Moon; ; Hardmode Anvil & Pumpkin & Ectoplasm & (@calamity | Hallowed Bar);
Spooky Armor; ArmorMinions(4); Pumpkin Moon;
Mourning Wood; Location | Item; Pumpkin Moon;
Necromantic Scroll; Minions(1); Mourning Wood;
Papyrus Scarab; Minions(1); Tinkerer's Workshop & Hercules Beetle & Necromantic Scroll;
Pumpking; Location | Item; Pumpkin Moon;
The Horseman's Blade; ; #Pumpking;
Baleful Harvest; Achievement; Pumpkin Moon;
// Frost Moon
Frost Moon; ; Hardmode Anvil & Ectoplasm & (@calamity | Soul of Fright);
Everscream; Location | Item; Frost Moon;
Santa-NK1; Location | Item; Frost Moon;
Ice Queen; Location | Item; Frost Moon;
Ice Scream; Achievement; Frost Moon;
Christmas; ; Frost Moon;
// Frost Legion
Frost Legion; Location | Item; (Wall of Flesh & Christmas) | (@calamity & Soul of Light & Soul of Night);
Santa Claus; Npc; Frost Legion & Christmas;
// Ravager
Ravager; Calamity | Location | Item; Hardmode Anvil & Lihzahrd Temple & Lihzahrd Brick;
Fleshy Geode; Calamity; #Ravager;
// Empress of Light
Empress of Light; Location | Item | Goal; Wall of Flesh & Hallow & (@calamity | Plantera);
# empress_of_light
// Lunatic Cultist
Lunatic Cultist; Location | Item; (@calamity | (Dungeon & Golem)) & Wall of Flesh;
Astrum Deus; Calamity | Location | Item; Titan Heart;
# lunatic_cultist
# astrum_deus
Ancient Manipulator; ; #Lunatic Cultist;
// Lunar Events
Lunar Events; Location | Item; #Lunatic Cultist;
Fragment; ; #Lunar Events | #Astrum Deus;
Galactica Singularity; Calamity; Ancient Manipulator & Fragment;
Meld Blob; Calamity; #Lunar Events | #Astrum Deus;
Meld Construct; Calamity; Ancient Manipulator & Meld Blob & Stardust;
// Astrum Deus
Astral Ore; Calamity; Wall of Flesh & Astrum Deus;
Astral Bar; Calamity; Ancient Manipulator & Stardust & Astral Ore;
Astral Hamaxe; Calamity | Hammer(100); Ancient Manipulator & Astral Bar;
Astral Pickaxe; Calamity | Pickaxe(220); Ancient Manipulator & Astral Bar;
// Moon Lord
Moon Lord; Location | Item | Goal; #Lunar Events;
# moon_lord
Slayer of Worlds; Achievement; #Evil Boss & #The Destroyer & #Duke Fishron & #Eye of Cthulhu & #Golem & #King Slime & #Lunatic Cultist & #Moon Lord & #Plantera & #Queen Bee & #Skeletron & #Skeletron Prime & #The Twins & #Wall of Flesh;
Luminite; ; #Moon Lord;
Luminite Bar; ; Ancient Manipulator & Luminite;
Luminite Hamaxe; Hammer(100); Ancient Manipulator & Fragment & Luminite Bar;
Luminite Pickaxe; Pickaxe(225); Ancient Manipulator & Fragment & Luminite Bar;
Genesis Pickaxe; Calamity | Pickaxe(225); Ancient Manipulator & Meld Construct & Luminite Bar;
Stardust Armor; ArmorMinions(5); Ancient Manipulator & Fragment & Luminite Bar;
Terrarian; ; #Moon Lord;
Sick Throw; Achievement; Terrarian;
Meowmere; ; #Moon Lord;
Star Wrath; ; #Moon Lord;
Exodium Cluster; Calamity; Moon Lord & @pickaxe(225);
Normality Relocator; Calamity; Ancient Manipulator & Rod of Discord & Exodium Cluster & Fragment;
Unholy Essence; Calamity; Moon Lord | #Providence, the Profaned Goddess;
Phantoplasm; Calamity; Moon Lord & (Wall of Flesh | Dungeon); // TODO Check
Eldritch Soul Artifact; Calamity; Exodium Cluster & Navyplate & Phantoplasm;
// Profaned Guardians
Profaned Guardians; Calamity | Location | Item; Ancient Manipulator & Unholy Essence & Luminite Bar;
// Dragonfolly
The Dragonfolly; Calamity | Location | Item; Ancient Manipulator & Unholy Essence & Luminite Bar;
Effulgent Feather; Calamity; Moon Lord | #The Dragonfolly;
// Providence, the Profaned Goddess
Providence, the Profaned Goddess; Calamity | Location | Item | Goal; #Profaned Guardians;
# providence_the_profaned_goddess
Divine Geode; Calamity; #Providence, the Profaned Goddess;
Profaned Soul Artifact; Calamity | Minions(1); Exodium Cluster & Havocplate & Divine Geode;
Rune of Kos; Calamity; #Providence, the Profaned Goddess;
Uelibloom Ore; Calamity; Providence, the Profaned Goddess;
Uelibloom Bar; Calamity; Hardmode Forge & Uelibloom Ore;
Grax; Calamity | Hammer(110); Ancient Manipulator & Inferna Cutter & Luminite Hamaxe & Uelibloom Bar;
Blossom Pickaxe; Calamity | Pickaxe(250); Ancient Manipulator & Uelibloom Bar;
Voltage Regulation System; Calamity; Ancient Manipulator & Mysterious Circuitry & Dubious Plating & Uelibloom Bar & Luminite Bar & Advanced Display;
Necromantic Geode; Calamity; #Ravager & Providence, the Profaned Goddess;
// Sentinels of the Devourer
Storm Weaver; Calamity | Location | Item; Rune of Kos;
Armored Shell; Calamity; #Storm Weaver;
Ceaseless Void; Calamity | Location | Item; Rune of Kos;
Dark Plasma; Calamity; #Ceaseless Void;
Signus, Envoy of the Devourer; Calamity | Location | Item; Rune of Kos;
Twisting Nether; Calamity; #Signus, Envoy of the Devourer;
// Polterghast
Polterghast; Calamity | Location | Item; Dungeon & ((Ancient Manipulator & Phantoplasm) | Moon Lord);
Colossal Squid; Calamity | Location | Item;
Reaper Shark; Calamity | Location | Item;
Eidolon Wyrm; Calamity | Location | Item;
// The Old Duke
Mauler; Calamity | Location | Item; #Acid Rain Tier 3;
Nuclear Terror; Calamity | Location | Item; #Acid Rain Tier 3;
Acid Rain Tier 3; Calamity | Location | Item; #Acid Rain Tier 1 & Polterghast; // TODO Check
The Old Duke; Calamity | Location | Item; #Acid Rain Tier 3 | (Bug Net & Moon Lord) | (Amidias & The Old Duke);
// The Devourer of Gods
The Devourer of Gods; Calamity | Location | Item | Goal; Ancient Manipulator & ((Armored Shell & Twisting Nether & Dark Plasma) | (Luminite Bar & Galactica Singularity & Phantoplasm));
# the_devourer_of_gods
Cosmilite Bar; Calamity; #The Devourer of Gods;
Cosmic Anvil; Calamity; Ancient Manipulator & Hardmode Anvil & Cosmilite Bar & Luminite Bar & Galactica Singularity & Exodium Cluster;
Nightmare Fuel; Calamity; Pumpkin Moon & The Devourer of Gods;
Endothermic Energy; Calamity; Frost Moon & The Devourer of Gods;
Darksun Fragment; Calamity; Solar Eclipse & The Devourer of Gods;
Dark Sun Ring; Calamity; Cosmic Anvil & Uelibloom Bar & Darksun Fragment;
Ascendant Spirit Essence; Calamity; Ancient Manipulator & Phantoplasm & Nightmare Fuel & Endothermic Energy & Darksun Fragment;
// Yharon, Dragon of Rebirth
Yharon, Dragon of Rebirth; Calamity | Location | Item | Goal; Ancient Manipulator & Effulgent Feather & Life Alloy;
# yharon_dragon_of_rebirth
Yharon Soul Fragment; Calamity; #Yharon, Dragon of Rebirth;
Auric Ore; Calamity; Yharon, Dragon of Rebirth & @pickaxe(250);
Auric Bar; Calamity; Cosmic Anvil & Auric Ore & Yharon Soul Fragment;
Zenith; Location | Item(Has Zenith) | Goal; Hardmode Anvil & Terra Blade & Meowmere & Star Wrath & Influx Waver & The Horseman's Blade & Seedler & Starfury & Bee Keeper & Enchanted Sword & Copper Shortsword & (~@calamity | Auric Bar);
# zenith
// Exo Mechs
Auric Quantum Cooling Cell; Calamity; Cosmic Anvil & Auric Bar & Mysterious Circuitry & Dubious Plating & Endothermic Energy & Core of Eleum & Voltage Regulation System;
Exo Mechs; Calamity | Location | Item | Final Boss; Codebreaker Base & Decryption Computer & Auric Quantum Cooling Cell;
Supreme Witch, Calamitas; Calamity | Location | Item | Final Boss; Cosmic Anvil & Brimstone Slag & Auric Bar & Core of Calamity & Ashes of Calamity;
# calamity_final_bosses
Exo Prism; Calamity; #Exo Mechs;
Draedon's Forge; Calamity; Cosmic Anvil & Hardmode Forge & Tinkerer's Workshop & Ancient Manipulator & Auric Bar & Exo Prism & Ascendant Spirit Essence;
// Supreme Witch, Calamitas
Ashes of Annihilation; Calamity; #Supreme Witch, Calamitas;
Shadowspec Bar; Calamity; Draedon's Forge & Auric Bar & Exo Prism & Ashes of Annihilation;
Crystyl Crusher; Calamity | Pickaxe(1000); Draedon's Forge & Luminite Pickaxe & Blossom Pickaxe & Shadowspec Bar;
Angelic Alliance; Calamity | Minions(2); Draedon's Forge & Hallowed Armor & Paladin's Shield & True Excalibur & Cross Necklace & Shadowspec Bar;
// Adult Eidolon Wyrm;
Adult Eidolon Wyrm; Calamity | Location | Item | Goal; Rod of Discord | Normality Relocator;
# adult_eidolon_wyrm

342
worlds/terraria/__init__.py Normal file
View File

@ -0,0 +1,342 @@
# Look at `Rules.dsv` first to get an idea for how this works
from typing import Union, Tuple, List, Dict, Set
from worlds.AutoWorld import WebWorld, World
from BaseClasses import Region, ItemClassification, Tutorial, CollectionState
from .Checks import (
TerrariaItem,
TerrariaLocation,
goals,
rules,
rule_indices,
labels,
rewards,
item_name_to_id,
location_name_to_id,
COND_ITEM,
COND_LOC,
COND_FN,
COND_GROUP,
npcs,
pickaxes,
hammers,
mech_bosses,
progression,
armor_minions,
accessory_minions,
)
from .Options import options
class TerrariaWeb(WebWorld):
tutorials = [
Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Terraria randomizer connected to an Archipelago Multiworld.",
"English",
"setup_en.md",
"setup/en",
["Seldom"],
)
]
class TerrariaWorld(World):
"""
Terraria is a 2D multiplayer sandbox game featuring mining, building, exploration, and combat.
Features 18 bosses and 4 classes.
"""
game = "Terraria"
web = TerrariaWeb()
option_definitions = options
# data_version is used to signal that items, locations or their names
# changed. Set this to 0 during development so other games' clients do not
# cache any texts, then increase by 1 for each release that makes changes.
data_version = 2
item_name_to_id = item_name_to_id
location_name_to_id = location_name_to_id
# Turn into an option when calamity is supported in the mod
calamity = False
ter_items: List[str]
ter_locations: List[str]
ter_goals: Dict[str, str]
goal_items: Set[str]
goal_locations: Set[str]
def generate_early(self) -> None:
goal, goal_locations = goals[self.multiworld.goal[self.player].value]
ter_goals = {}
goal_items = set()
for location in goal_locations:
_, flags, _, _ = rules[rule_indices[location]]
item = flags.get("Item") or f"Post-{location}"
ter_goals[item] = location
goal_items.add(item)
achievements = self.multiworld.achievements[self.player].value
location_count = 0
locations = []
for rule, flags, _, _ in rules[:goal]:
if (
(not self.calamity and "Calamity" in flags)
or (achievements < 1 and "Achievement" in flags)
or (achievements < 2 and "Grindy" in flags)
or (achievements < 3 and "Fishing" in flags)
or (
rule == "Zenith" and self.multiworld.goal[self.player].value != 11
) # Bad hardcoding
):
continue
if "Location" in flags or ("Achievement" in flags and achievements >= 1):
# Location
location_count += 1
locations.append(rule)
elif (
"Achievement" not in flags
and "Location" not in flags
and "Item" not in flags
):
# Event
locations.append(rule)
item_count = 0
items = []
for rule, flags, _, _ in rules[:goal]:
if not self.calamity and "Calamity" in flags:
continue
if "Item" in flags:
# Item
item_count += 1
if rule not in goal_locations:
items.append(rule)
elif (
"Achievement" not in flags
and "Location" not in flags
and "Item" not in flags
):
# Event
items.append(rule)
extra_checks = self.multiworld.fill_extra_checks_with[self.player].value
ordered_rewards = [
reward
for reward in labels["ordered"]
if self.calamity or "Calamity" not in rewards[reward]
]
while extra_checks == 1 and item_count < location_count and ordered_rewards:
items.append(ordered_rewards.pop(0))
item_count += 1
random_rewards = [
reward
for reward in labels["random"]
if self.calamity or "Calamity" not in rewards[reward]
]
self.multiworld.random.shuffle(random_rewards)
while extra_checks == 1 and item_count < location_count and random_rewards:
items.append(random_rewards.pop(0))
item_count += 1
while item_count < location_count:
items.append("Reward: Coins")
item_count += 1
self.ter_items = items
self.ter_locations = locations
self.ter_goals = ter_goals
self.goal_items = goal_items
self.goal_locations = goal_locations
def create_regions(self) -> None:
menu = Region("Menu", self.player, self.multiworld)
for location in self.ter_locations:
menu.locations.append(
TerrariaLocation(
self.player, location, location_name_to_id.get(location), menu
)
)
self.multiworld.regions.append(menu)
def create_item(self, item: str) -> TerrariaItem:
if item in progression:
classification = ItemClassification.progression
else:
classification = ItemClassification.filler
return TerrariaItem(item, classification, item_name_to_id[item], self.player)
def create_items(self) -> None:
for item in self.ter_items:
if (rule_index := rule_indices.get(item)) is not None:
_, flags, _, _ = rules[rule_index]
if "Item" in flags:
name = flags.get("Item") or f"Post-{item}"
else:
continue
else:
name = item
self.multiworld.itempool.append(self.create_item(name))
locked_items = {}
for location in self.ter_locations:
_, flags, _, _ = rules[rule_indices[location]]
if "Location" not in flags and "Achievement" not in flags:
if location in progression:
classification = ItemClassification.progression
else:
classification = ItemClassification.useful
locked_items[location] = TerrariaItem(
location, classification, None, self.player
)
for item, location in self.ter_goals.items():
locked_items[location] = self.create_item(item)
for location, item in locked_items.items():
self.multiworld.get_location(location, self.player).place_locked_item(item)
def check_condition(
self,
state,
sign: bool,
ty: int,
condition: Union[str, Tuple[Union[bool, None], list]],
arg: Union[str, int, None],
) -> bool:
if ty == COND_ITEM:
_, flags, _, _ = rules[rule_indices[condition]]
if "Item" in flags:
name = flags.get("Item") or f"Post-{condition}"
else:
name = condition
return sign == state.has(name, self.player)
elif ty == COND_LOC:
_, _, operator, conditions = rules[rule_indices[condition]]
return sign == self.check_conditions(state, operator, conditions)
elif ty == COND_FN:
if condition == "npc":
if type(arg) is not int:
raise Exception("@npc requires an integer argument")
npc_count = 0
for npc in npcs:
if state.has(npc, self.player):
npc_count += 1
if npc_count >= arg:
return sign
return not sign
elif condition == "calamity":
return sign == self.calamity
elif condition == "pickaxe":
if type(arg) is not int:
raise Exception("@pickaxe requires an integer argument")
for pickaxe, power in pickaxes.items():
if power >= arg and state.has(pickaxe, self.player):
return sign
return not sign
elif condition == "hammer":
if type(arg) is not int:
raise Exception("@hammer requires an integer argument")
for hammer, power in hammers.items():
if power >= arg and state.has(hammer, self.player):
return sign
return not sign
elif condition == "mech_boss":
if type(arg) is not int:
raise Exception("@mech_boss requires an integer argument")
boss_count = 0
for boss in mech_bosses:
if state.has(boss, self.player):
boss_count += 1
if boss_count >= arg:
return sign
return not sign
elif condition == "minions":
if type(arg) is not int:
raise Exception("@minions requires an integer argument")
minion_count = 1
for armor, minions in armor_minions.items():
if state.has(armor, self.player) and minions + 1 > minion_count:
minion_count = minions + 1
if minion_count >= arg:
return sign
for accessory, minions in accessory_minions.items():
if state.has(accessory, self.player):
minion_count += minions
if minion_count >= arg:
return sign
return not sign
else:
raise Exception(f"Unknown function {condition}")
elif ty == COND_GROUP:
operator, conditions = condition
return sign == self.check_conditions(state, operator, conditions)
def check_conditions(
self,
state,
operator: Union[bool, None],
conditions: List[
Tuple[
bool,
int,
Union[str, Tuple[Union[bool, None], list]],
Union[str, int, None],
]
],
) -> bool:
if operator is None:
if len(conditions) == 0:
return True
if len(conditions) > 1:
raise Exception("Found multiple conditions without an operator")
return self.check_condition(state, *conditions[0])
elif operator:
return any(
self.check_condition(state, *condition) for condition in conditions
)
else:
return all(
self.check_condition(state, *condition) for condition in conditions
)
def set_rules(self) -> None:
for location in self.ter_locations:
def check(state: CollectionState, location=location):
_, _, operator, conditions = rules[rule_indices[location]]
return self.check_conditions(state, operator, conditions)
self.multiworld.get_location(location, self.player).access_rule = check
self.multiworld.completion_condition[self.player] = lambda state: state.has_all(
self.goal_items, self.player
)
def fill_slot_data(self) -> Dict[str, object]:
return {
"goal": list(self.goal_locations),
"deathlink": bool(self.multiworld.death_link[self.player]),
}

View File

@ -0,0 +1,19 @@
# Terraria
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
Boss/event flags are randomized. So, defeating Empress of Light could give you Post-Skeletron, which allows you to enter
the Dungeon, for example. In your player settings, you may also add item rewards and achievements to the pool.
## What Terraria items can appear in other players' worlds?
Boss/event flags, and item rewards if enabled.
## What does another world's item look like in Terraria?
You won't see the items in Terraria, but you will receive a chat message, telling you what item you sent to whom.

View File

@ -0,0 +1,52 @@
# Terraria for Archipelago Setup Guide
## Required Software
Download and install [Terraria](https://store.steampowered.com/app/105600/Terraria/)
and [TModLoader](https://store.steampowered.com/app/1281930/tModLoader/) on Steam
## Installing the Archipelago Mod
Subscribe to [the mod](https://steamcommunity.com/sharedfiles/filedetails/?id=2922217554) on Steam.
This mod might not work with mods that significantly alter progression or vanilla features. It is
highly recommended to use utility mods and features to speed up gameplay, such as:
- Journey Mode
- Ore Excavator
- Magic Storage
- Alchemist NPC Lite
- Reduced Grinding
## Configuring your YAML File
### What is a YAML and why do I need one?
You can see the [basic multiworld setup guide](/tutorial/Archipelago/setup/en) here
on the Archipelago website to learn about why Archipelago uses YAML files and what they're for.
### Where do I get a YAML?
You can use the [game settings page for Terraria](/games/Terraria/player-settings) here
on the Archipelago website to generate a YAML using a graphical interface.
## Joining an Archipelago Game in Terraria
1. Launch TModLoader
2. In Workshop > Manage Mods, edit Archipelago Randomizer's settings
- "Name" should be the player name you set when creating your YAML file
- "Port" should be the port number associated with the Archipelago server. It will be a 4 or 5
digit number.
- If you're not hosting your game on the Archipelago website, change "Address" to the server's
URL or IP address
3. Create a new character and world as normal (or use an existing one if you prefer). Terraria is
usually significantly more difficult with this mod, so it is recommended to choose a lower
difficulty than you normally would.
4. Open the world in single player or multiplayer
5. When you're ready, open chat, and enter `/apstart` to start the game.
## Commands
While playing Archipelago, you can interact with the server using the commands listed in the
[commands guide](/tutorial/Archipelago/commands/en). To send a command, open chat, and enter `/ap`,
followed by the command you want to send. For example, `/ap !help`.