More map work, new teleporter logic, ball course puzzle logic and camera shake logic
parent
13a1e445b9
commit
167d48cbcf
|
@ -5,6 +5,7 @@ extends CharacterBody2D
|
|||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
@onready var map: MainLevel = get_tree().root.get_child(0).get_node("TileMap")
|
||||
@onready var tile_map: TileMapLayer = get_tree().root.get_child(0).get_node("TileMap/TileMapLayer")
|
||||
@onready var camera: Camera2D = $Sprite2D/Camera2D
|
||||
|
||||
@export var speed: float = 40
|
||||
@export var respawn_point: Marker2D
|
||||
|
@ -16,6 +17,10 @@ var should_move = false
|
|||
var is_dead = false
|
||||
var input_dir: Vector2
|
||||
var respawn_time: int = 10
|
||||
var can_teleport := true
|
||||
var rng := RandomNumberGenerator.new()
|
||||
var camera_shake: float = 0.0
|
||||
var camera_shake_fade: float = 0.0
|
||||
|
||||
func _ready():
|
||||
_respawn()
|
||||
|
@ -25,6 +30,7 @@ func _process(delta):
|
|||
if not is_dead:
|
||||
_try_get_input()
|
||||
_try_interact()
|
||||
_shake_camera(delta)
|
||||
if is_dead:
|
||||
if respawn_time > 0:
|
||||
respawn_time -= 1
|
||||
|
@ -95,6 +101,10 @@ func die():
|
|||
is_moving = false
|
||||
respawn_time = 10
|
||||
|
||||
func apply_camera_shake(strength: float = 10.0, fade: float = 5.0):
|
||||
camera_shake = strength
|
||||
camera_shake_fade = fade
|
||||
|
||||
func _try_interact():
|
||||
raycast.target_position = input_dir * 8
|
||||
raycast.force_raycast_update()
|
||||
|
@ -105,3 +115,16 @@ func _try_interact():
|
|||
(collider as PuzzleElement).interact(self)
|
||||
pass
|
||||
pass
|
||||
|
||||
func teleport(pos: Vector2i):
|
||||
global_position = pos
|
||||
can_teleport = false
|
||||
|
||||
func _shake_camera(delta):
|
||||
if camera_shake > 0:
|
||||
camera_shake = lerpf(camera_shake, 0, camera_shake_fade * delta)
|
||||
camera.offset = Vector2(rng.randf_range(-camera_shake, camera_shake), rng.randf_range(-camera_shake, camera_shake))
|
||||
|
||||
func _on_area_entered(area: Area2D):
|
||||
if area.is_in_group("death"):
|
||||
die()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://dpn82ibpmpb6u"]
|
||||
[gd_scene load_steps=8 format=3 uid="uid://dpn82ibpmpb6u"]
|
||||
|
||||
[ext_resource type="Script" path="res://entities/player/player.gd" id="1_jrd75"]
|
||||
[ext_resource type="Texture2D" uid="uid://dxkxa6e6ue1b6" path="res://entities/assets/bat.png" id="1_qfnf0"]
|
||||
|
@ -44,6 +44,9 @@ _data = {
|
|||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_l7fhd"]
|
||||
size = Vector2(8, 8)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_x0fgp"]
|
||||
size = Vector2(6, 6)
|
||||
|
||||
[node name="Player" type="CharacterBody2D"]
|
||||
z_index = 10
|
||||
collision_mask = 15
|
||||
|
@ -68,3 +71,12 @@ shape = SubResource("RectangleShape2D_l7fhd")
|
|||
[node name="RayCast2D" type="RayCast2D" parent="."]
|
||||
target_position = Vector2(0, 8)
|
||||
collision_mask = 3
|
||||
|
||||
[node name="Area2D" type="Area2D" parent="."]
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
|
||||
shape = SubResource("RectangleShape2D_x0fgp")
|
||||
|
||||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
|
||||
|
||||
[connection signal="area_entered" from="Area2D" to="." method="_on_area_entered"]
|
||||
|
|
|
@ -31,9 +31,9 @@ func _on_shoot_timer_timeout():
|
|||
add_child(inst)
|
||||
_arrows_shot.push_back(inst)
|
||||
|
||||
func _on_visibility_changed():
|
||||
func reset():
|
||||
for arrow in _arrows_shot:
|
||||
if arrow != null:
|
||||
arrow.queue_free()
|
||||
offset_start_time = _default_offset
|
||||
is_stopped = !visible
|
||||
is_stopped = true
|
||||
|
|
|
@ -8,5 +8,4 @@ script = ExtResource("1_tk6ny")
|
|||
|
||||
[node name="ShootTimer" type="Timer" parent="."]
|
||||
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||
[connection signal="timeout" from="ShootTimer" to="." method="_on_shoot_timer_timeout"]
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 214 B |
|
@ -0,0 +1,77 @@
|
|||
class_name Ball
|
||||
extends StaticBody2D
|
||||
|
||||
@onready var raycast: RayCast2D = $RayCast2D
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||
@onready var map: MainLevel = get_tree().root.get_child(0).get_node("TileMap")
|
||||
@onready var tile_map: TileMapLayer = get_tree().root.get_child(0).get_node("TileMap/TileMapLayer")
|
||||
@onready var puzzle: BallCoursePuzzle = $"../"
|
||||
|
||||
@export var start_position: Node2D
|
||||
@export var movement_dir: Vector2 = Vector2(0, 0)
|
||||
var can_roll := false
|
||||
var input_dir: Vector2
|
||||
var is_moving := false
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
reset()
|
||||
input_dir = movement_dir.normalized()
|
||||
|
||||
func _physics_process(delta):
|
||||
_try_move(delta)
|
||||
|
||||
func _process(delta):
|
||||
if can_roll:
|
||||
if not animation_player.is_playing():
|
||||
animation_player.play(&"roll")
|
||||
|
||||
if is_moving:
|
||||
return
|
||||
|
||||
raycast.target_position = input_dir * 8
|
||||
raycast.force_raycast_update()
|
||||
var collider: Node2D = raycast.get_collider()
|
||||
|
||||
if collider and collider.is_in_group("fake_wall"):
|
||||
collider.queue_free()
|
||||
queue_free()
|
||||
|
||||
if raycast.is_colliding():
|
||||
return
|
||||
|
||||
var current_tile: Vector2i = tile_map.local_to_map(global_position)
|
||||
var target_tile: Vector2i = Vector2i(
|
||||
current_tile.x + input_dir.x,
|
||||
current_tile.y + input_dir.y
|
||||
)
|
||||
|
||||
global_position = tile_map.map_to_local(target_tile)
|
||||
|
||||
var is_death_tile = map.is_death_tile(global_position)
|
||||
if is_death_tile:
|
||||
reset()
|
||||
return
|
||||
|
||||
is_moving = true
|
||||
sprite.global_position = tile_map.map_to_local(current_tile)
|
||||
|
||||
func _try_move(delta: int):
|
||||
if not is_moving:
|
||||
return
|
||||
|
||||
sprite.global_position = sprite.global_position.move_toward(global_position, 0.35)
|
||||
|
||||
if global_position == sprite.global_position:
|
||||
is_moving = false
|
||||
return
|
||||
|
||||
func reset():
|
||||
can_roll = false
|
||||
animation_player.stop()
|
||||
global_position = start_position.global_position
|
||||
|
||||
func _on_area_entered(area: Area2D):
|
||||
if area.is_in_group("death"):
|
||||
puzzle.reset()
|
|
@ -1,18 +1,77 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://dtya31nfxo0h7"]
|
||||
[gd_scene load_steps=9 format=3 uid="uid://dtya31nfxo0h7"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://ccs76qyn4ua40" path="res://puzzles/assets/ball.png" id="1_3p6vy"]
|
||||
[ext_resource type="Script" path="res://puzzles/ball/ball.gd" id="1_8r0ul"]
|
||||
|
||||
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_8in3v"]
|
||||
|
||||
[sub_resource type="Animation" id="Animation_2lwo2"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Sprite2D:rotation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [0.0]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_r6vqa"]
|
||||
resource_name = "roll"
|
||||
length = 3.0
|
||||
loop_mode = 1
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Sprite2D:rotation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 3),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 6.28319]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_srpra"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_2lwo2"),
|
||||
"roll": SubResource("Animation_r6vqa")
|
||||
}
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_we7nh"]
|
||||
size = Vector2(14, 14)
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_eid6d"]
|
||||
radius = 7.0
|
||||
|
||||
[node name="Ball" type="StaticBody2D"]
|
||||
collision_layer = 2
|
||||
physics_material_override = SubResource("PhysicsMaterial_8in3v")
|
||||
script = ExtResource("1_8r0ul")
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_srpra")
|
||||
}
|
||||
autoplay = "roll"
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
texture = ExtResource("1_3p6vy")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource("RectangleShape2D_we7nh")
|
||||
|
||||
[node name="RayCast2D" type="RayCast2D" parent="."]
|
||||
target_position = Vector2(8, 0)
|
||||
|
||||
[node name="Area2D" type="Area2D" parent="."]
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
|
||||
shape = SubResource("CircleShape2D_eid6d")
|
||||
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||
[connection signal="area_entered" from="Area2D" to="." method="_on_area_entered"]
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
class_name BallCoursePuzzle
|
||||
extends Puzzle
|
||||
|
||||
@export var ball: Ball
|
||||
@export var traps: Array[GroundButton]
|
||||
|
||||
func _process(delta):
|
||||
var is_finished = true
|
||||
|
||||
if ball != null:
|
||||
is_finished = false
|
||||
|
||||
if is_finished:
|
||||
complete()
|
||||
|
||||
func reset():
|
||||
ball.reset()
|
||||
for trap in traps:
|
||||
trap.reset()
|
|
@ -0,0 +1,9 @@
|
|||
class_name BallButton
|
||||
extends GroundButton
|
||||
|
||||
@export var ball: Ball
|
||||
|
||||
func _flip():
|
||||
sprite.frame = 1 if is_active else 0
|
||||
if is_active and ball and not ball.can_roll:
|
||||
ball.can_roll = true
|
|
@ -4,18 +4,29 @@ extends Area2D
|
|||
@onready var sprite = $Sprite2D
|
||||
|
||||
@export var nodes: Array[Node2D]
|
||||
@export var is_active = false
|
||||
var default_state = false
|
||||
|
||||
var is_active = false
|
||||
func _ready():
|
||||
default_state = is_active
|
||||
_flip()
|
||||
|
||||
func _on_body_entered(body):
|
||||
if body is Player:
|
||||
is_active = not is_active
|
||||
sprite.frame = 1 if is_active else 0
|
||||
for node in nodes:
|
||||
if node is TileMapLayer:
|
||||
var layer = node as TileMapLayer
|
||||
layer.enabled = !layer.enabled
|
||||
else:
|
||||
node.visible = false if is_active else true
|
||||
node.process_mode = PROCESS_MODE_DISABLED if is_active else PROCESS_MODE_INHERIT
|
||||
node.set_physics_process(!is_active)
|
||||
_flip()
|
||||
|
||||
func _flip():
|
||||
$Sprite2D.frame = 1 if is_active else 0
|
||||
for node in nodes:
|
||||
if node is TileMapLayer:
|
||||
var layer = node as TileMapLayer
|
||||
layer.enabled = !layer.enabled
|
||||
else:
|
||||
node.visible = false if is_active else true
|
||||
node.process_mode = PROCESS_MODE_DISABLED if is_active else PROCESS_MODE_INHERIT
|
||||
node.set_physics_process(!is_active)
|
||||
|
||||
func reset():
|
||||
is_active = default_state
|
||||
_flip()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
class_name Puzzle
|
||||
|
||||
extends Node
|
||||
|
||||
signal complete_puzzle(puzzle_name: String)
|
||||
@onready var sfx = preload("res://scenes/assets/jingles/win.wav")
|
||||
@onready var audio_player: AudioStreamPlayer2D = get_tree().root.get_child(0).get_node("Player/AudioStreamPlayer2D")
|
||||
|
||||
signal complete_puzzle
|
||||
|
||||
var is_complete = false;
|
||||
|
||||
|
@ -12,4 +14,6 @@ func complete():
|
|||
|
||||
is_complete = true
|
||||
|
||||
audio_player.stream = sfx
|
||||
audio_player.play()
|
||||
complete_puzzle.emit()
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
extends PuzzleManager
|
||||
|
||||
@onready var player: Player = get_tree().root.get_child(0).get_node("Player")
|
||||
|
||||
@export var ball_puzzle_elements: Array[Node2D]
|
||||
|
||||
func _on_finish_room():
|
||||
pass
|
||||
|
||||
|
||||
func _on_ball_puzzle_complete_puzzle():
|
||||
player.apply_camera_shake(10, 5)
|
||||
for elem in ball_puzzle_elements:
|
||||
elem.queue_free()
|
|
@ -0,0 +1,7 @@
|
|||
extends PuzzleManager
|
||||
|
||||
@export var next_room_stairs: Node2D
|
||||
|
||||
func _on_finish_room():
|
||||
next_room_stairs.visible = true
|
||||
next_room_stairs.process_mode = Node.PROCESS_MODE_INHERIT
|
|
@ -0,0 +1,22 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://dgatvman85cq7"]
|
||||
|
||||
[ext_resource type="Script" path="res://puzzles/teleporter/teleporter.gd" id="1_f3ibl"]
|
||||
[ext_resource type="Texture2D" uid="uid://cbuibrshdpkm1" path="res://scenes/assets/tileset.png" id="2_obpvk"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kuq27"]
|
||||
size = Vector2(6, 5)
|
||||
|
||||
[node name="Stairs" type="Area2D"]
|
||||
script = ExtResource("1_f3ibl")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
texture = ExtResource("2_obpvk")
|
||||
hframes = 8
|
||||
vframes = 8
|
||||
frame = 20
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(0, 0.5)
|
||||
shape = SubResource("RectangleShape2D_kuq27")
|
||||
|
||||
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
|
|
@ -0,0 +1,22 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://36rwjmb572qd"]
|
||||
|
||||
[ext_resource type="Script" path="res://puzzles/teleporter/teleporter.gd" id="1_3xtjc"]
|
||||
[ext_resource type="Texture2D" uid="uid://cbuibrshdpkm1" path="res://scenes/assets/tileset.png" id="2_j0ckt"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kuq27"]
|
||||
size = Vector2(6, 5)
|
||||
|
||||
[node name="Stairs" type="Area2D"]
|
||||
script = ExtResource("1_3xtjc")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
texture = ExtResource("2_j0ckt")
|
||||
hframes = 8
|
||||
vframes = 8
|
||||
frame = 21
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(0, 0.5)
|
||||
shape = SubResource("RectangleShape2D_kuq27")
|
||||
|
||||
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
|
|
@ -0,0 +1,16 @@
|
|||
extends Area2D
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta):
|
||||
pass
|
||||
|
||||
|
||||
func _on_body_exited(body):
|
||||
if body is Player:
|
||||
(body as Player).can_teleport = true
|
|
@ -0,0 +1,15 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://cr5ddcb7bxa5x"]
|
||||
|
||||
[ext_resource type="Script" path="res://puzzles/teleporter/teleport_location.gd" id="1_tqrxf"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_xddm4"]
|
||||
size = Vector2(7, 7)
|
||||
|
||||
[node name="TeleportLocation" type="Area2D"]
|
||||
script = ExtResource("1_tqrxf")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(0.5, 0.5)
|
||||
shape = SubResource("RectangleShape2D_xddm4")
|
||||
|
||||
[connection signal="body_exited" from="." to="." method="_on_body_exited"]
|
|
@ -1,8 +1,7 @@
|
|||
extends Area2D
|
||||
|
||||
@export var teleport_marker: Marker2D
|
||||
|
||||
@export var teleport_marker: Node2D
|
||||
|
||||
func _on_body_entered(body):
|
||||
if body is Player:
|
||||
body.global_position = teleport_marker.global_position
|
||||
if body is Player and (body as Player).can_teleport:
|
||||
(body as Player).teleport(teleport_marker.global_position)
|
||||
|
|
|
@ -1 +1 @@
|
|||
3,0,0,0,120,16,4,1,204,0,1,128,0,256,1,0,0,0,0,4,36,1,3,0,39,1,2,0,40,1,1,0,41,1,0,0,0,1,0,1,0,-1,-1,-1,-1,-1,-1,-1,
|
||||
3,0,0,0,120,16,4,1,62,0,2,128,0,256,1,0,0,0,0,3,41,4,2,0,44,4,0,0,47,4,3,0,0,1,0,1,0,-1,-1,-1,-1,-1,-1,-1,
|
Binary file not shown.
|
@ -2,7 +2,7 @@
|
|||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://dfc8jx220imb7"
|
||||
uid="uid://dvbepjvyfdw5k"
|
||||
path="res://.godot/imported/fail.wav-c48208a4611d55d0dcd0f0dbeab5bae3.sample"
|
||||
|
||||
[deps]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
3,0,0,0,120,16,4,1,112,0,4,128,0,256,1,0,0,0,0,4,52,4,4,0,55,4,0,0,57,4,1,0,59,4,2,0,0,1,0,1,0,-1,-1,-1,-1,-1,-1,-1,
|
Binary file not shown.
|
@ -0,0 +1,24 @@
|
|||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://fhwh6f2jmr6m"
|
||||
path="res://.godot/imported/win.wav-cc3fddf798237bb35a77f0272b1dd857.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://scenes/assets/jingles/win.wav"
|
||||
dest_files=["res://.godot/imported/win.wav-cc3fddf798237bb35a77f0272b1dd857.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=0
|
Binary file not shown.
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 1018 B |
601
scenes/main.tscn
601
scenes/main.tscn
File diff suppressed because one or more lines are too long
Reference in New Issue