mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00

The most serious issue this PR addresses is that entrances that use doors without items (a small subset of doors when door shuffle is on, but *every* door when door shuffle is off, which is the default) underestimate the requirements needed to use that entrance. The logic would calculate the panels needed to open the door, but would neglect to keep track of the rooms those panels were in, meaning that doors would be considered openable if you had the colors needed to solve a panel that's in a room you have no access to. Another issue is that, previously, logic would always consider the "ANOTHER TRY" panel accessible for the purposes of the LEVEL 2 panel hunt. This could result in seeds where the player is expected to have exactly the correct number of solves to reach LEVEL 2, but in reality is short by one because ANOTHER TRY itself is not revealed until the panel hunt is complete. This change marks ANOTHER TRY as non-counting, because even though it is technically a counting panel in-game, it can never contribute to the LEVEL 2 panel hunt. This issue could also apply to THE MASTER, since it is the only other counting panel with special access rules, although it is much less likely. This change adds special handling for counting THE MASTER. These issues were possible to manifest whenever the LEVEL 2 panel hunt was enabled, which it is by default. Smaller logic issues also fixed in this PR: * The Orange Tower Basement MASTERY panel was marked as requiring the mastery doors to be opened, when it was actually possible to get it without them by using a painting to get into the room. * The Pilgrim Room painting item was incorrectly being marked as a filler item, despite it being progression. * There has been another update to the game that adds connections between areas that were previously not connected. These changes were additive, which is why they are not critical. * The panel stacks in the rhyme room now require both colours on each panel.
330 lines
10 KiB
Ruby
330 lines
10 KiB
Ruby
# Script to validate a level config file. This checks that the names used within
|
|
# the file are consistent. It also checks that the panel and door IDs mentioned
|
|
# all exist in the map file.
|
|
#
|
|
# Usage: validate_config.rb [config file] [map file]
|
|
|
|
require 'set'
|
|
require 'yaml'
|
|
|
|
configpath = ARGV[0]
|
|
mappath = ARGV[1]
|
|
|
|
panels = Set["Countdown Panels/Panel_1234567890_wanderlust"]
|
|
doors = Set["Naps Room Doors/Door_hider_new1", "Tower Room Area Doors/Door_wanderer_entrance"]
|
|
paintings = Set[]
|
|
|
|
File.readlines(mappath).each do |line|
|
|
line.match(/node name=\"(.*)\" parent=\"Panels\/(.*)\" instance/) do |m|
|
|
panels.add(m[2] + "/" + m[1])
|
|
end
|
|
line.match(/node name=\"(.*)\" parent=\"Doors\/(.*)\" instance/) do |m|
|
|
doors.add(m[2] + "/" + m[1])
|
|
end
|
|
line.match(/node name=\"(.*)\" parent=\"Decorations\/Paintings\" instance/) do |m|
|
|
paintings.add(m[1])
|
|
end
|
|
line.match(/node name=\"(.*)\" parent=\"Decorations\/EndPanel\" instance/) do |m|
|
|
panels.add("EndPanel/" + m[1])
|
|
end
|
|
end
|
|
|
|
configured_rooms = Set["Menu"]
|
|
configured_doors = Set[]
|
|
configured_panels = Set[]
|
|
|
|
mentioned_rooms = Set[]
|
|
mentioned_doors = Set[]
|
|
mentioned_panels = Set[]
|
|
|
|
door_groups = {}
|
|
|
|
directives = Set["entrances", "panels", "doors", "paintings", "progression"]
|
|
panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting", "hunt"]
|
|
door_directives = Set["id", "painting_id", "panels", "item_name", "location_name", "skip_location", "skip_item", "group", "include_reduce", "junk_item", "event"]
|
|
painting_directives = Set["id", "enter_only", "exit_only", "orientation", "required_door", "required", "required_when_no_doors", "move", "req_blocked", "req_blocked_when_no_doors"]
|
|
|
|
non_counting = 0
|
|
|
|
config = YAML.load_file(configpath)
|
|
config.each do |room_name, room|
|
|
configured_rooms.add(room_name)
|
|
|
|
used_directives = Set[]
|
|
room.each_key do |key|
|
|
used_directives.add(key)
|
|
end
|
|
diff_directives = used_directives - directives
|
|
unless diff_directives.empty? then
|
|
puts("#{room_name} has the following invalid top-level directives: #{diff_directives.to_s}")
|
|
end
|
|
|
|
(room["entrances"] || {}).each do |source_room, entrance|
|
|
mentioned_rooms.add(source_room)
|
|
|
|
entrances = []
|
|
if entrance.kind_of? Hash
|
|
if entrance.keys() != ["painting"] then
|
|
entrances = [entrance]
|
|
end
|
|
elsif entrance.kind_of? Array
|
|
entrances = entrance
|
|
end
|
|
|
|
entrances.each do |e|
|
|
entrance_room = e.include?("room") ? e["room"] : room_name
|
|
mentioned_rooms.add(entrance_room)
|
|
mentioned_doors.add(entrance_room + " - " + e["door"])
|
|
end
|
|
end
|
|
|
|
(room["panels"] || {}).each do |panel_name, panel|
|
|
unless panel_name.kind_of? String then
|
|
puts "#{room_name} has an invalid panel name"
|
|
end
|
|
|
|
configured_panels.add(room_name + " - " + panel_name)
|
|
|
|
if panel.include?("id")
|
|
panel_ids = []
|
|
if panel["id"].kind_of? Array
|
|
panel_ids = panel["id"]
|
|
else
|
|
panel_ids = [panel["id"]]
|
|
end
|
|
|
|
panel_ids.each do |panel_id|
|
|
unless panels.include? panel_id then
|
|
puts "#{room_name} - #{panel_name} :::: Invalid Panel ID #{panel_id}"
|
|
end
|
|
end
|
|
else
|
|
puts "#{room_name} - #{panel_name} :::: Panel is missing an ID"
|
|
end
|
|
|
|
if panel.include?("required_room")
|
|
required_rooms = []
|
|
if panel["required_room"].kind_of? Array
|
|
required_rooms = panel["required_room"]
|
|
else
|
|
required_rooms = [panel["required_room"]]
|
|
end
|
|
|
|
required_rooms.each do |required_room|
|
|
mentioned_rooms.add(required_room)
|
|
end
|
|
end
|
|
|
|
if panel.include?("required_door")
|
|
required_doors = []
|
|
if panel["required_door"].kind_of? Array
|
|
required_doors = panel["required_door"]
|
|
else
|
|
required_doors = [panel["required_door"]]
|
|
end
|
|
|
|
required_doors.each do |required_door|
|
|
other_room = required_door.include?("room") ? required_door["room"] : room_name
|
|
mentioned_rooms.add(other_room)
|
|
mentioned_doors.add("#{other_room} - #{required_door["door"]}")
|
|
end
|
|
end
|
|
|
|
if panel.include?("required_panel")
|
|
required_panels = []
|
|
if panel["required_panel"].kind_of? Array
|
|
required_panels = panel["required_panel"]
|
|
else
|
|
required_panels = [panel["required_panel"]]
|
|
end
|
|
|
|
required_panels.each do |required_panel|
|
|
other_room = required_panel.include?("room") ? required_panel["room"] : room_name
|
|
mentioned_rooms.add(other_room)
|
|
mentioned_panels.add("#{other_room} - #{required_panel["panel"]}")
|
|
end
|
|
end
|
|
|
|
unless panel.include?("tag") then
|
|
puts "#{room_name} - #{panel_name} :::: Panel is missing a tag"
|
|
end
|
|
|
|
if panel.include?("non_counting") then
|
|
non_counting += 1
|
|
end
|
|
|
|
bad_subdirectives = []
|
|
panel.keys.each do |key|
|
|
unless panel_directives.include?(key) then
|
|
bad_subdirectives << key
|
|
end
|
|
end
|
|
unless bad_subdirectives.empty? then
|
|
puts "#{room_name} - #{panel_name} :::: Panel has the following invalid subdirectives: #{bad_subdirectives.join(", ")}"
|
|
end
|
|
end
|
|
|
|
(room["doors"] || {}).each do |door_name, door|
|
|
configured_doors.add("#{room_name} - #{door_name}")
|
|
|
|
if door.include?("id")
|
|
door_ids = []
|
|
if door["id"].kind_of? Array
|
|
door_ids = door["id"]
|
|
else
|
|
door_ids = [door["id"]]
|
|
end
|
|
|
|
door_ids.each do |door_id|
|
|
unless doors.include? door_id then
|
|
puts "#{room_name} - #{door_name} :::: Invalid Door ID #{door_id}"
|
|
end
|
|
end
|
|
end
|
|
|
|
if door.include?("painting_id")
|
|
painting_ids = []
|
|
if door["painting_id"].kind_of? Array
|
|
painting_ids = door["painting_id"]
|
|
else
|
|
painting_ids = [door["painting_id"]]
|
|
end
|
|
|
|
painting_ids.each do |painting_id|
|
|
unless paintings.include? painting_id then
|
|
puts "#{room_name} - #{door_name} :::: Invalid Painting ID #{painting_id}"
|
|
end
|
|
end
|
|
end
|
|
|
|
if not door.include?("id") and not door.include?("painting_id") and not door["skip_item"] and not door["event"] then
|
|
puts "#{room_name} - #{door_name} :::: Should be marked skip_item or event if there are no doors or paintings"
|
|
end
|
|
|
|
if door.include?("panels")
|
|
door["panels"].each do |panel|
|
|
if panel.kind_of? Hash then
|
|
other_room = panel.include?("room") ? panel["room"] : room_name
|
|
mentioned_panels.add("#{other_room} - #{panel["panel"]}")
|
|
else
|
|
other_room = panel.include?("room") ? panel["room"] : room_name
|
|
mentioned_panels.add("#{room_name} - #{panel}")
|
|
end
|
|
end
|
|
elsif not door["skip_location"]
|
|
puts "#{room_name} - #{door_name} :::: Should be marked skip_location if there are no panels"
|
|
end
|
|
|
|
if door.include?("group")
|
|
door_groups[door["group"]] ||= 0
|
|
door_groups[door["group"]] += 1
|
|
end
|
|
|
|
bad_subdirectives = []
|
|
door.keys.each do |key|
|
|
unless door_directives.include?(key) then
|
|
bad_subdirectives << key
|
|
end
|
|
end
|
|
unless bad_subdirectives.empty? then
|
|
puts "#{room_name} - #{door_name} :::: Door has the following invalid subdirectives: #{bad_subdirectives.join(", ")}"
|
|
end
|
|
end
|
|
|
|
(room["paintings"] || []).each do |painting|
|
|
if painting.include?("id") and painting["id"].kind_of? String then
|
|
unless paintings.include? painting["id"] then
|
|
puts "#{room_name} :::: Invalid Painting ID #{painting["id"]}"
|
|
end
|
|
else
|
|
puts "#{room_name} :::: Painting is missing an ID"
|
|
end
|
|
|
|
if painting["disable"] then
|
|
# We're good.
|
|
next
|
|
end
|
|
|
|
if painting.include?("orientation") then
|
|
unless ["north", "south", "east", "west"].include? painting["orientation"] then
|
|
puts "#{room_name} - #{painting["id"] || "painting"} :::: Invalid orientation #{painting["orientation"]}"
|
|
end
|
|
else
|
|
puts "#{room_name} :::: Painting is missing an orientation"
|
|
end
|
|
|
|
if painting.include?("required_door")
|
|
other_room = painting["required_door"].include?("room") ? painting["required_door"]["room"] : room_name
|
|
mentioned_doors.add("#{other_room} - #{painting["required_door"]["door"]}")
|
|
|
|
unless painting["enter_only"] then
|
|
puts "#{room_name} - #{painting["id"] || "painting"} :::: Should be marked enter_only if there is a required_door"
|
|
end
|
|
end
|
|
|
|
bad_subdirectives = []
|
|
painting.keys.each do |key|
|
|
unless painting_directives.include?(key) then
|
|
bad_subdirectives << key
|
|
end
|
|
end
|
|
unless bad_subdirectives.empty? then
|
|
puts "#{room_name} - #{painting["id"] || "painting"} :::: Painting has the following invalid subdirectives: #{bad_subdirectives.join(", ")}"
|
|
end
|
|
end
|
|
|
|
(room["progression"] || {}).each do |progression_name, door_list|
|
|
door_list.each do |door|
|
|
if door.kind_of? Hash then
|
|
mentioned_doors.add("#{door["room"]} - #{door["door"]}")
|
|
else
|
|
mentioned_doors.add("#{room_name} - #{door}")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
errored_rooms = mentioned_rooms - configured_rooms
|
|
unless errored_rooms.empty? then
|
|
puts "The folloring rooms are mentioned but do not exist: " + errored_rooms.to_s
|
|
end
|
|
|
|
errored_panels = mentioned_panels - configured_panels
|
|
unless errored_panels.empty? then
|
|
puts "The folloring panels are mentioned but do not exist: " + errored_panels.to_s
|
|
end
|
|
|
|
errored_doors = mentioned_doors - configured_doors
|
|
unless errored_doors.empty? then
|
|
puts "The folloring doors are mentioned but do not exist: " + errored_doors.to_s
|
|
end
|
|
|
|
door_groups.each do |group,num|
|
|
if num == 1 then
|
|
puts "Door group \"#{group}\" only has one door in it"
|
|
end
|
|
end
|
|
|
|
slashed_rooms = configured_rooms.select do |room|
|
|
room.include? "/"
|
|
end
|
|
unless slashed_rooms.empty? then
|
|
puts "The following rooms have slashes in their names: " + slashed_rooms.to_s
|
|
end
|
|
|
|
slashed_panels = configured_panels.select do |panel|
|
|
panel.include? "/"
|
|
end
|
|
unless slashed_panels.empty? then
|
|
puts "The following panels have slashes in their names: " + slashed_panels.to_s
|
|
end
|
|
|
|
slashed_doors = configured_doors.select do |door|
|
|
door.include? "/"
|
|
end
|
|
unless slashed_doors.empty? then
|
|
puts "The following doors have slashes in their names: " + slashed_doors.to_s
|
|
end
|
|
|
|
puts "#{configured_panels.size} panels (#{non_counting} non counting)"
|