1
0
Fork 0
Cleanomancer/Script/Level.gd

246 lines
6.5 KiB
GDScript

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 saved_steps := []
class StepSnap:
var waste := {}
var reservoir : int
var player_position : Vector2
var player_direction : Player.Movement
static func snap(var l : Level) -> StepSnap:
var step = StepSnap.new()
for w in l._waste.get_used_cells():
step.waste[w] = l._waste.get_cellv(w)
step.reservoir = l._player.reservoir
step.player_position = l._player.tile_position
step.player_direction = l._player.current_direction
return step
func restore(var l : Level) -> void:
l._waste.clear()
for w in self.waste:
l._waste.set_cellv(w, self.waste[w])
l._player.reservoir = self.reservoir
l._player.tile_position = self.player_position
l._player.current_direction = self.player_direction
func _ready():
_player.level = self
_player.tile_position = Vector2(start_x, start_y)
_stairs.visible = false
_stairs.modulate = Color.white
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()))
if not waste_line.empty():
level.take_snap()
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.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)
if not Engine.editor_hint:
_player.current_direction = _player.movements.back()
func take_snap():
if not completed:
saved_steps.push_back(StepSnap.snap(self))
func undo():
if not completed and not saved_steps.empty():
saved_steps.pop_back().restore(self)
func reset():
if not completed and not saved_steps.empty():
saved_steps.front().restore(self)
saved_steps.clear()
func check_player_stairs():
if get_tile_v(_player.tile_position).is_stairs():
_player.tile_position = Vector2(stairs_x - 1, stairs_y - 2)
_player.current_direction = _player.movements.back()
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():
var parent = get_parent()
if next_level:
if "End" in next_level.resource_path:
get_tree().change_scene_to(next_level)
return
var new = next_level.instance()
parent.add_child(new)
$"/root/Game".new_level(new)
parent.remove_child(self)