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_node_path("ComponentCollision") var component_collision_path: NodePath var component_collision: ComponentCollision @export_range(0, 2, 0.01) var SPEED: float const DIRECTIONS = [Vector2.UP, Vector2.RIGHT, Vector2.DOWN, Vector2.LEFT] var CURRENT_DIRECTION = Vector2.UP var FIXED_DIRECTION = null var was_colliding = false var animations: Dictionary = { "default": "default", "up": "up", "down": "down", "left": "left", "right": "right", } var CURRENT_ANIMATION = { "name": "default", "frame": 0, "progress": 0, } @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 var is_paused = false var tween: Tween func _ready(): #MovementTimer.wait_time = timer_time #MovementTimer.autostart = true #MovementTimer.connect("timeout", Callable(_on_movement_timer_timeout)) #add_child(MovementTimer) pass func init(): #if component_collision_path: #component_collision = get_node(component_collision_path) #component_collision.collision_area.connect("collided", func(body): #if check_direction_has_intersection(CURRENT_DIRECTION): #tween.stop() #move() #) ## TODO: area mit direction change detection? on solid and bomb? await get_tree().create_timer(0.2).timeout # wait for surrounding collision objects to load move() #func physics_process(delta): #if is_paused: #return null # #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 ## use raycast in every direction to check next valid tile, move there ## get start, current and end position, check if reached instead of velocity to tiles per second or 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() func pause(): is_paused = true #MovementTimer.paused = true process_mode = Node.PROCESS_MODE_DISABLED func unpause(): is_paused = false #MovementTimer.paused = false process_mode = Node.PROCESS_MODE_INHERIT sprite.play(CURRENT_ANIMATION.name) sprite.set_frame_and_progress(CURRENT_ANIMATION.frame, CURRENT_ANIMATION.progress) func move(): var can_move = true var randomize_direction = false #randomize_direction = randi() % 10 > 8 if check_direction_has_intersection(CURRENT_DIRECTION): randomize_direction = true var directions = DIRECTIONS.duplicate() if randomize_direction: directions.erase(CURRENT_DIRECTION) directions.shuffle() for idx in range(directions.size()): if not check_direction_has_intersection(directions[idx]): CURRENT_DIRECTION = directions[idx] break if idx == directions.size() - 1: can_move = false if FIXED_DIRECTION: CURRENT_DIRECTION = FIXED_DIRECTION emit_signal("direction_changed", CURRENT_DIRECTION) var frame = sprite.frame var progress = sprite.frame_progress var animation = sprite.animation if sprite.sprite_frames.has_animation(animations["default"]): sprite.play(animations["default"]) else: 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) CURRENT_ANIMATION.name = animation CURRENT_ANIMATION.frame = frame CURRENT_ANIMATION.progress = progress var target_grid_position = Utilities.from_grid_to_position(Utilities.get_level_position(entity) + CURRENT_DIRECTION) tween = create_tween() if can_move: tween.tween_property(entity, "position", target_grid_position, 1/SPEED).set_ease(Tween.EASE_IN_OUT) else: tween.tween_callback(func(): await get_tree().create_timer(1/SPEED).timeout ) tween.tween_callback(func(): move() ) func check_direction_has_intersection(direction: Vector2): var query = PhysicsRayQueryParameters2D.new() query.set_from(entity.position) query.set_to(Utilities.from_grid_to_position(Utilities.get_level_position(entity) + direction)) query.set_collision_mask(entity.collision_mask) var intersection = entity.get_world_2d().direct_space_state.intersect_ray(query) return intersection