Files
Super-Mario-Bros.-Remastere…/Scripts/Classes/Editor/LevelEditor.gd
KirbyKidJ afea937948 Doors and Key Fixes
This fixes the Door animation so that when you enter a door and exit around the same general area, the door you entered from closes automatically.
Also added some fixes for the Level Editor where after leaving the playtest mode, the number of keys you have resets to 0 and the locked doors re-lock.
2025-09-21 23:40:35 -07:00

696 lines
25 KiB
GDScript

class_name LevelEditor
extends Node
const CAM_MOVE_SPEED_SLOW := 128
const CAM_MOVE_SPEED_FAST := 256
var cursor_tile_position := Vector2i.ZERO
const CURSOR_OFFSET := Vector2(-8, -8)
var mode := 0
var current_entity_selector: EditorTileSelector = null
var current_entity_scene: PackedScene = null
var current_spawn_offset := Vector2i.ZERO
var current_tile_source := 0
var current_tile_coords := Vector2i.ZERO
var current_tile_flip := Vector2.ZERO ## 1 = true, 0 = false, x = hori, y = vert
var menu_open := false
var testing_level := false
var entity_tiles := [{}, {}, {}, {}, {}]
static var playing_level := false
var tile_list: Array[EditorTileSelector] = []
var tile_offsets := {}
signal level_start
var selected_tile_index := 0
var can_move_cam := true
var music_track_list: Array[String] = [ "res://Assets/Audio/BGM/Silence.json","res://Assets/Audio/BGM/Athletic.json", "res://Assets/Audio/BGM/Autumn.json", "res://Assets/Audio/BGM/Beach.json", "res://Assets/Audio/BGM/Bonus.json", "res://Assets/Audio/BGM/Bowser.json", "res://Assets/Audio/BGM/FinalBowser.json", "res://Assets/Audio/BGM/Castle.json", "res://Assets/Audio/BGM/CoinHeaven.json", "res://Assets/Audio/BGM/Desert.json", "res://Assets/Audio/BGM/Garden.json", "res://Assets/Audio/BGM/GhostHouse.json", "res://Assets/Audio/BGM/Jungle.json", "res://Assets/Audio/BGM/Mountain.json", "res://Assets/Audio/BGM/Overworld.json", "res://Assets/Audio/BGM/Pipeland.json", "res://Assets/Audio/BGM/BooRace.json", "res://Assets/Audio/BGM/Sky.json", "res://Assets/Audio/BGM/Snow.json", "res://Assets/Audio/BGM/Space.json", "res://Assets/Audio/BGM/Underground.json", "res://Assets/Audio/BGM/Underwater.json", "res://Assets/Audio/BGM/Volcano.json", "res://Assets/Audio/BGM/Airship.json"]
var music_track_names: Array[String] = ["BGM_NONE", "BGM_ATHLETIC", "BGM_AUTUMN", "BGM_BEACH", "BGM_BONUS", "BGM_BOWSER", "BGM_FINALBOWSER", "BGM_CASTLE", "BGM_COINHEAVEN", "BGM_DESERT", "BGM_GARDEN", "BGM_GHOSTHOUSE", "BGM_JUNGLE", "BGM_MOUNTAIN", "BGM_OVERWORLD", "BGM_PIPELAND", "BGM_RACE", "BGM_SKY", "BGM_SNOW", "BGM_SPACE", "BGM_UNDERGROUND", "BGM_UNDERWATER", "BGM_VOLCANO", "BGM_AIRSHIP"]
var bgm_id := 0
const MUSIC_TRACK_DIR := "res://Assets/Audio/BGM/"
var select_start := Vector2i.ZERO
var select_end := Vector2i.ZERO
signal close_confirm(save: bool)
var sub_level_id := 0
const BLANK_FILE := {"Info": {}, "Levels": [{}, {}, {}, {}, {}]}
static var level_file = {"Info": {}, "Levels": [{}, {}, {}, {}, {}]}
var current_layer := 0
@onready var tile_layer_nodes: Array[TileMapLayer] = [%TileLayer1, %TileLayer2, %TileLayer3, %TileLayer4, %TileLayer5]
@onready var entity_layer_nodes := [%EntityLayer1, %EntityLayer2, %EntityLayer3, %EntityLayer4, %EntityLayer5]
var saved_entity_layers := [null, null, null, null, null]
var copied_node: Node = null
var copied_tile_offset := Vector2.ZERO
var copied_tile_source_id := -1
var copied_tile_atlas_coors := Vector2i.ZERO
var copied_tile_terrain_id := -1
const CURSOR_ERASOR := preload("uid://d0j1my4kuapgb")
const CURSOR_PEN = preload("uid://bt0brcjv0efmw")
const CURSOR_PENCIL = preload("uid://c8oyhfvlv2gvh")
const CURSOR_RULER = preload("uid://cg2wkxnmjgplf")
const CURSOR_INSPECT = preload("uid://1l3foyjqeej")
var multi_selecting := false
var inspect_mode := false
var inspect_menu_open := false
var current_inspect_tile: Node = null
var selection_filter := ""
static var level_author := ""
static var level_desc := ""
static var level_name := ""
static var difficulty := 0
var current_terrain_id := 0
static var load_play := false
signal tile_selected(tile_selector: EditorTileSelector)
var tile_menu_open := false
signal editor_start
enum EditorState{IDLE, TILE_MENU, MODIFYING_TILE, SAVE_MENU, SELECTING_TILE_SCENE, QUITTING, PLAYTESTING, TRACK_EDITING}
var current_state := EditorState.IDLE
static var play_pipe_transition := false
static var play_door_transition := false
const BOUNDARY_CONNECT_TILE := Vector2i.ZERO
var undo_redo = UndoRedo.new()
func _ready() -> void:
$TileMenu.hide()
Global.set_discord_status("In The Level Editor...")
Global.level_editor = self
playing_level = false
menu_open = $TileMenu.visible
Global.get_node("GameHUD").hide()
Global.can_time_tick = false
for i in get_tree().get_nodes_in_group("Selectors"):
tile_list.append(i)
var idx := 0
for i in music_track_list:
if i == "": continue
$%LevelMusic.add_item(tr(music_track_names[idx]).to_upper())
idx += 1
await get_tree().process_frame
Level.start_level_path = scene_file_path
var layer_idx := 0
for i in entity_layer_nodes:
for x in i.get_children():
entity_tiles[layer_idx][x.get_meta("tile_position")] = x
if level_file != {}:
Level.can_set_time = true
$LevelLoader.load_level(Checkpoint.sublevel_id)
if Global.current_game_mode == Global.GameMode.CUSTOM_LEVEL:
$Info.hide()
%Grid.hide()
play_level()
else:
Global.current_game_mode = Global.GameMode.LEVEL_EDITOR
else:
Global.current_game_mode = Global.GameMode.LEVEL_EDITOR
for i: Player in get_tree().get_nodes_in_group("Players"):
i.recenter_camera()
%LevelName.text = level_name
%LevelAuthor.text = level_author
%Description.text = level_desc
func _physics_process(delta: float) -> void:
if current_state == EditorState.IDLE:
handle_tile_cursor()
if [EditorState.IDLE, EditorState.TRACK_EDITING].has(current_state):
handle_camera(delta)
%ThemeName.text = Global.level_theme
handle_hud()
if Input.is_action_just_pressed("editor_open_menu"):
if current_state == EditorState.IDLE:
open_tile_menu()
elif current_state == EditorState.TILE_MENU:
close_tile_menu()
if Input.is_action_just_pressed("editor_play") and (current_state == EditorState.IDLE or current_state == EditorState.PLAYTESTING) and Global.current_game_mode == Global.GameMode.LEVEL_EDITOR:
Checkpoint.passed = false
if current_state == EditorState.PLAYTESTING:
stop_testing()
else:
play_level()
handle_layers()
func handle_hud() -> void:
$TileCursor.visible = current_state == EditorState.IDLE
$Info.visible = not playing_level
%Grid.visible = not playing_level
func quit_editor() -> void:
%QuitDialog.show()
signal level_saved
func open_tile_menu() -> void:
$TileMenu.visible = true
current_state = EditorState.TILE_MENU
for i in get_tree().get_nodes_in_group("Selectors"):
i.disabled = false
i.update_visuals()
func close_tile_menu() -> void:
$TileMenu.visible = false
current_state = EditorState.IDLE
for i in get_tree().get_nodes_in_group("Selectors"):
i.disabled = false
func save_level_before_exit() -> void:
tile_menu_open = true
open_save_dialog()
await level_saved
go_back_to_menu()
func copy_node(tile_position := Vector2i.ZERO) -> void:
if tile_layer_nodes[current_layer].get_used_cells().has(tile_position):
var terrain_id = BetterTerrain.get_cell(tile_layer_nodes[current_layer], tile_position)
if terrain_id != -2:
copied_tile_terrain_id = terrain_id
return
mode = 0
copied_tile_source_id = tile_layer_nodes[current_layer].get_cell_source_id(tile_position)
copied_tile_atlas_coors = tile_layer_nodes[current_layer].get_cell_atlas_coords(tile_position)
elif entity_tiles[current_layer].has(tile_position):
copied_node = entity_tiles[current_layer][tile_position].duplicate()
copied_tile_offset = entity_tiles[current_layer][tile_position].get_meta("tile_offset")
func cut_node(tile_position := Vector2i.ZERO) -> void:
var old_copy = copied_node
copy_node(tile_position)
if copied_node != old_copy:
remove_tile(tile_position)
func paste_node(tile_position := Vector2i.ZERO) -> void:
place_tile(tile_position, true)
func go_back_to_menu() -> void:
Global.transition_to_scene("res://Scenes/Levels/CustomLevelMenu.tscn")
func open_bindings_menu() -> void:
$TileMenu/EditorKeybindsView.open()
current_state = EditorState.SAVE_MENU
await $TileMenu/EditorKeybindsView.closed
current_state = EditorState.TILE_MENU
func open_save_dialog() -> void:
current_state = EditorState.SAVE_MENU
can_move_cam = false
%SaveLevelDialog.show()
menu_open = true
func stop_testing() -> void:
cleanup()
return_to_editor()
func cleanup() -> void:
get_tree().paused = false
Global.p_switch_timer = 0
Global.cancel_score_tally()
playing_level = !playing_level
play_pipe_transition = false
play_door_transition = false
LevelPersistance.reset_states()
KeyItem.total_collected = 0
Global.get_node("GameHUD").visible = playing_level
Global.p_switch_active = false
if Global.current_game_mode == Global.GameMode.LEVEL_EDITOR:
Global.time = $Level.time_limit
elif Level.can_set_time and playing_level:
Global.time = $Level.time_limit
Global.can_time_tick = playing_level
print(Global.can_time_tick)
func update_music() -> void:
if music_track_list[bgm_id] != "":
$Level.music = load(music_track_list[bgm_id].replace(".remap", ""))
else:
$Level.music = null
func play_level() -> void:
$TileMenu.hide()
menu_open = false
update_music()
reset_values_for_play()
%Camera.enabled = false
level_start.emit()
get_tree().call_group("Players", "editor_level_start")
parse_tiles()
if Global.current_game_mode != Global.GameMode.CUSTOM_LEVEL:
level_file = await $LevelSaver.save_level(level_name, level_author, level_desc, difficulty)
current_state = EditorState.PLAYTESTING
handle_hud()
func parse_tiles() -> void:
saved_entity_layers = [null, null, null, null, null]
var idx := 0
for i in entity_layer_nodes:
if is_instance_valid(i) == false:
continue
saved_entity_layers[idx] = i.duplicate(DUPLICATE_USE_INSTANTIATION)
if i is Player:
i.direction = 1
i.velocity = Vector2.ZERO
i.global_position = i.global_position.snapped(Vector2(8, 8))
i.ready.emit()
i.set_process_mode(Node.PROCESS_MODE_INHERIT)
idx += 1
func return_to_editor() -> void:
AudioManager.stop_all_music()
$Level.music = null
%Camera.global_position = get_viewport().get_camera_2d().get_screen_center_position()
%Camera.reset_physics_interpolation()
return_editor_tiles()
%Camera.enabled = true
%Camera.make_current()
KeyItem.total_collected = 0
Door.unlocked_doors.clear()
editor_start.emit()
current_state = EditorState.IDLE
handle_hud()
func return_editor_tiles() -> void:
for i in entity_layer_nodes:
i.queue_free()
var idx := 0
for i in entity_tiles:
i.clear()
$Level.add_child(saved_entity_layers[idx])
entity_layer_nodes[idx] = saved_entity_layers[idx]
idx += 1
var layer_idx = 0
for x in entity_layer_nodes:
x.process_mode = PROCESS_MODE_DISABLED
for i in x.get_children():
i.owner = self
var _tile_position = (Vector2i(i.global_position) - Vector2i(8, 8)) / 16
entity_tiles[layer_idx].set(i.get_meta("tile_position", Vector2i.ZERO), i)
layer_idx += 1
func handle_camera(delta: float) -> void:
var input_vector = Input.get_vector("editor_cam_left", "editor_cam_right", "editor_cam_up", "editor_cam_down")
%Camera.global_position += input_vector * (CAM_MOVE_SPEED_FAST if Input.is_action_pressed("editor_cam_fast") else CAM_MOVE_SPEED_SLOW) * delta
%Camera.global_position.y = clamp(%Camera.global_position.y, $Level.vertical_height + (get_viewport().get_visible_rect().size.y / 2), 32 - (get_viewport().get_visible_rect().size.y / 2))
%Camera.global_position.x = clamp(%Camera.global_position.x, -256 + (get_viewport().get_visible_rect().size.x / 2), INF)
func handle_layers() -> void:
if Input.is_action_just_pressed("layer_up"):
current_layer += 1
if Input.is_action_just_pressed("layer_down"):
current_layer -= 1
current_layer = clamp(current_layer, 0, entity_layer_nodes.size() - 1)
var idx := 0
for i in entity_layer_nodes:
i.z_index = 0 if current_layer == idx or playing_level else -1
i.modulate = Color(1, 1, 1, 1) if current_layer == idx or playing_level else Color(1, 1, 1, 0.5)
tile_layer_nodes[idx].modulate = i.modulate
tile_layer_nodes[idx].z_index = i.z_index - 1
%LayerDisplay.get_child(idx).modulate = Color.WHITE if current_layer == idx else Color(0.1, 0.1, 0.1, 0.5)
idx += 1
%LayerLabel.text = "Layer " + str(current_layer + 1)
func save_level() -> void:
level_author = %LevelAuthor.text
level_desc = %Description.text
level_name = %LevelName.text
difficulty = %DifficultySlider.value
var file_name = level_name.to_pascal_case() + ".lvl"
%SaveLevelDialog.hide()
menu_open = false
level_file = $LevelSaver.save_level(level_name, level_author, level_desc, difficulty)
$LevelSaver.write_file(level_file, file_name)
%SaveDialog.text = str("'") + file_name + "'" + " Saved."
%SaveAnimation.play("Show")
current_state = EditorState.TILE_MENU
level_saved.emit()
func close_save_menu() -> void:
can_move_cam = true
%SaveLevelDialog.hide()
menu_open = false
current_state = EditorState.TILE_MENU
const CUSTOM_LEVEL_DIR := "user://custom_levels/"
func handle_tile_cursor() -> void:
Input.set_custom_mouse_cursor(null)
var snapped_position = ((%TileCursor.get_global_mouse_position() - CURSOR_OFFSET).snapped(Vector2(16, 16))) + CURSOR_OFFSET
%TileCursor.global_position = (snapped_position)
var old_index := selected_tile_index
var tile_position = global_position_to_tile_position(snapped_position + Vector2(-8, -8))
tile_position.y = clamp(tile_position.y, -30, 1)
tile_position.x = clamp(tile_position.x, -16, INF)
cursor_tile_position = tile_position
inspect_mode = Input.is_action_pressed("editor_inspect") and not multi_selecting
if inspect_mode and current_state == EditorState.IDLE:
handle_inspection(tile_position)
return
if Input.is_action_pressed("mb_left"):
if Input.is_action_pressed("editor_select") and not multi_selecting:
multi_select_start(tile_position)
elif Input.is_action_pressed("editor_select") == false:
multi_selecting = false
place_tile(tile_position)
Input.set_custom_mouse_cursor(CURSOR_PENCIL)
if Input.is_action_pressed("mb_right"):
if Input.is_action_pressed("editor_select") and not multi_selecting:
multi_select_start(tile_position)
Input.set_custom_mouse_cursor(CURSOR_RULER)
elif Input.is_action_pressed("editor_select") == false:
multi_selecting = false
remove_tile(tile_position)
Input.set_custom_mouse_cursor(CURSOR_ERASOR)
if current_state == EditorState.IDLE:
if Input.is_action_just_pressed("scroll_up"):
selected_tile_index += 1
if Input.is_action_just_pressed("scroll_down"):
selected_tile_index -= 1
if Input.is_action_just_pressed("editor_copy"):
copy_node(tile_position)
elif Input.is_action_just_pressed("editor_cut"):
cut_node(tile_position)
elif Input.is_action_pressed("ui_paste"):
paste_node(tile_position)
if Input.is_action_just_pressed("pick_tile"):
pick_tile(tile_position)
handle_multi_selecting(tile_position)
if old_index != selected_tile_index:
selected_tile_index = wrap(selected_tile_index, 0, tile_list.size())
on_tile_selected(tile_list[selected_tile_index])
show_scroll_preview()
func pick_tile(tile_position := Vector2i.ZERO) -> void:
if tile_layer_nodes[current_layer].get_used_cells().has(tile_position):
var terrain_id = BetterTerrain.get_cell(tile_layer_nodes[current_layer], tile_position)
if terrain_id != -2:
mode = 2
current_terrain_id = terrain_id
return
mode = 0
current_tile_source = tile_layer_nodes[current_layer].get_cell_source_id(tile_position)
current_tile_coords = tile_layer_nodes[current_layer].get_cell_atlas_coords(tile_position)
elif entity_tiles[current_layer].has(tile_position) and entity_tiles[current_layer][tile_position] is not Player:
mode = 1
current_entity_scene = load(entity_tiles[current_layer][tile_position].scene_file_path)
current_spawn_offset = entity_tiles[current_layer][tile_position].get_meta("tile_offset")
func handle_inspection(tile_position := Vector2i.ZERO) -> void:
Input.set_custom_mouse_cursor(CURSOR_INSPECT)
if Input.is_action_just_pressed("mb_left"):
if entity_tiles[current_layer].get(tile_position) != null:
open_tile_properties(entity_tiles[current_layer][tile_position])
func open_tile_properties(tile: Node2D) -> void:
var properties = get_tile_properties(tile)
if properties.is_empty():
return
current_inspect_tile = tile
%TileModifierMenu.override_scenes = tile.get_node("EditorPropertyExposer").properties_force_selector
%TileModifierMenu.properties = properties
%TileModifierMenu.editing_node = current_inspect_tile
%TileModifierMenu.open()
current_state = EditorState.MODIFYING_TILE
%TileModifierMenu.position = tile.get_global_transform_with_canvas().origin
%TileModifierMenu.position.x = clamp(%TileModifierMenu.position.x, 0, get_viewport().get_visible_rect().size.x - %TileModifierMenu.size.x - 2)
%TileModifierMenu.position.y = clamp(%TileModifierMenu.position.y, 0, get_viewport().get_visible_rect().size.y - %TileModifierMenu.size.y - 2)
await %TileModifierMenu.closed
current_state = EditorState.IDLE
func multi_select_start(tile_position := Vector2i.ZERO) -> void:
select_start = tile_position
multi_selecting = true
func handle_multi_selecting(tile_position := Vector2i.ZERO) -> void:
select_end = tile_position
%MultiSelectRect.visible = multi_selecting
var top_corner := select_start
if select_start.x > select_end.x:
top_corner.x = select_end.x
if select_start.y > select_end.y:
top_corner.y = select_end.y
%MultiSelectRect.global_position = top_corner * 16
%MultiSelectRect.size = abs(select_end - select_start) * 16 + Vector2i(16, 16)
if multi_selecting:
Input.set_custom_mouse_cursor(CURSOR_RULER)
if Input.is_action_just_released("mb_left"):
for x in abs(select_end.x - select_start.x) + 1:
for y in abs(select_end.y - select_start.y) + 1:
var position = top_corner + Vector2i(x, y)
place_tile(position)
multi_selecting = false
if Input.is_action_just_released("mb_right"):
for x in abs(select_end.x - select_start.x) + 1:
for y in abs(select_end.y - select_start.y) + 1:
var position = top_corner + Vector2i(x, y)
remove_tile(position)
multi_selecting = false
func show_scroll_preview() -> void:
$TileCursor/Previews.show()
for i in [$"TileCursor/Previews/-2", $"TileCursor/Previews/-1", $"TileCursor/Previews/0", $"TileCursor/Previews/1", $"TileCursor/Previews/2"]:
var position = selected_tile_index + int(i.name)
var selector = tile_list[wrap(position, 0, tile_list.size())]
i.texture = selector.get_node("%Icon").texture
i.get_node("Overlay").texture = selector.get_node("%SecondaryIcon").texture
i.get_node("Overlay").region_rect = selector.get_node("%SecondaryIcon").region_rect
i.region_rect = selector.get_node("%Icon").region_rect
$TileCursor/Timer.start()
await $TileCursor/Timer.timeout
$TileCursor/Previews.hide()
func open_tile_selection_menu_scene_ref(selector: TilePropertySceneRef) -> void:
open_tile_menu()
current_state = EditorState.SELECTING_TILE_SCENE
selection_filter = selector.editing_node.get_node("EditorPropertyExposer").filters[selector.tile_property_name]
for i in get_tree().get_nodes_in_group("Selectors"):
i.disabled = !i.has_meta(selection_filter)
i.update_visuals()
await tile_selected
if is_instance_valid(selector) == false:
return
selector.set_scene(current_entity_selector)
close_tile_menu()
current_state = EditorState.MODIFYING_TILE
func on_tile_selected(selector: EditorTileSelector) -> void:
mode = selector.type
current_entity_selector = selector
selected_tile_index = tile_list.find(selector)
print(selected_tile_index)
if selector.type == 1:
current_entity_scene = selector.entity_scene
current_spawn_offset = selector.tile_offset
elif selector.type == 2:
current_terrain_id = selector.terrain_id
else:
current_tile_source = selector.source_id
current_tile_coords = selector.tile_coords
current_tile_flip = Vector2(selector.flip_h, selector.flip_v)
tile_selected.emit(selector)
func reset_values_for_play() -> void:
Global.score = 0
Global.lives = 0
Global.coins = 0
cleanup()
func place_tile(tile_position := Vector2i.ZERO, use_copy := false) -> void:
$TileCursor/Previews.hide()
var mode_to_use = mode
if use_copy:
if copied_node != null:
mode_to_use = 1
elif copied_tile_terrain_id != -1:
mode_to_use = 2
else:
mode_to_use = 0
if mode_to_use == 0:
var alt_tile := 0
if current_tile_flip.x != 0:
alt_tile += TileSetAtlasSource.TRANSFORM_FLIP_H
if current_tile_flip.y != 0:
alt_tile += TileSetAtlasSource.TRANSFORM_FLIP_V
remove_tile(tile_position)
check_connect_boundary_tiles(tile_position, current_layer)
var source = current_tile_source
var atlas = current_tile_coords
if use_copy:
source = copied_tile_source_id
atlas = copied_tile_atlas_coors
tile_layer_nodes[current_layer].set_cell(tile_position, source, atlas, alt_tile)
elif mode_to_use == 2:
var terrain_id = current_terrain_id
if use_copy:
terrain_id = copied_tile_terrain_id
remove_tile(tile_position)
check_connect_boundary_tiles(tile_position, current_layer)
BetterTerrain.set_cell(tile_layer_nodes[current_layer], tile_position, terrain_id)
else:
var overlapping_tile = null
if entity_tiles[current_layer].get(tile_position) != null and current_entity_scene != null:
overlapping_tile = entity_tiles[current_layer][tile_position]
if overlapping_tile.scene_file_path == current_entity_scene.resource_path:
return
remove_tile(tile_position)
var node: Node = null
if use_copy and copied_node != null:
node = copied_node.duplicate()
var offset := Vector2i.ZERO
var off_string = EntityIDMapper.map[EntityIDMapper.get_map_id(copied_node.scene_file_path)][1].split(",")
offset = Vector2i(int(off_string[0]), int(off_string[1]))
node.global_position = (tile_position * 16) + (Vector2i(8, 8) + offset)
else:
node = current_entity_scene.instantiate()
node.global_position = (tile_position * 16) + (Vector2i(8, 8) + current_spawn_offset)
node.set_meta("tile_position", tile_position)
node.set_meta("tile_offset", current_spawn_offset)
entity_layer_nodes[current_layer].add_child(node)
node.reset_physics_interpolation()
entity_tiles[current_layer].set(tile_position, node)
BetterTerrain.update_terrain_cell(tile_layer_nodes[current_layer], tile_position, true)
func check_connect_boundary_tiles(tile_position := Vector2i.ZERO, layer := 0) -> void:
if tile_position.y > 0:
tile_layer_nodes[layer].set_cell(tile_position + Vector2i.DOWN, 6, BOUNDARY_CONNECT_TILE)
if tile_position.x <= -16:
tile_layer_nodes[layer].set_cell(tile_position + Vector2i.LEFT, 6, BOUNDARY_CONNECT_TILE)
if tile_position.y > 0 and tile_position.x <= -16:
tile_layer_nodes[layer].set_cell(tile_position + Vector2i.LEFT + Vector2i.DOWN, 6, BOUNDARY_CONNECT_TILE)
func remove_tile(tile_position := Vector2i.ZERO) -> void:
$TileCursor/Previews.hide()
tile_layer_nodes[current_layer].set_cell(tile_position, -1)
if entity_tiles[current_layer].get(tile_position) != null:
if entity_tiles[current_layer].get(tile_position) is Player:
return
entity_tiles[current_layer].get(tile_position).queue_free()
entity_tiles[current_layer].erase(tile_position)
BetterTerrain.update_terrain_cell(tile_layer_nodes[current_layer], tile_position, true)
func global_position_to_tile_position(position := Vector2.ZERO) -> Vector2i:
return Vector2i(position / 16)
func theme_selected(theme_idx := 0) -> void:
ResourceSetterNew.cache.clear()
AudioManager.current_level_theme = ""
$Level.theme = Level.THEME_IDXS[theme_idx]
Global.level_theme = $Level.theme
Global.level_theme_changed.emit()
func time_selected(time_idx := 0) -> void:
ResourceSetterNew.cache.clear()
AudioManager.current_level_theme = ""
$Level.theme_time = ["Day", "Night"][time_idx]
Global.theme_time = ["Day", "Night"][time_idx]
$Level/LevelBG.time_of_day = time_idx
Global.level_theme_changed.emit()
func music_selected(music_idx := 0) -> void:
bgm_id = music_idx
func campaign_selected(campaign_idx := 0) -> void:
ResourceSetterNew.cache.clear()
Global.current_campaign = ["SMB1", "SMBLL", "SMBS", "SMBANN"][campaign_idx]
$Level.campaign = Global.current_campaign
Global.level_theme_changed.emit()
func backscroll_toggled(new_value := false) -> void:
$Level.can_backscroll = new_value
func height_limit_changed(new_value := 0) -> void:
$Level.vertical_height = -new_value
func time_limit_changed(new_value := 0) -> void:
$Level.time_limit = new_value
func low_gravity_toggled(new_value := false) -> void:
Global.entity_gravity = 10 if new_value == false else 5
for i: Player in get_tree().get_nodes_in_group("Players"):
i.low_gravity = new_value
func transition_to_sublevel(sub_lvl_idx := 0) -> void:
Global.can_pause = false
var play_transition = playing_level
if play_transition:
await Global.do_fake_transition()
else:
level_file = $LevelSaver.save_level(level_name, level_author, level_desc, difficulty)
LevelPersistance.reset_states()
sub_level_id = sub_lvl_idx
await $LevelLoader.load_level(sub_lvl_idx)
if Settings.file.visuals.transition_animation == 0:
Global.do_fake_transition(0.1)
await get_tree().physics_frame
if (play_pipe_transition or play_door_transition) and play_transition:
parse_tiles()
if play_pipe_transition:
get_tree().call_group("Pipes", "run_pipe_check")
if play_door_transition:
get_tree().call_group("Doors", "run_door_check")
update_music()
PipeArea.exiting_pipe_id = -1
Global.can_pause = true
func _input(event: InputEvent) -> void:
if event is InputEventJoypadButton or event is InputEventJoypadMotion:
%ControllerInputWarning.show()
else:
%ControllerInputWarning.hide()
func get_tile_properties(tile: Node) -> Array:
var properties := []
var old_properties := []
if tile.get_node_or_null("EditorPropertyExposer") == null:
return []
var property_exposer: PropertyExposer = tile.get_node_or_null("EditorPropertyExposer")
old_properties = tile.get_property_list()
for i in old_properties:
if property_exposer.properties.has(i.name):
properties.append(i)
return properties
func on_tree_exited() -> void:
pass # Replace with function body.