Compare commits
10 Commits
44d83f30f1
...
2c3d8c7cfd
| Author | SHA1 | Date |
|---|---|---|
|
|
2c3d8c7cfd | |
|
|
35772ba2a6 | |
|
|
d5aca6bdf4 | |
|
|
dd4fcbdf2c | |
|
|
960a8612b4 | |
|
|
882d44264a | |
|
|
ac86c4371e | |
|
|
0ad1ce1ca0 | |
|
|
83c8a61f14 | |
|
|
6d1fcfce07 |
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://bdqk0x4qyk3mc"
|
||||
path="res://.godot/imported/pruge_bass.wav-c5ceaea0b44c81c9961ac70c3dc5f07c.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/music&sfx/sfx/pruge_bass.wav"
|
||||
dest_files=["res://.godot/imported/pruge_bass.wav-c5ceaea0b44c81c9961ac70c3dc5f07c.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
|
||||
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://bqg3q37ugk8ja"
|
||||
path="res://.godot/imported/purge_build_up.wav-478e240cc516d2308f089ccc744223fa.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/music&sfx/sfx/purge_build_up.wav"
|
||||
dest_files=["res://.godot/imported/purge_build_up.wav-478e240cc516d2308f089ccc744223fa.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
|
||||
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://bci64umwgnpjj"
|
||||
path="res://.godot/imported/purge_impact_boom.wav-dc17074eb0200fae32cf877c94544e56.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/music&sfx/sfx/purge_impact_boom.wav"
|
||||
dest_files=["res://.godot/imported/purge_impact_boom.wav-dc17074eb0200fae32cf877c94544e56.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
|
||||
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://xkygjr5ewvic"
|
||||
path="res://.godot/imported/purge_sustained_boom.wav-ebb185be11c79583807c007ee85b5815.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/music&sfx/sfx/purge_sustained_boom.wav"
|
||||
dest_files=["res://.godot/imported/purge_sustained_boom.wav-ebb185be11c79583807c007ee85b5815.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
|
||||
|
|
@ -6,4 +6,6 @@ class_name Perk
|
|||
@export var stats: String = ""
|
||||
@export var spell: String = SpellLibrary.NONE
|
||||
@export var icon: Texture2D = null
|
||||
@export var archetype_slot: String = ""
|
||||
@export var requires_mode: String = ""
|
||||
var effect: Callable
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func _play_hit_sound() -> void:
|
|||
ap.play()
|
||||
|
||||
func take_damage(amount: int) -> void:
|
||||
if is_dying or is_hurt:
|
||||
if is_dying:
|
||||
return
|
||||
hp -= amount
|
||||
if hp <= 0:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
extends ProjectileBase
|
||||
|
||||
var fireball_aoe = false
|
||||
var explosion_scene = preload("res://scenes/explosion.tscn")
|
||||
var tornado_scene = preload("res://scenes/tornado.tscn")
|
||||
@onready var perk_effects = get_node("/root/Game/PerkEffects")
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -9,12 +9,24 @@ func _ready() -> void:
|
|||
damage = 10
|
||||
|
||||
func _on_body_entered(body: Node2D) -> void:
|
||||
fireball_aoe = perk_effects.fireball_aoe_enabled
|
||||
if body.is_in_group("enemies"):
|
||||
body.take_damage(damage)
|
||||
if fireball_aoe:
|
||||
var boom = explosion_scene.instantiate()
|
||||
boom.global_position = global_position
|
||||
get_parent().call_deferred("add_child", boom)
|
||||
queue_free()
|
||||
if not body.is_in_group("enemies"):
|
||||
return
|
||||
body.take_damage(damage)
|
||||
match perk_effects.get_mode("fireball.impact"):
|
||||
"explosion":
|
||||
_spawn_explosion()
|
||||
"spawn_tornado":
|
||||
_spawn_tornado()
|
||||
queue_free()
|
||||
|
||||
func _spawn_explosion() -> void:
|
||||
var boom = explosion_scene.instantiate()
|
||||
boom.global_position = global_position
|
||||
get_parent().call_deferred("add_child", boom)
|
||||
|
||||
func _spawn_tornado() -> void:
|
||||
var tw = tornado_scene.instantiate()
|
||||
tw.global_position = global_position
|
||||
tw.explosion_damage = int(round(tw.explosion_damage * 0.33))
|
||||
get_parent().call_deferred("add_child", tw)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,15 +28,22 @@ func _ready() -> void:
|
|||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
func _is_eligible(perk: Perk) -> bool:
|
||||
if perk.requires_mode == "":
|
||||
return true
|
||||
var parts = perk.requires_mode.split(":")
|
||||
return perk_effects.get_mode(parts[0]) == parts[1]
|
||||
|
||||
func show_perks():
|
||||
player.stream = level_sound
|
||||
player.play()
|
||||
if perks.is_empty():
|
||||
var eligible = perks.filter(_is_eligible)
|
||||
if eligible.is_empty():
|
||||
return
|
||||
var tween = create_tween()
|
||||
tween.tween_property($ColorRect, "color:a", 0.6, 0.3)
|
||||
get_tree().paused = true
|
||||
var shuffled = perks.duplicate()
|
||||
var shuffled = eligible.duplicate()
|
||||
shuffled.shuffle()
|
||||
var chosen = shuffled.slice(0, 3)
|
||||
await get_tree().create_timer(0.4).timeout
|
||||
|
|
@ -54,6 +61,9 @@ func select_perk(perk):
|
|||
_spawn_level_up_particles(get_node("/root/Game/World/Witch").global_position)
|
||||
_spawn_level_up_particles(get_node("/root/Game/World/Player").global_position)
|
||||
perks.erase(perk)
|
||||
if perk.archetype_slot != "":
|
||||
for p in perks.filter(func(p): return p.archetype_slot == perk.archetype_slot):
|
||||
perks.erase(p)
|
||||
for child in $HBoxContainer.get_children():
|
||||
child.animate_out()
|
||||
var tween = create_tween()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ var fireball = preload("res://scenes/fireball.tscn")
|
|||
var throwing_knife = preload("res://scenes/throwing_knive.tscn")
|
||||
var cauldron
|
||||
var available_perks: Array[Perk] = []
|
||||
var fireball_aoe_enabled = false
|
||||
var spell_modes: Dictionary = {}
|
||||
var laser_retarget_enabled = false
|
||||
var throwing_knife_enabled = false
|
||||
var throwing_knife_cooldown: float = 2.0
|
||||
|
|
@ -39,6 +39,12 @@ func _stat_new(label: String, val: String) -> String:
|
|||
func _stat_toggle(label: String) -> String:
|
||||
return "[color=#aaaaaa]" + label + "[/color]\n[color=#888888]OFF[/color] → [color=#77dd77]ON[/color]"
|
||||
|
||||
func get_mode(slot: String) -> String:
|
||||
return spell_modes.get(slot, "base")
|
||||
|
||||
func set_mode(slot: String, mode: String) -> void:
|
||||
spell_modes[slot] = mode
|
||||
|
||||
func _ready() -> void:
|
||||
cauldron = witch.get_node("CauldronBar")
|
||||
|
||||
|
|
@ -78,19 +84,71 @@ func _ready() -> void:
|
|||
|
||||
var faoe = Perk.new()
|
||||
faoe.name = "AOE Fireball"
|
||||
faoe.description = "Fireballs explode on impact"
|
||||
faoe.description = "Fireballs explode on impact instead of hitting a single enemy"
|
||||
faoe.stats = _stat_toggle("AOE")
|
||||
faoe.spell = SpellLibrary.FIREBALL
|
||||
faoe.icon = _icon_fireball
|
||||
faoe.effect = fireball_aoe
|
||||
faoe.archetype_slot = "fireball.impact"
|
||||
faoe.effect = fireball_set_impact_explosion
|
||||
available_perks.append(faoe)
|
||||
|
||||
var ftor = Perk.new()
|
||||
ftor.name = "Fireball: Spawn Tornado"
|
||||
ftor.description = "Fireballs spawn a tornado on impact instead of exploding"
|
||||
ftor.stats = _stat_toggle("Tornado")
|
||||
ftor.spell = SpellLibrary.FIREBALL
|
||||
ftor.icon = _icon_fireball
|
||||
ftor.archetype_slot = "fireball.impact"
|
||||
ftor.effect = fireball_set_impact_tornado
|
||||
available_perks.append(ftor)
|
||||
|
||||
var fsg = Perk.new()
|
||||
fsg.name = "Fireball: Shotgun"
|
||||
fsg.description = "Fireball fires a spread of pellets in front of you instead of homing shots"
|
||||
fsg.stats = _stat_toggle("Shotgun")
|
||||
fsg.spell = SpellLibrary.FIREBALL
|
||||
fsg.icon = _icon_fireball
|
||||
fsg.archetype_slot = "fireball.cast"
|
||||
fsg.effect = fireball_set_cast_shotgun
|
||||
available_perks.append(fsg)
|
||||
|
||||
var fmg = Perk.new()
|
||||
fmg.name = "Fireball: Minigun"
|
||||
fmg.description = "Fireball rapid-fires at the nearest enemy instead of one burst"
|
||||
fmg.stats = _stat_toggle("Minigun")
|
||||
fmg.spell = SpellLibrary.FIREBALL
|
||||
fmg.icon = _icon_fireball
|
||||
fmg.archetype_slot = "fireball.cast"
|
||||
fmg.effect = fireball_set_cast_minigun
|
||||
available_perks.append(fmg)
|
||||
|
||||
var smp = Perk.new()
|
||||
smp.name = "Shotgun: More Pellets"
|
||||
smp.description = "Fire more pellets per shotgun blast"
|
||||
smp.stats = _stat("Pellets", str(witch.fireball_pellet_count), str(witch.fireball_pellet_count + 2))
|
||||
smp.spell = SpellLibrary.FIREBALL
|
||||
smp.icon = _icon_fireball
|
||||
smp.requires_mode = "fireball.cast:shotgun"
|
||||
smp.effect = shotgun_more_pellets
|
||||
available_perks.append(smp)
|
||||
|
||||
var msu = Perk.new()
|
||||
msu.name = "Minigun: Spin-Up"
|
||||
msu.description = "Fire fireballs faster in minigun mode"
|
||||
msu.stats = _stat("Interval", "%.3fs" % witch.fireball_minigun_interval, "%.3fs" % maxf(0.03, witch.fireball_minigun_interval - 0.015))
|
||||
msu.spell = SpellLibrary.FIREBALL
|
||||
msu.icon = _icon_fireball
|
||||
msu.requires_mode = "fireball.cast:minigun"
|
||||
msu.effect = minigun_spin_up
|
||||
available_perks.append(msu)
|
||||
|
||||
var fsp = Perk.new()
|
||||
fsp.name = "Fireball Spread"
|
||||
fsp.description = "Fireballs target more enemies"
|
||||
fsp.stats = _stat("Targets", str(witch.fireball_max_targets), str(witch.fireball_max_targets + 3))
|
||||
fsp.spell = SpellLibrary.FIREBALL
|
||||
fsp.icon = _icon_fireball
|
||||
fsp.requires_mode = "fireball.cast:base"
|
||||
fsp.effect = fireball_spread
|
||||
available_perks.append(fsp)
|
||||
|
||||
|
|
@ -166,8 +224,17 @@ func laser_retarget():
|
|||
func double_shuriken():
|
||||
witch.shuriken_count += 2
|
||||
|
||||
func fireball_aoe():
|
||||
fireball_aoe_enabled = true
|
||||
func fireball_set_impact_explosion() -> void:
|
||||
set_mode("fireball.impact", "explosion")
|
||||
|
||||
func fireball_set_impact_tornado() -> void:
|
||||
set_mode("fireball.impact", "spawn_tornado")
|
||||
|
||||
func fireball_set_cast_shotgun() -> void:
|
||||
set_mode("fireball.cast", "shotgun")
|
||||
|
||||
func fireball_set_cast_minigun() -> void:
|
||||
set_mode("fireball.cast", "minigun")
|
||||
|
||||
func fireball_spread():
|
||||
witch.fireball_max_targets = witch.fireball_max_targets * 1.5
|
||||
|
|
@ -177,9 +244,35 @@ func fireball_spread():
|
|||
fsp.stats = _stat("Targets", str(witch.fireball_max_targets), str(witch.fireball_max_targets * 1.5))
|
||||
fsp.spell = SpellLibrary.FIREBALL
|
||||
fsp.icon = _icon_fireball
|
||||
fsp.requires_mode = "fireball.cast:base"
|
||||
fsp.effect = fireball_spread
|
||||
available_perks.append(fsp)
|
||||
|
||||
|
||||
func shotgun_more_pellets() -> void:
|
||||
witch.fireball_pellet_count += 2
|
||||
var smp = Perk.new()
|
||||
smp.name = "Shotgun: More Pellets"
|
||||
smp.description = "Fire more pellets per shotgun blast"
|
||||
smp.stats = _stat("Pellets", str(witch.fireball_pellet_count), str(witch.fireball_pellet_count + 2))
|
||||
smp.spell = SpellLibrary.FIREBALL
|
||||
smp.icon = _icon_fireball
|
||||
smp.requires_mode = "fireball.cast:shotgun"
|
||||
smp.effect = shotgun_more_pellets
|
||||
available_perks.append(smp)
|
||||
|
||||
func minigun_spin_up() -> void:
|
||||
witch.fireball_minigun_interval = maxf(0.03, witch.fireball_minigun_interval - 0.015)
|
||||
if witch.fireball_minigun_interval > 0.03:
|
||||
var msu = Perk.new()
|
||||
msu.name = "Minigun: Spin-Up"
|
||||
msu.description = "Fire fireballs faster in minigun mode"
|
||||
msu.stats = _stat("Interval", "%.3fs" % (witch.fireball_minigun_interval + 0.015), "%.3fs" % witch.fireball_minigun_interval)
|
||||
msu.spell = SpellLibrary.FIREBALL
|
||||
msu.icon = _icon_fireball
|
||||
msu.requires_mode = "fireball.cast:minigun"
|
||||
msu.effect = minigun_spin_up
|
||||
available_perks.append(msu)
|
||||
|
||||
func brew_explosion():
|
||||
cauldron.brew_explosion = true
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ signal health_changed(current_hp: int, max_hp: int)
|
|||
signal died(attacker)
|
||||
|
||||
var camera
|
||||
@onready var perk_effects = get_node("/root/Game/PerkEffects")
|
||||
var is_casting = false
|
||||
var fireball = preload("res://scenes/fireball.tscn")
|
||||
var shuriken = preload("res://scenes/shuriken.tscn")
|
||||
|
|
@ -19,6 +20,9 @@ var max_hp: int = 50
|
|||
var current_hp: int = 50
|
||||
var is_invincible: bool = false
|
||||
var fireball_max_targets: int = 5
|
||||
var fireball_pellet_count: int = 6
|
||||
var fireball_minigun_shots: int = 6
|
||||
var fireball_minigun_interval: float = 0.08
|
||||
|
||||
const HP_BAR_WIDTH = 20
|
||||
const HP_BAR_HEIGHT = 3
|
||||
|
|
@ -55,6 +59,15 @@ func _on_collect(DropsBase):
|
|||
|
||||
|
||||
func shoot_fireballs():
|
||||
match perk_effects.get_mode("fireball.cast"):
|
||||
"shotgun":
|
||||
await _cast_fireball_shotgun()
|
||||
"minigun":
|
||||
await _cast_fireball_minigun()
|
||||
_:
|
||||
await _cast_fireball_base()
|
||||
|
||||
func _cast_fireball_base() -> void:
|
||||
var enemies = get_tree().get_nodes_in_group("enemies")
|
||||
enemies = enemies.filter(func(e): return is_instance_valid(e))
|
||||
enemies.sort_custom(func(a, b): return global_position.distance_to(a.global_position) < global_position.distance_to(b.global_position))
|
||||
|
|
@ -64,15 +77,46 @@ func shoot_fireballs():
|
|||
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)
|
||||
_spawn_fireball_toward(enemy.global_position)
|
||||
_play_fire_sfx()
|
||||
camera.shake(0.15, 0.5)
|
||||
await get_tree().create_timer(0.12).timeout
|
||||
$AnimatedSprite2D.play("south")
|
||||
|
||||
func _cast_fireball_shotgun() -> void:
|
||||
var target = get_nearest_enemy(global_position)
|
||||
var aim_pos = target.global_position if target != null else global_position + Vector2.DOWN * 100.0
|
||||
var base_dir = global_position.direction_to(aim_pos)
|
||||
_face_direction(base_dir)
|
||||
var spread_deg = 40.0
|
||||
for i in range(fireball_pellet_count):
|
||||
var t = float(i) / float(max(1, fireball_pellet_count - 1))
|
||||
var angle_offset = deg_to_rad(lerp(-spread_deg / 2.0, spread_deg / 2.0, t))
|
||||
var dir = base_dir.rotated(angle_offset)
|
||||
_spawn_fireball_toward(global_position + dir * 300.0)
|
||||
_play_fire_sfx()
|
||||
camera.shake(0.2, 0.6)
|
||||
$AnimatedSprite2D.play("south")
|
||||
|
||||
func _cast_fireball_minigun() -> void:
|
||||
for i in range(fireball_minigun_shots):
|
||||
var target = get_nearest_enemy(global_position)
|
||||
if target == null:
|
||||
break
|
||||
var dir = global_position.direction_to(target.global_position)
|
||||
_face_direction(dir)
|
||||
_spawn_fireball_toward(target.global_position)
|
||||
_play_fire_sfx()
|
||||
camera.shake(0.08, 0.3)
|
||||
await get_tree().create_timer(fireball_minigun_interval).timeout
|
||||
$AnimatedSprite2D.play("south")
|
||||
|
||||
func _spawn_fireball_toward(target_pos: Vector2) -> void:
|
||||
var fb = fireball.instantiate()
|
||||
fb.global_position = global_position
|
||||
get_parent().add_child(fb)
|
||||
fb.launch(target_pos)
|
||||
|
||||
func _face_direction(dir: Vector2) -> void:
|
||||
if abs(dir.x) >= abs(dir.y):
|
||||
$AnimatedSprite2D.play("east" if dir.x > 0 else "west")
|
||||
|
|
|
|||
Loading…
Reference in New Issue