//----------------------------------------------------------------------------- // // $Logfile:: /EF2/Code/DLLs/game/helper_node.cpp $ // $Revision:: 50 $ // $Author:: Singlis $ // $Date:: 9/26/03 2:36p $ // // Copyright (C) 2001 by Ritual Entertainment, Inc. // All rights reserved. // // This source may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // #include "_pch_cpp.h" //#include "g_local.h" #include "helper_node.h" #include "actor.h" extern Event EV_SpawnFlags; // //====================================== // Helper Node Event Declaration //====================================== // Event EV_HelperNodeController_SendEvent ( "sendevent", EV_DEFAULT, "ssSSSSSSS", "targetname eventName token1 token2 token3 token4 token5 token6 token7", "Sends the event to the specified node" ); Event EV_HelperNode_SetNodeAnim ( "setnodeanim", EV_SCRIPTONLY, "s", "animation", "Sets the animation that will be played by the actor on this node" ); Event EV_HelperNode_SetNodeEntryThread ( "setnodeentrythread", EV_SCRIPTONLY, "s", "thread", "Sets the thread that will be called by the actor when it reaches this node" ); Event EV_HelperNode_SetNodeExitThread ( "setnodeexitthread", EV_SCRIPTONLY, "s", "thread", "Sets the thread that will be called by the actor when it leaves this node" ); Event EV_HelperNode_SetAnimCount ( "animcount", EV_SCRIPTONLY, "i", "num_times", "Sets the max number of times that an animation can be set on this node" ); Event EV_HelperNode_SetAnimTarget ( "animtarget", EV_SCRIPTONLY, "s", "targetname", "Allows you to specify a target name to apply the animation to" ); Event EV_HelperNode_SetAnimActive ( "animactive", EV_SCRIPTONLY, "b", "active", "Specifies if the animation component is active" ); Event EV_HelperNode_SetCoverType ( "setcovertype", EV_SCRIPTONLY, "s", "cover_type", "Specifies the covertype of the node" ); Event EV_HelperNode_SetCoverDirection ( "setcoverdir", EV_SCRIPTONLY, "s", "cover_dir", "Specifies the direction from the cover that the AI can attack" ); Event EV_HelperNode_SetWait ( "wait", EV_SCRIPTONLY, "f", "time", "Makes a patrolling AI pause at this node for the specified time" ); Event EV_HelperNode_SetWaitRandom ( "waitrandom", EV_SCRIPTONLY, "f", "max_time", "Makes a patrolling AI pause randomly for a min time of 1 to a max time of specified" ); Event EV_HelperNode_WaitForAnim ( "waitforanim", EV_SCRIPTONLY, "b", "wait_for_anim", "Makes the AI wait until the anim is finished before finishing a behavior" ); Event EV_HelperNode_SetKillTarget ( "attacktarget", EV_SCRIPTONLY, "s", "targetname", "Specifies a target for a sniping AI to attack" ); Event EV_HelperNode_SetCustomType ( "setcustomtype", EV_SCRIPTONLY, "s", "type", "Specifies a custom helper node type" ); Event EV_HelperNode_SetMaxKills ( "maxkills", EV_SCRIPTONLY, "i", "max_kills", "Specifies the maximum number of kills that can be done from this node" ); Event EV_HelperNode_SetMinHealth ( "minhealth", EV_SCRIPTONLY, "f", "min_health", "Specifies the minimum health an actor is allowed to get to before leaving this node" ); Event EV_HelperNode_SetMinEnemyRange ( "minenemyrange", EV_SCRIPTONLY, "f", "min_range", "Specifies the minimum range an enemy is allowed to get before actor leaves this node" ); Event EV_HelperNode_SetActivationRange ( "activationrange", EV_SCRIPTONLY, "f", "activation_range", "Specifies the range an actor needs to be to consider using the node" ); Event EV_HelperNode_SetPriority ( "priority", EV_SCRIPTONLY, "f", "priority", "Specfies the priority for this node" ); Event EV_HelperNode_SetNodeID ( "nodeID", EV_SCRIPTONLY, "i", "node_id", "Assigns an ID to this node" ); Event EV_HelperNode_SetCriticalChange ( "criticalchange", EV_SCRIPTONLY, NULL, NULL, "Alerts the node to a critical change" ); Event EV_HelperNode_SetDescriptor ( "descriptor", EV_SCRIPTONLY, "s", "descriptorValue", "sets an additional descriptor for a node" ); Event EV_HelperNode_SendCommand ( "sendnodecommand", EV_SCRIPTONLY, "s", "commandToSend", "sends an event to the user of the node" ); Event EV_HelperNode_AddAnimation ( "addanim", EV_SCRIPTONLY, "ss", "anim waittype", "Adds an animation to the custom anim list" ); //====================================== // Helper Node Container //====================================== Container HelperNodes; char CoverTypeStrings[ COVER_TYPE_TOTAL_NUMBER ][ 32 ] = { "none", "crate", "wall" }; char CoverDirStrings[ COVER_DIRECTION_TOTAL_NUMBER ][ 32 ] = { "none", "left", "right", "all" }; char DescriptorTypeStrings[ DESCRIPTOR_TOTAL_NUMBER ][ 32 ] = { "none", "corridor" }; //====================================== // Helper Node Initialization //====================================== CLASS_DECLARATION( Entity, HelperNodeController, "helpernodecontroller" ) { { &EV_HelperNodeController_SendEvent, &HelperNodeController::SendEventToNode }, { NULL, NULL } }; HelperNodeController::HelperNodeController() { targetname = "nodecontroller"; } HelperNodeController::~HelperNodeController() { } void HelperNodeController::SendEventToNode( Event *ev ) { str nodeTargetName; str eventName; Event *nodeEvent; HelperNode* node; int i; //First grab our node target and event nodeTargetName = ev->GetString( 1 ); eventName = ev->GetString( 2 ); node = NULL; //Now lets find our target for ( i = HelperNodes.NumObjects() ; i > 0 ; i-- ) { node = HelperNodes.ObjectAt( i ); if ( node->targetname == nodeTargetName ) break; } if ( !node ) return; // Create our event nodeEvent = new Event( eventName.c_str() ); if ( !nodeEvent ) return; //Load our event with parameters for ( i = 3 ; i <= ev->NumArgs() ; i++ ) //start at 3, because we burnt through the first 2 already { nodeEvent->AddToken( ev->GetToken( i ) ); } node->ProcessEvent( nodeEvent ); } // // Class Declaration and Event Registration // CLASS_DECLARATION( Listener, HelperNode, "info_helpernode" ) { // Core Events { &EV_SetTargetName, &HelperNode::SetTargetName }, { &EV_SetTarget, &HelperNode::SetTarget }, { &EV_SetAngle, &HelperNode::SetAngle }, { &EV_SetOrigin, &HelperNode::SetOrigin }, { &EV_SpawnFlags, &HelperNode::SetFlags }, { &EV_HelperNode_SetNodeAnim, &HelperNode::SetAnim }, { &EV_HelperNode_SetNodeEntryThread, &HelperNode::SetEntryThread }, { &EV_HelperNode_SetNodeExitThread, &HelperNode::SetExitThread }, { &EV_HelperNode_SetAnimCount, &HelperNode::SetAnimCount }, { &EV_HelperNode_SetAnimTarget, &HelperNode::SetAnimTarget }, { &EV_HelperNode_SetAnimActive, &HelperNode::SetAnimActive }, { &EV_HelperNode_SetCoverType, &HelperNode::SetCoverType }, { &EV_HelperNode_SetCoverDirection, &HelperNode::SetCoverDir }, { &EV_HelperNode_SetWait, &HelperNode::SetWait }, { &EV_HelperNode_SetWaitRandom, &HelperNode::SetWaitRandom }, { &EV_HelperNode_WaitForAnim, &HelperNode::SetWaitForAnim }, { &EV_HelperNode_SetKillTarget, &HelperNode::SetKillTarget }, { &EV_HelperNode_SetCustomType, &HelperNode::SetCustomType }, { &EV_HelperNode_SetMaxKills, &HelperNode::SetMaxKills }, { &EV_HelperNode_SetMinHealth, &HelperNode::SetMinHealth }, { &EV_HelperNode_SetMinEnemyRange, &HelperNode::SetMinEnemyRange }, { &EV_HelperNode_SetActivationRange, &HelperNode::SetActivationRange }, { &EV_HelperNode_SetPriority, &HelperNode::SetPriority }, { &EV_HelperNode_SetNodeID, &HelperNode::SetID }, { &EV_HelperNode_SetCriticalChange, &HelperNode::SetCriticalChange }, { &EV_HelperNode_SetDescriptor, &HelperNode::SetDescriptor }, { &EV_HelperNode_SendCommand, &HelperNode::SendCommand }, { &EV_HelperNode_AddAnimation, &HelperNode::AddAnimation }, { NULL, NULL } }; // // Name: HelperNode() // Class: HelperNode // // Description: Constructor // // Parameters: None // // Returns: None // HelperNode::HelperNode() { if ( !LoadingSavegame ) { AddHelperNodeToList( this ); _init(); } } // // Name: ~HelperNode() // Class: HelperNode // // Description: Destructor // // Parameters: None // // Returns: None // HelperNode::~HelperNode() { customAnimListEntry_t* checkEntry; for ( int i = _customAnimList.NumObjects() ; i > 0 ; i-- ) { checkEntry = _customAnimList.ObjectAt( i ); delete checkEntry; checkEntry = NULL; _customAnimList.RemoveObjectAt( i ); } } // // Name: _init() // Class: HelperNode // // Description: Initializes default values // // Parameters: None // // Returns: None // void HelperNode::_init() { _entryThread = ""; _exitThread = ""; _anim = ""; _animtarget = ""; _killtarget = ""; // Default Value for _id means the node is open to everyone _id = -1; // Default Value for _animcount means no maximum _animcount = -1; // Default Value for _maxkills means no maximum _maxkills = -1; _animactive = true; _coveractive = true; _waitrandom = false; _waitforanim = false; _criticalchange = false; _waittime = 0.0f; _minhealth = 0.0f; _minenemyrange = 0.0f; _activationRange = 0.0f; _priority = 0.0f; _lastUseTime = 0.0f; _covertype = COVER_TYPE_NONE; _coverdir = COVER_DIRECTION_NONE; _descriptor = DESCRIPTOR_NONE; _reserved = false; _nodeflags = 0; _customAnimListIndex = 1; _usingCustomAnimList = false; _user = NULL; } //====================================== // Core Functionality //====================================== void HelperNode::SetTarget( Event *ev ) { str name; name = ev->GetString( 1 ); SetTarget(name); } void HelperNode::SetTargetName( Event *ev ) { str name; name = ev->GetString( 1 ); SetTargetName(name); } void HelperNode::SetAngle( Event *ev ) { Vector movedir; movedir = G_GetMovedir( ev->GetFloat( 1 ) ); SetAngle(movedir.toAngles()); } void HelperNode::SetOrigin( Event *ev ) { Vector pos; pos = ev->GetVector( 1 ); SetOrigin(pos); } void HelperNode::SetTarget( const str &name ) { target = name; } void HelperNode::SetTargetName( const str &name ) { targetname = name; } void HelperNode::SetAngle( const Vector &ang ) { angles = ang; } void HelperNode::SetOrigin( const Vector &pos ) { origin = pos; } //====================================== // Helper Node Event Interface //====================================== // // Name: SetThread() // Class: HelperNode // // Description: Calls SetEntryThread() // // Parameters: Event *ev - Event containing the string // // Returns: None // void HelperNode::SetEntryThread( Event *ev ) { SetEntryThread(ev->GetString( 1 ) ); } // // Name: SetExitThread() // Class: HelperNode // // Description: Calls SetExitThread() // // Parameters: Event *ev -- Event containing the string // // Returns: None // void HelperNode::SetExitThread( Event *ev ) { SetExitThread(ev->GetString( 1 ) ); } // // Name: SetAnim() // Class: HelperNode // // Description: Calls SetAnim() // // Parameters: Event *ev - Event containing the string // // Returns: None // void HelperNode::SetAnim( Event *ev ) { SetAnim( ev->GetString( 1 ) ); } // // Name: SetAnimTarget() // Class: HelperNode // // Description: Calls SetAnimTarget() // // Parameters: Event *ev - Event containing the string // // Returns: None // void HelperNode::SetAnimTarget( Event *ev ) { SetAnimTarget( ev->GetString( 1 ) ); } // // Name: SetKillTarget() // Class: HelperNode // // Description: Calls SetKillTarget() // // Parameters: Event *ev - Event contaning the string // // Returns: None // void HelperNode::SetKillTarget( Event *ev ) { SetKillTarget( ev->GetString( 1 ) ); } // // Name: SetCustomType() // Class: HelperNode // // Description: Calls SetCustomType() // // Parameters: Event *ev - Event contaning the string // // Returns: None // void HelperNode::SetCustomType( Event *ev ) { SetCustomType( ev->GetString( 1 ) ); } // // Name: SetID() // Class: HelperNode // // Description: Calls SetID() // // Parameters: Event *ev - Event containing the integer // // Returns: None // void HelperNode::SetID( Event *ev ) { SetID(ev->GetInteger( 1 ) ); } // // Name: SetAnimCount() // Class: HelperNode // // Description: Calls SetAnimCount() // // Parameters: Event *ev - Event containing the integer // // Returns: None // void HelperNode::SetAnimCount( Event *ev ) { SetAnimCount( ev->GetInteger( 1 ) ); } // // Name: SetMaxKills() // Class: HelperNode // // Description: Calls SetMaxKills() // // Parameters: Event *ev - Event containing the integer // // Returns: None // void HelperNode::SetMaxKills( Event *ev ) { SetMaxKills( ev->GetInteger( 1 ) ); } // // Name: SetWait() // Class: HelperNode // // Description: Calls SetWait() // // Parameters: Event *ev - Event containing the float // // Returns: None // void HelperNode::SetWait( Event *ev ) { SetWait( ev->GetFloat( 1 ) ); } // // Name: SetWaitRandom() // Class: HelperNode // // Description: Calls SetWaitRandom() // // Parameters: Event *ev - Event containing the float // // Returns: None // void HelperNode::SetWaitRandom( Event *ev ) { SetWaitRandom( ev->GetFloat( 1 ) ); } // // Name: SetWaitForAnim() // Class: HelperNode // // Description: Calls SetWaitForAnim() // // Parameters: Event *ev // // Returns: None // void HelperNode::SetWaitForAnim( Event *ev ) { SetWaitForAnim( ev->GetBoolean( 1 ) ); } // // Name: SetCriticalChange() // Class: HelperNode // // Description: Calls SetCriticalChange() // // Parameters: Event *ev // // Returns: None // void HelperNode::SetCriticalChange( Event *ev ) { SetCriticalChange( true ); } // // Name: SetDescriptor() // Class: HelperNode // // Description: Gets the enum for the string and calls // SetDescriptor() // // Parameters: Event *ev -- The event containing the string // // Returns: None // void HelperNode::SetDescriptor( Event *ev ) { str descriptor = ev->GetString( 1 ); SetDescriptor( _DescriptorForString(descriptor) ); } void HelperNode::SetPriority( Event *ev ) { float priority = ev->GetFloat( 1 ); SetPriority( priority ); } // // Name: SetMinHealth() // Class: HelperNode // // Description: Calls SetMinHealth() // // Parameters: Event *ev - Event containing the float // // Returns: None // void HelperNode::SetMinHealth( Event *ev ) { SetMinHealth( ev->GetFloat( 1 ) ); } // // Name: SetMinEnemyRange() // Class: HelperNode // // Description: Calls SetMinEnemyRange() // // Parameters: Event *ev - Event containing the float // // Returns: None // void HelperNode::SetMinEnemyRange( Event *ev ) { SetMinEnemyRange( ev->GetFloat( 1 ) ); } void HelperNode::SetActivationRange( Event *ev ) { SetActivationRange( ev->GetFloat( 1 ) ); } // // Name: SetFlags() // Class: HelperNode // // Description: Calls SetFlags() // // Parameters: Event *ev - Event containing the float // // Returns: None // void HelperNode::SetFlags( Event *ev ) { SetFlags(ev->GetInteger( 1 ) ); } // // Name: SetAnimActive() // Class: HelperNode // // Description: Calls SetAnimActive() // // Parameters: Event *ev - Event containing the boolean // // Returns: None // void HelperNode::SetAnimActive( Event *ev ) { SetAnimActive(ev->GetBoolean( 1 ) ); } // // Name: SetCoverActive() // Class: HelperNode // // Description: Calls SetCoverActive() // // Parameters: Event *ev - Event containing the boolean // // Returns: None // void HelperNode::SetCoverActive( Event *ev ) { SetCoverActive(ev->GetBoolean( 1 ) ); } // // Name: SetCoverType() // Class: HelperNode // // Description: Calls SetCoverType() // // Parameters: Event *ev - Event containing the string // // Returns: None // void HelperNode::SetCoverType( Event *ev ) { CoverType_t coverType; coverType = _CoverTypeForString( ev->GetString( 1 ) ); SetCoverType(coverType); } // // Name: SetCoverDir() // Class: HelperNode // // Description: Calls SetCoverDir() // // Parameters: Event *ev - Event containing the string // // Returns: None // void HelperNode::SetCoverDir( Event *ev ) { CoverDirection_t coverDir; coverDir = _CoverDirectionForString( ev->GetString( 1 ) ); SetCoverDir(coverDir); } //====================================== // Helper Node Mutators //====================================== // // Name: SetEntryThread() // Class: HelperNode // // Description: Sets the _entryThread variable // // Parameters: const str& thread - the thread to set // // Returns: None // void HelperNode::SetEntryThread( const str& thread ) { _entryThread = thread; } // // Name: SetExitThread() // Class: HelperNode // // Description: Sets the _exitThread variable // // Parameters: const str& thread - the thread to set // // Returns: None // void HelperNode::SetExitThread( const str& thread ) { _exitThread = thread; } // // Name: SetAnim() // Class: HelperNode // // Description: Sets the _anim variable // // Parameters: const str& anim // // Returns: None // void HelperNode::SetAnim( const str& anim ) { _anim = anim; } // // Name: SetAnimTarget() // Class: HelperNode // // Description: Sets the _animtarget variable // // Parameters: const str& animtarget // // Returns: None // void HelperNode::SetAnimTarget( const str& animtarget ) { _animtarget = animtarget; } // // Name: SetKillTarget() // Class: HelperNode // // Description: Sets the _killtarget variable // // Parameters: const str& killtarget // // Returns: None // void HelperNode::SetKillTarget( const str& killtarget ) { _killtarget = killtarget; } // // Name: SetCustomType() // Class: HelperNode // // Description: Sets the _customType variable // // Parameters: const str& customtype // // Returns: None // void HelperNode::SetCustomType( const str& customtype ) { _customType = customtype; } // // Name: SetID() // Class: HelperNode // // Description: Sets the _id variable // // Parameters: int ID - The id to set // // Returns: None // void HelperNode::SetID( int ID ) { _id = ID; } // // Name: SetAnimCount() // Class: HelperNode // // Description: Sets the _animcount variable // // Parameters: int animcount // // Returns: None // void HelperNode::SetAnimCount( int animcount ) { _animcount = animcount; } // // Name: SetMaxKills() // Class: HelperNode // // Description: Sets the _maxkills variable // // Parameters: int maxkills // // Returns: None // void HelperNode::SetMaxKills( int maxkills ) { _maxkills = maxkills; } // // Name: SetWait() // Class: HelperNode // // Description: Sets the _wait variable // // Parameters: float wait // // Returns: None // void HelperNode::SetWait( float wait ) { _waittime = wait; } // // Name: SetWaitRandom() // Class: HelperNode // // Description: Sets the _wait variable, and sets _waitRandom to true; // // Parameters: float wait // // Returns: None // void HelperNode::SetWaitRandom( float wait ) { _waittime = wait; _waitrandom = true; } // // Name: SetWaitForAnim() // Class: HelperNode // // Description: Sets the _waitforanim variable // // Parameters: qboolean wait // // Returns: None // void HelperNode::SetWaitForAnim(qboolean wait) { _waitforanim = wait; } // // Name: SetMinHealth() // Class: HelperNode // // Description: Sets _minhealth variable // // Parameters: float health - The health to set // // Returns: None // void HelperNode::SetMinHealth( float health ) { _minhealth = health; } // // Name: SetMinEnemyRange() // Class: HelperNode // // Description: Sets _minenemyrange variable // // Parameters: float range - The range to set // // Returns: None // void HelperNode::SetMinEnemyRange( float range ) { _minenemyrange = range; } void HelperNode::SetActivationRange( float range ) { _activationRange = range; } // // Name: SetAnimActive() // Class: HelperNode // // Description: Sets the _animactive variable // // Parameters: qboolean animactive // // Returns: None // void HelperNode::SetAnimActive( qboolean animactive ) { _animactive = animactive; } // // Name: SetCoverActive() // Class: HelperNode // // Description: Sets the _coveractive variable // // Parameters: qboolean coveractive // // Returns: None // void HelperNode::SetCoverActive( qboolean coveractive ) { _coveractive = coveractive; } // // Name: SetCoverType() // Class: HelperNode // // Description: Sets the _covertype variable // // Parameters: CoverType_t covertype // // Returns: None // void HelperNode::SetCoverType( CoverType_t covertype ) { _covertype = covertype; } // // Name: SetCoverDir() // Class: HelperNode // // Description: Sets the _coverdir variable // // Parameters: CoverDirection_t coverdir // // Returns: None // void HelperNode::SetCoverDir( CoverDirection_t coverdir ) { _coverdir = coverdir; } // // Name: SetCriticalChange() // Class: HelperNode // // Description: Sets the _criticalchange variable // // Parameters: qboolean change // // Returns: None // void HelperNode::SetCriticalChange( qboolean change ) { _criticalchange = change; } // // Name: SetDescriptor() // Class: HelperNode // // Description: Sets the _descriptor variable // // Parameters: NodeDescriptorType_t descriptor // // Returns: None // void HelperNode::SetDescriptor( NodeDescriptorType_t descriptor ) { _descriptor = descriptor; } void HelperNode::SetPriority( float priority ) { _priority = priority; } // // Name: SetFlags() // Class: HelperNode // // Description: Sets the NodeFlags // // Parameters: unsigned int flags - the integer mask of the flags // // Returns: None // void HelperNode::SetFlags( unsigned int flags ) { _nodeflags = flags; } //====================================== // Helper Node Accessors //====================================== // // Name: GetEntryThread() // Class: HelperNode // // Description: Returns the _thread variable // // Parameters: None // // Returns: _entryThread // const str& HelperNode::GetEntryThread() { return _entryThread; } // // Name: GetExitThread() // Class: HelperNode // // Description: Returns the _exitThread // // Parameters: None // // Returns: _exitThread // const str& HelperNode::GetExitThread() { return _exitThread; } // // Name: GetAnim() // Class: HelperNode // // Description: Returns the _anim variable // // Parameters: None // // Returns: _anim // const str& HelperNode::GetAnim() { return _anim; } // // Name: GetAnimTarget() // Class: HelperNode // // Description: Returns _animtarget variable // // Parameters: None // // Returns: _animtarget // const str& HelperNode::GetAnimTarget() { return _animtarget; } // // Name: GetKillTarget() // Class: HelperNode // // Description: Returns _killtarget variable // // Parameters: None // // Returns: _killtarget // const str& HelperNode::GetKillTarget() { return _killtarget; } // // Name: GetCustomType() // Class: HelperNode // // Description: Returns _customType variable // // Parameters: None // // Returns: _customType // const str& HelperNode::GetCustomType() { return _customType; } // // Name: GetID() // Class: HelperNode // // Description: Returns _id variable // // Parameters: None // // Returns: _id // int HelperNode::GetID() { return _id; } // // Name: GetAnimCount() // Class: HelperNode // // Description: Returns the _animcount variable // // Parameters: None // // Returns: _animcount // int HelperNode::GetAnimCount() { return _animcount; } // // Name: GetMaxKills() // Class: HelperNode // // Description: Returns the _maxkills variable // // Parameters: None // // Returns: _maxkills // int HelperNode::GetMaxKills() { return _maxkills; } // // Name: GetWaitTime() // Class: HelperNode // // Description: Returns _waittime // // Parameters: None // // Returns: _waittime // float HelperNode::GetWaitTime() { return _waittime; } // // Name: GetMinHealth() // Class: HelperNode // // Description: Returns _minhealth variable // // Parameters: None // // Returns: _minhealth // float HelperNode::GetMinHealth() { return _minhealth; } // // Name: GetMinEnemyRange() // Class: HelperNode // // Description: Returns _minenemyrange; // // Parameters: None // // Returns: _minenemyrange // float HelperNode::GetMinEnemyRange() { return _minenemyrange; } float HelperNode::GetActivationRange() { return _activationRange; } // // Name: GetCoverType() // Class: HelperNode // // Description: Returns _covertype // // Parameters: None // // Returns: _covertype // CoverType_t HelperNode::GetCoverType() { return _covertype; } // // Name: GetCoverDirection() // Class: HelperNode // // Description: Returns _coverdir // // Parameters: None // // Returns: _coverdir // CoverDirection_t HelperNode::GetCoverDirection() { return _coverdir; } // // Name: GetDescriptor // Class: HelperNode // // Description: Returns _descriptor // // Parameters: None // // Returns: NodeDescriptorType_t // NodeDescriptorType_t HelperNode::GetDescriptor() { return _descriptor; } float HelperNode::GetPriority() { return _priority; } //====================================== // Helper Node Queries //====================================== // // Name: isOfType() // Class: HelperNode // // Description: Checks if the nodeflags match the passed in mask // // Parameters: unsigned int mask // // Returns: true or false; // qboolean HelperNode::isOfType(unsigned int mask) { return _nodeflags & ( mask ); } // // Name: isAnimActive() // Class: HelperNode // // Description: Returns _animactive // // Parameters: None // // Returns: true or false // qboolean HelperNode::isAnimActive() { return _animactive; } // // Name: isCoverActive() // Class: HelperNode // // Description: Returns _coveractive // // Parameters: None // // Returns: true or false // qboolean HelperNode::isCoverActive() { return _coveractive; } // // Name: isWaitRandom() // Class: HelperNode // // Description: Returns _waitrandom // // Parameters: None // // Returns: true or false // qboolean HelperNode::isWaitRandom() { return _waitrandom; } // // Name: isWaitForAnim() // Class: HelperNode // // Description: Returns _waitforanim // // Parameters: None // // Returns: true or false // qboolean HelperNode::isWaitForAnim() { return _waitforanim; } // // Name: isChanged() // Class: HelperNode // // Description: Returns _criticalchange // // Parameters: None // // Returns: true or false // qboolean HelperNode::isChanged() { return _criticalchange; } qboolean HelperNode::isReserved() { return _reserved; } //====================================== // Helper Node Utility //====================================== // // Name: RunEntryThread() // Class: HelperNode // // Description: Runs the entry thread // // Parameters: None // // Returns: None // void HelperNode::RunEntryThread() { if ( _entryThread.length() <= 0 ) return; CThread *thread; thread = Director.CreateThread( _entryThread ); if ( thread ) thread->DelayedStart( 0.0f ); } // // Name: RunExitThread() // Class: HelperNode // // Description: Runs the exit thread // // Parameters: None // // Returns: None // void HelperNode::RunExitThread() { if ( _exitThread.length() <= 0 ) return; CThread *thread; thread = Director.CreateThread( _exitThread ); if ( thread ) thread->DelayedStart( 0.0f ); } //====================================== // Helper Node Convience Functions //====================================== // // Name: _CoverTypeForString() // Class: HelperNode // // Description: Returns the enumerated covertype based on the string // // Parameters: const str &covertype // // Returns: CoverType_t coverType // CoverType_t HelperNode::_CoverTypeForString( const str &covertype ) { CoverType_t coverType; for ( int i = 0 ; i < COVER_TYPE_TOTAL_NUMBER ; i++ ) { if ( !Q_stricmp(covertype.c_str() , CoverTypeStrings[i] ) ) { coverType = (CoverType_t)i; return coverType; } } gi.WDPrintf( "Unknown CoverType - %s\n", covertype.c_str() ); return COVER_TYPE_NONE; } // // Name: _CoverDirectionForString() // Class: HelperNode // // Description: Returns the enumerated coverdir based on the string // // Parameters: const str &coverdir // // Returns: CoverType_t coverDir // CoverDirection_t HelperNode::_CoverDirectionForString( const str &coverdir ) { CoverDirection_t coverDir; for ( int i = 0 ; i < COVER_DIRECTION_TOTAL_NUMBER ; i++ ) { if ( !Q_stricmp(coverdir.c_str() , CoverDirStrings[i] ) ) { coverDir = (CoverDirection_t)i; return coverDir; } } gi.WDPrintf( "Unknown CoverDirection - %s\n", coverdir.c_str() ); return COVER_DIRECTION_NONE; } // // Name: _CoverDirectionForString() // Class: HelperNode // // Description: Returns the enumerated coverdir based on the string // // Parameters: const str &coverdir // // Returns: CoverType_t coverDir // NodeDescriptorType_t HelperNode::_DescriptorForString( const str &descriptor ) { NodeDescriptorType_t descriptorType; for ( int i = 0 ; i < DESCRIPTOR_TOTAL_NUMBER ; i++ ) { if ( !Q_stricmp(descriptor.c_str() , DescriptorTypeStrings[i] ) ) { descriptorType = (NodeDescriptorType_t)i; return descriptorType; } } gi.WDPrintf( "Unknown Descriptor - %s\n", descriptor.c_str() ); return DESCRIPTOR_NONE; } // // Name: FindClosestHelperNode() // Class: None // // Description: Return nearest valid helpernode based on the criteria passed in // // Parameters: Actor &self -- Actor looking for the node // int mask -- Mask of the node // // Returns: HelperNode* bestNode // HelperNode* HelperNode::FindClosestHelperNode( Actor &self , int mask , float maxDist , float minDistanceFromPlayer, bool unreserveCurrentNode ) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; float activationRange; FindMovementPath find; Path *path; int nodeID; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node && node->isOfType(mask)) { activationRange = node->GetActivationRange(); if ( activationRange > 0 ) { if ( !self.WithinDistance( node->origin, activationRange ) ) continue; } else { if ( !self.WithinDistance( node->origin, maxDist ) ) continue; } if ( node == self.ignoreHelperNode.node ) continue; if ( node->isReserved() ) continue; Player* player; player = GetPlayer(0); if ( !player ) return false; if ( player->WithinDistance( node->origin , minDistanceFromPlayer ) ) continue; path = find.FindPath( self.origin, node->origin ); if ( path ) pathLen = path->Length(); delete path; path = NULL; if ( pathLen < bestDist ) { bestDist = pathLen; bestNode = node; } } } if ( self.currentHelperNode.node && unreserveCurrentNode ) self.currentHelperNode.node->UnreserveNode(); self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = mask; if ( bestNode ) bestNode->ReserveNode(); return bestNode; } // // Name: FindClosestHelperNode() // Class: None // // Description: Return nearest valid helpernode based on the criteria passed in // // Parameters: Actor &self -- Actor looking for the node // int mask -- Mask of the node // // Returns: HelperNode* bestNode // HelperNode* HelperNode::FindClosestHelperNode( Actor &self , int mask , NodeDescriptorType_t descriptor, float maxDist) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; float activationRange; FindMovementPath find; Path *path; int nodeID; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node && node->isOfType(mask) && node->GetDescriptor() == descriptor ) { activationRange = node->GetActivationRange(); if ( node->isReserved() ) continue; if ( node == self.ignoreHelperNode.node ) continue; if ( activationRange > 0 ) { if ( !self.WithinDistance( node->origin, activationRange ) ) continue; } else { if ( !self.WithinDistance( node->origin, maxDist ) ) continue; } path = find.FindPath( self.origin, node->origin ); if ( path ) pathLen = path->Length(); delete path; path = NULL; if ( pathLen < bestDist ) { bestDist = pathLen; bestNode = node; } } } if ( self.currentHelperNode.node ) self.currentHelperNode.node->UnreserveNode(); self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = mask; if ( bestNode ) bestNode->ReserveNode(); return bestNode; } HelperNode* HelperNode::FindClosestHelperNodeAtDistanceFrom( Actor &self , Entity *ent , int mask , NodeDescriptorType_t descriptor , float maxDistFromSelf , float minDistFromEnt ) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; float activationRange; FindMovementPath find; Path *path; int nodeID; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node && node->isOfType(mask) && node->GetDescriptor() == descriptor ) { activationRange = node->GetActivationRange(); if ( node->isReserved() ) continue; if ( activationRange > 0 ) { if ( !self.WithinDistance( node->origin, activationRange ) ) continue; } else { if ( !self.WithinDistance( node->origin, maxDistFromSelf ) ) continue; } if ( ent->WithinDistance( node->origin , minDistFromEnt ) ) continue; path = find.FindPath( self.origin, node->origin ); if ( path ) pathLen = path->Length(); delete path; path = NULL; if ( pathLen < bestDist ) { bestDist = pathLen; bestNode = node; } } } self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = mask; return bestNode; } HelperNode* HelperNode::FindHelperNodeClosestToWithoutPathing( Actor &self , Entity *ent , int mask , float maxDist ) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; float activationRange; int nodeID; if ( !ent ) return NULL; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node && node->isOfType(mask)) { activationRange = node->GetActivationRange(); if ( activationRange > 0 ) { if ( !ent->WithinDistance( node->origin, activationRange ) ) continue; } else { if ( !ent->WithinDistance( node->origin, maxDist ) ) continue; } if ( node == self.ignoreHelperNode.node ) continue; if ( node->isReserved() ) continue; Vector pathToNode = ent->origin - node->origin; pathLen = pathToNode.length(); if ( pathLen < bestDist ) { bestDist = pathLen; bestNode = node; } } } if ( self.currentHelperNode.node ) self.currentHelperNode.node->UnreserveNode(); self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = mask; if ( bestNode ) bestNode->ReserveNode(); return bestNode; } HelperNode* HelperNode::FindHelperNodeClosestTo( Actor &self, Entity *ent , int mask , float maxDist ) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; float activationRange; FindMovementPath find; Path *path; int nodeID; if ( !ent ) return NULL; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node && node->isOfType(mask)) { activationRange = node->GetActivationRange(); if ( activationRange > 0 ) { if ( !ent->WithinDistance( node->origin, activationRange ) ) continue; } else { if ( !ent->WithinDistance( node->origin, maxDist ) ) continue; } if ( node == self.ignoreHelperNode.node ) continue; if ( node->isReserved() ) continue; path = find.FindPath( ent->origin, node->origin ); if ( path ) pathLen = path->Length(); delete path; path = NULL; if ( pathLen < bestDist ) { bestDist = pathLen; bestNode = node; } } } if ( self.currentHelperNode.node ) self.currentHelperNode.node->UnreserveNode(); self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = mask; if ( bestNode ) bestNode->ReserveNode(); return bestNode; } // // Name: FindClosestHelperNodeWithoutPathing() // Class: None // // Description: Return nearest valid helpernode based on the criteria passed in // // Parameters: Actor &self -- Actor looking for the node // int mask -- Mask of the node // // Returns: HelperNode* bestNode // HelperNode* HelperNode::FindClosestHelperNodeWithoutPathing( Actor &self, float maxDist) { // Set up our pathing heuristics FindMovementPath find; find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances HelperNode *bestNode = NULL; float bestDistance = 999999999.8f; for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { HelperNode *node = NULL; node = HelperNodes.ObjectAt( i ); int nodeID = node->GetID(); if ( ( nodeID != -1 ) && ( nodeID != self.currentHelperNode.nodeID) ) continue; float activationRange = node->GetActivationRange(); if ( activationRange > 0 ) { if ( !self.WithinDistance( node->origin, activationRange ) ) continue; } else { if ( !self.WithinDistance( node->origin, maxDist ) ) continue; } float currentDistance = Vector::Distance( self.origin, node->origin ); if ( currentDistance < bestDistance ) { bestDistance = currentDistance; bestNode = node; } } self.currentHelperNode.node = bestNode; return bestNode; } HelperNode* HelperNode::FindClosestHelperNodeThatCannotSeeEntity( Actor &self , int mask , unsigned int clipMask , float maxDist , float minDist , Entity *ent , float minDistFromPlayer ) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; float activationRange; FindMovementPath find; Path *path; int nodeID; trace_t trace; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node && node->isOfType(mask)) { activationRange = node->GetActivationRange(); if ( activationRange > 0 ) { if ( !self.WithinDistance( node->origin, activationRange ) ) continue; } else { if ( !self.WithinDistance( node->origin, maxDist ) ) continue; } if ( node->isReserved() ) continue; //Check if node is within the minimum distance of the player Player* player; player = GetPlayer(0); if ( !player ) return false; if ( player->WithinDistance( node->origin , minDistFromPlayer ) ) continue; path = find.FindPath( self.origin, node->origin ); if ( !path ) continue; pathLen = path->Length(); delete path; path = NULL; if ( pathLen < bestDist && !ent->WithinDistance( node->origin , minDist ) ) { Vector endPos; endPos = ent->centroid; if ( ent->isSubclassOf(Actor) ) { Actor *theActor; theActor = (Actor*)ent; endPos = theActor->EyePosition(); } trace = G_Trace( node->origin , vec_zero , vec_zero , endPos , NULL , MASK_OPAQUE, false, "FindClosestHelperNode"); if ( trace.fraction > .9 ) continue; bestDist = pathLen; bestNode = node; } } } if ( bestNode && self.currentHelperNode.node ) self.currentHelperNode.node->UnreserveNode(); if ( bestNode ) { self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = mask; } if ( bestNode ) bestNode->ReserveNode(); return bestNode; } HelperNode* HelperNode::FindClosestHelperNode( Actor &self , const str& customType , float maxDist) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; FindMovementPath find; Path *path; int nodeID; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node == self.ignoreHelperNode.node ) continue; if ( node->isReserved() ) continue; if ( node && node->isOfType(NODETYPE_CUSTOM)) { str type; type = node->GetCustomType(); if ( !Q_stricmp(type.c_str() , customType.c_str() ) ) { if ( !self.WithinDistance( node->origin , maxDist ) ) continue; path = find.FindPath( self.origin, node->origin ); if ( path ) pathLen = path->Length(); delete path; path = NULL; if ( pathLen < bestDist ) { bestDist = pathLen; bestNode = node; } } } } if ( self.currentHelperNode.node ) self.currentHelperNode.node->UnreserveNode(); self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = NODETYPE_CUSTOM; if ( bestNode ) bestNode->ReserveNode(); return bestNode; } HelperNode* HelperNode::FindHighestPriorityNode( Actor& self , const str& customType ) { HelperNode *node; HelperNode *bestNode; float priority; float bestPriority; int nodeID; // Initialize our nodes and priorities bestNode = NULL; priority = 0.0f; bestPriority = 0.0f; for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node->isReserved() ) continue; if ( node->isOfType(NODETYPE_CUSTOM)) { str type; type = node->GetCustomType(); if ( !stricmp(type.c_str() , customType.c_str() ) ) { priority = node->GetPriority(); if ( priority > bestPriority ) { bestPriority = priority; bestNode = node; } } } } self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = NODETYPE_CUSTOM; return bestNode; } HelperNode* HelperNode::FindHighestPriorityNode( Actor& self , const str& customType , const str &targetedTo ) { HelperNode *node; HelperNode *bestNode; float priority; float bestPriority; int nodeID; // Initialize our nodes and priorities bestNode = NULL; priority = 0.0f; bestPriority = 999999.0f; for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node->isReserved() ) continue; if ( node->isOfType(NODETYPE_CUSTOM)) { str type; type = node->GetCustomType(); if ( !stricmp(type.c_str() , customType.c_str() ) && !stricmp(node->target, targetedTo.c_str() ) ) { priority = node->GetPriority(); if ( priority < bestPriority ) { bestPriority = priority; bestNode = node; } } } } self.currentHelperNode.node = bestNode; self.currentHelperNode.mask = NODETYPE_CUSTOM; return bestNode; } // // Name: FindClosestHelperNode() // Class: None // // Description: Return nearest valid helpernode based on the criteria passed in // // Parameters: Actor &self -- Actor looking for the node // int mask -- Mask of the node // // Returns: HelperNode* bestNode // HelperNode* HelperNode::FindClosestHelperNode( Actor &self , const str& targetName ) { HelperNode *node; HelperNode *bestNode; float pathLen; float bestDist; FindMovementPath find; Path *path; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // Initialize our nodes and distances bestNode = NULL; bestDist = 999999999.8f; pathLen = bestDist - 1.0f; //We are going to loop through all the helper nodes in the level, find ones matching //our mask, and compare path lengths to each of them. We can't do a plain ol'line of //sight check, because, it seems valid to me that the nearest helpernode might not be //visible. We also can't just compare raw proximity, because we could have a node that //is technically "closer" but behind a wall, forcing us to path a very long way to get //to it. //Note this is a potientially VERY expensive function, and we need to keep an eye on it //to make sure its not called a lot, and keep in mind that it may be a candidate for //extensive optmization for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); if ( node && node->targetname == targetName ) { path = find.FindPath( self.origin, node->origin ); if ( path ) pathLen = path->Length(); delete path; path = NULL; if ( pathLen < bestDist ) { bestDist = pathLen; bestNode = node; } } } self.currentHelperNode.node = bestNode; return bestNode; } //-------------------------------------------------------------- // Name: isHelperNodeInRange() // Class: HelperNode // // Description: Checks if the helper node is within range // // Parameters: Actor &self, // int mask, // float range, // // Returns: true or false; //-------------------------------------------------------------- bool HelperNode::isHelperNodeInRange( Actor &self , int mask , float range ) { HelperNode *node; node = FindHelperNodeClosestTo( self, &self, mask , range ); if ( node ) return true; return false; } // // Name: GetTargetdHelperNode() // Class: None // // Description: Returns the helpernode with the passed in targetName // // Parameters: const str &targetName -- the targetName of the node to find // // Returns: HelperNode* node // HelperNode* HelperNode::GetTargetedHelperNode( const str& targetName ) { HelperNode *node; for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); if ( node && (!Q_stricmp( node->targetname , targetName.c_str() ) ) ) { return node; } } return NULL; } // // Name: GetHelperNodeMask() // Class: None // // Description: Returns an integer bit mask based on the string passed in // // Parameters: const str& type -- string of the node type // // Returns: int mask // int HelperNode::GetHelperNodeMask( const str& type ) { int mask; mask = 0; // Create our mask if ( !Q_stricmp( type.c_str() , "flee" ) ) mask |= NODETYPE_FLEE; else if ( !Q_stricmp( type.c_str() , "work" ) ) mask |= NODETYPE_WORK; else if ( !Q_stricmp( type.c_str() , "anim" ) ) mask |= NODETYPE_ANIM; else if ( !Q_stricmp( type.c_str() , "cover" ) ) mask |= NODETYPE_COVER; else if ( !Q_stricmp( type.c_str() , "patrol" ) ) mask |= NODETYPE_PATROL; else if ( !Q_stricmp( type.c_str() , "sniper" ) ) mask |= NODETYPE_SNIPER; else if ( !Q_stricmp( type.c_str() , "custom" ) ) mask |= NODETYPE_CUSTOM; else if ( !Q_stricmp( type.c_str() , "combat" ) ) mask |= NODETYPE_COMBAT; return mask; } // // Editor Setup // /*****************************************************************************/ /*QUAKED info_helpernode (1 0 1) (-16 -16 0) (16 16 16) FLEE WORK ANIM COVER PATROL SNIPER CUSTOM COMBAT NodeType FLEE Purpose: Marks this spot as a safe destination for the AI when they flee Additional Commands for FLEE type Nodes None at this time NodeType WORK Purpose: Marks this spot as a good place for the AI to work at. The AI will turn to match the angles of helper node, so make sure to angle it appropriately. The AI will also run the animation you specify with the "setnodeanim" event and execute the thread you specify with the "setnodethread" event Additional Commands for WORK type Nodes setnodeanim -- Sets the animation that the AI will run while on this node setnodeentrythread -- Sets the thread that the AI will call when they arrive at this node setnodeexitthread -- Sets the thread that the AI will call when they leave the node wait -- Sets the time at this work node waitrandom -- Sets a random waittime from 1 to the time specified waitforanim -- Sets the actor to work until his animation is completed NodeType ANIM Purpose: Allows you to specify a "special" animation to be played at this location. For example, if you have a "dive through window" animation that you would like played as the AI moves through the node, you can specify it here Additional Commands for ANIM type Nodes setnodeanim -- Sets the animation that the AI will run while on this node animcount -- Number of times the anim can be played animtarget -- TargetName for a specific entity you wish to be animated animactive -- Sets if the animation component is active 1 is true ( default ) 0 is false if animactive is 0, then actors will not change animations at the node NodeType COVER Purpose: Marks this spot as a good place for the AI to use as cover. The AI will turn to match the angles of the helper node, so make sure to angle it appropriately. The AI will also use the cover according to the information you specify in the setcovertype and setcoverdir events. Additional Commands for COVER type Nodes setcovertype -- Sets the type of cover at this spot. Valid values are ( crate , wall ) setcoverdir -- Sets the direction that the AI can fire from ( Based Relative to the direction the node is facing ) valid values are ( left , right, all ) coveractive -- Sets if the cover component is active 1 is true ( default ) 0 is false if coveractive is 0, then actors will not use it for cover. NodeType PATROL Purpose: Marks a position in a patrol path. To be of any value, though, patrol helper nodes must be chained together. If you want a full circuit, then you must connect all nodes together. Any nodes CANNOT be cross linked together. I know, I know, it sucks, but unfortunately, due to limits with targetname, it is the way it must be. If you do not make a circuit, the AI will stand idle at the last node until it decides to do something else. Additional Commands for PATROL type Nodes wait -- Makes the AI pause at this node for the specifed amount of time waitrandom -- Makes the AI pause for a random amount of time (up to a max of the amount specified ) waitforanim -- Makes the AI pause long enough to complete his animation NodeType SNIPER Purpose: Marks a position as a good sniping position. Additional Commands for SNIPER type Nodes wait -- Specifies a maximum time the AI will sit at this Node attacktarget -- Specifies a target to kill -- If not specified, will try and snipe any enemies in view maxkills -- Specifies the maximum number of kills at this Node NodeType CUSTOM Purpose: Marks the node as a custom helper node. Must be used in conjunction with the customtype command NodeType COMBAT Purpose: Marks the node as a general place to do combat. NodeType ******************************************************************************/ //============================================================================= // HelperNodeController //============================================================================= void HelperNodeController::Archive( Archiver &arc ) { HelperNode* checkNode; int num , i; if ( arc.Saving() ) { num = HelperNodes.NumObjects(); arc.ArchiveInteger( &num ); for ( i = 1 ; i <= num ; i++ ) { checkNode = HelperNodes.ObjectAt( i ); arc.ArchiveObject( checkNode ); } } else { arc.ArchiveInteger( &num ); HelperNodes.ClearObjectList(); HelperNodes.Resize( num ); for ( i = 1 ; i<= num ; i++ ) { checkNode = new HelperNode; arc.ArchiveObject( checkNode ); HelperNodes.AddObject( checkNode ); } } } //-------------------------------------------------------------- // Name: CleanupHelperNodeList() // Class: HelperNode // // Description: Iterates through the helper node list and cleans // it out // // Parameters: None // // Returns: None //-------------------------------------------------------------- void HelperNode::CleanupHelperNodeList() { HelperNode* checkNode; for ( int i = HelperNodes.NumObjects() ; i > 0 ; i-- ) { checkNode = HelperNodes.ObjectAt( i ); if ( checkNode ) { delete checkNode; checkNode = NULL; HelperNodes.RemoveObjectAt( i ); } } } waitType_t HelperNode::GetWaitType( const str &waitType ) { if ( waitType == "anim" ) return WAITTYPE_ANIM; else if ( waitType == "event" ) return WAITTYPE_EVENT; else if ( waitType == "time" ) return WAITTYPE_TIME; assert( "Unknown waitType" ); return WAITTYPE_ERROR; } void HelperNode::SendCommand( Event *ev ) { Event *commandEvent; int numArgs; //First make sure we have a user to send //the command event to if ( !_user ) return; commandEvent = new Event ( EV_HelperNodeCommand ); numArgs = ev->NumArgs(); for ( int i = 1 ; i <= numArgs ; i++ ) { commandEvent->AddToken( ev->GetToken( i ) ); } _user->ProcessEvent( commandEvent ); } customAnimListEntry_t* HelperNode::GetNextAnimEntryFromList() { int animCount = _customAnimList.NumObjects(); int index = _customAnimListIndex; if ( index < animCount ) index++; return _customAnimList.ObjectAt( index ); } customAnimListEntry_t* HelperNode::GetCurrentAnimEntryFromList() { int index = _customAnimListIndex; return _customAnimList.ObjectAt( index ); } void HelperNode::AddAnimation( Event *ev ) { float time; if ( ev->NumArgs() > 2 ) time = ev->GetFloat( 3 ); else time = 0.0f; AddAnimation(ev->GetString( 1 ) , GetWaitType( ev->GetString( 2 ) ) , time ); } void HelperNode::AddAnimation( const str &anim , waitType_t waitType , float time ) { customAnimListEntry_t* checkEntry; checkEntry = new customAnimListEntry_t; checkEntry->anim = anim; checkEntry->waitType = waitType; checkEntry->time = time; _customAnimList.AddObject( checkEntry ); _usingCustomAnimList = true; } bool HelperNode::isAnimListFinished() { return _customAnimListIndex > _customAnimList.NumObjects(); } void HelperNode::NextAnim() { _customAnimListIndex++; } // // Global Functions // void AddHelperNodeToList( HelperNode* node ) { if ( node ) HelperNodes.AddUniqueObject( node ); } void RemoveHelperNodeFromList( HelperNode *node ) { HelperNode* checkNode; for ( int i = HelperNodes.NumObjects() ; i > 0 ; i-- ) { checkNode = HelperNodes.ObjectAt( i ); if ( checkNode == node ) { delete checkNode; checkNode = NULL; HelperNodes.RemoveObjectAt( i ); return; } } }