class_name ComponentMovement extends Node signal direction_changed @export_node_path("CharacterBody2D") var entityPath: NodePath @onready var entity: CharacterBody2D = get_node(entityPath) @export_node_path("AnimatedSprite2D") var spritePath: NodePath @onready var sprite: AnimatedSprite2D = get_node(spritePath) @export var SPEED: float const DIRECTIONS = [Vector2.UP, Vector2.RIGHT, Vector2.DOWN, Vector2.LEFT] var CURRENT_DIRECTION = Vector2.UP var LAST_DIRECTION = Vector2.UP var was_colliding = false var animations: Dictionary = { "default": "default", "up": "up", "down": "down", "left": "left", "right": "right", } @export var timer_time: float = 3.0 @onready var MovementTimer: Timer = Timer.new() @export var follows: bool = false @export_range(1, 100, 1) var follow_chance: int = 100 func _ready(): MovementTimer.wait_time = timer_time MovementTimer.autostart = true MovementTimer.connect("timeout", Callable(_on_movement_timer_timeout)) add_child(MovementTimer) func physics_process(delta): if CURRENT_DIRECTION == Vector2.UP: entity.velocity.y -= SPEED/2 if sprite.sprite_frames.has_animation(animations["up"]): sprite.play(animations["up"]) elif CURRENT_DIRECTION == Vector2.DOWN: entity.velocity.y += SPEED/2 if sprite.sprite_frames.has_animation(animations["down"]): sprite.play(animations["down"]) elif CURRENT_DIRECTION == Vector2.LEFT: entity.velocity.x -= SPEED/2 if sprite.sprite_frames.has_animation(animations["left"]): sprite.play(animations["left"]) elif CURRENT_DIRECTION == Vector2.RIGHT: entity.velocity.x += SPEED/2 if sprite.sprite_frames.has_animation(animations["right"]): sprite.play(animations["right"]) if sprite.sprite_frames.has_animation(animations["default"]): sprite.play(animations["default"]) var collision = entity.move_and_collide(entity.velocity * delta) entity.velocity = entity.velocity.lerp(Vector2(0, 0), 1) # speed too low => no collision # todo movement along grid (maybe move position to current tile center) var target_grid_position = Utilities.from_grid_to_position(Utilities.get_level_position(entity) + CURRENT_DIRECTION) entity.position = entity.position.move_toward(target_grid_position, delta*SPEED/2) # todo or set velocity to tiles per SPEED per second somehow, instead of Timer if collision: was_colliding = true if MovementTimer.time_left > 0: _on_movement_timer_timeout() else: was_colliding = false return collision func set_animations(new_animations: Dictionary): animations.merge(new_animations, true) func _on_movement_timer_timeout(): var randomize_direction = false if false and follows and randi_range(0, 100) <= follow_chance: var player_direction = entity.position.direction_to(Global.player.position) var query = PhysicsRayQueryParameters2D.new() query.set_from(entity.position) query.set_to(Global.player.position) query.set_collision_mask(entity.collision_mask) var intersection = entity.get_world_2d().direct_space_state.intersect_ray(query) if intersection: randomize_direction = true if abs(player_direction.x) >= abs(player_direction.y): if player_direction.x >= 0: CURRENT_DIRECTION = Vector2.RIGHT elif player_direction.x < 0: CURRENT_DIRECTION = Vector2.LEFT elif abs(player_direction.y) > abs(player_direction.x): if player_direction.y >= 0: CURRENT_DIRECTION = Vector2.DOWN elif player_direction.y < 0: CURRENT_DIRECTION = Vector2.UP if was_colliding and LAST_DIRECTION == CURRENT_DIRECTION: randomize_direction = true else: randomize_direction = true if randomize_direction: var directions = DIRECTIONS.duplicate() directions.erase(CURRENT_DIRECTION) directions.shuffle() if not directions.is_empty(): CURRENT_DIRECTION = directions[0] emit_signal("direction_changed", CURRENT_DIRECTION) LAST_DIRECTION = CURRENT_DIRECTION if not sprite.sprite_frames.has_animation(animations["default"]): var frame = sprite.frame var progress = sprite.frame_progress if CURRENT_DIRECTION == Vector2.UP and sprite.sprite_frames.has_animation(animations["up"]): sprite.play(animations["up"]) elif CURRENT_DIRECTION == Vector2.DOWN and sprite.sprite_frames.has_animation(animations["down"]): sprite.play(animations["down"]) elif CURRENT_DIRECTION == Vector2.LEFT and sprite.sprite_frames.has_animation(animations["left"]): sprite.play(animations["left"]) elif CURRENT_DIRECTION == Vector2.RIGHT and sprite.sprite_frames.has_animation(animations["right"]): sprite.play(animations["right"]) sprite.set_frame_and_progress(frame, progress) MovementTimer.start()