diff --git a/worlds/tunic/combat_logic.py b/worlds/tunic/combat_logic.py index 5dc2562067..1d7956a0f2 100644 --- a/worlds/tunic/combat_logic.py +++ b/worlds/tunic/combat_logic.py @@ -19,8 +19,8 @@ class EncounterData(NamedTuple): # general requirements for enemy encounters in the game, for use in determining whether you can access a chest enemy_encounters: Dict[str, EncounterData] = { - # pink slimes are basically free - "Blue Slimes": EncounterData(0), + # pink and blue slimes really only need a stick + "Slimes": EncounterData(0), "Rudelings": EncounterData(4), "Shield Rudelings": EncounterData(6), # just gets stunlocked with a stick @@ -68,7 +68,8 @@ def has_combat_logic(encounters: List[str], state: CollectionState, player: int) return False # if you met the item requirements, and you have enough power, then you may proceed level_req: int = max(data.power_required for data in encounter_data) - if sum_power(state, player) >= level_req: + # the not level_req is to short circuit early for spots with combat level 0 + if not level_req or sum_power(state, player) >= level_req: return True diff --git a/worlds/tunic/er_data.py b/worlds/tunic/er_data.py index b3e2877429..65d3089304 100644 --- a/worlds/tunic/er_data.py +++ b/worlds/tunic/er_data.py @@ -522,7 +522,6 @@ class RegionInfo(NamedTuple): dead_end: int = 0 # if a region has only one exit -# todo: make this work with the new options class DeadEnd(IntEnum): free = 0 # not a dead end all_cats = 1 # dead end in every logic category @@ -555,6 +554,8 @@ tunic_er_regions: Dict[str, RegionInfo] = { "Overworld to West Garden Upper": RegionInfo("Overworld Redux"), # usually leads to garden knight "Overworld to West Garden from Furnace": RegionInfo("Overworld Redux"), # isolated stairway with one chest "Overworld Well Ladder": RegionInfo("Overworld Redux"), # just the ladder entrance itself as a region + "Overworld Well Entry Area": RegionInfo("Overworld Redux"), # the page, the bridge, etc. + "Overworld Tunnel to Beach": RegionInfo("Overworld Redux"), # the tunnel with the chest "Overworld Beach": RegionInfo("Overworld Redux"), # from the two turrets to invisble maze, and lower atoll entry "Overworld Tunnel Turret": RegionInfo("Overworld Redux"), # the tunnel turret by the southwest beach ladder "Overworld to Atoll Upper": RegionInfo("Overworld Redux"), # the little ledge before the ladder @@ -745,6 +746,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = { "Overworld": { "Overworld Beach": [], + "Overworld Tunnel to Beach": + [], "Overworld to Atoll Upper": [["Hyperdash"]], "Overworld Belltower": @@ -755,7 +758,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = { [], "Overworld Special Shop Entry": [["Hyperdash"], ["LS1"]], - "Overworld Well Ladder": + "Overworld Well Entry Area": [], "Overworld Ruined Passage Door": [], @@ -828,6 +831,12 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = { # "Overworld": # [], # }, + "Overworld Tunnel to Beach": { + # "Overworld": + # [], + "Overworld Beach": + [], + }, "Overworld Beach": { # "Overworld": # [], @@ -854,9 +863,15 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = { "Overworld Beach": [], }, - "Overworld Well Ladder": { + "Overworld Well Entry Area": { # "Overworld": # [], + "Overworld Well Ladder": + [], + }, + "Overworld Well Ladder": { + "Overworld Well Entry Area": + [], }, "Overworld at Patrol Cave": { "East Overworld": diff --git a/worlds/tunic/er_rules.py b/worlds/tunic/er_rules.py index 5099f0a585..b66149ca67 100644 --- a/worlds/tunic/er_rules.py +++ b/worlds/tunic/er_rules.py @@ -52,10 +52,18 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_ connecting_region=regions["Overworld Beach"], rule=lambda state: has_ladder("Ladders in Overworld Town", state, world) or state.has_any({laurels, grapple}, player)) + # regions["Overworld Beach"].connect( + # connecting_region=regions["Overworld"], + # rule=lambda state: has_ladder("Ladders in Overworld Town", state, world) + # or state.has_any({laurels, grapple}, player)) + + # region for combat logic, no need to connect it to beach since it would be the same as the ow -> beach cxn + ow_tunnel_beach = regions["Overworld"].connect( + connecting_region=regions["Overworld Tunnel to Beach"]) + regions["Overworld Beach"].connect( - connecting_region=regions["Overworld"], - rule=lambda state: has_ladder("Ladders in Overworld Town", state, world) - or state.has_any({laurels, grapple}, player)) + connecting_region=regions["Overworld Tunnel to Beach"], + rule=lambda state: state.has(laurels, player) or has_ladder("Ladders in Overworld Town", state, world)) regions["Overworld Beach"].connect( connecting_region=regions["Overworld West Garden Laurels Entry"], @@ -268,11 +276,17 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_ connecting_region=regions["East Overworld"], rule=lambda state: state.has(laurels, player)) - regions["Overworld"].connect( + # region made for combat logic + ow_to_well_entry = regions["Overworld"].connect( + connecting_region=regions["Overworld Well Entry Area"]) + regions["Overworld Well Entry Area"].connect( + connecting_region=regions["Overworld"]) + + regions["Overworld Well Entry Area"].connect( connecting_region=regions["Overworld Well Ladder"], rule=lambda state: has_ladder("Ladders in Well", state, world)) regions["Overworld Well Ladder"].connect( - connecting_region=regions["Overworld"], + connecting_region=regions["Overworld Well Entry Area"], rule=lambda state: has_ladder("Ladders in Well", state, world)) # nmg: can ice grapple through the door @@ -503,7 +517,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_ regions["West Garden after Boss"].connect( connecting_region=regions["West Garden"], rule=lambda state: state.has(laurels, player)) - regions["West Garden"].connect( + wg_to_after_gk = regions["West Garden"].connect( connecting_region=regions["West Garden after Boss"], rule=lambda state: state.has(laurels, player) or has_sword(state, player)) @@ -1003,7 +1017,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_ connecting_region=regions["Far Shore"]) # Misc - regions["Spirit Arena"].connect( + heir_fight = regions["Spirit Arena"].connect( connecting_region=regions["Spirit Arena Victory"], rule=lambda state: (state.has(gold_hexagon, player, world.options.hexagon_goal.value) if world.options.hexagon_quest else @@ -1142,6 +1156,23 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_ for region in ladder_regions.values(): world.multiworld.regions.append(region) + # for combat logic, easiest to replace or add to existing rules + if world.options.combat_logic >= CombatLogic.option_bosses_only: + set_rule(wg_to_after_gk, + lambda state: state.has(laurels, player) + or has_combat_logic(["Garden Knight"], state, player)) + if not world.options.hexagon_quest: + add_rule(heir_fight, + lambda state: has_combat_logic(["The Heir"], state, player)) + + if world.options.combat_logic == CombatLogic.option_on: + # need to fight through the rudelings and turret, or just laurels from near the windmill + set_rule(ow_to_well_entry, + lambda state: state.has(laurels, player) + or has_combat_logic(["Shield Rudelings", "Autobolts"], state, player)) + set_rule(ow_tunnel_beach, + lambda state: has_combat_logic(["Shield Rudelings"], state, player)) + def set_er_location_rules(world: "TunicWorld") -> None: player = world.player @@ -1364,9 +1395,7 @@ def set_er_location_rules(world: "TunicWorld") -> None: lambda state: has_sword(state, player)) if world.options.combat_logic >= CombatLogic.option_bosses_only: - set_rule(multiworld.get_entrance("West Garden -> West Garden after Boss", player), - lambda state: state.has(laurels, player) - or has_combat_logic(["Garden Knight"], state, player)) + # garden knight is in the regions part above set_rule(multiworld.get_location("Fortress Arena - Siege Engine/Vault Key Pickup", player), lambda state: has_combat_logic(["Siege Engine"], state, player)) set_rule(multiworld.get_location("Librarian - Hexagon Green", player), @@ -1375,6 +1404,19 @@ def set_er_location_rules(world: "TunicWorld") -> None: lambda state: has_combat_logic(["Boss Scavenger"], state, player)) set_rule(multiworld.get_location("Cathedral Gauntlet - Gauntlet Reward", player), lambda state: has_combat_logic(["Gauntlet"], state, player)) - if not world.options.hexagon_quest: - add_rule(multiworld.get_entrance("Spirit Arena -> Spirit Arena Victory", player), - lambda state: has_combat_logic(["The Heir"], state, player)) + + if world.options.combat_logic == CombatLogic.option_on: + set_rule(multiworld.get_location("Overworld - [East] Between Ladders Near Ruined Passage", player), + lambda state: has_combat_logic(["Slimes"], state, player)) + set_rule(multiworld.get_location("Overworld - [East] Chest Near Pots", player), + lambda state: has_combat_logic(["Slimes"], state, player)) + add_rule(multiworld.get_location("Overworld - [Northeast] Flowers Holy Cross", player), + lambda state: has_combat_logic(["Slimes"], state, player)) + set_rule(multiworld.get_location("Overworld - [Northwest] Chest Near Quarry Gate", player), + lambda state: has_combat_logic(["Rudelings"], state, player)) + set_rule(multiworld.get_location("Overworld - [Northeast] Chest Above Patrol Cave", player), + lambda state: has_combat_logic(["Shield Rudelings"], state, player)) + set_rule(multiworld.get_location("Overworld - [Southwest] West Beach Guarded By Turret", player), + lambda state: has_combat_logic(["Autobolts"], state, player)) + add_rule(multiworld.get_location("Overworld - [Southwest] West Beach Guarded By Turret 2", player), + lambda state: has_combat_logic(["Autobolts"], state, player)) diff --git a/worlds/tunic/locations.py b/worlds/tunic/locations.py index 79c39d72a1..9e0b8e52f5 100644 --- a/worlds/tunic/locations.py +++ b/worlds/tunic/locations.py @@ -131,7 +131,7 @@ location_table: Dict[str, TunicLocationData] = { "Overworld - [Southwest] West Beach Guarded By Turret": TunicLocationData("Overworld", "Overworld Beach"), "Overworld - [Southwest] Chest Guarded By Turret": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Shadowy Corner Chest": TunicLocationData("Overworld", "Overworld"), - "Overworld - [Southwest] Obscured In Tunnel To Beach": TunicLocationData("Overworld", "Overworld"), + "Overworld - [Southwest] Obscured In Tunnel To Beach": TunicLocationData("Overworld", "Overworld Tunnel to Beach"), "Overworld - [Southwest] Grapple Chest Over Walkway": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Chest Beneath Quarry Gate": TunicLocationData("Overworld", "Overworld after Envoy"), "Overworld - [Southeast] Chest Near Swamp": TunicLocationData("Overworld", "Overworld Swamp Lower Entry"), @@ -158,7 +158,7 @@ location_table: Dict[str, TunicLocationData] = { "Overworld - [Northwest] Page on Pillar by Dark Tomb": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Fire Wand Pickup": TunicLocationData("Overworld", "Upper Overworld"), "Overworld - [West] Page On Teleporter": TunicLocationData("Overworld", "Overworld"), - "Overworld - [Northwest] Page By Well": TunicLocationData("Overworld", "Overworld"), + "Overworld - [Northwest] Page By Well": TunicLocationData("Overworld", "Overworld Well Entry Area"), "Patrol Cave - Normal Chest": TunicLocationData("Overworld", "Patrol Cave"), "Ruined Shop - Chest 1": TunicLocationData("Overworld", "Ruined Shop"), "Ruined Shop - Chest 2": TunicLocationData("Overworld", "Ruined Shop"),