diff --git a/locations/locations.json b/locations/locations.json index d0c915a..d05a670 100644 --- a/locations/locations.json +++ b/locations/locations.json @@ -4,7 +4,10 @@ "children": [ { "name": "Dream Breaker", - "access_rules": [], + "access_rules": [[ + "$can_glitch|Dilapidated Dungeon - Dream Breaker", + "[$can_reach|Dilapidated Dungeon - Dream Breaker]" + ]], "sections": [ { "item_count": 1, @@ -22,7 +25,10 @@ }, { "name": "Slide", - "access_rules": ["$breaker"], + "access_rules": [[ + "$can_glitch|Dilapidated Dungeon - Slide", + "[$can_reach|Dilapidated Dungeon - Slide]" + ]], "sections": [ { "item_count": 1, @@ -40,7 +46,10 @@ }, { "name": "Dark Orbs", - "access_rules": ["$dungeon_dark_orbs|true,[$dungeon_dark_orbs]"], + "access_rules": [[ + "$can_glitch|Dilapidated Dungeon - Dark Orbs", + "[$can_reach|Dilapidated Dungeon - Dark Orbs]" + ]], "sections": [ { "item_count": 1, @@ -58,11 +67,13 @@ }, { "name": "Rafters", - "access_rules": ["$dungeon_strong_eyes|true,[$dungeon_strong_eyes]"], + "access_rules": [[ + "$can_glitch|Dilapidated Dungeon - Rafters", + "[$can_reach|Dilapidated Dungeon - Rafters]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$dungeon_rafters|true,[$dungeon_rafters]"], "chest_opened_img": "images/items/Small_Key.png", "chest_unopened_img": "images/items/Small_Key_gray.png" } @@ -77,11 +88,13 @@ }, { "name": "Strong Eyes", - "access_rules": ["$dungeon_strong_eyes|true,[$dungeon_strong_eyes]"], + "access_rules": [[ + "$can_glitch|Dilapidated Dungeon - Strong Eyes", + "[$can_reach|Dilapidated Dungeon - Strong Eyes]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$strong_eyes_in_dungeon|true,[$strong_eyes_in_dungeon]"], "chest_opened_img": "images/items/Small_Key.png", "chest_unopened_img": "images/items/Small_Key_gray.png" } @@ -96,7 +109,10 @@ }, { "name": "Alcove Near Mirror", - "access_rules": ["$breaker"], + "access_rules": [[ + "$can_glitch|Dilapidated Dungeon - Alcove Near Mirror", + "[$can_reach|Dilapidated Dungeon - Alcove Near Mirror]" + ]], "sections": [ { "item_count": 1, @@ -114,11 +130,13 @@ }, { "name": "Past Poles", - "access_rules": ["$dungeon_strong_eyes|true,[$dungeon_strong_eyes]"], + "access_rules": [[ + "$can_glitch|Dilapidated Dungeon - Past Poles", + "[$can_reach|Dilapidated Dungeon - Past Poles]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$dungeon_past_poles|true,[$dungeon_past_poles]"], "chest_opened_img": "images/items/healthup.png", "chest_unopened_img": "images/items/healthup_gray.png" } @@ -135,13 +153,13 @@ }, { "name": "Castle Sansa", - "access_rules": [ - "$castle_sansa|true, [$castle_sansa]" - ], "children": [ { "name": "Indignation", - "access_rules": [], + "access_rules": [[ + "$can_glitch|Castle Sansa - Indignation", + "[$can_reach|Castle Sansa - Indignation]" + ]], "sections": [ { "item_count": 1, @@ -159,9 +177,10 @@ }, { "name": "Floater In Courtyard", - "access_rules": [ - "$Floater_in_courtyard|true, [$Floater_in_courtyard]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Floater In Courtyard", + "[$can_reach|Castle Sansa - Floater In Courtyard]" + ]], "sections": [ { "item_count": 1, @@ -179,9 +198,10 @@ }, { "name": "Locked Door", - "access_rules": [ - "[$has_small_keys],smallkey:1,$Castle_locked_door|true, [$Castle_locked_door]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Locked Door", + "[$can_reach|Castle Sansa - Locked Door]" + ]], "sections": [ { "item_count": 1, @@ -199,9 +219,10 @@ }, { "name": "Platform In Main Halls", - "access_rules": [ - "$Castle_platform_main|true, [$Castle_platform_main]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Platform In Main Halls", + "[$can_reach|Castle Sansa - Platform In Main Halls]" + ]], "sections": [ { "item_count": 1, @@ -219,9 +240,10 @@ }, { "name": "Tall Room Near Wheel Crawlers", - "access_rules": [ - "$Castle_tall_room_wheel_crawlers|true, [$Castle_tall_room_wheel_crawlers]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Tall Room Near Wheel Crawlers", + "[$can_reach|Castle Sansa - Tall Room Near Wheel Crawlers]" + ]], "sections": [ { "item_count": 1, @@ -239,9 +261,10 @@ }, { "name": "Alcove Near Dungeon", - "access_rules": [ - "$Castle_alcove_near_dungeon|true, [$Castle_alcove_near_dungeon]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Alcove Near Dungeon", + "[$can_reach|Castle Sansa - Alcove Near Dungeon]" + ]], "sections": [ { "item_count": 1, @@ -259,9 +282,10 @@ }, { "name": "Corner Corridor", - "access_rules": [ - "$Castle_corner_corridor|true, [$Castle_corner_corridor]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Corner Corridor", + "[$can_reach|Castle Sansa - Corner Corridor]" + ]], "sections": [ { "item_count": 1, @@ -279,9 +303,10 @@ }, { "name": "Wheel Crawlers", - "access_rules": [ - "$Castle_wheel_crawler|true, [$Castle_wheel_crawler]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Wheel Crawlers", + "[$can_reach|Castle Sansa - Wheel Crawlers]" + ]], "sections": [ { "item_count": 1, @@ -299,9 +324,10 @@ }, { "name": "Balcony", - "access_rules": [ - "$Castle_balcony|true, [$Castle_balcony]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Balcony", + "[$can_reach|Castle Sansa - Balcony]" + ]], "sections": [ { "item_count": 1, @@ -321,17 +347,16 @@ }, { "name": "Upper Castle Sansa", - "access_rules": [], "children": [ { "name": "High Climb From Courtyard", - "access_rules": [ - "$Castle_high_climb|true, [$Castle_high_climb]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - High Climb From Courtyard", + "[$can_reach|Castle Sansa - High Climb From Courtyard]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$Castle_courtyard_high_climb|true, [$Castle_courtyard_high_climb]"], "chest_opened_img": "images/items/aspect.png", "chest_unopened_img": "images/items/aspect_gray.png" } @@ -346,13 +371,13 @@ }, { "name": "Alcove Near Scythe Corridor", - "access_rules": [ - "$Scythe_corridor|true, [$Scythe_corridor|true]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Alcove Near Scythe Corridor", + "[$can_reach|Castle Sansa - Alcove Near Scythe Corridor]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$Castle_alcove_scythe|true, [$Castle_alcove_scythe]"], "chest_opened_img": "images/items/healthup.png", "chest_unopened_img": "images/items/healthup_gray.png" } @@ -367,13 +392,13 @@ }, { "name": "Near Theatre Front", - "access_rules": [ - "$Theatre_front|true, [$Theatre_front]" - ], + "access_rules": [[ + "$can_glitch|Castle Sansa - Near Theatre Front", + "[$can_reach|Castle Sansa - Near Theatre Front]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$Castle_theatre_front|true, [$Castle_theatre_front]"], "chest_opened_img": "images/items/aspect.png", "chest_unopened_img": "images/items/aspect_gray.png" } @@ -390,13 +415,13 @@ }, { "name": "Sansa Keep", - "access_rules": [], "children": [ { "name": "Sunsetter", - "access_rules": [ - "$keep_sunsetter,$breaker" - ], + "access_rules": [[ + "$can_glitch|Sansa Keep - Sunsetter", + "[$can_reach|Sansa Keep - Sunsetter]" + ]], "sections": [ { "item_count": 1, @@ -414,13 +439,10 @@ }, { "name": "Strikebreak", - "access_rules": [ - "$keep_main,$can_slidejump", - "$keep_main,$slide,$Getkicks|1", - "$keep_main,$slide,cling", - "$keep_main,$can_strikebreak,$Getkicks|1", - "$keep_main,$can_strikebreak,cling" - ], + "access_rules": [[ + "$can_glitch|Sansa Keep - Strikebreak", + "[$can_reach|Sansa Keep - Strikebreak]" + ]], "sections": [ { "item_count": 1, @@ -438,10 +460,10 @@ }, { "name": "Near Theatre", - "access_rules": [ - "$keep_main,$Kickorplunge|1", - "$keep_main,cling" - ], + "access_rules": [[ + "$can_glitch|Sansa Keep - Near Theatre", + "[$can_reach|Sansa Keep - Near Theatre]" + ]], "sections": [ { "item_count": 1, @@ -459,9 +481,10 @@ }, { "name": "Levers Room", - "access_rules": [ - "$keep_main,$breaker" - ], + "access_rules": [[ + "$can_glitch|Sansa Keep - Levers Room", + "[$can_reach|Sansa Keep - Levers Room]" + ]], "sections": [ { "item_count": 1, @@ -479,11 +502,10 @@ }, { "name": "Alcove Near Locked Door", - "access_rules": [ - "$keep_main,$can_slidejump", - "$keep_main,$Getkicks|3", - "$keep_main,sunsetter" - ], + "access_rules": [[ + "$can_glitch|Sansa Keep - Alcove Near Locked Door", + "[$can_reach|Sansa Keep - Alcove Near Locked Door]" + ]], "sections": [ { "item_count": 1, @@ -501,10 +523,10 @@ }, { "name": "Major Key", - "access_rules": [ - "$keep_main,cling,sunsetter,$can_bounce", - "$keep_main,cling,$can_bounce,$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|Sansa Keep - Lonely Throne", + "[$can_reach|Sansa Keep - Lonely Throne]" + ]], "sections": [ { "item_count": 1, @@ -524,18 +546,17 @@ }, { "name": "Listless Library", - "access_rules": [], "children": [ { "name": "Sun Greaves", - "access_rules": [ - "$library_greaves|true,[$library_greaves]" - ], + "access_rules": [[ + "$can_glitch|Listless Library - Sun Greaves", + "[$can_reach|Listless Library - Sun Greaves]" + ]], "visibility_rules": ["op_splitkick_off"], "sections": [ { "item_count": 1, - "access_rules": ["$library_sun_greaves|true,[$library_sun_greaves]"], "chest_opened_img": "images/items/ability.png", "chest_unopened_img": "images/items/ability_gray.png" } @@ -550,14 +571,15 @@ }, { "name": "Sun Greaves Split", - "access_rules": [ - "$library_greaves|true,[$library_greaves]" - ], + "access_rules": [[ + "$can_glitch|Listless Library - Sun Greaves", + "[$can_reach|Listless Library - Sun Greaves]" + + ]], "visibility_rules": ["op_splitkick_on"], "sections": [ { "item_count": 3, - "access_rules": ["$library_sun_greaves|true,[$library_sun_greaves]"], "chest_opened_img": "images/items/ability.png", "chest_unopened_img": "images/items/ability_gray.png" } @@ -572,12 +594,12 @@ }, { "name": "Locked Door Across", - "access_rules": [ - "$library_locked|true,[$library_locked]" - ], + "access_rules": [[ + "$can_glitch|Listless Library - Locked Door Across", + "[$can_reach|Listless Library - Locked Door Across]" + ]], "sections": [ { - "access_rules": ["$library_locked_across|true,[$library_locked_across]"], "item_count": 1 } ], @@ -591,13 +613,13 @@ }, { "name": "Locked Door Left", - "access_rules": [ - "$library_locked|true,[$library_locked]" - ], + "access_rules": [[ + "$can_glitch|Listless Library - Locked Door Left", + "[$can_reach|Listless Library - Locked Door Left]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$library_locked_left|true,[$library_locked_left]"], "chest_opened_img": "images/items/healthup.png", "chest_unopened_img": "images/items/healthup_gray.png" } @@ -612,13 +634,13 @@ }, { "name": "Upper Back", - "access_rules": [ - "$library_top|true,[$library_top]" - ], + "access_rules": [[ + "$can_glitch|Listless Library - Upper Back", + "[$can_reach|Listless Library - Upper Back]" + ]], "sections": [ { "item_count": 1, - "access_rules": ["$library_upper_back|true,[$library_upper_back]"], "chest_opened_img": "images/items/healthup.png", "chest_unopened_img": "images/items/healthup_gray.png" } @@ -635,13 +657,13 @@ }, { "name": "Twilight Theatre", - "access_rules": [], "children": [ { "name": "Soul Cutter", - "access_rules": [ - "$theatre_main,$can_strikebreak" - ], + "access_rules": [[ + "$can_glitch|Twilight Theatre - Soul Cutter", + "[$can_reach|Twilight Theatre - Soul Cutter]" + ]], "sections": [ { "item_count": 1, @@ -659,11 +681,10 @@ }, { "name": "Corner Beam", - "access_rules": [ - "$theatre_pillar,cling,$Getkicks|3", - "$theatre_pillar,cling,$can_slidejump", - "$theatre_pillar,$can_slidejump,$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|Twilight Theatre - Corner Beam", + "[$can_reach|Twilight Theatre - Corner Beam]" + ]], "sections": [ { "item_count": 1, @@ -681,10 +702,10 @@ }, { "name": "Locked Door", - "access_rules": [ - "$theatre_main,[$has_small_keys],smallkey:1,cling", - "$theatre_main,[$has_small_keys],smallkey:1,$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|Twilight Theatre - Locked Door", + "[$can_reach|Twilight Theatre - Locked Door]" + ]], "sections": [ { "item_count": 1, @@ -702,9 +723,10 @@ }, { "name": "Murderous Goat", - "access_rules": [ - "$theatre_main" - ], + "access_rules": [[ + "$can_glitch|Twilight Theatre - Murderous Goat", + "[$can_reach|Twilight Theatre - Murderous Goat]" + ]], "sections": [ { "item_count": 1, @@ -722,10 +744,10 @@ }, { "name": "Back Of Auditorium", - "access_rules": [ - "$theatre_main,$Getkicks|3", - "$theatre_main,cling" - ], + "access_rules": [[ + "$can_glitch|Twilight Theatre - Back Of Auditorium", + "[$can_reach|Twilight Theatre - Back Of Auditorium]" + ]], "sections": [ { "item_count": 1, @@ -743,10 +765,10 @@ }, { "name": "Major Key", - "access_rules": [ - "$theatre_main,$can_soulcutter,cling,$can_slidejump", - "$theatre_main,$can_soulcutter,cling,$Getkicks|1" - ], + "access_rules": [[ + "$can_glitch|Twilight Theatre - Center Stage", + "[$can_reach|Twilight Theatre - Center Stage]" + ]], "sections": [ { "item_count": 1, @@ -766,13 +788,13 @@ }, { "name": "Empty Bailey", - "access_rules": ["$empty_bailey"], "children": [ { "name": "Solar Wind", - "access_rules": [ - "$slide" - ], + "access_rules": [[ + "$can_glitch|Empty Bailey - Solar Wind", + "[$can_reach|Empty Bailey - Solar Wind]" + ]], "sections": [ { "item_count": 1, @@ -790,11 +812,10 @@ }, { "name": "Cheese Bell", - "access_rules": [ - "$can_slidejump,sunsetter,$Getkicks|1", - "$can_slidejump,cling", - "sunsetter,$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|Empty Bailey - Cheese Bell", + "[$can_reach|Empty Bailey - Cheese Bell]" + ]], "sections": [ { "item_count": 1, @@ -812,9 +833,10 @@ }, { "name": "Inside Building", - "access_rules": [ - "$slide" - ], + "access_rules": [[ + "$can_glitch|Empty Bailey - Inside Building", + "[$can_reach|Empty Bailey - Inside Building]" + ]], "sections": [ { "item_count": 1, @@ -832,10 +854,10 @@ }, { "name": "Center Steeple", - "access_rules": [ - "$Getkicks|3", - "sunsetter,$slide" - ], + "access_rules": [[ + "$can_glitch|Empty Bailey - Center Steeple", + "[$can_reach|Empty Bailey - Center Steeple]" + ]], "sections": [ { "item_count": 1, @@ -853,11 +875,10 @@ }, { "name": "Major Key", - "access_rules": [ - "sunsetter", - "cling", - "$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|Empty Bailey - Guarded Hand", + "[$can_reach|Empty Bailey - Guarded Hand]" + ]], "sections": [ { "item_count": 1, @@ -877,13 +898,13 @@ }, { "name": "Underbelly", - "access_rules": [], "children": [ { "name": "Ascendant Light", - "access_rules": [ - "$underbelly_main" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Ascendant Light", + "[$can_reach|The Underbelly - Ascendant Light]" + ]], "sections": [ { "item_count": 1, @@ -901,10 +922,10 @@ }, { "name": "Locked Door", - "access_rules": [ - "$underbelly_main,[$has_small_keys],smallkey:1,$slide,$Getkicks|3", - "$underbelly_main,[$has_small_keys],smallkey:1,$slide,sunsetter" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Locked Door", + "[$can_reach|The Underbelly - Locked Door]" + ]], "sections": [ { "item_count": 1, @@ -922,10 +943,10 @@ }, { "name": "Strikebreak Wall", - "access_rules": [ - "$underbelly_main,$can_strikebreak,$can_bounce,$can_slidejump", - "$underbelly_main,$can_strikebreak,$can_bounce,$Kickorplunge|1" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Strikebreak Wall", + "[$can_reach|The Underbelly - Strikebreak Wall]" + ]], "sections": [ { "item_count": 1, @@ -943,10 +964,10 @@ }, { "name": "Main Room", - "access_rules": [ - "$underbelly_main,sunsetter", - "$underbelly_main,$can_slidejump" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Main Room", + "[$can_reach|The Underbelly - Main Room]" + ]], "sections": [ { "item_count": 1, @@ -964,10 +985,10 @@ }, { "name": "Rafters Near Keep", - "access_rules": [ - "$underbelly_hole,$Getkicks|3", - "$underbelly_hole,sunsetter" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Rafters Near Keep", + "[$can_reach|The Underbelly - Rafters Near Keep]" + ]], "sections": [ { "item_count": 1, @@ -985,10 +1006,10 @@ }, { "name": "Building Near Little Guy", - "access_rules": [ - "$underbelly_main,$Getkicks|3", - "$underbelly_main,sunsetter" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Building Near Little Guy", + "[$can_reach|The Underbelly - Building Near Little Guy]" + ]], "sections": [ { "item_count": 1, @@ -1006,9 +1027,10 @@ }, { "name": "Alcove Near Light", - "access_rules": [ - "$underbelly_main" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Alcove Near Light", + "[$can_reach|The Underbelly - Alcove Near Light]" + ]], "sections": [ { "item_count": 1, @@ -1026,11 +1048,10 @@ }, { "name": "Major Key", - "access_rules": [ - "$underbelly_hole,sunsetter,$can_soulcutter,$can_bounce", - "$underbelly_hole,sunsetter,$can_soulcutter,cling", - "$underbelly_hole,sunsetter,$can_slidejump,$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|The Underbelly - Surrounded By Holes", + "[$can_reach|The Underbelly - Surrounded By Holes]" + ]], "sections": [ { "item_count": 1, @@ -1050,13 +1071,13 @@ }, { "name": "Tower Remains", - "access_rules": ["$tower_remains"], "children": [ { "name": "Cling Gem", - "access_rules": [ - "$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|Tower Remains - Cling Gem", + "[$can_reach|Tower Remains - Cling Gem]" + ]], "sections": [ { "item_count": 1, @@ -1074,9 +1095,10 @@ }, { "name": "Major Key", - "access_rules": [ - "cling,$Getkicks|3" - ], + "access_rules": [[ + "$can_glitch|Tower Remains - Atop The Tower", + "[$can_reach|Tower Remains - Atop The Tower]" + ]], "sections": [ { "item_count": 1, @@ -1096,13 +1118,13 @@ }, { "name": "Distorted Memory", - "access_rules": ["$the_great_door"], "children": [ { "name": "D S T RT ED M M O Y", - "access_rules": [ - "majorkey:5" - ], + "access_rules": [[ + "$can_glitch|D S T RT ED M M O Y", + "[$can_reach|D S T RT ED M M O Y]" + ]], "sections": [ { "name": "Beat the Game!!", diff --git a/scripts/logic/helper.lua b/scripts/logic/helper.lua new file mode 100644 index 0000000..66db6ba --- /dev/null +++ b/scripts/logic/helper.lua @@ -0,0 +1,241 @@ +-- Helper for AP-style rule definition +-- this mostly maps to BaseClasses.py + +local free = function(state) return true end + + +function table.shallow_copy(t) + local t2 = {} + for k,v in pairs(t) do + t2[k] = v + end + return t2 + end + + +AppendableList = { + append = function(t,v) + t[#t+1]=v + end +} +AppendableList.__index = AppendableList + + +State = {} +State.__index = State + +function State:new(definition) + local res = {} + res.stale = true -- TODO: update this + res.definition = definition + res.reachable_regions = {} + --res.blocked_connections = {} + setmetatable(res, self) + return res +end + +function State:has(item_name) + return Tracker:ProviderCountForCode(item_name) > 0 +end + +function State:has_any(item_names) + for _, item_name in ipairs(item_names) do + if self:has(item_name) then + return true + end + end + return false +end + +function State:has_all(item_names) + for _, item_name in ipairs(item_names) do + if not self:has(item_name) then + return false + end + end + return true +end + +function State:count(item_name) + return Tracker:ProviderCountForCode(item_name) +end + +function State:update_reachable_regions() + self.stale = false + local reachable = {} -- TODO: cache + local start = self.definition:get_region("Menu") + local queue = table.shallow_copy(start.exits) + reachable[start] = start + self.reachable_regions = reachable + --print(dump(queue, 8)) + + while #queue > 0 do + connection = queue[#queue] + queue[#queue] = nil + new_region = connection.connected_region + + --print("checking " .. connection.name) + if not reachable[new_region] and connection:can_reach(self) then + reachable[new_region] = true + for _, exit_ in ipairs(new_region.exits) do + queue[#queue + 1] = exit_ + end + end + end +end + + +Location = {} +Location.__index = Location + +function Location:new(name, code, parent_region) + local res = {} + res.name = name + res.code = code + res.parent_region = parent_region + res.access_rule = free + setmetatable(res, self) + return res +end + +function Location:set_rule(rule) + self.access_rule = rule +end + +function Location:can_reach(state) + print(self.name .. ": " .. tostring(self.access_rule(state)) .. " and " .. tostring(self.parent_region:can_reach(state))) + return self.access_rule(state) and self.parent_region:can_reach(state) +end + + +Region = {} +Region.__index = Region + +function Region:new(name, definition) + local res = {} + res.name = name + res.locations = {} + res.exits = {} + res.entrances = {} + res.definition = definition + setmetatable(res, self) + setmetatable(res.locations, AppendableList) + setmetatable(res.exits, AppendableList) + setmetatable(res.entrances, AppendableList) + return res +end + +function Region:create_exit(name) + exit_ = Entrance:new(name, self) + self.exits:append(exit_) + return exit_ +end + +function Region:connect(connecting_region, name, rule) + if name == nil then + name = self.name .. " -> " .. connecting_region.name + end + exit_ = self:create_exit(name) + if rule then + exit_.access_rule = rule + end + exit_:connect(connecting_region) + return exit_ +end + +function Region:add_exits(exits) + -- TODO: implement exits for Dict[str, Optional[str]] type + -- TODO: implement rules: Dict[str, Callable[[CollectionState], bool]] = None) -> None: + name = nil -- TODO + for _, connecting_region_name in ipairs(exits) do + local destination = self.definition:get_region(connecting_region_name) + if not destination then + error("No such region " .. connecting_region_name) + end + self:connect(destination, name) + end +end + +function Region:can_reach(state) + if state.stale then + --print(" updating regions") + state:update_reachable_regions() + --print(" " .. dump(state.reachable_regions)) + end + --print(tostring(state.reachable_regions[self])) + return not not state.reachable_regions[self] +end + + +Entrance = {} +Entrance.__index = Entrance + +function Entrance:new(name, parent_region) + local res = {} + res.name = name + res.parent_region = parent_region + res.access_rule = free + setmetatable(res, self) + return res +end + +function Entrance:set_rule(rule) + self.access_rule = rule +end + +function Entrance:connect(destination, addresses, target) + self.connected_region = destination + self.target = target -- is this lttp crap? + self.addresses = addresses -- is this lttp crap? + destination.entrances:append(self) +end + +function Entrance:can_reach(state) + --print(" " .. tostring(self.parent_region:can_reach(state)) .. " and " .. tostring(self.access_rule(state))) + return self.parent_region:can_reach(state) and self.access_rule(state) +end + + +Definition = {} +Definition.__index = Definition + +function Definition:new() + local res = {} + res.regions = {} + setmetatable(res, self) + setmetatable(res.regions, AppendableList) + return res +end + +function Definition:get_region(name) + --return self.regions[name] + for _, region in ipairs(self.regions) do + if region.name == name then + return region + end + end +end + +function Definition:get_entrance(name) + --return self.entrances[name] + for _, region in ipairs(self.regions) do + --print(region.name .. ":" .. dump(region.entrances)) + for _, entrance in ipairs(region.entrances) do + --print(entrance.name .. " == " .. name .. " ?") + if entrance.name == name then + return entrance + end + end + end +end + +function Definition:get_location(name) + --return self.locations[name] + for _, region in ipairs(self.regions) do + for _, location in ipairs(region.locations) do + if location.name == name then + return location + end + end + end +end diff --git a/scripts/logic/locations.lua b/scripts/logic/locations.lua new file mode 100644 index 0000000..78ecc43 --- /dev/null +++ b/scripts/logic/locations.lua @@ -0,0 +1,199 @@ +function PseudoregaliaLocationData(args) + -- TODO: fill in missing/optional args + return args +end + +location_table = { + ["Dilapidated Dungeon - Dream Breaker"] = PseudoregaliaLocationData{ + code=2365810001, + region="Dungeon Mirror",}, + ["Dilapidated Dungeon - Slide"] = PseudoregaliaLocationData{ + code=2365810002, + region="Dungeon Slide",}, + ["Dilapidated Dungeon - Alcove Near Mirror"] = PseudoregaliaLocationData{ + code=2365810003, + region="Dungeon => Castle",}, + ["Dilapidated Dungeon - Dark Orbs"] = PseudoregaliaLocationData{ + code=2365810004, + region="Dungeon Escape Upper",}, + ["Dilapidated Dungeon - Past Poles"] = PseudoregaliaLocationData{ + code=2365810005, + region="Dungeon Strong Eyes",}, + ["Dilapidated Dungeon - Rafters"] = PseudoregaliaLocationData{ + code=2365810006, + region="Dungeon Strong Eyes",}, + ["Dilapidated Dungeon - Strong Eyes"] = PseudoregaliaLocationData{ + code=2365810007, + region="Dungeon Strong Eyes",}, + + ["Castle Sansa - Indignation"] = PseudoregaliaLocationData{ + code=2365810008, + region="Castle Main",}, + ["Castle Sansa - Alcove Near Dungeon"] = PseudoregaliaLocationData{ + code=2365810009, + region="Castle Main",}, + ["Castle Sansa - Balcony"] = PseudoregaliaLocationData{ + code=2365810010, + region="Castle Main",}, + ["Castle Sansa - Corner Corridor"] = PseudoregaliaLocationData{ + code=2365810011, + region="Castle Main",}, + ["Castle Sansa - Floater In Courtyard"] = PseudoregaliaLocationData{ + code=2365810012, + region="Castle Main",}, + ["Castle Sansa - Locked Door"] = PseudoregaliaLocationData{ + code=2365810013, + region="Castle Main",}, + ["Castle Sansa - Platform In Main Halls"] = PseudoregaliaLocationData{ + code=2365810014, + region="Castle Main",}, + ["Castle Sansa - Tall Room Near Wheel Crawlers"] = PseudoregaliaLocationData{ + code=2365810015, + region="Castle Main",}, + ["Castle Sansa - Wheel Crawlers"] = PseudoregaliaLocationData{ + code=2365810016, + region="Castle Main",}, + ["Castle Sansa - High Climb From Courtyard"] = PseudoregaliaLocationData{ + code=2365810017, + region="Castle High Climb",}, + ["Castle Sansa - Alcove Near Scythe Corridor"] = PseudoregaliaLocationData{ + code=2365810018, + region="Castle By Scythe Corridor",}, + ["Castle Sansa - Near Theatre Front"] = PseudoregaliaLocationData{ + code=2365810019, + region="Castle Moon Room",}, + + ["Sansa Keep - Strikebreak"] = PseudoregaliaLocationData{ + code=2365810020, + region="Keep Main",}, + ["Sansa Keep - Alcove Near Locked Door"] = PseudoregaliaLocationData{ + code=2365810021, + region="Keep Locked Room",}, + ["Sansa Keep - Levers Room"] = PseudoregaliaLocationData{ + code=2365810022, + region="Keep Main",}, + ["Sansa Keep - Lonely Throne"] = PseudoregaliaLocationData{ + code=2365810023, + region="Keep Path To Throne",}, + ["Sansa Keep - Near Theatre"] = PseudoregaliaLocationData{ + code=2365810024, + region="Keep Main",}, + ["Sansa Keep - Sunsetter"] = PseudoregaliaLocationData{ + code=2365810025, + region="Keep Sunsetter",}, + + ["Listless Library - Sun Greaves"] = PseudoregaliaLocationData{ + code=2365810026, + region="Library Greaves",}, + ["Listless Library - Upper Back"] = PseudoregaliaLocationData{ + code=2365810027, + region="Library Top"}, + ["Listless Library - Locked Door Across"] = PseudoregaliaLocationData{ + code=2365810028, + region="Library Locked",}, + ["Listless Library - Locked Door Left"] = PseudoregaliaLocationData{ + code=2365810029, + region="Library Locked",}, + + ["Twilight Theatre - Soul Cutter"] = PseudoregaliaLocationData{ + code=2365810030, + region="Theatre Main",}, + ["Twilight Theatre - Back Of Auditorium"] = PseudoregaliaLocationData{ + code=2365810031, + region="Theatre Main",}, + ["Twilight Theatre - Center Stage"] = PseudoregaliaLocationData{ + code=2365810032, + region="Theatre Main",}, + ["Twilight Theatre - Locked Door"] = PseudoregaliaLocationData{ + code=2365810033, + region="Theatre Main",}, + ["Twilight Theatre - Murderous Goat"] = PseudoregaliaLocationData{ + code=2365810034, + region="Theatre Main",}, + ["Twilight Theatre - Corner Beam"] = PseudoregaliaLocationData{ + code=2365810035, + region="Theatre Pillar",}, + + ["Empty Bailey - Solar Wind"] = PseudoregaliaLocationData{ + code=2365810036, + region="Empty Bailey",}, + ["Empty Bailey - Center Steeple"] = PseudoregaliaLocationData{ + code=2365810037, + region="Empty Bailey",}, + ["Empty Bailey - Cheese Bell"] = PseudoregaliaLocationData{ + code=2365810038, + region="Empty Bailey"}, + ["Empty Bailey - Guarded Hand"] = PseudoregaliaLocationData{ + code=2365810039, + region="Empty Bailey",}, + ["Empty Bailey - Inside Building"] = PseudoregaliaLocationData{ + code=2365810040, + region="Empty Bailey",}, + + ["The Underbelly - Ascendant Light"] = PseudoregaliaLocationData{ + code=2365810041, + region="Underbelly Ascendant Light",}, + ["The Underbelly - Alcove Near Light"] = PseudoregaliaLocationData{ + code=2365810042, + region="Underbelly Light Pillar",}, + ["The Underbelly - Building Near Little Guy"] = PseudoregaliaLocationData{ + code=2365810043, + region="Underbelly Little Guy",}, + ["The Underbelly - Locked Door"] = PseudoregaliaLocationData{ + code=2365810044, + region="Underbelly By Heliacal",}, + ["The Underbelly - Main Room"] = PseudoregaliaLocationData{ + code=2365810045, + region="Underbelly Main Upper",}, + ["The Underbelly - Rafters Near Keep"] = PseudoregaliaLocationData{ + code=2365810046, + region="Underbelly => Keep",}, + ["The Underbelly - Strikebreak Wall"] = PseudoregaliaLocationData{ + code=2365810047, + region="Underbelly Main Upper",}, + ["The Underbelly - Surrounded By Holes"] = PseudoregaliaLocationData{ + code=2365810048, + region="Underbelly Hole",}, + + ["Tower Remains - Cling Gem"] = PseudoregaliaLocationData{ + code=2365810049, + region="Tower Remains",}, + ["Tower Remains - Atop The Tower"] = PseudoregaliaLocationData{ + code=2365810050, + region="The Great Door",}, + + ["Listless Library - Sun Greaves 1"] = PseudoregaliaLocationData{ + code=2365810051, + region="Library Greaves",}, + ["Listless Library - Sun Greaves 2"] = PseudoregaliaLocationData{ + code=2365810052, + region="Library Greaves",}, + ["Listless Library - Sun Greaves 3"] = PseudoregaliaLocationData{ + code=2365810053, + region="Library Greaves",}, + + ["Dilapidated Dungeon - Unlock Door"] = PseudoregaliaLocationData{ + region="Dungeon Strong Eyes", + locked_item="Unlocked Door",}, + ["Castle Sansa - Unlock Door (Professionalism)"] = PseudoregaliaLocationData{ + region="Castle Main", + locked_item="Unlocked Door",}, + ["Castle Sansa - Unlock Door (Sansa Keep)"] = PseudoregaliaLocationData{ + region="Castle Main", + locked_item="Unlocked Door",}, + ["Sansa Keep - Unlock Door"] = PseudoregaliaLocationData{ + region="Keep Main", + locked_item="Unlocked Door",}, + ["Listless Library - Unlock Door"] = PseudoregaliaLocationData{ + region="Library Main", + locked_item="Unlocked Door",}, + ["Twilight Theatre - Unlock Door"] = PseudoregaliaLocationData{ + region="Theatre Main", + locked_item="Unlocked Door",}, + ["The Underbelly - Unlock Door"] = PseudoregaliaLocationData{ + region="Underbelly By Heliacal", + locked_item="Unlocked Door",}, + + ["D S T RT ED M M O Y"] = PseudoregaliaLocationData{ + region="The Great Door",}, +} diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index 00fb28f..a963c4c 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -1,765 +1,125 @@ ----[[ -function has(item, amount) - local count = Tracker:ProviderCountForCode(item) - amount = tonumber(amount) - if not amount then - return count > 0 +-- ap-style logic +-- TODO: use require +ScriptHost:LoadScript("scripts/logic/helper.lua") -- load helper for AP-style logic +ScriptHost:LoadScript("scripts/logic/locations.lua") -- load location_table +ScriptHost:LoadScript("scripts/logic/regions.lua") -- load region_table +ScriptHost:LoadScript("scripts/logic/rules/base.lua") -- load PseudoregaliaRulesHelpers +ScriptHost:LoadScript("scripts/logic/rules/normal.lua") -- load PseudoregaliaNormalRules +ScriptHost:LoadScript("scripts/logic/rules/hard.lua") -- load PseudoregaliaHardRules +ScriptHost:LoadScript("scripts/logic/rules/expert.lua") -- load PseudoregaliaExpertRules +ScriptHost:LoadScript("scripts/logic/rules/lunatic.lua") -- load PseudoregaliaLunaticRules +-- TODO: normal, hard, expert, lunatic rules +-- TODO: init to set up locations, regions and rules + + +local def = Definition:new() +local state = State:new(def) -- TODO: add caching and update in watch for code + + +-- item name to code mapping +local codes = { + ["Dream Breaker"] = "breaker", + ["Sun Greaves"] = "greaves", + ["Slide"] = "slide", + ["Solar Wind"] = "solar", + ["Sunsetter"] = "sunsetter", + ["Strikebreak"] = "strikebreak", + ["Cling Gem"] = "cling", + ["Ascendant Light"] = "ascendant", + ["Soul Cutter"] = "cutter", + ["Heliacal Power"] = "heliacal", + ["Small Key"] = "smallkey", + ["Major Key - Empty Bailey"] = "majorkey", + ["Major Key - The Underbelly"] = "majorkey", + ["Major Key - Tower Remains"] = "majorkey", + ["Major Key - Sansa Keep"] = "majorkey", + ["Major Key - Twilight Theatre"] = "majorkey", +} + +-- patch up State.has to match the codes +local _has = getmetatable(state).has +getmetatable(state).has = function(state, name) + local code = codes[name] + if code then + return _has(state, code) else - return count >= amount + -- TODO: warn? + return _has(state, name) end end - --- Abilities -function breaker(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("breaker") or has("breaker1") -end - -function greaves(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("greaves") -end - -function slide(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("slide") or has("slide1") -end - -function solar(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("solar") or has("slide2") -end - -function sunsetter(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("sunsetter") -end - -function strikebreak(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("strikebreak") or has("breaker2") -end - -function cling(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("cling") -end - -function ascendant(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("ascendant") -end - -function cutter(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("cutter") or has("breaker3") -end - -function heliacal(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return Tracker:ProviderCountForCode("heliacal") -end - -function progkick(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return Tracker:ProviderCountForCode("airkick") -end - --- Difficulty Settings - -function Knows_obscure(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("obscure") -end - -function Normal(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("normal") -end - -function Hard(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("hard") -end - -function Expert(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("expert") -end - -function Lunatic(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - return has("lunatic") -end - --- Quick Functions -function has_small_keys(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("has_small_keys") - return has("smallkey",7) -end - -function can_bounce(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("can_bounce") - return breaker(n) and ascendant(n) -end - ---function more_kicks(n) - -- print("more_kicks") - --return greaves(n) and heliacal(n) --- - -function can_slidejump(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("can_slidejump") - return (slide(n) and solar(n)) or has("slide2") -end - -function navigate_darkrooms(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("navigate_darkrooms") - return (breaker(n) or ascendant(n)) -end - -function can_strikebreak(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("can_strikebreak") - return (breaker(n) and strikebreak(n)) or has("breaker2") -end - -function can_soulcutter(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("can_soulcutter") - return (breaker(n) and strikebreak(n) and cutter(n)) or has("breaker3") -end - -function can_sunsetter(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("can_sunsetter") - return breaker(n) and sunsetter(n) -end - -function can_attack(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - -- print("can_sunsetter") - return breaker(n) or sunsetter(n) -end - -function Kickorplunge(count, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - local total = 0 - if has("greaves") then - total = total + 3 - end - if has("sunsetter") then - total = total + 1 - end - total = total + heliacal() --see note - total = total + progkick() --see note - count = tonumber(count) - return (total >= count) - end - -function Getkicks(count, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - local kicks = 0 - if has("greaves") then - kicks = kicks + 3 - end - kicks = kicks + heliacal() - kicks = kicks + progkick() - count = tonumber(count) - return (kicks >= count) -end - --- Region functions --- Dungeon -function dungeon_mirror(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("dungeon_mirror") - return breaker(n) -end - -function dungeon_strong_eyes(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (slide(n) and breaker(n)) or (has("smallkey",1) and (empty_bailey(n) or Castle_spiral_climb(n)) and dungeon_mirror(n)) - end - --print("dungeon_strong_eyes") - return (slide(n) and dungeon_mirror(n)) or (has_small_keys(n) and (empty_bailey(n) or Castle_spiral_climb(n)) and dungeon_mirror(n)) -end - --- Underbelly -function underbelly_main(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("underbelly_main") - return breaker(n) or (sunsetter(n) and (tower_remains(n) or underbelly_hole(n))) -end - -function underbelly_hole(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("underbelly_hole") - return Kickorplunge(1) and ((cling(n) and theatre_main(n)) or ((has_small_keys(n) and dungeon_strong_eyes(n)) or Castle_spiral_climb(n))) - --return Kickorplunge(1) and keep_main(n) -end - --- Theatre -function theatre_main(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("theatre_main") - return (cling(n) and (Getkicks(3) or can_slidejump(n)) and dungeon_mirror(n)) or - (cling(n) and (Getkicks(3) or can_slidejump(n)) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or ((sunsetter(n) or breaker(n)) and (breaker(n) or (sunsetter(n) and tower_remains(n)))) or Castle_spiral_climb(n))) or -- castle_sansa() = reduced keep_main() rule, and then reduced castle_sansa more to help more recurssions - ((sunsetter(n) and cling(n)) or (sunsetter(n) and Getkicks(4)) and theatre_pillar(n)) or - Theatre_front(n) -end - -function theatre_pillar(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("theatre_pillar") - return (empty_bailey(n)) or (Normal(n) and (Kickorplunge(2) or (cling(n) and Kickorplunge(1))) and castle_sansa(n)) or - (Hard(n) and (cling(n) or (Kickorplunge(1))) and castle_sansa(n)) or - ((Expert(n) or Lunatic(n)) and (cling(n) or slide(n) or Kickorplunge(1)) and castle_sansa(n)) -end - --- Library -function library_main(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (can_attack(n) and castle_sansa(n)) - end - --print("library_main") - return (Normal(n) and (breaker(n) or (Knows_obscure(n) and can_attack(n))) and castle_sansa(n)) or - (Expert(n) and can_attack(n) and castle_sansa(n)) -end - -function library_locked(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (has("smallkey",1) and library_main(n)) - end - --print("library_locked") - return (Normal(n) and has_small_keys(n) and library_main(n)) -end - -function library_greaves(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (slide(n) and library_main(n)) or - (cling(n) or Getkicks(2) or (can_bounce(n) and Getkicks(1) and sunsetter(n))) and library_top(n) - end - --print("library_greaves") - return (Normal(n) and slide(n) and library_main(n)) or - (Normal(n) and ((cling(n) and Kickorplunge(1)) or (Getkicks(3) and sunsetter(n)) or (Getkicks(3) and can_bounce(n))) and library_top(n)) or - (Hard(n) and (cling(n) or Getkicks(3) or (Getkicks(2) and sunsetter(n) and can_bounce(n))) and library_top(n)) or - (Expert(n) and (cling(n) or Getkicks(2)) and library_top(n)) or - (Lunatic(n) and (cling(n) or Getkicks(2) or (can_bounce(n) and Getkicks(1) and sunsetter(n))) and library_top(n)) -end - -function library_top(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return ((cling(n) or (Kickorplunge(2) or slide(n))) and library_main(n)) or - (cling(n) or (Getkicks(1) or slide(n))) and slide(n) and library_main(n) - end - --print("library_top") - return (Normal(n) and (Kickorplunge(4) or (Knows_obscure(n) and Getkicks(1) and sunsetter(n))) and library_main(n)) or - (Normal(n) and (cling(n) or Getkicks(2)) and slide(n) and library_main(n)) or -- reduced library_greaves for recurssions - (Hard(n) and (cling(n) or Kickorplunge(4) or (Knows_obscure(n) and Kickorplunge(2))) and library_main(n)) or - (Hard(n) and (cling(n) or Getkicks(1)) and slide(n) and library_main(n)) or -- reduced library_greaves for recurssions - (Expert(n) and (cling(n) or Kickorplunge(2) or slide(n)) and library_main(n)) or - (Expert(n) and (cling(n) or Getkicks(1) or slide(n)) and slide(n) and library_main(n)) -- reduced library_greaves for recurssions -end - --- Keep -function keep_main(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("keep_main") - return (cling(n) and theatre_main(n)) or castle_sansa(n) -end - -function keep_sunsetter(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("keep_sunsetter") - return (cling(n) or has_small_keys(n) or greaves(n)) and keep_main(n) -end - --- Bailey -function empty_bailey(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("empty_bailey") - return (sunsetter(n) or breaker(n)) and (breaker(n) or (sunsetter(n) and underbelly_hole(n))) -- reduced underbelly_main to `breaker(n) or (sunsetter(n) and (tower_remains(n) or underbelly_hole(n)))` and then removed the 'tower_remains' to help eliminate recurssions - --return (sunsetter(n) or breaker(n)) and underbelly_main(n) -end - --- Tower -function tower_remains(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("tower_remains") - return (cling(n) or Getkicks(1) or (slide(n) and sunsetter(n))) and empty_bailey(n) -end - -function the_great_door(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("the_great_door") - return cling(n) and Getkicks(3) and tower_remains(n) -end - --- Castle -function castle_sansa(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (has("smallkey",1) and dungeon_strong_eyes(n)) or empty_bailey(n) or Castle_spiral_climb(n) - end - --print("castle_sansa") - return (has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n) or Castle_spiral_climb(n) -end - -function Castle_spiral_climb(n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - --print("Castle_spiral_climb") - return (Normal(n) and (Getkicks(2) or (cling(n) and sunsetter(n))) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n))) or -- reduced castle_sansa - (Normal(n) and (cling(n) or (Getkicks(4) and sunsetter(n))) and Scythe_corridor(n)) or - (Hard(n) and (cling(n) or Kickorplunge(2) or (can_slidejump(n) and sunsetter(n))) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n))) or -- reduced castle_sansa - (Hard(n) and (cling(n) or Getkicks(3)) and Scythe_corridor(n)) or - (Expert(n) and (cling(n) or slide(n) or Kickorplunge(2)) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n))) -- reduced castle_sansa -end - -function Castle_high_climb(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return Castle_spiral_climb(n) or - (cling(n) or slide(n) or Kickorplunge(2)) and Scythe_corridor(n) - end - --print("Castle_high_climb") - return (Normal(n) and ((Getkicks(3) and sunsetter(n)) or (breaker(n) and Getkicks(1)) or (Knows_obscure(n) and sunsetter(n) and Getkicks(1))) and Castle_spiral_climb(n)) or - (Normal(n) and (cling(n) or Getkicks(4) or (Getkicks(2) and sunsetter(n)) or (Getkicks(1) and sunsetter(n) and can_slidejump(n))) and Scythe_corridor(n)) or - (Hard(n) and (cling(n) or Kickorplunge(3) or (breaker(n) and Getkicks(1)) or (Knows_obscure(n) and sunsetter(n) and Getkicks(1)) or (Knows_obscure(n) and can_attack(n) and can_slidejump(n))) and Castle_spiral_climb(n)) or - (Hard(n) and (cling(n) or Getkicks(4) or (Getkicks(3) and breaker(n)) or (Getkicks(1) and sunsetter(n))) and Scythe_corridor(n)) or - (Expert(n) and Castle_spiral_climb(n)) or - (Expert(n) and (cling(n) or slide(n) or Kickorplunge(2)) and Scythe_corridor(n)) -end - ---function Scythe_corridor(outOflogic, n) -- ORIGINAL - --if n == nil then; n = 0; end - --if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - --n = n + 1 - --if outOflogic then - --return (cling(n) or Getkicks(3)) and Castle_spiral_climb(n) or - --(cling(n) or slide(n) or Getkicks(3)) and Theatre_front(n) - --end - --print("Scythe_corridor") - --return (Normal(n) and cling(n) and Castle_spiral_climb(n)) or - --(Normal(n) and (cling(n) or (can_slidejump(n) and Getkicks(1)) or Getkicks(4)) and Theatre_front(n)) or - --(Expert(n) and (cling(n) or Kickorplunge(4)) and Castle_spiral_climb(n)) or - --(Expert(n) and (cling(n) or slide(n) or Kickorplunge(2)) and Theatre_front(n)) or - --(Lunatic(n) and (cling(n) or Getkicks(3)) and Castle_spiral_climb(n)) or - --(Lunatic(n) and (cling(n) or slide(n) or Getkicks(3)) and Theatre_front(n)) ---end - -function Scythe_corridor(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Getkicks(3)) and ((cling(n) or slide(n) or Kickorplunge(2)) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n))) or - (cling(n) or slide(n) or Getkicks(3)) and Theatre_front(n) - end - --print("Scythe_corridor") - return (Normal(n) and cling(n) and ((Getkicks(2) or (cling(n) and sunsetter(n))) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n)))) or - (Normal(n) and (cling(n) or (can_slidejump(n) and Getkicks(1)) or Getkicks(4)) and Theatre_front(n)) or - (Expert(n) and (cling(n) or Kickorplunge(4)) and ((cling(n) or slide(n) or Kickorplunge(2)) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n)))) or - (Expert(n) and (cling(n) or slide(n) or Kickorplunge(2)) and Theatre_front(n)) or - (Lunatic(n) and (cling(n) or Getkicks(3)) and ((cling(n) or slide(n) or Kickorplunge(2)) and ((has_small_keys(n) and dungeon_strong_eyes(n)) or empty_bailey(n)))) or - (Lunatic(n) and (cling(n) or slide(n) or Getkicks(3)) and Theatre_front(n)) -end - ---function Theatre_front(n) - --if n == nil then; n = 0; end - --if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - --n = n + 1 - --return (Normal(n) and cling(n) and Kickorplunge(2) and Scythe_corridor(n)) or - --(Hard(n) and cling(n) and Scythe_corridor(n)) or - --(Expert(n) and (cling(n) or (slide(n) and Getkicks(2))) and Scythe_corridor(n)) or - --(Lunatic(n) and (cling(n) or (slide(n) and Kickorplunge(2))) and Scythe_corridor(n)) ---end - -function Theatre_front(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or (slide(n) and Kickorplunge(2))) and ((cling(n) and Castle_spiral_climb(n)) or - ((cling(n) or Kickorplunge(4)) and Castle_spiral_climb(n)) or - ((cling(n) or Getkicks(3)) and Castle_spiral_climb(n))) - end - --print("Theatre_front") - return (Normal(n) and cling(n) and Kickorplunge(2) and ((Normal(n) and cling(n) and Castle_spiral_climb(n)) or -- "reduced" Scythe_corridor to help with recurssions. see old code above - (Expert(n) and (cling(n) or Kickorplunge(4)) and Castle_spiral_climb(n)) or - (Lunatic(n) and (cling(n) or Getkicks(3)) and Castle_spiral_climb(n)))) or - (Hard(n) and cling(n) and ((Normal(n) and cling(n) and Castle_spiral_climb(n)) or -- "reduced" Scythe_corridor to help with recurssions. see old code above - (Expert(n) and (cling(n) or Kickorplunge(4)) and Castle_spiral_climb(n)) or - (Lunatic(n) and (cling(n) or Getkicks(3)) and Castle_spiral_climb(n)))) or - (Expert(n) and (cling(n) or (slide(n) and Getkicks(2))) and ((Normal(n) and cling(n) and Castle_spiral_climb(n)) or -- "reduced" Scythe_corridor to help with recurssions. see old code above - (Expert(n) and (cling(n) or Kickorplunge(4)) and Castle_spiral_climb(n)) or - (Lunatic(n) and (cling(n) or Getkicks(3)) and Castle_spiral_climb(n)))) or - (Lunatic(n) and (cling(n) or (slide(n) and Kickorplunge(2))) and ((Normal(n) and cling(n) and Castle_spiral_climb(n)) or -- "reduced" Scythe_corridor to help with recurssions. see old code above - (Expert(n) and (cling(n) or Kickorplunge(4)) and Castle_spiral_climb(n)) or - (Lunatic(n) and (cling(n) or Getkicks(3)) and Castle_spiral_climb(n)))) -end - -function Castle_moon_room(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or slide(n) or Getkicks(4)) and Theatre_front(n) - end - --print("Castle_moon_room") - return (Normal(n) and (cling(n) or (can_slidejump(n) and Kickorplunge(2))) and Theatre_front(n)) or - (Hard(n) and (cling(n) or (can_slidejump(n) and Kickorplunge(2)) or Getkicks(4)) and Theatre_front(n)) or - (Expert(n) and (cling(n) or slide(n) or Getkicks(4)) and Theatre_front(n)) -end - --- LOCATION LOGIC --- DUNGEON // NO FUNCTIONS FOR 'DREAM BREAKER', 'SLIDE', AND 'ALCOVE NEAR MIRROR' -function dungeon_dark_orbs(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or (Getkicks(1) and can_bounce(n)) or (Getkicks(3) and sunsetter(n)) or (slide(n) and Getkicks(1)) or (slide(n) and can_bounce(n))) - end - return (Normal(n) and ((cling(n) and can_bounce(n)) or (cling(n) and Kickorplunge(3)) or (Getkicks(2) and can_bounce(n)) or (can_slidejump(n) and Getkicks(1) and can_bounce(n)))) or - (Hard(n) and (cling(n) or (Getkicks(1) and can_bounce(n)) or (can_slidejump(n) and sunsetter(n) and can_bounce(n)) or (Getkicks(3) and sunsetter(n)))) or - (Expert(n) and (cling(n) or (Getkicks(1) and can_bounce(n)) or (Getkicks(3) and sunsetter(n)) or (slide(n) and Getkicks(1)) or (slide(n) and can_bounce(n)))) -end - -function dungeon_rafters(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Kickorplunge(2) or (can_bounce(n) and Kickorplunge(1)) or slide(n)) - end - return (Normal(n) and (Kickorplunge(3) or (Knows_obscure(n) and can_bounce(n) and cling(n)))) or - (Hard(n) and (cling(n) or Getkicks(3) or (Getkicks(1) and sunsetter(n)) or (Getkicks(1) and can_bounce(n)))) or - (Expert(n) and (cling(n) or Kickorplunge(2) or (can_bounce(n) and Getkicks(1)) or (slide(n) and Kickorplunge(1)))) or - (Lunatic(n) and (cling(n) or Kickorplunge(2) or (can_bounce(n) and Kickorplunge(1)) or slide(n))) -end - -function strong_eyes_in_dungeon(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (breaker(n) or cling(n) or (slide(n) and Kickorplunge(1))) - end - return (Normal(n) and (breaker(n) or (Knows_obscure(n) and ((cling(n) and Getkicks(1) and sunsetter(n)) or (cling(n) and Getkicks(3)))))) or - (Hard(n) and (breaker(n) or (Knows_obscure(n) and cling(n) and Kickorplunge(2)))) or - (Expert(n) and (breaker(n) or cling(n) or (slide(n) and Getkicks(1)))) or - (Lunatic(n) and (breaker(n) or cling(n) or (slide(n) and Kickorplunge(1)))) -end - -function dungeon_past_poles(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Getkicks(2) or (slide(n) and Getkicks(1) and sunsetter(n))) - end - return (Normal(n) and (Getkicks(3) or (cling(n) and Kickorplunge(1)))) or - (Hard(n) and (cling(n) or Getkicks(2))) or - (Lunatic(n) and (cling(n) or Getkicks(2) or (slide(n) and Getkicks(1) and sunsetter(n)))) -end - --- CASTLE -function Floater_in_courtyard(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - --print(outOflogic) - --print(((can_bounce(n) and (Kickorplunge(1) or slide(n))) or (slide(n) and Getkicks(1)) or Getkicks(3) or cling(n))) - return ((can_bounce(n) and (Kickorplunge(1) or slide(n))) or (slide(n) and Getkicks(1)) or Getkicks(3) or cling(n)) - end - return (Normal(n) and ((can_bounce(n) and sunsetter(n)) or (can_bounce(n) and Getkicks(2)) or (cling(n) and Getkicks(2)) or (cling(n) and sunsetter(n)) or Getkicks(4))) or - (Hard(n) and ((can_bounce(n) and sunsetter(n)) or (can_bounce(n) and Getkicks(1)) or Kickorplunge(4) or cling(n))) or - (Expert(n) and ((can_bounce(n) and (Kickorplunge(1) or slide(n))) or (slide(n) and Getkicks(1)) or Getkicks(3) or cling(n))) -end - -function Castle_locked_door(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return can_attack(n) - end - return (Normal(n) and (breaker(n) or (Knows_obscure(n) and can_attack(n)))) or - (Expert(n) and can_attack(n)) -end - -function Castle_platform_main(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (Kickorplunge(1) or cling(n) or slide(n) or can_bounce(n)) - end - return (Normal(n) and (sunsetter(n) or cling(n) or Getkicks(2))) or - (Hard(n) and (Kickorplunge(1) or cling(n))) or - (Expert(n) and (Kickorplunge(1) or cling(n) or slide(n))) or - (Lunatic(n) and (Kickorplunge(1) or cling(n) or slide(n) or can_bounce(n))) -end - -function Castle_tall_room_wheel_crawlers(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Getkicks(1) or slide(n)) - end - return (Normal(n) and (Getkicks(2) or (cling(n) and Kickorplunge(1)))) or - (Hard(n) and (cling(n) or Getkicks(1) or (Knows_obscure(n) and can_slidejump(n) and sunsetter(n)))) or - (Expert(n) and (cling(n) or Getkicks(1) or slide(n))) -end - -function Castle_alcove_near_dungeon(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Kickorplunge(1) or slide(n)) +-- patch up State.count to match the codes +local _count = getmetatable(state).count +getmetatable(state).count = function(state, name) + local code = codes[name] + if code then + return _count(state, code) + else + return _count(state, name) end - return (Normal(n) and (Kickorplunge(2) or (cling(n) and Kickorplunge(1)))) or - (Hard(n) and (cling(n) or Kickorplunge(1))) or - (Expert(n) and (cling(n) or Kickorplunge(1) or slide(n))) end -function Castle_balcony(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Getkicks(3) or (slide(n) or (sunsetter(n) and Getkicks(1)))) +function can_reach(location_name, out_of_logic) + if out_of_logic then + --return true -- TODO: return logic for lunatic end - return (Normal(n) and (cling(n) or Kickorplunge(3) or (can_slidejump(n) and Kickorplunge(2)))) or - (Hard(n) and (cling(n) or Kickorplunge(3) or (slide(n) and sunsetter(n)) or (slide(n) and Getkicks(1) and breaker(n)))) or - (Expert(n) and (cling(n) or Getkicks(3) or (slide(n) or (sunsetter(n) and Getkicks(1))))) + state.stale = true + return def:get_location(location_name):can_reach(state) end -function Castle_corner_corridor(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Getkicks(3) or (Getkicks(1) and slide(n))) - end - return (Normal(n) and (cling(n) or Getkicks(4))) or - (Hard(n) and (cling(n) or Getkicks(3))) or - (Expert(n) and (cling(n) or Getkicks(3) or (Getkicks(2) and slide(n)))) or - (Lunatic(n) and (cling(n) or Getkicks(3) or (Getkicks(1) and slide(n)))) +function can_glitch(location_name) + return can_reach(location_name, true) end -function Castle_wheel_crawler(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (can_bounce(n) or cling(n) or Kickorplunge(1) or slide(1)) +function dump(o, level) + level = level or 0 + if level > 10 then + return "F" end - return (Normal(n) and (can_bounce(n) or cling(n) or (Getkicks(2) or (Getkicks(1) and can_slidejump(n))))) or - (Hard(n) and (can_bounce(n) or cling(n) or Getkicks(1) or (can_slidejump(n) and sunsetter(n)) or (Knows_obscure(n) and sunsetter(n)))) or - (Expert(n) and (can_bounce(n) or cling(n) or Kickorplunge(1) or slide(1))) -end - -function Castle_alcove_scythe(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Kickorplunge(1)) + if type(o) == 'table' then + local s = '{ ' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. dump(v, level + 1) .. ',' + end + return s .. '} ' + else + return tostring(o) end - return (Normal(n) and (Kickorplunge(4) or (cling(n) and Getkicks(1) and sunsetter(n)))) or - (Hard(n) and (cling(n) or (Getkicks(2) and sunsetter(n)))) or - (Expert(n) and (cling(n) or Kickorplunge(3) or (slide(n) and Kickorplunge(1)))) or - (Lunatic(n) and (cling(n) or Kickorplunge(1))) end + +--print(dump(def)) +--print(dump(def.regions)) +--print(dump(getmetatable(def.regions))) -function Castle_theatre_front(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or slide(n) or Getkicks(4) or (Getkicks(2) and sunsetter(n))) +function create_regions() + for region_name, _ in pairs(region_table) do + def.regions:append(Region:new(region_name, def)) end - return (Normal(n) and (Getkicks(4) or (Getkicks(2) and sunsetter(n)))) or - (Hard(n) and (cling(n) or Getkicks(4) or (Getkicks(2) and sunsetter(n)))) or - (Expert(n) and (cling(n) or slide(n) or Getkicks(4) or (Getkicks(2) and sunsetter(n)))) -end -function Castle_courtyard_high_climb(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (Getkicks(2) or cling(n) or slide(n) or (can_attack(n) and Getkicks(1))) + for loc_name, loc_data in pairs(location_table) do + -- if not loc_data.can_create() ... + region = def:get_region(loc_data.region) + new_loc = Location:new(loc_name, loc_data.code, region) + region.locations:append(new_loc) end - return (Normal(n) and (Getkicks(2) or (cling(n) and sunsetter(n)) or (breaker(n) and Getkicks(1)) or (Knows_obscure(n) and sunsetter(n) and Getkicks(1)))) or - (Hard(n) and (Getkicks(2) or cling(n) or (sunsetter(n) and can_slidejump(n)) or (breaker(n) and Getkicks(1)) or (Knows_obscure(n) and sunsetter(n) and Getkicks(1)))) or - (Expert(n) and (Getkicks(2) or cling(n) or slide(n) or (can_attack(n) and Getkicks(1)))) -end -function Castle_courtyard_high_climb(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (Getkicks(2) or cling(n) or slide(n) or (can_attack(n) and Getkicks(1))) + for region_name, exit_list in pairs(region_table) do + region = def:get_region(region_name) + region:add_exits(exit_list) end - return (Normal(n) and (Getkicks(2) or (cling(n) and sunsetter(n)) or (breaker(n) and Getkicks(1)) or (Knows_obscure(n) and sunsetter(n) and Getkicks(1)))) or - (Hard(n) and (Getkicks(2) or cling(n) or (sunsetter(n) and can_slidejump(n)) or (breaker(n) and Getkicks(1)) or (Knows_obscure(n) and sunsetter(n) and Getkicks(1)))) or - (Expert(n) and (Getkicks(2) or cling(n) or slide(n) or (can_attack(n) and Getkicks(1)))) -end --- LISTLESS LIBRARY -function library_sun_greaves(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return can_attack(n) - end - --print("library_sun_greaves") - return (Normal(n) and (breaker(n) or (Knows_obscure(n) and sunsetter(n)))) or - (Expert(n) and can_attack(n)) + -- locked items + -- TODO: events if it uses events end -function library_upper_back(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (breaker(n) or (Knows_obscure(n) and sunsetter(n))) and (cling(n) or Kickorplunge(2) or slide(n)) - end - --print("library_upper_back") - return (Normal(n) and (breaker(n) or (Knows_obscure(n) and sunsetter(n))) and ((cling(n) and Kickorplunge(1)) or Kickorplunge(2))) or - (Hard(n) and (breaker(n) or (Knows_obscure(n) and sunsetter(n))) and (cling(n) or Kickorplunge(2))) or - (Expert(n) and (breaker(n) or (Knows_obscure(n) and sunsetter(n))) and (cling(n) or Kickorplunge(2) or slide(n))) +function set_rules() + -- TODO: difficulty + PseudoregaliaNormalRules:new(def):set_pseudoregalia_rules() end -function library_locked_across(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Kickorplunge(1) or slide(n)) - end - --print("library_locked_across") - return (Normal(n) and (cling(n) or Getkicks(1) or can_slidejump(n))) or - (Hard(n) and (cling(n) or Kickorplunge(1) or can_slidejump(n))) or - (Expert(n) and (cling(n) or Kickorplunge(1) or slide(n))) -end +create_regions() +set_rules() -function library_locked_left(outOflogic, n) - if n == nil then; n = 0; end - if n > 10 then; return false; end -- detect 10th step when trying to resolve and abort - n = n + 1 - if outOflogic then - return (cling(n) or Kickorplunge(2) or (slide(n) and Kickorplunge(1))) - end - --print("library_locked_left") - return (Normal(n) and (cling(n) or Kickorplunge(3) or (can_slidejump(n) and Getkicks(1)))) or - (Expert(n) and (cling(n) or Kickorplunge(2) or (slide(n) and Kickorplunge(1)))) -end -- LAYOUT SWITCHING function apLayoutChange1() @@ -845,7 +205,7 @@ function apLayoutChange7() end end end - + ScriptHost:AddWatchForCode("useApLayout1", "progbreakerLayout", apLayoutChange1) ScriptHost:AddWatchForCode("useApLayout2", "progslideLayout", apLayoutChange2) ScriptHost:AddWatchForCode("useApLayout3", "splitkickLayout", apLayoutChange3) diff --git a/scripts/logic/regions.lua b/scripts/logic/regions.lua new file mode 100644 index 0000000..99c9644 --- /dev/null +++ b/scripts/logic/regions.lua @@ -0,0 +1,155 @@ +region_table = { + ["Menu"] = { + "Dungeon Mirror", + }, + ["Dungeon Mirror"] = { + "Dungeon Slide", + }, + ["Dungeon Slide"] = { + "Dungeon Mirror", + "Dungeon Strong Eyes", + "Dungeon Escape Lower", + }, + ["Dungeon Strong Eyes"] = { + "Dungeon Slide", + "Dungeon => Castle", + }, + ["Dungeon => Castle"] = { + "Dungeon Mirror", + "Dungeon Strong Eyes", + "Castle Main", + }, + ["Dungeon Escape Lower"] = { + "Dungeon Slide", + "Dungeon Escape Upper", + "Underbelly => Dungeon", + }, + ["Dungeon Escape Upper"] = { + "Dungeon Escape Lower", + "Theatre Outside Scythe Corridor", + }, + + ["Castle Main"] = { + "Dungeon => Castle", + "Keep Main", + "Empty Bailey", + "Library Main", + "Theatre Pillar", + "Castle Spiral Climb", + }, + ["Castle Spiral Climb"] = { + "Castle Main", + "Castle High Climb", + "Castle By Scythe Corridor", + }, + ["Castle High Climb"] = { + }, + ["Castle By Scythe Corridor"] = { + "Castle Spiral Climb", + "Castle High Climb", + "Castle => Theatre (Front)", + }, + ["Castle => Theatre (Front)"] = { + "Castle By Scythe Corridor", + "Castle Moon Room", + "Theatre Main", + }, + ["Castle Moon Room"] = { + }, + + ["Library Main"] = { + "Library Locked", + "Library Greaves", + "Library Top", + }, + ["Library Locked"] = { + }, + ["Library Greaves"] = { + "Library Top", + }, + ["Library Top"] = { + "Library Greaves", + }, + + ["Keep Main"] = { + "Keep Locked Room", + "Keep Sunsetter", + "Keep Path To Throne", + "Keep => Underbelly", + "Theatre Outside Scythe Corridor", + }, + ["Keep Locked Room"] = { + "Keep Sunsetter", + }, + ["Keep Sunsetter"] = { + }, + ["Keep => Underbelly"] = { + "Keep Main", + "Underbelly Hole", + }, + ["Keep Path To Throne"] = { + }, + + ["Empty Bailey"] = { + "Castle Main", + "Tower Remains", + "Theatre Pillar", + }, + ["Tower Remains"] = { + "Underbelly Little Guy", + "The Great Door", + }, + ["Underbelly => Dungeon"] = { + "Dungeon Escape Lower", + "Underbelly Light Pillar", + "Underbelly Ascendant Light", + }, + ["Underbelly Light Pillar"] = { + "Underbelly Main Upper", + "Underbelly => Dungeon", + "Underbelly Ascendant Light", + }, + ["Underbelly Ascendant Light"] = { + "Underbelly Light Pillar", + "Underbelly => Dungeon", + }, + ["Underbelly Main Lower"] = { + "Underbelly Little Guy", + "Underbelly Hole", + "Underbelly By Heliacal", + "Underbelly Main Upper", + }, + ["Underbelly Main Upper"] = { + "Underbelly Main Lower", + "Underbelly Light Pillar", + "Underbelly By Heliacal", + }, + ["Underbelly By Heliacal"] = { + "Underbelly Main Upper", + }, + ["Underbelly Little Guy"] = { + "Empty Bailey", + "Underbelly Main Lower", + }, + ["Underbelly => Keep"] = { + "Keep => Underbelly", + "Underbelly Hole", + }, + ["Underbelly Hole"] = { + "Underbelly Main Lower", + "Underbelly => Keep", + }, + + ["Theatre Main"] = { + "Keep Main", + }, + ["Theatre Pillar"] = { + "Theatre Main", + }, + ["Theatre Outside Scythe Corridor"] = { + "Theatre Main", + }, + + ["The Great Door"] = { + }, +} diff --git a/scripts/logic/rules/base.lua b/scripts/logic/rules/base.lua new file mode 100644 index 0000000..a755fc2 --- /dev/null +++ b/scripts/logic/rules/base.lua @@ -0,0 +1,207 @@ +PseudoregaliaRulesHelpers = {} -- rules base. See normal, hard, expert and lunatic for finished rules. + + +local free = function(state) return true end +local no = function(state) return false end + +function PseudoregaliaRulesHelpers.new(cls, definition) + local self = {} + self. definition = definition -- this is equivalent to multiworld in AP + self.region_rules = { + ["Empty Bailey -> Castle Main"] = free, + ["Empty Bailey -> Theatre Pillar"] = free, + ["Empty Bailey -> Tower Remains"] = function(state) + return self:has_gem(state) or state:has_all({"Slide", "Sunsetter"}) or self:get_kicks(state, 1) + end, + ["Tower Remains -> Underbelly Little Guy"] = function(state) + return self:has_plunge(state) + end, + ["Tower Remains -> The Great Door"] = function(state) + return self:has_gem(state) and self:get_kicks(state, 3) + end, + ["Theatre Main -> Keep Main"] = function(state) + return self:has_gem(state) + end, + ["Theatre Pillar -> Theatre Main"] = function(state) + return state:has_all({"Sunsetter", "Cling Gem"}) or self:has_plunge(state) and self:get_kicks(state, 4) + end, + ["Theatre Outside Scythe Corridor -> Theatre Main"] = function(state) + return self:has_gem(state) and (self:get_kicks(state, 3) or self:can_slidejump(state)) + end, + } + self.location_rules = { + ["Empty Bailey - Solar Wind"] = function(state) + return self:has_slide(state) + end, + ["Empty Bailey - Cheese Bell"] = function(state) + return (self:can_slidejump(state) and self:get_kicks(state, 1) and self:has_plunge(state) + or self:can_slidejump(state) and self:has_gem(state) + or self:get_kicks(state, 3) and self:has_plunge(state)) + end, + ["Empty Bailey - Inside Building"] = function(state) + return self:has_slide(state) + end, + ["Empty Bailey - Center Steeple"] = function(state) + return self:get_kicks(state, 3) or state:has_all({"Sunsetter", "Slide"}) + end, + ["Empty Bailey - Guarded Hand"] = function(state) + return self:has_plunge(state) or self:has_gem(state) or self:get_kicks(state, 3) + end, + ["Twilight Theatre - Soul Cutter"] = function(state) + return self:can_strikebreak(state) + end, + ["Twilight Theatre - Corner Beam"] = function(state) + return (self:has_gem(state) and self:get_kicks(state, 3) + or self:has_gem(state) and self:can_slidejump(state) + or self:get_kicks(state, 3) and self:can_slidejump(state)) + end, + ["Twilight Theatre - Locked Door"] = function(state) + return self:has_small_keys(state) and ( + self:has_gem(state) or self:get_kicks(state, 3) + ) + end, + ["Twilight Theatre - Back Of Auditorium"] = function(state) + return self:get_kicks(state, 3) or self:has_gem(state) + end, + ["Twilight Theatre - Murderous Goat"] = free, + ["Twilight Theatre - Center Stage"] = function(state) + return self:can_soulcutter(state) and self:has_gem(state) and ( + self:can_slidejump(state) or self:get_kicks(state, 1)) + end, + ["Tower Remains - Cling Gem"] = function(state) + return self:get_kicks(state, 3) + end, + ["Tower Remains - Atop The Tower"] = free, + } + self.required_small_keys = 6 + cls.__index = cls + setmetatable(self, cls) + return self +end + +function PseudoregaliaRulesHelpers:has_breaker(state) + return state:has_any({"Dream Breaker", "Progressive Dream Breaker"}) +end + +function PseudoregaliaRulesHelpers:has_slide(state) + return state:has_any({"Slide", "Progressive Slide"}) +end + +function PseudoregaliaRulesHelpers:has_plunge(state) + return state:has("Sunsetter") +end + +function PseudoregaliaRulesHelpers:has_gem(state) + return state:has("Clung Gem") +end + +function PseudoregaliaRulesHelpers:can_bounce(state) + return self:has_breaker(state) and state:has("Ascendant Light") +end + +function PseudoregaliaRulesHelpers:can_attack(state) + error("can_attack has to be overridden") +end + +function PseudoregaliaRulesHelpers:get_kicks(state, count) + local kicks = 0 + if state:has("Sun Greaves") then + kicks = kicks + 3 + end + kicks = kicks + state:count("Heliacal Power") + kicks = kicks + state:count("Air Kick") + return kicks >= count +end + +function PseudoregaliaRulesHelpers:kick_or_plunge(state, count) + local total = 0 + if state:has("Sun Greaves") then + total = total + 3 + end + if state:has("Sunsetter") then + total = total + 1 + end + total = total + state:count("Heliacal Power") + total = total + state:count("Air Kick") + return total >= count +end + +function PseudoregaliaRulesHelpers:has_small_keys(state) + if not self:can_attack(state) then + return false + end + return state:count("Small Key") >= self.required_small_keys +end + +function PseudoregaliaRulesHelpers:navigate_darkrooms(state) + return self:has_breaker(state) or state:has("Ascendant Light") +end + +function PseudoregaliaRulesHelpers:can_slidejump(state) + return (state:has_all({"Slide", "Solar Wind"}) + or state:count("Progressive Slide") >= 2) +end + +function PseudoregaliaRulesHelpers:can_strikebreak(state) + return (state:has_all({"Dream Breaker", "Strikebreak"}) + or state:count("Progressive Dream Breaker") >= 2) +end + +function PseudoregaliaRulesHelpers:can_soulcutter(state) + return (state:has_all({"Dream Breaker", "Strikebreak", "Soul Cutter"}) + or state:count("Progressive Dream Breaker") >= 3) +end + +function PseudoregaliaRulesHelpers:knows_obscure(state) + error("knows_obscure has to be overridden") +end + +function PseudoregaliaRulesHelpers:set_pseudoregalia_rules() + local split_kicks = false -- TODO: load from code or slot data + local obscure_logic = false -- TODO: as above + local logic_level = 0 -- TODO: as above + + if obscure_logic then + self.knows_obscure = free + self.can_attack = function(self, state) return self:has_breaker(state) or self:has_plunge(state) end + else + self.knows_obscure = no + self.can_attack = function(self, state) return self:has_breaker(state) end + end + + if logic_level == 0 then + self.required_small_keys = 7 + end + + for name, rule in pairs(self.region_rules) do + local entrance = self.definition:get_entrance(name) + if entrance then + entrance:set_rule(rule) + else + print("Missing entrance: " .. name) + end + end + for name, rule in pairs(self.location_rules) do + local library = name:find("^Listless Library") ~= nil + if (not library or + not (split_kicks and name.find("Greaves$") ~= nil) + and not (not split_kicks and tonumber(name:sub(-1)) ~= nil)) then + local location = self.definition:get_location(name) + if location then + location:set_rule(rule) + else + print("Missing location: " .. name) + end + end + end + + self.definition:get_location("D S T RT ED M M O Y"):set_rule(function(state) + return state:has_all({ + "Major Key - Empty Bailey", + "Major Key - The Underbelly", + "Major Key - Tower Remains", + "Major Key - Sansa Keep", + "Major Key - Twilight Theatre", + }) + end) +end diff --git a/scripts/logic/rules/expert.lua b/scripts/logic/rules/expert.lua new file mode 100644 index 0000000..8b2798b --- /dev/null +++ b/scripts/logic/rules/expert.lua @@ -0,0 +1 @@ +-- TODO: require base diff --git a/scripts/logic/rules/hard.lua b/scripts/logic/rules/hard.lua new file mode 100644 index 0000000..8b2798b --- /dev/null +++ b/scripts/logic/rules/hard.lua @@ -0,0 +1 @@ +-- TODO: require base diff --git a/scripts/logic/rules/lunatic.lua b/scripts/logic/rules/lunatic.lua new file mode 100644 index 0000000..8b2798b --- /dev/null +++ b/scripts/logic/rules/lunatic.lua @@ -0,0 +1 @@ +-- TODO: require base diff --git a/scripts/logic/rules/normal.lua b/scripts/logic/rules/normal.lua new file mode 100644 index 0000000..3ade0e8 --- /dev/null +++ b/scripts/logic/rules/normal.lua @@ -0,0 +1,363 @@ +-- TODO: require base + +PseudoregaliaNormalRules = PseudoregaliaRulesHelpers:new(nil) + +function PseudoregaliaNormalRules.new(cls, definition) + local self = PseudoregaliaRulesHelpers.new(cls, definition) + + for k, v in pairs({ + ["Dungeon Mirror -> Dungeon Slide"] = function(state) + return self:can_attack(state) + end, + ["Dungeon Slide -> Dungeon Mirror"] = function(state) + return self:can_attack(state) + end, + ["Dungeon Slide -> Dungeon Strong Eyes"] = function(state) + return self:has_slide(state) + end, + ["Dungeon Slide -> Dungeon Escape Lower"] = function(state) + return self:can_attack(state) and self:navigate_darkrooms(state) + end, + ["Dungeon Strong Eyes -> Dungeon Slide"] = function(state) + return self:has_slide(state) + end, + ["Dungeon Strong Eyes -> Dungeon => Castle"] = function(state) + return self:has_small_keys(state) + end, + ["Dungeon => Castle -> Dungeon Strong Eyes"] = function(state) + return self:has_small_keys(state) + end, + ["Dungeon Escape Lower -> Dungeon Slide"] = function(state) + return self:can_attack(state) + end, + ["Dungeon Escape Lower -> Dungeon Escape Upper"] = function(state) + return (self:can_bounce(state) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:get_kicks(state, 3)) + end, + ["Dungeon Escape Upper -> Theatre Outside Scythe Corridor"] = function(state) + return (self:can_bounce(state) + or self:kick_or_plunge(state, 1) + or self:has_gem(state)) + end, + ["Castle Main -> Library Main"] = function(state) + return self:has_breaker(state) or self:knows_obscure(state) and self:can_attack(state) + end, + ["Castle Main -> Theatre Pillar"] = function(state) + return self:has_gem(state) and self:kick_or_plunge(state, 1) or self:kick_or_plunge(state, 2) + end, + ["Castle Main -> Castle Spiral Climb"] = function(state) + return self:get_kicks(state, 2) or self:has_gem(state) and self:has_plunge(state) + end, + ["Castle Spiral Climb -> Castle High Climb"] = function(state) + return (self:has_gem(state) + or self:get_kicks(state, 3) and self:has_plunge(state) + or self:has_breaker(state) and self:get_kicks(state, 1) + or self:knows_obscure(state) and self:has_plunge(state) and self:get_kicks(state, 1)) + end, + ["Castle Spiral Climb -> Castle By Scythe Corridor"] = function(state) + return self:has_gem(state) + end, + ["Castle By Scythe Corridor -> Castle Spiral Climb"] = function(state) + return self:has_gem(state) or self:get_kicks(state, 4) and self:has_plunge(state) + end, + ["Castle By Scythe Corridor -> Castle High Climb"] = function(state) + return (self:has_gem(state) + or self:get_kicks(state, 4) + or self:get_kicks(state, 2) and self:has_plunge(state) + or self:get_kicks(state, 1) and self:has_plunge(state) and self:can_slidejump(state)) + end, + ["Castle By Scythe Corridor -> Castle => Theatre (Front)"] = function(state) + return self:has_gem(state) and self:kick_or_plunge(state, 2) + end, + ["Castle => Theatre (Front) -> Castle By Scythe Corridor"] = function(state) + return (self:has_gem(state) + or self:can_slidejump(state) and self:get_kicks(state, 1) + or self:get_kicks(state, 4)) + end, + ["Castle => Theatre (Front) -> Castle Moon Room"] = function(state) + return self:has_gem(state) or self:can_slidejump(state) and self:kick_or_plunge(state, 1) + end, + ["Library Main -> Library Locked"] = function(state) + return self:has_small_keys(state) + end, + ["Library Main -> Library Greaves"] = function(state) + return self:has_slide(state) + end, + ["Library Main -> Library Top"] = function(state) + return (self:kick_or_plunge(state, 4) + or self:knows_obscure(state) and self:get_kicks(state, 1) and self:has_plunge(state)) + end, + ["Library Greaves -> Library Top"] = function(state) + return self:has_gem(state) or self:get_kicks(state, 2) + end, + ["Library Top -> Library Greaves"] = function(state) + return (self:has_gem(state) and self:kick_or_plunge(state, 1) + or self:get_kicks(state, 3) and self:has_plunge(state) + or self:get_kicks(state, 3) and self:can_bounce(state)) + end, + ["Keep Main -> Keep Locked Room"] = function(state) + return (self:has_small_keys(state) + or self:get_kicks(state, 3) + or self:has_plunge(state) and self:get_kicks(state, 1) + or self:has_gem(state) and self:has_plunge(state) + or self:has_gem(state) and self:get_kicks(state, 1)) + end, + ["Keep Main -> Keep Sunsetter"] = function(state) + return self:has_gem(state) + end, + ["Keep Main -> Keep => Underbelly"] = function(state) + return self:kick_or_plunge(state, 1) or self:has_gem(state) + end, + ["Keep Main -> Theatre Outside Scythe Corridor"] = function(state) + return (self:has_gem(state) + or self:get_kicks(state, 1) + or self:can_bounce(state) + or self:can_slidejump(state)) + end, + ["Keep Main -> Keep Path To Throne"] = function(state) + return self:has_breaker(state) + end, + ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) + return (self:can_bounce(state) + or self:has_gem(state) + or self:get_kicks(state, 2) + or self:get_kicks(state, 1) and self:can_slidejump(state) + or self:knows_obscure(state) and self:has_breaker(state)) + end, + ["Underbelly Light Pillar -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) or self:kick_or_plunge(state, 4) + end, + ["Underbelly Light Pillar -> Underbelly Ascendant Light"] = function(state) + return self:has_breaker(state) and ( + self:has_plunge(state) or self:get_kicks(state, 4) + ) or self:knows_obscure(state) and self:has_gem(state) and self:get_kicks(state, 1) + end, + ["Underbelly Ascendant Light -> Underbelly => Dungeon"] = function(state) + return (self:can_bounce(state) + or self:has_gem(state) + or self:get_kicks(state, 2) + or self:get_kicks(state, 1) and self:can_slidejump(state)) + end, + ["Underbelly Main Lower -> Underbelly Hole"] = function(state) + return self:has_plunge(state) and ( + self:get_kicks(state, 1) or self:can_slidejump(state) or self:can_attack(state) + ) + end, + ["Underbelly Main Lower -> Underbelly By Heliacal"] = function(state) + return self:has_slide(state) and self:has_plunge(state) + end, + ["Underbelly Main Lower -> Underbelly Main Upper"] = function(state) + return self:has_plunge(state) and ( + self:get_kicks(state, 2) + or self:get_kicks(state, 1) and self:has_gem(state) + ) + end, + ["Underbelly Main Upper -> Underbelly Light Pillar"] = function(state) + return (self:has_breaker(state) and self:has_plunge(state) + or self:has_breaker(state) and self:get_kicks(state, 2) + or self:has_gem(state) and ( + self:get_kicks(state, 2) and self:has_plunge(state) + or self:get_kicks(state, 4) + )) + end, + ["Underbelly Main Upper -> Underbelly By Heliacal"] = function(state) + return self:has_breaker(state) and ( + state:has("Ascendant Light") + or self:can_slidejump(state) and self:get_kicks(state, 3) + or self:has_gem(state) and self:get_kicks(state, 2)) + end, + ["Underbelly By Heliacal -> Underbelly Main Upper"] = function(state) + return self:has_breaker(state) or self:knows_obscure(state) and ( + self:get_kicks(state, 1) + or self:has_gem(state) and self:can_slidejump(state)) + end, + ["Underbelly Little Guy -> Underbelly Main Lower"] = function(state) + return self:has_gem(state) or self:kick_or_plunge(state, 1) + end, + ["Underbelly => Keep -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + end, + ["Underbelly Hole -> Underbelly Main Lower"] = function(state) + return (self:get_kicks(state, 2) + or self:has_gem(state) and self:can_slidejump(state) + or self:can_attack(state)) + end, + ["Underbelly Hole -> Underbelly => Keep"] = function(state) + return self:has_slide(state) + end, + }) do + self.region_rules[k] = v + end + + for k, v in pairs({ + ["Dilapidated Dungeon - Dark Orbs"] = function(state) + return (self:has_gem(state) and self:can_bounce(state) + or self:has_gem(state) and self:kick_or_plunge(state, 3) + or self:get_kicks(state, 2) and self:can_bounce(state) + or self:can_slidejump(state) and self:get_kicks(state, 1) and self:can_bounce(state)) + end, + ["Dilapidated Dungeon - Past Poles"] = function(state) + return (self:has_gem(state) and self:kick_or_plunge(state, 1) + or self:get_kicks(state, 3)) + end, + ["Dilapidated Dungeon - Rafters"] = function(state) + return (self:kick_or_plunge(state, 3) + or self:knows_obscure(state) and self:can_bounce(state) and self:has_gem(state)) + end, + ["Dilapidated Dungeon - Strong Eyes"] = function(state) + return (self:has_breaker(state) + or self:knows_obscure(state) and ( + self:has_gem(state) and self:get_kicks(state, 1) and self:has_plunge(state) + or self:has_gem(state) and self:get_kicks(state, 3))) + end, + ["Castle Sansa - Alcove Near Dungeon"] = function(state) + return (self:has_gem(state) and self:kick_or_plunge(state, 1) + or self:kick_or_plunge(state, 2)) + end, + ["Castle Sansa - Balcony"] = function(state) + return (self:has_gem(state) + or self:kick_or_plunge(state, 3) + or self:can_slidejump(state) and self:kick_or_plunge(state, 2)) + end, + ["Castle Sansa - Corner Corridor"] = function(state) + return (self:has_gem(state) + or self:get_kicks(state, 4)) + end, + ["Castle Sansa - Floater In Courtyard"] = function(state) + return (self:can_bounce(state) and self:has_plunge(state) + or self:can_bounce(state) and self:get_kicks(state, 2) + or self:has_gem(state) and self:get_kicks(state, 2) + or self:has_gem(state) and self:has_plunge(state) + or self:get_kicks(state, 4) + or self:knows_obscure(state) and self:can_bounce(state) and self:get_kicks(state, 1) + or self:knows_obscure(state) and self:has_gem(state) and self:get_kicks(state, 1)) + end, + ["Castle Sansa - Locked Door"] = function(state) + return self:has_small_keys(state) + end, + ["Castle Sansa - Platform In Main Halls"] = function(state) + return (self:has_plunge(state) + or self:has_gem(state) + or self:get_kicks(state, 2)) + end, + ["Castle Sansa - Tall Room Near Wheel Crawlers"] = function(state) + return (self:has_gem(state) and self:kick_or_plunge(state, 1) + or self:get_kicks(state, 2)) + end, + ["Castle Sansa - Wheel Crawlers"] = function(state) + return (self:can_bounce(state) + or self:has_gem(state) + or self:get_kicks(state, 2) + or self:get_kicks(state, 1) and self:can_slidejump(state) + or self:knows_obscure(state) and self:has_plunge(state)) + end, + ["Castle Sansa - High Climb From Courtyard"] = function(state) + return (self:get_kicks(state, 2) + or self:has_gem(state) and self:has_plunge(state) + or self:has_breaker(state) and self:get_kicks(state, 1) + or self:knows_obscure(state) and self:has_plunge(state) and self:get_kicks(state, 1)) + end, + ["Castle Sansa - Alcove Near Scythe Corridor"] = function(state) + return (self:has_gem(state) and self:get_kicks(state, 1) and self:has_plunge(state) + or self:kick_or_plunge(state, 4)) + end, + ["Castle Sansa - Near Theatre Front"] = function(state) + return (self:get_kicks(state, 4) + or self:get_kicks(state, 2) and self:has_plunge(state)) + end, + ["Listless Library - Sun Greaves"] = function(state) + return (self:has_breaker(state) + or self:knows_obscure(state) and self:has_plunge(state)) + end, + ["Listless Library - Sun Greaves 1"] = function(state) + return (self:has_breaker(state) + or self:knows_obscure(state) and self:has_plunge(state)) + end, + ["Listless Library - Sun Greaves 2"] = function(state) + return (self:has_breaker(state) + or self:knows_obscure(state) and self:has_plunge(state)) + end, + ["Listless Library - Sun Greaves 3"] = function(state) + return (self:has_breaker(state) + or self:knows_obscure(state) and self:has_plunge(state)) + end, + ["Listless Library - Upper Back"] = function(state) + return ((self:has_breaker(state) or self:knows_obscure(state) and self:has_plunge(state)) + and ( + self:has_gem(state) and self:kick_or_plunge(state, 1) + or self:kick_or_plunge(state, 2))) + end, + ["Listless Library - Locked Door Across"] = function(state) + return (self:has_gem(state) + or self:get_kicks(state, 1) + or self:can_slidejump(state)) + end, + ["Listless Library - Locked Door Left"] = function(state) + return (self:has_gem(state) + or self:can_slidejump(state) and self:get_kicks(state, 1) + or self:kick_or_plunge(state, 3)) + end, + ["Sansa Keep - Near Theatre"] = function(state) + return (self:kick_or_plunge(state, 1) + or self:has_gem(state)) + end, + ["Sansa Keep - Levers Room"] = function(state) + return self:can_attack(state) + end, + ["Sansa Keep - Sunsetter"] = function(state) + return self:can_attack(state) + end, + ["Sansa Keep - Strikebreak"] = function(state) + return ((self:can_attack(state) and (self:has_slide(state) or self:can_strikebreak(state))) + and ( + self:has_gem(state) + or self:has_plunge(state) and self:get_kicks(state, 1) + or self:get_kicks(state, 3))) + end, + ["Sansa Keep - Lonely Throne"] = function(state) + return ((self:has_gem(state) and ( + self:has_plunge(state) and self:get_kicks(state, 1) + or self:has_plunge(state) and state:has("Ascendant Light") + or self:get_kicks(state, 1) and state:has("Ascendant Light")) + ) or (state:has("Ascendant Light") and self:kick_or_plunge(state, 4))) + end, + ["The Underbelly - Rafters Near Keep"] = function(state) + return (self:has_plunge(state) + or self:get_kicks(state, 2) + or self:can_bounce(state)) + end, + ["The Underbelly - Locked Door"] = function(state) + return self:has_small_keys(state) + end, + ["The Underbelly - Main Room"] = function(state) + return (self:has_plunge(state) + or self:has_gem(state) + or self:get_kicks(state, 2) + or self:can_slidejump(state) and self:get_kicks(state, 1)) + end, + ["The Underbelly - Alcove Near Light"] = function(state) + return (self:can_attack(state) + or self:has_gem(state) + or self:get_kicks(state, 4) + or self:get_kicks(state, 3) and self:can_slidejump(state)) + end, + ["The Underbelly - Building Near Little Guy"] = function(state) + return self:has_plunge(state) or self:get_kicks(state, 3) + end, + ["The Underbelly - Strikebreak Wall"] = function(state) + return (self:can_bounce(state) + or self:get_kicks(state, 4) + or self:get_kicks(state, 2) and self:has_plunge(state)) + end, + ["The Underbelly - Surrounded By Holes"] = function(state) + return self:can_soulcutter(state) and ( + self:can_bounce(state or self:get_kicks(state, 2)) + ) or self:can_slidejump(state) and self:has_gem(state) and self:get_kicks(state, 1) + end, + }) do + self.location_rules[k] = v + end + + return self +end