extends CharacterBody3D var gravity : float = ProjectSettings.get_setting("physics/3d/default_gravity") var grabbable : bool = false var shootable : bool = true var weakpoint : bool = false var playerallied : bool = false var rootnode : bool = true var waitingfor : int = 0 var perception_refreshinterval : float = 0.6 var perception_radius : float = 32 var perception_giveuptime : float = 6 var path_refreshinterval : float = 1.5 @export var hp : float = 8 @export var physics_push_force : float = 1.5 var lunge_startdistance : float = 6 var lunge_pausetime : float = 0.1 var lunge_traveldistance : float = 12 var lunge_travelheight : float = 0.3 var lunge_velocity : float = 18 var lunge_upwardvelocity : float = 4 var lunge_damage : float = 8 var lunge_recovery : float = 0.4 var currentlunge_angle : Vector3 var currentlunge_startpos : Vector3 var currentlunge_apex : float var currentlunge_lethal : bool = true var state : String = "idle" var movement_speed: float = 8 var movement_delta: float var path_point_margin: float = 1 var current_path_index: int = 0 var current_path_point: Vector3 var current_path: PackedVector3Array @onready var default_3d_map_rid: RID = get_world_3d().get_navigation_map() var startpos : Vector3 var forth : bool = false @export var min_player_distance : float = 2.0 @export var min_ally_distance : float = 6.0 var runtarget : Vector3 @export var player : Node const sprite_front1 = preload("res://sprites/enemies/chompyboy_front1.png") const sprite_left1 = preload("res://sprites/enemies/chompyboy_left1.png") const sprite_right1 = preload("res://sprites/enemies/chompyboy_right1.png") const sprite_back1 = preload("res://sprites/enemies/chompyboy_back1.png") # Called when the node enters the scene tree for the first time. func _ready(): rotation_degrees.y += 180 player.enemies.append(self) $perception_timer.start(perception_refreshinterval) func _physics_process(delta): sprite_angle() ai_process() if state == "chasing" and player.global_transform.origin.distance_to(runtarget) > 4: runtarget = player.global_transform.origin if current_path.is_empty() and NavigationServer3D.map_is_active(default_3d_map_rid): if state == "chasing" and global_transform.origin.distance_to(runtarget) > 4: set_movement_target(runtarget) $chase_timer.start(path_refreshinterval) move_and_slide() if velocity.y > 3: print("hey! stop that! current state "+state+" current velocity "+str(velocity.y)+"current motion mode "+str(get_motion_mode())) velocity.y = 3 for i in get_slide_collision_count(): var slaminto = get_slide_collision(i) if slaminto.get_collider() is RigidBody3D: slaminto.get_collider().apply_central_impulse(-slaminto.get_normal() * physics_push_force) elif slaminto.get_collider() is CharacterBody3D: slaminto.get_collider().velocity += (-slaminto.get_normal() * movement_speed) if (state == "lunge_rise" or state == "lunge_descend") and slaminto.get_collider().get("playerallied") == true and currentlunge_lethal == true and slaminto.get_collider().get("hp") > 0: slaminto.get_collider().deal_damage(lunge_damage) currentlunge_lethal = false if !current_path.is_empty(): if global_transform.origin.distance_to(player.global_transform.origin) <= min_player_distance: clearpath() $chase_timer.start(path_refreshinterval) if global_transform.origin.distance_to(current_path_point) <= path_point_margin: current_path_index += 1 if current_path_index >= current_path.size(): clearpath() $chase_timer.start(path_refreshinterval) if state == "chasing" and is_on_floor() and !current_path.is_empty() and hp > 0: if len(current_path) > 0: current_path_point = current_path[current_path_index] var direction = global_transform.origin.direction_to(current_path_point) var acceleration = 8.0 velocity.x = lerp(velocity.x, direction.x * movement_speed, acceleration * delta) velocity.z = lerp(velocity.z, direction.z * movement_speed, acceleration * delta) # global_transform = global_transform.interpolate_with(global_transform.looking_at(Vector3(current_path_point.x,global_position.y,current_path_point.z)), 0.4) global_transform = global_transform.looking_at(Vector3(current_path_point.x,global_position.y,current_path_point.z)) elif state == "lunge_start": currentlunge_startpos = global_transform.origin currentlunge_angle = global_transform.origin.direction_to(player.global_transform.origin) currentlunge_apex = player.global_transform.origin.y + lunge_travelheight global_transform = global_transform.looking_at(Vector3(player.global_transform.origin.x,global_position.y,player.global_transform.origin.z)) state = "lunge_rise" motion_mode = 1 elif state == "lunge_rise": var acceleration = 8.0 velocity.x = lerp(velocity.x, currentlunge_angle.x * lunge_velocity, acceleration * delta) velocity.z = lerp(velocity.z, currentlunge_angle.z * lunge_velocity, acceleration * delta) if currentlunge_startpos.distance_to(global_transform.origin) >= lunge_traveldistance: state = "lunge_startrecovery" elif global_transform.origin.y < currentlunge_apex: velocity.y = lerp(velocity.y, lunge_upwardvelocity, acceleration * delta) else: motion_mode = 0 state = "lunge_descend" if get_real_velocity().x == 0 and get_real_velocity().z == 0 and $ai_timer.is_stopped(): $ai_timer.start(1) elif state == "lunge_descend": var acceleration = 8.0 velocity.x = lerp(velocity.x, currentlunge_angle.x * lunge_velocity, acceleration * delta) velocity.z = lerp(velocity.z, currentlunge_angle.z * lunge_velocity, acceleration * delta) if currentlunge_startpos.distance_to(global_transform.origin) >= lunge_traveldistance: state = "lunge_startrecovery" if get_real_velocity().x == 0 and get_real_velocity().z == 0 and $ai_timer.is_stopped(): $ai_timer.start(1) elif state == "lunge_recovering": var acceleration = 8.0 velocity.x = lerp(velocity.x, 0.0, acceleration * delta) velocity.z = lerp(velocity.z, 0.0, acceleration * delta) elif current_path.is_empty() or waitingfor != 0: velocity.x = 0 velocity.z = 0 if hp > 0: var velocityclamp = 0.25 if abs(velocity.x) + abs(velocity.z) > (velocityclamp * 2): # $AnimationPlayer.play("Run") pass else: # $AnimationPlayer.play("Idle") pass else: velocity = Vector3(0,0,0) if not is_on_floor(): velocity.y -= gravity * delta func ai_process(): if state == "chasing" and global_transform.origin.distance_to(player.global_transform.origin) <= lunge_startdistance: state = "lunge_charge" $ai_timer.start(lunge_pausetime) elif state == "lunge_startrecovery": motion_mode = 0 state = "lunge_recovering" $ai_timer.start(lunge_recovery) elif state == "goidle": $ai_timer.stop() clearpath() state = "idle" pass func deal_damage(damage): print(str(damage)+" damage taken, "+str(hp)+" HP left") hp = hp - damage if hp <= 0: print("death") delete_me() func set_movement_target(target_position: Vector3): var start_position: Vector3 = global_transform.origin current_path = NavigationServer3D.map_get_path( default_3d_map_rid, start_position, target_position, true ) if not current_path.is_empty(): current_path_index = 0 current_path_point = current_path[0] func clearpath(): current_path = [] current_path_index = 0 current_path_point = global_transform.origin '''func _on_animation_player_animation_finished(anim_name): if anim_name == "Idle": # $AnimationPlayer.play("Idle") pass if anim_name == "Run": # $AnimationPlayer.play("Run") pass if anim_name == "Death": delete_me() """set_collision_layer_value(1, false) $Head.set_collision_layer_value(1, false)"""''' func delete_me(): player.enemies.erase(self) get_parent().remove_child(self) queue_free() func sprite_angle(): var playerpos = Vector2(player.global_position.x, player.global_position.z) var spritepos = Vector2(global_position.x, global_position.z) var directiontoplayer = rad_to_deg((playerpos - spritepos).angle()) var selfangle = rad_to_deg(global_rotation.y) var spriteangle = fposmod((directiontoplayer + selfangle) + 90, 359) '''print("playerdirection "+str(directiontoplayer)+", angle "+str(selfangle)+", sprite "+str(spriteangle))''' if spriteangle < 45 and $Sprite.texture != sprite_front1: $Sprite.texture = sprite_front1 elif spriteangle >= 45 and spriteangle < 135 and $Sprite.texture != sprite_right1: $Sprite.texture = sprite_right1 elif spriteangle >= 135 and spriteangle < 225 and $Sprite.texture != sprite_back1: $Sprite.texture = sprite_back1 elif spriteangle >= 225 and spriteangle < 315 and $Sprite.texture != sprite_left1: $Sprite.texture = sprite_left1 elif spriteangle >= 315 and spriteangle < 360 and $Sprite.texture != sprite_front1: $Sprite.texture = sprite_front1 func _on_chase_timer_timeout(): if player.global_transform.origin.distance_to(runtarget) > 4: runtarget = player.global_transform.origin if global_transform.origin.distance_to(runtarget) > 4: set_movement_target(runtarget) $chase_timer.start(path_refreshinterval) func _on_ai_timer_timeout(): $ai_timer.stop() if state == "lunge_charge": state = "lunge_start" currentlunge_lethal = true elif state == "lunge_recovering": state = "chasing" # halt lunge when stuck in same position for too long elif state == "lunge_rise" and get_real_velocity().x == 0 and get_real_velocity().z == 0: state = "lunge_startrecovery" elif state == "lunge_descend" and get_real_velocity().x == 0 and get_real_velocity().z == 0: state = "lunge_startrecovery" # go idle when chasing player that has been out of sight for too long elif state == "chasing": state = "goidle" print("player gone, giving up...") func _on_perception_timer_timeout(): $perception_timer.stop() $sightchecker.target_position = to_local(player.global_transform.origin) $sightchecker.force_raycast_update() if global_transform.origin.distance_to(player.global_transform.origin) <= perception_radius and $sightchecker.get_collider() == player: if state == "idle": state = "chasing" print("player sighted!") elif state == "chasing" and !$ai_timer.is_stopped(): $ai_timer.stop() elif state == "chasing" and $ai_timer.is_stopped(): $ai_timer.start(perception_giveuptime) $perception_timer.start(perception_refreshinterval) func _on_hitflash_timer_timeout(): pass # Replace with function body.