diff --git a/worlds/tunic/er_data.py b/worlds/tunic/er_data.py index e999026d..6316292e 100644 --- a/worlds/tunic/er_data.py +++ b/worlds/tunic/er_data.py @@ -75,7 +75,7 @@ portal_mapping: List[Portal] = [ destination="Town Basement", tag="_beach"), Portal(name="Changing Room Entrance", region="Overworld", destination="Changing Room", tag="_"), - Portal(name="Cube Cave Entrance", region="Overworld", + Portal(name="Cube Cave Entrance", region="Cube Cave Entrance Region", destination="CubeRoom", tag="_"), Portal(name="Stairs from Overworld to Mountain", region="Upper Overworld", destination="Mountain", tag="_"), @@ -562,6 +562,7 @@ tunic_er_regions: Dict[str, RegionInfo] = { "Overworld Temple Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal "Overworld Town Portal": RegionInfo("Overworld Redux"), # being able to go to or come from the portal "Overworld Spawn Portal": RegionInfo("Overworld Redux"), # being able to go to or come from the portal + "Cube Cave Entrance Region": RegionInfo("Overworld Redux"), # other side of the bomb wall "Stick House": RegionInfo("Sword Cave", dead_end=DeadEnd.all_cats), "Windmill": RegionInfo("Windmill"), "Old House Back": RegionInfo("Overworld Interiors"), # part with the hc door @@ -775,6 +776,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = { [["UR"]], "Overworld Old House Door": [], + "Cube Cave Entrance Region": + [], }, "East Overworld": { "Above Ruined Passage": @@ -920,6 +923,10 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = { "Overworld": [], }, + "Cube Cave Entrance Region": { + "Overworld": + [], + }, "Old House Front": { "Old House Back": [], diff --git a/worlds/tunic/er_rules.py b/worlds/tunic/er_rules.py index a54ea23b..3d1973be 100644 --- a/worlds/tunic/er_rules.py +++ b/worlds/tunic/er_rules.py @@ -1,6 +1,7 @@ from typing import Dict, Set, List, Tuple, TYPE_CHECKING from worlds.generic.Rules import set_rule, forbid_item -from .rules import has_ability, has_sword, has_stick, has_ice_grapple_logic, has_lantern, has_mask, can_ladder_storage +from .rules import (has_ability, has_sword, has_stick, has_ice_grapple_logic, has_lantern, has_mask, can_ladder_storage, + bomb_walls) from .er_data import Portal from BaseClasses import Region, CollectionState @@ -11,6 +12,7 @@ laurels = "Hero's Laurels" grapple = "Magic Orb" ice_dagger = "Magic Dagger" fire_wand = "Magic Wand" +gun = "Gun" lantern = "Lantern" fairies = "Fairy" coins = "Golden Coin" @@ -31,6 +33,10 @@ def has_ladder(ladder: str, state: CollectionState, world: "TunicWorld") -> bool return not world.options.shuffle_ladders or state.has(ladder, world.player) +def can_shop(state: CollectionState, world: "TunicWorld") -> bool: + return has_sword(state, world.player) and state.can_reach_region("Shop", world.player) + + def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_pairs: Dict[Portal, Portal]) -> None: player = world.player options = world.options @@ -217,12 +223,12 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_ regions["Overworld"].connect( connecting_region=regions["Overworld after Envoy"], - rule=lambda state: state.has_any({laurels, grapple}, player) + rule=lambda state: state.has_any({laurels, grapple, gun}, player) or state.has("Sword Upgrade", player, 4) or options.logic_rules) regions["Overworld after Envoy"].connect( connecting_region=regions["Overworld"], - rule=lambda state: state.has_any({laurels, grapple}, player) + rule=lambda state: state.has_any({laurels, grapple, gun}, player) or state.has("Sword Upgrade", player, 4) or options.logic_rules) @@ -329,6 +335,12 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_ connecting_region=regions["Overworld"], rule=lambda state: state.has_any({grapple, laurels}, player)) + regions["Overworld"].connect( + connecting_region=regions["Cube Cave Entrance Region"], + rule=lambda state: state.has(gun, player) or can_shop(state, world)) + regions["Cube Cave Entrance Region"].connect( + connecting_region=regions["Overworld"]) + # Overworld side areas regions["Old House Front"].connect( connecting_region=regions["Old House Back"]) @@ -1527,6 +1539,10 @@ def set_er_location_rules(world: "TunicWorld") -> None: set_rule(world.get_location("Library Fuse"), lambda state: has_ability(prayer, state, world)) + # Bombable Walls + for location_name in bomb_walls: + set_rule(world.get_location(location_name), lambda state: state.has(gun, player) or can_shop(state, world)) + # Shop set_rule(world.get_location("Shop - Potion 1"), lambda state: has_sword(state, player)) diff --git a/worlds/tunic/items.py b/worlds/tunic/items.py index a8aec9f7..3e7f2c1a 100644 --- a/worlds/tunic/items.py +++ b/worlds/tunic/items.py @@ -43,7 +43,7 @@ item_table: Dict[str, TunicItemData] = { "Magic Orb": TunicItemData(ItemClassification.progression, 1, 27), "Hero's Laurels": TunicItemData(ItemClassification.progression, 1, 28), "Lantern": TunicItemData(ItemClassification.progression, 1, 29), - "Gun": TunicItemData(ItemClassification.useful, 1, 30, "Weapons"), + "Gun": TunicItemData(ItemClassification.progression, 1, 30, "Weapons"), "Shield": TunicItemData(ItemClassification.useful, 1, 31), "Dath Stone": TunicItemData(ItemClassification.useful, 1, 32), "Hourglass": TunicItemData(ItemClassification.useful, 1, 33), diff --git a/worlds/tunic/rules.py b/worlds/tunic/rules.py index 2ff588da..68756869 100644 --- a/worlds/tunic/rules.py +++ b/worlds/tunic/rules.py @@ -1,7 +1,7 @@ from random import Random from typing import Dict, TYPE_CHECKING -from worlds.generic.Rules import set_rule, forbid_item +from worlds.generic.Rules import set_rule, forbid_item, add_rule from BaseClasses import CollectionState from .options import TunicOptions if TYPE_CHECKING: @@ -11,6 +11,7 @@ laurels = "Hero's Laurels" grapple = "Magic Orb" ice_dagger = "Magic Dagger" fire_wand = "Magic Wand" +gun = "Gun" lantern = "Lantern" fairies = "Fairy" coins = "Golden Coin" @@ -26,6 +27,11 @@ green_hexagon = "Green Questagon" blue_hexagon = "Blue Questagon" gold_hexagon = "Gold Questagon" +bomb_walls = ["East Forest - Bombable Wall", "Eastern Vault Fortress - [East Wing] Bombable Wall", + "Overworld - [Central] Bombable Wall", "Overworld - [Southwest] Bombable Wall Near Fountain", + "Quarry - [West] Upper Area Bombable Wall", "Quarry - [East] Bombable Wall", + "Ruined Atoll - [Northwest] Bombable Wall"] + def randomize_ability_unlocks(random: Random, options: TunicOptions) -> Dict[str, int]: ability_requirement = [1, 1, 1] @@ -110,7 +116,7 @@ def set_region_rules(world: "TunicWorld") -> None: lambda state: state.has_any({grapple, laurels}, player) and has_ability(prayer, state, world) world.get_entrance("Overworld -> Quarry").access_rule = \ lambda state: (has_sword(state, player) or state.has(fire_wand, player)) \ - and (state.has_any({grapple, laurels}, player) or can_ladder_storage(state, world)) + and (state.has_any({grapple, laurels, gun}, player) or can_ladder_storage(state, world)) world.get_entrance("Quarry Back -> Quarry").access_rule = \ lambda state: has_sword(state, player) or state.has(fire_wand, player) world.get_entrance("Quarry -> Lower Quarry").access_rule = \ @@ -326,6 +332,13 @@ def set_location_rules(world: "TunicWorld") -> None: set_rule(world.get_location("Hero's Grave - Feathers Relic"), lambda state: state.has(laurels, player) and has_ability(prayer, state, world)) + # Bombable Walls + for location_name in bomb_walls: + # has_sword is there because you can buy bombs in the shop + set_rule(world.get_location(location_name), lambda state: state.has(gun, player) or has_sword(state, player)) + add_rule(world.get_location("Cube Cave - Holy Cross Chest"), + lambda state: state.has(gun, player) or has_sword(state, player)) + # Shop set_rule(world.get_location("Shop - Potion 1"), lambda state: has_sword(state, player))