From 51b1e59934aaedc7585a889e2bd705102d832196 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Thu, 21 Dec 2023 00:34:46 +0100 Subject: [PATCH 01/24] initial ap-style logic rework --- locations/locations.json | 396 ++++++++------- scripts/logic/helper.lua | 241 +++++++++ scripts/logic/locations.lua | 199 ++++++++ scripts/logic/logic.lua | 834 ++++---------------------------- scripts/logic/regions.lua | 155 ++++++ scripts/logic/rules/base.lua | 207 ++++++++ scripts/logic/rules/expert.lua | 1 + scripts/logic/rules/hard.lua | 1 + scripts/logic/rules/lunatic.lua | 1 + scripts/logic/rules/normal.lua | 363 ++++++++++++++ 10 files changed, 1474 insertions(+), 924 deletions(-) create mode 100644 scripts/logic/helper.lua create mode 100644 scripts/logic/locations.lua create mode 100644 scripts/logic/regions.lua create mode 100644 scripts/logic/rules/base.lua create mode 100644 scripts/logic/rules/expert.lua create mode 100644 scripts/logic/rules/hard.lua create mode 100644 scripts/logic/rules/lunatic.lua create mode 100644 scripts/logic/rules/normal.lua 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 From 879629e3323747ba78f0a6ea95b4db2aed595675 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:03:29 +0100 Subject: [PATCH 02/24] cling --- scripts/logic/rules/base.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/logic/rules/base.lua b/scripts/logic/rules/base.lua index a755fc2..858dfde 100644 --- a/scripts/logic/rules/base.lua +++ b/scripts/logic/rules/base.lua @@ -92,7 +92,7 @@ function PseudoregaliaRulesHelpers:has_plunge(state) end function PseudoregaliaRulesHelpers:has_gem(state) - return state:has("Clung Gem") + return state:has("Cling Gem") end function PseudoregaliaRulesHelpers:can_bounce(state) From bb17b7517126e9289990615639c161a8e0f602c6 Mon Sep 17 00:00:00 2001 From: Brooty Johnson <83629348+Br00ty@users.noreply.github.com> Date: Fri, 22 Dec 2023 03:15:43 -0500 Subject: [PATCH 03/24] import hard, expert, lunatic logic --- scripts/logic/logic.lua | 4 + scripts/logic/rules/expert.lua | 433 +++++++++++++++++++++++++++++++- scripts/logic/rules/hard.lua | 419 ++++++++++++++++++++++++++++++ scripts/logic/rules/lunatic.lua | 429 +++++++++++++++++++++++++++++++ 4 files changed, 1284 insertions(+), 1 deletion(-) diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index a963c4c..e3ca319 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -114,7 +114,11 @@ end function set_rules() -- TODO: difficulty + -- TODO: setup difficulty toggles/addwatch for codes normal, hard, expert, lunatic PseudoregaliaNormalRules:new(def):set_pseudoregalia_rules() + PseudoregaliaHardRules:new(def):set_pseudoregalia_rules() + PseudoregaliaExpertRules:new(def):set_pseudoregalia_rules() + PseudoregaliaLunaticRules:new(def):set_pseudoregalia_rules() end create_regions() diff --git a/scripts/logic/rules/expert.lua b/scripts/logic/rules/expert.lua index 8b2798b..963207c 100644 --- a/scripts/logic/rules/expert.lua +++ b/scripts/logic/rules/expert.lua @@ -1 +1,432 @@ --- TODO: require base +-- TODO = require base + + +local free = function(state) return true end +local no = function(state) return false end + +PseudoregaliaExpertRules = PseudoregaliaRulesHelpers:new(nil) + +function PseudoregaliaExpertRules.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 Mirror" = function(state) True, + ["Dungeon => Castle -> Dungeon Strong Eyes"] = function(state) + return self:has_small_keys(state) + end, + --# "Dungeon => Castle -> Castle Main" = function(state) True, + ["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:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) and self:get_kicks(state, 1) + end, + --# "Dungeon Escape Lower -> Underbelly => Dungeon" = function(state) True, + ["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) + or self:has_slide(state) + end, + --# "Dungeon Escape Upper -> Theatre Outside Scythe Corridor" = function(state) True, + --# "Castle Main -> Dungeon => Castle" = function(state) True, + --# "Castle Main -> Keep Main" = function(state) True, + --# "Castle Main -> Empty Bailey" = function(state) True, + ["Castle Main -> Library Main"] = function(state) + return self:can_attack(state) + end, + ["Castle Main -> Theatre Pillar"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:kick_or_plunge(state, 1) + end, + ["Castle Main -> Castle Spiral Climb"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:kick_or_plunge(state, 2) + end, + --# "Castle Spiral Climb -> Castle Main" = function(state) True, + --# "Castle Spiral Climb -> Castle High Climb" = function(state) True, + --# Anything that gets you into spiral climb can get from there to high climb + ["Castle Spiral Climb -> Castle By Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 4) + end, + ["Castle By Scythe Corridor -> Castle Spiral Climb"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + end, + ["Castle By Scythe Corridor -> Castle => Theatre (Front)"] = function(state) + return self:has_gem(state) + or self:has_slide(state) and self:get_kicks(state, 2) + end, + ["Castle By Scythe Corridor -> Castle High Climb"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:kick_or_plunge(state, 2) + end, + ["Castle => Theatre (Front) -> Castle By Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:get_kicks(state, 3) + end, + ["Castle => Theatre (Front) -> Castle Moon Room"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:get_kicks(state, 4) + end, + --# "Castle => Theatre (Front) -> Theatre Main" = function(state) True, + ["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:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) + end, + ["Library Greaves -> Library Top"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Library Top -> Library Greaves"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) + end, + --# "Keep Main -> Keep Locked Room" = function(state) True, + --# "Keep Main -> Keep Sunsetter" = function(state) True, + ["Keep Main -> Keep => Underbelly"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + or self:has_slide(state) + end, + ["Keep Locked Room -> Keep Sunsetter"] = free, + ["Keep => Underbelly -> Underbelly Hole"] = free, + ["Keep Main -> Theatre Outside Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + or self:can_bounce(state) + end, + ["Keep Main -> Keep Path To Throne"] = function(state) + return self:has_breaker(state) + end, + --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, + --# "Keep => Underbelly -> Keep Main" = function(state) True, + --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, + --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, + --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, + ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) + return self:has_breaker(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:get_kicks(state, 1) and self:has_slide(state) + end, + --# "Underbelly Light Pillar -> Underbelly Main Upper" = function(state) True, + ["Underbelly Light Pillar -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 4) + or self:has_plunge(state) and self:get_kicks(state, 2) + or self:has_slide(state) and self:kick_or_plunge(state, 2) + end, + ["Underbelly Light Pillar -> Underbelly Ascendant Light"] = function(state) + return self:has_breaker(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 2) + or self:get_kicks(state, 1) and self:has_gem(state) + or self:has_slide(state)) + or self:has_plunge(state) + and ( + self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state)) + end, + --# "Underbelly Ascendant Light -> Underbelly Light Pillar" = function(state) True, + ["Underbelly Ascendant Light -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) and self:get_kicks(state, 1) + end, + --# "Underbelly Main Lower -> Underbelly Little Guy" = function(state) True, + ["Underbelly Main Lower -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + and ( + self:get_kicks(state, 1) + or self:has_gem(state) + or self:has_slide(state) + or self:can_attack(state)) + end, + ["Underbelly Main Lower -> Underbelly By Heliacal"] = function(state) + return self:has_slide(state) + end, + ["Underbelly Main Lower -> Underbelly Main Upper"] = function(state) + return self:has_plunge(state) and self:get_kicks(state, 2) + or self:has_gem(state) and self:kick_or_plunge(state, 1) + or self:get_kicks(state, 4) + or self:has_slide(state) + and ( + self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:get_kicks(state, 1) and self:has_breaker(state)) + end, + --# "Underbelly Main Upper -> Underbelly Main Lower" = function(state) True, + ["Underbelly Main Upper -> Underbelly Light Pillar"] = function(state) + return self:has_breaker(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 2) + or self:has_slide(state)) + or self:has_slide(state) and self:get_kicks(state, 1) + or self:has_plunge(state) and self:get_kicks(state, 2) + or self:has_gem(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 2)) + end, + ["Underbelly Main Upper -> Underbelly By Heliacal"] = function(state) + return self:has_breaker(state) + and ( + state:has("Ascendant Light") + or self:has_gem(state) + or self:has_plunge(state) and self:get_kicks(state, 3)) + end, + ["Underbelly By Heliacal -> Underbelly Main Upper"] = function(state) + return self:can_attack(state) + or self:has_gem(state) + or self:get_kicks(state, 2) + end, + --# "Underbelly Little Guy -> Empty Bailey" = function(state) True, + --# "Underbelly Little Guy -> Underbelly Main Lower" = function(state) True, + --# "Underbelly => Keep -> Keep => Underbelly" = function(state) True, + ["Underbelly => Keep -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + end, + ["Underbelly Hole -> Underbelly Main Lower"] = function(state) + return self:get_kicks(state, 1) + or self:has_gem(state) + or self:can_attack(state) + or self:has_slide(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 - Dream Breaker" = function(state) True, + --# "Dilapidated Dungeon - Slide" = function(state) True, + --# "Dilapidated Dungeon - Alcove Near Mirror" = function(state) True, + ["Dilapidated Dungeon - Dark Orbs"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) and self:can_bounce(state) + or self:get_kicks(state, 3) and self:has_plunge(state) + or self:has_slide(state) and self:get_kicks(state, 1) + or self:has_slide(state) and self:can_bounce(state) + end, + ["Dilapidated Dungeon - Past Poles"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) + end, + ["Dilapidated Dungeon - Rafters"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:can_bounce(state) and self:get_kicks(state, 1) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + end, + ["Dilapidated Dungeon - Strong Eyes"] = function(state) + return self:has_breaker(state) + or self:has_gem(state) + or self:has_slide(state) and self:get_kicks(state, 1) + end, + --# "Castle Sansa - Indignation" = function(state) True, + ["Castle Sansa - Floater In Courtyard"] = function(state) + return self:can_bounce(state) + and ( + self:kick_or_plunge(state, 1) + or self:has_slide(state)) + or self:has_slide(state) and self:get_kicks(state, 1) + or self:get_kicks(state, 3) + or self:has_gem(state) + end, + ["Castle Sansa - Locked Door"] = function(state) + return self:has_small_keys(state) + end, + ["Castle Sansa - Platform In Main Halls"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + or self:has_slide(state) + end, + ["Castle Sansa - Tall Room Near Wheel Crawlers"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Alcove Near Dungeon"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Balcony"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:has_plunge(state) and self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Corner Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 2) and self:has_slide(state) + end, + ["Castle Sansa - Wheel Crawlers"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Alcove Near Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 3) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + end, + ["Castle Sansa - Near Theatre Front"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:get_kicks(state, 4) + or self:get_kicks(state, 2) 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) + or self:can_attack(state) and self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Listless Library - Sun Greaves"] = function(state) + return self:can_attack(state) + end, + ["Listless Library - Sun Greaves 1"] = function(state) + return self:can_attack(state) + end, + ["Listless Library - Sun Greaves 2"] = function(state) + return self:can_attack(state) + end, + ["Listless Library - Sun Greaves 3"] = function(state) + return self:can_attack(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) + or self:kick_or_plunge(state, 2) + or self:has_slide(state)) + end, + ["Listless Library - Locked Door Across"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 1) + or self:has_slide(state) + end, + ["Listless Library - Locked Door Left"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + end, + ["Sansa Keep - Near Theatre"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + end, + --# "Sansa Keep - Alcove Near Locked Door" = function(state) True, + ["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:can_strikebreak(state) and self:kick_or_plunge(state, 1) + end, + ["Sansa Keep - Lonely Throne"] = function(state) + return self:has_gem(state) + or self:has_plunge(state) and self:get_kicks(state, 4) + or state:has("Ascendant Light") and self:kick_or_plunge(state, 3) + or self:has_slide(state) and self:get_kicks(state, 3) + end, + --# "The Underbelly - Ascendant Light" = function(state) True, + ["The Underbelly - Rafters Near Keep"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + or self:has_slide(state) + 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:has_slide(state) + end, + ["The Underbelly - Alcove Near Light"] = function(state) + return self:can_attack(state) + or self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_slide(state) + end, + ["The Underbelly - Building Near Little Guy"] = function(state) + return self:has_plunge(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["The Underbelly - Strikebreak Wall"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + or self:has_slide(state) and self:has_gem(state) + end, + ["The Underbelly - Surrounded By Holes"] = function(state) + return self:can_soulcutter(state) + and ( + self:can_bounce(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + ) + or self:has_gem(state) + or self:has_slide(state) and self:get_kicks(state, 1) + end, + }) do + self.location_rules[k] = v + end + + return self +end \ No newline at end of file diff --git a/scripts/logic/rules/hard.lua b/scripts/logic/rules/hard.lua index 8b2798b..321d753 100644 --- a/scripts/logic/rules/hard.lua +++ b/scripts/logic/rules/hard.lua @@ -1 +1,420 @@ -- TODO: require base + +PseudoregaliaHardRules = PseudoregaliaRulesHelpers:new(nil) + +function PseudoregaliaHardRules.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 Mirror" = function(state) True, -- not used atm? + ["Dungeon => Castle -> Dungeon Strong Eyes"] = function(state) + return self:has_small_keys(state) + end, + --# "Dungeon => Castle -> Castle Main" = function(state) True, -- not used atm? + ["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:has_gem(state) + or self:kick_or_plunge(state, 2) + end, + --# "Dungeon Escape Lower -> Underbelly => Dungeon" = function(state) True, -- not used atm? + ["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, + --# "Dungeon Escape Upper -> Theatre Outside Scythe Corridor" = function(state) True, -- not used atm? + --# "Castle Main -> Dungeon => Castle" = function(state) True, -- not used atm? + --# "Castle Main -> Keep Main" = function(state) True, -- not used atm? + --# "Castle Main -> Empty Bailey" = function(state) True, -- not used atm? + ["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) + or self:kick_or_plunge(state, 1) + end, + ["Castle Main -> Castle Spiral Climb"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:can_slidejump(state) and self:has_plunge(state) + end, + --# "Castle Spiral Climb -> Castle Main" = function(state) True, + ["Castle Spiral Climb -> Castle High Climb"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 3) + 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) + or self:knows_obscure(state) and self:can_attack(state) and self:can_slidejump(state) + 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, 3) + end, + ["Castle By Scythe Corridor -> Castle => Theatre (Front)"] = function(state) + return self:has_gem(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, 3) and self:has_breaker(state) + or self:get_kicks(state, 1) and self:has_plunge(state) + 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, 2) + or self:get_kicks(state, 4) + end, + --# "Castle => Theatre (Front) -> Theatre Main" = function(state) True, + ["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:has_gem(state) + or self:kick_or_plunge(state, 4) + or self:knows_obscure(state) and self:kick_or_plunge(state, 2) + end, + ["Library Greaves -> Library Top"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + end, + ["Library Top -> Library Greaves"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 2) and self:has_plunge(state) and self:can_bounce(state) + end, + --# "Keep Main -> Keep Locked Room" = function(state) True, + --# "Keep Main -> Keep Sunsetter" = function(state) True, + ["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, + --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, + --# "Keep => Underbelly -> Keep Main" = function(state) True, + --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, + --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, + --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, + ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(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 Main Upper" = function(state) True, + ["Underbelly Light Pillar -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 4) + or self:has_plunge(state) and self:get_kicks(state, 2) + end, + ["Underbelly Light Pillar -> Underbelly Ascendant Light"] = function(state) + return self:has_breaker(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 3)) + or self:knows_obscure(state) and self:has_plunge(state) + and ( + self:has_gem(state) + or self:get_kicks(state, 1) + or self:can_slidejump(state)) + end, + --# "Underbelly Ascendant Light -> Underbelly Light Pillar" = function(state) True, + ["Underbelly Ascendant Light -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:get_kicks(state, 1) and self:can_slidejump(state) + or self:knows_obscure(state) and self:has_slide(state) and self:get_kicks(state, 1) + end, + --# "Underbelly Main Lower -> Underbelly Little Guy" = function(state) True, + ["Underbelly Main Lower -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + and ( + self:get_kicks(state, 1) + or self:has_gem(state) + 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) + or self:knows_obscure(state) and self:get_kicks(state, 2)) + 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)) + or self:knows_obscure(state) and self:has_gem(state) and self:get_kicks(state, 1) + end, + --# "Underbelly Main Upper -> Underbelly Main Lower" = function(state) True, + ["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:has_plunge(state) + or self:get_kicks(state, 3)) + end, + ["Underbelly Main Upper -> Underbelly By Heliacal"] = function(state) + return self:has_breaker(state) + and ( + state:has("Ascendant Light") + or self:has_gem(state) + or self:has_plunge(state) and self:get_kicks(state, 3) + or self:can_slidejump(state) and self:get_kicks(state, 3)) + end, + ["Underbelly By Heliacal -> Underbelly Main Upper"] = function(state) + return self:has_breaker(state) and self:has_plunge(state) + or self:knows_obscure(state) + and ( + self:has_plunge(state) + or self:has_breaker(state) + or self:get_kicks(state, 4)) + end, + --# "Underbelly Little Guy -> Empty Bailey" = function(state) True, + --# "Underbelly Little Guy -> Underbelly Main Lower" = function(state) True, + --# "Underbelly => Keep -> Keep => Underbelly" = function(state) True, + ["Underbelly => Keep -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + end, + ["Underbelly Hole -> Underbelly Main Lower"] = function(state) + return self:get_kicks(state, 1) + or self:has_gem(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 - Dream Breaker" = function(state) True, + --# "Dilapidated Dungeon - Slide" = function(state) True, + --# "Dilapidated Dungeon - Alcove Near Mirror" = function(state) True, + ["Dilapidated Dungeon - Dark Orbs"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) and self:can_bounce(state) + or self:can_slidejump(state) and self:has_plunge(state) and self:can_bounce(state) + or self:get_kicks(state, 3) and self:has_plunge(state) + end, + ["Dilapidated Dungeon - Past Poles"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) + end, + ["Dilapidated Dungeon - Rafters"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:get_kicks(state, 1) and self:can_bounce(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:kick_or_plunge(state, 2) + end, + --# "Castle Sansa - Indignation" = function(state) True, + ["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:kick_or_plunge(state, 4) + or self:has_gem(state) + or self:knows_obscure(state) and self:can_bounce(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:kick_or_plunge(state, 1) + or self:has_gem(state) + end, + ["Castle Sansa - Tall Room Near Wheel Crawlers"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:knows_obscure(state) and self:can_slidejump(state) and self:has_plunge(state) + end, + ["Castle Sansa - Alcove Near Dungeon"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:knows_obscure(state) and self:has_plunge(state) + end, + ["Castle Sansa - Balcony"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 3) + or self:has_slide(state) and self:has_plunge(state) + or self:has_slide(state) and self:get_kicks(state, 1) and self:has_breaker(state) + end, + ["Castle Sansa - Corner Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + end, + ["Castle Sansa - Wheel Crawlers"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:get_kicks(state, 1) + or self:can_slidejump(state) and self:has_plunge(state) + or self:knows_obscure(state) and self:has_plunge(state) + end, + ["Castle Sansa - Alcove Near Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) and self:has_plunge(state) + end, + ["Castle Sansa - Near Theatre Front"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 4) + or self:get_kicks(state, 2) 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) + or self:has_plunge(state) and self:can_slidejump(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, + ["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) + or self:kick_or_plunge(state, 2)) + end, + ["Listless Library - Locked Door Across"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(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:get_kicks(state, 2) + end, + ["Sansa Keep - Near Theatre"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + end, + --# "Sansa Keep - Alcove Near Locked Door" = function(state) True, + ["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:kick_or_plunge(state, 1)) + end, + ["Sansa Keep - Lonely Throne"] = function(state) + return self:has_gem(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 2) + or self:get_kicks(state, 1) and state:has("Ascendant Light") + or self:get_kicks(state, 1) and self:knows_obscure(state)) + or self:has_plunge(state) and self:get_kicks(state, 4) + or state:has("Ascendant Light") and self:get_kicks(state, 3) + end, + --# "The Underbelly - Ascendant Light" = function(state) True, + ["The Underbelly - Rafters Near Keep"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + or self:has_slide(state) + 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) + end, + ["The Underbelly - Alcove Near Light"] = function(state) + return self:can_attack(state) + or self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 2) and self:can_slidejump(state) + end, + ["The Underbelly - Building Near Little Guy"] = function(state) + return self:has_plunge(state) + or self:get_kicks(state, 2) + end, + ["The Underbelly - Strikebreak Wall"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) 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, 1) + ) + or self:has_gem(state) + end, + }) do + self.location_rules[k] = v + end + + return self + end \ No newline at end of file diff --git a/scripts/logic/rules/lunatic.lua b/scripts/logic/rules/lunatic.lua index 8b2798b..11fa641 100644 --- a/scripts/logic/rules/lunatic.lua +++ b/scripts/logic/rules/lunatic.lua @@ -1 +1,430 @@ -- TODO: require base + +PseudoregaliaLunaticRules = PseudoregaliaRulesHelpers:new(nil) + +function PseudoregaliaLunaticRules.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 Mirror" = function(state) True, + ["Dungeon => Castle -> Dungeon Strong Eyes"] = function(state) + return self:has_small_keys(state) + end, + --# "Dungeon => Castle -> Castle Main" = function(state) True, + ["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:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + end, + --# "Dungeon Escape Lower -> Underbelly => Dungeon" = function(state) True, + ["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) + or self:has_slide(state) + end, + --# "Dungeon Escape Upper -> Theatre Outside Scythe Corridor" = function(state) True, + --# "Castle Main -> Dungeon => Castle" = function(state) True, + --# "Castle Main -> Keep Main" = function(state) True, + --# "Castle Main -> Empty Bailey" = function(state) True, + ["Castle Main -> Library Main"] = function(state) + return self:can_attack(state) + end, + ["Castle Main -> Theatre Pillar"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:kick_or_plunge(state, 1) + end, + ["Castle Main -> Castle Spiral Climb"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:kick_or_plunge(state, 2) + end, + --# "Castle Spiral Climb -> Castle High Climb" = function(state) True, + --# Anything that gets you into spiral climb can get from there to high climb + ["Castle Spiral Climb -> Castle By Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + end, + ["Castle By Scythe Corridor -> Castle Spiral Climb"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + end, + ["Castle By Scythe Corridor -> Castle => Theatre (Front)"] = function(state) + return self:has_gem(state) + or self:has_slide(state) and self:kick_or_plunge(state, 2) + end, + ["Castle By Scythe Corridor -> Castle High Climb"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:kick_or_plunge(state, 2) + end, + ["Castle => Theatre (Front) -> Castle By Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:get_kicks(state, 3) + end, + ["Castle => Theatre (Front) -> Castle Moon Room"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:get_kicks(state, 4) + end, + --# "Castle => Theatre (Front) -> Theatre Main" = function(state) True, + ["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:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) + end, + ["Library Greaves -> Library Top"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Library Top -> Library Greaves"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) + or self:can_bounce(state) and self:get_kicks(state, 1) and self:has_plunge(state) + end, + --# "Keep Main -> Keep Locked Room" = function(state) True, + --# "Keep Main -> Keep Sunsetter" = function(state) True, + ["Keep Main -> Keep => Underbelly"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + or self:has_slide(state) + end, + ["Keep Main -> Theatre Outside Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + or self:can_bounce(state) + end, + ["Keep Main -> Keep Path To Throne"] = function(state) + return self:has_breaker(state) + end, + --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, + --# "Keep => Underbelly -> Keep Main" = function(state) True, + --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, + --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, + --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, + ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) + return self:has_breaker(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:get_kicks(state, 1) and self:has_slide(state) + end, + --# "Underbelly Light Pillar -> Underbelly Main Upper" = function(state) True, + ["Underbelly Light Pillar -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 4) + or self:has_plunge(state) and self:get_kicks(state, 2) + or self:has_slide(state) and self:kick_or_plunge(state, 2) + end, + ["Underbelly Light Pillar -> Underbelly Ascendant Light"] = function(state) + return self:has_breaker(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 2) + or self:get_kicks(state, 1) and self:has_gem(state) + or self:has_slide(state)) + or self:has_plunge(state) + and ( + self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state)) + end, + --# "Underbelly Ascendant Light -> Underbelly Light Pillar" = function(state) True, + ["Underbelly Ascendant Light -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) and self:get_kicks(state, 1) + end, + --# "Underbelly Main Lower -> Underbelly Little Guy" = function(state) True, + ["Underbelly Main Lower -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + and ( + self:get_kicks(state, 1) + or self:has_gem(state) + or self:has_slide(state) + or self:can_attack(state)) + end, + ["Underbelly Main Lower -> Underbelly By Heliacal"] = function(state) + return self:has_slide(state) + end, + ["Underbelly Main Lower -> Underbelly Main Upper"] = function(state) + return self:has_plunge(state) and self:get_kicks(state, 2) + or self:has_gem(state) and self:kick_or_plunge(state, 1) + or self:get_kicks(state, 4) + or self:has_slide(state) + and ( + self:has_gem(state) + or self:get_kicks(state, 2) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:get_kicks(state, 1) and self:has_breaker(state)) + end, + --# "Underbelly Main Upper -> Underbelly Main Lower" = function(state) True, + ["Underbelly Main Upper -> Underbelly Light Pillar"] = function(state) + return self:has_breaker(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 2) + or self:has_slide(state)) + or self:has_slide(state) and self:get_kicks(state, 1) + or self:has_plunge(state) and self:get_kicks(state, 2) + or self:has_gem(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 2)) + end, + ["Underbelly Main Upper -> Underbelly By Heliacal"] = function(state) + return self:has_breaker(state) + and ( + state:has("Ascendant Light") + or self:has_gem(state) + or self:has_plunge(state) and self:get_kicks(state, 3)) + end, + ["Underbelly By Heliacal -> Underbelly Main Upper"] = function(state) + return self:can_attack(state) + or self:has_gem(state) + or self:get_kicks(state, 2) + end, + --# "Underbelly Little Guy -> Empty Bailey" = function(state) True, + --# "Underbelly Little Guy -> Underbelly Main Lower" = function(state) True, + --# "Underbelly => Keep -> Keep => Underbelly" = function(state) True, + ["Underbelly => Keep -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + end, + ["Underbelly Hole -> Underbelly Main Lower"] = function(state) + return self:get_kicks(state, 1) + or self:has_gem(state) + or self:can_attack(state) + or self:has_slide(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 - Dream Breaker" = function(state) True, + --# "Dilapidated Dungeon - Slide" = function(state) True, + --# "Dilapidated Dungeon - Alcove Near Mirror" = function(state) True, + ["Dilapidated Dungeon - Dark Orbs"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) and self:can_bounce(state) + or self:get_kicks(state, 3) and self:has_plunge(state) + or self:has_slide(state) and self:get_kicks(state, 1) + or self:has_slide(state) and self:can_bounce(state) + end, + ["Dilapidated Dungeon - Past Poles"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) + or self:has_slide(state) and self:get_kicks(state, 1) and self:has_plunge(state) + end, + ["Dilapidated Dungeon - Rafters"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:can_bounce(state) and self:kick_or_plunge(state, 1) + or self:has_slide(state) + end, + ["Dilapidated Dungeon - Strong Eyes"] = function(state) + return self:has_breaker(state) + or self:has_gem(state) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + end, + --# "Castle Sansa - Indignation" = function(state) True, + ["Castle Sansa - Floater In Courtyard"] = function(state) + return self:can_bounce(state) + and ( + self:kick_or_plunge(state, 1) + or self:has_slide(state)) + or self:has_slide(state) and self:get_kicks(state, 1) + or self:get_kicks(state, 3) + or self:has_gem(state) + 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_gem(state) + or self:kick_or_plunge(state, 1) + or self:has_slide(state) + or self:can_bounce(state) + end, + ["Castle Sansa - Tall Room Near Wheel Crawlers"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Alcove Near Dungeon"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Balcony"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:has_plunge(state) and self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Corner Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_slide(state) + end, + ["Castle Sansa - Wheel Crawlers"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 1) + or self:has_slide(state) + end, + ["Castle Sansa - Alcove Near Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 1) --# This never really matters and that makes me sad + end, + ["Castle Sansa - Near Theatre Front"] = function(state) + return self:has_gem(state) + or self:has_slide(state) + or self:get_kicks(state, 4) + or self:get_kicks(state, 2) 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) + or self:can_attack(state) and self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["Listless Library - Sun Greaves"] = function(state) + return self:can_attack(state) + end, + ["Listless Library - Sun Greaves 1"] = function(state) + return self:can_attack(state) + end, + ["Listless Library - Sun Greaves 2"] = function(state) + return self:can_attack(state) + end, + ["Listless Library - Sun Greaves 3"] = function(state) + return self:can_attack(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) + or self:kick_or_plunge(state, 2) + or self:has_slide(state)) + end, + ["Listless Library - Locked Door Across"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 1) + or self:has_slide(state) + end, + ["Listless Library - Locked Door Left"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + end, + ["Sansa Keep - Near Theatre"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + end, + --# "Sansa Keep - Alcove Near Locked Door" = function(state) True, + --# "Sansa Keep - Levers Room" = function(state) True, + ["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:can_strikebreak(state) and self:kick_or_plunge(state, 1) + end, + ["Sansa Keep - Lonely Throne"] = function(state) + return self:has_gem(state) + or self:has_plunge(state) and self:get_kicks(state, 4) + or state:has("Ascendant Light") and self:kick_or_plunge(state, 3) + or self:has_slide(state) and self:kick_or_plunge(state, 3) + or (self:has_slide(state) + and state:has("Ascendant Light") + and self:get_kicks(state, 1) + and self:has_plunge(state) + and self:can_soulcutter(state)) + end, + --# "The Underbelly - Ascendant Light" = function(state) True, + ["The Underbelly - Rafters Near Keep"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + or self:has_slide(state) + 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:has_slide(state) + end, + ["The Underbelly - Alcove Near Light"] = function(state) + return self:can_attack(state) + or self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_slide(state) + end, + ["The Underbelly - Building Near Little Guy"] = function(state) + return self:has_plunge(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + end, + ["The Underbelly - Strikebreak Wall"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + or self:has_slide(state) and self:has_gem(state) + end, + ["The Underbelly - Surrounded By Holes"] = function(state) + return self:can_soulcutter(state) + and ( + self:can_bounce(state) + or self:get_kicks(state, 1) + or self:has_slide(state) + ) + or self:has_gem(state) + or self:has_slide(state) and self:get_kicks(state, 1) + end, + }) do + self.location_rules[k] = v + end + + return self +end \ No newline at end of file From 592a420c11f618f6a219b107d2796ff99ea44449 Mon Sep 17 00:00:00 2001 From: Brooty Johnson <83629348+Br00ty@users.noreply.github.com> Date: Fri, 22 Dec 2023 03:21:11 -0500 Subject: [PATCH 04/24] add line break and shift hard logic over 1 tab --- scripts/logic/rules/expert.lua | 2 +- scripts/logic/rules/hard.lua | 810 ++++++++++++++++---------------- scripts/logic/rules/lunatic.lua | 2 +- 3 files changed, 407 insertions(+), 407 deletions(-) diff --git a/scripts/logic/rules/expert.lua b/scripts/logic/rules/expert.lua index 963207c..27a13d3 100644 --- a/scripts/logic/rules/expert.lua +++ b/scripts/logic/rules/expert.lua @@ -429,4 +429,4 @@ function PseudoregaliaExpertRules.new(cls, definition) end return self -end \ No newline at end of file +end diff --git a/scripts/logic/rules/hard.lua b/scripts/logic/rules/hard.lua index 321d753..7634e40 100644 --- a/scripts/logic/rules/hard.lua +++ b/scripts/logic/rules/hard.lua @@ -6,415 +6,415 @@ function PseudoregaliaHardRules.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 Mirror" = function(state) True, -- not used atm? - ["Dungeon => Castle -> Dungeon Strong Eyes"] = function(state) - return self:has_small_keys(state) - end, - --# "Dungeon => Castle -> Castle Main" = function(state) True, -- not used atm? - ["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:has_gem(state) - or self:kick_or_plunge(state, 2) - end, - --# "Dungeon Escape Lower -> Underbelly => Dungeon" = function(state) True, -- not used atm? - ["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, - --# "Dungeon Escape Upper -> Theatre Outside Scythe Corridor" = function(state) True, -- not used atm? - --# "Castle Main -> Dungeon => Castle" = function(state) True, -- not used atm? - --# "Castle Main -> Keep Main" = function(state) True, -- not used atm? - --# "Castle Main -> Empty Bailey" = function(state) True, -- not used atm? - ["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) - or self:kick_or_plunge(state, 1) - end, - ["Castle Main -> Castle Spiral Climb"] = function(state) - return self:has_gem(state) - or self:kick_or_plunge(state, 2) - or self:can_slidejump(state) and self:has_plunge(state) - end, - --# "Castle Spiral Climb -> Castle Main" = function(state) True, - ["Castle Spiral Climb -> Castle High Climb"] = function(state) - return self:has_gem(state) - or self:kick_or_plunge(state, 3) - 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) - or self:knows_obscure(state) and self:can_attack(state) and self:can_slidejump(state) - 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, 3) - end, - ["Castle By Scythe Corridor -> Castle => Theatre (Front)"] = function(state) - return self:has_gem(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, 3) and self:has_breaker(state) - or self:get_kicks(state, 1) and self:has_plunge(state) - 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, 2) - or self:get_kicks(state, 4) - end, - --# "Castle => Theatre (Front) -> Theatre Main" = function(state) True, - ["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:has_gem(state) - or self:kick_or_plunge(state, 4) - or self:knows_obscure(state) and self:kick_or_plunge(state, 2) - end, - ["Library Greaves -> Library Top"] = function(state) - return self:has_gem(state) + ["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 Mirror" = function(state) True, -- not used atm? + ["Dungeon => Castle -> Dungeon Strong Eyes"] = function(state) + return self:has_small_keys(state) + end, + --# "Dungeon => Castle -> Castle Main" = function(state) True, -- not used atm? + ["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:has_gem(state) + or self:kick_or_plunge(state, 2) + end, + --# "Dungeon Escape Lower -> Underbelly => Dungeon" = function(state) True, -- not used atm? + ["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, + --# "Dungeon Escape Upper -> Theatre Outside Scythe Corridor" = function(state) True, -- not used atm? + --# "Castle Main -> Dungeon => Castle" = function(state) True, -- not used atm? + --# "Castle Main -> Keep Main" = function(state) True, -- not used atm? + --# "Castle Main -> Empty Bailey" = function(state) True, -- not used atm? + ["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) + or self:kick_or_plunge(state, 1) + end, + ["Castle Main -> Castle Spiral Climb"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:can_slidejump(state) and self:has_plunge(state) + end, + --# "Castle Spiral Climb -> Castle Main" = function(state) True, + ["Castle Spiral Climb -> Castle High Climb"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 3) + 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) + or self:knows_obscure(state) and self:can_attack(state) and self:can_slidejump(state) + 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, 3) + end, + ["Castle By Scythe Corridor -> Castle => Theatre (Front)"] = function(state) + return self:has_gem(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, 3) and self:has_breaker(state) + or self:get_kicks(state, 1) and self:has_plunge(state) + 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, 2) + or self:get_kicks(state, 4) + end, + --# "Castle => Theatre (Front) -> Theatre Main" = function(state) True, + ["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:has_gem(state) + or self:kick_or_plunge(state, 4) + or self:knows_obscure(state) and self:kick_or_plunge(state, 2) + end, + ["Library Greaves -> Library Top"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + end, + ["Library Top -> Library Greaves"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 2) and self:has_plunge(state) and self:can_bounce(state) + end, + --# "Keep Main -> Keep Locked Room" = function(state) True, + --# "Keep Main -> Keep Sunsetter" = function(state) True, + ["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, + --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, + --# "Keep => Underbelly -> Keep Main" = function(state) True, + --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, + --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, + --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, + ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(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 Main Upper" = function(state) True, + ["Underbelly Light Pillar -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 4) + or self:has_plunge(state) and self:get_kicks(state, 2) + end, + ["Underbelly Light Pillar -> Underbelly Ascendant Light"] = function(state) + return self:has_breaker(state) + and ( + self:has_plunge(state) + or self:get_kicks(state, 3)) + or self:knows_obscure(state) and self:has_plunge(state) + and ( + self:has_gem(state) or self:get_kicks(state, 1) - end, - ["Library Top -> Library Greaves"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 3) - or self:get_kicks(state, 2) and self:has_plunge(state) and self:can_bounce(state) - end, - --# "Keep Main -> Keep Locked Room" = function(state) True, - --# "Keep Main -> Keep Sunsetter" = function(state) True, - ["Keep Main -> Keep => Underbelly"] = function(state) - return self:kick_or_plunge(state, 1) + or self:can_slidejump(state)) + end, + --# "Underbelly Ascendant Light -> Underbelly Light Pillar" = function(state) True, + ["Underbelly Ascendant Light -> Underbelly => Dungeon"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:kick_or_plunge(state, 2) + or self:get_kicks(state, 1) and self:can_slidejump(state) + or self:knows_obscure(state) and self:has_slide(state) and self:get_kicks(state, 1) + end, + --# "Underbelly Main Lower -> Underbelly Little Guy" = function(state) True, + ["Underbelly Main Lower -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + and ( + self:get_kicks(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, - --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, - --# "Keep => Underbelly -> Keep Main" = function(state) True, - --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, - --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, - --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, - ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) - return self:can_bounce(state) - or self:has_gem(state) - or self:kick_or_plunge(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 Main Upper" = function(state) True, - ["Underbelly Light Pillar -> Underbelly => Dungeon"] = function(state) - return self:can_bounce(state) - or self:get_kicks(state, 4) - or self:has_plunge(state) and self:get_kicks(state, 2) - end, - ["Underbelly Light Pillar -> Underbelly Ascendant Light"] = function(state) - return self:has_breaker(state) - and ( - self:has_plunge(state) - or self:get_kicks(state, 3)) - or self:knows_obscure(state) and self:has_plunge(state) - and ( - self:has_gem(state) - or self:get_kicks(state, 1) - or self:can_slidejump(state)) - end, - --# "Underbelly Ascendant Light -> Underbelly Light Pillar" = function(state) True, - ["Underbelly Ascendant Light -> Underbelly => Dungeon"] = function(state) - return self:can_bounce(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) + or self:knows_obscure(state) and self:get_kicks(state, 2)) + 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)) + or self:knows_obscure(state) and self:has_gem(state) and self:get_kicks(state, 1) + end, + --# "Underbelly Main Upper -> Underbelly Main Lower" = function(state) True, + ["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:has_plunge(state) + or self:get_kicks(state, 3)) + end, + ["Underbelly Main Upper -> Underbelly By Heliacal"] = function(state) + return self:has_breaker(state) + and ( + state:has("Ascendant Light") or self:has_gem(state) - or self:kick_or_plunge(state, 2) - or self:get_kicks(state, 1) and self:can_slidejump(state) - or self:knows_obscure(state) and self:has_slide(state) and self:get_kicks(state, 1) - end, - --# "Underbelly Main Lower -> Underbelly Little Guy" = function(state) True, - ["Underbelly Main Lower -> Underbelly Hole"] = function(state) - return self:has_plunge(state) - and ( - self:get_kicks(state, 1) - or self:has_gem(state) - 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) - or self:knows_obscure(state) and self:get_kicks(state, 2)) - 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)) - or self:knows_obscure(state) and self:has_gem(state) and self:get_kicks(state, 1) - end, - --# "Underbelly Main Upper -> Underbelly Main Lower" = function(state) True, - ["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:has_plunge(state) - or self:get_kicks(state, 3)) - end, - ["Underbelly Main Upper -> Underbelly By Heliacal"] = function(state) - return self:has_breaker(state) - and ( - state:has("Ascendant Light") - or self:has_gem(state) - or self:has_plunge(state) and self:get_kicks(state, 3) - or self:can_slidejump(state) and self:get_kicks(state, 3)) - end, - ["Underbelly By Heliacal -> Underbelly Main Upper"] = function(state) - return self:has_breaker(state) and self:has_plunge(state) - or self:knows_obscure(state) - and ( - self:has_plunge(state) - or self:has_breaker(state) - or self:get_kicks(state, 4)) - end, - --# "Underbelly Little Guy -> Empty Bailey" = function(state) True, - --# "Underbelly Little Guy -> Underbelly Main Lower" = function(state) True, - --# "Underbelly => Keep -> Keep => Underbelly" = function(state) True, - ["Underbelly => Keep -> Underbelly Hole"] = function(state) - return self:has_plunge(state) - end, - ["Underbelly Hole -> Underbelly Main Lower"] = function(state) - return self:get_kicks(state, 1) - or self:has_gem(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 + or self:has_plunge(state) and self:get_kicks(state, 3) + or self:can_slidejump(state) and self:get_kicks(state, 3)) + end, + ["Underbelly By Heliacal -> Underbelly Main Upper"] = function(state) + return self:has_breaker(state) and self:has_plunge(state) + or self:knows_obscure(state) + and ( + self:has_plunge(state) + or self:has_breaker(state) + or self:get_kicks(state, 4)) + end, + --# "Underbelly Little Guy -> Empty Bailey" = function(state) True, + --# "Underbelly Little Guy -> Underbelly Main Lower" = function(state) True, + --# "Underbelly => Keep -> Keep => Underbelly" = function(state) True, + ["Underbelly => Keep -> Underbelly Hole"] = function(state) + return self:has_plunge(state) + end, + ["Underbelly Hole -> Underbelly Main Lower"] = function(state) + return self:get_kicks(state, 1) + or self:has_gem(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 - Dream Breaker" = function(state) True, - --# "Dilapidated Dungeon - Slide" = function(state) True, - --# "Dilapidated Dungeon - Alcove Near Mirror" = function(state) True, - ["Dilapidated Dungeon - Dark Orbs"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 1) and self:can_bounce(state) - or self:can_slidejump(state) and self:has_plunge(state) and self:can_bounce(state) - or self:get_kicks(state, 3) and self:has_plunge(state) - end, - ["Dilapidated Dungeon - Past Poles"] = function(state) - return self:has_gem(state) + for k, v in pairs({ + --# "Dilapidated Dungeon - Dream Breaker" = function(state) True, + --# "Dilapidated Dungeon - Slide" = function(state) True, + --# "Dilapidated Dungeon - Alcove Near Mirror" = function(state) True, + ["Dilapidated Dungeon - Dark Orbs"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) and self:can_bounce(state) + or self:can_slidejump(state) and self:has_plunge(state) and self:can_bounce(state) + or self:get_kicks(state, 3) and self:has_plunge(state) + end, + ["Dilapidated Dungeon - Past Poles"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) + end, + ["Dilapidated Dungeon - Rafters"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:get_kicks(state, 1) and self:can_bounce(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:kick_or_plunge(state, 2) + end, + --# "Castle Sansa - Indignation" = function(state) True, + ["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:kick_or_plunge(state, 4) + or self:has_gem(state) + or self:knows_obscure(state) and self:can_bounce(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:kick_or_plunge(state, 1) + or self:has_gem(state) + end, + ["Castle Sansa - Tall Room Near Wheel Crawlers"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:knows_obscure(state) and self:can_slidejump(state) and self:has_plunge(state) + end, + ["Castle Sansa - Alcove Near Dungeon"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 1) + or self:knows_obscure(state) and self:has_plunge(state) + end, + ["Castle Sansa - Balcony"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(state, 3) + or self:has_slide(state) and self:has_plunge(state) + or self:has_slide(state) and self:get_kicks(state, 1) and self:has_breaker(state) + end, + ["Castle Sansa - Corner Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 3) + end, + ["Castle Sansa - Wheel Crawlers"] = function(state) + return self:can_bounce(state) + or self:has_gem(state) + or self:get_kicks(state, 1) + or self:can_slidejump(state) and self:has_plunge(state) + or self:knows_obscure(state) and self:has_plunge(state) + end, + ["Castle Sansa - Alcove Near Scythe Corridor"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 2) and self:has_plunge(state) + end, + ["Castle Sansa - Near Theatre Front"] = function(state) + return self:has_gem(state) + or self:get_kicks(state, 4) + or self:get_kicks(state, 2) 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) + or self:has_plunge(state) and self:can_slidejump(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, + ["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) + or self:kick_or_plunge(state, 2)) + end, + ["Listless Library - Locked Door Across"] = function(state) + return self:has_gem(state) + or self:kick_or_plunge(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:get_kicks(state, 2) + end, + ["Sansa Keep - Near Theatre"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + end, + --# "Sansa Keep - Alcove Near Locked Door" = function(state) True, + ["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:kick_or_plunge(state, 1)) + end, + ["Sansa Keep - Lonely Throne"] = function(state) + return self:has_gem(state) + and ( + self:has_plunge(state) or self:get_kicks(state, 2) - end, - ["Dilapidated Dungeon - Rafters"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 3) - or self:get_kicks(state, 1) and self:has_plunge(state) - or self:get_kicks(state, 1) and self:can_bounce(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:kick_or_plunge(state, 2) - end, - --# "Castle Sansa - Indignation" = function(state) True, - ["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:kick_or_plunge(state, 4) - or self:has_gem(state) - or self:knows_obscure(state) and self:can_bounce(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:kick_or_plunge(state, 1) - or self:has_gem(state) - end, - ["Castle Sansa - Tall Room Near Wheel Crawlers"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 1) - or self:knows_obscure(state) and self:can_slidejump(state) and self:has_plunge(state) - end, - ["Castle Sansa - Alcove Near Dungeon"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 1) - or self:knows_obscure(state) and self:has_plunge(state) - end, - ["Castle Sansa - Balcony"] = function(state) - return self:has_gem(state) - or self:kick_or_plunge(state, 3) - or self:has_slide(state) and self:has_plunge(state) - or self:has_slide(state) and self:get_kicks(state, 1) and self:has_breaker(state) - end, - ["Castle Sansa - Corner Corridor"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 3) - end, - ["Castle Sansa - Wheel Crawlers"] = function(state) - return self:can_bounce(state) - or self:has_gem(state) + or self:get_kicks(state, 1) and state:has("Ascendant Light") + or self:get_kicks(state, 1) and self:knows_obscure(state)) + or self:has_plunge(state) and self:get_kicks(state, 4) + or state:has("Ascendant Light") and self:get_kicks(state, 3) + end, + --# "The Underbelly - Ascendant Light" = function(state) True, + ["The Underbelly - Rafters Near Keep"] = function(state) + return self:kick_or_plunge(state, 1) + or self:has_gem(state) + or self:has_slide(state) + 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) + end, + ["The Underbelly - Alcove Near Light"] = function(state) + return self:can_attack(state) + or self:has_gem(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 2) and self:can_slidejump(state) + end, + ["The Underbelly - Building Near Little Guy"] = function(state) + return self:has_plunge(state) + or self:get_kicks(state, 2) + end, + ["The Underbelly - Strikebreak Wall"] = function(state) + return self:can_bounce(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) 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, 1) - or self:can_slidejump(state) and self:has_plunge(state) - or self:knows_obscure(state) and self:has_plunge(state) - end, - ["Castle Sansa - Alcove Near Scythe Corridor"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 2) and self:has_plunge(state) - end, - ["Castle Sansa - Near Theatre Front"] = function(state) - return self:has_gem(state) - or self:get_kicks(state, 4) - or self:get_kicks(state, 2) 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) - or self:has_plunge(state) and self:can_slidejump(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, - ["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) - or self:kick_or_plunge(state, 2)) - end, - ["Listless Library - Locked Door Across"] = function(state) - return self:has_gem(state) - or self:kick_or_plunge(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:get_kicks(state, 2) - end, - ["Sansa Keep - Near Theatre"] = function(state) - return self:kick_or_plunge(state, 1) - or self:has_gem(state) - end, - --# "Sansa Keep - Alcove Near Locked Door" = function(state) True, - ["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:kick_or_plunge(state, 1)) - end, - ["Sansa Keep - Lonely Throne"] = function(state) - return self:has_gem(state) - and ( - self:has_plunge(state) - or self:get_kicks(state, 2) - or self:get_kicks(state, 1) and state:has("Ascendant Light") - or self:get_kicks(state, 1) and self:knows_obscure(state)) - or self:has_plunge(state) and self:get_kicks(state, 4) - or state:has("Ascendant Light") and self:get_kicks(state, 3) - end, - --# "The Underbelly - Ascendant Light" = function(state) True, - ["The Underbelly - Rafters Near Keep"] = function(state) - return self:kick_or_plunge(state, 1) - or self:has_gem(state) - or self:has_slide(state) - 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) - end, - ["The Underbelly - Alcove Near Light"] = function(state) - return self:can_attack(state) - or self:has_gem(state) - or self:get_kicks(state, 3) - or self:get_kicks(state, 2) and self:can_slidejump(state) - end, - ["The Underbelly - Building Near Little Guy"] = function(state) - return self:has_plunge(state) - or self:get_kicks(state, 2) - end, - ["The Underbelly - Strikebreak Wall"] = function(state) - return self:can_bounce(state) - or self:get_kicks(state, 3) - or self:get_kicks(state, 1) 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, 1) - ) - or self:has_gem(state) - end, - }) do - self.location_rules[k] = v - end - - return self - end \ No newline at end of file + ) + or self:has_gem(state) + end, + }) do + self.location_rules[k] = v + end + + return self +end diff --git a/scripts/logic/rules/lunatic.lua b/scripts/logic/rules/lunatic.lua index 11fa641..2aec1a2 100644 --- a/scripts/logic/rules/lunatic.lua +++ b/scripts/logic/rules/lunatic.lua @@ -427,4 +427,4 @@ function PseudoregaliaLunaticRules.new(cls, definition) end return self -end \ No newline at end of file +end From 2b850530d0d56f44715a293b43900866d81bbb8b Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 24 Dec 2023 13:07:20 +0100 Subject: [PATCH 05/24] fix layout schema validation --- layouts/settings_popup.json | 1 - 1 file changed, 1 deletion(-) diff --git a/layouts/settings_popup.json b/layouts/settings_popup.json index 8e6be6f..af52c04 100644 --- a/layouts/settings_popup.json +++ b/layouts/settings_popup.json @@ -6,7 +6,6 @@ "margin": "0,0", "content": [ { - "name": "Settings", "type": "itemgrid", "item_margin": "0,5", "item_size": "200, 80", From c1249eaed54ec54b5878d3430018b8f80954f406 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:08:45 +0100 Subject: [PATCH 06/24] prepare logic/rules for require() PopTracker's reguire() is too incomplete to actually use yet --- scripts/logic/rules/base.lua | 2 ++ scripts/logic/rules/expert.lua | 2 ++ scripts/logic/rules/hard.lua | 2 ++ scripts/logic/rules/lunatic.lua | 2 ++ scripts/logic/rules/normal.lua | 2 ++ 5 files changed, 10 insertions(+) diff --git a/scripts/logic/rules/base.lua b/scripts/logic/rules/base.lua index 858dfde..1305987 100644 --- a/scripts/logic/rules/base.lua +++ b/scripts/logic/rules/base.lua @@ -205,3 +205,5 @@ function PseudoregaliaRulesHelpers:set_pseudoregalia_rules() }) end) end + +return PseudoregaliaRulesHelpers -- prepare for require() diff --git a/scripts/logic/rules/expert.lua b/scripts/logic/rules/expert.lua index 27a13d3..1585dbd 100644 --- a/scripts/logic/rules/expert.lua +++ b/scripts/logic/rules/expert.lua @@ -430,3 +430,5 @@ function PseudoregaliaExpertRules.new(cls, definition) return self end + +return PseudoregaliaExpertRules -- prepare for require() diff --git a/scripts/logic/rules/hard.lua b/scripts/logic/rules/hard.lua index 7634e40..4f3cf00 100644 --- a/scripts/logic/rules/hard.lua +++ b/scripts/logic/rules/hard.lua @@ -418,3 +418,5 @@ function PseudoregaliaHardRules.new(cls, definition) return self end + +return PseudoregaliaHardRules -- prepare for require() diff --git a/scripts/logic/rules/lunatic.lua b/scripts/logic/rules/lunatic.lua index 2aec1a2..877ae70 100644 --- a/scripts/logic/rules/lunatic.lua +++ b/scripts/logic/rules/lunatic.lua @@ -428,3 +428,5 @@ function PseudoregaliaLunaticRules.new(cls, definition) return self end + +return PseudoregaliaLunaticRules -- prepare for require() diff --git a/scripts/logic/rules/normal.lua b/scripts/logic/rules/normal.lua index 3ade0e8..1161eb7 100644 --- a/scripts/logic/rules/normal.lua +++ b/scripts/logic/rules/normal.lua @@ -361,3 +361,5 @@ function PseudoregaliaNormalRules.new(cls, definition) return self end + +return PseudoregaliaNormalRules -- prepare for require() From 7e28b109efd0051fbfc34da0e841427f24053611 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:15:57 +0100 Subject: [PATCH 07/24] cleanup logic/helper.lua --- scripts/logic/helper.lua | 42 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/scripts/logic/helper.lua b/scripts/logic/helper.lua index 66db6ba..9b8ac8f 100644 --- a/scripts/logic/helper.lua +++ b/scripts/logic/helper.lua @@ -1,5 +1,10 @@ --- Helper for AP-style rule definition --- this mostly maps to BaseClasses.py +-- Helper for Archipelago-style logic definition +-- this mostly maps to AP's BaseClasses.py and Options.py +-- see https://github.com/ArchipelagoMW/Archipelago for original copyright + + +-- python-style helpers + local free = function(state) return true end @@ -13,7 +18,7 @@ function table.shallow_copy(t) end -AppendableList = { +local AppendableList = { append = function(t,v) t[#t+1]=v end @@ -21,10 +26,17 @@ AppendableList = { AppendableList.__index = AppendableList +-- Base Classes + + State = {} State.__index = State function State:new(definition) + if not definition then + error("definition required") + end + local res = {} res.stale = true -- TODO: update this res.definition = definition @@ -67,14 +79,12 @@ function State:update_reachable_regions() 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 @@ -103,7 +113,6 @@ function Location:set_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 @@ -143,9 +152,13 @@ function Region:connect(connecting_region, name, rule) return exit_ end -function Region:add_exits(exits) +function Region:add_exits(exits, rules) -- TODO: implement exits for Dict[str, Optional[str]] type -- TODO: implement rules: Dict[str, Callable[[CollectionState], bool]] = None) -> None: + if rules then + error("rules handling not implemented in Region:add_exits") + end + name = nil -- TODO for _, connecting_region_name in ipairs(exits) do local destination = self.definition:get_region(connecting_region_name) @@ -158,11 +171,8 @@ 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 @@ -191,7 +201,6 @@ function Entrance:connect(destination, addresses, target) 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 @@ -202,13 +211,14 @@ Definition.__index = Definition function Definition:new() local res = {} res.regions = {} + res.options = {} -- TODO: add something like options_dataclass setmetatable(res, self) setmetatable(res.regions, AppendableList) return res end function Definition:get_region(name) - --return self.regions[name] + --return self.regions[name] -- TODO: cache for _, region in ipairs(self.regions) do if region.name == name then return region @@ -217,11 +227,9 @@ function Definition:get_region(name) end function Definition:get_entrance(name) - --return self.entrances[name] + --return self.entrances[name] -- TODO: cache 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 @@ -230,7 +238,7 @@ function Definition:get_entrance(name) end function Definition:get_location(name) - --return self.locations[name] + --return self.locations[name] -- TODO: cache for _, region in ipairs(self.regions) do for _, location in ipairs(region.locations) do if location.name == name then From a240affc89216db1f1b5f3bfd3f12f61fba4785d Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:23:11 +0100 Subject: [PATCH 08/24] cache reachable_regions when possible --- scripts/logic/logic.lua | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index e3ca319..54f0e05 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -15,6 +15,10 @@ ScriptHost:LoadScript("scripts/logic/rules/lunatic.lua") -- load PseudoregaliaLu local def = Definition:new() local state = State:new(def) -- TODO: add caching and update in watch for code +-- version helper +local v = {} +PopVersion:gsub("([^%.]+)", function(c) v[#v+1] = tonumber(c) end) +local hasAnyWatch = v[1] > 0 or v[2] > 25 or v[2] == 25 and v[3] > 4 -- item name to code mapping local codes = { @@ -59,10 +63,9 @@ getmetatable(state).count = function(state, name) end function can_reach(location_name, out_of_logic) - if out_of_logic then - --return true -- TODO: return logic for lunatic + if not hasAnyWatch then + state.stale = true end - state.stale = true return def:get_location(location_name):can_reach(state) end @@ -121,8 +124,27 @@ function set_rules() PseudoregaliaLunaticRules:new(def):set_pseudoregalia_rules() end -create_regions() -set_rules() +function stateChanged(code) -- run by watch for code "*" (any) + if code:find("^logic") then return end -- handled in difficultyChanged watch + print(code .. " changed") + state.stale = true + glitchState.stale = true +end + +function difficultyChanged() -- run by watch for code "logic" + print("difficulty changed") + set_rules() -- recreate rules with new code(s) in Tracker +end + +-- initialize logic +create_regions() -- TODO: this depends on progressive options, so we need another watch for code +difficultyChanged() + +-- add watches +ScriptHost:AddWatchForCode("difficultyChanged", "logic", difficultyChanged) +if hasAnyWatch then + ScriptHost:AddWatchForCode("stateChanged", "*", stateChanged) +end -- LAYOUT SWITCHING From bef6bde40cd3e784d607cff6020664450b02eb03 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:32:31 +0100 Subject: [PATCH 09/24] implement difficulty options also a bunch of cleanup and modularize helper.lua --- items/items.json | 8 +-- scripts/logic/constants.lua | 9 ++++ scripts/logic/helper.lua | 85 ++++++++++++++++++++++++++++++-- scripts/logic/logic.lua | 98 +++++++++++++++++++++++-------------- scripts/logic/options.lua | 27 ++++++++++ 5 files changed, 181 insertions(+), 46 deletions(-) create mode 100644 scripts/logic/constants.lua create mode 100644 scripts/logic/options.lua diff --git a/items/items.json b/items/items.json index 5f9b57f..115a68f 100644 --- a/items/items.json +++ b/items/items.json @@ -165,22 +165,22 @@ { "img": "images/logic_settings/normal.png", "codes": "logic,normal", - "inherit_codes": true + "inherit_codes": false }, { "img": "images/logic_settings/hard.png", "codes": "logic,hard", - "inherit_codes": true + "inherit_codes": false }, { "img": "images/logic_settings/expert.png", "codes": "logic,expert", - "inherit_codes": true + "inherit_codes": false }, { "img": "images/logic_settings/lunatic.png", "codes": "logic,lunatic", - "inherit_codes": true + "inherit_codes": false } ] }, diff --git a/scripts/logic/constants.lua b/scripts/logic/constants.lua new file mode 100644 index 0000000..1ae6f74 --- /dev/null +++ b/scripts/logic/constants.lua @@ -0,0 +1,9 @@ +constants = { + difficulties = { + ["NORMAL"] = 1, + ["HARD"] = 2, + ["EXPERT"] = 3, + ["LUNATIC"] = 4, + } +} +return constants diff --git a/scripts/logic/helper.lua b/scripts/logic/helper.lua index 9b8ac8f..9f31bc8 100644 --- a/scripts/logic/helper.lua +++ b/scripts/logic/helper.lua @@ -29,7 +29,13 @@ AppendableList.__index = AppendableList -- Base Classes -State = {} +local State = {} +local Location = {} +local Region = {} +local Entrance = {} +local Definition = {} + + State.__index = State function State:new(definition) @@ -95,7 +101,6 @@ function State:update_reachable_regions() end -Location = {} Location.__index = Location function Location:new(name, code, parent_region) @@ -117,7 +122,6 @@ function Location:can_reach(state) end -Region = {} Region.__index = Region function Region:new(name, definition) @@ -177,7 +181,6 @@ function Region:can_reach(state) end -Entrance = {} Entrance.__index = Entrance function Entrance:new(name, parent_region) @@ -205,7 +208,6 @@ function Entrance:can_reach(state) end -Definition = {} Definition.__index = Definition function Definition:new() @@ -247,3 +249,76 @@ function Definition:get_location(name) end end end + +function Definition:set_options(options, values) + -- missing value will try to resulve through code and fall back to default + if values == nil then + values = {} + end + for name, class in pairs(options) do + self.options[name] = class:new(values[name]) + end +end + + +-- Options + + +local Choice = { + value_to_code = {}, -- mapping to Tracker codes + default = 0, +} +Choice.__index = Choice + +function Choice.new(cls, initial_value) + local self = {} + cls.__index = cls + cls.__eq = Choice.__eq + setmetatable(self, cls) + self.code_to_value = {} + for value, code in pairs(self.value_to_code) do + if value == nil or code == nil then + error("Invalid option in code_to_value") + end + self.code_to_value[code] = value + if initial_value == nil then + -- if value is not provided, try to load from Tracker + if Tracker:ProviderCountForCode(code) > 0 then + initial_value = value + end + end + end + if initial_value == nil then + self.value = self.default + else + self.value = initial_value + end + return self +end + +function Choice:__eq(other) + -- NOTE: this is NOT being called for type(other) == string or number. Use .value for that. + if type(other) == type(self) then + return self.value == other.value -- compare value of two instances + else + return self.value == other -- compare value of instance to const + end +end + + +-- Module + + +-- create module table +helper = { + AppendableList = AppendableList, + State = State, + Location = Location, + Region = Region, + Entrance = Entrance, + Definition = Definition, + Choice = Choice, +} + +-- return the module table for require +return helper diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index 54f0e05..a0f7c45 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -1,5 +1,5 @@ -- ap-style logic --- TODO: use require +-- TODO: use require; this will need a PopTracker update to make "nested" require() work better 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 @@ -8,12 +8,22 @@ ScriptHost:LoadScript("scripts/logic/rules/normal.lua") -- load PseudoregaliaNor 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 +ScriptHost:LoadScript("scripts/logic/constants.lua") +ScriptHost:LoadScript("scripts/logic/options.lua") +-- shorthand names from imports +local Definition = helper.Definition +local State = helper.State +local Region = helper.Region +local Location = helper.Location +local difficulties = constants.difficulties +local pseudoregalia_options = options.pseudoregalia_options -local def = Definition:new() -local state = State:new(def) -- TODO: add caching and update in watch for code +-- state and world definition variables +local def = Definition:new() -- "world" definition for logic +local state = State:new(def) -- TODO: add caching and update in watch for code +local glitchDef = Definition:new() -- "world" definition for out-of-logic +local glitchState = State:new(glitchDef) -- TODO: add caching and update in watch for code -- version helper local v = {} @@ -42,7 +52,7 @@ local codes = { -- patch up State.has to match the codes local _has = getmetatable(state).has -getmetatable(state).has = function(state, name) +State.has = function(state, name) local code = codes[name] if code then return _has(state, code) @@ -51,9 +61,10 @@ getmetatable(state).has = function(state, name) return _has(state, name) end end + -- patch up State.count to match the codes local _count = getmetatable(state).count -getmetatable(state).count = function(state, name) +State.count = function(state, name) local code = codes[name] if code then return _count(state, code) @@ -62,7 +73,11 @@ getmetatable(state).count = function(state, name) end end -function can_reach(location_name, out_of_logic) + +-- logic resolvers (called from json locations) + + +function can_reach(location_name) if not hasAnyWatch then state.stale = true end @@ -70,44 +85,34 @@ function can_reach(location_name, out_of_logic) end function can_glitch(location_name) - return can_reach(location_name, true) + if not hasAnyWatch then + glitchState.stale = true + end + return glitchDef:get_location(location_name):can_reach(glitchState) end -function dump(o, level) - level = level or 0 - if level > 10 then - return "F" - end - 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 + +-- logic init (called to init/update def and state for logic and out-of-logic) + + +function set_options() + def:set_options(pseudoregalia_options) end - ---print(dump(def)) ---print(dump(def.regions)) ---print(dump(getmetatable(def.regions))) -function create_regions() +function _create_regions(def) for region_name, _ in pairs(region_table) do def.regions:append(Region:new(region_name, def)) end for loc_name, loc_data in pairs(location_table) do -- if not loc_data.can_create() ... - region = def:get_region(loc_data.region) + local region = def:get_region(loc_data.region) new_loc = Location:new(loc_name, loc_data.code, region) region.locations:append(new_loc) end for region_name, exit_list in pairs(region_table) do - region = def:get_region(region_name) + local region = def:get_region(region_name) region:add_exits(exit_list) end @@ -115,13 +120,29 @@ function create_regions() -- TODO: events if it uses events end +function create_regions() + _create_regions(def) + _create_regions(glitchDef) +end + function set_rules() - -- TODO: difficulty - -- TODO: setup difficulty toggles/addwatch for codes normal, hard, expert, lunatic - PseudoregaliaNormalRules:new(def):set_pseudoregalia_rules() - PseudoregaliaHardRules:new(def):set_pseudoregalia_rules() - PseudoregaliaExpertRules:new(def):set_pseudoregalia_rules() - PseudoregaliaLunaticRules:new(def):set_pseudoregalia_rules() + local difficulty = def.options.logic_level.value -- .value because lua can't override __eq for number + if difficulty == difficulties.NORMAL then + print("Setting difficulty to normal") + PseudoregaliaNormalRules:new(def):set_pseudoregalia_rules() + elseif difficulty == difficulties.HARD then + print("Setting difficulty to hard") + PseudoregaliaHardRules:new(def):set_pseudoregalia_rules() + elseif difficulty == difficulties.EXPERT then + print("Setting difficulty to expert") + PseudoregaliaExpertRules:new(def):set_pseudoregalia_rules() + elseif difficulty == difficulties.LUNATIC then + print("Setting difficulty to lunatic") + PseudoregaliaLunaticRules:new(def):set_pseudoregalia_rules() + else + error("Unknown difficulty " .. tostring(difficulty.value)) + end + PseudoregaliaLunaticRules:new(glitchDef):set_pseudoregalia_rules() end function stateChanged(code) -- run by watch for code "*" (any) @@ -133,6 +154,7 @@ end function difficultyChanged() -- run by watch for code "logic" print("difficulty changed") + set_options() -- update world option emulation set_rules() -- recreate rules with new code(s) in Tracker end @@ -148,6 +170,8 @@ end -- LAYOUT SWITCHING + + function apLayoutChange1() local progBreaker = Tracker:FindObjectForCode("progbreakerLayout") if (string.find(Tracker.ActiveVariantUID, "standard")) then diff --git a/scripts/logic/options.lua b/scripts/logic/options.lua new file mode 100644 index 0000000..930f528 --- /dev/null +++ b/scripts/logic/options.lua @@ -0,0 +1,27 @@ +-- this is roughly eqiovalent to AP player options +-- TODO: use require for constants and helper + + +local difficulties = constants.difficulties +local Choice = helper.Choice + + +LogicLevel = { + value_to_code = { + [difficulties.NORMAL] = "normal", + [difficulties.HARD] = "hard", + [difficulties.EXPERT] = "expert", + [difficulties.LUNATIC] = "lunatic", + }, + default = difficulties.NORMAL, +} +setmetatable(LogicLevel, Choice) + + +options = { + pseudoregalia_options = { + logic_level = LogicLevel + } +} + +return options From c5841756ff595f54863e2db8051b16f06ad8107d Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:28:44 +0100 Subject: [PATCH 10/24] fix changing difficulty not updating all rules --- scripts/logic/helper.lua | 3 +++ scripts/logic/logic.lua | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/logic/helper.lua b/scripts/logic/helper.lua index 9f31bc8..b787e92 100644 --- a/scripts/logic/helper.lua +++ b/scripts/logic/helper.lua @@ -21,6 +21,9 @@ function table.shallow_copy(t) local AppendableList = { append = function(t,v) t[#t+1]=v + end, + clear = function(t) + for i = 1,#t do t[i] = nil end end } AppendableList.__index = AppendableList diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index a0f7c45..16bec15 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -97,9 +97,12 @@ end function set_options() def:set_options(pseudoregalia_options) + glitchDef:set_options(pseudoregalia_options) end function _create_regions(def) + def.regions:clear() -- allow running _create_regions multiple times + for region_name, _ in pairs(region_table) do def.regions:append(Region:new(region_name, def)) end @@ -107,7 +110,7 @@ function _create_regions(def) for loc_name, loc_data in pairs(location_table) do -- if not loc_data.can_create() ... local region = def:get_region(loc_data.region) - new_loc = Location:new(loc_name, loc_data.code, region) + local new_loc = Location:new(loc_name, loc_data.code, region) region.locations:append(new_loc) end @@ -126,6 +129,9 @@ function create_regions() end function set_rules() + -- set_pseudoregalia_rules does not rewrite everything, so we have to recreate locations + _create_regions(def) + -- set rules depending on logic (and other options) local difficulty = def.options.logic_level.value -- .value because lua can't override __eq for number if difficulty == difficulties.NORMAL then print("Setting difficulty to normal") From 82145ce620c110fc323d0ec8335f166b9d882f62 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:36:23 +0100 Subject: [PATCH 11/24] add obscure logic option --- scripts/logic/helper.lua | 15 +++++++++++++++ scripts/logic/logic.lua | 20 ++++++++++++++------ scripts/logic/options.lua | 10 +++++++++- scripts/logic/rules/base.lua | 2 +- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/scripts/logic/helper.lua b/scripts/logic/helper.lua index b787e92..c3f7aba 100644 --- a/scripts/logic/helper.lua +++ b/scripts/logic/helper.lua @@ -308,6 +308,20 @@ function Choice:__eq(other) end end +local Toggle = { + default = false, +} +Toggle.__index = Toggle +setmetatable(Toggle, Choice) + +function Toggle.new(cls, initial_value) + if cls.code == nil then + error("Toggle needs to define code") + end + cls.value_to_code = {[true] = cls.code} -- so we can use Choice's code + return Choice.new(cls, initial_value) +end + -- Module @@ -321,6 +335,7 @@ helper = { Entrance = Entrance, Definition = Definition, Choice = Choice, + Toggle = Toggle, } -- return the module table for require diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index 16bec15..f987db2 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -25,6 +25,8 @@ local state = State:new(def) -- TODO: add caching and update in watch for code local glitchDef = Definition:new() -- "world" definition for out-of-logic local glitchState = State:new(glitchDef) -- TODO: add caching and update in watch for code +DEBUG = true + -- version helper local v = {} PopVersion:gsub("([^%.]+)", function(c) v[#v+1] = tonumber(c) end) @@ -152,24 +154,30 @@ function set_rules() end function stateChanged(code) -- run by watch for code "*" (any) - if code:find("^logic") then return end -- handled in difficultyChanged watch - print(code .. " changed") + if DEBUG then + if code ~= "obscure" and code:find("^logic") == nil then + print(code .. " changed") + end + end state.stale = true glitchState.stale = true end -function difficultyChanged() -- run by watch for code "logic" - print("difficulty changed") +function logicChanged() -- run by watch for code "logic", "obscure" + print("logic changed") set_options() -- update world option emulation set_rules() -- recreate rules with new code(s) in Tracker + state.stale = true + glitchState.stale = true end -- initialize logic create_regions() -- TODO: this depends on progressive options, so we need another watch for code -difficultyChanged() +logicChanged() -- add watches -ScriptHost:AddWatchForCode("difficultyChanged", "logic", difficultyChanged) +ScriptHost:AddWatchForCode("difficultyChanged", "logic", logicChanged) +ScriptHost:AddWatchForCode("obscureChanged", "obscure", logicChanged) if hasAnyWatch then ScriptHost:AddWatchForCode("stateChanged", "*", stateChanged) end diff --git a/scripts/logic/options.lua b/scripts/logic/options.lua index 930f528..3852f57 100644 --- a/scripts/logic/options.lua +++ b/scripts/logic/options.lua @@ -4,6 +4,7 @@ local difficulties = constants.difficulties local Choice = helper.Choice +local Toggle = helper.Toggle LogicLevel = { @@ -18,9 +19,16 @@ LogicLevel = { setmetatable(LogicLevel, Choice) +ObscureLogic = { + code = "obscure" +} +setmetatable(ObscureLogic, Toggle) + + options = { pseudoregalia_options = { - logic_level = LogicLevel + logic_level = LogicLevel, + obscure_logic = ObscureLogic, } } diff --git a/scripts/logic/rules/base.lua b/scripts/logic/rules/base.lua index 1305987..1dedc7b 100644 --- a/scripts/logic/rules/base.lua +++ b/scripts/logic/rules/base.lua @@ -158,7 +158,7 @@ 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 obscure_logic = self.definition.options.obscure_logic.value local logic_level = 0 -- TODO: as above if obscure_logic then From d514c0b90e64b6033b0b8526c54b6ce59930a4a0 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:51:49 +0100 Subject: [PATCH 12/24] move layout switching to separate file --- scripts/init.lua | 1 + scripts/layout_switch.lua | 95 ++++++++++++++++++++++++++++++++++++++ scripts/layouts.lua | 2 +- scripts/logic/logic.lua | 96 --------------------------------------- 4 files changed, 97 insertions(+), 97 deletions(-) create mode 100644 scripts/layout_switch.lua diff --git a/scripts/init.lua b/scripts/init.lua index 7d45a32..9e6890e 100644 --- a/scripts/init.lua +++ b/scripts/init.lua @@ -15,6 +15,7 @@ ScriptHost:LoadScript("scripts/utils.lua") -- Layout Switching ScriptHost:LoadScript("scripts/layouts.lua") +ScriptHost:LoadScript("scripts/layout_switch.lua") -- Logic ScriptHost:LoadScript("scripts/logic/logic.lua") diff --git a/scripts/layout_switch.lua b/scripts/layout_switch.lua new file mode 100644 index 0000000..4a9f0cb --- /dev/null +++ b/scripts/layout_switch.lua @@ -0,0 +1,95 @@ +-- LAYOUT SWITCHING +-- change layout depending on options + + +function apLayoutChange1() + local progBreaker = Tracker:FindObjectForCode("progbreakerLayout") + if (string.find(Tracker.ActiveVariantUID, "standard")) then + if progBreaker.Active then + Tracker:AddLayouts("layouts/items_only_progbreaker.json") + --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER + else + Tracker:AddLayouts("layouts/items_standard.json") + end + end +end + +function apLayoutChange2() + local progSlide = Tracker:FindObjectForCode("progslideLayout") + if (string.find(Tracker.ActiveVariantUID, "standard")) then + if progSlide.Active then + Tracker:AddLayouts("layouts/items_only_progslide.json") + --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER + else + Tracker:AddLayouts("layouts/items_standard.json") + end + end +end + +function apLayoutChange3() + local splitKick = Tracker:FindObjectForCode("splitkickLayout") + if (string.find(Tracker.ActiveVariantUID, "standard")) then + if splitKick.Active then + Tracker:AddLayouts("layouts/items_only_splitkick.json") + --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER + else + Tracker:AddLayouts("layouts/items_standard.json") + end + end +end + +function apLayoutChange4() + local progBprogS = Tracker:FindObjectForCode("progbreakerprogslideLayout") + if (string.find(Tracker.ActiveVariantUID, "standard")) then + if progBprogS.Active then + Tracker:AddLayouts("layouts/items_progbreaker_and_progslide.json") + --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER + else + Tracker:AddLayouts("layouts/items_standard.json") + end + end +end + +function apLayoutChange5() + local progBsplitK = Tracker:FindObjectForCode("progbreakersplitkickLayout") + if (string.find(Tracker.ActiveVariantUID, "standard")) then + if progBsplitK.Active then + Tracker:AddLayouts("layouts/items_progbreaker_and_splitkick.json") + --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER + else + Tracker:AddLayouts("layouts/items_standard.json") + end + end +end + +function apLayoutChange6() + local progSsplitK = Tracker:FindObjectForCode("progslidesplitkickLayout") + if (string.find(Tracker.ActiveVariantUID, "standard")) then + if progSsplitK.Active then + Tracker:AddLayouts("layouts/items_progslide_and_splitkick.json") + --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER + else + Tracker:AddLayouts("layouts/items_standard.json") + end + end +end + +function apLayoutChange7() + local progBSsplitK = Tracker:FindObjectForCode("progsandsplitLayout") + if (string.find(Tracker.ActiveVariantUID, "standard")) then + if progBSsplitK.Active then + Tracker:AddLayouts("layouts/items_progs_and_split.json") + --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER + else + Tracker:AddLayouts("layouts/items_standard.json") + end + end +end + +ScriptHost:AddWatchForCode("useApLayout1", "progbreakerLayout", apLayoutChange1) +ScriptHost:AddWatchForCode("useApLayout2", "progslideLayout", apLayoutChange2) +ScriptHost:AddWatchForCode("useApLayout3", "splitkickLayout", apLayoutChange3) +ScriptHost:AddWatchForCode("useApLayout4", "progbreakerprogslideLayout", apLayoutChange4) +ScriptHost:AddWatchForCode("useApLayout5", "progbreakersplitkickLayout", apLayoutChange5) +ScriptHost:AddWatchForCode("useApLayout6", "progslidesplitkickLayout", apLayoutChange6) +ScriptHost:AddWatchForCode("useApLayout7", "progsandsplitLayout", apLayoutChange7) diff --git a/scripts/layouts.lua b/scripts/layouts.lua index 2c38670..0a41042 100644 --- a/scripts/layouts.lua +++ b/scripts/layouts.lua @@ -7,4 +7,4 @@ if (string.find(Tracker.ActiveVariantUID,"standard")) then Tracker:AddLocations("locations/locations.json") Tracker:AddLayouts("layouts/broadcast.json") Tracker:AddLayouts("layouts/tracker.json") -end \ No newline at end of file +end diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index f987db2..5e233bc 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -181,99 +181,3 @@ ScriptHost:AddWatchForCode("obscureChanged", "obscure", logicChanged) if hasAnyWatch then ScriptHost:AddWatchForCode("stateChanged", "*", stateChanged) end - - --- LAYOUT SWITCHING - - -function apLayoutChange1() - local progBreaker = Tracker:FindObjectForCode("progbreakerLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBreaker.Active then - Tracker:AddLayouts("layouts/items_only_progbreaker.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange2() - local progSlide = Tracker:FindObjectForCode("progslideLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progSlide.Active then - Tracker:AddLayouts("layouts/items_only_progslide.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange3() - local splitKick = Tracker:FindObjectForCode("splitkickLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if splitKick.Active then - Tracker:AddLayouts("layouts/items_only_splitkick.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange4() - local progBprogS = Tracker:FindObjectForCode("progbreakerprogslideLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBprogS.Active then - Tracker:AddLayouts("layouts/items_progbreaker_and_progslide.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange5() - local progBsplitK = Tracker:FindObjectForCode("progbreakersplitkickLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBsplitK.Active then - Tracker:AddLayouts("layouts/items_progbreaker_and_splitkick.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange6() - local progSsplitK = Tracker:FindObjectForCode("progslidesplitkickLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progSsplitK.Active then - Tracker:AddLayouts("layouts/items_progslide_and_splitkick.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange7() - local progBSsplitK = Tracker:FindObjectForCode("progsandsplitLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBSsplitK.Active then - Tracker:AddLayouts("layouts/items_progs_and_split.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -ScriptHost:AddWatchForCode("useApLayout1", "progbreakerLayout", apLayoutChange1) -ScriptHost:AddWatchForCode("useApLayout2", "progslideLayout", apLayoutChange2) -ScriptHost:AddWatchForCode("useApLayout3", "splitkickLayout", apLayoutChange3) -ScriptHost:AddWatchForCode("useApLayout4", "progbreakerprogslideLayout", apLayoutChange4) -ScriptHost:AddWatchForCode("useApLayout5", "progbreakersplitkickLayout", apLayoutChange5) -ScriptHost:AddWatchForCode("useApLayout6", "progslidesplitkickLayout", apLayoutChange6) -ScriptHost:AddWatchForCode("useApLayout7", "progsandsplitLayout", apLayoutChange7) From d5b180b277b4a9684ed2d785a19023b8010d9b98 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:57:56 +0100 Subject: [PATCH 13/24] more logic.lua cleanup --- scripts/logic/logic.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index 5e233bc..e3742e2 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -1,4 +1,8 @@ -- ap-style logic + +-- set global DEBUG to true to get more output +-- DEBUG = true + -- TODO: use require; this will need a PopTracker update to make "nested" require() work better ScriptHost:LoadScript("scripts/logic/helper.lua") -- load helper for AP-style logic ScriptHost:LoadScript("scripts/logic/locations.lua") -- load location_table @@ -25,12 +29,10 @@ local state = State:new(def) -- TODO: add caching and update in watch for code local glitchDef = Definition:new() -- "world" definition for out-of-logic local glitchState = State:new(glitchDef) -- TODO: add caching and update in watch for code -DEBUG = true - -- version helper local v = {} PopVersion:gsub("([^%.]+)", function(c) v[#v+1] = tonumber(c) end) -local hasAnyWatch = v[1] > 0 or v[2] > 25 or v[2] == 25 and v[3] > 4 +local hasAnyWatch = v[1] > 0 or v[2] > 25 or v[2] == 25 and v[3] > 4 -- available since 0.25.5 -- item name to code mapping local codes = { @@ -53,7 +55,7 @@ local codes = { } -- patch up State.has to match the codes -local _has = getmetatable(state).has +local _has = State.has State.has = function(state, name) local code = codes[name] if code then @@ -65,7 +67,7 @@ State.has = function(state, name) end -- patch up State.count to match the codes -local _count = getmetatable(state).count +local _count = State.count State.count = function(state, name) local code = codes[name] if code then From 8d996f1f1eb3c8812231a4e9070e701d526c4d2c Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 25 Dec 2023 13:50:34 +0100 Subject: [PATCH 14/24] helper.lua cleanup `new`s --- scripts/logic/helper.lua | 85 +++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/scripts/logic/helper.lua b/scripts/logic/helper.lua index c3f7aba..be285fe 100644 --- a/scripts/logic/helper.lua +++ b/scripts/logic/helper.lua @@ -19,12 +19,16 @@ function table.shallow_copy(t) local AppendableList = { + new = function(cls,self) + if self == nil then self = {} end + return setmetatable(self, cls) + end, append = function(t,v) t[#t+1]=v end, clear = function(t) for i = 1,#t do t[i] = nil end - end + end, } AppendableList.__index = AppendableList @@ -46,13 +50,12 @@ function State:new(definition) error("definition required") end - local res = {} - res.stale = true -- TODO: update this - res.definition = definition - res.reachable_regions = {} - --res.blocked_connections = {} - setmetatable(res, self) - return res + return setmetatable({ + stale = true, + definition = definition, + reachable_regions = {}, + --res.blocked_connections = {}, + }, self) end function State:has(item_name) @@ -107,13 +110,12 @@ end 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 + return setmetatable({ + name = name, + code = code, + parent_region = parent_region, + access_rule = free, + }, self) end function Location:set_rule(rule) @@ -128,35 +130,31 @@ end 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 + return setmetatable({ + name = name, + definition = definition, + locations = AppendableList:new(), + exits = AppendableList:new(), + entrances = AppendableList:new(), + }, self) end function Region:create_exit(name) - exit_ = Entrance:new(name, self) - self.exits:append(exit_) - return exit_ + local 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) + local exit = self:create_exit(name) if rule then - exit_.access_rule = rule + exit.access_rule = rule end - exit_:connect(connecting_region) - return exit_ + exit:connect(connecting_region) + return exit end function Region:add_exits(exits, rules) @@ -187,12 +185,11 @@ end 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 + return setmetatable({ + name = name, + parent_region = parent_region, + access_rule = free, + }, self) end function Entrance:set_rule(rule) @@ -214,12 +211,10 @@ end Definition.__index = Definition function Definition:new() - local res = {} - res.regions = {} - res.options = {} -- TODO: add something like options_dataclass - setmetatable(res, self) - setmetatable(res.regions, AppendableList) - return res + return setmetatable({ + regions = AppendableList:new(), + options = {}, + }, self) end function Definition:get_region(name) From 70084620beb9cd6af07238ffbbc91dfd749d9217 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 26 Dec 2023 02:32:21 +0100 Subject: [PATCH 15/24] implement keys per difficulty --- scripts/logic/logic.lua | 6 +++--- scripts/logic/rules/base.lua | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index e3742e2..e959af7 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -5,6 +5,8 @@ -- TODO: use require; this will need a PopTracker update to make "nested" require() work better ScriptHost:LoadScript("scripts/logic/helper.lua") -- load helper for AP-style logic +ScriptHost:LoadScript("scripts/logic/constants.lua") +ScriptHost:LoadScript("scripts/logic/options.lua") 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 @@ -12,8 +14,6 @@ ScriptHost:LoadScript("scripts/logic/rules/normal.lua") -- load PseudoregaliaNor 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 -ScriptHost:LoadScript("scripts/logic/constants.lua") -ScriptHost:LoadScript("scripts/logic/options.lua") -- shorthand names from imports local Definition = helper.Definition @@ -101,7 +101,7 @@ end function set_options() def:set_options(pseudoregalia_options) - glitchDef:set_options(pseudoregalia_options) + glitchDef:set_options(pseudoregalia_options, {logic_level = difficulties.LUNATIC}) end function _create_regions(def) diff --git a/scripts/logic/rules/base.lua b/scripts/logic/rules/base.lua index 1dedc7b..eabcaf0 100644 --- a/scripts/logic/rules/base.lua +++ b/scripts/logic/rules/base.lua @@ -1,9 +1,12 @@ PseudoregaliaRulesHelpers = {} -- rules base. See normal, hard, expert and lunatic for finished rules. +local NORMAL = constants.difficulties.NORMAL + 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 @@ -159,7 +162,7 @@ end function PseudoregaliaRulesHelpers:set_pseudoregalia_rules() local split_kicks = false -- TODO: load from code or slot data local obscure_logic = self.definition.options.obscure_logic.value - local logic_level = 0 -- TODO: as above + local logic_level = self.definition.options.logic_level.value if obscure_logic then self.knows_obscure = free @@ -169,7 +172,7 @@ function PseudoregaliaRulesHelpers:set_pseudoregalia_rules() self.can_attack = function(self, state) return self:has_breaker(state) end end - if logic_level == 0 then + if logic_level == NORMAL then self.required_small_keys = 7 end From bfd258c0b5c172efa089498b97fb02fba0c20fa8 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 26 Dec 2023 02:43:18 +0100 Subject: [PATCH 16/24] implement major keys handling --- scripts/logic/logic.lua | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index e959af7..b081da8 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -47,34 +47,40 @@ local codes = { ["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", + -- Major Keys have custom handling since they are just a count in the tracker. + --["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 +-- patch up State.has and State.count to match the codes local _has = State.has +local _count = State.count + State.has = function(state, name) local code = codes[name] if code then return _has(state, code) - else - -- TODO: warn? - return _has(state, name) end + if name:find("^Major Key - ") then + return _count(state, "majorkey") >= 5 + end + -- TODO: warn? + return _has(state, name) end --- patch up State.count to match the codes -local _count = State.count State.count = function(state, name) local code = codes[name] if code then return _count(state, code) - else - return _count(state, name) end + if name:find("^Major Key - ") then + return (_count(state, "majorkey") >= 5) and 1 or 0 + end + -- TODO: warn? + return _count(state, name) end From 2743c7cb3aae61d77539bb275e411b3a206f00d7 Mon Sep 17 00:00:00 2001 From: Brooty Johnson <83629348+Br00ty@users.noreply.github.com> Date: Tue, 26 Dec 2023 15:57:08 -0500 Subject: [PATCH 17/24] update location ID's + update major key location names --- locations/locations.json | 10 +-- scripts/autotracking/location_mapping.lua | 101 +++++++++++----------- 2 files changed, 57 insertions(+), 54 deletions(-) diff --git a/locations/locations.json b/locations/locations.json index d05a670..77fb69f 100644 --- a/locations/locations.json +++ b/locations/locations.json @@ -522,7 +522,7 @@ ] }, { - "name": "Major Key", + "name": "Lonely Throne", "access_rules": [[ "$can_glitch|Sansa Keep - Lonely Throne", "[$can_reach|Sansa Keep - Lonely Throne]" @@ -764,7 +764,7 @@ ] }, { - "name": "Major Key", + "name": "Center Stage", "access_rules": [[ "$can_glitch|Twilight Theatre - Center Stage", "[$can_reach|Twilight Theatre - Center Stage]" @@ -874,7 +874,7 @@ ] }, { - "name": "Major Key", + "name": "Guarded Hand", "access_rules": [[ "$can_glitch|Empty Bailey - Guarded Hand", "[$can_reach|Empty Bailey - Guarded Hand]" @@ -1047,7 +1047,7 @@ ] }, { - "name": "Major Key", + "name": "Surrounded By Holes", "access_rules": [[ "$can_glitch|The Underbelly - Surrounded By Holes", "[$can_reach|The Underbelly - Surrounded By Holes]" @@ -1094,7 +1094,7 @@ ] }, { - "name": "Major Key", + "name": "Atop The Tower", "access_rules": [[ "$can_glitch|Tower Remains - Atop The Tower", "[$can_reach|Tower Remains - Atop The Tower]" diff --git a/scripts/autotracking/location_mapping.lua b/scripts/autotracking/location_mapping.lua index 66394da..6abe9ad 100644 --- a/scripts/autotracking/location_mapping.lua +++ b/scripts/autotracking/location_mapping.lua @@ -5,58 +5,61 @@ LOCATION_MAPPING = { [2365810001] = {"@Dilapitaded Dungeon/Dream Breaker/"}, [2365810002] = {"@Dilapitaded Dungeon/Slide/"}, - [2365810003] = {"@Castle Sansa/Indignation/"}, - [2365810004] = {"@Sansa Keep/Sunsetter/"}, - [2365810005] = {"@Sansa Keep/Strikebreak/"}, - [2365810006] = {"@Listless Library/Sun Greaves/"}, - [2365810007] = {"@Twilight Theatre/Soul Cutter/"}, - [2365810008] = {"@Empty Bailey/Solar Wind/"}, - [2365810009] = {"@Underbelly/Ascendant Light/"}, - [2365810010] = {"@Tower Remains/Cling Gem/"}, - - [2365810011] = {"@Underbelly/Locked Door/"}, - [2365810012] = {"@Twilight Theatre/Corner Beam/"}, - [2365810013] = {"@Castle Sansa/Floater In Courtyard/"}, - [2365810014] = {"@Empty Bailey/Cheese Bell/"}, - [2365810015] = {"@Twilight Theatre/Locked Door/"}, - [2365810016] = {"@Dilapitaded Dungeon/Dark Orbs/"}, - [2365810017] = {"@Upper Castle Sansa/Near Theatre Front/"}, - [2365810018] = {"@Underbelly/Strikebreak Wall/"}, - [2365810019] = {"@Upper Castle Sansa/High Climb From Courtyard/"}, - [2365810020] = {"@Listless Library/Locked Door Across/"}, - [2365810021] = {"@Sansa Keep/Near Theatre/"}, - [2365810022] = {"@Castle Sansa/Locked Door/"}, + [2365810003] = {"@Dilapitaded Dungeon/Alcove Near Mirror/"}, + [2365810004] = {"@Dilapitaded Dungeon/Dark Orbs/"}, + [2365810005] = {"@Dilapitaded Dungeon/Past Poles/"}, + [2365810006] = {"@Dilapitaded Dungeon/Rafters/"}, + [2365810007] = {"@Dilapitaded Dungeon/Strong Eyes/"}, - [2365810023] = {"@Dilapitaded Dungeon/Rafters/"}, - [2365810024] = {"@Dilapitaded Dungeon/Strong Eyes/"}, - [2365810025] = {"@Castle Sansa/Platform In Main Halls/"}, - [2365810026] = {"@Castle Sansa/Tall Room Near Wheel Crawlers/"}, - [2365810027] = {"@Sansa Keep/Levers Room/"}, - [2365810028] = {"@Empty Bailey/Inside Building/"}, - [2365810029] = {"@Underbelly/Main Room/"}, + [2365810008] = {"@Castle Sansa/Indignation/"}, + [2365810009] = {"@Castle Sansa/Alcove Near Dungeon/"}, + [2365810010] = {"@Castle Sansa/Balcony/"}, + [2365810011] = {"@Castle Sansa/Corner Corridor/"}, + [2365810012] = {"@Castle Sansa/Floater In Courtyard/"}, + [2365810013] = {"@Castle Sansa/Locked Door/"}, + [2365810014] = {"@Castle Sansa/Platform In Main Halls/"}, + [2365810015] = {"@Castle Sansa/Tall Room Near Wheel Crawlers/"}, + [2365810016] = {"@Castle Sansa/Wheel Crawlers/"}, + [2365810017] = {"@Upper Castle Sansa/High Climb From Courtyard/"}, + [2365810018] = {"@Upper Castle Sansa/Alcove Near Scythe Corridor/"}, + [2365810019] = {"@Upper Castle Sansa/Near Theatre Front/"}, - [2365810030] = {"@Dilapitaded Dungeon/Alcove Near Mirror/"}, - [2365810031] = {"@Dilapitaded Dungeon/Past Poles/"}, - [2365810032] = {"@Castle Sansa/Alcove Near Dungeon/"}, - [2365810033] = {"@Castle Sansa/Corner Corridor/"}, - [2365810034] = {"@Castle Sansa/Wheel Crawlers/"}, - [2365810035] = {"@Upper Castle Sansa/Alcove Near Scythe Corridor/"}, - [2365810036] = {"@Castle Sansa/Balcony/"}, - [2365810037] = {"@Listless Library/Upper Back/"}, - [2365810038] = {"@Listless Library/Locked Door Left/"}, - [2365810039] = {"@Sansa Keep/Alcove Near Locked Door/"}, - [2365810040] = {"@Empty Bailey/Center Steeple/"}, - [2365810041] = {"@Underbelly/Rafters Near Keep/"}, - [2365810042] = {"@Underbelly/Building Near Little Guy/"}, - [2365810043] = {"@Underbelly/Alcove Near Light/"}, - [2365810044] = {"@Twilight Theatre/Murderous Goat/"}, - [2365810045] = {"@Twilight Theatre/Back Of Auditorium/"}, + [2365810020] = {"@Sansa Keep/Strikebreak/"}, + [2365810021] = {"@Sansa Keep/Alcove Near Locked Door/"}, + [2365810022] = {"@Sansa Keep/Levers Room/"}, + [2365810023] = {"@Sansa Keep/Lonely Throne/"}, + [2365810024] = {"@Sansa Keep/Near Theatre/"}, + [2365810025] = {"@Sansa Keep/Sunsetter/"}, - [2365810046] = {"@Empty Bailey/Major Key/"}, - [2365810047] = {"@Underbelly/Major Key/"}, - [2365810048] = {"@Tower Remains/Major Key/"}, - [2365810049] = {"@Sansa Keep/Major Key/"}, - [2365810050] = {"@Twilight Theatre/Major Key/"}, + [2365810026] = {"@Listless Library/Sun Greaves/"}, + [2365810027] = {"@Listless Library/Upper Back/"}, + [2365810028] = {"@Listless Library/Locked Door Across/"}, + [2365810029] = {"@Listless Library/Locked Door Left/"}, + + [2365810030] = {"@Twilight Theatre/Soul Cutter/"}, + [2365810031] = {"@Twilight Theatre/Back Of Auditorium/"}, + [2365810032] = {"@Twilight Theatre/Center Stage/"}, + [2365810033] = {"@Twilight Theatre/Locked Door/"}, + [2365810034] = {"@Twilight Theatre/Murderous Goat/"}, + [2365810035] = {"@Twilight Theatre/Corner Beam/"}, + + [2365810036] = {"@Empty Bailey/Solar Wind/"}, + [2365810037] = {"@Empty Bailey/Center Steeple/"}, + [2365810038] = {"@Empty Bailey/Cheese Bell/"}, + [2365810039] = {"@Empty Bailey/Guarded Hand/"}, + [2365810040] = {"@Empty Bailey/Inside Building/"}, + + [2365810041] = {"@Underbelly/Ascendant Light/"}, + [2365810042] = {"@Underbelly/Alcove Near Light/"}, + [2365810043] = {"@Underbelly/Building Near Little Guy/"}, + [2365810044] = {"@Underbelly/Locked Door/"}, + [2365810045] = {"@Underbelly/Main Room/"}, + [2365810046] = {"@Underbelly/Rafters Near Keep/"}, + [2365810047] = {"@Underbelly/Strikebreak Wall/"}, + [2365810048] = {"@Underbelly/Surrounded By Holes/"}, + + [2365810049] = {"@Tower Remains/Cling Gem/"}, + [2365810050] = {"@Tower Remains/Atop The Tower/"}, [2365810051] = {"@Listless Library/Sun Greaves Split/"}, [2365810052] = {"@Listless Library/Sun Greaves Split/"}, From 942a342d96f4d838bd4cffb616e768c92a31e82c Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Wed, 27 Dec 2023 03:10:31 +0100 Subject: [PATCH 18/24] fix whitespace in location_mapping.lua --- scripts/autotracking/location_mapping.lua | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/scripts/autotracking/location_mapping.lua b/scripts/autotracking/location_mapping.lua index 6abe9ad..0ef8b7c 100644 --- a/scripts/autotracking/location_mapping.lua +++ b/scripts/autotracking/location_mapping.lua @@ -4,32 +4,32 @@ -- here are the SM locations as an example: https://github.com/Cyb3RGER/sm_ap_tracker/blob/main/scripts/autotracking/location_mapping.lua LOCATION_MAPPING = { [2365810001] = {"@Dilapitaded Dungeon/Dream Breaker/"}, - [2365810002] = {"@Dilapitaded Dungeon/Slide/"}, - [2365810003] = {"@Dilapitaded Dungeon/Alcove Near Mirror/"}, - [2365810004] = {"@Dilapitaded Dungeon/Dark Orbs/"}, - [2365810005] = {"@Dilapitaded Dungeon/Past Poles/"}, - [2365810006] = {"@Dilapitaded Dungeon/Rafters/"}, + [2365810002] = {"@Dilapitaded Dungeon/Slide/"}, + [2365810003] = {"@Dilapitaded Dungeon/Alcove Near Mirror/"}, + [2365810004] = {"@Dilapitaded Dungeon/Dark Orbs/"}, + [2365810005] = {"@Dilapitaded Dungeon/Past Poles/"}, + [2365810006] = {"@Dilapitaded Dungeon/Rafters/"}, [2365810007] = {"@Dilapitaded Dungeon/Strong Eyes/"}, - [2365810008] = {"@Castle Sansa/Indignation/"}, - [2365810009] = {"@Castle Sansa/Alcove Near Dungeon/"}, - [2365810010] = {"@Castle Sansa/Balcony/"}, - [2365810011] = {"@Castle Sansa/Corner Corridor/"}, - [2365810012] = {"@Castle Sansa/Floater In Courtyard/"}, - [2365810013] = {"@Castle Sansa/Locked Door/"}, - [2365810014] = {"@Castle Sansa/Platform In Main Halls/"}, - [2365810015] = {"@Castle Sansa/Tall Room Near Wheel Crawlers/"}, - [2365810016] = {"@Castle Sansa/Wheel Crawlers/"}, - [2365810017] = {"@Upper Castle Sansa/High Climb From Courtyard/"}, - [2365810018] = {"@Upper Castle Sansa/Alcove Near Scythe Corridor/"}, - [2365810019] = {"@Upper Castle Sansa/Near Theatre Front/"}, + [2365810008] = {"@Castle Sansa/Indignation/"}, + [2365810009] = {"@Castle Sansa/Alcove Near Dungeon/"}, + [2365810010] = {"@Castle Sansa/Balcony/"}, + [2365810011] = {"@Castle Sansa/Corner Corridor/"}, + [2365810012] = {"@Castle Sansa/Floater In Courtyard/"}, + [2365810013] = {"@Castle Sansa/Locked Door/"}, + [2365810014] = {"@Castle Sansa/Platform In Main Halls/"}, + [2365810015] = {"@Castle Sansa/Tall Room Near Wheel Crawlers/"}, + [2365810016] = {"@Castle Sansa/Wheel Crawlers/"}, + [2365810017] = {"@Upper Castle Sansa/High Climb From Courtyard/"}, + [2365810018] = {"@Upper Castle Sansa/Alcove Near Scythe Corridor/"}, + [2365810019] = {"@Upper Castle Sansa/Near Theatre Front/"}, - [2365810020] = {"@Sansa Keep/Strikebreak/"}, - [2365810021] = {"@Sansa Keep/Alcove Near Locked Door/"}, - [2365810022] = {"@Sansa Keep/Levers Room/"}, + [2365810020] = {"@Sansa Keep/Strikebreak/"}, + [2365810021] = {"@Sansa Keep/Alcove Near Locked Door/"}, + [2365810022] = {"@Sansa Keep/Levers Room/"}, [2365810023] = {"@Sansa Keep/Lonely Throne/"}, - [2365810024] = {"@Sansa Keep/Near Theatre/"}, - [2365810025] = {"@Sansa Keep/Sunsetter/"}, + [2365810024] = {"@Sansa Keep/Near Theatre/"}, + [2365810025] = {"@Sansa Keep/Sunsetter/"}, [2365810026] = {"@Listless Library/Sun Greaves/"}, [2365810027] = {"@Listless Library/Upper Back/"}, From 342f640bda1195a99f22baca4502eba69c0580ea Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Wed, 27 Dec 2023 03:19:47 +0100 Subject: [PATCH 19/24] implement and simplify layout switching --- items/items.json | 50 ------------ scripts/autotracking/archipelago.lua | 52 ++---------- scripts/layout_switch.lua | 118 +++++++-------------------- 3 files changed, 37 insertions(+), 183 deletions(-) diff --git a/items/items.json b/items/items.json index 115a68f..a36cfed 100644 --- a/items/items.json +++ b/items/items.json @@ -221,56 +221,6 @@ } ] }, - // LAYOUTS - { - "name": "Progressive Breaker Layout", - "type": "toggle", - "img": "images/icons/icon_attack.png", - "disabled_img": "images/icons/icon_attack_grey.png", - "codes": "progbreakerLayout" - }, - { - "name": "Progressive Slide Layout", - "type": "toggle", - "img": "images/icons/icon_slide.png", - "disabled_img": "images/icons/icon_slide_grey.png", - "codes": "progslideLayout" - }, - { - "name": "Split Kicks Layout", - "type": "toggle", - "img": "images/icons/icon_HeliacalPower.png", - "disabled_img": "images/icons/icon_HeliacalPower_grey.png", - "codes": "splitkickLayout" - }, - { - "name": "Progressive Breaker and Progressive Slide Layout", - "type": "toggle", - "img": "images/icons/icon_HeliacalPower.png", - "disabled_img": "images/icons/icon_HeliacalPower_grey.png", - "codes": "progbreakerprogslideLayout" - }, - { - "name": "Progressive Breaker and Split Kick Layout", - "type": "toggle", - "img": "images/icons/icon_HeliacalPower.png", - "disabled_img": "images/icons/icon_HeliacalPower_grey.png", - "codes": "progbreakersplitkickLayout" - }, - { - "name": "Progressive Slide and Split Kick Layout", - "type": "toggle", - "img": "images/icons/icon_HeliacalPower.png", - "disabled_img": "images/icons/icon_HeliacalPower_grey.png", - "codes": "progslidesplitkickLayout" - }, - { - "name": "All Progressives and Split Kick Layout", - "type": "toggle", - "img": "images/icons/icon_HeliacalPower.png", - "disabled_img": "images/icons/icon_HeliacalPower_grey.png", - "codes": "progsandsplitLayout" - }, // EXAMPLE CODE { "name": "Progressive", diff --git a/scripts/autotracking/archipelago.lua b/scripts/autotracking/archipelago.lua index b35282f..86eabc2 100644 --- a/scripts/autotracking/archipelago.lua +++ b/scripts/autotracking/archipelago.lua @@ -88,7 +88,9 @@ function onClear(slot_data) obj.Active = slot_data.obscure_logic end end - + + pauseLayoutUpdate = true -- pause updating until all codes are set since update is expensive + if slot_data.progressive_breaker then print("slot_data.progressive_breaker: " .. tostring(slot_data.progressive_breaker)) local obj = Tracker:FindObjectForCode("op_progbreaker") @@ -105,16 +107,9 @@ function onClear(slot_data) end end - --if slot_data.split_sun_greaves then - --print("slot_data.split_sun_greaves: " .. tostring(slot_data.split_sun_greaves)) - --local obj = Tracker:FindObjectForCode("op_splitkick_on") - --if obj then - --obj.Active = slot_data.split_sun_greaves - --end - --end - if slot_data.split_sun_greaves then print("slot_data.split_sun_greaves: " .. tostring(slot_data.split_sun_greaves)) + -- op_splitkick is progressive because both stages are used for visibility_rules if slot_data.split_sun_greaves == false then Tracker:FindObjectForCode("op_splitkick_on").CurrentStage = 0 elseif slot_data.split_sun_greaves == true then @@ -122,43 +117,8 @@ function onClear(slot_data) end end - -- Layout Toggles - if slot_data.progressive_breaker and slot_data.progressive_slide and slot_data.split_sun_greaves then - local obj = Tracker:FindObjectForCode("progsandsplitLayout") - if obj then - obj.Active = slot_data.progressive_breaker and slot_data.progressive_slide and slot_data.split_sun_greaves - end - elseif slot_data.progressive_breaker and slot_data.progressive_slide then - local obj = Tracker:FindObjectForCode("progbreakerprogslideLayout") - if obj then - obj.Active = slot_data.progressive_breaker and slot_data.progressive_slide - end - elseif slot_data.progressive_breaker and slot_data.split_sun_greaves then - local obj = Tracker:FindObjectForCode("progbreakersplitkickLayout") - if obj then - obj.Active = slot_data.progressive_breaker and slot_data.split_sun_greaves - end - elseif slot_data.progressive_slide and slot_data.split_sun_greaves then - local obj = Tracker:FindObjectForCode("progslidesplitkickLayout") - if obj then - obj.Active = slot_data.progressive_slide and slot_data.split_sun_greaves - end - elseif slot_data.progressive_breaker then - local obj = Tracker:FindObjectForCode("progbreakerLayout") - if obj then - obj.Active = slot_data.progressive_breaker - end - elseif slot_data.progressive_slide then - local obj = Tracker:FindObjectForCode("progslideLayout") - if obj then - obj.Active = slot_data.progressive_slide - end - elseif slot_data.split_sun_greaves then - local obj = Tracker:FindObjectForCode("splitkickLayout") - if obj then - obj.Active = slot_data.split_sun_greaves - end - end + pauseLayoutUpdate = false + updateLayout() -- actually update LOCAL_ITEMS = {} GLOBAL_ITEMS = {} diff --git a/scripts/layout_switch.lua b/scripts/layout_switch.lua index 4a9f0cb..88324a0 100644 --- a/scripts/layout_switch.lua +++ b/scripts/layout_switch.lua @@ -1,95 +1,39 @@ -- LAYOUT SWITCHING -- change layout depending on options +local currentLayoutNum = 1 -- standard -function apLayoutChange1() - local progBreaker = Tracker:FindObjectForCode("progbreakerLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBreaker.Active then - Tracker:AddLayouts("layouts/items_only_progbreaker.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end +function updateLayout() + if pauseLayoutUpdate then -- global set from AP autotracking to pause updating + return -- update deferred end -end - -function apLayoutChange2() - local progSlide = Tracker:FindObjectForCode("progslideLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progSlide.Active then - Tracker:AddLayouts("layouts/items_only_progslide.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange3() - local splitKick = Tracker:FindObjectForCode("splitkickLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if splitKick.Active then - Tracker:AddLayouts("layouts/items_only_splitkick.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange4() - local progBprogS = Tracker:FindObjectForCode("progbreakerprogslideLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBprogS.Active then - Tracker:AddLayouts("layouts/items_progbreaker_and_progslide.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange5() - local progBsplitK = Tracker:FindObjectForCode("progbreakersplitkickLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBsplitK.Active then - Tracker:AddLayouts("layouts/items_progbreaker_and_splitkick.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange6() - local progSsplitK = Tracker:FindObjectForCode("progslidesplitkickLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progSsplitK.Active then - Tracker:AddLayouts("layouts/items_progslide_and_splitkick.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end - end -end - -function apLayoutChange7() - local progBSsplitK = Tracker:FindObjectForCode("progsandsplitLayout") - if (string.find(Tracker.ActiveVariantUID, "standard")) then - if progBSsplitK.Active then - Tracker:AddLayouts("layouts/items_progs_and_split.json") - --Tracker:AddLayouts("layouts/broadcast_horizontal_AP.json") -- ADD LATER - else - Tracker:AddLayouts("layouts/items_standard.json") - end + -- read toggles + local progBreaker = Tracker:ProviderCountForCode("op_progbreaker") > 0 + local progSlide = Tracker:ProviderCountForCode("op_progslide") > 0 + local splitKicks = Tracker:ProviderCountForCode("op_splitkick_on") > 0 + -- encode 3 toggles into a 3 bit integer (8 states), +1 for Lua array starting at 1 + local layoutNum = 1 + (progBreaker and 1 or 0) + (progSlide and 2 or 0) + (splitKicks and 4 or 0) + print(tostring(currentLayoutNum) .. " -> " .. tostring(layoutNum)) + if layoutNum == currentLayoutNum then + return -- unchanged end + -- select layout from number + local layoutNames = { + "layouts/items_standard.json", + "layouts/items_only_progbreaker.json", + "layouts/items_only_progslide.json", + "layouts/items_progbreaker_and_progslide.json", + "layouts/items_only_splitkick.json", + "layouts/items_progbreaker_and_splitkick.json", + "layouts/items_progslide_and_splitkick.json", + "layouts/items_progs_and_split.json", + } + local layoutName = layoutNames[layoutNum] + -- load layout + Tracker:AddLayouts(layoutName) + currentLayoutNum = layoutNum -- remember what is currently loaded end -ScriptHost:AddWatchForCode("useApLayout1", "progbreakerLayout", apLayoutChange1) -ScriptHost:AddWatchForCode("useApLayout2", "progslideLayout", apLayoutChange2) -ScriptHost:AddWatchForCode("useApLayout3", "splitkickLayout", apLayoutChange3) -ScriptHost:AddWatchForCode("useApLayout4", "progbreakerprogslideLayout", apLayoutChange4) -ScriptHost:AddWatchForCode("useApLayout5", "progbreakersplitkickLayout", apLayoutChange5) -ScriptHost:AddWatchForCode("useApLayout6", "progslidesplitkickLayout", apLayoutChange6) -ScriptHost:AddWatchForCode("useApLayout7", "progsandsplitLayout", apLayoutChange7) +ScriptHost:AddWatchForCode("op_progbreaker", "op_progbreaker", updateLayout) +ScriptHost:AddWatchForCode("op_progslide", "op_progslide", updateLayout) +ScriptHost:AddWatchForCode("op_splitkick", "op_splitkick", updateLayout) From 8f027392cf9b8b11d42820a36a7f56173ad68885 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Wed, 27 Dec 2023 14:36:48 +0100 Subject: [PATCH 20/24] implement prog breaker and slide logic --- scripts/layout_switch.lua | 1 - scripts/logic/logic.lua | 56 ++++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/scripts/layout_switch.lua b/scripts/layout_switch.lua index 88324a0..02293d7 100644 --- a/scripts/layout_switch.lua +++ b/scripts/layout_switch.lua @@ -13,7 +13,6 @@ function updateLayout() local splitKicks = Tracker:ProviderCountForCode("op_splitkick_on") > 0 -- encode 3 toggles into a 3 bit integer (8 states), +1 for Lua array starting at 1 local layoutNum = 1 + (progBreaker and 1 or 0) + (progSlide and 2 or 0) + (splitKicks and 4 or 0) - print(tostring(currentLayoutNum) .. " -> " .. tostring(layoutNum)) if layoutNum == currentLayoutNum then return -- unchanged end diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index b081da8..a7d5c31 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -29,6 +29,9 @@ local state = State:new(def) -- TODO: add caching and update in watch for code local glitchDef = Definition:new() -- "world" definition for out-of-logic local glitchState = State:new(glitchDef) -- TODO: add caching and update in watch for code +local isProgBreaker = false -- caching here to avoid going through Tracker +local isProgSlide = false + -- version helper local v = {} PopVersion:gsub("([^%.]+)", function(c) v[#v+1] = tonumber(c) end) @@ -47,6 +50,9 @@ local codes = { ["Soul Cutter"] = "cutter", ["Heliacal Power"] = "heliacal", ["Small Key"] = "smallkey", + -- Progressive breaker and slide are handled differently. + --["Progressive Dream Breaker"] = "progbreaker", + --["Progressive Slide"] = "progslide", -- Major Keys have custom handling since they are just a count in the tracker. --["Major Key - Empty Bailey"] = "majorkey", --["Major Key - The Underbelly"] = "majorkey", @@ -56,27 +62,48 @@ local codes = { } -- patch up State.has and State.count to match the codes -local _has = State.has local _count = State.count State.has = function(state, name) - local code = codes[name] - if code then - return _has(state, code) - end - if name:find("^Major Key - ") then - return _count(state, "majorkey") >= 5 - end - -- TODO: warn? - return _has(state, name) + return state:count(name) > 0 -- use count to only implement the crazy mappings once end State.count = function(state, name) + -- handle the ones that are simple lookups local code = codes[name] if code then + if isProgBreaker and (code == "breaker" or code == "strikebreak" or code == "cutter") then + -- individual breakers and slides have to return explicit 0 for progressive + -- because the state of the individual items is untouched when switching + -- NOTE: the rules being separate for progressive and non-progressive + -- could've been fixed/simplified in the APWorld by overriding collect + return 0 + end + if isProgSlide and (code == "slide" or code == "solar") then + return 0 + end return _count(state, code) end + -- handle the ones that need special handling + if name == "Progressive Dream Breaker" then + -- when switching settings to non-progressive, we have to return explicit 0 (the prog item state is unchanged) + if not isProgBreaker then + return 0 + end + -- not sure if progressive items should be able to return multiple of the same code, + -- but that's currently not the case in Pop at least, so we use CurrentStage + return Tracker:FindObjectForCode("progbreaker").CurrentStage + end + if name == "Progressive Slide" then + -- as above + if not isProgSlide then + return 0 + end + -- as above + return Tracker:FindObjectForCode("progslide").CurrentStage + end if name:find("^Major Key - ") then + -- map each individual key to having all 5 return (_count(state, "majorkey") >= 5) and 1 or 0 end -- TODO: warn? @@ -179,6 +206,12 @@ function logicChanged() -- run by watch for code "logic", "obscure" glitchState.stale = true end +function progLogicChanged() -- run by watch for code "op_progbreaker", "op_progslide" + -- cache prog breaker/slide into variable for faster access + isProgBreaker = Tracker:ProviderCountForCode("op_progbreaker") > 0 + isProgSlide = Tracker:ProviderCountForCode("op_progslide") > 0 +end + -- initialize logic create_regions() -- TODO: this depends on progressive options, so we need another watch for code logicChanged() @@ -186,6 +219,9 @@ logicChanged() -- add watches ScriptHost:AddWatchForCode("difficultyChanged", "logic", logicChanged) ScriptHost:AddWatchForCode("obscureChanged", "obscure", logicChanged) +ScriptHost:AddWatchForCode("splitSunGreavesChanged", "op_splitkick_on", logicChanged) +ScriptHost:AddWatchForCode("progBreakerLogicChanged", "op_progbreaker", progLogicChanged) +ScriptHost:AddWatchForCode("progSlideLogicChanged", "op_progslide", progLogicChanged) if hasAnyWatch then ScriptHost:AddWatchForCode("stateChanged", "*", stateChanged) end From 4ab91605622cc685892935670a57ebe20ea26e81 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Wed, 27 Dec 2023 15:13:34 +0100 Subject: [PATCH 21/24] update comments --- scripts/logic/logic.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index a7d5c31..1bded2b 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -106,7 +106,9 @@ State.count = function(state, name) -- map each individual key to having all 5 return (_count(state, "majorkey") >= 5) and 1 or 0 end - -- TODO: warn? + if DEBUG then + print("Unknown item " .. name) + end return _count(state, name) end @@ -213,7 +215,7 @@ function progLogicChanged() -- run by watch for code "op_progbreaker", "op_prog end -- initialize logic -create_regions() -- TODO: this depends on progressive options, so we need another watch for code +create_regions() -- NOTE: we don't handle can_create for Locations, so this needs to only be run once logicChanged() -- add watches From 557dacd7fb5d92ebf0eb17251115aa88b9f8ef9d Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Wed, 27 Dec 2023 15:53:22 +0100 Subject: [PATCH 22/24] implement split kick logic --- scripts/logic/logic.lua | 13 +++++++++++-- scripts/logic/options.lua | 7 +++++++ scripts/logic/rules/base.lua | 4 ++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/scripts/logic/logic.lua b/scripts/logic/logic.lua index 1bded2b..6f9d3da 100644 --- a/scripts/logic/logic.lua +++ b/scripts/logic/logic.lua @@ -31,6 +31,7 @@ local glitchState = State:new(glitchDef) -- TODO: add caching and update in wat local isProgBreaker = false -- caching here to avoid going through Tracker local isProgSlide = false +local isSplitKicks = false -- version helper local v = {} @@ -40,7 +41,7 @@ local hasAnyWatch = v[1] > 0 or v[2] > 25 or v[2] == 25 and v[3] > 4 -- availab -- item name to code mapping local codes = { ["Dream Breaker"] = "breaker", - ["Sun Greaves"] = "greaves", + ["Sun Greaves"] = "greaves", -- provides 3 kicks if not split ["Slide"] = "slide", ["Solar Wind"] = "solar", ["Sunsetter"] = "sunsetter", @@ -48,8 +49,9 @@ local codes = { ["Cling Gem"] = "cling", ["Ascendant Light"] = "ascendant", ["Soul Cutter"] = "cutter", - ["Heliacal Power"] = "heliacal", + ["Heliacal Power"] = "heliacal", -- provides 1 kick if not split ["Small Key"] = "smallkey", + ["Air Kick"] = "splitkick", -- 4 individual kicks if split -- Progressive breaker and slide are handled differently. --["Progressive Dream Breaker"] = "progbreaker", --["Progressive Slide"] = "progslide", @@ -82,6 +84,12 @@ State.count = function(state, name) if isProgSlide and (code == "slide" or code == "solar") then return 0 end + if isSplitKicks and (code == "greaves" or code == "heliacal") then + return 0 + end + if not isSplitKicks and code == "splitkick" then + return 0 + end return _count(state, code) end -- handle the ones that need special handling @@ -202,6 +210,7 @@ end function logicChanged() -- run by watch for code "logic", "obscure" print("logic changed") + isSplitKicks = Tracker:ProviderCountForCode("op_splitkick_on") > 0 -- cache for State.count set_options() -- update world option emulation set_rules() -- recreate rules with new code(s) in Tracker state.stale = true diff --git a/scripts/logic/options.lua b/scripts/logic/options.lua index 3852f57..61949c1 100644 --- a/scripts/logic/options.lua +++ b/scripts/logic/options.lua @@ -25,10 +25,17 @@ ObscureLogic = { setmetatable(ObscureLogic, Toggle) +SplitSunGreaves = { + code = "op_splitkick_on" +} +setmetatable(SplitSunGreaves, Toggle) + + options = { pseudoregalia_options = { logic_level = LogicLevel, obscure_logic = ObscureLogic, + split_sun_greaves = SplitSunGreaves, } } diff --git a/scripts/logic/rules/base.lua b/scripts/logic/rules/base.lua index eabcaf0..f4be683 100644 --- a/scripts/logic/rules/base.lua +++ b/scripts/logic/rules/base.lua @@ -160,7 +160,7 @@ function PseudoregaliaRulesHelpers:knows_obscure(state) end function PseudoregaliaRulesHelpers:set_pseudoregalia_rules() - local split_kicks = false -- TODO: load from code or slot data + local split_kicks = self.definition.options.split_sun_greaves.value local obscure_logic = self.definition.options.obscure_logic.value local logic_level = self.definition.options.logic_level.value @@ -187,7 +187,7 @@ function PseudoregaliaRulesHelpers:set_pseudoregalia_rules() 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) + 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 From 821ef03cd5b773214cb3c338f50abf0e49fcfbc0 Mon Sep 17 00:00:00 2001 From: Brooty Johnson <83629348+Br00ty@users.noreply.github.com> Date: Mon, 1 Jan 2024 06:35:15 -0500 Subject: [PATCH 23/24] fix logic for underbelly - surrounded by holes --- scripts/logic/regions.lua | 2 +- scripts/logic/rules/expert.lua | 16 ++++++++-------- scripts/logic/rules/hard.lua | 9 +++++---- scripts/logic/rules/lunatic.lua | 13 +++++++------ scripts/logic/rules/normal.lua | 5 +++-- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/scripts/logic/regions.lua b/scripts/logic/regions.lua index 99c9644..7c0ea04 100644 --- a/scripts/logic/regions.lua +++ b/scripts/logic/regions.lua @@ -85,7 +85,7 @@ region_table = { }, ["Keep => Underbelly"] = { "Keep Main", - "Underbelly Hole", + "Underbelly => Keep", }, ["Keep Path To Throne"] = { }, diff --git a/scripts/logic/rules/expert.lua b/scripts/logic/rules/expert.lua index 1585dbd..cb75dd9 100644 --- a/scripts/logic/rules/expert.lua +++ b/scripts/logic/rules/expert.lua @@ -124,8 +124,7 @@ function PseudoregaliaExpertRules.new(cls, definition) or self:has_gem(state) or self:has_slide(state) end, - ["Keep Locked Room -> Keep Sunsetter"] = free, - ["Keep => Underbelly -> Underbelly Hole"] = free, + --# ["Keep Locked Room -> Keep Sunsetter"] = free, ["Keep Main -> Theatre Outside Scythe Corridor"] = function(state) return self:has_gem(state) or self:get_kicks(state, 1) @@ -137,7 +136,7 @@ function PseudoregaliaExpertRules.new(cls, definition) end, --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, --# "Keep => Underbelly -> Keep Main" = function(state) True, - --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, + --# "Keep => Underbelly -> Underbelly => Keep" = function(state) True, --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) @@ -408,11 +407,12 @@ function PseudoregaliaExpertRules.new(cls, definition) or self:has_slide(state) end, ["The Underbelly - Strikebreak Wall"] = function(state) - return self:can_bounce(state) - or self:get_kicks(state, 3) - or self:get_kicks(state, 1) and self:has_plunge(state) - or self:has_slide(state) and self:kick_or_plunge(state, 1) - or self:has_slide(state) and self:has_gem(state) + return self:can_strikebreak(state) and ( + self:can_bounce(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + or self:has_slide(state) and self:has_gem(state)) end, ["The Underbelly - Surrounded By Holes"] = function(state) return self:can_soulcutter(state) diff --git a/scripts/logic/rules/hard.lua b/scripts/logic/rules/hard.lua index 4f3cf00..790c54f 100644 --- a/scripts/logic/rules/hard.lua +++ b/scripts/logic/rules/hard.lua @@ -132,7 +132,7 @@ function PseudoregaliaHardRules.new(cls, definition) end, --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, --# "Keep => Underbelly -> Keep Main" = function(state) True, - --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, + --# "Keep => Underbelly -> Underbelly => Keep" = function(state) True, --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) @@ -400,9 +400,10 @@ function PseudoregaliaHardRules.new(cls, definition) or self:get_kicks(state, 2) end, ["The Underbelly - Strikebreak Wall"] = function(state) - return self:can_bounce(state) - or self:get_kicks(state, 3) - or self:get_kicks(state, 1) and self:has_plunge(state) + return self:can_strikebreak(state) and ( + self:can_bounce(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state)) end, ["The Underbelly - Surrounded By Holes"] = function(state) return self:can_soulcutter(state) diff --git a/scripts/logic/rules/lunatic.lua b/scripts/logic/rules/lunatic.lua index 877ae70..07e63cc 100644 --- a/scripts/logic/rules/lunatic.lua +++ b/scripts/logic/rules/lunatic.lua @@ -131,7 +131,7 @@ function PseudoregaliaLunaticRules.new(cls, definition) end, --# "Keep Locked Room -> Keep Sunsetter" = function(state) True, --# "Keep => Underbelly -> Keep Main" = function(state) True, - --# "Keep => Underbelly -> Underbelly Hole" = function(state) True, + --# "Keep => Underbelly -> Underbelly => Keep" = function(state) True, --# "Underbelly => Dungeon -> Dungeon Escape Lower" = function(state) True, --# "Underbelly => Dungeon -> Underbelly Light Pillar" = function(state) True, ["Underbelly => Dungeon -> Underbelly Ascendant Light"] = function(state) @@ -406,11 +406,12 @@ function PseudoregaliaLunaticRules.new(cls, definition) or self:has_slide(state) end, ["The Underbelly - Strikebreak Wall"] = function(state) - return self:can_bounce(state) - or self:get_kicks(state, 3) - or self:get_kicks(state, 1) and self:has_plunge(state) - or self:has_slide(state) and self:kick_or_plunge(state, 1) - or self:has_slide(state) and self:has_gem(state) + return self:can_strikebreak(state) and ( + self:can_bounce(state) + or self:get_kicks(state, 3) + or self:get_kicks(state, 1) and self:has_plunge(state) + or self:has_slide(state) and self:kick_or_plunge(state, 1) + or self:has_slide(state) and self:has_gem(state)) end, ["The Underbelly - Surrounded By Holes"] = function(state) return self:can_soulcutter(state) diff --git a/scripts/logic/rules/normal.lua b/scripts/logic/rules/normal.lua index 1161eb7..1505503 100644 --- a/scripts/logic/rules/normal.lua +++ b/scripts/logic/rules/normal.lua @@ -346,13 +346,14 @@ function PseudoregaliaNormalRules.new(cls, definition) return self:has_plunge(state) or self:get_kicks(state, 3) end, ["The Underbelly - Strikebreak Wall"] = function(state) - return (self:can_bounce(state) + return self:can_strikebreak(state) and ( + 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)) + 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 From b6bd9f63bf70b0cb7b72497eb99419011de5ba00 Mon Sep 17 00:00:00 2001 From: Brooty Johnson <83629348+Br00ty@users.noreply.github.com> Date: Tue, 2 Jan 2024 16:18:48 -0500 Subject: [PATCH 24/24] update key for game completion --- scripts/autotracking/archipelago.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/autotracking/archipelago.lua b/scripts/autotracking/archipelago.lua index 86eabc2..2743aeb 100644 --- a/scripts/autotracking/archipelago.lua +++ b/scripts/autotracking/archipelago.lua @@ -8,7 +8,7 @@ GLOBAL_ITEMS = {} HOSTED = {gameover=1} function onSetReply(key, value, old) - if key == "Pseudoregalia - Player " .. Archipelago.PlayerNumber .. " - Game Complete" then + if key == "Pseudoregalia - Team " .. Archipelago.TeamNumber .. " - Player " .. Archipelago.PlayerNumber .. " - Game Complete" then Tracker:FindObjectForCode("gameover", ITEMS).Active = true end end @@ -124,7 +124,7 @@ function onClear(slot_data) GLOBAL_ITEMS = {} --Tracker:FindObjectForCode("apLayout").Active = true - Archipelago:SetNotify({"Pseudoregalia - Player " .. Archipelago.PlayerNumber .. " - Game Complete"}) + Archipelago:SetNotify({"Pseudoregalia - Team " .. Archipelago.TeamNumber .. " - Player " .. Archipelago.PlayerNumber .. " - Game Complete"}) end -- called when an item gets collected