From 85e0ca75ff4d95ae181ed2507702e326b454ccd8 Mon Sep 17 00:00:00 2001 From: Artur <2123806@stud.th-mannheim.de> Date: Tue, 2 Jun 2026 16:36:27 +0200 Subject: [PATCH] Polished Laser visuals and added perk that added retargeting on kill --- assets/music&sfx/sfx/laser.wav | Bin 0 -> 120296 bytes assets/music&sfx/sfx/laser.wav.import | 24 ++++++ scripts/laser.gd | 113 ++++++++++++++++++-------- scripts/perk_effects.gd | 18 ++++ scripts/witch.gd | 10 ++- 5 files changed, 132 insertions(+), 33 deletions(-) create mode 100644 assets/music&sfx/sfx/laser.wav create mode 100644 assets/music&sfx/sfx/laser.wav.import diff --git a/assets/music&sfx/sfx/laser.wav b/assets/music&sfx/sfx/laser.wav new file mode 100644 index 0000000000000000000000000000000000000000..874f9a1e073f8bc0f998e8e97421f3a11273bcbe GIT binary patch literal 120296 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 void: target = get_highest_hp_enemy() if target == null: queue_free() return - spawn_beam() + current_angle = global_position.direction_to(target.global_position).angle() + rebuild_segments() func _process(delta: float) -> void: if done: return elapsed += delta tick_timer += delta + + if sweeping: + current_angle = lerp_angle(current_angle, sweep_to, 1.0 - exp(-SWEEP_RATE * delta)) + if abs(angle_difference(current_angle, sweep_to)) < 0.005: + current_angle = sweep_to + sweeping = false + rebuild_segments() + if tick_timer >= TICK_INTERVAL: tick_timer -= TICK_INTERVAL do_damage_tick() @@ -34,32 +50,65 @@ func _process(delta: float) -> void: done = true cleanup() -func spawn_beam() -> void: - var origin := global_position - var dest := target.global_position - var dir := origin.direction_to(dest) - var dist := origin.distance_to(dest) - var angle := dir.angle() +func rebuild_segments() -> void: + var dir := Vector2.from_angle(current_angle) + var beam_start := global_position + dir * start_offset + var dest_dist := global_position.distance_to(target.global_position) + var beam_dist := dest_dist - start_offset + var n_mid := int(max(0.0, beam_dist - segment_size) / segment_size) - place_seg("start", origin, angle) - - var n_mid := int(max(0.0, dist - segment_size * 2.0) / segment_size) + var positions: Array = [beam_start] + var types: Array = ["start"] for i in range(n_mid): - var pos := origin + dir * (segment_size * (float(i) + 1.0)) - mid_segs.append(place_seg("middle", pos, angle)) + positions.append(beam_start + dir * (segment_size * float(i + 1))) + types.append("middle") + positions.append(beam_start + dir * (segment_size * float(n_mid + 1))) + types.append("end") - place_seg("end", dest, angle) + while all_segs.size() < positions.size(): + var seg = beam_seg.instantiate() + get_parent().add_child(seg) + all_segs.append(seg) + while all_segs.size() > positions.size(): + var seg = all_segs.pop_back() + if is_instance_valid(seg): + seg.queue_free() -func place_seg(type: String, pos: Vector2, angle: float) -> Node: - var seg = beam_seg.instantiate() - seg.beam_type = type - seg.global_position = pos - seg.rotation = angle - get_parent().add_child(seg) - all_segs.append(seg) - return seg + mid_segs.clear() + for i in range(all_segs.size()): + var seg = all_segs[i] + if not is_instance_valid(seg): + continue + seg.global_position = positions[i] + seg.rotation = current_angle + if seg.beam_type != types[i]: + seg.beam_type = types[i] + seg.shape.disabled = types[i] != "middle" + if seg.sprite.sprite_frames.has_animation(types[i]): + seg.sprite.play(types[i]) + if types[i] == "middle": + mid_segs.append(seg) + + if all_segs.size() > 1 and is_instance_valid(all_segs[0]): + var ref_frame: int = all_segs[0].sprite.frame + var ref_progress: float = all_segs[0].sprite.frame_progress + for i in range(1, all_segs.size()): + var seg = all_segs[i] + if is_instance_valid(seg): + seg.sprite.frame = ref_frame + seg.sprite.frame_progress = ref_progress + +func retarget(new_target: Node2D) -> void: + target = new_target + sweep_to = global_position.direction_to(new_target.global_position).angle() + sweeping = true func do_damage_tick() -> void: + if (not is_instance_valid(target) or target.is_dying) and perk_effects.laser_retarget_enabled: + var new_target = get_highest_hp_enemy() + if new_target != null: + retarget(new_target) + if is_instance_valid(target) and not target.is_dying: target.take_damage(PRIMARY_TICK_DMG) @@ -79,9 +128,9 @@ func cleanup() -> void: seg.queue_free() queue_free() -func get_highest_hp_enemy() -> Node: - var best: Node = null - var best_hp: int = -1 +func get_highest_hp_enemy() -> Node2D: + var best: Node2D = null + var best_hp: int = -1 for enemy in get_tree().get_nodes_in_group("enemies"): if not is_instance_valid(enemy) or enemy.is_dying: continue diff --git a/scripts/perk_effects.gd b/scripts/perk_effects.gd index 15bbf1f..ccd2584 100644 --- a/scripts/perk_effects.gd +++ b/scripts/perk_effects.gd @@ -7,6 +7,7 @@ var throwing_knife = preload("res://scenes/throwing_knive.tscn") var cauldron var available_perks: Array[Perk] = [] var fireball_aoe_enabled = false +var laser_retarget_enabled = false var throwing_knife_enabled = false var throwing_knife_cooldown: float = 2.0 var throwing_knife_count: int = 1 @@ -21,6 +22,7 @@ var _spellbook_angle: float = 0.0 var _spellbooks: Array = [] var _icon_knife = preload("res://assets/weapons/knvie.png") +var _icon_laser: AtlasTexture 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 @@ -51,6 +53,19 @@ func _ready() -> void: _icon_player.atlas = preload("res://assets/Swordsman_lvl1/Without_shadow/Swordsman_lvl1_Idle_without_shadow.png") _icon_player.region = Rect2(0, 0, 64, 64) + _icon_laser = AtlasTexture.new() + _icon_laser.atlas = preload("res://assets/Fire Pixel Bullet 16x16/All_Fire_Bullet_Pixel_16x16_05.png") + _icon_laser.region = Rect2(96, 16, 16, 16) + + var lrt = Perk.new() + lrt.name = "Laser Lock-On" + lrt.description = "Laser retargets to the next highest health enemy on kill" + lrt.stats = _stat_toggle("Retarget") + lrt.spell = SpellLibrary.LASER + lrt.icon = _icon_laser + lrt.effect = laser_retarget + available_perks.append(lrt) + var dsh = Perk.new() dsh.name = "Double Shuriken" dsh.description = "Fire two shurikens at once" @@ -135,6 +150,9 @@ func _process(delta: float) -> void: var offset = (TAU / _spellbooks.size()) * i _spellbooks[i].global_position = witch.global_position + Vector2(cos(_spellbook_angle + offset), sin(_spellbook_angle + offset)) * SPELLBOOK_RADIUS +func laser_retarget(): + laser_retarget_enabled = true + func double_shuriken(): witch.shuriken_count = 2 diff --git a/scripts/witch.gd b/scripts/witch.gd index e6d73c3..1b5be8a 100644 --- a/scripts/witch.gd +++ b/scripts/witch.gd @@ -10,7 +10,8 @@ var fire_swirl = preload("res://scenes/fire_swirl.tscn") var tornado = preload("res://scenes/tornado.tscn") var laser = preload("res://scenes/laser.tscn") var shuriken_count = 1 -var _fire_sfx = preload("res://assets/music&sfx/sfx/fire.wav") +var _fire_sfx = preload("res://assets/music&sfx/sfx/fire.wav") +var _laser_sfx = preload("res://assets/music&sfx/sfx/laser.wav") var max_hp: int = 100 var current_hp: int = 100 @@ -106,6 +107,13 @@ func shoot_laser(): ls.global_position = global_position get_parent().add_child(ls) camera.shake(0.4, 1.2) + var asp = AudioStreamPlayer.new() + asp.stream = _laser_sfx + asp.volume_db = 6 + asp.bus = "SFX" + get_parent().add_child(asp) + asp.play() + asp.finished.connect(asp.queue_free) func take_damage(amount: int) -> void: if is_invincible: