Archipelago/worlds/osrs/Rules.py

338 lines
19 KiB
Python

"""
Ensures a target level can be reached with available resources
"""
from worlds.generic.Rules import CollectionRule, add_rule
from .Names import RegionNames, ItemNames
def get_fishing_skill_rule(level, player, options) -> CollectionRule:
if options.max_fishing_level < level:
return lambda state: False
if options.brutal_grinds or level < 5:
return lambda state: state.can_reach_region(RegionNames.Shrimp, player)
if level < 20:
return lambda state: state.can_reach_region(RegionNames.Shrimp, player) and \
state.can_reach_region(RegionNames.Port_Sarim, player)
else:
return lambda state: state.can_reach_region(RegionNames.Shrimp, player) and \
state.can_reach_region(RegionNames.Port_Sarim, player) and \
state.can_reach_region(RegionNames.Fly_Fish, player)
def get_mining_skill_rule(level, player, options) -> CollectionRule:
if options.max_mining_level < level:
return lambda state: False
if options.brutal_grinds or level < 15:
return lambda state: state.can_reach_region(RegionNames.Bronze_Ores, player) or \
state.can_reach_region(RegionNames.Clay_Rock, player)
else:
# Iron is the best way to train all the way to 99, so having access to iron is all you need to check for
return lambda state: (state.can_reach_region(RegionNames.Bronze_Ores, player) or
state.can_reach_region(RegionNames.Clay_Rock, player)) and \
state.can_reach_region(RegionNames.Iron_Rock, player)
def get_woodcutting_skill_rule(level, player, options) -> CollectionRule:
if options.max_woodcutting_level < level:
return lambda state: False
if options.brutal_grinds or level < 15:
# I've checked. There is not a single chunk in the f2p that does not have at least one normal tree.
# Even the desert.
return lambda state: True
if level < 30:
return lambda state: state.can_reach_region(RegionNames.Oak_Tree, player)
else:
return lambda state: state.can_reach_region(RegionNames.Oak_Tree, player) and \
state.can_reach_region(RegionNames.Willow_Tree, player)
def get_smithing_skill_rule(level, player, options) -> CollectionRule:
if options.max_smithing_level < level:
return lambda state: False
if options.brutal_grinds:
return lambda state: state.can_reach_region(RegionNames.Bronze_Ores, player) and \
state.can_reach_region(RegionNames.Furnace, player)
if level < 15:
# Lumbridge has a special bronze-only anvil. This is the only anvil of its type so it's not included
# in the "Anvil" resource region. We still need to check for it though.
return lambda state: state.can_reach_region(RegionNames.Bronze_Ores, player) and \
state.can_reach_region(RegionNames.Furnace, player) and \
(state.can_reach_region(RegionNames.Anvil, player) or
state.can_reach_region(RegionNames.Lumbridge, player))
if level < 30:
# For levels between 15 and 30, the lumbridge anvil won't cut it. Only a real one will do
return lambda state: state.can_reach_region(RegionNames.Bronze_Ores, player) and \
state.can_reach_region(RegionNames.Iron_Rock, player) and \
state.can_reach_region(RegionNames.Furnace, player) and \
state.can_reach_region(RegionNames.Anvil, player)
else:
return lambda state: state.can_reach_region(RegionNames.Bronze_Ores, player) and \
state.can_reach_region(RegionNames.Iron_Rock, player) and \
state.can_reach_region(RegionNames.Coal_Rock, player) and \
state.can_reach_region(RegionNames.Furnace, player) and \
state.can_reach_region(RegionNames.Anvil, player)
def get_crafting_skill_rule(level, player, options):
if options.max_crafting_level < level:
return lambda state: False
# Crafting is really complex. Need a lot of sub-rules to make this even remotely readable
def can_spin(state):
return state.can_reach_region(RegionNames.Sheep, player) and \
state.can_reach_region(RegionNames.Spinning_Wheel, player)
def can_pot(state):
return state.can_reach_region(RegionNames.Clay_Rock, player) and \
state.can_reach_region(RegionNames.Barbarian_Village, player)
def can_tan(state):
return state.can_reach_region(RegionNames.Milk, player) and \
state.can_reach_region(RegionNames.Al_Kharid, player)
def mould_access(state):
return state.can_reach_region(RegionNames.Al_Kharid, player) or \
state.can_reach_region(RegionNames.Rimmington, player)
def can_silver(state):
return state.can_reach_region(RegionNames.Silver_Rock, player) and \
state.can_reach_region(RegionNames.Furnace, player) and mould_access(state)
def can_gold(state):
return state.can_reach_region(RegionNames.Gold_Rock, player) and \
state.can_reach_region(RegionNames.Furnace, player) and mould_access(state)
if options.brutal_grinds or level < 5:
return lambda state: can_spin(state) or can_pot(state) or can_tan(state)
can_smelt_gold = get_smithing_skill_rule(40, player, options)
can_smelt_silver = get_smithing_skill_rule(20, player, options)
if level < 16:
return lambda state: can_pot(state) or can_tan(state) or (can_gold(state) and can_smelt_gold(state))
else:
return lambda state: can_tan(state) or (can_silver(state) and can_smelt_silver(state)) or \
(can_gold(state) and can_smelt_gold(state))
def get_cooking_skill_rule(level, player, options) -> CollectionRule:
if options.max_cooking_level < level:
return lambda state: False
if options.brutal_grinds or level < 15:
return lambda state: state.can_reach_region(RegionNames.Milk, player) or \
state.can_reach_region(RegionNames.Egg, player) or \
state.can_reach_region(RegionNames.Shrimp, player) or \
(state.can_reach_region(RegionNames.Wheat, player) and
state.can_reach_region(RegionNames.Windmill, player))
else:
can_catch_fly_fish = get_fishing_skill_rule(20, player, options)
return lambda state: (
(state.can_reach_region(RegionNames.Fly_Fish, player) and can_catch_fly_fish(state)) or
(state.can_reach_region(RegionNames.Port_Sarim, player))
) and (
state.can_reach_region(RegionNames.Milk, player) or
state.can_reach_region(RegionNames.Egg, player) or
state.can_reach_region(RegionNames.Shrimp, player) or
(state.can_reach_region(RegionNames.Wheat, player) and
state.can_reach_region(RegionNames.Windmill, player))
)
def get_runecraft_skill_rule(level, player, options) -> CollectionRule:
if options.max_runecraft_level < level:
return lambda state: False
if not options.brutal_grinds:
# Ensure access to the relevant altars
if level >= 5:
return lambda state: state.has(ItemNames.QP_Rune_Mysteries, player) and \
state.can_reach_region(RegionNames.Falador_Farm, player) and \
state.can_reach_region(RegionNames.Lumbridge_Swamp, player)
if level >= 9:
return lambda state: state.has(ItemNames.QP_Rune_Mysteries, player) and \
state.can_reach_region(RegionNames.Falador_Farm, player) and \
state.can_reach_region(RegionNames.Lumbridge_Swamp, player) and \
state.can_reach_region(RegionNames.East_Of_Varrock, player)
if level >= 14:
return lambda state: state.has(ItemNames.QP_Rune_Mysteries, player) and \
state.can_reach_region(RegionNames.Falador_Farm, player) and \
state.can_reach_region(RegionNames.Lumbridge_Swamp, player) and \
state.can_reach_region(RegionNames.East_Of_Varrock, player) and \
state.can_reach_region(RegionNames.Al_Kharid, player)
return lambda state: state.has(ItemNames.QP_Rune_Mysteries, player) and \
state.can_reach_region(RegionNames.Falador_Farm, player)
def get_magic_skill_rule(level, player, options) -> CollectionRule:
if options.max_magic_level < level:
return lambda state: False
return lambda state: state.can_reach_region(RegionNames.Mind_Runes, player)
def get_firemaking_skill_rule(level, player, options) -> CollectionRule:
if options.max_firemaking_level < level:
return lambda state: False
if not options.brutal_grinds:
if level >= 30:
can_chop_willows = get_woodcutting_skill_rule(30, player, options)
return lambda state: state.can_reach_region(RegionNames.Willow_Tree, player) and can_chop_willows(state)
if level >= 15:
can_chop_oaks = get_woodcutting_skill_rule(15, player, options)
return lambda state: state.can_reach_region(RegionNames.Oak_Tree, player) and can_chop_oaks(state)
# If brutal grinds are on, or if the level is less than 15, you can train it.
return lambda state: True
def get_skill_rule(skill, level, player, options) -> CollectionRule:
if skill.lower() == "fishing":
return get_fishing_skill_rule(level, player, options)
if skill.lower() == "mining":
return get_mining_skill_rule(level, player, options)
if skill.lower() == "woodcutting":
return get_woodcutting_skill_rule(level, player, options)
if skill.lower() == "smithing":
return get_smithing_skill_rule(level, player, options)
if skill.lower() == "crafting":
return get_crafting_skill_rule(level, player, options)
if skill.lower() == "cooking":
return get_cooking_skill_rule(level, player, options)
if skill.lower() == "runecraft":
return get_runecraft_skill_rule(level, player, options)
if skill.lower() == "magic":
return get_magic_skill_rule(level, player, options)
if skill.lower() == "firemaking":
return get_firemaking_skill_rule(level, player, options)
return lambda state: True
def generate_special_rules_for(entrance, region_row, outbound_region_name, player, options):
if outbound_region_name == RegionNames.Cooks_Guild:
add_rule(entrance, get_cooking_skill_rule(32, player, options))
elif outbound_region_name == RegionNames.Crafting_Guild:
add_rule(entrance, get_crafting_skill_rule(40, player, options))
elif outbound_region_name == RegionNames.Corsair_Cove:
# Need to be able to start Corsair Curse in addition to having the item
add_rule(entrance, lambda state: state.can_reach(RegionNames.Falador_Farm, "Region", player))
elif outbound_region_name == "Camdozaal*":
add_rule(entrance, lambda state: state.has(ItemNames.QP_Below_Ice_Mountain, player))
elif region_row.name == "Dwarven Mountain Pass" and outbound_region_name == "Anvil*":
add_rule(entrance, lambda state: state.has(ItemNames.QP_Dorics_Quest, player))
# Special logic for canoes
canoe_regions = [RegionNames.Lumbridge, RegionNames.South_Of_Varrock, RegionNames.Barbarian_Village,
RegionNames.Edgeville, RegionNames.Wilderness]
if region_row.name in canoe_regions:
# Skill rules for greater distances
woodcutting_rule_d1 = get_woodcutting_skill_rule(12, player, options)
woodcutting_rule_d2 = get_woodcutting_skill_rule(27, player, options)
woodcutting_rule_d3 = get_woodcutting_skill_rule(42, player, options)
woodcutting_rule_all = get_woodcutting_skill_rule(57, player, options)
if region_row.name == RegionNames.Lumbridge:
# Canoe Tree access for the Location
if outbound_region_name == RegionNames.Canoe_Tree:
add_rule(entrance,
lambda state: (state.can_reach_region(RegionNames.South_Of_Varrock, player)
and woodcutting_rule_d1(state)) or
(state.can_reach_region(RegionNames.Barbarian_Village, player)
and woodcutting_rule_d2(state)) or
(state.can_reach_region(RegionNames.Edgeville, player)
and woodcutting_rule_d3(state)) or
(state.can_reach_region(RegionNames.Wilderness, player)
and woodcutting_rule_all(state)))
# Access to other chunks based on woodcutting settings
elif outbound_region_name == RegionNames.South_Of_Varrock:
add_rule(entrance, woodcutting_rule_d1)
elif outbound_region_name == RegionNames.Barbarian_Village:
add_rule(entrance, woodcutting_rule_d2)
elif outbound_region_name == RegionNames.Edgeville:
add_rule(entrance, woodcutting_rule_d3)
elif outbound_region_name == RegionNames.Wilderness:
add_rule(entrance, woodcutting_rule_all)
elif region_row.name == RegionNames.South_Of_Varrock:
if outbound_region_name == RegionNames.Canoe_Tree:
add_rule(entrance,
lambda state: (state.can_reach_region(RegionNames.Lumbridge, player)
and woodcutting_rule_d1(state)) or
(state.can_reach_region(RegionNames.Barbarian_Village, player)
and woodcutting_rule_d1(state)) or
(state.can_reach_region(RegionNames.Edgeville, player)
and woodcutting_rule_d2(state)) or
(state.can_reach_region(RegionNames.Wilderness, player)
and woodcutting_rule_d3(state)))
# Access to other chunks based on woodcutting settings
elif outbound_region_name == RegionNames.Lumbridge:
add_rule(entrance, woodcutting_rule_d1)
elif outbound_region_name == RegionNames.Barbarian_Village:
add_rule(entrance, woodcutting_rule_d1)
elif outbound_region_name == RegionNames.Edgeville:
add_rule(entrance, woodcutting_rule_d3)
elif outbound_region_name == RegionNames.Wilderness:
add_rule(entrance, woodcutting_rule_all)
elif region_row.name == RegionNames.Barbarian_Village:
if outbound_region_name == RegionNames.Canoe_Tree:
add_rule(entrance,
lambda state: (state.can_reach_region(RegionNames.Lumbridge, player)
and woodcutting_rule_d2(state)) or (state.can_reach_region(RegionNames.South_Of_Varrock, player)
and woodcutting_rule_d1(state)) or (state.can_reach_region(RegionNames.Edgeville, player)
and woodcutting_rule_d1(state)) or (state.can_reach_region(RegionNames.Wilderness, player)
and woodcutting_rule_d2(state)))
# Access to other chunks based on woodcutting settings
elif outbound_region_name == RegionNames.Lumbridge:
add_rule(entrance, woodcutting_rule_d2)
elif outbound_region_name == RegionNames.South_Of_Varrock:
add_rule(entrance, woodcutting_rule_d1)
# Edgeville does not need to be checked, because it's already adjacent
elif outbound_region_name == RegionNames.Wilderness:
add_rule(entrance, woodcutting_rule_d3)
elif region_row.name == RegionNames.Edgeville:
if outbound_region_name == RegionNames.Canoe_Tree:
add_rule(entrance,
lambda state: (state.can_reach_region(RegionNames.Lumbridge, player)
and woodcutting_rule_d3(state)) or
(state.can_reach_region(RegionNames.South_Of_Varrock, player)
and woodcutting_rule_d2(state)) or
(state.can_reach_region(RegionNames.Barbarian_Village, player)
and woodcutting_rule_d1(state)) or
(state.can_reach_region(RegionNames.Wilderness, player)
and woodcutting_rule_d1(state)))
# Access to other chunks based on woodcutting settings
elif outbound_region_name == RegionNames.Lumbridge:
add_rule(entrance, woodcutting_rule_d3)
elif outbound_region_name == RegionNames.South_Of_Varrock:
add_rule(entrance, woodcutting_rule_d2)
# Barbarian Village does not need to be checked, because it's already adjacent
# Wilderness does not need to be checked, because it's already adjacent
elif region_row.name == RegionNames.Wilderness:
if outbound_region_name == RegionNames.Canoe_Tree:
add_rule(entrance,
lambda state: (state.can_reach_region(RegionNames.Lumbridge, player)
and woodcutting_rule_all(state)) or
(state.can_reach_region(RegionNames.South_Of_Varrock, player)
and woodcutting_rule_d3(state)) or
(state.can_reach_region(RegionNames.Barbarian_Village, player)
and woodcutting_rule_d2(state)) or
(state.can_reach_region(RegionNames.Edgeville, player)
and woodcutting_rule_d1(state)))
# Access to other chunks based on woodcutting settings
elif outbound_region_name == RegionNames.Lumbridge:
add_rule(entrance, woodcutting_rule_all)
elif outbound_region_name == RegionNames.South_Of_Varrock:
add_rule(entrance, woodcutting_rule_d3)
elif outbound_region_name == RegionNames.Barbarian_Village:
add_rule(entrance, woodcutting_rule_d2)
# Edgeville does not need to be checked, because it's already adjacent