added so player can cast Flame spell
This commit is contained in:
@@ -21,6 +21,13 @@ var knockback_time: float = 0.0
|
||||
var knockback_duration: float = 0.3
|
||||
var knockback_force: float = 125.0 # Scaled down for 1x scale
|
||||
|
||||
# Burn debuff
|
||||
var burn_debuff_timer: float = 0.0 # Timer for burn debuff
|
||||
var burn_debuff_duration: float = 5.0 # Burn lasts 5 seconds
|
||||
var burn_debuff_damage_per_second: float = 1.0 # 1 HP per second
|
||||
var burn_debuff_visual: Node2D = null # Visual indicator for burn debuff
|
||||
var burn_damage_timer: float = 0.0 # Timer for burn damage ticks
|
||||
|
||||
# Z-axis for flying enemies
|
||||
var position_z: float = 0.0
|
||||
var velocity_z: float = 0.0
|
||||
@@ -95,6 +102,19 @@ func _physics_process(delta):
|
||||
# Only server (authority) runs AI and physics
|
||||
if multiplayer.has_multiplayer_peer() and not is_multiplayer_authority():
|
||||
# Clients only interpolate position (handled by _sync_position)
|
||||
# But still update burn visual animation on clients
|
||||
if burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||
if burn_debuff_visual is Sprite2D:
|
||||
var burn_sprite = burn_debuff_visual as Sprite2D
|
||||
var anim_timer = burn_sprite.get_meta("burn_animation_timer", 0.0)
|
||||
anim_timer += delta
|
||||
if anim_timer >= 0.1: # ~10 FPS
|
||||
anim_timer = 0.0
|
||||
var frame = burn_sprite.get_meta("burn_animation_frame", 0)
|
||||
frame = (frame + 1) % 16
|
||||
burn_sprite.frame = frame
|
||||
burn_sprite.set_meta("burn_animation_frame", frame)
|
||||
burn_sprite.set_meta("burn_animation_timer", anim_timer)
|
||||
return
|
||||
|
||||
# Update attack timer
|
||||
@@ -125,6 +145,53 @@ func _physics_process(delta):
|
||||
# Check collisions with interactable objects
|
||||
_check_interactable_object_collision()
|
||||
|
||||
# Update burn debuff
|
||||
if burn_debuff_timer > 0.0:
|
||||
burn_debuff_timer -= delta
|
||||
burn_damage_timer += delta
|
||||
|
||||
# Deal burn damage every second (no knockback)
|
||||
if burn_damage_timer >= 1.0:
|
||||
burn_damage_timer = 0.0
|
||||
# Deal burn damage directly (no knockback, no animation)
|
||||
if character_stats:
|
||||
var old_hp = character_stats.hp
|
||||
character_stats.modify_health(-burn_debuff_damage_per_second)
|
||||
current_health = character_stats.hp
|
||||
if character_stats.hp <= 0:
|
||||
character_stats.no_health.emit()
|
||||
var actual_damage = old_hp - character_stats.hp
|
||||
LogManager.log(str(name) + " takes " + str(actual_damage) + " burn damage! Health: " + str(current_health) + "/" + str(character_stats.maxhp), LogManager.CATEGORY_ENEMY)
|
||||
# Show damage number for burn damage
|
||||
_show_damage_number(actual_damage, global_position, false, false, false) # Show burn damage number
|
||||
# Sync burn damage visual to clients
|
||||
if multiplayer.has_multiplayer_peer() and is_inside_tree():
|
||||
var enemy_name = name
|
||||
var enemy_index = get_meta("enemy_index") if has_meta("enemy_index") else -1
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_sync_enemy_damage_visual"):
|
||||
game_world._rpc_to_ready_peers("_sync_enemy_damage_visual", [enemy_name, enemy_index, actual_damage, global_position, false])
|
||||
|
||||
# Animate burn visual if it's a sprite (only on authority/server)
|
||||
if is_multiplayer_authority() and burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||
if burn_debuff_visual is Sprite2D:
|
||||
var burn_sprite = burn_debuff_visual as Sprite2D
|
||||
var anim_timer = burn_sprite.get_meta("burn_animation_timer", 0.0)
|
||||
anim_timer += delta
|
||||
if anim_timer >= 0.1: # ~10 FPS
|
||||
anim_timer = 0.0
|
||||
var frame = burn_sprite.get_meta("burn_animation_frame", 0)
|
||||
frame = (frame + 1) % 16
|
||||
burn_sprite.frame = frame
|
||||
burn_sprite.set_meta("burn_animation_frame", frame)
|
||||
burn_sprite.set_meta("burn_animation_timer", anim_timer)
|
||||
|
||||
# Remove burn debuff when timer expires
|
||||
if burn_debuff_timer <= 0.0:
|
||||
burn_debuff_timer = 0.0
|
||||
burn_damage_timer = 0.0
|
||||
_remove_burn_debuff()
|
||||
|
||||
# Sync position and animation to clients (only server sends)
|
||||
if multiplayer.has_multiplayer_peer() and is_multiplayer_authority():
|
||||
# Get state value if enemy has a state variable (for bats/slimes)
|
||||
@@ -284,7 +351,7 @@ func _find_nearest_player_to_position(pos: Vector2, max_range: float = 100.0) ->
|
||||
|
||||
return nearest
|
||||
|
||||
func take_damage(amount: float, from_position: Vector2, is_critical: bool = false):
|
||||
func take_damage(amount: float, from_position: Vector2, is_critical: bool = false, is_burn_damage: bool = false, apply_burn_debuff: bool = false):
|
||||
# Only process damage on server/authority
|
||||
if not is_multiplayer_authority():
|
||||
return
|
||||
@@ -335,11 +402,17 @@ func take_damage(amount: float, from_position: Vector2, is_critical: bool = fals
|
||||
actual_damage = amount
|
||||
LogManager.log(str(name) + " took " + str(amount) + " damage! Health: " + str(current_health) + " (critical: " + str(is_critical) + ")", LogManager.CATEGORY_ENEMY)
|
||||
|
||||
# Calculate knockback direction (away from attacker)
|
||||
var knockback_direction = (global_position - from_position).normalized()
|
||||
velocity = knockback_direction * knockback_force
|
||||
is_knocked_back = true
|
||||
knockback_time = 0.0
|
||||
# Only apply knockback if not burn damage
|
||||
if not is_burn_damage:
|
||||
# Calculate knockback direction (away from attacker)
|
||||
var knockback_direction = (global_position - from_position).normalized()
|
||||
velocity = knockback_direction * knockback_force
|
||||
is_knocked_back = true
|
||||
knockback_time = 0.0
|
||||
|
||||
# Apply burn debuff if requested
|
||||
if apply_burn_debuff:
|
||||
_apply_burn_debuff()
|
||||
|
||||
_on_take_damage(from_position)
|
||||
|
||||
@@ -379,10 +452,10 @@ func take_damage(amount: float, from_position: Vector2, is_critical: bool = fals
|
||||
call_deferred("_notify_doors_enemy_died")
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func rpc_take_damage(amount: float, from_position: Vector2, is_critical: bool = false):
|
||||
func rpc_take_damage(amount: float, from_position: Vector2, is_critical: bool = false, is_burn_damage: bool = false, apply_burn_debuff: bool = false):
|
||||
# RPC version - only process on server/authority
|
||||
if is_multiplayer_authority():
|
||||
take_damage(amount, from_position, is_critical)
|
||||
take_damage(amount, from_position, is_critical, is_burn_damage, apply_burn_debuff)
|
||||
|
||||
func _show_damage_number(amount: float, from_position: Vector2, is_critical: bool = false, is_miss: bool = false, is_dodged: bool = false):
|
||||
# Show damage number (red/orange for crits, cyan for dodge, gray for miss, using dmg_numbers.png font) above enemy
|
||||
@@ -475,6 +548,91 @@ func _set_animation(_anim_name: String):
|
||||
# (e.g., enemy_humanoid.gd uses player-like animation system)
|
||||
pass
|
||||
|
||||
func _apply_burn_debuff():
|
||||
# Apply burn debuff to enemy
|
||||
if burn_debuff_timer > 0.0:
|
||||
# Already burning - refresh duration
|
||||
burn_debuff_timer = burn_debuff_duration
|
||||
burn_damage_timer = 0.0 # Reset damage timer
|
||||
LogManager.log(str(name) + " burn debuff refreshed", LogManager.CATEGORY_ENEMY)
|
||||
return
|
||||
|
||||
# Start burn debuff
|
||||
burn_debuff_timer = burn_debuff_duration
|
||||
burn_damage_timer = 0.0
|
||||
LogManager.log(str(name) + " applied burn debuff (" + str(burn_debuff_duration) + " seconds)", LogManager.CATEGORY_ENEMY)
|
||||
|
||||
# Create visual indicator
|
||||
_create_burn_debuff_visual()
|
||||
|
||||
# Sync burn debuff to clients
|
||||
if multiplayer.has_multiplayer_peer() and is_inside_tree():
|
||||
var enemy_name = name
|
||||
var enemy_index = get_meta("enemy_index") if has_meta("enemy_index") else -1
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_sync_enemy_burn_debuff"):
|
||||
game_world._rpc_to_ready_peers("_sync_enemy_burn_debuff", [enemy_name, enemy_index, true])
|
||||
|
||||
func _create_burn_debuff_visual():
|
||||
# Remove existing visual if any
|
||||
if burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||
burn_debuff_visual.queue_free()
|
||||
|
||||
# Load burn debuff scene
|
||||
var burn_debuff_scene = load("res://scenes/debuff_burn.tscn")
|
||||
if burn_debuff_scene:
|
||||
burn_debuff_visual = burn_debuff_scene.instantiate()
|
||||
add_child(burn_debuff_visual)
|
||||
# Position on enemy (centered)
|
||||
burn_debuff_visual.position = Vector2(0, 0)
|
||||
burn_debuff_visual.z_index = 5 # Above enemy sprite
|
||||
LogManager.log(str(name) + " created burn debuff visual", LogManager.CATEGORY_ENEMY)
|
||||
else:
|
||||
# Fallback: create simple sprite if scene doesn't exist
|
||||
var burn_texture = load("res://assets/gfx/fx/burn.png")
|
||||
if burn_texture:
|
||||
var burn_sprite = Sprite2D.new()
|
||||
burn_sprite.name = "BurnDebuffSprite"
|
||||
burn_sprite.texture = burn_texture
|
||||
burn_sprite.hframes = 4
|
||||
burn_sprite.vframes = 4
|
||||
burn_sprite.frame = 0
|
||||
burn_sprite.position = Vector2(0, 0)
|
||||
burn_sprite.z_index = 5 # Above enemy sprite
|
||||
burn_sprite.set_meta("burn_animation_frame", 0)
|
||||
burn_sprite.set_meta("burn_animation_timer", 0.0)
|
||||
add_child(burn_sprite)
|
||||
burn_debuff_visual = burn_sprite
|
||||
|
||||
func _remove_burn_debuff():
|
||||
# Remove burn debuff visual
|
||||
if burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||
burn_debuff_visual.queue_free()
|
||||
burn_debuff_visual = null
|
||||
LogManager.log(str(name) + " burn debuff removed", LogManager.CATEGORY_ENEMY)
|
||||
|
||||
# Sync burn debuff removal to clients
|
||||
if multiplayer.has_multiplayer_peer() and is_inside_tree():
|
||||
var enemy_name = name
|
||||
var enemy_index = get_meta("enemy_index") if has_meta("enemy_index") else -1
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world and game_world.has_method("_sync_enemy_burn_debuff"):
|
||||
game_world._rpc_to_ready_peers("_sync_enemy_burn_debuff", [enemy_name, enemy_index, false])
|
||||
|
||||
func _sync_burn_debuff(apply: bool):
|
||||
# Client-side sync of burn debuff visual
|
||||
if apply:
|
||||
if burn_debuff_timer <= 0.0:
|
||||
# Only create visual if not already burning
|
||||
_create_burn_debuff_visual()
|
||||
burn_debuff_timer = burn_debuff_duration
|
||||
burn_damage_timer = 0.0
|
||||
else:
|
||||
# Remove visual
|
||||
_remove_burn_debuff()
|
||||
burn_debuff_timer = 0.0
|
||||
burn_damage_timer = 0.0
|
||||
|
||||
func _die():
|
||||
if is_dead:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user