diff --git a/DungeonEntrance.tscn b/DungeonEntrance.tscn index 10dc03d..d7915b5 100644 --- a/DungeonEntrance.tscn +++ b/DungeonEntrance.tscn @@ -1,11 +1,14 @@ -[gd_scene load_steps=7 format=3 uid="uid://b55f770t7xs6a"] +[gd_scene load_steps=9 format=3 uid="uid://b55f770t7xs6a"] [ext_resource type="Texture2D" uid="uid://npjqgc3tdgs1" path="res://gimp/DungeonEntrance.png" id="1_vo6aq"] [ext_resource type="Texture2D" uid="uid://nocjsuuft8qx" path="res://gimp/DungeonGuard.png" id="2_nujkm"] +[ext_resource type="Script" path="res://DungeonGuard.gd" id="3_hisk5"] -[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_18p8i"] -radius = 15.0 -height = 32.0 +[sub_resource type="RectangleShape2D" id="RectangleShape2D_fey6x"] +size = Vector2(30.4094, 31.5789) + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_0v5y2"] +size = Vector2(30.4094, 31.5789) [sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_ocep5"] normal = Vector2(1, 0) @@ -35,13 +38,22 @@ texture_filter = 1 position = Vector2(0, -552) scale = Vector2(1.71, 1.71) texture = ExtResource("2_nujkm") +script = ExtResource("3_hisk5") -[node name="DungeonGuardStaticBody" type="StaticBody2D" parent="DungeonGuard"] -visible = false +[node name="DungeonGuardBody" type="StaticBody2D" parent="DungeonGuard"] -[node name="DungeonGuardCollider" type="CollisionShape2D" parent="DungeonGuard/DungeonGuardStaticBody"] -visible = false -shape = SubResource("CapsuleShape2D_18p8i") +[node name="DungeonGuardCollider" type="CollisionShape2D" parent="DungeonGuard/DungeonGuardBody"] +shape = SubResource("RectangleShape2D_fey6x") + +[node name="Area2D" type="Area2D" parent="DungeonGuard"] +collision_layer = 3 +collision_mask = 0 +input_pickable = false +monitoring = false + +[node name="CollisionShape2D" type="CollisionShape2D" parent="DungeonGuard/Area2D"] +shape = SubResource("RectangleShape2D_0v5y2") +debug_color = Color(0.643137, 0.341176, 0.988235, 0.419608) [node name="StaticBody2D" type="StaticBody2D" parent="."] diff --git a/DungeonGuard.gd b/DungeonGuard.gd new file mode 100644 index 0000000..68d84c4 --- /dev/null +++ b/DungeonGuard.gd @@ -0,0 +1,23 @@ +extends Sprite2D + +var health = 30 +var health_dirty = true + +# 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): + if health_dirty: + health_dirty = false + var hp_label = find_child("GuardHPLabel") + if hp_label == null: + hp_label = Label.new() + hp_label.set_name(&"GuardHPLabel") + add_child(hp_label, true) + hp_label.set_owner(self) + hp_label.position.y += 20.0 + hp_label.position.x -= 20.0 + hp_label.text = "%d HP" % health diff --git a/MainLogic.gd b/MainLogic.gd index c9744b8..fafc75e 100644 --- a/MainLogic.gd +++ b/MainLogic.gd @@ -14,6 +14,10 @@ var sfx_select var sfx_confirm var sfx_cancel var sfx_summon +var sfx_enemy_hit +var sfx_parry +var sfx_sword_hit +var sfx_hammer_hit @onready var camera = $Camera2D @@ -36,6 +40,8 @@ const diamond_dist_rate = 50.0 const diamond_start_dist = 800.0 const diamond_min_dist = 150.0 +const parry_penalty = 0.5 + enum StateT { Start, Start_TextRendered, @@ -67,8 +73,17 @@ enum BattleState { SummonSword, SummonHammer, EnemyAttack, + PlayerAttack, + PlayerWin, + PlayerLose, } +enum GuardPhase { + Stomp, +} + +var guard_phase = GuardPhase.Stomp + static var state_dict = {} var tween_volume @@ -80,12 +95,17 @@ var diamonds_gone = false var gander var level +var level_guard_static_body = null var level_guard = null +var level_guard_cached_pos = null var level_cached_pos = null var viewport_size +var play_attack_sfx_type = null +var play_attack_sfx_once = false + # Called when the node enters the scene tree for the first time. func _ready(): if not state_dict.has("state"): @@ -106,6 +126,10 @@ func _ready(): sfx_confirm = load("res://audio/LD55_sfx_confirm.mp3") sfx_cancel = load("res://audio/LD55_sfx_cancel.mp3") sfx_summon = load("res://audio/LD55_sfx_summon.mp3") + sfx_enemy_hit = load("res://audio/LD55_sfx_enemy_hit.mp3") + sfx_parry = load("res://audio/LD55_sfx_parry.mp3") + sfx_sword_hit = load("res://audio/LD55_sfx_sword_hit.mp3") + sfx_hammer_hit = load("res://audio/LD55_sfx_hammer_hit.mp3") func update_text(text, next_state): if state_dict["timer"] > text_speed: @@ -144,6 +168,7 @@ func _process(delta): var gander_scene = preload("res://gander_schwartz.tscn") gander = gander_scene.instantiate() add_child(gander) + gander.set_owner(self) gander.position.x = 800 gander.position.y = 50 gander.velocity.x = -gander.SPEED @@ -206,13 +231,16 @@ func _process(delta): StateT.Dungeon_Entrance: camera_to_gander(delta) if level_guard == null: - level_guard = level.find_child("DungeonGuardStaticBody") - if level_guard != null and gander.last_collided_id == level_guard.get_instance_id(): + level_guard = level.find_child("DungeonGuard") + if level_guard_static_body == null: + level_guard_static_body = level.find_child("DungeonGuardBody") + if level_guard_static_body != null and gander.last_collided_id == level_guard_static_body.get_instance_id(): print("collided with guard.") gander.last_collided_id = null music_player.stop() music_player.stream = preload("res://audio/LD55_3.mp3") music_player.stream.loop = true + music_player.volume_db = -8.0 music_player.play() level.find_child("DungeonGuardCollider").set_deferred("disabled", true) gander.find_child("CollisionShape2D").set_deferred("disabled", true) @@ -229,6 +257,8 @@ func _process(delta): tween_scene.tween_callback(func(): gander.auto_control_action = "facing_left" state_dict["state"] = StateT.Dungeon_Entrance_Battle + state_dict["parry_timer"] = 0.0 + state_dict["parry_failed_played"] = false state_dict["battle_state"] = BattleState.MainMenu state_dict["battle_menu_setup"] = false state_dict["battle_refresh_gui"] = false @@ -247,10 +277,19 @@ func _process(delta): tween_text.tween_callback(func(): camera.remove_child(indicator_arrow) ) + gander.battle_started = true ) StateT.Dungeon_Entrance_Battle: + if state_dict["battle_state"] == BattleState.EnemyAttack: + if level_guard_cached_pos == null: + level_guard_cached_pos = level_guard.position camera_to_target(delta, level_cached_pos + Vector2(0.0, 500.0)) setup_battle_menu() + state_dict["parry_timer"] -= delta + if not gander.parry_success and gander.hit_active and not state_dict["parry_failed_played"]: + sfx_player.stream = sfx_enemy_hit + sfx_player.play() + state_dict["parry_failed_played"] = true _: pass if gander is MainCharacter and not gander.player_controlled and gander.current_scene_type == gander.GanderSceneT.Introduction: @@ -407,6 +446,25 @@ func camera_to_target(delta, vec2): camera.position += diff * delta * camera_move_speed func setup_battle_menu(): + if state_dict["battle_state"] != BattleState.PlayerWin and state_dict["battle_state"] != BattleState.PlayerLose: + if gander.health <= 0: + state_dict["battle_state"] = BattleState.PlayerLose + music_player.stop() + music_player.stream = load("res://audio/LD55_5.mp3") + music_player.play() + remove_child(gander) + main_label.text = "Thanks for playing! I ran out of time for this overly ambitious game..." + lower_label.text = "Ohhhh, it ended with a game over... Better luck next time?" + elif level_guard.health <= 0: + state_dict["battle_state"] = BattleState.PlayerWin + music_player.stop() + music_player.stream = load("res://audio/LD55_4.mp3") + music_player.play() + level_guard.get_parent().remove_child(level_guard) + main_label.text = "Thanks for playing! I ran out of time for this overly ambitious game..." + lower_label.text = "Good show, good show!" + elif state_dict["battle_state"] == BattleState.PlayerWin or state_dict["battle_state"] == BattleState.PlayerLose: + return match state_dict["battle_state"]: BattleState.MainMenu: if not state_dict["battle_menu_setup"] or state_dict["battle_refresh_gui"]: @@ -477,7 +535,7 @@ func setup_battle_menu(): var summon_node = find_child("SummonAttempt") if summon_node.success_count >= summon_node.MAX_SUCCESS: camera.remove_child(summon_node) - var summon_item = find_child("SummonItem") + var summon_item = gander.find_child("SummonItem") if summon_item == null: summon_item = Sprite2D.new() summon_item.texture = load("res://gimp/sword.png") @@ -487,19 +545,26 @@ func setup_battle_menu(): else: summon_item.texture = load("res://gimp/sword.png") gander.summon_item = summon_item + state_dict["battle_item"] = summon_item + gander.attack_type = gander.AttackType.SWORD + gander.summon_item_summoned = true sfx_player.stream = sfx_summon sfx_player.play() state_dict["battle_state"] = BattleState.EnemyAttack + level_guard_cached_pos = null + pick_guard_phase() elif summon_node.error_count >= summon_node.MAX_ERRORS: tween_scene = get_tree().create_tween() tween_scene.tween_method(func(c): for i in range(8): summon_node.summon_arrows_arr[i].self_modulate = c, Color(1.0, 0.0, 0.0), Color(1.0, 0.0, 0.0, 0.0), 1.0) tween_scene.tween_callback(func(): camera.remove_child(summon_node)) state_dict["battle_state"] = BattleState.EnemyAttack + level_guard_cached_pos = null + pick_guard_phase() BattleState.SummonHammer: var summon_node = find_child("SummonAttempt") if summon_node.success_count >= summon_node.MAX_SUCCESS: camera.remove_child(summon_node) - var summon_item = find_child("SummonItem") + var summon_item = gander.find_child("SummonItem") if summon_item == null: summon_item = Sprite2D.new() summon_item.texture = load("res://gimp/hammer.png") @@ -509,27 +574,92 @@ func setup_battle_menu(): else: summon_item.texture = load("res://gimp/hammer.png") gander.summon_item = summon_item + state_dict["battle_item"] = summon_item + gander.attack_type = gander.AttackType.HAMMER + gander.summon_item_summoned = true sfx_player.stream = sfx_summon sfx_player.play() state_dict["battle_state"] = BattleState.EnemyAttack + level_guard_cached_pos = null + pick_guard_phase() elif summon_node.error_count >= summon_node.MAX_ERRORS: tween_scene = get_tree().create_tween() tween_scene.tween_method(func(c): for i in range(8): summon_node.summon_arrows_arr[i].self_modulate = c, Color(1.0, 0.0, 0.0), Color(1.0, 0.0, 0.0, 0.0), 1.0) tween_scene.tween_callback(func(): camera.remove_child(summon_node)) state_dict["battle_state"] = BattleState.EnemyAttack + level_guard_cached_pos = null + pick_guard_phase() BattleState.EnemyAttack: if not state_dict["battle_menu_setup"] or state_dict["battle_refresh_gui"]: state_dict["battle_menu_setup"] = true state_dict["battle_refresh_gui"] = false + state_dict["parry_failed_played"] = false var battle_arrow = camera.find_child("BattleArrow") var battle_menu_item_0 = camera.find_child("BattleMenuItem0") var battle_menu_item_1 = camera.find_child("BattleMenuItem1") battle_arrow.self_modulate = Color(1, 1, 1, 0) battle_menu_item_0.text = "" battle_menu_item_1.text = "" + if gander.summon_item != null: + lower_label.text = "Press key-Z, button-A, or Spacebar at the right time to parry!" + match guard_phase: + GuardPhase.Stomp: + tween_scene = get_tree().create_tween() + var diff = gander.position.x - level_guard_cached_pos.x + tween_scene.tween_property(level_guard, "position", level_guard_cached_pos + Vector2(diff / 2.0, -200.0), 1.0) + tween_scene.tween_property(level_guard, "position", level_guard_cached_pos + Vector2(diff / 2.0, 0.0), 0.7) + tween_scene.tween_property(level_guard, "position", level_guard_cached_pos + Vector2(diff, -200.0), 1.0) + tween_scene.tween_property(level_guard, "position", level_guard_cached_pos + Vector2(diff, 0.0), 0.7) + tween_scene.tween_callback(func(): + state_dict["battle_state"] = BattleState.MainMenu + state_dict["battle_menu_setup"] = false + lower_label.text = "" + ) + tween_scene.tween_property(level_guard, "position", level_guard_cached_pos, 1.0) + _: + state_dict["battle_state"] = BattleState.MainMenu + state_dict["battle_menu_setup"] = false + BattleState.PlayerAttack: + if gander.attack_target == null: + state_dict["battle_state"] = BattleState.EnemyAttack + state_dict["battle_menu_setup"] = false + play_attack_sfx_type = null + play_attack_sfx_once = false + elif not play_attack_sfx_once and play_attack_sfx_type != null: + var rng = RandomNumberGenerator.new() + if play_attack_sfx_type == "sword": + sfx_player.stream = sfx_sword_hit + sfx_player.play() + level_guard.health -= rng.randi_range(3, 4) + level_guard.health_dirty = true + elif play_attack_sfx_type == "hammer": + sfx_player.stream = sfx_hammer_hit + sfx_player.play() + level_guard.health -= rng.randi_range(2, 5) + level_guard.health_dirty = true + play_attack_sfx_type = null + play_attack_sfx_once = true func handle_battle_input(event: InputEvent): - if state_dict["battle_state"] == BattleState.SummonSword or state_dict["battle_state"] == BattleState.SummonHammer: + if state_dict["battle_state"] == BattleState.SummonSword or state_dict["battle_state"] == BattleState.SummonHammer \ + or state_dict["battle_state"] == BattleState.PlayerWin or state_dict["battle_state"] == BattleState.PlayerLose: + return + elif state_dict["battle_state"] == BattleState.EnemyAttack: + if event.is_pressed() and event.is_action("Confirm"): + if state_dict["parry_timer"] < 0.0 and gander.parry_active and not gander.hit_active and gander.summon_item != null: + lower_label.text = "Nice parry!" + gander.parry_success = true + sfx_player.stream = sfx_parry + sfx_player.play() + tween_scene.kill() + tween_scene = get_tree().create_tween() + tween_scene.tween_property(level_guard, "position", level_guard_cached_pos, 0.8) + tween_scene.tween_callback(func(): + state_dict["battle_state"] = BattleState.MainMenu + state_dict["battle_menu_setup"] = false + lower_label.text = "" + ) + state_dict["parry_timer"] = parry_penalty return if event.is_pressed(): if event.is_action("Down"): @@ -550,8 +680,12 @@ func handle_battle_input(event: InputEvent): func handle_battle_action(action): match action: "attack": + state_dict["battle_state"] = BattleState.PlayerAttack + state_dict["battle_menu_setup"] = false sfx_player.stream = sfx_confirm sfx_player.play() + if gander.summon_item != null: + gander.attack_target = level_guard "summon": state_dict["battle_state"] = BattleState.SummonMenu state_dict["battle_menu_setup"] = false @@ -592,3 +726,6 @@ func battle_arrow_positioning(): battle_arrow.position.y = (viewport_size.y + arrow_rect.size.y) / 2.0 - (arrow_rect.size.y * (state_dict["battle_options"].size() - state_dict["battle_selection"])) sfx_player.stream = sfx_select sfx_player.play() + +func pick_guard_phase(): + guard_phase = GuardPhase.Stomp diff --git a/audio/LD55_sfx_enemy_hit.mp3 b/audio/LD55_sfx_enemy_hit.mp3 new file mode 100644 index 0000000..6eddbc7 Binary files /dev/null and b/audio/LD55_sfx_enemy_hit.mp3 differ diff --git a/audio/LD55_sfx_enemy_hit.mp3.import b/audio/LD55_sfx_enemy_hit.mp3.import new file mode 100644 index 0000000..e3813a8 --- /dev/null +++ b/audio/LD55_sfx_enemy_hit.mp3.import @@ -0,0 +1,19 @@ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://bu34dnb7q3arr" +path="res://.godot/imported/LD55_sfx_enemy_hit.mp3-33970ec45349dad7d9a01186fd03e09a.mp3str" + +[deps] + +source_file="res://audio/LD55_sfx_enemy_hit.mp3" +dest_files=["res://.godot/imported/LD55_sfx_enemy_hit.mp3-33970ec45349dad7d9a01186fd03e09a.mp3str"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/audio/LD55_sfx_parry.mp3 b/audio/LD55_sfx_parry.mp3 new file mode 100644 index 0000000..ff066d6 Binary files /dev/null and b/audio/LD55_sfx_parry.mp3 differ diff --git a/audio/LD55_sfx_parry.mp3.import b/audio/LD55_sfx_parry.mp3.import new file mode 100644 index 0000000..44c7c83 --- /dev/null +++ b/audio/LD55_sfx_parry.mp3.import @@ -0,0 +1,19 @@ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://cnumagq6o3qxj" +path="res://.godot/imported/LD55_sfx_parry.mp3-7a46deb538aa1e0147d4915f42b03709.mp3str" + +[deps] + +source_file="res://audio/LD55_sfx_parry.mp3" +dest_files=["res://.godot/imported/LD55_sfx_parry.mp3-7a46deb538aa1e0147d4915f42b03709.mp3str"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/gander_schwartz.gd b/gander_schwartz.gd index 55a22c1..08ec5df 100644 --- a/gander_schwartz.gd +++ b/gander_schwartz.gd @@ -13,21 +13,149 @@ var auto_control_action = "facing_front" var last_collided_id = null +@onready var parry_area = $ParryArea2D +@onready var hit_area = $HitArea2D + @onready var animated = $AnimatedSprite2D +var battle_started = false + +var health = 30 +var health_dirty = true + var summon_item = null var summon_item_angle = 0.0 +var summon_item_summoned = false +var summon_tween + +var parry_tween = null + +var parry_active = false +var parry_body = null +var hit_active = false +var parry_success = false +var parry_success_checked = false + +enum AttackType { + SWORD, + HAMMER, +} + +var attack_target = null +var attack_tween = null +var attack_type = AttackType.SWORD const SPEED = 150.0 const ANIM_DEADZONE = 0.3 const SUMMON_ITEM_DIST = 100.0 const SUMMON_ITEM_Y_OFFSET = -30.0 +func parry_entered(node): + parry_active = true + parry_success_checked = false + parry_body = node +func parry_exited(node): + parry_active = false + parry_body = null + if battle_started: + if parry_success: + print("Successful parry") + parry_success = false + else: + print("Failed to parry") + var rng = RandomNumberGenerator.new() + health -= rng.randi_range(1, 5) + health_dirty = true + +func hit_entered(node): + hit_active = true +func hit_exited(node): + hit_active = false + +func _ready(): + parry_area.area_entered.connect(parry_entered) + parry_area.area_exited.connect(parry_exited) + hit_area.area_entered.connect(hit_entered) + hit_area.area_exited.connect(hit_exited) + func _process(delta): - summon_item_angle += delta - if summon_item != null: - summon_item.position.x = cos(summon_item_angle) * SUMMON_ITEM_DIST - summon_item.position.y = sin(summon_item_angle) * SUMMON_ITEM_DIST + SUMMON_ITEM_Y_OFFSET + if parry_tween == null and attack_target == null: + summon_item_angle += delta + if summon_item_angle > TAU: + summon_item_angle -= TAU + if summon_item != null and not parry_success: + summon_item.position.x = cos(-summon_item_angle) * SUMMON_ITEM_DIST + summon_item.position.y = sin(-summon_item_angle) * SUMMON_ITEM_DIST + SUMMON_ITEM_Y_OFFSET + elif parry_success and not parry_success_checked and parry_body != null: + parry_success_checked = true + parry_tween = get_tree().create_tween() + parry_tween.set_parallel() + parry_tween.set_trans(Tween.TRANS_SPRING) + var between = parry_body.get_parent().position - self.position + parry_tween.tween_property(summon_item, "position", between, 0.2) + parry_tween.set_trans(Tween.TRANS_EXPO) + parry_tween.tween_property(summon_item, "rotation", TAU * 10, 0.3) + parry_tween.set_parallel(false) + var old_x = cos(-summon_item_angle) * SUMMON_ITEM_DIST + var old_y = sin(-summon_item_angle) * SUMMON_ITEM_DIST + SUMMON_ITEM_Y_OFFSET + parry_tween.set_trans(Tween.TRANS_EXPO) + parry_tween.tween_property(summon_item, "position", Vector2(old_x, old_y), 0.4) + parry_tween.tween_callback(func(): + parry_tween = null + summon_item.rotation = 0.0 + ) + elif attack_target != null and summon_item != null: + attack_tween = get_tree().create_tween() + var prev_x = cos(-summon_item_angle) * SUMMON_ITEM_DIST + var prev_y = sin(-summon_item_angle) * SUMMON_ITEM_DIST + SUMMON_ITEM_Y_OFFSET + attack_tween.set_parallel() + attack_tween.tween_property(summon_item, "position", attack_target.position - self.position, 0.2) + attack_tween.tween_property(summon_item, "rotation", -PI / 2.0, 0.2) + attack_tween.set_parallel(false) + var parent = get_parent() + attack_tween.tween_callback(func(): + match attack_type: + AttackType.SWORD: + parent.play_attack_sfx_type = "sword" + AttackType.HAMMER: + parent.play_attack_sfx_type = "hammer" + ) + attack_tween.set_parallel() + attack_tween.tween_property(summon_item, "position", Vector2(prev_x, prev_y), 0.4) + attack_tween.tween_property(summon_item, "rotation", 0.0, 0.4) + attack_tween.set_parallel(false) + attack_tween.tween_callback(func(): + attack_target = null + ) + + if summon_item_summoned: + summon_item_summoned = false + var summon_overlay = Sprite2D.new() + summon_overlay.set_name(&"SummonOverlay") + summon_overlay.set_texture(summon_item.get_texture()) + add_child(summon_overlay, true) + summon_overlay.set_owner(self) + summon_overlay.set_scale(Vector2(5.0, 5.0)) + summon_overlay.self_modulate = Color(1.0, 1.0, 1.0, 0.4) + summon_tween = get_tree().create_tween() + summon_tween.set_parallel() + summon_tween.set_trans(Tween.TRANS_EXPO) + summon_tween.tween_property(summon_overlay, "rotation", -TAU * 50.0, 2.0) + summon_tween.set_trans(Tween.TRANS_QUAD) + summon_tween.tween_property(summon_overlay, "self_modulate", Color(1.0, 1.0, 1.0, 0.0), 2.0) + summon_tween.set_parallel(false) + summon_tween.tween_callback(func(): self.remove_child(summon_overlay)) + if health_dirty: + health_dirty = false + var hp_label = find_child("PlayerHPLabel") + if hp_label == null: + hp_label = Label.new() + hp_label.set_name(&"PlayerHPLabel") + add_child(hp_label, true) + hp_label.set_owner(self) + hp_label.position.y += 10.0 + hp_label.position.x -= 18.0 + hp_label.text = "%d HP" % health func _physics_process(delta): var vec2 = Vector2() diff --git a/gander_schwartz.tscn b/gander_schwartz.tscn index 4deb67f..f71ba98 100644 --- a/gander_schwartz.tscn +++ b/gander_schwartz.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=21 format=3 uid="uid://ktxqc7xtqkex"] +[gd_scene load_steps=23 format=3 uid="uid://ktxqc7xtqkex"] [ext_resource type="Script" path="res://gander_schwartz.gd" id="1_6ob4s"] [ext_resource type="Texture2D" uid="uid://cv0qw4j1q78r8" path="res://gimp/GanderSchwartz_spritesheet.png" id="1_n7bds"] @@ -162,6 +162,14 @@ animations = [{ radius = 15.0 height = 80.0 +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_sy0be"] +radius = 32.0 +height = 126.0 + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_58op3"] +radius = 13.0 +height = 76.0 + [node name="GanderSchwartz" type="CharacterBody2D"] motion_mode = 1 script = ExtResource("1_6ob4s") @@ -178,3 +186,25 @@ frame_progress = 0.126405 [node name="CollisionShape2D" type="CollisionShape2D" parent="."] position = Vector2(0, -40) shape = SubResource("CapsuleShape2D_u3k2h") + +[node name="ParryArea2D" type="Area2D" parent="."] +collision_layer = 4 +collision_mask = 2 +input_pickable = false +monitorable = false + +[node name="CollisionShape2D" type="CollisionShape2D" parent="ParryArea2D"] +position = Vector2(0, -40) +shape = SubResource("CapsuleShape2D_sy0be") +debug_color = Color(0.631373, 0.352941, 0.984314, 0.419608) + +[node name="HitArea2D" type="Area2D" parent="."] +collision_layer = 4 +collision_mask = 2 +input_pickable = false +monitorable = false + +[node name="CollisionShape2D" type="CollisionShape2D" parent="HitArea2D"] +position = Vector2(0, -40) +shape = SubResource("CapsuleShape2D_58op3") +debug_color = Color(0.988235, 0, 0.290196, 0.419608)