extends Node2D const GRID_SIZE = 16 const grassDecorationIds = [0, 1, 2, 3, 4, 5, 6, 7] const treeDecorationIds = [8, 9, 10, 11] const stoneDecorationIds = [12, 13, 14, 15, 16] const mushroomDecorationIds = [17, 18, 19, 20, 21, 22, 23, 24] var explosionEffect = preload("res://ExplosionEffect.tscn") var thread: Thread var wall_positions: PoolVector2Array = [] var crate_prefab = preload("res://Crate.tscn") var target_prefab = preload("res://Target.tscn") var game_over := false onready var crates = $LevelContainer/Crates onready var player = $LevelContainer/Player onready var targets = $LevelContainer/Targets onready var walls = $LevelContainer/Walls onready var floors = $LevelContainer/Floors onready var timer = $Timer onready var others = $LevelContainer/Others onready var cam = $LevelContainer/Player/Camera2D var consol var current_level := "" onready var tilemaps = [walls, others, floors] var just_started = true var level_size = Vector2(0, 0) signal game_over signal level_completed(completed) signal level_reset signal level_made func _ready(): player.connect("level_reset_requested", self, "_on_Player_level_reset_requested") thread = Thread.new() reset_time() func reset_time(): MainInstances.stopwatch.reset() player.started = false func start_stopwatch(): MainInstances.stopwatch.start() func load_level(level: String, decorate = true): $LevelContainer/Walls.modulate = Color.white $LevelContainer/Player/RayCast2D.set_collision_mask_bit(0, true) if thread.is_alive(): return if thread.is_active(): thread.wait_to_finish() reset_time() consol = MainInstances.console if decorate: consol.Log("Generating level " + level, .5, .5) SaveLoad.files.level.data.current_level = level SaveLoad.save("level") thread.start(self, "level_load", [level, decorate]) func level_load(level: Array): just_started = true player.set_moves(0) current_level = level[0] call_deferred("_reset_level", level[1]) func _exit_tree(): if thread.is_active(): thread.wait_to_finish() func _reset_level(decorate): game_over = false player.initialize() delete_children(crates) if decorate: walls.clear() others.clear() delete_children(floors) delete_children(targets) if current_level == "": return var file = File.new() file.open("res://Levels/%s.sokolvl" % current_level, File.READ) var version = 0 var row = 0 var player_pos level_size = Vector2(0, 0) while !file.eof_reached(): var line = file.get_line() if line.begins_with(";"): var meta = line.split(": ", false, 1) if meta[0] == ";version": version = int(meta[1]) elif line != "": if version != 1: push_error("Not supported .sokolvl version: " + str(version)) return var col = 0 for x in line: var tile_pos = Vector2(col, row) * GRID_SIZE if x == "#": if decorate: add_wall(tile_pos) if x in [".", "X", "O", "@", "%", "A"]: if decorate: add_floor(tile_pos) if x in ["@", "A"]: player_pos = tile_pos if x in ["X", "%"]: add_crate(tile_pos) if x in ["O", "%", "A"]: if decorate: add_target(tile_pos) col += 1 row += 1 level_size.y += 1 level_size.x = max(level_size.x, col) file.close() $CanvasLayer/HUD/LevelLabel.text = "Level = %s" % current_level var new_zoom = .5 new_zoom = clamp(new_zoom, get_parent().min_zoom, get_parent().max_zoom) new_zoom = Vector2(new_zoom, new_zoom) var level_int if level_size.x > level_size.y: level_int = level_size.x else: level_int = level_size.y new_zoom += Vector2(level_int / 45, level_int / 45) $Tween.interpolate_property( cam, "zoom", cam.zoom, new_zoom, 2, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT ) $Tween.start() timer.start(2) if decorate: decorate(-50, 50) initialize_player(player_pos) Utils.unload_loading_screen() yield(timer, "timeout") just_started = false emit_signal("level_made") return static func delete_children(node): for n in node.get_children(): node.remove_child(n) n.queue_free() func decorate(x, y): for tile in check_for_empty_tile(Vector2(x, y)): match randi() % 101: 1: add_mushroom(tile) 2: add_rock(tile) 3: add_tree(tile) func _on_Crate_target_updated(): var crates_in_place = 0 for crate in crates.get_children(): if crate.target_count > 0: crates_in_place += 1 if crates_in_place == crates.get_child_count(): emit_signal("level_completed") func _on_Player_level_reset_requested(): if player.tween.is_active(): yield(player.tween, "tween_all_completed") load_level(current_level, false) emit_signal("level_reset") func _on_Crate_game_over(): if not game_over: game_over = true emit_signal("game_over") func add_target(tile_pos): var target = target_prefab.instance() target.main = self target.position = tile_pos targets.add_child(target) func add_crate(tile_pos): var crate = crate_prefab.instance() crate.position = tile_pos crate.main = get_parent() crate.connect("target_updated", self, "_on_Crate_target_updated") crate.connect("game_over_detected", self, "_on_Crate_game_over") crates.add_child(crate) func initialize_player(tile_pos): player.position = tile_pos player.world = get_parent() func add_floor(tile_pos): floors.set_cellv(tile_pos / 16, 0) randomize() if randi() % 5 == 2: others.set_cellv(tile_pos / 16, grassDecorationIds[randi() % grassDecorationIds.size()]) func add_wall(tile_pos): wall_positions.append(tile_pos) walls.set_cellv(tile_pos / 16, 1) walls.update_bitmask_area(tile_pos / 16) func check_for_empty_tile(size: Vector2 = Vector2(-75, 75)): var empty_tiles: PoolVector2Array = [] for x in range(size.x, size.y): for y in range(size.x, size.y): var tile_pos = Vector2(x, y) var lower_tile_pos = tile_pos var left_tile_pos = tile_pos var right_tile_pos = tile_pos var up_tile_pos = tile_pos var down_right_tile_pos = tile_pos var down_left_tile_pos = tile_pos down_right_tile_pos += Vector2.DOWN + Vector2.RIGHT down_left_tile_pos += Vector2.DOWN + Vector2.LEFT lower_tile_pos += Vector2.DOWN left_tile_pos += Vector2.LEFT right_tile_pos += Vector2.RIGHT up_tile_pos += Vector2.UP var tile_positions = [ down_left_tile_pos, down_right_tile_pos, lower_tile_pos, left_tile_pos, right_tile_pos, up_tile_pos, tile_pos ] var count2 := 0 for tile in tile_positions: var count := 0 for tilemap in tilemaps: if empty(tilemap, tile): count += 1 if count == tilemaps.size(): count2 += 1 if count2 == tile_positions.size(): empty_tiles.append(tile_pos) return empty_tiles func empty(tilemap, tile) -> bool: if tilemap.get_cellv(tile) != -1: return false return true func add_tree(tile): others.set_cellv(tile, treeDecorationIds[randi() % treeDecorationIds.size()]) func add_rock(tile): others.set_cellv(tile, stoneDecorationIds[randi() % stoneDecorationIds.size()]) func add_mushroom(tile): others.set_cellv(tile, mushroomDecorationIds[randi() % mushroomDecorationIds.size()]) func explode_walls(): for positions in wall_positions: Utils.instance_scene_on_main(positions, explosionEffect) func _on_Player_won(): emit_signal("level_completed", true)