From 767449fe54d604b864089174742bff0361e453a7 Mon Sep 17 00:00:00 2001 From: myin Date: Sun, 30 Jun 2024 14:21:18 +0200 Subject: [PATCH] create rooms --- godot/src/GridMapUI.gd | 26 +++++ godot/src/GridRoomMap.gd | 112 +++++++++++++++++++++ godot/src/base-system/combat/HitFlash2D.gd | 2 +- godot/src/base-system/combat/Hurtbox.gd | 3 +- godot/src/game.gd | 80 ++++++++++----- godot/src/game.tscn | 39 ++++++- godot/src/props/door.gd | 3 + godot/src/props/room.gd | 37 ++++--- 8 files changed, 253 insertions(+), 49 deletions(-) create mode 100644 godot/src/GridMapUI.gd create mode 100644 godot/src/GridRoomMap.gd diff --git a/godot/src/GridMapUI.gd b/godot/src/GridMapUI.gd new file mode 100644 index 0000000..6522c4b --- /dev/null +++ b/godot/src/GridMapUI.gd @@ -0,0 +1,26 @@ +class_name GridMapUI +extends Control + +@export var room_manager: GridRoomMap + +func _ready(): + room_manager.changed.connect(func(): update()) + +func update(): + queue_redraw() + +func _draw(): + for room in room_manager.rooms: + var color = Color.WHITE + if room == room_manager.boss_room: + color = Color.RED + elif room in room_manager.items: + color = Color.GREEN + elif room == room_manager.coord: + color = Color.BLUE + elif room == room_manager.spawn: + color = Color.ORANGE + elif room in room_manager.cleared: + color = Color.GRAY + + draw_circle(Vector2(room) * Vector2(5, 5), 2, color) diff --git a/godot/src/GridRoomMap.gd b/godot/src/GridRoomMap.gd new file mode 100644 index 0000000..9a38333 --- /dev/null +++ b/godot/src/GridRoomMap.gd @@ -0,0 +1,112 @@ +class_name GridRoomMap +extends Node + +signal changed() +signal generated() + +const GROUP = "GridRoomMap" +const DIRS = [Vector2i.LEFT, Vector2i.UP, Vector2i.RIGHT, Vector2i.DOWN] + +@export var map_size := Vector2i(10, 10) +@export var iterations := 30 +@export var item_room_chance := 0.1 + +@onready var spawn: Vector2i = floor(map_size / 2) +@onready var cleared = [spawn] +@onready var coord: Vector2i = spawn: + set(v): + if not coord in rooms: + print("Invalid move to %s" % coord) + return + + coord = v + changed.emit() + +var boss_room: Vector2i +var pre_boss_room: Vector2i +var rooms = {} +var items = [] + +func _ready(): + add_to_group(GROUP) + +func generate(): + _create_rooms() + _mark_item_rooms() + _mark_boss_room() + self.coord = spawn + generated.emit() + +func is_item_room(): + return coord in items + +func is_boss_room(): + return coord == boss_room + +func is_pre_boss_room(): + return coord == pre_boss_room + +func is_enemy_room(): + return not is_item_room() and not is_boss_room() and not is_pre_boss_room() + +func is_room_cleared(c: Vector2i): + return c in cleared or is_item_room() or is_pre_boss_room() + +func set_room_cleared(c): + if not is_room_cleared(c): + cleared.append(c) + +func get_room(c = coord): + return rooms[c] + +func _mark_item_rooms(): + for room in rooms: + var close_items = items.filter(func(p): return Vector2(abs(p - room)).length() <= 2) + if randf() < item_room_chance and close_items.is_empty(): + items.append(room) + +func _mark_boss_room(): + var x = _get_random_boss_room() + if x.is_empty(): + return + + pre_boss_room = x[0] + boss_room = x[1] + rooms[boss_room] = {} + +func _get_random_boss_room(): + var room_keys = rooms.keys() + room_keys.shuffle() + + for room in room_keys: + for dir in _get_unlinked_dirs(room): + var pos = room + dir + if get_linked_dirs(pos).size() == 1 and not pos in items: + return [room, pos] + + return [] + +func _get_unlinked_dirs(pos: Vector2i) -> Array: + return _get_available_dirs(pos).filter(func(d): return not (pos + d) in rooms) + +func get_linked_dirs(pos: Vector2i = coord) -> Array: + return _get_available_dirs(pos).filter(func(d): return (pos + d) in rooms) + +func _get_available_dirs(pos: Vector2i): + return DIRS.duplicate().filter(func(d): return not _is_outside_map(pos + d)) + +func _create_rooms(): + var start = spawn + for i in range(iterations): + rooms[start] = {} + + var available_dirs = _get_available_dirs(start) + if available_dirs.is_empty(): continue + + var dir = available_dirs.pick_random() + start += dir + +func _is_outside_map(pos: Vector2i): + var start = Vector2i.ZERO + var end = map_size + return pos.x < start.x or pos.x > end.x or pos.y < start.y or pos.y > end.y diff --git a/godot/src/base-system/combat/HitFlash2D.gd b/godot/src/base-system/combat/HitFlash2D.gd index 52e3787..4cdd1a0 100644 --- a/godot/src/base-system/combat/HitFlash2D.gd +++ b/godot/src/base-system/combat/HitFlash2D.gd @@ -5,7 +5,7 @@ extends Node @export var node: Node2D func flash(): - if node and node.material: + if node and node.material and is_inside_tree(): var mat = node.material as ShaderMaterial _set_hit_flash(mat, true) await get_tree().create_timer(time).timeout diff --git a/godot/src/base-system/combat/Hurtbox.gd b/godot/src/base-system/combat/Hurtbox.gd index 4cac6b1..cd6b8ab 100644 --- a/godot/src/base-system/combat/Hurtbox.gd +++ b/godot/src/base-system/combat/Hurtbox.gd @@ -31,5 +31,6 @@ func damage(dmg: int, knockback_force: Vector2): if frame_freeze and not health.is_dead(): frame_freeze.freeze() - get_tree().create_timer(invincible_time).timeout.connect(func(): invincible = false) + if is_inside_tree(): + get_tree().create_timer(invincible_time).timeout.connect(func(): invincible = false) return true diff --git a/godot/src/game.gd b/godot/src/game.gd index 8bdae25..f189cfa 100644 --- a/godot/src/game.gd +++ b/godot/src/game.gd @@ -7,6 +7,7 @@ const ROOM = preload("res://src/props/room.tscn") @onready var music_player = $MusicPlayer @onready var mirror_viewport = $MirrorViewport @onready var relative_remote_transform_2d = $Player/RelativeRemoteTransform2D +@onready var grid_room_map = $GridRoomMap @export var enemy_value := 5.0 @export var enemy_kills := 5.0 @@ -14,57 +15,82 @@ const ROOM = preload("res://src/props/room.tscn") @export var value_increase := 1.5 @export var kill_increase := 1.8 -var previous_dir := Vector2.ZERO var room_dirs := [Vector2.UP, Vector2.LEFT, Vector2.RIGHT, Vector2.DOWN] +var rooms = {} var _logger = Logger.new("Game") func _ready(): GameManager.game_start() - _setup_first_room.call_deferred() music_player.play("RESET") GameManager.mirrored.connect(func(mirror): music_player.play("mirror" if mirror else "normal")) -func _setup_first_room(): - var room = _create_room() - add_child(room) - room.start(enemy_value, enemy_kills) - -func _setup_next_room(current_room: Room): - var room = _create_room() - enemy_value *= value_increase - enemy_kills *= kill_increase - var dir = room_dirs.filter(func(d): return d != previous_dir).pick_random() - _logger.debug("Creating room in dir %s" % dir) - current_room.rooms[dir] = room - current_room.open_door(dir) + grid_room_map.generated.connect(func(): + for coord in grid_room_map.rooms: + _create_room(coord) + + print("Created rooms") + var room = rooms[grid_room_map.coord] + var pos = room.global_position + player.global_position = pos + cam.global_position = pos + relative_remote_transform_2d.center = pos + room.open_doors() + + await get_tree().create_timer(0.5).timeout + cam.position_smoothing_enabled = true + ) + grid_room_map.generate() - room.global_position = current_room.global_position + dir * room.get_size() - add_child.call_deferred(room) +#func _setup_first_room(): + #var room = _create_room() + #add_child(room) + #room.start(enemy_value, enemy_kills) +# +#func _setup_next_room(current_room: Room): + #var room = _create_room() + #var dir = room_dirs.filter(func(d): return d != previous_dir).pick_random() + #_logger.debug("Creating room in dir %s" % dir) + #current_room.rooms[dir] = room + #current_room.open_doors() + # + #room.global_position = current_room.global_position + dir * room.get_size() + #add_child.call_deferred(room) -func _create_room() -> Room: +func _create_room(coord: Vector2i) -> Room: var room = ROOM.instantiate() var mat = room.material as ShaderMaterial mat.set_shader_parameter("reflection_viewport", mirror_viewport.get_viewport().get_texture()) + add_child(room) + rooms[coord] = room + room.global_position = coord * room.get_size() + room.setup_doors(grid_room_map.get_linked_dirs(coord)) + room.finished.connect(func(): - _setup_next_room(room) - GameManager.room_done() + grid_room_map.set_room_cleared(coord) + room.open_doors() + enemy_value *= value_increase + enemy_kills *= kill_increase ) room.entered.connect(func(dir): _logger.debug("Player entered in direction %s" % dir) - var new_room = room.rooms[dir] - cam.global_position = new_room.global_position + var next_coord = coord + dir + if not next_coord in rooms: + print("Room not available in %s" % next_coord) + return + + var next_room = rooms[next_coord] + cam.global_position = next_room.global_position relative_remote_transform_2d.center = cam.global_position player.velocity = Vector2.ZERO - player.global_position = new_room.get_door(-dir).global_position + dir * 10 + player.global_position = next_room.get_door(-dir).global_position + Vector2(dir) * 10 player.immediate_return_trident() - previous_dir = -dir - new_room.start(enemy_value, enemy_kills, dir) - GameManager.room_start() - get_tree().create_timer(1.0).timeout.connect(func(): room.queue_free()) + grid_room_map.coord = next_coord + if not grid_room_map.is_room_cleared(next_coord): + next_room.start(enemy_value, enemy_kills, dir) ) return room diff --git a/godot/src/game.tscn b/godot/src/game.tscn index 682171c..ef24fa8 100644 --- a/godot/src/game.tscn +++ b/godot/src/game.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://di8g8e6s34cik"] +[gd_scene load_steps=13 format=3 uid="uid://di8g8e6s34cik"] [ext_resource type="Script" path="res://src/game.gd" id="1_fscq0"] [ext_resource type="Script" path="res://src/base-system/RelativeRemoteTransform2D.gd" id="3_vvprc"] @@ -6,6 +6,8 @@ [ext_resource type="AudioStream" uid="uid://bygqjhqr85tjf" path="res://assets/sound/危機.mp3" id="5_3pgwk"] [ext_resource type="PackedScene" uid="uid://dlby6flxkmnjs" path="res://src/character/player.tscn" id="6_2xq02"] [ext_resource type="PackedScene" uid="uid://1sccjkvbts1e" path="res://src/character/player_reflection.tscn" id="6_bcrcl"] +[ext_resource type="Script" path="res://src/GridRoomMap.gd" id="7_hygqf"] +[ext_resource type="Script" path="res://src/GridMapUI.gd" id="8_21wk6"] [sub_resource type="Animation" id="Animation_880qs"] length = 0.001 @@ -33,6 +35,18 @@ tracks/1/keys = { "update": 1, "values": [true] } +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("BGM:volume_db") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [-30.0] +} [sub_resource type="Animation" id="Animation_sp5nt"] resource_name = "mirror" @@ -149,7 +163,6 @@ _data = { script = ExtResource("1_fscq0") [node name="Camera2D" type="Camera2D" parent="."] -position_smoothing_enabled = true [node name="Player" parent="." instance=ExtResource("6_2xq02")] z_index = 10 @@ -163,12 +176,17 @@ relative_to_origin = 1.06 [node name="BGM" type="AudioStreamPlayer" parent="."] process_mode = 3 stream = ExtResource("4_rug7x") -volume_db = -25.0 +volume_db = -30.0 + +[node name="BGM2" type="AudioStreamPlayer" parent="."] +process_mode = 3 +stream = ExtResource("4_rug7x") +volume_db = -30.0 [node name="BGMirror" type="AudioStreamPlayer" parent="."] process_mode = 3 stream = ExtResource("5_3pgwk") -volume_db = -50.0 +volume_db = -60.0 [node name="MusicPlayer" type="AnimationPlayer" parent="."] process_mode = 3 @@ -188,5 +206,18 @@ position_smoothing_enabled = true [node name="PlayerReflection" parent="MirrorViewport" node_paths=PackedStringArray("player") instance=ExtResource("6_bcrcl")] player = NodePath("../../Player") +[node name="GridRoomMap" type="Node" parent="."] +script = ExtResource("7_hygqf") + +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="GridMapUI" type="Control" parent="CanvasLayer" node_paths=PackedStringArray("room_manager")] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("8_21wk6") +room_manager = NodePath("../../GridRoomMap") + [connection signal="died" from="Player" to="." method="_on_player_died"] [connection signal="reflected" from="Player" to="." method="_on_player_reflected"] diff --git a/godot/src/props/door.gd b/godot/src/props/door.gd index dc7967c..cc43314 100644 --- a/godot/src/props/door.gd +++ b/godot/src/props/door.gd @@ -14,6 +14,9 @@ func open(): func close(): animation_player.play("close") +func disable(): + $CollisionShape2D.disabled = true + hide() func _on_enter_area_body_entered(body): entered.emit() diff --git a/godot/src/props/room.gd b/godot/src/props/room.gd index 3e96d44..b0502de 100644 --- a/godot/src/props/room.gd +++ b/godot/src/props/room.gd @@ -21,10 +21,10 @@ const BOMB_ENEMY = preload("res://src/character/bomb_enemy.tscn") @onready var enemy_spawner = $EnemySpawner @onready var doors = { - Vector2.UP: door_n, - Vector2.DOWN: door_s, - Vector2.LEFT: door_w, - Vector2.RIGHT: door_e, + Vector2i.UP: door_n, + Vector2i.DOWN: door_s, + Vector2i.LEFT: door_w, + Vector2i.RIGHT: door_e, } var max_enemy_value := 5.0 @@ -34,25 +34,30 @@ var available_enemies := [ENEMY] var spawned_enemies: Array[Enemy] = [] var enemies_killed := 0 -var rooms = {} - func _ready(): - door_n.entered.connect(func(): entered.emit(Vector2.UP)) - door_s.entered.connect(func(): entered.emit(Vector2.DOWN)) - door_w.entered.connect(func(): entered.emit(Vector2.LEFT)) - door_e.entered.connect(func(): entered.emit(Vector2.RIGHT)) + door_n.entered.connect(func(): entered.emit(Vector2i.UP)) + door_s.entered.connect(func(): entered.emit(Vector2i.DOWN)) + door_w.entered.connect(func(): entered.emit(Vector2i.LEFT)) + door_e.entered.connect(func(): entered.emit(Vector2i.RIGHT)) func get_size(): var size = get_used_rect().size + Vector2i(2, 0) - return Vector2(size * tile_set.tile_size) + return Vector2i(size * tile_set.tile_size) + +func setup_doors(valid_dirs: Array): + for d in doors: + if not d in valid_dirs: + doors[d].disable() -func open_door(dir: Vector2): - doors[dir].open() +func open_doors(): + for d in doors: + if doors[d].visible: + doors[d].open() -func get_door(dir: Vector2): +func get_door(dir: Vector2i): return doors[dir] -func start(max_value: int, max_killed: int, from: Vector2 = Vector2.ZERO): +func start(max_value: int, max_killed: int, from := Vector2i.ZERO): max_enemy_value = max_value max_enemies_killed = max_killed enemy_spawner.start() @@ -80,7 +85,7 @@ func _on_enemy_spawner_timeout(): finished.emit() ) - _check_current_enemy_values()a + _check_current_enemy_values() #var rect = get_used_rect() #var start_x = rect.position + Vector2.RIGHT