extends "res://scripts/enemy_base.gd" # Rat Enemy - Moves in 4 directions, chases players enum RatState { IDLE, WANDERING } var state: RatState = RatState.IDLE var state_timer: float = 0.0 var idle_duration: float = 3.0 # Longer idle time var wander_duration: float = 2.0 # Short wander time var detection_range: float = 150.0 func _ready(): super._ready() max_health = 15.0 # Reduced from 25.0 for better balance current_health = max_health move_speed = 40.0 # Much slower damage = 8.0 exp_reward = 5.0 # Rats give low EXP state_timer = idle_duration # CRITICAL: Ensure collision mask is set correctly (walls are on layer 7 = bit 6 = 64) collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64) # Override to set weak stats for rats func _initialize_character_stats(): super._initialize_character_stats() # Rats are weak enemies - very low END for low DEF character_stats.baseStats.end = 5 # END=5 gives DEF=1.0 (5 * 0.2) character_stats.baseStats.str = 7 # Low STR = low damage character_stats.baseStats.dex = 8 # Slightly higher DEX (rats are fast) character_stats.baseStats.int = 5 character_stats.baseStats.wis = 5 character_stats.baseStats.lck = 5 # Re-initialize hp and mp with new stats character_stats.hp = character_stats.maxhp character_stats.mp = character_stats.maxmp # Sync max_health from character_stats max_health = character_stats.maxhp current_health = max_health func _ai_behavior(delta): # Update state timer state_timer -= delta # Find nearest player target_player = _find_nearest_player() # State machine match state: RatState.IDLE: _idle_behavior(delta) RatState.WANDERING: _wandering_behavior(delta) # Update animation _update_animation(delta) func _idle_behavior(_delta): velocity = Vector2.ZERO # Show idle frame anim_frame = 2 # Switch to wandering after idle duration (don't chase players) if state_timer <= 0: state = RatState.WANDERING state_timer = wander_duration func _wandering_behavior(_delta): # Pick a random direction at the start of wandering if state_timer >= wander_duration - 0.1: # Only change direction at start var dirs = [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT] var random_dir = dirs[randi() % dirs.size()] velocity = random_dir * move_speed current_direction = _get_direction_from_vector(random_dir) # Keep moving in the same direction during wander # (velocity is already set, just maintain it) # Switch back to idle after wander duration if state_timer <= 0: state = RatState.IDLE state_timer = idle_duration velocity = Vector2.ZERO # Stop moving func _snap_to_4_directions(dir: Vector2) -> Vector2: # Snap to closest cardinal direction if abs(dir.x) > abs(dir.y): return Vector2(sign(dir.x), 0) else: return Vector2(0, sign(dir.y)) func _update_animation(delta): if state == RatState.IDLE or velocity.length() < 1.0: # Show idle frame anim_frame = 2 else: # Animate moving (frames 0, 1, 2) anim_time += delta if anim_time >= anim_speed: anim_time = 0.0 anim_frame = (anim_frame + 1) % 3 # Map directions to 4 sprite directions (only use cardinal directions) var sprite_dir = _get_sprite_direction_4() # Set sprite frame if sprite: sprite.frame = anim_frame + (sprite_dir * 3) func _get_sprite_direction_4() -> int: # Only use 4 cardinal directions match current_direction: Direction.DOWN, Direction.DOWN_LEFT, Direction.DOWN_RIGHT: return 0 # Down Direction.LEFT, Direction.UP_LEFT: return 1 # Left Direction.RIGHT, Direction.UP_RIGHT: return 2 # Right Direction.UP: return 3 # Up return 0 func _update_client_visuals(): # Update visuals on clients based on synced state super._update_client_visuals() # Update sprite frame based on synced anim_frame and direction if sprite: var sprite_dir = _get_sprite_direction_4() sprite.frame = anim_frame + (sprite_dir * 3) # 3 frames per direction func _die(): if is_dead: return # Remove collision layer so they don't collide with players, but still collide with walls set_collision_layer_value(2, false) # Remove from enemy collision layer (layer 2) # Call parent _die() which handles death sync and _play_death_animation() super._die() func _play_death_animation(): # Simple fade out var fade_tween = create_tween() fade_tween.tween_property(sprite, "modulate:a", 0.0, 0.4) await fade_tween.finished queue_free()