ui; game saving and loading; bad guy movement; damage and death anims

This commit is contained in:
torontocaper committed 2025-11-10 21:17:55 -05:00
1 parent f0fec8bfc7
commit 09a6140d15
45 files changed
+534 -183

No files matched your search

+44 -1
View File
@@ -13,11 +13,16 @@ signal level_changed(level)
signal destroyed(body)
signal player_damaged(health, max_health)
signal player_death
signal game_loaded(stashed_data)
signal game_saved(level, time_remaining, player_health, enemies_dictionary)
signal timer_updated(time_remaining)
signal coin_collected(coins_remaining)
var enemy_stats : CharacterStats
var player_stats : CharacterStats
var enemies_dict : Dictionary = {}
var player_current_health : int = 0
var stash_data : Dictionary = {}
func _ready() -> void:
# load in characters
@@ -30,8 +35,17 @@ func _ready() -> void:
timer.connect("timeout", second_counter)
timer.start()
func _process(_delta: float) -> void:
if Input.is_action_just_pressed("save"):
save_game()
pass
if Input.is_action_just_pressed("load"):
load_game()
pass
func second_counter() -> void:
time_available -= 1
timer_updated.emit(time_available)
if time_available <= 0:
print_debug("You ran out of time! Emitting level_changed signal")
#get_tree().call_deferred("change_scene_to_file", levels[current_level])
@@ -63,6 +77,7 @@ func on_coin_collected(_body, coin) -> void:
destroyed.emit(coin)
func set_total_coins(value) -> void:
coin_collected.emit(value)
print_debug("Setting coin total to %s" % str(value))
if value == 0:
# You won!
@@ -86,13 +101,20 @@ func on_player_slimed(_body, slime) -> void:
player_damaged.emit(player_current_health, player_stats.starting_health)
print_debug("Player Health: " + str(player_current_health) + " of " + str(player_stats.starting_health))
func add_enemy_to_level(enemy) -> void:
func handle_player_death() -> void:
level_changed.emit(levels[current_level])
func add_enemy_to_level(enemy, _stat = null) -> void:
print_debug("GC adding %s to level" % enemy.name)
var rand_damage : int = randi() % 10
var this_enemy_stats : Dictionary = {
"health": enemy_stats.health,
"damage": enemy_stats.melee_damage + rand_damage
}
if _stat:
this_enemy_stats.health = _stat.health
this_enemy_stats.damage = _stat.damage
print_debug("This enemy's health is %s and its damage is %s" % [this_enemy_stats.health, this_enemy_stats.damage])
enemies_dict[enemy] = this_enemy_stats
func bullet_damage(thing_damaged, _bullet) -> void:
@@ -109,3 +131,24 @@ func bullet_damage(thing_damaged, _bullet) -> void:
func remove_enemy_from_level(enemy) -> void:
enemies_dict.erase(enemy)
func save_game() -> void:
game_saved.emit(current_level, time_available, player_current_health, enemies_dict)
func load_game() -> void:
if ResourceLoader.exists("res://resources/level" + str(current_level) + "_game_save_stats.tres"):
var saved_game : SaveObject = load("res://resources/level" + str(current_level) + "_game_save_stats.tres")
enemies_dict.clear()
stash_data = saved_game.game_save
game_loaded.emit(stash_data)
func stash_game(stash) -> void:
print_debug("Stashing game state")
stash_data = stash
var stash_object = SaveObject.new()
stash_object.game_save = stash
ResourceSaver.save(stash_object, "res://resources/level" + str(current_level) + "_game_save_stats.tres")
func set_player_health(value: int) -> void:
print_debug("Setting player health to %s" % str(value))
player_current_health = value
+27 -4
View File
@@ -4,6 +4,8 @@ extends CharacterBody2D
##
## Controls the player character in this 2D side-scrolling platformer
signal player_died
enum FaceDirection {
LEFT,
RIGHT
@@ -15,7 +17,9 @@ enum State {
FALL,
SHOOT_STILL,
SHOOT_RUN,
PUNCH
PUNCH,
HURT,
DIE
}
const JUMP_VELOCITY := -500.0 ## Power applied for jumping
@@ -49,14 +53,21 @@ func _ready() -> void:
add_child(shoot_cooldown_timer)
func _physics_process(delta: float) -> void:
handle_input()
if current_state != State.HURT and current_state != State.DIE:
handle_input()
move_and_slide()
handle_movement(delta)
move_and_slide()
handle_collisions()
update_state()
update_animation()
update_debug()
func handle_damage(_health, _max_health) -> void:
current_state = State.HURT
func handle_death() -> void:
current_state = State.DIE
func handle_input() -> void:
# Handle jumping.
@@ -144,7 +155,7 @@ func handle_collisions() -> void:
var collider = right_cast.get_collider()
#print_debug("Colliding with %s " % collider.name)
if collider is Node and collider is RigidBody2D and collider.is_in_group("punchable"):
print_debug("We have a punch target")
#print_debug("We have a punch target")
punch_target = collider
punch_enabled = true
@@ -201,6 +212,14 @@ func update_state() -> void:
await player_sprite.animation_finished
current_state = State.IDLE
State.HURT:
await player_sprite.animation_finished
current_state = State.IDLE
State.DIE:
await player_sprite.animation_finished
player_died.emit()
func update_animation() -> void:
match facing:
FaceDirection.LEFT:
@@ -222,6 +241,10 @@ func update_animation() -> void:
player_sprite.play("shoot_run")
State.PUNCH:
player_sprite.play("punch")
State.HURT:
player_sprite.play("hurt")
State.DIE:
player_sprite.play("die")
func update_debug():
%StateLabel.text = "Current state: %s" % State.keys()[current_state]
+88
View File
@@ -2,12 +2,18 @@ class_name SceneManager
extends Node2D
var bullet = preload("res://scenes/bullet.tscn")
var coin_scene = preload("res://scenes/coin.tscn")
var crate_scene = preload("res://scenes/small_crate.tscn")
var enemy_scene = preload("res://scenes/slimer.tscn")
var grenade = preload("res://scenes/grenade.tscn")
var bullet_array = []
var total_allowed_bullets : int = 7
@onready var coins: Node = $"../Coins"
@onready var crates: Node = $"../Crates"
@onready var enemies: Node = $"../Enemies"
@onready var player: Player = $"../Player"
@onready var ui: UI = $"../CanvasLayer/UI"
func _ready() -> void:
GameController.reset()
@@ -23,6 +29,14 @@ func build_level() -> void:
# Wire up signals from GameController
GameController.level_changed.connect(change_scene)
GameController.destroyed.connect(destroy)
GameController.game_saved.connect(on_game_saved)
GameController.game_loaded.connect(on_game_loaded)
GameController.player_damaged.connect(player.handle_damage)
GameController.player_damaged.connect(ui.update_health)
GameController.player_death.connect(player.handle_death)
player.player_died.connect(GameController.handle_player_death)
GameController.timer_updated.connect(ui.update_timer)
GameController.coin_collected.connect(ui.update_coins)
func update_enemies() -> void:
var total_enemies : int = 0
@@ -82,3 +96,77 @@ func change_scene(level) -> void:
func destroy(body) -> void:
body.queue_free()
func on_game_loaded(stash) -> void:
print_debug("Loading level with data %s" % str(stash))
var player_data = stash.player_data
var coins_data = stash.coins_data
var crates_data = stash.crates_data
var enemies_data = stash.enemies_data
# get rid of existing coins
if coins:
for coin in coins.get_children():
# remove listeners
coin.tree_exited.disconnect(update_coins)
coins.remove_child(coin)
coin.queue_free()
for coin_transform in coins_data:
var coin_object : Coin = coin_scene.instantiate()
coins.add_child(coin_object)
coin_object.transform = coin_transform
update_coins()
if crates:
for crate in crates.get_children():
crates.remove_child(crate)
crate.queue_free()
for crate_transform in crates_data:
var crate_object : RigidBody2D = crate_scene.instantiate()
crates.add_child(crate_object)
crate_object.transform = crate_transform
if enemies:
for enemy in enemies.get_children():
enemies.remove_child(enemy)
enemy.queue_free()
for enemy_dict in enemies_data:
var enemy_object : Slimer = enemy_scene.instantiate()
enemies.add_child(enemy_object)
enemy_object.transform = enemy_dict.position
GameController.add_enemy_to_level(enemy_object, enemy_dict)
player.transform = player_data.position
func on_game_saved(current_level: int, time_available: int, player_current_health: int, enemies_dict: Dictionary) -> void:
print_debug("Saving game!")
print_debug("Player health = %s" % str(player_current_health))
print_debug("Player transforms = %s" % str(player.transform))
var player_data = {
"health": player_current_health,
"position": player.transform,
}
var coin_data : Array ## Coin position(s)
if coins:
for coin in coins.get_children():
print_debug("This coin is at %s" % str(coin.transform))
coin_data.push_front(coin.transform)
var crate_data : Array ## Crate position(s)
if crates:
for crate in crates.get_children():
crate_data.push_front(crate.transform)
var enemy_data : Array ## Array of enemies at savepoint
for enemy_key in enemies_dict:
print_debug("Enemy found at %s" % enemy_key.transform)
print_debug("Enemy health is %s" % enemies_dict[enemy_key].damage)
print_debug("Enemy damage is %s" % enemies_dict[enemy_key].health)
var enemy = {
"position": enemy_key.transform,
"health": enemies_dict[enemy_key].health,
"damage": enemies_dict[enemy_key].damage
}
enemy_data.push_front(enemy)
var stash = {
"player_data": player_data,
"coins_data": coin_data,
"crates_data": crate_data,
"enemies_data": enemy_data
}
GameController.stash_game(stash)
+14 -1
View File
@@ -5,13 +5,26 @@ extends Area2D
@onready var left_cast: RayCast2D = $LeftCast
@onready var right_down_cast: RayCast2D = $RightDownCast
@onready var left_down_cast: RayCast2D = $LeftDownCast
@onready var slime_sprite: AnimatedSprite2D = $SlimeSprite
var speed : int = 100
var speed : int = 25
var direction : int = 1
signal player_slimed(body, slime)
func _process(delta: float) -> void:
if not right_down_cast.is_colliding():
direction = -1
slime_sprite.flip_h = true
#if not left_down_cast.is_colliding():
#direction = 1
#slime_sprite.flip_h = false
#if not right_cast.is_colliding():
#direction = -1
#slime_sprite.flip_h = true
#if not left_cast.is_colliding():
#direction = 1
#slime_sprite.flip_h = false
position.x += direction * speed * delta
func _on_body_entered(body: Node2D) -> void:
+15
View File
@@ -0,0 +1,15 @@
class_name UI
extends Control
@onready var health_label: Label = $VBoxContainer/HBoxContainer/MarginContainer/HealthLabel
@onready var timer_label: Label = $VBoxContainer/HBoxContainer/MarginContainer2/TimerLabel
@onready var coins_label: Label = $VBoxContainer/HBoxContainer/MarginContainer3/CoinsLabel
func update_health(current_health, max_health) -> void:
health_label.text = "Health: " + str(current_health)
func update_timer(time_remaining) -> void:
timer_label.text = "Time: " + str(time_remaining)
func update_coins(coins_remaining) -> void:
coins_label.text = "Coins: " + str(coins_remaining)
+1
View File
@@ -0,0 +1 @@
uid://bpyw8duggqhke