ef2gamesource/dlls/game/actor.cpp

19686 lines
439 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /Code/DLLs/game/actor.cpp $
// $Revision:: 557 $
// $Author:: Steven $
// $Date:: 10/13/03 9:43a $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// Base class for character AI.
//
#include "_pch_cpp.h"
//#include "g_local.h"
#include "actor.h"
#include "behavior.h"
#include "behaviors_general.h"
#include "behaviors_specific.h"
#include "scriptmaster.h"
#include "doors.h"
#include "gibs.h"
#include "misc.h"
#include "specialfx.h"
#include "object.h"
#include "scriptslave.h"
#include "explosion.h"
#include "misc.h"
#include "PlayerStart.h"
#include "characterstate.h"
#include "weaputils.h"
#include "player.h"
#include "armor.h"
#include "groupcoordinator.hpp"
#include <qcommon/gameplaymanager.h>
#include "teammateroster.hpp"
#include "talk.hpp"
#include "equipment.h"
Container<Actor *> SleepList; //All actors in the level that are asleep
Container<Actor *> ActiveList; //All actors in the level that are active
Container<Sentient *> TeamMateList; //Global list of all teammates
Container<BehaviorPackageType_t *> PackageList; //Global list of all behavior packages ( in BehaviorPackages.txt )
extern Container<int> SpecialPathNodes;
Event EV_Actor_SetSelfDetonateModel
(
"selfdetonatemodel",
EV_TIKIONLY,
"s",
"modelname",
"Set the modelname of the explosion to be spawned when an actor self-detonates"
);
Event EV_Actor_BlindlyFollowPath
(
"blindlyfollowpath",
EV_SCRIPTONLY,
"sFS",
"anim_name offset pathnode",
"Actor walks to specified path node without avoidance or collision"
);
Event EV_Actor_SetSimplifiedThink
(
"setsimplifiedthink",
EV_DEFAULT,
"B",
"boolean",
"change actor to SimplifiedThink think strategy"
);
Event EV_Actor_SetActorToActorDamageModifier
(
"actortoactordamage",
EV_DEFAULT,
"f",
"modifier",
"Amount to modifiy damage by 1 is full damage, 0 would be no damage"
);
Event EV_Actor_OnUse
(
"onuse",
EV_SCRIPTONLY,
"s",
"thread_name",
"Sets the thread to call"
);
Event EV_Actor_NoUse
(
"nouse",
EV_SCRIPTONLY,
NULL,
NULL,
"Clears the on use thread"
);
Event EV_Actor_ClearCurrentEnemy
(
"clearCurrentEnemy",
EV_DEFAULT,
NULL,
NULL,
"Sets Current Enemy to Null"
);
Event EV_Actor_SetTargetType
(
"setTargetType",
EV_DEFAULT,
"i",
"set_target_type",
"Set Type of Target (0) Any, (1) Player Only, (2) Actors Only (3) Scripted Only (4) Level_Interaction Triggers Only "
);
Event EV_Actor_Sleep
(
"sleep",
EV_DEFAULT,
NULL,
NULL,
"Put the actor to sleep."
);
Event EV_Actor_Wakeup
(
"wakeup",
EV_SCRIPTONLY,
NULL,
NULL,
"Wake up the actor."
);
Event EV_Actor_Fov
(
"fov",
EV_CONSOLE,
"f",
"fov",
"Sets the actor's field of view (fov)."
);
Event EV_Actor_VisionDistance
(
"visiondistance",
EV_DEFAULT,
"f",
"vision_distance",
"Sets the distance the actor can see."
);
Event EV_Actor_Start
(
"start",
EV_DEFAULT,
NULL,
NULL,
"Initializes the actor a little, "
"it is not meant to be called from script."
);
Event EV_Actor_Dead
(
"dead",
EV_CODEONLY,
NULL,
NULL,
"Does everything necessary when an actor dies, "
"it is not meant to be called from script."
);
Event EV_Actor_SetEnemyType
(
"enemytype",
EV_DEFAULT,
"s",
"enemytype",
"Sets the name of this actor's enemy type."
);
Event EV_Actor_Swim
(
"swim",
EV_DEFAULT,
NULL,
NULL,
"Specifies actor as being able to swim."
);
Event EV_Actor_Fly
(
"fly",
EV_DEFAULT,
"B",
"fly_bool",
"Specifies actor as being able to fly (optional bool can turn fly on or off)."
);
Event EV_Actor_NotLand
(
"noland",
EV_DEFAULT,
NULL,
NULL,
"Specifies actor as not being able to walk on land."
);
Event EV_Actor_RunThread
(
"runthread",
EV_CODEONLY,
"s",
"label",
"Runs the specified thread."
);
Event EV_Actor_Statemap
(
"statemap",
EV_DEFAULT,
"sS",
"statemap_name state_name",
"Sets which statemap file to use and optionally what the first state to go to."
);
Event EV_Actor_MasterStateMap
(
"masterstatemap",
EV_DEFAULT,
"sS",
"statemap_name state_name",
"Sets which masterstatemap file to use and optionally what the first state to go to."
);
Event EV_Actor_FuzzyEngine
(
"fuzzyengine",
EV_DEFAULT,
"s",
"fuzzyengine_name",
"Sets which fuzzy engine file to use"
);
Event EV_Actor_SetBehaviorPackage
(
"setbehaviorpackage",
EV_DEFAULT,
"s",
"package_name",
"sets the actor to use the specified behavior package AND sets the masterstate"
);
Event EV_Actor_UseBehaviorPackage
(
"usebehaviorpackage",
EV_DEFAULT,
"s",
"package_name",
"sets the actor to use the specified behavior package but does NOT set the master state"
);
Event EV_Actor_ChildUseBehaviorPackage
(
"childusebehaviorpackage",
EV_DEFAULT,
"ss",
"childname package_name",
"sets the child to use the specified behavior package but does NOT set the master state"
);
Event EV_Actor_ChildSetAnim
(
"childsetanim",
EV_DEFAULT,
"ss",
"childname anim_name",
"sets the child to play the anim specified"
);
Event EV_Actor_ChildSuicide
(
"childsuicide",
EV_DEFAULT,
"s",
"childname",
"sets the child to kill itself"
);
Event EV_Actor_IfEnemyVisible
(
"ifenemyvisible",
EV_SCRIPTONLY,
"SSSSSS",
"token1 token2 token3 token4 token5 token6",
"Process the following command if enemy is visible"
);
Event EV_Actor_IfNear
(
"ifnear",
EV_SCRIPTONLY,
"sfSSSSSS",
"name distance token1 token2 token3 token4 token5 token6",
"Process the following command if enemy is within specified distance"
);
Event EV_Actor_ForwardSpeed
(
"forwardspeed",
EV_DEFAULT,
"f",
"forwardspeed",
"Sets the actor's forward speed."
);
Event EV_Actor_Idle
(
"idlestate",
EV_SCRIPTONLY,
"S",
"state_name",
"Tells the actor to go into idle mode."
);
Event EV_Actor_LookAt
(
"lookat",
EV_SCRIPTONLY,
"e",
"ent",
"Specifies an entity to look at."
);
Event EV_Actor_TurnTo
(
"turntoangle",
EV_SCRIPTONLY,
"f",
"direction",
"Specifies the direction to look in."
);
Event EV_Actor_HeadWatch
(
"headwatch",
EV_SCRIPTONLY,
"eF",
"entity_to_watch max_speed",
"Actor watches the specified entity by turning his head."
);
Event EV_Actor_HeadAndEyeWatch
(
"headandeyewatch",
EV_SCRIPTONLY,
"eF",
"entity_to_watch max_speed",
"Actor watches the specified entity by turning his eyes,then head."
);
Event EV_Actor_ResetHead
(
"resethead",
EV_DEFAULT,
"F",
"max_speed",
"Actor resets its head back to looking forwards."
);
Event EV_Actor_EyeWatch
(
"eyewatch",
EV_SCRIPTONLY,
"eF",
"entity_to_watch max_speed",
"Actor watches the specified entity by turning his eyes."
);
Event EV_Actor_ResetEye
(
"reseteyes",
EV_DEFAULT,
"F",
"max_speed",
"Actor resets its eyes back to looking forwards."
);
Event EV_Actor_ResetTorso
(
"resettorso",
EV_DEFAULT,
"f",
"max_speed",
"Actor resets its torso to looking forwards"
);
Event EV_Actor_BehaviorFinished
(
"behaviorfinished",
EV_CODEONLY,
"iS",
"behaviorReturnCode behaviorFailureReason",
"The last behavior finished with the specified "
"return code and optionally a failure reason."
"This is sent to controllers of the actor."
);
Event EV_Actor_ControlLost
(
"controlost",
EV_CODEONLY,
NULL,
NULL,
"Sent to a controller when it loses control."
);
Event EV_Actor_EndBehavior
(
"endbehavior",
EV_CODEONLY,
NULL,
NULL,
"Ends the current behavior, "
"it is not meant to be called from script."
);
Event EV_Actor_EndHeadBehavior
(
"endheadbehavior",
EV_CODEONLY,
NULL,
NULL,
"Ends the current head behavior "
"it is not meant to be called from script."
);
Event EV_Actor_EndEyeBehavior
(
"endeyebehavior",
EV_CODEONLY,
NULL,
NULL,
"Ends the current eye behavior "
"it is not meant to be called from script."
);
Event EV_Actor_EndTorsoBehavior
(
"endtorsobehavior",
EV_CODEONLY,
NULL,
NULL,
"Ends the current torso behavior "
"it is not meant to be called from script."
);
Event EV_Actor_NotifyBehavior
(
"notifybehavior",
EV_CODEONLY,
NULL,
NULL,
"Notifies the current behavior of an event,"
"it is not meant to be called from script."
);
Event EV_Actor_NotifyHeadBehavior
(
"notifyheadbehavior",
EV_CODEONLY,
NULL,
NULL,
"Notifies the current head behavior of an event"
"it is not meant to be called from script."
);
Event EV_Actor_NotifyEyeBehavior
(
"notifyeyebehavior",
EV_CODEONLY,
NULL,
NULL,
"Notifies the current eye behavior of an event"
"it is not meant to be called from script."
);
Event EV_Actor_NotifyTorsoBehavior
(
"notifytorsobehavior",
EV_CODEONLY,
NULL,
NULL,
"Notifies the current torso behavior of an event"
"it is not meant to be called from script."
);
Event EV_Actor_FallToDeath
(
"falltodeath",
EV_SCRIPTONLY,
"fffsssF",
"forwardmove sidemove speed startanim fallanim deathanim anim_delay",
"makes an actor fall to his death"
);
Event EV_Actor_WalkTo
(
"walkto",
EV_DEFAULT,
"sSFF",
"pathnode anim_name force maxfailures",
"Actor walks to specified path node"
);
Event EV_Actor_WalkWatch
(
"walkwatch",
EV_SCRIPTONLY,
"seS",
"pathnode entity anim_name",
"Actor walks to specified path node and watches the specified entity"
);
Event EV_Actor_WarpTo
(
"warpto",
EV_SCRIPTONLY,
"s",
"node_name",
"Warps the actor to the specified node"
);
Event EV_Actor_JumpTo
(
"jumpto",
EV_SCRIPTONLY,
"sFF",
"pathnode_or_entity launchAngle dummy_arg",
"Actor jumps to specified path node"
);
Event EV_Actor_PickupEnt
(
"pickupent",
EV_DEFAULT,
"es",
"entity_to_pickup pickup_anim_name",
"Makes actor pick up the specified entity"
);
Event EV_Actor_ThrowEnt
(
"throwent",
EV_DEFAULT,
"s",
"throw_anim_name",
"Makes actor throw the entity in hands"
);
Event EV_Actor_Anim
(
"anim",
EV_DEFAULT,
"s",
"anim_name",
"Starts the PlayAnim behavior."
);
Event EV_Actor_SetAnim
(
"setanim",
EV_DEFAULT,
"sF",
"anim_name animationRate",
"Sets the animation directly."
);
Event EV_Actor_Attack
(
"attack",
EV_SCRIPTONLY,
"eB",
"ent force",
"Makes the actor attack the specified entity."
);
Event EV_Actor_AttackPlayer
(
"attackplayer",
EV_SCRIPTONLY,
NULL,
NULL,
"Makes enemies of all the players."
);
Event EV_Actor_ReserveNode
(
"reservenode",
EV_CODEONLY,
"vf",
"pos time",
"Reserves a path node for the specified amount of time."
);
Event EV_Actor_ReleaseNode
(
"releasenode",
EV_CODEONLY,
"v",
"pos",
"Releases a path node from being reserved."
);
Event EV_Actor_IfCanHideAt
(
"ifcanhideat",
EV_SCRIPTONLY,
"vSSSSSS",
"pos token1 token2 token3 token4 token5 token6",
"Processes command if actor can hide at specified position."
);
Event EV_Actor_IfEnemyWithin
(
"ifenemywithin",
EV_SCRIPTONLY,
"fSSSSSS",
"distance token1 token2 token3 token4 token5 token6",
"Processes command if actor is within distance of its current enemy."
);
Event EV_Actor_Remove
(
"remove_useless",
EV_CODEONLY,
NULL,
NULL,
"Removes a useless dead body from the game."
);
Event EV_Actor_Melee
(
"melee",
EV_DEFAULT,
"FSSVFIF",
"damage tag_name means_of_death attack_vector knockback use_pitch_to_enemy attack_min_height",
"Makes the actor do a melee attack. "
"attack_vector = width length height"
);
Event EV_Actor_PainThreshold
(
"painthreshold",
EV_TIKIONLY,
"f",
"pain_threshold",
"Sets the actor's pain threshold."
);
Event EV_Actor_SetKillThread
(
"killthread",
EV_SCRIPTONLY,
"s",
"kill_thread",
"Sets the actor's kill thread."
);
Event EV_Actor_EyePositionOffset
(
"eyeoffset",
EV_TIKIONLY,
"v",
"eyeoffset",
"Sets the actor's eye position."
);
Event EV_Actor_DeathFade
(
"deathfade",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor fade when dead."
);
Event EV_Actor_DeathEffect
(
"deathEffect",
EV_DEFAULT,
"s",
"deathEffectName",
"Displays a display effect instead of fading, shrinking, etc."
);
Event EV_Actor_DeathShrink
(
"deathshrink",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor shrink when dead."
);
Event EV_Actor_DeathSink
(
"deathsink",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor sink into the ground when dead."
);
Event EV_Actor_StaySolid
(
"staysolid",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor stay solid after death."
);
Event EV_Actor_NoChatter
(
"nochatter",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor not chatter."
);
Event EV_Actor_TurnSpeed
(
"turnspeed",
EV_DEFAULT,
"f",
"turnspeed",
"Sets the actor's turnspeed."
);
Event EV_Actor_SetActorFlag
(
"setactorflag",
EV_DEFAULT,
"sB",
"flag_name flag_bool",
"Sets an Actor's flag"
);
Event EV_Actor_SetNotifyFlag
(
"setnotifyflag",
EV_DEFAULT,
"sB",
"flag_name flag_bool",
"Sets an Actor's Notify Flag"
);
Event EV_Actor_SetVar
(
"setvar",
EV_DEFAULT,
"ss",
"var_name var_value",
"Sets a variable"
);
Event EV_Actor_PersistData
(
"persistData",
EV_CODEONLY,
"ss",
"var_name var_value",
"Sets a persistant variable"
);
Event EV_Actor_SetVarTime
(
"setvartime",
EV_CODEONLY,
"s",
"var_name",
"Sets the variable name to the current level time"
);
Event EV_Anim_Done
(
"anim_done",
EV_CODEONLY,
NULL,
NULL,
"Called when the actor's animation is done, "
"it is not meant to be called from script."
);
Event EV_Torso_Anim_Done
(
"torso_anim_done",
EV_CODEONLY,
NULL,
NULL,
"Called when actor's torso anim is done, "
"If you call this from script, I will hunt you down, and end you"
);
Event EV_Posture_Anim_Done
(
"posture_anim_done",
EV_CODEONLY,
NULL,
NULL,
"Called when a posture animation is done"
);
Event EV_Actor_ProjAttack
(
"proj",
EV_DEFAULT,
"ssIBFFBF",
"tag_name projectile_name number_of_tags arc_bool speed offset lead spread",
"Fires a projectile from the actor towards the current enemy."
);
Event EV_Actor_BulletAttack
(
"bullet",
EV_DEFAULT,
"sbffsvF",
"tag_name use_current_pitch damage knockback means_of_death spread range",
"Fires a bullet from the actor from the specified tag towards the current enemy."
);
Event EV_Actor_RadiusAttack
(
"radiusattack",
EV_DEFAULT,
"ssfffb",
"tag_name means_of_death damage radius knockback constant_damage",
"Does a radius attack from the tag name"
);
Event EV_Actor_Active
(
"active",
EV_SCRIPTONLY,
"i",
"active_flag",
"Specifies whether the actor's is active or not."
);
Event EV_Actor_SpawnGib
(
"spawngib",
EV_DEFAULT,
"vffssSSSSSSSS",
"offset final_pitch width cap_name surface_name1 surface_name2 surface_name3 surface_name4 surface_name5 surface_name6 surface_name7 surface_name8 surface_name9" ,
"Spawns a body part."
);
Event EV_Actor_SpawnGibAtTag
(
"spawngibattag",
EV_DEFAULT,
"sffssSSSSSSSS",
"tag_name final_pitch width cap_name surface_name1 surface_name2 surface_name3 surface_name4 surface_name5 surface_name6 surface_name7 surface_name8 surface_name9" ,
"Spawns a body part."
);
Event EV_Actor_SpawnNamedGib
(
"spawnnamedgib",
EV_DEFAULT,
"ssff",
"gib_name tag_name final_pitch width",
"Spawns a body named gib."
);
Event EV_Actor_SpawnBlood
(
"spawnblood",
EV_DEFAULT,
"ssB",
"blood_name tag_name use_last_spawn_result" ,
"Spawns blood at the specified tag."
);
Event EV_Actor_AIOn
(
"ai_on",
EV_SCRIPTONLY,
NULL,
NULL,
"Turns the AI on for this actor."
);
Event EV_Actor_AIOff
(
"ai_off",
EV_SCRIPTONLY,
NULL,
NULL,
"Turns the AI off for this actor."
);
Event EV_Actor_RespondTo
(
"respondto",
EV_DEFAULT,
"sb",
"stimuli respond",
"sets AI response to stimuli"
);
Event EV_Actor_PermanentlyRespondTo
(
"permanentrespondto",
EV_TIKIONLY,
"sb",
"stimuli respond",
"sets AI response to stimuli"
);
Event EV_Actor_SetIdleThread
(
"setidlethread",
EV_SCRIPTONLY,
"s",
"thread",
"Sets the thread that will be run if this actor gets back to the idle state again."
);
Event EV_Actor_SetMaxInactiveTime
(
"max_inactive_time",
EV_DEFAULT,
"f",
"max_inactive_time",
"Sets the maximum amount of time an actor will stay idle before going to sleep.\n"
"Also sepecifies the maximum amount of time an actor will keep looking for an\n"
"enemy that the actor can no longer see."
);
Event EV_ActorRegisterParts
(
"register_parts",
EV_CODEONLY,
"ei",
"entity forward",
"Registers the passed in part as another part of this actor and specifies\n"
"whether or not to forward this message to the other parts."
);
Event EV_ActorRegisterSelf
(
"register_self",
EV_CODEONLY,
NULL,
NULL,
"Starts registration process for multi-entity actors"
);
Event EV_ActorName
(
"name",
EV_DEFAULT,
"s",
"name",
"Specifies the name of this actor type."
);
Event EV_ActorPartName
(
"part_name",
EV_TIKIONLY,
"s",
"part_name",
"Specifies the name of this part (implying that this is a multi-part creature."
);
Event EV_ActorSetupTriggerField
(
"trigger_field",
EV_TIKIONLY,
"vv",
"min max",
"Specifies to create a trigger field around the actor of the specified size."
);
Event EV_ActorTriggerTouched
(
"trigger_touched",
EV_CODEONLY,
"e",
"ent",
"Notifies the actor that its trigger field has been touched."
);
Event EV_ActorIncomingProjectile
(
"incoming_proj",
EV_CODEONLY,
"e",
"ent",
"Notifies the actor of an incoming projectile."
);
Event EV_ActorSpawnActor
(
"spawnactor",
EV_DEFAULT,
"ssibffFBF",
"model_name tag_name how_many attack width height spawn_offset force add_height",
"Spawns the specified number of actors."
);
Event EV_ActorSpawnActorAboveEnemy
(
"spawnactoraboveenemy",
EV_DEFAULT,
"sibfff",
"model_name how_many attack width height how_far",
"Spawns actors above current enemy"
);
Event EV_ActorSpawnActorAtLocation
(
"spawnactoratlocation",
EV_DEFAULT,
"ssibff",
"model_name pathnode_name how_many_path_nodes attack width height",
"Spawns the specified actor at the specified pathnode."
);
Event EV_Actor_AddDialog
(
"dialog",
EV_DEFAULT,
"sSSSSSS",
"alias token1 token2 token3 token4 token5 token6",
"Add a dialog to this sentient."
);
Event EV_Actor_DialogDone
(
"dialogdone",
EV_CODEONLY,
NULL,
NULL,
"Called when the sentient's dialog is done, "
"it is not meant to be called from script."
);
Event EV_Actor_PlayDialog
(
"playdialog",
EV_DEFAULT,
"SFFBBSE",
"sound_file volume min_dist headDisplay do_talk state_name user",
"Plays a dialog."
);
Event EV_Actor_StopDialog
(
"stopdialog",
EV_SCRIPTONLY,
NULL,
NULL,
"Stops the actor's dialog."
);
Event EV_Actor_BroadcastDialog
(
"broadcastdialog",
EV_CODEONLY,
"s",
"context",
"Broadcasts a context dialog"
);
Event EV_Actor_BranchDialog
(
"branchdialog",
EV_DEFAULT,
"s",
"dialogName",
"Presents a branch dialog to the player."
);
Event EV_Actor_AllowTalk
(
"allowtalk",
EV_DEFAULT,
"i",
"allow_bool",
"Sets whether or not the actor will bother to talk to the player."
);
Event EV_Actor_AllowHangBack
(
"allowhangback",
EV_DEFAULT,
"i",
"allow_bool",
"Sets whether or not the actor will bother to hang back."
);
Event EV_Actor_SolidMask
(
"solidmask",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor use a solid mask."
);
Event EV_Actor_NotSolidMask
(
"notsolidmask",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor use a nonsolid mask."
);
Event EV_Actor_NoMask
(
"nomask",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor use a mask of 0."
);
Event EV_Actor_SetMask
(
"setmask",
EV_DEFAULT,
"s",
"mask_name",
"Sets the actor's mask to the specified mask."
);
Event EV_Actor_Pickup
(
"actor_pickup",
EV_TIKIONLY,
"s",
"tag_name",
"Makes the actor pickup current pickup_ent (should only be called from a tiki)."
);
Event EV_Actor_Throw
(
"actor_throw",
EV_TIKIONLY,
"s",
"tag_name",
"Makes the actor throw whatever is in its hand (should only be called from a tiki)."
);
Event EV_Actor_DamageOnceStart
(
"damage_once_start",
EV_TIKIONLY,
NULL,
NULL,
"Makes the actor only do melee damage at most once during this attack."
);
Event EV_Actor_DamageOnceStop
(
"damage_once_stop",
EV_TIKIONLY,
NULL,
NULL,
"Specifies that the actor is done with the damage once event."
);
Event EV_Actor_DamageEnemy
(
"damageenemy",
EV_DEFAULT,
"fS",
"damage model",
"Damages the current enemy by the specified amount."
);
Event EV_Actor_DamageSelf
(
"damageself",
EV_DEFAULT,
"fs",
"damage means_of_death",
"Damages Self"
);
Event EV_Actor_TurnTowardsEnemy
(
"turntowardsenemy",
EV_SCRIPTONLY,
"f",
"angle",
"Turns the actor towards the current enemy."
);
Event EV_Actor_TurnTowardsPlayer
(
"turntowardsplayer",
EV_SCRIPTONLY,
NULL,
NULL,
"Turns the actor towards the player."
);
Event EV_Actor_TurnTowardsEntity
(
"turntowardsentity",
EV_SCRIPTONLY,
"e",
"entity",
"Turns the actor towards the entity"
);
Event EV_Actor_Suicide
(
"suicide",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor commit suicide."
);
Event EV_Actor_GotoNextStage
(
"gotonextstage",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor goto his next stage."
);
Event EV_Actor_GotoPrevStage
(
"gotoprevstage",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor goto his previous stage."
);
Event EV_Actor_GotoStage
(
"gotostage",
EV_DEFAULT,
"i",
"stage_number",
"Makes the actor goto the specified stage."
);
Event EV_Actor_GetStage
(
"getstage",
EV_SCRIPTONLY,
"@f",
"Result",
"Returns this actors current stage."
);
Event EV_Actor_NotifyOthersAtDeath
(
"notifyothersatdeath",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor notify other actors of the same type when killed."
);
Event EV_Actor_SetBounceOff
(
"bounceoff",
EV_DEFAULT,
NULL,
NULL,
"Makes projectiles bounce off of actor (if they can't damage actor)."
);
Event EV_Actor_SetHaveThing
(
"havething",
EV_DEFAULT,
"ib",
"thing_number have_bool",
"Sets whether or not the actor has this thing number."
);
Event EV_Actor_SetUseGravity
(
"usegravity",
EV_DEFAULT,
"b",
"use_gravity",
"Tells the actor whether or not to use gravity for this animation."
);
Event EV_Actor_SetDeathSize
(
"deathsize",
EV_TIKIONLY,
"vv",
"min max",
"Sets the actors new size for death."
);
Event EV_Actor_Fade
(
"actorfade",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor fade out."
);
Event EV_Actor_AttackMode
(
"attackmode",
EV_SCRIPTONLY,
"b",
"attack_bool",
"Makes the actor go directly into attacking the player if bool is true."
);
Event EV_Actor_BounceOff
(
"bounceoffevent",
EV_CODEONLY,
"v",
"object_origin",
"Lets the actor know something just bounces off of it."
);
Event EV_Actor_SetBounceOffEffect
(
"bounceoffeffect",
EV_DEFAULT,
"s",
"bounce_off_effect_name",
"Sets the name of the effect to play when something bounces off the actor."
);
Event EV_Actor_AddSpawnItem
(
"spawnitem",
EV_DEFAULT,
"s",
"spawn_item_name",
"Adds this names item to what will be spawned when this actor is killed."
);
Event EV_Actor_SetSpawnChance
(
"spawnchance",
EV_DEFAULT,
"f",
"spawn_chance",
"Sets the chance that this actor will spawn something when killed."
);
Event EV_Actor_ClearSpawnItems
(
"clearspawnitems",
EV_DEFAULT,
NULL,
NULL,
"Clears the list of items to spawn when this actor is killed."
);
Event EV_Actor_SetAllowFall
(
"allowfall",
EV_DEFAULT,
"B",
"allow_fall_bool",
"Makes the actor ignore falls when trying to move."
);
Event EV_Actor_SetCanBeFinishedBy
(
"canbefinishedby",
EV_TIKIONLY,
"sSSSSS",
"mod1 mod2 mod3 mod4 mod5 mod6",
"Adds to the can be finished by list for this actor."
);
Event EV_Actor_SetFeetWidth
(
"feetwidth",
EV_TIKIONLY,
"f",
"feet_width",
"Sets the width of the feet for this actor if different than the bounding box size."
);
Event EV_Actor_SetCanWalkOnOthers
(
"canwalkonothers",
EV_DEFAULT,
NULL,
NULL,
"Allows the actor to walk on top of others."
);
Event EV_Actor_Push
(
"push",
EV_CODEONLY,
"v",
"dir",
"Pushes the actor in the specified direction."
);
Event EV_Actor_Pushable
(
"pushable",
EV_DEFAULT,
"B",
"flag",
"Sets whether or not an actor can be pushed out of the way."
);
Event EV_Actor_ChargeWater
(
"chargewater",
EV_DEFAULT,
"ff",
"damage range",
"Does a charge water attack."
);
Event EV_Actor_SendCommand
(
"sendcommand",
EV_CODEONLY,
"ss",
"command part_name",
"Sends a command to another one of its parts."
);
Event EV_Actor_SetTargetable
(
"targetable",
EV_DEFAULT,
"b",
"should_target",
"Sets whether or not this actor should be targetable by the player."
);
Event EV_Actor_ChangeType
(
"changetype",
EV_DEFAULT,
"s",
"new_model_name",
"Changes the actor to the specified new type of actor."
);
Event EV_Actor_IgnoreMonsterClip
(
"ignoremonsterclip",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor ignore monster clip brushes."
);
Event EV_Actor_MinimumMeleeHeight
(
"minmeleeheight",
EV_DEFAULT,
"f",
"minimum_height",
"Sets the minimum height a melee attack has to be to hurt the actor."
);
Event EV_Actor_SetDamageAngles
(
"damageangles",
EV_DEFAULT,
"f",
"damage_angles",
"Sets the the angles where the actor can be hurt (like fov)."
);
Event EV_Actor_Immortal
(
"immortal",
EV_DEFAULT,
"b",
"immortal_bool",
"Sets whether or not the actor is immortal or not."
);
Event EV_Actor_SetDieCompletely
(
"diecompletely",
EV_DEFAULT,
"b",
"die_bool",
"Sets whether or not the actor dies completely (if he doesn't he mostly just"
" runs his kill_thread)."
);
Event EV_Actor_SetBleedAfterDeath
(
"bleed_after_death",
EV_DEFAULT,
"b",
"bleed_bool",
"Sets whether or not the actor will bleed after dying."
);
Event EV_Actor_IgnorePlacementWarning
(
"ignore_placement_warning",
EV_DEFAULT,
"s",
"warning_string",
"Makes the specified placement warning not get printed for this actor."
);
Event EV_Actor_SetIdleStateName
(
"set_idle_state_name",
EV_SCRIPTONLY,
"s",
"new_idle_state_name",
"Sets the actor's new idle state name."
);
Event EV_Actor_SetNotAllowedToKill
(
"not_allowed_to_kill",
EV_DEFAULT,
NULL,
NULL,
"Player fails the level if he kills an actor with this set."
);
Event EV_Actor_IgnoreWater
(
"ignorewater",
EV_DEFAULT,
"b",
"ignore_water_bool",
"Sets whether or not this actor will ignore water when moving."
);
Event EV_Actor_SimplePathfinding
(
"simplepathfinding",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor use simplier path finding."
);
Event EV_Actor_NoPainSounds
(
"nopainsounds",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor not broadcast sounds (AI stimuli) when taking pain or killed."
);
Event EV_Actor_BroadcastAlert
(
"alertevent",
EV_DEFAULT,
"F",
"soundRadius",
"Alerts Entities within the radius of the enemy's location."
);
Event EV_Actor_UpdateBossHealth
(
"updatebosshealth",
EV_DEFAULT,
"BB",
"updateFlag forceOn",
"Tells the actor to update the bosshealth cvar each time it thinks."
);
Event EV_Actor_SetMaxBossHealth
(
"maxbosshealth",
EV_DEFAULT,
"f",
"max_boss_health",
"Sets the actor's max boss health."
);
Event EV_Actor_IgnorePainFromActors
(
"ignorepainfromactors",
EV_DEFAULT,
NULL,
NULL,
"Makes this actor ignore pain from other actors."
);
Event EV_Actor_DamageAllowed
(
"damageallowed",
EV_DEFAULT,
"b",
"damage_allowed",
"Turns melee damage on and off."
);
Event EV_Actor_SetEmotion
(
"emotion",
EV_DEFAULT,
"S",
"expression_name",
"Sets the actors current emotion."
);
Event EV_Actor_ReturnProjectile
(
"returnproj",
EV_TIKIONLY,
NULL,
NULL,
"Returns a projectile to the current enemy"
);
Event EV_Actor_SetRadiusDialogRange
(
"setradiusdialogrange",
EV_DEFAULT,
"f",
"range",
"Sets the range for playing radius dialog"
);
Event EV_Actor_SetDialogMode
(
"setdialogmode",
EV_DEFAULT,
"s",
"mode_type",
"Sets the Dialog Mode for the actor, valid values are 'anxious', 'normal', or 'ignore'"
);
Event EV_Actor_DialogAnimDone
(
"dialoganimdone",
EV_CODEONLY,
NULL,
NULL,
"Not meant to be called from script -- So DONT FREAKIN DO IT!!!!"
);
Event EV_Actor_SetEyeAngleConstraints
(
"seteyeangleconstraints",
EV_TIKIONLY,
"ffff",
"min_eye_yaw_angle max_eye_yaw_angle min_eye_pitch_angle max_eye_pitch_angle",
"Sets the constraints on eye movement"
);
Event EV_Actor_SetActivateThread
(
"setactivatethread",
EV_SCRIPTONLY,
"s",
"thread_name",
"Sets the thread to call when the AI Activates"
);
Event EV_Actor_SetValidTarget
(
"setvalidtarget",
EV_DEFAULT,
"b",
"valid_target",
"Sets whether or not actor is valid target in actor to actor confrontations"
);
Event EV_Actor_SetAlertThread
(
"setalertthread",
EV_SCRIPTONLY,
"s",
"thread_name",
"sets a thread to be called when AI goes to alert"
);
Event EV_Actor_RunAlertThread
(
"runalertthread",
EV_CODEONLY,
NULL,
NULL,
"runs an actors alert thread - NOT MEANT TO BE CALLED FROM SCRIPT"
);
Event EV_Actor_CheckActorDead
(
"checkactordead",
EV_SCRIPTONLY,
"@ie",
"dead_bool entity_to_check",
"checks if an actor is dead"
);
Event EV_Actor_EnemyActorFlag
(
"setenemyactorflag",
EV_SCRIPTONLY,
"sB",
"flag_name flag_bool",
"Sets an Actor's flag"
);
Event EV_Actor_EnemyAIOn
(
"enemyaion",
EV_CODEONLY,
NULL,
NULL,
"turns on the current enemy AI"
);
Event EV_Actor_EnemyAIOff
(
"enemyaioff",
EV_CODEONLY,
NULL,
NULL,
"turns off the current enemy AI"
);
Event EV_Actor_AttachCurrentEnemy
(
"attachcurrentenemy",
EV_CODEONLY,
"s",
"bone",
"attach current enemy to the given bone"
);
Event EV_Actor_AttachActor
(
"attachactor",
EV_DEFAULT,
"sss",
"model targetname bone",
"attach actor to the given bone"
);
Event EV_Actor_SetEnemyAttached
(
"setenemyattached",
EV_DEFAULT,
"b",
"attached",
"sets whether or not the current enemy is attached -- Quetzal Specific"
);
Event EV_Actor_PickUpThrowObject
(
"pickupthrowobject",
EV_SCRIPTONLY,
"s",
"bone",
"bone to attach object to"
);
Event EV_Actor_TossThrowObject
(
"tossthrowobject",
EV_DEFAULT,
"ff",
"speed gravity",
"throws a throw object"
);
Event EV_Actor_SetTurretMode
(
"setturretmode",
EV_DEFAULT,
"b",
"on_off",
"sets turret mode on or off"
);
Event EV_Actor_SetHitscanResponseChance
(
"sethitscanresponse",
EV_DEFAULT,
"f",
"chance",
"sets chance an actor will respond to hitscan attacks"
);
Event EV_Actor_SetWeaponReady
(
"setweaponready",
EV_DEFAULT,
"b",
"ready",
"sets if the actor has its weapon ready or not"
);
Event EV_Actor_SetOnDamageThread
(
"actorondamage",
EV_SCRIPTONLY,
"sI",
"thread_name damage_threshold",
"sets the thread that is called when actor is damaged"
);
Event EV_Actor_SetTimeBetweenSleepChecks
(
"timebetweensleepchecks",
EV_SCRIPTONLY,
"f",
"delay",
"sets the time between tests to see if the actor should sleep"
);
Event EV_Actor_SetAimLeadFactors
(
"setaimleadfactors",
EV_DEFAULT,
"ff",
"minLeadFactor maxLeadFactor",
"sets the lead factor for projectile aiming; 0 = don't lead, 1 = perfect lead"
);
Event EV_Actor_SetActorType
(
"actortype",
EV_DEFAULT,
"s",
"actor_type",
"sets the actortype"
);
Event EV_Actor_RegisterBehaviorPackage
(
"registerpackage",
EV_TIKIONLY,
"s",
"package_name",
"registers a behavior package"
);
Event EV_Actor_UnregisterBehaviorPackage
(
"unregisterpackage",
EV_TIKIONLY,
"s",
"package_name",
"unregisters a behavior package"
);
Event EV_Actor_SetBehaviorPackageTendency
(
"setpackagetendency",
EV_DEFAULT,
"sf",
"package_name tendency",
"sets the tendency to execute the behavior package"
);
Event EV_Actor_SetAbsoluteMaxRange
(
"setabsolutemaxrange",
EV_DEFAULT,
"f",
"absolute_max_range",
"sets the absolute maximum range the actor will get from an entity"
);
Event EV_Actor_SetAbsoluteMinRange
(
"setabsoluteminrange",
EV_DEFAULT,
"f",
"absolute_min_range",
"sets the absolute minimum range the actor will get from an entity"
);
Event EV_Actor_SetPreferredMaxRange
(
"setpreferredmaxrange",
EV_DEFAULT,
"f",
"preferred_max_range",
"sets the preferred maximum range the actor would like to be from an entity"
);
Event EV_Actor_SetPreferredMinRange
(
"setpreferredminrange",
EV_DEFAULT,
"f",
"preferred_min_range",
"sets the preferred minimum range the actor would like to be from an entity"
);
Event EV_Actor_DebugStates
(
"debugstates",
EV_CODEONLY,
"i",
"debug_state",
"sets debug level for actor statemachine"
);
Event EV_Actor_SetHeadWatchTarget
(
"headwatchtarget",
EV_SCRIPTONLY,
"sf",
"target speed",
"sets the headwatch target... currently to enemy or none"
);
Event EV_Actor_SetHeadTwitch
(
"headTwitch",
EV_SCRIPTONLY,
"b",
"bool",
"Sets whether or not the head should twitch."
);
Event EV_Actor_SetFuzzyEngineActive
(
"fuzzyengineactive",
EV_DEFAULT,
"b",
"active",
"sets the fuzzy engine active or not"
);
//
// Waypoint stuff
//
Event EV_Actor_FollowWayPoints
(
"followwaypoints",
EV_SCRIPTONLY,
"sS",
"waypointnode_name starting_anim_name",
"Makes an actor follow a waypoint path starting at , starting at the start_point"
);
Event EV_Actor_Disable
(
"disable",
EV_DEFAULT,
"i",
"disable_flag",
"disable actor ( 1 ) or not disable actor ( 0 )"
);
Event EV_Actor_Cripple
(
"cripple",
EV_DEFAULT,
"i",
"cripple_flag",
"cripple actor ( 1 ) or not cripple actor ( 0 )"
);
Event EV_Actor_In_Alcove
(
"in_alcove",
EV_DEFAULT,
"i",
"in_alcove_flat",
"in alcove ( 1 ) or not in alcove ( 0 )"
);
//
// Weapon stuff
//
Event EV_Actor_GiveActorWeapon
(
"giveactorweapon",
EV_DEFAULT,
"sI",
"weapon amount",
"Gives a weapon to an actor"
);
Event EV_Actor_RemoveActorWeapon
(
"removeactorweapon",
EV_DEFAULT,
"s",
"weapon",
"removes an actors weapon"
);
Event EV_Actor_UseWeapon
(
"useactorweapon",
EV_TIKIONLY,
"sS",
"weapon hand",
"Makes the specified weapon active for the actor \n"
"If they have the weapon"
);
Event EV_Actor_SetMovementMode
(
"movementmode",
EV_DEFAULT,
"s",
"movment_mode",
"sets the movment mode of the actor"
);
Event EV_Actor_ResetMoveDir
(
"resetmovedir",
EV_CODEONLY,
NULL,
NULL,
"Resets and resyncs movedir with animdir"
);
Event EV_Actor_SetNodeID
(
"setnodeid",
EV_SCRIPTONLY,
"i",
"id_number",
"Sets the ID number of the helper nodes that this actor can use"
);
//
// Personality Stuff
//
Event EV_Actor_SetAggressiveness
(
"aggressive",
EV_DEFAULT,
"f",
"aggressiveness",
"sets the aggressiveness of the actor... valid range between 0 and 1"
);
Event EV_Actor_SetTalkiness
(
"talkiness",
EV_DEFAULT,
"f",
"talkiness",
"sets the talkiness of the actor... valid range between 0 and 1"
);
Event EV_Actor_SetTendency
(
"settendency",
EV_DEFAULT,
"sf",
"name value",
"Sets a tendency for the actor"
);
Event EV_Actor_SetFloatProperty
(
"setfloatproperty",
EV_DEFAULT,
"sf",
"name value",
"Sets a float property on the actor"
);
Event EV_Actor_SetGroupNumber
(
"groupnumber",
EV_DEFAULT,
"i",
"group_number",
"sets the group number of the actor"
);
Event EV_Actor_Blink
(
"blink",
EV_DEFAULT,
"b",
"shouldBlink",
"Sets whether or not the actor should blink"
);
Event EV_Actor_ClearArmorAdapations
(
"cleararmoradaptions",
EV_SCRIPTONLY,
NULL,
NULL,
"clears armor adaptions"
);
Event EV_Actor_SetFollowTarget
(
"followtarget",
EV_SCRIPTONLY,
"e",
"entity_to_follow",
"Sets the following target for the actor"
);
Event EV_Actor_SetFollowRange
(
"followrange",
EV_SCRIPTONLY,
"f",
"maxRange",
"Sets a range that the target considers _close enough_ while following"
);
Event EV_Actor_SetFollowRangeMin
(
"followrangemin",
EV_SCRIPTONLY,
"f",
"minRange",
"Sets a minimum range for following"
);
Event EV_Actor_SetCombatFollowRange
(
"followcombatrange",
EV_SCRIPTONLY,
"f",
"maxRange",
"Sets a range that the target considers _close enough_ while following in a combat situation"
);
Event EV_Actor_SetCombatFollowRangeMin
(
"followcombatrangemin",
EV_SCRIPTONLY,
"f",
"minRange",
"Sets a minimum range for following in a combat situation"
);
Event EV_Actor_SetSteeringDirectionPreference
(
"steeringdirectionpreference",
EV_SCRIPTONLY,
"s",
"preference",
"Sends a string to define the way actors will turn when avoiding obstacles"
);
Event EV_Actor_SetStickToGround
(
"setsticktoground",
EV_DEFAULT,
"b",
"stick",
"Sets a bool that determines whether the actor ground follows"
);
Event EV_Actor_SetDialogMorphMult
(
"dialogMorphMult",
EV_DEFAULT,
"f",
"dialogMorphMult",
"Sets the multiplier for all dialog morphs for this actor."
);
// Context Dialog Context Events
Event EV_ContextDialog_InContext
(
"incontext",
EV_CODEONLY,
"s",
"context",
"Used to start a context dialog"
);
Event EV_ContextDialog_IgnoreNextContext
(
"ignorenextcontext",
EV_CODEONLY,
"bs",
"flag context",
"Makes the actor ignore the next context event it receives"
);
Event EV_Actor_ForceSetClip
(
"forcesetclip",
EV_CODEONLY,
NULL,
NULL,
"Makes the actor set his contents to setclip"
);
Event EV_Actor_WhatAreYouDoing
(
"whatareyoudoing",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor print a bunch of debug state info to the console"
);
Event EV_Actor_WhatsWrong
(
"whatswrong",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor print the current behaviors failure reaon to the console"
);
Event EV_Actor_PutawayWeapon
(
"putawayweapon",
EV_DEFAULT,
"S",
"hand",
"Deactivate the weapon in the specified hand."
);
Event EV_Actor_SetCombatTraceInterval
(
"combattraceinterval",
EV_DEFAULT,
"f",
"interval",
"Determines how often an actor will re-trace when doing can-attack types of checks"
);
Event EV_Actor_UseWeaponDamage
(
"useweapondamage",
EV_TIKIONLY,
"SB",
"hand setflag",
"Makes the melee event reference the damage of the weapon in the specified hand."
);
Event EV_Actor_StrictlyFollowPath
(
"strictlyfollowpath",
EV_DEFAULT,
"b",
"boolean",
"Lets the actor know if he should follow paths exactly or if he can go directly to his goal."
);
Event EV_Actor_EvaluateEnemies
(
"evaluateenemies",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor evaluate his enemy list"
);
Event EV_Actor_ForgetEnemies
(
"forgetenemies",
EV_DEFAULT,
NULL,
NULL,
"Makes the actor forget about all enemies for one frame"
);
Event EV_Actor_SetMaxHeadYaw
(
"maxheadyaw",
EV_DEFAULT,
"f",
"maxyaw",
"Sets the max yaw the headwatcher can turn the head"
);
Event EV_Actor_SetMaxHeadPitch
(
"maxheadpitch",
EV_DEFAULT,
"f",
"maxpitch",
"Sets the max pitch the headwatcher can turn the head"
);
Event EV_Actor_SetPostureStateMap
(
"posturestatemap",
EV_DEFAULT,
"sB",
"statemap loadingFlag",
"Sets the state machine for the posture controller"
);
Event EV_Actor_SendEventToGroup
(
"sendeventtogroup",
EV_DEFAULT,
"sSSSSS",
"event parm parm parm parm parm",
"sends the specified event to the entire group"
);
Event EV_Actor_GroupAttack
(
"groupattack",
EV_DEFAULT,
NULL,
NULL,
"Sends and attack event to the whole group with this actors current enemy"
);
Event EV_Actor_GroupActorType
(
"groupactortype",
EV_DEFAULT,
"s",
"actortype",
"Sends an actortype event to the whole group"
);
Event EV_Actor_SetMasterState
(
"setmasterstate",
EV_DEFAULT,
"s",
"state_name",
"Sets the master state"
);
Event EV_Actor_PrintDebugMessage
(
"printmessage",
EV_DEFAULT,
"s",
"message",
"Prints a warning message to the console"
);
Event EV_Actor_SelectNextEnemy
(
"SelectNextEnemy",
EV_DEFAULT,
NULL,
NULL,
"Sets the actor's current enemy to be the next enemy in it's hate list, assuming there is one."
);
Event EV_Actor_SelectClosestEnemy
(
"selectclosestenemy",
EV_DEFAULT,
NULL,
NULL,
"Sets the actor's current enemy to be the closest enemy in it's hate list "
);
Event EV_Actor_SetGroupDeathThread
(
"groupdeaththread",
EV_DEFAULT,
"s",
"thread_name",
"Sets a thread to call when all the members of a group have been killed"
);
Event EV_Actor_SetAnimSet
(
"useanimset",
EV_DEFAULT,
"s",
"anim_set",
"Sets the AnimSet... Valid Set Names are AnimSet1 , AnimSet2 "
);
Event EV_Actor_SetPostureState
(
"setposturestate",
EV_DEFAULT,
"ss",
"currentState requestedState",
"Sets the Posture State"
);
Event EV_Actor_SetTalkWatchMode
(
"settalkwatchmode",
EV_DEFAULT,
"sB",
"mode useConvAnim",
"Sets the talk watch mode -- valid entries are turnto, headwatchonly, and ignore"
);
Event EV_Actor_PrepareMissionFailure
(
"failmission",
EV_DEFAULT,
"fS",
"time reason",
"Fails the mission in the time specified"
);
Event EV_Actor_FailMission
(
"domissionfailure",
EV_DEFAULT,
"S",
"reason",
"Fails the mission"
);
Event EV_Actor_DebugEvent
(
"debugevent",
EV_DEFAULT,
NULL,
NULL,
"Called for Debug Purposes from state machine"
);
Event EV_Actor_UnreserveCurrentHelperNode
(
"unreservecurrenthelpernode",
EV_CODEONLY,
NULL,
NULL,
"Unreserves the current helper node"
);
Event EV_Actor_ProjectileClose
(
"projectileclose",
EV_CODEONLY,
"e",
"owener",
"Informs Actor that a projectile is close"
);
Event EV_Actor_SaveOffLastHitBone
(
"saveofflasthitbone",
EV_CODEONLY,
NULL,
NULL,
"Saves off the last hit_bone"
);
Event EV_Actor_ClearTorsoAnim
(
"cleartorsoanim",
EV_CODEONLY,
NULL,
NULL,
"clears the f'ng torso anim"
);
Event EV_Actor_SetPlayPainSoundInterval
(
"setplaypainsoundinterval",
EV_DEFAULT,
"f",
"interval",
"Sets the pain sound interval"
);
Event EV_Actor_SetContextSoundInterval
(
"setcontextsoundinterval",
EV_DEFAULT,
"f",
"interval",
"Sets the context sound ( dialog ) interval "
);
Event EV_Actor_SetMinPainTime
(
"setminpaintime",
EV_DEFAULT,
"f",
"time",
"Sets the minimum pain time"
);
Event EV_Actor_SetEnemyTargeted
(
"setenemytargeted",
EV_DEFAULT,
"b",
"targeted",
"Sets whether or not the enemy should display targeted shader"
);
Event EV_Actor_SetActivationDelay
(
"setactivationdelay",
EV_DEFAULT,
"f",
"delay",
"If set up to use it, actors will delay action for the specifed delay time"
);
Event EV_Actor_SetActivationStart
(
"startactivationtimer",
EV_DEFAULT,
NULL,
NULL,
"Sets the activationStart time to the current level time"
);
Event EV_Actor_SetCheckConeOfFireDistance
(
"SetCheckConeOfFireDistance",
EV_DEFAULT,
"f",
"distance",
"Sets how close an actor must be for IN_CONE_OF_FIRE checks from a state machine."
);
Event EV_Actor_AddCustomThread
(
"addcustomthread",
EV_DEFAULT,
"ss",
"threadType threadName",
"Adds a custom thread to the actor. The thread type is the specific type to call, the name is the thread to call"
);
Event EV_Actor_SetHeadWatchMaxDistance
(
"setheadwatchmaxdistance",
EV_DEFAULT,
"f",
"maxdistance",
"Sets the max distance for the headwatcher"
);
Event EV_Actor_AnimateOnce
(
"animateonce",
EV_DEFAULT,
"s",
"anim_name",
"Runs the specified animation one time, then holds the last frame"
);
Event EV_Actor_SetDeathKnockbackValues
(
"setdeathknockbackvalues",
EV_DEFAULT,
"ff",
"vertical_value horiz_value",
"Sets Death Knockback Values"
);
char actor_flag_strings[ ACTOR_FLAG_MAX ][ 32 ] =
{
"noiseheard",
"investigating",
"deathgib",
"deathfade",
"nochatter",
"inactive",
"animdone",
"statedonetimevalid",
"masterstatedonetimevalid",
"AIon",
"lastcanseeenemy",
"lastcanseeenemynofov",
"dialogplaying",
"radiusdialogplaying",
"allowtalk",
"damageonceon",
"damageoncedamaged",
"bounceoff",
"notifiyothersatdeath",
"hasthing1",
"hasthing2",
"hasthing3",
"hasthing4",
"lastattackhit",
"started",
"allowhangback",
"usegravity",
"spawnfailed",
"fadingout",
"deathshrink",
"deathsink",
"staysolid",
"stunned",
"allowfall",
"finished",
"inlimbo",
"canwalkonothers",
"pushable",
"lasttrytalk",
"targetable",
"immortal",
"turninghead",
"movingeyes",
"diecompletely",
"bleedafterdeath",
"ignorestuckwarning",
"ignoreoffgroundwarning",
"allowedtokill",
"touchtriggers",
"ignorewater",
"neverignoresounds",
"simplepathfinding",
"havemoved",
"nopainsounds",
"updatebosshealth",
"ignorepainfromactors",
"damageallowed",
"atcovernode",
"waitfornewenemy",
"takedamage",
"usedamageskins",
"captured",
"turretmode",
"incominghitscan",
"respondingtohitscan",
"meleehitworld",
"torsoanimdone",
"weaponready",
"disabled",
"inalcove",
"inconeoffire",
"inplayerconeoffire",
"playerincallvolume",
"incallvolume",
"outoftorsorange",
"ducked",
"prone",
"shouldblink",
"crippled",
"retreating",
"hidden",
"followinginformation",
"displayingfailureFX",
"groupmemberinjured",
"canhealother",
"strictlyfollowpath",
"postureanimdone",
"attackingenemy",
"updatehatebasedonattackers",
"lastcanseeplayer",
"lastcanseeplayernofov",
"meleeallowed",
"playingdialoganim",
"usinghud",
"forcelifebar",
"updateactionlevel",
"canchangeanim",
"usefollowrangefornodes",
"immediateactivate",
"cannotdisintegrate",
"cannotuse",
"cannotfreeze"
};
char actor_notify_strings[ ACTOR_FLAG_MAX ][ 32 ] =
{
"on_damage",
"on_killed",
"on_spottedenemy"
};
CLASS_DECLARATION( Sentient, Actor, "monster_generic" )
{
{ &EV_Activate, &Actor::ActivateEvent },
{ &EV_Actor_BlindlyFollowPath, &Actor::BlindlyFollowPath },
{ &EV_Actor_SetSimplifiedThink, &Actor::SetSimplifiedThink },
{ &EV_Actor_SetActorToActorDamageModifier, &Actor::SetActorToActorDamageModifier },
{ &EV_Use, &Actor::UseEvent },
{ &EV_Actor_OnUse, &Actor::SetOnUseThread },
{ &EV_Actor_NoUse, &Actor::ClearOnUseThread },
{ &EV_Actor_Sleep, &Actor::Sleep },
{ &EV_Actor_Wakeup, &Actor::Wakeup },
{ &EV_Actor_SetTargetType, &Actor::SetTargetType },
{ &EV_Actor_Start, &Actor::Start },
{ &EV_Pain, &Actor::Pain },
{ &EV_Killed, &Actor::Killed },
{ &EV_Actor_Dead, &Actor::Dead },
{ &EV_Actor_Suicide, &Actor::Suicide },
{ &EV_Actor_ForwardSpeed, &Actor::ForwardSpeedEvent },
{ &EV_Actor_Fov, &Actor::SetFOV },
{ &EV_Actor_VisionDistance, &Actor::SetVisionDistance },
{ &EV_Actor_SetEnemyType, &Actor::SetEnemyType },
{ &EV_Actor_ClearCurrentEnemy, &Actor::ClearCurrentEnemy },
{ &EV_Actor_Swim, &Actor::SwimEvent },
{ &EV_Actor_Fly, &Actor::FlyEvent },
{ &EV_Actor_NotLand, &Actor::NotLandEvent },
{ &EV_Actor_SetDialogMode, &Actor::SetDialogMode },
{ &EV_Actor_DialogAnimDone, &Actor::DialogAnimDone },
{ &EV_Actor_RunThread, &Actor::RunThread },
{ &EV_Actor_Statemap, &Actor::LoadStateMap },
{ &EV_Actor_MasterStateMap, &Actor::LoadMasterStateMap },
{ &EV_Actor_SetBehaviorPackage, &Actor::SetBehaviorPackage },
{ &EV_Actor_UseBehaviorPackage, &Actor::UseBehaviorPackage },
{ &EV_Actor_ChildUseBehaviorPackage, &Actor::ChildUseBehaviorPackage },
{ &EV_Actor_ChildSetAnim, &Actor::ChildSetAnim },
{ &EV_Actor_ChildSuicide, &Actor::ChildSuicide },
{ &EV_Actor_IfEnemyVisible, &Actor::IfEnemyVisibleEvent },
{ &EV_Actor_IfNear, &Actor::IfNearEvent },
{ &EV_Actor_Idle, &Actor::GoIdle },
{ &EV_Actor_LookAt, &Actor::LookAt },
{ &EV_Actor_TurnTo, &Actor::TurnToEvent },
{ &EV_Actor_HeadWatch, &Actor::HeadWatchEvent },
{ &EV_Actor_ResetHead, &Actor::ResetHeadEvent },
{ &EV_Actor_EyeWatch, &Actor::EyeWatchEvent },
{ &EV_Actor_ResetEye, &Actor::ResetEyeEvent },
{ &EV_Actor_ResetTorso, &Actor::ResetTorsoEvent },
{ &EV_Actor_HeadAndEyeWatch, &Actor::HeadAndEyeWatchEvent },
{ &EV_Actor_EndBehavior, &Actor::EndBehaviorEvent },
{ &EV_Actor_EndHeadBehavior, &Actor::EndHeadBehaviorEvent },
{ &EV_Actor_EndEyeBehavior, &Actor::EndEyeBehaviorEvent },
{ &EV_Actor_EndTorsoBehavior, &Actor::EndTorsoBehaviorEvent },
{ &EV_Actor_NotifyBehavior, &Actor::NotifyBehavior },
{ &EV_Actor_NotifyHeadBehavior, &Actor::NotifyHeadBehavior },
{ &EV_Actor_NotifyEyeBehavior, &Actor::NotifyEyeBehavior },
{ &EV_Actor_NotifyTorsoBehavior, &Actor::NotifyTorsoBehavior },
{ &EV_Actor_FallToDeath, &Actor::FallToDeathEvent },
{ &EV_Actor_WalkTo, &Actor::WalkTo },
{ &EV_Actor_WalkWatch, &Actor::WalkWatch },
{ &EV_Actor_JumpTo, &Actor::JumpToEvent },
{ &EV_Actor_WarpTo, &Actor::WarpTo },
{ &EV_Actor_Anim, &Actor::Anim },
{ &EV_Actor_SetAnim, &Actor::SetAnim },
{ &EV_Actor_Attack, &Actor::AttackEntity },
{ &EV_Actor_AttackPlayer, &Actor::AttackPlayer },
{ &EV_Actor_Remove, &Actor::RemoveUselessBody },
{ &EV_Actor_ReserveNode, &Actor::ReserveNodeEvent },
{ &EV_Actor_ReleaseNode, &Actor::ReleaseNodeEvent },
{ &EV_Actor_IfCanHideAt, &Actor::IfCanHideAtEvent },
{ &EV_Actor_IfEnemyWithin, &Actor::IfEnemyWithinEvent },
{ &EV_HeardSound, &Actor::HeardSound },
{ &EV_Actor_BroadcastAlert, &Actor::BroadcastAlert },
{ &EV_Actor_Melee, &Actor::MeleeEvent },
{ &EV_Actor_PainThreshold, &Actor::SetPainThresholdEvent },
{ &EV_Actor_SetKillThread, &Actor::SetKillThreadEvent },
{ &EV_SetHealth, &Actor::SetHealth },
{ &EV_SetMaxHealth, &Actor::SetMaxHealth },
{ &EV_Actor_EyePositionOffset, &Actor::EyeOffset },
{ &EV_Actor_DeathFade, &Actor::DeathFadeEvent },
{ &EV_Actor_DeathEffect, &Actor::setDeathEffect },
{ &EV_Actor_DeathShrink, &Actor::DeathShrinkEvent },
{ &EV_Actor_DeathSink, &Actor::DeathSinkEvent },
{ &EV_Actor_StaySolid, &Actor::StaySolidEvent },
{ &EV_Actor_NoChatter, &Actor::NoChatterEvent },
{ &EV_Actor_TurnSpeed, &Actor::SetTurnSpeed },
{ &EV_Actor_SetActorFlag, &Actor::SetActorFlag },
{ &EV_Actor_SetNotifyFlag, &Actor::SetNotifyFlag },
{ &EV_Actor_SetVar, &Actor::SetVar },
{ &EV_Actor_PersistData, &Actor::SetVar },
{ &EV_Actor_SetVarTime, &Actor::SetVarTime },
{ &EV_Actor_SetMaxInactiveTime, &Actor::SetMaxInactiveTime },
{ &EV_Anim_Done, &Actor::AnimDone },
{ &EV_Torso_Anim_Done, &Actor::TorsoAnimDone },
{ &EV_Actor_ProjAttack, &Actor::FireProjectile },
{ &EV_Actor_BulletAttack, &Actor::FireBullet },
{ &EV_Actor_RadiusAttack, &Actor::FireRadiusAttack },
{ &EV_Actor_Active, &Actor::Active },
{ &EV_Actor_SpawnGib, &Actor::SpawnGib },
{ &EV_Actor_SpawnGibAtTag, &Actor::SpawnGibAtTag },
{ &EV_Actor_SpawnNamedGib, &Actor::SpawnNamedGib },
{ &EV_Actor_SpawnBlood, &Actor::SpawnBlood },
{ &EV_Actor_AIOn, &Actor::TurnAIOn },
{ &EV_Actor_AIOff, &Actor::TurnAIOff },
{ &EV_Actor_SetIdleThread, &Actor::SetIdleThread },
{ &EV_ActorRegisterParts, &Actor::RegisterParts },
{ &EV_ActorRegisterSelf, &Actor::RegisterSelf },
{ &EV_ActorName, &Actor::Name },
{ &EV_ActorPartName, &Actor::PartName },
{ &EV_Actor_SendCommand, &Actor::SendCommand },
{ &EV_ActorSetupTriggerField, &Actor::SetupTriggerField },
{ &EV_ActorTriggerTouched, &Actor::TriggerTouched },
{ &EV_ActorIncomingProjectile, &Actor::IncomingProjectile },
{ &EV_ActorSpawnActor, &Actor::SpawnActorAtTag },
{ &EV_ActorSpawnActorAtLocation, &Actor::SpawnActorAtLocation },
{ &EV_ActorSpawnActorAboveEnemy, &Actor::SpawnActorAboveEnemy },
{ &EV_Actor_AddDialog, &Actor::AddDialog },
{ &EV_Actor_DialogDone, &Actor::DialogDone },
{ &EV_Actor_PlayDialog, &Actor::PlayDialog },
{ &EV_Actor_StopDialog, &Actor::StopDialog },
{ &EV_Actor_BranchDialog, &Actor::BranchDialog },
{ &EV_Sentient_SetMouthAngle, &Actor::SetMouthAngle },
{ &EV_Actor_AllowTalk, &Actor::AllowTalk },
{ &EV_Actor_AllowHangBack, &Actor::AllowHangBack },
{ &EV_Actor_SolidMask, &Actor::SolidMask },
{ &EV_Actor_IgnoreMonsterClip, &Actor::IgnoreMonsterClip },
{ &EV_Actor_NotSolidMask, &Actor::NotSolidMask },
{ &EV_Actor_NoMask, &Actor::NoMask },
{ &EV_Actor_SetMask, &Actor::SetMask },
{ &EV_Actor_PickupEnt, &Actor::PickupEnt },
{ &EV_Actor_ThrowEnt, &Actor::ThrowEnt },
{ &EV_Actor_Pickup, &Actor::Pickup },
{ &EV_Actor_Throw, &Actor::Throw },
{ &EV_Actor_DamageOnceStart, &Actor::DamageOnceStart },
{ &EV_Actor_DamageOnceStop, &Actor::DamageOnceStop },
{ &EV_Actor_DamageEnemy, &Actor::DamageEnemy },
{ &EV_Actor_DamageSelf, &Actor::DamageSelf },
{ &EV_Actor_TurnTowardsEnemy, &Actor::TurnTowardsEnemy },
{ &EV_Actor_TurnTowardsPlayer, &Actor::TurnTowardsPlayer },
{ &EV_Actor_TurnTowardsEntity, &Actor::TurnTowardsEntity },
{ &EV_Actor_GotoNextStage, &Actor::GotoNextStage },
{ &EV_Actor_GotoPrevStage, &Actor::GotoPrevStage },
{ &EV_Actor_GotoStage, &Actor::GotoStage },
{ &EV_Actor_GetStage, &Actor::GetStage },
{ &EV_Actor_NotifyOthersAtDeath, &Actor::NotifyOthersAtDeath },
{ &EV_Actor_SetBounceOff, &Actor::SetBounceOff },
{ &EV_Actor_BounceOff, &Actor::BounceOffEvent },
{ &EV_Actor_SetBounceOffEffect, &Actor::SetBounceOffEffect },
{ &EV_Actor_SetHaveThing, &Actor::SetHaveThing },
{ &EV_Actor_SetUseGravity, &Actor::SetUseGravity },
{ &EV_Actor_SetAllowFall, &Actor::SetAllowFall },
{ &EV_Actor_SetDeathSize, &Actor::SetDeathSize },
{ &EV_Actor_Fade, &Actor::FadeEvent },
{ &EV_Stun, &Actor::StunEvent },
{ &EV_Actor_AddSpawnItem, &Actor::AddSpawnItem },
{ &EV_Actor_SetSpawnChance, &Actor::SetSpawnChance },
{ &EV_Actor_ClearSpawnItems, &Actor::ClearSpawnItems },
{ &EV_Actor_SetCanBeFinishedBy, &Actor::SetCanBeFinishedBy },
{ &EV_Actor_SetFeetWidth, &Actor::SetFeetWidth },
{ &EV_Actor_SetCanWalkOnOthers, &Actor::SetCanWalkOnOthers },
{ &EV_Actor_Push, &Actor::Push },
{ &EV_Actor_Pushable, &Actor::Pushable },
{ &EV_Actor_ChargeWater, &Actor::ChargeWater },
{ &EV_Actor_SetTargetable, &Actor::SetTargetable },
{ &EV_Actor_ChangeType, &Actor::ChangeType },
{ &EV_Actor_MinimumMeleeHeight, &Actor::SetMinimumMeleeHeight },
{ &EV_Actor_SetDamageAngles, &Actor::SetDamageAngles },
{ &EV_Actor_Immortal, &Actor::SetImmortal },
{ &EV_Actor_SetDieCompletely, &Actor::SetDieCompletely },
{ &EV_Actor_SetBleedAfterDeath, &Actor::SetBleedAfterDeath },
{ &EV_Actor_IgnorePlacementWarning, &Actor::IgnorePlacementWarning },
{ &EV_Actor_SetIdleStateName, &Actor::SetIdleStateName },
{ &EV_Actor_SetNotAllowedToKill, &Actor::SetNotAllowedToKill },
{ &EV_TouchTriggers, &Actor::TouchTriggers },
{ &EV_Actor_IgnoreWater, &Actor::IgnoreWater },
{ &EV_Actor_SimplePathfinding, &Actor::SimplePathfinding },
{ &EV_Actor_NoPainSounds, &Actor::NoPainSounds },
{ &EV_Actor_UpdateBossHealth, &Actor::UpdateBossHealth },
{ &EV_Actor_SetMaxBossHealth, &Actor::SetMaxBossHealth },
{ &EV_Actor_IgnorePainFromActors, &Actor::IgnorePainFromActors },
{ &EV_Actor_DamageAllowed, &Actor::DamageAllowed },
{ &EV_Touch, &Actor::Touched },
{ &EV_Actor_SetEmotion, &Actor::SetEmotion },
{ &EV_Actor_ReturnProjectile, &Actor::ReturnProjectile },
{ &EV_Actor_SetRadiusDialogRange, &Actor::SetRadiusDialogRange },
{ &EV_Actor_SetEyeAngleConstraints, &Actor::SetEyeAngles },
{ &EV_Actor_SetActivateThread, &Actor::SetActivateThread },
{ &EV_Actor_SetValidTarget, &Actor::SetValidTarget },
{ &EV_Actor_SetAlertThread, &Actor::SetAlertThread },
{ &EV_Actor_RunAlertThread, &Actor::RunAlertThread },
{ &EV_Actor_CheckActorDead, &Actor::checkActorDead },
{ &EV_Actor_EnemyActorFlag, &Actor::SetFlagOnEnemy },
{ &EV_Actor_EnemyAIOn, &Actor::TurnOnEnemyAI },
{ &EV_Actor_EnemyAIOff, &Actor::TurnOffEnemyAI },
{ &EV_Actor_AttachCurrentEnemy, &Actor::AttachCurrentEnemy },
{ &EV_Actor_AttachActor, &Actor::AttachActor },
{ &EV_Actor_SetEnemyAttached, &Actor::SetEnemyAttached },
{ &EV_Actor_PickUpThrowObject, &Actor::PickupThrowObject },
{ &EV_Actor_TossThrowObject, &Actor::TossThrowObject },
{ &EV_Actor_SetTurretMode, &Actor::SetTurretMode },
{ &EV_Actor_SetHitscanResponseChance, &Actor::SetHitscanResponse },
{ &EV_Actor_SetWeaponReady, &Actor::SetWeaponReady },
{ &EV_Actor_GiveActorWeapon, &Actor::GiveActorWeapon },
{ &EV_Actor_RemoveActorWeapon, &Actor::RemoveActorWeapon },
{ &EV_Actor_SetOnDamageThread, &Actor::SetOnDamageThread },
{ &EV_Actor_SetTimeBetweenSleepChecks, &Actor::SetTimeBetweenSleepChecks },
{ &EV_Actor_FollowWayPoints, &Actor::FollowWayPoints },
{ &EV_Actor_SetAimLeadFactors, &Actor::SetAimLeadFactors },
{ &EV_Actor_SetActorType, &Actor::SetActorType },
{ &EV_Actor_RespondTo, &Actor::RespondTo },
{ &EV_Actor_PermanentlyRespondTo, &Actor::PermanentlyRespondTo },
{ &EV_Actor_RegisterBehaviorPackage, &Actor::RegisterBehaviorPackage },
{ &EV_Actor_UnregisterBehaviorPackage, &Actor::UnRegisterBehaviorPackage },
{ &EV_Actor_SetBehaviorPackageTendency, &Actor::SetPackageTendency },
{ &EV_Actor_FuzzyEngine, &Actor::LoadFuzzyEngine },
{ &EV_Actor_SetAbsoluteMaxRange, &Actor::SetAbsoluteMax },
{ &EV_Actor_SetAbsoluteMinRange, &Actor::SetAbsoluteMin },
{ &EV_Actor_SetPreferredMaxRange, &Actor::SetPreferredMax },
{ &EV_Actor_SetPreferredMinRange, &Actor::SetPreferredMin },
{ &EV_Actor_UseWeapon, &Actor::UseActorWeapon },
{ &EV_Sentient_Attack, &Actor::FireWeapon },
{ &EV_Sentient_StopFire, &Actor::StopFireWeapon },
{ &EV_Actor_DebugStates, &Actor::DebugStates },
{ &EV_Actor_Disable, &Actor::SetDisabled },
{ &EV_Actor_Cripple, &Actor::SetCrippled },
{ &EV_Actor_In_Alcove, &Actor::SetInAlcove },
{ &EV_Actor_ResetMoveDir, &Actor::ResetMoveDir },
{ &EV_Actor_SetMovementMode, &Actor::SetMovementMode },
{ &EV_Actor_SetHeadWatchTarget, &Actor::SetHeadWatchTarget },
{ &EV_Actor_SetHeadTwitch, &Actor::setHeadTwitch },
{ &EV_Actor_SetFuzzyEngineActive, &Actor::SetFuzzyEngineActive },
{ &EV_Actor_SetNodeID, &Actor::SetNodeID },
{ &EV_Actor_WhatAreYouDoing, &Actor::WhatAreYouDoing },
{ &EV_Actor_WhatsWrong, &Actor::WhatsWrong },
// Personality Stuff
{ &EV_Actor_SetAggressiveness, &Actor::SetAggressiveness },
{ &EV_Actor_SetTalkiness, &Actor::SetTalkiness },
{ &EV_Actor_SetGroupNumber, &Actor::SetGroupNumber },
{ &EV_Actor_SetTendency, &Actor::SetTendency },
{ &EV_Actor_SetFloatProperty, &Actor::SetTendency },
{ &EV_Actor_Blink, &Actor::SetBlink },
{ &EV_Actor_ClearArmorAdapations, &Actor::ClearArmorAdaptions },
{ &EV_Actor_SetFollowTarget, &Actor::SetFollowTarget },
{ &EV_Actor_SetFollowRange, &Actor::SetFollowRange },
{ &EV_Actor_SetFollowRangeMin, &Actor::SetFollowRangeMin },
{ &EV_Actor_SetCombatFollowRange, &Actor::SetFollowCombatRange },
{ &EV_Actor_SetCombatFollowRangeMin, &Actor::SetFollowCombatRangeMin },
{ &EV_Actor_SetSteeringDirectionPreference, &Actor::SetSteeringDirectionPreference },
{ &EV_Actor_SetStickToGround, &Actor::SetStickToGround },
{ &EV_Actor_SetDialogMorphMult, &Actor::setDialogMorphMult },
// Context Dialog Stuff
{ &EV_ContextDialog_InContext, &Actor::InContext },
{ &EV_ContextDialog_IgnoreNextContext, &Actor::SetIgnoreNextContext },
{ &EV_Actor_BroadcastDialog, &Actor::BroadcastDialog },
{ &EV_Actor_ForceSetClip, &Actor::ForceSetClip },
{ &EV_Actor_SetCombatTraceInterval, &Actor::SetCombatTraceInterval },
{ &EV_Actor_PutawayWeapon, &Actor::PutawayWeapon },
{ &EV_Actor_UseWeaponDamage, &Actor::UseWeaponDamage },
{ &EV_Actor_StrictlyFollowPath, &Actor::StrictlyFollowPath },
{ &EV_HelperNodeCommand, &Actor::HelperNodeCommand },
{ &EV_Actor_SetMaxHeadYaw, &Actor::SetMaxHeadYaw },
{ &EV_Actor_SetMaxHeadPitch, &Actor::SetMaxHeadPitch },
{ &EV_Actor_SetPostureState, &Actor::SetPostureState },
{ &EV_Actor_SaveOffLastHitBone, &Actor::SaveOffLastHitBone },
{ &EV_Actor_SetPlayPainSoundInterval, &Actor::SetPlayPainSoundInterval },
{ &EV_Sentient_GroupMemberInjured, &Actor::GroupMemberInjured },
{ &EV_Actor_EvaluateEnemies, &Actor::EvaluateEnemies },
{ &EV_Actor_ForgetEnemies, &Actor::ForgetEnemies },
{ &EV_Actor_SetPostureStateMap, &Actor::LoadPostureStateMachine },
{ &EV_Posture_Anim_Done, &Actor::PostureAnimDone },
{ &EV_Actor_SendEventToGroup, &Actor::SendEventToGroup },
{ &EV_Actor_GroupAttack, &Actor::GroupAttack },
{ &EV_Actor_GroupActorType, &Actor::GroupActorType },
{ &EV_Actor_SetMasterState, &Actor::SetMasterState },
{ &EV_Actor_PrintDebugMessage, &Actor::PrintDebugMessage },
{ &EV_ProcessGameplayData, &Actor::processGameplayData },
{ &EV_Actor_SelectNextEnemy, &Actor::SelectNextEnemy },
{ &EV_Actor_SelectClosestEnemy, &Actor::SelectClosestEnemy },
{ &EV_Actor_SetGroupDeathThread, &Actor::SetGroupDeathThread },
{ &EV_Actor_SetAnimSet, &Actor::SetAnimSet },
{ &EV_Actor_SetSelfDetonateModel, &Actor::SetSelfDetonateModel },
{ &EV_Actor_SetTalkWatchMode, &Actor::SetTalkWatchMode },
{ &EV_Actor_PrepareMissionFailure, &Actor::PrepareToFailMission },
{ &EV_Actor_FailMission, &Actor::FailMission },
{ &EV_Actor_DebugEvent, &Actor::DebugEvent },
{ &EV_Actor_UnreserveCurrentHelperNode, &Actor::UnreserveCurrentHelperNode },
{ &EV_Actor_ProjectileClose, &Actor::ProjectileClose },
{ &EV_Actor_ClearTorsoAnim, &Actor::ClearTorsoAnim },
{ &EV_Actor_SetContextSoundInterval, &Actor::SetContextInterval },
{ &EV_Actor_SetMinPainTime, &Actor::SetMinPainTime },
{ &EV_Actor_SetEnemyTargeted, &Actor::SetEnemyTargeted },
{ &EV_Actor_SetActivationDelay, &Actor::SetActivationDelay },
{ &EV_Actor_SetActivationStart, &Actor::SetActivationStart },
{ &EV_Actor_SetCheckConeOfFireDistance, &Actor::SetCheckConeOfFireDistance },
{ &EV_Actor_AddCustomThread, &Actor::AddCustomThread },
{ &EV_Actor_SetHeadWatchMaxDistance, &Actor::SetHeadWatchMaxDistance },
{ &EV_Actor_AnimateOnce, &Actor::AnimateOnce },
{ &EV_Actor_SetDeathKnockbackValues, &Actor::SetDeathKnockbackValues },
//Game Specific Events
{ NULL, NULL }
};
//===================================================================================
// Construction and Destruction
//==================================================================================
//
// Name: Actor()
// Parameters: None
// Description: Constructor
//
Actor::Actor()
{
Event *immunity_event;
forcedEnemy = NULL;
//
// make sure this is a modelanim entity
//
edict->s.eType = ET_MODELANIM;
edict->clipmask = MASK_MONSTERSOLID;
edict->svflags |= SVF_MONSTER;
edict->ownerNum = ENTITYNUM_NONE;
// Set ActorType and TargetType
actortype = IS_ENEMY;
targetType = ATTACK_ANY;
//Set SolidType and MoveType
setSolidType( SOLID_BBOX );
setMoveType( MOVETYPE_STEP );
// Clear All Flags
actor_flags1 = 0;
actor_flags2 = 0;
actor_flags3 = 0;
actor_flags4 = 0;
// Set default AnimSet
animset = "AnimSet1";
// Clear Notify Flags;
notify_flags1 = 0;
// Clear All Behaviors
behavior = NULL;
headBehavior = NULL;
eyeBehavior = NULL;
torsoBehavior = NULL;
// Set up Statemap
statemap = NULL;
currentState = NULL;
globalState = NULL;
lastState = NULL;
state_time = level.time;
times_done = 0;
masterstatemap = NULL;
currentMasterState = NULL;
lastMasterState = NULL;
masterstate_time = level.time;
masterstate_times_done = 0;
// Set up FuzzyEngine
fuzzyEngine = NULL;
fuzzyEngine_active = false;
// Threads
onuse_thread_name = "";
escape_thread = "";
captured_thread = "";
activate_thread = "";
ondamage_thread = "";
alert_thread = "";
//Death KnockBack
deathKnockbackVerticalValue = 300;
deathKnockbackHorizontalValue = 500;
// Ranges
absoluteMin = 20.0f;
absoluteMax = 175.0f; // Was 300
preferredMin = 50.0f;
preferredMax = 175.0f; // Was 375
//preferredMax = 95.0f; // Was 375
// Lead Factors for projectile aiming
minLeadFactor = 0.0f;
maxLeadFactor = 2.0f;
//SomeDialog Defaults
radiusDialogRange = 125.0f;
DialogMode = DIALOG_MODE_NORMAL;
dialog_list = NULL;
dialog_done_time = 0;
_ignoreNextContext = false;
_nextContextTime = 0.0f;
_contextInterval = 15.0f;
saved_bone_hit = -9999;
//Eye Angle Defaults
minEyeYawAngle = -30.0f;
maxEyeYawAngle = 30.0f;
minEyePitchAngle = -30.0f;
maxEyePitchAngle = 30.0f;
//Group Number
//groupnumber = 0;
//Do We Bleed?
if ( com_blood->integer )
{
flags |= FL_BLOOD;
flags |= FL_DIE_GIBS;
}
// don't talk all at once initially
chattime = G_Random( 20.0f );
nextsoundtime = 0;
// All actors start immune to falling damage
immunity_event = new Event( EV_Sentient_AddImmunity );
immunity_event->AddString( "falling" );
ProcessEvent( immunity_event );
// ThrowObject Stuff
haveThrowObject = false;
useConvAnims = true;
// Set our default size
setSize( Vector( "-16 -16 0" ), Vector( "16 16 116" ) );
// Health
health = 100;
max_health = health;
// General Data
takedamage = DAMAGE_AIM;
deadflag = DEAD_NO;
mode = ACTOR_MODE_AI; //ACTOR_MODE_IDLE;
max_inactive_time = MAX_INACTIVE_TIME;
newanimevent = NULL;
newTorsoAnimEvent = NULL;
groundentity = NULL;
saved_mode = ACTOR_MODE_NONE;
showStates = DEBUG_NONE;
talkMode = TALK_TURNTO;
mass = 200;
stage = 1;
newanimnum = -1;
newTorsoAnimNum = -1;
pain_threshold = 20;
minimum_melee_height = -100;
air_finished = level.time + 5.0f;
globalState = NULL;
global_state_name = "GLOBAL_MAIN";
actorrange_time = 0;
canseeenemy_time = 0;
canseeplayer_time = 0;
last_time_active = 0;
next_player_near = 0;
last_used_time = 0;
bullet_hits = 0;
state_flags = 0;
num_of_spawns = 0;
next_drown_time = 0;
next_pain_time = 0;
min_pain_time = 1.0;
next_forced_pain_time = 0;
max_pain_time = 2.5;
last_jump_time = 0;
spawn_chance = 0;
feet_width = 0;
next_find_enemy_time = 0;
damage_angles = 0;
real_head_pitch = 0;
next_pain_sound_time = 0;
max_boss_health = 0;
next_blink_time = 0;
shotsFired = 0;
ondamage_threshold = 0;
timeBetweenSleepChecks = 10.0f;
currentSplineTime = 0;
_nextCheckForWorkNodeTime = 0.0f;
_nextCheckForHibernateNodeTime = 0.0f;
_nextCheckForEnemyPath = 0.0f;
_havePathToEnemy = false;
_nextPathDistanceToFollowTargetCheck = 0.0f;
saved_anim_event_name = "";
newanim = "";
newTorsoAnim = "";
last_anim_event_name = "";
last_torso_anim_event_name = "";
emotion = "none";
hitscan_response_chance = 0.0f;
actor_to_actor_damage_modifier = 1.0f;
gunoffset = Vector(0, 0, 44);
eyeoffset = Vector(0, 0, 0);
eyeposition = Vector(0, 0, 64);
velocity = Vector(0, 0, -20);
incoming_bullet = false;
validTarget = true;
haveAttached = false;
showStates = DEBUG_NONE;
_useWeaponDamage = WEAPON_ERROR;
_checkedChance = false;
_dialogMorphMult = 1.0f;
_nextPlayPainSoundTime = 0.0f;
_playPainSoundInterval = 2.0f; //was 0.5
activationDelay = 0.0f;
activationStart = 0.0f;
//1st playable hack
lastPathCheck_Work = 0.0f;
lastPathCheck_Flee = 0.0f;
lastPathCheck_Patrol = 0.0f;
testing = false;
_levelAIOff = false;
// CurrentHelperNode
currentHelperNode.node = NULL;
currentHelperNode.mask = 0;
currentHelperNode.nodeID = 0;
ignoreHelperNode.node = NULL;
ignoreHelperNode.mask = 0;
ignoreHelperNode.nodeID = 0;
// FollowTarget
followTarget.currentFollowTarget = NULL;
followTarget.specifiedFollowTarget = NULL;
followTarget.maxRangeIdle = 384.0f;
followTarget.minRangeIdle = 256.0;
followTarget.maxRangeCombat = 512.0f;
followTarget.minRangeCombat = 256.0f;
_steeringDirectionPreference = STEER_RIGHT_ALWAYS;
//set initial move dir;
//angles.AngleVectors( &movedir );
bounce_off_velocity = -0.5f;
// Set all the flags
SetActorFlag( ACTOR_FLAG_TAKE_DAMAGE, true );
SetActorFlag( ACTOR_FLAG_USE_DAMAGESKINS, true );
SetActorFlag( ACTOR_FLAG_AI_ON, true );
SetActorFlag( ACTOR_FLAG_ALLOW_TALK, true );
SetActorFlag( ACTOR_FLAG_ALLOW_HANGBACK, true );
SetActorFlag( ACTOR_FLAG_LAST_ATTACK_HIT, true );
SetActorFlag( ACTOR_FLAG_USE_GRAVITY, true );
SetActorFlag( ACTOR_FLAG_TARGETABLE, true );
SetActorFlag( ACTOR_FLAG_ALLOWED_TO_KILL, true );
SetActorFlag( ACTOR_FLAG_DIE_COMPLETELY, true );
SetActorFlag( ACTOR_FLAG_BLEED_AFTER_DEATH, true );
SetActorFlag( ACTOR_FLAG_TOUCH_TRIGGERS, true );
SetActorFlag( ACTOR_FLAG_DAMAGE_ALLOWED, true );
SetActorFlag( ACTOR_FLAG_SHOULD_BLINK, true );
SetActorFlag( ACTOR_FLAG_MELEE_ALLOWED, true );
SetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY, false );
SetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY_NOFOV, false );
SetActorFlag( ACTOR_FLAG_CAPTURED, false );
SetActorFlag( ACTOR_FLAG_TURRET_MODE, false );
SetActorFlag( ACTOR_FLAG_INCOMING_HITSCAN, false );
SetActorFlag( ACTOR_FLAG_RESPONDING_TO_HITSCAN, false );
SetActorFlag( ACTOR_FLAG_MELEE_HIT_WORLD, false );
SetActorFlag( ACTOR_FLAG_INACTIVE, false );
SetActorFlag( ACTOR_FLAG_ANIM_DONE, false );
SetActorFlag( ACTOR_FLAG_STATE_DONE_TIME_VALID, false );
SetActorFlag( ACTOR_FLAG_MASTER_STATE_DONE_TIME_VALID , false );
SetActorFlag( ACTOR_FLAG_NOISE_HEARD, false );
SetActorFlag( ACTOR_FLAG_INVESTIGATING, false );
SetActorFlag( ACTOR_FLAG_DIALOG_PLAYING, false );
SetActorFlag( ACTOR_FLAG_RADIUS_DIALOG_PLAYING, false );
SetActorFlag( ACTOR_FLAG_NOTIFY_OTHERS_AT_DEATH, false );
SetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_ON, false );
SetActorFlag( ACTOR_FLAG_BOUNCE_OFF, false );
SetActorFlag( ACTOR_FLAG_HAS_THING1, false );
SetActorFlag( ACTOR_FLAG_HAS_THING2, false );
SetActorFlag( ACTOR_FLAG_HAS_THING3, false );
SetActorFlag( ACTOR_FLAG_HAS_THING4, false );
SetActorFlag( ACTOR_FLAG_SPAWN_FAILED, false );
SetActorFlag( ACTOR_FLAG_FADING_OUT, false );
SetActorFlag( ACTOR_FLAG_ALLOW_FALL, false );
SetActorFlag( ACTOR_FLAG_STUNNED, false );
SetActorFlag( ACTOR_FLAG_PUSHABLE, false );
SetActorFlag( ACTOR_FLAG_WAIT_FOR_NEW_ENEMY, false );
SetActorFlag( ACTOR_FLAG_DEATHFADE, false );
SetActorFlag( ACTOR_FLAG_DEATHSHRINK, false );
SetActorFlag( ACTOR_FLAG_DEATHSINK, false );
SetActorFlag( ACTOR_FLAG_NOCHATTER, false );
SetActorFlag( ACTOR_FLAG_STAYSOLID, false );
SetActorFlag( ACTOR_FLAG_FINISHED, false );
SetActorFlag( ACTOR_FLAG_IN_LIMBO, false );
SetActorFlag( ACTOR_FLAG_CAN_WALK_ON_OTHERS, false );
SetActorFlag( ACTOR_FLAG_LAST_TRY_TALK, false );
SetActorFlag( ACTOR_FLAG_IMMORTAL, false );
SetActorFlag( ACTOR_FLAG_AT_COVER_NODE, false );
SetActorFlag( ACTOR_FLAG_TURNING_HEAD, false );
SetActorFlag( ACTOR_FLAG_IGNORE_STUCK_WARNING, false );
SetActorFlag( ACTOR_FLAG_IGNORE_OFF_GROUND_WARNING, false );
SetActorFlag( ACTOR_FLAG_IGNORE_WATER, false );
SetActorFlag( ACTOR_FLAG_NEVER_IGNORE_SOUNDS, false );
SetActorFlag( ACTOR_FLAG_SIMPLE_PATHFINDING, false );
SetActorFlag( ACTOR_FLAG_NO_PAIN_SOUNDS, false );
SetActorFlag( ACTOR_FLAG_UPDATE_BOSS_HEALTH, false );
SetActorFlag( ACTOR_FLAG_IGNORE_PAIN_FROM_ACTORS, false );
SetActorFlag( ACTOR_FLAG_STARTED, false );
SetActorFlag( ACTOR_FLAG_DEATHGIB, false );
SetActorFlag( ACTOR_FLAG_WEAPON_READY, false );
SetActorFlag( ACTOR_FLAG_DISABLED, false );
SetActorFlag( ACTOR_FLAG_CRIPPLED, false );
SetActorFlag( ACTOR_FLAG_IN_ALCOVE, false );
SetActorFlag( ACTOR_FLAG_IN_CONE_OF_FIRE, false );
SetActorFlag( ACTOR_FLAG_IN_PLAYER_CONE_OF_FIRE, false );
SetActorFlag( ACTOR_FLAG_PLAYER_IN_CALL_VOLUME, false );
SetActorFlag( ACTOR_FLAG_IN_CALL_VOLUME, false );
SetActorFlag( ACTOR_FLAG_OUT_OF_TORSO_RANGE, false );
SetActorFlag( ACTOR_FLAG_DUCKED, false );
SetActorFlag( ACTOR_FLAG_PRONE, false );
SetActorFlag( ACTOR_FLAG_RETREATING, false );
SetActorFlag( ACTOR_FLAG_HIDDEN, false );
SetActorFlag( ACTOR_FLAG_FOLLOWING_IN_FORMATION, false );
SetActorFlag( ACTOR_FLAG_DISPLAYING_FAILURE_FX, false );
SetActorFlag( ACTOR_FLAG_GROUPMEMBER_INJURED, false );
SetActorFlag( ACTOR_FLAG_CAN_HEAL_OTHER, false );
SetActorFlag( ACTOR_FLAG_STRICTLY_FOLLOW_PATHS, false );
SetActorFlag( ACTOR_FLAG_ATTACKING_ENEMY, false );
SetActorFlag( ACTOR_FLAG_UPDATE_HATE_WITH_ATTACKERS, false );
SetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER, false );
SetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER_NOFOV, false );
SetActorFlag( ACTOR_FLAG_PLAYING_DIALOG_ANIM, false );
SetActorFlag( ACTOR_FLAG_USING_HUD, false );
SetActorFlag( ACTOR_FLAG_FORCE_LIFEBAR, false );
SetActorFlag( ACTOR_FLAG_UPDATE_ACTION_LEVEL, true );
SetActorFlag( ACTOR_FLAG_CAN_CHANGE_ANIM, true );
SetActorFlag( ACTOR_FLAG_USE_FOLLOWRANGE_FOR_NODES, false );
SetActorFlag( ACTOR_FLAG_IMMEDIATE_ACTIVATE, false );
SetActorFlag( ACTOR_FLAG_CANNOT_USE, false );
SetActorFlag( ACTOR_FLAG_CANNOT_FREEZE, false );
SetNotifyFlag( NOTIFY_FLAG_DAMAGED, false );
SetNotifyFlag( NOTIFY_FLAG_KILLED, false );
SetNotifyFlag( NOTIFY_FLAG_SPOTTED_ENEMY, false );
// Init Helper Classes
InitThinkStrategy();
InitGameComponent();
InitSensoryPerception();
InitEnemyManager();
InitStrategos();
InitPackageManager();
InitMovementSubsystem();
InitPersonality();
InitCombatSubsystem();
InitHeadWatcher();
InitPostureController();
// Call ShowModel
showModel();
//
// The following is here for reference
//
/*
ObjectProgram = new Program;
ObjectProgram->Load( "ai/test.scr" );
program2.Load("ai/test.scr");
CThread *gamescript = 0;
gamescript = Director.CreateThread( "obj_main" , ObjectProgram );
gamescript->DelayedStart( 0 );
*/
// Make sure our animdir and movedir are the same;
setAngles( angles );
movementSubsystem->setAnimDir( orientation[0] );
movementSubsystem->setMoveDir( orientation[0] );
if ( !LoadingSavegame )
PostEvent( EV_Actor_Start, EV_POSTSPAWN );
}
//
// Name: ~Actor()
// Parameters: None
// Description: Destructor
//
Actor::~Actor()
{
Actor *actor;
actor = this;
int i;
if ( groupcoordinator )
groupcoordinator->RemoveEntityFromGroup( this , GetGroupID() );
if ( GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) )
{
StopDialog();
}
if ( SleepList.ObjectInList( actor ) )
{
SleepList.RemoveObject( actor );
}
if ( ActiveList.ObjectInList( actor ) )
{
ActiveList.RemoveObject( actor );
}
if ( TeamMateList.ObjectInList( actor ) )
{
TeamMateList.RemoveObject( actor );
}
for( i=stateVarList.NumObjects(); i>0; i-- )
{
StateVar *var = stateVarList.ObjectAt( i );
delete var;
}
stateVarList.FreeObjectList();
for ( i = threadList.NumObjects(); i > 0; i-- )
{
threadlist_t *threadEntry = threadList.ObjectAt( i );
delete threadEntry;
}
threadList.FreeObjectList();
if ( behavior )
{
delete behavior;
behavior = NULL;
}
if ( headBehavior )
{
delete headBehavior;
headBehavior = NULL;
}
if ( eyeBehavior )
{
delete eyeBehavior;
eyeBehavior = NULL;
}
if ( torsoBehavior )
{
delete torsoBehavior;
torsoBehavior = NULL;
}
if ( thinkStrategy )
{
delete thinkStrategy;
thinkStrategy = NULL;
}
if ( gameComponent )
{
delete gameComponent;
gameComponent = NULL;
}
if ( sensoryPerception )
{
delete sensoryPerception;
sensoryPerception = NULL;
}
if ( strategos )
{
delete strategos;
strategos = NULL;
}
if ( enemyManager )
{
delete enemyManager;
enemyManager = NULL;
}
if ( packageManager )
{
delete packageManager;
packageManager = NULL;
}
if ( movementSubsystem )
{
delete movementSubsystem;
movementSubsystem = NULL;
}
if ( personality )
{
delete personality;
personality = NULL;
}
if ( combatSubsystem )
{
delete combatSubsystem;
combatSubsystem = NULL;
}
if ( headWatcher )
{
delete headWatcher;
headWatcher = NULL;
}
if ( postureController )
{
delete postureController;
postureController = NULL;
}
freeConditionals( conditionals );
freeConditionals( master_conditionals );
freeConditionals( fuzzy_conditionals );
FreeDialogList();
}
//===================================================================================
// Activity Level
//===================================================================================
//
// Name: Sleep()
// Parameters: Event *ev
// Description: Event Interface for Sleep()
//
void Actor::Sleep( Event *ev )
{
Sleep();
}
//
// Name: Sleep()
// Parameters: None
// Description: Puts the actor in a sleep state
//
void Actor::Sleep( void )
{
Actor *actor;
// inanimate actors don't target enemies
if ( actortype == IS_INANIMATE )
return;
actor = this;
if ( !SleepList.ObjectInList( actor ) )
SleepList.AddObject( actor );
if ( ActiveList.ObjectInList( actor ) )
ActiveList.RemoveObject( actor );
enemyManager->ClearCurrentEnemy();
turnThinkOff();
}
//
// Name: Wakeup
// Parameters: Event *ev
// Description: Event interface for Wakeup()
//
void Actor::Wakeup( Event *ev )
{
Wakeup();
}
//
// Name: Wakeup()
// Parameters: None
// Description: Puts the actor in an awake state
//
void Actor::Wakeup( void )
{
Actor *actor;
// See if already awake
if ( isThinkOn() && !LoadingSavegame )
return;
// inanimate actors don't target enemies
if ( actortype == IS_INANIMATE )
return;
actor = this;
if ( SleepList.ObjectInList( actor ) )
SleepList.RemoveObject( actor );
if ( !ActiveList.ObjectInList( actor ) )
ActiveList.AddObject( actor );
turnThinkOn();
// here the last_time_active must be updated!
// otherwise, a sleeping, script-controlled entity with no sensory stimulus will immediately
// be put back to sleep in TrySleep on the next frame!
last_time_active = level.time;
}
//
// Name: Start()
// Parameters: Event *ev
// Description: Handles some initialization of the Actor
//
void Actor::Start( Event *ev )
{
// Register with other parts of self if there are any
if ( target.length() > 0 )
PostEvent( EV_ActorRegisterSelf, FRAMETIME );
// add them to the active list (they will be removed by sleep).
//if ( !ActiveList.ObjectInList( ( Actor * )this ) )
// ActiveList.AddObject( ( Actor * )this );
// Align our movement and anim dirs
Vector animDir;
angles.AngleVectors( &animDir );
movementSubsystem->setMoveDir( animDir );
movementSubsystem->setAnimDir( animDir );
_dropActorToGround();
//if ( (!behavior || ( currentBehavior == "Idle" )) && max_inactive_time > 0.0f )
// Sleep();
//else
// Wakeup();
//Let's go ahead and wakeup,
//but let's try to fall asleep immediately after so we don't burn a lot of
//cpu waiting for our inactivetime to kick in
Wakeup();
enemyManager->TrySleep();
SetActorFlag( ACTOR_FLAG_STARTED, true );
// Make sure the actor is in the idle animation of it has no state machine
if ( !statemap )
SetAnim( "idle" );
// Override starting health if we're using the GameplayManager
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
GameplayFormulaData fd(this);
if ( gpm->hasObject(getArchetype()) && gpm->hasFormula("Health"))
health = gpm->calculate("Health", fd);
}
//===================================================================================
// Stimuli Control
//===================================================================================
void Actor::RespondTo( Event *ev )
{
str stimuli = ev->GetString( 1 );
qboolean respond = ev->GetBoolean( 2 );
sensoryPerception->RespondTo( stimuli , respond );
}
void Actor::PermanentlyRespondTo( Event *ev )
{
str stimuli = ev->GetString( 1 );
qboolean respond = ev->GetBoolean( 2 );
sensoryPerception->PermanentlyRespondTo( stimuli , respond );
}
//===================================================================================
// Event Handlers -- Actions
// -- These Events are Primarily called from state machines, inside entry or
// exit commands. The Events in this section are general, and not really
// directly related to any kind of overriding concept, like Combat or Behavior
// management.
//===================================================================================
//
// Name: TurnTowardsEnemy()
// Parameters: Event *ev
// Description: Immediately turns the actor towards its enemy
//
void Actor::TurnTowardsEnemy( Event *ev )
{
float extraYaw;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy || (currentEnemy->flags & FL_NOTARGET ))
return;
//See if we want to add any additional yaw
if (ev->NumArgs() > 0 )
extraYaw = ev->GetInteger( 1 );
else
extraYaw = 0;
turnTowardsEntity( currentEnemy, extraYaw );
}
//
// Name: TurnTowardsPlayer()
// Parameters: Event *ev
// Description: Immediately turns the actor towards the player
//
void Actor::TurnTowardsPlayer( Event *ev )
{
float extraYaw;
Player* player;
player = GetPlayer(0);
// don't target while player is not in the game or he's in notarget
if( !player || ( player->flags & FL_NOTARGET ) )
return;
//See if we want to add any additional yaw
if (ev->NumArgs() > 0 )
extraYaw = ev->GetInteger( 1 );
else
extraYaw = 0;
turnTowardsEntity( player, extraYaw );
}
void Actor::TurnTowardsEntity( Event *ev )
{
/*
float extraYaw;
Entity *ent;
ent = ev->GetEntity( 1 );
if ( !ent )
return;
if (ev->NumArgs() > 1 )
extraYaw = ev->GetInteger( 2 );
else
extraYaw = 0;
turnTowardsEntity( ent, extraYaw );
*/
TurnTo *turnTo;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_TurnTo );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
turnTo = new TurnTo;
turnTo->SetTarget ( ev->GetEntity( 1 ) );
SetBehavior( turnTo, NULL, ev->GetThread() );
}
//
// Name: GoIdle()
// Parameters: Event *ev
// Description: Forces the Actor into Idle State and Idle Mode
//
void Actor::GoIdle ( Event *ev )
{
const char *state_name;
if ( !ModeAllowed( ACTOR_MODE_IDLE ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_Idle );
return;
}
if ( ev->NumArgs() > 0 )
state_name = ev->GetString( 1 );
else
state_name = idle_state_name;
SetState( state_name );
StartMode( ACTOR_MODE_IDLE );
}
//===================================================================================
// Event Handlers -- Script Commands
// -- These events are Primarily called from scripts, and are not often called
// from within a statemachine
//===================================================================================
//
// Name: LookAt
// Parameters: Event *ev
// Description: Sets the behavior to TurnTo
//
void Actor::LookAt( Event *ev )
{
Entity *ent;
TurnTo *turnTo;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_LookAt );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
ent = ev->GetEntity( 1 );
if ( ent && ( ent != world ) )
{
turnTo = new TurnTo;
turnTo->SetTarget( ent );
SetBehavior( turnTo, NULL, ev->GetThread() );
}
}
//
// Name: TurnToEvent
// Parameters: Event *ev
// Description: Turns the Actor in a specified direction
//
void Actor::TurnToEvent( Event *ev )
{
TurnTo *turnTo;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_TurnTo );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
turnTo = new TurnTo;
turnTo->SetDirection( ev->GetFloat( 1 ) );
if ( ev->NumArgs() > 1 )
turnTo->useAnims( ev->GetBoolean( 2 ) );
SetBehavior( turnTo, NULL, ev->GetThread() );
}
//
// Name: HeadAndEyeWatchEvent()
// Parameters: Event *ev
// Description: Sets the Actor to Head and Eye Watch the
// Specified Entity
//
void Actor::HeadAndEyeWatchEvent( Event *ev )
{
Event *event;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_HeadWatch );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
event = new Event( EV_Behavior_Args );
event->AddEntity( ev->GetEntity( 1 ) );
if ( ev->NumArgs() > 1 )
event->AddFloat( ev->GetFloat( 2 ) );
SetHeadBehavior( new HeadAndEyeWatch, event, ev->GetThread() );
}
//
// Name: HeadWatchEvent()
// Parameters: Event *ev
// Description: Sets the Actor to Head Watch the
// Specified Entity
//
void Actor::HeadWatchEvent ( Event *ev )
{
//Event *event;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_HeadWatch );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
/*event = new Event( EV_Behavior_Args );
event->AddEntity( ev->GetEntity( 1 ) );
if ( ev->NumArgs() > 1 )
event->AddFloat( ev->GetFloat( 2 ) );
*/
Entity* entToWatch;
entToWatch = ev->GetEntity( 1 );
if ( !entToWatch ) return;
headWatcher->SetWatchTarget( entToWatch );
if ( ev->NumArgs() > 1 )
headWatcher->SetWatchSpeed( ev->GetFloat(2) );
//SetHeadBehavior( new HeadWatch, event, ev->GetThread() );
}
//
// Name: ResetHeadEvent
// Parameters: Event *ev
// Description: Resets the head
//
void Actor::ResetHeadEvent ( Event *ev )
{
//Event *event;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_ResetHead );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
/*
event = new Event( EV_Behavior_Args );
event->AddEntity( NULL );
if ( ev->NumArgs() > 0 )
event->AddFloat( ev->GetFloat( 1 ) );
*/
headWatcher->SetWatchTarget( NULL );
//SetHeadBehavior( new HeadWatch, event, ev->GetThread() );
}
//
// Name: EyeWatchEvent()
// Parameters: Event *ev
// Description: Sets the actor to Eye Watch
// The Specified Entity
//
void Actor::EyeWatchEvent ( Event *ev )
{
Event *event;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_EyeWatch );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
event = new Event( EV_Behavior_Args );
event->AddEntity( ev->GetEntity( 1 ) );
if ( ev->NumArgs() > 1 )
event->AddFloat( ev->GetFloat( 2 ) );
SetEyeBehavior( new EyeWatch, event, ev->GetThread() );
}
//
// Name: ResetEyeEvent()
// Parameters: Event *ev
// Description: Resets the eyes
//
void Actor::ResetEyeEvent( Event *ev )
{
Event *event;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_ResetEye );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
event = new Event( EV_Behavior_Args );
event->AddEntity( NULL );
if ( ev->NumArgs() > 0 )
event->AddFloat( ev->GetFloat( 1 ) );
SetEyeBehavior( new EyeWatch, event, ev->GetThread() );
}
//
// Name: ResetTorsoEvent()
// Parameters: Event *ev
// Description: Resets the torso
//
void Actor::ResetTorsoEvent( Event *ev )
{
/*
Event *event;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_ResetTorso );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
event = new Event( EV_Behavior_Args );
event->AddEntity( NULL );
event->AddInteger( 0 );
event->AddInteger( 30 );
event->AddInteger( 0 );
SetEyeBehavior( new TorsoTurn, event, ev->GetThread() );
*/
SetControllerTag( ACTOR_TORSO_TAG, gi.Tag_NumForName( edict->s.modelindex, "Bip01 Spine1" ) );
SetControllerAngles( ACTOR_TORSO_TAG, vec_zero );
}
//
// Name: FallToDeathEvent()
// Parameters: Event *ev
// Description: Allows the Actor to play a sequence of animations
// So that it will fall from a position and die on
// impact
//
void Actor::FallToDeathEvent( Event *ev )
{
Event *e;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_FallToDeath );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
e->AddFloat( ev->GetFloat( 1 ) );
e->AddFloat( ev->GetFloat( 2 ) );
e->AddFloat( ev->GetFloat( 3 ) );
e->AddString( ev->GetString( 4 ) );
e->AddString( ev->GetString( 5 ) );
e->AddString( ev->GetString( 6 ) );
if (ev->NumArgs() > 6 )
e->AddFloat( ev->GetFloat( 7 ) );
SetBehavior( new FallToDeath, e, ev->GetThread() );
}
//
// Name: WalkTo()
// Parameters: Event *ev
// Description: Makes an Actor WalkTo a location
//
void Actor::WalkTo ( Event *ev )
{
Event *e;
//HelperNode *node;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_WalkTo );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
if ( ev->NumArgs() > 1 )
e->AddString( ev->GetString( 2 ) );
else
e->AddString( "walk" );
if ( ev->NumArgs() > 0 )
{
//If we have a helper node, let's use its origin
//node = HelperNode::FindClosestHelperNode( *this , ev->GetString( 1 ) );
//if ( node )
// e->AddVector( node->origin );
//else
e->AddToken( ev->GetToken( 1 ) );
}
if ( ev->NumArgs() > 2 )
{
e->AddInteger( 0 );
e->AddInteger( ev->GetInteger( 3 ) );
}
if ( ev->NumArgs() > 3 )
{
e->AddInteger( ev->GetInteger( 4 ) );
}
//SetBehavior( new GotoPathNode, e, ev->GetThread() );
SetBehavior( new GotoSpecified, e, ev->GetThread() );
}
//
// Name: BlindlyFollowPath()
// Parameters: Event *ev
// Description: Makes an Actor blindly follow a helper node path
//
void Actor::BlindlyFollowPath ( Event *ev )
{
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
{
RepostEvent( ev, EV_Actor_BlindlyFollowPath );
}
else if ( mode == ACTOR_MODE_AI )
{
SendMoveDone( ev->GetThread() );
}
return;
}
StartMode( ACTOR_MODE_SCRIPT );
Event *e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
if ( ev->NumArgs() > 0 )
{
e->AddString( ev->GetString( 1 ) );
}
else
{
e->AddString( "walk" );
}
if ( ev->NumArgs() > 1 )
{
e->AddFloat( ev->GetFloat( 2 ) );
}
if ( ev->NumArgs() > 2 )
{
e->AddString( ev->GetString( 3 ) );
}
SetBehavior( new FollowPathBlindly(), e, ev->GetThread() );
}
//
// Name: FollowWayPoints()
// Parameters: Event *ev
// Description: Makes an Actor Follow a Waypoint Chain
//
void Actor::FollowWayPoints ( Event *ev )
{
Event *e;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_WalkTo );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
//PathName
e->AddString( ev->GetString( 1 ) );
//Animation
if ( ev->NumArgs() > 1 )
e->AddString( ev->GetString( 2 ) );
else
e->AddString( "walk" );
//StartPoint
if ( ev->NumArgs() > 2 )
e->AddString( ev->GetString( 3 ) );
SetBehavior( new GotoWayPoint, e, ev->GetThread() );
}
//
// Name: WalkWatch
// Parameters: Event *ev
// Description: Makes an Actor walk to a specified location while
// watching a Specified Entity
//
void Actor::WalkWatch ( Event *ev )
{
Event *e;
if ( ev->NumArgs() < 2 )
return;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_WalkWatch );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
// Get animation name
if ( ev->NumArgs() > 2 )
e->AddString( ev->GetString( 3 ) );
else
e->AddString( "walk" );
// Get the node to walk to
e->AddToken( ev->GetToken( 1 ) );
// Get the entity to watch
e->AddEntity( ev->GetEntity( 2 ) );
SetBehavior( new GotoPathNode, e, ev->GetThread() );
}
//
// Name: WarpTo()
// Parameters: Event *ev
// Description: Makes an actor warp to a specified location
//
void Actor::WarpTo ( Event *ev )
{
PathNode *goal_node;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_WarpTo );
return;
}
if ( ev->NumArgs() > 0 )
{
goal_node = thePathManager.FindNode( ev->GetString( 1 ) );
if ( goal_node )
{
origin = goal_node->origin;
setOrigin( origin );
angles = goal_node->angles;
setAngles( angles );
NoLerpThisFrame();
}
else
{
gi.WDPrintf( "Warpto failed : couldn't find goal node %s\n", ev->GetString( 1 ) );
}
}
}
//
// Name: PickupEnt()
// Parameters: Event *ev
// Description: Makes the Actor Pickup the specified entity
//
void Actor::PickupEnt( Event *ev )
{
Event *e;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_PickupEnt );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
e->AddEntity( ev->GetEntity( 1 ) );
e->AddString( ev->GetString( 2 ) );
SetBehavior( new PickupEntity, e, ev->GetThread() );
}
//
// Name: ThrowEnt()
// Parameters: Event *ev
// Description: Makes the actor throw the specified entity
//
void Actor::ThrowEnt( Event *ev )
{
Event *e;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
RepostEvent( ev, EV_Actor_ThrowEnt );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
e->AddString( ev->GetString( 1 ) );
SetBehavior( new ThrowEntity, e, ev->GetThread() );
}
//
// Name: AttackEntity()
// Parameters: Event *ev
// Description: Makes the actor attack the specified entity
//
void Actor::AttackEntity( Event *ev )
{
Entity *target = ev->GetEntity( 1 );
bool forceEnemy = true;
if ( !GetActorFlag( ACTOR_FLAG_STARTED ) )
{
PostEvent( *ev, FRAMETIME );
return;
}
if ( ev->NumArgs() > 1 )
forceEnemy = ev->GetBoolean( 2 );
if ( forceEnemy )
forcedEnemy = target;
if ( forceEnemy )
{
enemyManager->SetCurrentEnemy( target );
enemyManager->LockOnCurrentEnemy( true );
return;
}
sensoryPerception->Stimuli( STIMULI_SIGHT, target );
if ( enemyManager->IsInHateList( target ) )
{
enemyManager->SetCurrentEnemy( target );
}
}
//
// Name: AttackPlayer()
// Parameters: Event *ev
// Description: Makes the Actor attack the Player
//
void Actor::AttackPlayer( Event *ev )
{
if ( !GetActorFlag( ACTOR_FLAG_STARTED ) )
{
PostEvent( *ev, FRAMETIME );
return;
}
int i;
gentity_t *ent;
for( i = 0; i < maxclients->integer; i++ )
{
ent = &g_entities[ i ];
if ( !ent->inuse || !ent->client || !ent->entity )
{
continue;
}
strategos->Attack( ent->entity );
}
}
//
// Name: JumpToEvent()
// Parameters: Event *ev
// Description: Makes the Actor Jump to a specified location
//
void Actor::JumpToEvent( Event *ev )
{
Event *e;
int i;
int n;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
PostEvent( *ev, FRAMETIME );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
StartMode( ACTOR_MODE_SCRIPT );
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
n = ev->NumArgs();
for( i = 1; i <= n; i++ )
{
e->AddToken( ev->GetToken( i ) );
}
SetBehavior( new JumpToPathNode, e, ev->GetThread() );
}
//
// Name: RepostEvent()
// Parameters: Event *ev,
// Event &event_type
// Description: Reposts the passed in event
//
void Actor::RepostEvent( Event *ev, const Event &event_type )
{
Event *event;
int i;
str token;
event = new Event( event_type );
for( i = 1 ; i <= ev->NumArgs() ; i++ )
{
token = ev->GetString( i );
event->AddString( token.c_str() );
}
event->SetThread( ev->GetThread() );
PostEvent( event, FRAMETIME );
}
//===================================================================================
// Event Handlers -- Data Manipulation
// -- These Events are meant to set data the actor uses, such as TargetType or
// EyeHeight. These events are called from script, statemachines, and sometimes
// from code. Right now there are too many events in this section. Much like
// the Actions section above, the events here should be general and not part
// of an overriding concept. As I continue to refine, I will be working toward
// that.
//===================================================================================
//
// Name: SetMinimumMeleeHeight()
// Parameters: Event *ev
// Description: Sets minimum_melee_height
//
void Actor::SetMinimumMeleeHeight ( Event *ev )
{
minimum_melee_height = ev->GetFloat( 1 );
}
//
// Name: SetDamageAngles()
// Parameters: Event *ev
// Description: Sets damage_angles
//
void Actor::SetDamageAngles ( Event *ev )
{
damage_angles = ev->GetFloat( 1 );
}
//
// Name: SetImmortal()
// Parameters: Event *ev
// Description: Sets the ACTOR_FLAG_IMMORTAL
//
void Actor::SetImmortal ( Event *ev )
{
qboolean isImmortal = true;
if ( ev->NumArgs() > 0 )
isImmortal = ev->GetBoolean( 1 );
SetActorFlag( ACTOR_FLAG_IMMORTAL, isImmortal );
}
//
// Name: SetTargetType()
// Parameters: Event *ev
// Description: Sets targetType -- Needs to be removed
//
void Actor::SetTargetType ( Event *ev )
{
targetType = (targetType_t)ev->GetInteger( 1 );
}
//
// Name: SetEyeAngles()
// Parameters: Event *ev
// Description: Sets the eyeangles;
//
void Actor::SetEyeAngles ( Event *ev )
{
minEyeYawAngle = ev->GetFloat( 1 );
maxEyeYawAngle = ev->GetFloat( 2 );
minEyePitchAngle = ev->GetFloat( 3 );
maxEyePitchAngle = ev->GetFloat( 4 );
if ( minEyeYawAngle < -180.0f ) minEyeYawAngle = -180.0f;
if ( maxEyeYawAngle > 180.0f ) maxEyeYawAngle = 180.0f;
if ( minEyePitchAngle < -180.0f ) minEyePitchAngle = -180.0f;
if ( maxEyePitchAngle > 180.0f ) maxEyePitchAngle = 180.0f;
}
//
// Name: SetTakeDamage()
// Parameters: Event *ev
// Description: Sets the ACTOR_FLAG_TAKE_DAMAGE
//
void Actor::SetTakeDamage ( Event *ev )
{
qboolean damage = true;
if ( ev->NumArgs() > 0 )
damage = ev->GetBoolean( 1 );
SetActorFlag( ACTOR_FLAG_TAKE_DAMAGE, damage );
}
//
// Name: SetIdleStateName()
// Parameters: Event *ev
// Description: Sets the idle_state_name
//
void Actor::SetIdleStateName ( Event *ev )
{
idle_state_name = ev->GetString( 1 );
}
//
// Name: SetActivateThread()
// Parameters: Event *ev
// Description: Sets the activate_thread
//
void Actor::SetActivateThread ( Event *ev )
{
activate_thread = ev->GetString( 1 );
}
//
// Name: SetValidTarget()
// Parameters: Event *ev
// Description: Sets validTarget Boolean -- Needs to go away, I believe
// We should find a better way to do this
//
void Actor::SetValidTarget ( Event *ev )
{
Event *flagEvent;
qboolean valid;
valid = ev->GetBoolean( 1 );
flagEvent = new Event(EV_EntityFlags);
if( valid )
flagEvent->AddString( "-notarget" );
else
flagEvent->AddString( "+notarget" );
ProcessEvent(flagEvent);
}
//
// Name: SetAlertThread()
// Parameters: Event *ev
// Description: Sets the alert_thread
//
void Actor::SetAlertThread ( Event *ev )
{
alert_thread = ev->GetString( 1 );
}
//
// Name: SetEnemyType()
// Parameters: Event *ev
// Description: Sets our enemytype
//
void Actor::SetEnemyType ( Event *ev )
{
enemytype = ev->GetString( 1 );
}
//
// Name: SetWeaponReady()
// Parameters: Event *ev
// Description: Sets the ACTOR_FLAG_WEAPON_READY
//
void Actor::SetWeaponReady ( Event *ev )
{
SetActorFlag( ACTOR_FLAG_WEAPON_READY , ev->GetBoolean( 1 ) );
}
//===================================================================================
// StateMachine And FuzzyEngine Functions
// -- These Functions deal with managing the Actor's State Machine
// and Fuzzy Engine
//===================================================================================
//
// Name: LoadStateMap()
// Parameters: Event *ev
// Description: Loads a StateMap into memory
//
void Actor::LoadStateMap( Event *ev )
{
str anim_name;
qboolean loading;
qboolean packageChange;
// Load the new state map
statemap_name = ev->GetString( 1 );
freeConditionals( conditionals );
//conditionals.FreeObjectList();
statemap = GetStatemap( statemap_name, ( Condition<Class> * )Conditions, &conditionals, false );
// Check if we're changing behavior packages
packageChange = false;
if ( ev->NumArgs() > 3 )
{
packageChange = ev->GetBoolean( 4 );
}
// Set the first state
if ( ev->NumArgs() > 1 )
idle_state_name = ev->GetString( 2 );
else if ( fuzzyEngine )
idle_state_name = "START";
else
idle_state_name = "IDLE";
if ( ev->NumArgs() > 2 )
loading = ev->GetBoolean( 3 );
else
loading = false;
// Initialize the actors first animation
if ( !loading )
{
SetState( idle_state_name.c_str() );
SetGlobalState(global_state_name.c_str() );
if ( currentState )
anim_name = currentState->getLegAnim( *this, &conditionals );
if ( anim_name == "" && !newanim.length() )
anim_name = "idle";
SetAnim( anim_name.c_str(), EV_Anim_Done );
ChangeAnim();
}
}
//--------------------------------------------------------------
// Name: LoadMasterStateMap()
// Class: Actor
//
// Description: Loads the Master State Map for the Actor
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::LoadMasterStateMap( Event *ev )
{
qboolean loading;
// Load the new state map
masterstatemap_name = ev->GetString( 1 );
freeConditionals( master_conditionals );
masterstatemap = GetStatemap( masterstatemap_name, ( Condition<Class> * )Conditions, &master_conditionals, false );
// Set the first state
master_idle_state_name = "START";
if ( ev->NumArgs() > 2 )
loading = ev->GetBoolean( 3 );
else
loading = false;
// Initialize the actors first animation
if ( !loading )
{
SetMasterState( master_idle_state_name.c_str() );
}
}
//
// Name: SetGlobalState()
// Parameters: const char *state_name
// Description: Sets the Current Global State to state_name
//
void Actor::SetGlobalState( const char *state_name )
{
if ( !statemap )
return;
if ( deadflag )
return;
if ( globalState )
globalState->ProcessExitCommands( this );
globalState = statemap->FindGlobalState( state_name );
}
//
// Name: SetState()
// Parameters: const char *state_name
// Description: Sets the Current State to state_name
//
void Actor::SetState( const char *state_name )
{
ClassDef *cls;
int i;
Event *e;
if ( !statemap )
return;
if ( deadflag )
return;
if ( currentState )
currentState->ProcessExitCommands( this );
currentState = statemap->FindState( state_name );
state_time = level.time;
// Set the behavior
if ( currentState )
{
cls = getClass( currentState->getBehaviorName() );
if ( cls )
{
if ( currentState->numBehaviorParms() )
{
e = new Event( EV_Behavior_Args );
for ( i = 1 ; i <= currentState->numBehaviorParms() ; i++ )
e->AddString( currentState->getBehaviorParm( i ) );
SetBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetBehavior( ( Behavior * )cls->newInstance() );
}
}
cls = getClass( currentState->getHeadBehaviorName() );
if ( cls )
{
if ( currentState->numHeadBehaviorParms() )
{
e = new Event( EV_Behavior_Args );
for ( i = 1; i <= currentState->numHeadBehaviorParms() ; i++ )
e->AddString( currentState->getHeadBehaviorParm( i ) );
SetHeadBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetHeadBehavior( ( Behavior * )cls->newInstance() );
}
}
cls = getClass( currentState->getEyeBehaviorName() );
if ( cls )
{
if ( currentState->numEyeBehaviorParms() )
{
e = new Event ( EV_Behavior_Args );
for ( i = 1; i <= currentState->numEyeBehaviorParms() ; i++ )
e->AddString( currentState->getEyeBehaviorParm( i ) );
SetEyeBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetEyeBehavior( ( Behavior * )cls->newInstance() );
}
}
cls = getClass( currentState->getTorsoBehaviorName() );
if ( cls )
{
if ( currentState->numTorsoBehaviorParms() )
{
e = new Event ( EV_Behavior_Args );
for ( i = 1; i <= currentState->numTorsoBehaviorParms(); i++ )
e->AddString( currentState->getTorsoBehaviorParm( i ) );
SetTorsoBehavior( (Behavior *)cls->newInstance(), e );
}
else
{
SetTorsoBehavior( (Behavior *)cls->newInstance() );
}
}
InitState();
currentState->ProcessEntryCommands( this );
}
else
{
gi.WDPrintf( "State %s not found\n", state_name );
}
}
void Actor::resetStateMachine( void )
{
SetState( idle_state_name );
}
void Actor::SetMasterState( Event *ev )
{
SetMasterState(ev->GetString( 1 ) );
}
//--------------------------------------------------------------
// Name: SetMasterState()
// Class: Actor
//
// Description: Sets the Current Master State to state_name
//
// Parameters: const str &state_name
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetMasterState( const str &state_name )
{
if ( !masterstatemap )
return;
if ( deadflag )
return;
if ( currentMasterState )
currentMasterState->ProcessExitCommands( this );
currentMasterState = masterstatemap->FindState( state_name );
masterstate_time = level.time;
// Masterstates don't have behaviors -- They are for transitioning
// the regular statemaps that DO have behaviors.
// Set the behavior
if ( currentMasterState )
{
InitMasterState();
currentMasterState->ProcessEntryCommands( this );
}
else
{
gi.WDPrintf( "Master State %s not found\n", state_name.c_str() );
}
}
//
// Name: InitState()
// Parameters: None
// Description: Initializes a state
//
void Actor::InitState( void )
{
float min_time;
float max_time;
if ( !currentState )
return;
min_time = currentState->getMinTime();
max_time = currentState->getMaxTime();
if ( ( min_time != -1.0f ) && ( max_time != -1.0f ) )
{
SetActorFlag( ACTOR_FLAG_STATE_DONE_TIME_VALID, true );
state_done_time = level.time + min_time + G_Random( max_time - min_time );
}
else
{
SetActorFlag( ACTOR_FLAG_STATE_DONE_TIME_VALID, false );
}
state_time = level.time;
times_done = 0;
ClearStateFlags();
command = "";
SetActorFlag( ACTOR_FLAG_ANIM_DONE, false );
SetActorFlag( ACTOR_FLAG_NOISE_HEARD, false );
}
//--------------------------------------------------------------
// Name: InitMasterState()
// Class: Actor
//
// Description: Initializes a master state
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::InitMasterState( void )
{
float min_time;
float max_time;
if ( !currentMasterState )
return;
min_time = currentMasterState->getMinTime();
max_time = currentMasterState->getMaxTime();
if ( ( min_time != -1.0f ) && ( max_time != -1.0f ) )
{
SetActorFlag( ACTOR_FLAG_MASTER_STATE_DONE_TIME_VALID, true );
masterstate_done_time = level.time + min_time + G_Random( max_time - min_time );
}
else
{
SetActorFlag( ACTOR_FLAG_MASTER_STATE_DONE_TIME_VALID, false );
}
masterstate_time = level.time;
masterstate_times_done = 0;
}
//
// Name: RegisterBehaviorPackage()
// Parameters: Event *ev
// Description: Registers a BehaviorPackage with the packageManager
//
void Actor::RegisterBehaviorPackage( Event *ev )
{
packageManager->RegisterPackage(ev->GetString( 1 ) );
}
//
// Name: UnRegisterBehaviorPackage()
// Parameters: Event *ev
// Description: UnRegisters a BehaviorPackage with the packageManager
//
void Actor::UnRegisterBehaviorPackage( Event *ev )
{
packageManager->UnregisterPackage(ev->GetString( 1 ) );
}
void Actor::SetPackageTendency( Event *ev )
{
str packageName = ev->GetString( 1 );
float tendency = ev->GetFloat( 2 );
personality->SetBehaviorTendency( packageName, tendency );
}
//
// Name: LoadFuzzyEngine()
// Parameters: Event *ev
// Description: Loads a Fuzzy Engine into memory
//
void Actor::LoadFuzzyEngine( Event *ev )
{
// Load the new fuzzy engine
fuzzyengine_name = ev->GetString( 1 );
//fuzzy_conditionals.FreeObjectList();
freeConditionals( fuzzy_conditionals );
fuzzyEngine = GetFuzzyEngine( fuzzyengine_name, ( Condition<Class> * )Conditions, &fuzzy_conditionals, false );
fuzzyEngine_active = true;
}
//--------------------------------------------------------------
// Name: ProcessActorStateMachine()
// Class: Actor
//
// Description: Evaluates our state map file
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::ProcessActorStateMachine( void )
{
int count;
str behavior_arg;
str behavior_name;
str headBehavior_name;
str eyeBehavior_name;
str torsoBehavior_name;
str currentTorsoAnim;
str currentanim;
str stateLegAnim;
str stateTorsoAnim;
ClassDef *cls = NULL;
State *laststate = NULL;
stateLegAnim = animname;
stateTorsoAnim = TorsoAnimName;
count = 0;
if ( deadflag || !currentState )
return;
if ( ( mode != ACTOR_MODE_AI ) && ( mode != ACTOR_MODE_IDLE ) )
return;
do
{
// Since we could get into an infinite loop here, do a check to make sure we don't.
count++;
if ( count > 10 )
{
gi.WDPrintf( "Possible infinite loop in state '%s'\n", currentState->getName() );
if ( count > 50 )
{
gi.Error( ERR_DROP, "Stopping due to possible infinite state loop\n" );
break;
}
}
// Determine the next state to go to
laststate = currentState;
//Attempt to evaluate our global state.
if ( !currentState->IgnoreGlobalStates() && globalState )
{
currentState = globalState->Evaluate( *this , &conditionals );
//If, after the evaluation, our state is still the global state,
//that means nothing in the global state required a transition,
//so we can safely evaluate our main state
if ( currentState == globalState )
{
currentState = laststate;
}
else
{
ClearStateFlags();
// Process exit commands of the last state
laststate->ProcessExitCommands( this );
// Process entry commands of the new state
currentState->ProcessEntryCommands( this );
}
}
//Evaluate our current state
currentState = currentState->Evaluate( *this, &conditionals );
if ( !currentState )
return;
// Change the behavior if the state has changed
if ( laststate != currentState )
{
// Clear our _checkedChance flag
_checkedChance = false;
// Process exit commands of the last state
laststate->ProcessExitCommands( this );
lastState = laststate;
// Process entry commands of the new state
currentState->ProcessEntryCommands( this );
// End Old Behaviors
if ( behavior )
{
EndBehavior();
//behavior->End(*this);
//behavior = NULL;
}
if ( torsoBehavior )
{
EndTorsoBehavior();
//torsoBehavior->End(*this);
//torsoBehavior = NULL;
}
// Setup the new behavior
behavior_name = currentState->getBehaviorName();
// Check if our behavior is set up in the GPD
if ( !behavior_name.length() )
{
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
str stateName = currentState->getName();
str objname = getArchetype();
stateName = objname + "." + stateName;
if ( gpm->hasObject( stateName ) )
{
if ( gpm->hasProperty( stateName , "behavior" ) )
{
behavior_name = gpm->getStringValue( stateName , "behavior" );
}
}
}
if ( behavior_name.length() )
{
cls = getClass( currentState->getBehaviorName() );
}
//else if ( behavior )
// {
// behavior->End(*this);
// behavior = NULL;
// }
if ( cls )
{
if ( currentState->numBehaviorParms() )
{
int i;
Event *e = new Event( EV_Behavior_Args );
for ( i = 1 ; i <= currentState->numBehaviorParms() ; i++ )
e->AddString( currentState->getBehaviorParm( i ) );
SetBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetBehavior( ( Behavior * )cls->newInstance() );
}
}
else if ( behavior_name.length() )
{
gi.WDPrintf( "Invalid behavior name %s\n", behavior_name.c_str() );
}
// Setup the new torso behavior
torsoBehavior_name = currentState->getTorsoBehaviorName();
cls = 0;
if ( torsoBehavior_name.length() )
cls = getClass( currentState->getTorsoBehaviorName() );
//else if ( torsoBehavior )
// {
// torsoBehavior->End(*this);
// torsoBehavior = NULL;
// }
if ( cls )
{
if ( currentState->numTorsoBehaviorParms() )
{
int i;
Event *e = new Event( EV_Behavior_Args );
for ( i = 1 ; i <= currentState->numTorsoBehaviorParms() ; i++ )
e->AddString( currentState->getTorsoBehaviorParm( i ) );
SetTorsoBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetTorsoBehavior( ( Behavior * )cls->newInstance() );
}
}
else if ( torsoBehavior_name.length() )
{
gi.WDPrintf( "Invalid torso behavior name %s\n", torsoBehavior_name.c_str() );
}
// Initialize some stuff for changing states
InitState();
}
/*
// Setup the new head behavior
headBehavior_name = currentState->getHeadBehaviorName();
cls = 0;
if ( headBehavior_name.length() )
cls = getClass( currentState->getHeadBehaviorName() );
else if ( headBehavior )
{
headBehavior->End(*this);
headBehavior = NULL;
}
if ( cls )
{
if ( currentState->numHeadBehaviorParms() )
{
int i;
Event *e = new Event( EV_Behavior_Args );
for ( i = 1 ; i <= currentState->numHeadBehaviorParms() ; i++ )
e->AddString( currentState->getHeadBehaviorParm( i ) );
SetHeadBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetHeadBehavior( ( Behavior * )cls->newInstance() );
}
}
else if ( headBehavior_name.length() )
{
gi.WDPrintf( "Invalid head behavior name %s\n", headBehavior_name.c_str() );
}
// Setup the new eye behavior
eyeBehavior_name = currentState->getEyeBehaviorName();
cls = 0;
if ( eyeBehavior_name.length() )
cls = getClass( currentState->getEyeBehaviorName() );
else if ( eyeBehavior )
{
eyeBehavior->End(*this);
eyeBehavior = NULL;
}
if ( cls )
{
if ( currentState->numEyeBehaviorParms() )
{
int i;
Event *e = new Event( EV_Behavior_Args );
for ( i = 1 ; i <= currentState->numEyeBehaviorParms() ; i++ )
e->AddString( currentState->getEyeBehaviorParm( i ) );
SetEyeBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetEyeBehavior( ( Behavior * )cls->newInstance() );
}
}
else if ( eyeBehavior_name.length() )
{
gi.WDPrintf( "Invalid eye behavior name %s\n", eyeBehavior_name.c_str() );
}
// Setup the new torso behavior
torsoBehavior_name = currentState->getTorsoBehaviorName();
cls = 0;
if ( torsoBehavior_name.length() )
cls = getClass( currentState->getTorsoBehaviorName() );
else if ( torsoBehavior )
{
torsoBehavior->End(*this);
torsoBehavior = NULL;
}
if ( cls )
{
if ( currentState->numTorsoBehaviorParms() )
{
int i;
Event *e = new Event( EV_Behavior_Args );
for ( i = 1 ; i <= currentState->numTorsoBehaviorParms() ; i++ )
e->AddString( currentState->getTorsoBehaviorParm( i ) );
SetTorsoBehavior( ( Behavior * )cls->newInstance(), e );
}
else
{
SetTorsoBehavior( ( Behavior * )cls->newInstance() );
}
}
else if ( torsoBehavior_name.length() )
{
gi.WDPrintf( "Invalid torso behavior name %s\n", torsoBehavior_name.c_str() );
}
// Initialize some stuff for changing states
InitState();
}
*/
// See if we've SOMEHOW managed to get in a state with no behavior,
// yet we're still playing a behavior. WTF. Break out of the behavior
// immediately if this is the case.
behavior_name = currentState->getBehaviorName();
if ( !behavior_name.length() )
{
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
str stateName = currentState->getName();
str objname = getArchetype();
stateName = objname + "." + stateName;
if ( gpm->hasObject( stateName ) )
{
if ( gpm->hasProperty( stateName , "behavior" ) )
{
behavior_name = gpm->getStringValue( stateName , "behavior" );
}
}
}
if ( behavior && !behavior_name.length() )
{
EndBehavior();
//behavior->End(*this);
//behavior = NULL;
}
// Change the animation if it has changed
currentanim = currentState->getLegAnim( *this, &conditionals );
currentTorsoAnim = currentState->getTorsoAnim( *this, &conditionals );
// We need to store the stateAnimation in local variables because we won't actually change
// the animation until the next frame update. If we don't store them here, we'll reset them
// every time we loop -- I tried moving this section outside of the while loop, but that didn't
// work well either.
if ( deadflag ) return;
if ( !GetActorFlag(ACTOR_FLAG_PLAYING_DIALOG_ANIM) )
{
if ( currentanim.length() && ( strcmp( stateLegAnim , currentanim.c_str() ) != 0 ) )
{
SetAnim( currentanim, EV_Anim_Done, legs );
stateLegAnim = currentanim;
}
if ( currentTorsoAnim.length() && ( strcmp( stateTorsoAnim , currentTorsoAnim.c_str() ) != 0 ) )
{
SetAnim( currentTorsoAnim, EV_Torso_Anim_Done, torso );
stateTorsoAnim = currentTorsoAnim;
}
}
/*
if ( (laststate != currentState) && !currentTorsoAnim.length() )
{
TorsoAnimName = "";
animate->ClearTorsoAnim();
}
*/
if ( showStates != DEBUG_NONE && laststate != currentState )
_printDebugInfo(laststate->getName() , currentState->getName() , currentanim , currentTorsoAnim );
}
while( laststate != currentState );
}
//--------------------------------------------------------------
// Name: ProcessMasterStateMachine()
// Class: Actor
//
// Description: Processes our Master State Machine. This file
// is used to specify which state machine we will
// actually run. It CANNOT run behaviors or set
// animations... It is only in place to set which
// main state machine will run
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::ProcessMasterStateMachine( void )
{
int count;
State *laststate = NULL;
if ( !masterstatemap )
return;
if ( deadflag || !currentMasterState )
return;
if ( ( mode != ACTOR_MODE_AI ) && ( mode != ACTOR_MODE_IDLE ) )
return;
count = 0;
do
{
// Since we could get into an infinite loop here, do a check to make sure we don't.
count++;
if ( count > 10 )
{
gi.WDPrintf( "Possible infinite loop in Master State '%s'\n", currentMasterState->getName() );
if ( count > 50 )
{
gi.Error( ERR_DROP, "Stopping due to possible infinite state loop\n" );
break;
}
}
// Determine the next state to go to
laststate = currentMasterState;
currentMasterState = currentMasterState->Evaluate( *this, &master_conditionals );
if ( !currentMasterState )
return;
if ( laststate != currentMasterState )
{
laststate->ProcessExitCommands( this );
lastMasterState = laststate;
// Process entry commands of the new state
currentMasterState->ProcessEntryCommands( this );
InitMasterState();
}
} while( laststate != currentMasterState );
}
//===================================================================================
// Behavior Management Functions
// -- These Functions deal with managing the Actor's Behaviors
//===================================================================================
//
// Name: SendMoveDone()
// Parameters: CThread *script_thread
// Description: Notifies the script that the behavior is finished
//
void Actor::SendMoveDone( CThread *script_thread )
{
Event *event;
if ( script_thread )
{
event = new Event( EV_MoveDone );
event->AddEntity( this );
script_thread->PostEvent( event, FRAMETIME );
}
}
//
// Name: EndBehavior()
// Parameters: None
// Description: Ends the current behavior
//
void Actor::EndBehavior( void )
{
Event *event;
if ( behavior )
{
behavior->End( *this );
// If we are controlled, notify our controller of the behavior's ending.
if ( _controller && (_controller == behavior->GetController()) )
{
event = new Event ( EV_Actor_BehaviorFinished );
event->AddInteger( behaviorCode );
event->AddString( behaviorFailureReason );
_controller->ProcessEvent( event );
}
delete behavior;
behavior = NULL;
}
// Required so that script threads will get the waitfor notification
if ( scriptthread )
{
CThread *t = scriptthread;
scriptthread = NULL;
if ( t )
{
event = new Event( EV_MoveDone );
event->AddEntity( this );
t->ProcessEvent( event );
}
}
// Prevent previous behaviors from stopping the next behavior
CancelEventsOfType( EV_Actor_EndBehavior );
}
//
// Name: EndHeadBehavior()
// Parameters: None
// Description: Ends the current head behavior
//
void Actor::EndHeadBehavior( void )
{
if ( headBehavior )
{
headBehavior->End( *this );
delete headBehavior;
headBehavior = NULL;
}
// Prevent previous behaviors from stopping the next behavior
CancelEventsOfType( EV_Actor_EndHeadBehavior );
}
//
// Name: EndEyeBehavior()
// Parameters: None
// Description: Ends the current eye behavior
//
void Actor::EndEyeBehavior( void )
{
if ( eyeBehavior )
{
eyeBehavior->End( *this );
delete eyeBehavior;
eyeBehavior = NULL;
}
// Prevent previous behaviors from stopping the next behavior
CancelEventsOfType( EV_Actor_EndEyeBehavior );
}
//
// Name: EndTorsoBehavior()
// Parameters: None
// Description: Ends the current torso behavior
//
void Actor::EndTorsoBehavior( void )
{
if ( torsoBehavior )
{
torsoBehavior->End( *this );
delete torsoBehavior;
torsoBehavior = NULL;
}
// Prevent previous behaviors from stopping the next behavior
CancelEventsOfType( EV_Actor_EndTorsoBehavior );
}
//
// Name: EndAllBehaviors()
// Parameters: None
// Description: Ends all current behaviors
//
void Actor::EndAllBehaviors( void )
{
EndBehavior();
EndHeadBehavior();
EndEyeBehavior();
EndTorsoBehavior();
}
//
// Name: SetBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current behavior
//
void Actor::SetBehavior ( Behavior *newbehavior, Event *startevent, CThread *newthread )
{
if ( ( deadflag ) && ( actortype != IS_INANIMATE ) )
{
// Delete the unused stuff
if ( newbehavior )
delete newbehavior;
if ( startevent )
delete startevent;
return;
}
// End any previous behavior, but don't call EV_MoveDone if we're using the same thread,
// or we'll end THIS behavior
if ( scriptthread == newthread )
{
scriptthread = NULL;
}
EndBehavior();
behavior = newbehavior;
behaviorCode = BEHAVIOR_EVALUATING;
if ( behavior )
{
Wakeup();
if ( startevent )
{
behavior->ProcessEvent( startevent );
}
currentBehavior = behavior->getClassname();
if ( _controller ) behavior->SetController( _controller );
behavior->SetSelf( this );
behavior->Begin( *this );
scriptthread = newthread;
}
}
//
// Name: SetHeadBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current head behavior
//
void Actor::SetHeadBehavior( Behavior *newHeadBehavior, Event *startevent, CThread *newthread )
{
if ( ( deadflag ) && ( actortype != IS_INANIMATE ) )
{
// Delete the unused stuff
if ( newHeadBehavior )
delete newHeadBehavior;
if ( startevent )
delete startevent;
return;
}
// End any previous behavior, but don't call EV_MoveDone if we're using the same thread,
// or we'll end THIS behavior
if ( scriptthread == newthread )
{
scriptthread = NULL;
}
if ( headBehavior )
{
currentHeadBehavior = headBehavior->getClassname();
str newHeadBehaviorName = newHeadBehavior->getClassname();
if ( currentHeadBehavior == newHeadBehaviorName )
{
if ( startevent )
{
headBehavior->ProcessEvent( startevent );
}
return;
}
}
EndHeadBehavior();
headBehavior = newHeadBehavior;
if ( headBehavior )
{
Wakeup();
if ( startevent )
{
headBehavior->ProcessEvent( startevent );
}
currentHeadBehavior = headBehavior->getClassname();
headBehavior->Begin( *this );
scriptthread = newthread;
}
}
//
// Name: SetEyeBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current eye behavior
//
void Actor::SetEyeBehavior ( Behavior *newEyeBehavior, Event *startevent, CThread *newthread )
{
if ( ( deadflag ) && ( actortype != IS_INANIMATE ) )
{
// Delete the unused stuff
if ( newEyeBehavior )
delete newEyeBehavior;
if ( startevent )
delete startevent;
return;
}
// End any previous behavior, but don't call EV_MoveDone if we're using the same thread,
// or we'll end THIS behavior
if ( scriptthread == newthread )
{
scriptthread = NULL;
}
EndEyeBehavior();
eyeBehavior = newEyeBehavior;
if ( eyeBehavior )
{
Wakeup();
if ( startevent )
{
eyeBehavior->ProcessEvent( startevent );
}
currentEyeBehavior = eyeBehavior->getClassname();
eyeBehavior->Begin( *this );
scriptthread = newthread;
}
}
//
// Name: SetTorsoBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current torso behavior
//
void Actor::SetTorsoBehavior( Behavior *newTorsoBehavior, Event *startevent, CThread *newthread )
{
if ( ( deadflag ) && ( actortype != IS_INANIMATE ) )
{
// Delete the unused stuff
if ( newTorsoBehavior )
delete newTorsoBehavior;
if ( startevent )
delete startevent;
return;
}
// End any previous behavior, but don't call EV_MoveDone if we're using the same thread,
// or we'll end THIS behavior
if ( scriptthread == newthread )
{
scriptthread = NULL;
}
EndTorsoBehavior();
torsoBehavior = newTorsoBehavior;
torsoBehaviorCode = BEHAVIOR_EVALUATING;
if ( torsoBehavior )
{
//turnThinkOn();
Wakeup();
if ( startevent )
{
torsoBehavior->ProcessEvent( startevent );
}
currentTorsoBehavior = torsoBehavior->getClassname();
torsoBehavior->Begin( *this );
scriptthread = newthread;
}
}
//
// Name: EndBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndBehavior
//
void Actor::EndBehaviorEvent( Event *ev )
{
EndBehavior();
}
//
// Name: EndHeadBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndHeadBehavior
//
void Actor::EndHeadBehaviorEvent( Event *ev )
{
EndHeadBehavior();
}
//
// Name: EndEyeBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndEyeBehavior
//
void Actor::EndEyeBehaviorEvent( Event *ev )
{
EndEyeBehavior();
}
//
// Name: EndTorsoBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndTorsoBehavior
//
void Actor::EndTorsoBehaviorEvent( Event *ev )
{
EndTorsoBehavior();
}
//
// Name: NotifyBehavior()
// Parameters: Event *ev
// Description: Tells the current behavior that the anim is done
//
void Actor::NotifyBehavior( Event *ev )
{
if ( behavior )
{
behavior->ProcessEvent( EV_Behavior_AnimDone );
SetActorFlag( ACTOR_FLAG_ANIM_DONE, true );
}
}
//
// Name: NotifyHeadBehavior()
// Parameters: Event *ev
// Description: Tells the current head behavior that the anim is done
//
void Actor::NotifyHeadBehavior( Event *ev )
{
if ( headBehavior )
{
headBehavior->ProcessEvent( EV_Behavior_AnimDone );
SetActorFlag( ACTOR_FLAG_ANIM_DONE, true );
}
}
//
// Name: NotifyEyeBehavior()
// Parameters: Event *ev
// Description: Tells the current eye behavior that the anim is done
//
void Actor::NotifyEyeBehavior( Event *ev )
{
if ( eyeBehavior )
{
eyeBehavior->ProcessEvent( EV_Behavior_AnimDone );
SetActorFlag( ACTOR_FLAG_ANIM_DONE, true );
}
}
//
// Name: NotifyTorsoBehavior()
// Parameters: Event *ev
// Description: Tells the current torso behavior that the anim is done
//
void Actor::NotifyTorsoBehavior( Event *ev )
{
if ( torsoBehavior )
{
torsoBehavior->ProcessEvent( EV_Behavior_AnimDone );
SetActorFlag( ACTOR_FLAG_TORSO_ANIM_DONE, true );
}
}
//***********************************************************************************************
//
// Actor type script commands
//
//***********************************************************************************************
void Actor::SetDialogMode( Event *ev )
{
str modeType = ev->GetString(1);
if(stricmp(modeType,"anxious") == 0 )
{
DialogMode = DIALOG_MODE_ANXIOUS;
return;
}
if(stricmp(modeType,"normal") == 0 )
{
DialogMode = DIALOG_MODE_NORMAL;
return;
}
if(stricmp(modeType,"ignore") == 0)
{
DialogMode = DIALOG_MODE_IGNORE;
SetActorFlag( ACTOR_FLAG_ALLOW_TALK, 0 );
return;
}
gi.WDPrintf( "SetDialogMode has an unknown dialog type - - Valid strings are 'anxious', 'normal', or 'ignore'\n");
gi.WDPrintf( "Defaulting to type 'normal'");
DialogMode = DIALOG_MODE_NORMAL;
}
void Actor::RunAlertThread ( Event *ev )
{
if (alert_thread.length() )
RunThread(alert_thread);
}
void Actor::RunDamageThread ( void )
{
if ( ondamage_threshold > 0 )
{
if ( ( health <= ondamage_threshold ) && ondamage_thread.length() )
RunThread( ondamage_thread );
return;
}
if ( ondamage_thread.length() )
RunThread( ondamage_thread );
}
//***********************************************************************************************
//
// Targeting functions
//
//***********************************************************************************************
qboolean Actor::CloseToEnemy ( const Vector &pos, float howclose )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( !IsEntityAlive( currentEnemy ) )
{
return false;
}
if ( WithinDistance( currentEnemy, howclose ) )
{
return true;
}
return false;
}
void Actor::EyeOffset ( Event *ev )
{
eyeposition -= eyeoffset;
eyeoffset = ev->GetVector( 1 );
eyeposition += eyeoffset;
}
qboolean Actor::EntityInRange ( Entity *ent, float range, float min_height, float max_height , bool XYOnly )
{
float r;
Vector delta , cent , enemyCent;
float height_diff;
// Make sure the entity is alive
if ( !IsEntityAlive( ent ) )
{
return false;
}
cent = centroid;
enemyCent = ent->centroid;
// See if the entity is in range
if ( XYOnly )
{
cent.z = 0.0f;
enemyCent.z = 0.0f;
}
delta = enemyCent - cent;
if ( ( max_height != 0 ) || ( min_height != 0 ) )
{
height_diff = delta[ 2 ];
if ( ( height_diff < min_height ) || ( height_diff > max_height ) )
{
return false;
}
delta[ 2 ] = 0;
}
r = delta * delta;
return ( r < ( range * range ) );
}
//***********************************************************************************************
//
// Thread based script commands
//
//***********************************************************************************************
void Actor::RunThread( Event *ev )
{
str thread_name;
thread_name = ev->GetString( 1 );
RunThread(thread_name);
}
void Actor::RunThread( const str &thread_name )
{
if ( thread_name.length() <= 0 )
return;
ExecuteThread(thread_name,true,this);
//jhefty: scripting getcurrententity will now work, leaving old code commented
//in case something magical is happening with CreateThread/DelayedStart
/*CThread *thread;
thread = Director.CreateThread( thread_name );
if ( thread )
thread->DelayedStart( 0.0f );*/
}
//***********************************************************************************************
//
// Path and node management
//
//***********************************************************************************************
PathNode *Actor::NearestNodeInPVS ( const Vector &pos )
{
Vector delta;
PathNode *node;
PathNode *bestnode;
float bestdist;
float dist;
int number_nodes;
int i;
MapCell *cell;
Vector min;
Vector max;
cell = thePathManager.GetMapCell( pos );
if ( !cell )
return NULL;
number_nodes = cell->NumNodes();
bestnode = NULL;
bestdist = 999999999.0f; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
for( i = 0; i < number_nodes; i++ )
{
node = ( PathNode * )cell->GetNode( i );
if ( !node )
continue;
delta = node->origin - pos;
// get the distance squared (faster than getting real distance)
dist = delta * delta;
if ( ( dist < bestdist ) && gi.inPVS( node->origin, (Vector)pos ) )
{
bestnode = node;
bestdist = dist;
// if we're close enough, early exit
if ( dist < 16.0f )
break;
}
}
return bestnode;
}
void Actor::SetPath( Path *newpath )
{
movementSubsystem->setPath( newpath );
}
void Actor::ReserveNodeEvent( Event *ev )
{
PathNode *node;
Vector pos;
pos = ev->GetVector( 1 );
node = thePathManager.NearestNode( pos, this );
if ( node && ( !node->entnum || ( node->entnum == entnum ) || ( node->occupiedTime < level.time ) ) )
{
// Mark node as occupied for a short time
node->occupiedTime = level.time + ev->GetFloat( 2 );
node->entnum = entnum;
}
}
void Actor::ReleaseNodeEvent( Event *ev )
{
PathNode *node;
Vector pos;
pos = ev->GetVector( 1 );
node = thePathManager.NearestNode( pos, this );
if ( node && ( node->entnum == entnum ) )
{
node->occupiedTime = 0;
node->entnum = 0;
}
}
trace_t Actor::Trace( const Vector &end, const char *reason ) const
{
return Trace( origin, end, reason );
}
trace_t Actor::Trace( const float distance, const char *reason ) const
{
Vector endPoint(orientation[0][0], orientation[0][1], orientation[0][2]);
endPoint.normalize();
endPoint *= distance;
endPoint += origin;
return Trace(endPoint, reason );
}
trace_t Actor::Trace( const float angle, const float distance, const char *reason ) const
{
vec3_t end;
RotatePointAroundVector( end, orientation[2], orientation[0], angle );
Vector endPoint(end);
endPoint.normalize();
endPoint *= distance;
endPoint += origin;
return Trace(endPoint, reason );
}
trace_t Actor::Trace( const Vector &begin, const Vector &end, const char *reason ) const
{
return Trace(begin, end, edict->clipmask, reason );
}
trace_t Actor::Trace( const Vector &begin, const Vector &end, const int contentMask, const char *reason ) const
{
Vector beginPoint;
Vector endPoint;
trace_t fullStepUpTrace;
trace_t halfStepUpTrace;
trace_t straightTrace;
trace_t *bestTrace;
float bestFraction;
bool betterTrace;
// Try the normal trace first
beginPoint = begin + movementSubsystem->getStep();
endPoint = end + movementSubsystem->getStep();
fullStepUpTrace = G_Trace( beginPoint, mins , maxs , endPoint, this, contentMask, false, reason );
bestTrace = &fullStepUpTrace;
bestFraction = fullStepUpTrace.fraction;
// If the first trace didn't work very well try it again but only step up half way
if ( bestFraction < 1.0f || bestTrace->startsolid )
{
beginPoint = begin + ( movementSubsystem->getStep() / 2.0f );
endPoint = end + ( movementSubsystem->getStep() / 2.0f );
halfStepUpTrace = G_Trace( beginPoint, mins , maxs , endPoint, this, contentMask, false, reason );
// See if this trace is the best one
if ( halfStepUpTrace.fraction > bestFraction && ( !halfStepUpTrace.startsolid || bestTrace->startsolid ) )
betterTrace = true;
else if ( !halfStepUpTrace.startsolid && bestTrace->startsolid )
betterTrace = true;
else
betterTrace = false;
if ( betterTrace )
{
bestTrace = &halfStepUpTrace;
bestFraction = halfStepUpTrace.fraction;
}
}
// If the second trace didn't work very well try it again but don't step up at all
if ( bestFraction < 1.0f || bestTrace->startsolid )
{
beginPoint = begin;
endPoint = end;
straightTrace = G_Trace( beginPoint, mins , maxs , endPoint, this, contentMask, false, reason );
// See if this trace is the best one
if ( straightTrace.fraction > bestFraction && ( !straightTrace.startsolid || bestTrace->startsolid ) )
betterTrace = true;
else if ( !straightTrace.startsolid && bestTrace->startsolid )
betterTrace = true;
else
betterTrace = false;
if ( betterTrace )
{
bestTrace = &straightTrace;
bestFraction = straightTrace.fraction;
}
}
if ( g_showactortrace->integer )
{
G_DebugLine( bestTrace->endpos, endPoint, 1.0f, 0.0f, 0.0f, 1.0f );
G_DebugLine( beginPoint, bestTrace->endpos , 0.0f, 0.0f, 1.0f, 1.0f );
G_DebugLine( beginPoint, endPoint, 0.0f, 1.0f, 0.0f, 1.0f );
}
return *bestTrace;
}
//***********************************************************************************************
//
// Animation control functions
//
//***********************************************************************************************
void Actor::RemoveAnimDoneEvent( void )
{
animate->SetAnimDoneEvent( NULL );
if ( newanimevent )
{
delete newanimevent;
newanimevent = NULL;
}
last_anim_event_name = "";
}
void Actor::ChangeAnim( void )
{
//float time;
Vector totalmove;
if ( ( newanimnum == -1 ) && ( newTorsoAnimNum == -1 ) )
{
return;
}
if ( newTorsoAnimNum != -1 )
{
// If we're changing to the same anim, don't restart the animation
if ( ( newTorsoAnimNum == CurrentAnim(torso) ) && ( TorsoAnimName == newTorsoAnim ) && !( edict->s.torso_frame & FRAME_EXPLICIT ) )
{
animate->SetAnimDoneEvent( newTorsoAnimEvent , torso );
}
else
{
TorsoAnimName = newTorsoAnim;
animate->NewAnim( newTorsoAnimNum, newTorsoAnimEvent, torso );
}
// clear the new anim variables
newTorsoAnimNum = -1;
newTorsoAnim = "";
newTorsoAnimEvent = NULL;
}
if ( newanimnum != -1 )
{
// If we're changing to the same anim, don't restart the animation
if ( ( newanimnum == CurrentAnim(legs) ) && ( animname == newanim ) && !( edict->s.frame & FRAME_EXPLICIT ) )
{
animate->SetAnimDoneEvent( newanimevent );
}
else
{
animname = newanim;
animate->NewAnim( newanimnum, newanimevent, legs );
//time = gi.Anim_Time( edict->s.modelindex, newanimnum );
}
// clear the new anim variables
newanimnum = -1;
newanim = "";
newanimevent = NULL;
}
}
void Actor::AnimDone( Event *ev )
{
SetActorFlag( ACTOR_FLAG_ANIM_DONE, true );
}
void Actor::TorsoAnimDone( Event *ev )
{
SetActorFlag( ACTOR_FLAG_TORSO_ANIM_DONE, true );
}
qboolean Actor::SetAnim( const str &anim, Event *ev, bodypart_t part, const float animationRate )
{
int num;
if ( !anim.length() )
return false;
if ( !GetActorFlag( ACTOR_FLAG_CAN_CHANGE_ANIM ) ) return false;
num = gi.Anim_Random( edict->s.modelindex, anim.c_str() );
if ( num != -1 )
{
if ( part == legs )
{
newanim = anim;
newanimnum = num;
animnum = newanimnum;
if ( newanimevent )
delete newanimevent;
newanimevent = ev;
if ( newanimevent )
last_anim_event_name = newanimevent->getName();
else
last_anim_event_name = "";
}
if ( part == torso )
{
newTorsoAnim = anim;
newTorsoAnimNum = num;
if ( newTorsoAnimEvent )
delete newTorsoAnimEvent;
newTorsoAnimEvent = ev;
if ( newTorsoAnimEvent )
last_torso_anim_event_name = newTorsoAnimEvent->getName();
else
last_torso_anim_event_name = "";
}
if ( actortype == IS_INANIMATE )
{
// inanimate objects change anims immediately
ChangeAnim();
}
edict->s.animationRate = animationRate;
return true;
}
warning( "Actor::SetAnim", "Actor \"%s\" has no anim named \"%s\"\n", model.c_str(), anim.c_str() );
// kill the event
if ( ev )
{
delete ev;
}
return false;
}
void Actor::AnimateOnce( Event *ev )
{
animate->RandomAnimate( ev->GetString( 1 ), EV_StopAnimating );
}
qboolean Actor::SetAnim( const str &anim, Event &ev, bodypart_t part, const float animationRate )
{
Event * event;
event = new Event( ev );
assert(event);
return SetAnim( anim, event, part, animationRate );
}
void Actor::SetAnim( Event *ev )
{
if ( ev->NumArgs() > 1 )
{
SetAnim( ev->GetString( 1 ), NULL, legs, ev->GetFloat(2) );
}
else
{
SetAnim( ev->GetString( 1 ) );
}
ChangeAnim();
}
void Actor::Anim( Event *ev )
{
Event *e;
if ( !ModeAllowed( ACTOR_MODE_SCRIPT ) )
{
if ( mode == ACTOR_MODE_TALK )
PostEvent( *ev, FRAMETIME );
else if ( mode == ACTOR_MODE_AI )
SendMoveDone( ev->GetThread() );
return;
}
if ( ( deadflag ) && ( actortype != IS_INANIMATE ) )
{
return;
}
e = new Event( EV_Behavior_Args );
e->SetSource( ev->GetSource() );
if ( ev->GetSource() == EV_FROM_SCRIPT )
{
StartMode( ACTOR_MODE_SCRIPT );
e->SetThread( ev->GetThread() );
e->SetLineNumber( ev->GetLineNumber() );
}
e->AddToken( ev->GetToken( 1 ) );
SetBehavior( new PlayAnim, e, ev->GetThread() );
}
//***********************************************************************************************
//
// Script conditionals
//
//***********************************************************************************************
void Actor::IfEnemyVisibleEvent( Event *ev )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( currentEnemy )
ev->ReturnInteger( sensoryPerception->CanSeeEntity( this , currentEnemy, true, true ) );
else
ev->ReturnInteger( false );
}
void Actor::IfNearEvent( Event *ev )
{
CThread *thread;
Entity *ent;
Entity *bestent;
float bestdist;
float dist;
str name;
Vector delta;
float distance;
TargetList *tlist;
int n;
int i;
thread = ev->GetThread();
assert( thread );
if ( !thread )
{
return;
}
name = ev->GetString( 1 );
distance = ev->GetFloat( 2 );
if ( name[ 0 ] == '*' )
{
ent = ev->GetEntity( 1 );
ev->ReturnInteger( WithinDistance( ent, distance ) );
}
else if ( name[ 0 ] == '$' )
{
bestent = NULL;
bestdist = distance * distance;
tlist = world->GetTargetList( str( &name[ 1 ] ) );
n = tlist->list.NumObjects();
for( i = 1; i <= n; i++ )
{
ent = tlist->list.ObjectAt( i );
delta = centroid - ent->centroid;
dist = delta * delta;
if ( dist <= bestdist )
{
bestent = ent;
bestdist = dist;
}
}
ev->ReturnInteger( ( bestent != NULL ) );
}
else
{
bestent = NULL;
bestdist = distance * distance;
ent = NULL;
for( ent = findradius( ent, origin, distance ) ; ent ; ent = findradius( ent, origin, distance ) )
{
if ( ent->inheritsFrom( name.c_str() ) )
{
delta = centroid - ent->centroid;
dist = delta * delta;
if ( dist <= bestdist )
{
bestent = ent;
bestdist = dist;
}
}
}
ev->ReturnInteger( bestent != NULL );
}
}
void Actor::IfCanHideAtEvent( Event *ev )
{
PathNode *node;
Vector pos;
CThread *thread;
bool result;
thread = ev->GetThread();
assert( thread );
if ( !thread )
{
return;
}
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
pos = ev->GetVector( 1 );
node = thePathManager.NearestNode( pos, this );
result = false;
if ( sensoryPerception )
{
if ( node && ( node->nodeflags & ( AI_DUCK | AI_COVER ) ) &&
!sensoryPerception->CanSeeEntity( pos , currentEnemy , true , true ) )
{
if ( !node->entnum || ( node->entnum == entnum ) || ( node->occupiedTime < level.time ) )
{
result = true;
}
}
}
ev->ReturnInteger( result );
}
void Actor::IfEnemyWithinEvent( Event *ev )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
{
ev->ReturnInteger( false );
return;
}
ev->ReturnInteger( WithinDistance( currentEnemy, ev->GetFloat( 1 ) ) );
}
//***********************************************************************************************
//
// Sound reaction functions
//
//***********************************************************************************************
void Actor::NoPainSounds( Event *ev )
{
SetActorFlag( ACTOR_FLAG_NO_PAIN_SOUNDS, true );
}
void Actor::HeardSound( Event *ev )
{
Vector location;
int soundType = SOUNDTYPE_GENERAL;
location = ev->GetVector( 2 );
if (ev->NumArgs() > 2)
{
soundType = ev->GetInteger( 3 );
}
Entity *soundEnt;
Entity *theEntity;
soundEnt = ev->GetEntity( 1 );
theEntity = soundEnt;
if ( soundEnt && soundEnt->isSubclassOf( Weapon ) )
{
Weapon *soundWeapon;
soundWeapon = ( Weapon*)soundEnt;
theEntity = soundWeapon->GetOwner();
}
sensoryPerception->Stimuli( STIMULI_SOUND, location, soundType );
if( GetActorFlag( ACTOR_FLAG_NOISE_HEARD ) )
{
enemyManager->TryToAddToHateList( theEntity );
}
//HeardDialog( soundType );
}
void Actor::BroadcastAlert( float rad )
{
Vector enemypos;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if ( !( this->flags & FL_NOTARGET ) )
{
enemypos = currentEnemy->centroid;
G_BroadcastAlert( this, centroid, enemypos, rad );
}
}
void Actor::BroadcastAlert( float rad, int soundtype )
{
Vector enemypos;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if ( !( this->flags & FL_NOTARGET ) )
{
enemypos = currentEnemy->centroid;
G_BroadcastSound( this, centroid, rad, soundtype );
}
}
void Actor::BroadcastAlert( Event *ev )
{
float rad = ev->GetFloat( 1 );
BroadcastAlert( rad , SOUNDTYPE_ALERT );
}
//***********************************************************************************************
//
// Pain and death related functions
//
//***********************************************************************************************
void Actor::Pain( Event *ev )
{
float damage;
Entity *ent;
int mod;
Vector direction;
Vector position;
Vector dir;
State *tempState;
bool showPain;
damage = ev->GetFloat( 1 );
ent = ev->GetEntity( 2 );
mod = ev->GetInteger( 3 );
position = ev->GetVector( 4 );
direction = ev->GetVector( 5 );
if ( ev->NumArgs() > 5 )
{
showPain = ev->GetBoolean( 6 );
}
else
showPain = false;
// Add to the players action level and (hit) count
/* if ( damage && !deadflag && ent && ent->isSubclassOf( Player ) )
{
Player *player = (Player *)ent;
//player->IncreaseActionLevel( damage / 4.0f );
if ( player->p_heuristics )
player->p_heuristics->IncrementShotsHit();
} */
if ( deadflag )
{
// Do gib stuff
if ( statemap )
{
tempState = statemap->FindState( "GIB" );
if ( tempState )
tempState = tempState->Evaluate( *this, &conditionals );
if ( tempState )
{
tempState->ProcessEntryCommands( this );
}
}
return;
}
if ( ( damage > 0.0f ) && ( next_pain_sound_time <= level.time ) && !GetActorFlag( ACTOR_FLAG_NO_PAIN_SOUNDS ) )
{
next_pain_sound_time = level.time + 0.4f + G_Random( 0.2f );
BroadcastSound();
}
if ( level.time >= _nextPlayPainSoundTime && !GetActorFlag( ACTOR_FLAG_NO_PAIN_SOUNDS ))
{
_nextPlayPainSoundTime = level.time + _playPainSoundInterval;
Sound( "snd_generalpain" );
}
if ( ( mod == MOD_BULLET ) && behavior && ( currentBehavior == "Pain" ) )
{
bullet_hits++;
}
// Determine which pain flags to set
AddStateFlag( STATE_FLAG_SMALL_PAIN );
if ( damage < pain_threshold )
{
if ( G_Random( 1.0f ) < ( damage / pain_threshold ) )
{
if ( ( means_of_death == MOD_SWORD ) || ( means_of_death == MOD_AXE ) || ( means_of_death == MOD_FIRESWORD ) ||
( means_of_death == MOD_ELECTRICSWORD ) )
pain_type = PAIN_BIG;
else
pain_type = PAIN_SMALL;
AddStateFlag( STATE_FLAG_IN_PAIN );
}
}
else
{
pain_type = PAIN_BIG;
AddStateFlag( STATE_FLAG_IN_PAIN );
}
//-------------------------------------------------------------------
// Pain Based on Damage Modification System:
// -- Because I don't want to screw anyone over, I'm leaving
// the old BIG_PAIN/SMALL_PAIN stuff in place, and it will
// work like it always has, so nobody should be screwed by the
// changes I'm making
//
// However, in the future, we want to get away from this system
// and use the damage modification system. Basically, we have
// a boolean flag in this function, which is coming from
// the DMS. If it's true, then I'm setting the SHOW_PAIN state flag
// to true. To show pain, from the state machine, use the SHOW_PAIN
// conditional
//--------------------------------------------------------------------
// Check for GPM Pain Override Here
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
if ( gpm->hasObject(getArchetype()) )
{
str propName = MOD_NumToName(mod);
float painChanceForActor = 0.0f;
if ( propName.length() )
{
str objName;
objName = getArchetype();
objName += ".PainChance";
if ( gpm->hasObject( objName ) )
{
if ( gpm->hasProperty( objName , propName ) )
painChanceForActor = gpm->getFloatValue( objName, propName );
}
}
if ( G_Random() <= painChanceForActor )
showPain = true;
else
showPain = false;
}
if ( ( level.time >= next_forced_pain_time ) || ( level.time >= next_pain_time && showPain ) )
{
next_pain_time = level.time + min_pain_time;
next_forced_pain_time = level.time + max_pain_time;
AddStateFlag( STATE_FLAG_SHOW_PAIN );
}
// Determine pain angles
if ( Vector( position - centroid ).length() > 1.0f )
dir = centroid - position;
else
dir = direction;
dir = dir * -1.0f;
pain_angles = dir.toAngles();
pain_angles[YAW] = AngleNormalize180( angles[YAW] - pain_angles[YAW] );
pain_angles[PITCH] = AngleNormalize180( angles[PITCH] - pain_angles[PITCH] );
}
void Actor::StunEvent( Event *ev )
{
float time;
time = ev->GetFloat( 1 );
SetActorFlag( ACTOR_FLAG_STUNNED, true );
stunned_end_time = level.time + time;
}
void Actor::CheckStun( void )
{
if ( GetActorFlag( ACTOR_FLAG_STUNNED ) && ( stunned_end_time <= level.time ) )
SetActorFlag( ACTOR_FLAG_STUNNED, false );
}
void Actor::Dead( Event *ev )
{
Vector min, max;
// stop animating legs
animate->StopAnimatingAtEnd();
// Make sure we can walk through this guys corpse
edict->clipmask = MASK_DEADSOLID;
if ( edict->solid != SOLID_NOT )
setContents( CONTENTS_CORPSE );
if ( edict->s.torso_anim & ANIM_BLEND )
animate->StopAnimatingAtEnd( torso );
edict->s.eFlags |= EF_DONT_PROCESS_COMMANDS;
if ( !groundentity && ( velocity != vec_zero ) && ( movetype != MOVETYPE_STATIONARY ) )
{
// wait until we hit the ground
PostEvent( EV_Actor_Dead, FRAMETIME );
return;
}
// don't allow them to fly, think, or swim anymore
flags &= ~( FL_SWIM | FL_FLY );
turnThinkOff();
deadflag = DEAD_DEAD;
setMoveType( MOVETYPE_NONE );
setOrigin( origin );
if ( trigger )
{
trigger->ProcessEvent( EV_Remove );
}
if ( spawnparent )
PostEvent( EV_Actor_Fade, .5f );
else
PostEvent( EV_Actor_Fade, 5.0f );
}
void Actor::KilledEffects( Entity *attacker )
{
Entity *ent;
Event *event;
str target_name;
if ( g_debugtargets->integer )
{
G_DebugTargets( this, "Actor::KilledEffects" );
}
//
// kill the killtargets
//
target_name = KillTarget();
if ( target_name && strcmp( target_name, "" ) )
{
ent = NULL;
do
{
ent = G_FindTarget( ent, target_name );
if ( !ent )
{
break;
}
ent->PostEvent( EV_Remove, 0.0f );
}
while ( 1 );
}
//
// fire targets
//
target_name = Target();
if ( target_name && strcmp( target_name, "" ) )
{
ent = NULL;
do
{
ent = G_FindTarget( ent, target_name );
if ( !ent )
{
break;
}
event = new Event( EV_Activate );
event->AddEntity( attacker );
ent->PostEvent( event, 0.0f );
}
while ( 1 );
}
//
// see if we have a kill_thread
//
if ( kill_thread.length() > 1 )
{
CThread * thread;
//
// create the thread, but don't start it yet
//
thread = ExecuteThread( kill_thread, false, this );
if ( !thread )
warning( "Killed", "could not process kill_thread" );
}
}
void Actor::Killed( Event *ev )
{
Vector position;
ClassDef *cls;
str deathanim;
str newdeathanim;
Entity *attacker;
float damage;
qboolean fallingDeath;
Weapon *weapon = 0;
attacker = ev->GetEntity( 1 );
damage = ev->GetFloat( 2 );
if ( ev->NumArgs() > 4 )
fallingDeath = ev->GetBoolean( 5 );
else
fallingDeath = false;
if ( ev->NumArgs() > 5 )
weapon = (Weapon *)ev->GetEntity( 6 );
// Update boss health if necessary
if ( GetActorFlag( ACTOR_FLAG_UPDATE_BOSS_HEALTH ) && max_boss_health )
{
char bosshealth_string[20];
sprintf( bosshealth_string, "%.5f", health / max_boss_health );
gi.cvar_set( "bosshealth", bosshealth_string );
}
//if the actor is a teammate, update the teammates killed #.
//We dont who the attacker is.
if(actortype == IS_TEAMMATE)
{
Player* player;
player = GetPlayer(0);
if(player->p_heuristics)
{
player->p_heuristics->IncrementTeammatesKilled();
}
if( player->client )
{
++player->client->ps.stats[ STAT_TEAMMATES_KILLED ];
}
}
else
{
UnreserveCurrentHelperNode();
}
// Add to the players action level
if ( damage && attacker && attacker->isSubclassOf( Player ) )
{
Player *player = (Player *)attacker;
//player->IncreaseActionLevel( damage / 4.0f );
// Calculate the number of points for this kill (0, if we're not using,
// the gameplay system).
float points = 0.0f;
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
GameplayFormulaData fd(player, this, weapon, player->getAttackType());
if ( gpm->hasObject(player->getArchetype()) && gpm->hasFormula("Points"))
points = gpm->calculate("Points", fd);
player->AwardPoints((int)points);
if(actortype == IS_ENEMY)
{
if(player->p_heuristics)
{
player->p_heuristics->IncrementEnemiesKilled();
}
if( player->client )
{
++player->client->ps.stats[ STAT_ENEMIES_KILLED ];
}
}
}
if ( damage && attacker && attacker->isSubclassOf( Actor ) )
{
Actor *actor = (Actor*)attacker;
actor->InContext( "killedenemy" , 0 );
}
// If we have a behavior going for some reason, kill it now.
// if ( behavior )
// {
// behavior->End(*this);
// behavior = NULL;
// }
if ( attacker )
KilledEffects( attacker );
if ( next_pain_sound_time <= level.time && !GetActorFlag( ACTOR_FLAG_NO_PAIN_SOUNDS ) )
{
next_pain_sound_time = level.time + 0.4f + G_Random( 0.2f );
BroadcastSound();
}
if ( !GetActorFlag( ACTOR_FLAG_DIE_COMPLETELY ) )
return;
// See if means of death should be bumped up from MOD_BULLET to MOD_FAST_BULLET
if ( means_of_death == MOD_BULLET )
{
if ( bullet_hits < 5 )
bullet_hits = 0;
if ( (int)G_Random( 100 ) < ( bullet_hits * 10 ) )
means_of_death = MOD_FAST_BULLET;
}
if ( means_of_death == MOD_ELECTRIC )
{
Event *event;
event = new Event( EV_DisplayEffect );
event->AddString( "electric" );
ProcessEvent( event );
event = new Event( EV_DisplayEffect );
event->AddString( "noelectric" );
PostEvent( event, 3.0f + G_Random( 2.0f ) );
}
if (
means_of_death == MOD_EXPLOSION ||
means_of_death == MOD_SMALL_EXPLOSION ||
means_of_death == MOD_PLASMASHOTGUN
)
{
float chance = G_Random();
if ( chance >= .45 )
{
Vector attackerToSelf = origin - attacker->origin;
attackerToSelf.z = deathKnockbackVerticalValue;
attackerToSelf.normalize();
attackerToSelf *= deathKnockbackHorizontalValue;
velocity = attackerToSelf;
}
}
// Make sure all bones are put back in their normal positions
if ( edict->s.bone_tag[ ACTOR_MOUTH_TAG ] != -1 )
SetControllerAngles( ACTOR_MOUTH_TAG, vec_zero );
if ( edict->s.bone_tag[ ACTOR_HEAD_TAG ] != -1 )
SetControllerAngles( ACTOR_HEAD_TAG, vec_zero );
if ( edict->s.bone_tag[ ACTOR_TORSO_TAG ] != -1 )
SetControllerAngles( ACTOR_TORSO_TAG, vec_zero );
if ( edict->s.bone_tag[ ACTOR_LEYE_TAG ] != -1 )
SetControllerAngles( ACTOR_LEYE_TAG, vec_zero );
if ( edict->s.bone_tag[ ACTOR_REYE_TAG ] != -1 )
SetControllerAngles( ACTOR_REYE_TAG, vec_zero );
if ( !fallingDeath )
{
// Stop behavior
cls = getClass( "idle" );
SetBehavior( ( Behavior * )cls->newInstance() );
}
//If the MissionFailed keeps coming up, make sure the tiki file is set correctly,
if ( !GetActorFlag( ACTOR_FLAG_ALLOWED_TO_KILL ) && attacker && attacker->isSubclassOf( Player ) )
G_MissionFailed( "CivilianKilled" );
// don't allow them to fly or swim anymore
flags &= ~( FL_SWIM | FL_FLY );
deadflag = DEAD_DYING;
groundentity = NULL;
edict->svflags |= SVF_DEADMONSTER;
stopStasis();
if ( !GetActorFlag( ACTOR_FLAG_STAYSOLID ) )
{
edict->clipmask = MASK_DEADSOLID;
if ( edict->solid != SOLID_NOT )
setContents( CONTENTS_CORPSE );
}
// Stop the actor from talking
if ( GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) )
{
CancelEventsOfType( EV_SetControllerAngles );
StopSound( CHAN_DIALOG );
}
// Remove the actor from it's group
//groupcoordinator->RemoveEntityFromGroup( this , GetGroupID() );
groupcoordinator->MemberDied( this , GetGroupID() );
if ( !fallingDeath )
{
deathanim = "death";
if ( GetActorFlag( ACTOR_FLAG_IN_ALCOVE ) )
deathanim = "death_alcove";
// See if this actor has a death state in its state machine
if ( statemap )
{
int count = 0;
State *temp_state;
State *last_temp_state = NULL;
str temp_anim;
if ( behavior )
{
behavior->End( *this );
delete behavior;
behavior = NULL;
}
if ( torsoBehavior )
{
torsoBehavior->End( *this );
delete torsoBehavior;
torsoBehavior = NULL;
}
temp_state = statemap->FindState( "DEATH" );
if ( temp_state )
{
do
{
count++;
if ( count > 50 )
{
gi.Error( ERR_DROP, "Stopping due to possible infinite state loop in death state\n" );
break;
}
// Process the current state
if ( last_temp_state != temp_state )
{
// Get the new animation name
temp_anim = temp_state->getLegAnim( *this, &conditionals );
if ( temp_anim.length() )
newdeathanim = temp_anim;
// Process exit commands of the last state
if ( last_temp_state )
last_temp_state->ProcessExitCommands( this );
// Process the entry commands of the new state
temp_state->ProcessEntryCommands( this );
}
// Determine the next state to go to
last_temp_state = temp_state;
temp_state = temp_state->Evaluate( *this, &conditionals );
}
while ( last_temp_state != temp_state );
}
if ( newdeathanim.length() > 0 )
deathanim = newdeathanim;
}
// Play the death animation
if ( animate )
{
animate->ClearTorsoAnim();
animate->ClearLegsAnim();
}
SetAnim( deathanim, EV_Actor_Dead , legs);
ChangeAnim();
}
// See if we were spawned by another actor
if ( spawnparent )
{
spawnparent->num_of_spawns--;
}
// See if we should notify others
if ( GetActorFlag( ACTOR_FLAG_NOTIFY_OTHERS_AT_DEATH ) )
NotifyOthersOfDeath();
// Do Game Specific Death Stuff
if ( gameComponent && attacker )
gameComponent->HandleDeath( attacker );
SpawnItems();
DropItemsOnDeath();
if ( means_of_death == MOD_GIB )
{
Event *event;
int i;
if ( blood_model.length() == 0 )
blood_model = "fx/fx_bspurt.tik";
if ( max_gibs == 0 )
max_gibs = 4;
for( i = 0 ; i < 4 ; i++ )
{
event = new Event( EV_Sentient_SpawnBloodyGibs );
event->AddInteger( max_gibs );
ProcessEvent( event );
}
PostEvent( EV_Remove, 0.0f );
}
//Gotta Make Sure that this guy dies if it's from a falling death
if ( fallingDeath )
{
PostEvent ( EV_Actor_Dead, 0.0f );
PostEvent ( EV_Remove , 5.0f );
}
}
qboolean Actor::RespondToHitscan( void )
{
//First Check our response chance value, we may not need to do anything at all
if( G_Random() > hitscan_response_chance )
{
SetActorFlag( ACTOR_FLAG_INCOMING_HITSCAN , false );
return false;
}
//Let the Statemachine tell us what to do
if ( statemap )
{
State *temp_state;
temp_state = statemap->FindState( "INCOMING_HITSCAN" );
if ( temp_state )
{
SetActorFlag( ACTOR_FLAG_RESPONDING_TO_HITSCAN, true );
currentState = temp_state;
ProcessActorStateMachine();
}
else
{
SetActorFlag( ACTOR_FLAG_INCOMING_HITSCAN , false );
return false;
}
}
//Lastly clear our flag
SetActorFlag( ACTOR_FLAG_INCOMING_HITSCAN , false );
SetActorFlag( ACTOR_FLAG_RESPONDING_TO_HITSCAN, false );
return true;
}
void Actor::HandleGameSpecificEvent( Event *ev )
{
if ( gameComponent )
gameComponent->HandleEvent( ev );
}
void Actor::SetHitscanResponse( Event *ev )
{
hitscan_response_chance = ev->GetFloat ( 1 );
if (hitscan_response_chance > 1.0f )
hitscan_response_chance = 1.0f;
if (hitscan_response_chance < 0.0f )
hitscan_response_chance = 0.0f;
}
void Actor::SetDieCompletely( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DIE_COMPLETELY, ev->GetBoolean( 1 ) );
}
void Actor::SetBleedAfterDeath( Event *ev )
{
SetActorFlag( ACTOR_FLAG_BLEED_AFTER_DEATH, ev->GetBoolean( 1 ) );
}
void Actor::SpawnGib( Event *ev )
{
RealSpawnGib( false, ev );
}
void Actor::SpawnGibAtTag( Event *ev )
{
RealSpawnGib( true, ev );
}
void Actor::RealSpawnGib( qboolean use_tag, Event *ev )
{
str tag_name;
Gib *gib;
Entity *ent = NULL;
Vector final_gib_offset;
Vector orig;
Vector dir;
int current_arg;
float final_pitch;
float vel;
trace_t trace;
float time;
float pitch_change;
float pitch_vel;
str attach_tag_name;
Vector offset;
str cap_name;
float width;
const char *current_arg_str;
int current_surface;
const char *current_surface_name;
qboolean use_blood;
str blood_name;
str blood_splat_name;
str blood_model_name;
float blood_splat_size;
Vector gib_mins;
Vector gib_maxs;
float m;
qboolean at_least_one_visible_surface = false;
int surface_length;
int tagnum;
orientation_t orn;
float raw_offset;
Vector raw_offset_dir;
Vector real_tag_pos;
Vector real_tag_dir;
Vector real_tag_angles;
SetActorFlag( ACTOR_FLAG_SPAWN_FAILED, true );
if ( !com_blood->integer )
return;
if ( GetActorFlag( ACTOR_FLAG_FADING_OUT ) )
return;
if ( ev->NumArgs() < 5 )
return;
if ( use_tag )
{
attach_tag_name = ev->GetString( 1 );
raw_offset = ev->GetFloat( 2 );
width = ev->GetFloat( 3 );
// Get all the tag information
tagnum = gi.Tag_NumForName( edict->s.modelindex, attach_tag_name.c_str() );
if ( tagnum == -1 )
return;
GetRawTag( tagnum, &orn );
GetTag( tagnum, &real_tag_pos, &real_tag_dir );
real_tag_angles = real_tag_dir.toAngles();
// Determine the final pitch of the gib
final_pitch = AngleNormalize180( angles[PITCH] - real_tag_angles[PITCH] );
// Determine the offset of the gib
raw_offset_dir = orn.axis[0];
offset = orn.origin;
offset += raw_offset * raw_offset_dir;
MatrixTransformVector( offset, orientation, orig );
orig += origin;
}
else
{
offset = ev->GetVector( 1 );
final_pitch = ev->GetFloat( 2 );
width = ev->GetFloat( 3 );
MatrixTransformVector( offset, orientation, orig );
orig += origin;
}
// Determine the mass
m = mass;
if ( m < 50.0f )
m = 50.0f;
else if ( m > 250.0f )
m = 250.0f;
// Determine which blood spurt & splat to use for the gib
blood_name = GetBloodSpurtName();
blood_splat_name = GetBloodSplatName();
blood_splat_size = GetBloodSplatSize();
if ( blood_name.length() && blood_splat_name.length() )
use_blood = true;
else
use_blood = false;
if ( GetActorFlag( ACTOR_FLAG_BLEED_AFTER_DEATH ) )
blood_model_name = blood_model;
// Get the mins and maxs for this gib
gib_mins = Vector( -width, -width, -width );
gib_maxs = Vector( width, width, width );
// Make sure we can spawn in a gib here
trace = G_Trace( orig, gib_mins, gib_maxs, orig, NULL, MASK_DEADSOLID, false, "spawngib" );
if ( trace.allsolid || trace.startsolid )
return;
// Make sure at least one of the surfaces is not hidden
for( current_arg = 5 ; current_arg <= ev->NumArgs() ; current_arg++ )
{
current_arg_str = ev->GetString( current_arg );
for( current_surface = 0 ; current_surface < numsurfaces ; current_surface++ )
{
current_surface_name = gi.Surface_NumToName( edict->s.modelindex, current_surface );
if ( current_arg_str[ strlen( current_arg_str ) - 1 ] == '*' )
surface_length = strlen( current_arg_str ) - 1;
else
surface_length = strlen( current_arg_str );
if ( Q_stricmpn( current_surface_name, current_arg_str, surface_length ) == 0 )
{
if ( !( edict->s.surfaces[ current_surface ] & MDL_SURFACE_NODRAW ) )
at_least_one_visible_surface = true;
}
}
}
if ( !at_least_one_visible_surface )
return;
// Determine time till it hits the ground
vel = 100.0f + G_Random( 200.0f * ( 2.0f - ( ( m - 50.0f ) / 200.0f ) ) );
time = SpawnGetTime( vel , orig, gib_mins, gib_maxs );
// Flip final pitch 180 degrees?
if ( G_Random() > .5f )
{
final_pitch += 180.0f;
final_pitch = AngleNormalize360( final_pitch );
}
// Calculate the pitch change and velocity
pitch_change = AngleNormalize180( final_pitch - angles[PITCH] );
pitch_vel = pitch_change / time;
// Spawn in the hidden gib
gib = new Gib( "", use_blood, blood_name, blood_model_name, blood_splat_name, blood_splat_size, final_pitch );
gib->setOrigin( orig );
gib->angles[PITCH] = angles[PITCH];
gib->angles[ROLL] = 0;
if ( use_tag )
gib->angles[YAW] = real_tag_angles[YAW];
else
gib->angles[YAW] = angles[YAW];
gib->setAngles( gib->angles );
gib->velocity = Vector( G_CRandom( 150.0f * ( 2.0f - ( ( m - 50.0f ) / 200.0f ) ) ),
G_CRandom( 150.0f * ( 2.0f - ( ( m - 50.0f ) / 200.0f ) ) ), vel );
gib->avelocity = Vector( pitch_vel, G_CRandom( 300.0f ), 0.0f );
gib->setSize( gib_mins, gib_maxs );
gib->edict->svflags |= SVF_DEADMONSTER;
gib->edict->clipmask = MASK_DEADSOLID;
gib->setSolidType( SOLID_BBOX );
gib->setContents( CONTENTS_SHOOTABLE_ONLY );
gib->link();
// Spawn in the visible gib
ent = new Entity( ENTITY_CREATE_FLAG_ANIMATE );
ent->setModel( model );
// Make sure the init stuff in the tiki get processed now
//ent->ProcessPendingEvents();
ent->CancelPendingEvents();
// Make sure no client side commands are processed
ent->edict->s.eFlags |= EF_DONT_PROCESS_COMMANDS;
// Set the animation to the current anim & frame of the actor
ent->animate->RandomAnimate( animate->AnimName() );
ent->animate->SetFrame( CurrentFrame() );
ent->setAngles( angles );
ent->bind( gib, true );
ent->gravity = 0;
final_gib_offset = offset * -1.0f;
ent->setOrigin( final_gib_offset );
ent->setMoveType( MOVETYPE_BOUNCE );
ent->setSolidType( SOLID_NOT );
ent->showModel();
// Hide all of the surfaces on the gib
ent->SurfaceCommand( "all", "+nodraw" );
cap_name = ev->GetString( 4 );
// Show the cap surface on the gib and the actor
if ( cap_name.length() )
{
ent->SurfaceCommand( cap_name.c_str(), "-nodraw" );
SurfaceCommand( cap_name.c_str(), "-nodraw" );
}
// Show and hide all of the rest of the necessary surfaces
for ( current_arg = 5 ; current_arg <= ev->NumArgs() ; current_arg++ )
{
current_arg_str = ev->GetString( current_arg );
if ( current_arg_str[ strlen( current_arg_str ) - 1 ] == '*' )
surface_length = strlen( current_arg_str ) - 1;
else
surface_length = strlen( current_arg_str );
for( current_surface = 0 ; current_surface < numsurfaces ; current_surface++ )
{
current_surface_name = gi.Surface_NumToName( edict->s.modelindex, current_surface );
if ( Q_stricmpn( current_surface_name, current_arg_str, surface_length ) == 0 )
{
if ( !( edict->s.surfaces[ current_surface ] & MDL_SURFACE_NODRAW ) )
{
// Show this surface on the gib
ent->SurfaceCommand( current_surface_name, "-nodraw" );
// Hide this surface on the actor
SurfaceCommand( current_surface_name, "+nodraw" );
}
}
}
}
// Make sure the gibs go away after a while
if ( GetActorFlag( ACTOR_FLAG_DEATHFADE ) )
{
ent->PostEvent( EV_Fade, 10.0f );
gib->PostEvent( EV_Fade, 10.5f );
}
else if ( GetActorFlag( ACTOR_FLAG_DEATHSHRINK ) )
{
ent->PostEvent( EV_FadeOut, 10.0f );
gib->PostEvent( EV_FadeOut, 10.5f );
}
else
{
ent->PostEvent( EV_Unbind, 10.0f );
ent->PostEvent( EV_DeathSinkStart, 12.0f );
gib->PostEvent( EV_Remove, 10.5f );
}
// Mark the spawn as being successful
SetActorFlag( ACTOR_FLAG_SPAWN_FAILED, false );
// Play the severed sound
ent->Sound( "impact_sever", CHAN_BODY );
}
void Actor::SpawnNamedGib( Event *ev )
{
str gib_name;
str tag_name;
Gib *gib;
Vector orig;
float final_pitch;
float vel;
trace_t trace;
float time;
float pitch_change;
float pitch_vel;
float width;
Vector gib_mins;
Vector gib_maxs;
SetActorFlag( ACTOR_FLAG_SPAWN_FAILED, true );
/*
if ( !com_blood->integer )
return;
*/
if ( GetActorFlag( ACTOR_FLAG_FADING_OUT ) )
return;
// Get all of the parameters
gib_name = ev->GetString( 1 );
tag_name = ev->GetString( 2 );
final_pitch = ev->GetFloat( 3 );
width = ev->GetFloat( 4 );
// Get the tag position
GetTag( tag_name, &orig );
// Get the mins and maxs for this gib
gib_mins = Vector( -width, -width, -width );
gib_maxs = Vector( width, width, width );
// Make sure we can spawn in a gib here
trace = G_Trace( orig, gib_mins, gib_maxs, orig, NULL, MASK_DEADSOLID, false, "spawnnamedgib1" );
if ( trace.allsolid || trace.startsolid )
SetActorFlag( ACTOR_FLAG_SPAWN_FAILED, true );
// Determine time till it hits the ground
vel = 400.0f + G_Random( 400.0f );
time = SpawnGetTime( vel , orig, gib_mins, gib_maxs );
pitch_change = AngleNormalize180( final_pitch - angles[PITCH] );
pitch_vel = pitch_change / time;
// Spawn the gib
gib = new Gib( gib_name, false, "", "", "", final_pitch );
gib->setOrigin( orig );
gib->setAngles( angles );
gib->velocity = Vector( G_CRandom( 200.0f ), G_CRandom( 200.0f ), vel );
gib->avelocity = Vector( pitch_vel, G_CRandom( 300.0f ), 0.0f );
gib->edict->svflags |= SVF_DEADMONSTER;
gib->edict->clipmask = MASK_DEADSOLID;
gib->setSolidType( SOLID_BBOX );
gib->setContents( CONTENTS_CORPSE );
// Make sure the gib goes away after a while
if ( GetActorFlag( ACTOR_FLAG_DEATHFADE ) )
gib->PostEvent( EV_Fade, 10.0f );
else if ( GetActorFlag( ACTOR_FLAG_DEATHSHRINK ) )
gib->PostEvent( EV_FadeOut, 10.0f );
else
gib->PostEvent( EV_DeathSinkStart, 10.0f );
// Mark the spawn as being successful
SetActorFlag( ACTOR_FLAG_SPAWN_FAILED, false );
}
float Actor::SpawnGetTime( float vel, const Vector &orig, const Vector &gib_mins, const Vector &gib_maxs )
{
float grav;
Vector end_pos;
Vector dir;
float time;
float other;
trace_t trace;
float dist;
grav = -sv_currentGravity->value;
end_pos = orig;
end_pos[2] = -10000.0f;
trace = G_Trace( orig, gib_mins, gib_maxs, end_pos, NULL, MASK_DEADSOLID, false, "SpawnGetTime" );
end_pos = trace.endpos;
dir = end_pos - orig;
dist = dir.length();
time = ( ( grav / -20.0f ) - vel ) / grav;
other = sqrt( ( grav / 20.0f + vel ) * ( grav / 20.0f + vel ) - ( 2.0f * grav * dist ) );
time = time - ( other / grav );
return time;
}
void Actor::SpawnBlood( Event *ev )
{
str blood_name;
str tag_name;
qboolean use_last_result = false;
Event *attach_event;
if ( !com_blood->integer )
return;
blood_name = ev->GetString( 1 );
tag_name = ev->GetString( 2 );
// See if we care about the last spawn working or not
if ( ev->NumArgs() > 2 )
use_last_result = ev->GetBoolean( 3 );
if ( use_last_result && GetActorFlag( ACTOR_FLAG_SPAWN_FAILED ) )
return;
// Spawn the blood
attach_event = (Event *)new Event( EV_AttachModel );
attach_event->AddString( blood_name );
attach_event->AddString( tag_name );
attach_event->AddInteger( 1 );
attach_event->AddString( "" );
attach_event->AddInteger( 0 );
attach_event->AddInteger( 5 );
PostEvent( attach_event, 0.0f );
}
void Actor::RemoveUselessBody( Event *ev )
{
PostEvent( EV_FadeOut, 5.0f );
}
void Actor::SetPainThresholdEvent( Event *ev )
{
pain_threshold = ( ev->GetFloat( 1 ) );
}
void Actor::SetKillThreadEvent( Event *ev )
{
kill_thread = ev->GetString( 1 );
}
void Actor::DeathFadeEvent( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DEATHFADE, true );
}
void Actor::setDeathEffect( Event *ev )
{
_deathEffect = ev->GetString( 1 );
}
void Actor::DeathShrinkEvent( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DEATHSHRINK, true );
}
void Actor::DeathSinkEvent( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DEATHSINK, true );
}
void Actor::StaySolidEvent( Event *ev )
{
SetActorFlag( ACTOR_FLAG_STAYSOLID, true );
}
void Actor::Suicide( Event *ev )
{
Event *event;
qboolean use_last_mod;
health = 0;
if ( ev->NumArgs() > 0 )
use_last_mod = ev->GetBoolean( 1 );
else
use_last_mod = false;
if ( !use_last_mod )
means_of_death = MOD_SUICIDE;
event = new Event( EV_Killed );
event->AddEntity( this );
event->AddInteger( 0 );
event->AddEntity( this );
event->AddInteger( MOD_SUICIDE );
ProcessEvent( event );
}
void Actor::SetDeathSize( Event *ev )
{
Vector death_min;
Vector death_max;
trace_t trace;
death_min = ev->GetVector( 1 );
death_max = ev->GetVector( 2 );
// Make sure actor will not be stuck if we change the bounding box
trace = G_Trace( origin, death_min, death_max, origin, this, edict->clipmask, false, "Actor::SetDeathSize" );
if ( !trace.startsolid )
{
setSize( death_min, death_max );
return;
}
// Try again, calculate a smaller death bounding box
death_min = (death_min + mins) * .5f;
death_max = (death_max + maxs) * .5f;
trace = G_Trace( origin, death_min, death_max, origin, this, edict->clipmask, false, "Actor::SetDeathSize2" );
if ( !trace.startsolid )
setSize( death_min, death_max );
}
void Actor::FadeEvent( Event *ev )
{
SetActorFlag( ACTOR_FLAG_FADING_OUT, true );
if ( GetActorFlag( ACTOR_FLAG_DEATHFADE ) )
{
ProcessEvent( EV_ForceAlpha );
ProcessEvent( EV_Fade );
}
else if ( GetActorFlag( ACTOR_FLAG_DEATHSHRINK ) )
ProcessEvent( EV_FadeOut );
else if ( GetActorFlag( ACTOR_FLAG_DEATHSINK ) )
ProcessEvent( EV_DeathSinkStart );
else if ( _deathEffect.length() > 0 )
{
Event *newEvent;
newEvent = new Event( EV_DisplayEffect );
newEvent->AddString( _deathEffect );
ProcessEvent( newEvent );
PostEvent( EV_Remove, 5.0f );
}
else
SetActorFlag( ACTOR_FLAG_FADING_OUT, false );
}
//***********************************************************************************************
//
// Movement functions
//
//***********************************************************************************************
void Actor::SimplePathfinding( Event *ev )
{
SetActorFlag( ACTOR_FLAG_SIMPLE_PATHFINDING, true );
}
void Actor::SetCanWalkOnOthers( Event *ev )
{
SetActorFlag( ACTOR_FLAG_CAN_WALK_ON_OTHERS, true );
}
void Actor::SetFeetWidth( Event *ev )
{
feet_width = ev->GetFloat( 1 );
}
void Actor::ForwardSpeedEvent( Event *ev )
{
movementSubsystem->setForwardSpeed( ev->GetFloat( 1 ) );
}
void Actor::SwimEvent( Event *ev )
{
flags &= ~FL_FLY;
flags |= FL_SWIM;
}
void Actor::FlyEvent( Event *ev )
{
if ( ev->NumArgs() == 0 )
{
// Turn flying on
flags &= ~FL_SWIM;
flags |= FL_FLY;
}
else
{
if ( ev->GetBoolean( 1 ) )
{
// Turn flying on
flags &= ~FL_SWIM;
flags |= FL_FLY;
}
else
{
// Turn flying off
flags &= ~FL_FLY;
}
}
}
void Actor::NotLandEvent( Event *ev )
{
flags &= FL_SWIM | FL_FLY;
}
void Actor::Push( Event *ev )
{
movementSubsystem->Push(ev->GetVector( 1 ) );
}
void Actor::Push(const Vector &dir)
{
movementSubsystem->Push(dir );
}
void Actor::Pushable( Event *ev )
{
bool flag;
if ( ev->NumArgs() )
flag = ev->GetBoolean( 1 );
else
flag = true;
SetActorFlag( ACTOR_FLAG_PUSHABLE, flag );
}
//***********************************************************************************************
//
// Debug functions
//
//***********************************************************************************************
void Actor::ShowInfo(void)
{
gi.Printf( "\nEntity # : %d\n", entnum );
gi.Printf( "Class ID : %s\n", getClassID() );
gi.Printf( "Classname : %s\n", getClassname() );
gi.Printf( "Name : %s\n", name.c_str() );
if ( part_name.length() > 0 )
gi.Printf( "Part name : %s\n", part_name.c_str() );
gi.Printf( "\n" );
gi.Printf( "Targetname : %s\n", TargetName() );
gi.Printf( "Origin : ( %f, %f, %f )\n", origin.x, origin.y, origin.z );
gi.Printf( "Bounds : Mins( %.2f, %.2f, %.2f ) Maxs( %.2f, %.2f, %.2f )\n", mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z );
gi.Printf( "\n" );
if ( behavior )
gi.Printf( "Behavior : %s\n", behavior->getClassname() );
else
gi.Printf( "Behavior : NULL -- was '%s'\n", currentBehavior.c_str() );
if ( headBehavior )
gi.Printf( "Head Behavior : %s\n", headBehavior->getClassname() );
else
gi.Printf( "Head Behavior : NULL -- was '%s'\n", currentHeadBehavior.c_str() );
if ( eyeBehavior )
gi.Printf( "Eye Behavior : %s\n", eyeBehavior->getClassname() );
else
gi.Printf( "Eye Behavior : NULL -- was '%s'\n", currentEyeBehavior.c_str() );
if ( torsoBehavior )
gi.Printf( "Torso Behavior : %s\n", torsoBehavior->getClassname() );
else
gi.Printf( "Torso Behavior : NULL -- was '%s'\n", currentTorsoBehavior.c_str() );
if ( currentState )
gi.Printf( "State : %s\n", currentState->getName() );
else
gi.Printf( "State : NONE\n" );
if ( GetActorFlag( ACTOR_FLAG_AI_ON ) )
gi.Printf( "AI is ON\n" );
else
gi.Printf( "AI is OFF\n" );
if ( sensoryPerception )
{
sensoryPerception->ShowInfo();
}
if ( isThinkOn() )
gi.Printf( "Think is ON\n" );
else
gi.Printf( "Think is OFF\n" );
if ( mode == ACTOR_MODE_IDLE )
gi.Printf( "Mode : IDLE\n" );
else if ( mode == ACTOR_MODE_AI )
gi.Printf( "Mode : AI\n" );
else if ( mode == ACTOR_MODE_SCRIPT )
gi.Printf( "Mode : SCRIPT\n" );
else if ( mode == ACTOR_MODE_TALK )
gi.Printf( "Mode : TALK\n" );
gi.Printf( "\n" );
gi.Printf( "Actortype : %d\n", actortype );
gi.Printf( "Model : %s\n", model.c_str() );
gi.Printf( "Anim : %s\n", animname.c_str() );
gi.Printf( "Health : %f\n", health );
gi.Printf( "\ncurrentEnemy: " );
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( currentEnemy )
{
gi.Printf( "%d : '%s'\n", currentEnemy->entnum, currentEnemy->targetname.c_str() );
}
else
{
gi.Printf( "None\n" );
}
gi.Printf( "actortype: %d\n", actortype );
switch( deadflag )
{
case DEAD_NO :
gi.Printf( "deadflag: NO\n" );
break;
case DEAD_DYING :
gi.Printf( "deadflag: DYING\n" );
break;
case DEAD_DEAD :
gi.Printf( "deadflag: DEAD\n" );
break;
case DEAD_RESPAWNABLE :
gi.Printf( "deadflag: RESPAWNABLE\n" );
break;
}
gi.Printf( "\n" );
if ( behavior )
{
gi.Printf( "Behavior Info:\n" );
gi.Printf( "Game time: %f\n", level.time );
behavior->ShowInfo( *this );
gi.Printf( "\n" );
}
if ( headBehavior )
{
gi.Printf( "Head Behavior Info:\n" );
gi.Printf( "Game time: %f\n", level.time );
headBehavior->ShowInfo( *this );
gi.Printf( "\n" );
}
if ( eyeBehavior )
{
gi.Printf( "Eye Behavior Info:\n" );
gi.Printf( "Game time: %f\n", level.time );
eyeBehavior->ShowInfo( *this );
gi.Printf( "\n" );
}
if ( torsoBehavior )
{
gi.Printf( "Torso Behavior Info:\n" );
gi.Printf( "Game time: %f\n", level.time );
torsoBehavior->ShowInfo( *this );
gi.Printf( "\n" );
}
}
//***********************************************************************************************
//
// Stimuli functions
//
//***********************************************************************************************
void Actor::TurnAIOn(Event *ev)
{
TurnAIOn();
}
void Actor::TurnAIOn( void )
{
if ( GetActorFlag( ACTOR_FLAG_AI_ON ) )
return;
SetActorFlag( ACTOR_FLAG_AI_ON, true );
if ( sensoryPerception )
sensoryPerception->RespondTo( STIMULI_ALL, true );
EndMode();
mode = ACTOR_MODE_AI;
Wakeup();
}
void Actor::TurnAIOff( Event *ev )
{
TurnAIOff();
}
void Actor::TurnAIOff( void )
{
SetActorFlag( ACTOR_FLAG_AI_ON, false );
if ( sensoryPerception )
sensoryPerception->RespondTo( STIMULI_NONE, true );
if ( mode == ACTOR_MODE_AI )
{
// Ai is currently on, get out of AI mode
//gi.WDPrintf( "Forcing an actor (#%d, %s) out of AI mode, this can be dangerous.\n", entnum, name.c_str() );
enemyManager->SetCurrentEnemy( NULL );
enemyManager->LockOnCurrentEnemy( false );
EndMode();
}
}
void Actor::ActivateAI( void )
{
if ( !statemap && !fuzzyEngine && !masterstatemap)
return;
last_time_active = level.time;
if ( (mode == ACTOR_MODE_AI) || mode == ACTOR_MODE_TALK )
return;
StartMode( ACTOR_MODE_AI );
if ( fuzzyEngine )
SetState( "START" );
if ( sensoryPerception )
sensoryPerception->RespondTo(STIMULI_ALL , true );
if (activate_thread.length() )
RunThread(activate_thread);
}
void Actor::SetIdleThread( Event *ev )
{
idle_thread = ev->GetString( 1 );
}
//***********************************************************************************************
//
// Targeting functions
//
//***********************************************************************************************
void AI_SenseEnemies ( void )
{
Actor *actor;
Player *player;
player = GetPlayer( 0 );
// process the list in reverse order in case SleepList is changed
for( int i = SleepList.NumObjects(); i > 0; i-- )
{
actor = SleepList.ObjectAt( i );
/*
//First Check if the player is in PVS -- If so we want to wake up anyway
//Assuming, of course, the scriptor did not turn the AI off explicitly
if ( player )
{
if ( actor->GetActorFlag(ACTOR_FLAG_AI_ON) && gi.inPVS( actor->centroid , player->centroid ) )
{
actor->Wakeup();
actor->ActivateAI();
}
}
*/
if ( actor )
actor->sensoryPerception->SenseEnemies();
}
}
//*********************************************************************************************
//
// GetPlayer
//
//*********************************************************************************************
Player *GetPlayer( int index )
{
gentity_t *ed;
if( index > game.maxclients )
return 0;
ed = &g_entities[ index ];
if ( !ed->inuse || !ed->entity )
return 0;
return ( Player * )g_entities[index].entity;
}
//***********************************************************************************************
//
// Actor checks
//
//***********************************************************************************************
// Temporary
qboolean Actor::checkInAIMode( Conditional &condition )
{
if ( mode == ACTOR_MODE_AI )
return true;
return false;
}
void Actor::checkActorDead( Event *ev )
{
Actor* act = (Actor*)ev->GetEntity( 1 );
if ( act )
ev->ReturnInteger( act->checkActorDead() );
else
ev->ReturnInteger( false );
}
qboolean Actor::checkActorDead( )
{
if ( deadflag || ( health <= 0 ) )
return true;
return false;
}
qboolean Actor::checkanimname( Conditional &condition )
{
str anim_name_test;
int use_length;
int result;
anim_name_test = condition.getParm( 1 );
if ( ( animname.length() == 0 ) || ( anim_name_test.length() == 0 ) )
return false;
if ( condition.numParms() > 1 )
use_length = atoi( condition.getParm( 2 ) );
else
use_length = false;
if ( use_length )
result = strncmp( animname.c_str(), anim_name_test.c_str(), anim_name_test.length() );
else
result = strcmp( animname.c_str(), anim_name_test.c_str() );
return (result == 0);
}
qboolean Actor::checkActorFlag( Conditional &condition )
{
str flagName = condition.getParm( 1 );
return GetActorFlag( flagName );
}
qboolean Actor::checkinactive( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_INACTIVE );
}
qboolean Actor::checkanimdone( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_ANIM_DONE );
}
qboolean Actor::checktorsoanimdone( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_TORSO_ANIM_DONE );
}
qboolean Actor::checkdead( Conditional &condition )
{
return deadflag != 0;
}
qboolean Actor::checkhaveenemy( Conditional &condition )
{
// Get our current enemy
Entity *currentEnemy;
//enemyManager->FindHighestHateEnemy();
//currentEnemy = enemyManager->GetCurrentEnemy();
//if ( !currentEnemy )
// return false;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( currentEnemy && IsEntityAlive( currentEnemy ) )
return true;
if ( enemyManager->getEnemyCount() )
return true;
return false;
}
qboolean Actor::checkenemydead( Conditional &condition )
{
return checkenemydead();
}
qboolean Actor::checkenemydead( void )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( currentEnemy->deadflag || ( currentEnemy->health <= 0 ) )
return true;
return false;
}
qboolean Actor::checkenemynoclip( Conditional &condition )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( currentEnemy->movetype == MOVETYPE_NOCLIP )
return true;
return false;
}
qboolean Actor::checkcanseeenemy( Conditional &condition )
{
qboolean use_fov = true;
qboolean can_see;
qboolean in_fov;
qboolean real_can_see;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( condition.numParms() > 0 )
use_fov = atoi( condition.getParm( 1 ) );
// See if we should check again
if ( canseeenemy_time > level.time )
{
if ( use_fov )
return GetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY );
else
return GetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY_NOFOV );
}
can_see = true;
in_fov = true;
// Check to see if we can see enemy
if ( !IsEntityAlive( currentEnemy ) )
{
can_see = false;
in_fov = false;
}
if ( sensoryPerception )
{
if ( can_see && !sensoryPerception->CanSeeEntity( this , currentEnemy , true , true ) )
can_see = false;
}
// Save can see info
SetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY, can_see && in_fov );
SetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY_NOFOV, can_see );
canseeenemy_time = level.time + 0.2f + G_Random( 0.1f );
if ( use_fov )
real_can_see = GetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY );
else
real_can_see = GetActorFlag( ACTOR_FLAG_LAST_CANSEEENEMY_NOFOV );
// Save the last known position of our enemy
if ( real_can_see )
last_known_enemy_pos = currentEnemy->centroid;
return real_can_see;
}
qboolean Actor::checkcanseeplayer( Conditional &condition )
{
qboolean use_fov = true;
qboolean can_see;
qboolean in_fov;
qboolean real_can_see;
// Get our current enemy
Entity *player;
player = GetPlayer ( 0 );
if ( !player )
return false;
if ( condition.numParms() > 0 )
use_fov = atoi( condition.getParm( 1 ) );
// See if we should check again
if ( canseeplayer_time > level.time )
{
if ( use_fov )
return GetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER );
else
return GetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER_NOFOV );
}
can_see = true;
in_fov = sensoryPerception->InFOV( player );
// Check to see if we can see enemy
if ( !IsEntityAlive( player ) )
{
can_see = false;
in_fov = false;
}
if ( sensoryPerception )
{
if ( can_see && !sensoryPerception->CanSeeEntity( this , player , true , true ) )
can_see = false;
}
// Save can see info
SetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER, can_see && in_fov );
SetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER_NOFOV, can_see );
canseeplayer_time = level.time + 0.2f + G_Random( 0.1f );
if ( use_fov )
real_can_see = GetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER );
else
real_can_see = GetActorFlag( ACTOR_FLAG_LAST_CANSEEPLAYER_NOFOV );
// Save the last known position of our enemy
if ( real_can_see )
last_known_player_pos = player->origin;
return real_can_see;
}
qboolean Actor::checkcanshootenemy( Conditional &condition )
{
str tag_name;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
/*
if ( condition.numParms() > 1 )
tag_name = condition.getParm( 2 );
return ( checkcanseeenemy( condition ) && TestAttack( tag_name ) );
*/
return combatSubsystem->CanAttackTarget( currentEnemy );
}
qboolean Actor::checkCanAttackAnyEnemy ( Conditional &condition )
{
return enemyManager->CanAttackAnyEnemy();
}
qboolean Actor::checkenemyinfov( Conditional &condition )
{
float check_fov;
float check_fovdot;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( !sensoryPerception )
return false;
if ( condition.numParms() > 0 )
{
check_fov = (float)atof( condition.getParm( 1 ) );
check_fovdot = (float) cos( check_fov * 0.5 * M_PI / 180.0 );
return sensoryPerception->InFOV( currentEnemy->centroid, check_fov, check_fovdot );
}
else
{
return sensoryPerception->InFOV( currentEnemy );
}
}
qboolean Actor::checkenemyonground( Conditional &condition )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( currentEnemy->groundentity )
return true;
else
return false;
}
qboolean Actor::checkenemyrelativeyaw( Conditional &condition )
{
Vector dir;
Vector dir_angles;
float relative_yaw;
float check_yaw_min;
float check_yaw_max;
Vector temp_angles;
qboolean use_range;
// Get our current enemy
Entity *currentEnemy;
//currentEnemy = enemyManager->GetCurrentEnemy();
//if ( !currentEnemy )
// return false;
currentEnemy = GetPlayer(0);
check_yaw_min = (float)atof( condition.getParm( 1 ) );
if ( condition.numParms() > 1 )
{
check_yaw_max = atof( condition.getParm( 2 ) );
use_range = true;
}
else
{
check_yaw_max = 0;
use_range = false;
}
dir = origin - currentEnemy->origin;
dir_angles = dir.toAngles();
temp_angles = currentEnemy->angles;
relative_yaw = AngleNormalize180( currentEnemy->angles[YAW] - dir_angles[YAW] );
if (use_range)
{
//Special Case Check
if ( check_yaw_max < check_yaw_min )
{
if ( ( relative_yaw < check_yaw_max ) && ( relative_yaw >= -180 ) )
return true;
if ( ( relative_yaw > check_yaw_min ) && ( relative_yaw <= 180 ) )
return true;
return false;
}
if ( ( relative_yaw >= check_yaw_min ) && ( relative_yaw <= check_yaw_max ) )
return true;
else
return false;
}
if ( relative_yaw < check_yaw_min )
return true;
else
return false;
}
qboolean Actor::checkenemyyawrange ( Conditional &condition )
{
//This function will return true if the the currentEnemy is within the
//angles passed in.
Vector dir;
float check_yaw_min;
float check_yaw_max;
float dirYaw;
float originYaw;
float angleCheck;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
check_yaw_min = (float)atof( condition.getParm( 1 ) );
check_yaw_max = (float)atof( condition.getParm( 2 ) );
//Normalize the Angles
check_yaw_min = AngleNormalize180(check_yaw_min);
check_yaw_max = AngleNormalize180(check_yaw_max);
dir = currentEnemy->origin - origin;
dirYaw = dir.toYaw();
originYaw = angles[YAW];
angleCheck = AngleNormalize180(dirYaw - originYaw);
//Special Case Check for the 180 Problem
if ( check_yaw_min > check_yaw_max )
{
if ( ( angleCheck < 180.0f ) && ( angleCheck > check_yaw_min ) )
return true;
if ( ( angleCheck > -180.0f ) && ( angleCheck < check_yaw_max ) )
return true;
}
if ( ( angleCheck >= check_yaw_min ) && ( angleCheck <= check_yaw_max ) )
return true;
else
return false;
}
qboolean Actor::checkcanjumptoenemy( Conditional &condition )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
return ( movementSubsystem->CanWalkTo( currentEnemy->origin, 0.0f, currentEnemy->entnum ) );
}
qboolean Actor::checkcanflytoenemy( Conditional &condition )
{
trace_t trace;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
trace = G_Trace( origin, mins, maxs, currentEnemy->centroid, this, edict->clipmask, false, "Actor::checkcanflytoenemy" );
if ( trace.startsolid || trace.allsolid )
return false;
if ( trace.entityNum == currentEnemy->entnum )
return true;
return false;
}
qboolean Actor::checkinpain( Conditional &condition )
{
return ( state_flags & STATE_FLAG_IN_PAIN );
}
qboolean Actor::checksmallpain( Conditional &condition )
{
return ( state_flags & STATE_FLAG_SMALL_PAIN );
}
qboolean Actor::checkpainyaw( Conditional &condition )
{
float check_yaw;
check_yaw = (float)atof( condition.getParm( 1 ) );
if ( pain_angles[YAW] <= check_yaw )
return true;
else
return false;
}
qboolean Actor::checkpainpitch( Conditional &condition )
{
float check_pitch;
check_pitch = (float)atof( condition.getParm( 1 ) );
if ( pain_angles[PITCH] <= check_pitch )
return true;
else
return false;
}
qboolean Actor::checkstunned( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_STUNNED );
}
qboolean Actor::checkfinished( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_FINISHED );
}
qboolean Actor::checkmeleehit( Conditional &condition )
{
return ( state_flags & STATE_FLAG_MELEE_HIT );
}
qboolean Actor::checkblockedhit( Conditional &condition )
{
return ( state_flags & STATE_FLAG_BLOCKED_HIT );
}
qboolean Actor::checkblocked( Conditional &condition )
{
if ( attack_blocked && ( attack_blocked_time + .75 > level.time ) )
{
attack_blocked = false;
return true;
}
else
return false;
}
qboolean Actor::checkonfire( Conditional &condition )
{
return on_fire;
}
qboolean Actor::checkotherdied( Conditional &condition )
{
return ( state_flags & STATE_FLAG_OTHER_DIED );
}
qboolean Actor::checkstuck( Conditional &condition )
{
return ( state_flags & STATE_FLAG_STUCK );
}
qboolean Actor::checknopath( Conditional &condition )
{
return ( state_flags & STATE_FLAG_NO_PATH );
}
qboolean Actor::checkbehaviordone( Conditional &condition )
{
return ( behavior == NULL );
}
qboolean Actor::checkheadbehaviordone( Conditional &condition )
{
return ( headBehavior == NULL );
}
qboolean Actor::checkeyebehaviordone( Conditional &condition )
{
return ( eyeBehavior == NULL );
}
qboolean Actor::checktorsobehaviordone( Conditional &condition )
{
return ( torsoBehavior == NULL );
}
qboolean Actor::checktorsobehaviorfailed( Conditional &condition )
{
return (
(torsoBehaviorCode != BEHAVIOR_SUCCESS)
&& (torsoBehaviorCode != BEHAVIOR_EVALUATING )
);
}
qboolean Actor::checktorsobehaviorsuccess( Conditional &condition )
{
return (torsoBehaviorCode == BEHAVIOR_SUCCESS);
}
qboolean Actor::checktimedone( Conditional &condition )
{
if ( GetActorFlag( ACTOR_FLAG_STATE_DONE_TIME_VALID ) )
{
return ( state_done_time < level.time );
}
return false;
}
qboolean Actor::checkdone ( Conditional &condition )
{
return ( checkbehaviordone( condition ) || checktimedone( condition ) );
}
qboolean Actor::checkenemyrange ( Conditional &condition )
{
float range;
float min_height;
float max_height;
int XYOnly;
bool onlyXY;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
enemyManager->FindHighestHateEnemy();
if ( !currentEnemy )
return false;
range = (float)atof( condition.getParm( 1 ) );
if ( condition.numParms() > 1 )
max_height = (float)atof( condition.getParm( 2 ) );
else
max_height = 0;
if ( condition.numParms() > 2 )
min_height = (float)atof( condition.getParm( 3 ) );
else
min_height = -max_height;
if ( condition.numParms() > 3 )
XYOnly = atoi( condition.getParm( 4 ) );
else
XYOnly = 0;
// Stupid compiler warning complaints about forcing an int to bool
// So that's why this BS is here
if ( XYOnly )
onlyXY = true;
else
onlyXY = false;
/*
Vector temp;
temp = currentEnemy->origin - origin;
float templen = temp.length();
*/
return EntityInRange( currentEnemy, range, min_height, max_height , onlyXY );
}
qboolean Actor::checkEnemyAttached( Conditional &condition )
{
return haveAttached;
}
qboolean Actor::checkparentrange( Conditional &condition )
{
float range;
float height;
Actor *act;
if ( !spawnparent ) return false;
range = (float)atof( condition.getParm( 1 ) );
if ( condition.numParms() == 2 )
height = (float)atof( condition.getParm( 2 ) );
else
height = 0.0f;
act = (Actor*)(Entity*)spawnparent;
if( EntityInRange( act, range, -height, height ) )
{
return true;
}
return false;
}
qboolean Actor::checkplayerrange( Conditional &condition )
{
float range;
float height;
bool XYOnly;
Player *player;
range = (float)atof( condition.getParm( 1 ) );
if ( condition.numParms() == 2 )
height = (float)atof( condition.getParm( 2 ) );
else
height = 0.0f;
XYOnly = false;
if ( condition.numParms() == 3 )
{
int XYCheck = atoi(condition.getParm( 3 ) );
if ( XYCheck )
XYOnly = true;
}
for(int i = 0; i < game.maxclients; i++)
{
player = GetPlayer(i);
if( EntityInRange( player, range, -height, height, XYOnly ) )
{
return true;
}
}
return false;
}
qboolean Actor::checkplayerrange( float range, float height )
{
Player *player;
for(int i = 0; i < game.maxclients; i++)
{
player = GetPlayer(i);
if( EntityInRange( player, range, -height, height ) )
{
return true;
}
}
return false;
}
qboolean Actor::checkmovingactorrange( Conditional &condition )
{
float range;
float height = 0;
float height_diff;
Entity *ent_in_range;
int i;
Vector delta;
float r2;
gentity_t *ed;
float smallest_dist2;
float dist2;
// Get distances
range = (float)atof( condition.getParm( 1 ) );
if ( condition.numParms() == 2 )
{
height = (float)atof( condition.getParm( 2 ) );
}
r2 = range * range;
if ( ( actorrange_time > level.time ) && ( height == last_height ) )
{
ent_in_range = last_ent;
if ( IsEntityAlive( ent_in_range ) )
{
delta = origin - ent_in_range->centroid;
if ( height )
{
height_diff = delta[ 2 ];
delta[ 2 ] = 0;
if ( ( height_diff < -height ) || ( height_diff > height ) )
return false;
}
if ( sensoryPerception )
{
// dot product returns length squared
if ( ( ( delta * delta ) <= r2 ) && sensoryPerception->CanSeeEntity( this, ent_in_range, true, true ))
return true;
}
}
return false;
}
actorrange_time = level.time + 0.2f + G_Random( 0.1f );
last_height = height;
last_ent = NULL;
smallest_dist2 = 99999999;
// See if any clients are in range
for( i = 0 ; i < game.maxclients; i++ )
{
ed = &g_entities[ i ];
if ( !ed->inuse || !ed->entity )
{
continue;
}
ent_in_range = ed->entity;
if ( IsEntityAlive( ent_in_range ) )
{
delta = origin - ent_in_range->centroid;
if ( height > 0.0f )
{
height_diff = delta[ 2 ];
if ( ( height_diff < -height ) || ( height_diff > height ) )
{
continue;
}
delta[ 2 ] = 0;
}
// dot product returns length squared
dist2 = delta * delta;
if ( sensoryPerception )
{
if ( ( dist2 <= r2 ) && sensoryPerception->CanSeeEntity( this , ent_in_range, true, true ) )
{
if ( dist2 < smallest_dist2 )
{
smallest_dist2 = dist2;
last_ent = ent_in_range;
}
}
}
}
}
// See if any actors are in range
for( i = 1; i <= ActiveList.NumObjects(); i++ )
{
ent_in_range = ActiveList.ObjectAt( i );
if (
( ent_in_range->movetype != MOVETYPE_NONE ) &&
( ent_in_range->movetype != MOVETYPE_STATIONARY ) &&
( this != ent_in_range ) &&
( ent_in_range->health > 0 ) &&
!( ent_in_range->flags & FL_NOTARGET )
)
{
delta = origin - ent_in_range->centroid;
if ( height > 0.0f )
{
height_diff = delta[ 2 ];
if ( ( height_diff < -height ) || ( height_diff > height ) )
{
continue;
}
delta[ 2 ] = 0.0f;
}
// dot product returns length squared
dist2 = delta * delta;
if ( sensoryPerception )
{
if ( ( dist2 <= r2 ) && sensoryPerception->CanSeeEntity( this, ent_in_range , true , true ) )
{
if ( dist2 < smallest_dist2 )
{
smallest_dist2 = dist2;
last_ent = ent_in_range;
}
}
}
}
}
if ( last_ent )
return true;
return false;
}
qboolean Actor::checkchance( Conditional &condition )
{
float percent_chance;
bool checkedChance = false;
percent_chance = (float)atof( condition.getParm( 1 ) );
//Stupid crazy conversion here, not because I am stupid, but becaus"e the
//compiler is...
if ( condition.numParms() > 1 )
{
int value = atoi( condition.getParm( 2 ) );
if ( value > 0 )
checkedChance = true;
}
if ( checkedChance && _checkedChance )
return false;
if ( checkedChance && !_checkedChance )
_checkedChance = true;
return ( G_Random() < percent_chance );
}
qboolean Actor::checkstatetime( Conditional &condition )
{
float time_to_wait;
time_to_wait = (float)atof( condition.getParm( 1 ) );
return ( state_time + time_to_wait < level.time );
}
qboolean Actor::checktimesdone( Conditional &condition )
{
return ( times_done == atoi( condition.getParm( 1 ) ) );
}
qboolean Actor::checkmeansofdeath( Conditional &condition )
{
int mod;
mod = MOD_NameToNum( condition.getParm( 1 ) );
return ( mod == means_of_death );
}
qboolean Actor::checknoiseheard( Conditional &condition )
{
str soundTypeStr;
int soundTypeIdx;
if ( !sensoryPerception )
return false;
if (condition.numParms() > 0 )
{
soundTypeStr = condition.getParm( 1 );
soundTypeIdx = Soundtype_string_to_int( soundTypeStr );
if ( soundTypeIdx == sensoryPerception->GetLastSoundType())
{
//Clear our soundtype
sensoryPerception->SetLastSoundType( SOUNDTYPE_NONE );
return true;
}
else
{
return false;
}
}
return GetActorFlag( ACTOR_FLAG_NOISE_HEARD );
}
qboolean Actor::checkpartstate( Conditional &condition )
{
str part_name;
str state_name;
Actor *part;
part_name = condition.getParm( 1 );
state_name = condition.getParm( 2 );
part = FindPartActor( part_name );
return ( part && part->currentState && ( strnicmp( part->currentState->getName(), state_name.c_str(), strlen( state_name.c_str() ) ) == 0 ) );
}
qboolean Actor::checkpartflag( Conditional &condition )
{
str part_name;
str flag_name;
unsigned int flag;
int current_part;
part_t *part;
Entity *partent;
Actor *partact;
part_name = condition.getParm( 1 );
flag_name = condition.getParm( 2 );
if ( stricmp( flag_name, "pain" ) == 0 )
{
flag = STATE_FLAG_IN_PAIN;
}
else if ( stricmp( flag_name, "small_pain" ) == 0 )
{
flag = STATE_FLAG_SMALL_PAIN;
}
else if ( stricmp( flag_name, "melee_hit" ) == 0 )
{
flag = STATE_FLAG_MELEE_HIT;
}
else if ( stricmp( flag_name, "touched" ) == 0 )
{
flag = STATE_FLAG_TOUCHED;
}
else if ( stricmp( flag_name, "activated" ) == 0 )
{
flag = STATE_FLAG_ACTIVATED;
}
else if ( stricmp( flag_name, "used" ) == 0 )
{
flag = STATE_FLAG_USED;
}
else if ( stricmp( flag_name, "twitch" ) == 0 )
{
flag = STATE_FLAG_TWITCH;
}
else
{
gi.WDPrintf( "Unknown flag name (%s) in checkpartflag.", flag_name.c_str() );
flag = 0;
}
for( current_part = 1; current_part <= parts.NumObjects(); current_part++ )
{
part = &parts.ObjectAt( current_part );
partent = part->ent;
partact = (Actor *)partent;
if ( partact && ( partact->part_name == part_name ) )
{
if ( part->state_flags & flag )
{
return true;
}
}
}
return false;
}
qboolean Actor::checkpartdead( Conditional &condition )
{
str part_name;
str state_name;
Actor *part;
part_name = condition.getParm( 1 );
part = FindPartActor( part_name );
if ( !part )
return false;
return ( part->deadflag || ( part->health <= 0 ) );
}
qboolean Actor::checknumspawns( Conditional &condition )
{
int check_num;
check_num = atoi( condition.getParm( 1 ) );
return ( num_of_spawns < check_num );
}
qboolean Actor::checkcommand( Conditional &condition )
{
return ( command == condition.getParm( 1 ) );
}
qboolean Actor::checktouched( Conditional &condition )
{
return state_flags & STATE_FLAG_TOUCHED;
}
qboolean Actor::checktouchedbyplayer ( Conditional &condition )
{
return checktouchedbyplayer();
}
qboolean Actor::checktouchedbyplayer()
{
return state_flags & STATE_FLAG_TOUCHED_BY_PLAYER;
}
qboolean Actor::checkInTheWay( Conditional &condition )
{
return checkInTheWay();
}
qboolean Actor::checkInTheWay()
{
if ( state_flags & STATE_FLAG_IN_THE_WAY )
return true;
if ( state_flags & STATE_FLAG_TOUCHED_BY_PLAYER )
return true;
return false;
}
qboolean Actor::checkactivated( Conditional &condition )
{
return state_flags & STATE_FLAG_ACTIVATED;
}
qboolean Actor::checkused( Conditional &condition )
{
return ( state_flags & STATE_FLAG_USED );
}
qboolean Actor::checktwitch( Conditional &condition )
{
return ( state_flags & STATE_FLAG_TWITCH );
}
qboolean Actor::checkhealth( Conditional &condition )
{
return ( health < (float)atof( condition.getParm( 1 ) ) );
}
qboolean Actor::checkonground( Conditional &condition )
{
CheckGround();
return groundentity != NULL;
}
qboolean Actor::checkinwater( Conditional &condition )
{
return (waterlevel > 0 );
}
qboolean Actor::checkincomingmeleeattack( Conditional &condition )
{
return checkincomingmeleeattack();
}
qboolean Actor::checkincomingmeleeattack()
{
//Entity *enemy_ent;
Sentient *enemy;
trace_t trace;
Vector forward;
Vector end_pos;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( IsEntityAlive( currentEnemy ) )
{
if ( currentEnemy->isSubclassOf( Sentient ) )
{
//enemy_ent = ( Entity * )currentEnemy;
enemy = ( Sentient * )( Entity * )currentEnemy;
if ( enemy->in_melee_attack )
{
enemy->angles.AngleVectors( &forward );
end_pos = ( forward * 160.0f ) + enemy->centroid;
trace = G_Trace( enemy->centroid, vec_zero, vec_zero, end_pos, enemy, MASK_SHOT, false, "Actor::checkincomingmeleeattack" );
if ( trace.entityNum == entnum )
{
return true;
}
}
}
}
return false;
}
qboolean Actor::checkincomingrangedattack( Conditional &condition )
{
//Entity *enemy_ent;
Sentient *enemy;
trace_t trace;
Vector forward;
Vector end_pos;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( IsEntityAlive( currentEnemy ) )
{
if ( currentEnemy->isSubclassOf( Sentient ) )
{
//enemy_ent = ( Entity * )currentEnemy;
enemy = ( Sentient * )( Entity * )currentEnemy;
if ( enemy->in_ranged_attack )
{
enemy->angles.AngleVectors( &forward );
end_pos = ( forward * 125.0f ) + enemy->centroid;
trace = G_Trace( enemy->centroid, vec_zero, vec_zero, origin, enemy, MASK_SHOT, false, "Actor::checkincomingrangedattack" );
if ( trace.entityNum == entnum )
{
return true;
}
}
}
}
return false;
}
qboolean Actor::checkincomingprojectile( Conditional &condition )
{
trace_t trace;
Vector forward;
Vector end_pos;
float time = 0;
float time_left;
Vector dir;
float dist;
if ( condition.numParms() == 1 )
{
time = (float)atof( condition.getParm( 1 ) );
}
if ( incoming_proj && ( incoming_time <= level.time ) )
{
incoming_proj->angles.AngleVectors( &forward );
end_pos = ( forward * 1000.0f ) + incoming_proj->centroid;
trace = G_Trace( incoming_proj->centroid, vec_zero, vec_zero, end_pos, incoming_proj, MASK_SHOT, false, "Actor::checkincomingprojectile" );
if ( trace.entityNum == entnum )
{
if ( time )
{
dir = trace.endpos - incoming_proj->centroid;
dist = dir.length();
time_left = dist / incoming_proj->velocity.length();
return ( time_left <= time );
}
return true;
}
}
return false;
}
qboolean Actor::checkenemystunned( Conditional &condition )
{
Sentient *enemy;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( IsEntityAlive( currentEnemy ) )
{
if ( currentEnemy->isSubclassOf( Sentient ) )
{
enemy = (Sentient *)(Entity *)currentEnemy;
if ( enemy->in_stun )
return true;
}
}
return false;
}
qboolean Actor::checkenemyinpath( Conditional &condition )
{
trace_t trace;
Vector end_pos;
int mask;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( IsEntityAlive( currentEnemy ) )
{
Vector forward( orientation[ 0 ] );
forward *= 1000.0f;
end_pos = origin + forward;
// Pick a reasonable mask (most actors will just use their normal mask, actors that can walk though
// actors use MASK_SHOT)
if ( edict->clipmask & CONTENTS_BODY )
mask = edict->clipmask;
else
mask = MASK_SHOT;
trace = G_Trace( centroid, vec_zero, vec_zero, end_pos, this, mask, false, "Actor::checkenemyinpath" );
if ( trace.entityNum == currentEnemy->entnum )
{
return true;
}
}
return false;
}
qboolean Actor::checkstage( Conditional &condition )
{
return ( stage == (float)atoi( condition.getParm( 1 ) ) );
}
qboolean Actor::checkheld( Conditional &condition )
{
return ( edict->s.parent != ENTITYNUM_NONE );
}
qboolean Actor::checkenemymelee( Conditional &condition )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
return ( EntityHasFireType( currentEnemy, FT_MELEE ) );
}
qboolean Actor::checkenemyranged( Conditional &condition )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
return ( EntityHasFireType( currentEnemy, FT_BULLET ) || EntityHasFireType( currentEnemy, FT_PROJECTILE ) );
}
qboolean Actor::checkplayerranged( Conditional &condition )
{
return checkplayerranged();
}
qboolean Actor::checkplayerranged()
{
Player *player;
player = NULL;
player = GetPlayer( 0 );
if ( !player ) return false;
return ( EntityHasFireType( player, FT_BULLET ) || EntityHasFireType( player, FT_PROJECTILE ) );
}
qboolean Actor::checkhasthing( Conditional &condition )
{
int thing_number;
int i;
for( i = 1 ; i <= condition.numParms() ; i++ )
{
thing_number = atoi( condition.getParm( i ) );
switch( thing_number )
{
case 1 :
if ( GetActorFlag( ACTOR_FLAG_HAS_THING1 ) )
return true;
break;
case 2 :
if ( GetActorFlag( ACTOR_FLAG_HAS_THING2 ) )
return true;
break;
case 3 :
if ( GetActorFlag( ACTOR_FLAG_HAS_THING3 ) )
return true;
break;
case 4 :
if ( GetActorFlag( ACTOR_FLAG_HAS_THING4 ) )
return true;
break;
}
}
return false;
}
qboolean Actor::checkatcovernode( Conditional &condtion )
{
return GetActorFlag( ACTOR_FLAG_AT_COVER_NODE );
}
qboolean Actor::checkallowhangback( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_ALLOW_HANGBACK );
}
qboolean Actor::checkname( Conditional &condition )
{
return ( name == condition.getParm( 1 ) );
}
qboolean Actor::checkVar( Conditional &condition )
{
StateVar *checkVar = 0;
str varName = condition.getParm( 1 );
str varValue = condition.getParm( 2 );
for (int i = 1; i <= stateVarList.NumObjects() ; i++ )
{
checkVar = stateVarList.ObjectAt( i );
if( !stricmp ( checkVar->varName, varName ) )
{
if (checkVar->varValue == varValue )
return true;
else
return false;
}
}
//Need to throw and exception here, the var in question is not in the list
//gi.WDPrintf( "Var %s is not in stateVarList\n", varName.c_str() );
return false;
}
//--------------------------------------------------------------
// Name: checkVarTimeDifference
// Class: Actor
//
// Description: Checks if the difference between the statevar's time
// and the current level time is greater than or equal to
// the time specified in the conditional
//
// Parameters: Conditional &condition
//
// Returns: None
//--------------------------------------------------------------
qboolean Actor::checkVarTimeDifference( Conditional &condition )
{
StateVar *checkVar = NULL;
str varName = condition.getParm( 1 );
float varTime = atof(condition.getParm( 2 ));
for (int i = 1; i <= stateVarList.NumObjects() ; i++ )
{
checkVar = stateVarList.ObjectAt( i );
if( !stricmp ( checkVar->varName, varName ) )
{
if ( level.time - checkVar->varTime >= varTime )
return true;
else
return false;
}
}
//Need to throw and exception here, the var in question is not in the list
//gi.WDPrintf( "Var %s is not in stateVarList\n", varName.c_str() );
return false;
}
qboolean Actor::checkNodeExists( Conditional &condition )
{
str nodeName = condition.getParm( 1 );
PathNode *testNode = 0;
nodeName += "0";
testNode = thePathManager.FindNode( nodeName );
if( testNode )
{
return true;
}
return false;
}
qboolean Actor::checkCoverNodes( Conditional &condition )
{
for ( int i = 1 ; i <= thePathManager.NumberOfSpecialNodes(); i++ )
{
PathNode *node = thePathManager.GetSpecialNode( i );
if ( node && ( node->nodeflags & ( AI_DUCK | AI_COVER ) ) &&
( ( node->occupiedTime <= level.time ) || ( node->entnum == entnum ) ) )
{
return true;
}
}
return false;
}
qboolean Actor::checkSurfaceDamaged( Conditional &condition )
{
int surface_number;
str surface_name = condition.getParm( 1 );
if ( last_surface_hit == -1 )
return false;
surface_number = gi.Surface_NameToNum( edict->s.modelindex, surface_name.c_str() );
if ( surface_number == last_surface_hit )
return true;
else
return false;
}
qboolean Actor::checkBoneDamaged( Conditional &condition )
{
int bone_number;
str bone_name = condition.getParm( 1 );
if ( saved_bone_hit == -9999 )
return false;
bone_number = gi.Tag_NumForName( edict->s.modelindex, bone_name.c_str() );
if ( bone_number == saved_bone_hit )
return true;
else
return false;
}
qboolean Actor::checkRegionDamaged( Conditional &condition )
{
int region_bit;
str region_name;
// See if any region has been hit
if ( last_region_hit == 0 )
return false;
// Get the region to test
region_name = condition.getParm( 1 );
// Figure out which bit to check
region_bit = 0;
if ( region_name == "back" )
region_bit = REGIONAL_DAMAGE_BACK;
else if ( region_name == "front" )
region_bit = REGIONAL_DAMAGE_FRONT;
// Return whether or not this region has been hit
return ( last_region_hit & region_bit );
}
qboolean Actor::checkCaptured( Conditional &condition )
{
return ( GetActorFlag(ACTOR_FLAG_CAPTURED) );
}
qboolean Actor::checkCanWalkForward( Conditional &condition )
{
trace_t trace;
Vector endpos;
Vector startpos;
str rangestr;
float range;
angles.AngleVectors(&endpos); //Get Forward direction
endpos += origin;
endpos.z += 50.0f; // Pull it off the ground a little
rangestr = condition.getParm( 1 );
range = (float)atof( rangestr.c_str() );
endpos *= range;
startpos = origin;
startpos.z += 50.0f;
trace = G_Trace( startpos, mins, maxs, endpos, this, edict->clipmask, false, "Actor::start" );
if (trace.fraction == 1.0f )
return true;
else
return false;
}
qboolean Actor::checkHasThrowObject( Conditional &condition )
{
return haveThrowObject;
}
qboolean Actor::checkEnemyIsThrowObject( Conditional &condition )
{
Entity* ent = 0;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
ent = (Entity*)currentEnemy;
if ( ent->isSubclassOf(ThrowObject) )
return true;
else
return false;
}
qboolean Actor::checkTurretMode( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_TURRET_MODE );
}
qboolean Actor::checkGameSpecific( Conditional &condition )
{
if (!gameComponent)
return false;
return gameComponent->DoCheck( condition );
}
qboolean Actor::checkWeaponReady( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_WEAPON_READY );
}
qboolean Actor::checkMeleeHitWorld( Conditional &condition )
{
qboolean hit;
if ( GetActorFlag( ACTOR_FLAG_MELEE_HIT_WORLD ) )
{
hit = true;
SetActorFlag( ACTOR_FLAG_MELEE_HIT_WORLD, false );
}
else
hit = false;
return hit;
}
qboolean Actor::checkPlayerValid( Conditional &condition )
{
Player *player = GetPlayer(0);
if ( !player )
return false;
if ( EntityIsValidTarget( player ) )
return true;
return false;
}
qboolean Actor::checkInAbsoluteRange( Conditional &condition )
{
if ( !Q_stricmp( condition.getParm( 1 ) , "player" ) )
{
Player *player;
Entity *enemy;
Vector dist;
float length;
player = GetPlayer( 0 );
if ( !player )
return false;
dist = origin - player->origin;
length = dist.length();
enemy = NULL;
enemy = enemyManager->GetCurrentEnemy();
if ( enemy )
{
if ( ( length < (absoluteMax * 2 ) ) && ( length > absoluteMin ) )
return true;
}
if ( ( length < absoluteMax ) && ( length > absoluteMin ) )
return true;
}
return false;
}
qboolean Actor::checkInPreferredRange( Conditional &condition )
{
if ( !Q_stricmp( condition.getParm( 1 ) , "player" ) )
{
Player *player;
Vector dist;
float length;
player = GetPlayer( 0 );
if ( !player )
return false;
dist = origin - player->origin;
length = dist.length();
if ( ( length < preferredMax ) && ( length > preferredMin ) )
return true;
}
return false;
}
qboolean Actor::checkCrippled( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_CRIPPLED );
}
qboolean Actor::checkDisabled( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_DISABLED );
}
qboolean Actor::checkInAlcove( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_IN_ALCOVE );
}
qboolean Actor::checkPlayerInCallVolume( Conditional &condition )
{
return GetActorFlag(ACTOR_FLAG_PLAYER_IN_CALL_VOLUME);
}
qboolean Actor::checkInCallVolume( Conditional &condition )
{
return GetActorFlag(ACTOR_FLAG_IN_CALL_VOLUME);
}
qboolean Actor::checkUsingWeaponNamed( Conditional &condition )
{
str weaponName = condition.getParm( 1 );
return checkUsingWeaponNamed ( weaponName );
}
qboolean Actor::checkUsingWeaponNamed ( const str &name )
{
return combatSubsystem->UsingWeaponNamed( name );
}
qboolean Actor::checkOutOfTorsoRange( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_OUT_OF_TORSO_RANGE );
}
qboolean Actor::returntrue( Conditional &condition )
{
return true;
}
//--------------------------------------------------------------
//
// Name: checkPropChance
// Class: Actor
//
// Description: Check the chance based on the property passed.
// This property should be located in the Actor's
// StateData object in the gameplay database.
//
// Parameters: Conditional &conditional
// - objname -- Name of the object to check
// - propname -- Name of the property to get the chance for.
//
// Returns: qboolean
//
//--------------------------------------------------------------
qboolean Actor::checkPropChance( Conditional &condition )
{
str propname;
str objname = condition.getParm( 1 );
str scopestr;
if ( condition.numParms() > 1 )
propname = condition.getParm( 2 );
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
if ( !objname.length() )
scopestr = getArchetype();
else
scopestr = getArchetype() + "." + objname;
if ( !gpm->hasObject(scopestr) )
return false;
float chance;
if ( propname.length() )
chance = gpm->getFloatValue(scopestr, propname);
else
chance = gpm->getFloatValue(scopestr, "value");
return ( G_Random() <= chance );
}
//--------------------------------------------------------------
//
// Name: checkPropExists
// Class: Actor
//
// Description: Check to see if the property exists
// The Actor's StateData object will be asked
// if it has the property.
//
// Parameters: Conditional &conditional
// - objname -- Name of the object to check
// - propname -- Name of the property to check for
//
// Returns: qboolean
//
//--------------------------------------------------------------
qboolean Actor::checkPropExists( Conditional &condition )
{
str objname = condition.getParm( 1 );
str propname = condition.getParm( 2 );
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
str scopestr = getArchetype() + "." + objname;
if ( !gpm->hasProperty(scopestr, propname) )
return false;
return true;
}
Condition<Actor> Actor::Conditions[] =
{
{ "default", &Actor::returntrue },
{ "INACTIVE", &Actor::checkinactive },
{ "ANIM_DONE", &Actor::checkanimdone },
{ "TORSO_ANIM_DONE", &Actor::checktorsoanimdone },
{ "DEAD", &Actor::checkdead },
{ "ACTOR_FLAG", &Actor::checkActorFlag },
{ "HAVE_ENEMY", &Actor::checkhaveenemy },
{ "ENEMY_DEAD", &Actor::checkenemydead },
{ "ENEMY_NOCLIP", &Actor::checkenemynoclip },
{ "CAN_ATTACK_ENEMY", &Actor::checkCanAttackEnemy },
{ "CAN_SEE_ENEMY", &Actor::checkcanseeenemy },
{ "CAN_SEE_PLAYER", &Actor::checkcanseeplayer },
{ "ENEMY_IN_FOV", &Actor::checkenemyinfov },
{ "ENEMY_RELATIVE_YAW", &Actor::checkenemyrelativeyaw },
{ "CHECK_ENEMY_YAW_RANGE", &Actor::checkenemyyawrange },
{ "CAN_SHOOT_ANY_ENEMY", &Actor::checkCanAttackAnyEnemy },
{ "ENEMY_ON_GROUND", &Actor::checkenemyonground },
{ "CAN_JUMP_TO_ENEMY", &Actor::checkcanjumptoenemy },
{ "CAN_FLY_TO_ENEMY", &Actor::checkcanflytoenemy },
{ "PAIN", &Actor::checkinpain },
{ "SMALL_PAIN", &Actor::checksmallpain },
{ "PAIN_YAW", &Actor::checkpainyaw },
{ "PAIN_PITCH", &Actor::checkpainpitch },
{ "SHOW_PAIN", &Actor::checkShowPain },
{ "STUNNED", &Actor::checkstunned },
{ "FINISHED", &Actor::checkfinished },
{ "MELEE_HIT", &Actor::checkmeleehit },
{ "HIT_WORLD", &Actor::checkMeleeHitWorld },
{ "BLOCKED_HIT", &Actor::checkblockedhit },
{ "BLOCKED", &Actor::checkblocked },
{ "OTHER_DIED", &Actor::checkotherdied },
{ "STUCK", &Actor::checkstuck },
{ "NO_PATH", &Actor::checknopath },
{ "STEERING_FAILED", &Actor::checkSteeringFailed },
{ "HAVE_PATH_TO_ENEMY", &Actor::checkHavePathToEnemy },
{ "ON_FIRE", &Actor::checkonfire },
{ "BEHAVIOR_DONE", &Actor::checkbehaviordone },
{ "BEHAVIOR_SUCCESS", &Actor::checkbehaviorsuccess },
{ "BEHAVIOR_FAILED", &Actor::checkbehaviorfailed },
{ "HEAD_BEHAVIOR_DONE", &Actor::checkheadbehaviordone },
{ "EYE_BEHAVIOR_DONE", &Actor::checkeyebehaviordone },
{ "TORSO_BEHAVIOR_DONE", &Actor::checktorsobehaviordone },
{ "TORSO_BEHAVIOR_FAILED", &Actor::checktorsobehaviorfailed },
{ "TORSO_BEHAVIOR_SUCCESS", &Actor::checktorsobehaviorsuccess },
{ "TIME_DONE", &Actor::checktimedone },
{ "DONE", &Actor::checkdone },
{ "RANGE", &Actor::checkenemyrange },
{ "ENEMY_WITHIN_RANGE", &Actor::checkEnemyWithinRange },
{ "ENEMY_ATTACHED", &Actor::checkEnemyAttached },
{ "PLAYER_RANGE", &Actor::checkplayerrange },
{ "PARENT_RANGE", &Actor::checkparentrange },
{ "CHANCE", &Actor::checkchance },
{ "MOVING_ACTOR_RANGE", &Actor::checkmovingactorrange },
{ "STATE_TIME", &Actor::checkstatetime },
{ "TIMES_DONE", &Actor::checktimesdone },
{ "MOD", &Actor::checkmeansofdeath },
{ "NOISE_HEARD", &Actor::checknoiseheard },
{ "PART_STATE", &Actor::checkpartstate },
{ "PART_DEAD", &Actor::checkpartdead },
{ "PART_FLAG", &Actor::checkpartflag },
{ "NUM_SPAWNS", &Actor::checknumspawns },
{ "COMMAND", &Actor::checkcommand },
{ "TOUCHED", &Actor::checktouched },
{ "TOUCHED_BY_PLAYER", &Actor::checktouchedbyplayer },
{ "ACTIVATED", &Actor::checkactivated },
{ "USED", &Actor::checkused },
{ "TWITCH", &Actor::checktwitch },
{ "HEALTH", &Actor::checkhealth },
{ "HEALTH_PERCENT_LESS_THAN", &Actor::checkhealthpercent },
{ "HEALTH_PERCENT_IN_RANGE", &Actor::checkhealthpercentinrange },
{ "ON_GROUND", &Actor::checkonground },
{ "IN_WATER", &Actor::checkinwater },
{ "INCOMING_MELEE_ATTACK", &Actor::checkincomingmeleeattack },
{ "INCOMING_RANGED_ATTACK", &Actor::checkincomingrangedattack },
{ "INCOMING_PROJECTILE", &Actor::checkincomingprojectile },
{ "ENEMY_STUNNED", &Actor::checkenemystunned },
{ "ENEMY_IN_PATH", &Actor::checkenemyinpath },
{ "STAGE", &Actor::checkstage },
{ "HELD", &Actor::checkheld },
{ "ENEMY_HAS_MELEE", &Actor::checkenemymelee },
{ "ENEMY_HAS_RANGED", &Actor::checkenemyranged },
{ "PLAYER_HAS_WEAPON", &Actor::checkplayerranged },
{ "HAS_THING", &Actor::checkhasthing },
{ "AT_COVER_NODE", &Actor::checkatcovernode },
{ "ALLOW_HANGBACK", &Actor::checkallowhangback },
{ "NAME", &Actor::checkname },
{ "ANIM_NAME", &Actor::checkanimname },
{ "CHECK_VAR", &Actor::checkVar },
{ "CHECK_VAR_TIME_DIFFERENCE", &Actor::checkVarTimeDifference },
{ "NODE_EXISTS", &Actor::checkNodeExists },
{ "COVER_NODES", &Actor::checkCoverNodes },
{ "SURFACE_DAMAGED", &Actor::checkSurfaceDamaged },
{ "BONE_DAMAGED", &Actor::checkBoneDamaged },
{ "REGION_DAMAGED", &Actor::checkRegionDamaged },
{ "CAPTURED", &Actor::checkCaptured },
{ "CAN_WALK_FORWARD", &Actor::checkCanWalkForward },
{ "HAS_THROWOBJECT", &Actor::checkHasThrowObject },
{ "ENEMY_IS_THROWOBJECT", &Actor::checkEnemyIsThrowObject },
{ "TURRET_MODE", &Actor::checkTurretMode },
{ "WEAPON_READY", &Actor::checkWeaponReady },
{ "PLAYER_VALID", &Actor::checkPlayerValid },
{ "IN_PREFERRED_RANGE", &Actor::checkInPreferredRange },
{ "IN_ABSOLUTE_RANGE", &Actor::checkInAbsoluteRange },
{ "IN_AI_MODE", &Actor::checkInAIMode },
{ "DISABLED", &Actor::checkDisabled },
{ "CRIPPLED", &Actor::checkCrippled },
{ "IN_ALCOVE", &Actor::checkInAlcove },
{ "PLAYER_IN_CALL_VOLUME", &Actor::checkPlayerInCallVolume },
{ "IN_CALL_VOLUME", &Actor::checkInCallVolume },
{ "IS_AGGRESSIVE", &Actor::checkIsAggressive },
{ "IN_CONE_OF_FIRE", &Actor::checkInConeOfFire },
{ "IN_PLAYER_CONE_OF_FIRE", &Actor::checkInPlayerConeOfFire },
{ "PATROL_NODE_IN_DISTANCE", &Actor::checkPatrolWaypointNodeInDistance },
{ "PATH_NODE_IN_DISTANCE", &Actor::checkPathNodeTypeInDistance },
{ "WEAPON_NAMED", &Actor::checkUsingWeaponNamed },
{ "OUT_OF_TORSO_RANGE", &Actor::checkOutOfTorsoRange },
{ "WANTS_TO_EXECUTE_PACKAGE", &Actor::checkWantsToExecutePackage },
{ "EXECUTED_IN_LAST", &Actor::checkExecutedPackageInLastTimeFrame },
{ "FORWARD_CLEAR", &Actor::checkForwardDirectionClear },
{ "REAR_CLEAR", &Actor::checkRearDirectionClear },
{ "LEFT_CLEAR", &Actor::checkLeftDirectionClear },
{ "RIGHT_CLEAR", &Actor::checkRightDirectionClear },
{ "LAST_STATE", &Actor::checkLastState },
{ "GROUP_MEMBER_IN_RANGE", &Actor::checkGroupMememberRange },
{ "ACTORTYPE", &Actor::checkActorType },
{ "IS_TEAMMATE", &Actor::checkIsTeammate },
{ "HAVE_ACTIVE_WEAPON", &Actor::checkHaveActiveWeapon },
{ "WEAPON_IS_MELEE", &Actor::checkWeaponIsMelee },
{ "WEAPON_CHANGED", &Actor::checkWeaponChanged },
{ "GROUP_HAS_THIS_NAME_LESS_THAN", &Actor::checkCountOfIdenticalNamesInGroup },
{ "REQUESTED_POSTURE", &Actor::checkRequestedPosture },
{ "POSTURE_ANIM_DONE", &Actor::checkPostureAnimDone },
{ "DAMAGE_THRESHOLD_EXCEEDED", &Actor::checkDamageThresholdExceeded },
{ "ATTACKED", &Actor::checkAttacked },
{ "ATTACKED_BY_PLAYER", &Actor::checkAttackedByPlayer },
{ "HELPERNODE_FLAGGED_IN_RANGED" , &Actor::checkHelperNodeWithFlagInRange },
{ "ENEMY_USING_WEAPON_NAMED", &Actor::checkEnemyWeaponNamed },
{ "PLAYER_USING_WEAPON_NAMED", &Actor::checkPlayerWeaponNamed },
{ "GROUP_HAS_NUMATTACKERS_LESS_THAN", &Actor::checkGroupAttackerCount },
{ "CURRENT_ENEMY_HAS_NUMATTACKERS_LESS_THAN", &Actor::checkCurrentEnemyGroupAttackerCount },
{ "HAVE_BEST_WEAPON" , &Actor::checkHaveBestWeapon },
{ "POSTURE", &Actor::checkPosture },
{ "ANY_ENEMY_IN_RANGE", &Actor::checkAnyEnemyInRange },
{ "VALID_COVER_NODE_IN_RANGE", &Actor::checkValidCoverNodeInRange },
{ "VALID_COMBAT_NODE_IN_RANGE", &Actor::checkValidCombatNodeInRange },
{ "VALID_WORK_NODE_IN_RANGE", &Actor::checkValidWorkNodeInRange },
{ "VALID_HIBERNATE_NODE_IN_RANGE", &Actor::checkValidHibernateNodeInRange },
{ "VALID_PATROL_NODE_IN_RANGE", &Actor::checkValidPatrolNodeInRange },
{ "VALID_CUSTOM_NODE_IN_RANGE", &Actor::checkValidCustomNodeInRange },
{ "VALID_SNIPER_NODE_IN_RANGE", &Actor::checkValidSniperNodeInRange },
{ "ENEMY_CAN_SEE_CURRENT_NODE", &Actor::checkEnemyCanSeeCurrentNode },
{ "FOLLOW_TARGET_OUT_OF_RANGE", &Actor::checkSpecifiedFollowTargetOutOfRange },
{ "WITHIN_FOLLOW_TARGET_MIN_RANGE", &Actor::checkWithinFollowRangeMin },
{ "IN_THE_WAY", &Actor::checkInTheWay },
{ "SHOULD_DO_ACTION", &Actor::checkShouldDoAction },
{ "HAVE_ARMOR", &Actor::checkHaveArmor },
{ "ALLOWED_TO_MELEE_ENEMY", &Actor::checkAllowedToMeleeEnemy },
{ "CURRENT_NODE_COVERTYPE", &Actor::checkCurrentNodeHasThisCoverType },
{ "BLOCKED_BY_ENEMY", &Actor::checkBlockedByEnemy },
{ "ENEMY_PROJECTILE_CLOSE", &Actor::checkEnemyProjectileClose },
{ "ACTIVATION_DELAY_DONE", &Actor::checkActivationDelayTime },
{ "TALKING", &Actor::checkTalking },
{ "ANY_ENEMIES_NEARBY", &Actor::checkEnemiesNearby },
//
// Property Conditionals for snagging data from the GPD
//
{ "PROP_EXISTS", &Actor::checkPropExists },
{ "PROP_CHANCE", &Actor::checkPropChance },
{ "PROP_ENEMY_RANGE", &Actor::checkPropEnemyRange },
// Depreciated Conditionals -- Need to be removed as soon as possible
{ "CAN_SHOOT_ENEMY", &Actor::checkcanshootenemy },
{ NULL, NULL }
};
//***********************************************************************************************
//
// Code for seperate parts
//
//***********************************************************************************************
void Actor::RegisterParts( Event *ev )
{
Entity *targetent;
qboolean forward;
int current_part;
part_t *forward_part;
part_t new_part;
Event *event;
targetent = ev->GetEntity( 1 );
forward = ev->GetInteger( 2 );
if ( !targetent )
return;
// See if we should tell other parts about each other
if ( forward )
{
// Tell all old parts about this new part and tell the new part about all of the old ones
for ( current_part = 1 ; current_part <= parts.NumObjects() ; current_part++ )
{
forward_part = &parts.ObjectAt( current_part );
if ( forward_part )
{
// Tell old part about new part
event = new Event( EV_ActorRegisterParts );
event->AddEntity( targetent );
event->AddInteger( false );
forward_part->ent->PostEvent( event, 0.0f );
// Tell new part about old part
event = new Event( EV_ActorRegisterParts );
event->AddEntity( forward_part->ent );
event->AddInteger( false );
targetent->PostEvent( event, 0.0f );
}
}
}
// Add this part to our part list
new_part.ent = targetent;
new_part.state_flags = 0;
parts.AddObject( new_part );
}
void Actor::PartName( Event *ev )
{
part_name = ev->GetString( 1 );
}
void Actor::RegisterSelf( Event *ev )
{
Entity *targetent;
Actor *targetact;
Event *event;
part_t new_part;
if ( target.length() > 0 )
{
// Get the target entity
targetent = G_FindTarget( this, target.c_str() );
if ( !targetent )
return;
// See if this target entity is a another part of ourselves
if ( targetent->isSubclassOf( Actor ) )
{
targetact = (Actor *)targetent;
if ( ( name.length() > 0 ) && ( targetact->name == name ) )
{
// Tell other part about ourselves
event = new Event( EV_ActorRegisterParts );
event->AddEntity( this );
event->AddInteger( true );
targetent->PostEvent( event, 0.0f );
// Add this part to our part list
new_part.ent = targetent;
new_part.state_flags = 0;
parts.AddObject( new_part );
}
}
}
}
Actor *Actor::FindPartActor( const char *name )
{
int current_part;
part_t *part;
Entity *partent;
Actor *partact;
for ( current_part = 1 ; current_part <= parts.NumObjects() ; current_part++ )
{
part = &parts.ObjectAt( current_part );
partent = part->ent;
partact = (Actor *)partent;
if ( partact && ( partact->part_name == name ) )
return partact;
}
return NULL;
}
void Actor::SendCommand( Event *ev )
{
str command;
str part_to_send_to;
int i;
part_t *part;
Actor *partact;
command = ev->GetString( 1 );
part_to_send_to = ev->GetString( 2 );
if ( ( command.length() == 0 ) || ( part_to_send_to.length() == 0 ) )
return;
for( i = 1 ; i <= parts.NumObjects(); i++ )
{
part = &parts.ObjectAt( i );
partact = ( Actor * )(Entity *)part->ent;
if ( partact && ( partact->part_name == part_to_send_to ) )
{
partact->command = command;
}
}
}
//***********************************************************************************************
//
// Dialog functions
//
//***********************************************************************************************
qboolean Actor::DialogExists( const str &aliasName )
{
DialogNode_t *dialog_node;
dialog_node = dialog_list;
while(dialog_node != NULL)
{
if ( stricmp(dialog_node->alias_name, aliasName.c_str()) == 0)
return true;
dialog_node = dialog_node->next;
}
return false;
}
void Actor::AddDialog( Event *ev )
{
DialogNode_t *dialog_node;
DialogNode_t *new_node;
new_node = NewDialogNode();
if (new_node != NULL)
{
// Add the alias name to the dialog
strcpy(new_node->alias_name, ev->GetString( 1 ));
// Add all the other parameters to the dialog
AddDialogParms( new_node, ev );
if ( dialog_list == NULL )
{
// Add the new dialog to this dialog list
new_node->next = NULL;
dialog_list = new_node;
return;
}
dialog_node = dialog_list;
while ( dialog_node->next != NULL )
{
dialog_node = dialog_node->next;
}
// Add the new dialog to this dialog list
dialog_node->next = new_node;
new_node->next = NULL;
return;
}
}
DialogNode_t *Actor::NewDialogNode( void )
{
DialogNode_t *dialog_node;
dialog_node = new DialogNode_t;
memset( dialog_node, 0 , sizeof( DialogNode_t ) );
dialog_node->random_percent = 1.0;
dialog_node->dType = DIALOG_TYPE_NORMAL;
return dialog_node;
}
void Actor::AddDialogParms( DialogNode_t *dialog_node, Event *ev )
{
const char *token;
int parm_type;
float temp_float;
int current_parm;
int num_parms;
if ( dialog_node == NULL )
return;
current_parm = 2;
num_parms = ev->NumArgs();
// Get all of the parameters
while( 1 )
{
if ( current_parm > num_parms )
break;
token = ev->GetString( current_parm );
current_parm++;
parm_type = DIALOG_PARM_TYPE_NONE;
if (stricmp(token, "randompick") == 0)
dialog_node->random_flag = true;
else if (stricmp(token, "radiusdialog" ) == 0)
dialog_node->dType = DIALOG_TYPE_RADIUS;
else if (stricmp(token, "greetingdialog" ) == 0)
dialog_node->dType = DIALOG_TYPE_GREETING;
else if (stricmp(token, "combatdialog" ) == 0)
dialog_node->dType = DIALOG_TYPE_COMBAT;
else if (stricmp(token, "playerhas") == 0)
parm_type = DIALOG_PARM_TYPE_PLAYERHAS;
else if (stricmp(token, "playerhasnot") == 0)
parm_type = DIALOG_PARM_TYPE_PLAYERHASNOT;
else if (stricmp(token, "has") == 0)
parm_type = DIALOG_PARM_TYPE_HAS;
else if (stricmp(token, "has_not") == 0)
parm_type = DIALOG_PARM_TYPE_HASNOT;
else if (stricmp(token, "depends") == 0)
parm_type = DIALOG_PARM_TYPE_DEPENDS;
else if (stricmp(token, "dependsnot") == 0)
parm_type = DIALOG_PARM_TYPE_DEPENDSNOT;
else if (stricmp(token, "dependsint") == 0)
parm_type = DIALOG_PARM_TYPE_DEPENDSINT;
else if (stricmp(token, "contextinitiator" ) == 0)
parm_type = DIALOG_PARM_TYPE_CONTEXT_INITIATOR;
else if (stricmp(token, "contextresponse" ) == 0)
parm_type = DIALOG_PARM_TYPE_CONTEXT_RESPONSE;
else if (stricmp(token, "random") == 0)
{
if ( current_parm > num_parms )
break;
token = ev->GetString( current_parm );
current_parm++;
temp_float = (float)atof(token);
if ( ( temp_float >= 0.0f ) && ( temp_float <= 1.0f ) )
dialog_node->random_percent = temp_float;
else
gi.WDPrintf("Random percent out of range for dialog (alias %s)\n", dialog_node->alias_name);
}
else
gi.WDPrintf("Unknown parameter for dialog (alias %s)\n", dialog_node->alias_name);
if (parm_type != DIALOG_PARM_TYPE_NONE)
{
if ( current_parm > num_parms )
break;
token = ev->GetString( current_parm );
current_parm++;
if (dialog_node->number_of_parms < MAX_DIALOG_PARMS)
{
strcpy(dialog_node->parms[dialog_node->number_of_parms].parm, token);
dialog_node->parms[dialog_node->number_of_parms].type = parm_type;
if ( parm_type == DIALOG_PARM_TYPE_DEPENDSINT )
{
token = ev->GetString( current_parm );
current_parm++;
strcpy( dialog_node->parms[ dialog_node->number_of_parms ].parm2, token );
}
dialog_node->number_of_parms++;
}
else
{
gi.WDPrintf("Too many parms for dialog (alias %s)\n", dialog_node->alias_name);
}
}
}
}
void Actor::PlayDialog( Event *ev )
{
Sentient *user = NULL;
const char *dialog_name = NULL;
const char *state_name = NULL;
float volume = DEFAULT_VOL;
float min_dist = DEFAULT_MIN_DIST;
qboolean headDisplay = false;
bool useTalk = false;
if (ev->NumArgs() > 0)
{
dialog_name = ev->GetString( 1 );
if ( strcmp( dialog_name, "" ) == 0 )
dialog_name = NULL;
}
if ( ev->NumArgs() > 1 )
volume = ev->GetFloat( 2 );
if ( ev->NumArgs() > 2 )
{
str minDistString;
min_dist = ev->GetFloat( 3 );
minDistString = ev->GetString( 3 );
if ( stricmp( minDistString.c_str(), LEVEL_WIDE_STRING ) == 0 )
min_dist = LEVEL_WIDE_MIN_DIST;
else
min_dist = ev->GetFloat( 3 );
if ( min_dist >= LEVEL_WIDE_MIN_DIST_CUTOFF )
min_dist = LEVEL_WIDE_MIN_DIST;
}
if ( ev->NumArgs() > 3 )
headDisplay = ev->GetBoolean( 4 );
if ( ev->NumArgs() > 4 )
useTalk = ev->GetBoolean( 4 );
if ( ev->NumArgs() > 5 )
{
state_name = ev->GetString( 6 );
if ( strcmp( state_name, "" ) == 0 )
state_name = NULL;
}
if ( ev->NumArgs() > 6 )
user = (Sentient *)ev->GetEntity( 7 );
//Note: Dialog coming from an event is ALWAYS important, so we want to be able to overide
//current dialog playing
PlayDialog( user, volume, min_dist, dialog_name, state_name, headDisplay, useTalk , true );
}
void Actor::PlayDialog( Sentient *user, float volume, float min_dist, const char *dialog_name, const char *state_name, qboolean headDisplay , bool useTalk , bool important )
{
str real_dialog;
char *morph_target_name;
const char *dialogAnim;
str dialog_anim;
int frame_number;
int index;
int morph_channel;
float dialog_length;
float morph_time;
float amplitude;
Event *new_event;
bool changedAnim;
//First, if we are playing dialog, check if our new dialog is important
if ( GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) && !important )
return;
//If we get here, then our dialog IS important, so let's check again if we're playing
// dialog... If so, then we need to stop our current dialog.
if ( GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) )
StopDialog();
SetActorFlag( ACTOR_FLAG_USING_HUD, false );
if ( dialog_name )
real_dialog = dialog_name;
else
real_dialog = FindDialog( user , DIALOG_TYPE_NORMAL );
if ( !real_dialog.length())
return;
// localize the selected dialog filename
char localizedDialogName[MAX_QPATH];
gi.LocalizeFilePath( real_dialog, localizedDialogName );
//Find the Anim to go along with the dialog, if there is one
dialogAnim = gi.Alias_FindSpecificAnim( edict->s.modelindex , localizedDialogName );
if(dialogAnim)
dialog_anim = dialogAnim;
if ( dialog_anim.length() > 0 )
{
if (gi.Alias_CheckLoopAnim( edict->s.modelindex, localizedDialogName ))
SetAnim(dialog_anim);
else
SetAnim(dialog_anim, EV_Actor_DialogAnimDone );
}
Sound( str( localizedDialogName ), CHAN_DIALOG, volume, min_dist );
SetActorFlag( ACTOR_FLAG_DIALOG_PLAYING, true );
dialog_length = gi.SoundLength( localizedDialogName );
dialog_done_time = level.time + dialog_length;
// Add dialog to player
Player *player = GetPlayer( 0 );
if ( player )
{
if ( headDisplay )
{
player->SetupDialog( this, localizedDialogName );
SetActorFlag( ACTOR_FLAG_USING_HUD, true );
}
else
player->SetupDialog( NULL, localizedDialogName );
}
if ( dialog_length > 0.0f )
{
Event *headTwitchEvent;
if ( state_name != NULL && currentState )
{
if ( ( mode == ACTOR_MODE_SCRIPT ) || ( mode == ACTOR_MODE_IDLE ) )
{
dialog_old_state_name = currentState->getName();
dialog_state_name = state_name;
Event *idle_event = new Event( EV_Actor_Idle );
idle_event->AddString( state_name );
ProcessEvent( idle_event );
}
}
index = -1;
changedAnim = false;
// Start head twitch now
CancelEventsOfType( EV_Actor_SetHeadTwitch );
headTwitchEvent = new Event( EV_Actor_SetHeadTwitch );
headTwitchEvent->AddInteger( true );
ProcessEvent( headTwitchEvent );
// Stop head twitch when dialog is done
headTwitchEvent = new Event( EV_Actor_SetHeadTwitch );
headTwitchEvent->AddInteger( false );
PostEvent( headTwitchEvent, dialog_length );
while( 1 )
{
morph_target_name = (char *)gi.GetNextMorphTarget( localizedDialogName, &index, &frame_number, &amplitude );
if ( index == -1 || !morph_target_name )
break;
// Determine the time we should start this morph
morph_time = frame_number * ( 1.0f / LIP_SYNC_HZ );
// Start 2 frames early
morph_time -= 2.0f * FRAMETIME;
if ( morph_time < 0.0f )
morph_time = 0.0f;
if ( strnicmp( morph_target_name, "emt_", 4 ) == 0 )
{
// Set the emotion
new_event = new Event( EV_Actor_SetEmotion );
new_event->AddString( morph_target_name + 4 );
PostEvent( new_event, morph_time );
}
else if ( strnicmp( morph_target_name, "anm_", 4 ) == 0 )
{
// Make sure we don't screw things up if we're in TALK_MODE
if (mode != ACTOR_MODE_TALK )
{
// Change the animation
new_event = new Event( EV_Actor_Anim );
new_event->AddString( morph_target_name + 4 );
PostEvent( new_event, morph_time );
SetActorFlag(ACTOR_FLAG_PLAYING_DIALOG_ANIM , true );
changedAnim = true;
}
}
else
{
morph_channel = GetMorphChannel( morph_target_name );
new_event = new Event ( EV_Morph );
new_event->AddString( morph_target_name );
if ( morph_channel == MORPH_CHAN_MOUTH )
amplitude *= _dialogMorphMult;
new_event->AddFloat( amplitude );
if ( morph_channel == MORPH_CHAN_EYES || morph_channel == MORPH_CHAN_LEFT_LID || morph_channel == MORPH_CHAN_RIGHT_LID )
new_event->AddFloat( 0.10f );
else if ( morph_channel == MORPH_CHAN_MOUTH )
new_event->AddFloat( 0.15f );
else
new_event->AddFloat( 0.25f );
PostEvent( new_event, morph_time, EVENT_DIALOG_ANIM );
}
}
// Make sure the mouth shuts after dialog
new_event = new Event ( EV_Morph );
new_event->AddString( "morph_mouth_base" );
new_event->AddFloat( 0.0f );
new_event->AddFloat( 0.10f );
PostEvent( new_event, dialog_length, EVENT_DIALOG_ANIM );
// Send the dialog done event
PostEvent( EV_Actor_DialogDone, dialog_length );
if ( emotion != "none" )
{
new_event = new Event( EV_Actor_SetEmotion );
new_event->AddString( emotion.c_str() );
PostEvent( new_event, dialog_length );
}
// Reset the anim if it has changed
if ( changedAnim )
{
new_event = new Event( EV_Actor_Anim );
new_event->AddString( "idle" );
PostEvent( new_event, dialog_length );
Event *clearFlag;
clearFlag = new Event ( EV_Actor_SetActorFlag );
clearFlag->AddString( "playingdialoganim" );
clearFlag->AddInteger( 0 );
PostEvent( clearFlag , dialog_length );
}
}
else
{
SetActorFlag( ACTOR_FLAG_DIALOG_PLAYING, false );
gi.WDPrintf( "Lip file not found for dialog %s\n", localizedDialogName );
}
if(useTalk)
StartTalkBehavior(user);
}
void Actor::PlayRadiusDialog( Sentient* user )
{
str real_dialog;
if ( GetActorFlag( ACTOR_FLAG_RADIUS_DIALOG_PLAYING ) )
return;
real_dialog = FindDialog( user, DIALOG_TYPE_RADIUS );
if ( !real_dialog.length() )
return;
StopDialog();
SetActorFlag( ACTOR_FLAG_RADIUS_DIALOG_PLAYING, true );
PlayDialog( user, DEFAULT_VOL, -1.0f, real_dialog.c_str() , NULL );
}
void Actor::StopDialog( Event *ev )
{
StopDialog();
}
void Actor::StopDialog()
{
Event *new_event;
if ( !GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) )
return;
StopSound( CHAN_DIALOG );
// Stop all facial motion
CancelEventsOfType( EV_Morph );
CancelEventsOfType( EV_Unmorph );
// Undo current morph
new_event = new Event ( EV_Morph );
new_event->AddString( "morph_base" );
new_event->AddFloat( 100.0f );
new_event->AddFloat( 0.10f );
ProcessEvent( new_event );
// Let everything know dialog is done
CancelEventsOfType( EV_Actor_DialogDone );
CancelEventsOfType( EV_Actor_BroadcastDialog );
ProcessEvent( EV_Actor_DialogDone );
if ( GetActorFlag( ACTOR_FLAG_USING_HUD ) )
{
Player *player = GetPlayer( 0 );
if ( player )
{
player->ClearDialog();
}
}
}
//-----------------------------------------------------
//
// Name:
// Class:
//
// Description:
//
// Parameters:
//
// Returns:
//-----------------------------------------------------
void Actor::setBranchDialog( void )
{
Player* player = GetPlayer(0);
str commandString;
if( player )
{
commandString = "stufftext \"displaybranchdialog ";
commandString += _branchDialogName;
commandString += "\"\n";
gi.SendServerCommand(player->entnum, commandString);
gi.SendServerCommand(player->entnum, "stufftext \"pushmenu branchdialog\"\n");
}
}
//-----------------------------------------------------
//
// Name:
// Class:
//
// Description:
//
// Parameters:
//
// Returns:
//-----------------------------------------------------
void Actor::clearBranchDialog( void )
{
_branchDialogName = "";
}
//-----------------------------------------------------
//
// Name: BranchDialog
// Class: Actor
//
// Description: Displays a branch dialog to the player.
//
// Parameters: ev - the branch dialog event.
//
// Returns:
//-----------------------------------------------------
void Actor::BranchDialog(Event* ev)
{
Player* player = GetPlayer( 0 );
player->setBranchDialogActor(this);
_branchDialogName = ev->GetString(1);
setBranchDialog();
}
void Actor::SetEmotion( Event *ev )
{
Event *new_event;
// Unmorph the old emotion
if ( emotion != "none" )
{
new_event = new Event( EV_Unmorph );
new_event->AddString( emotion );
ProcessEvent( new_event );
}
if ( ev->NumArgs() > 0 )
emotion = ev->GetString( 1 );
// Morph the new emotion
if ( emotion != "none" )
{
new_event = new Event( EV_Morph );
new_event->AddString( emotion );
ProcessEvent( new_event );
}
}
void Actor::SetBlink( Event *ev )
{
SetActorFlag( ACTOR_FLAG_SHOULD_BLINK, ev->GetBoolean( 1 ) );
}
void Actor::TryBlink( void )
{
float average_blink_time;
Event *new_event;
if ( next_blink_time <= level.time )
{
if ( emotion == "angry" )
average_blink_time = 6.0f;
else if ( emotion == "nervous" )
average_blink_time = 2.0f;
else
average_blink_time = 4.0f;
new_event = new Event( EV_Morph );
new_event->AddString( "morph_lid-lshut" );
new_event->AddFloat( 100.0f );
new_event->AddFloat( .15f );
new_event->AddInteger( true );
ProcessEvent( new_event );
new_event = new Event( EV_Morph );
new_event->AddString( "morph_lid-rshut" );
new_event->AddFloat( 100.0f );
new_event->AddFloat( .15f );
new_event->AddInteger( true );
ProcessEvent( new_event );
next_blink_time = level.time + average_blink_time + G_CRandom( average_blink_time / 4.0f );
}
}
void Actor::SetRadiusDialogRange( Event *ev )
{
radiusDialogRange = ev->GetFloat(1);
}
void Actor::SetSimplifiedThink( Event *ev )
{
if ( ( ev->NumArgs() > 0 ) && ( ev->GetBoolean( 1 ) == false ) )
{
if ( thinkStrategy != NULL )
{
delete thinkStrategy;
thinkStrategy = NULL;
}
thinkStrategy = new DefaultThink();
}
else
{
if ( thinkStrategy != NULL )
{
delete thinkStrategy;
thinkStrategy = NULL;
}
thinkStrategy = new SimplifiedThink( (Actor *)this );
}
if ( !thinkStrategy )
gi.Error( ERR_FATAL, "Actor Could not create thinkStrategy" );
}
void Actor::SetActorToActorDamageModifier( Event *ev )
{
float modifier = ev->GetFloat( 1 );
if ( modifier > 2.0f ) modifier = 2.0f; //Don't get out of hand
if ( modifier < 0.0f ) modifier = 0.0f; //That's as low as you can go... we don't want them healing each other
actor_to_actor_damage_modifier = modifier;
}
void Actor::ReturnProjectile( Event *ev )
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if (!incoming_proj) return;
Vector dir = currentEnemy->origin - origin;
Vector vel = incoming_proj->velocity;
Vector Angles = dir.toAngles();
vel*=-1.0f;
incoming_proj->setAngles(Angles);
incoming_proj->velocity = vel;
}
float Actor::GetDialogRemainingTime( void )
{
if ( GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) )
{
return dialog_done_time - level.time;
}
else
{
return 0;
}
}
void Actor::FreeDialogList( void )
{
DialogNode_t *dialog_node;
dialog_node = dialog_list;
while( dialog_node != NULL )
{
dialog_list = dialog_node->next;
delete dialog_node;
dialog_node = dialog_list;
}
}
void Actor::DialogDone( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DIALOG_PLAYING, false );
SetActorFlag( ACTOR_FLAG_RADIUS_DIALOG_PLAYING, false );
if ( dialog_state_name )
{
dialog_state_name = "";
if ( ( mode != ACTOR_MODE_AI ) && ( mode != ACTOR_MODE_TALK ) )
{
if ( dialog_old_state_name.length() )
SetState( dialog_old_state_name.c_str() );
}
}
}
void Actor::SetMouthAngle( Event *ev )
{
int tag_num;
float angle_percent;
Vector mouth_angles;
angle_percent = ev->GetFloat( 1 );
if ( angle_percent < 0.0f )
angle_percent = 0.0f;
if ( angle_percent > 1.0f )
angle_percent = 1.0f;
tag_num = gi.Tag_NumForName( edict->s.modelindex, "tag_mouth" );
if ( tag_num != -1 )
{
SetControllerTag( ACTOR_MOUTH_TAG, tag_num );
mouth_angles = vec_zero;
mouth_angles[PITCH] = max_mouth_angle * angle_percent;
SetControllerAngles( ACTOR_MOUTH_TAG, mouth_angles );
}
}
void Actor::DialogAnimDone( Event *ev )
{
SetAnim( "idle" );
}
//***********************************************************************************************
//
// Mode functions
//
//***********************************************************************************************
qboolean Actor::ModeAllowed( int new_mode )
{
if ( deadflag && ( actortype != IS_INANIMATE ) )
return false;
if ( ( new_mode == ACTOR_MODE_SCRIPT ) || ( new_mode == ACTOR_MODE_IDLE ) )
{
if ( ( mode == ACTOR_MODE_AI ) || ( mode == ACTOR_MODE_TALK ) )
return false;
}
else if ( new_mode == ACTOR_MODE_TALK )
{
//Check if we're already speaking
if ( GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) )
return false;
if ( /*( mode == ACTOR_MODE_AI ) ||*/ ( mode == ACTOR_MODE_TALK ) || ( actortype == IS_ENEMY ) || !GetActorFlag( ACTOR_FLAG_ALLOW_TALK ) ||
!dialog_list || level.cinematic )
return false;
}
return true;
}
void Actor::StartMode( int new_mode )
{
if ( new_mode == ACTOR_MODE_TALK )
{
SaveMode();
CancelEventsOfType( EV_Actor_EndBehavior );
CancelEventsOfType( EV_Actor_EndHeadBehavior );
CancelEventsOfType( EV_Actor_EndEyeBehavior );
CancelEventsOfType( EV_Actor_EndTorsoBehavior );
RemoveAnimDoneEvent();
}
mode = new_mode;
}
void Actor::EndMode( void )
{
str currentanim;
if ( mode == ACTOR_MODE_AI )
{
if ( GetActorFlag( ACTOR_FLAG_UPDATE_BOSS_HEALTH ) && max_boss_health )
{
char bosshealth_string[20];
sprintf( bosshealth_string, "%.5f", 0.0 );
gi.cvar_set( "bosshealth", bosshealth_string );
}
mode = ACTOR_MODE_IDLE;
ProcessEvent( EV_Actor_Idle );
if ( currentState )
{
currentanim = currentState->getLegAnim( *this, &conditionals );
if ( currentanim.length() && ( currentanim != animname ) )
SetAnim( currentanim, EV_Anim_Done );
}
enemyManager->ClearCurrentEnemy();
}
else if ( mode == ACTOR_MODE_TALK )
{
next_player_near = level.time + 5.0f;
RestoreMode();
}
}
void Actor::SaveMode( void )
{
if ( mode == ACTOR_MODE_IDLE )
{
saved_mode = ACTOR_MODE_IDLE;
if ( currentState )
saved_state_name = currentState->getName();
else
saved_state_name = "";
}
else if ( mode == ACTOR_MODE_AI )
{
saved_mode = ACTOR_MODE_AI;
if ( currentState )
saved_state_name = currentState->getName();
else
saved_state_name = "";
}
else if ( mode == ACTOR_MODE_SCRIPT )
{
saved_mode = ACTOR_MODE_SCRIPT;
saved_behavior = behavior;
saved_headBehavior = headBehavior;
saved_eyeBehavior = eyeBehavior;
saved_torsoBehavior = torsoBehavior;
saved_scriptthread = scriptthread;
saved_anim_name = animname;
saved_anim_event_name = last_anim_event_name;
//Reset Eyes and Head if they're f'd up.
if(headBehavior)
headBehavior->End(*this);
if(eyeBehavior)
eyeBehavior->End(*this);
if(torsoBehavior)
torsoBehavior->End(*this);
behavior = NULL;
headBehavior = NULL;
eyeBehavior = NULL;
torsoBehavior = NULL;
scriptthread = NULL;
}
else
{
gi.WDPrintf( "Can't saved specified mode: %d\n", mode );
}
}
void Actor::RestoreMode( void )
{
Event *idle_event;
if ( saved_mode == ACTOR_MODE_IDLE )
{
mode = ACTOR_MODE_IDLE;
idle_event = new Event( EV_Actor_Idle );
idle_event->AddString( saved_state_name );
ProcessEvent( idle_event );
}
if ( saved_mode == ACTOR_MODE_AI )
{
mode = ACTOR_MODE_AI;
SetState( saved_state_name );
}
else if ( saved_mode == ACTOR_MODE_SCRIPT )
{
StartMode( ACTOR_MODE_SCRIPT );
behavior = saved_behavior;
headBehavior = saved_headBehavior;
eyeBehavior = saved_eyeBehavior;
torsoBehavior = saved_torsoBehavior;
scriptthread = saved_scriptthread;
if ( saved_behavior )
currentBehavior = saved_behavior->getClassname();
else
currentBehavior = "";
/*
if ( saved_headBehavior )
currentHeadBehavior = saved_headBehavior->getClassname();
else
currentHeadBehavior = "";
if ( saved_eyeBehavior )
currentEyeBehavior = saved_eyeBehavior->getClassname();
else
currentEyeBehavior = "";
*/
if ( saved_torsoBehavior )
currentTorsoBehavior = saved_torsoBehavior->getClassname();
else
currentTorsoBehavior = "";
if ( saved_anim_event_name.length() )
{
Event *event = new Event( saved_anim_event_name.c_str() );
SetAnim( saved_anim_name, event );
}
else
SetAnim( saved_anim_name );
}
else
{
gi.WDPrintf( "Can't restore specified mode: %d\n", saved_mode );
}
saved_mode = ACTOR_MODE_NONE;
}
//***********************************************************************************************
//
// Finishing functions
//
//***********************************************************************************************
qboolean Actor::CanBeFinished( void )
{
// See if actor can be finished by any means of death
if ( can_be_finsihed_by_mods.NumObjects() > 0 )
return true;
else
return false;
}
qboolean Actor::CanBeFinishedBy( int meansofdeath )
{
int number_of_mods;
int i;
// Make sure in limbo
if ( !InLimbo() )
return false;
// Make sure can be finished by this means of death
number_of_mods = can_be_finsihed_by_mods.NumObjects();
for( i = 1 ; i <= number_of_mods ; i++ )
{
if ( meansofdeath == can_be_finsihed_by_mods.ObjectAt( i ) )
return true;
}
return false;
}
void Actor::SetCanBeFinishedBy( Event *ev )
{
str mod_string;
int new_mod;
int number_of_mods;
int i;
number_of_mods = ev->NumArgs();
for ( i = 1 ; i <= number_of_mods ; i++ )
{
mod_string = ev->GetString( i );
new_mod = MOD_NameToNum( mod_string );
if ( new_mod != -1 )
can_be_finsihed_by_mods.AddObject( new_mod );
}
}
void Actor::Finish( int meansofdeath )
{
// Make sure we can be finsihed by this means of death
if ( CanBeFinishedBy( meansofdeath ) )
{
// Save that the actor is being finished
SetActorFlag( ACTOR_FLAG_FINISHED, true );
// Kill the actor
ProcessEvent( EV_Actor_Suicide );
// Make sure the correct means of death is set
means_of_death = meansofdeath;
}
else
{
gi.WDPrintf( "Actor can't be finished by %d means of death\n", meansofdeath );
}
}
void Actor::StartLimbo( void )
{
State *temp_state;
qboolean found_state;
// Make sure we have a little bit of health
health = 1;
// Go to the limbo state
found_state = false;
if ( statemap )
{
temp_state = statemap->FindState( "LIMBO" );
if ( temp_state )
{
currentState = temp_state;
InitState();
found_state = true;
SetActorFlag( ACTOR_FLAG_IN_LIMBO, true );
}
}
if ( !found_state )
{
// Didn't find a limbo state so just die
ProcessEvent( EV_Actor_Suicide );
}
}
qboolean Actor::InLimbo( void )
{
return GetActorFlag( ACTOR_FLAG_IN_LIMBO );
}
//***********************************************************************************************
//
// General functions
//
//***********************************************************************************************
void Actor::IgnorePainFromActors( Event *ev )
{
SetActorFlag( ACTOR_FLAG_IGNORE_PAIN_FROM_ACTORS, true );
}
void Actor::UpdateBossHealth( Event *ev )
{
bool update = true;
bool forceBar = false;
if ( ev->NumArgs() > 0 )
update = ev->GetBoolean( 1 );
if ( ev->NumArgs() > 1 )
forceBar = ev->GetBoolean( 2 );
if ( !update && GetActorFlag( ACTOR_FLAG_UPDATE_BOSS_HEALTH ) )
{
char bosshealth_string[20];
sprintf( bosshealth_string, "%.5f", 0.0 );
gi.cvar_set( "bosshealth", bosshealth_string );
}
SetActorFlag( ACTOR_FLAG_UPDATE_BOSS_HEALTH, update );
SetActorFlag( ACTOR_FLAG_FORCE_LIFEBAR , forceBar );
}
void Actor::SetMaxBossHealth( Event *ev )
{
max_boss_health = ev->GetFloat( 1 );
}
void Actor::TouchTriggers( Event *ev )
{
SetActorFlag( ACTOR_FLAG_TOUCH_TRIGGERS, ev->GetBoolean( 1 ) );
}
void Actor::IgnoreWater( Event *ev )
{
SetActorFlag( ACTOR_FLAG_IGNORE_WATER, ev->GetBoolean( 1 ) );
}
void Actor::SetNotAllowedToKill( Event *ev )
{
SetActorFlag( ACTOR_FLAG_ALLOWED_TO_KILL, false );
}
void Actor::IgnorePlacementWarning( Event *ev )
{
str warning;
warning = ev->GetString( 1 );
if ( warning == "stuck" )
SetActorFlag( ACTOR_FLAG_IGNORE_STUCK_WARNING, true );
else if ( warning == "off_ground" )
SetActorFlag( ACTOR_FLAG_IGNORE_OFF_GROUND_WARNING, true );
}
void Actor::SetTargetable( Event *ev )
{
qboolean targetable;
targetable = ev->GetBoolean( 1 );
SetActorFlag( ACTOR_FLAG_TARGETABLE, targetable );
}
qboolean Actor::CanTarget( void ) const
{
return GetActorFlag( ACTOR_FLAG_TARGETABLE );
}
void Actor::SetSpawnChance( Event *ev )
{
spawn_chance = ev->GetFloat( 1 );
}
void Actor::AddSpawnItem( Event *ev )
{
str spawn_item_name;
spawn_item_name = ev->GetString( 1 );
spawn_items.AddObject( spawn_item_name );
}
void Actor::ClearSpawnItems( Event *ev )
{
spawn_items.ClearObjectList();
}
void Actor::SpawnItems( void )
{
int number_of_spawn_items;
int i;
qboolean spawn_random = false;
str spawn_item_name;
Player *player;
float health_chance;
//float water_chance;
float plasma_chance;
float bullets_chance;
float player_health;
int player_plasma;
int player_bullets;
if ( spawn_chance == 0.0f ) return ;
number_of_spawn_items = spawn_items.NumObjects();
// Spawn in all of the items in the spawn_item list
if ( (number_of_spawn_items) && (G_Random( 100.0f) < spawn_chance) )
{
for( i = 1 ; i <= number_of_spawn_items ; i++ )
{
spawn_item_name = spawn_items.ObjectAt( i );
if ( spawn_item_name == "random" )
spawn_random = true;
else
SpawnItem( spawn_item_name );
}
}
else
{
spawn_random = true;
}
// See if we should spawn a random item
if ( spawn_random )
{
if ( G_Random( 100.0f ) < spawn_chance )
{
// Set up default chances
health_chance = 1.0f;
//water_chance = 1.0f;
plasma_chance = 1.0f;
bullets_chance = 1.0f;
// See what he player needs
player = (Player *)g_entities[ 0 ].entity;
player_health = player->health;
player_plasma = player->AmmoCount( "Plasma" );
player_bullets = player->AmmoCount( "Bullet" );
// See if the player is low on health
if ( player_health <= 50.0f )
health_chance *= ( 60.0f - player_health ) / 10.0f;
if ( player_plasma <= 20.0f )
plasma_chance *= ( 30.0f - player_plasma ) / 10.0f;
if ( player_bullets <= 50.0f )
bullets_chance *= ( 60.0f - player_bullets ) / 10.0f;
}
}
}
void Actor::SpawnItem( const str &spawn_item_name )
{
SpawnArgs args;
Entity *ent;
Item *item;
args.setArg( "model", spawn_item_name );
ent = args.Spawn();
if ( !ent || !ent->isSubclassOf( Item ) )
return;
item = (Item *)ent;
item->setOrigin( centroid );
item->ProcessPendingEvents();
item->PlaceItem();
item->setOrigin( centroid );
item->velocity = Vector( G_CRandom( 100.0f ), G_CRandom( 100.0f ), 200.0f + G_Random( 200.0f ) );
item->edict->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
// Give the new item a targetname
item->targetname = targetname;
item->targetname += "_item";
item->SetTargetName( item->targetname );
}
qboolean Actor::CanJump( void )
{
return ( animate->HasAnim( "jump" ) && animate->HasAnim( "fall" ) && animate->HasAnim( "land" ) );
}
void Actor::SetUseGravity( Event *ev )
{
qboolean use_gravity = (qboolean) gravity ;
if (ev->NumArgs() > 0 )
{
use_gravity = ev->GetBoolean( 1 );
}
else
gi.WDPrintf( "SetUseGravity missing boolean argument. Currently set to %d\n", gravity);
SetActorFlag( ACTOR_FLAG_USE_GRAVITY, use_gravity );
if ( use_gravity )
gravity = 1;
else
gravity = 0;
}
void Actor::SetAllowFall( Event *ev )
{
qboolean allow_fall;
if ( ev->NumArgs() > 0 )
allow_fall = ev->GetBoolean( 1 );
else
allow_fall = true;
SetActorFlag( ACTOR_FLAG_ALLOW_FALL, allow_fall );
}
void Actor::SetHaveThing( Event *ev )
{
int thing_number;
qboolean thing_bool;
thing_number = ev->GetInteger( 1 );
thing_bool = ev->GetBoolean( 2 );
SetHaveThing ( thing_number , thing_bool );
}
void Actor::SetHaveThing( int thing_number, qboolean thing_bool )
{
switch( thing_number )
{
case 1 :
SetActorFlag( ACTOR_FLAG_HAS_THING1, thing_bool );
break;
case 2 :
SetActorFlag( ACTOR_FLAG_HAS_THING2, thing_bool );
break;
case 3 :
SetActorFlag( ACTOR_FLAG_HAS_THING3, thing_bool );
break;
case 4 :
SetActorFlag( ACTOR_FLAG_HAS_THING4, thing_bool );
break;
default :
gi.WDPrintf( "Has thing %d out of range\n", thing_number );
return;
}
}
void Actor::SetStickToGround( const bool stick )
{
movementSubsystem->SetStickToGround( stick );
}
void Actor::SetStickToGround( Event *ev )
{
movementSubsystem->SetStickToGround( ev->GetBoolean( 1 ) );
}
const bool Actor::GetStickToGround( void ) const
{
return movementSubsystem->GetStickToGround();
}
void Actor::SetActorFlag( int flag, qboolean flag_value )
{
unsigned int *flags;
int index;
int bit = 0;
if ( flag > ACTOR_FLAG_MAX )
{
gi.WDPrintf( "Actor flag %d out of range\n", flag );
return;
}
index = flag / 32;
switch( index )
{
case 0 :
flags = &actor_flags1;
bit = flag;
break;
case 1 :
flags = &actor_flags2;
bit = flag - 32;
break;
case 2 :
flags = &actor_flags3;
bit = flag - 64;
break;
case 3 :
flags = &actor_flags4;
bit = flag - 96;
break;
default :
gi.WDPrintf( "Actor flag %d out of range\n", flag );
return;
}
if ( flag_value )
*flags |= 1 << bit;
else
*flags &= ~( 1 << bit );
}
void Actor::SetActorFlag( const str &flag_name , qboolean flag_value )
{
int flag;
flag = ActorFlag_string_to_int( flag_name );
SetActorFlag( flag, flag_value );
}
void Actor::SetActorFlag( Event *ev )
{
str flag_name;
int flag;
qboolean flag_bool;
flag_name = ev->GetString(1);
flag_bool = ev->GetBoolean(2);
flag = ActorFlag_string_to_int( flag_name );
SetActorFlag( flag, flag_bool );
}
qboolean Actor::GetActorFlag( const str &flag_name ) const
{
int flag;
flag = ActorFlag_string_to_int( flag_name );
return GetActorFlag( flag );
}
qboolean Actor::GetActorFlag( int flag ) const
{
const unsigned int *flags;
int index;
int bit = 0;
if ( flag > ACTOR_FLAG_MAX )
{
gi.WDPrintf( "Actor flag %d out of range\n", flag );
return false;
}
index = flag / 32;
switch( index )
{
case 0 :
flags = &actor_flags1;
bit = flag;
break;
case 1 :
flags = &actor_flags2;
bit = flag - 32;
break;
case 2 :
flags = &actor_flags3;
bit = flag - 64;
break;
case 3:
flags = &actor_flags4;
bit = flag - 96;
break;
default :
gi.WDPrintf( "Actor flag %d out of range\n", flag );
return false;
}
if ( *flags & ( 1 << bit ) )
return true;
else
return false;
}
void Actor::SetNotifyFlag( int flag, qboolean flag_value )
{
unsigned int *flags;
int index;
if ( flag > NOTIFY_FLAG_MAX )
{
gi.WDPrintf( "Actor flag %d out of range\n", flag );
return;
}
index = flag / 32;
switch( index )
{
case 0 :
flags = &notify_flags1;
break;
default :
gi.WDPrintf( "Notify flag %d out of range\n", flag );
return;
}
if ( flag_value )
*flags |= 1 << flag;
else
*flags &= ~( 1 << flag );
}
void Actor::SetNotifyFlag( const str &flag_name , qboolean flag_value )
{
int flag;
flag = NotifyFlag_string_to_int( flag_name );
SetNotifyFlag( flag, flag_value );
}
void Actor::SetNotifyFlag( Event *ev )
{
str flag_name;
int flag;
qboolean flag_bool;
flag_name = ev->GetString(1);
flag_bool = ev->GetBoolean(2);
flag = NotifyFlag_string_to_int( flag_name );
SetNotifyFlag( flag, flag_bool );
}
qboolean Actor::GetNotifyFlag( int flag )
{
unsigned int *flags;
int index;
if ( flag > NOTIFY_FLAG_MAX )
{
gi.WDPrintf( "Actor flag %d out of range\n", flag );
return false;
}
index = flag / 32;
switch( index )
{
case 0 :
flags = &notify_flags1;
break;
default :
gi.WDPrintf( "Notify flag %d out of range\n", flag );
return false;
}
if ( *flags & ( 1 << flag ) )
return true;
else
return false;
}
void Actor::SetBounceOff( Event *ev )
{
SetActorFlag( ACTOR_FLAG_BOUNCE_OFF, true );
}
void Actor::BounceOffEvent( Event *ev )
{
Vector object_origin;
Vector object_angles;
if ( bounce_off_effect.length() )
{
object_origin = ev->GetVector( 1 );
//Calculate Angles as being a Vector from the centroid to point of impact;
object_angles = object_origin - centroid;
object_angles.toAngles();
SpawnEffect( bounce_off_effect , object_origin, object_angles , 5.0f );
}
}
void Actor::SetBounceOffEffect( Event *ev )
{
bounce_off_effect = ev->GetString( 1 );
}
void Actor::GotoNextStage( Event *ev )
{
stage++;
}
void Actor::GotoPrevStage( Event *ev )
{
stage--;
if ( stage < 1 )
stage = 1;
}
void Actor::GotoStage( Event *ev )
{
stage = ev->GetInteger( 1 );
}
//--------------------------------------------------------------
//
// Name: GetStage
// Class: Actor
//
// Description: Added so the scripters can access the current stage
// of this actor.
//
// Parameters: Event *ev -- not used
//
// Returns: None (stage number via event)
//
//--------------------------------------------------------------
void Actor::GetStage( Event *ev )
{
ev->ReturnFloat( (float)stage );
}
void Actor::NotifyOthersAtDeath( Event *ev )
{
SetActorFlag( ACTOR_FLAG_NOTIFY_OTHERS_AT_DEATH, true );
}
void Actor::NotifyOthersOfDeath( void )
{
int i;
Actor *act;
for( i = 1; i <= ActiveList.NumObjects(); i++ )
{
act = ActiveList.ObjectAt( i );
//if ( name.length() && name == act->name && Vector( act->origin - origin ).length() < 1000 )
if ( name.length() && ( name == act->name ) )
act->AddStateFlag( STATE_FLAG_OTHER_DIED );
}
}
void Actor::Pickup( Event *ev )
{
str tag_name;
int tag_num;
Vector new_angles;
tag_name = ev->GetString( 1 );
tag_num = gi.Tag_NumForName( edict->s.modelindex, tag_name.c_str() );
new_angles = Vector(0, 0, 0);
if ( pickup_ent )
{
pickup_ent->setAngles( new_angles );
pickup_ent->attach( entnum, tag_num );
}
}
void Actor::Throw( Event *ev )
{
int i;
int num;
Entity *child;
Vector pos;
Vector forward;
str tag_name;
int tag_num;
tag_name = ev->GetString( 1 );
tag_num = gi.Tag_NumForName( edict->s.modelindex, tag_name.c_str() );
if ( bind_info )
{
for ( i=0,num = bind_info->numchildren; i < MAX_MODEL_CHILDREN; i++ )
{
if ( bind_info->children[i] == ENTITYNUM_NONE )
{
continue;
}
child = ( Entity * )G_GetEntity( bind_info->children[i] );
if ( child->edict->s.tag_num == tag_num )
{
child->detach();
child->setSolidType( SOLID_BBOX );
child->setAngles( angles );
child->groundentity = NULL;
tag_num = gi.Tag_NumForName( edict->s.modelindex, tag_name.c_str() );
GetTag( tag_num, &pos, &forward );
child->velocity = orientation[0];
child->velocity *= 500.0f;
child->velocity.z = 400.0f;
}
num--;
if ( !num )
break;
}
}
}
void Actor::SolidMask( Event *ev )
{
edict->clipmask = MASK_MONSTERSOLID;
}
void Actor::IgnoreMonsterClip( Event *ev )
{
edict->clipmask &= ~CONTENTS_MONSTERCLIP;
}
void Actor::NotSolidMask( Event *ev )
{
edict->clipmask = MASK_SOLID;
}
void Actor::NoMask( Event *ev )
{
edict->clipmask = 0;
}
void Actor::ResetMoveDir( Event *ev )
{
Vector newForward;
angles.AngleVectors( &newForward );
movementSubsystem->setMoveDir( newForward );
}
void Actor::SetMask( Event *ev )
{
str mask_name;
mask_name = ev->GetString( 1 );
if ( mask_name == "monstersolid" )
edict->clipmask = MASK_MONSTERSOLID;
else if ( mask_name == "deadsolid" )
edict->clipmask = MASK_DEADSOLID;
else if ( mask_name == "none" )
edict->clipmask = 0;
else if ( mask_name == "pathsolid" )
edict->clipmask = MASK_PATHSOLID;
else
gi.WDPrintf( "Unknown mask name - %s\n", mask_name.c_str() );
}
void Actor::setSize( Vector min, Vector max )
{
Sentient::setSize( min * edict->s.scale, max * edict->s.scale );
}
void Actor::SetHealth( Event *ev )
{
health = ev->GetFloat( 1 ); //* edict->s.scale
if (max_health < health )
{
max_health = health;
}
}
void Actor::SetMaxHealth( Event *ev )
{
max_health = ev->GetFloat( 1 ); // * edict->s.scale;
}
void Actor::SetVar( Event *ev )
{
StateVar *checkVar = 0;
str varName = ev->GetString(1);
str varValue = ev->GetString(2);
//First Check if we already have the Var and it just needs
//to have its value updated
for (int i = 1; i <= stateVarList.NumObjects(); i++ )
{
checkVar = stateVarList.ObjectAt(i);
if( !stricmp( checkVar->varName, varName ) )
{
checkVar->varValue = varValue;
return;
}
}
//Didn't find one, so we have to create a new one.
checkVar = new StateVar;
checkVar->varName = varName;
checkVar->varValue = varValue;
stateVarList.AddObject( checkVar );
}
//--------------------------------------------------------------
// Name: SetVarTime
// Class: Actor
//
// Description: Sets the level time into a state var
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetVarTime( Event *ev )
{
StateVar *checkVar = 0;
str varName = ev->GetString(1);
//First Check if we already have the Var and it just needs
//to have its value updated
for (int i = 1; i <= stateVarList.NumObjects(); i++ )
{
checkVar = stateVarList.ObjectAt(i);
if( !stricmp( checkVar->varName, varName ) )
{
checkVar->varTime = level.time;
return;
}
}
//Didn't find one, so we have to create a new one.
checkVar = new StateVar;
checkVar->varName = varName;
checkVar->varTime = level.time;
stateVarList.AddObject( checkVar );
}
void Actor::SetTurnSpeed( Event *ev )
{
movementSubsystem->setTurnSpeed(ev->GetFloat( 1 ) );
}
void Actor::SetMaxInactiveTime( Event *ev )
{
max_inactive_time = ev->GetFloat( 1 );
}
bool Actor::IsEntityAlive( const Entity *ent )
{
return ( ent && !ent->deadflag && ( ent->health > 0.0f ) && !(ent->flags & FL_NOTARGET) && level.ai_on );
}
void Actor::Name( Event *ev )
{
name = ev->GetString( 1 );
}
void Actor::SetupTriggerField( Event *ev )
{
Vector min;
Vector max;
TouchField *trig;
min = ev->GetVector( 1 );
max = ev->GetVector( 2 );
min = min + origin;
max = max + origin;
trig = new TouchField;
trig->Setup( this, EV_ActorTriggerTouched, min, max, TRIGGER_PLAYERS | TRIGGER_MONSTERS );
trigger = trig;
}
void Actor::TriggerTouched( Event *ev )
{
Entity *other;
other = ev->GetEntity( 1 );
if (
( other->movetype != MOVETYPE_NONE ) &&
( other->movetype != MOVETYPE_STATIONARY ) &&
( IsEntityAlive( other ) )
)
AddStateFlag( STATE_FLAG_TOUCHED );
}
void Actor::AddStateFlag( unsigned int flag )
{
int current_other_part;
part_t *other_part;
Entity *other_ent;
Actor *other_act;
int current_part;
part_t *part;
// Update my state flags
state_flags |= flag;
// Update all the other parts of my state flags
for ( current_other_part = 1 ; current_other_part <= parts.NumObjects() ; current_other_part++ )
{
other_part = &parts.ObjectAt( current_other_part );
other_ent = other_part->ent;
other_act = (Actor *)other_ent;
// Look for ourselves in this part's part list
for ( current_part = 1 ; current_part <= other_act->parts.NumObjects() ; current_part++ )
{
part = &other_act->parts.ObjectAt( current_part );
if ( part->ent == this )
{
// Found ourselves, update state flags
part->state_flags |= flag;
}
}
}
}
void Actor::ClearStateFlags( void )
{
int current_part;
part_t *part;
// Clear my state flags
state_flags = 0;
// Clear all the other parts state flags
for ( current_part = 1 ; current_part <= parts.NumObjects() ; current_part++ )
{
part = &parts.ObjectAt( current_part );
part->state_flags = 0;
}
movementSubsystem->clearBlockingEntity();
}
void Actor::NoChatterEvent( Event *ev )
{
SetActorFlag( ACTOR_FLAG_NOCHATTER, true );
}
void Actor::Chatter( const char *snd, float chance, float volume, int channel )
{
str realname;
if ( GetActorFlag( ACTOR_FLAG_NOCHATTER ) || ( chattime > level.time ) )
{
return;
}
if ( G_Random( 10.0f ) > chance )
{
chattime = level.time + 1.0f + G_Random( 2.0f );
return;
}
realname = GetRandomAlias( snd );
if ( realname.length() > 1 )
{
float delay;
delay = gi.SoundLength( realname.c_str() );
if ( delay < 0.0f )
gi.WDPrintf( "Lip file not found for dialog %s\n", realname.c_str() );
chattime = level.time + delay + 4.0f + G_Random( 5.0f );
Sound( realname, channel, volume );
}
else
{
// set it into the future, so we don't check it again right away
chattime = level.time + 1.0f;
}
}
void Actor::ActivateEvent( Event *ev )
{
if ( ( deadflag ) && ( actortype != IS_INANIMATE ) )
{
return;
}
ProcessEvent( EV_Actor_AttackPlayer );
AddStateFlag( STATE_FLAG_ACTIVATED );
}
void Actor::UseEvent( Event *ev )
{
Entity *entity;
// Can only be used once every 1/4 second
if ( last_used_time + 0.25f >= level.time )
return;
if ( GetActorFlag(ACTOR_FLAG_CANNOT_USE) )
return;
//Can't switch to talk mode if a cinematic is going on
if ( level.cinematic )
return;
// Actors can't be used by equipment
entity = ev->GetEntity( 1 );
if ( entity->isSubclassOf( Equipment ) )
return;
last_used_time = level.time;
AddStateFlag( STATE_FLAG_USED );
if ( onuse_thread_name.length() > 0 )
{
RunThread(onuse_thread_name);
}
if ( entity->isSubclassOf( Sentient ) && getSolidType() == SOLID_BBOX && !hidden() )
{
Sentient *user;
user = (Sentient *)entity;
StartTalkBehavior( user );
}
}
void Actor::StartTalkBehavior(Sentient *user)
{
Talk *talk;
if ( !ModeAllowed( ACTOR_MODE_TALK ) )
return;
StartMode( ACTOR_MODE_TALK );
talk = new Talk;
talk->SetUser( user );
SetBehavior( talk );
}
void Actor::SetOnUseThread( Event *ev )
{
onuse_thread_name = ev->GetString( 1 );
}
void Actor::ClearOnUseThread( Event *ev )
{
onuse_thread_name = "";
}
void Actor::Think( void )
{
if ( !Director.PlayerReady() )
{
last_time_active = level.time;
return;
}
if ( thinkStrategy )
thinkStrategy->Think( *this );
if ( !level.ai_on )
LevelAIOff();
else
LevelAIOn();
/*
Vector BS( 0 , 0 , 100 );
G_DrawDebugNumber( origin + BS , currentHelperNode.nodeID , 1 , 0 , 1, 0 );
if ( currentHelperNode.node )
{
if ( currentHelperNode.node->isReserved() )
G_DebugLine( centroid, currentHelperNode.node->origin, 1.0f, 1.0f, 1.0f, 1.0f );
}
*/
/*
str pState;
switch ( movementSubsystem->getPostureState() )
{
case POSTURE_TRANSITION_STAND_TO_CROUCH:
pState = "POSTURE_TRANSITION_STAND_TO_CROUCH\n";
break;
case POSTURE_TRANSITION_STAND_TO_PRONE:
pState = "POSTURE_TRANSITION_STAND_TO_PRONE\n";
break;
case POSTURE_TRANSITION_STAND_TO_LEAN_LEFT_DUAL:
pState = "POSTURE_TRANSITION_STAND_TO_LEAN_LEFT_DUAL\n";
break;
case POSTURE_TRANSITION_STAND_TO_LEAN_RIGHT_DUAL:
pState = "POSTURE_TRANSITION_STAND_TO_LEAN_RIGHT_DUAL\n";
break;
case POSTURE_TRANSITION_CROUCH_TO_STAND:
pState = "POSTURE_TRANSITION_CROUCH_TO_STAND\n";
break;
case POSTURE_TRANSITION_CROUCH_TO_PRONE:
pState = "POSTURE_TRANSITION_CROUCH_TO_PRONE\n";
break;
case POSTURE_TRANSITION_PRONE_TO_CROUCH:
pState = "POSTURE_TRANSITION_PRONE_TO_CROUCH\n";
break;
case POSTURE_TRANSITION_PRONE_TO_STAND:
pState = "POSTURE_TRANSITION_PRONE_TO_STAND\n";
break;
case POSTURE_TRANSITION_LEAN_LEFT_DUAL_TO_STAND:
pState = "POSTURE_TRANSITION_LEAN_LEFT_DUAL_TO_STAND\n";
break;
case POSTURE_TRANSITION_LEAN_RIGHT_DUAL_TO_STAND:
pState = "POSTURE_TRANSITION_LEAN_RIGHT_DUAL_TO_STAND\n";
break;
case POSTURE_STAND:
pState = "POSTURE_STAND\n";
break;
case POSTURE_CROUCH:
pState = "POSTURE_CROUCH\n";
break;
case POSTURE_PRONE:
pState = "POSTURE_PRONE\n";
break;
case POSTURE_LEAN_LEFT_DUAL:
pState = "POSTURE_LEAN_LEFT_DUAL\n";
break;
case POSTURE_LEAN_RIGHT_DUAL:
pState = "POSTURE_LEAN_RIGHT_DUAL\n";
break;
}
gi.Printf( pState.c_str() );
*/
}
qboolean Actor::GetClosestTag( const str &tag_name, int number_of_tags, const Vector &target, Vector *orig )
{
str temp_tag_name;
Vector temp_orig;
Vector diff;
float dist;
float best_dist = -1;
qboolean found = false;
int i;
char number[5];
if ( number_of_tags == 1 )
{
return GetTag( tag_name.c_str(), orig );
}
for( i = 1 ; i <= number_of_tags ; i++ )
{
sprintf( number, "%d", i );
temp_tag_name = tag_name + str( number );
if ( GetTag( temp_tag_name.c_str(), &temp_orig ) )
{
diff = target - temp_orig;
dist = diff * diff;
if ( ( dist < best_dist ) || ( best_dist < 0 ) )
{
best_dist = dist;
found = true;
*orig = temp_orig;
}
}
}
return found;
}
void Actor::Active( Event *ev )
{
int active_flag;
if ( ev->NumArgs() > 0 )
{
active_flag = ev->GetInteger( 1 );
if ( active_flag )
SetActorFlag( ACTOR_FLAG_INACTIVE, false );
else
SetActorFlag( ACTOR_FLAG_INACTIVE, true );
}
}
void Actor::SpawnActorAboveEnemy( Event *ev )
{
str model_name;
int how_many;
qboolean attack;
float width;
float height;
float how_far;
trace_t trace;
Vector spawn_mins;
Vector spawn_maxs;
Vector new_orig;
Vector new_dir;
Vector Enemy_orig;
Vector Enemy_dir;
model_name = ev->GetString( 1 );
how_many = ev->GetInteger( 2 );
attack = ev->GetBoolean( 3 );
width = ev->GetFloat( 4 );
height = ev->GetFloat( 5 );
how_far = ev->GetFloat( 6 );
//Set Spawn Thing Origin and Angles to make it above the player
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if ( currentEnemy->isSubclassOf (Player ) )
{
Player* player;
player = (Player*)(Entity*)currentEnemy;
Enemy_orig = player->origin;
Enemy_dir = player->angles;
}
else
{
Actor* act;
act = (Actor*)(Entity*)currentEnemy;
Enemy_orig = act->origin;
Enemy_dir = act->angles;
}
new_orig = Enemy_orig;
new_orig.z += how_far;
new_dir = Enemy_dir;
spawn_mins.x = -width;
spawn_mins.y = -width;
spawn_mins.z = 0;
spawn_maxs.x = width;
spawn_maxs.y = width;
spawn_maxs.z = height;
trace = G_Trace( Enemy_orig, spawn_mins, spawn_maxs, new_orig, NULL, MASK_MONSTERSOLID, false, "SpawnActorAbovePlayer" );
SpawnActor( model_name, trace.endpos, new_dir, how_many, attack, width, height, true);
}
void Actor::SpawnActorAtLocation( Event *ev )
{
str model_name;
str pathnode_name;
qboolean attack;
Vector orig;
Vector ang;
float width;
float height;
PathNode *goal;
int number_of_pathnodes;
Entity *effect;
trace_t trace;
Vector spawn_mins;
Vector spawn_maxs;
model_name = ev->GetString( 1 );
pathnode_name = ev->GetString( 2 );
number_of_pathnodes = ev->GetInteger( 3 );
attack = ev->GetBoolean( 4 );
width = ev->GetFloat( 5 );
height = ev->GetFloat( 6 );
// Get the pathnode name to spawn in to
pathnode_name += (int)G_Random( (float)number_of_pathnodes ) + 1;
// Find the path node
goal = thePathManager.FindNode( pathnode_name );
if ( !goal )
{
gi.WPrintf( "Can't find position %s\n", pathnode_name.c_str() );
return;
}
// Set the spawn in position/direction
orig = goal->origin;
ang = goal->angles;
spawn_mins.x = -width;
spawn_mins.y = -width;
spawn_mins.z = 0;
spawn_maxs.x = width;
spawn_maxs.y = width;
spawn_maxs.z = height;
trace = G_Trace( orig + Vector( "0 0 64" ), spawn_mins, spawn_maxs, orig - Vector( "0 0 128" ), NULL, MASK_MONSTERSOLID, false, "SpawnActorAtLocation" );
if ( trace.allsolid )
return;
orig = trace.endpos;
SpawnActor( model_name, orig, ang, 1, attack, width, height );
// Spawn in teleport effect
effect = new Entity( ENTITY_CREATE_FLAG_ANIMATE );
effect->setModel( "fx_teleport3.tik" );
effect->setOrigin( orig );
effect->setSolidType( SOLID_NOT );
effect->animate->RandomAnimate( "idle", EV_Remove );
effect->Sound( "snd_teleport" );
}
void Actor::SpawnActorAtTag( Event *ev )
{
str model_name;
str tag_name;
int how_many;
qboolean attack;
float spawn_offset = 0;
Vector tag_orig;
Vector tag_dir;
Vector new_orig;
Vector new_dir;
float width;
float height;
qboolean force;
float addHeight;
model_name = ev->GetString( 1 );
tag_name = ev->GetString( 2 );
how_many = ev->GetInteger( 3 );
attack = ev->GetBoolean( 4 );
width = ev->GetFloat( 5 );
height = ev->GetFloat( 6 );
if ( ev->NumArgs() > 6 )
spawn_offset = ev->GetFloat( 7 );
else
spawn_offset = 0.0f;
if ( spawn_offset < 0.0f ) spawn_offset = 0.0f;
if ( ev->NumArgs() > 7 )
force = ev->GetBoolean( 8 );
else
force = false;
if ( ev->NumArgs() > 8 )
addHeight = ev->GetFloat( 9 );
else
addHeight = 0;
// Calculate a good origin/angles
GetTag( tag_name.c_str(), &tag_orig, &tag_dir );
if(spawn_offset)
{
new_orig = tag_orig + tag_dir * spawn_offset;
new_orig[2] = new_orig[2] + addHeight;
new_dir = tag_dir * -1.0f;
SpawnActor( model_name, new_orig, new_dir, how_many, attack, width, height, force );
return;
}
float offset = 250.0f;
for ( int i = 0; i < how_many; i++)
{
Vector side;
origin.AngleVectors(NULL,&side,NULL);
side.normalize();
new_orig = side*offset+origin;
if ( i%2 == 0 )
new_orig*=-1.0f;
else
offset*=2.25f;
new_dir = tag_dir * -1.0f;
SpawnActor( model_name, new_orig, new_dir, how_many, attack, width, height, force );
}
}
void Actor::SpawnActor( const str &model_name, const Vector &orig, const Vector &ang, int how_many,
qboolean attack, float width, float height, qboolean force )
{
Actor *new_actor;
int current_actor;
trace_t trace;
Vector spawn_mins;
Vector spawn_maxs;
// Make sure this origin is reasonable
spawn_mins[0] = -width;
spawn_mins[1] = -width;
spawn_mins[2] = 0;
spawn_maxs[0] = width;
spawn_maxs[1] = width;
spawn_maxs[2] = height;
trace = G_Trace( orig, spawn_mins, spawn_maxs, orig, NULL, MASK_MONSTERSOLID, false, "Actor::SpawnActor" );
if ( (trace.fraction != 1.0f || trace.allsolid) && !force )
return;
// Spawn in all new actors
for( current_actor = 0 ; current_actor < how_many ; current_actor++ )
{
new_actor = new Actor;
new_actor->setModel( model_name );
new_actor->setOrigin( orig );
new_actor->setAngles( ang );
// Make new actor attack player if requested
if ( attack )
new_actor->PostEvent( EV_Actor_AttackPlayer, 0.0f );
// Update number of spawns
num_of_spawns++;
// Save our parent
new_actor->spawnparent = this;
// Give the new actor a targetname
new_actor->targetname = targetname;
new_actor->targetname += "_spawned";
new_actor->SetTargetName( new_actor->targetname );
}
}
void Actor::TryTalkToPlayer( void )
{
int player_near = false;
Entity *ent_in_range;
int i;
Vector delta;
gentity_t *ed;
float dist2;
Sentient *user = NULL;
Talk *talk;
// See if we should even bother looking for players
if ( level.cinematic )
next_player_near = level.time + 5.0f;
if ( deadflag || actortype != IS_FRIEND || next_player_near > level.time || !ModeAllowed( ACTOR_MODE_TALK ) )
return;
next_player_near = level.time + .2f + G_Random( .1f );
// See if we are near the player
for( i = 0 ; i < game.maxclients; i++ )
{
ed = &g_entities[ i ];
if ( !ed->inuse || !ed->entity )
continue;
ent_in_range = ed->entity;
if ( EntityHasFireType( ent_in_range, FT_MELEE ) || EntityHasFireType( ent_in_range, FT_BULLET ) ||
EntityHasFireType( ent_in_range, FT_PROJECTILE ) )
continue;
if ( IsEntityAlive( ent_in_range ) && ( ent_in_range->velocity == vec_zero ) && sensoryPerception)
{
delta = centroid - ent_in_range->centroid;
// dot product returns length squared
dist2 = delta * delta;
if ( ( dist2 <= 100.0f * 100.0f ) && sensoryPerception->CanSeeEntity( this , ent_in_range , true , true ) )
{
player_near = true;
user = (Sentient *)ent_in_range;
}
}
}
if ( !player_near )
{
SetActorFlag( ACTOR_FLAG_LAST_TRY_TALK, false );
return;
}
if ( !GetActorFlag( ACTOR_FLAG_LAST_TRY_TALK ) )
{
SetActorFlag( ACTOR_FLAG_LAST_TRY_TALK, true );
return;
}
SetActorFlag( ACTOR_FLAG_LAST_TRY_TALK, false );
// Go to talk mode
StartMode( ACTOR_MODE_TALK );
talk = new Talk;
talk->SetUser( user );
SetBehavior( talk );
}
void Actor::AllowTalk( Event *ev )
{
SetActorFlag( ACTOR_FLAG_ALLOW_TALK, ev->GetBoolean( 1 ) );
}
void Actor::AllowHangBack( Event *ev )
{
SetActorFlag( ACTOR_FLAG_ALLOW_HANGBACK, ev->GetBoolean( 1 ) );
}
qboolean Actor::CheckBottom( void )
{
//Vector test_mins, test_maxs;
Vector start, stop;
trace_t trace;
int x, y;
int corners_ok;
int middle_ok;
float width;
int mask;
width = maxs[0];
// First see if the origin is on a solid (this is the really simple test)
start = origin - Vector( 0.0f, 0.0f, 1.0f );
if ( gi.pointcontents( start, 0 ) == CONTENTS_SOLID )
return true;
// Next see if at least 3 corners are on a solid (this is the simple test)
corners_ok = 0;
start[ 2 ] = absmin[ 2 ] - 1.0f;
for( x = 0; x <= 1; x++ )
{
for( y = 0; y <= 1; y++ )
{
start[ 0 ] = x ? absmax[ 0 ] : absmin[ 0 ];
start[ 1 ] = y ? absmax[ 1 ] : absmin[ 1 ];
if ( gi.pointcontents( start, 0 ) == CONTENTS_SOLID )
corners_ok++;
}
}
if ( corners_ok >= 3 )
return true;
// Next do the hard test
corners_ok = 0;
middle_ok = 0;
// Test the origin (if it is close to ground is a plus)
start = origin;
stop = start - Vector( 0.0f, 0.0f, width ); // Test down as far as the actor is wide
// Build the correct mask (the actor's normal mask without body)
mask = edict->clipmask & ~CONTENTS_BODY;
trace = G_Trace( start, vec_zero, vec_zero, stop, this, mask, false, "CheckBottom 1" );
if ( trace.fraction < 1.0f && trace.plane.normal[2] > .7f )
middle_ok = 1;
// Test all of the corners
for( x = 0; x <= 1; x++ )
{
for( y = 0; y <= 1; y++ )
{
start[ 0 ] = x ? absmax[ 0 ] : absmin[ 0 ];
start[ 1 ] = y ? absmax[ 1 ] : absmin[ 1 ];
start[ 2 ] = origin[ 2 ];
stop = start - Vector( 0.0f, 0.0f, 2.0f * width );
trace = G_Trace( start, vec_zero, vec_zero, stop, this, mask, false, "CheckBottom 2" );
if ( ( trace.fraction < 1.0f ) && ( trace.plane.normal[2] > .7f ) )
corners_ok++;
if ( ( middle_ok && corners_ok >= 1 ) || ( corners_ok >= 3 ) )
return true;
}
}
return false;
}
void Actor::ChangeType( Event *ev )
{
velocity = vec_zero;
setModel( ev->GetString( 1 ) );
PostEvent( EV_Actor_Wakeup, FRAMETIME );
NoLerpThisFrame();
}
void Actor::GetStateAnims( Container<const char *> *c )
{
if ( statemap )
statemap->GetAllAnims( c );
}
void Actor::Touched( Event *ev )
{
Entity *other;
other = ev->GetEntity( 1 );
sensoryPerception->Stimuli( STIMULI_SIGHT, other );
if ( other->isSubclassOf( Player) )
{
AddStateFlag( STATE_FLAG_TOUCHED_BY_PLAYER );
}
}
int Actor::ActorFlag_string_to_int( const str &actorflagstr ) const
{
str test;
for (int i = 0; i < ACTOR_FLAG_MAX; i++)
{
test = actor_flag_strings[i];
if ( !actorflagstr.icmp( actor_flag_strings[ i ] ) )
return i;
}
gi.WDPrintf( "Unknown actor flag - %s\n", actorflagstr.c_str() );
return -1;
}
int Actor::NotifyFlag_string_to_int( const str &notifyflagstr )
{
str test;
for (int i = 0; i < NOTIFY_FLAG_MAX; i++)
{
test = actor_notify_strings[i];
if ( !notifyflagstr.icmp( actor_notify_strings[ i ] ) )
return i;
}
gi.WDPrintf( "Unknown Notify Flag - %s\n", notifyflagstr.c_str() );
return -1;
}
void Actor::ArmorDamage( Event *ev )
{
AddStateFlag( STATE_FLAG_ATTACKED );
//if (!TakeDamage())
// return;
Entity *enemy;
enemy = ev->GetEntity( 3 );
if ( enemy->isSubclassOf( Player) )
{
//Teammates don't count MOD explosion, because it might
//be splash damage
if ( actortype == IS_TEAMMATE )
{
int MOD = ev->GetInteger(9);
if ( MOD != MOD_EXPLOSION )
AddStateFlag( STATE_FLAG_ATTACKED_BY_PLAYER );
}
else
{
AddStateFlag( STATE_FLAG_ATTACKED_BY_PLAYER );
}
}
if ( !enemy )
return;
::Damage damage(ev);
// Only react to an attack if we respond to pain
if ( sensoryPerception && sensoryPerception->ShouldRespondToStimuli( STIMULI_PAIN ) )
{
enemyManager->TryToAddToHateList( enemy );
}
enemyManager->AdjustDamageCaused( enemy , damage.damage );
strategos->NotifyDamageChanged( enemy , damage.damage );
if( GetActorFlag( ACTOR_FLAG_RESPONDING_TO_HITSCAN ) )
return;
//Okay, if we're about take damage from a hitscan weapon, we need to give the actor a chance to
//respond to it. If we get a 'true' back, then that means the function handled it and we can bail,
//if not, then we need to deal with the damage
if ( GetActorFlag(ACTOR_FLAG_INCOMING_HITSCAN) )
{
if ( RespondToHitscan() )
return;
}
//Here for Legacy
if ( ondamage_thread.length() )
{
RunDamageThread();
}
//New Way
RunCustomThread( "damaged" );
//Handle the GameSpecificStuff
if ( gameComponent )
gameComponent->HandleArmorDamage( ev ); // What's this?
if ( GetNotifyFlag(NOTIFY_FLAG_DAMAGED) )
_notifyGroupOfDamage();
//Now Check if the damage is coming from another Actor, if so, we may need to modifiy it.
//This is because the some enemies are so haus, they could knock the living crap out of each other
//if we don't modifiy the damage
/*Entity* ent = ev->GetEntity(3);
if (ent->isSubclassOf(Actor) )
{
float modDamage = (ev->GetFloat(1)) * actor_to_actor_damage_modifier;
Event *event = 0; //Shut up compiler... sheesh...
event = new Event( EV_Damage );
event->AddFloat(modDamage);
event->AddEntity(ev->GetEntity(2));
event->AddEntity(ev->GetEntity(3));
event->AddVector(ev->GetVector(4));
event->AddVector(ev->GetVector(5));
event->AddVector(ev->GetVector(6));
event->AddInteger(ev->GetInteger(7));
event->AddInteger(ev->GetInteger(8));
event->AddInteger(ev->GetInteger(9));
Sentient::ArmorDamage(event);
delete event;
return;
}*/
Sentient::ArmorDamage(damage);
sensoryPerception->Stimuli( STIMULI_PAIN , enemy );
}
Actor *GetActor ( const str &actor_name )
{
Actor* testActor;
int i;
for ( i = 1; i <= SleepList.NumObjects(); i++ )
{
testActor = (Actor*)SleepList.ObjectAt( i );
if (testActor->targetname == actor_name)
return testActor;
}
for ( i = 1; i <= ActiveList.NumObjects(); i++ )
{
testActor = (Actor*)ActiveList.ObjectAt( i );
if (testActor->targetname == actor_name)
return testActor;
}
return NULL;
}
void Actor::SetFlagOnEnemy( Event *ev )
{
str flag_name;
int flag;
qboolean flag_bool;
Actor* act = 0;
flag_name = ev->GetString(1);
flag_bool = ev->GetBoolean(2);
flag = ActorFlag_string_to_int( flag_name );
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if ( !currentEnemy->isSubclassOf( Actor ) )
return;
act = (Actor*)(Entity*)currentEnemy;
if ( act )
act->SetActorFlag( flag, flag_bool );
}
void Actor::TurnOnEnemyAI( Event *ev )
{
Actor* act = 0;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if ( !currentEnemy->isSubclassOf( Actor ) )
return;
act = (Actor*)(Entity*)currentEnemy;
if ( act )
act->TurnAIOn();
}
void Actor::TurnOffEnemyAI( Event *ev )
{
Actor* act = 0;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if ( !currentEnemy->isSubclassOf( Actor ) )
return;
act = (Actor*)(Entity*)currentEnemy;
if ( act )
act->TurnAIOff();
}
void Actor::PickupThrowObject( Event *ev )
{
Entity *ent;
str bone;
ent = (Entity*)enemyManager->GetAlternateTarget();
if (!ent->isSubclassOf(ThrowObject) )
return;
ThrowObject* tobj = 0;
tobj = (ThrowObject*)ent;
if (!tobj) return;
bone = ev->GetString( 1 );
tobj->Pickup(this , bone );
haveThrowObject = true;
}
void Actor::TossThrowObject( Event *ev )
{
// Due to the changes with enemy management, throw object stuff no longer works!!!
ThrowObject* tobj = 0;
float speed = 0;
float gravity = 1;
float damage = 25;
Entity* currentEnt;
currentEnt = enemyManager->GetCurrentEnemy();
if ( !currentEnt )
return;
tobj = (ThrowObject*)(Entity*)enemyManager->GetAlternateTarget();
if(!tobj || !currentEnt->isSubclassOf(Sentient)) return;
speed = ev->GetFloat( 1 );
gravity = ev->GetFloat( 2 );
if (ev->NumArgs() > 2 )
damage = ev->GetFloat( 3 );
//Throw requires a Sentient Pointer, so we need to cast. However, we should
//probably look into changing Throw to take an entity instead
tobj->Throw(this, speed , (Sentient*)currentEnt , gravity, damage );
}
void Actor::SetTurretMode( Event *ev )
{
qboolean tmode;
if (ev->NumArgs() > 0 )
tmode = ev->GetBoolean( 1 );
else
tmode = true;
SetActorFlag( ACTOR_FLAG_TURRET_MODE , tmode );
}
void Actor::SetOnDamageThread( Event *ev )
{
ondamage_thread = ev->GetString( 1 );
if ( ev->NumArgs() > 1 )
ondamage_threshold = ev->GetInteger( 2 );
}
void Actor::SetTimeBetweenSleepChecks( Event *ev )
{
timeBetweenSleepChecks = ev->GetFloat( 1 );
}
void Actor::AttachCurrentEnemy( Event *ev )
{
Actor *act = 0;
Vector offset;
str bone;
int tagnum;
bone = ev->GetString( 1 );
offset = ev->GetVector( 2 );
tagnum = gi.Tag_NumForName( this->edict->s.modelindex, bone );
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
if ( !currentEnemy->isSubclassOf( Actor ) )
return;
act = (Actor*)(Entity*)currentEnemy;
if ( act )
{
act->attach(this->entnum , tagnum , false , offset );
haveAttached = true;
}
}
void Actor::AttachActor( Event *ev )
{
Actor* new_actor = 0;
new_actor = new Actor;
if ( !new_actor )
return;
str modelName = ev->GetString( 1 );
str targetName = ev->GetString( 2 );
str bone = ev->GetString( 3 );
Vector offset;
new_actor->setModel( modelName );
new_actor->SetTargetName( targetName.c_str() );
if ( ev->NumArgs() > 3 )
offset = ev->GetVector( 4 );
int tagnum = gi.Tag_NumForName( this->edict->s.modelindex, bone );
new_actor->attach(this->entnum , tagnum , false , offset );
}
void Actor::SetEnemyAttached( Event *ev )
{
haveAttached = ev->GetBoolean( 1 );
}
void Actor::GiveActorWeapon( Event *ev )
{
const char *type;
float amount;
float skillLevel;
amount = 1.0;
skillLevel = 1.0f;
type = ev->GetString( 1 );
if ( ev->NumArgs() > 1 )
skillLevel = ev->GetFloat( 2 );
giveItem( type, (int)amount, false, skillLevel );
}
void Actor::RemoveActorWeapon( Event *ev )
{
takeItem( ev->GetString( 1 ) );
}
void Actor::PutawayWeapon( Event *ev )
{
weaponhand_t hand = WEAPON_RIGHT;
if ( ev->NumArgs() > 0 )
hand = WeaponHandNameToNum(ev->GetString( 1 ));
Sentient::DeactivateWeapon(hand);
}
void Actor::UseActorWeapon( Event *ev )
{
str weaponName;
str handToUse;
weaponhand_t hand;
weaponName = ev->GetString( 1 );
hand = WEAPON_DUAL;
if ( ev->NumArgs() > 1 )
{
handToUse = ev->GetString( 2 );
if ( !stricmp( handToUse.c_str() , "right" ) )
hand = WEAPON_RIGHT;
if ( !stricmp( handToUse.c_str() , "left" ) )
hand = WEAPON_LEFT;
if ( !stricmp( handToUse.c_str() , "dual" ) )
hand = WEAPON_DUAL;
}
combatSubsystem->UseActorWeapon( weaponName , hand );
}
void Actor::AttachModelToTag( const str &modelName , const str &tagName )
{
Event *attach_event;
attach_event = new Event( EV_AttachModel );
attach_event->AddString( modelName );
attach_event->AddString( tagName );
PostEvent( attach_event, 0.0f );
}
void Actor::DetachModelFromTag( const str &tagName )
{
Event *detach_event;
detach_event = new Event( EV_RemoveAttachedModel );
detach_event->AddString( tagName );
PostEvent ( detach_event, 0.0f );
}
//==============================================
// Sensory Perception Initilization
//==============================================
void Actor::SetFOV( Event *ev )
{
sensoryPerception->SetFOV( ev->GetFloat( 1 ) );
}
void Actor::SetVisionDistance( Event *ev )
{
sensoryPerception->SetVisionDistance( ev->GetFloat( 1 ) );
}
void Actor::ClearCurrentEnemy( Event *ev )
{
enemyManager->ClearCurrentEnemy();
}
//
// Name: SetAbsoluteMax()
// Parameters: Event *ev
// Description: Sets an absoluteMax variable that can be used to "leash"
// an actor to an entity
//
void Actor::SetAbsoluteMax( Event *ev )
{
absoluteMax = ev->GetFloat( 1 );
}
//
// Name: SetAbsoluteMin()
// Parameters: Event *ev
// Description: Sets an absoluteMin variable that can be used to "leash"
// an actor to an entity
//
void Actor::SetAbsoluteMin( Event *ev )
{
absoluteMin = ev->GetFloat( 1 );
}
//
// Name: SetPreferredMax()
// Parameters: Event *ev
// Description: Sets a preferredMax variable that can be used to "leash"
// an actor to an entity
//
void Actor::SetPreferredMax( Event *ev )
{
preferredMax = ev->GetFloat( 1 );
}
//
// Name: SetPreferredMin()
// Parameters: Event *ev
// Description: Sets a preferredMin variable that can be used to "leash"
// an actor to an entity
//
void Actor::SetPreferredMin( Event *ev )
{
preferredMin = ev->GetFloat( 1 );
}
void Actor::SetDisabled( Event *ev )
{
qboolean disabled = ev->GetBoolean( 1 );
SetActorFlag(ACTOR_FLAG_DISABLED , disabled );
}
void Actor::SetCrippled( Event *ev )
{
qboolean crippled = ev->GetBoolean( 1 );
SetActorFlag(ACTOR_FLAG_CRIPPLED , crippled );
}
void Actor::SetInAlcove( Event *ev )
{
qboolean inalcove = ev->GetBoolean( 1 );
SetActorFlag( ACTOR_FLAG_IN_ALCOVE , inalcove );
}
void Actor::SetAimLeadFactors( Event *ev )
{
minLeadFactor = ev->GetFloat( 1 );
maxLeadFactor = ev->GetFloat( 2 );
}
void Actor::SetActorType( Event *ev )
{
str aType;
aType = ev->GetString( 1 );
if ( !Q_stricmp( aType.c_str() , "inanimate" ) )
{
// Inanimates are special... be sure to clear the
// monster flag, don't let them move, bleed, or
// gib.
actortype = IS_INANIMATE;
edict->svflags &= ~SVF_MONSTER;
setMoveType( MOVETYPE_STATIONARY );
flags &= ~FL_BLOOD;
flags &= ~FL_DIE_GIBS;
return;
}
else if ( !Q_stricmp( aType.c_str() , "monster" ) )
{
actortype = IS_MONSTER;
edict->s.eFlags |= EF_ENEMY;
level.enemySpawned( this );
}
else if ( !Q_stricmp( aType.c_str() , "enemy" ) )
{
actortype = IS_ENEMY;
edict->s.eFlags |= EF_ENEMY;
level.enemySpawned( this );
}
else if ( !Q_stricmp( aType.c_str() , "civilian" ) )
{
actortype = IS_CIVILIAN;
edict->s.eFlags |= EF_FRIEND;
}
else if ( !Q_stricmp( aType.c_str() , "friend" ) )
{
actortype = IS_FRIEND;
edict->s.eFlags |= EF_FRIEND;
}
else if ( !Q_stricmp( aType.c_str() , "animal" ) )
{
actortype = IS_ANIMAL;
edict->s.eFlags |= EF_FRIEND;
}
else if ( !Q_stricmp( aType.c_str() , "teammate" ) )
{
TeamMateList.AddUniqueObject(this);
actortype = IS_TEAMMATE;
// Teammates Never Go To Sleep
max_inactive_time = -1.0f;
edict->s.eFlags |= EF_FRIEND;
edict->clipmask = MASK_MONSTERSOLID | MASK_PLAYERSOLID;
}
}
void Actor::DebugStates( Event *ev )
{
int state = ev->GetInteger( 1 );
if ( state >= MAX_DEBUG_TYPES )
return;
showStates = ( stateDebugType_t )ev->GetInteger( 1 );
}
//***********************************************************************************************
//
// Combat functions
//
//***********************************************************************************************
void Actor::IncomingProjectile( Event *ev )
{
incoming_proj = ev->GetEntity( 1 );
incoming_time = level.time + .1f; //+ G_Random( .1 );
}
void Actor::FireProjectile( Event *ev )
{
Vector orig;
Vector dir;
str tag_name;
str projectile_name;
int number_of_tags = 1;
qboolean arc = false;
float speed = 0.0f;
float offset = 0.0f;
bool leadTarget = false;
float spread = 0.0f;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
enemyManager->FindHighestHateEnemy();
if ( !currentEnemy )
return;
tag_name = ev->GetString( 1 );
projectile_name = ev->GetString( 2 );
if ( ev->NumArgs() > 2 )
number_of_tags = ev->GetInteger( 3 );
if ( ev->NumArgs() > 3 )
arc = ev->GetBoolean( 4 );
if ( ev->NumArgs() > 4 )
speed = ev->GetFloat( 5 );
if ( ev->NumArgs() > 5 )
offset = ev->GetFloat( 6 );
if ( ev->NumArgs() > 6 )
leadTarget = ev->GetBoolean( 7 );
if ( ev->NumArgs() > 7 )
spread = ev->GetFloat( 8 );
// Find the closest tag
if ( !GetClosestTag( tag_name, number_of_tags, currentEnemy->centroid, &orig ) )
{
// Could not find the tag so just use the centroid of the actor
orig[0] = edict->centroid[0];
orig[1] = edict->centroid[1];
orig[2] = edict->centroid[2];
}
// Add projectile to world
Vector targetVelocity = currentEnemy->velocity;
Vector targetPos = currentEnemy->centroid;
Vector newTargetPos = targetPos;
if ( leadTarget )
{
float projSpeed = speed;
if ( projSpeed <= 0 )
projSpeed = 1;
newTargetPos = combatSubsystem->GetLeadingTargetPos( projSpeed , currentEnemy->centroid , currentEnemy );
}
//Apply Spread
if ( spread > 0 )
{
newTargetPos.x = newTargetPos.x + ( G_CRandom(spread) );
newTargetPos.y = newTargetPos.y + ( G_CRandom(spread) );
newTargetPos.z = newTargetPos.z + ( G_CRandom(spread) );
}
dir = newTargetPos - orig;
if ( arc )
{
Vector xydir;
float traveltime;
float vertical_speed;
Vector proj_velocity;
xydir = dir;
xydir.z = 0.0f;
if ( speed == 0.0f )
speed = 500.0f;
traveltime = xydir.length() / speed;
vertical_speed = ( dir.z / traveltime ) + ( 0.5f * gravity * sv_currentGravity->value * traveltime );
xydir.normalize();
proj_velocity = speed * xydir;
proj_velocity.z = vertical_speed;
speed = proj_velocity.length();
proj_velocity.normalize();
dir = proj_velocity;
}
if ( offset )
{
Vector offset_angle = dir.toAngles();
offset_angle[YAW] += offset;
offset_angle.AngleVectors( &dir );
}
dir.normalize();
ProjectileAttack( orig, dir, this, projectile_name.c_str(), 1.0f, speed );
SaveAttack( orig, dir );
}
void Actor::FireRadiusAttack ( Event *ev )
{
str tagName = ev->GetString( 1 );
str meansOfDeath = ev->GetString( 2 );
float damage = ev->GetFloat( 3 );
float radius = ev->GetFloat( 4 );
float knockback = ev->GetFloat( 5 );
qboolean constant = ev->GetBoolean( 6 );
int MOD = MOD_NameToNum( meansOfDeath );
RadiusDamage( this , this, damage, this, MOD, radius, knockback, constant );
}
void Actor::FireBullet( Event *ev )
{
str tag_name;
qboolean use_current_pitch;
float range = 1000;
float damage;
float knockback;
str means_of_death_string;
int attack_means_of_death;
Vector spread;
Vector pos;
Vector forward;
Vector left;
Vector right;
Vector up;
Vector attack_angles;
Vector dir;
Vector enemy_angles;
tag_name = ev->GetString( 1 );
use_current_pitch = ev->GetBoolean( 2 );
damage = ev->GetFloat( 3 );
knockback = ev->GetFloat( 4 );
means_of_death_string = ev->GetString( 5 );
spread = ev->GetVector( 6 );
if ( ev->NumArgs() > 6 )
range = ev->GetFloat( 7 );
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
// Get the position where the bullet starts
GetTag( tag_name, &pos, &forward, &left, &up );
right = left * -1.0f;
// Get the real pitch of the bullet attack
if ( !use_current_pitch && currentEnemy )
{
attack_angles = forward.toAngles();
dir = currentEnemy->centroid - pos;
// Temporary Change ( 10/3/01 -- SK )
dir.z += 10;
enemy_angles = dir.toAngles();
attack_angles[PITCH] = enemy_angles[PITCH];
// Temporary Change (10/3/01 -- SK )
//attack_angles.AngleVectors( &forward, &left, &up );
enemy_angles.AngleVectors( &forward, &left, &up );
right = left * -1.0f;
}
//Little Sanity Check
if ( shotsFired > 1000 )
shotsFired = 1000;
attack_means_of_death = MOD_NameToNum( means_of_death_string );
BulletAttack( pos, forward, right, up, range, damage, knockback, 0, attack_means_of_death, spread, 1, this );
SaveAttack( pos, forward );
}
void Actor::SaveAttack( const Vector &orig, const Vector &dir )
{
Vector attack_mins;
Vector attack_maxs;
Vector end;
trace_t trace;
qboolean hit;
Entity *ent;
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
{
SetActorFlag( ACTOR_FLAG_LAST_ATTACK_HIT, true );
return;
}
// Do trace
attack_mins = Vector( -1.0f, -1.0f, -1.0f );
attack_maxs = Vector( 1.0f ,1.0f ,1.0f );
end = orig + ( dir * 8192.0f );
trace = G_Trace( orig, attack_mins, attack_maxs, end, this, MASK_SHOT, false, "Actor::SaveAttack" );
// See what we hit
last_attack_entity_hit = NULL;
if ( trace.ent )
{
ent = trace.ent->entity;
if ( ent == currentEnemy )
{
hit = true;
}
else if ( ent->isSubclassOf( Entity ) && ( ent != world ) )
{
last_attack_entity_hit = ent;
last_attack_entity_hit_pos = ent->origin;
hit = false;
}
else
{
hit = false;
}
}
else
{
hit = false;
}
// Do an extra check because of NOCLIP
if ( currentEnemy->movetype == MOVETYPE_NOCLIP )
hit = true;
// Save last attack info
last_attack_pos = origin;
last_attack_enemy_pos = currentEnemy->origin;
SetActorFlag( ACTOR_FLAG_LAST_ATTACK_HIT, hit );
}
qboolean Actor::TestAttack( const str &tag_name )
{
qboolean hit;
trace_t trace;
Vector attack_mins;
Vector attack_maxs;
Vector start;
// Make sure we still have an enemy and he is hitable
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
if ( currentEnemy->movetype == MOVETYPE_NOCLIP )
return false;
// Make sure we won't hit any friends
attack_mins = Vector( -1.0f, -1.0f, -1.0f );
attack_maxs = Vector( 1.0f, 1.0f, 1.0f );
if ( tag_name.length() )
{
GetTag( tag_name.c_str(), &start );
}
else
{
start = centroid;
}
trace = G_Trace( start, attack_mins, attack_maxs, currentEnemy->centroid, this, MASK_SHOT, false, "Actor::TestAttack" );
if ( trace.ent && ( trace.ent->entity != currentEnemy ) && trace.ent->entity->isSubclassOf( Sentient ) && !enemyManager->IsValidEnemy( trace.ent->entity ) )
return false;
// See if we hit last time
hit = GetActorFlag( ACTOR_FLAG_LAST_ATTACK_HIT );
if ( hit )
return true;
// Didn't hit last time so see if anything has changed
// See if actor has moved
if ( last_attack_pos != origin )
return true;
// See if enemy has moved
if ( last_attack_enemy_pos != currentEnemy->origin )
return true;
// See if entity in the way has moved
if ( last_attack_entity_hit && ( last_attack_entity_hit_pos != last_attack_entity_hit->origin ) )
return true;
// See if entity in the way was a door and has opened
if ( last_attack_entity_hit && last_attack_entity_hit->isSubclassOf( Door ) )
{
Door *door;
door = (Door *)(Entity *)last_attack_entity_hit;
if ( door->isOpen() )
return true;
}
// See if entity in the way has become non-solid
if ( last_attack_entity_hit && ( last_attack_entity_hit->edict->solid == SOLID_NOT ) )
return true;
// Nothing has changed so this attack should fail too
return false;
}
void Actor::MeleeEvent( Event *ev )
{
Vector pos;
Vector end;
Vector dir;
float damage = 20;
qboolean success;
str tag_name;
Vector attack_vector;
float attack_width = 0;
float attack_max_height = 0;
float attack_min_height = 0;
float attack_length = 100;
str means_of_death_string;
meansOfDeath_t attack_means_of_death;
float knockback;
qboolean use_pitch_to_enemy = false;
float attack_final_height;
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
// See if we should really attack
if ( GetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_ON ) && GetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_DAMAGED ) )
return;
if ( !GetActorFlag( ACTOR_FLAG_DAMAGE_ALLOWED ) )
return;
// Get all of the parameters
if ( ev->NumArgs() > 0 )
damage = ev->GetFloat( 1 );
if ( _useWeaponDamage != WEAPON_ERROR )
{
Weapon *weap = GetActiveWeapon(_useWeaponDamage);
if ( weap )
damage = weap->GetBulletDamage();
}
if ( ev->NumArgs() > 1 )
tag_name = ev->GetString( 2 );
if ( ev->NumArgs() > 2 )
means_of_death_string = ev->GetString( 3 );
if ( ev->NumArgs() > 3 )
{
attack_vector = ev->GetVector( 4 );
attack_width = attack_vector[0];
attack_length = attack_vector[1];
attack_max_height = attack_vector[2];
}
if ( ev->NumArgs() > 4 )
{
knockback = ev->GetFloat( 5 );
}
else
{
knockback = damage * 8.0f;
}
if ( ev->NumArgs() > 5 )
{
use_pitch_to_enemy = ev->GetInteger( 6 );
}
if ( ev->NumArgs() > 6 )
attack_min_height = ev->GetFloat( 7 );
else
attack_min_height = -attack_max_height;
if ( ev->NumArgs() > 7 )
attack_final_height = ev->GetFloat( 8 );
else
attack_final_height = 50.0f;
if ( tag_name.length() && GetTag( tag_name.c_str(), &pos, &dir ) )
{
end = pos + ( dir * attack_length );
}
else
{
pos = edict->centroid;
dir = orientation[0];
dir.normalize();
end = pos + ( dir * attack_length );
if ( attack_length )
end[2] += attack_final_height;
}
if ( use_pitch_to_enemy )
{
if ( currentEnemy )
{
Vector enemy_dir;
Vector angles;
Vector enemy_angles;
float length;
dir = end - pos;
length = dir.length();
angles = dir.toAngles();
enemy_dir = currentEnemy->centroid - pos;
enemy_angles = enemy_dir.toAngles();
angles[PITCH] = enemy_angles[PITCH];
angles.AngleVectors( &dir );
end = pos + ( dir * length );
}
}
if ( means_of_death_string.length() > 0 )
attack_means_of_death = (meansOfDeath_t)MOD_NameToNum( means_of_death_string );
else
attack_means_of_death = MOD_CRUSH;
// Do the actual attack
Weapon *weap = 0;
if ( _useWeaponDamage != WEAPON_ERROR )
weap = GetActiveWeapon(_useWeaponDamage);
if ( weap )
success = MeleeAttack( pos, end, damage, this, attack_means_of_death, attack_width, attack_min_height, attack_max_height, knockback, true, NULL, weap );
else
success = MeleeAttack( pos, end, damage, this, attack_means_of_death, attack_width, attack_min_height, attack_max_height, knockback );
if ( success )
{
AddStateFlag( STATE_FLAG_MELEE_HIT );
RunCustomThread( "meleehit" );
if ( GetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_ON ) )
SetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_DAMAGED, true );
}
}
void Actor::ChargeWater( Event *ev )
{
int cont;
float damage;
float radius;
Entity *ent;
int brushnum;
int entity_brushnum;
float real_damage;
Vector dir;
float dist;
// See if we are standing in water
cont = gi.pointcontents( origin, 0 );
if ( !(cont & MASK_WATER) )
return;
// Get parms
damage = ev->GetFloat( 1 );
radius = ev->GetFloat( 2 );
if ( !damage || !radius )
return;
// Determine what brush we are in
brushnum = gi.pointbrushnum( origin, 0 );
// Find everything in radius
ent = NULL;
for( ent = findradius( ent, origin, radius ) ; ent ; ent = findradius( ent, origin, radius ) )
{
if ( ent->takedamage )
{
entity_brushnum = gi.pointbrushnum( origin, 0 );
if ( brushnum == entity_brushnum )
{
dir = ent->origin - origin;
dist = dir.length();
if ( dist < radius )
{
real_damage = damage - ( damage * ( dist / radius ) );
ent->Damage( this, this, real_damage, origin, dir, vec_zero, 0, 0, MOD_ELECTRICWATER );
}
}
}
}
}
void Actor::DamageOnceStart( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_ON, true );
SetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_DAMAGED, false );
}
void Actor::DamageOnceStop( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DAMAGE_ONCE_ON, false );
}
void Actor::DamageAllowed( Event *ev )
{
SetActorFlag( ACTOR_FLAG_DAMAGE_ALLOWED, ev->GetBoolean( 1 ) );
}
qboolean Actor::CanAttackFrom( const Vector &pos, const Entity *ent, qboolean usecurrentangles )
{
int mask;
Vector delta;
Vector start;
Vector end;
float len;
trace_t trace;
Entity *t;
Vector ang;
if ( usecurrentangles )
{
Vector dir;
// Fixme ?
//start = pos + GunPosition() - origin;
start = pos + centroid - origin;
end = ent->centroid;
end.z += ( ent->absmax.z - ent->centroid.z ) * 0.75f;
delta = end - start;
ang = delta.toAngles();
ang.x = ang.x;
ang.y = angles.y;
len = delta.length();
ang.AngleVectors( &dir, NULL, NULL );
dir *= len;
end = start + dir;
}
else
{
// Fixme ?
//start = pos + GunPosition() - origin;
start = pos + centroid - origin;
end = ent->centroid;
end.z += ( ent->absmax.z - ent->centroid.z ) * 0.75f;
delta = end - start;
len = delta.length();
}
// shoot past the guy we're shooting at
end += delta * 4.0f;
// Check if he's visible
mask = MASK_SHOT;
trace = G_Trace( start, vec_zero, vec_zero, end, this, mask, false, "Actor::CanShootFrom" );
if ( trace.startsolid )
{
return false;
}
// see if we hit anything at all
if ( !trace.ent )
{
return false;
}
// If we hit the guy we wanted, then shoot
if ( trace.ent == ent->edict )
{
return true;
}
// If we hit someone else we don't like, then shoot
t = trace.ent->entity;
if ( enemyManager->IsValidEnemy( t ) )
{
return true;
}
// if we hit something breakable, check if shooting it will
// let us shoot someone.
if ( t->isSubclassOf( Object ) ||
t->isSubclassOf( ScriptModel ) )
{
trace = G_Trace( Vector( trace.endpos ), vec_zero, vec_zero, end, t, mask, false, "Actor::CanShootFrom 2" );
if ( trace.startsolid )
{
return false;
}
// see if we hit anything at all
if ( !trace.ent )
{
return false;
}
// If we hit the guy we wanted, then shoot
if ( trace.ent == ent->edict )
{
return true;
}
// If we hit someone else we don't like, then shoot
if ( enemyManager->IsValidEnemy( trace.ent->entity ) )
{
return true;
}
// Forget it then
return false;
}
return false;
}
qboolean Actor::CanAttack( Entity *ent, qboolean usecurrentangles )
{
return CanAttackFrom( origin, ent, usecurrentangles );
}
qboolean Actor::EntityHasFireType( Entity *ent, firetype_t fire_type )
{
Player *player;
Weapon *weapon;
firetype_t weapon_fire_type;
if ( !ent )
return false;
if ( !ent->isSubclassOf( Player ) )
return true;
player = (Player *)(Entity *)ent;
// Try left hand
weapon = player->GetActiveWeapon( WEAPON_LEFT );
if ( weapon )
{
weapon_fire_type = weapon->GetFireType( FIRE_MODE1 );
if ( weapon_fire_type == fire_type )
return true;
}
// Try right hand
weapon = player->GetActiveWeapon( WEAPON_RIGHT );
if ( weapon )
{
weapon_fire_type = weapon->GetFireType( FIRE_MODE1 );
if ( weapon_fire_type == fire_type )
return true;
}
// Try dual weapons
weapon = player->GetActiveWeapon( WEAPON_DUAL );
if ( weapon )
{
weapon_fire_type = weapon->GetFireType( FIRE_MODE1 );
if ( weapon_fire_type == fire_type )
return true;
}
return false;
}
void Actor::DamageEnemy( Event *ev )
{
// Get our current enemy
Entity *currentEnemy;
Vector dir;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
dir = currentEnemy->origin - origin;
dir.normalize();
float damage = 0.0f;
float knockback = 0.0f;
if ( ev->NumArgs() > 0 )
damage = ev->GetFloat( 1 );
if ( ev->NumArgs() > 1 )
{
str modelName = ev->GetString( 2 );
Event *attachEvent = new Event(EV_SpawnEffect);
if ( !attachEvent )
return;
attachEvent->AddString( modelName );
attachEvent->AddString( "Bip01" );
attachEvent->AddFloat( 2.5 );
currentEnemy->ProcessEvent( attachEvent );
}
if ( ev->NumArgs() > 2 )
knockback = ev->GetFloat( 3 );
if ( damage > 0.0f )
currentEnemy->Damage( this, this, damage, vec_zero, dir, vec_zero, (int)knockback, 0, MOD_CRUSH );
}
void Actor::DamageSelf( Event *ev )
{
float damage = 0;
str MOD_String = "crush";
int attack_means_of_death;
Event* event;
if (ev->NumArgs() > 0 )
damage = ev->GetFloat( 1 );
if (ev->NumArgs() > 1 )
MOD_String = ev->GetString( 2 );
attack_means_of_death = MOD_NameToNum( MOD_String );
event = new Event( EV_Damage );
event->AddFloat(damage);
event->AddEntity(this);
event->AddEntity(this);
event->AddVector(vec_zero);
event->AddVector(vec_zero);
event->AddVector(vec_zero);
event->AddInteger( 0 );
event->AddInteger( 0 );
event->AddInteger( attack_means_of_death );
ProcessEvent( event );
}
qboolean Actor::IsImmortal ( void )
{
return GetActorFlag( ACTOR_FLAG_IMMORTAL );
}
qboolean Actor::TakeDamage ( void )
{
return GetActorFlag( ACTOR_FLAG_TAKE_DAMAGE );
}
void Actor::FireWeapon( Event *ev )
{
combatSubsystem->FireWeapon();
}
void Actor::StopFireWeapon( Event *ev )
{
combatSubsystem->StopFireWeapon();
}
//===================================================================================
// Init Functions For Helper Classes
//===================================================================================
//
// Name: InitGameComponent()
// Parameters: None
// Description: Initalizes the actor's GameComponent
//
void Actor::InitGameComponent()
{
//Here is where the subclass of actorgamecomponents gets set. If you change the
//game (i.e. a mod or new game ) Change this here too
gameComponent = NULL;
gameComponent = new EFGameComponent( this );
if ( !gameComponent )
gi.Error( ERR_FATAL, "Actor Could not create gameComponent" );
}
//
// Name: InitSensoryPerception()
// Parameters: None
// Description: Initalizes the actor's SensoryPerception
//
void Actor::InitSensoryPerception()
{
sensoryPerception = NULL;
sensoryPerception = new SensoryPerception( this );
if ( !sensoryPerception )
gi.Error( ERR_FATAL, "Actor Could not create sensoryPerception" );
}
//
// Name: InitThinkStrategy()
// Parameters: None
// Description: Initalizes the actor's ThinkStrategy
//
void Actor::InitThinkStrategy()
{
thinkStrategy = NULL;
thinkStrategy = new DefaultThink();
if ( !thinkStrategy )
gi.Error( ERR_FATAL, "Actor Could not create thinkStrategy" );
}
//
// Name: InitStrategos()
// Parameters: None
// Description: Initalizes the actor's Strategos
//
void Actor::InitStrategos()
{
strategos = NULL;
strategos = new DefaultStrategos ( this );
if ( !strategos )
gi.Error( ERR_FATAL, "Actor Could not create strategos" );
}
//
// Name: InitEnemyManager()
// Parameters: None
// Description: Initalizes the actor's EnemyManager
//
void Actor::InitEnemyManager()
{
enemyManager = NULL;
enemyManager = new EnemyManager( this );
if ( !enemyManager )
gi.Error( ERR_FATAL, "Actor Could not create enemyManager" );
}
//
// Name: InitPackageManager()
// Parameters: None
// Description: Initalizes the actor's PackageManager
//
void Actor::InitPackageManager()
{
packageManager = NULL;
packageManager = new PackageManager( this );
if ( !packageManager )
gi.Error( ERR_FATAL, "Actor Could not create packageManager" );
}
//
// Name: InitMovementSubsystem()
// Parameters: None
// Description: Initalizes the actor's MovementSubsystem
//
void Actor::InitMovementSubsystem()
{
movementSubsystem = NULL;
movementSubsystem = new MovementSubsystem( this );
if ( !movementSubsystem )
gi.Error( ERR_FATAL, "Actor Could not create movementSubsystem" );
}
//
// Name: InitPersonality()
// Parameters: None
// Description: Initialize the actor's Personality
//
void Actor::InitPersonality()
{
personality = NULL;
personality = new Personality( this );
if ( !personality )
gi.Error( ERR_FATAL, "Actor Could not create personality" );
}
//
// Name: InitCombatSubsystem()
// Parameters: None
// Description: Initialize the actor's CombatSubsystem
//
void Actor::InitCombatSubsystem()
{
combatSubsystem = NULL;
combatSubsystem = new CombatSubsystem( this );
if ( !combatSubsystem )
gi.Error( ERR_FATAL, "Actor Could not create personality" );
}
void Actor::InitHeadWatcher()
{
headWatcher = NULL;
headWatcher = new HeadWatcher( this );
if ( !headWatcher )
gi.Error( ERR_FATAL, "Actor Could not create personality" );
}
void Actor::InitPostureController()
{
postureController = NULL;
postureController = new PostureController( this );
if ( !postureController )
gi.Error( ERR_FATAL, "Actor Could not create postureController" );
}
//===================================================================================
// Private Functions
//===================================================================================
//
// Name: _dropActorToGround()
// Parameters: None
// Description: Tries to drop the actor to the ground
//
void Actor::_dropActorToGround()
{
trace_t trace;
Vector end;
Vector start;
qboolean stuck;
str actor_name;
stuck = false;
start = origin + Vector( "0 0 1" );
end = origin;
end[ 2 ] -= 16;
//trace = G_Trace( start, mins, maxs, end, this, MASK_SOLID, false, "Actor::start" );
trace = G_Trace( start, mins, maxs, end, this, edict->clipmask, false, "Actor::start" );
if ( trace.startsolid || trace.allsolid )
stuck = true;
else if ( !( flags & FL_FLY ) )
{
setOrigin( trace.endpos );
groundentity = trace.ent;
}
if ( name.length() )
actor_name = name;
else
actor_name = getClassID();
if ( trace.fraction == 1 && movetype == MOVETYPE_STATIONARY && !GetActorFlag( ACTOR_FLAG_IGNORE_OFF_GROUND_WARNING ) )
gi.WDPrintf( "%s (%d) off of ground at '%5.1f %5.1f %5.1f'\n", actor_name.c_str(), entnum, origin.x, origin.y, origin.z );
if ( stuck && !GetActorFlag( ACTOR_FLAG_IGNORE_STUCK_WARNING ) )
{
groundentity = world->edict;
gi.WDPrintf( "%s (%d) stuck in world at '%5.1f %5.1f %5.1f'\n", actor_name.c_str(), entnum, origin.x, origin.y, origin.z );
}
SetActorFlag( ACTOR_FLAG_HAVE_MOVED, false );
last_origin = origin;
last_ground_z = origin.z;
}
//--------------------------------------------------------------
// Name: turnTowardsEntity()
// Class: Actor
//
// Description: Sets our Angles, AnimDir and MoveDir so that
// we are facing the specified entity
//
// Parameters: Entity *ent
// float extraYaw
//
// Returns: None
//--------------------------------------------------------------
void Actor::turnTowardsEntity( Entity *ent , float extraYaw )
{
Vector dir;
Vector new_angles;
//First, get the vector from myself to the entity.
//Then convert it to angles, and add any additional
//requested YAW
dir = ent->centroid - origin;
new_angles = dir.toAngles();
new_angles[YAW] += extraYaw;
//Now, Set my angles to these newly calculated Angles
angles[YAW] = new_angles[YAW];
angles[ROLL] = 0;
angles[PITCH] = 0;
//Now, before we can set my Anim and Movement Dir Vectors, we
//must convert my current angles back into a direction vector
//this is very very important. If we don't do this, then
//then my angles and my movement dir get out of sync resulting
//in some pretty bad wonk-i-ness.
angles.AngleVectors( &dir );
//Okay, now let's actually SET everything
setAngles( angles );
movementSubsystem->setAnimDir( dir );
movementSubsystem->setMoveDir( dir );
}
void Actor::_printDebugInfo(const str &laststate , const str &currentState , const str &legAnim , const str &torsoAnim )
{
// Print Debug Stuff
gi.Printf( "\n");
gi.Printf( "Name : %s\n", name.c_str() );
gi.Printf( "TargetName : %s\n", targetname.c_str() );
gi.Printf( "EntNum : %d\n", entnum );
gi.Printf( "\n" );
switch( showStates )
{
case DEBUG_STATES_ONLY:
gi.Printf( "StateMap or Package: %s\n", statemap_name.c_str() );
gi.Printf( "LastState : %s\n", laststate.c_str() );
gi.Printf( "CurrentState : %s\n", currentState.c_str() );
break;
case DEBUG_STATES_BEHAVIORS:
gi.Printf( "StateMap or Package: %s\n", statemap_name.c_str() );
gi.Printf( "LastState : %s\n", laststate.c_str() );
gi.Printf( "CurrentState : %s\n", currentState.c_str() );
gi.Printf( "\n" );
if ( behavior )
{
gi.Printf( "Behavior : %s\n", behavior->getClassname() );
if ( behavior->GetInternalStateName().length() )
gi.Printf( "Internal Behavior State: %s\n" , behavior->GetInternalStateName().c_str() );
else
gi.Printf( "Internal Behavior State: Unspecified\n" );
}
if ( headBehavior )
gi.Printf( "HeadBehavior : %s\n", headBehavior->getClassname() );
if ( eyeBehavior )
gi.Printf( "EyeBehavior : %s\n", eyeBehavior->getClassname() );
if ( torsoBehavior )
gi.Printf( "TorsoBehavior : %s\n", torsoBehavior->getClassname() );
break;
case DEBUG_ALL:
gi.Printf( "StateMap or Package: %s\n", statemap_name.c_str() );
gi.Printf( "LastState : %s\n", laststate.c_str() );
gi.Printf( "CurrentState : %s\n", currentState.c_str() );
gi.Printf( "\n" );
if ( behavior )
{
gi.Printf( "Behavior : %s\n", behavior->getClassname() );
if ( behavior->GetInternalStateName().length() )
gi.Printf( "Internal Behavior State: %s\n" , behavior->GetInternalStateName().c_str() );
else
gi.Printf( "Internal Behavior State: Unspecified\n" );
}
if ( headBehavior )
gi.Printf( "HeadBehavior : %s\n", headBehavior->getClassname() );
if ( eyeBehavior )
gi.Printf( "EyeBehavior : %s\n", eyeBehavior->getClassname() );
if ( torsoBehavior )
gi.Printf( "TorsoBehavior : %s\n", torsoBehavior->getClassname() );
gi.Printf( "\n" );
gi.Printf( "LegAnim : %s\n", legAnim.c_str() );
gi.Printf( "TorsoAnim : %s\n", torsoAnim.c_str() );
break;
default:
return;
}
}
//===================================================================================
// Archive Functions
//===================================================================================
//
// Name: Archive()
// Parameters: Archiver &arc
// Description: Archives the Actor Data
//
inline void Actor::Archive( Archiver &arc )
{
str temp_state_name;
str temp_global_state_name;
str temp_last_state_name;
str temp_master_state_name;
str temp_last_master_state_name;
qboolean behavior_bool;
qboolean hBehavior_bool;
qboolean eBehavior_bool;
qboolean tBehavior_bool;
int num;
StateVar *stateVar;
threadlist_t* threadListEntry;
int i;
Sentient::Archive( arc );
arc.ArchiveSafePointer( &forcedEnemy );
arc.ArchiveString( &newanim );
arc.ArchiveInteger( &newanimnum );
arc.ArchiveInteger( &animnum );
arc.ArchiveString( &animname );
arc.ArchiveEventPointer( &newanimevent );
arc.ArchiveString( &last_anim_event_name );
arc.ArchiveString( &newTorsoAnim );
arc.ArchiveInteger( &newTorsoAnimNum );
arc.ArchiveString( &TorsoAnimName );
arc.ArchiveEventPointer( &newTorsoAnimEvent );
arc.ArchiveString( &last_torso_anim_event_name );
arc.ArchiveFloat( &absoluteMin );
arc.ArchiveFloat( &absoluteMax );
arc.ArchiveFloat( &preferredMin );
arc.ArchiveFloat( &preferredMax );
arc.ArchiveFloat( &activationDelay );
arc.ArchiveFloat( &activationStart );
ArchiveEnum( actortype, actortype_t );
ArchiveEnum( targetType, targetType_t );
arc.ArchiveBoolean( &validTarget );
arc.ArchiveBool( &_checkedChance );
arc.ArchiveBool( &_levelAIOff );
arc.ArchiveFloat( &bounce_off_velocity );
if ( arc.Saving() )
{
if ( behavior )
{
behavior_bool = true;
arc.ArchiveBoolean( &behavior_bool );
arc.ArchiveObject( behavior );
}
else
{
behavior_bool = false;
arc.ArchiveBoolean( &behavior_bool );
}
if ( headBehavior )
{
hBehavior_bool = true;
arc.ArchiveBoolean( &hBehavior_bool );
arc.ArchiveObject( headBehavior );
}
else
{
hBehavior_bool = false;
arc.ArchiveBoolean( &hBehavior_bool );
}
if ( eyeBehavior )
{
eBehavior_bool = true;
arc.ArchiveBoolean( &eBehavior_bool );
arc.ArchiveObject( eyeBehavior );
}
else
{
eBehavior_bool = false;
arc.ArchiveBoolean( &eBehavior_bool );
}
if ( torsoBehavior )
{
tBehavior_bool = true;
arc.ArchiveBoolean( &tBehavior_bool );
arc.ArchiveObject( torsoBehavior );
}
else
{
tBehavior_bool = false;
arc.ArchiveBoolean( &tBehavior_bool );
}
}
else
{
arc.ArchiveBoolean( &behavior_bool );
if ( behavior_bool )
{
behavior = ( Behavior * )arc.ReadObject();
currentBehavior = behavior->getClassname();
behaviorFailureReason = behavior->GetFailureReason();
}
else
{
behavior = NULL;
currentBehavior = "";
behaviorFailureReason = "";
}
arc.ArchiveBoolean( &hBehavior_bool );
if ( hBehavior_bool )
{
headBehavior = ( Behavior * )arc.ReadObject();
currentHeadBehavior = headBehavior->getClassname();
}
else
{
headBehavior = NULL;
currentHeadBehavior = "";
}
arc.ArchiveBoolean( &eBehavior_bool );
if ( eBehavior_bool )
{
eyeBehavior = ( Behavior * )arc.ReadObject();
currentEyeBehavior = eyeBehavior->getClassname();
}
else
{
eyeBehavior = NULL;
currentEyeBehavior = "";
}
arc.ArchiveBoolean( &tBehavior_bool );
if ( tBehavior_bool )
{
torsoBehavior = ( Behavior * )arc.ReadObject();
currentTorsoBehavior = torsoBehavior->getClassname();
}
else
{
torsoBehavior = NULL;
currentTorsoBehavior = "";
}
}
ArchiveEnum( behaviorCode, BehaviorReturnCode_t );
ArchiveEnum( headBehaviorCode, BehaviorReturnCode_t );
ArchiveEnum( eyeBehaviorCode, BehaviorReturnCode_t );
ArchiveEnum( torsoBehaviorCode, BehaviorReturnCode_t );
arc.ArchiveBoolean( &haveThrowObject );
arc.ArchiveString( &animset );
arc.ArchiveUnsigned( &actor_flags1 );
arc.ArchiveUnsigned( &actor_flags2 );
arc.ArchiveUnsigned( &actor_flags3 );
arc.ArchiveUnsigned( &actor_flags4 );
arc.ArchiveUnsigned( &notify_flags1 );
arc.ArchiveUnsigned( &state_flags );
arc.ArchiveFloat ( &chattime );
arc.ArchiveFloat ( &nextsoundtime );
arc.ArchiveFloat ( &_nextCheckForEnemyPath );
arc.ArchiveBool ( &_havePathToEnemy );
arc.ArchiveFloat ( &_nextPathDistanceToFollowTargetCheck );
arc.ArchiveFloat( &_nextPlayPainSoundTime );
arc.ArchiveFloat( &_playPainSoundInterval );
// Save dialog stuff
ArchiveEnum( DialogMode, DialogMode_t );
arc.ArchiveFloat ( &radiusDialogRange );
if ( arc.Saving() )
{
DialogNode_t *dialog_node;
byte more;
str alias_name;
str parm;
int current_parm;
dialog_node = dialog_list;
while( dialog_node )
{
more = true;
arc.ArchiveByte( &more );
alias_name = dialog_node->alias_name;
arc.ArchiveString( &alias_name );
arc.ArchiveInteger( &dialog_node->random_flag );
arc.ArchiveInteger( &dialog_node->number_of_parms );
arc.ArchiveFloat( &dialog_node->random_percent );
ArchiveEnum( dialog_node->dType , DialogType_t );
for( current_parm = 0 ; current_parm < dialog_node->number_of_parms ; current_parm++ )
{
arc.ArchiveByte( &dialog_node->parms[ current_parm ].type );
parm = dialog_node->parms[ current_parm ].parm;
arc.ArchiveString( &parm );
parm = dialog_node->parms[ current_parm ].parm2;
arc.ArchiveString( &parm );
}
dialog_node = dialog_node->next;
}
more = false;
arc.ArchiveByte( &more );
}
else
{
byte more;
DialogNode_t *new_dialog_node;
DialogNode_t *current_dialog_node = NULL;
str alias_name;
str parm;
int current_parm;
arc.ArchiveByte( &more );
while( more )
{
new_dialog_node = NewDialogNode();
if ( current_dialog_node )
current_dialog_node->next = new_dialog_node;
else
dialog_list = new_dialog_node;
current_dialog_node = new_dialog_node;
new_dialog_node->next = NULL;
arc.ArchiveString( &alias_name );
strcpy( new_dialog_node->alias_name, alias_name.c_str() );
arc.ArchiveInteger( &new_dialog_node->random_flag );
arc.ArchiveInteger( &new_dialog_node->number_of_parms );
arc.ArchiveFloat( &new_dialog_node->random_percent );
ArchiveEnum( new_dialog_node->dType , DialogType_t );
for( current_parm = 0 ; current_parm < new_dialog_node->number_of_parms ; current_parm++ )
{
arc.ArchiveByte( &new_dialog_node->parms[ current_parm ].type );
arc.ArchiveString( &parm );
strcpy( new_dialog_node->parms[ current_parm ].parm, parm.c_str() );
arc.ArchiveString( &parm );
strcpy( new_dialog_node->parms[ current_parm ].parm2, parm.c_str() );
}
arc.ArchiveByte( &more );
}
}
arc.ArchiveString( &_branchDialogName );
arc.ArchiveFloat( &dialog_done_time );
arc.ArchiveString( &dialog_state_name );
arc.ArchiveString( &dialog_old_state_name );
arc.ArchiveBool( &_ignoreNextContext );
arc.ArchiveString( &_nextContextToIgnore );
arc.ArchiveFloat( &_nextContextTime );
arc.ArchiveFloat(&_contextInterval);
arc.ArchiveSafePointer( &scriptthread );
arc.ArchiveString( &kill_thread );
arc.ArchiveString( &escape_thread );
arc.ArchiveString( &captured_thread );
arc.ArchiveString( &activate_thread );
arc.ArchiveString( &onuse_thread_name );
arc.ArchiveString( &ondamage_thread );
arc.ArchiveString( &alert_thread );
arc.ArchiveString( &idle_thread );
arc.ArchiveFloat( &pain_threshold );
arc.ArchiveFloat( &next_drown_time );
arc.ArchiveFloat( &air_finished );
arc.ArchiveInteger( &pain_type );
arc.ArchiveVector( &pain_angles );
arc.ArchiveInteger( &bullet_hits );
arc.ArchiveFloat( &next_pain_time );
arc.ArchiveFloat( &min_pain_time );
arc.ArchiveFloat( &next_forced_pain_time );
arc.ArchiveFloat( &max_pain_time );
arc.ArchiveString( &_deathEffect );
if ( arc.Saving() )
{
arc.ArchiveString( &statemap_name );
arc.ArchiveString( &masterstatemap_name );
if ( currentState )
temp_state_name = currentState->getName();
if ( globalState )
temp_global_state_name = globalState->getName();
if ( lastState )
temp_last_state_name = lastState->getName();
if ( currentMasterState )
temp_master_state_name = currentMasterState->getName();
if ( lastMasterState )
temp_last_master_state_name = lastMasterState->getName();
arc.ArchiveString( &temp_state_name );
arc.ArchiveString( &temp_global_state_name );
arc.ArchiveString( &temp_last_state_name );
arc.ArchiveString( &temp_master_state_name );
arc.ArchiveString( &temp_last_master_state_name );
}
else
{
arc.ArchiveString( &statemap_name );
arc.ArchiveString( &masterstatemap_name );
if ( statemap_name.length() )
{
Event *event;
event = new Event( EV_Actor_Statemap );
event->AddString( statemap_name.c_str() );
event->AddString( "" );
event->AddInteger( 1 );
ProcessEvent( event );
}
arc.ArchiveString( &temp_state_name );
arc.ArchiveString( &temp_global_state_name );
arc.ArchiveString( &temp_last_state_name );
if ( statemap )
{
currentState = statemap->FindState( temp_state_name.c_str() );
globalState = statemap->FindGlobalState( temp_global_state_name.c_str() );
lastState = statemap->FindState( temp_last_state_name.c_str() );
}
if ( masterstatemap_name.length() )
{
Event *event;
event = new Event( EV_Actor_MasterStateMap );
event->AddString( masterstatemap_name.c_str() );
event->AddString( "" );
event->AddInteger( 1 );
ProcessEvent ( event );
}
arc.ArchiveString( &temp_master_state_name );
arc.ArchiveString( &temp_last_master_state_name );
if ( masterstatemap )
{
currentMasterState = masterstatemap->FindState( temp_master_state_name.c_str() );
lastMasterState = masterstatemap->FindState( temp_last_master_state_name.c_str() );
}
}
arc.ArchiveFloat( &state_time );
arc.ArchiveFloat( &masterstate_time );
arc.ArchiveInteger( &times_done );
arc.ArchiveInteger( &masterstate_times_done );
arc.ArchiveFloat( &state_done_time );
arc.ArchiveFloat( &masterstate_done_time );
arc.ArchiveFloat( &last_time_active );
ArchiveEnum( showStates, stateDebugType_t );
ArchiveEnum( talkMode, talkModeStates_t );
arc.ArchiveBool( &useConvAnims );
// Don't save these
//static Condition<Actor> Conditions[];
//Container<Conditional *> conditionals;
//Container<Conditional *> master_conditionals;
arc.ArchiveString( &fuzzyengine_name );
arc.ArchiveBoolean( &fuzzyEngine_active );
if ( arc.Loading() )
{
if ( fuzzyengine_name.length() )
{
Event *event;
event = new Event ( EV_Actor_FuzzyEngine );
event->AddString( fuzzyengine_name.c_str() );
ProcessEvent ( event );
}
}
// Don't save
//Container<Conditional *> fuzzy_conditionals;
arc.ArchiveFloat( &maxEyeYawAngle );
arc.ArchiveFloat( &minEyeYawAngle );
arc.ArchiveFloat( &maxEyePitchAngle );
arc.ArchiveFloat( &minEyePitchAngle );
arc.ArchiveInteger( &saved_mode );
if ( arc.Saving() )
{
if ( saved_behavior )
{
behavior_bool = true;
arc.ArchiveBoolean( &behavior_bool );
arc.ArchiveObject( saved_behavior );
}
else
{
behavior_bool = false;
arc.ArchiveBoolean( &behavior_bool );
}
if ( saved_headBehavior )
{
hBehavior_bool = true;
arc.ArchiveBoolean( &hBehavior_bool );
arc.ArchiveObject( saved_headBehavior );
}
else
{
hBehavior_bool = false;
arc.ArchiveBoolean( &hBehavior_bool );
}
if ( saved_eyeBehavior )
{
eBehavior_bool = true;
arc.ArchiveBoolean( &eBehavior_bool );
arc.ArchiveObject( saved_eyeBehavior );
}
else
{
eBehavior_bool = false;
arc.ArchiveBoolean( &eBehavior_bool );
}
if ( saved_torsoBehavior )
{
tBehavior_bool = true;
arc.ArchiveBoolean( &tBehavior_bool );
arc.ArchiveObject( saved_torsoBehavior );
}
else
{
tBehavior_bool = false;
arc.ArchiveBoolean( &tBehavior_bool );
}
}
else
{
arc.ArchiveBoolean( &behavior_bool );
if ( behavior_bool )
saved_behavior = ( Behavior * )arc.ReadObject();
else
saved_behavior = NULL;
arc.ArchiveBoolean( &hBehavior_bool );
if ( hBehavior_bool )
saved_headBehavior = ( Behavior * )arc.ReadObject();
else
saved_headBehavior = NULL;
arc.ArchiveBoolean( &eBehavior_bool );
if ( eBehavior_bool )
saved_eyeBehavior = ( Behavior * )arc.ReadObject();
else
saved_eyeBehavior = NULL;
arc.ArchiveBoolean( &tBehavior_bool );
if ( tBehavior_bool )
saved_torsoBehavior = ( Behavior * )arc.ReadObject();
else
saved_torsoBehavior = NULL;
}
arc.ArchiveSafePointer( &saved_scriptthread );
arc.ArchiveSafePointer( &saved_actorthread );
arc.ArchiveString( &saved_anim_name );
arc.ArchiveString( &saved_state_name );
arc.ArchiveString( &saved_anim_event_name );
arc.ArchiveString( &part_name );
{
part_t part;
int current_part;
int number_of_parts;
part_t *part_ptr;
if ( arc.Saving() )
{
number_of_parts = parts.NumObjects();
}
else
{
parts.ClearObjectList();
}
arc.ArchiveInteger( &number_of_parts );
if ( arc.Loading() )
parts.Resize( number_of_parts );
for( current_part = 1; current_part <= number_of_parts ; current_part++ )
{
if ( arc.Saving() )
{
part = parts.ObjectAt( current_part );
part_ptr = &part;
}
else
{
parts.AddObject( part );
part_ptr = parts.AddressOfObjectAt( current_part );
}
arc.ArchiveSafePointer( &part_ptr->ent );
arc.ArchiveUnsigned( &part_ptr->state_flags );
}
}
arc.ArchiveSafePointer( &incoming_proj );
arc.ArchiveFloat( &incoming_time );
arc.ArchiveBoolean( &incoming_bullet );
arc.ArchiveString( &name );
arc.ArchiveFloat( &max_inactive_time );
arc.ArchiveVector( &eyeoffset );
arc.ArchiveFloat( &last_jump_time );
arc.ArchiveString( &enemytype );
arc.ArchiveFloat( &actorrange_time );
arc.ArchiveFloat( &last_height );
arc.ArchiveSafePointer( &last_ent );
arc.ArchiveFloat( &canseeenemy_time );
arc.ArchiveFloat( &canseeplayer_time );
arc.ArchiveInteger( &stage );
arc.ArchiveInteger( &num_of_spawns );
arc.ArchiveSafePointer( &spawnparent );
arc.ArchiveVector( &last_attack_pos );
arc.ArchiveVector( &last_attack_enemy_pos );
arc.ArchiveSafePointer( &last_attack_entity_hit );
arc.ArchiveVector( &last_attack_entity_hit_pos );
arc.ArchiveInteger( &mode );
arc.ArchiveVector( &last_known_enemy_pos );
arc.ArchiveVector( &last_known_player_pos );
arc.ArchiveFloat( &feet_width );
arc.ArchiveVector( &last_origin );
arc.ArchiveFloat( &next_find_enemy_time );
arc.ArchiveFloat( &minimum_melee_height );
arc.ArchiveFloat( &damage_angles );
arc.ArchiveFloat( &real_head_pitch );
arc.ArchiveFloat( &next_pain_sound_time );
arc.ArchiveFloat( &last_ground_z );
arc.ArchiveString( &emotion );
arc.ArchiveFloat( &next_blink_time );
arc.ArchiveFloat( &actor_to_actor_damage_modifier );
arc.ArchiveFloat( &last_used_time );
arc.ArchiveFloat( &hitscan_response_chance );
arc.ArchiveInteger( &shotsFired );
arc.ArchiveInteger( &ondamage_threshold );
arc.ArchiveFloat( &timeBetweenSleepChecks );
arc.ArchiveInteger ( &saved_bone_hit );
arc.ArchiveSafePointer( &_controller );
ArchiveEnum( _controlType, Actor::ActorControlType );
// Save out currentHelperNode
arc.ArchiveSafePointer( &currentHelperNode.node );
arc.ArchiveInteger( &currentHelperNode.mask );
arc.ArchiveInteger( &currentHelperNode.nodeID );
arc.ArchiveSafePointer( &ignoreHelperNode.node );
arc.ArchiveInteger( &ignoreHelperNode.mask );
arc.ArchiveInteger( &ignoreHelperNode.nodeID );
// Save out followTarget
arc.ArchiveSafePointer( &followTarget.currentFollowTarget );
arc.ArchiveSafePointer( &followTarget.specifiedFollowTarget );
arc.ArchiveFloat( &followTarget.maxRangeIdle );
arc.ArchiveFloat( &followTarget.minRangeIdle );
arc.ArchiveFloat( &followTarget.maxRangeCombat );
arc.ArchiveFloat( &followTarget.minRangeCombat );
arc.ArchiveInteger( &_steeringDirectionPreference );
if ( arc.Saving() )
{
num = stateVarList.NumObjects();
arc.ArchiveInteger( &num );
for( i = 1 ; i <= num ; i++ )
{
stateVar = stateVarList.ObjectAt( i );
arc.ArchiveString( &stateVar->varName );
arc.ArchiveString( &stateVar->varValue );
arc.ArchiveFloat( &stateVar->varTime );
}
}
else
{
arc.ArchiveInteger( &num );
for( i = 1 ; i <= num ; i++ )
{
stateVar = new StateVar;
stateVarList.AddObject( stateVar );
arc.ArchiveString( &stateVar->varName );
arc.ArchiveString( &stateVar->varValue );
arc.ArchiveFloat( &stateVar->varTime );
}
}
if ( arc.Saving() )
{
num = threadList.NumObjects();
arc.ArchiveInteger( &num );
for( i = 1 ; i <= num ; i++ )
{
threadListEntry = threadList.ObjectAt( i );
arc.ArchiveString( &threadListEntry->threadType );
arc.ArchiveString( &threadListEntry->threadName );
}
}
else
{
arc.ArchiveInteger( &num );
for ( i = 1; i <= num; i++ )
{
threadListEntry = new threadlist_t;
threadList.AddObject( threadListEntry );
arc.ArchiveString( &threadListEntry->threadType );
arc.ArchiveString( &threadListEntry->threadName );
}
}
arc.ArchiveSafePointer( &trigger );
arc.ArchiveString( &command );
arc.ArchiveString( &idle_state_name );
arc.ArchiveString( &master_idle_state_name );
arc.ArchiveString( &global_state_name );
arc.ArchiveFloat( &next_player_near );
arc.ArchiveSafePointer( &pickup_ent );
arc.ArchiveFloat( &stunned_end_time );
spawn_items.Archive( arc );
arc.ArchiveFloat( &spawn_chance );
arc.ArchiveString( &bounce_off_effect );
can_be_finsihed_by_mods.Archive( arc );
arc.ArchiveFloat( &max_boss_health );
arc.ArchiveBoolean( &haveAttached );
arc.ArchiveFloat( &currentSplineTime );
arc.ArchiveFloat( &_dialogMorphMult );
ArchiveEnum( _useWeaponDamage, weaponhand_t );
arc.ArchiveFloat( &_nextCheckForWorkNodeTime );
arc.ArchiveFloat( &_nextCheckForHibernateNodeTime );
arc.ArchiveFloat( &minLeadFactor );
arc.ArchiveFloat( &maxLeadFactor );
//arc.ArchiveInteger( &groupnumber );
// Handle the Archiving of our helper classes
// Archiving thinkStrategy is a little more complex than normal because thinkStrategy can point to multiple types
// of child classes and when we read it in we need to make sure to have the correct one
if ( arc.Saving() )
{
bool simplifiedThink;
if ( thinkStrategy->isSimple() )
simplifiedThink = true;
else
simplifiedThink = false;
arc.ArchiveBool( &simplifiedThink );
thinkStrategy->DoArchive ( arc );
}
else
{
bool simplifiedThink;
arc.ArchiveBool( &simplifiedThink );
if ( simplifiedThink )
{
delete thinkStrategy;
thinkStrategy = new SimplifiedThink( (Actor *)this );
}
thinkStrategy->DoArchive ( arc );
}
gameComponent->DoArchive ( arc , this );
sensoryPerception->DoArchive ( arc , this );
strategos->DoArchive ( arc , this );
enemyManager->DoArchive ( arc , this );
packageManager->DoArchive ( arc , this );
movementSubsystem->DoArchive ( arc , this );
personality->DoArchive ( arc , this );
combatSubsystem->DoArchive ( arc , this );
headWatcher->DoArchive ( arc , this );
postureController->DoArchive ( arc , this );
arc.ArchiveFloat( &lastPathCheck_Work );
arc.ArchiveFloat( &lastPathCheck_Flee );
arc.ArchiveFloat( &lastPathCheck_Patrol );
arc.ArchiveBoolean( &testing );
if ( isThinkOn() )
Wakeup();
else
Sleep();
}
void Actor::SetAggressiveness( Event *ev )
{
personality->SetAggressiveness( ev->GetFloat( 1 ) );
}
qboolean Actor::checkWantsToExecutePackage( Conditional &condition )
{
float interval;
if ( condition.numParms() > 0 )
interval = atof( condition.getParm( 1 ) );
else
interval = 0.0f;
return personality->WantsToExecuteCurrentPackage( interval );
}
qboolean Actor::checkExecutedPackageInLastTimeFrame( Conditional &condition )
{
float interval;
if ( condition.numParms() > 0 )
interval = atof( condition.getParm( 1 ) );
else
interval = 0.0f;
return personality->ExecutedPackageInLastTimeFrame( interval );
}
qboolean Actor::checkIsAggressive( Conditional &condition )
{
float baseLine;
baseLine = atof(condition.getParm( 1 ));
return ( baseLine <= personality->GetAggressiveness() );
}
qboolean Actor::checkInConeOfFire( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_IN_CONE_OF_FIRE );
}
qboolean Actor::checkInPlayerConeOfFire( Conditional &condition )
{
return GetActorFlag( ACTOR_FLAG_IN_PLAYER_CONE_OF_FIRE );
}
void Actor::SetGroupNumber( Event *ev )
{
//This is here for legacy. In the future we need to
//move all group set up to the group coordinator alone
//however, currently, we have a large number of scripts
//that are assigning actors groups in this manner
AddToGroup( ev->GetInteger( 1 ) );
}
void Actor::_notifyGroupOfDamage()
{
_notifyGroupOfEnemy();
}
void Actor::_notifyGroupOfKilled()
{
_notifyGroupOfEnemy();
}
void Actor::_notifyGroupSpottedEnemy()
{
_notifyGroupOfEnemy();
}
void Actor::_notifyGroupOfEnemy()
{
Actor *act;
Entity *currentEnemy;
int i;
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return;
for( i = 1; i <= ActiveList.NumObjects(); i++ )
{
act = ActiveList.ObjectAt( i );
if ( act->GetGroupID() == GetGroupID() )
{
act->sensoryPerception->Stimuli(STIMULI_ALL);
act->enemyManager->TryToAddToHateList( currentEnemy );
act->enemyManager->SetCurrentEnemy( currentEnemy );
act->personality->SetAggressiveness( 1.0f);
}
}
for ( i = 1; i <= SleepList.NumObjects(); i++ )
{
act = SleepList.ObjectAt( i );
if ( act->GetGroupID() == GetGroupID() )
{
act->sensoryPerception->Stimuli(STIMULI_ALL);
act->enemyManager->TryToAddToHateList( currentEnemy );
act->enemyManager->SetCurrentEnemy( currentEnemy );
act->personality->SetAggressiveness( 1.0f);
}
}
}
qboolean Actor::checkPatrolWaypointNodeInDistance( Conditional &condition )
{
float distance = atof(condition.getParm( 1 ) );
Entity* ent_in_range;
Vector NodeToSelf;
gentity_t *ed;
Vector pos;
Vector nodeOrigin;
/*
int wtf;
wtf = lastPathCheck_Flee + HACK_PATH_CHECK;
if ( wtf >= level.time )
return false;
lastPathCheck_Patrol = level.time + HACK_PATH_CHECK + G_Random();
*/
for ( int i = 0; i < MAX_GENTITIES; i++ )
{
ed = &g_entities[i];
if ( !ed->inuse || !ed->entity )
{
continue;
}
ent_in_range = g_entities[i].entity;
pos = origin;
pos.z += 80;
nodeOrigin = ent_in_range->origin;
/*
if (!sensoryPerception->CanSeePosition( pos , nodeOrigin , true , true ) )
continue;
*/
if( ent_in_range->isSubclassOf( PatrolWayPointNode ) )
{
NodeToSelf = ent_in_range->origin - origin;
if ( NodeToSelf.length() <= distance )
return true;
}
}
return false;
}
qboolean Actor::checkPathNodeTypeInDistance( Conditional &condition )
{
str nodeType = condition.getParm( 1 );
float distance = atof(condition.getParm( 2 ));
if ( !Q_stricmp( "work" , nodeType.c_str() ) )
return _WorkNodeInDistance( distance );
if ( !Q_stricmp( "flee" , nodeType.c_str() ) )
return _FleeNodeInDistance( distance );
return false;
}
void Actor::SetHeadWatchTarget( Event *ev )
{
str watchTarget;
float speed;
watchTarget = ev->GetString( 1 );
if ( ev->NumArgs() > 1 )
{
speed = ev->GetFloat( 2 );
headWatcher->SetWatchSpeed( speed );
}
if ( !Q_stricmp( "enemy" , watchTarget.c_str() ) )
{
headWatcher->SetWatchTarget( enemyManager->GetCurrentEnemy() );
return;
}
if ( !Q_stricmp( "none" , watchTarget.c_str() ) )
{
headWatcher->SetWatchTarget( NULL );
return;
}
if ( !Q_stricmp( "player" , watchTarget.c_str() ) )
{
Player *player = GetPlayer(0);
headWatcher->SetWatchTarget( player );
}
if ( !Q_stricmp( "teammate" , watchTarget.c_str() ) )
{
float bestDist = 99999;
Vector selfToTeammate;
Sentient *teammate = GetPlayer(0);
Sentient *closestTeammate = NULL;
for ( int i = 1 ; i <= TeamMateList.NumObjects() ; i++ )
{
teammate = TeamMateList.ObjectAt( i );
if ( teammate->entnum == entnum)
continue;
selfToTeammate = teammate->origin - origin;
if ( selfToTeammate.length() <= bestDist )
{
closestTeammate = teammate;
bestDist = selfToTeammate.length();
}
}
if ( !closestTeammate )
closestTeammate = teammate;
headWatcher->SetWatchTarget( closestTeammate );
return;
}
headWatcher->SetWatchTarget( watchTarget );
}
void Actor::SetHeadWatchTarget( Entity *ent )
{
if ( !ent )
ent = NULL;
headWatcher->SetWatchTarget( ent );
}
void Actor::SetHeadWatchSpeed( Event *ev )
{
float speed = ev->GetFloat( 1 );
headWatcher->SetWatchSpeed( speed );
}
void Actor::SetHeadWatchSpeed( float speed )
{
headWatcher->SetWatchSpeed( speed );
}
void Actor::setHeadTwitch( Event *ev )
{
headWatcher->setHeadTwitch( ev->GetBoolean( 1 ) );
}
void Actor::SetFuzzyEngineActive( Event *ev )
{
qboolean active = ev->GetBoolean( 1 );
fuzzyEngine_active = active;
}
qboolean Actor::_isWorkNodeValid( PathNode *node )
{
WorkTrigger *target = NULL;
if ( node->targetEntity )
{
Entity *entity = node->targetEntity;
if( entity->isSubclassOf( WorkTrigger ) )
{
target = (WorkTrigger*)entity;
//If it's not reserved, but still marked as occupied -- Don't go
if ( node->occupiedTime > level.time && !target->isReserved() )
return false;
if ( target->isAllowedToWork( targetname , entnum ) )
return true;
}
}
/* if ( node->target )
{
str targetName;
targetName = node->target;
if ( targetName.length() > 0 )
{
Entity* ent_in_range;
gentity_t *ed;
for ( int i = 0; i < MAX_GENTITIES; i++ )
{
ed = &g_entities[i];
if ( !ed->inuse || !ed->entity )
{
continue;
}
ent_in_range = g_entities[i].entity;
if( ent_in_range->isSubclassOf( WorkTrigger ) )
{
if (!Q_stricmp(ent_in_range->targetname.c_str() , targetName.c_str() ))
{
target = (WorkTrigger*)ent_in_range;
//If it's not reserved, but still marked as occupied -- Don't go
if ( node->occupiedTime > level.time && !target->isReserved() )
return false;
if ( target->isAllowedToWork( targetname , entnum ) )
return true;
}
}
}
}
} */
if ( !target )
return true;
return false;
}
qboolean Actor::_FleeNodeInDistance( float dist )
{
int wtf;
wtf = (int)(lastPathCheck_Flee + HACK_PATH_CHECK);
if ( wtf >= level.time )
return false;
lastPathCheck_Flee = level.time + HACK_PATH_CHECK + G_Random();
Vector delta;
Vector pos;
Vector nodeOrigin;
for ( int i = 1 ; i <= thePathManager.NumberOfSpecialNodes(); i++ )
{
PathNode *node = thePathManager.GetSpecialNode( i );
pos = origin;
pos.z += 80;
nodeOrigin = node->origin;
if (!sensoryPerception->CanSeePosition( pos , nodeOrigin , true , true ) )
continue;
if ( node && ( node->nodeflags & ( AI_FLEE ) ) &&
( ( node->occupiedTime <= level.time ) ) && ( node->entnum == 0 || node->entnum == entnum ) )
{
delta = node->origin - origin;
if ( delta.length() <= dist )
return true;
}
}
return false;
}
qboolean Actor::_WorkNodeInDistance( float dist )
{
int wtf;
wtf = (int)(lastPathCheck_Work + HACK_PATH_CHECK);
if ( wtf >= level.time )
return false;
lastPathCheck_Work = level.time + G_Random();
Vector delta;
Vector pos;
Vector nodeOrigin;
for ( int i = 1 ; i <= thePathManager.NumberOfSpecialNodes(); i++ )
{
PathNode *node = thePathManager.GetSpecialNode( i );
pos = origin;
pos.z += 80;
nodeOrigin = node->origin;
if (!sensoryPerception->CanSeePosition( pos , nodeOrigin , true , true ) )
continue;
if ( node && ( node->nodeflags & ( AI_WORK ) ) &&
( ( node->occupiedTime <= level.time ) ) && ( node->entnum == 0 || node->entnum == entnum ) )
{
if ( !_isWorkNodeValid( node ) )
continue;
delta = node->origin - origin;
if ( delta.length() <= dist )
return true;
}
}
return false;
}
void Actor::ClearArmorAdaptions( Event *ev )
{
AdaptiveArmor::ClearAdaptionList();
}
void Actor::SetMovementMode( Event *ev )
{
str modeType;
modeType = ev->GetString( 1 );
if ( !Q_stricmp( "normal" , modeType.c_str() ) )
movementSubsystem->setMovementType( MOVEMENT_TYPE_NORMAL );
else if ( !Q_stricmp( "anim" , modeType.c_str() ) )
movementSubsystem->setMovementType( MOVEMENT_TYPE_ANIM );
}
void Actor::SetCinematicAnim( const str &animName )
{
Entity::SetCinematicAnim( animName); // Ensure entity level cinematic stuff is enabled
movementSubsystem->setMovementType( MOVEMENT_TYPE_ANIM );
}
void Actor::CinematicAnimDone( void )
{
Entity::CinematicAnimDone(); // Ensures entity level cinematic stuff is disabled
movementSubsystem->setMovementType( MOVEMENT_TYPE_NORMAL );
}
qboolean Actor::checkForwardDirectionClear( Conditional &condition )
{
return checkForwardDirectionClear(atof(condition.getParm( 1 ) ));
}
qboolean Actor::checkForwardDirectionClear(float dist)
{
trace_t trace;
Vector endPos;
Vector startPos;
Vector forward;
Vector angles;
startPos = origin;
startPos.z += 32;
angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
angles.AngleVectors( &forward );
endPos = ( forward * dist) + startPos;
trace = G_Trace(startPos, mins, maxs, endPos, NULL, edict->clipmask, false, "checkForwardDirectionClear" );
if (trace.fraction == 1.0 )
{
return ( movementSubsystem->CanWalkTo( trace.endpos, 0.0f, entnum ) );
}
return false;
}
qboolean Actor::checkRearDirectionClear( Conditional &condition )
{
return checkRearDirectionClear(atof(condition.getParm( 1 ) ));
}
qboolean Actor::checkRearDirectionClear(float dist)
{
trace_t trace;
Vector endPos;
Vector startPos;
Vector forward;
Vector angles;
startPos = origin;
startPos.z += 32;
angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
angles[YAW] = AngleNormalize180(angles[YAW] + 180);
angles.AngleVectors( &forward );
endPos = ( forward * dist) + startPos;
trace = G_Trace(startPos, mins, maxs, endPos, NULL, edict->clipmask, false, "checkForwardDirectionClear" );
if (trace.fraction == 1.0 )
{
return ( movementSubsystem->CanWalkTo( trace.endpos, 0.0f, entnum ) );
}
return false;
}
qboolean Actor::checkLeftDirectionClear( Conditional &condition )
{
return checkLeftDirectionClear(atof(condition.getParm( 1 ) ));
}
qboolean Actor::checkLeftDirectionClear(float dist)
{
trace_t trace;
Vector endPos;
Vector startPos;
Vector left;
Vector angles;
startPos = origin;
//startPos.z += 32;
angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
angles.AngleVectors( NULL, &left, NULL );
endPos = ( left * dist) + startPos;
//trace = G_Trace(startPos, mins, maxs, endPos, NULL, edict->clipmask, false, "checkForwardDirectionClear" );
trace = Trace( endPos , "CheckMyLeft" );
//G_DebugLine( startPos, endPos, 1.0f, 1.0f, 1.0f, 1.0f );
if (trace.fraction == 1.0 )
{
return ( movementSubsystem->CanWalkTo( trace.endpos, 0.0f, entnum ) );
}
return false;
}
qboolean Actor::checkRightDirectionClear( Conditional &condition )
{
return checkRightDirectionClear(atof(condition.getParm( 1 ) ));
}
qboolean Actor::checkRightDirectionClear(float dist)
{
trace_t trace;
Vector endPos;
Vector startPos;
Vector left;
Vector angles;
startPos = origin;
//startPos.z += 16;
angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
angles[YAW] = AngleNormalize180(angles[YAW] + 180);
angles.AngleVectors( NULL, &left, NULL );
endPos = ( left * dist) + startPos;
//trace = G_Trace(startPos, mins, maxs, endPos, NULL, edict->clipmask, false, "checkForwardDirectionClear" );
trace = Trace( endPos , "CheckMyRight" );
//G_DebugLine( startPos, endPos, 1.0f, 1.0f, 1.0f, 1.0f );
if (trace.fraction == 1.0 )
{
return ( movementSubsystem->CanWalkTo( trace.endpos, 0.0f, entnum ) );
}
return false;
}
qboolean Actor::checkbehaviorsuccess( Conditional &condition )
{
if ( behaviorCode == BEHAVIOR_SUCCESS )
return true;
return false;
}
qboolean Actor::checkbehaviorfailed( Conditional &condition )
{
if ( behaviorCode == BEHAVIOR_FAILED ||
behaviorCode == BEHAVIOR_FAILED_STEERING_BLOCKED_BY_ENEMY ||
behaviorCode == BEHAVIOR_FAILED_STEERING_BLOCKED_BY_CIVILIAN ||
behaviorCode == BEHAVIOR_FAILED_STEERING_BLOCKED_BY_FRIEND ||
behaviorCode == BEHAVIOR_FAILED_STEERING_BLOCKED_BY_TEAMMATE ||
behaviorCode == BEHAVIOR_FAILED_STEERING_BLOCKED_BY_WORLD ||
behaviorCode == BEHAVIOR_FAILED_STEERING_BLOCKED_BY_DOOR ||
behaviorCode == BEHAVIOR_FAILED_STEERING_CANNOT_GET_TO_PATH ||
behaviorCode == BEHAVIOR_FAILED_STEERING_NO_PATH
)
return true;
return false;
}
void Actor::SetNodeID( Event *ev )
{
currentHelperNode.nodeID = ev->GetInteger( 1 );
}
void Actor::SetFollowTarget( Event *ev )
{
Entity *ent;
ent = NULL;
ent = ev->GetEntity( 1 );
if ( ent )
followTarget.specifiedFollowTarget = ent;
}
void Actor::SetSteeringDirectionPreference( Event *ev )
{
str preference = ev->GetString( 1 );
if ( preference == "steer_left_always" )
{
_steeringDirectionPreference = STEER_LEFT_ALWAYS;
}
else if ( preference == "steer_randomly" )
{
_steeringDirectionPreference = STEER_RANDOMLY;
}
else if ( preference == "steer_best" )
{
_steeringDirectionPreference = STEER_RANDOMLY;
}
else
{
_steeringDirectionPreference = STEER_RIGHT_ALWAYS;
}
}
void Actor::SetFollowRange( Event *ev )
{
followTarget.maxRangeIdle = ev->GetFloat( 1 );
}
void Actor::SetFollowRangeMin( Event *ev )
{
followTarget.minRangeIdle = ev->GetFloat( 1 );
}
void Actor::SetFollowCombatRange( Event *ev )
{
followTarget.maxRangeCombat = ev->GetFloat( 1 );
}
void Actor::SetFollowCombatRangeMin( Event *ev )
{
followTarget.minRangeCombat = ev->GetFloat( 1 );
}
qboolean Actor::checkLastState( Conditional &condition )
{
str lastStateName;
lastStateName = condition.getParm( 1 );
if ( !Q_stricmp( lastStateName.c_str() , lastState->getName() ) )
return true;
else
return false;
}
void Actor::SetTalkiness( Event *ev )
{
personality->SetTalkiness( ev->GetFloat( 1 ) );
}
void Actor::SetTendency( Event *ev )
{
personality->SetTendency( ev->GetString( 1 ) , ev->GetFloat( 2 ) );
}
void AI_DisplayInfo( void )
{
if ( ai_numactive->integer )
{
gi.Printf( "Active actors - %d\n", ActiveList.NumObjects() );
}
}
//
//=================================================================================================
// Context Dialog Functionality
//=================================================================================================
//
//
// Name: InContext()
// Class: Actor
//
// Description: Generates the proper event based on the context provided in the event
//
// Parameters: Event *ev -- Event containing the context
//
// Returns: None
//
void Actor::InContext( Event *ev )
{
str theContext = ev->GetString( 1 );
bool useDefaultMinDist = false;
if ( ev->NumArgs() > 1 )
useDefaultMinDist = ev->GetBoolean( 2 );
InContext( theContext , useDefaultMinDist );
}
void Actor::InContext( const str &theContext , bool useDefaultMinDist )
{
Event *ignoreEvent;
Event *broadcastEvent;
str realDialog;
float dialogLength;
if ( !WantsToTalk() ) return;
if ( GetActorFlag( ACTOR_FLAG_DIALOG_PLAYING ) ) return;
if ( _nextContextTime > level.time ) return;
if ( _ignoreNextContext )
{
if ( !stricmp( theContext.c_str() , _nextContextToIgnore.c_str() ) )
{
_ignoreNextContext = false;
_nextContextToIgnore = "";
return;
}
}
realDialog = FindDialog( this, DIALOG_TYPE_CONTEXT_INITIATOR, theContext );
if ( !realDialog.length() )
return;
ignoreEvent = new Event ( EV_ContextDialog_IgnoreNextContext );
ignoreEvent->AddInteger( 1 );
ignoreEvent->AddString( theContext );
groupcoordinator->SendEventToGroup( ignoreEvent , GetGroupID() );
char localizedDialogName[MAX_QPATH];
gi.LocalizeFilePath( realDialog, localizedDialogName );
_nextContextTime = level.time + G_Random() + _contextInterval;
dialogLength = gi.SoundLength( localizedDialogName );
broadcastEvent = new Event ( EV_Actor_BroadcastDialog );
broadcastEvent->AddString( theContext );
PostEvent( broadcastEvent, dialogLength );
if ( useDefaultMinDist )
PlayDialog( this, DEFAULT_VOL, DEFAULT_MIN_DIST, realDialog.c_str() , NULL );
else
PlayDialog( this, DEFAULT_VOL, CONTEXT_WIDE_MIN_DIST, realDialog.c_str() , NULL );
}
//
// Name: BroadcastDialog()
// Class: Actor
//
// Description: Broadcasts the dialog to nearby actors so that they can "hear" it
//
// Parameters: dialogContexts_t context -- The context
// ContextDialogType_t contextType -- the context type
//
// Returns: None
//
void Actor::BroadcastDialog(Event *ev)
{
Entity *ent;
Actor *act;
Actor *bestAct = NULL;
Vector delta;
float dist;
float bestDist;
str context = ev->GetString( 1 );
str responseDialog;
str bestResponseDialog;
bestDist = SOUND_RADIUS;
for( int i = 1; i <= SentientList.NumObjects(); i++ )
{
ent = SentientList.ObjectAt( i );
if ( ( ent == this ) || ent->deadflag )
{
continue;
}
if ( ent->isSubclassOf( Actor ) )
{
act = (Actor*)ent;
delta = origin - act->centroid;
dist = delta.length();
if ( dist <= SOUND_RADIUS && dist < bestDist )
{
if ( edict->areanum == ent->edict->areanum || gi.AreasConnected( edict->areanum, ent->edict->areanum ) )
{
responseDialog = act->FindDialog( act, DIALOG_TYPE_CONTEXT_RESPONSE, context );
if ( responseDialog.length() )
{
bestAct = act;
bestDist = dist;
bestResponseDialog = responseDialog;
}
}
}
}
}
if ( bestAct )
{
//
// Play the Response
//
bestAct->PlayDialog( bestAct, DEFAULT_VOL, -1.0f, bestResponseDialog.c_str() , NULL );
}
}
//
// Name: WantsToTalk()
// Class: Actor
//
// Description: Checks if the actor "wants" to talk -- based on his personality
//
// Parameters: None
//
// Returns: true or false
//
qboolean Actor::WantsToTalk()
{
return ( G_Random() <= personality->GetTalkiness() );
}
qboolean Actor::checkGroupMememberRange( Conditional &condition )
{
Actor *act;
float dist;
float reqDist;
Vector actToSelf;
reqDist = atof(condition.getParm( 1 ));
for( int i = 1; i <= ActiveList.NumObjects(); i++ )
{
act = ActiveList.ObjectAt( i );
if ( act && act != this && act->GetGroupID() == GetGroupID() )
{
actToSelf = origin - act->origin;
dist = actToSelf.length();
if ( dist <= reqDist )
{
return true;
}
}
}
return false;
}
qboolean Actor::checkActorType( Conditional &condition )
{
str aType = condition.getParm( 1 );
if ( !stricmp( aType.c_str() , "inanimate" ) )
{
if ( actortype == IS_INANIMATE )
return true;
else
return false;
}
else if ( !Q_stricmp( aType.c_str() , "monster" ) )
{
if ( actortype == IS_MONSTER )
return true;
else
return false;
}
else if ( !Q_stricmp( aType.c_str() , "enemy" ) )
{
if ( actortype == IS_ENEMY )
return true;
else
return false;
}
else if ( !Q_stricmp( aType.c_str() , "civilian" ) )
{
if ( actortype == IS_CIVILIAN )
return true;
else
return false;
}
else if ( !Q_stricmp( aType.c_str() , "friend" ) )
{
if ( actortype == IS_FRIEND )
return true;
else
return false;
}
else if ( !Q_stricmp( aType.c_str() , "animal" ) )
{
if ( actortype == IS_ANIMAL )
return true;
else
return false;
}
else if ( !Q_stricmp( aType.c_str() , "teammate" ) )
{
if ( actortype == IS_TEAMMATE )
return true;
else
return false;
}
return false;
}
qboolean Actor::checkIsTeammate( Conditional &condition )
{
if ( actortype == IS_TEAMMATE )
return true;
else
return false;
}
qboolean Actor::checkHaveActiveWeapon( Conditional &condition )
{
return combatSubsystem->HaveWeapon();
}
qboolean Actor::checkWeaponIsMelee( Conditional &condition )
{
return combatSubsystem->WeaponIsFireType( FT_MELEE );
}
qboolean Actor::checkWeaponChanged( Conditional &condition )
{
return ( state_flags & STATE_FLAG_CHANGED_WEAPON );
}
//----------------------------------------------------------------
// Name: FindActorByName
// Class: Actor
//
// Description: Goes through the ActiveList and finds an actor with
// the matching name.
//
// Parameters: const str &name -- The name
//
// Returns: Actor pointer, or NULL if it's not found
//----------------------------------------------------------------
Actor* Actor::FindActorByName(const str &charName)
{
int i;
Actor *act;
for ( i=1; i<=ActiveList.NumObjects(); i++ )
{
act = ActiveList.ObjectAt(i);
if ( !act )
continue;
if ( act->targetname == charName )
return act;
}
return NULL;
}
//----------------------------------------------------------------
// Name: setDialogMorphMult
// Class: Actor
//
// Description: Sets the multiplier for all dialog morphs for this actor
//
// Parameters: Event *ev - contains, float dialogMorphMultiplier
//
// Returns: none
//----------------------------------------------------------------
void Actor::setDialogMorphMult( Event *ev )
{
_dialogMorphMult = ev->GetFloat( 1 );
}
//--------------------------------------------------------------
// Name: canBeDamageBy()
// Class: Actor
//
// Description: Checks if we can be damaged by the specified
// MeansOfDeath
//
// Parameters: meansOfDeath_t MeansOfDeath
//
// Returns:
//--------------------------------------------------------------
bool Actor::canBeDamagedBy(meansOfDeath_t MeansOfDeath)
{
float resistance;
resistance = GetResistanceModifier( MeansOfDeath );
if ( resistance >= 100.0 )
return false;
if ( currentBaseArmor )
{
return currentBaseArmor->CanBeDamagedBy( MeansOfDeath );
}
return true;
}
//--------------------------------------------------------------
// Name: ForceSetClip
// Class: Actor
//
// Description: Forces the actor's mask and contents to be
// "Set" type. This will need to change to be
// a group type mask when more group stuff is
// implemented
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::ForceSetClip( Event *ev )
{
edict->contents = CONTENTS_SETCLIP ;
edict->clipmask = MASK_SETCLIP ;
}
//--------------------------------------------------------------
// Name: checkCountOfIdenticalNamesInGroup()
// Class: Actor
//
// Description: Converts the checkvalue imbeded in the conditional
// into an integer then passes it on to another
// checkCountOfIdenticalNamesInGroup()
//
// Parameters: Conditional &condition
//
// Returns: true or false;
//--------------------------------------------------------------
qboolean Actor::checkCountOfIdenticalNamesInGroup( Conditional &condition )
{
int checkValue;
str checkName;
checkName = condition.getParm( 1 );
checkValue = atoi(condition.getParm( 2 ));
return checkCountOfIdenticalNamesInGroup( checkName , checkValue );
}
//--------------------------------------------------------------
// Name: checkCountOfIdentcialNamesInGroup()
// Class: Actor
//
// Description: Checks if the number of group members with the same
// name as this actor is LESS than the number
// passed into the conditional
//
// Parameters: Conditional &condition
//
// Returns: true or false;
//--------------------------------------------------------------
qboolean Actor::checkCountOfIdenticalNamesInGroup( const str &checkName , int checkValue )
{
ActorGroup* group;
int count;
group = (ActorGroup*)groupcoordinator->GetGroup( GetGroupID() );
if ( !group )
return true;
count = group->CountMembersWithThisName( checkName );
if ( count < checkValue )
return true;
return false;
}
qboolean Actor::checkCanAttackEnemy(Conditional &condition)
{
return checkCanAttackEnemy();
}
qboolean Actor::checkCanAttackEnemy()
{
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
return combatSubsystem->CanAttackTarget( currentEnemy );
}
//--------------------------------------------------------------
// Name: checkGroupAttackerCount
// Class: Actor
//
// Description: Returns true if the number attacking is less than the number
// passed in. False otherwise.
//
// Parameters: Conditional &condition
//
// Returns: true or false;
//--------------------------------------------------------------
qboolean Actor::checkGroupAttackerCount( Conditional &condition )
{
return checkGroupAttackerCountForEntity( condition, NULL );
}
//--------------------------------------------------------------
// Name: SetBehaviorPackage()
// Class: Actor
//
// Description: Calls SetBehaviorPackage()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetBehaviorPackage( Event *ev )
{
SetBehaviorPackage(ev->GetString( 1 ) );
}
//--------------------------------------------------------------
// Name: SetBehaviorPackage()
// Class: Actor
//
// Description: Attempts to set the requested behavior package
//
// Parameters: const str &packageName
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetBehaviorPackage( const str &packageName )
{
if ( !masterstatemap )
{
assert ( masterstatemap );
gi.WDPrintf( "You cannot set a behavior package on actor %s because it does not have a masterstatemap, please report this to the AI Programmer\n" , targetname.c_str() );
return;
}
if ( !stricmp( packageName.c_str() , "auto" ) )
{
SetMasterState( "START" );
}
else
{
SetMasterState( "SCRIPTED" );
strategos->SetBehaviorPackage( packageName );
}
}
//--------------------------------------------------------------
// Name: UseBehaviorPackage()
// Class: Actor
//
// Description: Calls UseBehaviorPackage()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::UseBehaviorPackage( Event *ev )
{
UseBehaviorPackage(ev->GetString( 1 ) );
}
//--------------------------------------------------------------
// Name: UseBehaviorPackage()
// Class: Actor
//
// Description: Tells the Strategos to set the requested
// behavior packaged
//
// Parameters: const str &packageName
//
// Returns: None
//--------------------------------------------------------------
void Actor::UseBehaviorPackage( const str &packageName )
{
strategos->SetBehaviorPackage( packageName );
}
//--------------------------------------------------------------
// Name: ChildUseBehaviorPackage()
// Class: Actor
//
// Description: Calls UseBehaviorPackage()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::ChildUseBehaviorPackage( Event *ev )
{
ChildUseBehaviorPackage(ev->GetString( 1 ) , ev->GetString( 2 ) );
}
//--------------------------------------------------------------
//
// Name: GetAttachedChildActor
// Class: Actor
//
// Description: Gets an attached child actor by name
//
// Parameters: const str& childName -- Child actor to find
//
// Returns: Actor* -- The child or NULL if not found.
//
//--------------------------------------------------------------
Actor* Actor::GetAttachedChildActor( const str& childName )
{
int num;
Entity *child = 0;
Actor *childActor = 0;
if ( !bind_info )
return NULL;
num = bind_info->numchildren;
child = NULL;
childActor = NULL;
for ( int i=0; i < MAX_MODEL_CHILDREN; i++ )
{
if ( bind_info->children[i] == ENTITYNUM_NONE )
continue;
child = ( Entity * )G_GetEntity( bind_info->children[i] );
if ( !stricmp(child->TargetName() , childName.c_str() ) )
{
if ( child->isSubclassOf(Actor) )
childActor = (Actor*)child;
if ( childActor )
return childActor;
}
}
return NULL;
}
//--------------------------------------------------------------
// Name: ChildUseBehaviorPackage()
// Class: Actor
//
// Description: Tells the Strategos to set the requested
// behavior packaged
//
// Parameters: const str &packageName
//
// Returns: None
//--------------------------------------------------------------
void Actor::ChildUseBehaviorPackage( const str &childName , const str &packageName )
{
Actor *childActor = 0;
childActor = GetAttachedChildActor(childName);
if ( childActor )
childActor->SetBehaviorPackage( packageName );
}
//--------------------------------------------------------------
// Name: ChildSetAnim()
// Class: Actor
//
// Description: Calls ChildSetAnim()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::ChildSetAnim( Event *ev )
{
ChildSetAnim(ev->GetString( 1 ) , ev->GetString( 2 ) );
}
//--------------------------------------------------------------
// Name: ChildSuicide()
// Class: Actor
//
// Description: Calls ChildSetAnim()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::ChildSuicide( Event *ev )
{
Actor *childActor = 0;
str childName = ev->GetString( 1 );
childActor = GetAttachedChildActor(childName);
if ( childActor )
childActor->ProcessEvent(EV_Actor_Suicide);
}
//--------------------------------------------------------------
// Name: ChildSetAnim()
// Class: Actor
//
// Description: Tells the Strategos to set the requested
// anim
//
// Parameters: const str &childName -- Child to find
// const str &animName -- Name of anim to set
//
// Returns: None
//--------------------------------------------------------------
void Actor::ChildSetAnim( const str &childName , const str &animName )
{
Actor *childActor = 0;
childActor = GetAttachedChildActor(childName);
if ( childActor )
childActor->SetAnim(animName);
}
//--------------------------------------------------------------
// Name: WhatsWrong()
// Class: Actor
//
// Description: Reports the failure condition for the current behavior
// to the console
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::WhatsWrong( Event *ev )
{
gi.Printf( "\n------------------------------------------" );
if ( !behaviorFailureReason.length() )
gi.Printf( "\nNo Failure Reason given for behavior %s\n", currentBehavior.c_str() );
else
gi.Printf( "\nReason: %s" , behaviorFailureReason.c_str() );
gi.Printf( "\n------------------------------------------" );
}
//--------------------------------------------------------------
// Name: WhatAreYouDoing()
// Class: Actor
//
// Description: Debug Function that can be called from the console
// to give us state information on this actor
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::WhatAreYouDoing( Event *ev )
{
str tName;
tName = targetname;
if ( !tName.length() )
tName = "None";
gi.Printf( "\n-----------------------------------------------" );
gi.Printf( "\nTargetName : %s\n" , tName.c_str() );
gi.Printf( "\n-----------------------------------------------" );
if ( masterstatemap )
{
PrintMasterStateInfo();
PrintBehaviorPackageInfo();
}
else
{
gi.Printf( "\nNo Master State" );
gi.Printf( "\n" );
if ( statemap )
PrintStateMapInfo();
else
gi.Printf( "\nNo State Map\n" );
}
gi.Printf( "\n-----------------------------------------------" );
}
//--------------------------------------------------------------
// Name: PrintMasterStateInfo()
// Class: Actor
//
// Description: Prints information about the MasterState in the console
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::PrintMasterStateInfo()
{
gi.Printf( "\nMasterState Information:" );
gi.Printf( "\nMasterState File: %s" , masterstatemap->Filename() );
gi.Printf( "\nMasterState Current State: %s" , currentMasterState->getName() );
gi.Printf( "\nMasterState Last State: %s" , lastMasterState->getName() );
gi.Printf( " \n" );
gi.Printf( " \n" );
}
//--------------------------------------------------------------
// Name: PrintBehaviorPackageInfo()
// Class: Actor
//
// Description: Prints information about the BehaviorPackage in the console
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::PrintBehaviorPackageInfo()
{
gi.Printf( "\nBehaviorPackage Information:" );
gi.Printf( "\nCurrent Behavior Package Name: %s" , packageManager->GetCurrentPackageName().c_str() );
if ( currentState )
gi.Printf( "\nCurrent Behavior Package State: %s" , currentState->getName() );
else
gi.Printf( "\nCurrent Behavior Package State: !!!!!NULL!!!!!");
gi.Printf( "\nLast Behavior Package State: %s" , lastState->getName() );
gi.Printf( " \n" );
gi.Printf( " \n" );
}
//--------------------------------------------------------------
// Name: PrintStateMapInfo()
// Class: Actor
//
// Description: Prints information about the state map in the console
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::PrintStateMapInfo()
{
gi.Printf( "\nState Map Information: " );
gi.Printf( "\nState Map File: %s" , statemap->Filename() );
gi.Printf( "\nCurrent State: %s" , currentState->getName() );
gi.Printf( "\nLast State: %s" , lastState->getName() );
gi.Printf( " \n" );
gi.Printf( " \n" );
}
//--------------------------------------------------------------
// Name: SetCombatTraceInterval()
// Class: Actor
//
// Description: Sets how often the actor will re-trace when doing
// a can_attack type of check
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetCombatTraceInterval( Event *ev )
{
combatSubsystem->SetTraceInterval( ev->GetFloat( 1 ) );
}
//--------------------------------------------------------------
// Name: ActorTypeStringToInt()
// Class: Actor
//
// Description: Returns an Actor Type ID based on the string passed in
//
// Parameters: const str &type
//
// Returns: unsigned int
//--------------------------------------------------------------
unsigned int Actor::ActorTypeStringToInt( const str &type )
{
unsigned int retValue = 99999;
if ( type == "inanimate" )
retValue = IS_INANIMATE;
else if ( type == "monster" )
retValue = IS_MONSTER;
else if ( type == "enemy" )
retValue = IS_ENEMY;
else if ( type == "civilian" )
retValue = IS_CIVILIAN;
else if ( type == "friend" )
retValue = IS_FRIEND;
else if ( type == "animal" )
retValue = IS_ANIMAL;
else if ( type == "teammate" )
retValue = IS_TEAMMATE;
assert ( retValue != 99999 );
return retValue;
}
//--------------------------------------------------------------
// Name: GroupMemberInjured()
// Class: Actor
//
// Description: Appropriately sets the ACTOR_FLAG_GROUPMEMBER_INJURED flag
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::GroupMemberInjured( Event *ev )
{
bool injured = ev->GetBoolean( 1 );
if ( injured )
SetActorFlag( ACTOR_FLAG_GROUPMEMBER_INJURED , true );
else
SetActorFlag( ACTOR_FLAG_GROUPMEMBER_INJURED , false );
}
//--------------------------------------------------------------
// Name: StrictlyFollowPath()
// Class: Actor
//
// Description: Appropriately sets the ACTOR_FLAG_STRICTLY_FOLLOW_PATHS flag
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::StrictlyFollowPath( Event *ev )
{
SetActorFlag( ACTOR_FLAG_STRICTLY_FOLLOW_PATHS , ev->GetBoolean( 1 ) );
}
//--------------------------------------------------------------
//
// Name: IsFinishable
// Class: Actor
//
// Description: Checks to see if this is a 'finishable' actor
//
// Parameters: None
//
// Returns: True if so, false otherwise.
//
//--------------------------------------------------------------
bool Actor::IsFinishable()
{
// More conditions? Non-hardcoded min health?
if ( health > 0 && health < 30 )
return true;
return false;
}
//--------------------------------------------------------------
//
// Name: UseWeaponDamage
// Class: Actor
//
// Description: Causes the MeleeEvent to use the damage from the
// weapon in the specified hand
//
// Parameters: Event *ev
//
// Returns: None
//
//--------------------------------------------------------------
void Actor::UseWeaponDamage( Event *ev )
{
if ( ev->NumArgs() > 0 )
_useWeaponDamage = WeaponHandNameToNum(ev->GetString( 1 ));
else
_useWeaponDamage = WEAPON_RIGHT;
if ( ev->NumArgs() > 1 )
{
if ( !ev->GetBoolean( 2 ) )
_useWeaponDamage = WEAPON_ERROR;
}
}
//--------------------------------------------------------------
// Name: HelperNodeCommand()
// Class: Actor
//
// Description: Takes the event from the helper node
// and passes it on to the behavior for
// it to deal with
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::HelperNodeCommand( Event *ev )
{
}
//--------------------------------------------------------------
// Name: SetIgnoreNextContext()
// Class: Actor
//
// Description: Sets the _ignoreNextContext flag
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetIgnoreNextContext( Event *ev )
{
_ignoreNextContext = ev->GetBoolean( 1 );
_nextContextToIgnore = ev->GetString( 2 );
}
void Actor::EvaluateEnemies( Event *ev )
{
if ( !enemyManager->IsLockedOnCurrentEnemy() )
enemyManager->FindHighestHateEnemy();
}
void Actor::ForgetEnemies( Event* ev )
{
if( enemyManager )
{
enemyManager->ClearCurrentEnemy();
enemyManager->ClearHateList();
}
}
//==============================================================
// Controller functions
//==============================================================
//===============================================================
// Name: RequestControl
// Class: Actor
//
// Description: Requests control of the actor from a listener. If
// the actor is not under control, the request is granted.
// If the actor is under control, but the previous
// request was not exclusive, then the previous controller
// loses control, is notified of this, and the new
// controller takes over.
//
// If the previous controller did take exclusive control,
// this request is denied.
//
// I considered adding ACTOR_CONTROL_SHARED, but decided
// I'll wait on that until its needed.
//
// Parameters: Listener* -- the controller
// ActorControlType -- one of:
// Actor::ACTOR_CONTROL_AUTO_RELEASE
// Actor::ACTOR_CONTROL_LOCKED
//
// Returns: bool -- true if the request was granted.
//
//===============================================================
bool Actor::RequestControl
(
Listener *controller,
ActorControlType controlType
)
{
if ( !_controller )
{
_controller = controller ;
_controlType = controlType ;
return true ;
}
if ( _controlType != ACTOR_CONTROL_LOCKED )
{
Event *ev = new Event( EV_Actor_ControlLost );
_controller->ProcessEvent( ev );
_controller = controller ;
return true ;
}
return false ;
}
//===============================================================
// Name: ReleaseControl
// Class: Actor
//
// Description: Releases control of an actor by a controller. This
// request is only denied if the requester is not the
// controller.
//
// Parameters: Listener* -- the controller releasing control.
//
// Returns: bool -- true unless requester is not controller,
// or actor isn't under control.
//
//===============================================================
bool Actor::ReleaseControl
(
Listener *controller
)
{
if ( !_controller ) return false ;
if ( _controller != controller ) return false ;
_controller = 0 ;
_controlType = ACTOR_CONTROL_NONE ;
return true ;
}
//--------------------------------------------------------------
// Name: SetMaxHeadYaw()
// Class: Actor
//
// Description: Sets the max head yaw for the headwatcher class
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetMaxHeadYaw( Event *ev )
{
headWatcher->SetMaxHeadYaw( ev->GetFloat( 1 ) );
}
//--------------------------------------------------------------
// Name: SetMaxHeadPitch()
// Class: Actor
//
// Description: Sets the max head pitch for the headwatcher class
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::SetMaxHeadPitch( Event *ev )
{
headWatcher->SetMaxHeadPitch( ev->GetFloat( 1 ) );
}
//--------------------------------------------------------------
// Name: LoadPostureStateMachine()
// Class: Actor
//
// Description: Creates a new Posture State Machine
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::LoadPostureStateMachine( Event *ev )
{
bool loading;
if ( ev->NumArgs() > 1 )
loading = ev->GetBoolean( 2 );
else
loading = false;
postureController->setPostureStateMap( ev->GetString( 1 ) , loading );
}
//--------------------------------------------------------------
// Name: PostureAnimDone()
// Class: Actor
//
// Description: Event Handler for the completion of a posture animation
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::PostureAnimDone( Event *ev )
{
SetActorFlag(ACTOR_FLAG_POSTURE_ANIM_DONE , true );
}
//--------------------------------------------------------------
// Name: checkRequestedPosture()
// Class: Actor
//
// Description: Queries the posture controller to compare the current
// posture state name with the requested posture state
// name
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkRequestedPosture( Conditional &condition )
{
str postureName = condition.getParm( 1 );
str requestedPostureName = postureController->getRequestedPostureName();
if ( postureName == requestedPostureName )
return true;
return false;
}
//--------------------------------------------------------------
// Name: checkPostureAnimDone()
// Class: Actor
//
// Description: Checks the Posture Anim Done Flag
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkPostureAnimDone( Conditional &condition )
{
return GetActorFlag(ACTOR_FLAG_POSTURE_ANIM_DONE );
}
//--------------------------------------------------------------
// Name: checkDamageThresholdExceeded()
// Class: Actor
//
// Description: Calls checkDamageThresholdExceeded()
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkDamageThresholdExceeded( Conditional &condition )
{
return checkDamageThresholdExceeded();
}
//--------------------------------------------------------------
// Name: checkDamageThresholdExceeded()
// Class: Actor
//
// Description: Checks if the amount of damage taken exceeds
// the amount of damage allowed in a specified
// amount of time
//
// Parameters: None
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkDamageThresholdExceeded()
{
return state_flags & STATE_FLAG_DAMAGE_THRESHOLD_EXCEEDED;
}
//--------------------------------------------------------------
// Name: checkhealthpercent()
// Class: Actor
//
// Description: Checks if the health is at or below the
// given percent ( in whole numbers )
// example: 50 is 50%
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkhealthpercent( Conditional &condition )
{
float percent = (.01 ) * atof( condition.getParm( 1 ) );
return ( health <= ( percent * max_health ) );
}
//--------------------------------------------------------------
// Name: checkHelperNodeWithFlagInRange()
// Class: Actor
//
// Description: Calls checkHelperNodeWithFlagInRange()
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkHelperNodeWithFlagInRange( Conditional &condition )
{
str flagName = condition.getParm( 1 );
float range = atof(condition.getParm( 2 ) );
return checkHelperNodeWithFlagInRange( flagName , range );
}
//--------------------------------------------------------------
// Name: checkHelperNodeWithFlagInRange()
// Class: Actor
//
// Description: Checks if a helper node with a specified flag
// is within a specified range of the actor
//
// Parameters: const str &flag
// float range
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkHelperNodeWithFlagInRange( const str &flag, float range )
{
int mask;
mask = HelperNode::GetHelperNodeMask( flag );
return HelperNode::isHelperNodeInRange( *this , mask , range );
}
//--------------------------------------------------------------
// Name: SendEventToGroup()
// Class: Actor
//
// Description: Sends an event to the Actor's group
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::SendEventToGroup( Event *ev )
{
int parmCount = ev->NumArgs();
str eventToSend = ev->GetString( 1 );
Event *event = new Event( eventToSend.c_str() );
for ( int i = 2 ; i <= parmCount ; i++ )
{
event->AddToken( ev->GetToken( i ) );
}
groupcoordinator->SendEventToGroup( event , GetGroupID() );
}
//--------------------------------------------------------------
// Name: checkEnemyWeaponNamed()
// Class: Actor
//
// Description: Calls checkEnemyWeaponNamed
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkEnemyWeaponNamed( Conditional &condition )
{
str weaponName = condition.getParm( 1 );
return checkEnemyWeaponNamed( weaponName );
}
//--------------------------------------------------------------
// Name: checkEnemyWeaponNamed()
// Class: Actor
//
// Description: Checks if the Actor's current enemy is using
// a weapon with the specified name
//
// Parameters: const str &anme
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkEnemyWeaponNamed( const str& name )
{
Entity *enemy;
enemyManager->FindHighestHateEnemy();
enemy = enemyManager->GetCurrentEnemy();
if ( enemy )
{
if ( enemy->isSubclassOf( Actor ) )
{
Actor *act;
act = (Actor*)enemy;
return act->combatSubsystem->UsingWeaponNamed( name );
}
if ( enemy->isSubclassOf( Player ) )
{
Player *player;
Weapon *pWeapon;
player = (Player*)enemy;
pWeapon = player->GetActiveWeapon(WEAPON_DUAL);
if ( pWeapon && pWeapon->getName() == name )
return true;
pWeapon = player->GetActiveWeapon(WEAPON_LEFT);
if ( pWeapon && pWeapon->getName() == name )
return true;
pWeapon = player->GetActiveWeapon(WEAPON_RIGHT);
if ( pWeapon && pWeapon->getName() == name )
return true;
}
}
return false;
}
//--------------------------------------------------------------
// Name: checkPlayerWeaponNamed()
// Class: Actor
//
// Description: Calls checkEnemyWeaponNamed
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkPlayerWeaponNamed( Conditional &condition )
{
str weaponName = condition.getParm( 1 );
return checkPlayerWeaponNamed( weaponName );
}
//--------------------------------------------------------------
// Name: checkPlayerWeaponNamed()
// Class: Actor
//
// Description: Checks if the Actor's current enemy is using
// a weapon with the specified name
//
// Parameters: const str &anme
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkPlayerWeaponNamed( const str& name )
{
Player *player;
player = GetPlayer ( 0 );
if ( player )
{
Weapon *pWeapon;
pWeapon = player->GetActiveWeapon(WEAPON_DUAL);
if ( pWeapon && pWeapon->getName() == name )
return true;
pWeapon = player->GetActiveWeapon(WEAPON_LEFT);
if ( pWeapon && pWeapon->getName() == name )
return true;
pWeapon = player->GetActiveWeapon(WEAPON_RIGHT);
if ( pWeapon && pWeapon->getName() == name )
return true;
}
return false;
}
//--------------------------------------------------------------
// Name: GroupAttack()
// Class: Actor
//
// Description: Sends an attack event to the Actor's group
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::GroupAttack( Event *ev )
{
Event *attackEvent;
Entity *enemy;
bool force = false;
enemy = enemyManager->GetCurrentEnemy();
if ( !enemy )
return;
if ( ev->NumArgs() > 0 )
force = ev->GetBoolean( 1 );
attackEvent = new Event(EV_Actor_Attack);
attackEvent->AddEntity( enemy );
attackEvent->AddInteger( force );
groupcoordinator->SendEventToGroup( attackEvent, GetGroupID() );
}
//--------------------------------------------------------------
// Name: GroupAttack()
// Class: Actor
//
// Description: Sends an attack event to the Actor's group
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
void Actor::GroupActorType( Event *ev )
{
Event *typeEvent;
str type;
typeEvent = new Event(EV_Actor_SetActorType);
typeEvent->AddString( ev->GetString( 1 ) );
groupcoordinator->SendEventToGroup( typeEvent, GetGroupID() );
}
//--------------------------------------------------------------
// Name: checkEnemyWithinRange()
// Class: Actor
//
// Description: Calls checkEnemyWithinRange()
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkEnemyWithinRange( Conditional &condition )
{
return checkEnemyWithinRange( atof(condition.getParm( 1 ) ) , atof(condition.getParm(2) ) );
}
//--------------------------------------------------------------
// Name: checkEnemyWithinRange()
// Class: Actor
//
// Description: Checks if the linear distance to the actors enemy is
// greater than or equal to the min AND less than or equal
// to the max
//
// Parameters: float min
// float max
//
// Returns: true or false
//--------------------------------------------------------------
qboolean Actor::checkEnemyWithinRange( float min , float max )
{
float dist = enemyManager->GetDistanceFromEnemy();
return ( dist <= max && dist >= min );
}
qboolean Actor::checkhealthpercentinrange( Conditional &condition )
{
float minpercent = (.01 ) * atof( condition.getParm( 1 ) );
float maxpercent = (.01 ) * atof( condition.getParm( 2 ) );
return ( (health >= ( minpercent * max_health ) && (health <= ( maxpercent * max_health ) ) ) );
}
void Actor::PrintDebugMessage( Event *ev )
{
str msg = ev->GetString( 1 );
gi.WDPrintf( "\n--------------------------------------------------------------\n" );
gi.WDPrintf( msg + "\n" );
gi.WDPrintf( "--------------------------------------------------------------\n" );
}
qboolean Actor::checkAttacked( Conditional &condition )
{
return checkAttacked();
}
qboolean Actor::checkAttacked()
{
return state_flags & STATE_FLAG_ATTACKED;
}
qboolean Actor::checkAttackedByPlayer( Conditional &condition )
{
return checkAttackedByPlayer();
}
qboolean Actor::checkAttackedByPlayer()
{
return state_flags & STATE_FLAG_ATTACKED_BY_PLAYER;
}
qboolean Actor::checkShowPain( Conditional &condition )
{
return checkShowPain();
}
qboolean Actor::checkShowPain()
{
return state_flags & STATE_FLAG_SHOW_PAIN;
}
qboolean Actor::checkPropEnemyRange( Conditional &condition )
{
str propname;
str objname = condition.getParm( 1 );
if ( condition.numParms() > 1 )
propname = condition.getParm( 2 );
return checkPropEnemyRange( objname , propname );
}
qboolean Actor::checkPropEnemyRange( const str& objname , const str& propname )
{
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
str scopestr = getArchetype() + "." + objname;
if ( !gpm->hasObject(scopestr) )
return false;
float range;
if ( propname.length() )
range = gpm->getFloatValue(scopestr, propname);
else
range = gpm->getFloatValue(scopestr, "value");
// Get our current enemy
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
enemyManager->FindHighestHateEnemy();
if ( !currentEnemy )
return false;
return EntityInRange( currentEnemy, range, 0, 0 , false );
}
void Actor::processGameplayData( Event *ev )
{
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
if ( !gpm->hasObject(getArchetype()) )
return;
str objname = getArchetype();
// Grab our FOV From the GPD
if ( gpm->hasProperty(objname, "fov") )
sensoryPerception->SetFOV( gpm->getFloatValue( objname , "fov" ));
// Grab our PlayerHateModifier
// Grab our VisionDistance
if ( gpm->hasProperty(objname, "visiondistance" ) )
sensoryPerception->SetVisionDistance( gpm->getFloatValue( objname , "visiondistance" ) );
}
void Actor::SelectNextEnemy( Event* ev )
{
enemyManager->FindNextEnemy();
}
void Actor::SelectClosestEnemy( Event *ev )
{
enemyManager->FindClosestEnemy();
}
//--------------------------------------------------------------
// Name: checkCurrentEnemyGroupAttackerCount
// Class: Actor
//
// Description: Returns true if the number of actors in this actor's group that
// are attacking current enemy is less than the number passed in. False otherwise.
//
// Parameters: Conditional &condition
//
// Returns: true or false;
//
//--------------------------------------------------------------
qboolean Actor::checkCurrentEnemyGroupAttackerCount( Conditional &condition )
{
Entity* e = enemyManager->GetCurrentEnemy();
return checkGroupAttackerCountForEntity( condition, e );
}
//--------------------------------------------------------------
// Name: checkGroupAttackerCountForEntity
// Class: Actor
//
// Note: This is a helper function for checkEnemyAttackerCount and checkEnemyGroupCount
//
// Description: Returns true if the number of actors in this actor's group that
// are attacking passed enemy is less than the number passed in. False otherwise.
//
// Parameters: Conditional& condition
// Entity* attackTarget - pass NULL to signify any attack target is OK;
// i.e., count how many of my groupmates are attacking at all.
//
// Returns: true or false;
//
//--------------------------------------------------------------
qboolean Actor::checkGroupAttackerCountForEntity( Conditional& condition, Entity* attackTarget )
{
int checkValue = atoi(condition.getParm( 1 ));
return checkGroupAttackerCountForEntity( checkValue , attackTarget );
}
qboolean Actor::checkGroupAttackerCountForEntity( int checkValue, Entity* attackTarget )
{
int count;
ActorGroup* group;
group = (ActorGroup*)groupcoordinator->GetGroup( GetGroupID() );
if ( !group )
return true;
count = group->CountMembersAttackingEnemy( attackTarget );
if ( count < checkValue )
return true;
return false;
}
void Actor::SetGroupDeathThread( Event *ev )
{
groupcoordinator->SetGroupDeathThread( ev->GetString( 1 ) , GetGroupID() );
}
qboolean Actor::checkHaveBestWeapon( Conditional &condition )
{
return checkHaveBestWeapon();
}
qboolean Actor::checkHaveBestWeapon()
{
str bestWeaponName;
float powerRating;
str currentPostureState;
Entity *currentEnemy;
Weapon *bestWeapon;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return true;
//First Check if our current weapon is useless
powerRating = combatSubsystem->GetActiveWeaponPowerRating( currentEnemy );
if ( powerRating < .05 )
{
//Put WeaponUseless Context Here
}
bestWeapon = combatSubsystem->GetBestAvailableWeapon( currentEnemy );
if( !bestWeapon )
return false;
if ( combatSubsystem->GetActiveWeaponName() == bestWeapon->getName() )
return true;
return false;
}
qboolean Actor::checkPosture( Conditional &condition )
{
return checkPosture( condition.getParm( 1 ) );
}
qboolean Actor::checkPosture( const str &postureName )
{
if ( postureController->getCurrentPostureName() == postureName )
return true;
return false;
}
qboolean Actor::checkAnyEnemyInRange( Conditional &condition )
{
float range = atof( condition.getParm( 1 ) );
return checkAnyEnemyInRange( range );
}
qboolean Actor::checkAnyEnemyInRange( float range )
{
return enemyManager->IsAnyEnemyInRange( range );
}
void Actor::SetAnimSet( Event *ev )
{
SetAnimSet(ev->GetString( 1 ) );
}
void Actor::SetAnimSet( const str& animSet )
{
animset = animSet;
}
void Actor::SetSelfDetonateModel ( Event * ev )
{
explosionModel = ev->GetString ( 1 );
}
const str& Actor::GetAnimSet()
{
return animset;
}
const str Actor::GetStateVar( const str& varName )
{
StateVar *checkVar = 0;
for (int i = 1; i <= stateVarList.NumObjects() ; i++ )
{
checkVar = stateVarList.ObjectAt( i );
if( checkVar->varName == varName )
{
return checkVar->varValue;
}
}
return "";
}
void Actor::ClearTorsoAnim( Event *ev )
{
ClearTorsoAnim();
}
void Actor::ClearTorsoAnim()
{
newTorsoAnimNum = -1;
newTorsoAnim = "";
newTorsoAnimEvent = NULL;
TorsoAnimName = "";
animate->ClearTorsoAnim();
}
void Actor::ClearLegAnim()
{
newanimnum = -1;
newanim = "";
newanimevent = NULL;
animate->ClearLegsAnim();
}
qboolean Actor::checkValidCoverNodeInRange( Conditional &condition )
{
float minDistanceFromPlayer = 96.0f;
float maxDistanceFromSelf = atof(condition.getParm( 1 ));
if ( GetActorFlag(ACTOR_FLAG_USE_FOLLOWRANGE_FOR_NODES) )
{
if ( enemyManager->HasEnemy() )
{
maxDistanceFromSelf = followTarget.maxRangeCombat;
}
else
maxDistanceFromSelf = followTarget.minRangeCombat;
//Fudge the value a little bit to allow for some "spring"
maxDistanceFromSelf = maxDistanceFromSelf * .85;
}
float minDistanceFromCurrentEnemy = atof(condition.getParm( 2 ));
if ( condition.numParms() > 2 )
minDistanceFromPlayer = atof(condition.getParm(3) );
return checkValidCoverNodeInRange( maxDistanceFromSelf , minDistanceFromCurrentEnemy, minDistanceFromPlayer );
}
qboolean Actor::checkValidCoverNodeInRange( float maxDistanceFromSelf, float minDistanceFromCurrentEnemy, float minDistanceFromPlayer )
{
HelperNode* coverNode = NULL;
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
//If we don't have a current enemy, give it one more evaluation chance
if ( !currentEnemy )
{
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy )
return false;
}
//
//See if we have a cover node that meets or requirements.
//If we do, the FindClosestHelperNodeThatCannotSeeEntity will set our currentHelperNode information for us
//
coverNode = HelperNode::FindClosestHelperNodeThatCannotSeeEntity(*this , NODETYPE_COVER , edict->clipmask, maxDistanceFromSelf, minDistanceFromCurrentEnemy, currentEnemy , minDistanceFromPlayer );
if ( coverNode )
return true;
return false;
}
qboolean Actor::checkValidCombatNodeInRange( Conditional &condition )
{
float minDistanceFromPlayer = 96.0f;
float maxDistanceFromSelf = atof(condition.getParm( 1 ));
if ( GetActorFlag(ACTOR_FLAG_USE_FOLLOWRANGE_FOR_NODES) )
{
if ( enemyManager->HasEnemy() )
{
maxDistanceFromSelf = followTarget.maxRangeCombat;
}
else
maxDistanceFromSelf = followTarget.minRangeCombat;
}
int unreserveCurrentNode = true; // this is int to avoid compiler warning C4800 :(
if ( condition.numParms() > 1 )
minDistanceFromPlayer = atof(condition.getParm( 2 ) );
if( condition.numParms() > 2 )
unreserveCurrentNode = atoi( condition.getParm( 3 ) );
return checkValidCombatNodeInRange( maxDistanceFromSelf , minDistanceFromPlayer, (unreserveCurrentNode != 0) );
}
qboolean Actor::checkValidCombatNodeInRange( float maxDistanceFromSelf , float minDistanceFromPlayer, bool unreserveCurrentNode )
{
HelperNode* coverNode = NULL;
//
//First, see if the level designer gave us a specific node
//
if ( currentHelperNode.node && (currentHelperNode.node->target.length()))
{
coverNode = currentHelperNode.node->GetTargetedHelperNode(currentHelperNode.node->target);
if ( coverNode )
{
if ( coverNode->isOfType( NODETYPE_COMBAT ) )
{
currentHelperNode.node->UnreserveNode();
currentHelperNode.node = coverNode;
currentHelperNode.mask = NODETYPE_COMBAT;
currentHelperNode.node->ReserveNode();
return true;
}
}
}
//
//See if we have a cover node that meets our requirements.
//
coverNode = HelperNode::FindClosestHelperNode(*this , NODETYPE_COMBAT , maxDistanceFromSelf, minDistanceFromPlayer, unreserveCurrentNode);
//if ( actortype == IS_ENEMY )
// coverNode = HelperNode::FindClosestHelperNode(*this , NODETYPE_COMBAT , maxDistanceFromSelf);
//else
// coverNode = HelperNode::FindHelperNodeClosestTo(*this, GetPlayer(0) , NODETYPE_COMBAT , maxDistanceFromSelf );
if ( coverNode )
return true;
return false;
}
qboolean Actor::checkEnemyCanSeeCurrentNode( Conditional &condition )
{
return checkEnemyCanSeeCurrentNode();
}
qboolean Actor::checkEnemyCanSeeCurrentNode()
{
trace_t trace;
Entity *currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentHelperNode.node )
return false;
if ( !currentEnemy )
{
enemyManager->FindClosestEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
}
if ( !currentEnemy )
return false;
if ( currentEnemy->isSubclassOf(Sentient) )
{
Sentient* theEnemy;
theEnemy = (Sentient*)currentEnemy;
trace = G_Trace( currentHelperNode.node->origin , vec_zero , vec_zero , theEnemy->EyePosition() , NULL , MASK_OPAQUE, false, "CoverCombatWithRangedWeapon::think");
//G_DebugLine( currentHelperNode.node->origin, theEnemy->EyePosition(), 1.0f, 0.0f, 0.0f, 1.0f );
}
else
{
trace = G_Trace( currentHelperNode.node->origin , vec_zero , vec_zero , currentEnemy->centroid , NULL , MASK_OPAQUE, false, "CoverCombatWithRangedWeapon::think");
//G_DebugLine( currentHelperNode.node->origin, currentEnemy->centroid, 1.0f, 0.0f, 0.0f, 1.0f );
}
if ( trace.fraction >= .95 )
return true;
return false;
}
qboolean Actor::checkShouldDoAction( Conditional &condition )
{
str tendencyName = condition.getParm( 1 );
return checkShouldDoAction( tendencyName );
}
qboolean Actor::checkShouldDoAction( const str &tendencyName )
{
float tendency = personality->GetTendency( tendencyName );
return ( G_Random() < tendency );
}
qboolean Actor::checkValidWorkNodeInRange( Conditional &condition )
{
int unreserveCurrentNode = true; // this is int to avoid compiler warning C4800 :(
float maxDistanceFromSelf = atof(condition.getParm( 1 ));
if( condition.numParms() > 1 )
unreserveCurrentNode = atoi( condition.getParm( 2 ) );
return checkValidWorkNodeInRange( maxDistanceFromSelf, (unreserveCurrentNode != 0) );
}
qboolean Actor::checkValidWorkNodeInRange( float maxDistanceFromSelf, bool unreserveCurrentNode )
{
HelperNode* coverNode = NULL;
if ( level.time < _nextCheckForWorkNodeTime )
return false;
//
//See if we have a work node that meets our requirements.
//
coverNode = HelperNode::FindClosestHelperNode(*this , NODETYPE_WORK , maxDistanceFromSelf , 0.0f, unreserveCurrentNode );
_nextCheckForWorkNodeTime = ( G_Random() + level.time + 0.50f );
if ( coverNode )
return true;
return false;
}
qboolean Actor::checkValidHibernateNodeInRange( Conditional &condition )
{
float maxDistanceFromSelf = atof( condition.getParm( 1 ) );
return checkValidHibernateNodeInRange( maxDistanceFromSelf );
}
qboolean Actor::checkValidHibernateNodeInRange( float maxDistanceFromSelf )
{
HelperNode* hibernateNode = NULL;
if ( level.time < _nextCheckForHibernateNodeTime )
return false;
//
//See if we have a work node that meets our requirements.
//
hibernateNode = HelperNode::FindClosestHelperNode(*this , "hibernate" , maxDistanceFromSelf );
_nextCheckForHibernateNodeTime = ( G_Random() + level.time + 0.50f );
if ( hibernateNode )
return true;
return false;
}
qboolean Actor::checkValidPatrolNodeInRange( Conditional &condition )
{
float maxDistanceFromSelf = atof(condition.getParm( 1 ));
return checkValidPatrolNodeInRange( maxDistanceFromSelf );
}
qboolean Actor::checkValidPatrolNodeInRange( float maxDistanceFromSelf )
{
HelperNode* patrolNode = NULL;
//
//See if we have a cover node that meets or requirements.
//
patrolNode = HelperNode::FindClosestHelperNode(*this , NODETYPE_PATROL , maxDistanceFromSelf, 0);
if ( patrolNode )
return true;
return false;
}
qboolean Actor::checkValidSniperNodeInRange( Conditional &condition )
{
float maxDistanceFromSelf = atof(condition.getParm( 1 ));
return checkValidSniperNodeInRange( maxDistanceFromSelf );
}
qboolean Actor::checkValidSniperNodeInRange( float maxDistanceFromSelf )
{
HelperNode* sniperNode = NULL;
//
//See if we have a cover node that meets or requirements.
//
sniperNode = HelperNode::FindClosestHelperNode(*this , NODETYPE_SNIPER , maxDistanceFromSelf);
if ( sniperNode )
return true;
return false;
}
qboolean Actor::checkValidCustomNodeInRange( Conditional &condition )
{
str customType = condition.getParm( 1 );
float maxDistanceFromSelf = atof( condition.getParm( 2 ) );
return checkValidCustomNodeInRange( customType , maxDistanceFromSelf );
}
qboolean Actor::checkValidCustomNodeInRange( const str &customType , float maxDistanceFromSelf )
{
HelperNode* customNode = NULL;
//
//See if we have a cover node that meets or requirements.
//
customNode = HelperNode::FindClosestHelperNode(*this , customType , maxDistanceFromSelf);
if ( customNode )
return true;
return false;
}
qboolean Actor::checkSpecifiedFollowTargetOutOfRange( Conditional &condition )
{
return checkSpecifiedFollowTargetOutOfRange();
}
qboolean Actor::checkSpecifiedFollowTargetOutOfRange()
{
if ( !followTarget.specifiedFollowTarget )
followTarget.specifiedFollowTarget = GetPlayer( 0 );
if ( followTarget.specifiedFollowTarget )
{
float range = followTarget.maxRangeIdle;
Entity *enemy;
enemy = enemyManager->GetCurrentEnemy();
if ( !enemy )
{
enemyManager->FindHighestHateEnemy();
enemy = enemyManager->GetCurrentEnemy();
}
if ( enemy )
range = followTarget.maxRangeCombat;
if ( EntityInRange( followTarget.specifiedFollowTarget, range, 0, 0 , false ) )
return false;
else
return true;
}
return false;
}
void Actor::SetPostureState( Event *ev )
{
postureController->setPostureState( ev->GetString( 1 ) , ev->GetString( 2 ) );
}
//--------------------------------------------------------------
// Name: FindDialog
// Class: Actor
//
// Description: Finds an appropriate dialog alias based on the dialogType
//
// Parameters: DialogType_t -- The type of dialog to find
//
// Returns: const str
//--------------------------------------------------------------
const str Actor::FindDialog( Sentient *user, DialogType_t dialogType , const str& context )
{
DialogNode_t *dialog_node;
int good_dialog;
ScriptVariable *script_var;
const char* the_dialog;
str dialog_name;
bool usingContext = false;
dialog_node = dialog_list;
while(dialog_node != NULL)
{
// See if we should play the current dialog
good_dialog = true;
if ( dialogType == DIALOG_TYPE_NORMAL )
{
//If we're looking for normal dialog,
//We don't play radius dialog.
if ( dialog_node->dType != DIALOG_TYPE_NORMAL )
{
dialog_node = dialog_node->next;
continue;
}
}
if ( dialogType == DIALOG_TYPE_RADIUS )
{
//If we're looking for radius dialog
//It's got to be radius dialog
if ( dialog_node->dType != DIALOG_TYPE_RADIUS )
{
dialog_node = dialog_node->next;
continue;
}
}
if ( dialogType == DIALOG_TYPE_GREETING )
{
//If we're looking for greeting dialog
//It's got to be greeting dialog
if ( dialog_node->dType != DIALOG_TYPE_GREETING )
{
dialog_node = dialog_node->next;
continue;
}
}
if ( dialogType == DIALOG_TYPE_COMBAT )
{
//If we're looking for combat dialog
//It's got to be combat dialog
if ( dialog_node->dType != DIALOG_TYPE_COMBAT )
{
dialog_node = dialog_node->next;
continue;
}
}
if ( dialogType == DIALOG_TYPE_CONTEXT_INITIATOR || dialogType == DIALOG_TYPE_CONTEXT_RESPONSE )
{
//
// If we're context dialog we HAVE to have at least 1 parameter
//
if ( dialog_node->number_of_parms < 1 )
{
dialog_node = dialog_node->next;
continue;
}
}
for(int i = 0 ; i < dialog_node->number_of_parms ; i++)
{
// Test to see if this parm passes
switch(dialog_node->parms[ i ].type)
{
case DIALOG_PARM_TYPE_PLAYERHAS :
if (!user || !user->HasItem(dialog_node->parms[ i ].parm))
good_dialog = false;
break;
case DIALOG_PARM_TYPE_PLAYERHASNOT :
if (!user || user->HasItem(dialog_node->parms[ i ].parm))
good_dialog = false;
break;
case DIALOG_PARM_TYPE_HAS :
if (!HasItem(dialog_node->parms[ i ].parm))
good_dialog = false;
break;
case DIALOG_PARM_TYPE_HASNOT :
if (HasItem(dialog_node->parms[ i ].parm))
good_dialog = false;
break;
case DIALOG_PARM_TYPE_DEPENDS :
script_var = NULL;
if ( strnicmp( dialog_node->parms[ i ].parm, "game.", 5 ) == 0 )
script_var = gameVars.GetVariable( dialog_node->parms[ i ].parm + 5 );
else if ( strnicmp( dialog_node->parms[ i ].parm, "level.", 6 ) == 0 )
script_var = levelVars.GetVariable( dialog_node->parms[ i ].parm + 6 );
if ( !script_var || !script_var->intValue() )
good_dialog = false;
break;
case DIALOG_PARM_TYPE_DEPENDSNOT :
script_var = NULL;
if ( strnicmp( dialog_node->parms[ i ].parm, "game.", 5 ) == 0 )
script_var = gameVars.GetVariable( dialog_node->parms[ i ].parm + 5 );
else if ( strnicmp( dialog_node->parms[ i ].parm, "level.", 6 ) == 0 )
script_var = levelVars.GetVariable( dialog_node->parms[ i ].parm + 6 );
if ( script_var && script_var->intValue() )
good_dialog = false;
break;
case DIALOG_PARM_TYPE_DEPENDSINT :
script_var = NULL;
if ( strnicmp( dialog_node->parms[ i ].parm, "game.", 5 ) == 0 )
script_var = gameVars.GetVariable( dialog_node->parms[ i ].parm + 5 );
else if ( strnicmp( dialog_node->parms[ i ].parm, "level.", 6 ) == 0 )
script_var = levelVars.GetVariable( dialog_node->parms[ i ].parm + 6 );
if ( !script_var || ( script_var->intValue() != atoi( dialog_node->parms[ i ].parm2 ) ) )
good_dialog = false;
break;
case DIALOG_PARM_TYPE_CONTEXT_INITIATOR:
if ( dialogType != DIALOG_TYPE_CONTEXT_INITIATOR )
good_dialog = false;
if ( stricmp( dialog_node->parms[i].parm , context.c_str() ) )
good_dialog = false;
usingContext = true;
break;
case DIALOG_PARM_TYPE_CONTEXT_RESPONSE:
if ( dialogType != DIALOG_TYPE_CONTEXT_RESPONSE )
good_dialog = false;
if ( stricmp( dialog_node->parms[i].parm , context.c_str() ) )
good_dialog = false;
usingContext = true;
break;
}
// If dialog is already not good go to next dialog
if (!good_dialog)
break;
}
if ( ( dialog_node->random_percent < 1.0f ) && ( G_Random() > dialog_node->random_percent ) )
good_dialog = false;
if ( dialogType == DIALOG_TYPE_CONTEXT_INITIATOR || dialogType == DIALOG_TYPE_CONTEXT_RESPONSE )
{
if ( !usingContext )
good_dialog = false;
}
if (good_dialog)
{
// Found a good dialog now get the real sound name from the alias
the_dialog = NULL;
the_dialog = gi.Alias_FindDialog( edict->s.modelindex, dialog_node->alias_name, dialog_node->random_flag, entnum);
if ( the_dialog )
{
dialog_name = the_dialog;
break;
}
}
// Try the next dialog in the list
dialog_node = dialog_node->next;
}
return dialog_name;
}
qboolean Actor::checkHaveArmor( Conditional &condition )
{
return checkHaveArmor();
}
qboolean Actor::checkHaveArmor()
{
if ( !currentBaseArmor )
return false;
if ( currentBaseArmor->getAmount() <= 0 )
return false;
return true;
}
qboolean Actor::checkWithinFollowRangeMin( Conditional &condition )
{
return checkWithinFollowRangeMin();
}
qboolean Actor::checkWithinFollowRangeMin()
{
if ( !followTarget.specifiedFollowTarget )
followTarget.specifiedFollowTarget = GetPlayer( 0 );
if ( followTarget.specifiedFollowTarget )
{
float range = followTarget.minRangeIdle;
Entity *enemy;
enemy = enemyManager->GetCurrentEnemy();
if ( !enemy )
{
enemyManager->FindHighestHateEnemy();
enemy = enemyManager->GetCurrentEnemy();
}
if ( enemy )
range = followTarget.minRangeCombat;
if ( EntityInRange( followTarget.specifiedFollowTarget, range, 0, 0 , false ) )
{
if ( level.time > _nextPathDistanceToFollowTargetCheck )
{
FindMovementPath find;
Path *path;
float pathLen;
_nextPathDistanceToFollowTargetCheck = G_Random(.33 ) + DEFAULT_PATH_TO_ENEMY_INTERVAL + level.time;
// Set up our pathing heuristics
find.heuristic.self = this;
find.heuristic.setSize( size );
find.heuristic.entnum = entnum;
path = find.FindPath( origin, followTarget.specifiedFollowTarget->origin );
if ( !path )
{
return true;
}
pathLen = path->Length();
delete path;
path = NULL;
if ( pathLen > range )
return false;
else
return true;
}
}
else
return false;
}
return false;
}
void Actor::SetTalkWatchMode( Event *ev )
{
str mode = ev->GetString( 1 );
if ( mode == "ignore" )
talkMode = TALK_IGNORE;
if ( mode == "headwatchonly" )
talkMode = TALK_HEADWATCH;
if ( mode == "turnto" )
talkMode = TALK_TURNTO;
if ( ev->NumArgs() > 1 )
useConvAnims = ev->GetBoolean(2);
}
qboolean Actor::checkAllowedToMeleeEnemy( Conditional &condition )
{
return checkAllowedToMeleeEnemy();
}
qboolean Actor::checkAllowedToMeleeEnemy()
{
Entity *enemy = enemyManager->GetCurrentEnemy();
if ( !enemy ) return false;
if ( enemy->isSubclassOf(Player) ) return true;
if ( !enemy->isSubclassOf(Actor) ) return false;
Actor *actor = (Actor*)enemy;
if ( actor->GetActorFlag(ACTOR_FLAG_MELEE_ALLOWED) )
return true;
return false;
}
qboolean Actor::checkCurrentNodeHasThisCoverType( Conditional &condition )
{
str coverType = condition.getParm( 1 );
return checkCurrentNodeHasThisCoverType( coverType );
}
qboolean Actor::checkCurrentNodeHasThisCoverType( const str &coverType )
{
if ( !currentHelperNode.node )
return false;
if ( coverType == "none" )
{
if ( currentHelperNode.node->GetCoverType() == COVER_TYPE_NONE )
return true;
}
if ( coverType == "crate" )
{
if ( currentHelperNode.node->GetCoverType() == COVER_TYPE_CRATE )
return true;
}
if ( coverType == "wall" )
{
if ( currentHelperNode.node->GetCoverType() == COVER_TYPE_WALL )
return true;
}
return false;
}
void Actor::PrepareToFailMission( Event *ev )
{
float time;
str reason;
time = ev->GetFloat( 1 );
reason = "DefaultFailure";
if ( ev->NumArgs() > 1 )
reason = ev->GetString( 2 );
Event *failureEvent;
failureEvent = new Event(EV_Actor_FailMission);
failureEvent->AddString( reason );
PostEvent( failureEvent, time );
}
void Actor::FailMission( Event *ev )
{
str reason = "DefaultFailure";
if ( ev->NumArgs() > 0 )
reason = ev->GetString( 1 );
G_MissionFailed(reason);
}
void Actor::DebugEvent( Event *ev )
{
//Here to catch when a state hits a debug event
int x;
x = 0;
}
qboolean Actor::checkSteeringFailed( Conditional &condition )
{
return ( state_flags & STATE_FLAG_STEERING_FAILED );
}
qboolean Actor::checkHavePathToEnemy( Conditional &condition )
{
return checkHavePathToEnemy();
}
qboolean Actor::checkHavePathToEnemy()
{
if ( !enemyManager->HasEnemy() )
{
_nextCheckForEnemyPath = 0;
return false;
}
if ( level.time >= _nextCheckForEnemyPath )
{
Entity *currentEnemy = enemyManager->GetCurrentEnemy();
if ( !currentEnemy ) return false;
FindMovementPath find;
Path *path;
_nextCheckForEnemyPath = G_Random() + DEFAULT_PATH_TO_ENEMY_INTERVAL;
// Set up our pathing heuristics
find.heuristic.self = this;
find.heuristic.setSize( size );
find.heuristic.entnum = entnum;
path = find.FindPath( origin, currentEnemy->origin );
if ( !path )
{
_havePathToEnemy = false;
return false;
}
delete path;
path = NULL;
_havePathToEnemy = true;
}
return _havePathToEnemy;
}
void Actor::UnreserveCurrentHelperNode( Event *ev )
{
UnreserveCurrentHelperNode();
}
void Actor::UnreserveCurrentHelperNode()
{
if ( currentHelperNode.node )
currentHelperNode.node->UnreserveNode();
}
qboolean Actor::checkBlockedByEnemy( Conditional &condition )
{
if ( !(state_flags & STATE_FLAG_BLOCKED_BY_ENTITY) )
return false;
Entity *blockingEntity = movementSubsystem->getBlockingEntity();
if ( !blockingEntity )
return false;
sensoryPerception->Stimuli( STIMULI_SIGHT, blockingEntity );
if ( enemyManager->Hates( blockingEntity ) )
return true;
return false;
}
void Actor::ProjectileClose( Event *ev )
{
Entity *owner;
owner = ev->GetEntity( 1 );
if ( !owner ) return;
if ( enemyManager->Hates( owner ) )
{
AddStateFlag(STATE_FLAG_ENEMY_PROJECTILE_CLOSE);
}
}
qboolean Actor::checkEnemyProjectileClose( Conditional &condition )
{
return checkEnemyProjectileClose();
}
qboolean Actor::checkEnemyProjectileClose()
{
return ( state_flags & STATE_FLAG_ENEMY_PROJECTILE_CLOSE );
}
void Actor::SaveOffLastHitBone( Event *ev )
{
saved_bone_hit = last_bone_hit;
}
void Actor::SetPlayPainSoundInterval( Event *ev )
{
_playPainSoundInterval = ev->GetFloat( 1 );
}
void Actor::SetContextInterval( Event *ev )
{
SetContextInterval(ev->GetFloat(1));
}
void Actor::SetContextInterval( float interval )
{
_contextInterval = interval;
}
void Actor::SetMinPainTime( Event *ev )
{
SetMinPainTime( ev->GetFloat( 1 ) );
}
void Actor::SetMinPainTime( float time )
{
min_pain_time = time;
}
void Actor::SetEnemyTargeted( Event *ev )
{
SetEnemyTargeted(ev->GetBoolean(1) );
}
void Actor::SetEnemyTargeted( bool targeted )
{
Entity* enemy;
enemy = enemyManager->GetCurrentEnemy();
if ( !enemy ) return;
if ( enemy->isSubclassOf(Player) )
{
Player* player;
player = (Player*)enemy;
player->setTargeted(targeted);
}
}
void Actor::SetActivationDelay( Event *ev )
{
SetActivationDelay(ev->GetFloat(1) );
}
void Actor::SetActivationDelay( float delay )
{
activationDelay = delay;
}
void Actor::SetActivationStart( Event *ev )
{
SetActivationStart();
}
void Actor::SetActivationStart()
{
activationStart = level.time;
}
qboolean Actor::checkActivationDelayTime( Conditional &condition )
{
return checkActivationDelayTime();
}
qboolean Actor::checkActivationDelayTime()
{
//Yes, I could do this all 1337-like in one line, but debugging that sucks.
if ( level.time >= activationStart + activationDelay )
return true;
return false;
}
void Actor::SetCheckConeOfFireDistance( Event* ev )
{
SetCheckConeOfFireDistance( ev->GetFloat( 1 ) );
}
void Actor::SetCheckConeOfFireDistance( float distance )
{
assert( strategos );
assert( distance > 0 );
this->strategos->SetCheckInConeDistMax( distance );
}
//-----------------------------------------------------------------
// Note: This Needs to be its own class
//-----------------------------------------------------------------
void Actor::AddCustomThread( Event *ev )
{
AddCustomThread( ev->GetString( 1 ) , ev->GetString( 2 ) );
}
void Actor::AddCustomThread( const str& threadType , const str& threadName )
{
threadlist_t* threadListEntry;
//First Search the container to see if we already have a custom thread with
//the specified type. If we don't great, we'll just add this thing. If we do
//then we are going to replace the threadName with the one passed in
if ( threadList.NumObjects() )
{
for ( int i = 1; i <= threadList.NumObjects() ; i++ )
{
threadListEntry = threadList.ObjectAt( i );
if ( !stricmp(threadListEntry->threadType.c_str() , threadType.c_str() ) )
{
threadListEntry->threadName = threadName;
return;
}
}
}
//Since we didn't find a matching threadType, we'll just add what we need.
threadListEntry = new threadlist_t;
threadListEntry->threadType = threadType;
threadListEntry->threadName = threadName;
threadList.AddObject( threadListEntry );
}
bool Actor::HaveCustomThread( const str& threadType )
{
threadlist_t* threadListEntry;
// Search the container to see if we have a threadType entry matching the
// threadType we're looking for.
if ( threadList.NumObjects() )
{
for ( int i = 1; i <= threadList.NumObjects() ; i++ )
{
threadListEntry = threadList.ObjectAt( i );
if ( !stricmp(threadListEntry->threadType.c_str() , threadType.c_str() ) )
{
return true;
}
}
}
return false;
}
void Actor::RunCustomThread( const str& threadType )
{
threadlist_t* threadListEntry;
str threadName;
// Search the container to see if we have a threadType entry matching the
// threadType we're looking for.
if ( threadList.NumObjects() )
{
for ( int i = 1; i <= threadList.NumObjects() ; i++ )
{
threadListEntry = threadList.ObjectAt( i );
if ( !stricmp(threadListEntry->threadType.c_str() , threadType.c_str() ) )
{
threadName = threadListEntry->threadName;
if ( threadName.length() )
{
ExecuteThread(threadName,true,this);
}
}
}
}
}
const str Actor::GetCustomThread( const str& threadType )
{
threadlist_t* threadListEntry;
str threadName;
// Search the container to see if we have a threadType entry matching the
// threadType we're looking for.
if ( threadList.NumObjects() )
{
for ( int i = 1; i <= threadList.NumObjects() ; i++ )
{
threadListEntry = threadList.ObjectAt( i );
if ( !stricmp(threadListEntry->threadType.c_str() , threadType.c_str() ) )
{
return threadListEntry->threadName;
}
}
}
return threadName;
}
void Actor::LevelAIOff()
{
//Okay, since we don't want to turn on every single AI in the game
//when we get a level.ai_on event -- Only the AI that were on before
//we need to only turn off the AI that is on
if ( GetActorFlag( ACTOR_FLAG_AI_ON ) )
{
_levelAIOff = true;
TurnAIOff();
}
}
void Actor::LevelAIOn()
{
if ( GetActorFlag( ACTOR_FLAG_AI_ON ) ) return;
if ( _levelAIOff )
{
_levelAIOff = false;
TurnAIOn();
}
}
void Actor::SetHeadWatchMaxDistance( Event *ev )
{
headWatcher->SetMaxDistance( ev->GetFloat( 1 ) );
}
void Actor::SetBounceOffVelocity( Event *ev )
{
bounce_off_velocity = ev->GetFloat( 1 );
}
qboolean Actor::checkTalking( Conditional &condition )
{
return checkTalking();
}
qboolean Actor::checkTalking()
{
if ( mode == ACTOR_MODE_TALK )
return true;
return false;
}
qboolean Actor::checkEnemiesNearby(Conditional &condition )
{
float dist = atof(condition.getParm(1) );
return checkEnemiesNearby(dist);
}
qboolean Actor::checkEnemiesNearby(float distance)
{
Entity *ent;
for ( int i = 1 ; i <= ActiveList.NumObjects() ; i++ )
{
ent = ActiveList.ObjectAt( i );
if ( (enemyManager->Hates( ent )) && WithinDistance(ent, distance ) )
return true;
}
return false;
}
void Actor::SetDeathKnockbackValues(Event *ev)
{
deathKnockbackVerticalValue = ev->GetFloat( 1 );
deathKnockbackHorizontalValue = ev->GetFloat( 2 );
}
void Actor::SetIgnoreWatchTarget( bool ignore )
{
headWatcher->SetIgnoreWatchTarget( ignore );
}