Probably juiced the fuck out of this game. Added variois sounds, tweens etc. Also added centralized spelllibrary for future spells. Overhauled Card Perk Design

main
Artur 2026-05-20 19:07:39 +02:00
parent b7a3934daf
commit 320949fcb3
25 changed files with 468 additions and 80 deletions

View File

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://b27862pnesv4a"
path="res://.godot/imported/47313572-experimental-8-bit-sound-270302.mp3-c4341312242ec2d6d5bacb1c4c433fda.mp3str"
[deps]
source_file="res://assets/music&sfx/sfx/47313572-experimental-8-bit-sound-270302.mp3"
dest_files=["res://.godot/imported/47313572-experimental-8-bit-sound-270302.mp3-c4341312242ec2d6d5bacb1c4c433fda.mp3str"]
[params]
loop=false
loop_offset=0.0
bpm=0.0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://3ubacx6k21aq"
path="res://.godot/imported/card_shuffle.wav-2df5de9a976f5727aeeddc7f5f41163e.sample"
[deps]
source_file="res://assets/music&sfx/sfx/card_shuffle.wav"
dest_files=["res://.godot/imported/card_shuffle.wav-2df5de9a976f5727aeeddc7f5f41163e.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View File

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://o81j4w2j1p16"
path="res://.godot/imported/data_pion-sfx9-fwoosh-324525.mp3-aae109eab17a5ca7e7981c978d3c52e3.mp3str"
[deps]
source_file="res://assets/music&sfx/sfx/data_pion-sfx9-fwoosh-324525.mp3"
dest_files=["res://.godot/imported/data_pion-sfx9-fwoosh-324525.mp3-aae109eab17a5ca7e7981c978d3c52e3.mp3str"]
[params]
loop=false
loop_offset=0.0
bpm=0.0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://buxdyhc4ih63f"
path="res://.godot/imported/fire.wav-a8c495294a82be1272b8f8a56ccb40f5.sample"
[deps]
source_file="res://assets/music&sfx/sfx/fire.wav"
dest_files=["res://.godot/imported/fire.wav-a8c495294a82be1272b8f8a56ccb40f5.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View File

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://bh8o5pk2cco1a"
path="res://.godot/imported/lesiakower-coin-collect-retro-8-bit-sound-effect-145251.mp3-0a5ea69aced7938e6b9174c8f11f81d9.mp3str"
[deps]
source_file="res://assets/music&sfx/sfx/lesiakower-coin-collect-retro-8-bit-sound-effect-145251.mp3"
dest_files=["res://.godot/imported/lesiakower-coin-collect-retro-8-bit-sound-effect-145251.mp3-0a5ea69aced7938e6b9174c8f11f81d9.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://bjyttcuhiidrq"
path="res://.godot/imported/lesiakower-level-up-enhancement-8-bit-retro-sound-effect-153002.mp3-a2556229ce18ac544b49cfbc2bd40be8.mp3str"
[deps]
source_file="res://assets/music&sfx/sfx/lesiakower-level-up-enhancement-8-bit-retro-sound-effect-153002.mp3"
dest_files=["res://.godot/imported/lesiakower-level-up-enhancement-8-bit-retro-sound-effect-153002.mp3-a2556229ce18ac544b49cfbc2bd40be8.mp3str"]
[params]
loop=false
loop_offset=0.0
bpm=0.0
beat_count=0
bar_beats=4

View File

@ -22,6 +22,7 @@ buses/default_bus_layout="uid://5oswo22yvmtg"
[autoload]
MusicManager="*uid://dmu7041wi1upt"
SpellLibrary="*res://scripts/SpellLibrary.gd"
[display]

View File

@ -13,25 +13,30 @@ region = Rect2(561, 17, 46, 62)
[sub_resource type="LabelSettings" id="LabelSettings_qag4p"]
line_spacing = 5.0
font = ExtResource("3_n4umd")
font_size = 12
font_size = 16
outline_size = 2
outline_color = Color(0, 0, 0, 1)
[sub_resource type="AtlasTexture" id="AtlasTexture_n4umd"]
atlas = ExtResource("4_nlhlf")
region = Rect2(258, 241, 25, 14)
[sub_resource type="LabelSettings" id="LabelSettings_ni87r"]
font = ExtResource("4_40ty6")
font_size = 11
line_spacing = 6.0
font = ExtResource("3_n4umd")
font_size = 17
outline_size = 2
outline_color = Color(0, 0, 0, 1)
[node name="PerkCard" type="Control" unique_id=1471811151]
custom_minimum_size = Vector2(200, 300)
custom_minimum_size = Vector2(240, 360)
layout_mode = 3
anchors_preset = 0
script = ExtResource("1_t8gqh")
[node name="Card" type="TextureRect" parent="." unique_id=1832247473]
texture_filter = 1
custom_minimum_size = Vector2(200, 300)
custom_minimum_size = Vector2(240, 360)
layout_mode = 0
offset_right = 40.0
offset_bottom = 40.0
@ -42,10 +47,10 @@ layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -50.5
offset_left = -65.0
offset_top = 43.0
offset_right = 50.5
offset_bottom = 92.0
offset_right = 65.0
offset_bottom = 100.0
grow_horizontal = 2
text = "Zweizeilige
Überschrift
@ -54,6 +59,7 @@ label_settings = SubResource("LabelSettings_qag4p")
horizontal_alignment = 1
[node name="TextureRect" type="TextureRect" parent="Card/Name" unique_id=2128840891]
texture_filter = 1
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
@ -67,35 +73,33 @@ offset_bottom = 19.0
grow_horizontal = 2
grow_vertical = 0
texture = SubResource("AtlasTexture_n4umd")
stretch_mode = 5
[node name="Description" type="Label" parent="Card" unique_id=859816848]
[node name="Description" type="RichTextLabel" parent="Card" unique_id=859816848]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -65.0
offset_top = -176.0
offset_right = 69.0
offset_bottom = -43.0
offset_left = -100.0
offset_top = -215.0
offset_right = 100.0
offset_bottom = -36.0
grow_horizontal = 2
grow_vertical = 0
text = "Text in Zeile Eins
Text in Zeile Eins
Text in Zeile Eins
Text in Zeile Eins
Text in Zeile Zwei
Text in Zeile Drei
Text in Zeile Vier
Text in Zeile Fünf"
label_settings = SubResource("LabelSettings_ni87r")
horizontal_alignment = 1
autowrap_mode = 2
theme_override_fonts/normal_font = ExtResource("3_n4umd")
theme_override_font_sizes/normal_font_size = 17
theme_override_constants/outline_size = 2
theme_override_colors/default_color = Color(1, 1, 1, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
bbcode_enabled = true
scroll_active = false
text = ""
[node name="Button" type="Button" parent="." unique_id=1274377333]
layout_mode = 0
offset_right = 200.0
offset_bottom = 300.0
offset_right = 240.0
offset_bottom = 360.0
[connection signal="pressed" from="Button" to="." method="_on_button_pressed"]

View File

@ -39,7 +39,7 @@ animations = [{
}],
"loop": true,
"name": &"default",
"speed": 10.0
"speed": 35.0
}]
[node name="Fireball" type="Area2D" unique_id=2100779306]

View File

@ -15,9 +15,9 @@
[ext_resource type="Script" uid="uid://pmmc7tivuuo4" path="res://scripts/perk_effects.gd" id="16_ca42v"]
[ext_resource type="Script" uid="uid://dm1sntwjrv3vl" path="res://scripts/level_up_manager.gd" id="16_rysoc"]
[ext_resource type="Texture2D" uid="uid://dp8o40ssuh8ip" path="res://assets/Tiny RPG Mana Soul GUI v1.0/20250421barB-Sheet.png" id="16_ssvqc"]
[ext_resource type="Script" path="res://scripts/pause_menu.gd" id="18_264po"]
[ext_resource type="Script" uid="uid://duauc778wc3hd" path="res://scripts/pause_menu.gd" id="18_264po"]
[ext_resource type="FontFile" uid="uid://8v71dcws4q6o" path="res://assets/fonts/slkscre.ttf" id="19_1kice"]
[ext_resource type="Script" path="res://scripts/options_menu_ingame.gd" id="20_1kice"]
[ext_resource type="Script" uid="uid://586y330mhx8" path="res://scripts/options_menu_ingame.gd" id="20_1kice"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_vtaks"]
texture = ExtResource("7_gee14")

View File

@ -5,14 +5,20 @@ var witch
var player
var is_spawning = true
signal collected
# Called when the node enters the scene tree for the first time.
var _collect_sfx = preload("res://assets/music&sfx/sfx/lesiakower-coin-collect-retro-8-bit-sound-effect-145251.mp3")
var _sfx_player: AudioStreamPlayer
func _ready() -> void:
body_entered.connect(_on_body_entered)
witch = get_node("/root/Game/Witch")
collected.connect(witch._on_collect)
player = get_node("/root/Game/Player")
_sfx_player = AudioStreamPlayer.new()
_sfx_player.stream = _collect_sfx
_sfx_player.volume_db = -18
add_child(_sfx_player)
_animate_spawn()
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
@ -43,6 +49,8 @@ func _on_body_entered(body: Node2D) -> void:
pass
func collect():
_sfx_player.pitch_scale = randf_range(0.85, 1.15)
_sfx_player.play()
is_spawning = true
var position_drop = create_tween()
var target = witch.global_position

View File

@ -3,5 +3,7 @@ class_name Perk
@export var name: String
@export var description: String
@export var stats: String = ""
@export var spell: String = SpellLibrary.NONE
@export var icon: Texture2D = null
var effect: Callable

View File

@ -0,0 +1,32 @@
extends Node
const APPLE = 0
const GRAPE = 1
const NONE = "NONE"
const SHURIKEN = "SHURIKEN"
const FIREBALL = "FIREBALL"
const FIRE_SWIRL = "FIRE_SWIRL"
const TORNADO = "TORNADO"
# Each spell's display recipe: sorted apples-first, grapes-last
var recipes: Dictionary = {
SHURIKEN: [APPLE, GRAPE, GRAPE],
FIREBALL: [APPLE, APPLE, APPLE],
FIRE_SWIRL: [APPLE, APPLE, GRAPE],
TORNADO: [GRAPE, GRAPE, GRAPE],
}
# Takes cauldron slot_states (uses texture indices: 2 = apple, 4 = grape)
# and returns which spell that combination brews.
func identify(cauldron_slots: Array) -> String:
var apples = cauldron_slots.count(2)
var grapes = cauldron_slots.count(4)
for spell_id in recipes:
var r: Array = recipes[spell_id]
if r.count(APPLE) == apples and r.count(GRAPE) == grapes:
return spell_id
return NONE
func get_recipe(spell: String) -> Array:
return recipes.get(spell, [])

View File

@ -0,0 +1 @@
uid://dpn7aoem55ptp

View File

@ -12,8 +12,10 @@ var slot_states = [0, 0, 0]
var progres_index = 0
var is_brewing
var brew_explosion = true
@onready var witch = get_parent()
var explosion_scene = preload("res://scenes/explosion.tscn")# Called when the node enters the scene tree for the first time.
@onready var witch = get_parent()
var explosion_scene = preload("res://scenes/explosion.tscn")
var _ignite_sfx = preload("res://assets/music&sfx/sfx/data_pion-sfx9-fwoosh-324525.mp3")
var _ignite_player: AudioStreamPlayer# Called when the node enters the scene tree for the first time.
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
@ -31,7 +33,10 @@ func _ready() -> void:
slots = [$Empty1, $Empty2, $Empty3]
colors = [base, yellow, red, orange, purple]
enrich_burning_colors()
pass # Replace with function body.
_ignite_player = AudioStreamPlayer.new()
_ignite_player.stream = _ignite_sfx
_ignite_player.volume_db = -15
add_child(_ignite_player)
# Called every frame. 'delta' is the elapsed time since the previous frame.
@ -51,6 +56,8 @@ func reset_texture():
func ignite_cauldrons():
for i in slots.size():
slots[i].texture = burning_colors[slot_states[i]]
_ignite_player.pitch_scale = randf_range(0.8, 1.2)
_ignite_player.play()
await get_tree().create_timer(0.4).timeout
if brew_explosion:
var boom = explosion_scene.instantiate()
@ -83,16 +90,11 @@ func progres_bar(fruit):
func brew(fruits):
is_brewing = true
await ignite_cauldrons()
var apple_count = fruits.count(2)
var grape_count = fruits.count(4)
if apple_count == 2 and grape_count == 1:
witch.shoot_fire_swirl()
elif apple_count == 3:
witch.shoot_fireballs()
elif grape_count == 3:
witch.shoot_tornado()
elif apple_count == 1 and grape_count == 2:
witch.shoot_shuriken()
match SpellLibrary.identify(fruits):
SpellLibrary.SHURIKEN: witch.shoot_shuriken()
SpellLibrary.FIREBALL: witch.shoot_fireballs()
SpellLibrary.FIRE_SWIRL: witch.shoot_fire_swirl()
SpellLibrary.TORNADO: witch.shoot_tornado()
reset_texture()
is_brewing = false

View File

@ -2,21 +2,34 @@ extends Control
@onready var perk_effects = get_node("/root/Game/PerkEffects")
var level_sound = preload("res://assets/music&sfx/sfx/lesiakower-level-up-enhancement-8-bit-retro-sound-effect-153002.mp3")
var select_sound = preload("res://assets/music&sfx/sfx/47313572-experimental-8-bit-sound-270302.mp3")
var card_shuffle_sound = preload("res://assets/music&sfx/sfx/card_shuffle.wav")
var perks
var player = AudioStreamPlayer.new()
var select_player = AudioStreamPlayer.new()
var shuffle_player = AudioStreamPlayer.new()
@onready var perkCard = preload("res://scenes/PerkCard.tscn")
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
perks = perk_effects.available_perks
get_node("/root/Game/DropManager").leveled_up.connect(show_perks)
pass # Replace with function body.
add_child(player)
add_child(select_player)
add_child(shuffle_player)
player.volume_db = -10
select_player.volume_db = -10
shuffle_player.volume_db = -10
select_player.stream = select_sound
shuffle_player.stream = card_shuffle_sound
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func show_perks():
player.stream = level_sound
player.play()
if perks.is_empty():
return
var tween = create_tween()
@ -25,11 +38,14 @@ func show_perks():
var shuffled = perks.duplicate()
shuffled.shuffle()
var chosen = shuffled.slice(0, 3)
await get_tree().create_timer(0.4).timeout
for x in chosen:
var card = perkCard.instantiate()
$HBoxContainer.add_child(card)
card.setup(x, func(): select_perk(x))
card.setup(x, func(): select_perk(x), func(): select_player.play())
shuffle_player.play()
await get_tree().create_timer(0.25).timeout
func select_perk(perk):
perk.effect.call()

View File

@ -1,42 +1,155 @@
extends Control
@export var icon: Texture2D
# Called when the node enters the scene tree for the first time.
var on_select: Callable
var play_sound: Callable
var _recipe_panel: Control = null
var _panel_tween: Tween = null
const _PANEL_REST_Y = 18
const _FRUIT_ATLAS = preload("res://assets/16x16 Pixelart Food Icons/Pixel_Foods(ARTLİNE).png")
const _FONT_BOLD = preload("res://assets/fonts/slkscrb.ttf")
func setup(perk: Perk, select: Callable):
func setup(perk: Perk, select: Callable, sound: Callable):
$Button.button_down.connect(_on_button_down)
$Button.modulate.a = 0
$Card/Name.text = perk.name
$Card/Description.text = perk.description
var content = perk.description
if perk.stats != "":
content += "\n\n" + perk.stats
$Card/Description.text = "[center]" + content + "[/center]"
if perk.icon != null:
$Card/Name/TextureRect.texture = perk.icon
_build_recipe_panel(perk)
animate_in()
$Button.modulate.a = 0.3
$Button.mouse_entered.connect(func(): $Button.modulate.a = 0.6)
$Button.mouse_exited.connect(func(): $Button.modulate.a = 0.3)
$Button.mouse_entered.connect(_on_hover_enter)
$Button.mouse_exited.connect(_on_hover_exit)
on_select = select
play_sound = sound
func _on_button_pressed() -> void:
func _build_recipe_panel(perk: Perk) -> void:
var is_brewed = perk.spell != SpellLibrary.NONE
var panel_w := 240
var panel_h := 74 if is_brewed else 38
_recipe_panel = Control.new()
_recipe_panel.position = Vector2(0, _PANEL_REST_Y)
_recipe_panel.size = Vector2(panel_w, panel_h)
_recipe_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
var border := ColorRect.new()
border.mouse_filter = Control.MOUSE_FILTER_IGNORE
border.color = Color(0.55, 0.4, 0.1, 1.0)
border.size = Vector2(panel_w, panel_h)
_recipe_panel.add_child(border)
var bg := ColorRect.new()
bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
bg.color = Color(0.08, 0.04, 0.14, 0.96)
bg.position = Vector2(2, 2)
bg.size = Vector2(panel_w - 4, panel_h - 4)
_recipe_panel.add_child(bg)
var header := Label.new()
header.mouse_filter = Control.MOUSE_FILTER_IGNORE
header.add_theme_font_override("font", _FONT_BOLD)
header.add_theme_font_size_override("font_size", 13)
header.add_theme_color_override("font_color", Color(0.85, 0.72, 0.45, 1.0))
header.add_theme_constant_override("outline_size", 2)
header.add_theme_color_override("font_outline_color", Color(0, 0, 0, 1))
header.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
header.size = Vector2(panel_w, 22)
if is_brewed:
header.text = "BREW RECIPE"
header.position = Vector2(0, 6)
_recipe_panel.add_child(header)
var hbox := HBoxContainer.new()
hbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
hbox.add_theme_constant_override("separation", 4)
hbox.alignment = BoxContainer.ALIGNMENT_CENTER
hbox.size = Vector2(panel_w, 36)
hbox.position = Vector2(0, 32)
var recipe := SpellLibrary.get_recipe(perk.spell)
for i in recipe.size():
if i > 0:
var plus := Label.new()
plus.mouse_filter = Control.MOUSE_FILTER_IGNORE
plus.text = "+"
plus.add_theme_font_override("font", _FONT_BOLD)
plus.add_theme_font_size_override("font_size", 14)
plus.add_theme_color_override("font_color", Color(1, 1, 1, 0.6))
plus.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
hbox.add_child(plus)
var fruit := TextureRect.new()
fruit.mouse_filter = Control.MOUSE_FILTER_IGNORE
var atlas := AtlasTexture.new()
atlas.atlas = _FRUIT_ATLAS
atlas.region = Rect2(1, 2, 16, 15) if recipe[i] == SpellLibrary.APPLE else Rect2(55, 19, 15, 16)
fruit.texture = atlas
fruit.custom_minimum_size = Vector2(32, 32)
fruit.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
fruit.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
hbox.add_child(fruit)
_recipe_panel.add_child(hbox)
else:
header.text = "AUTO / PASSIVE"
header.position = Vector2(0, panel_h / 2 - 11)
_recipe_panel.add_child(header)
add_child(_recipe_panel)
move_child(_recipe_panel, 0)
func _on_hover_enter():
if _panel_tween:
_panel_tween.kill()
_panel_tween = create_tween().set_parallel()
_panel_tween.tween_property(self, "scale", Vector2(0.93, 0.93), 0.1).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUAD)
_panel_tween.tween_property(_recipe_panel, "position:y", -float(_recipe_panel.size.y), 0.22).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
func _on_hover_exit():
if _panel_tween:
_panel_tween.kill()
_panel_tween = create_tween().set_parallel()
_panel_tween.tween_property(self, "scale", Vector2(1.0, 1.0), 0.1).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUAD)
_panel_tween.tween_property(_recipe_panel, "position:y", float(_PANEL_REST_Y), 0.15).set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_QUAD)
func _on_button_down() -> void:
if _recipe_panel:
_recipe_panel.visible = false
play_sound.call()
await animate_out()
on_select.call()
pass # Replace with function body.
func animate_in():
await get_tree().process_frame
pivot_offset = size / 2
var icon_node = $Card/Name/TextureRect
icon_node.pivot_offset = icon_node.size / 2
var pulse = create_tween().set_loops()
pulse.tween_property(icon_node, "scale", Vector2(1.12, 1.12), 0.9).set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE)
pulse.tween_property(icon_node, "scale", Vector2(1.0, 1.0), 0.9).set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE)
var start_y = global_position.y + 500
var end_y = global_position.y
global_position.y = start_y
var tween = create_tween()
tween.tween_property(self, "global_position:y", end_y, 0.3).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUAD)
func animate_out():
var start_y = global_position.y
var start_y = global_position.y
var end_y = global_position.y - 500
global_position.y = start_y
var tween = create_tween()
tween.tween_property(self, "global_position:y", end_y, 0.3).set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_QUAD)
await tween.finished

View File

@ -20,31 +20,74 @@ const SPELLBOOK_RADIUS: float = 60.0
var _spellbook_angle: float = 0.0
var _spellbooks: Array = []
var _icon_knife = preload("res://assets/weapons/knvie.png")
var _icon_book = preload("res://assets/books_set_2/books_pentagram.png")
var _icon_brew = preload("res://assets/books_set_2/books_health_potion.png")
var _icon_shuriken: AtlasTexture
var _icon_fireball: AtlasTexture
func _stat(label: String, from: String, to: String) -> String:
return "[color=#aaaaaa]" + label + "[/color]\n[color=#888888]" + from + "[/color] → [color=#77dd77]" + to + "[/color]"
func _stat_new(label: String, val: String) -> String:
return "[color=#aaaaaa]" + label + "[/color]\n[color=#77dd77]" + val + "[/color]"
func _stat_toggle(label: String) -> String:
return "[color=#aaaaaa]" + label + "[/color]\n[color=#888888]OFF[/color] → [color=#77dd77]ON[/color]"
func _ready() -> void:
cauldron = witch.get_node("CauldronBar")
_icon_shuriken = AtlasTexture.new()
_icon_shuriken.atlas = preload("res://assets/Fire Pixel Bullet 16x16/All_Fire_Bullet_Pixel_16x16_02.png")
_icon_shuriken.region = Rect2(496, 32, 16, 16)
_icon_fireball = AtlasTexture.new()
_icon_fireball.atlas = preload("res://assets/Fire Pixel Bullet 16x16/All_Fire_Bullet_Pixel_16x16_00.png")
_icon_fireball.region = Rect2(576, 1, 16, 16)
var dsh = Perk.new()
dsh.name = "Double Shuriken"
dsh.description = "Throw two shurikens instead of one"
dsh.description = "Fire two shurikens at once"
dsh.stats = _stat("Shurikens", "1", "2")
dsh.spell = SpellLibrary.SHURIKEN
dsh.icon = _icon_shuriken
dsh.effect = double_shuriken
available_perks.append(dsh)
var faoe = Perk.new()
faoe.name = "AOE Fireball"
faoe.description = "Fireballs spawn an explosion on contact"
faoe.description = "Fireballs explode on impact"
faoe.stats = _stat_toggle("AOE")
faoe.spell = SpellLibrary.FIREBALL
faoe.icon = _icon_fireball
faoe.effect = fireball_aoe
available_perks.append(faoe)
var bexp = Perk.new()
bexp.name = "Brew Explosion"
bexp.description = "The Witch casts an explosion on finishing a brew"
bexp.description = "Trigger an explosion on brew"
bexp.stats = _stat_toggle("Explosion")
bexp.spell = SpellLibrary.NONE
bexp.icon = _icon_brew
bexp.effect = brew_explosion
available_perks.append(bexp)
var tk = Perk.new()
tk.name = "Throwing Knife"
tk.description = "Automatically throw a knife at the nearest enemy every second"
tk.description = "Auto-throw at nearby enemies"
tk.stats = _stat_new("Cooldown", "%.1fs" % throwing_knife_cooldown)
tk.spell = SpellLibrary.NONE
tk.icon = _icon_knife
tk.effect = enable_throwing_knife
available_perks.append(tk)
var sb = Perk.new()
sb.name = "Spellbook"
sb.description = "A spellbook orbits the witch, dealing 12 damage to enemies it touches"
sb.description = "An orbiting book hits enemies"
sb.stats = "[color=#aaaaaa]Damage Count[/color]\n[color=#77dd77]" + str(spellbook_damage) + " 1[/color]"
sb.spell = SpellLibrary.NONE
sb.icon = _icon_book
sb.effect = unlock_spellbook
available_perks.append(sb)
@ -73,12 +116,16 @@ func enable_throwing_knife():
throwing_knife_enabled = true
var upg = Perk.new()
upg.name = "Knife Cooldown"
upg.description = "Reduce throwing knife cooldown by 0.2s (min 1s)"
upg.description = "Throw knives more often"
upg.stats = _stat("Cooldown", "%.1fs" % throwing_knife_cooldown, "%.1fs" % maxf(1.0, throwing_knife_cooldown - 0.2))
upg.icon = _icon_knife
upg.effect = knife_cooldown_upgrade
available_perks.append(upg)
var ek = Perk.new()
ek.name = "Extra Knife"
ek.description = "Throw one additional knife per attack (max 3)"
ek.description = "Throw one more knife"
ek.stats = _stat("Knives", str(throwing_knife_count), str(throwing_knife_count + 1))
ek.icon = _icon_knife
ek.effect = extra_knife
available_perks.append(ek)
@ -87,7 +134,9 @@ func extra_knife():
if throwing_knife_count < 3:
var ek = Perk.new()
ek.name = "Extra Knife"
ek.description = "Throw one additional knife per attack (max 3)"
ek.description = "Throw one more knife"
ek.stats = _stat("Knives", str(throwing_knife_count), str(throwing_knife_count + 1))
ek.icon = _icon_knife
ek.effect = extra_knife
available_perks.append(ek)
@ -96,7 +145,9 @@ func knife_cooldown_upgrade():
if throwing_knife_cooldown > 1.0:
var upg = Perk.new()
upg.name = "Knife Cooldown"
upg.description = "Reduce throwing knife cooldown by 0.2s (min 1s)"
upg.description = "Throw knives more often"
upg.stats = _stat("Cooldown", "%.1fs" % throwing_knife_cooldown, "%.1fs" % maxf(1.0, throwing_knife_cooldown - 0.2))
upg.icon = _icon_knife
upg.effect = knife_cooldown_upgrade
available_perks.append(upg)
@ -111,17 +162,23 @@ func unlock_spellbook() -> void:
_rebuild_spellbooks()
var eb = Perk.new()
eb.name = "Extra Book"
eb.description = "Add another spellbook (max 5)"
eb.description = "Add one more orbiting book"
eb.stats = _stat("Books", str(spellbook_count), str(spellbook_count + 1))
eb.icon = _icon_book
eb.effect = extra_book
available_perks.append(eb)
var fo = Perk.new()
fo.name = "Faster Orbit"
fo.description = "Spellbooks orbit the witch faster"
fo.description = "Books orbit faster"
fo.stats = _stat("Speed", "%.1f" % spellbook_speed, "%.1f" % (spellbook_speed + 0.5))
fo.icon = _icon_book
fo.effect = faster_orbit
available_perks.append(fo)
var bd = Perk.new()
bd.name = "Book Damage"
bd.description = "Spellbooks deal 4 more damage"
bd.description = "Books hit harder"
bd.stats = _stat("Damage", str(spellbook_damage), str(spellbook_damage + 4))
bd.icon = _icon_book
bd.effect = book_damage
available_perks.append(bd)
@ -131,7 +188,9 @@ func extra_book() -> void:
if spellbook_count < 5:
var eb = Perk.new()
eb.name = "Extra Book"
eb.description = "Add another spellbook (max 5)"
eb.description = "Add one more orbiting book"
eb.stats = _stat("Books", str(spellbook_count), str(spellbook_count + 1))
eb.icon = _icon_book
eb.effect = extra_book
available_perks.append(eb)
@ -139,7 +198,9 @@ func faster_orbit() -> void:
spellbook_speed += 0.5
var fo = Perk.new()
fo.name = "Faster Orbit"
fo.description = "Spellbooks orbit the witch faster"
fo.description = "Books orbit faster"
fo.stats = _stat("Speed", "%.1f" % spellbook_speed, "%.1f" % (spellbook_speed + 0.5))
fo.icon = _icon_book
fo.effect = faster_orbit
available_perks.append(fo)
@ -149,7 +210,9 @@ func book_damage() -> void:
book.damage = spellbook_damage
var bd = Perk.new()
bd.name = "Book Damage"
bd.description = "Spellbooks deal 4 more damage"
bd.description = "Books hit harder"
bd.stats = _stat("Damage", str(spellbook_damage), str(spellbook_damage + 4))
bd.icon = _icon_book
bd.effect = book_damage
available_perks.append(bd)
@ -162,4 +225,3 @@ func _rebuild_spellbooks() -> void:
book.damage = spellbook_damage
witch.get_parent().add_child(book)
_spellbooks.append(book)

View File

@ -9,6 +9,7 @@ var shuriken = preload("res://scenes/shuriken.tscn")
var fire_swirl = preload("res://scenes/fire_swirl.tscn")
var tornado = preload("res://scenes/tornado.tscn")
var shuriken_count = 1
var _fire_sfx = preload("res://assets/music&sfx/sfx/fire.wav")
var max_hp: int = 100
var current_hp: int = 100
@ -51,11 +52,33 @@ func _on_collect(DropsBase):
func shoot_fireballs():
var enemies = get_tree().get_nodes_in_group("enemies")
for enemy in enemies:
if not is_instance_valid(enemy):
continue
var dir = global_position.direction_to(enemy.global_position)
_face_direction(dir)
var fb = fireball.instantiate()
fb.global_position = global_position
get_parent().add_child(fb)
fb.launch(enemy.global_position)
camera.shake(0.3,0.8)
_play_fire_sfx()
camera.shake(0.15, 0.5)
await get_tree().create_timer(0.12).timeout
$AnimatedSprite2D.play("south")
func _face_direction(dir: Vector2) -> void:
if abs(dir.x) >= abs(dir.y):
$AnimatedSprite2D.play("east" if dir.x > 0 else "west")
else:
$AnimatedSprite2D.play("south" if dir.y > 0 else "north")
func _play_fire_sfx() -> void:
var asp = AudioStreamPlayer.new()
asp.stream = _fire_sfx
asp.volume_db = -10
asp.pitch_scale = randf_range(0.9, 1.1)
get_parent().add_child(asp)
asp.play()
asp.finished.connect(asp.queue_free)
func shoot_fire_swirl():
var fs = fire_swirl.instantiate()