From 7ead578152cd4faed37290d9171de95e9f3f812a Mon Sep 17 00:00:00 2001 From: MarkYabut Date: Tue, 3 Nov 2026 20:50:59 -0500 Subject: [PATCH] fin~ player damage, player death, slime damag,e slime death, custom resources. multiple levels --- .../player/death/Player Death 64x64.png | Bin 0 -> 1584 bytes .../death/Player Death 64x64.png.import | 40 ++ .../player/hurt/Player Hurt 48x48.png | Bin 0 -> 1448 bytes .../player/hurt/Player Hurt 48x48.png.import | 40 ++ project.godot | 4 + scenes/bullet.tscn | 1 + scenes/game.tscn | 5 +- scenes/level2.tscn | 508 ++++++++++++++++++ scenes/level3.tscn | 508 ++++++++++++++++++ scenes/player.tscn | 117 +++- scripts/bullet.gd | 4 + scripts/gamecontroller.gd | 65 ++- scripts/player.gd | 24 +- scripts/rscs/characterstats.gd | 9 + scripts/rscs/characterstats.gd.uid | 1 + scripts/rscs/playerStats.tres | 11 + scripts/rscs/slimeStats.tres | 11 + scripts/scene_manager.gd | 46 +- scripts/slime.gd | 10 +- 19 files changed, 1383 insertions(+), 21 deletions(-) create mode 100644 assets/graphics/player/death/Player Death 64x64.png create mode 100644 assets/graphics/player/death/Player Death 64x64.png.import create mode 100644 assets/graphics/player/hurt/Player Hurt 48x48.png create mode 100644 assets/graphics/player/hurt/Player Hurt 48x48.png.import create mode 100644 scenes/level2.tscn create mode 100644 scenes/level3.tscn create mode 100644 scripts/rscs/characterstats.gd create mode 100644 scripts/rscs/characterstats.gd.uid create mode 100644 scripts/rscs/playerStats.tres create mode 100644 scripts/rscs/slimeStats.tres diff --git a/assets/graphics/player/death/Player Death 64x64.png b/assets/graphics/player/death/Player Death 64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..2e6a1ae0514b237d754893f563c87ba5f95dc109 GIT binary patch literal 1584 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 void: func setSpeed(value)->void: speed = value + + +func _on_area_entered(area: Area2D) -> void: + bulletDamageSignal.emit(area, self) diff --git a/scripts/gamecontroller.gd b/scripts/gamecontroller.gd index 4b455dc..0133dd2 100644 --- a/scripts/gamecontroller.gd +++ b/scripts/gamecontroller.gd @@ -2,16 +2,38 @@ class_name GameController extends Node2D signal teleportSignal(body:RigidBody2D) signal destroySignal(body) +signal damageSignal(body) var numberOfCrates = 2 var totalCoins = 0 var coinsRemaining = 0 var coinsCollected = 0 +var enemy:CharacterStats +var player:CharacterStats + +var enemiesDict={} + +var playerStats={} + + + +# level information +var levelArray = ["res://scenes/game.tscn", "res://scenes/level2.tscn", "res://scenes/level3.tscn"] +var currentLevel = 0 # Called when the node enters the scene tree for the first time. func _ready() -> void: get_window().grab_focus() + enemy = load("res://scripts/rscs/slimeStats.tres") + player = load("res://scripts/rscs/playerStats.tres") +func reset()->void: + playerStats={ + "health":player.currentHealth, + "meleeAttack":player.meleeDamage, + "rangeAttack":player.rangeDamage + } + coinsCollected = 0 # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: @@ -30,16 +52,34 @@ func _on_trigger(effect: Variant, Object: Variant) -> void: teleportSignal.emit(Object) func _on_bullet_hit(body, bullet) -> void: - print("GC knows bullet hit") + #print("GC knows bullet hit") if body is Crate: destroySignal.emit(body) destroySignal.emit(bullet) + if body is Slime: + enemiesDict[body]["health"] -= playerStats.rangeAttack + if enemiesDict[body]["health"] <= 0: + print("Enemy is dead") + destroySignal.emit(body) + destroySignal.emit(bullet) + removeEnemyFromLevel(body) + else: + print("Enemy takes damage") + destroySignal.emit(bullet) + damageSignal.emit(body) + func crateTotal(amount)->void: numberOfCrates = amount if numberOfCrates<=0: print("YOU WON!!!") + if currentLevelvoid: if totalCoins == 0: totalCoins = amount @@ -50,3 +90,26 @@ func coinCollected(body, coin)->void: #give them a coin coinsCollected +=1 destroySignal.emit(coin) +func slimeAttack(body, slime)->void: + print("GC knows slime attacking") + playerStats.health -= enemiesDict[slime].meleeDamage + if playerStats.health >0: + print("player takes damage") + damageSignal.emit(body) + else: + print("player is dead!!") + destroySignal.emit(body) + +func addEnemyToLevel(slime)->void: + print("Adding enemy to level") + var stat={ + "health":enemy.currentHealth + randi_range(0,10), + "meleeDamage":enemy.meleeDamage+ randi_range(0,10) + } + enemiesDict[slime]=stat +func removeEnemyFromLevel(enemy)->void: + print("removeEnemyFromLevel") + enemiesDict.erase(enemy) + +func levelChange()-> void: + get_tree().change_scene_to_file(levelArray[currentLevel]) diff --git a/scripts/player.gd b/scripts/player.gd index 01d4963..c28ef9c 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -11,10 +11,11 @@ class_name Player extends CharacterBody2D var direction signal bulletSignal(pos, speed) +signal deathCompleteSignal enum FaceDirection {LEFT,RIGHT} var facing:FaceDirection = FaceDirection.RIGHT -enum PlayerStates {IDLE,RUNNING,JUMPING,FALLING} +enum PlayerStates {IDLE,RUNNING,JUMPING,FALLING,HURT,DEATH} var current_player_state:PlayerStates = PlayerStates.IDLE var upJump:bool = false @@ -31,8 +32,9 @@ func _physics_process(delta: float) -> void: # Add the gravity. if not is_on_floor(): velocity += get_gravity() * delta - - handle_input() + + if not current_player_state == PlayerStates.DEATH: + handle_input() handle_movement() handle_states() handle_animations() @@ -133,9 +135,25 @@ func handle_animations()->void: player_graphic.play("jump") PlayerStates.FALLING: player_graphic.play("fall") + PlayerStates.HURT: + player_graphic.play("hurt") + PlayerStates.DEATH: + player_graphic.play("death") func _animation_finished() -> void: match current_player_state: PlayerStates.JUMPING: upJump = false + PlayerStates.HURT: + current_player_state = PlayerStates.IDLE + PlayerStates.DEATH: + deathCompleteSignal.emit() + +func takeDamage()->void: + print("player taking damage") + current_player_state = PlayerStates.HURT + +func kill()->void: + print("kill the player") + current_player_state = PlayerStates.DEATH diff --git a/scripts/rscs/characterstats.gd b/scripts/rscs/characterstats.gd new file mode 100644 index 0000000..28c02f9 --- /dev/null +++ b/scripts/rscs/characterstats.gd @@ -0,0 +1,9 @@ +class_name CharacterStats extends Resource + +@export var currentHealth := 100 +@export var maxHealth := 150 +@export var startingHealth := 100 + +#damage +@export var meleeDamage := 20 +@export var rangeDamage := 15 diff --git a/scripts/rscs/characterstats.gd.uid b/scripts/rscs/characterstats.gd.uid new file mode 100644 index 0000000..97639f4 --- /dev/null +++ b/scripts/rscs/characterstats.gd.uid @@ -0,0 +1 @@ +uid://bjb5jd6442ta7 diff --git a/scripts/rscs/playerStats.tres b/scripts/rscs/playerStats.tres new file mode 100644 index 0000000..edd4836 --- /dev/null +++ b/scripts/rscs/playerStats.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="CharacterStats" format=3 uid="uid://6ijgoaamy7w1"] + +[ext_resource type="Script" uid="uid://bjb5jd6442ta7" path="res://scripts/rscs/characterstats.gd" id="1_sqkfu"] + +[resource] +script = ExtResource("1_sqkfu") +currentHealth = 150 +startingHealth = 150 +meleeDamage = 0 +rangeDamage = 30 +metadata/_custom_type_script = "uid://bjb5jd6442ta7" diff --git a/scripts/rscs/slimeStats.tres b/scripts/rscs/slimeStats.tres new file mode 100644 index 0000000..9696d24 --- /dev/null +++ b/scripts/rscs/slimeStats.tres @@ -0,0 +1,11 @@ +[gd_resource type="Resource" script_class="CharacterStats" format=3 uid="uid://5bqgu1h1cmaa"] + +[ext_resource type="Script" uid="uid://bjb5jd6442ta7" path="res://scripts/rscs/characterstats.gd" id="1_ppipo"] + +[resource] +script = ExtResource("1_ppipo") +currentHealth = 80 +maxHealth = 90 +startingHealth = 80 +rangeDamage = 0 +metadata/_custom_type_script = "uid://bjb5jd6442ta7" diff --git a/scripts/scene_manager.gd b/scripts/scene_manager.gd index 993ef1b..7df3fb9 100644 --- a/scripts/scene_manager.gd +++ b/scripts/scene_manager.gd @@ -1,10 +1,10 @@ class_name SceneManager extends Node2D @onready var triggers: Node2D = $"../Triggers" -@onready var game: GameController = $".." @onready var player: Player = $"../Player" @onready var crates: Node2D = $"../Crates" @onready var coins: Node2D = $"../Coins" +@onready var enemies: Node2D = $"../Enemies" #recipe for bullets var bullet = preload("res://scenes/bullet.tscn") @@ -14,12 +14,16 @@ var bulletArray:Array[Bullet] = [] # Called when the node enters the scene tree for the first time. func _ready() -> void: + Gamecontroller.reset() buildLevel() - game.teleportSignal.connect(teleport) - game.destroySignal.connect(destroy) + Gamecontroller.teleportSignal.connect(teleport) + Gamecontroller.destroySignal.connect(destroy) + Gamecontroller.damageSignal.connect(damage) player.bulletSignal.connect(makeBullet) + player.deathCompleteSignal.connect(Gamecontroller.levelChange) updateCrates() updateCoins() + updateEnemies() # Called every frame. 'delta' is the elapsed time since the previous frame. @@ -36,7 +40,7 @@ func updateCrates()->void: obj.tree_exited.connect(updateCrates) totalCrates +=1 # tell the gamecontroller how many crates - game.crateTotal(totalCrates) + Gamecontroller.crateTotal(totalCrates) func updateCoins()->void: var totalCoins = 0 @@ -45,17 +49,27 @@ func updateCoins()->void: if obj is Coin: if not obj.tree_exited.is_connected(updateCoins): obj.tree_exited.connect(updateCoins) - if not obj.coinCollisionSignal.is_connected(game.coinCollected): - obj.coinCollisionSignal.connect(game.coinCollected) + if not obj.coinCollisionSignal.is_connected(Gamecontroller.coinCollected): + obj.coinCollisionSignal.connect(Gamecontroller.coinCollected) totalCoins +=1 - game.coinTotal(totalCoins) + Gamecontroller.coinTotal(totalCoins) + +func updateEnemies()->void: + if enemies: + for obj in enemies.get_children(): + var enemy = obj as Slime + if enemy: + Gamecontroller.addEnemyToLevel(enemy) + if not enemy.enemyAttackSignal.is_connected(Gamecontroller.slimeAttack): + enemy.enemyAttackSignal.connect(Gamecontroller.slimeAttack) + func buildLevel()->void: if triggers: for obj in triggers.get_children(): if obj is Trigger: - if not obj.areaTriggered.is_connected(game._on_trigger): - obj.areaTriggered.connect(game._on_trigger) + if not obj.areaTriggered.is_connected(Gamecontroller._on_trigger): + obj.areaTriggered.connect(Gamecontroller._on_trigger) func teleport(body, _bodyposition=null)->void: if body is Crate: @@ -80,8 +94,8 @@ func bulletFactory()->Bullet: myBullet = bullet.instantiate() add_sibling(myBullet) #wire up a signal to the game controller - if not myBullet.bulletDamageSignal.is_connected(game._on_bullet_hit): - myBullet.bulletDamageSignal.connect(game._on_bullet_hit) + if not myBullet.bulletDamageSignal.is_connected(Gamecontroller._on_bullet_hit): + myBullet.bulletDamageSignal.connect(Gamecontroller._on_bullet_hit) else: #recycle bullet myBullet = bulletArray.pop_back() @@ -97,6 +111,16 @@ func destroy(target)->void: target.queue_free() if target is Coin: target.queue_free() + if target is Slime: + target.queue_free() + if target is Player: + target.kill() + +func damage(target)->void: + if target is Slime: + target.takeDamage() + if target is Player: + target.takeDamage() func stash(target:Bullet)->void: print("stash a bullet") diff --git a/scripts/slime.gd b/scripts/slime.gd index 6896a91..430330f 100644 --- a/scripts/slime.gd +++ b/scripts/slime.gd @@ -14,7 +14,7 @@ var facing:FaceDirection = FaceDirection.RIGHT #direction 1 move to right, 0 no movement, -1 to the left var direction = 1 -enum EnemyStates {RUN, ATTACK, SPAWN} +enum EnemyStates {RUN, ATTACK, SPAWN, HURT} var currentEnemyState:EnemyStates = EnemyStates.SPAWN @@ -51,6 +51,8 @@ func handleAnimation()->void: slime_graphic.play("run") EnemyStates.ATTACK: slime_graphic.play("attack") + EnemyStates.HURT: + slime_graphic.play("attack") func handleCollisions()->void: if not right_down_cast.is_colliding(): @@ -84,3 +86,9 @@ func _on_animation_finished() -> void: EnemyStates.ATTACK: currentEnemyState = EnemyStates.RUN direction = (randi() % 2) *2 -1 + EnemyStates.HURT: + currentEnemyState = EnemyStates.RUN + +func takeDamage()->void: + print("Slime taking damage!!") + currentEnemyState = EnemyStates.HURT