diff --git a/Assets/Cut Scenes/Calendar Cutscene.mov b/Assets/Cut Scenes/Calendar Cutscene.mov new file mode 100644 index 0000000..4184f4b Binary files /dev/null and b/Assets/Cut Scenes/Calendar Cutscene.mov differ diff --git a/Assets/Cut Scenes/Calendar Cutscene.ogv b/Assets/Cut Scenes/Calendar Cutscene.ogv new file mode 100644 index 0000000..404979c Binary files /dev/null and b/Assets/Cut Scenes/Calendar Cutscene.ogv differ diff --git a/Assets/Cut Scenes/Calendar Cutscene.ogv.uid b/Assets/Cut Scenes/Calendar Cutscene.ogv.uid new file mode 100644 index 0000000..ce169e3 --- /dev/null +++ b/Assets/Cut Scenes/Calendar Cutscene.ogv.uid @@ -0,0 +1 @@ +uid://bbi3yd8miilul diff --git a/Assets/Cut Scenes/Game over video.mov b/Assets/Cut Scenes/Game over video.mov new file mode 100644 index 0000000..9c099c3 Binary files /dev/null and b/Assets/Cut Scenes/Game over video.mov differ diff --git a/Assets/Cut Scenes/Game over video.ogv b/Assets/Cut Scenes/Game over video.ogv new file mode 100644 index 0000000..b7bf049 Binary files /dev/null and b/Assets/Cut Scenes/Game over video.ogv differ diff --git a/Assets/Cut Scenes/Game over video.ogv.uid b/Assets/Cut Scenes/Game over video.ogv.uid new file mode 100644 index 0000000..af29ee3 --- /dev/null +++ b/Assets/Cut Scenes/Game over video.ogv.uid @@ -0,0 +1 @@ +uid://b41725daretoj diff --git a/Assets/Cut Scenes/Weewoo.mov b/Assets/Cut Scenes/Weewoo.mov new file mode 100644 index 0000000..6e6201a Binary files /dev/null and b/Assets/Cut Scenes/Weewoo.mov differ diff --git a/Assets/Cut Scenes/Weewoo.ogv b/Assets/Cut Scenes/Weewoo.ogv new file mode 100644 index 0000000..2d28b11 Binary files /dev/null and b/Assets/Cut Scenes/Weewoo.ogv differ diff --git a/Assets/Cut Scenes/Weewoo.ogv.uid b/Assets/Cut Scenes/Weewoo.ogv.uid new file mode 100644 index 0000000..a5aaf92 --- /dev/null +++ b/Assets/Cut Scenes/Weewoo.ogv.uid @@ -0,0 +1 @@ +uid://l34f82rsegd6 diff --git a/Scenes/Characters/Player.tscn b/Scenes/Characters/Player.tscn index 7c75976..253b536 100644 --- a/Scenes/Characters/Player.tscn +++ b/Scenes/Characters/Player.tscn @@ -716,7 +716,7 @@ z_index = 10 texture_filter = 1 position = Vector2(0, -6) sprite_frames = SubResource("SpriteFrames_nn08x") -animation = &"idle" +animation = &"death" autoplay = "idle" [node name="ChildGraphic" type="AnimatedSprite2D" parent="."] diff --git a/Scenes/ambulance_cutscene.tscn b/Scenes/ambulance_cutscene.tscn new file mode 100644 index 0000000..e288576 --- /dev/null +++ b/Scenes/ambulance_cutscene.tscn @@ -0,0 +1,25 @@ +[gd_scene load_steps=3 format=3 uid="uid://ci5nqe61278ke"] + +[ext_resource type="VideoStream" uid="uid://l34f82rsegd6" path="res://Assets/Cut Scenes/Weewoo.ogv" id="1_3dlu2"] +[ext_resource type="Script" uid="uid://basrxhaxblk5q" path="res://Scripts/ambulance_cutscene.gd" id="1_yn2le"] + +[node name="AmbulanceCutscene" type="Control"] +layout_mode = 3 +anchors_preset = 0 +offset_right = 960.0 +offset_bottom = 540.0 +script = ExtResource("1_yn2le") + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +scale = Vector2(0.34, 0.34) +stream = ExtResource("1_3dlu2") +autoplay = true +expand = true + +[connection signal="finished" from="VideoStreamPlayer" to="." method="_on_video_stream_player_finished"] diff --git a/Scenes/final_death_cutscene.tscn b/Scenes/final_death_cutscene.tscn new file mode 100644 index 0000000..d6ee020 --- /dev/null +++ b/Scenes/final_death_cutscene.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=2 format=3 uid="uid://dofx2pgo5xgla"] + +[ext_resource type="VideoStream" uid="uid://b41725daretoj" path="res://Assets/Cut Scenes/Game over video.ogv" id="1_h6d1g"] + +[node name="FinalDeathCutscene" type="Control"] +layout_mode = 3 +anchors_preset = 0 +offset_right = 960.0 +offset_bottom = 540.0 + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +scale = Vector2(0.34, 0.34) +stream = ExtResource("1_h6d1g") +autoplay = true +expand = true diff --git a/Scripts/ambulance_cutscene.gd b/Scripts/ambulance_cutscene.gd new file mode 100644 index 0000000..aa9a14c --- /dev/null +++ b/Scripts/ambulance_cutscene.gd @@ -0,0 +1,13 @@ +extends Control + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + + +func _on_video_stream_player_finished() -> void: + print("AMBULANCE VIDEO FINISHED") + Gamecontroller.playerHealth = Gamecontroller.player.starting_health + get_tree().change_scene_to_file("res://Scenes/Levels/MainGame.tscn") diff --git a/Scripts/ambulance_cutscene.gd.uid b/Scripts/ambulance_cutscene.gd.uid new file mode 100644 index 0000000..3533c04 --- /dev/null +++ b/Scripts/ambulance_cutscene.gd.uid @@ -0,0 +1 @@ +uid://basrxhaxblk5q diff --git a/Scripts/demon.gd b/Scripts/demon.gd index df3c23d..9acbc37 100644 --- a/Scripts/demon.gd +++ b/Scripts/demon.gd @@ -28,6 +28,15 @@ func _ready() -> void: func _physics_process(_delta: float) -> void: if is_dead: return + + if player and player.is_dead: + player = null + is_attacking = false + attack_hitbox.monitoring = false + velocity = Vector2.ZERO + sprite.play("idle") + move_and_slide() + return if is_hurt: velocity = knockback_velocity @@ -89,9 +98,9 @@ func do_attack_damage() -> void: print("Demon bodies found: ", bodies.size()) for body in bodies: - if body is Player: + if body is Player and not body.is_dead: print("DEMON HIT PLAYER") - body.playerTakesDamage(1) + body.receive_damage(1, global_position) attack_hitbox.monitoring = false diff --git a/Scripts/gamecontroller.gd b/Scripts/gamecontroller.gd index 18cb6e2..313fd9b 100644 --- a/Scripts/gamecontroller.gd +++ b/Scripts/gamecontroller.gd @@ -24,6 +24,7 @@ var player:CharacterStats var enemy:CharacterStats var playerHealth:int +var playerLives: int = 3 var enemiesDict = {} @@ -112,3 +113,16 @@ func addEnemyToLevel(slime): "damage":enemy.meleeDamage + randDamage } enemiesDict[slime]=enemyStat + +func damage_player(amount: int) -> void: + playerHealth -= amount + + if playerHealth <= 0: + playerHealth = 0 + playerLives -= 1 + + print("Player lives remaining: ", playerLives) + + playerDiesSignal.emit() + else: + playerTakesDamageSignal.emit(playerHealth) diff --git a/Scripts/player.gd b/Scripts/player.gd index 359b492..9e67fed 100644 --- a/Scripts/player.gd +++ b/Scripts/player.gd @@ -14,6 +14,12 @@ const JUMP_VELOCITY = -400.0 @onready var punch_hitbox: Area2D = $PunchHitbox var using_child_form := false var punch_hitbox_start_x := 0.0 +var is_invulnerable := false +var is_knocked_back := false + +@export var knockback_strength := 250.0 +@export var knockback_upward := 100.0 +@export var knockback_duration := 0.25 enum FaceDirection{LEFT, RIGHT} var facing:FaceDirection = FaceDirection.RIGHT @@ -34,6 +40,8 @@ var stairs_z_above := 0 # filled by stairs trigger var stairs_z_below := -1 # filled by stairs trigger var stairs_node: Node = null +var is_dead := false + signal deathAnimationCompleteSignal func _physics_process(delta: float) -> void: @@ -68,7 +76,7 @@ func _physics_process(delta: float) -> void: move_and_slide() handle_collisions() - + func update_states(): match current_state: #idle when movement in x @@ -85,12 +93,12 @@ func update_states(): current_state = State.IDLE else: current_state = State.RUN - + func get_active_graphic() -> AnimatedSprite2D: if using_child_form: return child_graphic return player_graphic - + func update_animation(): var graphic = get_active_graphic() match current_state: @@ -106,11 +114,22 @@ func update_animation(): State.HURT: graphic.play("hurt") State.DEATH: - graphic.play("death") + if graphic.animation != "death": + graphic.play("death") State.PUNCH: graphic.play("punch") - + func handle_movement(_delta): + if is_dead: + velocity = Vector2.ZERO + return + + if is_knocked_back: + return + + if is_knocked_back: + return + if direction: velocity.x = direction * SPEED if direction <0: @@ -146,6 +165,9 @@ func update_punch_hitbox_position() -> void: print("PunchHitbox X: ", punch_hitbox.position.x) func handle_input(): + if is_dead: + return + direction = Input.get_axis("ui_left", "ui_right") if direction < 0: @@ -181,7 +203,6 @@ func handle_input(): %SceneManager.makeBullet(right_spawn.global_transform, 700) if facing == FaceDirection.LEFT: %SceneManager.makeBullet(left_spawn.global_transform, -700) - func handle_collisions(): for i in get_slide_collision_count(): @@ -207,7 +228,6 @@ func handle_collisions(): if not left_cast.is_colliding() && not right_cast.is_colliding(): pushEnabled = false - const STAIRS_LAYER := 3 # correct layer number for "stairs" func _ready() -> void: @@ -274,6 +294,9 @@ func on_stairs_top_reached(stairs: Node) -> void: _end_stairs_mode() func _do_punch_damage() -> void: + if is_dead: + return + print("Punch damage started") punch_hitbox.monitoring = true @@ -300,9 +323,15 @@ func _on_animation_finished() -> void: current_state = State.IDLE State.DEATH: deathAnimationCompleteSignal.emit() + # Wait 2 seconds after the death animation + await get_tree().create_timer(2.0).timeout + if Gamecontroller.playerLives > 0: + get_tree().change_scene_to_file("res://Scenes/ambulance_cutscene.tscn") + else: + get_tree().change_scene_to_file("res://Scenes/final_death_cutscene.tscn") State.PUNCH: current_state = State.IDLE - + func become_child() -> void: using_child_form = true @@ -318,12 +347,63 @@ func become_adult() -> void: child_graphic.visible = false print("Player switched to ADULT form") - + +func flash_while_invulnerable() -> void: + var graphic = get_active_graphic() + + while is_invulnerable: + graphic.visible = false + await get_tree().create_timer(0.1).timeout + + graphic.visible = true + await get_tree().create_timer(0.1).timeout + +func receive_damage(amount: int, attacker_position: Vector2) -> void: + if is_dead: + return + + if is_invulnerable: + print("Player is invulnerable - damage ignored") + return + + is_invulnerable = true + is_knocked_back = true + + # Push player AWAY from whoever attacked + var knockback_direction: float = sign(global_position.x - attacker_position.x) + + # Fallback in case both are exactly on top of one another + if knockback_direction == 0: + knockback_direction = -1 + + velocity.x = knockback_direction * knockback_strength + velocity.y = -knockback_upward + + Gamecontroller.damage_player(amount) + + flash_while_invulnerable() + + await get_tree().create_timer(knockback_duration).timeout + is_knocked_back = false + + await get_tree().create_timer(1.0 - knockback_duration).timeout + + is_invulnerable = false + get_active_graphic().visible = true + print("Player can take damage again") + func playerTakesDamage(health): - print("Player sees remaining health "+str(health)) + print("Player sees remaining health " + str(health)) current_state = State.HURT func playerDies(): + if is_dead: + return + + is_dead = true + is_invulnerable = true + is_knocked_back = false + velocity = Vector2.ZERO + print("Player sees they died.") current_state = State.DEATH - diff --git a/Scripts/rscs/playerStats.tres b/Scripts/rscs/playerStats.tres index 88763dc..3380a6a 100644 --- a/Scripts/rscs/playerStats.tres +++ b/Scripts/rscs/playerStats.tres @@ -4,9 +4,9 @@ [resource] script = ExtResource("1_eiyfu") -max_health = 100 -starting_health = 100 -health = 100 +max_health = 3 +starting_health = 3 +health = 3 meleeDamage = 5 rangeDamage = 10 metadata/_custom_type_script = "uid://b4t8cviiuvt18" diff --git a/Scripts/skeleton.gd b/Scripts/skeleton.gd index 98a16dd..7bea5a8 100644 --- a/Scripts/skeleton.gd +++ b/Scripts/skeleton.gd @@ -35,6 +35,15 @@ func _ready() -> void: func _physics_process(delta: float) -> void: if is_dead: return + + if player and player.is_dead: + player = null + is_attacking = false + attack_hitbox.monitoring = false + velocity.x = 0 + sprite.play("idle") + move_and_slide() + return if not is_on_floor(): velocity.y += gravity * delta @@ -100,9 +109,9 @@ func do_attack_damage() -> void: print("Bodies found: ", bodies.size()) for body in bodies: - if body is Player: - print("PLAYER HIT") - body.playerTakesDamage(1) + if body is Player and not body.is_dead: + print("SKELETON HIT PLAYER") + body.receive_damage(1, global_position) attack_hitbox.monitoring = false