Compare commits

...

10 commits

Author SHA1 Message Date
c6439e9660 Add LICENSE 2024-04-14 22:55:36 +09:00
6874bb28a8 Export settings 2024-04-14 22:47:21 +09:00
d919f42cbd Impl. summon health potion 2024-04-14 22:37:23 +09:00
e9bc30fd13 Work
Game now reaches the end after the guardian battle.

Work on parry system, battle system.
2024-04-14 22:21:54 +09:00
d4809bd78d Work
Added summon sfx.
2024-04-14 17:33:07 +09:00
174bd2575c Work
Some more work towards implementing battle.

Added summoning mini-game for summoning.
2024-04-14 17:24:30 +09:00
1fdd0d7ae6 Work
Tweaks and improvements.

Added a few sprites for battle.

Some setup for battle.
2024-04-14 15:31:39 +09:00
dc54aa3209 Work (music)
Add victory music and game-over music.
2024-04-13 21:17:39 +09:00
5a51da1968 Add two more audio files
Added "ambient noise", and "battle music".

TODO: Battle with Dungeon Guardian.
2024-04-13 20:19:30 +09:00
c180b0312c Work
Set up logic for collision with guard.

Some resizing of the DungeonEntrance scene.
2024-04-13 19:07:54 +09:00
43 changed files with 1477 additions and 27 deletions

View file

@ -1,30 +1,78 @@
[gd_scene load_steps=4 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)
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_gq7j0"]
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_xvl4y"]
normal = Vector2(-1, 0)
[node name="Node2D" type="Node2D"]
[node name="DungeonEntrance" type="Sprite2D" parent="."]
texture_filter = 1
position = Vector2(-1, -320)
position = Vector2(0, -784)
scale = Vector2(1.995, 1.995)
texture = ExtResource("1_vo6aq")
[node name="StaticBody2D" type="StaticBody2D" parent="DungeonEntrance"]
visible = false
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="DungeonEntrance/StaticBody2D"]
visible = false
polygon = PackedVector2Array(-160, 127, -159, -129, 160, -126, 161, 127, 21, 127, 21, 79, -19, 79, -19, 127)
[node name="DungeonGuard" type="Sprite2D" parent="."]
texture_filter = 1
position = Vector2(0, -208)
position = Vector2(0, -552)
scale = Vector2(1.71, 1.71)
texture = ExtResource("2_nujkm")
script = ExtResource("3_hisk5")
[node name="StaticBody2D" type="StaticBody2D" parent="DungeonGuard"]
[node name="DungeonGuardBody" type="StaticBody2D" parent="DungeonGuard"]
[node name="CollisionShape2D" type="CollisionShape2D" parent="DungeonGuard/StaticBody2D"]
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="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D"]
position = Vector2(-307, -1)
shape = SubResource("WorldBoundaryShape2D_ocep5")
[node name="CollisionShape2D2" type="CollisionShape2D" parent="StaticBody2D"]
position = Vector2(0, 243)
shape = SubResource("WorldBoundaryShape2D_gq7j0")
[node name="CollisionShape2D3" type="CollisionShape2D" parent="StaticBody2D"]
position = Vector2(314, 5)
shape = SubResource("WorldBoundaryShape2D_xvl4y")
[node name="Label" type="Label" parent="."]
z_index = 5
offset_left = -110.0
offset_top = 208.0
offset_right = 112.0
offset_bottom = 231.0
text = "The Dungeon is to the north!"

23
DungeonGuard.gd Normal file
View file

@ -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

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2024 Stephen Seo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -9,6 +9,19 @@ extends Node2D
@onready var earth_diamond = $EarthDiamond
@onready var music_player = $MusicPlayer
@onready var sfx_player = $SFXPlayer
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
const camera_move_speed = 1.5
const text_speed = 0.08
@ -17,8 +30,8 @@ const start_text = "You seek the elementals?\nProve your worth!\nShow the elemen
const intro_text_00 = "The name's Gander Schwartz.\nBut my friends call me the \"Wandering Gander\"."
const intro_text_01 = "I'm what most would call a \"third-rate summoner\",\nbut there's a reason for that.\nI summon \"items\", not \"beasts\"."
const intro_text_02 = "Most summoners summon beasts to fight for them.\nI summon items to fight with, or even tools to solve puzzles in dungeons."
const intro_text_03 = "There is one dungeon I've been itching to conquer.\nThe elemental dungeon!"
const intro_text_04 = "If I beat the elemental dungeon,\nI'll be able to enhance my summons with elemental properties!"
const intro_text_03 = "There is one dungeon I've been itching to conquer.\nThe Elemental Dungeon!"
const intro_text_04 = "If I beat the Elemental Dungeon,\nI'll be able to enhance my summons with elemental properties!"
const intro_text_05 = "No longer would I need to light an oil lantern with flint and steel,\nor keep lugging around leather skins for water!"
const intro_text_06 = "But the dungeon is a challenge.\nA challenge I hope to best with my wits and my item summoning!"
@ -27,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,
@ -46,19 +61,52 @@ enum StateT {
Introduction_05_post,
Introduction_06,
Introduction_06_post,
Dungeon_Entrance
Dungeon_Entrance_pre,
Dungeon_Entrance_loading,
Dungeon_Entrance,
Dungeon_Entrance_Battle,
}
enum BattleState {
MainMenu,
SummonMenu,
SummonSword,
SummonHammer,
SummonHealth,
EnemyAttack,
PlayerAttack,
PlayerWin,
PlayerLose,
}
enum GuardPhase {
Stomp,
}
var guard_phase = GuardPhase.Stomp
static var state_dict = {}
var tween_volume
var tween_text
var tween_scene
var diamonds_gone = false
var music_file
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"):
@ -70,6 +118,19 @@ func _ready():
"angle" : 0.0
}
music_player.play()
viewport_size = get_viewport().size
get_viewport().size_changed.connect(func():
viewport_size = get_viewport().size
state_dict["battle_refresh_gui"] = true
)
sfx_select = load("res://audio/LD55_sfx_select.mp3")
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:
@ -102,18 +163,19 @@ func _process(delta):
main_label.text = ""
lower_label.text = ""
music_player.volume_db = 0.0
music_player.stream = AudioStreamMP3.new()
music_file = FileAccess.open("res://audio/LD55_1.mp3", FileAccess.READ)
music_player.stream.data = music_file.get_buffer(music_file.get_length())
music_player.stream = load("res://audio/LD55_1.mp3")
music_player.stream.loop = true
music_player.play()
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
gander.auto_control_action = "walking_left"
tween_scene = get_tree().create_tween()
tween_scene.tween_method(func(c): RenderingServer.set_default_clear_color(c), Color(0.13, 0.13, 0.13, 1.0), Color(0.3, 0.4, 0.1, 1.0), 4)
StateT.Introduction_00:
update_stop_diamonds(delta)
update_text(intro_text_00, StateT.Introduction_00_post)
@ -149,6 +211,86 @@ func _process(delta):
update_text(intro_text_06, StateT.Introduction_06_post)
StateT.Introduction_06_post:
pass
StateT.Dungeon_Entrance_loading:
gander.player_controlled = true
gander.current_scene_type = gander.GanderSceneT.Gameplay
var dungeon_scene = load("res://DungeonEntrance.tscn")
level = dungeon_scene.instantiate()
add_child(level)
state_dict["state"] = StateT.Dungeon_Entrance
lower_label.text = "Arrow keys/WASD/Left-Stick to move."
tween_text = get_tree().create_tween()
tween_text.tween_property(lower_label, "self_modulate", Color(1, 1, 1, 0), 5)
tween_text.tween_callback(func():
lower_label.text = ""
lower_label.self_modulate = Color(1, 1, 1, 1)
)
music_player.volume_db = 0.0
music_player.stream = load("res://audio/LD55_2.mp3")
music_player.stream.loop = true
music_player.play()
StateT.Dungeon_Entrance:
camera_to_gander(delta)
if level_guard == null:
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)
gander.player_controlled = false
gander.current_scene_type = gander.GanderSceneT.Battle
tween_scene = get_tree().create_tween()
level_cached_pos = level.find_child("DungeonEntrance").position
var battle_pos = level_cached_pos + Vector2(0.0, 500.0)
tween_scene.set_parallel()
tween_scene.tween_property(level.find_child("DungeonGuard"), "position", battle_pos - Vector2(200.0, 30.0), 2.0)
tween_scene.tween_property(gander, "position", battle_pos + Vector2(200.0, 0.0), 2.0)
gander.auto_control_action = "walking_right"
tween_scene.set_parallel(false)
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
state_dict["battle_item"] = null
var indicator_arrow = Sprite2D.new()
indicator_arrow.texture = load("res://gimp/arrow.png")
indicator_arrow.position.x = (indicator_arrow.get_rect().size.x - viewport_size.x) / 2.0
indicator_arrow.position.y = (viewport_size.y - indicator_arrow.get_rect().size.y) / 2.0
camera.add_child(indicator_arrow)
indicator_arrow.set_owner(camera)
tween_text = get_tree().create_tween()
tween_text.set_parallel()
tween_text.tween_property(indicator_arrow, "scale", Vector2(20.0, 20.0), 1.5)
tween_text.tween_property(indicator_arrow, "self_modulate", Color(1, 1, 1, 0), 1.5)
tween_text.set_parallel(false)
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:
@ -159,7 +301,9 @@ func _process(delta):
func _unhandled_input(event):
if event.is_pressed() and event.is_action("Confirm"):
if state_dict["state"] == StateT.Dungeon_Entrance_Battle:
handle_battle_input(event)
elif event.is_pressed() and event.is_action("Confirm"):
match state_dict["state"]:
StateT.Start:
main_label.text = start_text
@ -170,7 +314,10 @@ func _unhandled_input(event):
state_dict["state"] = StateT.Start_Stopping
tween_volume = get_tree().create_tween()
tween_volume.tween_property(music_player, "volume_db", -80.0, 4.0)
tween_volume.tween_callback(start_volume_tween_callback)
tween_volume.tween_callback(func():
music_player.stop()
state_dict["state"] = StateT.MainMenu
)
main_label.text = ""
lower_label.text = ""
StateT.Introduction_00:
@ -239,16 +386,19 @@ func _unhandled_input(event):
state_dict["text_idx"] = 0
main_label.text = intro_text_06
StateT.Introduction_06_post:
state_dict["state"] = StateT.Dungeon_Entrance
state_dict["state"] = StateT.Dungeon_Entrance_pre
state_dict["timer"] = 0.0
state_dict["text_idx"] = 0
main_label.text = ""
tween_volume = get_tree().create_tween()
tween_volume.tween_property(music_player, "volume_db", -80.0, 4.0)
tween_volume.tween_callback(func():
music_player.stop()
state_dict["state"] = StateT.Dungeon_Entrance_loading
music_player.volume_db = 0.0
)
_:
pass
func start_volume_tween_callback():
music_player.stop()
state_dict["state"] = StateT.MainMenu
func diamond_position_update():
fire_diamond.position.x = cos(state_dict["start_diamonds"]["angle"]) * state_dict["start_diamonds"]["dist"]
@ -288,3 +438,346 @@ func update_stop_diamonds(delta):
state_dict["start_diamonds"]["angle"] -= TAU
diamond_position_update()
func camera_to_gander(delta):
camera_to_target(delta, gander.position)
func camera_to_target(delta, vec2):
var diff = vec2 - camera.position
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"]:
state_dict["battle_menu_setup"] = true
state_dict["battle_refresh_gui"] = false
var battle_arrow = camera.find_child("BattleArrow")
if battle_arrow == null:
battle_arrow = Sprite2D.new()
battle_arrow.set_name(&"BattleArrow")
battle_arrow.texture = load("res://gimp/arrow.png")
camera.add_child(battle_arrow, true)
battle_arrow.set_owner(camera)
battle_arrow.self_modulate = Color(1, 1, 1, 1)
var battle_menu_item_0 = camera.find_child("BattleMenuItem0")
if battle_menu_item_0 == null:
battle_menu_item_0 = Label.new()
battle_menu_item_0.set_name(&"BattleMenuItem0")
camera.add_child(battle_menu_item_0, true)
battle_menu_item_0.set_owner(camera)
var battle_menu_item_1 = camera.find_child("BattleMenuItem1")
if battle_menu_item_1 == null:
battle_menu_item_1 = Label.new()
battle_menu_item_1.set_name(&"BattleMenuItem1")
camera.add_child(battle_menu_item_1, true)
battle_menu_item_1.set_owner(camera)
var battle_menu_item_2 = camera.find_child("BattleMenuItem2")
if battle_menu_item_2 == null:
battle_menu_item_2 = Label.new()
battle_menu_item_2.set_name(&"BattleMenuItem2")
camera.add_child(battle_menu_item_2, true)
battle_menu_item_2.set_owner(camera)
if state_dict["battle_item"] == null:
battle_menu_item_0.text = "Summon Item"
battle_menu_item_1.text = ""
var arrow_rect = battle_arrow.get_rect()
battle_arrow.position.x = (arrow_rect.size.x - viewport_size.x) / 2.0
battle_arrow.position.y = (viewport_size.y - arrow_rect.size.y) / 2.0
battle_menu_item_0.position.x = arrow_rect.size.x - viewport_size.x / 2.0
battle_menu_item_0.position.y = battle_arrow.position.y
state_dict["battle_selection"] = 0
state_dict["battle_options"] = ["summon"]
else:
battle_menu_item_0.text = "Attack with Item"
battle_menu_item_1.text = "Summon new Item"
var arrow_rect = battle_arrow.get_rect()
battle_arrow.position.x = (arrow_rect.size.x - viewport_size.x) / 2.0
battle_arrow.position.y = (viewport_size.y - arrow_rect.size.y * 3.0) / 2.0
battle_menu_item_0.position.x = arrow_rect.size.x - viewport_size.x / 2.0
battle_menu_item_0.position.y = battle_arrow.position.y
battle_menu_item_1.position.x = arrow_rect.size.x - viewport_size.x / 2.0
battle_menu_item_1.position.y = battle_arrow.position.y + arrow_rect.size.y
state_dict["battle_selection"] = 0
state_dict["battle_options"] = ["attack", "summon"]
BattleState.SummonMenu:
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
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")
var battle_menu_item_2 = camera.find_child("BattleMenuItem2")
battle_arrow.self_modulate = Color(1, 1, 1, 1)
battle_menu_item_0.text = "Summon Sword"
battle_menu_item_1.text = "Summon Hammer"
battle_menu_item_2.text = "Summon Health Potion (+5 HP)"
var arrow_rect = battle_arrow.get_rect()
battle_arrow.position.x = (arrow_rect.size.x - viewport_size.x) / 2.0
battle_arrow.position.y = (viewport_size.y - arrow_rect.size.y * 5.0) / 2.0
battle_menu_item_0.position.x = arrow_rect.size.x - viewport_size.x / 2.0
battle_menu_item_0.position.y = battle_arrow.position.y
battle_menu_item_1.position.x = arrow_rect.size.x - viewport_size.x / 2.0
battle_menu_item_1.position.y = battle_arrow.position.y + arrow_rect.size.y
battle_menu_item_2.position.x = arrow_rect.size.x - viewport_size.x / 2.0
battle_menu_item_2.position.y = battle_arrow.position.y + arrow_rect.size.y * 2.0
state_dict["battle_selection"] = 0
state_dict["battle_options"] = ["summon_sword", "summon_hammer", "summon_health"]
BattleState.SummonSword:
var summon_node = find_child("SummonAttempt")
if summon_node.success_count >= summon_node.MAX_SUCCESS:
camera.remove_child(summon_node)
var summon_item = gander.find_child("SummonItem")
if summon_item == null:
summon_item = Sprite2D.new()
summon_item.texture = load("res://gimp/sword.png")
summon_item.set_name(&"SummonItem")
gander.add_child(summon_item, true)
summon_item.set_owner(gander)
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 = gander.find_child("SummonItem")
if summon_item == null:
summon_item = Sprite2D.new()
summon_item.texture = load("res://gimp/hammer.png")
summon_item.set_name(&"SummonItem")
gander.add_child(summon_item, true)
summon_item.set_owner(gander)
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.SummonHealth:
var summon_node = find_child("SummonAttempt")
if summon_node.success_count >= summon_node.MAX_SUCCESS:
camera.remove_child(summon_node)
var summon_item = gander.find_child("SummonItem")
if summon_item == null:
pass
else:
gander.remove_child(summon_item)
gander.summon_item = null
gander.summon_item_summoned = false
gander.health += 5
if gander.health > gander.max_health:
gander.health = gander.max_health
gander.health_dirty = true
sfx_player.stream = sfx_summon
sfx_player.play()
state_dict["battle_state"] = BattleState.EnemyAttack
state_dict["battle_item"] = null
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")
var battle_menu_item_2 = camera.find_child("BattleMenuItem2")
battle_arrow.self_modulate = Color(1, 1, 1, 0)
battle_menu_item_0.text = ""
battle_menu_item_1.text = ""
battle_menu_item_2.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 \
or state_dict["battle_state"] == BattleState.SummonHealth \
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"):
state_dict["battle_selection"] += 1
if state_dict["battle_selection"] >= state_dict["battle_options"].size():
state_dict["battle_selection"] = 0
battle_arrow_positioning()
elif event.is_action("Up"):
state_dict["battle_selection"] -= 1
if state_dict["battle_selection"] < 0:
state_dict["battle_selection"] = state_dict["battle_options"].size() - 1
battle_arrow_positioning()
elif event.is_action("Confirm"):
handle_battle_action(state_dict["battle_options"][state_dict["battle_selection"]])
elif event.is_action("Cancel"):
handle_battle_action("cancel")
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
sfx_player.stream = sfx_confirm
sfx_player.play()
"summon_sword":
state_dict["battle_state"] = BattleState.SummonSword
state_dict["battle_menu_setup"] = false
var summon_scene = load("res://summoning.tscn")
var summon_node = summon_scene.instantiate()
summon_node.set_name(&"SummonAttempt")
camera.add_child(summon_node, true)
summon_node.set_owner(camera)
sfx_player.stream = sfx_confirm
sfx_player.play()
"summon_hammer":
state_dict["battle_state"] = BattleState.SummonHammer
state_dict["battle_menu_setup"] = false
var summon_scene = load("res://summoning.tscn")
var summon_node = summon_scene.instantiate()
summon_node.set_name(&"SummonAttempt")
camera.add_child(summon_node, true)
summon_node.set_owner(camera)
sfx_player.stream = sfx_confirm
sfx_player.play()
"summon_health":
state_dict["battle_state"] = BattleState.SummonHealth
state_dict["battle_menu_setup"] = false
var summon_scene = load("res://summoning.tscn")
var summon_node = summon_scene.instantiate()
summon_node.set_name(&"SummonAttempt")
camera.add_child(summon_node, true)
summon_node.set_owner(camera)
sfx_player.stream = sfx_confirm
sfx_player.play()
"cancel":
state_dict["battle_state"] = BattleState.MainMenu
state_dict["battle_menu_setup"] = false
sfx_player.stream = sfx_cancel
sfx_player.play()
_:
pass
func battle_arrow_positioning():
var battle_arrow: Sprite2D = camera.find_child("BattleArrow")
if battle_arrow != null:
var arrow_rect = battle_arrow.get_rect()
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

BIN
audio/LD55_2.mp3 Normal file

Binary file not shown.

19
audio/LD55_2.mp3.import Normal file
View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://18u0r46uho0f"
path="res://.godot/imported/LD55_2.mp3-34aece3f1076ff4ee4a342f09742ae03.mp3str"
[deps]
source_file="res://audio/LD55_2.mp3"
dest_files=["res://.godot/imported/LD55_2.mp3-34aece3f1076ff4ee4a342f09742ae03.mp3str"]
[params]
loop=true
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
audio/LD55_3.mp3 Normal file

Binary file not shown.

19
audio/LD55_3.mp3.import Normal file
View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://dymc7p8pyr5y4"
path="res://.godot/imported/LD55_3.mp3-20ea91918fd2eaba4ffbeab07a2323ed.mp3str"
[deps]
source_file="res://audio/LD55_3.mp3"
dest_files=["res://.godot/imported/LD55_3.mp3-20ea91918fd2eaba4ffbeab07a2323ed.mp3str"]
[params]
loop=true
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
audio/LD55_4.mp3 Normal file

Binary file not shown.

19
audio/LD55_4.mp3.import Normal file
View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://bg8bmtdt0c7kb"
path="res://.godot/imported/LD55_4.mp3-e210e6cf7c5b774440f99e24ae975dbc.mp3str"
[deps]
source_file="res://audio/LD55_4.mp3"
dest_files=["res://.godot/imported/LD55_4.mp3-e210e6cf7c5b774440f99e24ae975dbc.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
audio/LD55_5.mp3 Normal file

Binary file not shown.

19
audio/LD55_5.mp3.import Normal file
View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://eygtgx2p30nt"
path="res://.godot/imported/LD55_5.mp3-b747345ea626a35323705fe51d8f7236.mp3str"
[deps]
source_file="res://audio/LD55_5.mp3"
dest_files=["res://.godot/imported/LD55_5.mp3-b747345ea626a35323705fe51d8f7236.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
audio/LD55_sfx_cancel.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://bwu8jqdfa78j"
path="res://.godot/imported/LD55_sfx_cancel.mp3-cadc5a46716d397037e2593cd887cdee.mp3str"
[deps]
source_file="res://audio/LD55_sfx_cancel.mp3"
dest_files=["res://.godot/imported/LD55_sfx_cancel.mp3-cadc5a46716d397037e2593cd887cdee.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
audio/LD55_sfx_confirm.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://d0ekb3bk74p26"
path="res://.godot/imported/LD55_sfx_confirm.mp3-f5943f8e9f465983820ed67c99df309e.mp3str"
[deps]
source_file="res://audio/LD55_sfx_confirm.mp3"
dest_files=["res://.godot/imported/LD55_sfx_confirm.mp3-f5943f8e9f465983820ed67c99df309e.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -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

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://clxh0pmhvavne"
path="res://.godot/imported/LD55_sfx_hammer_hit.mp3-3164a70ab500317d475c03a94852df6f.mp3str"
[deps]
source_file="res://audio/LD55_sfx_hammer_hit.mp3"
dest_files=["res://.godot/imported/LD55_sfx_hammer_hit.mp3-3164a70ab500317d475c03a94852df6f.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
audio/LD55_sfx_parry.mp3 Normal file

Binary file not shown.

View file

@ -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

BIN
audio/LD55_sfx_select.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://d1q1gfvi1i8po"
path="res://.godot/imported/LD55_sfx_select.mp3-fd7a087b2218aec4761b872fc6130921.mp3str"
[deps]
source_file="res://audio/LD55_sfx_select.mp3"
dest_files=["res://.godot/imported/LD55_sfx_select.mp3-fd7a087b2218aec4761b872fc6130921.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
audio/LD55_sfx_summon.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://3iqdxpvo7xkp"
path="res://.godot/imported/LD55_sfx_summon.mp3-7fc4acd7d569064aef60ead4e1e4c32a.mp3str"
[deps]
source_file="res://audio/LD55_sfx_summon.mp3"
dest_files=["res://.godot/imported/LD55_sfx_summon.mp3-7fc4acd7d569064aef60ead4e1e4c32a.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://c2tvj7l0latop"
path="res://.godot/imported/LD55_sfx_sword_hit.mp3-d10764d82f6cd1f6e91970f219d44ea5.mp3str"
[deps]
source_file="res://audio/LD55_sfx_sword_hit.mp3"
dest_files=["res://.godot/imported/LD55_sfx_sword_hit.mp3-d10764d82f6cd1f6e91970f219d44ea5.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

140
export_presets.cfg Normal file
View file

@ -0,0 +1,140 @@
[preset.0]
name="Linux/X11"
platform="Linux/X11"
runnable=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../GodotExported/LD55_Linux64_GanderAndTheElementalDungeon_by_BurnedKirby/LD55_Linux64_GanderAndTheElementalDungeon_by_BurnedKirby.x86_64"
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false
[preset.0.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=false
texture_format/bptc=true
texture_format/s3tc=true
texture_format/etc=false
texture_format/etc2=false
binary_format/architecture="x86_64"
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="#!/usr/bin/env bash
export DISPLAY=:0
unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
\"{temp_dir}/{exe_name}\" {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\""
[preset.1]
name="Windows Desktop"
platform="Windows Desktop"
runnable=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../GodotExported/LD55_Windows_GanderAndTheElementalDungeon_by_BurnedKirby/LD55_Windows_GanderAndTheElementalDungeon_by_BurnedKirby.exe"
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false
[preset.1.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=false
texture_format/bptc=true
texture_format/s3tc=true
texture_format/etc=false
texture_format/etc2=false
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=false
application/icon=""
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
application/export_angle=0
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
$settings = New-ScheduledTaskSettingsSet
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
Start-ScheduledTask -TaskName godot_remote_debug
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
Remove-Item -Recurse -Force '{temp_dir}'"
[preset.2]
name="Web"
platform="Web"
runnable=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../GodotExported/Web/LD55.html"
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false
[preset.2.options]
custom_template/debug=""
custom_template/release=""
variant/extensions_support=false
vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false
html/export_icon=true
html/custom_html_shell=""
html/head_include=""
html/canvas_resize_policy=2
html/focus_canvas_on_start=true
html/experimental_virtual_keyboard=false
progressive_web_app/enabled=false
progressive_web_app/offline_page=""
progressive_web_app/display=1
progressive_web_app/orientation=0
progressive_web_app/icon_144x144=""
progressive_web_app/icon_180x180=""
progressive_web_app/icon_512x512=""
progressive_web_app/background_color=Color(0, 0, 0, 1)

View file

@ -6,14 +6,157 @@ var player_controlled = false
enum GanderSceneT {
Introduction,
Gameplay,
Battle,
}
var current_scene_type = GanderSceneT.Introduction
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 max_health = 30
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):
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()
@ -57,4 +200,10 @@ func _physics_process(delta):
elif animated.animation != auto_control_action:
animated.play(auto_control_action)
move_and_slide()
if current_scene_type != GanderSceneT.Battle:
move_and_slide()
var last_collision = get_last_slide_collision()
if last_collision != null:
last_collided_id = last_collision.get_collider_id()
else:
last_collided_id = null

View file

@ -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)

BIN
gimp/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

34
gimp/arrow.png.import Normal file
View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c7ey8ay7etot7"
path="res://.godot/imported/arrow.png-1ff2c1793b6898e6750b81c11b16be86.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://gimp/arrow.png"
dest_files=["res://.godot/imported/arrow.png-1ff2c1793b6898e6750b81c11b16be86.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
gimp/hammer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

34
gimp/hammer.png.import Normal file
View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b6ci7lm15okhr"
path="res://.godot/imported/hammer.png-430b8e98de82f10ad08a61167ab515de.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://gimp/hammer.png"
dest_files=["res://.godot/imported/hammer.png-430b8e98de82f10ad08a61167ab515de.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
gimp/summon_arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dvyxj067n812g"
path="res://.godot/imported/summon_arrow.png-8be3d4d199ac77e639f7001be6b20b00.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://gimp/summon_arrow.png"
dest_files=["res://.godot/imported/summon_arrow.png-8be3d4d199ac77e639f7001be6b20b00.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
gimp/sword.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

34
gimp/sword.png.import Normal file
View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bemf77dxebgw1"
path="res://.godot/imported/sword.png-10267e2e65de70563df10750bee7e827.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://gimp/sword.png"
dest_files=["res://.godot/imported/sword.png-10267e2e65de70563df10750bee7e827.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -44,3 +44,5 @@ texture = ExtResource("5_3irym")
[node name="MusicPlayer" type="AudioStreamPlayer" parent="."]
stream = ExtResource("6_3kpot")
[node name="SFXPlayer" type="AudioStreamPlayer" parent="."]

View file

@ -10,10 +10,12 @@ config_version=5
[application]
config/name="LD55"
config/name="LD55 - Gander and the Elemental Dungeon - by BurnedKirby"
run/main_scene="res://main_scene.tscn"
config/features=PackedStringArray("4.2", "GL Compatibility")
boot_splash/bg_color=Color(0.141176, 0.141176, 0.141176, 1)
config/icon="res://icon.svg"
boot_splash/minimum_display_time=1000
[input]

123
summoning.gd Normal file
View file

@ -0,0 +1,123 @@
extends Node2D
enum Direction {
LEFT,
RIGHT,
UP,
DOWN,
}
@onready var ar_one = $SummonArrow
@onready var ar_two = $SummonArrow2
@onready var ar_three = $SummonArrow3
@onready var ar_four = $SummonArrow4
@onready var ar_five = $SummonArrow5
@onready var ar_six = $SummonArrow6
@onready var ar_seven = $SummonArrow7
@onready var ar_eight = $SummonArrow8
@onready var timer_box = $TimerBox
const TIMER_BOX_WIDTH = 380.0
var summon_arrows_arr = []
var arrow_directions = []
const MAX_ERRORS = 3
const MAX_SUCCESS = 8
var error_count = 0
var success_count = 0
const TIMER_AMOUNT = 5.0
var timer = 0.0
func int_to_dir(i):
match i:
0:
return Direction.LEFT
1:
return Direction.RIGHT
2:
return Direction.UP
3:
return Direction.DOWN
_:
return Direction.DOWN
func dir_to_angle(d: Direction):
match d:
Direction.LEFT:
return 0.0
Direction.RIGHT:
return PI
Direction.UP:
return PI / 2.0
Direction.DOWN:
return PI * 3.0 / 2.0
func _ready():
summon_arrows_arr.append(ar_one)
summon_arrows_arr.append(ar_two)
summon_arrows_arr.append(ar_three)
summon_arrows_arr.append(ar_four)
summon_arrows_arr.append(ar_five)
summon_arrows_arr.append(ar_six)
summon_arrows_arr.append(ar_seven)
summon_arrows_arr.append(ar_eight)
var rng = RandomNumberGenerator.new()
for i in range(8):
arrow_directions.append(int_to_dir(rng.randi_range(0, 3)))
summon_arrows_arr[i].rotation = dir_to_angle(arrow_directions[i])
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
timer += delta
if timer >= TIMER_AMOUNT:
timer = 0.0
if success_count < MAX_SUCCESS and error_count < MAX_ERRORS:
on_summon_error()
if success_count < MAX_SUCCESS and error_count < MAX_ERRORS:
var interp_amount = (TIMER_AMOUNT - timer) / TIMER_AMOUNT
timer_box.scale.x = interp_amount * TIMER_BOX_WIDTH
timer_box.self_modulate = Color((1.0 - interp_amount), interp_amount, 0.0)
func _unhandled_input(event):
if error_count >= MAX_ERRORS or success_count >= MAX_SUCCESS:
return
if event.is_pressed():
if event.is_action("Left"):
if arrow_directions[success_count] == Direction.LEFT:
on_summon_success(success_count)
else:
on_summon_error()
elif event.is_action("Right"):
if arrow_directions[success_count] == Direction.RIGHT:
on_summon_success(success_count)
else:
on_summon_error()
elif event.is_action("Up"):
if arrow_directions[success_count] == Direction.UP:
on_summon_success(success_count)
else:
on_summon_error()
elif event.is_action("Down"):
if arrow_directions[success_count] == Direction.DOWN:
on_summon_success(success_count)
else:
on_summon_error()
func on_summon_success(idx):
summon_arrows_arr[idx].self_modulate = Color(0.0, 1.0, 0.0)
success_count += 1
func on_summon_error():
timer = 0.0
success_count = 0
error_count += 1
if error_count >= MAX_ERRORS:
for i in range(8):
summon_arrows_arr[i].self_modulate = Color(1.0, 0.0, 0.0)
else:
for i in range(8):
summon_arrows_arr[i].self_modulate = Color(1.0, 1.0, 1.0)

54
summoning.tscn Normal file
View file

@ -0,0 +1,54 @@
[gd_scene load_steps=4 format=3 uid="uid://b7tummdg08cfc"]
[ext_resource type="Script" path="res://summoning.gd" id="1_qh1sn"]
[ext_resource type="Texture2D" uid="uid://dvyxj067n812g" path="res://gimp/summon_arrow.png" id="2_io4qw"]
[sub_resource type="CanvasTexture" id="CanvasTexture_oraey"]
[node name="Node2D" type="Node2D"]
script = ExtResource("1_qh1sn")
[node name="SummonArrow" type="Sprite2D" parent="."]
position = Vector2(-136, -56)
texture = ExtResource("2_io4qw")
[node name="SummonArrow2" type="Sprite2D" parent="."]
position = Vector2(-46, -56)
texture = ExtResource("2_io4qw")
[node name="SummonArrow3" type="Sprite2D" parent="."]
position = Vector2(47, -55)
texture = ExtResource("2_io4qw")
[node name="SummonArrow4" type="Sprite2D" parent="."]
position = Vector2(143, -56)
texture = ExtResource("2_io4qw")
[node name="SummonArrow5" type="Sprite2D" parent="."]
position = Vector2(-144, 57)
texture = ExtResource("2_io4qw")
[node name="SummonArrow6" type="Sprite2D" parent="."]
position = Vector2(-49, 56)
texture = ExtResource("2_io4qw")
[node name="SummonArrow7" type="Sprite2D" parent="."]
position = Vector2(47, 57)
texture = ExtResource("2_io4qw")
[node name="SummonArrow8" type="Sprite2D" parent="."]
position = Vector2(136, 58)
texture = ExtResource("2_io4qw")
[node name="Label" type="Label" parent="."]
offset_left = -187.0
offset_top = -140.0
offset_right = 188.0
offset_bottom = -100.0
theme_override_font_sizes/font_size = 24
text = "Press the directions to summon!"
[node name="TimerBox" type="Sprite2D" parent="."]
position = Vector2(0, 135)
scale = Vector2(380, 50)
texture = SubResource("CanvasTexture_oraey")