retrace/Characters/Meldewesen/Meldewesen.gd
2020-01-26 16:09:13 +01:00

181 lines
5.8 KiB
GDScript

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