tool extends TileMap class_name Level export (PackedScene) var next_level onready var _floor := $Floor as TileMap onready var _props := $Props as TileMap onready var _waste := $Waste as TileMap onready var _player := $Player as Player onready var _stairs := $Stairs as AnimatedSprite onready var _reservoir := $Reservoir as AnimatedSprite onready var _wastesound := $Waste/Sound as AudioStreamPlayer export var start_x := 0 export var start_y := 0 export var stairs_x := 0 export var stairs_y := 0 var completed := false var initial_state func _ready(): _player.level = self _player.tile_position = Vector2(start_x, start_y) _stairs.visible = false _stairs.modulate = Color.white initial_state = { waste = {}, reservoir = _player.reservoir } for w in _waste.get_used_cells(): initial_state.waste[w] = _waste.get_cellv(w) func _process(_delta): if Engine.editor_hint: if not _floor is TileMap: _floor = $Floor as TileMap if not _player is Player: _player = $Player as Player player_initial_position() if not _stairs is AnimatedSprite: _stairs = $Stairs as AnimatedSprite _stairs.position = _floor.map_to_world(Vector2(stairs_x, stairs_y)) if not _reservoir is AnimatedSprite: _reservoir = $Reservoir as AnimatedSprite var self_cells = self.get_used_cells() if not self_cells.empty(): _floor.clear() for cell in self_cells: _floor.set_cellv(cell, self.get_cellv(cell)) self.clear() update_reservoir() class Tile: var x :int var y :int var level :Level func get_tile(var tile_map :TileMap) -> int: return tile_map.get_cell(x, y) func has_tile(var tile_map :TileMap) -> bool: return get_tile(tile_map) >= 0 func set_tile(var tile_map :TileMap, var tile :int) -> void: tile_map.set_cell(x, y, tile) func swap_tile(var tile_map:TileMap, var other :Tile) -> void: var tile := other.get_tile(tile_map) other.set_tile(tile_map, self.get_tile(tile_map)) self.set_tile(tile_map, tile) func has_floor() -> bool: return has_tile(level._floor) func has_prop() -> bool: return has_tile(level._props) func has_waste() -> bool: return has_tile(level._waste) func is_stairs(): return level.completed and (x == level.stairs_x or x == level.stairs_x - 1) and (y == level.stairs_y or y == level.stairs_y - 1) func is_stairs_entrance(): return level.completed and x == level.stairs_x - 1 and y == level.stairs_y - 1 func can_walk(var direction := Vector2.ZERO) -> bool: if is_stairs_entrance() and direction == Vector2.DOWN: return true return has_floor() and not has_prop() and not has_waste() and not is_stairs() func get_next_tile(var direction :Vector2) -> Tile: return level.get_tile(x + direction.x, y + direction.y) func can_push(var direction :Vector2) -> bool: if has_waste(): return get_next_tile(direction).can_push(direction) elif has_prop() or is_stairs(): return false elif has_floor(): return true else: return false func get_waste_line(var direction :Vector2, var continuous := false, var max_tiles := -1) -> Array: var waste_line := [] if max_tiles != 0 and (has_waste() and has_floor() or continuous and can_walk()): waste_line = get_next_tile(direction).get_waste_line(direction, continuous, max_tiles - 1) if has_waste(): waste_line.push_front(self) return waste_line func push(var direction :Vector2) -> void: if can_push(direction): var waste_line := get_waste_line(direction) if not waste_line.empty(): var last = waste_line.pop_back() last.push_r(direction, waste_line) func push_r(var direction: Vector2, var waste_line := []) -> void: var empty_tile = get_next_tile(direction) swap_tile(level._waste, empty_tile) if not waste_line.empty(): waste_line.pop_back().push_r(direction, waste_line) return func dash(var direction: Vector2, var tile_limit = INF, var waste_limit = INF) -> Tile: var waste_line = get_waste_line(direction, true, tile_limit) waste_line.resize(min(waste_limit, waste_line.size())) for tile in waste_line: tile.set_tile(level._waste, -1) level.check_completed() level.waste_sound(waste_line.size()) return waste_line.back() if not waste_line.empty() else self func get_tile_v(var position :Vector2) -> Tile: return get_tile(position.x, position.y) func get_tile(var x :int, var y :int) -> Tile: var tile = Tile.new() tile.x = x tile.y = y tile.level = self return tile func check_completed() -> void: var left = _waste.get_used_cells() if left.empty(): show_stairs() func show_stairs() -> void: _stairs.frame = 0 _stairs.visible = true _stairs.play() ($Stairs/Sound as AudioStreamPlayer).play() completed = true func update_reservoir() -> void: _reservoir.frame = _player.reservoir func waste_sound(var number :int): for _i in range(number): _wastesound.play() yield(_wastesound, "finished") func player_initial_position(): _player.level = self _player.tile_position = Vector2(start_x, start_y) _player.update_position() if not Engine.editor_hint: _player.current_direction = _player.movements.back() _player.update_animation() func reset(): if not completed: _waste.clear() for w in initial_state.waste: _waste.set_cellv(w, initial_state.waste[w]) player_initial_position() _player.reservoir = initial_state.reservoir update_reservoir() func end_of_level(): _player.visible = false remove_child(_player) var downstairs = $Stairs/Downstairs as AnimatedSprite downstairs.visible = true downstairs.play() downstairs.connect("animation_finished", self, "next_level") func next_level(): if next_level: get_parent().add_child(next_level.instance()) get_parent().remove_child(self)