338 lines
19 KiB
Python
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
|