ef2gamesource/dlls/game/actor.cpp

18218 lines
408 KiB
C++
Raw Permalink Normal View History

2012-12-30 16:37:54 +00:00
//-----------------------------------------------------------------------------
//
// $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 "behavior.h"
#include "scriptmaster.h"
#include "doors.h"
#include "gibs.h"
#include "object.h"
#include "scriptslave.h"
#include "characterstate.h"
#include "weaputils.h"
#include "armor.h"
#include "groupcoordinator.hpp"
#include <qcommon/gameplaymanager.h>
#include "talk.hpp"
#include "equipment.h"
2015-04-28 20:02:03 +00:00
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
2012-12-30 16:37:54 +00:00
Container<BehaviorPackageType_t *> PackageList; //Global list of all behavior packages ( in BehaviorPackages.txt )
extern Container<int> SpecialPathNodes;
Event EV_Actor_SetSelfDetonateModel
2015-04-28 20:02:03 +00:00
(
"selfdetonatemodel",
EV_TIKIONLY,
"s",
"modelname",
"Set the modelname of the explosion to be spawned when an actor self-detonates"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_BlindlyFollowPath
2015-04-28 20:02:03 +00:00
(
"blindlyfollowpath",
EV_SCRIPTONLY,
"sFS",
"anim_name offset pathnode",
"Actor walks to specified path node without avoidance or collision"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetSimplifiedThink
2015-04-28 20:02:03 +00:00
(
"setsimplifiedthink",
EV_DEFAULT,
"B",
"boolean",
"change actor to SimplifiedThink think strategy"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetActorToActorDamageModifier
2015-04-28 20:02:03 +00:00
(
"actortoactordamage",
EV_DEFAULT,
"f",
"modifier",
"Amount to modifiy damage by 1 is full damage, 0 would be no damage"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_OnUse
2015-04-28 20:02:03 +00:00
(
"onuse",
EV_SCRIPTONLY,
"s",
"thread_name",
"Sets the thread to call"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NoUse
2015-04-28 20:02:03 +00:00
(
"nouse",
EV_SCRIPTONLY,
nullptr,
nullptr,
"Clears the on use thread"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ClearCurrentEnemy
2015-04-28 20:02:03 +00:00
(
"clearCurrentEnemy",
EV_DEFAULT,
nullptr,
nullptr,
"Sets Current Enemy to Null"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetTargetType
2015-04-28 20:02:03 +00:00
(
"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 "
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Sleep
2015-04-28 20:02:03 +00:00
(
"sleep",
EV_DEFAULT,
nullptr,
nullptr,
"Put the actor to sleep."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Wakeup
2015-04-28 20:02:03 +00:00
(
"wakeup",
EV_SCRIPTONLY,
nullptr,
nullptr,
"Wake up the actor."
);
Event EV_Actor_Fov
(
"fov",
EV_CONSOLE,
"f",
"fov",
"Sets the actor's field of view (fov)."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_VisionDistance
2015-04-28 20:02:03 +00:00
(
"visiondistance",
EV_DEFAULT,
"f",
"vision_distance",
"Sets the distance the actor can see."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Start
2015-04-28 20:02:03 +00:00
(
"start",
EV_DEFAULT,
nullptr,
nullptr,
"Initializes the actor a little, "
"it is not meant to be called from script."
);
Event EV_Actor_Dead
(
"dead",
EV_CODEONLY,
nullptr,
nullptr,
"Does everything necessary when an actor dies, "
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetEnemyType
2015-04-28 20:02:03 +00:00
(
"enemytype",
EV_DEFAULT,
"s",
"enemytype",
"Sets the name of this actor's enemy type."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Swim
2015-04-28 20:02:03 +00:00
(
"swim",
EV_DEFAULT,
nullptr,
nullptr,
"Specifies actor as being able to swim."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Fly
2015-04-28 20:02:03 +00:00
(
"fly",
EV_DEFAULT,
"B",
"fly_bool",
"Specifies actor as being able to fly (optional bool can turn fly on or off)."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NotLand
2015-04-28 20:02:03 +00:00
(
"noland",
EV_DEFAULT,
nullptr,
nullptr,
"Specifies actor as not being able to walk on land."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_RunThread
2015-04-28 20:02:03 +00:00
(
"runthread",
EV_CODEONLY,
"s",
"label",
"Runs the specified thread."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Statemap
2015-04-28 20:02:03 +00:00
(
"statemap",
EV_DEFAULT,
"sS",
"statemap_name state_name",
"Sets which statemap file to use and optionally what the first state to go to."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_MasterStateMap
2015-04-28 20:02:03 +00:00
(
"masterstatemap",
EV_DEFAULT,
"sS",
"statemap_name state_name",
"Sets which masterstatemap file to use and optionally what the first state to go to."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_FuzzyEngine
2015-04-28 20:02:03 +00:00
(
"fuzzyengine",
EV_DEFAULT,
"s",
"fuzzyengine_name",
"Sets which fuzzy engine file to use"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetBehaviorPackage
2015-04-28 20:02:03 +00:00
(
"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"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ChildUseBehaviorPackage
2015-04-28 20:02:03 +00:00
(
"childusebehaviorpackage",
EV_DEFAULT,
"ss",
"childname package_name",
"sets the child to use the specified behavior package but does NOT set the master state"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ChildSetAnim
2015-04-28 20:02:03 +00:00
(
"childsetanim",
EV_DEFAULT,
"ss",
"childname anim_name",
"sets the child to play the anim specified"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ChildSuicide
2015-04-28 20:02:03 +00:00
(
"childsuicide",
EV_DEFAULT,
"s",
"childname",
"sets the child to kill itself"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IfEnemyVisible
2015-04-28 20:02:03 +00:00
(
"ifenemyvisible",
EV_SCRIPTONLY,
"SSSSSS",
"token1 token2 token3 token4 token5 token6",
"Process the following command if enemy is visible"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IfNear
2015-04-28 20:02:03 +00:00
(
"ifnear",
EV_SCRIPTONLY,
"sfSSSSSS",
"name distance token1 token2 token3 token4 token5 token6",
"Process the following command if enemy is within specified distance"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ForwardSpeed
2015-04-28 20:02:03 +00:00
(
"forwardspeed",
EV_DEFAULT,
"f",
"forwardspeed",
"Sets the actor's forward speed."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Idle
2015-04-28 20:02:03 +00:00
(
"idlestate",
EV_SCRIPTONLY,
"S",
"state_name",
"Tells the actor to go into idle mode."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_LookAt
2015-04-28 20:02:03 +00:00
(
"lookat",
EV_SCRIPTONLY,
"e",
"ent",
"Specifies an entity to look at."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_TurnTo
2015-04-28 20:02:03 +00:00
(
"turntoangle",
EV_SCRIPTONLY,
"f",
"direction",
"Specifies the direction to look in."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_HeadWatch
2015-04-28 20:02:03 +00:00
(
"headwatch",
EV_SCRIPTONLY,
"eF",
"entity_to_watch max_speed",
"Actor watches the specified entity by turning his head."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_HeadAndEyeWatch
2015-04-28 20:02:03 +00:00
(
"headandeyewatch",
EV_SCRIPTONLY,
"eF",
"entity_to_watch max_speed",
"Actor watches the specified entity by turning his eyes,then head."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ResetHead
2015-04-28 20:02:03 +00:00
(
"resethead",
EV_DEFAULT,
"F",
"max_speed",
"Actor resets its head back to looking forwards."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EyeWatch
2015-04-28 20:02:03 +00:00
(
"eyewatch",
EV_SCRIPTONLY,
"eF",
"entity_to_watch max_speed",
"Actor watches the specified entity by turning his eyes."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ResetEye
2015-04-28 20:02:03 +00:00
(
"reseteyes",
EV_DEFAULT,
"F",
"max_speed",
"Actor resets its eyes back to looking forwards."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ResetTorso
2015-04-28 20:02:03 +00:00
(
"resettorso",
EV_DEFAULT,
"f",
"max_speed",
"Actor resets its torso to looking forwards"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_BehaviorFinished
2015-04-28 20:02:03 +00:00
(
"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."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ControlLost
2015-04-28 20:02:03 +00:00
(
"controlost",
EV_CODEONLY,
nullptr,
nullptr,
"Sent to a controller when it loses control."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EndBehavior
2015-04-28 20:02:03 +00:00
(
"endbehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Ends the current behavior, "
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EndHeadBehavior
2015-04-28 20:02:03 +00:00
(
"endheadbehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Ends the current head behavior "
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EndEyeBehavior
2015-04-28 20:02:03 +00:00
(
"endeyebehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Ends the current eye behavior "
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EndTorsoBehavior
2015-04-28 20:02:03 +00:00
(
"endtorsobehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Ends the current torso behavior "
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NotifyBehavior
2015-04-28 20:02:03 +00:00
(
"notifybehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Notifies the current behavior of an event,"
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NotifyHeadBehavior
2015-04-28 20:02:03 +00:00
(
"notifyheadbehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Notifies the current head behavior of an event"
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NotifyEyeBehavior
2015-04-28 20:02:03 +00:00
(
"notifyeyebehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Notifies the current eye behavior of an event"
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NotifyTorsoBehavior
2015-04-28 20:02:03 +00:00
(
"notifytorsobehavior",
EV_CODEONLY,
nullptr,
nullptr,
"Notifies the current torso behavior of an event"
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_FallToDeath
2015-04-28 20:02:03 +00:00
(
"falltodeath",
EV_SCRIPTONLY,
"fffsssF",
"forwardmove sidemove speed startanim fallanim deathanim anim_delay",
"makes an actor fall to his death"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_WalkTo
2015-04-28 20:02:03 +00:00
(
"walkto",
EV_DEFAULT,
"sSFF",
"pathnode anim_name force maxfailures",
"Actor walks to specified path node"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_WalkWatch
2015-04-28 20:02:03 +00:00
(
"walkwatch",
EV_SCRIPTONLY,
"seS",
"pathnode entity anim_name",
"Actor walks to specified path node and watches the specified entity"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_WarpTo
2015-04-28 20:02:03 +00:00
(
"warpto",
EV_SCRIPTONLY,
"s",
"node_name",
"Warps the actor to the specified node"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_JumpTo
2015-04-28 20:02:03 +00:00
(
"jumpto",
EV_SCRIPTONLY,
"sFF",
"pathnode_or_entity launchAngle dummy_arg",
"Actor jumps to specified path node"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PickupEnt
2015-04-28 20:02:03 +00:00
(
"pickupent",
EV_DEFAULT,
"es",
"entity_to_pickup pickup_anim_name",
"Makes actor pick up the specified entity"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ThrowEnt
2015-04-28 20:02:03 +00:00
(
"throwent",
EV_DEFAULT,
"s",
"throw_anim_name",
"Makes actor throw the entity in hands"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Anim
2015-04-28 20:02:03 +00:00
(
"anim",
EV_DEFAULT,
"s",
"anim_name",
"Starts the PlayAnim behavior."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetAnim
2015-04-28 20:02:03 +00:00
(
"setanim",
EV_DEFAULT,
"sF",
"anim_name animationRate",
"Sets the animation directly."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Attack
2015-04-28 20:02:03 +00:00
(
"attack",
EV_SCRIPTONLY,
"eB",
"ent force",
"Makes the actor attack the specified entity."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AttackPlayer
2015-04-28 20:02:03 +00:00
(
"attackplayer",
EV_SCRIPTONLY,
nullptr,
nullptr,
"Makes enemies of all the players."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ReserveNode
2015-04-28 20:02:03 +00:00
(
"reservenode",
EV_CODEONLY,
"vf",
"pos time",
"Reserves a path node for the specified amount of time."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ReleaseNode
2015-04-28 20:02:03 +00:00
(
"releasenode",
EV_CODEONLY,
"v",
"pos",
"Releases a path node from being reserved."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IfCanHideAt
2015-04-28 20:02:03 +00:00
(
"ifcanhideat",
EV_SCRIPTONLY,
"vSSSSSS",
"pos token1 token2 token3 token4 token5 token6",
"Processes command if actor can hide at specified position."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IfEnemyWithin
2015-04-28 20:02:03 +00:00
(
"ifenemywithin",
EV_SCRIPTONLY,
"fSSSSSS",
"distance token1 token2 token3 token4 token5 token6",
"Processes command if actor is within distance of its current enemy."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Remove
2015-04-28 20:02:03 +00:00
(
"remove_useless",
EV_CODEONLY,
nullptr,
nullptr,
"Removes a useless dead body from the game."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Melee
2015-04-28 20:02:03 +00:00
(
"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"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PainThreshold
2015-04-28 20:02:03 +00:00
(
"painthreshold",
EV_TIKIONLY,
"f",
"pain_threshold",
"Sets the actor's pain threshold."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetKillThread
2015-04-28 20:02:03 +00:00
(
"killthread",
EV_SCRIPTONLY,
"s",
"kill_thread",
"Sets the actor's kill thread."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EyePositionOffset
2015-04-28 20:02:03 +00:00
(
"eyeoffset",
EV_TIKIONLY,
"v",
"eyeoffset",
"Sets the actor's eye position."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DeathFade
2015-04-28 20:02:03 +00:00
(
"deathfade",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor fade when dead."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DeathEffect
(
2015-04-28 20:02:03 +00:00
"deathEffect",
EV_DEFAULT,
"s",
"deathEffectName",
"Displays a display effect instead of fading, shrinking, etc."
2012-12-30 16:37:54 +00:00
);
Event EV_Actor_DeathShrink
2015-04-28 20:02:03 +00:00
(
"deathshrink",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor shrink when dead."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DeathSink
2015-04-28 20:02:03 +00:00
(
"deathsink",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor sink into the ground when dead."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_StaySolid
2015-04-28 20:02:03 +00:00
(
"staysolid",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor stay solid after death."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NoChatter
2015-04-28 20:02:03 +00:00
(
"nochatter",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor not chatter."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_TurnSpeed
2015-04-28 20:02:03 +00:00
(
"turnspeed",
EV_DEFAULT,
"f",
"turnspeed",
"Sets the actor's turnspeed."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetActorFlag
2015-04-28 20:02:03 +00:00
(
"setactorflag",
EV_DEFAULT,
"sB",
"flag_name flag_bool",
"Sets an Actor's flag"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetNotifyFlag
2015-04-28 20:02:03 +00:00
(
"setnotifyflag",
EV_DEFAULT,
"sB",
"flag_name flag_bool",
"Sets an Actor's Notify Flag"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetVar
2015-04-28 20:02:03 +00:00
(
"setvar",
EV_DEFAULT,
"ss",
"var_name var_value",
"Sets a variable"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PersistData
2015-04-28 20:02:03 +00:00
(
"persistData",
EV_CODEONLY,
"ss",
"var_name var_value",
"Sets a persistant variable"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetVarTime
2015-04-28 20:02:03 +00:00
(
"setvartime",
EV_CODEONLY,
"s",
"var_name",
"Sets the variable name to the current level time"
);
2012-12-30 16:37:54 +00:00
Event EV_Anim_Done
2015-04-28 20:02:03 +00:00
(
"anim_done",
EV_CODEONLY,
nullptr,
nullptr,
"Called when the actor's animation is done, "
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Torso_Anim_Done
2015-04-28 20:02:03 +00:00
(
"torso_anim_done",
EV_CODEONLY,
nullptr,
nullptr,
"Called when actor's torso anim is done, "
"If you call this from script, I will hunt you down, and end you"
);
2012-12-30 16:37:54 +00:00
Event EV_Posture_Anim_Done
2015-04-28 20:02:03 +00:00
(
"posture_anim_done",
EV_CODEONLY,
nullptr,
nullptr,
"Called when a posture animation is done"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ProjAttack
2015-04-28 20:02:03 +00:00
(
"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."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_BulletAttack
2015-04-28 20:02:03 +00:00
(
"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."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_RadiusAttack
2015-04-28 20:02:03 +00:00
(
"radiusattack",
EV_DEFAULT,
"ssfffb",
"tag_name means_of_death damage radius knockback constant_damage",
"Does a radius attack from the tag name"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Active
2015-04-28 20:02:03 +00:00
(
"active",
EV_SCRIPTONLY,
"i",
"active_flag",
"Specifies whether the actor's is active or not."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SpawnGib
2015-04-28 20:02:03 +00:00
(
"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."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SpawnGibAtTag
2015-04-28 20:02:03 +00:00
(
"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."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SpawnNamedGib
2015-04-28 20:02:03 +00:00
(
"spawnnamedgib",
EV_DEFAULT,
"ssff",
"gib_name tag_name final_pitch width",
"Spawns a body named gib."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SpawnBlood
2015-04-28 20:02:03 +00:00
(
"spawnblood",
EV_DEFAULT,
"ssB",
"blood_name tag_name use_last_spawn_result",
"Spawns blood at the specified tag."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AIOn
2015-04-28 20:02:03 +00:00
(
"ai_on",
EV_SCRIPTONLY,
nullptr,
nullptr,
"Turns the AI on for this actor."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AIOff
2015-04-28 20:02:03 +00:00
(
"ai_off",
EV_SCRIPTONLY,
nullptr,
nullptr,
"Turns the AI off for this actor."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_RespondTo
2015-04-28 20:02:03 +00:00
(
"respondto",
EV_DEFAULT,
"sb",
"stimuli respond",
"sets AI response to stimuli"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PermanentlyRespondTo
2015-04-28 20:02:03 +00:00
(
"permanentrespondto",
EV_TIKIONLY,
"sb",
"stimuli respond",
"sets AI response to stimuli"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetIdleThread
2015-04-28 20:02:03 +00:00
(
"setidlethread",
EV_SCRIPTONLY,
"s",
"thread",
"Sets the thread that will be run if this actor gets back to the idle state again."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMaxInactiveTime
2015-04-28 20:02:03 +00:00
(
"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."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorRegisterParts
2015-04-28 20:02:03 +00:00
(
"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."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorRegisterSelf
2015-04-28 20:02:03 +00:00
(
"register_self",
EV_CODEONLY,
nullptr,
nullptr,
"Starts registration process for multi-entity actors"
);
2012-12-30 16:37:54 +00:00
Event EV_ActorName
2015-04-28 20:02:03 +00:00
(
"name",
EV_DEFAULT,
"s",
"name",
"Specifies the name of this actor type."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorPartName
2015-04-28 20:02:03 +00:00
(
"part_name",
EV_TIKIONLY,
"s",
"part_name",
"Specifies the name of this part (implying that this is a multi-part creature."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorSetupTriggerField
2015-04-28 20:02:03 +00:00
(
"trigger_field",
EV_TIKIONLY,
"vv",
"min max",
"Specifies to create a trigger field around the actor of the specified size."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorTriggerTouched
2015-04-28 20:02:03 +00:00
(
"trigger_touched",
EV_CODEONLY,
"e",
"ent",
"Notifies the actor that its trigger field has been touched."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorIncomingProjectile
2015-04-28 20:02:03 +00:00
(
"incoming_proj",
EV_CODEONLY,
"e",
"ent",
"Notifies the actor of an incoming projectile."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorSpawnActor
2015-04-28 20:02:03 +00:00
(
"spawnactor",
EV_DEFAULT,
"ssibffFBF",
"model_name tag_name how_many attack width height spawn_offset force add_height",
"Spawns the specified number of actors."
);
2012-12-30 16:37:54 +00:00
Event EV_ActorSpawnActorAboveEnemy
2015-04-28 20:02:03 +00:00
(
"spawnactoraboveenemy",
EV_DEFAULT,
"sibfff",
"model_name how_many attack width height how_far",
"Spawns actors above current enemy"
);
2012-12-30 16:37:54 +00:00
Event EV_ActorSpawnActorAtLocation
2015-04-28 20:02:03 +00:00
(
"spawnactoratlocation",
EV_DEFAULT,
"ssibff",
"model_name pathnode_name how_many_path_nodes attack width height",
"Spawns the specified actor at the specified pathnode."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AddDialog
2015-04-28 20:02:03 +00:00
(
"dialog",
EV_DEFAULT,
"sSSSSSS",
"alias token1 token2 token3 token4 token5 token6",
"Add a dialog to this sentient."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DialogDone
2015-04-28 20:02:03 +00:00
(
"dialogdone",
EV_CODEONLY,
nullptr,
nullptr,
"Called when the sentient's dialog is done, "
"it is not meant to be called from script."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PlayDialog
2015-04-28 20:02:03 +00:00
(
"playdialog",
EV_DEFAULT,
"SFFBBSE",
"sound_file volume min_dist headDisplay do_talk state_name user",
"Plays a dialog."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_StopDialog
2015-04-28 20:02:03 +00:00
(
"stopdialog",
EV_SCRIPTONLY,
nullptr,
nullptr,
"Stops the actor's dialog."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_BroadcastDialog
2015-04-28 20:02:03 +00:00
(
"broadcastdialog",
EV_CODEONLY,
"s",
"context",
"Broadcasts a context dialog"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_BranchDialog
2015-04-28 20:02:03 +00:00
(
"branchdialog",
EV_DEFAULT,
"s",
"dialogName",
"Presents a branch dialog to the player."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AllowTalk
2015-04-28 20:02:03 +00:00
(
"allowtalk",
EV_DEFAULT,
"i",
"allow_bool",
"Sets whether or not the actor will bother to talk to the player."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AllowHangBack
2015-04-28 20:02:03 +00:00
(
"allowhangback",
EV_DEFAULT,
"i",
"allow_bool",
"Sets whether or not the actor will bother to hang back."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SolidMask
2015-04-28 20:02:03 +00:00
(
"solidmask",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor use a solid mask."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NotSolidMask
2015-04-28 20:02:03 +00:00
(
"notsolidmask",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor use a nonsolid mask."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NoMask
2015-04-28 20:02:03 +00:00
(
"nomask",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor use a mask of 0."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMask
2015-04-28 20:02:03 +00:00
(
"setmask",
EV_DEFAULT,
"s",
"mask_name",
"Sets the actor's mask to the specified mask."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Pickup
2015-04-28 20:02:03 +00:00
(
"actor_pickup",
EV_TIKIONLY,
"s",
"tag_name",
"Makes the actor pickup current pickup_ent (should only be called from a tiki)."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Throw
2015-04-28 20:02:03 +00:00
(
"actor_throw",
EV_TIKIONLY,
"s",
"tag_name",
"Makes the actor throw whatever is in its hand (should only be called from a tiki)."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DamageOnceStart
2015-04-28 20:02:03 +00:00
(
"damage_once_start",
EV_TIKIONLY,
nullptr,
nullptr,
"Makes the actor only do melee damage at most once during this attack."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DamageOnceStop
2015-04-28 20:02:03 +00:00
(
"damage_once_stop",
EV_TIKIONLY,
nullptr,
nullptr,
"Specifies that the actor is done with the damage once event."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DamageEnemy
2015-04-28 20:02:03 +00:00
(
"damageenemy",
EV_DEFAULT,
"fS",
"damage model",
"Damages the current enemy by the specified amount."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DamageSelf
2015-04-28 20:02:03 +00:00
(
"damageself",
EV_DEFAULT,
"fs",
"damage means_of_death",
"Damages Self"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_TurnTowardsEnemy
2015-04-28 20:02:03 +00:00
(
"turntowardsenemy",
EV_SCRIPTONLY,
"f",
"angle",
"Turns the actor towards the current enemy."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_TurnTowardsPlayer
2015-04-28 20:02:03 +00:00
(
"turntowardsplayer",
EV_SCRIPTONLY,
nullptr,
nullptr,
"Turns the actor towards the player."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_TurnTowardsEntity
2015-04-28 20:02:03 +00:00
(
"turntowardsentity",
EV_SCRIPTONLY,
"e",
"entity",
"Turns the actor towards the entity"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Suicide
2015-04-28 20:02:03 +00:00
(
"suicide",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor commit suicide."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_GotoNextStage
2015-04-28 20:02:03 +00:00
(
"gotonextstage",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor goto his next stage."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_GotoPrevStage
2015-04-28 20:02:03 +00:00
(
"gotoprevstage",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor goto his previous stage."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_GotoStage
2015-04-28 20:02:03 +00:00
(
"gotostage",
EV_DEFAULT,
"i",
"stage_number",
"Makes the actor goto the specified stage."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_GetStage
2015-04-28 20:02:03 +00:00
(
"getstage",
EV_SCRIPTONLY,
"@f",
"Result",
"Returns this actors current stage."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NotifyOthersAtDeath
2015-04-28 20:02:03 +00:00
(
"notifyothersatdeath",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor notify other actors of the same type when killed."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetBounceOff
2015-04-28 20:02:03 +00:00
(
"bounceoff",
EV_DEFAULT,
nullptr,
nullptr,
"Makes projectiles bounce off of actor (if they can't damage actor)."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetHaveThing
2015-04-28 20:02:03 +00:00
(
"havething",
EV_DEFAULT,
"ib",
"thing_number have_bool",
"Sets whether or not the actor has this thing number."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetUseGravity
2015-04-28 20:02:03 +00:00
(
"usegravity",
EV_DEFAULT,
"b",
"use_gravity",
"Tells the actor whether or not to use gravity for this animation."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetDeathSize
2015-04-28 20:02:03 +00:00
(
"deathsize",
EV_TIKIONLY,
"vv",
"min max",
"Sets the actors new size for death."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Fade
2015-04-28 20:02:03 +00:00
(
"actorfade",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor fade out."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AttackMode
2015-04-28 20:02:03 +00:00
(
"attackmode",
EV_SCRIPTONLY,
"b",
"attack_bool",
"Makes the actor go directly into attacking the player if bool is true."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_BounceOff
2015-04-28 20:02:03 +00:00
(
"bounceoffevent",
EV_CODEONLY,
"v",
"object_origin",
"Lets the actor know something just bounces off of it."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetBounceOffEffect
2015-04-28 20:02:03 +00:00
(
"bounceoffeffect",
EV_DEFAULT,
"s",
"bounce_off_effect_name",
"Sets the name of the effect to play when something bounces off the actor."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AddSpawnItem
2015-04-28 20:02:03 +00:00
(
"spawnitem",
EV_DEFAULT,
"s",
"spawn_item_name",
"Adds this names item to what will be spawned when this actor is killed."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetSpawnChance
2015-04-28 20:02:03 +00:00
(
"spawnchance",
EV_DEFAULT,
"f",
"spawn_chance",
"Sets the chance that this actor will spawn something when killed."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ClearSpawnItems
2015-04-28 20:02:03 +00:00
(
"clearspawnitems",
EV_DEFAULT,
nullptr,
nullptr,
"Clears the list of items to spawn when this actor is killed."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetAllowFall
2015-04-28 20:02:03 +00:00
(
"allowfall",
EV_DEFAULT,
"B",
"allow_fall_bool",
"Makes the actor ignore falls when trying to move."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetCanBeFinishedBy
2015-04-28 20:02:03 +00:00
(
"canbefinishedby",
EV_TIKIONLY,
"sSSSSS",
"mod1 mod2 mod3 mod4 mod5 mod6",
"Adds to the can be finished by list for this actor."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetFeetWidth
2015-04-28 20:02:03 +00:00
(
"feetwidth",
EV_TIKIONLY,
"f",
"feet_width",
"Sets the width of the feet for this actor if different than the bounding box size."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetCanWalkOnOthers
2015-04-28 20:02:03 +00:00
(
"canwalkonothers",
EV_DEFAULT,
nullptr,
nullptr,
"Allows the actor to walk on top of others."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Push
2015-04-28 20:02:03 +00:00
(
"push",
EV_CODEONLY,
"v",
"dir",
"Pushes the actor in the specified direction."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Pushable
2015-04-28 20:02:03 +00:00
(
"pushable",
EV_DEFAULT,
"B",
"flag",
"Sets whether or not an actor can be pushed out of the way."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ChargeWater
2015-04-28 20:02:03 +00:00
(
"chargewater",
EV_DEFAULT,
"ff",
"damage range",
"Does a charge water attack."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SendCommand
2015-04-28 20:02:03 +00:00
(
"sendcommand",
EV_CODEONLY,
"ss",
"command part_name",
"Sends a command to another one of its parts."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetTargetable
2015-04-28 20:02:03 +00:00
(
"targetable",
EV_DEFAULT,
"b",
"should_target",
"Sets whether or not this actor should be targetable by the player."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ChangeType
2015-04-28 20:02:03 +00:00
(
"changetype",
EV_DEFAULT,
"s",
"new_model_name",
"Changes the actor to the specified new type of actor."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IgnoreMonsterClip
2015-04-28 20:02:03 +00:00
(
"ignoremonsterclip",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor ignore monster clip brushes."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_MinimumMeleeHeight
2015-04-28 20:02:03 +00:00
(
"minmeleeheight",
EV_DEFAULT,
"f",
"minimum_height",
"Sets the minimum height a melee attack has to be to hurt the actor."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetDamageAngles
2015-04-28 20:02:03 +00:00
(
"damageangles",
EV_DEFAULT,
"f",
"damage_angles",
"Sets the the angles where the actor can be hurt (like fov)."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Immortal
2015-04-28 20:02:03 +00:00
(
"immortal",
EV_DEFAULT,
"b",
"immortal_bool",
"Sets whether or not the actor is immortal or not."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetDieCompletely
2015-04-28 20:02:03 +00:00
(
"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)."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetBleedAfterDeath
2015-04-28 20:02:03 +00:00
(
"bleed_after_death",
EV_DEFAULT,
"b",
"bleed_bool",
"Sets whether or not the actor will bleed after dying."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IgnorePlacementWarning
2015-04-28 20:02:03 +00:00
(
"ignore_placement_warning",
EV_DEFAULT,
"s",
"warning_string",
"Makes the specified placement warning not get printed for this actor."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetIdleStateName
2015-04-28 20:02:03 +00:00
(
"set_idle_state_name",
EV_SCRIPTONLY,
"s",
"new_idle_state_name",
"Sets the actor's new idle state name."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetNotAllowedToKill
2015-04-28 20:02:03 +00:00
(
"not_allowed_to_kill",
EV_DEFAULT,
nullptr,
nullptr,
"Player fails the level if he kills an actor with this set."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IgnoreWater
2015-04-28 20:02:03 +00:00
(
"ignorewater",
EV_DEFAULT,
"b",
"ignore_water_bool",
"Sets whether or not this actor will ignore water when moving."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SimplePathfinding
2015-04-28 20:02:03 +00:00
(
"simplepathfinding",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor use simplier path finding."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_NoPainSounds
2015-04-28 20:02:03 +00:00
(
"nopainsounds",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor not broadcast sounds (AI stimuli) when taking pain or killed."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_BroadcastAlert
2015-04-28 20:02:03 +00:00
(
"alertevent",
EV_DEFAULT,
"F",
"soundRadius",
"Alerts Entities within the radius of the enemy's location."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_UpdateBossHealth
2015-04-28 20:02:03 +00:00
(
"updatebosshealth",
EV_DEFAULT,
"BB",
"updateFlag forceOn",
"Tells the actor to update the bosshealth cvar each time it thinks."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMaxBossHealth
2015-04-28 20:02:03 +00:00
(
"maxbosshealth",
EV_DEFAULT,
"f",
"max_boss_health",
"Sets the actor's max boss health."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_IgnorePainFromActors
2015-04-28 20:02:03 +00:00
(
"ignorepainfromactors",
EV_DEFAULT,
nullptr,
nullptr,
"Makes this actor ignore pain from other actors."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DamageAllowed
2015-04-28 20:02:03 +00:00
(
"damageallowed",
EV_DEFAULT,
"b",
"damage_allowed",
"Turns melee damage on and off."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetEmotion
2015-04-28 20:02:03 +00:00
(
"emotion",
EV_DEFAULT,
"S",
"expression_name",
"Sets the actors current emotion."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ReturnProjectile
2015-04-28 20:02:03 +00:00
(
"returnproj",
EV_TIKIONLY,
nullptr,
nullptr,
"Returns a projectile to the current enemy"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetRadiusDialogRange
2015-04-28 20:02:03 +00:00
(
"setradiusdialogrange",
EV_DEFAULT,
"f",
"range",
"Sets the range for playing radius dialog"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetDialogMode
2015-04-28 20:02:03 +00:00
(
"setdialogmode",
EV_DEFAULT,
"s",
"mode_type",
"Sets the Dialog Mode for the actor, valid values are 'anxious', 'normal', or 'ignore'"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DialogAnimDone
2015-04-28 20:02:03 +00:00
(
"dialoganimdone",
EV_CODEONLY,
nullptr,
nullptr,
"Not meant to be called from script -- So DONT FREAKIN DO IT!!!!"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetEyeAngleConstraints
2015-04-28 20:02:03 +00:00
(
"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"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetActivateThread
2015-04-28 20:02:03 +00:00
(
"setactivatethread",
EV_SCRIPTONLY,
"s",
"thread_name",
"Sets the thread to call when the AI Activates"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetValidTarget
2015-04-28 20:02:03 +00:00
(
"setvalidtarget",
EV_DEFAULT,
"b",
"valid_target",
"Sets whether or not actor is valid target in actor to actor confrontations"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetAlertThread
2015-04-28 20:02:03 +00:00
(
"setalertthread",
EV_SCRIPTONLY,
"s",
"thread_name",
"sets a thread to be called when AI goes to alert"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_RunAlertThread
2015-04-28 20:02:03 +00:00
(
"runalertthread",
EV_CODEONLY,
nullptr,
nullptr,
"runs an actors alert thread - NOT MEANT TO BE CALLED FROM SCRIPT"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_CheckActorDead
2015-04-28 20:02:03 +00:00
(
"checkactordead",
EV_SCRIPTONLY,
"@ie",
"dead_bool entity_to_check",
"checks if an actor is dead"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EnemyActorFlag
2015-04-28 20:02:03 +00:00
(
"setenemyactorflag",
EV_SCRIPTONLY,
"sB",
"flag_name flag_bool",
"Sets an Actor's flag"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EnemyAIOn
2015-04-28 20:02:03 +00:00
(
"enemyaion",
EV_CODEONLY,
nullptr,
nullptr,
"turns on the current enemy AI"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EnemyAIOff
2015-04-28 20:02:03 +00:00
(
"enemyaioff",
EV_CODEONLY,
nullptr,
nullptr,
"turns off the current enemy AI"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AttachCurrentEnemy
2015-04-28 20:02:03 +00:00
(
"attachcurrentenemy",
EV_CODEONLY,
"s",
"bone",
"attach current enemy to the given bone"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AttachActor
2015-04-28 20:02:03 +00:00
(
"attachactor",
EV_DEFAULT,
"sss",
"model targetname bone",
"attach actor to the given bone"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetEnemyAttached
2015-04-28 20:02:03 +00:00
(
"setenemyattached",
EV_DEFAULT,
"b",
"attached",
"sets whether or not the current enemy is attached -- Quetzal Specific"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PickUpThrowObject
2015-04-28 20:02:03 +00:00
(
"pickupthrowobject",
EV_SCRIPTONLY,
"s",
"bone",
"bone to attach object to"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_TossThrowObject
2015-04-28 20:02:03 +00:00
(
"tossthrowobject",
EV_DEFAULT,
"ff",
"speed gravity",
"throws a throw object"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetTurretMode
2015-04-28 20:02:03 +00:00
(
"setturretmode",
EV_DEFAULT,
"b",
"on_off",
"sets turret mode on or off"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetHitscanResponseChance
2015-04-28 20:02:03 +00:00
(
"sethitscanresponse",
EV_DEFAULT,
"f",
"chance",
"sets chance an actor will respond to hitscan attacks"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetWeaponReady
2015-04-28 20:02:03 +00:00
(
"setweaponready",
EV_DEFAULT,
"b",
"ready",
"sets if the actor has its weapon ready or not"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetOnDamageThread
2015-04-28 20:02:03 +00:00
(
"actorondamage",
EV_SCRIPTONLY,
"sI",
"thread_name damage_threshold",
"sets the thread that is called when actor is damaged"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetTimeBetweenSleepChecks
2015-04-28 20:02:03 +00:00
(
"timebetweensleepchecks",
EV_SCRIPTONLY,
"f",
"delay",
"sets the time between tests to see if the actor should sleep"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetAimLeadFactors
2015-04-28 20:02:03 +00:00
(
"setaimleadfactors",
EV_DEFAULT,
"ff",
"minLeadFactor maxLeadFactor",
"sets the lead factor for projectile aiming; 0 = don't lead, 1 = perfect lead"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetActorType
2015-04-28 20:02:03 +00:00
(
"actortype",
EV_DEFAULT,
"s",
"actor_type",
"sets the actortype"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_RegisterBehaviorPackage
2015-04-28 20:02:03 +00:00
(
"registerpackage",
EV_TIKIONLY,
"s",
"package_name",
"registers a behavior package"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_UnregisterBehaviorPackage
2015-04-28 20:02:03 +00:00
(
"unregisterpackage",
EV_TIKIONLY,
"s",
"package_name",
"unregisters a behavior package"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetBehaviorPackageTendency
2015-04-28 20:02:03 +00:00
(
"setpackagetendency",
EV_DEFAULT,
"sf",
"package_name tendency",
"sets the tendency to execute the behavior package"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetAbsoluteMaxRange
2015-04-28 20:02:03 +00:00
(
"setabsolutemaxrange",
EV_DEFAULT,
"f",
"absolute_max_range",
"sets the absolute maximum range the actor will get from an entity"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetAbsoluteMinRange
2015-04-28 20:02:03 +00:00
(
"setabsoluteminrange",
EV_DEFAULT,
"f",
"absolute_min_range",
"sets the absolute minimum range the actor will get from an entity"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetPreferredMaxRange
2015-04-28 20:02:03 +00:00
(
"setpreferredmaxrange",
EV_DEFAULT,
"f",
"preferred_max_range",
"sets the preferred maximum range the actor would like to be from an entity"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetPreferredMinRange
2015-04-28 20:02:03 +00:00
(
"setpreferredminrange",
EV_DEFAULT,
"f",
"preferred_min_range",
"sets the preferred minimum range the actor would like to be from an entity"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DebugStates
2015-04-28 20:02:03 +00:00
(
"debugstates",
EV_CODEONLY,
"i",
"debug_state",
"sets debug level for actor statemachine"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetHeadWatchTarget
2015-04-28 20:02:03 +00:00
(
"headwatchtarget",
EV_SCRIPTONLY,
"sf",
"target speed",
"sets the headwatch target... currently to enemy or none"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetHeadTwitch
2015-04-28 20:02:03 +00:00
(
"headTwitch",
EV_SCRIPTONLY,
"b",
"bool",
"Sets whether or not the head should twitch."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetFuzzyEngineActive
2015-04-28 20:02:03 +00:00
(
"fuzzyengineactive",
EV_DEFAULT,
"b",
"active",
"sets the fuzzy engine active or not"
);
2012-12-30 16:37:54 +00:00
//
// Waypoint stuff
//
Event EV_Actor_FollowWayPoints
2015-04-28 20:02:03 +00:00
(
"followwaypoints",
EV_SCRIPTONLY,
"sS",
"waypointnode_name starting_anim_name",
"Makes an actor follow a waypoint path starting at , starting at the start_point"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Disable
2015-04-28 20:02:03 +00:00
(
"disable",
EV_DEFAULT,
"i",
"disable_flag",
"disable actor ( 1 ) or not disable actor ( 0 )"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Cripple
2015-04-28 20:02:03 +00:00
(
"cripple",
EV_DEFAULT,
"i",
"cripple_flag",
"cripple actor ( 1 ) or not cripple actor ( 0 )"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_In_Alcove
2015-04-28 20:02:03 +00:00
(
"in_alcove",
EV_DEFAULT,
"i",
"in_alcove_flat",
"in alcove ( 1 ) or not in alcove ( 0 )"
);
2012-12-30 16:37:54 +00:00
//
// Weapon stuff
//
Event EV_Actor_GiveActorWeapon
2015-04-28 20:02:03 +00:00
(
"giveactorweapon",
EV_DEFAULT,
"sI",
"weapon amount",
"Gives a weapon to an actor"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_RemoveActorWeapon
2015-04-28 20:02:03 +00:00
(
"removeactorweapon",
EV_DEFAULT,
"s",
"weapon",
"removes an actors weapon"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_UseWeapon
2015-04-28 20:02:03 +00:00
(
"useactorweapon",
EV_TIKIONLY,
"sS",
"weapon hand",
"Makes the specified weapon active for the actor \n"
"If they have the weapon"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMovementMode
2015-04-28 20:02:03 +00:00
(
"movementmode",
EV_DEFAULT,
"s",
"movment_mode",
"sets the movment mode of the actor"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ResetMoveDir
2015-04-28 20:02:03 +00:00
(
"resetmovedir",
EV_CODEONLY,
nullptr,
nullptr,
"Resets and resyncs movedir with animdir"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetNodeID
2015-04-28 20:02:03 +00:00
(
"setnodeid",
EV_SCRIPTONLY,
"i",
"id_number",
"Sets the ID number of the helper nodes that this actor can use"
);
2012-12-30 16:37:54 +00:00
//
// Personality Stuff
//
Event EV_Actor_SetAggressiveness
2015-04-28 20:02:03 +00:00
(
"aggressive",
EV_DEFAULT,
"f",
"aggressiveness",
"sets the aggressiveness of the actor... valid range between 0 and 1"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetTalkiness
2015-04-28 20:02:03 +00:00
(
"talkiness",
EV_DEFAULT,
"f",
"talkiness",
"sets the talkiness of the actor... valid range between 0 and 1"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetTendency
2015-04-28 20:02:03 +00:00
(
"settendency",
EV_DEFAULT,
"sf",
"name value",
"Sets a tendency for the actor"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetFloatProperty
2015-04-28 20:02:03 +00:00
(
"setfloatproperty",
EV_DEFAULT,
"sf",
"name value",
"Sets a float property on the actor"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetGroupNumber
2015-04-28 20:02:03 +00:00
(
"groupnumber",
EV_DEFAULT,
"i",
"group_number",
"sets the group number of the actor"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_Blink
2015-04-28 20:02:03 +00:00
(
"blink",
EV_DEFAULT,
"b",
"shouldBlink",
"Sets whether or not the actor should blink"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ClearArmorAdapations
2015-04-28 20:02:03 +00:00
(
"cleararmoradaptions",
EV_SCRIPTONLY,
nullptr,
nullptr,
"clears armor adaptions"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetFollowTarget
2015-04-28 20:02:03 +00:00
(
"followtarget",
EV_SCRIPTONLY,
"e",
"entity_to_follow",
"Sets the following target for the actor"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetFollowRange
2015-04-28 20:02:03 +00:00
(
"followrange",
EV_SCRIPTONLY,
"f",
"maxRange",
"Sets a range that the target considers _close enough_ while following"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetFollowRangeMin
2015-04-28 20:02:03 +00:00
(
"followrangemin",
EV_SCRIPTONLY,
"f",
"minRange",
"Sets a minimum range for following"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetCombatFollowRange
2015-04-28 20:02:03 +00:00
(
"followcombatrange",
EV_SCRIPTONLY,
"f",
"maxRange",
"Sets a range that the target considers _close enough_ while following in a combat situation"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetCombatFollowRangeMin
2015-04-28 20:02:03 +00:00
(
"followcombatrangemin",
EV_SCRIPTONLY,
"f",
"minRange",
"Sets a minimum range for following in a combat situation"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetSteeringDirectionPreference
2015-04-28 20:02:03 +00:00
(
"steeringdirectionpreference",
EV_SCRIPTONLY,
"s",
"preference",
"Sends a string to define the way actors will turn when avoiding obstacles"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetStickToGround
2015-04-28 20:02:03 +00:00
(
"setsticktoground",
EV_DEFAULT,
"b",
"stick",
"Sets a bool that determines whether the actor ground follows"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetDialogMorphMult
2015-04-28 20:02:03 +00:00
(
"dialogMorphMult",
EV_DEFAULT,
"f",
"dialogMorphMult",
"Sets the multiplier for all dialog morphs for this actor."
);
2012-12-30 16:37:54 +00:00
// Context Dialog Context Events
Event EV_ContextDialog_InContext
2015-04-28 20:02:03 +00:00
(
"incontext",
EV_CODEONLY,
"s",
"context",
"Used to start a context dialog"
);
2012-12-30 16:37:54 +00:00
Event EV_ContextDialog_IgnoreNextContext
2015-04-28 20:02:03 +00:00
(
"ignorenextcontext",
EV_CODEONLY,
"bs",
"flag context",
"Makes the actor ignore the next context event it receives"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ForceSetClip
2015-04-28 20:02:03 +00:00
(
"forcesetclip",
EV_CODEONLY,
nullptr,
nullptr,
"Makes the actor set his contents to setclip"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_WhatAreYouDoing
2015-04-28 20:02:03 +00:00
(
"whatareyoudoing",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor print a bunch of debug state info to the console"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_WhatsWrong
2015-04-28 20:02:03 +00:00
(
"whatswrong",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor print the current behaviors failure reaon to the console"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PutawayWeapon
2015-04-28 20:02:03 +00:00
(
"putawayweapon",
EV_DEFAULT,
"S",
"hand",
"Deactivate the weapon in the specified hand."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetCombatTraceInterval
2015-04-28 20:02:03 +00:00
(
"combattraceinterval",
EV_DEFAULT,
"f",
"interval",
"Determines how often an actor will re-trace when doing can-attack types of checks"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_UseWeaponDamage
2015-04-28 20:02:03 +00:00
(
"useweapondamage",
EV_TIKIONLY,
"SB",
"hand setflag",
"Makes the melee event reference the damage of the weapon in the specified hand."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_StrictlyFollowPath
2015-04-28 20:02:03 +00:00
(
"strictlyfollowpath",
EV_DEFAULT,
"b",
"boolean",
"Lets the actor know if he should follow paths exactly or if he can go directly to his goal."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_EvaluateEnemies
2015-04-28 20:02:03 +00:00
(
"evaluateenemies",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor evaluate his enemy list"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ForgetEnemies
2015-04-28 20:02:03 +00:00
(
"forgetenemies",
EV_DEFAULT,
nullptr,
nullptr,
"Makes the actor forget about all enemies for one frame"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMaxHeadYaw
2015-04-28 20:02:03 +00:00
(
"maxheadyaw",
EV_DEFAULT,
"f",
"maxyaw",
"Sets the max yaw the headwatcher can turn the head"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMaxHeadPitch
2015-04-28 20:02:03 +00:00
(
"maxheadpitch",
EV_DEFAULT,
"f",
"maxpitch",
"Sets the max pitch the headwatcher can turn the head"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetPostureStateMap
2015-04-28 20:02:03 +00:00
(
"posturestatemap",
EV_DEFAULT,
"sB",
"statemap loadingFlag",
"Sets the state machine for the posture controller"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SendEventToGroup
2015-04-28 20:02:03 +00:00
(
"sendeventtogroup",
EV_DEFAULT,
"sSSSSS",
"event parm parm parm parm parm",
"sends the specified event to the entire group"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_GroupAttack
2015-04-28 20:02:03 +00:00
(
"groupattack",
EV_DEFAULT,
nullptr,
nullptr,
"Sends and attack event to the whole group with this actors current enemy"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_GroupActorType
2015-04-28 20:02:03 +00:00
(
"groupactortype",
EV_DEFAULT,
"s",
"actortype",
"Sends an actortype event to the whole group"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMasterState
2015-04-28 20:02:03 +00:00
(
"setmasterstate",
EV_DEFAULT,
"s",
"state_name",
"Sets the master state"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PrintDebugMessage
2015-04-28 20:02:03 +00:00
(
"printmessage",
EV_DEFAULT,
"s",
"message",
"Prints a warning message to the console"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SelectNextEnemy
2015-04-28 20:02:03 +00:00
(
"SelectNextEnemy",
EV_DEFAULT,
nullptr,
nullptr,
"Sets the actor's current enemy to be the next enemy in it's hate list, assuming there is one."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SelectClosestEnemy
2015-04-28 20:02:03 +00:00
(
"selectclosestenemy",
EV_DEFAULT,
nullptr,
nullptr,
"Sets the actor's current enemy to be the closest enemy in it's hate list "
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetGroupDeathThread
2015-04-28 20:02:03 +00:00
(
"groupdeaththread",
EV_DEFAULT,
"s",
"thread_name",
"Sets a thread to call when all the members of a group have been killed"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetAnimSet
2015-04-28 20:02:03 +00:00
(
"useanimset",
EV_DEFAULT,
"s",
"anim_set",
"Sets the AnimSet... Valid Set Names are AnimSet1 , AnimSet2 "
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetPostureState
2015-04-28 20:02:03 +00:00
(
"setposturestate",
EV_DEFAULT,
"ss",
"currentState requestedState",
"Sets the Posture State"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetTalkWatchMode
2015-04-28 20:02:03 +00:00
(
"settalkwatchmode",
EV_DEFAULT,
"sB",
"mode useConvAnim",
"Sets the talk watch mode -- valid entries are turnto, headwatchonly, and ignore"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_PrepareMissionFailure
2015-04-28 20:02:03 +00:00
(
"failmission",
EV_DEFAULT,
"fS",
"time reason",
"Fails the mission in the time specified"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_FailMission
2015-04-28 20:02:03 +00:00
(
"domissionfailure",
EV_DEFAULT,
"S",
"reason",
"Fails the mission"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_DebugEvent
2015-04-28 20:02:03 +00:00
(
"debugevent",
EV_DEFAULT,
nullptr,
nullptr,
"Called for Debug Purposes from state machine"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_UnreserveCurrentHelperNode
2015-04-28 20:02:03 +00:00
(
"unreservecurrenthelpernode",
EV_CODEONLY,
nullptr,
nullptr,
"Unreserves the current helper node"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ProjectileClose
2015-04-28 20:02:03 +00:00
(
"projectileclose",
EV_CODEONLY,
"e",
"owener",
"Informs Actor that a projectile is close"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SaveOffLastHitBone
2015-04-28 20:02:03 +00:00
(
"saveofflasthitbone",
EV_CODEONLY,
nullptr,
nullptr,
"Saves off the last hit_bone"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_ClearTorsoAnim
2015-04-28 20:02:03 +00:00
(
"cleartorsoanim",
EV_CODEONLY,
nullptr,
nullptr,
"clears the f'ng torso anim"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetPlayPainSoundInterval
2015-04-28 20:02:03 +00:00
(
"setplaypainsoundinterval",
EV_DEFAULT,
"f",
"interval",
"Sets the pain sound interval"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetContextSoundInterval
2015-04-28 20:02:03 +00:00
(
"setcontextsoundinterval",
EV_DEFAULT,
"f",
"interval",
"Sets the context sound ( dialog ) interval "
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetMinPainTime
2015-04-28 20:02:03 +00:00
(
"setminpaintime",
EV_DEFAULT,
"f",
"time",
"Sets the minimum pain time"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetEnemyTargeted
2015-04-28 20:02:03 +00:00
(
"setenemytargeted",
EV_DEFAULT,
"b",
"targeted",
"Sets whether or not the enemy should display targeted shader"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetActivationDelay
2015-04-28 20:02:03 +00:00
(
"setactivationdelay",
EV_DEFAULT,
"f",
"delay",
"If set up to use it, actors will delay action for the specifed delay time"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetActivationStart
2015-04-28 20:02:03 +00:00
(
"startactivationtimer",
EV_DEFAULT,
nullptr,
nullptr,
"Sets the activationStart time to the current level time"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetCheckConeOfFireDistance
2015-04-28 20:02:03 +00:00
(
"SetCheckConeOfFireDistance",
EV_DEFAULT,
"f",
"distance",
"Sets how close an actor must be for IN_CONE_OF_FIRE checks from a state machine."
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AddCustomThread
2015-04-28 20:02:03 +00:00
(
"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"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetHeadWatchMaxDistance
2015-04-28 20:02:03 +00:00
(
"setheadwatchmaxdistance",
EV_DEFAULT,
"f",
"maxdistance",
"Sets the max distance for the headwatcher"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_AnimateOnce
2015-04-28 20:02:03 +00:00
(
"animateonce",
EV_DEFAULT,
"s",
"anim_name",
"Runs the specified animation one time, then holds the last frame"
);
2012-12-30 16:37:54 +00:00
Event EV_Actor_SetDeathKnockbackValues
2015-04-28 20:02:03 +00:00
(
"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
{ nullptr, nullptr }
};
2012-12-30 16:37:54 +00:00
//===================================================================================
// Construction and Destruction
//==================================================================================
//
// Name: Actor()
// Parameters: None
// Description: Constructor
//
Actor::Actor()
2015-04-28 20:02:03 +00:00
{
Init();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Init()
{
Event* immunity_event;
forcedEnemy = nullptr;
//
// 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 = nullptr;
headBehavior = nullptr;
eyeBehavior = nullptr;
torsoBehavior = nullptr;
// Set up Statemap
statemap = nullptr;
currentState = nullptr;
globalState = nullptr;
lastState = nullptr;
state_time = level.time;
times_done = 0;
masterstatemap = nullptr;
currentMasterState = nullptr;
lastMasterState = nullptr;
masterstate_time = level.time;
masterstate_times_done = 0;
// Set up FuzzyEngine
fuzzyEngine = nullptr;
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 = nullptr;
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 |= FlagBlood;
flags |= FlagDieGibs;
}
// 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 = DamageAim;
deadflag = DeadNo;
2015-04-29 20:34:44 +00:00
mode = ActorModeAi; //ActorModeIdle;
2015-04-28 20:02:03 +00:00
max_inactive_time = MAX_INACTIVE_TIME;
newanimevent = nullptr;
newTorsoAnimEvent = nullptr;
groundentity = nullptr;
2015-04-29 20:34:44 +00:00
saved_mode = ActorModeNone;
2015-04-28 20:02:03 +00:00
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 = nullptr;
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 = nullptr;
currentHelperNode.mask = 0;
currentHelperNode.nodeID = 0;
ignoreHelperNode.node = nullptr;
ignoreHelperNode.mask = 0;
ignoreHelperNode.nodeID = 0;
// FollowTarget
followTarget.currentFollowTarget = nullptr;
followTarget.specifiedFollowTarget = nullptr;
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);
}
2012-12-30 16:37:54 +00:00
//
// Name: ~Actor()
// Parameters: None
// Description: Destructor
//
Actor::~Actor()
2015-04-28 20:02:03 +00:00
{
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--)
{
auto var = stateVarList.ObjectAt(i);
delete var;
}
stateVarList.FreeObjectList();
for (i = threadList.NumObjects(); i > 0; i--)
{
auto threadEntry = threadList.ObjectAt(i);
delete threadEntry;
}
threadList.FreeObjectList();
if (behavior)
{
delete behavior;
behavior = nullptr;
}
if (headBehavior)
{
delete headBehavior;
headBehavior = nullptr;
}
if (eyeBehavior)
{
delete eyeBehavior;
eyeBehavior = nullptr;
}
if (torsoBehavior)
{
delete torsoBehavior;
torsoBehavior = nullptr;
}
if (thinkStrategy)
{
delete thinkStrategy;
thinkStrategy = nullptr;
}
if (gameComponent)
{
delete gameComponent;
gameComponent = nullptr;
}
if (sensoryPerception)
{
delete sensoryPerception;
sensoryPerception = nullptr;
}
if (strategos)
{
delete strategos;
strategos = nullptr;
}
if (enemyManager)
{
delete enemyManager;
enemyManager = nullptr;
}
if (packageManager)
{
delete packageManager;
packageManager = nullptr;
}
if (movementSubsystem)
{
delete movementSubsystem;
movementSubsystem = nullptr;
}
if (personality)
{
delete personality;
personality = nullptr;
}
if (combatSubsystem)
{
delete combatSubsystem;
combatSubsystem = nullptr;
}
if (headWatcher)
{
delete headWatcher;
headWatcher = nullptr;
}
if (postureController)
{
delete postureController;
postureController = nullptr;
}
freeConditionals(conditionals);
freeConditionals(master_conditionals);
freeConditionals(fuzzy_conditionals);
FreeDialogList();
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// Activity Level
//===================================================================================
//
// Name: Sleep()
// Parameters: Event *ev
// Description: Event Interface for Sleep()
//
2015-04-28 20:02:03 +00:00
void Actor::Sleep(Event*)
{
Sleep();
}
2012-12-30 16:37:54 +00:00
//
// Name: Sleep()
// Parameters: None
// Description: Puts the actor in a sleep state
//
2015-04-28 20:02:03 +00:00
void Actor::Sleep(void)
{
Actor* actor;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// inanimate actors don't target enemies
if (actortype == IS_INANIMATE)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
actor = this;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!SleepList.ObjectInList(actor))
SleepList.AddObject(actor);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ActiveList.ObjectInList(actor))
ActiveList.RemoveObject(actor);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
enemyManager->ClearCurrentEnemy();
turnThinkOff();
}
2012-12-30 16:37:54 +00:00
//
// Name: Wakeup
// Parameters: Event *ev
// Description: Event interface for Wakeup()
//
2015-04-28 20:02:03 +00:00
void Actor::Wakeup(Event*)
{
Wakeup();
}
2012-12-30 16:37:54 +00:00
//
// Name: Wakeup()
// Parameters: None
// Description: Puts the actor in an awake state
//
2015-04-28 20:02:03 +00:00
void Actor::Wakeup(void)
{
Actor* actor;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if already awake
if (isThinkOn() && !LoadingSavegame)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// inanimate actors don't target enemies
if (actortype == IS_INANIMATE)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
actor = this;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (SleepList.ObjectInList(actor))
SleepList.RemoveObject(actor);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!ActiveList.ObjectInList(actor))
ActiveList.AddObject(actor);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
turnThinkOn();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// 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;
}
2012-12-30 16:37:54 +00:00
//
// Name: Start()
// Parameters: Event *ev
// Description: Handles some initialization of the Actor
//
2015-04-28 20:02:03 +00:00
void Actor::Start(Event*)
{
// Register with other parts of self if there are any
if (target.length() > 0)
PostEvent(EV_ActorRegisterSelf, FRAMETIME);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// add them to the active list (they will be removed by sleep).
//if ( !ActiveList.ObjectInList( ( Actor * )this ) )
// ActiveList.AddObject( ( Actor * )this );
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Align our movement and anim dirs
Vector animDir;
angles.AngleVectors(&animDir);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
movementSubsystem->setMoveDir(animDir);
movementSubsystem->setAnimDir(animDir);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
_dropActorToGround();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//if ( (!behavior || ( currentBehavior == "Idle" )) && max_inactive_time > 0.0f )
// Sleep();
//else
// Wakeup();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//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();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_STARTED, true);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure the actor is in the idle animation of it has no state machine
if (!statemap)
SetAnim("idle");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Override starting health if we're using the GameplayManager
auto gpm = GameplayManager::getTheGameplayManager();
GameplayFormulaData fd(this);
if (gpm->hasObject(getArchetype()) && gpm->hasFormula("Health"))
health = gpm->calculate("Health", fd);
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// Stimuli Control
//===================================================================================
2015-04-28 20:02:03 +00:00
void Actor::RespondTo(Event* ev)
{
str stimuli = ev->GetString(1);
qboolean respond = ev->GetBoolean(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
sensoryPerception->RespondTo(stimuli, respond);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::PermanentlyRespondTo(Event* ev)
{
str stimuli = ev->GetString(1);
qboolean respond = ev->GetBoolean(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
sensoryPerception->PermanentlyRespondTo(stimuli, respond);
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::TurnTowardsEnemy(Event* ev)
{
float extraYaw;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get our current enemy
Entity* currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy || currentEnemy->flags & FlagNotarget)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//See if we want to add any additional yaw
if (ev->NumArgs() > 0)
extraYaw = ev->GetInteger(1);
else
extraYaw = 0;
turnTowardsEntity(currentEnemy, extraYaw);
}
2012-12-30 16:37:54 +00:00
//
// Name: TurnTowardsPlayer()
// Parameters: Event *ev
// Description: Immediately turns the actor towards the player
//
2015-04-28 20:02:03 +00:00
void Actor::TurnTowardsPlayer(Event* ev)
{
float extraYaw;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Player* player;
player = GetPlayer(0);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// don't target while player is not in the game or he's in notarget
if (!player || player->flags & FlagNotarget)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//See if we want to add any additional yaw
if (ev->NumArgs() > 0)
extraYaw = ev->GetInteger(1);
else
extraYaw = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
turnTowardsEntity(player, extraYaw);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TurnTowardsEntity(Event* ev)
{
/*
float extraYaw;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Entity *ent;
ent = ev->GetEntity( 1 );
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if ( !ent )
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1 )
extraYaw = ev->GetInteger( 2 );
else
extraYaw = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
turnTowardsEntity( ent, extraYaw );
*/
TurnTo* turnTo;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_TurnTo);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
turnTo = new TurnTo;
turnTo->SetTarget(ev->GetEntity(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(turnTo, nullptr, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: GoIdle()
// Parameters: Event *ev
// Description: Forces the Actor into Idle State and Idle Mode
//
2015-04-28 20:02:03 +00:00
void Actor::GoIdle(Event* ev)
{
const char* state_name;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeIdle))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_Idle);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 0)
state_name = ev->GetString(1);
else
state_name = idle_state_name;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetState(state_name);
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeIdle);
2015-04-28 20:02:03 +00:00
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::LookAt(Event* ev)
{
Entity* ent;
TurnTo* turnTo;
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_LookAt);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2015-04-28 20:02:03 +00:00
ent = ev->GetEntity(1);
if (ent && ent != world)
{
turnTo = new TurnTo;
turnTo->SetTarget(ent);
SetBehavior(turnTo, nullptr, ev->GetThread());
}
}
2012-12-30 16:37:54 +00:00
//
// Name: TurnToEvent
// Parameters: Event *ev
// Description: Turns the Actor in a specified direction
//
2015-04-28 20:02:03 +00:00
void Actor::TurnToEvent(Event* ev)
{
TurnTo* turnTo;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_TurnTo);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
turnTo = new TurnTo;
turnTo->SetDirection(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
turnTo->useAnims(ev->GetBoolean(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(turnTo, nullptr, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: HeadAndEyeWatchEvent()
// Parameters: Event *ev
// Description: Sets the Actor to Head and Eye Watch the
// Specified Entity
//
2015-04-28 20:02:03 +00:00
void Actor::HeadAndEyeWatchEvent(Event* ev)
{
Event* event;
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_HeadWatch);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event = new Event(EV_Behavior_Args);
event->AddEntity(ev->GetEntity(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
event->AddFloat(ev->GetFloat(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetHeadBehavior(new HeadAndEyeWatch, event, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: HeadWatchEvent()
// Parameters: Event *ev
// Description: Sets the Actor to Head Watch the
// Specified Entity
//
2015-04-28 20:02:03 +00:00
void Actor::HeadWatchEvent(Event* ev)
{
//Event *event;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_HeadWatch);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
/*event = new Event( EV_Behavior_Args );
event->AddEntity( ev->GetEntity( 1 ) );
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if ( ev->NumArgs() > 1 )
event->AddFloat( ev->GetFloat( 2 ) );
*/
Entity* entToWatch;
entToWatch = ev->GetEntity(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!entToWatch)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
headWatcher->SetWatchTarget(entToWatch);
if (ev->NumArgs() > 1)
headWatcher->SetWatchSpeed(ev->GetFloat(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//SetHeadBehavior( new HeadWatch, event, ev->GetThread() );
}
2012-12-30 16:37:54 +00:00
//
// Name: ResetHeadEvent
// Parameters: Event *ev
// Description: Resets the head
//
2015-04-28 20:02:03 +00:00
void Actor::ResetHeadEvent(Event* ev)
{
//Event *event;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_ResetHead);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
/*
event = new Event( EV_Behavior_Args );
event->AddEntity( nullptr );
if ( ev->NumArgs() > 0 )
event->AddFloat( ev->GetFloat( 1 ) );
*/
headWatcher->SetWatchTarget(nullptr);
//SetHeadBehavior( new HeadWatch, event, ev->GetThread() );
}
2012-12-30 16:37:54 +00:00
//
// Name: EyeWatchEvent()
// Parameters: Event *ev
// Description: Sets the actor to Eye Watch
// The Specified Entity
//
2015-04-28 20:02:03 +00:00
void Actor::EyeWatchEvent(Event* ev)
{
Event* event;
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_EyeWatch);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event = new Event(EV_Behavior_Args);
event->AddEntity(ev->GetEntity(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
event->AddFloat(ev->GetFloat(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetEyeBehavior(new EyeWatch, event, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: ResetEyeEvent()
// Parameters: Event *ev
// Description: Resets the eyes
//
2015-04-28 20:02:03 +00:00
void Actor::ResetEyeEvent(Event* ev)
{
Event* event;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_ResetEye);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event = new Event(EV_Behavior_Args);
event->AddEntity(nullptr);
if (ev->NumArgs() > 0)
event->AddFloat(ev->GetFloat(1));
SetEyeBehavior(new EyeWatch, event, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: ResetTorsoEvent()
// Parameters: Event *ev
// Description: Resets the torso
//
2015-04-28 20:02:03 +00:00
void Actor::ResetTorsoEvent(Event*)
{
/*
Event *event;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if ( !ModeAllowed( ActorModeScript ) )
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if ( mode == ActorModeTalk )
2015-04-28 20:02:03 +00:00
RepostEvent( ev, EV_Actor_ResetTorso );
2015-04-29 20:34:44 +00:00
else if ( mode == ActorModeAi )
2015-04-28 20:02:03 +00:00
SendMoveDone( ev->GetThread() );
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event = new Event( EV_Behavior_Args );
event->AddEntity( nullptr );
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event->AddInteger( 0 );
event->AddInteger( 30 );
event->AddInteger( 0 );
SetEyeBehavior( new TorsoTurn, event, ev->GetThread() );
*/
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
SetControllerTag(ActorTorsoTag, gi.Tag_NumForName(edict->s.modelindex, "Bip01 Spine1"));
SetControllerAngles(ActorTorsoTag, vec_zero);
2015-04-28 20:02:03 +00:00
}
2012-12-30 16:37:54 +00:00
//
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::FallToDeathEvent(Event* ev)
{
Event* e;
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_FallToDeath);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2015-04-28 20:02:03 +00:00
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());
}
2012-12-30 16:37:54 +00:00
//
// Name: WalkTo()
// Parameters: Event *ev
// Description: Makes an Actor WalkTo a location
//
2015-04-28 20:02:03 +00:00
void Actor::WalkTo(Event* ev)
{
Event* e;
//HelperNode *node;
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_WalkTo);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2015-04-28 20:02:03 +00:00
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());
}
2012-12-30 16:37:54 +00:00
//
// Name: BlindlyFollowPath()
// Parameters: Event *ev
// Description: Makes an Actor blindly follow a helper node path
//
2015-04-28 20:02:03 +00:00
void Actor::BlindlyFollowPath(Event* ev)
{
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
{
RepostEvent(ev, EV_Actor_BlindlyFollowPath);
2015-04-29 20:34:44 +00:00
} else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
{
SendMoveDone(ev->GetThread());
}
return;
}
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2015-04-28 20:02:03 +00:00
auto 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());
2012-12-30 16:37:54 +00:00
}
//
// Name: FollowWayPoints()
// Parameters: Event *ev
// Description: Makes an Actor Follow a Waypoint Chain
//
2015-04-28 20:02:03 +00:00
void Actor::FollowWayPoints(Event* ev)
{
Event* e;
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_WalkTo);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->SetSource(ev->GetSource());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->GetSource() == EV_FROM_SCRIPT)
{
e->SetThread(ev->GetThread());
e->SetLineNumber(ev->GetLineNumber());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//PathName
e->AddString(ev->GetString(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Animation
if (ev->NumArgs() > 1)
e->AddString(ev->GetString(2));
else
e->AddString("walk");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//StartPoint
if (ev->NumArgs() > 2)
e->AddString(ev->GetString(3));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(new GotoWayPoint, e, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: WalkWatch
// Parameters: Event *ev
// Description: Makes an Actor walk to a specified location while
// watching a Specified Entity
//
2015-04-28 20:02:03 +00:00
void Actor::WalkWatch(Event* ev)
{
Event* e;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() < 2)
return;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_WalkWatch);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->SetSource(ev->GetSource());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->GetSource() == EV_FROM_SCRIPT)
{
e->SetThread(ev->GetThread());
e->SetLineNumber(ev->GetLineNumber());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get animation name
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 2)
e->AddString(ev->GetString(3));
else
e->AddString("walk");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the node to walk to
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->AddToken(ev->GetToken(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the entity to watch
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->AddEntity(ev->GetEntity(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(new GotoPathNode, e, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: WarpTo()
// Parameters: Event *ev
// Description: Makes an actor warp to a specified location
//
2015-04-28 20:02:03 +00:00
void Actor::WarpTo(Event* ev)
{
PathNode* goal_node;
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
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));
}
}
}
2012-12-30 16:37:54 +00:00
//
// Name: PickupEnt()
// Parameters: Event *ev
// Description: Makes the Actor Pickup the specified entity
//
2015-04-28 20:02:03 +00:00
void Actor::PickupEnt(Event* ev)
{
Event* e;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_PickupEnt);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->SetSource(ev->GetSource());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->GetSource() == EV_FROM_SCRIPT)
{
e->SetThread(ev->GetThread());
e->SetLineNumber(ev->GetLineNumber());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->AddEntity(ev->GetEntity(1));
e->AddString(ev->GetString(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(new PickupEntity, e, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: ThrowEnt()
// Parameters: Event *ev
// Description: Makes the actor throw the specified entity
//
2015-04-28 20:02:03 +00:00
void Actor::ThrowEnt(Event* ev)
{
Event* e;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
RepostEvent(ev, EV_Actor_ThrowEnt);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->SetSource(ev->GetSource());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->GetSource() == EV_FROM_SCRIPT)
{
e->SetThread(ev->GetThread());
e->SetLineNumber(ev->GetLineNumber());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->AddString(ev->GetString(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(new ThrowEntity, e, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: AttackEntity()
// Parameters: Event *ev
// Description: Makes the actor attack the specified entity
//
2015-04-28 20:02:03 +00:00
void Actor::AttackEntity(Event* ev)
{
auto target = ev->GetEntity(1);
auto forceEnemy = true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!GetActorFlag(ACTOR_FLAG_STARTED))
{
PostEvent(*ev, FRAMETIME);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
forceEnemy = ev->GetBoolean(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (forceEnemy)
forcedEnemy = target;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (forceEnemy)
{
enemyManager->SetCurrentEnemy(target);
enemyManager->LockOnCurrentEnemy(true);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
sensoryPerception->Stimuli(StimuliSight, target);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (enemyManager->IsInHateList(target))
{
enemyManager->SetCurrentEnemy(target);
}
}
2012-12-30 16:37:54 +00:00
//
// Name: AttackPlayer()
// Parameters: Event *ev
// Description: Makes the Actor attack the Player
//
2015-04-28 20:02:03 +00:00
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);
}
}
2012-12-30 16:37:54 +00:00
//
// Name: JumpToEvent()
// Parameters: Event *ev
// Description: Makes the Actor Jump to a specified location
//
2015-04-28 20:02:03 +00:00
void Actor::JumpToEvent(Event* ev)
{
Event* e;
int i;
int n;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
PostEvent(*ev, FRAMETIME);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->SetSource(ev->GetSource());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->GetSource() == EV_FROM_SCRIPT)
{
e->SetThread(ev->GetThread());
e->SetLineNumber(ev->GetLineNumber());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
n = ev->NumArgs();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= n; i++)
{
e->AddToken(ev->GetToken(i));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(new JumpToPathNode, e, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
//
// Name: RepostEvent()
// Parameters: Event *ev,
// Event &event_type
// Description: Reposts the passed in event
//
2015-04-28 20:02:03 +00:00
void Actor::RepostEvent(Event* ev, const Event& event_type)
{
Event* event;
int i;
str token;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event = new Event(event_type);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= ev->NumArgs(); i++)
{
token = ev->GetString(i);
event->AddString(token.c_str());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event->SetThread(ev->GetThread());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
PostEvent(event, FRAMETIME);
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::SetMinimumMeleeHeight(Event* ev)
{
minimum_melee_height = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetDamageAngles()
// Parameters: Event *ev
// Description: Sets damage_angles
//
2015-04-28 20:02:03 +00:00
void Actor::SetDamageAngles(Event* ev)
{
damage_angles = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetImmortal()
// Parameters: Event *ev
// Description: Sets the ACTOR_FLAG_IMMORTAL
//
2015-04-28 20:02:03 +00:00
void Actor::SetImmortal(Event* ev)
{
qboolean isImmortal = true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 0)
isImmortal = ev->GetBoolean(1);
SetActorFlag(ACTOR_FLAG_IMMORTAL, isImmortal);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetTargetType()
// Parameters: Event *ev
// Description: Sets targetType -- Needs to be removed
//
2015-04-28 20:02:03 +00:00
void Actor::SetTargetType(Event* ev)
{
targetType = targetType_t(ev->GetInteger(1));
}
2012-12-30 16:37:54 +00:00
//
// Name: SetEyeAngles()
// Parameters: Event *ev
// Description: Sets the eyeangles;
//
2015-04-28 20:02:03 +00:00
void Actor::SetEyeAngles(Event* ev)
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
{
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;
}
2012-12-30 16:37:54 +00:00
//
// Name: SetTakeDamage()
// Parameters: Event *ev
// Description: Sets the ACTOR_FLAG_TAKE_DAMAGE
//
2015-04-28 20:02:03 +00:00
void Actor::SetTakeDamage(Event* ev)
{
qboolean damage = true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 0)
damage = ev->GetBoolean(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_TAKE_DAMAGE, damage);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetIdleStateName()
// Parameters: Event *ev
// Description: Sets the idle_state_name
//
2015-04-28 20:02:03 +00:00
void Actor::SetIdleStateName(Event* ev)
{
idle_state_name = ev->GetString(1);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetActivateThread()
// Parameters: Event *ev
// Description: Sets the activate_thread
//
2015-04-28 20:02:03 +00:00
void Actor::SetActivateThread(Event* ev)
{
activate_thread = ev->GetString(1);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetValidTarget()
// Parameters: Event *ev
// Description: Sets validTarget Boolean -- Needs to go away, I believe
// We should find a better way to do this
//
2015-04-28 20:02:03 +00:00
void Actor::SetValidTarget(Event* ev)
{
Event* flagEvent;
qboolean valid;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
valid = ev->GetBoolean(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
flagEvent = new Event(EV_EntityFlags);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (valid)
flagEvent->AddString("-notarget");
else
flagEvent->AddString("+notarget");
ProcessEvent(flagEvent);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetAlertThread()
// Parameters: Event *ev
// Description: Sets the alert_thread
//
2015-04-28 20:02:03 +00:00
void Actor::SetAlertThread(Event* ev)
{
alert_thread = ev->GetString(1);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetEnemyType()
// Parameters: Event *ev
// Description: Sets our enemytype
//
2015-04-28 20:02:03 +00:00
void Actor::SetEnemyType(Event* ev)
{
enemytype = ev->GetString(1);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetWeaponReady()
// Parameters: Event *ev
// Description: Sets the ACTOR_FLAG_WEAPON_READY
//
2015-04-28 20:02:03 +00:00
void Actor::SetWeaponReady(Event* ev)
{
SetActorFlag(ACTOR_FLAG_WEAPON_READY, ev->GetBoolean(1));
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::LoadStateMap(Event* ev)
{
str anim_name;
qboolean loading;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Load the new state map
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
statemap_name = ev->GetString(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
freeConditionals(conditionals);
//conditionals.FreeObjectList();
statemap = GetStatemap(statemap_name, reinterpret_cast<Condition<Class> *>(Conditions), &conditionals, false);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// 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";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 2)
loading = ev->GetBoolean(3);
else
loading = false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Initialize the actors first animation
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!loading)
{
SetState(idle_state_name.c_str());
SetGlobalState(global_state_name.c_str());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (currentState)
anim_name = currentState->getLegAnim(*this, &conditionals);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (anim_name == "" && !newanim.length())
anim_name = "idle";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetAnim(anim_name.c_str(), EV_Anim_Done);
ChangeAnim();
}
}
2012-12-30 16:37:54 +00:00
//--------------------------------------------------------------
// Name: LoadMasterStateMap()
// Class: Actor
//
// Description: Loads the Master State Map for the Actor
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::LoadMasterStateMap(Event* ev)
{
qboolean loading;
// Load the new state map
masterstatemap_name = ev->GetString(1);
freeConditionals(master_conditionals);
masterstatemap = GetStatemap(masterstatemap_name, reinterpret_cast<Condition<Class> *>(Conditions), &master_conditionals, false);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Set the first state
master_idle_state_name = "START";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 2)
loading = ev->GetBoolean(3);
else
loading = false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Initialize the actors first animation
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!loading)
{
SetMasterState(master_idle_state_name.c_str());
}
}
2012-12-30 16:37:54 +00:00
//
// Name: SetGlobalState()
// Parameters: const char *state_name
// Description: Sets the Current Global State to state_name
//
2015-04-28 20:02:03 +00:00
void Actor::SetGlobalState(const char* state_name)
{
if (!statemap)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (deadflag)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (globalState)
globalState->ProcessExitCommands(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
globalState = statemap->FindGlobalState(state_name);
}
2012-12-30 16:37:54 +00:00
//
// Name: SetState()
// Parameters: const char *state_name
// Description: Sets the Current State to state_name
//
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Set the behavior
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (currentState)
{
cls = getClass(currentState->getBehaviorName());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (cls)
{
if (currentState->numBehaviorParms())
{
e = new Event(EV_Behavior_Args);
for (i = 1; i <= currentState->numBehaviorParms(); i++)
e->AddString(currentState->getBehaviorParm(i));
SetBehavior(reinterpret_cast<Behavior *>(cls->newInstance()), e);
} else
{
SetBehavior(reinterpret_cast<Behavior *>(cls->newInstance()));
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
cls = getClass(currentState->getHeadBehaviorName());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (cls)
{
if (currentState->numHeadBehaviorParms())
{
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= currentState->numHeadBehaviorParms(); i++)
e->AddString(currentState->getHeadBehaviorParm(i));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetHeadBehavior(reinterpret_cast<Behavior *>(cls->newInstance()), e);
} else
{
SetHeadBehavior(reinterpret_cast<Behavior *>(cls->newInstance()));
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
cls = getClass(currentState->getEyeBehaviorName());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (cls)
{
if (currentState->numEyeBehaviorParms())
{
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= currentState->numEyeBehaviorParms(); i++)
e->AddString(currentState->getEyeBehaviorParm(i));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetEyeBehavior(reinterpret_cast<Behavior *>(cls->newInstance()), e);
} else
{
SetEyeBehavior(reinterpret_cast<Behavior *>(cls->newInstance()));
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
cls = getClass(currentState->getTorsoBehaviorName());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (cls)
{
if (currentState->numTorsoBehaviorParms())
{
e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= currentState->numTorsoBehaviorParms(); i++)
e->AddString(currentState->getTorsoBehaviorParm(i));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetTorsoBehavior(reinterpret_cast<Behavior *>(cls->newInstance()), e);
} else
{
SetTorsoBehavior(reinterpret_cast<Behavior *>(cls->newInstance()));
}
}
InitState();
currentState->ProcessEntryCommands(this);
} else
{
gi.WDPrintf("State %s not found\n", state_name);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::resetStateMachine(void)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetState(idle_state_name);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetMasterState(Event* ev)
{
SetMasterState(ev->GetString(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: SetMasterState()
// Class: Actor
//
// Description: Sets the Current Master State to state_name
//
// Parameters: const str &state_name
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
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());
}
}
2012-12-30 16:37:54 +00:00
//
// Name: InitState()
// Parameters: None
// Description: Initializes a state
//
2015-04-28 20:02:03 +00:00
void Actor::InitState(void)
{
float min_time;
float max_time;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentState)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
min_time = currentState->getMinTime();
max_time = currentState->getMaxTime();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (min_time != -1.0f && max_time != -1.0f)
{
SetActorFlag(ACTOR_FLAG_STATE_DONE_TIME_VALID, true);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
state_done_time = level.time + min_time + G_Random(max_time - min_time);
} else
{
SetActorFlag(ACTOR_FLAG_STATE_DONE_TIME_VALID, false);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
state_time = level.time;
times_done = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ClearStateFlags();
command = "";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_ANIM_DONE, false);
SetActorFlag(ACTOR_FLAG_NOISE_HEARD, false);
}
2012-12-30 16:37:54 +00:00
//--------------------------------------------------------------
// Name: InitMasterState()
// Class: Actor
//
// Description: Initializes a master state
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::InitMasterState(void)
{
if (!currentMasterState)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto min_time = currentMasterState->getMinTime();
auto max_time = currentMasterState->getMaxTime();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (min_time != -1.0f && max_time != -1.0f)
{
SetActorFlag(ACTOR_FLAG_MASTER_STATE_DONE_TIME_VALID, true);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
masterstate_done_time = level.time + min_time + G_Random(max_time - min_time);
} else
{
SetActorFlag(ACTOR_FLAG_MASTER_STATE_DONE_TIME_VALID, false);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
masterstate_time = level.time;
masterstate_times_done = 0;
}
2012-12-30 16:37:54 +00:00
//
// Name: RegisterBehaviorPackage()
// Parameters: Event *ev
// Description: Registers a BehaviorPackage with the packageManager
//
2015-04-28 20:02:03 +00:00
void Actor::RegisterBehaviorPackage(Event* ev)
{
packageManager->RegisterPackage(ev->GetString(1));
}
2012-12-30 16:37:54 +00:00
//
// Name: UnRegisterBehaviorPackage()
// Parameters: Event *ev
// Description: UnRegisters a BehaviorPackage with the packageManager
//
2015-04-28 20:02:03 +00:00
void Actor::UnRegisterBehaviorPackage(Event* ev)
{
packageManager->UnregisterPackage(ev->GetString(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetPackageTendency(Event* ev)
{
personality->SetBehaviorTendency(ev->GetString(1), ev->GetFloat(2));
}
2012-12-30 16:37:54 +00:00
//
// Name: LoadFuzzyEngine()
// Parameters: Event *ev
// Description: Loads a Fuzzy Engine into memory
//
2015-04-28 20:02:03 +00:00
void Actor::LoadFuzzyEngine(Event* ev)
{
// Load the new fuzzy engine
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
fuzzyengine_name = ev->GetString(1);
//fuzzy_conditionals.FreeObjectList();
freeConditionals(fuzzy_conditionals);
fuzzyEngine = GetFuzzyEngine(fuzzyengine_name, reinterpret_cast<Condition<Class> *>(Conditions), &fuzzy_conditionals, false);
fuzzyEngine_active = true;
}
2012-12-30 16:37:54 +00:00
//--------------------------------------------------------------
// Name: ProcessActorStateMachine()
// Class: Actor
//
// Description: Evaluates our state map file
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ProcessActorStateMachine(void)
{
auto count = 0;
str behavior_name;
str torsoBehavior_name;
str currentTorsoAnim;
str currentanim;
ClassDef* cls = nullptr;
State* laststate;
auto stateLegAnim = animname;
auto stateTorsoAnim = TorsoAnimName;
if (deadflag || !currentState)
return;
2015-04-29 20:34:44 +00:00
if (mode != ActorModeAi && mode != ActorModeIdle)
2015-04-28 20:02:03 +00:00
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 = nullptr;
}
if (torsoBehavior)
{
EndTorsoBehavior();
//torsoBehavior->End(*this);
//torsoBehavior = nullptr;
}
// Setup the new behavior
behavior_name = currentState->getBehaviorName();
// Check if our behavior is set up in the GPD
if (!behavior_name.length())
{
auto gpm = GameplayManager::getTheGameplayManager();
auto stateName = getArchetype() + "." + currentState->getName();
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 = nullptr;
// }
if (cls)
{
if (currentState->numBehaviorParms())
{
auto* e = new Event(EV_Behavior_Args);
for (auto i = 1; i <= currentState->numBehaviorParms(); i++)
e->AddString(currentState->getBehaviorParm(i));
SetBehavior(static_cast<Behavior *>(cls->newInstance()), e);
} else
{
SetBehavior(static_cast<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 = nullptr;
if (torsoBehavior_name.length())
cls = getClass(currentState->getTorsoBehaviorName());
//else if ( torsoBehavior )
// {
// torsoBehavior->End(*this);
// torsoBehavior = nullptr;
// }
if (cls)
{
if (currentState->numTorsoBehaviorParms())
{
auto e = new Event(EV_Behavior_Args);
for (auto i = 1; i <= currentState->numTorsoBehaviorParms(); i++)
e->AddString(currentState->getTorsoBehaviorParm(i));
SetTorsoBehavior(static_cast<Behavior *>(cls->newInstance()), e);
} else
{
SetTorsoBehavior(static_cast<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())
{
auto gpm = GameplayManager::getTheGameplayManager();
str stateName = currentState->getName();
auto 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 = nullptr;
}
// 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;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
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();
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
*/
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (showStates != DEBUG_NONE && laststate != currentState)
_printDebugInfo(laststate->getName(), currentState->getName(), currentanim, currentTorsoAnim);
} while (laststate != currentState);
}
2012-12-30 16:37:54 +00:00
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ProcessMasterStateMachine(void)
{
if (!masterstatemap)
return;
if (deadflag || !currentMasterState)
return;
2015-04-29 20:34:44 +00:00
if (mode != ActorModeAi && mode != ActorModeIdle)
2015-04-28 20:02:03 +00:00
return;
auto count = 0;
State* laststate;
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);
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::SendMoveDone(CThread* script_thread)
{
Event* event;
if (script_thread)
{
event = new Event(EV_MoveDone);
event->AddEntity(this);
script_thread->PostEvent(event, FRAMETIME);
}
}
2012-12-30 16:37:54 +00:00
//
// Name: EndBehavior()
// Parameters: None
// Description: Ends the current behavior
//
2015-04-28 20:02:03 +00:00
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 = nullptr;
}
// Required so that script threads will get the waitfor notification
if (scriptthread)
{
CThread* t = scriptthread;
scriptthread = nullptr;
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);
}
2012-12-30 16:37:54 +00:00
//
// Name: EndHeadBehavior()
// Parameters: None
// Description: Ends the current head behavior
//
2015-04-28 20:02:03 +00:00
void Actor::EndHeadBehavior(void)
{
if (headBehavior)
{
headBehavior->End(*this);
delete headBehavior;
headBehavior = nullptr;
}
// Prevent previous behaviors from stopping the next behavior
CancelEventsOfType(EV_Actor_EndHeadBehavior);
}
2012-12-30 16:37:54 +00:00
//
// Name: EndEyeBehavior()
// Parameters: None
// Description: Ends the current eye behavior
//
2015-04-28 20:02:03 +00:00
void Actor::EndEyeBehavior(void)
{
if (eyeBehavior)
{
eyeBehavior->End(*this);
delete eyeBehavior;
eyeBehavior = nullptr;
}
// Prevent previous behaviors from stopping the next behavior
CancelEventsOfType(EV_Actor_EndEyeBehavior);
}
2012-12-30 16:37:54 +00:00
//
// Name: EndTorsoBehavior()
// Parameters: None
// Description: Ends the current torso behavior
//
2015-04-28 20:02:03 +00:00
void Actor::EndTorsoBehavior(void)
{
if (torsoBehavior)
{
torsoBehavior->End(*this);
delete torsoBehavior;
torsoBehavior = nullptr;
}
// Prevent previous behaviors from stopping the next behavior
CancelEventsOfType(EV_Actor_EndTorsoBehavior);
}
2012-12-30 16:37:54 +00:00
//
// Name: EndAllBehaviors()
// Parameters: None
// Description: Ends all current behaviors
//
2015-04-28 20:02:03 +00:00
void Actor::EndAllBehaviors(void)
{
EndBehavior();
EndHeadBehavior();
EndEyeBehavior();
EndTorsoBehavior();
}
2012-12-30 16:37:54 +00:00
//
// Name: SetBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current behavior
//
2015-04-28 20:02:03 +00:00
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 = nullptr;
}
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;
}
}
2012-12-30 16:37:54 +00:00
//
// Name: SetHeadBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current head behavior
//
2015-04-28 20:02:03 +00:00
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 = nullptr;
}
if (headBehavior)
{
currentHeadBehavior = headBehavior->getClassname();
str newHeadBehaviorName = newHeadBehavior->getClassname();
if (currentHeadBehavior == newHeadBehaviorName)
{
if (startevent)
{
headBehavior->ProcessEvent(startevent);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
EndHeadBehavior();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
headBehavior = newHeadBehavior;
if (headBehavior)
{
Wakeup();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (startevent)
{
headBehavior->ProcessEvent(startevent);
}
currentHeadBehavior = headBehavior->getClassname();
headBehavior->Begin(*this);
scriptthread = newthread;
}
}
2012-12-30 16:37:54 +00:00
//
// Name: SetEyeBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current eye behavior
//
2015-04-28 20:02:03 +00:00
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 = nullptr;
}
EndEyeBehavior();
eyeBehavior = newEyeBehavior;
if (eyeBehavior)
{
Wakeup();
if (startevent)
{
eyeBehavior->ProcessEvent(startevent);
}
currentEyeBehavior = eyeBehavior->getClassname();
eyeBehavior->Begin(*this);
scriptthread = newthread;
}
}
2012-12-30 16:37:54 +00:00
//
// Name: SetTorsoBehavior()
// Parameters: Behavior *newbehavior
// Event *startevent
// CThread *newthread
// Description: Sets the current torso behavior
//
2015-04-28 20:02:03 +00:00
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 = nullptr;
}
EndTorsoBehavior();
torsoBehavior = newTorsoBehavior;
torsoBehaviorCode = BEHAVIOR_EVALUATING;
if (torsoBehavior)
{
//turnThinkOn();
Wakeup();
if (startevent)
{
torsoBehavior->ProcessEvent(startevent);
}
currentTorsoBehavior = torsoBehavior->getClassname();
torsoBehavior->Begin(*this);
scriptthread = newthread;
}
}
2012-12-30 16:37:54 +00:00
//
// Name: EndBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndBehavior
//
2015-04-28 20:02:03 +00:00
void Actor::EndBehaviorEvent(Event*)
{
EndBehavior();
}
2012-12-30 16:37:54 +00:00
//
// Name: EndHeadBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndHeadBehavior
//
2015-04-28 20:02:03 +00:00
void Actor::EndHeadBehaviorEvent(Event*)
{
EndHeadBehavior();
}
2012-12-30 16:37:54 +00:00
//
// Name: EndEyeBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndEyeBehavior
//
2015-04-28 20:02:03 +00:00
void Actor::EndEyeBehaviorEvent(Event*)
{
EndEyeBehavior();
}
2012-12-30 16:37:54 +00:00
//
// Name: EndTorsoBehaviorEvent
// Parameters: Event *ev
// Description: Calls EndTorsoBehavior
//
2015-04-28 20:02:03 +00:00
void Actor::EndTorsoBehaviorEvent(Event*)
{
EndTorsoBehavior();
}
2012-12-30 16:37:54 +00:00
//
// Name: NotifyBehavior()
// Parameters: Event *ev
// Description: Tells the current behavior that the anim is done
//
2015-04-28 20:02:03 +00:00
void Actor::NotifyBehavior(Event*)
{
if (behavior)
{
behavior->ProcessEvent(EV_Behavior_AnimDone);
SetActorFlag(ACTOR_FLAG_ANIM_DONE, true);
}
}
2012-12-30 16:37:54 +00:00
//
// Name: NotifyHeadBehavior()
// Parameters: Event *ev
// Description: Tells the current head behavior that the anim is done
//
2015-04-28 20:02:03 +00:00
void Actor::NotifyHeadBehavior(Event*)
{
if (headBehavior)
{
headBehavior->ProcessEvent(EV_Behavior_AnimDone);
SetActorFlag(ACTOR_FLAG_ANIM_DONE, true);
}
}
2012-12-30 16:37:54 +00:00
//
// Name: NotifyEyeBehavior()
// Parameters: Event *ev
// Description: Tells the current eye behavior that the anim is done
//
2015-04-28 20:02:03 +00:00
void Actor::NotifyEyeBehavior(Event*)
{
if (eyeBehavior)
{
eyeBehavior->ProcessEvent(EV_Behavior_AnimDone);
SetActorFlag(ACTOR_FLAG_ANIM_DONE, true);
}
}
2012-12-30 16:37:54 +00:00
//
// Name: NotifyTorsoBehavior()
// Parameters: Event *ev
// Description: Tells the current torso behavior that the anim is done
//
2015-04-28 20:02:03 +00:00
void Actor::NotifyTorsoBehavior(Event*)
{
if (torsoBehavior)
{
torsoBehavior->ProcessEvent(EV_Behavior_AnimDone);
SetActorFlag(ACTOR_FLAG_TORSO_ANIM_DONE, true);
}
}
2012-12-30 16:37:54 +00:00
//***********************************************************************************************
//
// Actor type script commands
//
//***********************************************************************************************
2015-04-28 20:02:03 +00:00
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;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::RunAlertThread(Event*)
{
if (alert_thread.length())
RunThread(alert_thread);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
//***********************************************************************************************
//
// Targeting functions
//
//***********************************************************************************************
2015-04-28 20:02:03 +00:00
qboolean Actor::CloseToEnemy(const Vector& pos, float howclose)
{
// Get our current enemy
Entity* currentEnemy;
2012-12-31 16:38:54 +00:00
2015-04-28 20:02:03 +00:00
Q_UNUSED(pos);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!IsEntityAlive(currentEnemy))
{
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (WithinDistance(currentEnemy, howclose))
{
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::EyeOffset(Event* ev)
{
eyeposition -= eyeoffset;
eyeoffset = ev->GetVector(1);
eyeposition += eyeoffset;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::EntityInRange(Entity* ent, float range, float min_height, float max_height, bool XYOnly)
{
float r;
Vector delta, cent, enemyCent;
float height_diff;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure the entity is alive
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!IsEntityAlive(ent))
{
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
cent = centroid;
enemyCent = ent->centroid;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if the entity is in range
if (XYOnly)
{
cent.z = 0.0f;
enemyCent.z = 0.0f;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
delta = enemyCent - cent;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (max_height != 0 || min_height != 0)
{
height_diff = delta[2];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (height_diff < min_height || height_diff > max_height)
{
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
delta[2] = 0;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
r = delta * delta;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return r < range * range;
}
2012-12-30 16:37:54 +00:00
//***********************************************************************************************
//
// Thread based script commands
//
//***********************************************************************************************
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//jhefty: scripting getcurrententity will now work, leaving old code commented
//in case something magical is happening with CreateThread/DelayedStart
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
/*CThread *thread;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
thread = Director.CreateThread( thread_name );
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if ( thread )
thread->DelayedStart( 0.0f );*/
}
2012-12-30 16:37:54 +00:00
//***********************************************************************************************
//
// Path and node management
//
//***********************************************************************************************
2015-04-28 20:02:03 +00:00
PathNode* Actor::NearestNodeInPVS(const Vector& pos)
{
PathNode* bestnode = nullptr;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto cell = thePathManager.GetMapCell(pos);
if (!cell)
return nullptr;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto bestdist = 999999999.0f; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
for (auto i = 0; i < cell->NumNodes(); i++)
{
auto node = cell->GetNode(i);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!node)
continue;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto delta = node->origin - pos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// get the distance squared (faster than getting real distance)
auto dist = delta * delta;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (dist < bestdist && gi.inPVS(node->origin, pos))
{
bestnode = node;
bestdist = dist;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// if we're close enough, early exit
if (dist < 16.0f)
break;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return bestnode;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetPath(Path* newpath)
{
movementSubsystem->setPath(newpath);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ReserveNodeEvent(Event* ev)
{
auto node = thePathManager.NearestNode(ev->GetVector(1), 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;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ReleaseNodeEvent(Event* ev)
{
auto node = thePathManager.NearestNode(ev->GetVector(1), this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (node && node->entnum == entnum)
{
node->occupiedTime = 0;
node->entnum = 0;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace_t Actor::Trace(const Vector& end, const char* reason) const
{
return Trace(origin, end, reason);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
trace_t Actor::Trace(const Vector& begin, const Vector& end, const char* reason) const
{
return Trace(begin, end, edict->clipmask, reason);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace_t Actor::Trace(const Vector& begin, const Vector& end, const int contentMask, const char* reason) const
{
bool betterTrace;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Try the normal trace first
auto beginPoint = begin + movementSubsystem->getStep();
auto endPoint = end + movementSubsystem->getStep();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto fullStepUpTrace = G_Trace(beginPoint, mins, maxs, endPoint, this, contentMask, false, reason);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto bestTrace = &fullStepUpTrace;
auto bestFraction = fullStepUpTrace.fraction;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// If the first trace didn't work very well try it again but only step up half way
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (bestFraction < 1.0f || bestTrace->startsolid)
{
beginPoint = begin + movementSubsystem->getStep() / 2.0f;
endPoint = end + movementSubsystem->getStep() / 2.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto halfStepUpTrace = G_Trace(beginPoint, mins, maxs, endPoint, this, contentMask, false, reason);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if this trace is the best one
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (halfStepUpTrace.fraction > bestFraction && (!halfStepUpTrace.startsolid || bestTrace->startsolid))
betterTrace = true;
else if (!halfStepUpTrace.startsolid && bestTrace->startsolid)
betterTrace = true;
else
betterTrace = false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (betterTrace)
{
bestTrace = &halfStepUpTrace;
bestFraction = halfStepUpTrace.fraction;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// If the second trace didn't work very well try it again but don't step up at all
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (bestFraction < 1.0f || bestTrace->startsolid)
{
beginPoint = begin;
endPoint = end;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto straightTrace = G_Trace(beginPoint, mins, maxs, endPoint, this, contentMask, false, reason);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if this trace is the best one
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (straightTrace.fraction > bestFraction && (!straightTrace.startsolid || bestTrace->startsolid))
betterTrace = true;
else if (!straightTrace.startsolid && bestTrace->startsolid)
betterTrace = true;
else
betterTrace = false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (betterTrace)
{
bestTrace = &straightTrace;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return *bestTrace;
}
2012-12-30 16:37:54 +00:00
//***********************************************************************************************
//
2015-04-28 20:02:03 +00:00
// Animation control functions
2012-12-30 16:37:54 +00:00
//
//***********************************************************************************************
2015-04-28 20:02:03 +00:00
void Actor::RemoveAnimDoneEvent(void)
{
animate->SetAnimDoneEvent(nullptr);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (newanimevent)
{
delete newanimevent;
newanimevent = nullptr;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
last_anim_event_name = "";
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ChangeAnim(void)
{
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 = nullptr;
}
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 = nullptr;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::AnimDone(Event*)
{
SetActorFlag(ACTOR_FLAG_ANIM_DONE, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TorsoAnimDone(Event*)
{
SetActorFlag(ACTOR_FLAG_TORSO_ANIM_DONE, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::SetAnim(const str& anim, Event* ev, bodypart_t part, const float animationRate)
{
if (!anim.length())
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!GetActorFlag(ACTOR_FLAG_CAN_CHANGE_ANIM))
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto num = gi.Anim_Random(edict->s.modelindex, anim.c_str());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (num != -1)
{
if (part == legs)
{
newanim = anim;
newanimnum = num;
animnum = newanimnum;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (newanimevent)
delete newanimevent;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
newanimevent = ev;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (newanimevent)
last_anim_event_name = newanimevent->getName();
else
last_anim_event_name = "";
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (part == torso)
{
newTorsoAnim = anim;
newTorsoAnimNum = num;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (newTorsoAnimEvent)
delete newTorsoAnimEvent;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
newTorsoAnimEvent = ev;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (newTorsoAnimEvent)
last_torso_anim_event_name = newTorsoAnimEvent->getName();
else
last_torso_anim_event_name = "";
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (actortype == IS_INANIMATE)
{
// inanimate objects change anims immediately
ChangeAnim();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
edict->s.animationRate = animationRate;
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
warning("Actor::SetAnim", "Actor \"%s\" has no anim named \"%s\"\n", model.c_str(), anim.c_str());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// kill the event
if (ev)
{
delete ev;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::AnimateOnce(Event* ev)
{
animate->RandomAnimate(ev->GetString(1), EV_StopAnimating);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::SetAnim(const str& anim, Event& ev, bodypart_t part, const float animationRate)
{
auto event = new Event(ev);
assert(event);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return SetAnim(anim, event, part, animationRate);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetAnim(Event* ev)
{
if (ev->NumArgs() > 1)
{
SetAnim(ev->GetString(1), nullptr, legs, ev->GetFloat(2));
} else
{
SetAnim(ev->GetString(1));
}
ChangeAnim();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Anim(Event* ev)
{
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeScript))
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
PostEvent(*ev, FRAMETIME);
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
SendMoveDone(ev->GetThread());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (deadflag && actortype != IS_INANIMATE)
{
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto e = new Event(EV_Behavior_Args);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->SetSource(ev->GetSource());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->GetSource() == EV_FROM_SCRIPT)
{
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->SetThread(ev->GetThread());
e->SetLineNumber(ev->GetLineNumber());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
e->AddToken(ev->GetToken(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetBehavior(new PlayAnim, e, ev->GetThread());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//***********************************************************************************************
//
// Script conditionals
//
//***********************************************************************************************
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::IfEnemyVisibleEvent(Event* ev)
{
auto currentEnemy = enemyManager->GetCurrentEnemy();
ev->ReturnInteger(currentEnemy != nullptr ? sensoryPerception->CanSeeEntity(this, currentEnemy, true, true) : false);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::IfNearEvent(Event* ev)
{
auto thread = ev->GetThread();
assert(thread);
if (!thread)
{
return;
}
str name = ev->GetString(1);
auto distance = ev->GetFloat(2);
if (name[0] == '*')
{
ev->ReturnInteger(WithinDistance(ev->GetEntity(1), distance));
} else if (name[0] == '$')
{
Entity* bestent = nullptr;
auto bestdist = distance * distance;
auto tlist = world->GetTargetList(str(&name[1]));
auto n = tlist->list.NumObjects();
for (auto i = 1; i <= n; i++)
{
auto ent = tlist->list.ObjectAt(i);
auto delta = centroid - ent->centroid;
auto dist = delta * delta;
if (dist <= bestdist)
{
bestent = ent;
bestdist = dist;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ev->ReturnInteger(bestent != nullptr);
} else
{
Entity* bestent = nullptr;
auto bestdist = distance * distance;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (auto ent = findradius(nullptr, origin, distance); ent; ent = findradius(ent, origin, distance))
{
if (ent->inheritsFrom(name.c_str()))
{
auto delta = centroid - ent->centroid;
auto dist = delta * delta;
if (dist <= bestdist)
{
bestent = ent;
bestdist = dist;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ev->ReturnInteger(bestent != nullptr);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::IfCanHideAtEvent(Event* ev)
{
auto thread = ev->GetThread();
assert(thread);
if (!thread)
{
return;
}
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
auto pos = ev->GetVector(1);
auto node = thePathManager.NearestNode(pos, this);
auto 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;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ev->ReturnInteger(result);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::IfEnemyWithinEvent(Event* ev)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
{
ev->ReturnInteger(false);
return;
}
ev->ReturnInteger(WithinDistance(currentEnemy, ev->GetFloat(1)));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//***********************************************************************************************
//
// Sound reaction functions
//
//***********************************************************************************************
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::NoPainSounds(Event*)
{
SetActorFlag(ACTOR_FLAG_NO_PAIN_SOUNDS, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::HeardSound(Event* ev)
{
auto soundType = ev->NumArgs() > 2 ? ev->GetInteger(3) : SOUNDTYPE_GENERAL;
auto soundEnt = ev->GetEntity(1);
auto theEntity = soundEnt;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (soundEnt && soundEnt->isSubclassOf(Weapon))
{
Weapon* soundWeapon;
soundWeapon = dynamic_cast<Weapon*>(soundEnt);
theEntity = soundWeapon->GetOwner();
}
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
sensoryPerception->Stimuli(StimuliSound, ev->GetVector(2), soundType);
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_NOISE_HEARD))
{
enemyManager->TryToAddToHateList(theEntity);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//HeardDialog( soundType );
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::BroadcastAlert(float rad)
{
auto currentEnemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!(flags & FlagNotarget))
{
auto enemypos = currentEnemy->centroid;
G_BroadcastAlert(this, centroid, enemypos, rad);
}
}
void Actor::BroadcastAlert(float rad, int soundtype)
{
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
if (!(flags & FlagNotarget))
{
G_BroadcastSound(this, centroid, rad, soundtype);
}
}
void Actor::BroadcastAlert(Event* ev)
{
BroadcastAlert(ev->GetFloat(1), SOUNDTYPE_ALERT);
}
//***********************************************************************************************
//
// Pain and death related functions
//
//***********************************************************************************************
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Pain(Event* ev)
{
auto damage = ev->GetFloat(1);
auto mod = ev->GetInteger(3);
auto position = ev->GetVector(4);
auto direction = ev->GetVector(5);
auto showPain = ev->NumArgs() > 5 ? ev->GetBoolean(6) : false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (deadflag)
{
// Do gib stuff
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (statemap)
{
auto tempState = statemap->FindState("GIB");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (tempState)
tempState = tempState->Evaluate(*this, &conditionals);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagSmallPain);
2015-04-28 20:02:03 +00:00
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)
2015-04-29 20:34:44 +00:00
pain_type = PainBig;
2015-04-28 20:02:03 +00:00
else
2015-04-29 20:34:44 +00:00
pain_type = PainSmall;
AddStateFlag(StateFlagInPain);
2015-04-28 20:02:03 +00:00
}
} else
{
2015-04-29 20:34:44 +00:00
pain_type = PainBig;
AddStateFlag(StateFlagInPain);
2015-04-28 20:02:03 +00:00
}
//-------------------------------------------------------------------
// 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
auto gpm = GameplayManager::getTheGameplayManager();
if (gpm->hasObject(getArchetype()))
{
auto propName = MOD_NumToName(mod);
auto painChanceForActor = 0.0f;
if (strlen(propName))
{
auto objName = getArchetype() + ".PainChance";
if (gpm->hasObject(objName))
{
if (gpm->hasProperty(objName, propName))
painChanceForActor = gpm->getFloatValue(objName, propName);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
showPain = G_Random() <= painChanceForActor ? true : false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagShowPain);
2015-04-28 20:02:03 +00:00
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine pain angles
auto dir = Vector(position - centroid).length() > 1.0f ? centroid - position : direction;
dir = dir * -1.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
pain_angles = dir.toAngles();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
pain_angles[YAW] = AngleNormalize180(angles[YAW] - pain_angles[YAW]);
pain_angles[PITCH] = AngleNormalize180(angles[PITCH] - pain_angles[PITCH]);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::StunEvent(Event* ev)
{
SetActorFlag(ACTOR_FLAG_STUNNED, true);
stunned_end_time = level.time + ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::CheckStun(void)
{
if (GetActorFlag(ACTOR_FLAG_STUNNED) && stunned_end_time <= level.time)
SetActorFlag(ACTOR_FLAG_STUNNED, false);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Dead(Event*)
{
// stop animating legs
animate->StopAnimatingAtEnd();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure we can walk through this guys corpse
edict->clipmask = MASK_DEADSOLID;
if (edict->solid != SOLID_NOT)
setContents(CONTENTS_CORPSE);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (edict->s.torso_anim & ANIM_BLEND)
animate->StopAnimatingAtEnd(torso);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
edict->s.eFlags |= EF_DONT_PROCESS_COMMANDS;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!groundentity && velocity != vec_zero && movetype != MOVETYPE_STATIONARY)
{
// wait until we hit the ground
PostEvent(EV_Actor_Dead, FRAMETIME);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// don't allow them to fly, think, or swim anymore
flags &= ~(FlagSwim | FlagFly);
turnThinkOff();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
deadflag = DeadDead;
setMoveType(MOVETYPE_NONE);
setOrigin(origin);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trigger)
{
trigger->ProcessEvent(EV_Remove);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
PostEvent(EV_Actor_Fade, spawnparent != nullptr ? .5f : 5.0f);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::KilledEffects(Entity* attacker)
{
if (g_debugtargets->integer)
{
G_DebugTargets(this, "Actor::KilledEffects");
}
//
// kill the killtargets
//
auto target_name = KillTarget();
if (target_name && strcmp(target_name, ""))
{
Entity* ent = nullptr;
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, ""))
{
Entity* ent = nullptr;
do
{
ent = G_FindTarget(ent, target_name);
if (!ent)
{
break;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto 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)
{
//
// create the thread, but don't start it yet
//
auto thread = ExecuteThread(kill_thread, false, this);
if (!thread)
warning("Killed", "could not process kill_thread");
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Killed(Event* ev)
{
auto fallingDeath = ev->NumArgs() > 5 ? ev->GetBoolean(5) : false;
auto weapon = ev->NumArgs() > 5 ? ev->GetEntity<Weapon>(6) : nullptr;
auto attacker = ev->GetEntity(1);
auto damage = ev->GetFloat(2);
// 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)
{
auto 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))
{
auto player = dynamic_cast<Player *>(attacker);
//player->IncreaseActionLevel( damage / 4.0f );
// Calculate the number of points for this kill (0, if we're not using,
// the gameplay system).
auto points = 0.0f;
auto* 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))
{
auto actor = dynamic_cast<Actor*>(attacker);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
actor->InContext("killedenemy", 0);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// If we have a behavior going for some reason, kill it now.
// if ( behavior )
// {
// behavior->End(*this);
// behavior = nullptr;
// }
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (attacker)
KilledEffects(attacker);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!GetActorFlag(ACTOR_FLAG_DIE_COMPLETELY))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if means of death should be bumped up from MOD_BULLET to MOD_FAST_BULLET
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (means_of_death == MOD_BULLET)
{
if (bullet_hits < 5)
bullet_hits = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (int(G_Random(100) < bullet_hits * 10))
means_of_death = MOD_FAST_BULLET;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (means_of_death == MOD_ELECTRIC)
{
auto event = new Event(EV_DisplayEffect);
event->AddString("electric");
ProcessEvent(event);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
event = new Event(EV_DisplayEffect);
event->AddString("noelectric");
PostEvent(event, 3.0f + G_Random(2.0f));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (
means_of_death == MOD_EXPLOSION ||
means_of_death == MOD_SMALL_EXPLOSION ||
means_of_death == MOD_PLASMASHOTGUN
)
{
if (G_Random() >= .45)
{
auto attackerToSelf = origin - attacker->origin;
attackerToSelf.z = deathKnockbackVerticalValue;
attackerToSelf.normalize();
attackerToSelf *= deathKnockbackHorizontalValue;
velocity = attackerToSelf;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure all bones are put back in their normal positions
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (edict->s.bone_tag[ActorMouthTag] != -1)
SetControllerAngles(ActorMouthTag, vec_zero);
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (edict->s.bone_tag[ActorHeadTag] != -1)
SetControllerAngles(ActorHeadTag, vec_zero);
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (edict->s.bone_tag[ActorTorsoTag] != -1)
SetControllerAngles(ActorTorsoTag, vec_zero);
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (edict->s.bone_tag[ActorLeyeTag] != -1)
SetControllerAngles(ActorLeyeTag, vec_zero);
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (edict->s.bone_tag[ActorReyeTag] != -1)
SetControllerAngles(ActorReyeTag, vec_zero);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ClassDef* cls;
if (!fallingDeath)
{
// Stop behavior
cls = getClass("idle");
SetBehavior(reinterpret_cast<Behavior *>(cls->newInstance()));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//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");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// don't allow them to fly or swim anymore
flags &= ~(FlagSwim | FlagFly);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
deadflag = DeadDying;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
groundentity = nullptr;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
edict->svflags |= SVF_DEADMONSTER;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
stopStasis();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!GetActorFlag(ACTOR_FLAG_STAYSOLID))
{
edict->clipmask = MASK_DEADSOLID;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (edict->solid != SOLID_NOT)
setContents(CONTENTS_CORPSE);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Stop the actor from talking
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_DIALOG_PLAYING))
{
CancelEventsOfType(EV_SetControllerAngles);
StopSound(CHAN_DIALOG);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Remove the actor from it's group
//groupcoordinator->RemoveEntityFromGroup( this , GetGroupID() );
groupcoordinator->MemberDied(this, GetGroupID());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!fallingDeath)
{
auto deathanim = "death";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_IN_ALCOVE))
deathanim = "death_alcove";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if this actor has a death state in its state machine
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (statemap)
{
auto count = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (behavior)
{
behavior->End(*this);
delete behavior;
behavior = nullptr;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (torsoBehavior)
{
torsoBehavior->End(*this);
delete torsoBehavior;
torsoBehavior = nullptr;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
State* last_temp_state = nullptr;
auto temp_state = statemap->FindState("DEATH");
str newdeathanim;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (temp_state)
{
do
{
count++;
if (count > 50)
{
gi.Error(ERR_DROP, "Stopping due to possible infinite state loop in death state\n");
break;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Process the current state
if (last_temp_state != temp_state)
{
// Get the new animation name
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
str tempAnim = temp_state->getLegAnim(*this, &conditionals);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (tempAnim.length())
newdeathanim = tempAnim;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Process exit commands of the last state
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (last_temp_state)
last_temp_state->ProcessExitCommands(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Process the entry commands of the new state
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
temp_state->ProcessEntryCommands(this);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine the next state to go to
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
last_temp_state = temp_state;
temp_state = temp_state->Evaluate(*this, &conditionals);
} while (last_temp_state != temp_state);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (newdeathanim.length() > 0)
deathanim = newdeathanim;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Play the death animation
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (animate)
{
animate->ClearTorsoAnim();
animate->ClearLegsAnim();
}
SetAnim(deathanim, EV_Actor_Dead, legs);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ChangeAnim();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if we were spawned by another actor
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (spawnparent)
{
spawnparent->num_of_spawns--;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if we should notify others
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_NOTIFY_OTHERS_AT_DEATH))
NotifyOthersOfDeath();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Do Game Specific Death Stuff
if (gameComponent && attacker)
gameComponent->HandleDeath(attacker);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SpawnItems();
DropItemsOnDeath();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (means_of_death == MOD_GIB)
{
if (blood_model.length() == 0)
blood_model = "fx/fx_bspurt.tik";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (max_gibs == 0)
max_gibs = 4;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (auto i = 0; i < 4; i++)
{
auto event = new Event(EV_Sentient_SpawnBloodyGibs);
event->AddInteger(max_gibs);
ProcessEvent(event);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
PostEvent(EV_Remove, 0.0f);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//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);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::RespondToHitscan(void)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
//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)
{
auto 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;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Lastly clear our flag
SetActorFlag(ACTOR_FLAG_INCOMING_HITSCAN, false);
SetActorFlag(ACTOR_FLAG_RESPONDING_TO_HITSCAN, false);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::HandleGameSpecificEvent(Event* ev)
{
if (gameComponent)
gameComponent->HandleEvent(ev);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetHitscanResponse(Event* ev)
{
hitscan_response_chance = ev->GetFloat(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (hitscan_response_chance > 1.0f)
hitscan_response_chance = 1.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (hitscan_response_chance < 0.0f)
hitscan_response_chance = 0.0f;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetDieCompletely(Event* ev)
{
SetActorFlag(ACTOR_FLAG_DIE_COMPLETELY, ev->GetBoolean(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetBleedAfterDeath(Event* ev)
{
SetActorFlag(ACTOR_FLAG_BLEED_AFTER_DEATH, ev->GetBoolean(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SpawnGib(Event* ev)
{
RealSpawnGib(false, ev);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SpawnGibAtTag(Event* ev)
{
RealSpawnGib(true, ev);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::RealSpawnGib(qboolean use_tag, Event* ev)
{
Vector final_gib_offset;
Vector orig;
int current_arg;
float final_pitch;
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_model_name;
qboolean at_least_one_visible_surface = false;
int surface_length;
orientation_t orn;
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)
{
str attach_tag_name = ev->GetString(1);
auto raw_offset = ev->GetFloat(2);
width = ev->GetFloat(3);
// Get all the tag information
auto tagnum = gi.Tag_NumForName(edict->s.modelindex, attach_tag_name.c_str());
if (tagnum == -1)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
GetRawTag(tagnum, &orn);
GetTag(tagnum, &real_tag_pos, &real_tag_dir);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
real_tag_angles = real_tag_dir.toAngles();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine the final pitch of the gib
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
final_pitch = AngleNormalize180(angles[PITCH] - real_tag_angles[PITCH]);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine the offset of the gib
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Vector raw_offset_dir = orn.axis[0];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
MatrixTransformVector(offset, orientation, orig);
orig += origin;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine the mass
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto m = mass;
if (m < 50.0f)
m = 50.0f;
else if (m > 250.0f)
m = 250.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine which blood spurt & splat to use for the gib
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto blood_name = GetBloodSpurtName();
auto blood_splat_name = GetBloodSplatName();
auto blood_splat_size = GetBloodSplatSize();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
use_blood = blood_name.length() && blood_splat_name.length();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_BLEED_AFTER_DEATH))
blood_model_name = blood_model;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the mins and maxs for this gib
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto gib_mins = Vector(-width, -width, -width);
auto gib_maxs = Vector(width, width, width);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure we can spawn in a gib here
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto trace = G_Trace(orig, gib_mins, gib_maxs, orig, nullptr, MASK_DEADSOLID, false, "spawngib");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.allsolid || trace.startsolid)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure at least one of the surfaces is not hidden
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (current_arg = 5; current_arg <= ev->NumArgs(); current_arg++)
{
current_arg_str = ev->GetString(current_arg);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (current_surface = 0; current_surface < numsurfaces; current_surface++)
{
current_surface_name = gi.Surface_NumToName(edict->s.modelindex, current_surface);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
surface_length = current_arg_str[strlen(current_arg_str) - 1] == '*' ? strlen(current_arg_str) - 1 : strlen(current_arg_str);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (Q_stricmpn(current_surface_name, current_arg_str, surface_length) == 0)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (!(edict->s.surfaces[current_surface] & MDL_SURFACE_NODRAW))
at_least_one_visible_surface = true;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!at_least_one_visible_surface)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine time till it hits the ground
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto vel = 100.0f + G_Random(200.0f * (2.0f - (m - 50.0f) / 200.0f));
auto time = SpawnGetTime(vel, orig, gib_mins, gib_maxs);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Flip final pitch 180 degrees?
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (G_Random() > .5f)
{
final_pitch += 180.0f;
final_pitch = AngleNormalize360(final_pitch);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Calculate the pitch change and velocity
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto pitch_change = AngleNormalize180(final_pitch - angles[PITCH]);
auto pitch_vel = pitch_change / time;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Spawn in the hidden gib
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto gib = new Gib("", use_blood, blood_name, blood_model_name, blood_splat_name, blood_splat_size, final_pitch);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->setOrigin(orig);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->angles[PITCH] = angles[PITCH];
gib->angles[ROLL] = 0;
gib->angles[YAW] = use_tag ? real_tag_angles[YAW] : angles[YAW];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->setAngles(gib->angles);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->setSize(gib_mins, gib_maxs);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->edict->svflags |= SVF_DEADMONSTER;
gib->edict->clipmask = MASK_DEADSOLID;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->setSolidType(SOLID_BBOX);
gib->setContents(CONTENTS_SHOOTABLE_ONLY);
gib->link();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Spawn in the visible gib
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto ent = new Entity(EntityCreateFlagAnimate);
ent->setModel(model);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure the init stuff in the tiki get processed now
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//ent->ProcessPendingEvents();
ent->CancelPendingEvents();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure no client side commands are processed
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ent->edict->s.eFlags |= EF_DONT_PROCESS_COMMANDS;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Set the animation to the current anim & frame of the actor
ent->animate->RandomAnimate(animate->AnimName());
ent->animate->SetFrame(CurrentFrame());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ent->setAngles(angles);
ent->bind(gib, true);
ent->gravity = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
final_gib_offset = offset * -1.0f;
ent->setOrigin(final_gib_offset);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ent->setMoveType(MOVETYPE_BOUNCE);
ent->setSolidType(SOLID_NOT);
ent->showModel();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Hide all of the surfaces on the gib
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ent->SurfaceCommand("all", "+nodraw");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
cap_name = ev->GetString(4);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Show the cap surface on the gib and the actor
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (cap_name.length())
{
ent->SurfaceCommand(cap_name.c_str(), "-nodraw");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SurfaceCommand(cap_name.c_str(), "-nodraw");
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Show and hide all of the rest of the necessary surfaces
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (current_arg = 5; current_arg <= ev->NumArgs(); current_arg++)
{
current_arg_str = ev->GetString(current_arg);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (current_arg_str[strlen(current_arg_str) - 1] == '*')
surface_length = strlen(current_arg_str) - 1;
else
surface_length = strlen(current_arg_str);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (current_surface = 0; current_surface < numsurfaces; current_surface++)
{
current_surface_name = gi.Surface_NumToName(edict->s.modelindex, current_surface);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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");
}
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure the gibs go away after a while
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Mark the spawn as being successful
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_SPAWN_FAILED, false);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Play the severed sound
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ent->Sound("impact_sever", CHAN_BODY);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SpawnNamedGib(Event* ev)
{
SetActorFlag(ACTOR_FLAG_SPAWN_FAILED, true);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_FADING_OUT))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get all of the parameters
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto gib_name = ev->GetString(1);
auto tag_name = ev->GetString(2);
auto final_pitch = ev->GetFloat(3);
auto width = ev->GetFloat(4);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the tag position
Vector orig;
GetTag(tag_name, &orig);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the mins and maxs for this gib
auto gib_mins = Vector(-width, -width, -width);
auto gib_maxs = Vector(width, width, width);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure we can spawn in a gib here
auto trace = G_Trace(orig, gib_mins, gib_maxs, orig, nullptr, MASK_DEADSOLID, false, "spawnnamedgib1");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.allsolid || trace.startsolid)
SetActorFlag(ACTOR_FLAG_SPAWN_FAILED, true);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine time till it hits the ground
auto vel = 400.0f + G_Random(400.0f);
auto time = SpawnGetTime(vel, orig, gib_mins, gib_maxs);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto pitch_change = AngleNormalize180(final_pitch - angles[PITCH]);
auto pitch_vel = pitch_change / time;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Spawn the gib
auto gib = new Gib(gib_name, false, "", "", "", final_pitch);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->setOrigin(orig);
gib->setAngles(angles);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->velocity = Vector(G_CRandom(200.0f), G_CRandom(200.0f), vel);
gib->avelocity = Vector(pitch_vel, G_CRandom(300.0f), 0.0f);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->edict->svflags |= SVF_DEADMONSTER;
gib->edict->clipmask = MASK_DEADSOLID;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gib->setSolidType(SOLID_BBOX);
gib->setContents(CONTENTS_CORPSE);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure the gib goes away after a while
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Mark the spawn as being successful
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_SPAWN_FAILED, false);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
float Actor::SpawnGetTime(float vel, const Vector& orig, const Vector& gib_mins, const Vector& gib_maxs)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto grav = -sv_currentGravity->value;
auto end_pos = orig;
end_pos[2] = -10000.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto trace = G_Trace(orig, gib_mins, gib_maxs, end_pos, nullptr, MASK_DEADSOLID, false, "SpawnGetTime");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
end_pos = trace.endpos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto dir = end_pos - orig;
auto dist = dir.length();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto time = (grav / -20.0f - vel) / grav;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto other = sqrt((grav / 20.0f + vel) * (grav / 20.0f + vel) - 2.0f * grav * dist);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
time = time - other / grav;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return time;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SpawnBlood(Event* ev)
{
if (!com_blood->integer)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto blood_name = ev->GetString(1);
auto tag_name = ev->GetString(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if we care about the last spawn working or not
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto use_last_result = ev->NumArgs() > 2 ? ev->GetBoolean(3) : false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (use_last_result && GetActorFlag(ACTOR_FLAG_SPAWN_FAILED))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Spawn the blood
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto attach_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*)
{
PostEvent(EV_FadeOut, 5.0f);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetPainThresholdEvent(Event* ev)
{
pain_threshold = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetKillThreadEvent(Event* ev)
{
kill_thread = ev->GetString(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DeathFadeEvent(Event*)
{
SetActorFlag(ACTOR_FLAG_DEATHFADE, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::setDeathEffect(Event* ev)
{
_deathEffect = ev->GetString(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DeathShrinkEvent(Event*)
{
SetActorFlag(ACTOR_FLAG_DEATHSHRINK, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DeathSinkEvent(Event*)
{
SetActorFlag(ACTOR_FLAG_DEATHSINK, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::StaySolidEvent(Event*)
{
SetActorFlag(ACTOR_FLAG_STAYSOLID, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Suicide(Event* ev)
{
health = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean use_last_mod = ev->NumArgs() > 0 ? ev->GetBoolean(1) : false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!use_last_mod)
means_of_death = MOD_SUICIDE;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto event = new Event(EV_Killed);
event->AddEntity(this);
event->AddInteger(0);
event->AddEntity(this);
event->AddInteger(MOD_SUICIDE);
ProcessEvent(event);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetDeathSize(Event* ev)
{
auto death_min = ev->GetVector(1);
auto death_max = ev->GetVector(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure actor will not be stuck if we change the bounding box
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto trace = G_Trace(origin, death_min, death_max, origin, this, edict->clipmask, false, "Actor::SetDeathSize");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!trace.startsolid)
{
setSize(death_min, death_max);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Try again, calculate a smaller death bounding box
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
death_min = (death_min + mins) * .5f;
death_max = (death_max + maxs) * .5f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(origin, death_min, death_max, origin, this, edict->clipmask, false, "Actor::SetDeathSize2");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!trace.startsolid)
setSize(death_min, death_max);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::FadeEvent(Event*)
{
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)
{
auto newEvent = new Event(EV_DisplayEffect);
newEvent->AddString(_deathEffect);
ProcessEvent(newEvent);
PostEvent(EV_Remove, 5.0f);
} else
SetActorFlag(ACTOR_FLAG_FADING_OUT, false);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//***********************************************************************************************
2012-12-30 16:37:54 +00:00
//
2015-04-28 20:02:03 +00:00
// Movement functions
2012-12-30 16:37:54 +00:00
//
2015-04-28 20:02:03 +00:00
//***********************************************************************************************
void Actor::SimplePathfinding(Event*)
{
SetActorFlag(ACTOR_FLAG_SIMPLE_PATHFINDING, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetCanWalkOnOthers(Event*)
{
SetActorFlag(ACTOR_FLAG_CAN_WALK_ON_OTHERS, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetFeetWidth(Event* ev)
{
feet_width = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ForwardSpeedEvent(Event* ev)
{
movementSubsystem->setForwardSpeed(ev->GetFloat(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SwimEvent(Event*)
{
flags &= ~FlagFly;
flags |= FlagSwim;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::FlyEvent(Event* ev)
{
if (ev->NumArgs() == 0)
{
// Turn flying on
flags &= ~FlagSwim;
flags |= FlagFly;
} else
{
if (ev->GetBoolean(1))
{
// Turn flying on
flags &= ~FlagSwim;
flags |= FlagFly;
} else
{
// Turn flying off
flags &= ~FlagFly;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::NotLandEvent(Event*)
{
flags &= FlagSwim | FlagFly;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Push(Event* ev)
{
movementSubsystem->Push(ev->GetVector(1));
}
void Actor::Push(const Vector& dir)
{
movementSubsystem->Push(dir);
}
void Actor::Pushable(Event* ev)
{
SetActorFlag(ACTOR_FLAG_PUSHABLE, ev->NumArgs() ? ev->GetBoolean(1) : true);
}
//***********************************************************************************************
2012-12-30 16:37:54 +00:00
//
2015-04-28 20:02:03 +00:00
// Debug functions
2012-12-30 16:37:54 +00:00
//
2015-04-28 20:02:03 +00:00
//***********************************************************************************************
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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 : nullptr -- was '%s'\n", currentBehavior.c_str());
if (headBehavior)
gi.Printf("Head Behavior : %s\n", headBehavior->getClassname());
else
gi.Printf("Head Behavior : nullptr -- was '%s'\n", currentHeadBehavior.c_str());
if (eyeBehavior)
gi.Printf("Eye Behavior : %s\n", eyeBehavior->getClassname());
else
gi.Printf("Eye Behavior : nullptr -- was '%s'\n", currentEyeBehavior.c_str());
if (torsoBehavior)
gi.Printf("Torso Behavior : %s\n", torsoBehavior->getClassname());
else
gi.Printf("Torso Behavior : nullptr -- 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");
2015-04-29 20:34:44 +00:00
if (mode == ActorModeIdle)
2015-04-28 20:02:03 +00:00
gi.Printf("Mode : IDLE\n");
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
gi.Printf("Mode : AI\n");
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeScript)
2015-04-28 20:02:03 +00:00
gi.Printf("Mode : SCRIPT\n");
2015-04-29 20:34:44 +00:00
else if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
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
auto 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 DeadNo:
gi.Printf("deadflag: NO\n");
break;
case DeadDying:
gi.Printf("deadflag: DYING\n");
break;
case DeadDead:
gi.Printf("deadflag: DEAD\n");
break;
case DeadRespawnable:
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");
}
}
2012-12-30 16:37:54 +00:00
//***********************************************************************************************
//
2015-04-28 20:02:03 +00:00
// Stimuli functions
2012-12-30 16:37:54 +00:00
//
//***********************************************************************************************
2015-04-28 20:02:03 +00:00
void Actor::TurnAIOn(Event*)
{
TurnAIOn();
}
void Actor::TurnAIOn(void)
{
if (GetActorFlag(ACTOR_FLAG_AI_ON))
return;
SetActorFlag(ACTOR_FLAG_AI_ON, true);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (sensoryPerception)
2015-04-29 20:34:44 +00:00
sensoryPerception->RespondTo(StimuliAll, true);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
EndMode();
2015-04-29 20:34:44 +00:00
mode = ActorModeAi;
2015-04-28 20:02:03 +00:00
Wakeup();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TurnAIOff(Event*)
{
TurnAIOff();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TurnAIOff(void)
{
SetActorFlag(ACTOR_FLAG_AI_ON, false);
if (sensoryPerception)
2015-04-29 20:34:44 +00:00
sensoryPerception->RespondTo(StimuliNone, true);
2015-04-28 20:02:03 +00:00
2015-04-29 20:34:44 +00:00
if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
{
// 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(nullptr);
enemyManager->LockOnCurrentEnemy(false);
EndMode();
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ActivateAI(void)
{
if (!statemap && !fuzzyEngine && !masterstatemap)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
last_time_active = level.time;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (mode == ActorModeAi || mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
return;
2015-04-29 20:34:44 +00:00
StartMode(ActorModeAi);
2015-04-28 20:02:03 +00:00
if (fuzzyEngine)
SetState("START");
if (sensoryPerception)
2015-04-29 20:34:44 +00:00
sensoryPerception->RespondTo(StimuliAll, true);
2015-04-28 20:02:03 +00:00
if (activate_thread.length())
RunThread(activate_thread);
}
void Actor::SetIdleThread(Event* ev)
{
idle_thread = ev->GetString(1);
}
//***********************************************************************************************
//
// Targeting functions
//
//***********************************************************************************************
void AI_SenseEnemies(void)
{
// process the list in reverse order in case SleepList is changed
for (auto i = SleepList.NumObjects(); i > 0; i--)
{
auto actor = SleepList.ObjectAt(i);
if (actor)
actor->sensoryPerception->SenseEnemies();
}
}
//*********************************************************************************************
//
// GetPlayer
//
//*********************************************************************************************
Player* GetPlayer(int index)
{
if (index > game.maxclients)
return nullptr;
auto ed = &g_entities[index];
if (!ed->inuse || !ed->entity)
return nullptr;
return dynamic_cast<Player *>(g_entities[index].entity);
}
//***********************************************************************************************
//
// Actor checks
//
//***********************************************************************************************
// Temporary
qboolean Actor::checkInAIMode(Conditional&)
{
2015-04-29 20:34:44 +00:00
return mode == ActorModeAi;
2015-04-28 20:02:03 +00:00
}
void Actor::checkActorDead(Event* ev)
{
auto act = ev->GetEntity<Actor>(1);
ev->ReturnInteger(act != nullptr ? act->checkActorDead() : false);
}
qboolean Actor::checkActorDead()
{
return deadflag || health <= 0;
}
qboolean Actor::checkanimname(Conditional& condition)
{
int32_t use_length;
int32_t result;
str anim_name_test = condition.getParm(1);
if (animname.length() == 0 || anim_name_test.length() == 0)
return false;
use_length = condition.numParms() > 1 ? atoi(condition.getParm(2)) : false;
result = use_length ? strncmp(animname.c_str(), anim_name_test.c_str(), anim_name_test.length()) : strcmp(animname.c_str(), anim_name_test.c_str());
return result == 0;
}
qboolean Actor::checkActorFlag(Conditional& condition)
{
return GetActorFlag(condition.getParm(1));
}
qboolean Actor::checkinactive(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_INACTIVE);
}
qboolean Actor::checkanimdone(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_ANIM_DONE);
}
qboolean Actor::checktorsoanimdone(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_TORSO_ANIM_DONE);
}
qboolean Actor::checkdead(Conditional&)
{
return deadflag != 0;
}
qboolean Actor::checkhaveenemy(Conditional&)
{
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (currentEnemy && IsEntityAlive(currentEnemy))
return true;
if (enemyManager->getEnemyCount())
return true;
return false;
}
qboolean Actor::checkenemydead(Conditional&)
{
return checkenemydead();
}
qboolean Actor::checkenemydead(void)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
if (currentEnemy->deadflag || currentEnemy->health <= 0)
return true;
return false;
}
qboolean Actor::checkenemynoclip(Conditional&)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
if (currentEnemy->movetype == MOVETYPE_NOCLIP)
return true;
return false;
}
qboolean Actor::checkcanseeenemy(Conditional& condition)
{
qboolean real_can_see;
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
auto use_fov = condition.numParms() > 0 ? atoi(condition.getParm(1)) : true;
// See if we should check again
if (canseeenemy_time > level.time)
{
return GetActorFlag(use_fov ? ACTOR_FLAG_LAST_CANSEEENEMY : ACTOR_FLAG_LAST_CANSEEENEMY_NOFOV);
}
auto can_see = true;
auto 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);
real_can_see = GetActorFlag(use_fov ? ACTOR_FLAG_LAST_CANSEEENEMY : 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)
{
// Get our current enemy
auto player = GetPlayer(0);
if (!player)
return false;
auto use_fov = condition.numParms() > 0 ? atoi(condition.getParm(1)) : true;
// See if we should check again
if (canseeplayer_time > level.time)
{
return GetActorFlag(use_fov ? ACTOR_FLAG_LAST_CANSEEPLAYER : ACTOR_FLAG_LAST_CANSEEPLAYER_NOFOV);
}
auto can_see = true;
auto 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);
auto real_can_see = GetActorFlag(use_fov ? ACTOR_FLAG_LAST_CANSEEPLAYER : 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&)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
return combatSubsystem->CanAttackTarget(currentEnemy);
}
qboolean Actor::checkCanAttackAnyEnemy(Conditional&)
{
return enemyManager->CanAttackAnyEnemy();
}
qboolean Actor::checkenemyinfov(Conditional& condition)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
if (!sensoryPerception)
return false;
if (condition.numParms() > 0)
{
auto check_fov = float(atof(condition.getParm(1)));
auto check_fovdot = float(cos(check_fov * 0.5 * M_PI / 180.0));
return sensoryPerception->InFOV(currentEnemy->centroid, check_fov, check_fovdot);
}
return sensoryPerception->InFOV(currentEnemy);
}
qboolean Actor::checkenemyonground(Conditional&)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
if (currentEnemy->groundentity)
return true;
return false;
}
qboolean Actor::checkenemyrelativeyaw(Conditional& condition)
{
float check_yaw_max;
qboolean use_range;
auto currentEnemy = GetPlayer(0);
auto 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;
}
auto dir = origin - currentEnemy->origin;
auto dir_angles = dir.toAngles();
auto 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;
return false;
}
if (relative_yaw < check_yaw_min)
return true;
return false;
}
qboolean Actor::checkenemyyawrange(Conditional& condition)
{
//This function will return true if the the currentEnemy is within the
//angles passed in.
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
auto check_yaw_min = float(atof(condition.getParm(1)));
auto 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);
auto dir = currentEnemy->origin - origin;
auto dirYaw = dir.toYaw();
auto originYaw = angles[YAW];
auto 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;
return false;
}
qboolean Actor::checkcanjumptoenemy(Conditional&)
{
// 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&)
{
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&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagInPain;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checksmallpain(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagSmallPain;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkpainyaw(Conditional& condition)
{
auto check_yaw = float(atof(condition.getParm(1)));
if (pain_angles[YAW] <= check_yaw)
return true;
return false;
}
qboolean Actor::checkpainpitch(Conditional& condition)
{
auto check_pitch = float(atof(condition.getParm(1)));
if (pain_angles[PITCH] <= check_pitch)
return true;
return false;
}
qboolean Actor::checkstunned(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_STUNNED);
}
qboolean Actor::checkfinished(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_FINISHED);
}
qboolean Actor::checkmeleehit(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagMeleeHit;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkblockedhit(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagBlockedHit;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkblocked(Conditional&)
{
if (attack_blocked && attack_blocked_time + .75 > level.time)
{
attack_blocked = false;
return true;
}
return false;
}
qboolean Actor::checkonfire(Conditional&)
{
return on_fire;
}
qboolean Actor::checkotherdied(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagOtherDied;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkstuck(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagStuck;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checknopath(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagNoPath;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkbehaviordone(Conditional&)
{
return behavior == nullptr;
}
qboolean Actor::checkheadbehaviordone(Conditional&)
{
return headBehavior == nullptr;
}
qboolean Actor::checkeyebehaviordone(Conditional&)
{
return eyeBehavior == nullptr;
}
qboolean Actor::checktorsobehaviordone(Conditional&)
{
return torsoBehavior == nullptr;
}
qboolean Actor::checktorsobehaviorfailed(Conditional&)
{
return torsoBehaviorCode != BEHAVIOR_SUCCESS && torsoBehaviorCode != BEHAVIOR_EVALUATING;
}
qboolean Actor::checktorsobehaviorsuccess(Conditional&)
{
return torsoBehaviorCode == BEHAVIOR_SUCCESS;
}
qboolean Actor::checktimedone(Conditional&)
{
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)
{
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
enemyManager->FindHighestHateEnemy();
if (!currentEnemy)
return false;
auto range = float(atof(condition.getParm(1)));
auto max_height = condition.numParms() > 1 ? float(atof(condition.getParm(2))) : 0.0f;
auto min_height = condition.numParms() > 2 ? float(atof(condition.getParm(3))) : -max_height;
auto XYOnly = condition.numParms() > 3 ? atoi(condition.getParm(4)) : 0;
// Stupid compiler warning complaints about forcing an int to bool
// So that's why this BS is here
auto onlyXY = XYOnly != 0;
return EntityInRange(currentEnemy, range, min_height, max_height, onlyXY);
}
qboolean Actor::checkEnemyAttached(Conditional&)
{
return haveAttached;
}
qboolean Actor::checkparentrange(Conditional& condition)
{
if (!spawnparent)
return false;
auto range = float(atof(condition.getParm(1)));
auto height = condition.numParms() == 2 ? float(atof(condition.getParm(2))) : 0.0f;
auto act = spawnparent;
if (EntityInRange(act, range, -height, height))
{
return true;
}
return false;
}
qboolean Actor::checkplayerrange(Conditional& condition)
{
auto range = float(atof(condition.getParm(1)));
auto height = condition.numParms() == 2 ? float(atof(condition.getParm(2))) : 0.0f;
auto XYOnly = condition.numParms() == 3 && atoi(condition.getParm(3)) != 0;
for (auto i = 0; i < game.maxclients; i++)
{
if (EntityInRange(GetPlayer(i), range, -height, height, XYOnly))
{
return true;
}
}
return false;
}
qboolean Actor::checkplayerrange(float range, float height)
{
for (auto i = 0; i < game.maxclients; i++)
{
if (EntityInRange(GetPlayer(i), range, -height, height))
{
return true;
}
}
return false;
}
qboolean Actor::checkmovingactorrange(Conditional& condition)
{
// Get distances
auto range = condition.getParm<float>(1);
auto height = condition.numParms() == 2 ? condition.getParm<float>(2) : 0.0f;
auto r2 = range * range;
if (actorrange_time > level.time && height == last_height)
{
auto ent_in_range = last_ent;
if (IsEntityAlive(ent_in_range))
{
auto delta = origin - ent_in_range->centroid;
if (height)
{
auto 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 = nullptr;
auto smallest_dist2 = 99999999.0f;
// See if any clients are in range
for (auto i = 0; i < game.maxclients; i++)
{
auto ed = &g_entities[i];
if (!ed->inuse || !ed->entity)
{
continue;
}
auto ent_in_range = ed->entity;
if (IsEntityAlive(ent_in_range))
{
auto delta = origin - ent_in_range->centroid;
if (height > 0.0f)
{
auto height_diff = delta[2];
if (height_diff < -height || height_diff > height)
{
continue;
}
delta[2] = 0;
}
// dot product returns length squared
auto 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 (auto i = 1; i <= ActiveList.NumObjects(); i++)
{
auto 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 & FlagNotarget)
)
{
auto delta = origin - ent_in_range->centroid;
if (height > 0.0f)
{
auto height_diff = delta[2];
if (height_diff < -height || height_diff > height)
{
continue;
}
delta[2] = 0.0f;
}
// dot product returns length squared
auto 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)
{
auto checkedChance = false;
auto percent_chance = condition.getParm<float>(1);
//Stupid crazy conversion here, not because I am stupid, but because the
//compiler is...
if (condition.numParms() > 1)
{
auto value = condition.getParm<int>(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)
{
auto time_to_wait = condition.getParm<float>(1);
return state_time + time_to_wait < level.time;
}
qboolean Actor::checktimesdone(Conditional& condition)
{
return times_done == condition.getParm<int>(1);
}
qboolean Actor::checkmeansofdeath(Conditional& condition)
{
auto mod = MOD_NameToNum(condition.getParm(1));
return mod == means_of_death;
}
qboolean Actor::checknoiseheard(Conditional& condition)
{
if (!sensoryPerception)
return false;
if (condition.numParms() > 0)
{
auto soundTypeIdx = Soundtype_string_to_int(condition.getParm(1));
if (soundTypeIdx == sensoryPerception->GetLastSoundType())
{
//Clear our soundtype
sensoryPerception->SetLastSoundType(SOUNDTYPE_NONE);
return true;
}
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)
{
2015-04-29 20:34:44 +00:00
flag = StateFlagInPain;
2015-04-28 20:02:03 +00:00
} else if (stricmp(flag_name, "small_pain") == 0)
{
2015-04-29 20:34:44 +00:00
flag = StateFlagSmallPain;
2015-04-28 20:02:03 +00:00
} else if (stricmp(flag_name, "melee_hit") == 0)
{
2015-04-29 20:34:44 +00:00
flag = StateFlagMeleeHit;
2015-04-28 20:02:03 +00:00
} else if (stricmp(flag_name, "touched") == 0)
{
2015-04-29 20:34:44 +00:00
flag = StateFlagTouched;
2015-04-28 20:02:03 +00:00
} else if (stricmp(flag_name, "activated") == 0)
{
2015-04-29 20:34:44 +00:00
flag = StateFlagActivated;
2015-04-28 20:02:03 +00:00
} else if (stricmp(flag_name, "used") == 0)
{
2015-04-29 20:34:44 +00:00
flag = StateFlagUsed;
2015-04-28 20:02:03 +00:00
} else if (stricmp(flag_name, "twitch") == 0)
{
2015-04-29 20:34:44 +00:00
flag = StateFlagTwitch;
2015-04-28 20:02:03 +00:00
} 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 = dynamic_cast<Actor *>(partent);
if (partact && partact->part_name == part_name)
{
if (part->state_flags & flag)
{
return true;
}
}
}
return false;
}
qboolean Actor::checkpartdead(Conditional& condition)
{
auto part = FindPartActor(condition.getParm(1));
if (!part)
return false;
return part->deadflag || part->health <= 0;
}
qboolean Actor::checknumspawns(Conditional& condition)
{
return num_of_spawns < condition.getParm<int>(1);
}
qboolean Actor::checkcommand(Conditional& condition)
{
return command == condition.getParm(1);
}
qboolean Actor::checktouched(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagTouched;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checktouchedbyplayer(Conditional&)
{
return checktouchedbyplayer();
}
qboolean Actor::checktouchedbyplayer()
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagTouchedByPlayer;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkInTheWay(Conditional&)
{
return checkInTheWay();
}
qboolean Actor::checkInTheWay()
{
2015-04-29 20:34:44 +00:00
if (state_flags & StateFlagInTheWay)
2015-04-28 20:02:03 +00:00
return true;
2015-04-29 20:34:44 +00:00
if (state_flags & StateFlagTouchedByPlayer)
2015-04-28 20:02:03 +00:00
return true;
return false;
}
qboolean Actor::checkactivated(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagActivated;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkused(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagUsed;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checktwitch(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagTwitch;
2015-04-28 20:02:03 +00:00
}
qboolean Actor::checkhealth(Conditional& condition)
{
return health < condition.getParm<float>(1);
}
qboolean Actor::checkonground(Conditional&)
{
CheckGround();
return groundentity != nullptr;
}
qboolean Actor::checkinwater(Conditional&)
{
return waterlevel > 0;
}
qboolean Actor::checkincomingmeleeattack(Conditional&)
{
return checkincomingmeleeattack();
}
qboolean Actor::checkincomingmeleeattack()
{
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
if (IsEntityAlive(currentEnemy))
{
if (currentEnemy->isSubclassOf(Sentient))
{
auto enemy = dynamic_cast<Sentient *>(currentEnemy);
if (enemy->in_melee_attack)
{
Vector forward;
enemy->angles.AngleVectors(&forward);
auto end_pos = forward * 160.0f + enemy->centroid;
auto 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&)
{
//Entity *enemy_ent;
Sentient* enemy;
trace_t trace;
Vector forward;
// Get our current enemy
Entity* currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
if (IsEntityAlive(currentEnemy))
{
if (currentEnemy->isSubclassOf(Sentient))
{
enemy = dynamic_cast<Sentient *>(currentEnemy);
if (enemy->in_ranged_attack)
{
enemy->angles.AngleVectors(&forward);
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)
{
auto time = condition.numParms() == 1 ? condition.getParm<float>(1) : 0;
if (incoming_proj && incoming_time <= level.time)
{
Vector forward;
incoming_proj->angles.AngleVectors(&forward);
auto end_pos = forward * 1000.0f + incoming_proj->centroid;
auto 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)
{
auto dir = trace.endpos - incoming_proj->centroid;
auto dist = dir.length();
auto time_left = dist / incoming_proj->velocity.length();
return time_left <= time;
}
return true;
}
}
return false;
}
qboolean Actor::checkenemystunned(Conditional&)
{
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
if (IsEntityAlive(currentEnemy))
{
if (currentEnemy->isSubclassOf(Sentient))
{
auto enemy = dynamic_cast<Sentient *>(currentEnemy);
if (enemy->in_stun)
return true;
}
}
return false;
}
qboolean Actor::checkenemyinpath(Conditional&)
{
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 == condition.getParm<int>(1);
}
qboolean Actor::checkheld(Conditional&)
{
return edict->s.parent != ENTITYNUM_NONE;
}
qboolean Actor::checkenemymelee(Conditional&)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
return EntityHasFireType(currentEnemy, FT_MELEE);
}
qboolean Actor::checkenemyranged(Conditional&)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
return EntityHasFireType(currentEnemy, FT_BULLET) || EntityHasFireType(currentEnemy, FT_PROJECTILE);
}
qboolean Actor::checkplayerranged(Conditional&)
{
return checkplayerranged();
}
qboolean Actor::checkplayerranged()
{
auto 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&)
{
return GetActorFlag(ACTOR_FLAG_AT_COVER_NODE);
}
qboolean Actor::checkallowhangback(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_ALLOW_HANGBACK);
}
qboolean Actor::checkname(Conditional& condition)
{
return name == condition.getParm(1);
}
qboolean Actor::checkVar(Conditional& condition)
{
auto varName = condition.getParm(1);
auto varValue = condition.getParm(2);
for (auto i = 1; i <= stateVarList.NumObjects(); i++)
{
auto checkVar = stateVarList.ObjectAt(i);
if (!stricmp(checkVar->varName, varName))
{
if (checkVar->varValue == varValue)
return true;
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)
{
auto varName = condition.getParm(1);
auto varTime = atof(condition.getParm(2));
for (auto i = 1; i <= stateVarList.NumObjects(); i++)
{
auto checkVar = stateVarList.ObjectAt(i);
if (!stricmp(checkVar->varName, varName))
{
if (level.time - checkVar->varTime >= varTime)
return true;
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)
{
auto testNode = thePathManager.FindNode(condition.getParm(1) + str("0"));
if (testNode)
{
return true;
}
return false;
}
qboolean Actor::checkCoverNodes(Conditional&)
{
for (auto i = 1; i <= thePathManager.NumberOfSpecialNodes(); i++)
{
auto 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)
{
if (last_surface_hit == -1)
return false;
auto surface_number = gi.Surface_NameToNum(edict->s.modelindex, condition.getParm(1));
if (surface_number == last_surface_hit)
return true;
return false;
}
qboolean Actor::checkBoneDamaged(Conditional& condition)
{
if (saved_bone_hit == -9999)
return false;
auto bone_number = gi.Tag_NumForName(edict->s.modelindex, condition.getParm(1));
if (bone_number == saved_bone_hit)
return true;
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&)
{
return GetActorFlag(ACTOR_FLAG_CAPTURED);
}
qboolean Actor::checkCanWalkForward(Conditional& condition)
{
Vector endpos;
angles.AngleVectors(&endpos); //Get Forward direction
endpos += origin;
endpos.z += 50.0f; // Pull it off the ground a little
auto range = condition.getParm<float>(1);
endpos *= range;
auto startpos = origin;
startpos.z += 50.0f;
auto trace = G_Trace(startpos, mins, maxs, endpos, this, edict->clipmask, false, "Actor::start");
if (trace.fraction == 1.0f)
return true;
return false;
}
qboolean Actor::checkHasThrowObject(Conditional&)
{
return haveThrowObject;
}
qboolean Actor::checkEnemyIsThrowObject(Conditional&)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
auto ent = currentEnemy;
if (ent->isSubclassOf(ThrowObject))
return true;
return false;
}
qboolean Actor::checkTurretMode(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_TURRET_MODE);
}
qboolean Actor::checkGameSpecific(Conditional& condition)
{
if (!gameComponent)
return false;
return gameComponent->DoCheck(condition);
}
qboolean Actor::checkWeaponReady(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_WEAPON_READY);
}
qboolean Actor::checkMeleeHitWorld(Conditional&)
{
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&)
{
auto 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"))
{
auto player = GetPlayer(0);
if (!player)
return false;
auto dist = origin - player->origin;
auto length = dist.length();
auto 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"))
{
auto player = GetPlayer(0);
if (!player)
return false;
auto dist = origin - player->origin;
auto length = dist.length();
if (length < preferredMax && length > preferredMin)
return true;
}
return false;
}
qboolean Actor::checkCrippled(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_CRIPPLED);
}
qboolean Actor::checkDisabled(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_DISABLED);
}
qboolean Actor::checkInAlcove(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_IN_ALCOVE);
}
qboolean Actor::checkPlayerInCallVolume(Conditional&)
{
return GetActorFlag(ACTOR_FLAG_PLAYER_IN_CALL_VOLUME);
}
qboolean Actor::checkInCallVolume(Conditional&)
{
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&)
{
return GetActorFlag(ACTOR_FLAG_OUT_OF_TORSO_RANGE);
}
qboolean Actor::returntrue(Conditional&)
{
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)
{
auto objname = condition.getParm(1);
auto scopestr = !strlen(objname) ? getArchetype() : getArchetype() + "." + objname;
auto gpm = GameplayManager::getTheGameplayManager();
if (!gpm->hasObject(scopestr))
return false;
auto propname = condition.numParms() > 1 ? condition.getParm(2) : "";
auto chance = strlen(propname) ? gpm->getFloatValue(scopestr, propname) : 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)
{
auto gpm = GameplayManager::getTheGameplayManager();
auto scopestr = getArchetype() + "." + condition.getParm(1);
if (!gpm->hasProperty(scopestr, condition.getParm(2)))
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 },
{ nullptr, nullptr }
};
//***********************************************************************************************
//
// 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*)
{
if (target.length() > 0)
{
// Get the target entity
auto 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))
{
auto targetact = dynamic_cast<Actor *>(targetent);
if (name.length() > 0 && targetact->name == name)
{
// Tell other part about ourselves
auto event = new Event(EV_ActorRegisterParts);
event->AddEntity(this);
event->AddInteger(true);
targetent->PostEvent(event, 0.0f);
// Add this part to our part list
part_t new_part;
new_part.ent = targetent;
new_part.state_flags = 0;
parts.AddObject(new_part);
}
}
}
}
Actor* Actor::FindPartActor(const char* name)
{
for (auto current_part = 1; current_part <= parts.NumObjects(); current_part++)
{
auto part = &parts.ObjectAt(current_part);
Entity* partent = part->ent;
auto partact = dynamic_cast<Actor *>(partent);
if (partact && partact->part_name == name)
return partact;
}
return nullptr;
}
void Actor::SendCommand(Event* ev)
{
auto command = ev->GetString(1);
auto part_to_send_to = ev->GetString(2);
if (strlen(command) == 0 || strlen(part_to_send_to) == 0)
return;
for (auto i = 1; i <= parts.NumObjects(); i++)
{
auto part = &parts.ObjectAt(i);
auto partact = dynamic_cast<Actor *>(static_cast<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 != nullptr)
{
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 != nullptr)
{
// 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 == nullptr)
{
// Add the new dialog to this dialog list
new_node->next = nullptr;
dialog_list = new_node;
return;
}
dialog_node = dialog_list;
while (dialog_node->next != nullptr)
{
dialog_node = dialog_node->next;
}
// Add the new dialog to this dialog list
dialog_node->next = new_node;
new_node->next = nullptr;
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 == nullptr)
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 = nullptr;
const char* dialog_name = nullptr;
const char* state_name = nullptr;
auto volume = DEFAULT_VOL;
auto min_dist = DEFAULT_MIN_DIST;
qboolean headDisplay = false;
auto useTalk = false;
if (ev->NumArgs() > 0)
{
dialog_name = ev->GetString(1);
if (strcmp(dialog_name, "") == 0)
dialog_name = nullptr;
}
if (ev->NumArgs() > 1)
volume = ev->GetFloat(2);
if (ev->NumArgs() > 2)
{
auto minDistString = ev->GetString(3);
if (stricmp(minDistString, 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 = nullptr;
}
if (ev->NumArgs() > 6)
user = ev->GetEntity<Sentient>(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
auto player = GetPlayer(0);
if (player)
{
if (headDisplay)
{
player->SetupDialog(this, localizedDialogName);
SetActorFlag(ACTOR_FLAG_USING_HUD, true);
} else
player->SetupDialog(nullptr, localizedDialogName);
}
if (dialog_length > 0.0f)
{
Event* headTwitchEvent;
if (state_name != nullptr && currentState)
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeScript || mode == ActorModeIdle)
2015-04-28 20:02:03 +00:00
{
dialog_old_state_name = currentState->getName();
dialog_state_name = state_name;
auto 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 = 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
2015-04-29 20:34:44 +00:00
if (mode != ActorModeTalk)
2015-04-28 20:02:03 +00:00
{
// 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(), nullptr);
}
void Actor::StopDialog(Event*)
{
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))
{
auto player = GetPlayer(0);
if (player)
{
player->ClearDialog();
}
}
}
//-----------------------------------------------------
//
// Name:
// Class:
//
// Description:
//
// Parameters:
//
// Returns:
//-----------------------------------------------------
void Actor::setBranchDialog(void)
{
auto player = GetPlayer(0);
if (player)
{
str 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)
{
auto 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 != nullptr)
{
delete thinkStrategy;
thinkStrategy = nullptr;
}
thinkStrategy = new DefaultThink();
} else
{
if (thinkStrategy != nullptr)
{
delete thinkStrategy;
thinkStrategy = nullptr;
}
thinkStrategy = new SimplifiedThink(this);
}
if (!thinkStrategy)
gi.Error(ERR_FATAL, "Actor Could not create thinkStrategy");
}
void Actor::SetActorToActorDamageModifier(Event* ev)
{
auto 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*)
{
// Get our current enemy
auto currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
if (!incoming_proj)
return;
auto dir = currentEnemy->origin - origin;
auto vel = incoming_proj->velocity;
auto 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;
}
return 0;
}
void Actor::FreeDialogList(void)
{
DialogNode_t* dialog_node;
dialog_node = dialog_list;
while (dialog_node != nullptr)
{
dialog_list = dialog_node->next;
delete dialog_node;
dialog_node = dialog_list;
}
}
void Actor::DialogDone(Event*)
{
SetActorFlag(ACTOR_FLAG_DIALOG_PLAYING, false);
SetActorFlag(ACTOR_FLAG_RADIUS_DIALOG_PLAYING, false);
if (dialog_state_name)
{
dialog_state_name = "";
2015-04-29 20:34:44 +00:00
if (mode != ActorModeAi && mode != ActorModeTalk)
2015-04-28 20:02:03 +00:00
{
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)
{
2015-04-29 20:34:44 +00:00
SetControllerTag(ActorMouthTag, tag_num);
2015-04-28 20:02:03 +00:00
mouth_angles = vec_zero;
mouth_angles[PITCH] = max_mouth_angle * angle_percent;
2015-04-29 20:34:44 +00:00
SetControllerAngles(ActorMouthTag, mouth_angles);
2015-04-28 20:02:03 +00:00
}
}
void Actor::DialogAnimDone(Event*)
{
SetAnim("idle");
}
//***********************************************************************************************
//
// Mode functions
//
//***********************************************************************************************
qboolean Actor::ModeAllowed(int new_mode)
{
if (deadflag && actortype != IS_INANIMATE)
return false;
2015-04-29 20:34:44 +00:00
if (new_mode == ActorModeScript || new_mode == ActorModeIdle)
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeAi || mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
return false;
2015-04-29 20:34:44 +00:00
} else if (new_mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
{
//Check if we're already speaking
if (GetActorFlag(ACTOR_FLAG_DIALOG_PLAYING))
return false;
2015-04-29 20:34:44 +00:00
if (/*mode == ActorModeAi ||*/ mode == ActorModeTalk || actortype == IS_ENEMY || !GetActorFlag(ACTOR_FLAG_ALLOW_TALK) ||
2015-04-28 20:02:03 +00:00
!dialog_list || level.cinematic)
return false;
}
return true;
}
void Actor::StartMode(int new_mode)
{
2015-04-29 20:34:44 +00:00
if (new_mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
{
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)
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
{
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);
}
2015-04-29 20:34:44 +00:00
mode = ActorModeIdle;
2015-04-28 20:02:03 +00:00
ProcessEvent(EV_Actor_Idle);
if (currentState)
{
str currentanim = currentState->getLegAnim(*this, &conditionals);
if (currentanim.length() && currentanim != animname)
SetAnim(currentanim, EV_Anim_Done);
}
enemyManager->ClearCurrentEnemy();
2015-04-29 20:34:44 +00:00
} else if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
{
next_player_near = level.time + 5.0f;
RestoreMode();
}
}
void Actor::SaveMode(void)
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeIdle)
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
saved_mode = ActorModeIdle;
2015-04-28 20:02:03 +00:00
if (currentState)
saved_state_name = currentState->getName();
else
saved_state_name = "";
2015-04-29 20:34:44 +00:00
} else if (mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
saved_mode = ActorModeAi;
2015-04-28 20:02:03 +00:00
if (currentState)
saved_state_name = currentState->getName();
else
saved_state_name = "";
2015-04-29 20:34:44 +00:00
} else if (mode == ActorModeScript)
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
saved_mode = ActorModeScript;
2015-04-28 20:02:03 +00:00
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 = nullptr;
headBehavior = nullptr;
eyeBehavior = nullptr;
torsoBehavior = nullptr;
scriptthread = nullptr;
} else
{
gi.WDPrintf("Can't saved specified mode: %d\n", mode);
}
}
void Actor::RestoreMode(void)
{
Event* idle_event;
2015-04-29 20:34:44 +00:00
if (saved_mode == ActorModeIdle)
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
mode = ActorModeIdle;
2015-04-28 20:02:03 +00:00
idle_event = new Event(EV_Actor_Idle);
idle_event->AddString(saved_state_name);
ProcessEvent(idle_event);
}
2015-04-29 20:34:44 +00:00
if (saved_mode == ActorModeAi)
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
mode = ActorModeAi;
2015-04-28 20:02:03 +00:00
SetState(saved_state_name);
2015-04-29 20:34:44 +00:00
} else if (saved_mode == ActorModeScript)
2015-04-28 20:02:03 +00:00
{
2015-04-29 20:34:44 +00:00
StartMode(ActorModeScript);
2015-04-28 20:02:03 +00:00
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())
{
auto 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);
}
2015-04-29 20:34:44 +00:00
saved_mode = ActorModeNone;
2015-04-28 20:02:03 +00:00
}
//***********************************************************************************************
//
// 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;
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*)
{
SetActorFlag(ACTOR_FLAG_IGNORE_PAIN_FROM_ACTORS, true);
}
void Actor::UpdateBossHealth(Event* ev)
{
auto update = true;
auto 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*)
{
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*)
{
spawn_items.ClearObjectList();
}
void Actor::SpawnItems(void)
{
if (spawn_chance == 0.0f)
return;
auto 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 (auto i = 1; i <= number_of_spawn_items; i++)
{
SpawnItem(spawn_items.ObjectAt(i));
}
}
}
void Actor::SpawnItem(const str& spawn_item_name)
{
SpawnArgs args;
args.setArg("model", spawn_item_name);
auto ent = args.Spawn();
if (!ent || !ent->isSubclassOf(Item))
return;
auto item = dynamic_cast<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)
{
auto 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));
}
bool Actor::GetStickToGround(void) const
{
return movementSubsystem->GetStickToGround();
}
void Actor::SetActorFlag(int flag, qboolean flag_value)
{
unsigned int* flags;
int index;
int bit;
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;
int32_t index;
int32_t bit;
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;
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;
return false;
}
void Actor::SetBounceOff(Event*)
{
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*)
{
stage++;
}
void Actor::GotoPrevStage(Event*)
{
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*)
{
SetActorFlag(ACTOR_FLAG_NOTIFY_OTHERS_AT_DEATH, true);
}
void Actor::NotifyOthersOfDeath(void)
{
for (auto i = 1; i <= ActiveList.NumObjects(); i++)
{
auto act = ActiveList.ObjectAt(i);
//if ( name.length() && name == act->name && Vector( act->origin - origin ).length() < 1000 )
if (name.length() && name == act->name)
2015-04-29 20:34:44 +00:00
act->AddStateFlag(StateFlagOtherDied);
2015-04-28 20:02:03 +00:00
}
}
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)
{
Vector pos;
Vector forward;
auto tag_name = ev->GetString(1);
auto tag_num = gi.Tag_NumForName(edict->s.modelindex, tag_name);
if (bind_info)
{
for (auto i = 0, num = bind_info->numchildren; i < MAX_MODEL_CHILDREN; i++)
{
if (bind_info->children[i] == ENTITYNUM_NONE)
{
continue;
}
auto child = 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 = nullptr;
tag_num = gi.Tag_NumForName(edict->s.modelindex, tag_name);
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*)
{
edict->clipmask = MASK_MONSTERSOLID;
}
void Actor::IgnoreMonsterClip(Event*)
{
edict->clipmask &= ~CONTENTS_MONSTERCLIP;
}
void Actor::NotSolidMask(Event*)
{
edict->clipmask = MASK_SOLID;
}
void Actor::NoMask(Event*)
{
edict->clipmask = 0;
}
void Actor::ResetMoveDir(Event*)
{
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;
auto varName = ev->GetString(1);
auto varValue = ev->GetString(2);
//First Check if we already have the Var and it just needs
//to have its value updated
for (auto 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;
auto varName = ev->GetString(1);
//First Check if we already have the Var and it just needs
//to have its value updated
for (auto 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 & FlagNotarget) && 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)
{
auto other = ev->GetEntity(1);
if (
other->movetype != MOVETYPE_NONE &&
other->movetype != MOVETYPE_STATIONARY &&
IsEntityAlive(other)
)
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagTouched);
2015-04-28 20:02:03 +00:00
}
void Actor::AddStateFlag(unsigned int flag)
{
// Update my state flags
state_flags |= flag;
// Update all the other parts of my state flags
for (auto current_other_part = 1; current_other_part <= parts.NumObjects(); current_other_part++)
{
auto other_part = &parts.ObjectAt(current_other_part);
Entity* other_ent = other_part->ent;
auto other_act = dynamic_cast<Actor *>(other_ent);
// Look for ourselves in this part's part list
for (auto current_part = 1; current_part <= other_act->parts.NumObjects(); current_part++)
{
auto 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*)
{
SetActorFlag(ACTOR_FLAG_NOCHATTER, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ActivateEvent(Event*)
{
if (deadflag && actortype != IS_INANIMATE)
{
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ProcessEvent(EV_Actor_AttackPlayer);
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagActivated);
2015-04-28 20:02:03 +00:00
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::UseEvent(Event* ev)
{
// Can only be used once every 1/4 second
if (last_used_time + 0.25f >= level.time)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_CANNOT_USE))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Can't switch to talk mode if a cinematic is going on
if (level.cinematic)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Actors can't be used by equipment
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto entity = ev->GetEntity(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (entity->isSubclassOf(Equipment))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
last_used_time = level.time;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagUsed);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (onuse_thread_name.length() > 0)
{
RunThread(onuse_thread_name);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (entity->isSubclassOf(Sentient) && getSolidType() == SOLID_BBOX && !hidden())
{
StartTalkBehavior(dynamic_cast<Sentient *>(entity));
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::StartTalkBehavior(Sentient* user)
{
Talk* talk;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (!ModeAllowed(ActorModeTalk))
2015-04-28 20:02:03 +00:00
return;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeTalk);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
talk = new Talk;
talk->SetUser(user);
SetBehavior(talk);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetOnUseThread(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
onuse_thread_name = ev->GetString(1);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::ClearOnUseThread(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
onuse_thread_name = "";
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::Think(void)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (!Director.PlayerReady())
{
last_time_active = level.time;
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (thinkStrategy)
thinkStrategy->Think(*this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!level.ai_on)
LevelAIOff();
else
LevelAIOn();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
/*
Vector BS( 0 , 0 , 100 );
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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 );
}
*/
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
/*
str pState;
switch ( movementSubsystem->getPostureState() )
{
case POSTURE_TRANSITION_STAND_TO_CROUCH:
pState = "POSTURE_TRANSITION_STAND_TO_CROUCH\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_STAND_TO_PRONE:
pState = "POSTURE_TRANSITION_STAND_TO_PRONE\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_STAND_TO_LEAN_LEFT_DUAL:
pState = "POSTURE_TRANSITION_STAND_TO_LEAN_LEFT_DUAL\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_STAND_TO_LEAN_RIGHT_DUAL:
pState = "POSTURE_TRANSITION_STAND_TO_LEAN_RIGHT_DUAL\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_CROUCH_TO_STAND:
pState = "POSTURE_TRANSITION_CROUCH_TO_STAND\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_CROUCH_TO_PRONE:
pState = "POSTURE_TRANSITION_CROUCH_TO_PRONE\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_PRONE_TO_CROUCH:
pState = "POSTURE_TRANSITION_PRONE_TO_CROUCH\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_PRONE_TO_STAND:
pState = "POSTURE_TRANSITION_PRONE_TO_STAND\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_LEAN_LEFT_DUAL_TO_STAND:
pState = "POSTURE_TRANSITION_LEAN_LEFT_DUAL_TO_STAND\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_TRANSITION_LEAN_RIGHT_DUAL_TO_STAND:
pState = "POSTURE_TRANSITION_LEAN_RIGHT_DUAL_TO_STAND\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_STAND:
pState = "POSTURE_STAND\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_CROUCH:
pState = "POSTURE_CROUCH\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_PRONE:
pState = "POSTURE_PRONE\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_LEAN_LEFT_DUAL:
pState = "POSTURE_LEAN_LEFT_DUAL\n";
break;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
case POSTURE_LEAN_RIGHT_DUAL:
pState = "POSTURE_LEAN_RIGHT_DUAL\n";
break;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gi.Printf( pState.c_str() );
2012-12-30 16:37:54 +00:00
*/
2015-04-28 20:02:03 +00:00
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::GetClosestTag(const str& tag_name, int number_of_tags, const Vector& target, Vector* orig)
{
Vector temp_orig;
auto best_dist = -1.0f;
auto found = false;
char number[5];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (number_of_tags == 1)
{
return GetTag(tag_name.c_str(), orig);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (auto i = 1; i <= number_of_tags; i++)
{
sprintf(number, "%d", i);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto temp_tag_name = tag_name + str(number);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetTag(temp_tag_name.c_str(), &temp_orig))
{
auto diff = target - temp_orig;
auto dist = diff * diff;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (dist < best_dist || best_dist < 0)
{
best_dist = dist;
found = true;
*orig = temp_orig;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return found;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Active(Event* ev)
{
int active_flag;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 0)
{
active_flag = ev->GetInteger(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (active_flag)
SetActorFlag(ACTOR_FLAG_INACTIVE, false);
else
SetActorFlag(ACTOR_FLAG_INACTIVE, true);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SpawnActorAboveEnemy(Event* ev)
{
Vector Enemy_orig;
Vector Enemy_dir;
//Set Spawn Thing Origin and Angles to make it above the player
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
if (currentEnemy->isSubclassOf(Player))
{
auto player = dynamic_cast<Player*>(currentEnemy);
Enemy_orig = player->origin;
Enemy_dir = player->angles;
} else
{
auto act = dynamic_cast<Actor*>(currentEnemy);
Enemy_orig = act->origin;
Enemy_dir = act->angles;
}
auto how_far = ev->GetFloat(6);
Vector new_orig;
new_orig = Enemy_orig;
new_orig.z += how_far;
Vector new_dir;
new_dir = Enemy_dir;
auto width = ev->GetFloat(4);
Vector spawn_mins;
spawn_mins.x = -width;
spawn_mins.y = -width;
spawn_mins.z = 0;
auto height = ev->GetFloat(5);
Vector spawn_maxs;
spawn_maxs.x = width;
spawn_maxs.y = width;
spawn_maxs.z = height;
auto trace = G_Trace(Enemy_orig, spawn_mins, spawn_maxs, new_orig, nullptr, MASK_MONSTERSOLID, false, "SpawnActorAbovePlayer");
SpawnActor(ev->GetString(1), trace.endpos, new_dir, ev->GetInteger(2), ev->GetBoolean(3), width, height, true);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the path node name to spawn in to
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
pathnode_name += int(G_Random(float(number_of_pathnodes))) + 1;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Find the path node
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
goal = thePathManager.FindNode(pathnode_name);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!goal)
{
gi.WPrintf("Can't find position %s\n", pathnode_name.c_str());
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Set the spawn in position/direction
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
orig = goal->origin;
ang = goal->angles;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
spawn_mins.x = -width;
spawn_mins.y = -width;
spawn_mins.z = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
spawn_maxs.x = width;
spawn_maxs.y = width;
spawn_maxs.z = height;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(orig + Vector("0 0 64"), spawn_mins, spawn_maxs, orig - Vector("0 0 128"), nullptr, MASK_MONSTERSOLID, false, "SpawnActorAtLocation");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.allsolid)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
orig = trace.endpos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SpawnActor(model_name, orig, ang, 1, attack, width, height);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Spawn in teleport effect
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
effect = new Entity(EntityCreateFlagAnimate);
effect->setModel("fx_teleport3.tik");
effect->setOrigin(orig);
effect->setSolidType(SOLID_NOT);
effect->animate->RandomAnimate("idle", EV_Remove);
effect->Sound("snd_teleport");
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SpawnActorAtTag(Event* ev)
{
str model_name;
str tag_name;
int how_many;
qboolean attack;
float spawn_offset;
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;
}
auto offset = 250.0f;
for (auto i = 0; i < how_many; i++)
{
Vector side;
origin.AngleVectors(nullptr, &side, nullptr);
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);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure this origin is reasonable
spawn_mins[0] = -width;
spawn_mins[1] = -width;
spawn_mins[2] = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
spawn_maxs[0] = width;
spawn_maxs[1] = width;
spawn_maxs[2] = height;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(orig, spawn_mins, spawn_maxs, orig, nullptr, MASK_MONSTERSOLID, false, "Actor::SpawnActor");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if ((trace.fraction != 1.0f || trace.allsolid) && !force)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Spawn in all new actors
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (current_actor = 0; current_actor < how_many; current_actor++)
{
new_actor = new Actor;
new_actor->setModel(model_name);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
new_actor->setOrigin(orig);
new_actor->setAngles(ang);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make new actor attack player if requested
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (attack)
new_actor->PostEvent(EV_Actor_AttackPlayer, 0.0f);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Update number of spawns
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
num_of_spawns++;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Save our parent
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
new_actor->spawnparent = this;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Give the new actor a targetname
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
new_actor->targetname = targetname;
new_actor->targetname += "_spawned";
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
new_actor->SetTargetName(new_actor->targetname);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TryTalkToPlayer(void)
{
int player_near = false;
Entity* ent_in_range;
int i;
Vector delta;
gentity_t* ed;
float dist2;
Sentient* user = nullptr;
Talk* talk;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if we should even bother looking for players
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (level.cinematic)
next_player_near = level.time + 5.0f;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
if (deadflag || actortype != IS_FRIEND || next_player_near > level.time || !ModeAllowed(ActorModeTalk))
2015-04-28 20:02:03 +00:00
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
next_player_near = level.time + .2f + G_Random(.1f);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if we are near the player
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 0; i < game.maxclients; i++)
{
ed = &g_entities[i];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!ed->inuse || !ed->entity)
continue;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ent_in_range = ed->entity;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (EntityHasFireType(ent_in_range, FT_MELEE) || EntityHasFireType(ent_in_range, FT_BULLET) ||
EntityHasFireType(ent_in_range, FT_PROJECTILE))
continue;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (IsEntityAlive(ent_in_range) && ent_in_range->velocity == vec_zero && sensoryPerception)
{
delta = centroid - ent_in_range->centroid;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// dot product returns length squared
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
dist2 = delta * delta;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (dist2 <= 100.0f * 100.0f && sensoryPerception->CanSeeEntity(this, ent_in_range, true, true))
{
player_near = true;
user = dynamic_cast<Sentient *>(ent_in_range);
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!player_near)
{
SetActorFlag(ACTOR_FLAG_LAST_TRY_TALK, false);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!GetActorFlag(ACTOR_FLAG_LAST_TRY_TALK))
{
SetActorFlag(ACTOR_FLAG_LAST_TRY_TALK, true);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_LAST_TRY_TALK, false);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Go to talk mode
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
StartMode(ActorModeTalk);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
talk = new Talk;
talk->SetUser(user);
SetBehavior(talk);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::AllowTalk(Event* ev)
{
SetActorFlag(ACTOR_FLAG_ALLOW_TALK, ev->GetBoolean(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::AllowHangBack(Event* ev)
{
SetActorFlag(ACTOR_FLAG_ALLOW_HANGBACK, ev->GetBoolean(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
width = maxs[0];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// First see if the origin is on a solid (this is the really simple test)
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
start = origin - Vector(0.0f, 0.0f, 1.0f);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (gi.pointcontents(start, 0) == CONTENTS_SOLID)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Next see if at least 3 corners are on a solid (this is the simple test)
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
corners_ok = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
start[2] = absmin[2] - 1.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (gi.pointcontents(start, 0) == CONTENTS_SOLID)
corners_ok++;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (corners_ok >= 3)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Next do the hard test
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
corners_ok = 0;
middle_ok = 0;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Test the origin (if it is close to ground is a plus)
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
start = origin;
stop = start - Vector(0.0f, 0.0f, width); // Test down as far as the actor is wide
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Build the correct mask (the actor's normal mask without body)
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
mask = edict->clipmask & ~CONTENTS_BODY;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(start, vec_zero, vec_zero, stop, this, mask, false, "CheckBottom 1");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.fraction < 1.0f && trace.plane.normal[2] > .7f)
middle_ok = 1;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Test all of the corners
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
stop = start - Vector(0.0f, 0.0f, 2.0f * width);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(start, vec_zero, vec_zero, stop, this, mask, false, "CheckBottom 2");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.fraction < 1.0f && trace.plane.normal[2] > .7f)
corners_ok++;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (middle_ok && corners_ok >= 1 || corners_ok >= 3)
return true;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ChangeType(Event* ev)
{
velocity = vec_zero;
setModel(ev->GetString(1));
PostEvent(EV_Actor_Wakeup, FRAMETIME);
NoLerpThisFrame();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::GetStateAnims(Container<const char *>* c)
{
if (statemap)
statemap->GetAllAnims(c);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::Touched(Event* ev)
{
Entity* other;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
other = ev->GetEntity(1);
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
sensoryPerception->Stimuli(StimuliSight, other);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (other->isSubclassOf(Player))
{
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagTouchedByPlayer);
2015-04-28 20:02:03 +00:00
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
int Actor::ActorFlag_string_to_int(const str& actorflagstr) const
{
for (auto i = 0; i < ACTOR_FLAG_MAX; i++)
{
if (!actorflagstr.icmp(actor_flag_strings[i]))
return i;
}
gi.WDPrintf("Unknown actor flag - %s\n", actorflagstr.c_str());
return -1;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
int Actor::NotifyFlag_string_to_int(const str& notifyflagstr)
{
for (auto i = 0; i < NOTIFY_FLAG_MAX; i++)
{
if (!notifyflagstr.icmp(actor_notify_strings[i]))
return i;
}
gi.WDPrintf("Unknown Notify Flag - %s\n", notifyflagstr.c_str());
return -1;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ArmorDamage(Event* ev)
{
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagAttacked);
2015-04-28 20:02:03 +00:00
//if (!TakeDamage())
// return;
auto enemy = ev->GetEntity(3);
if (enemy->isSubclassOf(Player))
{
//Teammates don't count MOD explosion, because it might
//be splash damage
if (actortype == IS_TEAMMATE)
{
auto MOD = ev->GetInteger(9);
if (MOD != MOD_EXPLOSION)
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagAttackedByPlayer);
2015-04-28 20:02:03 +00:00
} else
{
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagAttackedByPlayer);
2015-04-28 20:02:03 +00:00
}
}
if (!enemy)
return;
::Damage damage(ev);
// Only react to an attack if we respond to pain
2015-04-29 20:34:44 +00:00
if (sensoryPerception && sensoryPerception->ShouldRespondToStimuli(StimuliPain))
2015-04-28 20:02:03 +00:00
{
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;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Here for Legacy
if (ondamage_thread.length())
{
RunDamageThread();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//New Way
RunCustomThread("damaged");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Handle the GameSpecificStuff
if (gameComponent)
gameComponent->HandleArmorDamage(ev); // What's this?
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetNotifyFlag(NOTIFY_FLAG_DAMAGED))
_notifyGroupOfDamage();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2015-04-29 20:34:44 +00:00
sensoryPerception->Stimuli(StimuliPain, enemy);
2015-04-28 20:02:03 +00:00
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Actor* GetActor(const str& actor_name)
{
for (auto i = 1; i <= SleepList.NumObjects(); i++)
{
auto testActor = SleepList.ObjectAt(i);
if (testActor->targetname == actor_name)
return testActor;
}
for (auto i = 1; i <= ActiveList.NumObjects(); i++)
{
auto testActor = ActiveList.ObjectAt(i);
if (testActor->targetname == actor_name)
return testActor;
}
return nullptr;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetFlagOnEnemy(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str flag_name;
int flag;
qboolean flag_bool;
flag_name = ev->GetString(1);
flag_bool = ev->GetBoolean(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
flag = ActorFlag_string_to_int(flag_name);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy->isSubclassOf(Actor))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto act = dynamic_cast<Actor*>(currentEnemy);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (act)
act->SetActorFlag(flag, flag_bool);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TurnOnEnemyAI(Event*)
{
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy->isSubclassOf(Actor))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto act = dynamic_cast<Actor*>(currentEnemy);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (act)
act->TurnAIOn();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TurnOffEnemyAI(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy->isSubclassOf(Actor))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto act = dynamic_cast<Actor*>(currentEnemy);
if (act)
act->TurnAIOff();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::PickupThrowObject(Event* ev)
{
Entity* ent = enemyManager->GetAlternateTarget();
if (!ent->isSubclassOf(ThrowObject))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto tobj = dynamic_cast<ThrowObject*>(ent);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!tobj)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
tobj->Pickup(this, ev->GetString(1));
haveThrowObject = true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::TossThrowObject(Event* ev)
{
// Due to the changes with enemy management, throw object stuff no longer works!!!
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Entity* currentEnt = enemyManager->GetCurrentEnemy();
if (!currentEnt)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto tobj = dynamic_cast<ThrowObject*>(static_cast<Entity*>(enemyManager->GetAlternateTarget()));
if (!tobj || !currentEnt->isSubclassOf(Sentient))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//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, ev->GetFloat(1), dynamic_cast<Sentient*>(currentEnt), ev->GetFloat(2), ev->NumArgs() > 2 ? ev->GetFloat(3) : 25);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetTurretMode(Event* ev)
{
qboolean tmode;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 0)
tmode = ev->GetBoolean(1);
else
tmode = true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_TURRET_MODE, tmode);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetOnDamageThread(Event* ev)
{
ondamage_thread = ev->GetString(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
ondamage_threshold = ev->GetInteger(2);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetTimeBetweenSleepChecks(Event* ev)
{
timeBetweenSleepChecks = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::AttachCurrentEnemy(Event* ev)
{
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
if (!currentEnemy->isSubclassOf(Actor))
return;
auto bone = ev->GetString(1);
auto offset = ev->GetVector(2);
auto tagnum = gi.Tag_NumForName(this->edict->s.modelindex, bone);
auto act = dynamic_cast<Actor*>(currentEnemy);
if (act)
{
act->attach(this->entnum, tagnum, false, offset);
haveAttached = true;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::AttachActor(Event* ev)
{
auto new_actor = new Actor;
if (!new_actor)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Vector offset;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
new_actor->setModel(ev->GetString(1));
new_actor->SetTargetName(ev->GetString(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 3)
offset = ev->GetVector(4);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto tagnum = gi.Tag_NumForName(this->edict->s.modelindex, ev->GetString(3));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
new_actor->attach(this->entnum, tagnum, false, offset);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetEnemyAttached(Event* ev)
{
haveAttached = ev->GetBoolean(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::GiveActorWeapon(Event* ev)
{
auto amount = 1.0f;
auto skillLevel = 1.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
skillLevel = ev->GetFloat(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
giveItem(ev->GetString(1), int(amount), false, skillLevel);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::RemoveActorWeapon(Event* ev)
{
takeItem(ev->GetString(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::PutawayWeapon(Event* ev)
{
auto hand = WEAPON_RIGHT;
if (ev->NumArgs() > 0)
hand = WeaponHandNameToNum(ev->GetString(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
DeactivateWeapon(hand);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::UseActorWeapon(Event* ev)
{
str handToUse;
auto hand = WEAPON_DUAL;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
{
handToUse = ev->GetString(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!stricmp(handToUse.c_str(), "right"))
hand = WEAPON_RIGHT;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!stricmp(handToUse.c_str(), "left"))
hand = WEAPON_LEFT;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!stricmp(handToUse.c_str(), "dual"))
hand = WEAPON_DUAL;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
combatSubsystem->UseActorWeapon(ev->GetString(1), hand);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::AttachModelToTag(const str& modelName, const str& tagName)
{
auto attach_event = new Event(EV_AttachModel);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
attach_event->AddString(modelName);
attach_event->AddString(tagName);
PostEvent(attach_event, 0.0f);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DetachModelFromTag(const str& tagName)
{
auto detach_event = new Event(EV_RemoveAttachedModel);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
detach_event->AddString(tagName);
PostEvent(detach_event, 0.0f);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//==============================================
// Sensory Perception Initilization
//==============================================
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetFOV(Event* ev)
{
sensoryPerception->SetFOV(ev->GetFloat(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetVisionDistance(Event* ev)
{
sensoryPerception->SetVisionDistance(ev->GetFloat(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::ClearCurrentEnemy(Event*)
{
enemyManager->ClearCurrentEnemy();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//
// 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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//
// 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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//
// 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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//
// 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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetDisabled(Event* ev)
{
qboolean disabled = ev->GetBoolean(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_DISABLED, disabled);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetCrippled(Event* ev)
{
qboolean crippled = ev->GetBoolean(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_CRIPPLED, crippled);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetInAlcove(Event* ev)
{
qboolean inalcove = ev->GetBoolean(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_IN_ALCOVE, inalcove);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetAimLeadFactors(Event* ev)
{
minLeadFactor = ev->GetFloat(1);
maxLeadFactor = ev->GetFloat(2);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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 &= ~FlagBlood;
flags &= ~FlagDieGibs;
return;
}
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;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DebugStates(Event* ev)
{
auto state = ev->GetInteger(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (state >= MAX_DEBUG_TYPES)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
showStates = stateDebugType_t(ev->GetInteger(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//***********************************************************************************************
//
// Combat functions
//
//***********************************************************************************************
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::IncomingProjectile(Event* ev)
{
incoming_proj = ev->GetEntity(1);
incoming_time = level.time + .1f; //+ G_Random( .1 );
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::FireProjectile(Event* ev)
{
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
enemyManager->FindHighestHateEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Vector orig;
auto number_of_tags = ev->NumArgs() > 2 ? ev->GetInteger(3) : 1;
auto arc = ev->NumArgs() > 3 ? ev->GetBoolean(4) : false;
auto speed = ev->NumArgs() > 4 ? ev->GetFloat(5) : 0.0f;
auto offset = ev->NumArgs() > 5 ? ev->GetFloat(6) : 0.0f;
auto leadTarget = ev->NumArgs() > 6 ? ev->GetBoolean(7) : false;
auto spread = ev->NumArgs() > 7 ? ev->GetFloat(8) : 0.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Find the closest tag
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!GetClosestTag(ev->GetString(1), 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];
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Add projectile to world
auto targetPos = currentEnemy->centroid;
auto newTargetPos = targetPos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (leadTarget)
{
auto projSpeed = speed;
if (projSpeed <= 0)
projSpeed = 1;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
newTargetPos = combatSubsystem->GetLeadingTargetPos(projSpeed, currentEnemy->centroid, currentEnemy);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto dir = newTargetPos - orig;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (arc)
{
Vector proj_velocity;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto xydir = dir;
xydir.z = 0.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (speed == 0.0f)
speed = 500.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto traveltime = xydir.length() / speed;
auto vertical_speed = dir.z / traveltime + 0.5f * gravity * sv_currentGravity->value * traveltime;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
xydir.normalize();
proj_velocity = speed * xydir;
proj_velocity.z = vertical_speed;
speed = proj_velocity.length();
proj_velocity.normalize();
dir = proj_velocity;
}
if (offset)
{
auto offset_angle = dir.toAngles();
offset_angle[YAW] += offset;
offset_angle.AngleVectors(&dir);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
dir.normalize();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ProjectileAttack(orig, dir, this, ev->GetString(2), 1.0f, speed);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SaveAttack(orig, dir);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::FireRadiusAttack(Event* ev)
{
RadiusDamage(this, this, ev->GetFloat(3), this, MOD_NameToNum(ev->GetString(2)), ev->GetFloat(4), ev->GetFloat(5), ev->GetBoolean(6));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 6)
range = ev->GetFloat(7);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get our current enemy
Entity* currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the position where the bullet starts
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
GetTag(tag_name, &pos, &forward, &left, &up);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
right = left * -1.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get the real pitch of the bullet attack
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!use_current_pitch && currentEnemy)
{
attack_angles = forward.toAngles();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
dir = currentEnemy->centroid - pos;
// Temporary Change ( 10/3/01 -- SK )
dir.z += 10;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
enemy_angles = dir.toAngles();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
attack_angles[PITCH] = enemy_angles[PITCH];
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Temporary Change (10/3/01 -- SK )
//attack_angles.AngleVectors( &forward, &left, &up );
enemy_angles.AngleVectors(&forward, &left, &up);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
right = left * -1.0f;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Little Sanity Check
if (shotsFired > 1000)
shotsFired = 1000;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
attack_means_of_death = MOD_NameToNum(means_of_death_string);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
BulletAttack(pos, forward, right, up, range, damage, knockback, 0, attack_means_of_death, spread, 1, this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SaveAttack(pos, forward);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SaveAttack(const Vector& orig, const Vector& dir)
{
Vector attack_mins;
Vector attack_maxs;
Vector end;
trace_t trace;
qboolean hit;
Entity* ent;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Entity* currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
{
SetActorFlag(ACTOR_FLAG_LAST_ATTACK_HIT, true);
return;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Do trace
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
attack_mins = Vector(-1.0f, -1.0f, -1.0f);
attack_maxs = Vector(1.0f, 1.0f, 1.0f);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
end = orig + dir * 8192.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(orig, attack_mins, attack_maxs, end, this, MASK_SHOT, false, "Actor::SaveAttack");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See what we hit
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
last_attack_entity_hit = nullptr;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.ent)
{
ent = trace.ent->entity;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Do an extra check because of NOCLIP
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (currentEnemy->movetype == MOVETYPE_NOCLIP)
hit = true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Save last attack info
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
last_attack_pos = origin;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
last_attack_enemy_pos = currentEnemy->origin;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_LAST_ATTACK_HIT, hit);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::TestAttack(const str& tag_name)
{
qboolean hit;
trace_t trace;
Vector attack_mins;
Vector attack_maxs;
Vector start;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure we still have an enemy and he is hitable
// Get our current enemy
Entity* currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (currentEnemy->movetype == MOVETYPE_NOCLIP)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Make sure we won't hit any friends
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
attack_mins = Vector(-1.0f, -1.0f, -1.0f);
attack_maxs = Vector(1.0f, 1.0f, 1.0f);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (tag_name.length())
{
GetTag(tag_name.c_str(), &start);
} else
{
start = centroid;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(start, attack_mins, attack_maxs, currentEnemy->centroid, this, MASK_SHOT, false, "Actor::TestAttack");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.ent && trace.ent->entity != currentEnemy && trace.ent->entity->isSubclassOf(Sentient) && !enemyManager->IsValidEnemy(trace.ent->entity))
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if we hit last time
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
hit = GetActorFlag(ACTOR_FLAG_LAST_ATTACK_HIT);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (hit)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Didn't hit last time so see if anything has changed
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if actor has moved
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (last_attack_pos != origin)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if enemy has moved
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (last_attack_enemy_pos != currentEnemy->origin)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if entity in the way has moved
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (last_attack_entity_hit && last_attack_entity_hit_pos != last_attack_entity_hit->origin)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if entity in the way was a door and has opened
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (last_attack_entity_hit && last_attack_entity_hit->isSubclassOf(Door))
{
auto door = dynamic_cast<Door *>(static_cast<Entity *>(last_attack_entity_hit));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (door->isOpen())
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if entity in the way has become non-solid
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (last_attack_entity_hit && last_attack_entity_hit->edict->solid == SOLID_NOT)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Nothing has changed so this attack should fail too
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::MeleeEvent(Event* ev)
{
// Get our current enemy
Entity* 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;
Vector pos;
Vector end;
Vector dir;
// Get all of the parameters
auto damage = ev->NumArgs() > 0 ? ev->GetFloat(1) : 20;
if (_useWeaponDamage != WEAPON_ERROR)
{
auto weap = GetActiveWeapon(_useWeaponDamage);
if (weap)
damage = weap->GetBulletDamage();
}
Vector attack_vector;
auto attack_width = 0.0f;
auto attack_max_height = 0.0f;
auto attack_length = 100.0f;
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];
}
auto knockback = ev->NumArgs() > 4 ? ev->GetFloat(5) : damage * 8.0f;
auto use_pitch_to_enemy = ev->NumArgs() > 5 ? ev->GetInteger(6) : false;
auto attack_min_height = ev->NumArgs() > 6 ? ev->GetFloat(7) : -attack_max_height;
auto attack_final_height = ev->NumArgs() > 7 ? ev->GetFloat(8) : 50.0f;
str tag_name = ev->NumArgs() > 1 ? ev->GetString(2) : "";
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)
{
dir = end - pos;
auto length = dir.length();
auto angles = dir.toAngles();
auto enemy_dir = currentEnemy->centroid - pos;
auto enemy_angles = enemy_dir.toAngles();
angles[PITCH] = enemy_angles[PITCH];
angles.AngleVectors(&dir);
end = pos + dir * length;
}
}
auto means_of_death_string = ev->NumArgs() > 2 ? ev->GetString(3) : "";
auto attack_means_of_death = strlen(means_of_death_string) > 0 ? meansOfDeath_t(MOD_NameToNum(means_of_death_string)) : MOD_CRUSH;
// Do the actual attack
Weapon* weap = nullptr;
if (_useWeaponDamage != WEAPON_ERROR)
weap = GetActiveWeapon(_useWeaponDamage);
auto success = weap != nullptr ? MeleeAttack(pos, end, damage, this, attack_means_of_death, attack_width, attack_min_height, attack_max_height, knockback, true, nullptr, weap)
: MeleeAttack(pos, end, damage, this, attack_means_of_death, attack_width, attack_min_height, attack_max_height, knockback);
if (success)
{
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagMeleeHit);
2015-04-28 20:02:03 +00:00
RunCustomThread("meleehit");
if (GetActorFlag(ACTOR_FLAG_DAMAGE_ONCE_ON))
SetActorFlag(ACTOR_FLAG_DAMAGE_ONCE_DAMAGED, true);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// See if we are standing in water
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
cont = gi.pointcontents(origin, 0);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!(cont & MASK_WATER))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get parms
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
damage = ev->GetFloat(1);
radius = ev->GetFloat(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!damage || !radius)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Determine what brush we are in
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
brushnum = gi.pointbrushnum(origin, 0);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Find everything in radius
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ent = nullptr;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (ent = findradius(ent, origin, radius); ent; ent = findradius(ent, origin, radius))
{
if (ent->takedamage)
{
entity_brushnum = gi.pointbrushnum(origin, 0);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (brushnum == entity_brushnum)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DamageOnceStart(Event*)
{
SetActorFlag(ACTOR_FLAG_DAMAGE_ONCE_ON, true);
SetActorFlag(ACTOR_FLAG_DAMAGE_ONCE_DAMAGED, false);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DamageOnceStop(Event*)
{
SetActorFlag(ACTOR_FLAG_DAMAGE_ONCE_ON, false);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DamageAllowed(Event* ev)
{
SetActorFlag(ACTOR_FLAG_DAMAGE_ALLOWED, ev->GetBoolean(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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, nullptr, nullptr);
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;
}
// 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;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// If we hit the guy we wanted, then shoot
if (trace.ent == ent->edict)
{
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// If we hit someone else we don't like, then shoot
if (enemyManager->IsValidEnemy(trace.ent->entity))
{
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Forget it then
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::CanAttack(Entity* ent, qboolean usecurrentangles)
{
return CanAttackFrom(origin, ent, usecurrentangles);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::EntityHasFireType(Entity* ent, firetype_t fire_type)
{
if (!ent)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!ent->isSubclassOf(Player))
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto player = dynamic_cast<Player *>(ent);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Try left hand
auto weapon = player->GetActiveWeapon(WEAPON_LEFT);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
firetype_t weapon_fire_type;
if (weapon)
{
weapon_fire_type = weapon->GetFireType(FIRE_MODE1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (weapon_fire_type == fire_type)
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Try right hand
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
weapon = player->GetActiveWeapon(WEAPON_RIGHT);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (weapon)
{
weapon_fire_type = weapon->GetFireType(FIRE_MODE1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (weapon_fire_type == fire_type)
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Try dual weapons
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
weapon = player->GetActiveWeapon(WEAPON_DUAL);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (weapon)
{
weapon_fire_type = weapon->GetFireType(FIRE_MODE1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (weapon_fire_type == fire_type)
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::DamageEnemy(Event* ev)
{
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto dir = currentEnemy->origin - origin;
dir.normalize();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto damage = 0.0f;
auto knockback = 0.0f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 0)
damage = ev->GetFloat(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
{
auto attachEvent = new Event(EV_SpawnEffect);
if (!attachEvent)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
attachEvent->AddString(ev->GetString(2));
attachEvent->AddString("Bip01");
attachEvent->AddFloat(2.5);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
currentEnemy->ProcessEvent(attachEvent);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 2)
knockback = ev->GetFloat(3);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (damage > 0.0f)
currentEnemy->Damage(this, this, damage, vec_zero, dir, vec_zero, int32_t(knockback), 0, MOD_CRUSH);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::IsImmortal(void)
{
return GetActorFlag(ACTOR_FLAG_IMMORTAL);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::TakeDamage(void)
{
return GetActorFlag(ACTOR_FLAG_TAKE_DAMAGE);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::FireWeapon(Event*)
{
combatSubsystem->FireWeapon();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::StopFireWeapon(Event*)
{
combatSubsystem->StopFireWeapon();
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// Init Functions For Helper Classes
//===================================================================================
//
// Name: InitGameComponent()
// Parameters: None
// Description: Initalizes the actor's GameComponent
//
void Actor::InitGameComponent()
2015-04-28 20:02:03 +00:00
{
//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
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gameComponent = nullptr;
gameComponent = new EFGameComponent(this);
if (!gameComponent)
gi.Error(ERR_FATAL, "Actor Could not create gameComponent");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitSensoryPerception()
// Parameters: None
// Description: Initalizes the actor's SensoryPerception
//
void Actor::InitSensoryPerception()
2015-04-28 20:02:03 +00:00
{
sensoryPerception = nullptr;
sensoryPerception = new SensoryPerception(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!sensoryPerception)
gi.Error(ERR_FATAL, "Actor Could not create sensoryPerception");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitThinkStrategy()
// Parameters: None
// Description: Initalizes the actor's ThinkStrategy
//
void Actor::InitThinkStrategy()
2015-04-28 20:02:03 +00:00
{
thinkStrategy = nullptr;
thinkStrategy = new DefaultThink();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!thinkStrategy)
gi.Error(ERR_FATAL, "Actor Could not create thinkStrategy");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitStrategos()
// Parameters: None
// Description: Initalizes the actor's Strategos
//
void Actor::InitStrategos()
2015-04-28 20:02:03 +00:00
{
strategos = nullptr;
strategos = new DefaultStrategos(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!strategos)
gi.Error(ERR_FATAL, "Actor Could not create strategos");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitEnemyManager()
// Parameters: None
// Description: Initalizes the actor's EnemyManager
//
void Actor::InitEnemyManager()
2015-04-28 20:02:03 +00:00
{
enemyManager = nullptr;
enemyManager = new EnemyManager(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!enemyManager)
gi.Error(ERR_FATAL, "Actor Could not create enemyManager");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitPackageManager()
// Parameters: None
// Description: Initalizes the actor's PackageManager
//
void Actor::InitPackageManager()
2015-04-28 20:02:03 +00:00
{
packageManager = nullptr;
packageManager = new PackageManager(this);
if (!packageManager)
gi.Error(ERR_FATAL, "Actor Could not create packageManager");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitMovementSubsystem()
// Parameters: None
// Description: Initalizes the actor's MovementSubsystem
//
void Actor::InitMovementSubsystem()
2015-04-28 20:02:03 +00:00
{
movementSubsystem = nullptr;
movementSubsystem = new MovementSubsystem(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!movementSubsystem)
gi.Error(ERR_FATAL, "Actor Could not create movementSubsystem");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitPersonality()
// Parameters: None
// Description: Initialize the actor's Personality
//
void Actor::InitPersonality()
2015-04-28 20:02:03 +00:00
{
personality = nullptr;
personality = new Personality(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!personality)
gi.Error(ERR_FATAL, "Actor Could not create personality");
}
2012-12-30 16:37:54 +00:00
//
// Name: InitCombatSubsystem()
// Parameters: None
// Description: Initialize the actor's CombatSubsystem
//
void Actor::InitCombatSubsystem()
2015-04-28 20:02:03 +00:00
{
combatSubsystem = nullptr;
combatSubsystem = new CombatSubsystem(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!combatSubsystem)
gi.Error(ERR_FATAL, "Actor Could not create personality");
}
2012-12-30 16:37:54 +00:00
void Actor::InitHeadWatcher()
2015-04-28 20:02:03 +00:00
{
headWatcher = nullptr;
headWatcher = new HeadWatcher(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!headWatcher)
gi.Error(ERR_FATAL, "Actor Could not create personality");
}
2012-12-30 16:37:54 +00:00
void Actor::InitPostureController()
{
2015-04-28 20:02:03 +00:00
postureController = nullptr;
postureController = new PostureController(this);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!postureController)
gi.Error(ERR_FATAL, "Actor Could not create postureController");
2012-12-30 16:37:54 +00:00
}
//===================================================================================
// Private Functions
//===================================================================================
//
// Name: _dropActorToGround()
// Parameters: None
// Description: Tries to drop the actor to the ground
//
void Actor::_dropActorToGround()
2015-04-28 20:02:03 +00:00
{
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 & FlagFly))
{
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;
}
2012-12-30 16:37:54 +00:00
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
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;
}
}
2012-12-30 16:37:54 +00:00
//===================================================================================
// Archive Functions
//===================================================================================
//
// Name: Archive()
// Parameters: Archiver &arc
// Description: Archives the Actor Data
//
2015-04-28 20:02:03 +00:00
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 = dynamic_cast<Behavior *>(arc.ReadObject());
currentBehavior = behavior->getClassname();
behaviorFailureReason = behavior->GetFailureReason();
} else
{
behavior = nullptr;
currentBehavior = "";
behaviorFailureReason = "";
}
arc.ArchiveBoolean(&hBehavior_bool);
if (hBehavior_bool)
{
headBehavior = dynamic_cast<Behavior *>(arc.ReadObject());
currentHeadBehavior = headBehavior->getClassname();
} else
{
headBehavior = nullptr;
currentHeadBehavior = "";
}
arc.ArchiveBoolean(&eBehavior_bool);
if (eBehavior_bool)
{
eyeBehavior = dynamic_cast<Behavior *>(arc.ReadObject());
currentEyeBehavior = eyeBehavior->getClassname();
} else
{
eyeBehavior = nullptr;
currentEyeBehavior = "";
}
arc.ArchiveBoolean(&tBehavior_bool);
if (tBehavior_bool)
{
torsoBehavior = dynamic_cast<Behavior *>(arc.ReadObject());
currentTorsoBehavior = torsoBehavior->getClassname();
} else
{
torsoBehavior = nullptr;
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;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
more = false;
arc.ArchiveByte(&more);
} else
{
byte more;
DialogNode_t* new_dialog_node;
DialogNode_t* current_dialog_node = nullptr;
str alias_name;
str parm;
int current_parm;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveByte(&more);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
while (more)
{
new_dialog_node = NewDialogNode();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (current_dialog_node)
current_dialog_node->next = new_dialog_node;
else
dialog_list = new_dialog_node;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
current_dialog_node = new_dialog_node;
new_dialog_node->next = nullptr;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&alias_name);
strcpy(new_dialog_node->alias_name, alias_name.c_str());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (current_parm = 0; current_parm < new_dialog_node->number_of_parms; current_parm++)
{
arc.ArchiveByte(&new_dialog_node->parms[current_parm].type);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&parm);
strcpy(new_dialog_node->parms[current_parm].parm, parm.c_str());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&parm);
strcpy(new_dialog_node->parms[current_parm].parm2, parm.c_str());
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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 = dynamic_cast<Behavior *>(arc.ReadObject());
else
saved_behavior = nullptr;
arc.ArchiveBoolean(&hBehavior_bool);
if (hBehavior_bool)
saved_headBehavior = dynamic_cast<Behavior *>(arc.ReadObject());
else
saved_headBehavior = nullptr;
arc.ArchiveBoolean(&eBehavior_bool);
if (eBehavior_bool)
saved_eyeBehavior = dynamic_cast<Behavior *>(arc.ReadObject());
else
saved_eyeBehavior = nullptr;
arc.ArchiveBoolean(&tBehavior_bool);
if (tBehavior_bool)
saved_torsoBehavior = dynamic_cast<Behavior *>(arc.ReadObject());
else
saved_torsoBehavior = nullptr;
}
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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&part_ptr->ent);
arc.ArchiveUnsigned(&part_ptr->state_flags);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&incoming_proj);
arc.ArchiveFloat(&incoming_time);
arc.ArchiveBoolean(&incoming_bullet);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&name);
arc.ArchiveFloat(&max_inactive_time);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveVector(&eyeoffset);
arc.ArchiveFloat(&last_jump_time);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&enemytype);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&actorrange_time);
arc.ArchiveFloat(&last_height);
arc.ArchiveSafePointer(&last_ent);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&feet_width);
arc.ArchiveVector(&last_origin);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&next_find_enemy_time);
arc.ArchiveFloat(&minimum_melee_height);
arc.ArchiveFloat(&damage_angles);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&real_head_pitch);
arc.ArchiveFloat(&next_pain_sound_time);
arc.ArchiveFloat(&last_ground_z);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&emotion);
arc.ArchiveFloat(&next_blink_time);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&actor_to_actor_damage_modifier);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&last_used_time);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&hitscan_response_chance);
arc.ArchiveInteger(&shotsFired);
arc.ArchiveInteger(&ondamage_threshold);
arc.ArchiveFloat(&timeBetweenSleepChecks);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveInteger(&saved_bone_hit);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&_controller);
ArchiveEnum(_controlType, Actor::ActorControlType);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Save out currentHelperNode
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&currentHelperNode.node);
arc.ArchiveInteger(&currentHelperNode.mask);
arc.ArchiveInteger(&currentHelperNode.nodeID);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&ignoreHelperNode.node);
arc.ArchiveInteger(&ignoreHelperNode.mask);
arc.ArchiveInteger(&ignoreHelperNode.nodeID);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Save out followTarget
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&followTarget.currentFollowTarget);
arc.ArchiveSafePointer(&followTarget.specifiedFollowTarget);
arc.ArchiveFloat(&followTarget.maxRangeIdle);
arc.ArchiveFloat(&followTarget.minRangeIdle);
arc.ArchiveFloat(&followTarget.maxRangeCombat);
arc.ArchiveFloat(&followTarget.minRangeCombat);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveInteger(&_steeringDirectionPreference);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (arc.Saving())
{
num = stateVarList.NumObjects();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveInteger(&num);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= num; i++)
{
stateVar = stateVarList.ObjectAt(i);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&stateVar->varName);
arc.ArchiveString(&stateVar->varValue);
arc.ArchiveFloat(&stateVar->varTime);
}
} else
{
arc.ArchiveInteger(&num);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= num; i++)
{
stateVar = new StateVar;
stateVarList.AddObject(stateVar);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&stateVar->varName);
arc.ArchiveString(&stateVar->varValue);
arc.ArchiveFloat(&stateVar->varTime);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (arc.Saving())
{
num = threadList.NumObjects();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveInteger(&num);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= num; i++)
{
threadListEntry = threadList.ObjectAt(i);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&threadListEntry->threadType);
arc.ArchiveString(&threadListEntry->threadName);
}
} else
{
arc.ArchiveInteger(&num);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (i = 1; i <= num; i++)
{
threadListEntry = new threadlist_t;
threadList.AddObject(threadListEntry);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&threadListEntry->threadType);
arc.ArchiveString(&threadListEntry->threadName);
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&trigger);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&command);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&idle_state_name);
arc.ArchiveString(&master_idle_state_name);
arc.ArchiveString(&global_state_name);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&next_player_near);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveSafePointer(&pickup_ent);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&stunned_end_time);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
spawn_items.Archive(arc);
arc.ArchiveFloat(&spawn_chance);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveString(&bounce_off_effect);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
can_be_finsihed_by_mods.Archive(arc);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&max_boss_health);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveBoolean(&haveAttached);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&currentSplineTime);
arc.ArchiveFloat(&_dialogMorphMult);
ArchiveEnum(_useWeaponDamage, weaponhand_t);
arc.ArchiveFloat(&_nextCheckForWorkNodeTime);
arc.ArchiveFloat(&_nextCheckForHibernateNodeTime);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
arc.ArchiveFloat(&minLeadFactor);
arc.ArchiveFloat(&maxLeadFactor);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//arc.ArchiveInteger( &groupnumber );
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// 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(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&)
{
return GetActorFlag(ACTOR_FLAG_IN_CONE_OF_FIRE);
}
qboolean Actor::checkInPlayerConeOfFire(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return GetActorFlag(ACTOR_FLAG_IN_PLAYER_CONE_OF_FIRE);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
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));
}
2012-12-30 16:37:54 +00:00
void Actor::_notifyGroupOfDamage()
2015-04-28 20:02:03 +00:00
{
_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())
{
2015-04-29 20:34:44 +00:00
act->sensoryPerception->Stimuli(StimuliAll);
2015-04-28 20:02:03 +00:00
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())
{
2015-04-29 20:34:44 +00:00
act->sensoryPerception->Stimuli(StimuliAll);
2015-04-28 20:02:03 +00:00
act->enemyManager->TryToAddToHateList(currentEnemy);
act->enemyManager->SetCurrentEnemy(currentEnemy);
act->personality->SetAggressiveness(1.0f);
}
}
}
qboolean Actor::checkPatrolWaypointNodeInDistance(Conditional& condition)
{
auto distance = atof(condition.getParm(1));
for (auto i = 0; i < MAX_GENTITIES; i++)
{
auto ed = &g_entities[i];
if (!ed->inuse || !ed->entity)
{
continue;
}
auto ent_in_range = g_entities[i].entity;
if (ent_in_range->isSubclassOf(PatrolWayPointNode))
{
auto 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(nullptr);
return;
}
if (!Q_stricmp("player", watchTarget.c_str()))
{
auto player = GetPlayer(0);
headWatcher->SetWatchTarget(player);
}
if (!Q_stricmp("teammate", watchTarget.c_str()))
{
auto bestDist = 99999.0f;
Vector selfToTeammate;
Sentient* teammate = GetPlayer(0);
Sentient* closestTeammate = nullptr;
for (auto 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 = nullptr;
headWatcher->SetWatchTarget(ent);
}
void Actor::SetHeadWatchSpeed(Event* ev)
{
headWatcher->SetWatchSpeed(ev->GetFloat(1));
}
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 = nullptr;
if (node->targetEntity)
{
Entity* entity = node->targetEntity;
if (entity->isSubclassOf(WorkTrigger))
{
target = dynamic_cast<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 (!target)
return true;
return false;
}
qboolean Actor::_FleeNodeInDistance(float dist)
{
auto wtf = int32_t(lastPathCheck_Flee + HACK_PATH_CHECK);
if (wtf >= level.time)
return false;
lastPathCheck_Flee = level.time + HACK_PATH_CHECK + G_Random();
Vector pos;
Vector nodeOrigin;
Vector delta;
for (auto i = 1; i <= thePathManager.NumberOfSpecialNodes(); i++)
{
auto 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)
{
auto wtf = int32_t(lastPathCheck_Work + HACK_PATH_CHECK);
if (wtf >= level.time)
return false;
lastPathCheck_Work = level.time + G_Random();
Vector delta;
Vector pos;
Vector nodeOrigin;
for (auto i = 1; i <= thePathManager.NumberOfSpecialNodes(); i++)
{
auto 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*)
{
AdaptiveArmor::ClearAdaptionList();
}
void Actor::SetMovementMode(Event* ev)
{
str modeType;
modeType = ev->GetString(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetCinematicAnim(const str& animName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
Entity::SetCinematicAnim(animName); // Ensure entity level cinematic stuff is enabled
movementSubsystem->setMovementType(MOVEMENT_TYPE_ANIM);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::CinematicAnimDone(void)
{
Entity::CinematicAnimDone(); // Ensures entity level cinematic stuff is disabled
movementSubsystem->setMovementType(MOVEMENT_TYPE_NORMAL);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkForwardDirectionClear(Conditional& condition)
{
return checkForwardDirectionClear(atof(condition.getParm(1)));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkForwardDirectionClear(float dist)
{
trace_t trace;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Vector endPos;
Vector startPos;
Vector forward;
Vector angles;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
startPos = origin;
startPos.z += 32;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
angles.AngleVectors(&forward);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
endPos = forward * dist + startPos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
trace = G_Trace(startPos, mins, maxs, endPos, nullptr, edict->clipmask, false, "checkForwardDirectionClear");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.fraction == 1.0)
{
return movementSubsystem->CanWalkTo(trace.endpos, 0.0f, entnum);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkRearDirectionClear(Conditional& condition)
{
return checkRearDirectionClear(atof(condition.getParm(1)));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkRearDirectionClear(float dist)
{
Vector forward;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto startPos = origin;
startPos.z += 32;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
angles[YAW] = AngleNormalize180(angles[YAW] + 180);
angles.AngleVectors(&forward);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto endPos = forward * dist + startPos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto trace = G_Trace(startPos, mins, maxs, endPos, nullptr, edict->clipmask, false, "checkForwardDirectionClear");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (trace.fraction == 1.0)
{
return movementSubsystem->CanWalkTo(trace.endpos, 0.0f, entnum);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkLeftDirectionClear(Conditional& condition)
{
return checkLeftDirectionClear(atof(condition.getParm(1)));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkLeftDirectionClear(float dist)
{
Vector left;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto startPos = origin;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
angles.AngleVectors(nullptr, &left, nullptr);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto endPos = left * dist + startPos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto trace = Trace(endPos, "CheckMyLeft");
if (trace.fraction == 1.0)
{
return movementSubsystem->CanWalkTo(trace.endpos, 0.0f, entnum);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkRightDirectionClear(Conditional& condition)
{
return checkRightDirectionClear(atof(condition.getParm(1)));
}
2012-12-30 16:37:54 +00:00
qboolean Actor::checkRightDirectionClear(float dist)
2015-04-28 20:02:03 +00:00
{
Vector left;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto startPos = origin;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto angles = movementSubsystem->getAnimDir();
angles = angles.toAngles();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
angles[YAW] = AngleNormalize180(angles[YAW] + 180);
angles.AngleVectors(nullptr, &left, nullptr);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto endPos = left * dist + startPos;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto trace = Trace(endPos, "CheckMyRight");
if (trace.fraction == 1.0)
{
return movementSubsystem->CanWalkTo(trace.endpos, 0.0f, entnum);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkbehaviorsuccess(Conditional&)
{
if (behaviorCode == BEHAVIOR_SUCCESS)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkbehaviorfailed(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetNodeID(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
currentHelperNode.nodeID = ev->GetInteger(1);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetFollowTarget(Event* ev)
{
auto ent = ev->GetEntity(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ent)
followTarget.specifiedFollowTarget = ent;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetFollowRange(Event* ev)
{
followTarget.maxRangeIdle = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetFollowRangeMin(Event* ev)
{
followTarget.minRangeIdle = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetFollowCombatRange(Event* ev)
{
followTarget.maxRangeCombat = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetFollowCombatRangeMin(Event* ev)
{
followTarget.minRangeCombat = ev->GetFloat(1);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkLastState(Conditional& condition)
{
str lastStateName;
lastStateName = condition.getParm(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!Q_stricmp(lastStateName.c_str(), lastState->getName()))
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetTalkiness(Event* ev)
{
personality->SetTalkiness(ev->GetFloat(1));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void Actor::SetTendency(Event* ev)
{
personality->SetTendency(ev->GetString(1), ev->GetFloat(2));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
void AI_DisplayInfo(void)
{
if (ai_numactive->integer)
{
gi.Printf("Active actors - %d\n", ActiveList.NumObjects());
}
}
2012-12-30 16:37:54 +00:00
//
//=================================================================================================
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::InContext(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto useDefaultMinDist = ev->NumArgs() > 1 ? ev->GetBoolean(2) : false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
InContext(ev->GetString(1), useDefaultMinDist);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
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;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
realDialog = FindDialog(this, DIALOG_TYPE_CONTEXT_INITIATOR, theContext);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!realDialog.length())
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
ignoreEvent = new Event(EV_ContextDialog_IgnoreNextContext);
ignoreEvent->AddInteger(1);
ignoreEvent->AddString(theContext);
groupcoordinator->SendEventToGroup(ignoreEvent, GetGroupID());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
char localizedDialogName[MAX_QPATH];
gi.LocalizeFilePath(realDialog, localizedDialogName);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
_nextContextTime = level.time + G_Random() + _contextInterval;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
dialogLength = gi.SoundLength(localizedDialogName);
broadcastEvent = new Event(EV_Actor_BroadcastDialog);
broadcastEvent->AddString(theContext);
PostEvent(broadcastEvent, dialogLength);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (useDefaultMinDist)
PlayDialog(this, DEFAULT_VOL, DEFAULT_MIN_DIST, realDialog.c_str(), nullptr);
else
PlayDialog(this, DEFAULT_VOL, CONTEXT_WIDE_MIN_DIST, realDialog.c_str(), nullptr);
2012-12-30 16:37:54 +00:00
}
//
// 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
//
2015-04-28 20:02:03 +00:00
void Actor::BroadcastDialog(Event* ev)
{
Actor* bestAct = nullptr;
Vector delta;
str responseDialog;
str bestResponseDialog;
auto bestDist = SOUND_RADIUS;
for (auto i = 1; i <= SentientList.NumObjects(); i++)
{
Entity* ent = SentientList.ObjectAt(i);
if (ent == this || ent->deadflag)
{
continue;
}
if (ent->isSubclassOf(Actor))
{
auto act = dynamic_cast<Actor*>(ent);
delta = origin - act->centroid;
auto 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, ev->GetString(1));
if (responseDialog.length())
{
bestAct = act;
bestDist = dist;
bestResponseDialog = responseDialog;
}
}
}
}
}
if (bestAct)
{
//
// Play the Response
//
bestAct->PlayDialog(bestAct, DEFAULT_VOL, -1.0f, bestResponseDialog.c_str(), nullptr);
}
}
2012-12-30 16:37:54 +00:00
//
// 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()
2015-04-28 20:02:03 +00:00
{
return G_Random() <= personality->GetTalkiness();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkGroupMememberRange(Conditional& condition)
{
Vector actToSelf;
auto reqDist = atof(condition.getParm(1));
for (auto i = 1; i <= ActiveList.NumObjects(); i++)
{
auto act = ActiveList.ObjectAt(i);
if (act && act != this && act->GetGroupID() == GetGroupID())
{
actToSelf = origin - act->origin;
auto dist = actToSelf.length();
if (dist <= reqDist)
{
return true;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
qboolean Actor::checkActorType(Conditional& condition)
{
str aType = condition.getParm(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!stricmp(aType.c_str(), "inanimate"))
{
if (actortype == IS_INANIMATE)
return true;
return false;
}
if (!Q_stricmp(aType.c_str(), "monster"))
{
if (actortype == IS_MONSTER)
return true;
return false;
}
if (!Q_stricmp(aType.c_str(), "enemy"))
{
if (actortype == IS_ENEMY)
return true;
return false;
}
if (!Q_stricmp(aType.c_str(), "civilian"))
{
if (actortype == IS_CIVILIAN)
return true;
return false;
}
if (!Q_stricmp(aType.c_str(), "friend"))
{
if (actortype == IS_FRIEND)
return true;
return false;
}
if (!Q_stricmp(aType.c_str(), "animal"))
{
if (actortype == IS_ANIMAL)
return true;
return false;
}
if (!Q_stricmp(aType.c_str(), "teammate"))
{
if (actortype == IS_TEAMMATE)
return true;
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
}
qboolean Actor::checkIsTeammate(Conditional&)
{
if (actortype == IS_TEAMMATE)
return true;
return false;
}
qboolean Actor::checkHaveActiveWeapon(Conditional&)
{
return combatSubsystem->HaveWeapon();
}
qboolean Actor::checkWeaponIsMelee(Conditional&)
{
return combatSubsystem->WeaponIsFireType(FT_MELEE);
}
qboolean Actor::checkWeaponChanged(Conditional&)
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagChangedWeapon;
2012-12-30 16:37:54 +00:00
}
//----------------------------------------------------------------
// Name: FindActorByName
// Class: Actor
//
// Description: Goes through the ActiveList and finds an actor with
// the matching name.
//
// Parameters: const str &name -- The name
//
2015-04-28 20:02:03 +00:00
// Returns: Actor pointer, or nullptr if it's not found
2012-12-30 16:37:54 +00:00
//----------------------------------------------------------------
2015-04-28 20:02:03 +00:00
Actor* Actor::FindActorByName(const str& charName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
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 nullptr;
2012-12-30 16:37:54 +00:00
}
//----------------------------------------------------------------
// Name: setDialogMorphMult
// Class: Actor
//
// Description: Sets the multiplier for all dialog morphs for this actor
//
// Parameters: Event *ev - contains, float dialogMorphMultiplier
//
// Returns: none
//----------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::setDialogMorphMult(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
_dialogMorphMult = ev->GetFloat(1);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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)
{
2015-04-28 20:02:03 +00:00
float resistance;
resistance = GetResistanceModifier(MeansOfDeath);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (resistance >= 100.0)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (currentBaseArmor)
{
return currentBaseArmor->CanBeDamagedBy(MeansOfDeath);
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return true;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ForceSetClip(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
edict->contents = CONTENTS_SETCLIP;
edict->clipmask = MASK_SETCLIP;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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;
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkCountOfIdenticalNamesInGroup(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
int checkValue;
str checkName;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
checkName = condition.getParm(1);
checkValue = atoi(condition.getParm(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return checkCountOfIdenticalNamesInGroup(checkName, checkValue);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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;
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkCountOfIdenticalNamesInGroup(const str& checkName, int checkValue)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto group = dynamic_cast<ActorGroup*>(groupcoordinator->GetGroup(GetGroupID()));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!group)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (group->CountMembersWithThisName(checkName) < checkValue)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkCanAttackEnemy(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkCanAttackEnemy();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkCanAttackEnemy()
{
2015-04-28 20:02:03 +00:00
// Get our current enemy
Entity* currentEnemy;
currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return combatSubsystem->CanAttackTarget(currentEnemy);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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;
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkGroupAttackerCount(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkGroupAttackerCountForEntity(condition, nullptr);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: SetBehaviorPackage()
// Class: Actor
//
// Description: Calls SetBehaviorPackage()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::SetBehaviorPackage(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetBehaviorPackage(ev->GetString(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: SetBehaviorPackage()
// Class: Actor
//
// Description: Attempts to set the requested behavior package
//
// Parameters: const str &packageName
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
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);
}
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: UseBehaviorPackage()
// Class: Actor
//
// Description: Calls UseBehaviorPackage()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::UseBehaviorPackage(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
UseBehaviorPackage(ev->GetString(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: UseBehaviorPackage()
// Class: Actor
//
// Description: Tells the Strategos to set the requested
// behavior packaged
//
// Parameters: const str &packageName
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::UseBehaviorPackage(const str& packageName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
strategos->SetBehaviorPackage(packageName);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: ChildUseBehaviorPackage()
// Class: Actor
//
// Description: Calls UseBehaviorPackage()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ChildUseBehaviorPackage(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
ChildUseBehaviorPackage(ev->GetString(1), ev->GetString(2));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
//
// Name: GetAttachedChildActor
// Class: Actor
//
// Description: Gets an attached child actor by name
//
// Parameters: const str& childName -- Child actor to find
//
2015-04-28 20:02:03 +00:00
// Returns: Actor* -- The child or nullptr if not found.
2012-12-30 16:37:54 +00:00
//
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
Actor* Actor::GetAttachedChildActor(const str& childName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (!bind_info)
return nullptr;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Actor* childActor = nullptr;
for (auto i = 0; i < MAX_MODEL_CHILDREN; i++)
{
if (bind_info->children[i] == ENTITYNUM_NONE)
continue;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto child = G_GetEntity(bind_info->children[i]);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!stricmp(child->TargetName(), childName.c_str()))
{
if (child->isSubclassOf(Actor))
childActor = dynamic_cast<Actor*>(child);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (childActor)
return childActor;
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return nullptr;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: ChildUseBehaviorPackage()
// Class: Actor
//
// Description: Tells the Strategos to set the requested
// behavior packaged
//
// Parameters: const str &packageName
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ChildUseBehaviorPackage(const str& childName, const str& packageName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto childActor = GetAttachedChildActor(childName);
if (childActor)
childActor->SetBehaviorPackage(packageName);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: ChildSetAnim()
// Class: Actor
//
// Description: Calls ChildSetAnim()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ChildSetAnim(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
ChildSetAnim(ev->GetString(1), ev->GetString(2));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: ChildSuicide()
// Class: Actor
//
// Description: Calls ChildSetAnim()
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ChildSuicide(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto childActor = GetAttachedChildActor(ev->GetString(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (childActor)
childActor->ProcessEvent(EV_Actor_Suicide);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::ChildSetAnim(const str& childName, const str& animName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto childActor = GetAttachedChildActor(childName);
if (childActor)
childActor->SetAnim(animName);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: WhatsWrong()
// Class: Actor
//
// Description: Reports the failure condition for the current behavior
// to the console
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::WhatsWrong(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
gi.Printf("\n------------------------------------------");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!behaviorFailureReason.length())
gi.Printf("\nNo Failure Reason given for behavior %s\n", currentBehavior.c_str());
else
gi.Printf("\nReason: %s", behaviorFailureReason.c_str());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gi.Printf("\n------------------------------------------");
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::WhatAreYouDoing(Event*)
{
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-----------------------------------------------");
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: PrintMasterStateInfo()
// Class: Actor
//
// Description: Prints information about the MasterState in the console
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::PrintMasterStateInfo()
{
2015-04-28 20:02:03 +00:00
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");
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: PrintBehaviorPackageInfo()
// Class: Actor
//
// Description: Prints information about the BehaviorPackage in the console
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::PrintBehaviorPackageInfo()
{
2015-04-28 20:02:03 +00:00
gi.Printf("\nBehaviorPackage Information:");
gi.Printf("\nCurrent Behavior Package Name: %s", packageManager->GetCurrentPackageName().c_str());
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (currentState)
gi.Printf("\nCurrent Behavior Package State: %s", currentState->getName());
else
gi.Printf("\nCurrent Behavior Package State: !!!!!nullptr!!!!!");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
gi.Printf("\nLast Behavior Package State: %s", lastState->getName());
gi.Printf(" \n");
gi.Printf(" \n");
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: PrintStateMapInfo()
// Class: Actor
//
// Description: Prints information about the state map in the console
//
// Parameters: None
//
// Returns: None
//--------------------------------------------------------------
void Actor::PrintStateMapInfo()
{
2015-04-28 20:02:03 +00:00
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");
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::SetCombatTraceInterval(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
combatSubsystem->SetTraceInterval(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: ActorTypeStringToInt()
// Class: Actor
//
// Description: Returns an Actor Type ID based on the string passed in
//
// Parameters: const str &type
//
// Returns: unsigned int
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
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;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: GroupMemberInjured()
// Class: Actor
//
// Description: Appropriately sets the ACTOR_FLAG_GROUPMEMBER_INJURED flag
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::GroupMemberInjured(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_GROUPMEMBER_INJURED, ev->GetBoolean(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: StrictlyFollowPath()
// Class: Actor
//
// Description: Appropriately sets the ACTOR_FLAG_STRICTLY_FOLLOW_PATHS flag
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::StrictlyFollowPath(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_STRICTLY_FOLLOW_PATHS, ev->GetBoolean(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
//
// 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()
{
2015-04-28 20:02:03 +00:00
// More conditions? Non-hardcoded min health?
if (health > 0 && health < 30)
return true;
return false;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
//
// Name: UseWeaponDamage
// Class: Actor
//
// Description: Causes the MeleeEvent to use the damage from the
// weapon in the specified hand
//
// Parameters: Event *ev
//
// Returns: None
//
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::UseWeaponDamage(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 0)
_useWeaponDamage = WeaponHandNameToNum(ev->GetString(1));
else
_useWeaponDamage = WEAPON_RIGHT;
if (ev->NumArgs() > 1)
{
if (!ev->GetBoolean(2))
_useWeaponDamage = WEAPON_ERROR;
}
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::HelperNodeCommand(Event*)
2012-12-30 16:37:54 +00:00
{
}
//--------------------------------------------------------------
// Name: SetIgnoreNextContext()
// Class: Actor
//
// Description: Sets the _ignoreNextContext flag
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::SetIgnoreNextContext(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
_ignoreNextContext = ev->GetBoolean(1);
_nextContextToIgnore = ev->GetString(2);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::EvaluateEnemies(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (!enemyManager->IsLockedOnCurrentEnemy())
enemyManager->FindHighestHateEnemy();
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::ForgetEnemies(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (enemyManager)
{
enemyManager->ClearCurrentEnemy();
enemyManager->ClearHateList();
}
2012-12-30 16:37:54 +00:00
}
//==============================================================
// 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
(
2015-04-28 20:02:03 +00:00
Listener* controller,
ActorControlType controlType
2012-12-30 16:37:54 +00:00
)
{
2015-04-28 20:02:03 +00:00
if (!_controller)
{
_controller = controller;
_controlType = controlType;
return true;
}
if (_controlType != ACTOR_CONTROL_LOCKED)
{
auto ev = new Event(EV_Actor_ControlLost);
_controller->ProcessEvent(ev);
_controller = controller;
return true;
}
return false;
2012-12-30 16:37:54 +00:00
}
//===============================================================
// 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
(
2015-04-28 20:02:03 +00:00
Listener* controller
2012-12-30 16:37:54 +00:00
)
{
2015-04-28 20:02:03 +00:00
if (!_controller)
return false;
if (_controller != controller)
return false;
_controller = nullptr;
_controlType = ACTOR_CONTROL_NONE;
return true;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: SetMaxHeadYaw()
// Class: Actor
//
// Description: Sets the max head yaw for the headwatcher class
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::SetMaxHeadYaw(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
headWatcher->SetMaxHeadYaw(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: SetMaxHeadPitch()
// Class: Actor
//
// Description: Sets the max head pitch for the headwatcher class
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::SetMaxHeadPitch(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
headWatcher->SetMaxHeadPitch(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: LoadPostureStateMachine()
// Class: Actor
//
// Description: Creates a new Posture State Machine
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::LoadPostureStateMachine(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
bool loading;
if (ev->NumArgs() > 1)
loading = ev->GetBoolean(2);
else
loading = false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
postureController->setPostureStateMap(ev->GetString(1), loading);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: PostureAnimDone()
// Class: Actor
//
// Description: Event Handler for the completion of a posture animation
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::PostureAnimDone(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetActorFlag(ACTOR_FLAG_POSTURE_ANIM_DONE, true);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkRequestedPosture(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (postureController->getRequestedPostureName() == condition.getParm(1))
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: checkPostureAnimDone()
// Class: Actor
//
// Description: Checks the Posture Anim Done Flag
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkPostureAnimDone(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return GetActorFlag(ACTOR_FLAG_POSTURE_ANIM_DONE);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: checkDamageThresholdExceeded()
// Class: Actor
//
// Description: Calls checkDamageThresholdExceeded()
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkDamageThresholdExceeded(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkDamageThresholdExceeded();
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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()
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagDamageThresholdExceeded;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkhealthpercent(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto percent = .01f * atof(condition.getParm(1));
return health <= percent * max_health;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: checkHelperNodeWithFlagInRange()
// Class: Actor
//
// Description: Calls checkHelperNodeWithFlagInRange()
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkHelperNodeWithFlagInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str flagName = condition.getParm(1);
float range = atof(condition.getParm(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return checkHelperNodeWithFlagInRange(flagName, range);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkHelperNodeWithFlagInRange(const str& flag, float range)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
int mask;
mask = HelperNode::GetHelperNodeMask(flag);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return HelperNode::isHelperNodeInRange(*this, mask, range);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: SendEventToGroup()
// Class: Actor
//
// Description: Sends an event to the Actor's group
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::SendEventToGroup(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto event = new Event(ev->GetString(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
for (auto i = 2; i <= ev->NumArgs(); i++)
{
event->AddToken(ev->GetToken(i));
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
groupcoordinator->SendEventToGroup(event, GetGroupID());
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: checkEnemyWeaponNamed()
// Class: Actor
//
// Description: Calls checkEnemyWeaponNamed
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkEnemyWeaponNamed(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str weaponName = condition.getParm(1);
return checkEnemyWeaponNamed(weaponName);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkEnemyWeaponNamed(const str& name)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
enemyManager->FindHighestHateEnemy();
Entity* enemy = enemyManager->GetCurrentEnemy();
if (enemy)
{
if (enemy->isSubclassOf(Actor))
{
return dynamic_cast<Actor*>(enemy)->combatSubsystem->UsingWeaponNamed(name);
}
if (enemy->isSubclassOf(Player))
{
auto player = dynamic_cast<Player*>(enemy);
auto 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;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: checkPlayerWeaponNamed()
// Class: Actor
//
// Description: Calls checkEnemyWeaponNamed
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkPlayerWeaponNamed(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str weaponName = condition.getParm(1);
return checkPlayerWeaponNamed(weaponName);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkPlayerWeaponNamed(const str& name)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
Player* player;
player = GetPlayer(0);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (player)
{
Weapon* pWeapon;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
pWeapon = player->GetActiveWeapon(WEAPON_DUAL);
if (pWeapon && pWeapon->getName() == name)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
pWeapon = player->GetActiveWeapon(WEAPON_LEFT);
if (pWeapon && pWeapon->getName() == name)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
pWeapon = player->GetActiveWeapon(WEAPON_RIGHT);
if (pWeapon && pWeapon->getName() == name)
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: GroupAttack()
// Class: Actor
//
// Description: Sends an attack event to the Actor's group
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::GroupAttack(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto force = ev->NumArgs() > 0 ? ev->GetBoolean(1) : false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Entity* enemy = enemyManager->GetCurrentEnemy();
if (!enemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto attackEvent = new Event(EV_Actor_Attack);
attackEvent->AddEntity(enemy);
attackEvent->AddInteger(force);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
groupcoordinator->SendEventToGroup(attackEvent, GetGroupID());
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: GroupAttack()
// Class: Actor
//
// Description: Sends an attack event to the Actor's group
//
// Parameters: Event *ev
//
// Returns: None
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::GroupActorType(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto typeEvent = new Event(EV_Actor_SetActorType);
typeEvent->AddString(ev->GetString(1));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
groupcoordinator->SendEventToGroup(typeEvent, GetGroupID());
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// Name: checkEnemyWithinRange()
// Class: Actor
//
// Description: Calls checkEnemyWithinRange()
//
// Parameters: Conditional &condition
//
// Returns: true or false
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkEnemyWithinRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkEnemyWithinRange(atof(condition.getParm(1)), atof(condition.getParm(2)));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkEnemyWithinRange(float min, float max)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto dist = enemyManager->GetDistanceFromEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return dist <= max && dist >= min;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkhealthpercentinrange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto minpercent = .01f * atof(condition.getParm(1));
auto maxpercent = .01f * atof(condition.getParm(2));
return health >= minpercent * max_health && health <= maxpercent * max_health;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::PrintDebugMessage(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str msg = ev->GetString(1);
gi.WDPrintf("\n--------------------------------------------------------------\n");
gi.WDPrintf(msg + "\n");
gi.WDPrintf("--------------------------------------------------------------\n");
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkAttacked(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkAttacked();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkAttacked()
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagAttacked;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkAttackedByPlayer(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkAttackedByPlayer();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkAttackedByPlayer()
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagAttackedByPlayer;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkShowPain(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkShowPain();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkShowPain()
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagShowPain;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkPropEnemyRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str propname;
str objname = condition.getParm(1);
if (condition.numParms() > 1)
propname = condition.getParm(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return checkPropEnemyRange(objname, propname);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkPropEnemyRange(const str& objname, const str& propname)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto gpm = GameplayManager::getTheGameplayManager();
auto scopestr = getArchetype() + "." + objname;
if (!gpm->hasObject(scopestr))
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto range = propname.length() ? gpm->getFloatValue(scopestr, propname) : gpm->getFloatValue(scopestr, "value");
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Get our current enemy
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy)
enemyManager->FindHighestHateEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
currentEnemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy)
enemyManager->FindHighestHateEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return EntityInRange(currentEnemy, range, 0, 0, false);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::processGameplayData(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto gpm = GameplayManager::getTheGameplayManager();
if (!gpm->hasObject(getArchetype()))
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto objname = getArchetype();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Grab our FOV From the GPD
if (gpm->hasProperty(objname, "fov"))
sensoryPerception->SetFOV(gpm->getFloatValue(objname, "fov"));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Grab our PlayerHateModifier
// Grab our VisionDistance
if (gpm->hasProperty(objname, "visiondistance"))
sensoryPerception->SetVisionDistance(gpm->getFloatValue(objname, "visiondistance"));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SelectNextEnemy(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
enemyManager->FindNextEnemy();
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SelectClosestEnemy(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
enemyManager->FindClosestEnemy();
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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;
//
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkCurrentEnemyGroupAttackerCount(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
Entity* e = enemyManager->GetCurrentEnemy();
return checkGroupAttackerCountForEntity(condition, e);
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
2015-04-28 20:02:03 +00:00
// Entity* attackTarget - pass nullptr to signify any attack target is OK;
2012-12-30 16:37:54 +00:00
// i.e., count how many of my groupmates are attacking at all.
//
// Returns: true or false;
//
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
qboolean Actor::checkGroupAttackerCountForEntity(Conditional& condition, Entity* attackTarget)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkGroupAttackerCountForEntity(atoi(condition.getParm(1)), attackTarget);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkGroupAttackerCountForEntity(int checkValue, Entity* attackTarget)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto group = dynamic_cast<ActorGroup*>(groupcoordinator->GetGroup(GetGroupID()));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!group)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (group->CountMembersAttackingEnemy(attackTarget) < checkValue)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetGroupDeathThread(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
groupcoordinator->SetGroupDeathThread(ev->GetString(1), GetGroupID());
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkHaveBestWeapon(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkHaveBestWeapon();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkHaveBestWeapon()
{
2015-04-28 20:02:03 +00:00
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto bestWeapon = combatSubsystem->GetBestAvailableWeapon(currentEnemy);
if (!bestWeapon)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (combatSubsystem->GetActiveWeaponName() == bestWeapon->getName())
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkPosture(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkPosture(condition.getParm(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkPosture(const str& postureName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (postureController->getCurrentPostureName() == postureName)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkAnyEnemyInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
float range = atof(condition.getParm(1));
return checkAnyEnemyInRange(range);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkAnyEnemyInRange(float range)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return enemyManager->IsAnyEnemyInRange(range);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetAnimSet(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetAnimSet(ev->GetString(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetAnimSet(const str& animSet)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
animset = animSet;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetSelfDetonateModel(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
explosionModel = ev->GetString(1);
2012-12-30 16:37:54 +00:00
}
const str& Actor::GetAnimSet()
{
2015-04-28 20:02:03 +00:00
return animset;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
str Actor::GetStateVar(const str& varName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
for (auto i = 1; i <= stateVarList.NumObjects(); i++)
{
auto checkVar = stateVarList.ObjectAt(i);
if (checkVar->varName == varName)
{
return checkVar->varValue;
}
}
return "";
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::ClearTorsoAnim(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
ClearTorsoAnim();
2012-12-30 16:37:54 +00:00
}
void Actor::ClearTorsoAnim()
{
2015-04-28 20:02:03 +00:00
newTorsoAnimNum = -1;
newTorsoAnim = "";
newTorsoAnimEvent = nullptr;
TorsoAnimName = "";
animate->ClearTorsoAnim();
2012-12-30 16:37:54 +00:00
}
void Actor::ClearLegAnim()
{
2015-04-28 20:02:03 +00:00
newanimnum = -1;
newanim = "";
newanimevent = nullptr;
animate->ClearLegsAnim();
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidCoverNodeInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto minDistanceFromPlayer = 96.0f;
auto maxDistanceFromSelf = float(atof(condition.getParm(1)));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_USE_FOLLOWRANGE_FOR_NODES))
{
if (enemyManager->HasEnemy())
{
maxDistanceFromSelf = followTarget.maxRangeCombat;
} else
maxDistanceFromSelf = followTarget.minRangeCombat;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Fudge the value a little bit to allow for some "spring"
maxDistanceFromSelf = maxDistanceFromSelf * .85;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
float minDistanceFromCurrentEnemy = atof(condition.getParm(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (condition.numParms() > 2)
minDistanceFromPlayer = atof(condition.getParm(3));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return checkValidCoverNodeInRange(maxDistanceFromSelf, minDistanceFromCurrentEnemy, minDistanceFromPlayer);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidCoverNodeInRange(float maxDistanceFromSelf, float minDistanceFromCurrentEnemy, float minDistanceFromPlayer)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//If we don't have a current enemy, give it one more evaluation chance
if (!currentEnemy)
{
enemyManager->FindHighestHateEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!currentEnemy)
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//
//See if we have a cover node that meets or requirements.
//If we do, the FindClosestHelperNodeThatCannotSeeEntity will set our currentHelperNode information for us
//
auto coverNode = HelperNode::FindClosestHelperNodeThatCannotSeeEntity(*this, NODETYPE_COVER, edict->clipmask, maxDistanceFromSelf, minDistanceFromCurrentEnemy, currentEnemy, minDistanceFromPlayer);
if (coverNode)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidCombatNodeInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
auto minDistanceFromPlayer = 96.0f;
auto maxDistanceFromSelf = float(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);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidCombatNodeInRange(float maxDistanceFromSelf, float minDistanceFromPlayer, bool unreserveCurrentNode)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
HelperNode* coverNode;
//
//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;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkEnemyCanSeeCurrentNode(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkEnemyCanSeeCurrentNode();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkEnemyCanSeeCurrentNode()
{
2015-04-28 20:02:03 +00:00
if (!currentHelperNode.node)
return false;
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
{
enemyManager->FindClosestEnemy();
currentEnemy = enemyManager->GetCurrentEnemy();
}
if (!currentEnemy)
return false;
trace_t trace;
if (currentEnemy->isSubclassOf(Sentient))
{
auto theEnemy = dynamic_cast<Sentient*>(currentEnemy);
trace = G_Trace(currentHelperNode.node->origin, vec_zero, vec_zero, theEnemy->EyePosition(), nullptr, MASK_OPAQUE, false, "CoverCombatWithRangedWeapon::think");
} else
{
trace = G_Trace(currentHelperNode.node->origin, vec_zero, vec_zero, currentEnemy->centroid, nullptr, MASK_OPAQUE, false, "CoverCombatWithRangedWeapon::think");
}
if (trace.fraction >= .95)
return true;
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkShouldDoAction(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkShouldDoAction(condition.getParm(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkShouldDoAction(const str& tendencyName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return G_Random() < personality->GetTendency(tendencyName);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidWorkNodeInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
int32_t unreserveCurrentNode = true; // this is int to avoid compiler warning C4800 :(
auto maxDistanceFromSelf = condition.getParm<float>(1);
if (condition.numParms() > 1)
unreserveCurrentNode = condition.getParm<int32_t>(2);
return checkValidWorkNodeInRange(maxDistanceFromSelf, unreserveCurrentNode != 0);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidWorkNodeInRange(float maxDistanceFromSelf, bool unreserveCurrentNode)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (level.time < _nextCheckForWorkNodeTime)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//
//See if we have a work node that meets our requirements.
//
auto coverNode = HelperNode::FindClosestHelperNode(*this, NODETYPE_WORK, maxDistanceFromSelf, 0.0f, unreserveCurrentNode);
_nextCheckForWorkNodeTime = G_Random() + level.time + 0.50f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (coverNode)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidHibernateNodeInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
float maxDistanceFromSelf = atof(condition.getParm(1));
return checkValidHibernateNodeInRange(maxDistanceFromSelf);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidHibernateNodeInRange(float maxDistanceFromSelf)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (level.time < _nextCheckForHibernateNodeTime)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//
//See if we have a work node that meets our requirements.
//
auto hibernateNode = HelperNode::FindClosestHelperNode(*this, "hibernate", maxDistanceFromSelf);
_nextCheckForHibernateNodeTime = G_Random() + level.time + 0.50f;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (hibernateNode)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidPatrolNodeInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
float maxDistanceFromSelf = atof(condition.getParm(1));
return checkValidPatrolNodeInRange(maxDistanceFromSelf);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidPatrolNodeInRange(float maxDistanceFromSelf)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
//
//See if we have a cover node that meets or requirements.
//
auto patrolNode = HelperNode::FindClosestHelperNode(*this, NODETYPE_PATROL, maxDistanceFromSelf, 0);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (patrolNode)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidSniperNodeInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
float maxDistanceFromSelf = atof(condition.getParm(1));
return checkValidSniperNodeInRange(maxDistanceFromSelf);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidSniperNodeInRange(float maxDistanceFromSelf)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
//
//See if we have a cover node that meets or requirements.
//
auto sniperNode = HelperNode::FindClosestHelperNode(*this, NODETYPE_SNIPER, maxDistanceFromSelf);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (sniperNode)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidCustomNodeInRange(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str customType = condition.getParm(1);
float maxDistanceFromSelf = atof(condition.getParm(2));
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return checkValidCustomNodeInRange(customType, maxDistanceFromSelf);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkValidCustomNodeInRange(const str& customType, float maxDistanceFromSelf)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
//
//See if we have a cover node that meets or requirements.
//
auto customNode = HelperNode::FindClosestHelperNode(*this, customType, maxDistanceFromSelf);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (customNode)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkSpecifiedFollowTargetOutOfRange(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkSpecifiedFollowTargetOutOfRange();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkSpecifiedFollowTargetOutOfRange()
{
2015-04-28 20:02:03 +00:00
if (!followTarget.specifiedFollowTarget)
followTarget.specifiedFollowTarget = GetPlayer(0);
if (followTarget.specifiedFollowTarget)
{
auto range = followTarget.maxRangeIdle;
Entity* enemy = enemyManager->GetCurrentEnemy();
if (!enemy)
{
enemyManager->FindHighestHateEnemy();
enemy = enemyManager->GetCurrentEnemy();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (enemy)
range = followTarget.maxRangeCombat;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (EntityInRange(followTarget.specifiedFollowTarget, range, 0, 0, false))
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetPostureState(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
postureController->setPostureState(ev->GetString(1), ev->GetString(2));
2012-12-30 16:37:54 +00:00
}
//--------------------------------------------------------------
// 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
//--------------------------------------------------------------
2015-04-28 20:02:03 +00:00
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;
auto usingContext = false;
dialog_node = dialog_list;
while (dialog_node != nullptr)
{
// 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 (auto 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 = nullptr;
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 = nullptr;
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 = nullptr;
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 = 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&)
{
return checkHaveArmor();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkHaveArmor()
{
2015-04-28 20:02:03 +00:00
if (!currentBaseArmor)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (currentBaseArmor->getAmount() <= 0)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return true;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkWithinFollowRangeMin(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkWithinFollowRangeMin();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkWithinFollowRangeMin()
{
2015-04-28 20:02:03 +00:00
if (!followTarget.specifiedFollowTarget)
followTarget.specifiedFollowTarget = GetPlayer(0);
if (followTarget.specifiedFollowTarget)
{
auto range = followTarget.minRangeIdle;
Entity* enemy;
enemy = enemyManager->GetCurrentEnemy();
if (!enemy)
{
enemyManager->FindHighestHateEnemy();
enemy = enemyManager->GetCurrentEnemy();
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (enemy)
range = followTarget.minRangeCombat;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (EntityInRange(followTarget.specifiedFollowTarget, range, 0, 0, false))
{
if (level.time > _nextPathDistanceToFollowTargetCheck)
{
FindMovementPath find;
float pathLen;
_nextPathDistanceToFollowTargetCheck = G_Random(.33) + DEFAULT_PATH_TO_ENEMY_INTERVAL + level.time;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
// Set up our pathing heuristics
find.heuristic.self = this;
find.heuristic.setSize(size);
find.heuristic.entnum = entnum;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto path = find.FindPath(origin, followTarget.specifiedFollowTarget->origin);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!path)
{
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
pathLen = path->Length();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
delete path;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (pathLen > range)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return true;
}
} else
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetTalkWatchMode(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str mode = ev->GetString(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (mode == "ignore")
talkMode = TALK_IGNORE;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (mode == "headwatchonly")
talkMode = TALK_HEADWATCH;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (mode == "turnto")
talkMode = TALK_TURNTO;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (ev->NumArgs() > 1)
useConvAnims = ev->GetBoolean(2);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkAllowedToMeleeEnemy(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkAllowedToMeleeEnemy();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkAllowedToMeleeEnemy()
{
2015-04-28 20:02:03 +00:00
Entity* enemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!enemy)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (enemy->isSubclassOf(Player))
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!enemy->isSubclassOf(Actor))
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto actor = dynamic_cast<Actor*>(enemy);
if (actor->GetActorFlag(ACTOR_FLAG_MELEE_ALLOWED))
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkCurrentNodeHasThisCoverType(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str coverType = condition.getParm(1);
return checkCurrentNodeHasThisCoverType(coverType);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkCurrentNodeHasThisCoverType(const str& coverType)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
if (!currentHelperNode.node)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (coverType == "none")
{
if (currentHelperNode.node->GetCoverType() == COVER_TYPE_NONE)
return true;
}
if (coverType == "crate")
{
if (currentHelperNode.node->GetCoverType() == COVER_TYPE_CRATE)
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (coverType == "wall")
{
if (currentHelperNode.node->GetCoverType() == COVER_TYPE_WALL)
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::PrepareToFailMission(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
float time;
str reason;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
time = ev->GetFloat(1);
reason = "DefaultFailure";
if (ev->NumArgs() > 1)
reason = ev->GetString(2);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
Event* failureEvent;
failureEvent = new Event(EV_Actor_FailMission);
failureEvent->AddString(reason);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
PostEvent(failureEvent, time);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::FailMission(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
str reason = "DefaultFailure";
if (ev->NumArgs() > 0)
reason = ev->GetString(1);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
G_MissionFailed(reason);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::DebugEvent(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
//Here to catch when a state hits a debug event
assert(0);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkSteeringFailed(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagSteeringFailed;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkHavePathToEnemy(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkHavePathToEnemy();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkHavePathToEnemy()
{
2015-04-28 20:02:03 +00:00
if (!enemyManager->HasEnemy())
{
_nextCheckForEnemyPath = 0;
return false;
}
if (level.time >= _nextCheckForEnemyPath)
{
Entity* currentEnemy = enemyManager->GetCurrentEnemy();
if (!currentEnemy)
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
FindMovementPath find;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
_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;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto path = find.FindPath(origin, currentEnemy->origin);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!path)
{
_havePathToEnemy = false;
return false;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
delete path;
_havePathToEnemy = true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return _havePathToEnemy;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::UnreserveCurrentHelperNode(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
UnreserveCurrentHelperNode();
2012-12-30 16:37:54 +00:00
}
void Actor::UnreserveCurrentHelperNode()
{
2015-04-28 20:02:03 +00:00
if (currentHelperNode.node)
currentHelperNode.node->UnreserveNode();
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkBlockedByEnemy(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-29 20:34:44 +00:00
if (!(state_flags & StateFlagBlockedByEntity))
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
auto blockingEntity = movementSubsystem->getBlockingEntity();
if (!blockingEntity)
return false;
2012-12-30 16:37:54 +00:00
2015-04-29 20:34:44 +00:00
sensoryPerception->Stimuli(StimuliSight, blockingEntity);
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (enemyManager->Hates(blockingEntity))
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::ProjectileClose(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
Entity* owner;
owner = ev->GetEntity(1);
if (!owner)
return;
if (enemyManager->Hates(owner))
{
2015-04-29 20:34:44 +00:00
AddStateFlag(StateFlagEnemyProjectileClose);
2015-04-28 20:02:03 +00:00
}
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkEnemyProjectileClose(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkEnemyProjectileClose();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkEnemyProjectileClose()
{
2015-04-29 20:34:44 +00:00
return state_flags & StateFlagEnemyProjectileClose;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SaveOffLastHitBone(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
saved_bone_hit = last_bone_hit;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetPlayPainSoundInterval(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
_playPainSoundInterval = ev->GetFloat(1);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetContextInterval(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetContextInterval(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetContextInterval(float interval)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
_contextInterval = interval;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetMinPainTime(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetMinPainTime(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetMinPainTime(float time)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
min_pain_time = time;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetEnemyTargeted(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetEnemyTargeted(ev->GetBoolean(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetEnemyTargeted(bool targeted)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
Entity* enemy;
enemy = enemyManager->GetCurrentEnemy();
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (!enemy)
return;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
if (enemy->isSubclassOf(Player))
{
dynamic_cast<Player*>(enemy)->setTargeted(targeted);
}
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetActivationDelay(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetActivationDelay(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetActivationDelay(float delay)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
activationDelay = delay;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetActivationStart(Event*)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetActivationStart();
2012-12-30 16:37:54 +00:00
}
void Actor::SetActivationStart()
{
2015-04-28 20:02:03 +00:00
activationStart = level.time;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkActivationDelayTime(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkActivationDelayTime();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkActivationDelayTime()
{
2015-04-28 20:02:03 +00:00
//Yes, I could do this all 1337-like in one line, but debugging that sucks.
if (level.time >= activationStart + activationDelay)
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetCheckConeOfFireDistance(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
SetCheckConeOfFireDistance(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetCheckConeOfFireDistance(float distance)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
assert(strategos);
assert(distance > 0);
this->strategos->SetCheckInConeDistMax(distance);
2012-12-30 16:37:54 +00:00
}
//-----------------------------------------------------------------
// Note: This Needs to be its own class
//-----------------------------------------------------------------
2015-04-28 20:02:03 +00:00
void Actor::AddCustomThread(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
AddCustomThread(ev->GetString(1), ev->GetString(2));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::AddCustomThread(const str& threadType, const str& threadName)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
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 (auto i = 1; i <= threadList.NumObjects(); i++)
{
threadListEntry = threadList.ObjectAt(i);
if (!stricmp(threadListEntry->threadType.c_str(), threadType.c_str()))
{
threadListEntry->threadName = threadName;
return;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
//Since we didn't find a matching threadType, we'll just add what we need.
threadListEntry = new threadlist_t;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
threadListEntry->threadType = threadType;
threadListEntry->threadName = threadName;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
threadList.AddObject(threadListEntry);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
bool Actor::HaveCustomThread(const str& threadType)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
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 (auto i = 1; i <= threadList.NumObjects(); i++)
{
threadListEntry = threadList.ObjectAt(i);
if (!stricmp(threadListEntry->threadType.c_str(), threadType.c_str()))
{
return true;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::RunCustomThread(const str& threadType)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
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 (auto 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);
}
}
}
}
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
str Actor::GetCustomThread(const str& threadType)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
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 (auto i = 1; i <= threadList.NumObjects(); i++)
{
threadListEntry = threadList.ObjectAt(i);
if (!stricmp(threadListEntry->threadType.c_str(), threadType.c_str()))
{
return threadListEntry->threadName;
}
}
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return threadName;
2012-12-30 16:37:54 +00:00
}
void Actor::LevelAIOff()
{
2015-04-28 20:02:03 +00:00
//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();
}
2012-12-30 16:37:54 +00:00
}
void Actor::LevelAIOn()
{
2015-04-28 20:02:03 +00:00
if (GetActorFlag(ACTOR_FLAG_AI_ON))
return;
if (_levelAIOff)
{
_levelAIOff = false;
TurnAIOn();
}
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetHeadWatchMaxDistance(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
headWatcher->SetMaxDistance(ev->GetFloat(1));
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetBounceOffVelocity(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
bounce_off_velocity = ev->GetFloat(1);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkTalking(Conditional&)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
return checkTalking();
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkTalking()
{
2015-04-29 20:34:44 +00:00
if (mode == ActorModeTalk)
2015-04-28 20:02:03 +00:00
return true;
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
qboolean Actor::checkEnemiesNearby(Conditional& condition)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
float dist = atof(condition.getParm(1));
return checkEnemiesNearby(dist);
2012-12-30 16:37:54 +00:00
}
qboolean Actor::checkEnemiesNearby(float distance)
{
2015-04-28 20:02:03 +00:00
for (auto i = 1; i <= ActiveList.NumObjects(); i++)
{
auto ent = ActiveList.ObjectAt(i);
if (enemyManager->Hates(ent) && WithinDistance(ent, distance))
return true;
}
2012-12-30 16:37:54 +00:00
2015-04-28 20:02:03 +00:00
return false;
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetDeathKnockbackValues(Event* ev)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
deathKnockbackVerticalValue = ev->GetFloat(1);
deathKnockbackHorizontalValue = ev->GetFloat(2);
2012-12-30 16:37:54 +00:00
}
2015-04-28 20:02:03 +00:00
void Actor::SetIgnoreWatchTarget(bool ignore)
2012-12-30 16:37:54 +00:00
{
2015-04-28 20:02:03 +00:00
headWatcher->SetIgnoreWatchTarget(ignore);
2012-12-30 16:37:54 +00:00
}