关卡重构

master
chendian 1 year ago
parent 7e1cc47ed1
commit 5e54b9dc18

@ -1,53 +0,0 @@
@tool
extends "res://addons/MetroidvaniaSystem/Database/CellPaintEditor.gd"
func _editor_init() -> void:
use_cursor = false
super()
func _editor_input(event: InputEvent):
var cell_data := get_cell_at_cursor()
if event is InputEventMouseMotion:
if cell_data:
var rel := editor.map_overlay.get_local_mouse_position().posmodv(MetSys.CELL_SIZE)
var border := get_square_border_idx(cell_data.borders, rel)
var new_border := -1
var borders: Array[int] = cell_data.borders
for i in 4:
if border == i and borders[i] > -1:
new_border = border
break
if new_border != highlighted_border:
highlighted_border = new_border
editor.map_overlay.queue_redraw()
else:
highlighted_border = -1
super(event)
func update_hovered_room():
if highlighted_border == -1:
highlighted_room.clear()
return
super()
func modify_cell(cell_data: MetroidvaniaSystem.MapData.CellData, mode: int) -> bool:
if highlighted_border == -1:
return false
var modified: bool
if whole_room:
for i in 4:
modified = modify_border(cell_data, i, mode) or modified
else:
modified = modify_border(cell_data, highlighted_border, mode)
return modified
func modify_border(cell_data: MetroidvaniaSystem.MapData.CellData, border: int, mode: int) -> bool:
return false

@ -1,82 +0,0 @@
@tool
extends "res://addons/MetroidvaniaSystem/Database/SubEditor.gd"
enum { MODE_DRAW, MODE_ERASE, MODE_PICK }
var can_pick: bool
var super_use_cursor: bool
var whole_room: bool
func _editor_init() -> void:
super_use_cursor = use_cursor
func _editor_enter():
super()
%Shortcuts.show()
%ShortcutPick.visible = can_pick
func _editor_exit():
super()
%Shortcuts.hide()
func _editor_input(event: InputEvent):
if event is InputEventMouseButton:
if event.pressed:
if event.button_index == MOUSE_BUTTON_LEFT:
if can_pick and not whole_room and event.is_command_or_control_pressed():
paint(MODE_PICK)
else:
paint(MODE_DRAW)
elif event.button_index == MOUSE_BUTTON_RIGHT:
paint(MODE_ERASE)
elif event is InputEventMouseMotion:
if whole_room:
update_hovered_room()
if event.button_mask & (MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_RIGHT):
var erase := bool(event.button_mask & MOUSE_BUTTON_MASK_RIGHT)
paint(MODE_ERASE if erase else MODE_DRAW)
elif event is InputEventKey:
if not event.echo and event.keycode == KEY_SHIFT:
whole_room = event.pressed
use_cursor = super_use_cursor and not whole_room
if whole_room:
update_hovered_room()
else:
highlighted_room.clear()
editor.map_overlay.queue_redraw()
func update_hovered_room():
var hr := highlighted_room
highlighted_room = MetSys.map_data.get_whole_room(get_coords(get_cursor_pos()))
if highlighted_room != hr:
editor.map_overlay.queue_redraw()
func paint(mode: int):
var coords_to_modify: Array[Vector3i]
if whole_room:
coords_to_modify = highlighted_room
else:
coords_to_modify.assign([get_coords(get_cursor_pos())])
var modified: bool
for coords in coords_to_modify:
var cell_data := MetSys.map_data.get_cell_at(coords)
if not cell_data:
continue
if modify_cell(cell_data, mode) or modify_coords(coords, mode):
modified = true
if modified:
mark_modified()
editor.map.queue_redraw()
func modify_cell(cell_data: MetroidvaniaSystem.MapData.CellData, mode: int) -> bool:
return false
func modify_coords(coords: Vector3i, mode: int) -> bool:
return false

@ -1,19 +0,0 @@
[gd_resource type="Theme" format=3 uid="uid://dfyoc5jqnnpf5"]
[resource]
MetSys/colors/active_custom_element = Color(0.1, 0.1, 1, 0.8)
MetSys/colors/assigned_scene = Color(0.235294, 0.701961, 0.443137, 0.8)
MetSys/colors/border_highlight = Color(0, 1, 0, 0.5)
MetSys/colors/current_scene_room = Color(1, 0, 0, 0.5)
MetSys/colors/cursor_color = Color(0, 1, 0, 1)
MetSys/colors/cursor_color_erase = Color(1, 0, 0, 1)
MetSys/colors/custom_element_marker = Color(1, 1, 0, 1)
MetSys/colors/foreign_marked_collectible_room = Color(1, 0, 0, 0.25)
MetSys/colors/group_color = Color(0.576471, 0.439216, 0.858824, 0.8)
MetSys/colors/highlighted_room = Color(0, 1, 0, 0.25)
MetSys/colors/inactive_custom_element = Color(0, 0, 1, 0.4)
MetSys/colors/marked_collectible_room = Color(0.541176, 0.168627, 0.886275, 0.75)
MetSys/colors/room_assigned = Color(1, 1, 1, 1)
MetSys/colors/room_not_assigned = Color(1, 0, 0, 1)
MetSys/colors/scene_cell_border = Color(1, 1, 1, 0.33)
MetSys/colors/scene_room_exit = Color(1, 0, 1, 0.67)

@ -1,57 +0,0 @@
[gd_scene load_steps=8 format=3 uid="uid://crxgu2q73va5y"]
[ext_resource type="Theme" uid="uid://dfyoc5jqnnpf5" path="res://addons/MetroidvaniaSystem/Database/DatabaseTheme.tres" id="1_hfop4"]
[ext_resource type="PackedScene" uid="uid://dfbykrvyqnqr5" path="res://addons/MetroidvaniaSystem/Database/MapEditor.tscn" id="1_lpqq2"]
[ext_resource type="PackedScene" uid="uid://df5fuj4vjr11p" path="res://addons/MetroidvaniaSystem/Database/MapViewer.tscn" id="2_0tuq7"]
[ext_resource type="PackedScene" uid="uid://he12yvtcmf87" path="res://addons/MetroidvaniaSystem/Database/Manage.tscn" id="2_1dme8"]
[sub_resource type="GDScript" id="GDScript_ov8k3"]
script/source = "@tool
extends VBoxContainer
@onready var tabs = $TabContainer
var plugin: EditorPlugin
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0ndnc"]
bg_color = Color(0, 0, 0, 0.501961)
[sub_resource type="ButtonGroup" id="ButtonGroup_u4ywv"]
[node name="Main" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme = ExtResource("1_hfop4")
script = SubResource("GDScript_ov8k3")
[node name="Label" type="Label" parent="."]
layout_mode = 2
theme_type_variation = &"HeaderSmall"
text = "Metroidvania System Database"
horizontal_alignment = 1
[node name="TabContainer" type="TabContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_0ndnc")
tab_alignment = 1
[node name="Map Editor" parent="TabContainer" instance=ExtResource("1_lpqq2")]
layout_mode = 2
mode_group = SubResource("ButtonGroup_u4ywv")
[node name="Map Viewer" parent="TabContainer" instance=ExtResource("2_0tuq7")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="Manage" parent="TabContainer" instance=ExtResource("2_1dme8")]
visible = false
layout_mode = 2

@ -1,463 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://he12yvtcmf87"]
[sub_resource type="GDScript" id="GDScript_kxd0b"]
script/source = "@tool
extends Control
var plugin: EditorPlugin
func _enter_tree() -> void:
if owner:
plugin = owner.plugin
func _notification(what: int) -> void:
if not plugin:
return
if what == NOTIFICATION_THEME_CHANGED:
%ValidationPanel.add_theme_stylebox_override(&\"panel\", get_theme_stylebox(&\"panel\", &\"Tree\"))
func edit_settings_pressed():
plugin.get_editor_interface().edit_resource(MetSys.settings)
func force_reload() -> void:
MetSys.map_data = MetSys.MapData.new()
MetSys.map_data.load_data()
for group in MetSys.map_data.cell_groups.values():
var i: int
while i < group.size():
if MetSys.map_data.get_cell_at(group[i]):
i += 1
else:
group.remove_at(i)
func edit_database_theme() -> void:
plugin.get_editor_interface().edit_resource(owner.theme)
func reset_theme() -> void:
%AllowReset.button_pressed = false
var th: Theme = owner.theme
th.set_color(&\"group_color\", &\"MetSys\", Color(Color.MEDIUM_PURPLE, 0.8))
th.set_color(&\"highlighted_room\", &\"MetSys\", Color(Color.GREEN, 0.25))
th.set_color(&\"assigned_scene\", &\"MetSys\", Color(Color.MEDIUM_SEA_GREEN, 0.8))
th.set_color(&\"border_highlight\", &\"MetSys\", Color(Color.GREEN, 0.5))
th.set_color(&\"cursor_color\", &\"MetSys\", Color.GREEN)
th.set_color(&\"cursor_color_erase\", &\"MetSys\", Color.RED)
th.set_color(&\"marked_collectible_room\", &\"MetSys\", Color(Color.BLUE_VIOLET, 0.75))
th.set_color(&\"foreign_marked_collectible_room\", &\"MetSys\", Color(Color.RED, 0.25))
th.set_color(&\"current_scene_room\", &\"MetSys\", Color(Color.RED, 0.5))
th.set_color(&\"room_not_assigned\", &\"MetSys\", Color.RED)
th.set_color(&\"room_assigned\", &\"MetSys\", Color.WHITE)
th.set_color(&\"active_custom_element\", &\"MetSys\", Color(0.1, 0.1, 1, 0.8))
th.set_color(&\"inactive_custom_element\", &\"MetSys\", Color(0, 0, 1, 0.4))
th.set_color(&\"custom_element_marker\", &\"MetSys\", Color.YELLOW)
ResourceSaver.save(th)
func toggle_allow_reset(button_pressed: bool) -> void:
%ResetButton.disabled = not button_pressed
func refresh_custom_elements() -> void:
var custom_script := MetSys.settings.custom_element_script
MetSys.settings.custom_element_script = null
MetSys.settings.custom_element_script = custom_script
MetSys.settings.custom_elements_changed.emit()
func export_json() -> void:
$FileDialog.popup_centered_ratio(0.6)
func json_file_selected(path: String) -> void:
var map_data: Dictionary
var cells: Dictionary
map_data[\"cells\"] = cells
for cell in MetSys.map_data.cells:
var cell_to_save: Dictionary
cells[\"%s,%s,%s\" % [cell.x, cell.y, cell.z]] = cell_to_save
var cell_data := MetSys.map_data.get_cell_at(cell)
cell_to_save[\"borders\"] = cell_data.borders
if cell_data.symbol > -1:
cell_to_save[\"symbol\"] = cell_data.symbol
if cell_data.color.a > 0:
cell_to_save[\"color\"] = cell_data.color.to_html(false)
if cell_data.border_colors.any(func(color: Color) -> bool: return color.a > 0):
cell_to_save[\"border_colors\"] = cell_data.border_colors.map(func(color: Color) -> String:
return \"000000\" if color.a == 0 else color.to_html(false))
if not cell_data.assigned_scene.is_empty():
cell_to_save[\"assigned_scene\"] = cell_data.assigned_scene
var groups: Dictionary
for group in MetSys.map_data.cell_groups:
var group_cells: Array[Vector3i]
group_cells.assign(MetSys.map_data.cell_groups[group])
if group_cells.is_empty():
continue
var group_to_save: Array
groups[group] = group_to_save
for cell in group_cells:
group_to_save.append(\"%s,%s,%s\" % [cell.x, cell.y, cell.z])
if not groups.is_empty():
map_data[\"groups\"] = groups
var elements: Dictionary
for element in MetSys.map_data.custom_elements:
var element_to_save: Dictionary
elements[\"%s,%s,%s\" % [element.x, element.y, element.z]] = element_to_save
var element_data: Dictionary = MetSys.map_data.custom_elements[element]
element_to_save[\"name\"] = element_data[\"name\"]
element_to_save[\"size\"] = \"%s,%s\" % [element_data[\"size\"].x, element_data[\"size\"].y]
if not element_data[\"data\"].is_empty():
element_to_save[\"data\"] = element_data[\"data\"]
if not elements.is_empty():
map_data[\"custom_elements\"] = elements
var file := FileAccess.open(path, FileAccess.WRITE)
file.store_string(JSON.stringify(map_data, \"\\t\"))
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6lvlf"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 5.0
bg_color = Color(0.1, 0.1, 0.1, 0.6)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="GDScript" id="GDScript_x20sf"]
resource_name = "ValidationPlanel"
script/source = "@tool
extends PanelContainer
enum { SUCCESS, WARNING, ERROR, INFO }
var plugin: EditorPlugin
var warning_color: Color
var error_color: Color
var success_color: Color
var has_error: bool
func _enter_tree() -> void:
if owner:
plugin = owner.plugin
func _ready() -> void:
if plugin:
hide()
func _notification(what: int) -> void:
if what == NOTIFICATION_THEME_CHANGED:
warning_color = get_theme_color(&\"warning_color\", &\"Editor\")
error_color = get_theme_color(&\"error_color\", &\"Editor\")
success_color = get_theme_color(&\"success_color\", &\"Editor\")
func validate_map_data() -> void:
dismiss()
has_error = false
var map_theme: MapTheme = MetSys.settings.theme
var unused_symbols: Array[int]
unused_symbols.assign(range(map_theme.symbols.size()))
unused_symbols.erase(map_theme.uncollected_item_symbol)
unused_symbols.erase(map_theme.collected_item_symbol)
for coords in MetSys.map_data.cells:
var cell_data: MetroidvaniaSystem.MapData.CellData = MetSys.map_data.cells[coords]
if not MetSys.map_data.get_assigned_scene_at(coords):
add_label(\"No assigned map at: %s\" % coords, WARNING)
var symbol := cell_data.get_symbol()
if symbol >= map_theme.symbols.size():
add_label(\"Invalid symbol (%d) at: %s\" % [symbol, coords], ERROR)
else:
unused_symbols.erase(symbol)
for i in 4:
var border := cell_data.get_border(i)
if map_theme.rectangle and border >= map_theme.vertical_borders.size() + 2:
add_label(\"Invalid border (%d) at: %s\" % [border, coords], ERROR)
elif not map_theme.rectangle and border >= map_theme.borders.size() + 2:
add_label(\"Invalid border (%d) at: %s\" % [border, coords], ERROR)
elif cell_data.get_border(i) != 0:
var next: Vector3i = coords + Vector3i(MetroidvaniaSystem.MapData.FWD[i].x, MetroidvaniaSystem.MapData.FWD[i].y, 0)
if not MetSys.map_data.get_cell_at(next):
add_label(\"Passage to nowhere at: %s\" % coords, WARNING)
for symbol in unused_symbols:
add_label(\"Potentially unused symbol: %d\" % symbol, WARNING)
if not has_error:
add_label(\"Map data is valid.\", SUCCESS)
func validate_map_theme() -> void:
dismiss()
has_error = false
var map_theme: MapTheme = MetSys.settings.theme
if map_theme.center_texture:
add_label(\"Cell Shape: %s\" % (\"Rectangle\" if map_theme.rectangle else \"Square\"), INFO)
add_label(\"Base Cell Size: %s\" % map_theme.center_texture.get_size(), INFO)
else:
add_label(\"Theme is missing center texture. Map can't be drawn.\", ERROR)
return
if map_theme.is_unicorner():
add_label(\"Uses unified corners.\", INFO)
if map_theme.empty_space_texture and map_theme.empty_space_texture.get_size() != map_theme.center_texture.get_size():
add_label(\"Size mismatch between empty texture (%s) and center texture.\" % map_theme.empty_space_texture.get_size(), ERROR)
if map_theme.rectangle:
if map_theme.vertical_borders.size() != map_theme.horizontal_borders.size():
add_label(\"Number of horizontal and vertical borders do not match.\", ERROR)
check_vertical_border_texture(map_theme.vertical_wall, \"Vertical wall texture\")
check_vertical_border_texture(map_theme.vertical_passage, \"Vertical passage texture\")
for i in map_theme.vertical_borders.size():
var texture: Texture2D = map_theme.vertical_borders[i]
check_vertical_border_texture(texture, \"Vertical border texture at index %d\" % i)
check_horizontal_border_texture(map_theme.horizontal_wall, \"Horizontal wall texture\")
check_horizontal_border_texture(map_theme.horizontal_passage, \"Horizontal passage texture\")
for i in map_theme.horizontal_borders.size():
var texture: Texture2D = map_theme.horizontal_borders[i]
check_horizontal_border_texture(texture, \"Horizontal border texture at index %d\" % i)
else:
check_vertical_border_texture(map_theme.wall, \"Wall texture\")
check_vertical_border_texture(map_theme.passage, \"Passage texture\")
for i in map_theme.borders.size():
var texture: Texture2D = map_theme.borders[i]
check_vertical_border_texture(texture, \"Border texture at index %d\" % i)
if map_theme.uncollected_item_symbol >= map_theme.symbols.size():
add_label(\"Uncollected item symbol index is greater than number of available symbols.\", ERROR)
if map_theme.collected_item_symbol >= map_theme.symbols.size():
add_label(\"Collected item symbol index is greater than number of available symbols.\", ERROR)
for i in map_theme.symbols.size():
check_symbol_texture(map_theme.symbols[i], \"Symbol %d texture\" % i)
if map_theme.use_shared_borders:
check_corner_texture(map_theme.u_corner, \"U corner texture\")
check_corner_texture(map_theme.l_corner, \"L corner texture\")
check_corner_texture(map_theme.t_corner, \"T corner texture\")
check_corner_texture(map_theme.cross_corner, \"Cross corner texture\")
else:
check_corner_texture(map_theme.inner_corner, \"Inner corner texture\")
check_corner_texture(map_theme.outer_corner, \"Outer corner texture\")
if map_theme.player_location_scene:
var test := map_theme.player_location_scene.instantiate()
test.queue_free()
if not test is Node2D:
add_label(\"Player location scene is not a Node2D.\", ERROR)
else:
add_label(\"Missing player location scene. Player location can't be displayed using built-in methods.\", WARNING)
if not has_error:
add_label(\"Theme is valid.\", SUCCESS)
func check_vertical_border_texture(texture: Texture2D, texture_name: String):
if texture:
var map_theme: MapTheme = MetSys.settings.theme
if texture.get_height() != map_theme.center_texture.get_height():
add_label(\"%s has invalid height (%d). It should be vertical, oriented towards the right side and match the height of the center texture.\" % [texture_name, texture.get_height()], ERROR)
elif texture.get_width() > map_theme.center_texture.get_width() / 2:
add_label(\"%s is wider than half of the center texture. It may cause overlaps.\" % texture_name, WARNING)
else:
add_label(\"%s is empty.\" % texture_name, ERROR)
func check_horizontal_border_texture(texture: Texture2D, texture_name: String):
if texture:
var map_theme: MapTheme = MetSys.settings.theme
if texture.get_height() != map_theme.center_texture.get_width():
add_label(\"%s has invalid height (%d). It should be vertical, oriented towards the right side and height should match the width of the center texture.\" % [texture_name, texture.get_height()], ERROR)
elif texture.get_width() > map_theme.center_texture.get_height() / 2:
add_label(\"%s is wider than half of the height of the center texture. It may cause overlaps.\" % texture_name, WARNING)
else:
add_label(\"%s is empty.\" % texture_name, ERROR)
func check_symbol_texture(texture: Texture2D, texture_name: String):
if texture:
var map_theme: MapTheme = MetSys.settings.theme
if texture.get_width() > map_theme.center_texture.get_width() or texture.get_height() > map_theme.center_texture.get_height():
add_label(\"%s is bigger than center texture. It will stick out of cells.\" % texture_name, WARNING)
else:
add_label(\"%s is empty.\" % texture_name, ERROR)
func check_corner_texture(texture: Texture2D, texture_name: String):
if texture:
var map_theme: MapTheme = MetSys.settings.theme
if texture.get_width() > map_theme.center_texture.get_width() / 2 or texture.get_height() > map_theme.center_texture.get_height() / 2:
add_label(\"%s is bigger than half of the center texture. It may cause overlaps.\" % texture_name, WARNING)
else:
add_label(\"%s is empty.\" % texture_name, ERROR)
func add_label(text: String, type: int):
show()
var label := Label.new()
label.autowrap_mode = TextServer.AUTOWRAP_WORD
label.text = \"• \" + text
match type:
SUCCESS:
label.modulate = success_color
WARNING:
label.modulate = warning_color
ERROR:
label.modulate = error_color
has_error = true
%Output.add_child(label)
func dismiss() -> void:
if not visible:
return
for node in %Output.get_children():
node.queue_free()
hide()
"
[node name="Manage" type="MarginContainer"]
anchors_preset = 13
anchor_left = 0.5
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -200.0
offset_right = 200.0
grow_horizontal = 2
grow_vertical = 2
size_flags_vertical = 3
theme_override_constants/margin_top = 32
script = SubResource("GDScript_kxd0b")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
custom_minimum_size = Vector2(400, 0)
layout_mode = 2
size_flags_horizontal = 4
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 4
[node name="Button" type="Button" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
text = "一般设置"
[node name="Button6" type="Button" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
text = "刷新默认元素"
[node name="Button2" type="Button" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
text = "重新加载并清理地图数据"
[node name="HSeparator3" type="HSeparator" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
[node name="Button4" type="Button" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
text = "校验地图数据"
[node name="Button5" type="Button" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
text = "校验地图主题"
[node name="Button7" type="Button" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
text = "导出地图数据为JSON"
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
[node name="Button3" type="Button" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
text = "编辑数据库主题"
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
[node name="AllowReset" type="CheckBox" parent="VBoxContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="ResetButton" type="Button" parent="VBoxContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
disabled = true
text = "重置数据库主题"
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
[node name="ValidationPanel" type="PanelContainer" parent="VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_6lvlf")
script = SubResource("GDScript_x20sf")
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/ValidationPanel"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/ValidationPanel/VBoxContainer"]
layout_mode = 2
text = "校验结果"
horizontal_alignment = 1
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/ValidationPanel/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="Output" type="VBoxContainer" parent="VBoxContainer/ValidationPanel/VBoxContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Button" type="Button" parent="VBoxContainer/ValidationPanel/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 4
text = "关闭"
[node name="FileDialog" type="FileDialog" parent="."]
size = Vector2i(483, 162)
access = 2
filters = PackedStringArray("*.json")
[connection signal="pressed" from="VBoxContainer/VBoxContainer/Button" to="." method="edit_settings_pressed"]
[connection signal="pressed" from="VBoxContainer/VBoxContainer/Button6" to="." method="refresh_custom_elements"]
[connection signal="pressed" from="VBoxContainer/VBoxContainer/Button2" to="." method="force_reload"]
[connection signal="pressed" from="VBoxContainer/VBoxContainer/Button4" to="VBoxContainer/ValidationPanel" method="validate_map_data"]
[connection signal="pressed" from="VBoxContainer/VBoxContainer/Button5" to="VBoxContainer/ValidationPanel" method="validate_map_theme"]
[connection signal="pressed" from="VBoxContainer/VBoxContainer/Button7" to="." method="export_json"]
[connection signal="pressed" from="VBoxContainer/VBoxContainer/Button3" to="." method="edit_database_theme"]
[connection signal="toggled" from="VBoxContainer/VBoxContainer/HBoxContainer/AllowReset" to="." method="toggle_allow_reset"]
[connection signal="pressed" from="VBoxContainer/VBoxContainer/HBoxContainer/ResetButton" to="." method="reset_theme"]
[connection signal="pressed" from="VBoxContainer/ValidationPanel/VBoxContainer/Button" to="VBoxContainer/ValidationPanel" method="dismiss"]
[connection signal="file_selected" from="FileDialog" to="." method="json_file_selected"]

@ -1,91 +0,0 @@
@tool
extends "res://addons/MetroidvaniaSystem/Scripts/MapView.gd"
@onready var ghost_map: Control = %GhostMap
@onready var grid: Control = %Grid
@export var mode_group: ButtonGroup
var mode: int
var preview_layer := -1
var modified: bool
func _ready() -> void:
if not plugin:
return
plugin.dirty_toggled.connect(update_name)
await super()
mode_group.pressed.connect(mode_pressed)
get_current_sub_editor()._editor_enter()
MetSys.settings.theme_changed.connect(grid.queue_redraw)
map_overlay.mouse_entered.connect(map_overlay.grab_focus)
func mode_pressed(button: BaseButton):
get_current_sub_editor()._editor_exit()
mode = button.get_index() - 1
get_current_sub_editor()._editor_enter()
map_overlay.queue_redraw()
func on_layer_changed(l: int):
super(l)
ghost_map.queue_redraw()
func preview_layer_changed(value: float) -> void:
preview_layer = value
ghost_map.queue_redraw()
func get_current_sub_editor() -> Control:
return mode_group.get_buttons()[mode]
func _on_overlay_input(event: InputEvent) -> void:
super(event)
get_current_sub_editor()._editor_input(event)
func _on_drag():
ghost_map.queue_redraw()
func _on_overlay_draw() -> void:
if not plugin:
return
super()
map_overlay.draw_set_transform(Vector2(map_offset) * MetSys.CELL_SIZE)
var sub := get_current_sub_editor()
sub.top_draw = Callable()
sub._editor_draw(map_overlay)
if sub.top_draw.is_valid():
sub.top_draw.call(map_overlay)
func _on_ghost_map_draw() -> void:
if not plugin:
return
if preview_layer == -1 or preview_layer == current_layer:
return
for x in range(-100, 100):
for y in range(-100, 100):
MetSys.draw_cell(ghost_map, Vector2i(x, y) + map_offset, Vector3i(x, y, preview_layer))
func _on_grid_draw() -> void:
if not plugin:
return
var empty_texture: Texture2D = MetSys.settings.theme.empty_space_texture
for x in ceili(grid.size.x / MetSys.CELL_SIZE.x):
for y in ceili(grid.size.y / MetSys.CELL_SIZE.y):
if empty_texture:
grid.draw_texture(empty_texture, Vector2(x, y) * MetSys.CELL_SIZE)
else:
grid.draw_rect(Rect2(Vector2(x, y) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), Color(Color.WHITE, 0.1), false)
func update_name(dirty: bool):
if dirty:
name = "Map Editor (*)"
else:
name = "Map Editor"

@ -1,868 +0,0 @@
[gd_scene load_steps=13 format=3 uid="uid://dfbykrvyqnqr5"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Database/MapEditor.gd" id="1_i7yda"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Database/MapOverlay.gd" id="2_w1wnt"]
[sub_resource type="ButtonGroup" id="ButtonGroup_pv7fp"]
[sub_resource type="GDScript" id="GDScript_dsps8"]
resource_name = "RoomLayout"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/SubEditor.gd\"
var erase_mode: bool
func _editor_init() -> void:
room_only_cursor = false
func _update_theme():
theme_cache.cursor_color = get_theme_color(&\"cursor_color\", &\"MetSys\")
func _editor_input(event: InputEvent):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
drag_from = get_cursor_pos()
else:
var rect := get_rect_between(drag_from, get_cursor_pos())
update_rooms(rect)
drag_from = EDITOR_SCRIPT.NULL_VECTOR2I
elif event.button_index == MOUSE_BUTTON_RIGHT:
if event.pressed:
drag_from = get_cursor_pos()
erase_mode = true
theme_cache.cursor_color = get_theme_color(&\"cursor_color_erase\", &\"MetSys\")
queue_redraw()
else:
var rect := get_rect_between(drag_from, get_cursor_pos())
erase_rooms(rect)
erase_mode = false
theme_cache.cursor_color = get_theme_color(&\"cursor_color\", &\"MetSys\")
drag_from = EDITOR_SCRIPT.NULL_VECTOR2I
func update_rooms(rect: Rect2i):
var map_data: MetroidvaniaSystem.MapData = MetSys.map_data
for x in range(rect.position.x, rect.end.x):
for y in range(rect.position.y, rect.end.y):
var coords := Vector3i(x, y, editor.current_layer)
var cell = map_data.get_cell_at(coords)
if cell:
remove_assign(coords)
if x != rect.end.x - 1:
cell.borders[0] = -1
if y != rect.end.y - 1:
cell.borders[1] = -1
if x != rect.position.x:
cell.borders[2] = -1
if y != rect.position.y:
cell.borders[3] = -1
else:
cell = map_data.create_cell_at(coords)
if x == rect.end.x - 1:
cell.borders[0] = 0
if y == rect.end.y - 1:
cell.borders[1] = 0
if x == rect.position.x:
cell.borders[2] = 0
if y == rect.position.y:
cell.borders[3] = 0
mark_modified()
editor.map.queue_redraw()
func erase_rooms(rect: Rect2i):
var map_data: MetroidvaniaSystem.MapData = MetSys.map_data
for x in range(rect.position.x, rect.end.x):
for y in range(rect.position.y, rect.end.y):
var coords := Vector3i(x, y, editor.current_layer)
var cell_data := map_data.get_cell_at(coords)
if not cell_data:
continue
if x == rect.end.x - 1:
close_border(Vector2i(x + 1, y), 2)
if y == rect.end.y - 1:
close_border(Vector2i(x, y + 1), 3)
if x == rect.position.x:
close_border(Vector2i(x - 1, y), 0)
if y == rect.position.y:
close_border(Vector2i(x, y - 1), 1)
map_data.erase_cell(coords)
mark_modified()
editor.map.queue_redraw()
func remove_assign(coords: Vector3i):
var assigned_scene: String = MetSys.map_data.cells[coords].assigned_scene
MetSys.map_data.assigned_scenes.erase(assigned_scene)
func close_border(pos: Vector2i, border: int):
var cell: MetroidvaniaSystem.MapData.CellData = MetSys.map_data.get_cell_at(get_coords(pos))
if cell:
cell.borders[border] = 0
"
[sub_resource type="GDScript" id="GDScript_tlx5u"]
resource_name = "CellColor"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/CellPaintEditor.gd\"
func _editor_init() -> void:
super()
can_pick = true
func _editor_enter():
super()
%Colors.show()
func _editor_exit():
super()
%Colors.hide()
func modify_cell(cell_data: MetroidvaniaSystem.MapData.CellData, mode: int) -> bool:
if mode == MODE_PICK:
if cell_data.color.a > 0:
%CurrentColor.color = cell_data.color
else:
var target_color := Color.TRANSPARENT
if mode == MODE_DRAW:
target_color = %CurrentColor.color
if cell_data.color != target_color:
cell_data.color = target_color
return true
return false
"
[sub_resource type="GDScript" id="GDScript_ujwfx"]
resource_name = "CellSymbol"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/CellPaintEditor.gd\"
var symbol_group: ButtonGroup
func _editor_init() -> void:
can_pick = true
super()
symbol_group = ButtonGroup.new()
reload_symbols()
MetSys.settings.theme_changed.connect(reload_symbols)
MetSys.theme_modified.connect(func(changes: Array[String]):
if \"symbols\" in changes:
reload_symbols())
func reload_symbols():
for symbol in %SymbolContainer.get_children():
symbol.free()
for symbol in MetSys.settings.theme.symbols:
add_symbol(symbol)
func _editor_enter():
super()
%Symbols.show()
func _editor_exit():
super()
%Symbols.hide()
func modify_cell(cell_data: MetroidvaniaSystem.MapData.CellData, mode: int) -> bool:
if mode == MODE_PICK:
if cell_data.symbol > -1:
symbol_group.get_buttons()[cell_data.symbol].button_pressed = true
return false
var target_symbol := -1
if mode == MODE_DRAW:
target_symbol = symbol_group.get_pressed_button().get_index()
if cell_data.symbol != target_symbol:
cell_data.symbol = target_symbol
return true
return false
func add_symbol(texture: Texture2D):
var button := Button.new()
button.icon = texture
button.toggle_mode = true
button.button_group = symbol_group
if not symbol_group.get_pressed_button():
button.button_pressed = true
%SymbolContainer.add_child(button)
"
[sub_resource type="GDScript" id="GDScript_a80ln"]
resource_name = "CellGroup"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/CellPaintEditor.gd\"
var cell_groups: Dictionary
var drawing: int
func _editor_init() -> void:
super()
room_only_cursor = true
cell_groups = MetSys.map_data.cell_groups
func _update_theme():
theme_cache.group_color = get_theme_color(&\"group_color\", &\"MetSys\")
func _editor_enter():
super()
%Groups.show()
func _editor_exit():
super()
%Groups.hide()
func _editor_draw(map_overlay: CanvasItem):
super(map_overlay)
for p in cell_groups.get(%CurrentGroup.value as int, []):
map_overlay.draw_rect(Rect2(Vector2(p.x, p.y) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.group_color)
func modify_coords(coords: Vector3i, mode: int) -> bool:
var current_group: int = %CurrentGroup.value
if mode == MODE_DRAW:
if not current_group in cell_groups:
cell_groups[current_group] = []
if not coords in cell_groups[current_group]:
cell_groups[current_group].append(coords)
return true
else:
if coords in cell_groups[current_group]:
cell_groups[current_group].erase(coords)
return true
return false
func draw_group():
if drawing == 0:
return
var coords := get_coords(get_cursor_pos())
var current_group: int = %CurrentGroup.value
if drawing & MOUSE_BUTTON_MASK_LEFT != 0:
var cell_data: MetroidvaniaSystem.MapData.CellData = MetSys.map_data.get_cell_at(coords)
if not cell_data:
return
if not current_group in cell_groups:
cell_groups[current_group] = []
if not coords in cell_groups[current_group]:
cell_groups[current_group].append(coords)
mark_modified()
editor.map_overlay.queue_redraw()
elif drawing & MOUSE_BUTTON_MASK_RIGHT != 0:
if not current_group in cell_groups:
return
var cell_data: MetroidvaniaSystem.MapData.CellData = MetSys.map_data.get_cell_at(coords)
if not cell_data:
return
cell_groups[current_group].erase(coords)
mark_modified()
editor.map_overlay.queue_redraw()
"
[sub_resource type="GDScript" id="GDScript_ilxbw"]
resource_name = "BorderType"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/BorderPaintEditor.gd\"
var border_group: ButtonGroup
func _editor_init() -> void:
use_cursor = false
can_pick = true
super()
border_group = ButtonGroup.new()
reload_borders()
MetSys.settings.theme_changed.connect(reload_borders)
MetSys.theme_modified.connect(func(changes: Array[String]):
if \"borders\" in changes or \"vertical_borders\" or \"vertical_borders\" in changes:
reload_borders())
func reload_borders():
for symbol in %BorderContainer.get_children():
symbol.free()
if MetSys.settings.theme.rectangle:
add_border(MetSys.settings.theme.vertical_wall)
add_border(MetSys.settings.theme.vertical_passage)
for border in MetSys.settings.theme.vertical_borders:
add_border(border)
else:
add_border(MetSys.settings.theme.wall)
add_border(MetSys.settings.theme.passage)
for border in MetSys.settings.theme.borders:
add_border(border)
func _editor_enter():
super()
%Borders.show()
func _editor_exit():
super()
%Borders.hide()
func modify_border(cell_data: MetroidvaniaSystem.MapData.CellData, border: int, mode: int) -> bool:
if cell_data.borders[border] == -1:
return false
if mode == MODE_PICK:
border_group.get_buttons()[cell_data.borders[border]].button_pressed = true
else:
var target_border := 0
if mode == MODE_DRAW:
target_border = border_group.get_pressed_button().get_index()
if cell_data.borders[border] != target_border:
cell_data.borders[border] = target_border
return true
return false
func add_border(texture: Texture2D):
var button := Button.new()
button.icon = texture
button.toggle_mode = true
button.button_group = border_group
button.custom_minimum_size.x = MetSys.CELL_SIZE.x
button.icon_alignment = HORIZONTAL_ALIGNMENT_RIGHT
if not border_group.get_pressed_button():
button.button_pressed = true
%BorderContainer.add_child(button)
"
[sub_resource type="GDScript" id="GDScript_a3fsx"]
resource_name = "BorderColor"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/BorderPaintEditor.gd\"
func _editor_init():
can_pick = true
super()
func _editor_enter():
super()
%Colors.show()
func _editor_exit():
super()
%Colors.hide()
func modify_border(cell_data: MetroidvaniaSystem.MapData.CellData, border: int, mode: int) -> bool:
if cell_data.borders[border] == -1:
return false
if mode == MODE_PICK:
if cell_data.border_colors[border].a > 0:
%CurrentColor.color = cell_data.border_colors[border]
else:
var target_color := Color.TRANSPARENT
if mode == MODE_DRAW:
target_color = %CurrentColor.color
if cell_data.border_colors[border] != target_color:
cell_data.border_colors[border] = target_color
return true
return false
"
[sub_resource type="GDScript" id="GDScript_p75ne"]
resource_name = "SceneAssign"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/SubEditor.gd\"
func _editor_init() -> void:
use_cursor = false
func _update_theme():
theme_cache.assigned_scene = get_theme_color(&\"assigned_scene\", &\"MetSys\")
func _editor_input(event: InputEvent):
if event is InputEventMouseMotion:
var hr := highlighted_room
highlighted_room = MetSys.map_data.get_whole_room(get_coords(get_cursor_pos()))
if highlighted_room != hr:
editor.map_overlay.queue_redraw()
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
if not highlighted_room.is_empty():
if %FileDialog.root_subfolder.is_empty():
%FileDialog.root_subfolder = MetSys.settings.map_root_folder.trim_prefix(\"res://\")
%FileDialog.popup_centered_ratio(0.6)
elif event.button_index == MOUSE_BUTTON_RIGHT:
var first := true
for p in highlighted_room:
if first:
var assigned_scene: String = MetSys.map_data.get_cell_at(p).assigned_scene
MetSys.map_data.assigned_scenes.erase(assigned_scene)
first = false
MetSys.map_data.get_cell_at(p).assigned_scene = \"\"
MetSys.room_assign_updated.emit()
mark_modified()
editor.map_overlay.queue_redraw()
func _editor_draw(map_overlay: CanvasItem):
super(map_overlay)
for coords in MetSys.map_data.assigned_scenes.values():
if coords[0].z != editor.current_layer:
continue
for p in coords:
map_overlay.draw_rect(Rect2(Vector2(p.x, p.y) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.assigned_scene)
if not highlighted_room.is_empty():
map_overlay.draw_set_transform_matrix(Transform2D())
map_overlay.draw_string(get_theme_font(&\"font\", &\"Label\"), Vector2(0, 40),
MetSys.map_data.get_cell_at(highlighted_room.front()).assigned_scene)
func on_map_selected(path: String) -> void:
path = path.trim_prefix(MetSys.settings.map_root_folder)
MetSys.map_data.assigned_scenes[path] = []
for coords in highlighted_room:
MetSys.map_data.get_cell_at(coords).assigned_scene = path
MetSys.map_data.assigned_scenes[path].append(coords)
MetSys.room_assign_updated.emit()
mark_modified()
editor.map_overlay.queue_redraw()
"
[sub_resource type="GDScript" id="GDScript_gb3rf"]
resource_name = "CustomElements"
script/source = "@tool
extends \"res://addons/MetroidvaniaSystem/Database/SubEditor.gd\"
var current_element: String
var custom_elements: Dictionary
func _editor_init() -> void:
room_only_cursor = false
custom_elements = MetSys.map_data.custom_elements
MetSys.settings.custom_elements_changed.connect(reload_custom_elements)
reload_custom_elements()
func _update_theme():
theme_cache.active_custom_element = get_theme_color(&\"active_custom_element\", &\"MetSys\")
theme_cache.inactive_custom_element = get_theme_color(&\"inactive_custom_element\", &\"MetSys\")
theme_cache.custom_element_marker = get_theme_color(&\"custom_element_marker\", &\"MetSys\")
func reload_custom_elements():
for element in %CustomElementContainer.get_children():
element.queue_free()
current_element = \"\"
if not MetSys.settings.custom_elements or MetSys.settings.custom_elements.custom_elements.is_empty():
%NoElements.show()
return
else:
%NoElements.hide()
var element_group := ButtonGroup.new()
for element in MetSys.settings.custom_elements.custom_elements:
var button := CheckBox.new()
button.text = str(element).capitalize()
button.button_group = element_group
button.pressed.connect(set_current_element.bind(element))
%CustomElementContainer.add_child(button)
if not element_group.get_pressed_button():
button.button_pressed = true
set_current_element(element)
func _editor_enter():
%CustomElements.show()
func _editor_exit():
%CustomElements.hide()
func set_current_element(element: String):
current_element = element
editor.map_overlay.queue_redraw()
func _editor_draw(map_overlay: CanvasItem):
super(map_overlay)
for coords in custom_elements:
if coords.z != editor.current_layer:
continue
var element_color: Color
var element: Dictionary = custom_elements[coords]
if element.name == current_element:
element_color = theme_cache.active_custom_element
else:
element_color = theme_cache.inactive_custom_element
map_overlay.draw_rect(Rect2(Vector2(coords.x, coords.y) * MetSys.CELL_SIZE, Vector2(element.size) * MetSys.CELL_SIZE), element_color)
var square := minf(MetSys.CELL_SIZE.x, MetSys.CELL_SIZE.y)
map_overlay.draw_rect(Rect2((Vector2(coords.x, coords.y) + Vector2(0.5, 0.5)) * MetSys.CELL_SIZE - Vector2.ONE * square * 0.5, Vector2.ONE * square).grow(-square * 0.2), theme_cache.custom_element_marker)
func _editor_input(event: InputEvent):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
drag_from = get_cursor_pos()
else:
var rect := get_rect_between(drag_from, get_cursor_pos())
add_element(rect)
mark_modified()
drag_from = EDITOR_SCRIPT.NULL_VECTOR2I
elif event.button_index == MOUSE_BUTTON_RIGHT:
if event.pressed:
var coords := Vector3i(get_cursor_pos().x, get_cursor_pos().y, editor.current_layer)
if coords in MetSys.map_data.custom_elements:
MetSys.map_data.custom_elements.erase(coords)
mark_modified()
editor.map.queue_redraw()
func add_element(rect: Rect2i):
var element: Dictionary
element.name = current_element
element.size = rect.size
element.data = %CustomData.text
var coords := Vector3i(rect.position.x, rect.position.y, editor.current_layer)
MetSys.map_data.custom_elements[coords] = element
editor.map.queue_redraw()
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xiyo8"]
content_margin_left = 4.0
content_margin_right = 4.0
bg_color = Color(0, 0, 0, 0.501961)
[node name="MapEditor" type="HBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_i7yda")
mode_group = SubResource("ButtonGroup_pv7fp")
metadata/_edit_lock_ = true
[node name="PanelContainer" type="PanelContainer" parent="."]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer"]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Layer" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
metadata/_edit_lock_ = true
[node name="Label" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/Layer"]
layout_mode = 2
text = "当前层"
horizontal_alignment = 1
[node name="CurrentLayer" type="SpinBox" parent="PanelContainer/ScrollContainer/VBoxContainer/Layer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
[node name="Label2" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/Layer"]
layout_mode = 2
text = "预览层"
horizontal_alignment = 1
[node name="SpinBox2" type="SpinBox" parent="PanelContainer/ScrollContainer/VBoxContainer/Layer"]
layout_mode = 2
size_flags_horizontal = 4
min_value = -1.0
value = -1.0
[node name="RecenterButton" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Layer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
text = "重置相机"
[node name="HSeparator" type="HSeparator" parent="PanelContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
[node name="Modes" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="Label" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
text = "模式"
horizontal_alignment = 1
[node name="RoomLayout" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_pressed = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "房间布局"
script = SubResource("GDScript_dsps8")
[node name="CellColor" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "地块颜色"
script = SubResource("GDScript_tlx5u")
[node name="CellSymbol" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "地块符号"
script = SubResource("GDScript_ujwfx")
[node name="CellGroup" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "地块组"
script = SubResource("GDScript_a80ln")
[node name="BorderType" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "边缘类型"
script = SubResource("GDScript_ilxbw")
[node name="BorderColor" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "边缘颜色"
script = SubResource("GDScript_a3fsx")
[node name="SceneAssign" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "场景分配"
script = SubResource("GDScript_p75ne")
[node name="CustomElements" type="Button" parent="PanelContainer/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_pv7fp")
text = "自定义元素"
script = SubResource("GDScript_gb3rf")
[node name="HSeparator2" type="HSeparator" parent="PanelContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
[node name="EditControls" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 0
mouse_filter = 2
alignment = 2
metadata/_edit_lock_ = true
[node name="Symbols" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="SymbolContainer" type="HFlowContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Symbols"]
unique_name_in_owner = true
layout_mode = 2
[node name="Borders" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="BorderContainer" type="HFlowContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Borders"]
unique_name_in_owner = true
layout_mode = 2
[node name="Groups" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Groups"]
layout_mode = 2
text = "Group ID"
horizontal_alignment = 1
[node name="CurrentGroup" type="SpinBox" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Groups"]
unique_name_in_owner = true
layout_mode = 2
[node name="Colors" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="CurrentColor" type="ColorPickerButton" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Colors"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 30)
layout_mode = 2
edit_alpha = false
[node name="CustomElements" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/CustomElements"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/CustomElements/HBoxContainer"]
layout_mode = 2
text = "Data"
[node name="CustomData" type="LineEdit" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/CustomElements/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="NoElements" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/CustomElements"]
unique_name_in_owner = true
layout_mode = 2
text = "No Custom Elements Registered"
autowrap_mode = 2
[node name="CustomElementContainer" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/CustomElements"]
unique_name_in_owner = true
layout_mode = 2
[node name="Shortcuts" type="VBoxContainer" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
layout_mode = 2
[node name="ShortcutPick" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Shortcuts"]
unique_name_in_owner = true
layout_mode = 2
text = "Ctrl+Click: 从地图拾取"
autowrap_mode = 2
[node name="Label2" type="Label" parent="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Shortcuts"]
layout_mode = 2
text = "Shift+Click: 为整个房间设置"
autowrap_mode = 2
[node name="MapOverlay" type="Control" parent="."]
texture_filter = 1
clip_contents = true
layout_mode = 2
size_flags_horizontal = 3
focus_mode = 2
script = ExtResource("2_w1wnt")
[node name="ColorRect" type="ColorRect" parent="MapOverlay"]
modulate = Color(0, 0, 0, 0.12549)
show_behind_parent = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="Grid" type="Control" parent="MapOverlay"]
unique_name_in_owner = true
show_behind_parent = true
clip_contents = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
mouse_filter = 2
[node name="Map" type="Control" parent="MapOverlay"]
unique_name_in_owner = true
show_behind_parent = true
clip_contents = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
mouse_filter = 2
[node name="GhostMap" type="Control" parent="MapOverlay"]
unique_name_in_owner = true
modulate = Color(1, 1, 1, 0.12549)
show_behind_parent = true
clip_contents = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
mouse_filter = 2
[node name="StatusLabel" type="Label" parent="MapOverlay"]
unique_name_in_owner = true
layout_mode = 0
offset_right = 1.0
offset_bottom = 23.0
theme_override_styles/normal = SubResource("StyleBoxFlat_xiyo8")
vertical_alignment = 1
[node name="FileDialog" type="FileDialog" parent="."]
unique_name_in_owner = true
title = "Open a File"
size = Vector2i(345, 162)
ok_button_text = "打开"
file_mode = 0
filters = PackedStringArray("*.tscn")
[connection signal="value_changed" from="PanelContainer/ScrollContainer/VBoxContainer/Layer/SpinBox2" to="." method="preview_layer_changed"]
[connection signal="value_changed" from="PanelContainer/ScrollContainer/VBoxContainer/EditControls/Groups/CurrentGroup" to="MapOverlay" method="queue_redraw" unbinds=1]
[connection signal="draw" from="MapOverlay/Grid" to="." method="_on_grid_draw"]
[connection signal="draw" from="MapOverlay/GhostMap" to="." method="_on_ghost_map_draw"]
[connection signal="file_selected" from="FileDialog" to="PanelContainer/ScrollContainer/VBoxContainer/Modes/SceneAssign" method="on_map_selected"]

@ -1,12 +0,0 @@
@tool
extends Control
var cursor_inside: bool
func _notification(what: int) -> void:
if what == NOTIFICATION_MOUSE_ENTER:
cursor_inside = true
queue_redraw()
elif what == NOTIFICATION_MOUSE_EXIT:
cursor_inside = false
queue_redraw()

@ -1,101 +0,0 @@
@tool
extends "res://addons/MetroidvaniaSystem/Scripts/MapView.gd"
enum {MODE_LAYOUT = 1, MODE_ROOM_SYMBOL, MODE_ROOM_COLOR, MODE_ROOM_GROUP, MODE_BORDER_TYPE, MODE_BORDER_COLOR, MODE_MAP}
@export var mode_group: ButtonGroup
var theme_cache: Dictionary
var room_under_cursor: MetroidvaniaSystem.MapData.CellData
var current_hovered_item: Control
var extra_draw: Callable
func _ready() -> void:
if not plugin:
return
super()
plugin.scene_changed.connect(map_overlay.queue_redraw.unbind(1))
MetSys.map_updated.connect(map.queue_redraw)
func _notification(what: int) -> void:
if what == NOTIFICATION_THEME_CHANGED:
theme_cache.marked_collectible_room = get_theme_color(&"marked_collectible_room", &"MetSys")
theme_cache.foreign_marked_collectible_room = get_theme_color(&"foreign_marked_collectible_room", &"MetSys")
theme_cache.current_scene_room = get_theme_color(&"current_scene_room", &"MetSys")
theme_cache.cursor_color = get_theme_color(&"cursor_color", &"MetSys")
theme_cache.room_not_assigned = get_theme_color(&"room_not_assigned", &"MetSys")
theme_cache.room_assigned = get_theme_color(&"room_assigned", &"MetSys")
func _on_item_hover(item: Control):
item.mouse_exited.connect(_on_item_unhover.bind(item))
current_hovered_item = item
map_overlay.queue_redraw()
func _on_item_unhover(item: Control):
item.mouse_exited.disconnect(_on_item_unhover)
if item == current_hovered_item:
current_hovered_item = null
map_overlay.queue_redraw()
func _update_status_label():
status_label.show()
status_label.modulate = Color.WHITE
if room_under_cursor and not room_under_cursor.assigned_scene.is_empty():
status_label.text = str(get_cursor_pos(), " ", room_under_cursor.assigned_scene)
else:
if room_under_cursor:
status_label.modulate = Color.RED
status_label.text = str(get_cursor_pos())
func _on_overlay_input(event: InputEvent) -> void:
super(event)
if event is InputEventMouseMotion:
var cursor := get_cursor_pos()
room_under_cursor = MetSys.map_data.get_cell_at(Vector3i(cursor.x, cursor.y, current_layer))
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed and room_under_cursor and not room_under_cursor.assigned_scene.is_empty():
plugin.get_editor_interface().open_scene_from_path(MetSys.settings.map_root_folder.path_join(room_under_cursor.assigned_scene))
func _on_overlay_draw() -> void:
if not plugin:
return
super()
var mouse := get_cursor_pos()
if map_overlay.cursor_inside:
map_overlay.draw_rect(Rect2(Vector2(mouse + map_offset) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.cursor_color, false, 2)
if get_tree().edited_scene_root and get_tree().edited_scene_root.scene_file_path.begins_with(MetSys.settings.map_root_folder):
var current_scene := get_tree().edited_scene_root.scene_file_path.trim_prefix(MetSys.settings.map_root_folder)
for coords in MetSys.map_data.get_cells_assigned_to(current_scene):
if coords.z != current_layer:
break
map_overlay.draw_rect(Rect2(Vector2(coords.x + map_offset.x, coords.y + map_offset.y) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.current_scene_room)
if current_hovered_item:
var data: Dictionary = current_hovered_item.get_meta(&"data")
if "coords" in data:
var coords: Vector3i = data.coords
map_overlay.draw_rect(Rect2(Vector2(coords.x + map_offset.x, coords.y + map_offset.y) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.marked_collectible_room if coords.z == current_layer else theme_cache.foreign_marked_collectible_room)
else:
for coords in MetSys.map_data.get_cells_assigned_to(data.map):
if coords.z != current_layer:
break
map_overlay.draw_rect(Rect2(Vector2(coords.x + map_offset.x, coords.y + map_offset.y) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.marked_collectible_room)
break
if extra_draw.is_valid():
extra_draw.call(map_overlay)
func toggle_mapped(toggled_on: bool) -> void:
force_mapped = toggled_on
map.queue_redraw()

@ -1,417 +0,0 @@
[gd_scene load_steps=8 format=3 uid="uid://df5fuj4vjr11p"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Database/MapViewer.gd" id="1_s16ok"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Database/MapOverlay.gd" id="2_pgyha"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/EditorIconButton.gd" id="2_tjngl"]
[sub_resource type="ButtonGroup" id="ButtonGroup_lmh17"]
[sub_resource type="GDScript" id="GDScript_fjyy3"]
resource_name = "Settings"
script/source = "@tool
extends Button
var plugin: EditorPlugin
func _enter_tree() -> void:
if owner:
plugin = owner.plugin
func _ready() -> void:
if not plugin:
return
_pressed()
await get_tree().process_frame
for data in MetSys.settings.collectible_list:
var collectible := add_collectible()
collectible.set_data(data)
func _pressed() -> void:
for button in button_group.get_buttons():
button.exit()
%Settings.show()
func exit():
%Settings.hide()
func add_collectible() -> Control:
var collectible := preload(\"res://addons/MetroidvaniaSystem/Nodes/CollectibleElement.tscn\").instantiate()
%CollectibleList.add_child(collectible)
collectible.save_request.connect(save_collectible_list)
return collectible
func save_collectible_list():
var list: Array[Dictionary]
for collectible in %CollectibleList.get_children():
if not collectible.is_queued_for_deletion():
list.append(collectible.get_data())
MetSys.settings.collectible_list = list
ResourceSaver.save(MetSys.settings)
"
[sub_resource type="GDScript" id="GDScript_dicqo"]
resource_name = "Finder"
script/source = "@tool
extends Button
@onready var scan_progress: ProgressBar = %ScanProgress
@onready var show_on_map: CheckButton = %ShowOnMap
@onready var summary: VBoxContainer = %Summary
var thread: Thread
var found_elements: Array[Dictionary]
func _ready() -> void:
scan_progress.hide()
show_on_map.hide()
exit()
func _pressed() -> void:
for button in button_group.get_buttons():
button.exit()
%Finder.show()
func exit():
%Finder.hide()
func start_scan() -> void:
summary.hide()
%ScanButton.disabled = true
var collectible_list: Array[Dictionary]
for item in %CollectibleList.get_children():
collectible_list.append(item.get_data2())
found_elements.clear()
for item in summary.get_children():
item.free()
thread = Thread.new()
thread.start(scan_maps.bind(collectible_list))
set_process(true)
func _process(delta: float) -> void:
if not thread:
set_process(false)
return
if thread.is_alive():
return
thread.wait_to_finish()
thread = null
scan_progress.hide()
show_on_map.show()
summary.show()
%ScanButton.disabled = false
for item in %CollectibleList.get_children():
var data: Dictionary = item.get_data2()
var count := 0
var count_label := setup_header(data)
for found in found_elements:
if found.element != data.element:
continue
count += 1
setup_found(found)
count_label.text = str(count)
summary.add_child(HSeparator.new())
func scan_maps(element_list: Array[Dictionary]):
Thread.set_thread_safety_checks_enabled(false)
var maps: Array[String]
var folders: Array[String]
folders.append(MetSys.settings.map_root_folder)
while not folders.is_empty():
var folder := folders.pop_back()
folders.append_array(Array(DirAccess.get_directories_at(folder)).map(func(subfolder: String) -> String: return folder.path_join(subfolder)))
maps.append_array(Array(DirAccess.get_files_at(folder)).map(func(file: String) -> String: return folder.path_join(file)))
scan_progress.max_value = maps.size()
scan_progress.value = 0
scan_progress.show()
show_on_map.hide()
for map in maps:
var lines := FileAccess.open(map, FileAccess.READ).get_as_text().split(\"\\n\")
var current_element: Dictionary
for line in lines:
if not current_element.is_empty():
if line.begins_with(\"[\"):
found_elements.append(current_element)
current_element = {}
elif line.begins_with(\"position =\"):
current_element.position = str_to_var(line.get_slice(\"=\", 1))
else:
continue
for element in element_list:
if line.begins_with(\"[node name=\\\"%s\" % element.element):
current_element = element.duplicate()
current_element.map = map
break
if not current_element.is_empty():
found_elements.append(current_element)
scan_progress.value += 1
func complete_element(element: Dictionary):
var found := preload(\"res://addons/MetroidvaniaSystem/Nodes/CollectibleFoundItem.tscn\").instantiate()
found.set_element(element)
found.hovered.connect(owner._on_item_hover.bind(found))
summary.add_child.call_deferred(found)
func setup_header(data: Dictionary) -> Label:
var hbox := HBoxContainer.new()
summary.add_child(hbox)
var tex := TextureRect.new()
hbox.add_child(tex)
tex.texture = data.get(\"icon\")
var label := Label.new()
hbox.add_child(label)
label.text = data.element
label = Label.new()
hbox.add_child(label)
return label
func setup_found(data: Dictionary):
var label := Label.new()
summary.add_child(label)
label.mouse_filter = Control.MOUSE_FILTER_PASS
label.set_meta(&\"data\", data)
data.map = data.map.trim_prefix(MetSys.settings.map_root_folder)
var room := MetSys.map_data.get_cells_assigned_to(data.map)
if \"position\" in data and not room.is_empty():
var top_left := Vector2i.MAX
for coords in room:
top_left.x = mini(coords.x, top_left.x)
top_left.y = mini(coords.y, top_left.y)
var pos := top_left + Vector2i(data.position / MetSys.settings.in_game_cell_size)
data.coords = Vector3i(pos.x, pos.y, room[0].z)
label.text = \"%s %s\" % [data.map, data.coords]
else:
label.text = \"%s\" % data.map
label.mouse_entered.connect(owner._on_item_hover.bind(label))
func toggle_display_on_map(toggled_on: bool) -> void:
if toggled_on:
owner.extra_draw = draw_collectibles_on_map
else:
owner.extra_draw = Callable()
owner.map_overlay.queue_redraw()
func draw_collectibles_on_map(canvas_item: CanvasItem):
for element in found_elements:
var icon: Texture2D = element.icon
var target_size := icon.get_size() * (minf(MetSys.CELL_SIZE.x, MetSys.CELL_SIZE.y) / minf(icon.get_width(), icon.get_height()) * 0.9)
if \"coords\" in element:
var coords: Vector3i = element.coords
if coords.z != owner.current_layer:
continue
var pos := Vector2(coords.x + owner.map_offset.x, coords.y + owner.map_offset.y) * MetSys.CELL_SIZE
canvas_item.draw_texture_rect(icon, Rect2(pos + MetSys.CELL_SIZE * 0.5 - target_size * 0.5, target_size), false)
else:
for coords in MetSys.map_data.get_cells_assigned_to(element.map):
if coords.z != owner.current_layer:
break
var pos := Vector2(coords.x + owner.map_offset.x, coords.y + owner.map_offset.y) * MetSys.CELL_SIZE
canvas_item.draw_texture_rect(icon, Rect2(pos + MetSys.CELL_SIZE * 0.5 - target_size * 0.5, target_size), false)
break
"
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qbvjg"]
content_margin_left = 4.0
content_margin_right = 4.0
bg_color = Color(0, 0, 0, 0.501961)
[node name="MapViewer" type="HBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_s16ok")
metadata/_edit_lock_ = true
[node name="Panel" type="PanelContainer" parent="."]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
size_flags_horizontal = 0
[node name="ScrollContainer" type="ScrollContainer" parent="Panel"]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Layer" type="VBoxContainer" parent="Panel/ScrollContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
[node name="Label" type="Label" parent="Panel/ScrollContainer/VBoxContainer/Layer"]
layout_mode = 2
text = "当前层"
horizontal_alignment = 1
[node name="CurrentLayer" type="SpinBox" parent="Panel/ScrollContainer/VBoxContainer/Layer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
[node name="RecenterButton" type="Button" parent="Panel/ScrollContainer/VBoxContainer/Layer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
text = "重置视角"
[node name="CheckButton" type="CheckButton" parent="Panel/ScrollContainer/VBoxContainer/Layer"]
layout_mode = 2
size_flags_horizontal = 4
text = "预览映射"
[node name="HSeparator" type="HSeparator" parent="Panel/ScrollContainer/VBoxContainer"]
layout_mode = 2
[node name="Modes" type="VBoxContainer" parent="Panel/ScrollContainer/VBoxContainer"]
layout_mode = 2
[node name="Settings" type="Button" parent="Panel/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_pressed = true
button_group = SubResource("ButtonGroup_lmh17")
text = "收集品设置"
script = SubResource("GDScript_fjyy3")
[node name="Finder" type="Button" parent="Panel/ScrollContainer/VBoxContainer/Modes"]
layout_mode = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_lmh17")
text = "收集品查找"
script = SubResource("GDScript_dicqo")
[node name="HSeparator2" type="HSeparator" parent="Panel/ScrollContainer/VBoxContainer"]
layout_mode = 2
[node name="EditControls" type="VBoxContainer" parent="Panel/ScrollContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="Settings" type="VBoxContainer" parent="Panel/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_vertical = 3
[node name="ScrollContainer" type="ScrollContainer" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Settings"]
layout_mode = 2
size_flags_vertical = 3
vertical_scroll_mode = 2
[node name="CollectibleList" type="VBoxContainer" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Settings/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="Button" type="Button" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Settings"]
layout_mode = 2
icon_alignment = 1
script = ExtResource("2_tjngl")
icon_name = "Add"
[node name="Finder" type="VBoxContainer" parent="Panel/ScrollContainer/VBoxContainer/EditControls"]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_vertical = 3
[node name="ScanButton" type="Button" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Finder"]
unique_name_in_owner = true
layout_mode = 2
text = "Scan Maps"
[node name="ScanProgress" type="ProgressBar" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Finder"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ShowOnMap" type="CheckButton" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Finder"]
unique_name_in_owner = true
visible = false
layout_mode = 2
text = "Display on Map"
[node name="ScrollContainer" type="ScrollContainer" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Finder"]
layout_mode = 2
size_flags_vertical = 3
horizontal_scroll_mode = 3
vertical_scroll_mode = 2
[node name="Summary" type="VBoxContainer" parent="Panel/ScrollContainer/VBoxContainer/EditControls/Finder/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
[node name="MapOverlay" type="Control" parent="."]
clip_contents = true
layout_mode = 2
size_flags_horizontal = 3
script = ExtResource("2_pgyha")
[node name="ColorRect" type="ColorRect" parent="MapOverlay"]
modulate = Color(0, 0, 0, 0.12549)
show_behind_parent = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="Map" type="Control" parent="MapOverlay"]
unique_name_in_owner = true
show_behind_parent = true
anchors_preset = 0
size_flags_horizontal = 3
mouse_filter = 2
[node name="StatusLabel" type="Label" parent="MapOverlay"]
unique_name_in_owner = true
layout_mode = 0
offset_right = 1.0
offset_bottom = 23.0
theme_override_styles/normal = SubResource("StyleBoxFlat_qbvjg")
vertical_alignment = 1
[connection signal="toggled" from="Panel/ScrollContainer/VBoxContainer/Layer/CheckButton" to="." method="toggle_mapped"]
[connection signal="pressed" from="Panel/ScrollContainer/VBoxContainer/EditControls/Settings/Button" to="Panel/ScrollContainer/VBoxContainer/Modes/Settings" method="add_collectible"]
[connection signal="pressed" from="Panel/ScrollContainer/VBoxContainer/EditControls/Finder/ScanButton" to="Panel/ScrollContainer/VBoxContainer/Modes/Finder" method="start_scan"]
[connection signal="toggled" from="Panel/ScrollContainer/VBoxContainer/EditControls/Finder/ShowOnMap" to="Panel/ScrollContainer/VBoxContainer/Modes/Finder" method="toggle_display_on_map"]

@ -1,119 +0,0 @@
@tool
extends Control
const EDITOR_SCRIPT = preload("res://addons/MetroidvaniaSystem/Database/MapEditor.gd")
var editor: EDITOR_SCRIPT
var theme_cache: Dictionary
var use_cursor := true
var room_only_cursor := true
var drag_from: Vector2i = EDITOR_SCRIPT.NULL_VECTOR2I
var highlighted_room: Array[Vector3i]
var highlighted_border := -1
var top_draw: Callable
func _ready() -> void:
if not owner.plugin:
return
editor = owner
_editor_init.call_deferred()
func _editor_init():
pass
func _update_theme():
pass
func _editor_enter():
pass
func _editor_exit():
pass
func _editor_input(event: InputEvent):
pass
func _editor_draw(map_overlay: CanvasItem):
if highlighted_border == -1:
for p in highlighted_room:
map_overlay.draw_rect(Rect2(Vector2(p.x, p.y) * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.highlighted_room)
if drag_from == EDITOR_SCRIPT.NULL_VECTOR2I:
if use_cursor and map_overlay.cursor_inside and (not room_only_cursor or get_cell_at_cursor()):
map_overlay.draw_rect(Rect2(get_cursor_pos() as Vector2 * MetSys.CELL_SIZE, MetSys.CELL_SIZE), theme_cache.cursor_color, false, 2)
else:
var rect := get_rect_between(drag_from, get_cursor_pos())
top_draw = func(map_overlay: CanvasItem): map_overlay.draw_string(get_theme_font(&"font", &"Label"), Vector2(get_cursor_pos()) * MetSys.CELL_SIZE + MetSys.CELL_SIZE * Vector2.UP * 0.5, "%d x %d" % [rect.size.x, rect.size.y])
rect.position *= MetSys.CELL_SIZE
rect.size *= MetSys.CELL_SIZE
map_overlay.draw_rect(rect, theme_cache.cursor_color, false, 2)
if highlighted_border > -1:
if highlighted_room.is_empty():
draw_border_highlight(map_overlay, get_cursor_pos(), highlighted_border)
else:
for p in highlighted_room:
var cell_data := MetSys.map_data.get_cell_at(p)
for i in 4:
if cell_data.borders[i] > -1:
draw_border_highlight(map_overlay, Vector2(p.x, p.y), i)
func draw_border_highlight(map_overlay: CanvasItem, pos: Vector2, border: int):
match border:
MetSys.R:
map_overlay.draw_rect(Rect2(pos * MetSys.CELL_SIZE + Vector2(MetSys.CELL_SIZE.x * 0.667, 0), MetSys.CELL_SIZE * Vector2(0.333, 1)), theme_cache.border_highlight)
MetSys.D:
map_overlay.draw_rect(Rect2(pos * MetSys.CELL_SIZE + Vector2(0, MetSys.CELL_SIZE.y * 0.667), MetSys.CELL_SIZE * Vector2(1, 0.333)), theme_cache.border_highlight)
MetSys.L:
map_overlay.draw_rect(Rect2(pos * MetSys.CELL_SIZE, MetSys.CELL_SIZE * Vector2(0.333, 1)), theme_cache.border_highlight)
MetSys.U:
map_overlay.draw_rect(Rect2(pos * MetSys.CELL_SIZE, MetSys.CELL_SIZE * Vector2(1, 0.333)), theme_cache.border_highlight)
func get_cursor_pos() -> Vector2i:
return editor.get_cursor_pos()
func get_coords(p: Vector2i, layer := editor.current_layer) -> Vector3i:
return Vector3i(p.x, p.y, layer)
func get_rect_between(point1: Vector2, point2: Vector2) -> Rect2:
var start: Vector2
start.x = minf(point1.x, point2.x)
start.y = minf(point1.y, point2.y)
var end: Vector2
end.x = maxf(point1.x, point2.x)
end.y = maxf(point1.y, point2.y)
return Rect2(start, Vector2.ONE).expand(end + Vector2.ONE)
func get_square_border_idx(borders: Array[int], rel: Vector2) -> int:
if borders[MetSys.L] > -1 and rel.x < MetSys.CELL_SIZE.x / 3:
return MetSys.L
if borders[MetSys.R] > -1 and rel.x > MetSys.CELL_SIZE.x - MetSys.CELL_SIZE.x / 3:
return MetSys.R
if borders[MetSys.U] > -1 and rel.y < MetSys.CELL_SIZE.y / 3:
return MetSys.U
if borders[MetSys.D] > -1 and rel.y > MetSys.CELL_SIZE.y - MetSys.CELL_SIZE.y / 3:
return MetSys.D
return -1
func get_cell_at_cursor() -> MetroidvaniaSystem.MapData.CellData:
return MetSys.map_data.get_cell_at(get_coords(get_cursor_pos()))
func mark_modified():
editor.plugin.modified = true
func _notification(what: int) -> void:
if what == NOTIFICATION_THEME_CHANGED:
theme_cache.highlighted_room = get_theme_color(&"highlighted_room", &"MetSys")
theme_cache.border_highlight = get_theme_color(&"border_highlight", &"MetSys")
theme_cache.cursor_color = get_theme_color(&"cursor_color", &"MetSys")
_update_theme()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://chxprk5rw4ldq"
path="res://.godot/imported/Icon.png-ee9c40684788f1a27b35051aa11d9b76.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Icon.png"
dest_files=["res://.godot/imported/Icon.png-ee9c40684788f1a27b35051aa11d9b76.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

@ -1,76 +0,0 @@
@tool
extends EditorPlugin
enum { TAB_EDITOR, TAB_OVERVIEW, TAB_MANAGE }
var main: Control
var modified: bool:
set(m):
if m == modified:
return
modified = m
dirty_toggled.emit(modified)
var theme_scanner: Timer
var prev_theme_state: Array
signal dirty_toggled
func _has_main_screen() -> bool:
return true
func _get_plugin_name() -> String:
return "MetSys"
func _get_plugin_icon() -> Texture2D:
return preload("res://addons/MetroidvaniaSystem/Icon.png")
func _enter_tree() -> void:
theme_scanner = Timer.new()
theme_scanner.wait_time = 0.6
add_child(theme_scanner)
theme_scanner.timeout.connect(check_theme)
await get_tree().process_frame
if not get_singleton():
add_autoload_singleton("MetSys", "res://addons/MetroidvaniaSystem/Nodes/Singleton.tscn")
ProjectSettings.save()
OS.set_restart_on_exit(true, ["-e"])
get_tree().quit()
return
main = preload("res://addons/MetroidvaniaSystem/Database/Main.tscn").instantiate()
main.plugin = self
get_editor_interface().get_editor_main_screen().add_child(main)
main.hide()
get_singleton().settings.theme_changed.connect(func(): prev_theme_state.clear())
func _exit_tree() -> void:
main.queue_free()
func _make_visible(visible: bool) -> void:
main.visible = visible
if visible:
theme_scanner.start()
else:
theme_scanner.stop()
func _save_external_data() -> void:
get_singleton().map_data.save_data()
modified = false
func _get_unsaved_status(for_scene: String) -> String:
if modified and for_scene.is_empty():
return "MetSys map has been modified."
return ""
func get_singleton() -> MetroidvaniaSystem:
return get_tree().root.get_node_or_null(^"MetSys")
func check_theme():
var theme := get_singleton().settings.theme
var changed := theme.check_for_changes(prev_theme_state)
if not changed.is_empty():
get_singleton().theme_modified.emit(changed)

@ -1,42 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://w48b71tv2d4v"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/CollectibleElement.gd" id="1_1bebr"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/EditorIconButton.gd" id="2_3d05g"]
[node name="Collectible" type="PanelContainer"]
script = ExtResource("1_1bebr")
metadata/is_collectible = true
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="LineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Node Name"
[node name="Button" type="Button" parent="VBoxContainer/HBoxContainer2"]
layout_mode = 2
script = ExtResource("2_3d05g")
icon_name = "Remove"
[node name="IconContainer" type="HBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/IconContainer"]
layout_mode = 2
size_flags_horizontal = 0
text = "Icon"
[node name="SaveTimer" type="Timer" parent="."]
wait_time = 0.5
one_shot = true
[connection signal="text_changed" from="VBoxContainer/HBoxContainer2/LineEdit" to="SaveTimer" method="start" unbinds=1]
[connection signal="pressed" from="VBoxContainer/HBoxContainer2/Button" to="." method="delete"]
[connection signal="timeout" from="SaveTimer" to="." method="emit_signal" binds= [&"save_request"]]

@ -1,30 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://cwdt1r5mp5gpj"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/CollectibleFoundItem.gd" id="1_t85ni"]
[node name="Collectible" type="PanelContainer"]
script = ExtResource("1_t85ni")
metadata/is_collectible = true
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
mouse_filter = 2
[node name="Icon" type="TextureRect" parent="HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
expand_mode = 3
stretch_mode = 5
[node name="Label" type="Label" parent="HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Name"
[node name="Button" type="Button" parent="."]
unique_name_in_owner = true
layout_mode = 2
flat = true
[connection signal="mouse_entered" from="Button" to="." method="on_hover"]
[connection signal="mouse_exited" from="Button" to="." method="on_unhover"]

File diff suppressed because one or more lines are too long

@ -1,6 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://c01ibpvrp2wsd"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/MetroidvaniaSystem.gd" id="1_y1fyl"]
[node name="MetroidvaniaSettings" type="Node"]
script = ExtResource("1_y1fyl")

@ -1,37 +0,0 @@
@tool
extends PanelContainer
var resource_picker: EditorResourcePicker
signal save_request
func _ready() -> void:
if get_tree().edited_scene_root and get_tree().edited_scene_root.is_ancestor_of(self):
return
resource_picker = EditorResourcePicker.new()
%IconContainer.add_child(resource_picker)
resource_picker.base_type = "Texture2D"
resource_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL
resource_picker.resource_changed.connect(func(a): save_request.emit())
func get_data() -> Dictionary:
var data := {element = %LineEdit.text}
if resource_picker.edited_resource:
data.icon = resource_picker.edited_resource.resource_path
return data
func get_data2() -> Dictionary:
var data := get_data()
if "icon" in data:
data.icon = load(data.icon)
return data
func set_data(data: Dictionary):
%LineEdit.text = data.element
if "icon" in data:
resource_picker.edited_resource = load(data.icon)
func delete() -> void:
queue_free()
save_request.emit()

@ -1,32 +0,0 @@
@tool
extends PanelContainer
signal hovered
signal unhovered
var data: Dictionary
func set_element(element: Dictionary):
data = element
%Label.text = data.element
%Icon.texture = data.get("icon")
data.map = data.map.trim_prefix(MetSys.settings.map_root_folder)
var room := MetSys.map_data.get_cells_assigned_to(data.map)
if "position" in data and not room.is_empty():
var top_left := Vector2i.MAX
for coords in room:
top_left.x = mini(coords.x, top_left.x)
top_left.y = mini(coords.y, top_left.y)
var pos := top_left + Vector2i(data.position / MetSys.settings.in_game_cell_size)
data.coords = Vector3i(pos.x, pos.y, room[0].z)
%Button.tooltip_text = "%s\nat: %s %s" % [data.element, data.map, data.coords]
else:
%Button.tooltip_text = "%s\nat: %s" % [data.element, data.map]
func on_hover() -> void:
hovered.emit()
func on_unhover() -> void:
unhovered.emit()

@ -1,25 +0,0 @@
@tool
## A class that registers and draws custom elements.
##
## Custom elements are map elements that are too complex to be handled as regular cells. Example elements include location name labels, Metroid-like elevator shafts or room contents inside cell. Internally they are represented as rectangles with an origin point and a [String] extra data.
## [br][br]To use custom elements, create a new script that inherits this one and assign it to [code]custom_element_script[/code] property in MetSys Settings. Register your elements by calling [method register_elements] inside [method Object._init] then press Refresh Custom Elements button in the Manage tab of MetSys Database. This will create an instance of your script and the elements will be available in Custom Elements edit mode of the editor. You need to call [method MetroidvaniaSystem.draw_custom_elements] to draw the elements on your map.
extends RefCounted
var custom_elements: Dictionary
## Registers a new element. The [param name] will appear in the editor (capitalized) and [param draw_callback] will be used to draw your element. The callback is called automatically when the element needs to be drawn.
## [br][br]The callback signature is [code]method(canvas_item: CanvasItem, coords: Vector3i, pos: Vector2, size: Vector2, data: String)[/code]. [code]canvas_item[/code] is the [CanvasItem] that you should use to draw the elements, [code]coords[/code] are the coordinates of the origin point of the element, [code]pos[/code] is the position of the element's origin in pixels, [code]size[/code] is the size of the element's rectangle in pixels, [code]data[/code] is the data [String] that was assigned to the element.
## [br][br]Example draw callback that draws a label if the cell was discovered:
## [codeblock]
## func draw_label(canvas_item: CanvasItem, coords: Vector3i, pos: Vector2, size: Vector2, data: String):
## if not MetSys.is_cell_discovered(coords):
## return
##
## canvas_item.draw_string(ThemeDB.get_default_theme().default_font,
## pos + Vector2(-1, -0.5) * MetSys.CELL_SIZE, data, HORIZONTAL_ALIGNMENT_CENTER, MetSys.CELL_SIZE.x * 3)
func register_element(name: String, draw_callback: Callable):
custom_elements[name.replace("/", "_")] = draw_callback
func draw_element(canvas_item: CanvasItem, coords: Vector3i, name: String, pos: Vector2, size: Vector2, data: String):
var callback: Callable = custom_elements[name]
callback.call(canvas_item, coords, pos, size, data)

@ -1,11 +0,0 @@
@tool
extends Button
@export var icon_name: String
func _notification(what: int) -> void:
if what == NOTIFICATION_THEME_CHANGED:
if get_tree().edited_scene_root and get_tree().edited_scene_root.is_ancestor_of(self):
return
icon = get_theme_icon(icon_name, &"EditorIcons")

@ -1,19 +0,0 @@
## A class for creating custom cells at runtime.
##
## MapBuilder is obtained with [method MetroidvaniaSystem.get_map_builder] and can be used for procedural map generation or other use-cases where cells that weren't created in the editor need to be used.
## [br][br]A cell can be created with [method create_cell] (the position can't already have another cell). The method will return a CellOverride that you can use to customize the cell's appearance. Once you finished creating your cells, use [method update_map].
## [br][br]Note that, while cells created with this class are part of [method MetroidvaniaSystem.get_save_data]'s dump, you can't differentiate them from regular cell overrides. You need to keep this information yourself.
extends RefCounted
## The cells created with this MapBuilder.
var cells: Array[MetroidvaniaSystem.MapData.CellOverride]
## Creates a new cell on the world map and returns a CellOverride that can be used to customize the cell. You can destroy created cells using their [code]destroy()[/code] method.
func create_cell(at: Vector3i) -> MetroidvaniaSystem.MapData.CellOverride:
var cell: MetroidvaniaSystem.MapData.CellOverride = MetSys.map_data.create_custom_cell(at)
cells.append(cell)
return cell
## Requests the world map update. This simply emits [signal MetroidvaniaSystem.map_updated]. The signal is not emitted automatically when customizing the cells, unlike regular overrides. Updating the map in batches is more efficient.
func update_map():
MetSys.map_updated.emit()

@ -1,424 +0,0 @@
const FWD = { MetroidvaniaSystem.R: Vector2i.RIGHT, MetroidvaniaSystem.D: Vector2i.DOWN, MetroidvaniaSystem.L: Vector2i.LEFT, MetroidvaniaSystem.U: Vector2i.UP }
class CellData:
enum { DATA_EXITS, DATA_COLORS, DATA_SYMBOL, DATA_MAP, OVERRIDE_COORDS, OVERRIDE_CUSTOM }
var color: Color = Color.TRANSPARENT
var borders: Array[int] = [-1, -1, -1, -1]
var border_colors: Array[Color] = [Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT, Color.TRANSPARENT]
var symbol := -1
var assigned_scene: String
var override_map: String
var loading
func _init(line: String) -> void:
if line.is_empty():
return
loading = [line, -1]
var chunk := load_next_chunk()
for i in 4:
borders[i] = chunk.get_slice(",", i).to_int()
chunk = load_next_chunk()
if not chunk.is_empty():
var color_slice := chunk.get_slice(",", 0)
if not color_slice.is_empty():
color = Color(color_slice)
for i in 4:
color_slice = chunk.get_slice(",", i + 1)
if not color_slice.is_empty():
border_colors[i] = Color(color_slice)
chunk = load_next_chunk()
if not chunk.is_empty():
symbol = chunk.to_int()
assigned_scene = load_next_chunk()
loading = null
func get_string() -> String:
var data: PackedStringArray
data.append("%s,%s,%s,%s" % borders)
var colors: Array[Color]
colors.assign([color] + Array(border_colors))
if colors.any(func(col: Color): return col.a > 0):
data.append("%s,%s,%s,%s,%s" % colors.map(func(col: Color): return col.to_html(false) if col.a > 0 else ""))
else:
data.append("")
if symbol > -1:
data.append(str(symbol))
else:
data.append("")
data.append(assigned_scene.trim_prefix(MetSys.settings.map_root_folder + "/"))
return "|".join(data)
func load_next_chunk() -> String:
loading[1] += 1
return loading[0].get_slice("|", loading[1])
func get_color() -> Color:
var c: Color
var override := get_override()
if override and override.color.a > 0:
c = override.color
else:
c = color
if c.a > 0:
return c
return MetSys.settings.theme.default_center_color
func get_border(idx: int) -> int:
var override := get_override()
if override and override.borders[idx] != -2:
return override.borders[idx]
return borders[idx]
func get_border_color(idx: int) -> Color:
var c: Color
var override := get_override()
if override and override.border_colors[idx].a > 0:
c = override.border_colors[idx]
else:
c = border_colors[idx]
if c.a > 0:
return c
return MetSys.settings.theme.default_border_color
func get_symbol() -> int:
var override := get_override()
if override and override.symbol != -2:
return override.symbol
return symbol
func get_assigned_scene() -> String:
var override := get_override()
if override and override.assigned_scene != "/":
return override.assigned_scene
if not override_map.is_empty():
return override_map
return assigned_scene
func get_override() -> CellOverride:
if not MetSys.save_data:
return null
return MetSys.save_data.cell_overrides.get(self)
func get_coords() -> Vector3i:
return MetSys.map_data.cells.find_key(self)
## A runtime override for the cell's properties that allows to modify it's default appearance designed in the editor.
##
## CellOverride can be obtained from [method MetroidvaniaSystem.get_cell_override] or using the MapBuilder. It comes with a set of methods that allows customizing the cells. [signal MetroidvaniaSystem.map_updated] signal is automatically emitted after modifications, unless the cell comes from MapBuilder.
class CellOverride extends CellData:
var original_room: CellData
var custom_cell_coords := Vector3i.MAX
var commit_queued: bool
func _init(from: CellData) -> void:
original_room = from
borders = [-2, -2, -2, -2]
symbol = -2
assigned_scene = "/"
static func load_from_line(line: String) -> CellOverride:
var cell: CellData
var coords_string := line.get_slice("|", CellData.OVERRIDE_COORDS)
var coords := Vector3i(coords_string.get_slice(",", 0).to_int(), coords_string.get_slice(",", 1).to_int(), coords_string.get_slice(",", 2).to_int())
var is_custom := line.get_slice("|", CellData.OVERRIDE_CUSTOM) == "true"
if is_custom:
cell = MetSys.map_data.create_cell_at(coords)
else:
cell = MetSys.map_data.get_cell_at(coords)
var override := CellOverride.new(cell)
if is_custom:
override.custom_cell_coords = coords
var fake_cell := CellData.new(line)
override.borders = fake_cell.borders
override.border_colors = fake_cell.border_colors
override.color = fake_cell.color
override.symbol = fake_cell.symbol
override.set_assigned_scene(fake_cell.assigned_scene)
return override
## Sets a border of the cell. [param idx] is the direction index of the cell. Use [constant MetroidvaniaSystem.R], [constant MetroidvaniaSystem.D], [constant MetroidvaniaSystem.L], [constant MetroidvaniaSystem.U] constants here. The [param value] must be within the [member MapTheme.borders] array bounds + 2 (0 and 1 are the default wall and passage). Use the default to reset to the border assigned in the editor. Value of [code]-1[/code] will remove the border, but it's not recommended.
func set_border(idx: int, value := -2):
assert(idx >= 0 and idx < 4)
borders[idx] = value
_queue_commit()
## Sets the color of a cell's border. [param idx] is the direction index of the cell (see [method set_border]). If the default [param value] is used (or any color with [code]0[/code] alpha), the border will be reset to the default color.
func set_border_color(idx: int, value := Color.TRANSPARENT):
assert(idx >= 0 and idx < 4)
border_colors[idx] = value
_queue_commit()
## Sets the color of the cell's center texture.
func set_color(value := Color.TRANSPARENT):
color = value
_queue_commit()
## Sets the cell's assigned symbol. Value of [code]-1[/code] will remove the symbol. The markers assigned from [method MetroidvaniaSystem.set_custom_marker] still have a priority.
func set_symbol(value := -2):
assert(value >= -2 and value < MetSys.settings.theme.symbols.size())
symbol = value
_queue_commit()
## Changes the scene assigned to the cell. Unlike other methods, this has effect on the whole room that contains this cell. Using the default value will reset it to the scene assigned in the editor.
func set_assigned_scene(value := "/"):
if value == "/":
_cleanup_assigned_scene()
else:
if custom_cell_coords != Vector3i.MAX:
if not value in MetSys.map_data.assigned_scenes:
MetSys.map_data.assigned_scenes[value] = []
MetSys.map_data.assigned_scenes[value].append(custom_cell_coords)
else:
MetSys.map_data.scene_overrides[value] = original_room.assigned_scene
for coords in MetSys.map_data.get_whole_room(original_room.get_coords()):
var cell: CellData = MetSys.map_data.cells[coords]
if not cell.override_map.is_empty():
push_warning("Assigned map already overriden at: %s" % coords)
cell.override_map = value
assigned_scene = value
MetSys.room_assign_updated.emit()
_queue_commit()
## Applies the values from this CellOverride to all cells that belong to the specified [param group_id]. You need to apply your customization [i]before[/i] calling this method.
func apply_to_group(group_id: int):
assert(group_id in MetSys.map_data.cell_groups)
for coords in MetSys.map_data.cell_groups[group_id]:
var override: CellOverride = MetSys.get_cell_override(coords)
if override == self:
continue
override.borders = borders.duplicate()
override.color = color
override.border_colors = border_colors.duplicate()
override.symbol = symbol
_queue_commit()
## Destroys the cell. This method can only be used on the overrides from the MapBuilder.
func destroy() -> void:
if custom_cell_coords == Vector3i.MAX:
push_error("Only custom cell can be destroyed.")
return
var cell_coords := custom_cell_coords
custom_cell_coords = Vector3i.MAX
MetSys.remove_cell_override(cell_coords)
MetSys.map_data.erase_cell(cell_coords)
MetSys.map_data.cell_overrides.erase(cell_coords)
MetSys.map_data.custom_cells.erase(self)
if assigned_scene != "/":
MetSys.map_data.assigned_scenes[assigned_scene].erase(cell_coords)
if MetSys.save_data:
MetSys.save_data.discovered_cells.erase(cell_coords)
func _cleanup_assigned_scene() -> void:
if assigned_scene == "/":
return
MetSys.map_data.scene_overrides.erase(assigned_scene)
for coords in MetSys.map_data.get_whole_room(original_room.get_coords()):
MetSys.map_data.cells[coords].override_map = ""
func _get_override_string(coords: Vector3i) -> String:
return str(get_string(), "|", coords.x, ",", coords.y, ",", coords.z, "|", custom_cell_coords != Vector3i.MAX)
func _queue_commit():
if commit_queued or custom_cell_coords != Vector3i.MAX:
return
commit_queued = true
_commit.call_deferred()
func _commit() -> void:
commit_queued = false
MetSys.map_updated.emit()
var cells: Dictionary#[Vector3i, CellData]
var custom_cells: Dictionary#[Vector3i, CellData]
var assigned_scenes: Dictionary#[String, Array[Vector3i]]
var cell_groups: Dictionary#[int, Array[Vector3i]]
var custom_elements: Dictionary#[Vector3i, Struct]
var cell_overrides: Dictionary#[Vector3i, CellOverride]
var scene_overrides: Dictionary#[String, String]
func load_data():
var file := FileAccess.open(MetSys.settings.map_setting_folder.path_join("MapData.txt"), FileAccess.READ)
if not file:
push_warning("Map data file does not exist.")
return
var data := file.get_as_text().split("\n")
var i: int
var current_section := 0 # groups, custom_elements, cells
while i < data.size():
var line := data[i]
if line.begins_with("["):
current_section = 2
line = line.trim_prefix("[").trim_suffix("]")
var coords: Vector3i
coords.x = line.get_slice(",", 0).to_int()
coords.y = line.get_slice(",", 1).to_int()
coords.z = line.get_slice(",", 2).to_int()
i += 1
line = data[i]
var cell_data := CellData.new(line)
if not cell_data.assigned_scene.is_empty():
assigned_scenes[cell_data.assigned_scene] = [coords]
cells[coords] = cell_data
elif current_section == 1 or current_section == 0 and line.contains("/"):
current_section = 1
var element_data := line.split("/")
var element: Dictionary
element.name = element_data[1]
element.size = Vector2i(element_data[2].get_slice("x", 0).to_int(), element_data[2].get_slice("x", 1).to_int())
if element_data.size() == 4:
element.data = element_data[3]
else:
element.data = ""
var coords: Vector3i
coords.x = element_data[0].get_slice(",", 0).to_int()
coords.y = element_data[0].get_slice(",", 1).to_int()
coords.z = element_data[0].get_slice(",", 2).to_int()
custom_elements[coords] = element
elif current_section == 0:
var group_data := line.split(":")
var group_id := group_data[0].to_int()
var rooms_in_group: Array
for j in range(1, group_data.size()):
var coords: Vector3i
coords.x = group_data[j].get_slice(",", 0).to_int()
coords.y = group_data[j].get_slice(",", 1).to_int()
coords.z = group_data[j].get_slice(",", 2).to_int()
rooms_in_group.append(coords)
cell_groups[group_id] = rooms_in_group
i += 1
for map in assigned_scenes.keys():
var assigned_cells: Array[Vector3i]
assigned_cells.assign(assigned_scenes[map])
assigned_scenes[map] = get_whole_room(assigned_cells[0])
func save_data():
var file := FileAccess.open(MetSys.settings.map_setting_folder.path_join("MapData.txt"), FileAccess.WRITE)
if not file:
push_error("Could not open file '%s' for writing." % MetSys.settings.map_setting_folder.path_join("MapData.txt"))
return
for group in cell_groups:
if cell_groups[group].is_empty():
continue
var line: PackedStringArray
line.append(str(group))
for coords in cell_groups[group]:
line.append("%s,%s,%s" % [coords.x, coords.y, coords.z])
file.store_line(":".join(line))
for coords in custom_elements:
var line: PackedStringArray
line.append("%s,%s,%s" % [coords.x, coords.y, coords.z])
var element: Dictionary = custom_elements[coords]
line.append(element.name)
line.append("%sx%s" % [element.size.x, element.size.y])
if not element.data.is_empty():
line.append(element.data)
file.store_line("/".join(line))
for coords in cells:
file.store_line("[%s,%s,%s]" % [coords.x, coords.y, coords.z])
var cell_data := get_cell_at(coords)
file.store_line(cell_data.get_string())
func get_cell_at(coords: Vector3i) -> CellData:
return cells.get(coords)
func create_cell_at(coords: Vector3i) -> CellData:
cells[coords] = CellData.new("")
return cells[coords]
func create_custom_cell(coords: Vector3i) -> CellOverride:
assert(not coords in cells, "A cell already exists at this position")
var cell := create_cell_at(coords)
custom_cells[coords] = cell
var override: CellOverride = MetSys.save_data.add_cell_override(cell)
override.custom_cell_coords = coords
return override
func get_whole_room(at: Vector3i) -> Array[Vector3i]:
var room: Array[Vector3i]
var to_check: Array[Vector2i] = [Vector2i(at.x, at.y)]
var checked: Array[Vector2i]
while not to_check.is_empty():
var p: Vector2i = to_check.pop_back()
checked.append(p)
var coords := Vector3i(p.x, p.y, at.z)
if coords in cells:
room.append(coords)
for i in 4:
if cells[coords].borders[i] == -1:
var p2: Vector2i = p + FWD[i]
if not p2 in to_check and not p2 in checked:
to_check.append(p2)
return room
func get_cells_assigned_to(map: String) -> Array[Vector3i]:
if map in scene_overrides:
map = scene_overrides[map]
var ret: Array[Vector3i]
ret.assign(assigned_scenes.get(map, []))
return ret
func get_assigned_scene_at(coords: Vector3i) -> String:
var cell := get_cell_at(coords)
if cell:
return cell.get_assigned_scene()
else:
return ""
func erase_cell(coords: Vector3i):
var assigned_scene: String = cells[coords].assigned_scene
assigned_scenes.erase(assigned_scene)
cells.erase(coords)
for group in cell_groups.values():
group.erase(coords)

@ -1,146 +0,0 @@
@tool
## Resource that defines the cell appearance.
##
## MapTheme is assigned in MetSys Settings and defines the cell appearance when using [method MetroidvaniaSystem.draw_cell]. It has a few subtypes: shape can be either square or rectangular and borders can be either shared or not. Some properties are only available for certain theme subtypes. Check the Map Theme section in README for some more detailed information.
extends Resource
class_name MapTheme
const SQUARE_BORDERS = ["wall", "passage", "separator", "borders"]
const RECTANGLE_BORDERS = ["vertical_wall", "horizontal_wall", "vertical_passage", "horizontal_passage", "vertical_separator", "horizontal_separator", "vertical_borders", "horizontal_borders"]
const DEFAULT_CORNERS = ["inner_corner", "outer_corner"]
const SHARED_CORNERS = ["u_corner", "l_corner", "t_corner", "cross_corner"]
## The cell's center texture. The size of all other textures depends on this one. It can be square or rectangular and the shape affects some properties. The texture should be grayscale (preferrably white).
@export var center_texture: Texture2D:
set(ct):
center_texture = ct
var new_rectangle := center_texture.get_width() != center_texture.get_height()
if new_rectangle != rectangle:
rectangle = new_rectangle
notify_property_list_changed()
## The texture drawn in empty space. Optional.
@export var empty_space_texture: Texture2D
## The scene that appears at player location after using [method MetroidvaniaSystem.add_player_location]. Must have a [Node2D] root. Optional, but [method MetroidvaniaSystem.add_player_location] can't be used if the scene is not provided.
@export var player_location_scene: PackedScene
## If [code]true[/code], the player location is displayed accurately inside the cell. If [code]false[/code], the location marker always appears at the center of the cell.
@export var show_exact_player_location: bool
## Determines how mapped (unexplored) cells are displayed. Use Preview Mapped option in Map Viewer to preview this style.
@export_flags("Center", "Outline", "Borders", "Symbol") var mapped_display := 3
## If [code]true[/code], cell borders are drawn between the cells instead of inside them. This setting has a major effect on the theme.
@export var use_shared_borders: bool:
set(usb):
if usb != use_shared_borders:
use_shared_borders = usb
notify_property_list_changed()
@export_group("Colors")
## Default modulation of the center texture.
@export var default_center_color: Color
## Modulation of the center texture when the cell is only mapped. Usually this color is gray.
@export var mapped_center_color: Color
## Default modulation of the cell's border textures.
@export var default_border_color: Color
## Modulation of the cell's border textures when the cell is only mapped.
@export var mapped_border_color: Color
@export_group("Symbols")
## The list of symbols that can be assigned to cells, either from editor, using [method MetroidvaniaSystem.add_custom_marker] or from storable objects.
@export var symbols: Array[Texture2D]
## The symbol for discovered, but not collected item (or any other object). See [method MetroidvaniaSystem.add_storable_object_with_marker]. Must be within [member symbols] indicies. [code]-1[/code] means no symbol will be assigned automatically.
@export var uncollected_item_symbol := -1
## The symbol for collected item (or any other object). See [method MetroidvaniaSystem.store_object]. Must be within [member symbols] indicies. [code]-1[/code] means no symbol will be assigned automatically.
@export var collected_item_symbol := -1
@export_group("Border Textures")
## The texture used for wall borders. Must be oriented vertically facing towards east. Height should match [member center_texture].
@export var wall: Texture2D
## The texture used for passage borders. Same rules as [member wall].
@export var passage: Texture2D
## The texture used for separator, i.e. fake border that draws between cells with no border. Same rules as [member wall]. Optional.
@export var separator: Texture2D
## A set of alternative borders that can be assigned to the cell (either walls or passages). Same rules as [member wall].
@export var borders: Array[Texture2D]
## Same as [member wall], but only available when the [member center_texture] is a rectangle.
@export var vertical_wall: Texture2D
## Same as [member wall], but only available when the [member center_texture] is a rectangle. It should still be oriented horizontally and height should match center texture's width.
@export var horizontal_wall: Texture2D
## Same as [member passage], but only available when the [member center_texture] is a rectangle.
@export var vertical_passage: Texture2D
## Same as [member passage], but only available when the [member center_texture] is a rectangle. It should still be oriented horizontally and height should match center texture's width.
@export var horizontal_passage: Texture2D
## Same as [member separator], but only available when the [member center_texture] is a rectangle.
@export var vertical_separator: Texture2D
## Same as [member separator], but only available when the [member center_texture] is a rectangle. It should still be oriented horizontally and height should match center texture's width.
@export var horizontal_separator: Texture2D
## Same as [member borders], but only available when the [member center_texture] is a rectangle.
@export var vertical_borders: Array[Texture2D]
## Same as [member borders], but only available when the [member center_texture] is a rectangle. It should still be oriented horizontally and height should match center texture's width.
@export var horizontal_borders: Array[Texture2D]
@export_group("Corner Textures")
## The texture that draws at room's outer corners. It draws above borders.
@export var outer_corner: Texture2D
## The texture that draws at room's inner corners. Only appears in concave rooms. It draws above borders.
@export var inner_corner: Texture2D
## Only available when [member use_shared_borders] is enabled. Corner of a single border that divides U-shaped room.
@export var u_corner: Texture2D
## Only available when [member use_shared_borders] is enabled. Corner of 2 perpendicular borders.
@export var l_corner: Texture2D
## Only available when [member use_shared_borders] is enabled. Corner of 3 borders connecting at one point.
@export var t_corner: Texture2D
## Only available when [member use_shared_borders] is enabled. Corner of 4 borders.
@export var cross_corner: Texture2D
var rectangle: bool
func _validate_property(property: Dictionary) -> void:
if rectangle:
if property.name in SQUARE_BORDERS:
property.usage = 0
return
else:
if property.name in RECTANGLE_BORDERS:
property.usage = 0
return
if use_shared_borders:
if property.name in DEFAULT_CORNERS:
property.usage = 0
return
else:
if property.name in SHARED_CORNERS:
property.usage = 0
return
func is_unicorner() -> bool:
if use_shared_borders:
return l_corner == u_corner and t_corner == u_corner and cross_corner == u_corner
else:
return inner_corner == outer_corner
func check_for_changes(prev_state: Array) -> Array[String]:
var new_state: Array
var changed: Array[String]
var properties := get_property_list()
for property in properties:
new_state.append(get(property.name))
if changed:
continue
var idx := new_state.size() - 1
if idx >= prev_state.size():
continue
if new_state[idx] != prev_state[idx]:
changed.append(property.name)
if not changed.is_empty() or prev_state.size() != new_state.size():
prev_state.assign(new_state)
return changed

@ -1,135 +0,0 @@
extends Control
@onready var map_overlay: Control = $MapOverlay
@onready var map: Control = %Map
@onready var current_layer_spinbox: SpinBox = %CurrentLayer
@onready var status_label: Label = %StatusLabel
const NULL_VECTOR2I = Vector2i(-9999999, -9999999)
var plugin: EditorPlugin
var drag_from: Vector2i = NULL_VECTOR2I
var view_drag: Vector4
var map_offset := Vector2i(10, 10)
var current_layer: int
var force_mapped: bool
func _enter_tree() -> void:
if owner:
plugin = owner.plugin
func _ready() -> void:
map.draw.connect(_on_map_draw)
map_overlay.mouse_exited.connect(status_label.hide)
map_overlay.gui_input.connect(_on_overlay_input)
map_overlay.draw.connect(_on_overlay_draw)
current_layer_spinbox.value_changed.connect(on_layer_changed)
%RecenterButton.pressed.connect(on_recenter_view)
status_label.hide()
await get_tree().process_frame
update_map_position()
map.size = MetSys.CELL_SIZE * 200
var refresh := func():
update_map_position()
on_layer_changed(current_layer)
MetSys.settings.theme_changed.connect(refresh)
MetSys.theme_modified.connect(refresh.unbind(1))
func get_cursor_pos() -> Vector2i:
var pos := (map_overlay.get_local_mouse_position() - MetSys.CELL_SIZE / 2).snapped(MetSys.CELL_SIZE) / MetSys.CELL_SIZE as Vector2i - map_offset
return pos
func on_layer_changed(l: int):
current_layer = l
map.queue_redraw()
map_overlay.queue_redraw()
func on_recenter_view() -> void:
map_offset = Vector2i(10, 10)
map_overlay.queue_redraw()
update_map_position()
func _on_overlay_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
if view_drag != Vector4():
if event.position.x >= map_overlay.size.x:
map_overlay.warp_mouse(Vector2(event.position.x - map_overlay.size.x, event.position.y))
view_drag.x -= map_overlay.size.x
elif event.position.x < 0:
map_overlay.warp_mouse(Vector2(map_overlay.size.x + event.position.x, event.position.y))
view_drag.x += map_overlay.size.x
if event.position.y >= map_overlay.size.y:
map_overlay.warp_mouse(Vector2(event.position.x, event.position.y - map_overlay.size.y))
view_drag.y -= map_overlay.size.y
elif event.position.y < 0:
map_overlay.warp_mouse(Vector2(event.position.x, map_overlay.size.y + event.position.y))
view_drag.y += map_overlay.size.y
map_offset = Vector2(view_drag.z, view_drag.w) + (map_overlay.get_local_mouse_position() - Vector2(view_drag.x, view_drag.y)) / MetSys.CELL_SIZE
update_map_position()
map_overlay.queue_redraw()
_on_drag()
else:
map_overlay.queue_redraw()
_update_status_label()
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_MIDDLE:
if event.pressed:
view_drag.x = map_overlay.get_local_mouse_position().x
view_drag.y = map_overlay.get_local_mouse_position().y
view_drag.z = map_offset.x
view_drag.w = map_offset.y
else:
view_drag = Vector4()
func _on_drag():
pass
func _update_status_label():
status_label.show()
status_label.text = str(get_cursor_pos())
func _unhandled_key_input(event: InputEvent) -> void:
if not visible:
return
if event is InputEventKey and event.pressed:
if event.physical_keycode == KEY_Q:
current_layer_spinbox.value -= 1
accept_event()
elif event.physical_keycode == KEY_E:
current_layer_spinbox.value += 1
accept_event()
func _notification(what: int) -> void:
if what == NOTIFICATION_THEME_CHANGED:
if map_overlay:
map_overlay.queue_redraw()
func _on_map_draw() -> void:
if not plugin:
return
MetroidvaniaSystem.RoomDrawer.force_mapped = force_mapped
for x in range(-100, 100):
for y in range(-100, 100):
MetSys.draw_cell(map, Vector2i(x, y) + Vector2i(100, 100), Vector3i(x, y, current_layer), true, false)
if MetSys.settings.theme.use_shared_borders:
MetSys.draw_shared_borders()
MetroidvaniaSystem.RoomDrawer.force_mapped = false
func _on_overlay_draw() -> void:
MetSys.draw_custom_elements(map_overlay, Rect2i(-map_offset, map_overlay.size / MetSys.CELL_SIZE + Vector2.ONE), Vector2(), current_layer)
func update_map_position():
map.position = Vector2(map_offset - Vector2i(100, 100)) * MetSys.CELL_SIZE

@ -1,356 +0,0 @@
@tool
## The class behind the MetSys singleton. It provides almost all of the public API of the addon.
##
## MetroidvaniaSystem class has various methods for interacting with various MetSys sub-systems at runtime. All methods and members with a description are "public", while anything that doesn't have a description is supposed to be used only internally. There are a couple of sub-classes that you can use; you can access their documentation from the methods that return them.
class_name MetroidvaniaSystem extends Node
const DEFAULT_SYMBOL = -99
enum { DISPLAY_CENTER = 1, DISPLAY_OUTLINE = 2, DISPLAY_BORDERS = 4, DISPLAY_SYMBOLS = 8 }
const Settings = preload("res://addons/MetroidvaniaSystem/Scripts/Settings.gd")
const SaveData = preload("res://addons/MetroidvaniaSystem/Scripts/SaveData.gd")
const MapData = preload("res://addons/MetroidvaniaSystem/Scripts/MapData.gd")
const MapBuilder = preload("res://addons/MetroidvaniaSystem/Scripts/MapBuilder.gd")
const RoomInstance = preload("res://addons/MetroidvaniaSystem/Scripts/RoomInstance.gd")
const RoomDrawer = preload("res://addons/MetroidvaniaSystem/Scripts/RoomDrawer.gd")
const CustomElementManager = preload("res://addons/MetroidvaniaSystem/Scripts/CustomElementManager.gd")
enum { R, ## Right border.
D, ## Bottom border.
L, ## Left border.
U, ## Top border.
}
var settings: Settings
## The size of a map cell. Automatically set to the size of [member MapTheme.center_texture]. Read only.
var CELL_SIZE: Vector2
var map_data: MapData
var save_data: SaveData
var last_player_position := Vector3i.MAX
var exact_player_position: Vector2
var current_room: RoomInstance
## The current layer, for the purpose of visiting with [method set_player_position] among other things. MetSys does not modify it automatically in any way. Changing it emits [signal cell_changed] signal.
var current_layer: int:
set(layer):
if layer == current_layer:
return
current_layer = layer
cell_changed.emit(Vector3i(last_player_position.x, last_player_position.y, current_layer))
var _meta_list: Array[StringName]
## Emitted when the player crosses a cell boundary and visits another cell as a result of [method set_player_position]. The new cell coordinates are provided as an argument.
signal cell_changed(new_cell: Vector3i)
## Emitted when the player enters a new room, i.e. using [method set_player_position] results in a cell that has a different assigned scene. The new scene is provided as an argument, you can use it to easily make room transitions.
signal room_changed(new_room: String)
## Emitted when a map cell was added, deleted or modified. This includes cell overrides and MapBuilder updates.
signal map_updated
signal room_assign_updated
signal theme_modified(changes: Array[String])
func _enter_tree() -> void:
var settings_path := "res://setting/MetSysSettings.tres"
if ProjectSettings.has_setting("metroidvania_system/settings_file"):
settings_path = ProjectSettings.get_setting("metroidvania_system/settings_file")
else:
ProjectSettings.set_setting("metroidvania_system/settings_file", settings_path)
if ResourceLoader.exists(settings_path):
settings = load(settings_path)
else:
settings = Settings.new()
settings.theme = load("res://addons/MetroidvaniaSystem/Themes/Exquisite/Theme.tres")
ResourceSaver.save(settings, settings_path)
settings.theme_changed.connect(_update_theme)
_update_theme()
map_data = MapData.new()
map_data.load_data()
func _update_theme():
CELL_SIZE = settings.theme.center_texture.get_size()
map_updated.emit()
func _ready() -> void:
set_physics_process(false)
## Returns a [Dictionary] containing the MetSys' runtime data, like discovered cells or stored objects. You need to serialize it yourself, e.g. using [method FileAccess.store_var].
func get_save_data() -> Dictionary:
return save_data.get_data()
## Pass it the data from [method get_save_data] to restore the saved state of MetSys. Calling this method with the default parameter will clear the data, allowing to start new game session.
func set_save_data(data := {}):
save_data = SaveData.new()
save_data.set_data(data)
func visit_cell(coords: Vector3i):
save_data.explore_cell(coords)
var previous_map := map_data.get_assigned_scene_at(Vector3i(last_player_position.x, last_player_position.y, current_layer))
var new_map := map_data.get_assigned_scene_at(coords)
if not new_map.is_empty() and not previous_map.is_empty() and new_map != previous_map:
room_changed.emit(new_map)
## Returns [code]true[/code] if the call was discovered, either mapped (if [param include_mapped] is [code]true[/code]) or explored.
func is_cell_discovered(coords: Vector3i, include_mapped := true) -> bool:
if RoomDrawer.force_mapped:
return include_mapped
if not save_data:
return true
var discovered := save_data.is_cell_discovered(coords)
return discovered == 2 or include_mapped and discovered == 1
## Returns the ratio of explored cells vs all cells. Cells created with MapBuilder are also included.
func get_explored_ratio(layer := -1):
var all: float
var discovered: float
for coords in map_data.cells:
if layer != -1 and coords.z != layer:
continue
all += 1
discovered += int(is_cell_discovered(coords, false))
return discovered / all
## Sets the position of the player to be tracked by MetSys. Automatically explores cells when crossing cell boundary and emits [signal cell_changed] and [signal room_changed] signals.
## [br][br][param position] should be position in the room's coordinates, i.e. with [code](0, 0)[/code] being the top-left corner of the scene assigned to the room (which in most cases simply equals to global position of the player).
func set_player_position(position: Vector2):
exact_player_position = position
var player_pos := Vector2i((position / settings.in_game_cell_size).floor()) + current_room.min_cell
var player_pos_3d := Vector3i(player_pos.x, player_pos.y, current_layer)
if player_pos_3d != last_player_position:
visit_cell(Vector3i(player_pos.x, player_pos.y, current_layer))
last_player_position = player_pos_3d
cell_changed.emit(player_pos_3d)
## Discovers (maps) the cell at the given [param coords]. Fails if the cell does not exist.
func discover_cell(coords: Vector3i):
assert(coords in map_data.cells)
save_data.discover_cell(coords)
map_updated.emit()
## Discovers (maps) all cells that belong to the specified group. The group ID must exist (i.e. have at least a single cell with it assigned).
func discover_cell_group(group_id: int):
assert(group_id in map_data.cell_groups)
for coords in map_data.cell_groups[group_id]:
save_data.discover_cell(coords)
map_updated.emit()
## Assigns a custom symbol to the given cell that will override the symbol set in the editor. You can assign any number of symbols and the one with the highest ID will be displayed. [param symbol_id] must be within the symbols defined in [member MapTheme.symbols].
func add_custom_marker(coords: Vector3i, symbol_id: int):
assert(symbol_id >= 0 and symbol_id < mini(MetSys.settings.theme.symbols.size(), 63))
save_data.add_custom_marker(coords, symbol_id)
## Removes a custom symbol assigned in [method add_custom_marker]. Does nothing if the given [param symbol_id] was not assigned to the cell.
func remove_custom_marker(coords: Vector3i, symbol_id: int):
save_data.remove_custom_marker(coords, symbol_id)
## Registers a storable object, i.e. object whose state is supposed to be saved, and automatically leaves a marker on the map. Useful for collectibles.
## [br][br][param object] is the object that needs storing, [param stored_callback] is the callback that will be automatically called by this method when the object was already stored (defaults to [method Node.queue_free] for nodes). [param map_marker] is the ID of the symbol that will be added to the cell. If no symbol is provided, [member MapTheme.uncollected_item_symbol] will be used.
## [br][br][b]Note:[/b] [method get_object_id] is used to determine object's ID. You can make the same object appear in multiple places if you assign them custom ID (e.g. to make a collectible that changes its location after an event). [method get_object_coords] is used to determine where the marker should appear.
func register_storable_object_with_marker(object: Object, stored_callback := Callable(), map_marker := DEFAULT_SYMBOL) -> bool:
if stored_callback.is_null():
if object is Node:
stored_callback = Callable(object, &"queue_free")
elif not object is RefCounted:
stored_callback = Callable(object, &"free")
if save_data.is_object_stored(object):
stored_callback.call()
return true
if map_marker == DEFAULT_SYMBOL:
map_marker = settings.theme.uncollected_item_symbol
object.set_meta(&"map_marker", map_marker)
elif map_marker > -1:
object.set_meta(&"map_marker", map_marker)
if save_data.register_storable_object(object) and map_marker > -1:
save_data.add_custom_marker(get_object_coords(object), map_marker)
return false
## Same as [method register_storable_object_with_marker], but doesn't assign any marker. Useful for things like buttons, breakable walls etc.
func register_storable_object(object: Object, stored_callback := Callable()) -> bool:
return register_storable_object_with_marker(object, stored_callback, -1)
## Stores the given object. Using [method register_storable_object] aftewards will call its callback. Note that this method simply registers the object as stored, it does not free it nor anything. [param map_marker] is the ID of the symbol that will be added to the cell. If no symbol is provided, [member MapTheme.collected_item_symbol] will be used.
## [br][br][b]Note:[/b] The marker will be ignored if the object was not registered with a marker.
func store_object(object: Object, map_marker := DEFAULT_SYMBOL):
save_data.store_object(object)
if object.has_meta(&"map_marker"):
save_data.remove_custom_marker(get_object_coords(object), object.get_meta(&"map_marker"))
else:
map_marker = -1
if map_marker == DEFAULT_SYMBOL:
map_marker = settings.theme.collected_item_symbol
if map_marker > -1:
save_data.add_custom_marker(get_object_coords(object), map_marker)
## Returns the game-unique ID of an object. It's used to identify instances of objects in the game's world. It can be used manually when storable objects are insufficient for whatever reason.
## [br][br]The ID is first determined from [code]object_id[/code] metadata (see [method Object.set_meta]), then using [code]_get_object_id()[/code] method and finally using a heuristic based on the current scene and node's path in scene. If the [param object] is not a [Node] and no custom ID is provided, this method returns empty string.
func get_object_id(object: Object) -> String:
if object.has_meta(&"object_id"):
return object.get_meta(&"object_id")
elif object.has_method(&"_get_object_id"):
var id: String = object._get_object_id()
object.set_meta(&"object_id", id)
return id
elif object is Node:
var id := str(object.owner.scene_file_path.get_file().get_basename(), "/", object.get_parent().name if object.get_parent() != object.owner else ".", "/", object.name)
object.set_meta(&"object_id", id)
return id
return ""
## Returns the map coordinates of an object on the scene. It can be used e.g. to place custom markers for non-storable objects.
## [br][br]Similar to [method get_object_id], the method will first use [code]object_coords[/code] metadata and [code]_get_object_coords()[/code] method. If the [param object] is a [Node], the top-left coordinate of the current scene's location on map will be used. If the object is a [CanvasItem], it will return a precise coordinate based on the object's position on the scene.
func get_object_coords(object: Object) -> Vector3i:
if object.has_meta(&"object_coords"):
return object.get_meta(&"object_coords")
elif object.has_method(&"_get_object_coords"):
var coords: Vector3i = object._get_object_coords()
object.set_meta(&"object_coords", coords)
return coords
elif object is Node:
var room_name: String = object.owner.scene_file_path.trim_prefix(settings.map_root_folder)
room_name = MetSys.map_data.scene_overrides.get(room_name, room_name)
assert(room_name in map_data.assigned_scenes)
var coords: Vector3i = map_data.assigned_scenes[room_name].front()
for vec in map_data.assigned_scenes[room_name]:
coords.x = mini(coords.x, vec.x)
coords.y = mini(coords.y, vec.y)
if object is CanvasItem:
var position: Vector2 = object.position / settings.in_game_cell_size
coords.x += int(position.x)
coords.y += int(position.y)
object.set_meta(&"object_coords", coords)
return coords
return Vector3i()
## Translates map coordinates to 2D pixel coordinates. Can be used for custom drawing on the map.
## [br][br][param relative] allows to specify precise position inside the cell, with [code](0.5, 0.5)[/code] being the cell's center. [param base_offset] is an additional offset in pixels.
func get_cell_position(coords: Vector2i, relative := Vector2(0.5, 0.5), base_offset := Vector2()) -> Vector2:
return base_offset + (Vector2(coords) + relative) * MetSys.CELL_SIZE
## Returns a cell override at position [param coords]. If it doesn't exist, it will be created (unless [param auto_create] is [code]false[/code]). A cell must exist at the given [param coords].
## [br][br]Cell overrides allow to modify any cell's data at runtime. They are included with the data returned in [method get_save_data]. Creating an override and doing any modifications with emit [signal map_updated]. The signal emitted with modifications is deferred, i.e. multiple modifications will do only a single emission, at the end of the current frame.
## [br][br]Click this method's return value for more info.
func get_cell_override(coords: Vector3i, auto_create := true) -> MapData.CellOverride:
var cell := map_data.get_cell_at(coords)
assert(cell, "Can't override non-existent cell")
var existing := cell.get_override()
if existing:
return existing
elif auto_create:
return save_data.add_cell_override(cell)
else:
push_error("No override found at %s" % coords)
return null
## Returns an override for the first cell assigned to the given [param group_id]. The group ID must exist (i.e. have at least a single cell with it assigned).
## [br][br]Useful for applying override to a group.
func get_cell_override_from_group(group_id: int, auto_create := true) -> MapData.CellOverride:
assert(group_id in map_data.cell_groups)
return get_cell_override(map_data.cell_groups[group_id].front(), auto_create)
## Removes an override created with [method get_cell_override], reverting the cell to its original appearance, and emits [signal map_updated] signal. Does nothing if the override didn't exist.
## [br][br][b]Note:[/b] If the override was created with MapBuilder, use the [code]destroy()[/code] method instead.
func remove_cell_override(coords: Vector3i):
var cell = MetSys.map_data.get_cell_at(coords)
assert(cell, "Can't remove override of non-existent cell")
if save_data.remove_cell_override(cell):
map_updated.emit()
## Returns a MapBuilder object. It can be used to create custom cells at runtime (for procedural maps etc.). The created cells can be customized with overrides, which are created automatically.
## [br][br]Click this method's return value for more info.
func get_map_builder() -> MapBuilder:
return MapBuilder.new()
## Draws a single map cell. [param canvas_item] is the [CanvasItem] responsible for drawing, [param offset] is the drawing offset in map coordinates. [param coords] is the coordinate of the map cell that's going to be drawn (does not need to exist). If [param skip_empty] is [code]false[/code], [member MapTheme.empty_space_texture] will be drawn in place of empty cells, if available. If [param use_save_data] is [code]true[/code], the discovered rooms data will be used for drawing the map.
## [br][br]Example of drawing a 3x3 minimap where center is at [code]current_cell[/code]:
## [codeblock]
## for x in range(-1, 2):
## for y in range(-1, 2):
## MetSys.draw_cell(self, Vector2i(x + 1, y + 1), Vector3i(current_cell.x + x, current_cell.y + y, MetSys.current_layer))
## [/codeblock]
## [b]Note:[/b] Drawing a cell is an expensive operation, so avoid performing it too often. You can use the [signal map_updated] signal to only redraw when necessary.
func draw_cell(canvas_item: CanvasItem, offset: Vector2, coords: Vector3i, skip_empty := false, use_save_data := true):
RoomDrawer.draw(canvas_item, offset, coords, skip_empty, map_data, save_data if use_save_data else null)
## Performs a second drawing pass after all cells were drawn, for drawing the shared borders. Only required if [member MapTheme.use_shared_borders] is enabled.
func draw_shared_borders():
RoomDrawer.draw_shared_borders()
## Performs another drawing pass after all cells were draw, for drawing the custom elements. Only required if [code]custom_element_script[/code] is assigned in MetSyS Settings.
## [br][br][param canvas_item] is the [CanvasItem] responsible for drawing. [param rect] is the portion of the world map that's going to be drawn. All elements whose rects intersect with this rectangle will be drawn. [param drawing_offset] is an offset in pixels, in case your map has a margin etc. You can draw elements from another layer or leave default [param layer] to use the current one.
func draw_custom_elements(canvas_item: CanvasItem, rect: Rect2i, drawing_offset := Vector2(), layer := current_layer):
if not settings.custom_elements or map_data.custom_elements.is_empty():
return
RoomDrawer.draw_custom_elements(canvas_item, map_data.custom_elements, drawing_offset, rect, layer)
## Creates an instance of [member MapTheme.player_location_scene] and adds it as a child of the specified [param canvas_item]. The location scene will be moved to the player's location, respecting [member MapTheme.show_exact_player_location]. [param offset] is the offset in pixels for drawing the location. Use it if your map doesn't use [code](0, 0)[/code] as origin point.
## [br][br][b]Note:[/b] The scene automatically disables processing if it's not visible, so you don't need to worry about having animations and such. They will not run in the background.
func add_player_location(canvas_item: CanvasItem, offset := Vector2()) -> Node2D:
var location_instance: Node2D = settings.theme.player_location_scene.instantiate()
location_instance.set_script(load("res://addons/MetroidvaniaSystem/Scripts/PlayerLocationInstance.gd"))
location_instance.offset = offset
canvas_item.add_child(location_instance)
return location_instance
## Returns the current cell coordinates of the player, as determined from [method set_player_position].
func get_current_coords() -> Vector3i:
return Vector3i(last_player_position.x, last_player_position.y, current_layer)
## Same as [method get_current_coords], but does not include layer.
func get_current_flat_coords() -> Vector2i:
return Vector2i(last_player_position.x, last_player_position.y)
## Returns the currently active RoomInstance object.
## [br][br]Click this method's return value for more info.
func get_current_room_instance() -> RoomInstance:
if is_instance_valid(current_room):
return current_room
return null
## Returns the name of the current room, or empty string if there is no active RoomInstance. Use together with [method get_full_room_path] to get the full path.
func get_current_room_name() -> String:
if current_room:
return current_room.room_name
else:
return ""
## Returns the full path to the provided [param room_name] scene. This method assumes that the scene is inside the base map folder.
func get_full_room_path(room_name: String) -> String:
return settings.map_root_folder.path_join(room_name)
func _add_meta(meta: StringName, value: Variant):
set_meta(meta, value)
if _meta_list.is_empty():
_cleanup_meta.call_deferred()
_meta_list.append(meta)
func _cleanup_meta():
for meta in _meta_list:
remove_meta(meta)
_meta_list.clear()

@ -1,22 +0,0 @@
extends Node2D
var offset: Vector2
var exact: bool
func _ready() -> void:
exact = MetSys.settings.theme.show_exact_player_location
func _notification(what: int) -> void:
if what == NOTIFICATION_VISIBILITY_CHANGED:
if is_visible_in_tree():
process_mode = Node.PROCESS_MODE_INHERIT
else:
process_mode = Node.PROCESS_MODE_DISABLED
func _process(delta: float) -> void:
var last_player_position_2d := Vector2(MetSys.last_player_position.x, MetSys.last_player_position.y)
var player_position := last_player_position_2d * MetSys.CELL_SIZE + MetSys.CELL_SIZE / 2
if exact:
player_position += (MetSys.exact_player_position / MetSys.settings.in_game_cell_size).posmod(1) * MetSys.CELL_SIZE - MetSys.CELL_SIZE * 0.5
position = player_position + offset

@ -1,446 +0,0 @@
extends RefCounted
static var force_mapped: bool
static func draw(canvas_item: CanvasItem, offset: Vector2, coords: Vector3i, skip_empty: bool, map_data: MetroidvaniaSystem.MapData, save_data: MetroidvaniaSystem.SaveData):
if MetSys.settings.custom_elements and not map_data.custom_elements.is_empty():
setup_custom_elements(canvas_item, offset, coords)
var cell_data := map_data.get_cell_at(coords)
if not cell_data:
if not skip_empty:
draw_empty(canvas_item, offset)
return
var discovered := 2
if save_data:
discovered = save_data.is_cell_discovered(coords)
elif force_mapped:
discovered = 1
if discovered == 0:
if not skip_empty:
draw_empty(canvas_item, offset)
return
var theme: MapTheme = MetSys.settings.theme
var ci := canvas_item.get_canvas_item()
var display_flags: int = (int(discovered == 2) * 255) | theme.mapped_display
# center
if bool(display_flags & MetroidvaniaSystem.DISPLAY_CENTER):
var room_color := cell_data.get_color()
theme.center_texture.draw(ci, offset * MetSys.CELL_SIZE, room_color if discovered == 2 else theme.mapped_center_color)
# corners
if theme.use_shared_borders:
if not MetSys.has_meta(&"shared_borders_to_draw"):
MetSys._add_meta(&"shared_borders_to_draw", {})#[Vector3i, Vector2]
MetSys._add_meta(&"shared_borders_data", {"canvas_item": canvas_item, "display_flags": display_flags})
var shared_borders_to_draw: Dictionary = MetSys.get_meta(&"shared_borders_to_draw")
shared_borders_to_draw[coords] = offset
else:
draw_regular_borders(canvas_item, offset, coords, map_data, cell_data, display_flags, discovered)
canvas_item.draw_set_transform_matrix(Transform2D())
if bool(display_flags & MetroidvaniaSystem.DISPLAY_SYMBOLS):
var symbol: int = -1
if save_data and coords in save_data.custom_markers:
var custom: int = save_data.custom_markers[coords]
for i in range(63, -1, -1):
if custom & 1 << i:
symbol = i
break
if symbol == -1:
symbol = cell_data.get_symbol()
if symbol > - 1:
if symbol >= theme.symbols.size():
push_error("Bad symbol '%s' at '%s'" % [symbol, coords])
else:
canvas_item.draw_texture(theme.symbols[symbol], offset * MetSys.CELL_SIZE + MetSys.CELL_SIZE * 0.5 - theme.symbols[symbol].get_size() / 2)
static func draw_regular_borders(canvas_item: CanvasItem, offset: Vector2, coords: Vector3i, map_data: MetroidvaniaSystem.MapData, cell_data: MetroidvaniaSystem.MapData.CellData, display_flags: int, discovered: int):
var theme: MapTheme = MetSys.settings.theme
var ci := canvas_item.get_canvas_item()
# borders
var borders: Array[int] = [-1, -1, -1, -1]
var display_outlines := bool(display_flags & MetroidvaniaSystem.DISPLAY_OUTLINE)
for i in 4:
var border: int = cell_data.get_border(i)
if not display_outlines and border == 0:
borders[i] = -1
elif not bool(display_flags & MetroidvaniaSystem.DISPLAY_BORDERS):
borders[i] = mini(border, 0 if display_outlines else -1)
else:
borders[i] = border
for i in 4:
var texture: Texture2D
var color: Color
var rotation := PI * 0.5 * i
if borders[i] == -1:
if display_outlines:
texture = get_border_texture(theme, -1, i)
color = Color.WHITE
else:
var border: int = borders[i]
if not bool(display_flags & MetroidvaniaSystem.DISPLAY_BORDERS):
border = 0
texture = get_border_texture(theme, border, i)
if discovered == 2:
color = cell_data.get_border_color(i)
else:
color = theme.mapped_border_color
if not texture:
continue
canvas_item.draw_set_transform(offset * MetSys.CELL_SIZE + MetSys.CELL_SIZE * 0.5, rotation, Vector2.ONE)
match i:
MetroidvaniaSystem.R, MetroidvaniaSystem.L:
texture.draw(ci, -texture.get_size() / 2 + Vector2.RIGHT * (MetSys.CELL_SIZE.x * 0.5 - texture.get_width() / 2), color)
MetroidvaniaSystem.D, MetroidvaniaSystem.U:
texture.draw(ci, -texture.get_size() / 2 + Vector2.RIGHT * (MetSys.CELL_SIZE.y * 0.5 - texture.get_width() / 2), color)
if not display_outlines:
return
# outer corner
for i in 4:
var j := rotate(i)
if borders[i] == -1 or borders[j] == -1:
continue
var texture: Texture2D = theme.outer_corner
var corner_color: Color
if discovered == 2:
corner_color = get_shared_color(cell_data.get_border_color(i), cell_data.get_border_color(j), theme.default_border_color)
else:
corner_color = theme.mapped_border_color
canvas_item.draw_set_transform(offset * MetSys.CELL_SIZE + MetSys.CELL_SIZE * 0.5, PI * 0.5 * i, Vector2.ONE)
var corner_offset := -texture.get_size()
if theme.use_shared_borders:
corner_offset /= 2
match i:
MetroidvaniaSystem.R, MetroidvaniaSystem.L:
texture.draw(ci, MetSys.CELL_SIZE * 0.5 + corner_offset, corner_color)
MetroidvaniaSystem.D, MetroidvaniaSystem.U:
texture.draw(ci, Vector2(MetSys.CELL_SIZE.y, MetSys.CELL_SIZE.x) * 0.5 + corner_offset, corner_color)
# inner corner
for i in 4:
var j := rotate(i)
if borders[i] != -1 or borders[j] != -1:
continue
var neighbor_room := get_neighbor(map_data, coords, map_data.FWD[i] + map_data.FWD[j])
if neighbor_room:
if neighbor_room.borders[opposite(i)] == -1 and neighbor_room.borders[opposite(j)] == -1:
continue
var texture: Texture2D = theme.inner_corner
var color1 := get_neighbor(map_data, coords, map_data.FWD[i]).get_border_color(rotate(i))
var color2 := get_neighbor(map_data, coords, map_data.FWD[j]).get_border_color(rotate(j, -1))
var corner_color := get_shared_color(color1, color2, theme.default_border_color)
canvas_item.draw_set_transform(offset * MetSys.CELL_SIZE + MetSys.CELL_SIZE * 0.5, PI * 0.5 * i, Vector2.ONE)
var corner_offset := -texture.get_size()
if theme.use_shared_borders:
corner_offset /= 2
match i:
MetroidvaniaSystem.R, MetroidvaniaSystem.L:
theme.inner_corner.draw(ci, MetSys.CELL_SIZE * 0.5 + corner_offset, corner_color)
MetroidvaniaSystem.D, MetroidvaniaSystem.U:
theme.inner_corner.draw(ci, Vector2(MetSys.CELL_SIZE.y, MetSys.CELL_SIZE.x) * 0.5 + corner_offset, corner_color)
static func draw_shared_borders():
assert(MetSys.settings.theme.use_shared_borders, "This function requires shared borders to be enabled.")
if not MetSys.has_meta(&"shared_borders_to_draw"):
return
var theme: MapTheme = MetSys.settings.theme
var shared_borders_to_draw: Dictionary = MetSys.get_meta(&"shared_borders_to_draw")
var shared_borders_data: Dictionary = MetSys.get_meta(&"shared_borders_data")
var shared_borders_colors: Dictionary#[Vector3i, Color]
var shared_corners_data: Dictionary#[Vector3i, int]
var shared_corners_colors: Dictionary#[Vector3i, Color]
var display_flags: int = shared_borders_data["display_flags"]
var canvas_item: CanvasItem = shared_borders_data["canvas_item"]
var ci := canvas_item.get_canvas_item()
var additional_corners: Dictionary#[Vector3i, Vector2]
for coords in shared_borders_to_draw:
for x in range (-1, 1):
for y in range(-1, 1):
var coords2: Vector3i = coords + Vector3i(x, y, 0)
if not coords2 in shared_borders_to_draw:
additional_corners[coords2] = shared_borders_to_draw[coords] + Vector2(x, y)
shared_borders_to_draw.merge(additional_corners)
for coords in shared_borders_to_draw:
var corner_data := 0
corner_data |= (1 << 0) * (get_corner_bit(coords, MetroidvaniaSystem.R) | get_corner_bit(coords + Vector3i(1, 0, 0), MetroidvaniaSystem.L))
corner_data |= (1 << 1) * (get_corner_bit(coords, MetroidvaniaSystem.D) | get_corner_bit(coords + Vector3i(0, 1, 0), MetroidvaniaSystem.U))
corner_data |= (1 << 2) * (get_corner_bit(coords + Vector3i(0, 1, 0), MetroidvaniaSystem.R) | get_corner_bit(coords + Vector3i(1, 1, 0), MetroidvaniaSystem.L))
corner_data |= (1 << 3) * (get_corner_bit(coords + Vector3i(1, 0, 0), MetroidvaniaSystem.D) | get_corner_bit(coords + Vector3i(1, 1, 0), MetroidvaniaSystem.U))
if theme.is_unicorner():
corner_data = signi(corner_data) * 15
var use_default: bool
var color_blend: Array[Color]
for i in 4:
var color: Color
if i < 2:
color = get_shared_border_color(coords, i)
else:
color = get_shared_border_color(coords + Vector3i(1, 1, 0), i)
if color == theme.default_border_color:
use_default = true
elif color.a > 0:
color_blend.append(color)
shared_corners_data[coords] = corner_data
if not color_blend.is_empty():
var ratio: float = 1.0 / color_blend.size()
shared_corners_colors[coords] = color_blend.reduce(func(final: Color, current: Color) -> Color: return final.lerp(current, ratio))
elif use_default:
shared_corners_colors[coords] = theme.default_border_color
var border_data: Array[int] = [-1, -1]
var border_colors: Array[Color] = [Color(), Color()]
for i in 2:
var fwd := Vector3i(MetroidvaniaSystem.MapData.FWD[i].x, MetroidvaniaSystem.MapData.FWD[i].y, 0)
border_data[i] = maxi(get_border_at(coords, i), get_border_at(coords + fwd, opposite(i)))
border_colors[i] = get_shared_border_color(coords, i)
shared_borders_data[coords] = border_data
shared_borders_colors[coords] = border_colors
# borders
for coords in shared_borders_to_draw:
var borders: Array[int] = shared_borders_data[coords]
for i in 2:
var texture: Texture2D
var color: Color
var rotation := PI * 0.5 * i
if borders[i] == -1:
texture = get_border_texture(theme, -1, i)
color = Color.WHITE
else:
var border: int = borders[i]
if not bool(display_flags & MetroidvaniaSystem.DISPLAY_BORDERS):
border = 0
texture = get_border_texture(theme, border, i)
color = shared_borders_colors[coords][i]
if not texture:
continue
canvas_item.draw_set_transform(shared_borders_to_draw[coords] * MetSys.CELL_SIZE + MetSys.CELL_SIZE * 0.5, rotation, Vector2.ONE)
match i:
MetroidvaniaSystem.R, MetroidvaniaSystem.L:
texture.draw(ci, -texture.get_size() / 2 + Vector2.RIGHT * (MetSys.CELL_SIZE.x * 0.5), color)
MetroidvaniaSystem.D, MetroidvaniaSystem.U:
texture.draw(ci, -texture.get_size() / 2 + Vector2.RIGHT * (MetSys.CELL_SIZE.y * 0.5), color)
# corners
for coords in shared_borders_to_draw:
var offset: Vector2 = shared_borders_to_draw[coords]
var corner_data: int = shared_corners_data[coords]
var corner_type = signi(corner_data & 1) + signi(corner_data & 2) + signi(corner_data & 4) + signi(corner_data & 8)
var corner_texture: Texture2D
var corner_rotation := -1
match corner_type:
1:
corner_texture = theme.u_corner
match corner_data:
1:
corner_rotation = 0
2:
corner_rotation = 3
4:
corner_rotation = 2
8:
corner_rotation = 1
2:
corner_texture = theme.l_corner
match corner_data:
3:
corner_rotation = 3
6:
corner_rotation = 2
9:
corner_rotation = 0
12:
corner_rotation = 1
3:
corner_texture = theme.t_corner
match corner_data:
7:
corner_rotation = 1
11:
corner_rotation = 2
13:
corner_rotation = 3
14:
corner_rotation = 0
4:
corner_texture = theme.cross_corner
corner_rotation = 0
if corner_rotation == -1 or not corner_texture:
continue
canvas_item.draw_set_transform(offset * MetSys.CELL_SIZE + MetSys.CELL_SIZE, PI * 0.5 * corner_rotation, Vector2.ONE)
corner_texture.draw(canvas_item.get_canvas_item(), -corner_texture.get_size() / 2, shared_corners_colors[coords])
MetSys.remove_meta(&"shared_borders_to_draw")
MetSys.remove_meta(&"shared_borders_data")
canvas_item.draw_set_transform_matrix(Transform2D())
static func setup_custom_elements(canvas_item: CanvasItem, offset: Vector2, coords: Vector3i):
var custom_element_data: Dictionary
if not MetSys.has_meta(&"custom_elements_data"):
custom_element_data["canvas_item"] = canvas_item
MetSys._add_meta(&"custom_elements_data", custom_element_data)
static func draw_custom_elements(canvas_item: CanvasItem, elements: Dictionary, base_offset: Vector2, rect: Rect2i, layer: int):
var element_manager: MetroidvaniaSystem.CustomElementManager = MetSys.settings.custom_elements
var already_drawn: Array[Dictionary]
for y in rect.size.y:
for x in rect.size.y:
var pos := rect.position + Vector2i(x, y)
for coords in elements:
if coords.z != layer:
continue
var element: Dictionary = elements[coords]
if element in already_drawn:
continue
var elerect := Rect2i(coords.x, coords.y, element["size"].x, element["size"].y)
if elerect.has_point(pos):
element_manager.draw_element(canvas_item, coords, element.name, (base_offset + Vector2(coords.x, coords.y) - Vector2(pos) + Vector2(x, y)) * MetSys.CELL_SIZE, Vector2(element.size) * MetSys.CELL_SIZE, element.data)
already_drawn.append(element)
static func get_border_at(coords: Vector3i, idx: int) -> int:
var cell_data = get_discovered_cell_at(coords)
if not cell_data:
return -1
return cell_data.get_border(idx)
static func get_corner_bit(coords: Vector3i, idx: int) -> int:
return int(get_border_at(coords, idx) > -1)
static func get_shared_border_color(coords: Vector3i, idx: int) -> Color:
var fwd := Vector3i(MetroidvaniaSystem.MapData.FWD[idx].x, MetroidvaniaSystem.MapData.FWD[idx].y, 0)
var color := Color.TRANSPARENT
var cell_data = get_discovered_cell_at(coords)
if cell_data:
color = cell_data.get_border_color(idx)
cell_data = get_discovered_cell_at(coords + fwd)
if cell_data:
if color.a > 0:
color = get_shared_color(color, cell_data.get_border_color(opposite(idx)), MetSys.settings.theme.default_border_color)
else:
color = cell_data.get_border_color(opposite(idx))
return color
static func get_discovered_cell_at(coords: Vector3i) -> MetroidvaniaSystem.MapData.CellData:
var cell_data = MetSys.map_data.get_cell_at(coords)
if cell_data and MetSys.is_cell_discovered(coords):
return cell_data
return null
static func get_shared_color(color1: Color, color2: Color, default: Color) -> Color:
if color1 == default:
return color2
elif color2 == default:
return color1
return color1.lerp(color2, 0.5)
static func get_border_texture(theme: MapTheme, idx: int, direction: int) -> Texture2D:
var texture_name: StringName
if theme.rectangle:
if direction == MetroidvaniaSystem.R or direction == MetroidvaniaSystem.L:
match idx:
-1:
texture_name = &"vertical_separator"
0:
texture_name = &"vertical_wall"
1:
texture_name = &"vertical_passage"
2:
texture_name = &"vertical_borders"
else:
match idx:
-1:
texture_name = &"horizontal_separator"
0:
texture_name = &"horizontal_wall"
1:
texture_name = &"horizontal_passage"
_:
texture_name = &"vertical_borders"
else:
match idx:
-1:
texture_name = &"separator"
0:
texture_name = &"wall"
1:
texture_name = &"passage"
_:
texture_name = &"borders"
if idx >= 2:
return theme.get(texture_name)[idx - 2]
else:
return theme.get(texture_name)
static func draw_empty(canvas_item: CanvasItem, offset: Vector2):
var theme: MapTheme = MetSys.settings.theme
if theme.empty_space_texture:
var ci := canvas_item.get_canvas_item()
theme.empty_space_texture.draw(ci, offset * MetSys.CELL_SIZE, Color.WHITE)
static func get_neighbor(map_data: MetroidvaniaSystem.MapData, coords: Vector3i, offset: Vector2i) -> MetroidvaniaSystem.MapData.CellData:
var neighbor: Vector2i = Vector2i(coords.x, coords.y) + offset
return map_data.get_cell_at(Vector3i(neighbor.x, neighbor.y, coords.z))
static func rotate(i: int, amount := 1) -> int:
return (i + amount) % 4
static func opposite(i: int) -> int:
return (i + 2) % 4

@ -1,73 +0,0 @@
@tool
## A node representing MetSys room.
##
## RoomInstance is a node that allows for integration between scenes and MetSys database. In editor it will provide drawing for room bounds and in game it provides some helper methods for interacting with the map data. [code]RoomInstance.tscn[/code] scene is provided for convenience. You should add it to every scene used as MetSys room. It's recommended to put the node at [code](0, 0)[/code] global coordinates.
extends Node3D
var GRID_COLOR: Color
var GRID_PASSAGE_COLOR: Color
var cells: Array[Vector3i]
var initialized: bool
var room_name: String
var min_cell := Vector2i(999999, 999999)
var max_cell := Vector2i(-999999, -999999)
var layer: int
func _enter_tree() -> void:
if not Engine.is_editor_hint():
MetSys.current_room = self
if initialized:
return
initialized = true
if Engine.is_editor_hint():
MetSys.room_assign_updated.connect(_update_assigned_scene)
var theme: Theme = load("res://addons/MetroidvaniaSystem/Database/DatabaseTheme.tres")
GRID_COLOR = theme.get_color(&"scene_cell_border", &"MetSys")
GRID_PASSAGE_COLOR = theme.get_color(&"scene_room_exit", &"MetSys")
_update_assigned_scene()
func _exit_tree() -> void:
if MetSys.current_room == self:
MetSys.current_room = null
func _update_assigned_scene():
var owner_node := owner if owner != null else self
room_name = owner_node.scene_file_path.trim_prefix(MetSys.settings.map_root_folder)
cells = MetSys.map_data.get_cells_assigned_to(room_name)
if cells.is_empty():
return
layer = cells[0].z
for p in cells:
min_cell.x = mini(min_cell.x, p.x)
min_cell.y = mini(min_cell.y, p.y)
max_cell.x = maxi(max_cell.x, p.x)
max_cell.y = maxi(max_cell.y, p.y)
## Returns the full size of this room, based on the cells and [code]in_game_cell_size[/code] defined in MetSys Settings.
func get_size() -> Vector2:
return Vector2(max_cell - min_cell + Vector2i.ONE) * MetSys.settings.in_game_cell_size
## Returns this rooms cells in local coordinates, i.e. with [code](0, 0)[/code] being the top-left cell.
func get_local_cells() -> Array[Vector2i]:
var ret: Array[Vector2i]
ret.assign(cells.map(func(coords: Vector3i) -> Vector2i:
return Vector2i(coords.x - min_cell.x, coords.y - min_cell.y)))
return ret
## Returns the top-left cell's flat coordinates within the room's rectangle.
func get_base_coords() -> Vector2i:
return min_cell
## Returns the bottom-right cell's flat coordinates within the room's rectangle.
func get_end_coords() -> Vector2i:
return max_cell
## Returns the room's layer.
func get_layer() -> int:
return layer

@ -1,95 +0,0 @@
extends RefCounted
const SIMPLE_STORABLE_PROPERTIES: Array[StringName] = [&"discovered_cells", &"registered_objects", &"stored_objects", &"custom_markers"]
var discovered_cells: Dictionary#[Vector3i, int]
var registered_objects: Dictionary#[String, bool]
var stored_objects: Dictionary#[String, bool]
var custom_markers: Dictionary#[Vector3i, int]
var cell_overrides: Dictionary#[CellData, CellOverride]
func discover_cell(coords: Vector3i):
if discovered_cells.get(coords, 0) < 1:
discovered_cells[coords] = 1
func explore_cell(coords: Vector3i):
if discovered_cells.get(coords, 0) < 2:
discovered_cells[coords] = 2
MetSys.map_updated.emit()
func is_cell_discovered(coords: Vector3i) -> int:
return discovered_cells.get(coords, 0)
func register_storable_object(object: Object) -> bool:
var id: String = MetSys.get_object_id(object)
if not id in registered_objects:
registered_objects[id] = true
return true
return false
func store_object(object: Object):
var id: String = MetSys.get_object_id(object)
assert(id in registered_objects)
assert(not id in stored_objects)
registered_objects.erase(id)
stored_objects[id] = true
func is_object_stored(object: Object) -> bool:
var id: String = MetSys.get_object_id(object)
return id in stored_objects
func add_cell_override(room: MetroidvaniaSystem.MapData.CellData) -> MetroidvaniaSystem.MapData.CellOverride:
if not room in cell_overrides:
cell_overrides[room] = MetroidvaniaSystem.MapData.CellOverride.new(room)
return cell_overrides[room]
func remove_cell_override(room: MetroidvaniaSystem.MapData.CellData) -> bool:
var override := cell_overrides.get(room)
if override:
if override.custom_cell_coords != Vector3i.MAX:
push_error("Can't delete override of a custom cell. Use destroy() instead.")
return false
override._cleanup_assigned_scene()
cell_overrides.erase(room)
return override != null
func add_custom_marker(coords: Vector3i, symbol: int):
if not coords in custom_markers:
custom_markers[coords] = 0
custom_markers[coords] |= 1 << symbol
MetSys.map_updated.emit()
func remove_custom_marker(coords: Vector3i, symbol: int):
if not coords in custom_markers:
return
custom_markers[coords] &= ~(1 << symbol)
if custom_markers[coords] == 0:
custom_markers.erase(coords)
MetSys.map_updated.emit()
func get_data() -> Dictionary:
var data: Dictionary
for property in SIMPLE_STORABLE_PROPERTIES:
data[property] = get(property)
data[&"cell_overrides"] = cell_overrides.keys().map(func(room):
var coords: Vector3i = MetSys.map_data.cells.find_key(room)
return cell_overrides[room]._get_override_string(coords))
return data
func set_data(data: Dictionary):
if data.is_empty():
return
for property in SIMPLE_STORABLE_PROPERTIES:
set(property, data[property])
for override_string in data.cell_overrides:
var override: MetroidvaniaSystem.MapData.CellOverride = MetroidvaniaSystem.MapData.CellOverride.load_from_line(override_string)
cell_overrides[override.original_room] = override

@ -1,40 +0,0 @@
@tool
extends Resource
## The theme used for drawing map cells.
@export var theme: MapTheme:
set(t):
if t == theme:
return
theme = t
theme_changed.emit()
## The root directory where room scenes are located. All scenes used for MetSys editor should be within this folder or its subfolders.
@export_dir var map_root_folder: String = "res://"
@export_dir var map_setting_folder: String = "res://"
## The size of a map cell within an in-game room, i.e. this is the real game size of your map cells. Usually equal to the screen size.
@export var in_game_cell_size := Vector2(1152, 648)
@export var collectible_list: Array[Dictionary]
## The script that determines the custom elements available in the Custom Elements map editor mode. It should inherit [code]CustomElementManager.gd[/code], refer to that class' documentation on how to use it.
@export var custom_element_script: Script:
set(elements):
if elements == custom_element_script:
return
custom_element_script = elements
if elements:
custom_elements = elements.new()
else:
custom_elements = null
custom_elements_changed.emit()
var custom_elements: MetroidvaniaSystem.CustomElementManager
signal theme_changed
signal custom_elements_changed
func _validate_property(property: Dictionary) -> void:
if property.name == "collectible_list":
property.usage &= ~PROPERTY_USAGE_EDITOR

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://q4al3ip12dm1"
path="res://.godot/imported/BorderPassage.png-633225ee718470c51dbf8f1eadaceebc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/AoS/BorderPassage.png"
dest_files=["res://.godot/imported/BorderPassage.png-633225ee718470c51dbf8f1eadaceebc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 767 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://citg7br0tcbix"
path="res://.godot/imported/BorderWall.png-b3eebcdcaea75a2afb38071c89ec8187.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/AoS/BorderWall.png"
dest_files=["res://.godot/imported/BorderWall.png-b3eebcdcaea75a2afb38071c89ec8187.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://gumwhy8sgmcu"
path="res://.godot/imported/Corner.png-10c3b8f9b1a8940081842cbc25b22edb.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/AoS/Corner.png"
dest_files=["res://.godot/imported/Corner.png-10c3b8f9b1a8940081842cbc25b22edb.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cg88cixacoi6n"
path="res://.godot/imported/PlayerLocation.png-5bb116814673a6a1b60e3356eb77b54c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/AoS/PlayerLocation.png"
dest_files=["res://.godot/imported/PlayerLocation.png-5bb116814673a6a1b60e3356eb77b54c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

@ -1,74 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://b1hnpnqaqqcds"]
[ext_resource type="Texture2D" uid="uid://cg88cixacoi6n" path="res://addons/MetroidvaniaSystem/Themes/AoS/PlayerLocation.png" id="1_6xqmr"]
[sub_resource type="Animation" id="Animation_a76ue"]
resource_name = "Animate"
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Color(1, 1, 1, 0.0627451), Color(1, 1, 1, 1)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector2(1, 1), Vector2(1e-05, 1e-05)]
}
[sub_resource type="Animation" id="Animation_4lnw3"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 0.0627451)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(1, 1)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_qhx6n"]
_data = {
"Animate": SubResource("Animation_a76ue"),
"RESET": SubResource("Animation_4lnw3")
}
[node name="PlayerLocation" type="Sprite2D"]
modulate = Color(1, 1, 1, 0.0627451)
texture = ExtResource("1_6xqmr")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
autoplay = "Animate"
libraries = {
"": SubResource("AnimationLibrary_qhx6n")
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 796 B

@ -1,30 +0,0 @@
[gd_resource type="Resource" script_class="MapTheme" load_steps=7 format=3 uid="uid://bnbbd2mcri1tv"]
[ext_resource type="Texture2D" uid="uid://gumwhy8sgmcu" path="res://addons/MetroidvaniaSystem/Themes/AoS/Corner.png" id="1_1n1yf"]
[ext_resource type="PackedScene" uid="uid://b1hnpnqaqqcds" path="res://addons/MetroidvaniaSystem/Themes/AoS/PlayerLocation.tscn" id="3_bjiwt"]
[ext_resource type="Texture2D" uid="uid://da3bit4f5alc4" path="res://addons/MetroidvaniaSystem/Themes/AoS/RoomFill.png" id="4_0del7"]
[ext_resource type="Texture2D" uid="uid://q4al3ip12dm1" path="res://addons/MetroidvaniaSystem/Themes/AoS/BorderPassage.png" id="5_n3yba"]
[ext_resource type="Texture2D" uid="uid://citg7br0tcbix" path="res://addons/MetroidvaniaSystem/Themes/AoS/BorderWall.png" id="6_dpo7t"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/MapTheme.gd" id="7_hynsb"]
[resource]
script = ExtResource("7_hynsb")
center_texture = ExtResource("4_0del7")
player_location_scene = ExtResource("3_bjiwt")
show_exact_player_location = false
mapped_display = 7
use_shared_borders = true
default_center_color = Color(0, 0, 0.878431, 1)
mapped_center_color = Color(0.25098, 0.25098, 0.25098, 1)
default_border_color = Color(1, 1, 1, 1)
mapped_border_color = Color(0.533333, 0.533333, 0.533333, 1)
symbols = Array[Texture2D]([])
uncollected_item_symbol = -1
collected_item_symbol = -1
wall = ExtResource("6_dpo7t")
passage = ExtResource("5_n3yba")
borders = Array[Texture2D]([])
u_corner = ExtResource("1_1n1yf")
l_corner = ExtResource("1_1n1yf")
t_corner = ExtResource("1_1n1yf")
cross_corner = ExtResource("1_1n1yf")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dlhoaflaw78ms"
path="res://.godot/imported/BorderPassage.png-9267f5e7854f25956faac66e32244104.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/BS/BorderPassage.png"
dest_files=["res://.godot/imported/BorderPassage.png-9267f5e7854f25956faac66e32244104.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 776 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bfhk3kvl3cx6t"
path="res://.godot/imported/BorderPassageHorizontal.png-c5365caa5938f8ef2ff19b0f85764346.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/BS/BorderPassageHorizontal.png"
dest_files=["res://.godot/imported/BorderPassageHorizontal.png-c5365caa5938f8ef2ff19b0f85764346.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 767 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cp12io0ko3ari"
path="res://.godot/imported/BorderWall.png-26fbbf8b28dea53d162e4509b0078129.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/BS/BorderWall.png"
dest_files=["res://.godot/imported/BorderWall.png-26fbbf8b28dea53d162e4509b0078129.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bixscdbqbbnqm"
path="res://.godot/imported/BorderWallHorizontal.png-8a03585c3fec3e27ec36d9209e60ddb0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/BS/BorderWallHorizontal.png"
dest_files=["res://.godot/imported/BorderWallHorizontal.png-8a03585c3fec3e27ec36d9209e60ddb0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 780 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dp67rmhfarvuw"
path="res://.godot/imported/Corner.png-3b270bb0bc68b65f38a41512dcd55729.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/BS/Corner.png"
dest_files=["res://.godot/imported/Corner.png-3b270bb0bc68b65f38a41512dcd55729.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://rwf0o6emuvco"
path="res://.godot/imported/PlayerLocation.png-23939cf1e6de1a78232f9845375f45f9.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/BS/PlayerLocation.png"
dest_files=["res://.godot/imported/PlayerLocation.png-23939cf1e6de1a78232f9845375f45f9.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

@ -1,6 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://cq5af3a4hpkmc"]
[ext_resource type="Texture2D" uid="uid://rwf0o6emuvco" path="res://addons/MetroidvaniaSystem/Themes/BS/PlayerLocation.png" id="1_ky7bq"]
[node name="PlayerLocation" type="Sprite2D"]
texture = ExtResource("1_ky7bq")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://danai55i8ghuq"
path="res://.godot/imported/RoomFill.png-24f0edf089b5b86a6e70f860d60f551c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/BS/RoomFill.png"
dest_files=["res://.godot/imported/RoomFill.png-24f0edf089b5b86a6e70f860d60f551c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

@ -1,35 +0,0 @@
[gd_resource type="Resource" script_class="MapTheme" load_steps=9 format=3 uid="uid://dtpk6t0nnpjb"]
[ext_resource type="Texture2D" uid="uid://bfhk3kvl3cx6t" path="res://addons/MetroidvaniaSystem/Themes/BS/BorderPassageHorizontal.png" id="1_fsnn7"]
[ext_resource type="Texture2D" uid="uid://bixscdbqbbnqm" path="res://addons/MetroidvaniaSystem/Themes/BS/BorderWallHorizontal.png" id="2_gohjd"]
[ext_resource type="Texture2D" uid="uid://dp67rmhfarvuw" path="res://addons/MetroidvaniaSystem/Themes/BS/Corner.png" id="3_junke"]
[ext_resource type="PackedScene" uid="uid://cq5af3a4hpkmc" path="res://addons/MetroidvaniaSystem/Themes/BS/PlayerLocation.tscn" id="3_y5832"]
[ext_resource type="Texture2D" uid="uid://danai55i8ghuq" path="res://addons/MetroidvaniaSystem/Themes/BS/RoomFill.png" id="4_4g5i5"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/MapTheme.gd" id="7_2ip2q"]
[ext_resource type="Texture2D" uid="uid://dlhoaflaw78ms" path="res://addons/MetroidvaniaSystem/Themes/BS/BorderPassage.png" id="8_fo8f6"]
[ext_resource type="Texture2D" uid="uid://cp12io0ko3ari" path="res://addons/MetroidvaniaSystem/Themes/BS/BorderWall.png" id="9_c2e7j"]
[resource]
script = ExtResource("7_2ip2q")
center_texture = ExtResource("4_4g5i5")
player_location_scene = ExtResource("3_y5832")
show_exact_player_location = true
mapped_display = 7
use_shared_borders = true
default_center_color = Color(0, 0.368627, 0.996078, 1)
mapped_center_color = Color(0.25098, 0.25098, 0.25098, 1)
default_border_color = Color(1, 1, 1, 1)
mapped_border_color = Color(1, 1, 1, 1)
symbols = Array[Texture2D]([])
uncollected_item_symbol = -1
collected_item_symbol = -1
vertical_wall = ExtResource("9_c2e7j")
horizontal_wall = ExtResource("2_gohjd")
vertical_passage = ExtResource("8_fo8f6")
horizontal_passage = ExtResource("1_fsnn7")
vertical_borders = Array[Texture2D]([])
horizontal_borders = Array[Texture2D]([])
u_corner = ExtResource("3_junke")
l_corner = ExtResource("3_junke")
t_corner = ExtResource("3_junke")
cross_corner = ExtResource("3_junke")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 924 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b8ccoibsf323o"
path="res://.godot/imported/BorderPassage.png-0014d135952d67eb8309d6d7ed57443f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderPassage.png"
dest_files=["res://.godot/imported/BorderPassage.png-0014d135952d67eb8309d6d7ed57443f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://6lyqjfj3vsy7"
path="res://.godot/imported/BorderPassageHorizontal.png-f9924ee9207cc35f2ff1a8237bef6b88.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderPassageHorizontal.png"
dest_files=["res://.godot/imported/BorderPassageHorizontal.png-f9924ee9207cc35f2ff1a8237bef6b88.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 767 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://xn6gipdberdc"
path="res://.godot/imported/BorderWall.png-62cc1a22fdc4f3da3329cb0764e89558.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderWall.png"
dest_files=["res://.godot/imported/BorderWall.png-62cc1a22fdc4f3da3329cb0764e89558.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 832 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c5wg3is8k5pux"
path="res://.godot/imported/BorderWallHorizontal.png-a151ba4bbdf139b3a508f2d33464c8be.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderWallHorizontal.png"
dest_files=["res://.godot/imported/BorderWallHorizontal.png-a151ba4bbdf139b3a508f2d33464c8be.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://rgchhbw4jh7f"
path="res://.godot/imported/CornerInner.png-c52d89f0e905827e58f95c8e77d169fc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/CornerInner.png"
dest_files=["res://.godot/imported/CornerInner.png-c52d89f0e905827e58f95c8e77d169fc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 956 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dihccrjj2ehi4"
path="res://.godot/imported/CornerOuter.png-aadab3438335cc00d2d7825b4169097e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/CornerOuter.png"
dest_files=["res://.godot/imported/CornerOuter.png-aadab3438335cc00d2d7825b4169097e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://208uoowofggq"
path="res://.godot/imported/Empty.png-5c5b7398b3c73e9dac69098338edeff8.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/Empty.png"
dest_files=["res://.godot/imported/Empty.png-5c5b7398b3c73e9dac69098338edeff8.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://u3h7efr7bfkv"
path="res://.godot/imported/PlayerLocation.png-db89c3dd938814d3cdaf16f8d83e4f4c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/PlayerLocation.png"
dest_files=["res://.godot/imported/PlayerLocation.png-db89c3dd938814d3cdaf16f8d83e4f4c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

@ -1,53 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://brmrbsa4ongaw"]
[ext_resource type="Texture2D" uid="uid://u3h7efr7bfkv" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/PlayerLocation.png" id="1_8k1h2"]
[sub_resource type="Animation" id="Animation_ff42g"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
[sub_resource type="Animation" id="Animation_2cmdj"]
resource_name = "Spin"
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, 6.28319]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_dyruj"]
_data = {
"RESET": SubResource("Animation_ff42g"),
"Spin": SubResource("Animation_2cmdj")
}
[node name="PlayerLocation" type="Sprite2D"]
texture_filter = 1
texture = ExtResource("1_8k1h2")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
autoplay = "Spin"
playback_speed = 0.5
libraries = {
"": SubResource("AnimationLibrary_dyruj")
}
[node name="VisibleOnScreenEnabler2D" type="VisibleOnScreenEnabler2D" parent="."]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bxs8cn6khov7j"
path="res://.godot/imported/RoomFill.png-a2c5cfa768a806eaffa0be05b808df2d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/RoomFill.png"
dest_files=["res://.godot/imported/RoomFill.png-a2c5cfa768a806eaffa0be05b808df2d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b1hy1w1k1asap"
path="res://.godot/imported/RoomSeparator.png-fba7149bc653105012a4b989a09fd2c7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/RoomSeparator.png"
dest_files=["res://.godot/imported/RoomSeparator.png-fba7149bc653105012a4b989a09fd2c7.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ljjnp6f81hm"
path="res://.godot/imported/Collected.png-70d99c06085090377e196418cee66c19.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Collected.png"
dest_files=["res://.godot/imported/Collected.png-70d99c06085090377e196418cee66c19.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bcuwpuvv6bpsf"
path="res://.godot/imported/Collectible.png-9af2468e6f0024e693f1577ca821a870.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Collectible.png"
dest_files=["res://.godot/imported/Collectible.png-9af2468e6f0024e693f1577ca821a870.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dvp5dov6wsh2m"
path="res://.godot/imported/Portal.png-bff52835ac3177f5a3115b1a5554c998.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Portal.png"
dest_files=["res://.godot/imported/Portal.png-bff52835ac3177f5a3115b1a5554c998.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bc0o6ot1koste"
path="res://.godot/imported/Ring.png-531ccf1f93cfc7df1edda7c73e3f0897.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Ring.png"
dest_files=["res://.godot/imported/Ring.png-531ccf1f93cfc7df1edda7c73e3f0897.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

@ -1,43 +0,0 @@
[gd_resource type="Resource" script_class="MapTheme" load_steps=16 format=3 uid="uid://bt6s3nd8xo6b"]
[ext_resource type="Texture2D" uid="uid://6lyqjfj3vsy7" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderPassageHorizontal.png" id="1_hth3y"]
[ext_resource type="Texture2D" uid="uid://c5wg3is8k5pux" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderWallHorizontal.png" id="2_tawyw"]
[ext_resource type="Texture2D" uid="uid://208uoowofggq" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/Empty.png" id="2_tsiva"]
[ext_resource type="Texture2D" uid="uid://rgchhbw4jh7f" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/CornerInner.png" id="3_4qbbp"]
[ext_resource type="PackedScene" uid="uid://brmrbsa4ongaw" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/PlayerLocation.tscn" id="3_mk7eh"]
[ext_resource type="Texture2D" uid="uid://bxs8cn6khov7j" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/RoomFill.png" id="4_2clky"]
[ext_resource type="Texture2D" uid="uid://dihccrjj2ehi4" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/CornerOuter.png" id="4_xtd8s"]
[ext_resource type="Texture2D" uid="uid://b8ccoibsf323o" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderPassage.png" id="5_sn1if"]
[ext_resource type="Texture2D" uid="uid://b1hy1w1k1asap" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/RoomSeparator.png" id="6_f6pnw"]
[ext_resource type="Texture2D" uid="uid://xn6gipdberdc" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/BorderWall.png" id="7_eifkn"]
[ext_resource type="Script" path="res://addons/MetroidvaniaSystem/Scripts/MapTheme.gd" id="8_gh637"]
[ext_resource type="Texture2D" uid="uid://dvp5dov6wsh2m" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Portal.png" id="9_3gfe5"]
[ext_resource type="Texture2D" uid="uid://bc0o6ot1koste" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Ring.png" id="9_c11cy"]
[ext_resource type="Texture2D" uid="uid://bcuwpuvv6bpsf" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Collectible.png" id="11_o6efd"]
[ext_resource type="Texture2D" uid="uid://ljjnp6f81hm" path="res://addons/MetroidvaniaSystem/Themes/Exquisite/Symbols/Collected.png" id="13_hjnmv"]
[resource]
script = ExtResource("8_gh637")
center_texture = ExtResource("4_2clky")
empty_space_texture = ExtResource("2_tsiva")
player_location_scene = ExtResource("3_mk7eh")
show_exact_player_location = true
mapped_display = 11
use_shared_borders = false
default_center_color = Color(0.5, 0.25, 0.25, 1)
mapped_center_color = Color(0.745098, 0.745098, 0.745098, 1)
default_border_color = Color(1, 0.741176, 0.580392, 1)
mapped_border_color = Color(1, 1, 1, 1)
symbols = Array[Texture2D]([ExtResource("9_c11cy"), ExtResource("9_3gfe5"), ExtResource("11_o6efd"), ExtResource("13_hjnmv")])
uncollected_item_symbol = 2
collected_item_symbol = 3
vertical_wall = ExtResource("7_eifkn")
horizontal_wall = ExtResource("2_tawyw")
vertical_passage = ExtResource("5_sn1if")
horizontal_passage = ExtResource("1_hth3y")
vertical_separator = ExtResource("6_f6pnw")
horizontal_separator = ExtResource("6_f6pnw")
vertical_borders = Array[Texture2D]([])
horizontal_borders = Array[Texture2D]([])
outer_corner = ExtResource("4_xtd8s")
inner_corner = ExtResource("3_4qbbp")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bdj2f008uewl8"
path="res://.godot/imported/BorderPassage.png-3717726a9e8f42f33cda81f114d70776.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/MF/BorderPassage.png"
dest_files=["res://.godot/imported/BorderPassage.png-3717726a9e8f42f33cda81f114d70776.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://q0b38erpbso0"
path="res://.godot/imported/BorderWall.png-72c47be69f4a48ebfeda5788ec64219e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/MF/BorderWall.png"
dest_files=["res://.godot/imported/BorderWall.png-72c47be69f4a48ebfeda5788ec64219e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 868 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bhu6ahuyeibux"
path="res://.godot/imported/Door1.png-e528b647953d6dfe7941c40924408ce6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/MF/Borders/Door1.png"
dest_files=["res://.godot/imported/Door1.png-e528b647953d6dfe7941c40924408ce6.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ca1c415uaj78m"
path="res://.godot/imported/Door2.png-f4a3bf33e9c7d664e2e850bfa8d97b14.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/MF/Borders/Door2.png"
dest_files=["res://.godot/imported/Door2.png-f4a3bf33e9c7d664e2e850bfa8d97b14.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 B

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cebl0efe2kt0t"
path="res://.godot/imported/Door3.png-924d56b4d9b2633e731ac99de58ac625.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/MetroidvaniaSystem/Themes/MF/Borders/Door3.png"
dest_files=["res://.godot/imported/Door3.png-924d56b4d9b2633e731ac99de58ac625.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save