2016-03-01 15:47:10 +00:00
/*
* * a_randomspawner . cpp
* * A thing that randomly spawns one item in a list of many , before disappearing .
* * 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 .
*/
# 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 "v_text.h"
# include "doomstat.h"
# include "doomdata.h"
# define MAX_RANDOMSPAWNERS_RECURSION 32 // Should be largely more than enough, honestly.
static FRandom pr_randomspawn ( " RandomSpawn " ) ;
static bool IsMonster ( DDropItem * di )
{
const PClass * pclass = PClass : : FindClass ( di - > Name ) ;
if ( NULL = = pclass )
{
return false ;
}
return 0 ! = ( GetDefaultByType ( pclass ) - > flags3 & MF3_ISMONSTER ) ;
}
class ARandomSpawner : public AActor
{
DECLARE_CLASS ( ARandomSpawner , AActor )
// 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 ( )
{
DDropItem * di ; // di will be our drop item list iterator
DDropItem * drop ; // while drop stays as the reference point.
int n = 0 ;
bool nomonsters = ( dmflags & DF_NO_MONSTERS ) | | ( level . flags2 & LEVEL2_NOMONSTERS ) ;
Super : : BeginPlay ( ) ;
drop = di = GetDropItems ( ) ;
if ( di ! = NULL )
{
while ( di ! = NULL )
{
if ( di - > Name ! = NAME_None )
{
if ( ! nomonsters | | ! IsMonster ( di ) )
{
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.
}
di = di - > Next ;
}
}
if ( n = = 0 )
{ // Nothing left to spawn. They must have all been monsters, and monsters are disabled.
Destroy ( ) ;
return ;
}
// 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.
while ( n > - 1 & & di ! = NULL )
{
if ( di - > Name ! = NAME_None & &
( ! nomonsters | | ! IsMonster ( di ) ) )
{
n - = di - > Amount ;
if ( ( di - > Next ! = NULL ) & & ( n > - 1 ) )
di = di - > Next ;
else
n = - 1 ;
}
else
{
di = di - > Next ;
}
}
// So now we can spawn the dropped item.
if ( di = = NULL | | bouncecount > = MAX_RANDOMSPAWNERS_RECURSION ) // Prevents infinite recursions
{
Spawn ( " Unknown " , Pos ( ) , NO_REPLACE ) ; // Show that there's a problem.
Destroy ( ) ;
return ;
}
else if ( pr_randomspawn ( ) < = di - > Probability ) // prob 255 = always spawn, prob 0 = almost never spawn.
{
// Handle replacement here so as to get the proper speed and flags for missiles
PClassActor * cls ;
cls = PClass : : FindActor ( di - > Name ) ;
if ( cls ! = NULL )
{
PClassActor * rep = cls - > GetReplacement ( ) ;
if ( rep ! = NULL )
{
cls = rep ;
}
}
if ( cls ! = NULL )
{
Species = cls - > TypeName ;
AActor * defmobj = GetDefaultByType ( cls ) ;
2016-03-19 23:54:18 +00:00
this - > Speed = defmobj - > Speed ;
2016-03-01 15:47:10 +00:00
this - > flags | = ( defmobj - > flags & MF_MISSILE ) ;
this - > flags2 | = ( defmobj - > flags2 & MF2_SEEKERMISSILE ) ;
this - > flags4 | = ( defmobj - > flags4 & MF4_SPECTRAL ) ;
}
else
{
Printf ( TEXTCOLOR_RED " Unknown item class %s to drop from a random spawner \n " , di - > Name . GetChars ( ) ) ;
Species = NAME_None ;
}
}
}
}
// 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 ( )
{
Super : : PostBeginPlay ( ) ;
AActor * newmobj = NULL ;
bool boss = false ;
if ( Species = = NAME_None )
{
Destroy ( ) ;
return ;
}
PClassActor * cls = PClass : : FindActor ( Species ) ;
if ( this - > flags & MF_MISSILE & & target & & target - > target ) // Attempting to spawn a missile.
{
if ( ( tracer = = NULL ) & & ( flags2 & MF2_SEEKERMISSILE ) )
{
tracer = target - > target ;
}
2016-03-23 17:07:04 +00:00
newmobj = P_SpawnMissileXYZ ( Pos ( ) , target , target - > target , cls , false ) ;
2016-03-01 15:47:10 +00:00
}
else
{
newmobj = Spawn ( cls , Pos ( ) , NO_REPLACE ) ;
}
if ( newmobj ! = NULL )
{
// copy everything relevant
2016-03-16 11:41:26 +00:00
newmobj - > SpawnAngle = SpawnAngle ;
newmobj - > Angles = Angles ;
2016-03-22 23:53:09 +00:00
newmobj - > SpawnPoint = SpawnPoint ;
2016-03-01 15:47:10 +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 ] ;
newmobj - > special1 = special1 ;
newmobj - > special2 = special2 ;
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.
newmobj - > HandleSpawnFlags ( ) ;
newmobj - > SpawnFlags = SpawnFlags ;
newmobj - > tid = tid ;
newmobj - > AddToHash ( ) ;
2016-03-19 23:54:18 +00:00
newmobj - > Vel = Vel ;
2016-03-01 15:47:10 +00:00
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 ;
// Handle special altitude flags
if ( newmobj - > flags & MF_SPAWNCEILING )
{
2016-03-22 23:53:09 +00:00
newmobj - > SetZ ( newmobj - > ceilingz - newmobj - > Height - SpawnPoint . Z ) ;
2016-03-01 15:47:10 +00:00
}
else if ( newmobj - > flags2 & MF2_SPAWNFLOAT )
{
2016-03-20 19:55:06 +00:00
double space = newmobj - > ceilingz - newmobj - > Height - newmobj - > floorz ;
2016-03-20 18:52:35 +00:00
if ( space > 48 )
2016-03-01 15:47:10 +00:00
{
2016-03-20 18:52:35 +00:00
space - = 40 ;
newmobj - > SetZ ( ( space * pr_randomspawn ( ) ) / 256. + newmobj - > floorz + 40 ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-22 23:53:09 +00:00
newmobj - > AddZ ( SpawnPoint . Z ) ;
2016-03-01 15:47:10 +00:00
}
if ( newmobj - > flags & MF_MISSILE )
P_CheckMissileSpawn ( newmobj , 0 ) ;
// 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 ( ) - > GetReplacee ( ) ) ;
if ( rep & & ( ( rep - > flags4 & MF4_BOSSDEATH ) | | ( rep - > flags2 & MF2_BOSS ) ) )
boss = true ;
}
if ( boss )
this - > tracer = newmobj ;
else // "else" because a boss-replacing spawner must wait until it can call A_BossDeath.
Destroy ( ) ;
}
void Tick ( ) // This function is needed for handling boss replacers
{
Super : : Tick ( ) ;
if ( tracer = = NULL | | tracer - > health < = 0 )
{
A_BossDeath ( this ) ;
Destroy ( ) ;
}
}
} ;
2016-11-24 20:36:02 +00:00
IMPLEMENT_CLASS ( ARandomSpawner , false , false )