added blocking doors to paths.

This commit is contained in:
2026-01-10 19:46:55 +01:00
parent 24ea2f3c60
commit 25be2c00bd
33 changed files with 4383 additions and 455 deletions

View File

@@ -44,6 +44,11 @@ func _ready():
# Top-down physics
motion_mode = MOTION_MODE_FLOATING
# CRITICAL: Set collision mask to include interactable objects (layer 2) and walls (layer 7)
# This allows enemies to collide with interactable objects so they can path around them
# Walls are on layer 7 (bit 6 = 64), not layer 4!
collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
func _physics_process(delta):
if is_dead:
@@ -90,6 +95,9 @@ func _physics_process(delta):
# Check collisions with players
_check_player_collision()
# Check collisions with interactable objects
_check_interactable_object_collision()
# 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)
@@ -125,6 +133,60 @@ func _check_player_collision():
if collider and collider.is_in_group("player"):
_attack_player(collider)
func _check_interactable_object_collision():
# Check collisions with interactable objects and handle pathfinding around them
var blocked_objects = []
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
var collider = collision.get_collider()
if collider and collider.is_in_group("interactable_object"):
var obj = collider
# CRITICAL: Enemies cannot move objects that cannot be lifted
# If object is not liftable, enemy should try to path around it
if obj.has_method("can_be_lifted") and not obj.can_be_lifted():
# Object cannot be lifted - store for pathfinding
blocked_objects.append({"object": obj, "collision": collision})
# If object is liftable but not currently being held, we can still try to push it
# but enemies don't actively push liftable objects (only players do)
elif obj.has_method("is_being_held") and obj.is_being_held():
# Object is being held by someone - treat as obstacle
blocked_objects.append({"object": obj, "collision": collision})
# Handle pathfinding around blocked objects
if blocked_objects.size() > 0 and not is_knocked_back:
var collision_normal = blocked_objects[0].collision.get_normal()
var _obj_pos = blocked_objects[0].object.global_position
# Try to path around the object by moving perpendicular to collision normal
# This creates a side-stepping behavior to go around obstacles
var perpendicular = Vector2(-collision_normal.y, collision_normal.x) # Rotate 90 degrees
# Choose perpendicular direction that moves toward target (if we have one)
if target_player and is_instance_valid(target_player):
var to_target = (target_player.global_position - global_position).normalized()
# If perpendicular dot product with target direction is negative, flip it
if perpendicular.dot(to_target) < 0:
perpendicular = -perpendicular
# Apply perpendicular movement (side-step around object)
var side_step_velocity = perpendicular * move_speed * 0.7 # 70% of move speed for side-step
velocity = velocity.lerp(side_step_velocity, 0.3) # Smoothly blend with existing velocity
# Also add some push-away from object to create clearance
var push_away = collision_normal * move_speed * 0.3
velocity = velocity + push_away
# Limit total velocity to move_speed
if velocity.length() > move_speed:
velocity = velocity.normalized() * move_speed
# For humanoid enemies, sometimes try to destroy the object
if has_method("_try_attack_object") and randf() < 0.1: # 10% chance per frame when blocked
call("_try_attack_object", blocked_objects[0].object)
func _attack_player(player):
# Attack cooldown
if attack_timer > 0:
@@ -199,6 +261,11 @@ func take_damage(amount: float, from_position: Vector2):
# Flash red (even if dying, show the hit)
_flash_damage()
# Show damage number (red, using dmg_numbers.png font) above enemy
# Only show if damage > 0
if amount > 0:
_show_damage_number(amount, from_position)
# Sync damage visual to clients
# Use game_world to route damage visual sync instead of direct RPC to avoid node path issues
if multiplayer.has_multiplayer_peer() and is_inside_tree():
@@ -221,6 +288,44 @@ func rpc_take_damage(amount: float, from_position: Vector2):
if is_multiplayer_authority():
take_damage(amount, from_position)
func _show_damage_number(amount: float, from_position: Vector2):
# Show damage number (red, using dmg_numbers.png font) above enemy
# Only show if damage > 0
if amount <= 0:
return
var damage_number_scene = preload("res://scenes/damage_number.tscn")
if not damage_number_scene:
return
var damage_label = damage_number_scene.instantiate()
if not damage_label:
return
# Set damage text and red color
damage_label.label = str(int(amount))
damage_label.color = Color.RED
# Calculate direction from attacker (slight upward variation)
var direction_from_attacker = (global_position - from_position).normalized()
# Add slight upward bias
direction_from_attacker = direction_from_attacker.lerp(Vector2(0, -1), 0.5).normalized()
damage_label.direction = direction_from_attacker
# Position above enemy's head
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world:
var entities_node = game_world.get_node_or_null("Entities")
if entities_node:
entities_node.add_child(damage_label)
damage_label.global_position = global_position + Vector2(0, -16) # Above enemy head
else:
get_tree().current_scene.add_child(damage_label)
damage_label.global_position = global_position + Vector2(0, -16)
else:
get_tree().current_scene.add_child(damage_label)
damage_label.global_position = global_position + Vector2(0, -16)
func _flash_damage():
# Flash red visual effect
if sprite: