extends NPC export(NodePath) var _visibility_path: NodePath export(int) var _player_follow_pill_level = 3 const MAX_CMDS = 5 # maximum repeaded commands before Meldewesen gets piss const CMD_DIFF_MS = 4000 # time in ms before new command enum BEHAVIOR { NORMAL = 0, GO_WORK = 1, GO_HOME = 2, TAKE_PILLS = 3, DO_JOB = 4, ANGRY = 5 } var _curMood # current set behavior var _visibility: Area var _interactArea: Area var _audioPlayer: AudioStreamPlayer3D var _playerRef var _followingPlayer = false # true if Meldewesen finds player "suspicios" and follows var _huntingPlayer = false # active if the Meldewesen wants to "catch" the player var _seeingPlayer = false # as long as player is in visible range var _lastSound = 0 # timestamp of last played message var _countCmds = 0 # count of spoken commands -> after MAX_CMDS Meldewesen gets pissy func _ready(): #Logger.set_logger_level(Logger.LOG_LEVEL_FINE) _audioPlayer = get_node("AudioStreamPlayer3D") as AudioStreamPlayer3D assert(null != _audioPlayer) _visibility = get_node(_visibility_path) as Area assert(null != _visibility) _visibility.connect("body_entered", self, "_on_body_entered_visibility") _visibility.connect("body_exited", self, "_on_body_exited_visibility") # setup collisions with player var _interactArea = get_node("InteractArea") as Area assert(null != _interactArea) _interactArea.connect("area_entered", self, "_on_area_entered") # TODO: does this implementation have to be that way? for player in get_tree().get_nodes_in_group("Player"): if player.is_class("KinematicBody"): _playerRef = player break assert(null != _playerRef) func _process(_delta): # movement if current_target: # continue following player after illegal action or low pill level if _huntingPlayer or (_followingPlayer and Pills.get_round_level() <= _player_follow_pill_level): current_target = _playerRef.transform.origin # stop following player if pill level is high enough and player is not in an illegal area else: Logger.info("player pill level ok and no illegal actions detected!") _followingPlayer = false current_target = null if _seeingPlayer: # logic _set_behavior() # audio if _audioPlayer.stream != null: var cur_time = OS.get_ticks_msec() if cur_time - _lastSound > CMD_DIFF_MS: _audioPlayer.play() _lastSound = cur_time _countCmds += 1 func _on_area_entered (area: Area): if area.is_in_group("Player") and _huntingPlayer: Logger.info("caught player!") Daytime.emit_signal("respawn") func _load_sound (): match _curMood: BEHAVIOR.NORMAL: _audioPlayer.stream = null BEHAVIOR.GO_WORK: Logger.info("say go to work") _audioPlayer.stream = load("res://Resources/Audio/to-work.wav") BEHAVIOR.GO_HOME: Logger.info("say go home") _audioPlayer.stream = load("res://Resources/Audio/go-home.wav") BEHAVIOR.TAKE_PILLS: Logger.info("say take your pills") _audioPlayer.stream = load("res://Resources/Audio/take-pills.wav") BEHAVIOR.DO_JOB: Logger.info("say do your job") _audioPlayer.stream = load("res://Resources/Audio/do-job.wav") BEHAVIOR.ANGRY: Logger.info("say stop!") _audioPlayer.stream = load("res://Resources/Audio/Stop you violated the law.wav") func _set_behavior (): if _huntingPlayer == false: var reason = null var daytime = Daytime.get_time() if _playerRef.IsHunted: # player is already marked by another Meldewesen reason = "player is haunted" elif _playerRef.is_in_illegalzone(): # player is in illegal area reason = "player is haunted in illegal area" elif Pills.get_round_level() == 0: # player has taken no pills in a while reason = "player's pill level is zero" elif _playerRef.IsOutside and daytime > Daytime.WORK_TIME and daytime < Daytime.SLEEP_TIME: # outside during WORK_TIME reason = "player outside during worktime" #elif _playerRef.IsInFactory and daytime < Daytime.WORK_TIME and daytime > Daytime.SLEEP_TIME: # at work after out of WORK_TIME # EDIT: too early at work is not an angry reason :p elif _playerRef.IsInFactory and daytime > Daytime.SLEEP_TIME: # at work after out of WORK_TIME reason = "player in factory out of worktime" elif _countCmds > MAX_CMDS: # after MAX_CMDS repeats of the same command reason = "player is seemingly not coorperativ" # mark player for all other Meldewesen if seen during illegal action if reason != null: Logger.info("catch player! reason: " + reason) _huntingPlayer = true _playerRef.IsHunted = true if _followingPlayer == false: # If the player didn't take enough pills lately, they're suspicious -> following if Pills.get_round_level() <= _player_follow_pill_level: Logger.info("The player's pill level is too low - following!") _followingPlayer = true if _huntingPlayer or _followingPlayer: current_target = _playerRef.transform.origin var mood = BEHAVIOR.NORMAL var daytime = Daytime.get_time() if _playerRef.IsHunted: mood = BEHAVIOR.ANGRY # do your job elif _playerRef.is_in_workzone(): mood = BEHAVIOR.DO_JOB # take your pills elif Pills.get_level() < Pills.get_max()*0.5: mood = BEHAVIOR.TAKE_PILLS # go to work elif daytime < Daytime.WORK_TIME: mood = BEHAVIOR.GO_WORK # go home elif daytime > Daytime.SLEEP_TIME: mood = BEHAVIOR.GO_HOME if mood != _curMood: Logger.info("new mood: " + BEHAVIOR.keys()[mood] + " with cntcmd: " + String(_countCmds)) _countCmds = 0 _curMood = mood _load_sound() func _on_body_entered_visibility (body: PhysicsBody): #Logger.trace("Meldewesen seeing %s" % [body]) if body.is_in_group("Player"): Logger.info("Seeing player!") _seeingPlayer = true _set_behavior() func _on_body_exited_visibility(body: PhysicsBody): if body.is_in_group("Player"): Logger.info("Stopped seeing player!") _seeingPlayer = false _countCmds = 0 if _huntingPlayer == false and _followingPlayer == false: current_target = null