2008-08-23 08:48:19 +00:00
/*
* * a_randomspawner . cpp
* * A thing that randomly spawns one item in a list of many , before disappearing .
2009-09-15 14:16:55 +00:00
* * bouncecount is used to keep track of recursions ( so as to prevent infinite loops ) .
* * Species is used to store the index of the spawned actor ' s name .
2008-08-23 08:48:19 +00:00
*/
2008-06-22 09:13:19 +00:00
# include "actor.h"
# include "info.h"
# include "m_random.h"
# include "p_local.h"
# include "p_enemy.h"
# include "s_sound.h"
# include "statnums.h"
# include "gstrings.h"
# include "a_action.h"
# include "thingdef/thingdef.h"
2008-08-23 08:48:19 +00:00
# define MAX_RANDOMSPAWNERS_RECURSION 32 // Should be largely more than enough, honestly.
2008-06-22 09:13:19 +00:00
static FRandom pr_randomspawn ( " RandomSpawn " ) ;
class ARandomSpawner : public AActor
{
2008-08-09 11:35:42 +00:00
DECLARE_CLASS ( ARandomSpawner , AActor )
2008-06-22 09:13:19 +00:00
2009-09-15 14:16:55 +00:00
// To handle "RandomSpawning" missiles, the code has to be split in two parts.
// If the following code is not done in BeginPlay, missiles will use the
// random spawner's velocity (0...) instead of their own.
void BeginPlay ( )
2008-06-22 09:13:19 +00:00
{
FDropItem * di ; // di will be our drop item list iterator
FDropItem * drop ; // while drop stays as the reference point.
int n = 0 ;
2013-02-20 02:47:01 +00:00
bool nomonsters = ( dmflags & DF_NO_MONSTERS ) | | ( level . flags2 & LEVEL2_NOMONSTERS ) ;
2008-06-22 09:13:19 +00:00
2009-09-15 14:16:55 +00:00
Super : : BeginPlay ( ) ;
2008-09-21 18:02:38 +00:00
drop = di = GetDropItems ( ) ;
2008-06-22 09:13:19 +00:00
if ( di ! = NULL )
{
while ( di ! = NULL )
{
if ( di - > Name ! = NAME_None )
{
2013-02-20 02:47:01 +00:00
if ( ! nomonsters | | ! ( GetDefaultByType ( PClass : : FindClass ( di - > Name ) ) - > flags3 & MF3_ISMONSTER ) )
{
if ( di - > amount < 0 ) di - > amount = 1 ; // default value is -1, we need a positive value.
n + = di - > amount ; // this is how we can weight the list.
}
2008-06-22 09:13:19 +00:00
di = di - > Next ;
}
}
2013-02-20 02:47:01 +00:00
if ( n = = 0 )
{ // Nothing left to spawn. They must have all been monsters, and monsters are disabled.
Destroy ( ) ;
return ;
}
2008-06-22 09:13:19 +00:00
// Then we reset the iterator to the start position...
di = drop ;
// Take a random number...
n = pr_randomspawn ( n ) ;
// And iterate in the array up to the random number chosen.
2013-02-22 01:34:16 +00:00
while ( n > - 1 & & di ! = NULL )
2008-06-22 09:13:19 +00:00
{
2013-02-22 01:34:16 +00:00
if ( di - > Name ! = NAME_None & &
( ! nomonsters | | ! ( GetDefaultByType ( PClass : : FindClass ( di - > Name ) ) - > flags3 & MF3_ISMONSTER ) ) )
2008-06-22 09:13:19 +00:00
{
2013-02-22 01:34:16 +00:00
n - = di - > amount ;
if ( ( di - > Next ! = NULL ) & & ( n > - 1 ) )
di = di - > Next ;
else
n = - 1 ;
}
else
{
di = di - > Next ;
2008-06-22 09:13:19 +00:00
}
}
// So now we can spawn the dropped item.
2013-02-22 01:34:16 +00:00
if ( di = = NULL | | bouncecount > = MAX_RANDOMSPAWNERS_RECURSION ) // Prevents infinite recursions
2009-09-15 14:16:55 +00:00
{
2008-08-23 08:48:19 +00:00
Spawn ( " Unknown " , x , y , z , NO_REPLACE ) ; // Show that there's a problem.
2013-02-20 02:47:01 +00:00
Destroy ( ) ;
return ;
2009-09-15 14:16:55 +00:00
}
2008-08-23 08:48:19 +00:00
else if ( pr_randomspawn ( ) < = di - > probability ) // prob 255 = always spawn, prob 0 = never spawn.
2008-06-22 09:13:19 +00:00
{
2009-09-15 14:16:55 +00:00
// Handle replacement here so as to get the proper speed and flags for missiles
2009-11-08 02:51:22 +00:00
const PClass * cls ;
2009-09-15 14:16:55 +00:00
cls = PClass : : FindClass ( di - > Name ) ;
2009-11-08 02:51:22 +00:00
if ( cls ! = NULL )
{
2010-08-26 20:59:15 +00:00
const PClass * rep = cls - > GetReplacement ( ) ;
2009-11-08 02:51:22 +00:00
if ( rep ! = NULL )
{
cls = rep ;
}
}
if ( cls ! = NULL )
2009-06-07 16:38:19 +00:00
{
2009-09-15 14:16:55 +00:00
Species = cls - > TypeName ;
2009-11-08 02:51:22 +00:00
AActor * defmobj = GetDefaultByType ( cls ) ;
2009-09-15 14:16:55 +00:00
this - > Speed = defmobj - > Speed ;
this - > flags | = ( defmobj - > flags & MF_MISSILE ) ;
this - > flags2 | = ( defmobj - > flags2 & MF2_SEEKERMISSILE ) ;
this - > flags4 | = ( defmobj - > flags4 & MF4_SPECTRAL ) ;
2009-06-07 16:38:19 +00:00
}
2009-11-08 02:51:22 +00:00
else
{
Species = NAME_None ;
}
2009-09-15 14:16:55 +00:00
}
}
}
// The second half of random spawning. Now that the spawner is initialized, the
// real actor can be created. If the following code were in BeginPlay instead,
// missiles would not have yet obtained certain information that is absolutely
// necessary to them -- such as their source and destination.
void PostBeginPlay ( )
{
AActor * newmobj = NULL ;
bool boss = false ;
2012-03-07 01:03:56 +00:00
Super : : PostBeginPlay ( ) ;
2013-08-28 09:14:48 +00:00
if ( Species = = NAME_None )
{
Destroy ( ) ;
return ;
}
2009-09-15 14:16:55 +00:00
const PClass * cls = PClass : : FindClass ( Species ) ;
if ( this - > flags & MF_MISSILE & & target & & target - > target ) // Attempting to spawn a missile.
{
if ( ( tracer = = NULL ) & & ( flags2 & MF2_SEEKERMISSILE ) ) tracer = target - > target ;
newmobj = P_SpawnMissileXYZ ( x , y , z , target , target - > target , cls , false ) ;
}
else newmobj = Spawn ( cls , x , y , z , NO_REPLACE ) ;
if ( newmobj ! = NULL )
{
// copy everything relevant
newmobj - > SpawnAngle = newmobj - > angle = angle ;
2010-04-26 01:49:29 +00:00
newmobj - > SpawnPoint [ 2 ] = SpawnPoint [ 2 ] ;
2009-09-15 14:16:55 +00:00
newmobj - > special = special ;
newmobj - > args [ 0 ] = args [ 0 ] ;
newmobj - > args [ 1 ] = args [ 1 ] ;
newmobj - > args [ 2 ] = args [ 2 ] ;
newmobj - > args [ 3 ] = args [ 3 ] ;
newmobj - > args [ 4 ] = args [ 4 ] ;
2009-09-16 15:57:08 +00:00
newmobj - > special1 = special1 ;
newmobj - > special2 = special2 ;
2013-08-28 09:14:48 +00:00
newmobj - > SpawnFlags = SpawnFlags & ~ MTF_SECRET ; // MTF_SECRET needs special treatment to avoid incrementing the secret counter twice. It had already been processed for the spawner itself.
2009-09-15 14:16:55 +00:00
newmobj - > HandleSpawnFlags ( ) ;
2013-08-28 09:14:48 +00:00
newmobj - > SpawnFlags = SpawnFlags ;
2009-09-15 14:16:55 +00:00
newmobj - > tid = tid ;
newmobj - > AddToHash ( ) ;
newmobj - > velx = velx ;
newmobj - > vely = vely ;
newmobj - > velz = velz ;
newmobj - > master = master ; // For things such as DamageMaster/DamageChildren, transfer mastery.
newmobj - > target = target ;
newmobj - > tracer = tracer ;
newmobj - > CopyFriendliness ( this , false ) ;
// This handles things such as projectiles with the MF4_SPECTRAL flag that have
// a health set to -2 after spawning, for internal reasons.
if ( health ! = SpawnHealth ( ) ) newmobj - > health = health ;
if ( ! ( flags & MF_DROPPED ) ) newmobj - > flags & = ~ MF_DROPPED ;
2010-04-26 01:49:29 +00:00
// Handle special altitude flags
2009-09-15 14:16:55 +00:00
if ( newmobj - > flags & MF_SPAWNCEILING )
{
2010-04-26 01:49:29 +00:00
newmobj - > z = newmobj - > ceilingz - newmobj - > height - SpawnPoint [ 2 ] ;
2009-09-15 14:16:55 +00:00
}
else if ( newmobj - > flags2 & MF2_SPAWNFLOAT )
{
fixed_t space = newmobj - > ceilingz - newmobj - > height - newmobj - > floorz ;
if ( space > 48 * FRACUNIT )
2009-06-07 16:38:19 +00:00
{
2009-09-15 14:16:55 +00:00
space - = 40 * FRACUNIT ;
newmobj - > z = MulScale8 ( space , pr_randomspawn ( ) ) + newmobj - > floorz + 40 * FRACUNIT ;
2009-06-07 16:38:19 +00:00
}
2010-04-26 01:49:29 +00:00
newmobj - > z + = SpawnPoint [ 2 ] ;
2008-06-22 09:13:19 +00:00
}
2009-09-15 14:16:55 +00:00
if ( newmobj - > flags & MF_MISSILE )
2013-03-21 03:06:04 +00:00
P_CheckMissileSpawn ( newmobj , 0 ) ;
2009-09-15 14:16:55 +00:00
// Bouncecount is used to count how many recursions we're in.
if ( newmobj - > IsKindOf ( PClass : : FindClass ( " RandomSpawner " ) ) )
newmobj - > bouncecount = + + bouncecount ;
// If the spawned actor has either of those flags, it's a boss.
if ( ( newmobj - > flags4 & MF4_BOSSDEATH ) | | ( newmobj - > flags2 & MF2_BOSS ) )
boss = true ;
// If a replaced actor has either of those same flags, it's also a boss.
AActor * rep = GetDefaultByType ( GetClass ( ) - > ActorInfo - > GetReplacee ( ) - > Class ) ;
2009-11-08 02:51:22 +00:00
if ( rep & & ( ( rep - > flags4 & MF4_BOSSDEATH ) | | ( rep - > flags2 & MF2_BOSS ) ) )
2009-09-15 14:16:55 +00:00
boss = true ;
2008-06-22 09:13:19 +00:00
}
2013-02-20 02:47:01 +00:00
if ( boss )
this - > tracer = newmobj ;
else // "else" because a boss-replacing spawner must wait until it can call A_BossDeath.
Destroy ( ) ;
2008-06-22 09:13:19 +00:00
}
2008-08-23 08:48:19 +00:00
void Tick ( ) // This function is needed for handling boss replacers
{
Super : : Tick ( ) ;
2009-09-15 14:16:55 +00:00
if ( tracer = = NULL | | tracer - > health < = 0 )
2008-08-23 08:48:19 +00:00
{
CALL_ACTION ( A_BossDeath , this ) ;
Destroy ( ) ;
}
}
2008-06-22 09:13:19 +00:00
} ;
2008-08-09 11:35:42 +00:00
IMPLEMENT_CLASS ( ARandomSpawner )
2008-06-22 09:13:19 +00:00