actual initial commit lmao
This commit is contained in:
384
entities/npc.gd
Normal file
384
entities/npc.gd
Normal file
@@ -0,0 +1,384 @@
|
||||
extends CharacterBody3D
|
||||
|
||||
var npc : bool = true
|
||||
|
||||
@export var waypoint_route : Node3D = null
|
||||
var waypoints : Array
|
||||
|
||||
var walk_speed : float = 2.0
|
||||
var run_speed : float = 5.0
|
||||
var time_suspicionbuild : float = 3.0
|
||||
var time_suspiciondecay : float = 2.0
|
||||
var time_cooldown : float = 2.0
|
||||
|
||||
# var ragdolling : bool = false
|
||||
|
||||
var seeing_player : bool = false
|
||||
var move_this_frame : bool = false
|
||||
var unpause_this_frame : bool = false
|
||||
var on_route : bool = false
|
||||
|
||||
@export var behavior_type : String = "guard"
|
||||
var state : String = "default"
|
||||
var current_waypoint : int = -1
|
||||
var paused : bool = false
|
||||
var pauseanim : String
|
||||
var pauseturn : bool
|
||||
var pauseangle : float
|
||||
var lastseenpos : Vector3
|
||||
var pathupdateinterval : float = 0.25
|
||||
|
||||
var player : Node
|
||||
|
||||
@export var appearance : String = "guard"
|
||||
|
||||
var current_path : PackedVector3Array
|
||||
@onready var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
|
||||
var current_path_index : int = 0
|
||||
var current_path_point : Vector3
|
||||
var path_end_point : Vector3
|
||||
@onready var overheadstatus : Control = $StatusSprite/SubViewport/OverheadStatus
|
||||
|
||||
func _ready() -> void:
|
||||
overheadstatus.set_nothing()
|
||||
overheadstatus.set_timer(-1)
|
||||
|
||||
call_deferred("init_route")
|
||||
|
||||
$Timers/WaypointPause.timeout.connect(timeout_waypointpause)
|
||||
$Timers/Suspicion.timeout.connect(timeout_suspicion)
|
||||
$Timers/Sight.timeout.connect(timeout_sight)
|
||||
|
||||
if appearance == "guard":
|
||||
$CharacterArmature/Skeleton3D/head_guard.visible = true
|
||||
$CharacterArmature/Skeleton3D/body_guard.visible = true
|
||||
$CharacterArmature/Skeleton3D/feet_guard.visible = true
|
||||
$CharacterArmature/Skeleton3D/legs_guard.visible = true
|
||||
elif appearance == "barista":
|
||||
$CharacterArmature/Skeleton3D/head_barista.visible = true
|
||||
$CharacterArmature/Skeleton3D/body_barista.visible = true
|
||||
$CharacterArmature/Skeleton3D/feet_barista.visible = true
|
||||
$CharacterArmature/Skeleton3D/legs_barista.visible = true
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
seeing_player = checksight()
|
||||
ai_process()
|
||||
if move_this_frame:
|
||||
movement_process(delta)
|
||||
move_and_slide()
|
||||
animate()
|
||||
|
||||
func impact():
|
||||
# ragdolling = true
|
||||
state = "unconscious"
|
||||
set_collision_layer_value(1,0)
|
||||
# $CharacterArmature/Skeleton3D/ragdoll.active = true
|
||||
# $CharacterArmature/Skeleton3D/ragdoll.physical_bones_start_simulation()
|
||||
$AnimationPlayer.play("Death")
|
||||
stand_still()
|
||||
clearpath()
|
||||
|
||||
func ai_process():
|
||||
if unpause_this_frame:
|
||||
paused = false
|
||||
unpause_this_frame = false
|
||||
|
||||
if state == "to_default":
|
||||
state = "default"
|
||||
elif state == "to_cooldown":
|
||||
clearpath()
|
||||
state = "cooldown"
|
||||
$Timers/Suspicion.stop()
|
||||
$Timers/Suspicion.start(time_cooldown)
|
||||
elif state == "unconscious":
|
||||
on_route = false
|
||||
$Timers/Suspicion.stop()
|
||||
$Timers/Sight.stop()
|
||||
overheadstatus.set_nothing()
|
||||
overheadstatus.set_timer(-1)
|
||||
|
||||
if behavior_type == "guard":
|
||||
if state == "suspicion_escalatecheck":
|
||||
on_route = false
|
||||
$Timers/Suspicion.stop()
|
||||
$Timers/Sight.stop()
|
||||
|
||||
if seeing_player:
|
||||
setpath(lastseenpos)
|
||||
overheadstatus.set_exclamationpoint()
|
||||
overheadstatus.set_timer(-1)
|
||||
$Timers/Sight.start(pathupdateinterval)
|
||||
state = "pursuit"
|
||||
else:
|
||||
state = "default"
|
||||
elif state == "pursuit_updatepath":
|
||||
$Timers/Sight.stop()
|
||||
$Timers/Sight.start(pathupdateinterval)
|
||||
setpath(lastseenpos)
|
||||
state = "pursuit"
|
||||
elif state == "cooldown_end":
|
||||
$Timers/Suspicion.stop()
|
||||
$Timers/Sight.stop()
|
||||
on_route = true
|
||||
if len(waypoints) > 1:
|
||||
current_waypoint = nearest_waypoint()
|
||||
state = "default"
|
||||
|
||||
if state == "default":
|
||||
overheadstatus.set_nothing()
|
||||
overheadstatus.set_timer(-1)
|
||||
|
||||
if paused:
|
||||
$AnimationPlayer.play(pauseanim)
|
||||
if pauseturn == true:
|
||||
lerp_toward(pauseangle)
|
||||
|
||||
if seeing_player:
|
||||
$Timers/Suspicion.stop()
|
||||
$Timers/Suspicion.start(time_suspicionbuild)
|
||||
state = "suspicion"
|
||||
if paused:
|
||||
unpause_this_frame = true
|
||||
$Timers/WaypointPause.stop()
|
||||
else:
|
||||
on_route = true
|
||||
move_this_frame = true
|
||||
elif state == "suspicion":
|
||||
clearpath()
|
||||
on_route = false
|
||||
overheadstatus.set_questionmark()
|
||||
var timerpercent : float = ((time_suspicionbuild - $Timers/Suspicion.time_left) / time_suspicionbuild) * 100
|
||||
overheadstatus.set_timer(timerpercent)
|
||||
lerp_toward(angle_toward(lastseenpos))
|
||||
|
||||
if seeing_player:
|
||||
if $Timers/Suspicion.paused:
|
||||
$Timers/Suspicion.paused = false
|
||||
if !$Timers/Sight.is_stopped():
|
||||
$Timers/Sight.stop()
|
||||
$Timers/Sight.start(time_suspiciondecay)
|
||||
elif !seeing_player:
|
||||
if !$Timers/Suspicion.paused:
|
||||
$Timers/Suspicion.paused = true
|
||||
if $Timers/Sight.is_stopped():
|
||||
$Timers/Sight.start(time_suspiciondecay)
|
||||
elif state == "pursuit":
|
||||
if global_transform.origin.distance_to(player.global_transform.origin) <= 0.8:
|
||||
$Timers/Sight.stop()
|
||||
state = "to_cooldown"
|
||||
elif global_transform.origin.distance_to(path_end_point) <= 0.5:
|
||||
$Timers/Sight.stop()
|
||||
pauseangle = $CharacterArmature.global_rotation.y - deg_to_rad(90)
|
||||
clearpath()
|
||||
state = "pursuit_lookleft"
|
||||
else:
|
||||
on_route = false
|
||||
if !current_path.is_empty():
|
||||
move_this_frame = true
|
||||
elif state == "pursuit_lookleft":
|
||||
on_route = false
|
||||
|
||||
if seeing_player:
|
||||
state = "suspicion_escalatecheck"
|
||||
else:
|
||||
lerp_toward(pauseangle, 0.05)
|
||||
var snappedangle = snapped(fposmod($CharacterArmature.global_rotation.y, TAU), 0.01)
|
||||
if snappedangle == snapped(fposmod(pauseangle, TAU), 0.01):
|
||||
pauseangle = $CharacterArmature.global_rotation.y + deg_to_rad(180)
|
||||
state = "pursuit_lookright"
|
||||
elif state == "pursuit_lookright":
|
||||
on_route = false
|
||||
|
||||
if seeing_player:
|
||||
state = "suspicion_escalatecheck"
|
||||
else:
|
||||
lerp_toward(pauseangle, 0.05)
|
||||
var snappedangle = snapped(fposmod($CharacterArmature.global_rotation.y, TAU), 0.01)
|
||||
if snappedangle == snapped(fposmod(pauseangle, TAU), 0.01):
|
||||
pauseangle = $CharacterArmature.global_rotation.y + deg_to_rad(90)
|
||||
state = "pursuit_lookback"
|
||||
elif state == "pursuit_lookback":
|
||||
on_route = false
|
||||
|
||||
if seeing_player:
|
||||
state = "suspicion_escalatecheck"
|
||||
else:
|
||||
lerp_toward(pauseangle, 0.05)
|
||||
var snappedangle = snapped(fposmod($CharacterArmature.global_rotation.y, TAU), 0.01)
|
||||
if snappedangle == snapped(fposmod(pauseangle, TAU), 0.01):
|
||||
state = "to_cooldown"
|
||||
elif state == "cooldown":
|
||||
overheadstatus.set_3dots()
|
||||
overheadstatus.subtractive = true
|
||||
var percent : float = ($Timers/Suspicion.time_left / time_suspiciondecay) * 100
|
||||
overheadstatus.set_timer(percent)
|
||||
|
||||
func movement_process(delta):
|
||||
if not is_on_floor():
|
||||
velocity += get_gravity() * delta
|
||||
|
||||
if on_route and len(waypoints) == 1 and (global_transform.origin.distance_to(waypoints[0].global_transform.origin) < 0.6):
|
||||
if $CharacterArmature.global_rotation.y != waypoints[0].global_rotation.y:
|
||||
lerp_toward(waypoints[0].global_rotation.y)
|
||||
stand_still()
|
||||
elif !current_path.is_empty():
|
||||
current_path_point = current_path[current_path_index]
|
||||
if !paused:
|
||||
walk_toward(current_path_point)
|
||||
|
||||
if global_transform.origin.distance_to(current_path_point) <= 0.5:
|
||||
current_path_index += 1
|
||||
if current_path_index >= current_path.size():
|
||||
if on_route:
|
||||
var wayedpoint = waypoints[current_waypoint]
|
||||
if wayedpoint.pausetime > 0:
|
||||
paused = true
|
||||
$Timers/WaypointPause.start(wayedpoint.pausetime)
|
||||
pauseanim = wayedpoint.animation
|
||||
if wayedpoint.turntoward == true:
|
||||
pauseturn = true
|
||||
pauseangle = wayedpoint.global_rotation.y
|
||||
else:
|
||||
pauseturn = false
|
||||
current_waypoint += 1
|
||||
if current_waypoint > len(waypoints)-1:
|
||||
current_waypoint = 0
|
||||
nextwaypoint()
|
||||
else:
|
||||
clearpath()
|
||||
elif NavigationServer3D.map_get_iteration_id(default_3d_map_rid) != 0 and current_path.is_empty() and on_route:
|
||||
nextwaypoint()
|
||||
else:
|
||||
stand_still()
|
||||
|
||||
move_this_frame = false
|
||||
|
||||
func animate():
|
||||
if state == "unconscious":
|
||||
pass
|
||||
elif velocity.length() > 0:
|
||||
$AnimationPlayer.play("Walk")
|
||||
elif !paused or state != "default":
|
||||
$AnimationPlayer.play("Idle_Neutral")
|
||||
|
||||
func angle_toward(target : Vector3):
|
||||
return (Vector2(target.z, target.x) - Vector2($CharacterArmature.global_transform.origin.z, $CharacterArmature.global_transform.origin.x)).angle()
|
||||
|
||||
func walk_toward(point: Vector3):
|
||||
var direction = global_transform.origin.direction_to(point)
|
||||
velocity.x = direction.x * walk_speed
|
||||
velocity.z = direction.z * walk_speed
|
||||
|
||||
lerp_toward(atan2(velocity.x, velocity.z))
|
||||
|
||||
func stand_still():
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
|
||||
func nextwaypoint():
|
||||
var waypoint = waypoints[current_waypoint]
|
||||
var target_position: Vector3 = waypoint.global_transform.origin
|
||||
setpath(target_position)
|
||||
|
||||
func setpath(target_position):
|
||||
clearpath()
|
||||
var start_position: Vector3 = global_transform.origin
|
||||
current_path = NavigationServer3D.map_get_path(
|
||||
default_3d_map_rid,
|
||||
start_position,
|
||||
target_position,
|
||||
true
|
||||
)
|
||||
path_end_point = current_path[len(current_path)-1]
|
||||
|
||||
func clearpath():
|
||||
current_path = []
|
||||
current_path_index = 0
|
||||
current_path_point = global_transform.origin
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
|
||||
func checksight():
|
||||
var seenthings = $CharacterArmature/SightCollider.get_overlapping_bodies()
|
||||
|
||||
if !seenthings.is_empty():
|
||||
for item in seenthings:
|
||||
if item.get_name() == "PlayerVisibilityShape":
|
||||
player = item.get_parent().get_parent()
|
||||
if sightraycheck(item) == true:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func sightraycheck(target):
|
||||
# var radius = target.shape_owner_get_shape(0,0).radius
|
||||
var height : float
|
||||
var tramsorm : Transform3D
|
||||
|
||||
if target.get_parent().get_parent().get_name() == "Player" and player.crouched:
|
||||
height = target.shape_owner_get_shape(1,0).height
|
||||
tramsorm = target.shape_owner_get_transform(1).rotated(Vector3(0,1,0),target.global_rotation.y)
|
||||
else:
|
||||
height = target.shape_owner_get_shape(0,0).height
|
||||
tramsorm = target.shape_owner_get_transform(0).rotated(Vector3(0,1,0),target.global_rotation.y)
|
||||
|
||||
var top = to_local(target.global_transform.origin - $CharacterArmature/SightCollider/SightRay.transform.origin + tramsorm.origin + Vector3(0, (height/2)-0.1, 0))
|
||||
$CharacterArmature/SightCollider/SightRay.target_position = top
|
||||
$CharacterArmature/SightCollider/SightRay.global_rotation = global_rotation
|
||||
$CharacterArmature/SightCollider/SightRay.force_raycast_update()
|
||||
if $CharacterArmature/SightCollider/SightRay.is_colliding():
|
||||
if $CharacterArmature/SightCollider/SightRay.get_collider().get_name() == "PlayerVisibilityShape":
|
||||
lastseenpos = target.global_transform.origin
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func lerp_toward(angle: float, speed = 0.15):
|
||||
$CharacterArmature.global_rotation.y = lerp_angle($CharacterArmature.global_rotation.y, angle, speed)
|
||||
|
||||
func get_waypoints(path: Node3D):
|
||||
for point in path.get_children():
|
||||
waypoints.append(point)
|
||||
|
||||
func nearest_waypoint():
|
||||
var nearest : Vector3 = waypoints[0].global_transform.origin
|
||||
var nearest_index : int = 0
|
||||
for point in len(waypoints):
|
||||
if global_transform.origin.distance_to(waypoints[point].global_transform.origin) < global_transform.origin.distance_to(nearest):
|
||||
nearest = waypoints[point].global_transform.origin
|
||||
nearest_index = point
|
||||
return nearest_index
|
||||
|
||||
func init_route() -> void:
|
||||
if waypoint_route == null:
|
||||
var startpos = Marker3D.new()
|
||||
get_tree().current_scene.add_child(startpos)
|
||||
startpos.set_global_transform(global_transform)
|
||||
startpos.set_script(preload("res://scripts/waypoint.gd"))
|
||||
waypoints.append(startpos)
|
||||
else:
|
||||
get_waypoints(waypoint_route)
|
||||
|
||||
func timeout_waypointpause() -> void:
|
||||
if paused:
|
||||
unpause_this_frame = true
|
||||
|
||||
func timeout_suspicion() -> void:
|
||||
if behavior_type == "guard":
|
||||
if state == "suspicion":
|
||||
state = "suspicion_escalatecheck"
|
||||
if state == "pursuit_lookaround":
|
||||
state = "to_cooldown"
|
||||
elif state == "cooldown":
|
||||
state = "cooldown_end"
|
||||
|
||||
func timeout_sight() -> void:
|
||||
if behavior_type == "guard":
|
||||
if state == "suspicion":
|
||||
$Timers/Sight.stop()
|
||||
state = "to_default"
|
||||
elif state == "pursuit":
|
||||
$Timers/Sight.stop()
|
||||
$Timers/Sight.start(pathupdateinterval)
|
||||
if path_end_point.distance_to(lastseenpos) >= 2:
|
||||
state = "pursuit_updatepath"
|
||||
Reference in New Issue
Block a user