2006-02-24 04:48:15 +00:00
/*
* * info . cpp
* * Keeps track of available actors and their states
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2006-06-11 01:37:00 +00:00
* * Copyright 1998 - 2006 Randy Heit
2006-02-24 04:48:15 +00:00
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * This is completely different from Doom ' s info . c .
* *
*/
# include "info.h"
# include "m_fixed.h"
# include "c_dispatch.h"
2008-09-14 23:54:38 +00:00
# include "d_net.h"
2010-02-13 08:56:08 +00:00
# include "v_text.h"
2006-02-24 04:48:15 +00:00
# include "gi.h"
# include "actor.h"
# include "r_state.h"
# include "i_system.h"
# include "p_local.h"
2006-10-31 14:53:21 +00:00
# include "templates.h"
2007-04-22 09:06:29 +00:00
# include "cmdlib.h"
2009-06-26 17:17:52 +00:00
# include "g_level.h"
2006-02-24 04:48:15 +00:00
2008-10-25 17:38:00 +00:00
extern void LoadActors ( ) ;
2010-12-12 21:09:16 +00:00
extern void InitBotStuff ( ) ;
2010-12-14 00:50:02 +00:00
extern void ClearStrifeTypes ( ) ;
2006-02-24 04:48:15 +00:00
2012-11-10 03:36:30 +00:00
FRandom FState : : pr_statetics ( " StateTics " ) ;
2012-08-23 01:00:30 +00:00
2006-10-31 14:53:21 +00:00
//==========================================================================
//
//
//==========================================================================
2011-07-04 20:22:55 +00:00
int GetSpriteIndex ( const char * spritename , bool add )
2006-07-13 10:17:56 +00:00
{
2008-12-07 12:11:59 +00:00
static char lastsprite [ 5 ] ;
static int lastindex ;
2008-09-21 18:02:38 +00:00
// Make sure that the string is upper case and 4 characters long
2008-10-05 18:45:05 +00:00
char upper [ 5 ] = { 0 , 0 , 0 , 0 , 0 } ;
2008-09-21 18:02:38 +00:00
for ( int i = 0 ; spritename [ i ] ! = 0 & & i < 4 ; i + + )
{
upper [ i ] = toupper ( spritename [ i ] ) ;
}
2008-12-07 12:11:59 +00:00
// cache the name so if the next one is the same the function doesn't have to perform a search.
if ( ! strcmp ( upper , lastsprite ) )
{
return lastindex ;
}
strcpy ( lastsprite , upper ) ;
2006-07-13 10:17:56 +00:00
for ( unsigned i = 0 ; i < sprites . Size ( ) ; + + i )
{
2008-12-07 12:11:59 +00:00
if ( strcmp ( sprites [ i ] . name , upper ) = = 0 )
2006-07-13 10:17:56 +00:00
{
2008-12-07 12:11:59 +00:00
return ( lastindex = ( int ) i ) ;
2006-07-13 10:17:56 +00:00
}
}
2011-07-04 20:22:55 +00:00
if ( ! add )
{
return ( lastindex = - 1 ) ;
}
2006-07-13 10:17:56 +00:00
spritedef_t temp ;
2008-09-21 18:02:38 +00:00
strcpy ( temp . name , upper ) ;
2006-07-13 10:17:56 +00:00
temp . numframes = 0 ;
temp . spriteframes = 0 ;
2008-12-07 12:11:59 +00:00
return ( lastindex = ( int ) sprites . Push ( temp ) ) ;
2006-07-13 10:17:56 +00:00
}
2006-10-31 14:53:21 +00:00
//==========================================================================
//
//
//==========================================================================
2006-02-24 04:48:15 +00:00
void FActorInfo : : StaticInit ( )
{
2010-12-13 10:02:45 +00:00
sprites . Clear ( ) ;
2006-02-24 04:48:15 +00:00
if ( sprites . Size ( ) = = 0 )
{
spritedef_t temp ;
// Sprite 0 is always TNT1
memcpy ( temp . name , " TNT1 " , 5 ) ;
temp . numframes = 0 ;
temp . spriteframes = 0 ;
sprites . Push ( temp ) ;
// Sprite 1 is always ----
memcpy ( temp . name , " ---- " , 5 ) ;
sprites . Push ( temp ) ;
2010-04-19 02:46:50 +00:00
// Sprite 2 is always ####
memcpy ( temp . name , " #### " , 5 ) ;
sprites . Push ( temp ) ;
2006-02-24 04:48:15 +00:00
}
2008-10-25 17:38:00 +00:00
Printf ( " LoadActors: Load actor definitions. \n " ) ;
2010-12-14 00:50:02 +00:00
ClearStrifeTypes ( ) ;
2008-10-25 17:38:00 +00:00
LoadActors ( ) ;
2010-12-12 21:09:16 +00:00
InitBotStuff ( ) ;
2006-02-24 04:48:15 +00:00
}
2006-10-31 14:53:21 +00:00
//==========================================================================
//
2006-02-24 04:48:15 +00:00
// Called after Dehacked patches are applied
2006-10-31 14:53:21 +00:00
//
//==========================================================================
2006-02-24 04:48:15 +00:00
void FActorInfo : : StaticSetActorNums ( )
{
2006-05-10 02:40:43 +00:00
for ( unsigned int i = 0 ; i < PClass : : m_RuntimeActors . Size ( ) ; + + i )
2006-02-24 04:48:15 +00:00
{
2006-05-10 02:40:43 +00:00
PClass : : m_RuntimeActors [ i ] - > ActorInfo - > RegisterIDs ( ) ;
2006-02-24 04:48:15 +00:00
}
}
2006-10-31 14:53:21 +00:00
//==========================================================================
//
//
//==========================================================================
2006-02-24 04:48:15 +00:00
void FActorInfo : : RegisterIDs ( )
{
2010-02-13 08:56:08 +00:00
const PClass * cls = PClass : : FindClass ( Class - > TypeName ) ;
2015-04-04 22:31:15 +00:00
// Conversation IDs have never been filtered by game so we cannot start doing that.
if ( ConversationID > 0 )
{
StrifeTypes [ ConversationID ] = cls ;
if ( cls ! = Class )
{
Printf ( TEXTCOLOR_RED " Conversation ID %d refers to hidden class type '%s' \n " , SpawnID , cls - > TypeName . GetChars ( ) ) ;
}
}
2006-02-24 04:48:15 +00:00
if ( GameFilter = = GAME_Any | | ( GameFilter & gameinfo . gametype ) )
{
2012-11-09 23:53:58 +00:00
if ( SpawnID > 0 )
2006-02-24 04:48:15 +00:00
{
2010-02-13 08:56:08 +00:00
SpawnableThings [ SpawnID ] = cls ;
if ( cls ! = Class )
{
Printf ( TEXTCOLOR_RED " Spawn ID %d refers to hidden class type '%s' \n " , SpawnID , cls - > TypeName . GetChars ( ) ) ;
}
2006-02-24 04:48:15 +00:00
}
if ( DoomEdNum ! = - 1 )
{
2015-04-03 14:51:45 +00:00
FDoomEdEntry * oldent = DoomEdMap . CheckKey ( DoomEdNum ) ;
if ( oldent ! = NULL & & oldent - > Special = = - 2 )
{
Printf ( TEXTCOLOR_RED " Editor number %d defined twice for classes '%s' and '%s' \n " , DoomEdNum , cls - > TypeName . GetChars ( ) , oldent - > Type - > TypeName . GetChars ( ) ) ;
}
FDoomEdEntry ent ;
memset ( & ent , 0 , sizeof ( ent ) ) ;
ent . Type = cls ;
ent . Special = - 2 ; // use -2 instead of -1 so that we can recognize DECORATE defined entries and print a warning message if duplicates occur.
DoomEdMap . Insert ( DoomEdNum , ent ) ;
2010-02-13 08:56:08 +00:00
if ( cls ! = Class )
{
Printf ( TEXTCOLOR_RED " Editor number %d refers to hidden class type '%s' \n " , DoomEdNum , cls - > TypeName . GetChars ( ) ) ;
}
2006-02-24 04:48:15 +00:00
}
}
}
2006-10-31 14:53:21 +00:00
//==========================================================================
//
//
//==========================================================================
2009-06-26 17:17:52 +00:00
FActorInfo * FActorInfo : : GetReplacement ( bool lookskill )
2006-07-08 02:17:35 +00:00
{
2009-10-03 17:07:11 +00:00
FName skillrepname ;
if ( lookskill & & AllSkills . Size ( ) > ( unsigned ) gameskill )
2009-06-26 21:06:49 +00:00
{
2009-10-03 17:07:11 +00:00
skillrepname = AllSkills [ gameskill ] . GetReplacement ( this - > Class - > TypeName ) ;
if ( skillrepname ! = NAME_None & & PClass : : FindClass ( skillrepname ) = = NULL )
{
Printf ( " Warning: incorrect actor name in definition of skill %s: \n "
" class %s is replaced by non-existent class %s \n "
" Skill replacement will be ignored for this actor. \n " ,
AllSkills [ gameskill ] . Name . GetChars ( ) ,
this - > Class - > TypeName . GetChars ( ) , skillrepname . GetChars ( ) ) ;
AllSkills [ gameskill ] . SetReplacement ( this - > Class - > TypeName , NAME_None ) ;
AllSkills [ gameskill ] . SetReplacedBy ( skillrepname , NAME_None ) ;
lookskill = false ; skillrepname = NAME_None ;
}
2009-06-26 21:06:49 +00:00
}
2009-06-26 17:17:52 +00:00
if ( Replacement = = NULL & & ( ! lookskill | | skillrepname = = NAME_None ) )
2006-07-08 02:17:35 +00:00
{
return this ;
}
// The Replacement field is temporarily NULLed to prevent
// potential infinite recursion.
FActorInfo * savedrep = Replacement ;
Replacement = NULL ;
2009-06-26 17:17:52 +00:00
FActorInfo * rep = savedrep ;
// Handle skill-based replacement here. It has precedence on DECORATE replacement
// in that the skill replacement is applied first, followed by DECORATE replacement
// on the actor indicated by the skill replacement.
2009-06-26 21:06:49 +00:00
if ( lookskill & & ( skillrepname ! = NAME_None ) )
2009-06-26 17:17:52 +00:00
{
rep = PClass : : FindClass ( skillrepname ) - > ActorInfo ;
}
// Now handle DECORATE replacement chain
// Skill replacements are not recursive, contrarily to DECORATE replacements
rep = rep - > GetReplacement ( false ) ;
// Reset the temporarily NULLed field
2006-07-08 02:17:35 +00:00
Replacement = savedrep ;
return rep ;
}
2006-10-31 14:53:21 +00:00
//==========================================================================
//
//
//==========================================================================
2009-06-26 17:17:52 +00:00
FActorInfo * FActorInfo : : GetReplacee ( bool lookskill )
2006-07-16 15:00:10 +00:00
{
2009-10-03 17:07:11 +00:00
FName skillrepname ;
if ( lookskill & & AllSkills . Size ( ) > ( unsigned ) gameskill )
2009-09-14 19:44:14 +00:00
{
2009-11-14 08:50:23 +00:00
skillrepname = AllSkills [ gameskill ] . GetReplacedBy ( this - > Class - > TypeName ) ;
2009-10-03 17:07:11 +00:00
if ( skillrepname ! = NAME_None & & PClass : : FindClass ( skillrepname ) = = NULL )
{
Printf ( " Warning: incorrect actor name in definition of skill %s: \n "
" non-existent class %s is replaced by class %s \n "
" Skill replacement will be ignored for this actor. \n " ,
AllSkills [ gameskill ] . Name . GetChars ( ) ,
skillrepname . GetChars ( ) , this - > Class - > TypeName . GetChars ( ) ) ;
AllSkills [ gameskill ] . SetReplacedBy ( this - > Class - > TypeName , NAME_None ) ;
AllSkills [ gameskill ] . SetReplacement ( skillrepname , NAME_None ) ;
lookskill = false ;
}
2009-06-26 21:06:49 +00:00
}
2009-06-26 17:17:52 +00:00
if ( Replacee = = NULL & & ( ! lookskill | | skillrepname = = NAME_None ) )
2006-07-16 15:00:10 +00:00
{
return this ;
}
// The Replacee field is temporarily NULLed to prevent
// potential infinite recursion.
FActorInfo * savedrep = Replacee ;
Replacee = NULL ;
2009-09-14 19:44:14 +00:00
FActorInfo * rep = savedrep ;
if ( lookskill & & ( skillrepname ! = NAME_None ) & & ( PClass : : FindClass ( skillrepname ) ! = NULL ) )
{
rep = PClass : : FindClass ( skillrepname ) - > ActorInfo ;
}
rep = rep - > GetReplacee ( false ) ; Replacee = savedrep ;
2006-07-16 15:00:10 +00:00
return rep ;
}
2008-08-23 08:48:19 +00:00
//==========================================================================
//
//
//==========================================================================
void FActorInfo : : SetDamageFactor ( FName type , fixed_t factor )
{
2010-03-22 21:18:54 +00:00
if ( DamageFactors = = NULL )
2008-08-23 08:48:19 +00:00
{
2010-03-22 21:18:54 +00:00
DamageFactors = new DmgFactors ;
2008-08-23 08:48:19 +00:00
}
2010-03-22 21:18:54 +00:00
DamageFactors - > Insert ( type , factor ) ;
2008-08-23 08:48:19 +00:00
}
//==========================================================================
//
//
//==========================================================================
void FActorInfo : : SetPainChance ( FName type , int chance )
{
if ( chance > = 0 )
{
if ( PainChances = = NULL ) PainChances = new PainChanceList ;
2010-03-22 21:18:54 +00:00
PainChances - > Insert ( type , MIN ( chance , 256 ) ) ;
2008-08-23 08:48:19 +00:00
}
else
{
if ( PainChances ! = NULL )
PainChances - > Remove ( type ) ;
}
}
2006-10-31 14:53:21 +00:00
//==========================================================================
//
//
//==========================================================================
2011-06-06 22:23:43 +00:00
void FActorInfo : : SetPainFlash ( FName type , PalEntry color )
{
if ( PainFlashes = = NULL )
PainFlashes = new PainFlashList ;
PainFlashes - > Insert ( type , color ) ;
}
//==========================================================================
//
//
//==========================================================================
2012-07-21 02:58:52 +00:00
bool FActorInfo : : GetPainFlash ( FName type , PalEntry * color ) const
{
const FActorInfo * info = this ;
while ( info ! = NULL )
{
if ( info - > PainFlashes ! = NULL )
{
PalEntry * flash = info - > PainFlashes - > CheckKey ( type ) ;
if ( flash ! = NULL )
{
* color = * flash ;
return true ;
}
}
// Try parent class
info = info - > Class - > ParentClass - > ActorInfo ;
}
return false ;
}
//==========================================================================
//
//
//==========================================================================
2010-03-07 09:13:41 +00:00
void FActorInfo : : SetColorSet ( int index , const FPlayerColorSet * set )
{
if ( set ! = NULL )
{
if ( ColorSets = = NULL ) ColorSets = new FPlayerColorSetMap ;
ColorSets - > Insert ( index , * set ) ;
}
else
{
if ( ColorSets ! = NULL )
ColorSets - > Remove ( index ) ;
}
}
2012-03-23 01:20:45 +00:00
//==========================================================================
//
// DmgFactors :: CheckFactor
//
// Checks for the existance of a certain damage type. If that type does not
// exist, the damage factor for type 'None' will be returned, if present.
//
//==========================================================================
fixed_t * DmgFactors : : CheckFactor ( FName type )
{
fixed_t * pdf = CheckKey ( type ) ;
if ( pdf = = NULL & & type ! = NAME_None )
{
pdf = CheckKey ( NAME_None ) ;
}
return pdf ;
}
2008-12-16 01:23:44 +00:00
static void SummonActor ( int command , int command2 , FCommandLine argv )
2006-02-24 04:48:15 +00:00
{
if ( CheckCheatmode ( ) )
return ;
if ( argv . argc ( ) > 1 )
{
2006-05-10 02:40:43 +00:00
const PClass * type = PClass : : FindClass ( argv [ 1 ] ) ;
2006-02-24 04:48:15 +00:00
if ( type = = NULL )
{
Printf ( " Unknown class '%s' \n " , argv [ 1 ] ) ;
return ;
}
2008-12-16 01:23:44 +00:00
Net_WriteByte ( argv . argc ( ) > 2 ? command2 : command ) ;
2006-05-10 02:40:43 +00:00
Net_WriteString ( type - > TypeName . GetChars ( ) ) ;
2008-12-16 01:23:44 +00:00
2009-08-03 20:40:45 +00:00
if ( argv . argc ( ) > 2 ) {
Net_WriteWord ( atoi ( argv [ 2 ] ) ) ; // angle
if ( argv . argc ( ) > 3 ) Net_WriteWord ( atoi ( argv [ 3 ] ) ) ; // TID
else Net_WriteWord ( 0 ) ;
if ( argv . argc ( ) > 4 ) Net_WriteByte ( atoi ( argv [ 4 ] ) ) ; // special
else Net_WriteByte ( 0 ) ;
for ( int i = 5 ; i < 10 ; i + + ) { // args[5]
if ( i < argv . argc ( ) ) Net_WriteLong ( atoi ( argv [ i ] ) ) ;
else Net_WriteLong ( 0 ) ;
}
}
2006-02-24 04:48:15 +00:00
}
}
2008-12-16 01:23:44 +00:00
CCMD ( summon )
2006-02-24 04:48:15 +00:00
{
2008-12-16 01:23:44 +00:00
SummonActor ( DEM_SUMMON , DEM_SUMMON2 , argv ) ;
}
2006-02-24 04:48:15 +00:00
2008-12-16 01:23:44 +00:00
CCMD ( summonfriend )
{
SummonActor ( DEM_SUMMONFRIEND , DEM_SUMMONFRIEND2 , argv ) ;
2006-02-24 04:48:15 +00:00
}
2007-01-16 03:04:00 +00:00
2009-09-14 19:44:14 +00:00
CCMD ( summonmbf )
{
SummonActor ( DEM_SUMMONMBF , DEM_SUMMONFRIEND2 , argv ) ;
}
2007-01-16 03:04:00 +00:00
CCMD ( summonfoe )
{
2008-12-16 01:23:44 +00:00
SummonActor ( DEM_SUMMONFOE , DEM_SUMMONFOE2 , argv ) ;
2007-01-16 03:04:00 +00:00
}
2012-04-07 12:11:17 +00:00
// Damage type defaults / global settings
TMap < FName , DamageTypeDefinition > GlobalDamageDefinitions ;
2015-02-07 16:02:46 +00:00
void DamageTypeDefinition : : Apply ( FName type )
2012-04-07 12:11:17 +00:00
{
GlobalDamageDefinitions [ type ] = * this ;
}
2015-02-07 16:02:46 +00:00
DamageTypeDefinition * DamageTypeDefinition : : Get ( FName type )
2012-04-07 12:11:17 +00:00
{
return GlobalDamageDefinitions . CheckKey ( type ) ;
}
2015-02-07 16:02:46 +00:00
bool DamageTypeDefinition : : IgnoreArmor ( FName type )
2012-04-07 12:11:17 +00:00
{
DamageTypeDefinition * dtd = Get ( type ) ;
if ( dtd ) return dtd - > NoArmor ;
return false ;
}
//==========================================================================
//
// DamageTypeDefinition :: ApplyMobjDamageFactor
//
// Calculates mobj damage based on original damage, defined damage factors
// and damage type.
//
// If the specific damage type is not defined, the damage factor for
// type 'None' will be used (with 1.0 as a default value).
//
// Globally declared damage types may override or multiply the damage
// factor when 'None' is used as a fallback in this function.
//
//==========================================================================
2015-02-07 16:02:46 +00:00
int DamageTypeDefinition : : ApplyMobjDamageFactor ( int damage , FName type , DmgFactors const * const factors )
2012-04-07 12:11:17 +00:00
{
if ( factors )
{
// If the actor has named damage factors, look for a specific factor
fixed_t const * pdf = factors - > CheckKey ( type ) ;
if ( pdf ) return FixedMul ( damage , * pdf ) ; // type specific damage type
// If this was nonspecific damage, don't fall back to nonspecific search
if ( type = = NAME_None ) return damage ;
}
// If this was nonspecific damage, don't fall back to nonspecific search
else if ( type = = NAME_None )
{
return damage ;
}
else
{
// Normal is unsupplied / 1.0, so there's no difference between modifying and overriding
DamageTypeDefinition * dtd = Get ( type ) ;
return dtd ? FixedMul ( damage , dtd - > DefaultFactor ) : damage ;
}
{
fixed_t const * pdf = factors - > CheckKey ( NAME_None ) ;
2012-04-07 15:29:47 +00:00
DamageTypeDefinition * dtd = Get ( type ) ;
2012-04-07 12:11:17 +00:00
// Here we are looking for modifications to untyped damage
// If the calling actor defines untyped damage factor, that is contained in "pdf".
if ( pdf ) // normal damage available
{
if ( dtd )
{
if ( dtd - > ReplaceFactor ) return FixedMul ( damage , dtd - > DefaultFactor ) ; // use default instead of untyped factor
return FixedMul ( damage , FixedMul ( * pdf , dtd - > DefaultFactor ) ) ; // use default as modification of untyped factor
}
return FixedMul ( damage , * pdf ) ; // there was no default, so actor default is used
}
2012-04-07 15:29:47 +00:00
else if ( dtd )
{
return FixedMul ( damage , dtd - > DefaultFactor ) ; // implicit untyped factor 1.0 does not need to be applied/replaced explicitly
}
2012-04-07 12:11:17 +00:00
}
return damage ;
}