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:
parent
f318ca8886
commit
83387da6a4
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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,
|
||||
}
|
|
@ -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;
|
|
@ -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
|
|
@ -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]),
|
||||
}
|
|
@ -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.
|
|
@ -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`.
|
Loading…
Reference in New Issue