SpielBackupAlt/scripts/NPC.gd

279 lines
6.7 KiB
GDScript3
Raw Permalink Normal View History

2024-12-26 14:54:06 +00:00
extends Spatial
class_name NPC
export(Array, NodePath) var tasks : Array
export var modelSize = 1.0
export var spriteSize = 1.0
export var spriteOffset = 0.0
export var spriteFPS = 15
export var timeline = "Test"
var speed = 1.0
var taskNodes = []
var navAgent : NavigationAgent
var npcModel = null # animated npc model
var camera = null # player camera for sprite calculation
var action = "Idle" # current animation
var skipSprites = false # skips sprite calculation
var spriteFrame = 0
var shouldNavigate = false # Does the current task require navigation?
var isAggressive = false # should hunt player if he is spotted
var investigateSounds = false # should investigate sounds
# senses
var sightNear : bool = false
var sightFar : bool = false
var hearNear : bool = false
var hearFar : bool = false
var has_heard = null
var velocity = Vector3.ZERO
var targetPos
var direction
var navigate = false
var currentTask = null # The current task the NPC wants to do ( usually timed tasks )
var playerTask = null # when the. spotted player
var investigate : Spatial # A point of interest to investigate ( usually when the player made a sound )
var task = null # the actual task
func _ready():
$NPCSprite.texture = $NPCRenderer.get_texture()
$NPCRenderer.set_update_mode(Viewport.UPDATE_ONCE)
investigate = Spatial.new()
get_tree().current_scene.call_deferred("add_child", investigate)
navAgent = $NavigationAgent
navAgent.connect("velocity_computed", self, "_on_velocity_computed")
for i in tasks:
taskNodes.push_back(get_node(i))
func SetModel(model):
npcModel = model
camera = get_viewport().get_camera()
model.get_parent().remove_child(model)
$NPCRenderer.add_child(model)
model.scale_object_local(Vector3(modelSize, modelSize, modelSize))
$NPCSprite.scale_object_local(Vector3(spriteSize, spriteSize, spriteSize))
$NPCSprite.translate_object_local(Vector3(0, spriteOffset, 0))
$NPC_Name.name = name
func changeState(state):
action = state
if npcModel != null:
npcModel.setAnimation(state)
func _process(_delta):
if camera == null:
return
calcSprites()
moveNPC()
func _on_VisibilityEnabler_screen_entered():
skipSprites = false
func _on_VisibilityEnabler_screen_exited():
skipSprites = true
func calcSprites():
if skipSprites:
pass
var camPos = camera.global_transform.origin
camPos.y = 0
$Look.global_transform = global_transform
$Look.global_transform.origin.y = 0
$Look.look_at(camPos, Vector3.UP)
$Look.rotation_degrees.y = stepify($Look.rotation_degrees.y, 45.0)
$NPCRenderer/CameraRig.global_rotation = $Look.global_rotation
if npcModel != null:
npcModel.rotation = rotation
spriteFrame += 1
if spriteFrame % spriteFPS == 0:
spriteFrame = 0
$NPCRenderer.set_update_mode(Viewport.UPDATE_ONCE)
func Tick(hour, minute, _day):
for t in taskNodes:
if t.hour == hour and t.minute == minute:
currentTask = t
shouldNavigate = t.shouldNavigate
if t.changeAggression:
isAggressive = t.aggression
if t.changeInvestigation:
investigateSounds = t.shouldInvestigate
func _on_velocity_computed(new_velocity):
if task == null or targetPos == null:
return
velocity = new_velocity
global_translate(new_velocity)
global_transform.origin.y = targetPos.y #$RayCast.get_collision_point().y
look_at(targetPos, Vector3.UP)
##########
## SENSES
##########
func _on_HearingNear_body_entered(body):
if body.is_in_group("Player"):
hearNear = true
func _on_HearingNear_body_exited(body):
if body.is_in_group("Player"):
hearNear = false
func _on_HearingFar_body_entered(body):
if body.is_in_group("Player"):
hearFar = true
func _on_HearingFar_body_exited(body):
if body.is_in_group("Player"):
hearFar = false
func _on_SightNear_body_entered(body):
if body.is_in_group("Player"):
sightNear = true
func _on_SightNear_body_exited(body):
if body.is_in_group("Player"):
sightNear = false
func _on_SightFar_body_entered(body):
if body.is_in_group("Player"):
sightFar = true
func _on_SightFar_body_exited(body):
if body.is_in_group("Player"):
sightFar = false
func canSeePlayer(lightLevel):
if not sightFar and not sightNear:
return false
var space_state = get_world().direct_space_state
var result = space_state.intersect_ray($Head.global_transform.origin, camera.global_transform.origin)
if result.size() > 0:
var col = result["collider"]
if col.is_in_group("Player"):
if col.lightLevel > lightLevel:
## Detected player
playerTask = col
return true
if playerTask != null:
investigate.global_transform = playerTask.global_transform
playerTask = null
return false
###########
## REACTION
###########
func interact(_relate):
### TALKING TO NPC
#if behavior == null:
# return
#behavior.interact(relate)
pass
func heard(something):
if investigateSounds:
has_heard = something
func _on_InteractionDetection_body_entered(body):
if body.name == "Door" and velocity.length() > 0 and body.state == 0:
body.interact(self)
func _on_Tick_timeout():
# Update AI
investigate.global_translation != Vector3.ZERO
if isAggressive:
canSeePlayer(0.5)
if has_heard != null:
investigate.global_transform = has_heard.global_transform
has_heard = null
if playerTask != null:
task = playerTask
elif investigate.global_translation != Vector3.ZERO:
task = investigate
elif currentTask != null:
task = currentTask
else:
task = null
navigate = false
if task == null:
return
if task.has_method("get_transform"):
navAgent.set_target_location(task.get_transform().origin)
else:
navAgent.set_target_location(task.global_transform.origin)
navigate = not reachedTask()
func reachedTask():
if not task.has_method("waiting"):
return _reachedTarget()
#if task.should_align():
# global_transform = task.get_transform()
if task.waiting():
return false
if task.get_interaction() == null:
return true
if not task.get_interaction().has_method("interact"):
return true
task.get_interaction().interact(self)
if task.done():
task = null
currentTask = null
return true
else:
navAgent.set_target_location(task.get_transform().origin)
return false
func _reachedTarget():
if navAgent.is_navigation_finished():
velocity = Vector3.ZERO
navAgent.set_velocity(velocity)
return true
return false
func moveNPC():
if not navigate:
return
if task == currentTask and not shouldNavigate:
return
targetPos = navAgent.get_next_location()
targetPos.y = 0
direction = global_transform.origin.direction_to(targetPos)
var v = direction * speed * get_process_delta_time()
navAgent.set_velocity(v)