home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / game / spawner.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  15KB  |  598 lines

  1. /*
  2. ================
  3.  
  4. Spawner.cpp
  5.  
  6. ================
  7. */
  8.  
  9. #include "../idlib/precompiled.h"
  10. #pragma hdrstop
  11.  
  12. #include "Game_local.h"
  13. #include "spawner.h"
  14. #include "vehicle/Vehicle.h"
  15. #include "ai/AI.h"
  16. #include "ai/AI_Manager.h"
  17. #include "ai/AI_Util.h"
  18.  
  19. const idEventDef EV_Spawner_RemoveNullActiveEntities( "removeNullActiveEntities" );
  20. const idEventDef EV_Spawner_NumActiveEntities( "numActiveEntities", "", 'd' );
  21. const idEventDef EV_Spawner_GetActiveEntity( "getActiveEntity", "d", 'e' );
  22.  
  23. CLASS_DECLARATION( idEntity, rvSpawner )
  24.     EVENT( EV_Activate,                                rvSpawner::Event_Activate )
  25.     EVENT( EV_Spawner_RemoveNullActiveEntities,        rvSpawner::Event_RemoveNullActiveEntities )
  26.     EVENT( EV_Spawner_NumActiveEntities,            rvSpawner::Event_NumActiveEntities )
  27.     EVENT( EV_Spawner_GetActiveEntity,                rvSpawner::Event_GetActiveEntity )
  28. END_CLASS
  29.  
  30. /*
  31. ==============
  32. rvSpawner::Spawn
  33. ==============
  34. */
  35. void rvSpawner::Spawn( void ){
  36.     GetPhysics()->SetContents( 0 );
  37.  
  38.     // TEMP: read max_team_test until we can get it out of all the current maps
  39.     if ( !spawnArgs.GetInt ( "max_active", "4", maxActive ) ) {
  40.         if ( spawnArgs.GetInt ( "max_team_test", "4", maxActive ) ) {
  41.             gameLocal.Warning ( "spawner '%s' using outdated 'max_team_test', please change to 'max_active'", GetName() );
  42.         }
  43.     }
  44.  
  45.     maxToSpawn        = spawnArgs.GetInt( "count", "-1" );
  46.     skipVisible        = spawnArgs.GetBool ( "skipvisible", "1" );
  47.     spawnWaves        = spawnArgs.GetInt( "waves", "1" );
  48.     spawnDelay        = SEC2MS( spawnArgs.GetFloat( "delay", "2" ) );
  49.     numSpawned        = 0;
  50.     nextSpawnTime    = 0;
  51.  
  52.     // Spawn waves has to be less than max active
  53.     if ( spawnWaves > maxActive ) {
  54.         spawnWaves = maxActive;
  55.     }
  56.  
  57.     FindSpawnTypes ( );
  58. }
  59.  
  60. /*
  61. ==============
  62. rvSpawner::Save
  63. ==============
  64. */
  65. void rvSpawner::Save ( idSaveGame *savefile ) const{
  66.     savefile->WriteInt( numSpawned );
  67.     savefile->WriteInt( maxToSpawn );
  68.     savefile->WriteFloat( nextSpawnTime );
  69.     savefile->WriteInt( maxActive );
  70.  
  71.     int i;
  72.     savefile->WriteInt( currentActive.Num() );
  73.     for( i = 0; i < currentActive.Num(); i++ ) {
  74.         currentActive[i].Save ( savefile );
  75.     }
  76.  
  77.     savefile->WriteInt( spawnWaves );
  78.     savefile->WriteInt( spawnDelay );
  79.     savefile->WriteBool( skipVisible );
  80.  
  81.     savefile->WriteInt( spawnPoints.Num() );
  82.     for ( i = 0; i < spawnPoints.Num(); i++ ) {
  83.         spawnPoints[ i ].Save ( savefile );
  84.     }
  85.  
  86.     savefile->WriteInt ( callbacks.Num() );
  87.     for ( i = 0; i < callbacks.Num(); i ++ ) {
  88.         callbacks[i].ent.Save ( savefile );
  89.         savefile->WriteString ( callbacks[i].event );
  90.     }
  91. }
  92.  
  93. /*
  94. ==============
  95. rvSpawner::Restore
  96. ==============
  97. */
  98. void rvSpawner::Restore ( idRestoreGame *savefile ){
  99.     int num;
  100.     int i;
  101.  
  102.     savefile->ReadInt( numSpawned );
  103.     savefile->ReadInt( maxToSpawn );
  104.     savefile->ReadFloat( nextSpawnTime );
  105.     savefile->ReadInt( maxActive );
  106.     
  107.     savefile->ReadInt( num );
  108.     currentActive.Clear ( );
  109.     currentActive.SetNum( num );
  110.     for( i = 0; i < num; i++ ) {
  111.         currentActive[i].Restore ( savefile );
  112.     }
  113.  
  114.     savefile->ReadInt( spawnWaves );
  115.     savefile->ReadInt( spawnDelay );
  116.     savefile->ReadBool( skipVisible );
  117.  
  118.     savefile->ReadInt( num );
  119.     spawnPoints.SetNum( num);
  120.     for( i = 0; i < num; i ++ ) {
  121.         spawnPoints[i].Restore ( savefile );
  122.     }
  123.  
  124.     savefile->ReadInt ( num );
  125.     callbacks.SetNum ( num );
  126.     for ( i = 0; i < num; i ++ ) {
  127.         callbacks[i].ent.Restore ( savefile );
  128.         savefile->ReadString ( callbacks[i].event );
  129.     }
  130.  
  131.     FindSpawnTypes ();
  132. }
  133.  
  134. /*
  135. ==============
  136. rvSpawner::FindSpawnTypes
  137.  
  138. Generate the list of classnames to spawn from the spawnArgs.  Anything matching the 
  139. prefix "def_spawn" will be included in the list.
  140. ==============
  141. */
  142. void rvSpawner::FindSpawnTypes ( void ){
  143.     const idKeyValue *kv;    
  144.     for ( kv = spawnArgs.MatchPrefix( "def_spawn", NULL ); kv; kv = spawnArgs.MatchPrefix( "def_spawn", kv ) ) {
  145.         spawnTypes.Append ( kv->GetValue ( ) );
  146.     }
  147. }
  148.  
  149. /*
  150. ==============
  151. rvSpawner::FindTargets
  152. ==============
  153. */
  154. void rvSpawner::FindTargets ( void ) {
  155.     int i;
  156.     idBounds                        bounds( idVec3( -16, -16, 0 ), idVec3( 16, 16, 72 ) );
  157.     trace_t tr;
  158.     
  159.     idEntity::FindTargets ( );
  160.  
  161.     // Copy the relevant targets to the spawn point list (right now only target_null entities)
  162.     for ( i = targets.Num() - 1; i >= 0; i -- ) {
  163.         idEntity* ent;
  164.         ent = targets[i];
  165.         if ( idStr::Icmp ( ent->spawnArgs.GetString ( "classname" ), "target_null" ) ) {
  166.             continue;
  167.         }
  168.         
  169.         idEntityPtr<idEntity> &entityPtr = spawnPoints.Alloc();
  170.         entityPtr = ent;        
  171.  
  172.         if( !spawnArgs.GetBool("ignoreSpawnPointValidation") ) {
  173.             gameLocal.TraceBounds( this, tr, ent->GetPhysics()->GetOrigin(), ent->GetPhysics()->GetOrigin(), bounds, MASK_MONSTERSOLID, NULL );
  174.             if ( gameLocal.entities[tr.c.entityNum] && !gameLocal.entities[tr.c.entityNum]->IsType ( idActor::GetClassType ( ) ) ) {
  175.                 //drop a console warning here
  176.                 gameLocal.Warning ( "Spawner '%s' can't spawn at point '%s', the monster won't fit.", GetName(), ent->GetName() );        
  177.             }    
  178.         }
  179.     }
  180. }
  181.  
  182. /*
  183. ==============
  184. rvSpawner::ValidateSpawnPoint
  185. ==============
  186. */
  187. bool rvSpawner::ValidateSpawnPoint ( const idVec3 origin, const idBounds &bounds ){
  188.     trace_t tr;
  189.     if( spawnArgs.GetBool("ignoreSpawnPointValidation") ) {
  190.         return true;
  191.     }
  192.  
  193.     gameLocal.TraceBounds( this, tr, origin, origin, bounds, MASK_MONSTERSOLID, NULL );
  194.     return tr.fraction >= 1.0f;
  195. }
  196.  
  197. /*
  198. ==============
  199. rvSpawner::AddSpawnPoint
  200. ==============
  201. */
  202. void rvSpawner::AddSpawnPoint ( idEntity* point ) {
  203.     idEntityPtr<idEntity> &entityPtr = spawnPoints.Alloc();
  204.     entityPtr = point;
  205.     
  206.     // If there were no spawnPoints then start with the delay
  207.     if ( spawnPoints.Num () == 1 ) {
  208.         nextSpawnTime = gameLocal.time + spawnDelay;
  209.     }
  210. }
  211.  
  212. /*
  213. ==============
  214. rvSpawner::RemoveSpawnPoint
  215. ==============
  216. */
  217. void rvSpawner::RemoveSpawnPoint ( idEntity* point ) {
  218.     int i;
  219.     for ( i = spawnPoints.Num()-1; i >= 0; i -- ) {
  220.         if ( spawnPoints[i] == point ) {
  221.             spawnPoints.RemoveIndex ( i );
  222.             break;
  223.         }
  224.     }
  225. }
  226.  
  227. /*
  228. ==============
  229. rvSpawner::GetSpawnPoint
  230. ==============
  231. */
  232. void rvSpawner::AddCallback ( idEntity* owner, const idEventDef* ev ) {    
  233.     spawnerCallback_t& callback = callbacks.Alloc ( );
  234.     callback.event = ev->GetName ( );
  235.     callback.ent = owner;
  236. }
  237.  
  238. /*
  239. ==============
  240. rvSpawner::GetSpawnPoint
  241. ==============
  242. */
  243. idEntity *rvSpawner::GetSpawnPoint ( void ) {
  244.     idBounds                        bounds( idVec3( -16, -16, 0 ), idVec3( 16, 16, 72 ) );
  245.     idList< idEntityPtr<idEntity> >    spawns;
  246.     int                                spawnIndex;
  247.     idEntity*                        spawnEnt;
  248.  
  249.     // Run through all spawnPoints and choose a random one. Each time a spawn point is excluded
  250.     // it will be removed from the list until there are no more items in the list.
  251.     for ( spawns = spawnPoints ; spawns.Num(); spawns.RemoveIndex ( spawnIndex ) ) {        
  252.         spawnIndex = gameLocal.random.RandomInt ( spawns.Num() );
  253.         spawnEnt   = spawns[spawnIndex];
  254.         
  255.         if ( !spawnEnt || !spawnEnt->GetPhysics() ) {
  256.             continue;
  257.         }
  258.         
  259.         // Check to see if something is in the way at this spawn point
  260.         if ( !ValidateSpawnPoint ( spawnEnt->GetPhysics()->GetOrigin(), bounds ) ) {
  261.             continue;
  262.         }
  263.         
  264.         // Skip the spawn point because its currently visible?
  265.         if ( skipVisible && gameLocal.GetLocalPlayer()->CanSee ( spawnEnt, true ) ) {
  266.             continue;
  267.         }
  268.  
  269.         // Found one!
  270.         return spawnEnt;
  271.     }
  272.         
  273.     return NULL;
  274. }
  275.  
  276. /*
  277. ==============
  278. rvSpawner::GetSpawnType
  279. ==============
  280. */
  281. const char* rvSpawner::GetSpawnType ( idEntity* spawnPoint ) {
  282.     const idKeyValue* kv;
  283.     
  284.     if ( spawnPoint ) {
  285.         // If the spawn point has any "def_spawn" keys then they override the normal spawn keys
  286.         kv = spawnPoint->spawnArgs.MatchPrefix ( "def_spawn", NULL );
  287.         if ( kv ) {
  288.             const char* types [ MAX_SPAWN_TYPES ];
  289.             int            typeCount;
  290.  
  291.             for ( typeCount = 0; 
  292.                   typeCount < MAX_SPAWN_TYPES && kv; 
  293.                   kv = spawnPoint->spawnArgs.MatchPrefix ( "def_spawn", kv ) ) {
  294.                 types [ typeCount++ ] = kv->GetValue ( ).c_str();
  295.             }
  296.             
  297.             return types[ gameLocal.random.RandomInt( typeCount ) ];
  298.         }
  299.     }
  300.     
  301.     // No spawn types?
  302.     if ( !spawnTypes.Num ( ) ) {
  303.         return "";
  304.     }
  305.     
  306.     // Return from the spawners list of types
  307.     return spawnTypes[ gameLocal.random.RandomInt( spawnTypes.Num() ) ];
  308. }
  309.  
  310. /*
  311. ==============
  312. rvSpawner::CopyPrefixedSpawnArgs
  313. ==============
  314. */
  315. void rvSpawner::CopyPrefixedSpawnArgs( idEntity *src, const char *prefix, idDict &args ){
  316.     const idKeyValue *kv = src->spawnArgs.MatchPrefix( prefix, NULL );
  317.     while( kv ) {
  318.         args.Set( kv->GetKey().c_str() + idStr::Length( prefix ), kv->GetValue() );
  319.         kv = src->spawnArgs.MatchPrefix( prefix, kv );
  320.     }
  321. }
  322.  
  323. /*
  324. ==============
  325. rvSpawner::SpawnEnt
  326. ==============
  327. */
  328. bool rvSpawner::SpawnEnt( void ){
  329.     idDict        args;
  330.     idEntity*    spawnPoint;
  331.     idEntity*    spawnedEnt;
  332.     const char* temp;
  333.  
  334.     // Find a spawn point to spawn the entity
  335.     spawnPoint = GetSpawnPoint ( );
  336.     if( !spawnPoint ){
  337.         return false;
  338.     }
  339.  
  340.     // No valid spawn types for this point
  341.     temp = GetSpawnType ( spawnPoint );
  342.     if ( !temp || !*temp ) {
  343.         gameLocal.Warning ( "Spawner '%s' could not find any valid spawn types for spawn point '%s'", GetName(), spawnPoint->GetName() );
  344.         return false;
  345.     }
  346.  
  347.     // Build the spawn parameters for the entity about to be spawned
  348.     args.Set       ( "origin",            spawnPoint->GetPhysics()->GetOrigin().ToString() );
  349.     args.SetFloat  ( "angle",            spawnPoint->GetPhysics()->GetAxis().ToAngles()[YAW] );
  350.     args.Set       ( "classname",        temp );    
  351.     args.SetBool   ( "forceEnemy",        spawnArgs.GetBool ( "auto_target", "1" ) );
  352.     args.SetBool   ( "faceEnemy",        spawnArgs.GetBool ( "faceEnemy", "0" ) );
  353.  
  354.     // Copy all keywords prefixed with "spawn_" to the entity being spawned.
  355.     CopyPrefixedSpawnArgs( this, "spawn_", args );
  356.     if( spawnPoint != this ) {
  357.         CopyPrefixedSpawnArgs( spawnPoint, "spawn_", args );
  358.     }
  359.  
  360.     // Spawn the entity
  361.     if ( !gameLocal.SpawnEntityDef ( args, &spawnedEnt ) ) {
  362.         return false;
  363.     }
  364.  
  365.     // Activate the spawned entity
  366.     spawnedEnt->ProcessEvent( &EV_Activate, this );
  367.  
  368.     // Play a spawning effect if it has one - do we possibly want some script hooks in here?
  369.     gameLocal.PlayEffect ( spawnArgs, "fx_spawning", spawnPoint->GetPhysics()->GetOrigin(), idVec3(0,0,1).ToMat3() );
  370.  
  371.     // script function for spawning guys
  372.     if( spawnArgs.GetString( "call", "", &temp ) && *temp ) {
  373.         gameLocal.CallFrameCommand ( this, temp );
  374.     }
  375.  
  376.     // script function for the guy being spawned
  377.     if ( spawnArgs.GetString( "call_spawned", "", &temp ) && *temp ) {
  378.         gameLocal.CallFrameCommand ( spawnedEnt, temp );
  379.     }
  380.  
  381.     // Call all of our callbacks
  382.     int c;
  383.     for ( c = callbacks.Num() - 1; c >= 0; c-- ) {
  384.         if ( callbacks[c].ent ) {
  385.             callbacks[c].ent->ProcessEvent ( idEventDef::FindEvent ( callbacks[c].event ), this, spawnedEnt );
  386.         }
  387.     }
  388.  
  389.     // Activate the spawn point entity when an enemy is spawned there and all of its targets
  390.     if( spawnPoint != this ){
  391.         spawnPoint->ProcessEvent( &EV_Activate, spawnPoint );
  392.         spawnPoint->ActivateTargets( spawnedEnt );
  393.         
  394.         // One time use on this target?
  395.         if ( spawnPoint->spawnArgs.GetBool ( "remove" ) ) {
  396.             spawnPoint->PostEventMS ( &EV_Remove, 0 );
  397.         }
  398.     }
  399.  
  400.     // Increment the total number spawned
  401.     numSpawned++;
  402.  
  403.     return true;
  404. }
  405.  
  406. /*
  407. ==============
  408. rvSpawner::Think
  409. ==============
  410. */
  411. void rvSpawner::Think( void ){
  412.     if ( thinkFlags & TH_THINK ) {
  413.         if( ActiveListChanged() ) {// If an entity has been removed and we have not been informed via Detach
  414.             nextSpawnTime = gameLocal.GetTime() + spawnDelay;
  415.         }
  416.  
  417.         CheckSpawn ( );
  418.     }
  419. }
  420.  
  421. /*
  422. ==============
  423. rvSpawner::CheckSpawn
  424. ==============
  425. */
  426. void rvSpawner::CheckSpawn ( void ) {
  427.     int count;
  428.  
  429.     // Any spawn points?
  430.     if ( !spawnPoints.Num ( ) ) {
  431.         return;
  432.     }
  433.  
  434.     // Is it time to spawn yet?
  435.     if ( nextSpawnTime == 0 || gameLocal.time < nextSpawnTime ) {
  436.         return;
  437.     }
  438.  
  439.     // Any left to spawn?
  440.     if ( maxToSpawn > -1 && numSpawned >= maxToSpawn ){
  441.         return;
  442.     }
  443.  
  444.     // Spawn in waves?
  445.     for ( count = 0; count < spawnWaves; count ++ ) {
  446.         // Too many active?
  447.         if( currentActive.Num() >= maxActive ) {
  448.             return;
  449.         }
  450.  
  451.         // Spawn a new entity
  452.         SpawnEnt ( );
  453.  
  454.         // Are we at the limit now?
  455.         if ( maxToSpawn > -1 && numSpawned >= maxToSpawn ) {
  456.             CallScriptEvents( "script_used_up", this );
  457.             PostEventMS ( &EV_Remove, 0 );
  458.             break;
  459.         }
  460.     }
  461.  
  462.     // Dont spawn again until after the delay
  463.     nextSpawnTime = gameLocal.time + spawnDelay;
  464. }
  465.  
  466. /*
  467. ==============
  468. rvSpawner::CallScriptEvents
  469. ==============
  470. */
  471. void rvSpawner::CallScriptEvents( const char* prefixKey, idEntity* parm ) {
  472.     if( !prefixKey || !prefixKey[0] ) {
  473.         return;
  474.     }
  475.  
  476.     rvScriptFuncUtility func;
  477.     for( const idKeyValue* kv = spawnArgs.MatchPrefix(prefixKey); kv; kv = spawnArgs.MatchPrefix(prefixKey, kv) ) {
  478.         if( !kv->GetValue().Length() ) {
  479.             continue;
  480.         }
  481.  
  482.         if( func.Init(kv->GetValue()) <= SFU_ERROR ) {
  483.             continue;
  484.         }
  485.  
  486.         func.InsertEntity( parm, 0 );
  487.         func.CallFunc( &spawnArgs );
  488.     }
  489. }
  490.  
  491. /*
  492. ==============
  493. rvSpawner::ActiveListChanged
  494. ==============
  495. */
  496. bool rvSpawner::ActiveListChanged() {
  497.     int previousCount = currentActive.Num();
  498.  
  499.     currentActive.RemoveNull();
  500.  
  501.     return previousCount > currentActive.Num();
  502. }
  503.  
  504. /*
  505. ==============
  506. rvSpawner::Attach
  507.  
  508. Attach the given AI to the spawner.  This will increase the active count of the spawner and
  509. set the spawner pointer in the ai.
  510. ==============
  511. */
  512. void rvSpawner::Attach ( idEntity* ent ) {
  513.     currentActive.AddUnique( ent );
  514. }
  515.  
  516. /*
  517. ==============
  518. rvSpawner::Detach
  519.  
  520. Detaches the given AI from the spawner which will free up an active spot for spawning.
  521. Attach the given AI to the spawner.  This will increase the active count of the spawner and
  522. set the spawner pointer in the ai.
  523. ==============
  524. */
  525. void rvSpawner::Detach ( idEntity* ent ){
  526.     currentActive.Remove( ent );
  527.  
  528.     nextSpawnTime = gameLocal.GetTime() + spawnDelay;
  529. }
  530.  
  531. /*
  532. ==============
  533. rvSpawner::Event_Activate
  534. ==============
  535. */
  536. void rvSpawner::Event_Activate ( idEntity *activator ) {
  537.     
  538.     // "trigger_only" spawners will attempt to spawn when triggered
  539.     if ( spawnArgs.GetBool ( "trigger_only" ) ) {
  540.         // Update next spawn time to follo CheckSpawn into thinking its time to spawn again
  541.         nextSpawnTime = gameLocal.time;
  542.         CheckSpawn ( );
  543.         return;
  544.     }
  545.     
  546.     // If nextSpawnTime is zero then the spawner is currently deactivated
  547.     if ( nextSpawnTime == 0 ) {
  548.         // Start thinking
  549.         BecomeActive( TH_THINK );
  550.         
  551.         // Allow immediate spawn
  552.         nextSpawnTime = gameLocal.time;
  553.         
  554.         // Spawn any ai targets and add them to the current count
  555.         ActivateTargets ( this );
  556.     } else {
  557.         nextSpawnTime = 0;
  558.         BecomeInactive( TH_THINK );
  559.         
  560.         // Remove the spawner if need be
  561.         if ( spawnArgs.GetBool ( "remove", "1" ) ) {
  562.             PostEventMS ( &EV_Remove, 0 );
  563.         }
  564.     }
  565. }
  566.  
  567. /*
  568. ==============
  569. rvSpawner::Event_RemoveNullActiveEntities
  570. ==============
  571. */
  572. void rvSpawner::Event_RemoveNullActiveEntities( void ) {
  573.     for( int ix = currentActive.Num() - 1; ix >= 0; --ix ) {
  574.         if( !currentActive[ix].IsValid() ) {
  575.             currentActive.RemoveIndex( ix );
  576.         }
  577.     }
  578. }
  579.  
  580. /*
  581. ==============
  582. rvSpawner::Event_NumActiveEntities
  583. ==============
  584. */
  585. void rvSpawner::Event_NumActiveEntities( void ) {
  586.     idThread::ReturnInt( currentActive.Num() );
  587. }
  588.  
  589. /*
  590. ==============
  591. rvSpawner::Event_GetActiveEntity
  592. ==============
  593. */
  594. void rvSpawner::Event_GetActiveEntity( int index ) {
  595.     idThread::ReturnEntity( (index < 0 || index >= currentActive.Num()) ? NULL : currentActive[index] );
  596. }
  597.  
  598.