qzdoom-gpl/src/dobjtype.cpp
Christoph Oelckers 9102200771 - added: Let the kill CCMD also kill replacements of the monster that is specified.
- add a GetReplacement method to PClass to clean up some really ugly code
- Who wrote the 'kill' CCMD? The way it checked if two classes were identical was horrendously overcomplicated.

SVN r2601 (trunk)
2010-08-26 20:59:15 +00:00

581 lines
14 KiB
C++

/*
** dobjtype.cpp
** Implements the type information class
**
**---------------------------------------------------------------------------
** Copyright 1998-2008 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
*/
#include "dobject.h"
#include "i_system.h"
#include "actor.h"
#include "templates.h"
#include "autosegs.h"
#include "v_text.h"
TArray<PClass *> PClass::m_RuntimeActors;
TArray<PClass *> PClass::m_Types;
PClass *PClass::TypeHash[PClass::HASH_SIZE];
bool PClass::bShutdown;
// A harmless non-NULL FlatPointer for classes without pointers.
static const size_t TheEnd = ~(size_t)0;
static int STACK_ARGS cregcmp (const void *a, const void *b)
{
// VC++ introduces NULLs in the sequence. GCC seems to work as expected and not do it.
const ClassReg *class1 = *(const ClassReg **)a;
const ClassReg *class2 = *(const ClassReg **)b;
if (class1 == NULL) return 1;
if (class2 == NULL) return -1;
return strcmp (class1->Name, class2->Name);
}
void PClass::StaticInit ()
{
atterm (StaticShutdown);
// Sort classes by name to remove dependance on how the compiler ordered them.
REGINFO *head = &CRegHead;
REGINFO *tail = &CRegTail;
// MinGW's linker is linking the object files backwards for me now...
if (head > tail)
{
swapvalues (head, tail);
}
qsort (head + 1, tail - head - 1, sizeof(REGINFO), cregcmp);
FAutoSegIterator probe(CRegHead, CRegTail);
while (*++probe != NULL)
{
((ClassReg *)*probe)->RegisterClass ();
}
}
void PClass::StaticShutdown ()
{
TArray<size_t *> uniqueFPs(64);
unsigned int i, j;
for (i = 0; i < PClass::m_Types.Size(); ++i)
{
PClass *type = PClass::m_Types[i];
PClass::m_Types[i] = NULL;
if (type->FlatPointers != &TheEnd && type->FlatPointers != type->Pointers)
{
// FlatPointers are shared by many classes, so we must check for
// duplicates and only delete those that are unique.
for (j = 0; j < uniqueFPs.Size(); ++j)
{
if (type->FlatPointers == uniqueFPs[j])
{
break;
}
}
if (j == uniqueFPs.Size())
{
uniqueFPs.Push(const_cast<size_t *>(type->FlatPointers));
}
}
// For runtime classes, this call will also delete the PClass.
PClass::StaticFreeData (type);
}
for (i = 0; i < uniqueFPs.Size(); ++i)
{
delete[] uniqueFPs[i];
}
bShutdown = true;
}
void PClass::StaticFreeData (PClass *type)
{
if (type->Defaults != NULL)
{
M_Free(type->Defaults);
type->Defaults = NULL;
}
type->FreeStateList ();
if (type->ActorInfo != NULL)
{
if (type->ActorInfo->OwnedStates != NULL)
{
delete[] type->ActorInfo->OwnedStates;
type->ActorInfo->OwnedStates = NULL;
}
if (type->ActorInfo->DamageFactors != NULL)
{
delete type->ActorInfo->DamageFactors;
type->ActorInfo->DamageFactors = NULL;
}
if (type->ActorInfo->PainChances != NULL)
{
delete type->ActorInfo->PainChances;
type->ActorInfo->PainChances = NULL;
}
if (type->ActorInfo->ColorSets != NULL)
{
delete type->ActorInfo->ColorSets;
type->ActorInfo->ColorSets = NULL;
}
delete type->ActorInfo;
type->ActorInfo = NULL;
}
if (type->bRuntimeClass)
{
delete type;
}
else
{
type->Symbols.ReleaseSymbols();
}
}
void ClassReg::RegisterClass () const
{
assert (MyClass != NULL);
// Add type to list
MyClass->ClassIndex = PClass::m_Types.Push (MyClass);
MyClass->TypeName = FName(Name+1);
MyClass->ParentClass = ParentType;
MyClass->Size = SizeOf;
MyClass->Pointers = Pointers;
MyClass->ConstructNative = ConstructNative;
MyClass->InsertIntoHash ();
}
void PClass::InsertIntoHash ()
{
// Add class to hash table. Classes are inserted into each bucket
// in ascending order by name index.
unsigned int bucket = TypeName % HASH_SIZE;
PClass **hashpos = &TypeHash[bucket];
while (*hashpos != NULL)
{
int lexx = int(TypeName) - int((*hashpos)->TypeName);
if (lexx > 0)
{ // This type should come later in the chain
hashpos = &((*hashpos)->HashNext);
}
else if (lexx == 0)
{ // This type has already been inserted
// ... but there is no need whatsoever to make it a fatal error!
Printf (TEXTCOLOR_RED"Tried to register class '%s' more than once.\n", TypeName.GetChars());
break;
}
else
{ // Type comes right here
break;
}
}
HashNext = *hashpos;
*hashpos = this;
}
// Find a type, passed the name as a name
const PClass *PClass::FindClass (FName zaname)
{
if (zaname == NAME_None)
{
return NULL;
}
PClass *cls = TypeHash[zaname % HASH_SIZE];
while (cls != 0)
{
int lexx = int(zaname) - int(cls->TypeName);
if (lexx > 0)
{
cls = cls->HashNext;
}
else if (lexx == 0)
{
return cls->Size<0? NULL : cls;
}
else
{
break;
}
}
return NULL;
}
// Create a new object that this class represents
DObject *PClass::CreateNew () const
{
BYTE *mem = (BYTE *)M_Malloc (Size);
assert (mem != NULL);
// Set this object's defaults before constructing it.
if (Defaults != NULL)
memcpy (mem, Defaults, Size);
else
memset (mem, 0, Size);
ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this));
return (DObject *)mem;
}
// Create a new class based on an existing class
PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
{
assert (size >= Size);
PClass *type;
bool notnew;
const PClass *existclass = FindClass(name);
// This is a placeholder so fill it in
if (existclass != NULL && existclass->Size == (unsigned)-1)
{
type = const_cast<PClass*>(existclass);
if (!IsDescendantOf(type->ParentClass))
{
I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars());
}
DPrintf("Defining placeholder class %s\n", name.GetChars());
notnew = true;
}
else
{
type = new PClass;
notnew = false;
}
type->TypeName = name;
type->ParentClass = this;
type->Size = size;
type->Pointers = NULL;
type->ConstructNative = ConstructNative;
if (!notnew)
{
type->ClassIndex = m_Types.Push (type);
}
type->Meta = Meta;
// Set up default instance of the new class.
type->Defaults = (BYTE *)M_Malloc(size);
memcpy (type->Defaults, Defaults, Size);
if (size > Size)
{
memset (type->Defaults + Size, 0, size - Size);
}
type->FlatPointers = NULL;
type->bRuntimeClass = true;
type->ActorInfo = NULL;
type->Symbols.SetParentTable (&this->Symbols);
if (!notnew) type->InsertIntoHash();
// If this class has an actor info, then any classes derived from it
// also need an actor info.
if (this->ActorInfo != NULL)
{
FActorInfo *info = type->ActorInfo = new FActorInfo;
info->Class = type;
info->GameFilter = GAME_Any;
info->SpawnID = 0;
info->DoomEdNum = -1;
info->OwnedStates = NULL;
info->NumOwnedStates = 0;
info->Replacement = NULL;
info->Replacee = NULL;
info->StateList = NULL;
info->DamageFactors = NULL;
info->PainChances = NULL;
info->ColorSets = NULL;
m_RuntimeActors.Push (type);
}
return type;
}
// Add <extension> bytes to the end of this class. Returns the
// previous size of the class.
unsigned int PClass::Extend(unsigned int extension)
{
assert(this->bRuntimeClass);
unsigned int oldsize = Size;
Size += extension;
Defaults = (BYTE *)M_Realloc(Defaults, Size);
memset(Defaults + oldsize, 0, extension);
return oldsize;
}
// Like FindClass but creates a placeholder if no class
// is found. CreateDerivedClass will automatcally fill in
// the placeholder when the actual class is defined.
const PClass *PClass::FindClassTentative (FName name)
{
if (name == NAME_None)
{
return NULL;
}
PClass *cls = TypeHash[name % HASH_SIZE];
while (cls != 0)
{
int lexx = int(name) - int(cls->TypeName);
if (lexx > 0)
{
cls = cls->HashNext;
}
else if (lexx == 0)
{
return cls;
}
else
{
break;
}
}
PClass *type = new PClass;
DPrintf("Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars());
type->TypeName = name;
type->ParentClass = this;
type->Size = -1;
type->Pointers = NULL;
type->ConstructNative = NULL;
type->ClassIndex = m_Types.Push (type);
type->Defaults = NULL;
type->FlatPointers = NULL;
type->bRuntimeClass = true;
type->ActorInfo = NULL;
type->InsertIntoHash();
return type;
}
// This is used by DECORATE to assign ActorInfos to internal classes
void PClass::InitializeActorInfo ()
{
Symbols.SetParentTable (&ParentClass->Symbols);
Defaults = (BYTE *)M_Malloc(Size);
if (ParentClass->Defaults != NULL)
{
memcpy (Defaults, ParentClass->Defaults, ParentClass->Size);
if (Size > ParentClass->Size)
{
memset (Defaults + ParentClass->Size, 0, Size - ParentClass->Size);
}
}
else
{
memset (Defaults, 0, Size);
}
FActorInfo *info = ActorInfo = new FActorInfo;
info->Class = this;
info->GameFilter = GAME_Any;
info->SpawnID = 0;
info->DoomEdNum = -1;
info->OwnedStates = NULL;
info->NumOwnedStates = 0;
info->Replacement = NULL;
info->Replacee = NULL;
info->StateList = NULL;
info->DamageFactors = NULL;
info->PainChances = NULL;
info->ColorSets = NULL;
m_RuntimeActors.Push (this);
}
// Create the FlatPointers array, if it doesn't exist already.
// It comprises all the Pointers from superclasses plus this class's own Pointers.
// If this class does not define any new Pointers, then FlatPointers will be set
// to the same array as the super class's.
void PClass::BuildFlatPointers ()
{
if (FlatPointers != NULL)
{ // Already built: Do nothing.
return;
}
else if (ParentClass == NULL)
{ // No parent: FlatPointers is the same as Pointers.
if (Pointers == NULL)
{ // No pointers: Make FlatPointers a harmless non-NULL.
FlatPointers = &TheEnd;
}
else
{
FlatPointers = Pointers;
}
}
else
{
ParentClass->BuildFlatPointers ();
if (Pointers == NULL)
{ // No new pointers: Just use the same FlatPointers as the parent.
FlatPointers = ParentClass->FlatPointers;
}
else
{ // New pointers: Create a new FlatPointers array and add them.
int numPointers, numSuperPointers;
// Count pointers defined by this class.
for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++)
{ }
// Count pointers defined by superclasses.
for (numSuperPointers = 0; ParentClass->FlatPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++)
{ }
// Concatenate them into a new array
size_t *flat = new size_t[numPointers + numSuperPointers + 1];
if (numSuperPointers > 0)
{
memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers);
}
memcpy (flat + numSuperPointers, Pointers, sizeof(size_t)*(numPointers+1));
FlatPointers = flat;
}
}
}
void PClass::FreeStateList ()
{
if (ActorInfo != NULL && ActorInfo->StateList != NULL)
{
ActorInfo->StateList->Destroy();
M_Free (ActorInfo->StateList);
ActorInfo->StateList = NULL;
}
}
const PClass *PClass::NativeClass() const
{
const PClass *cls = this;
while (cls && cls->bRuntimeClass)
cls = cls->ParentClass;
return cls;
}
PClass *PClass::GetReplacement() const
{
return ActorInfo->GetReplacement()->Class;
}
// Symbol tables ------------------------------------------------------------
PSymbol::~PSymbol()
{
}
PSymbolTable::~PSymbolTable ()
{
ReleaseSymbols();
}
void PSymbolTable::ReleaseSymbols()
{
for (unsigned int i = 0; i < Symbols.Size(); ++i)
{
delete Symbols[i];
}
Symbols.Clear();
}
void PSymbolTable::SetParentTable (PSymbolTable *parent)
{
ParentSymbolTable = parent;
}
PSymbol *PSymbolTable::FindSymbol (FName symname, bool searchparents) const
{
int min, max;
min = 0;
max = (int)Symbols.Size() - 1;
while (min <= max)
{
unsigned int mid = (min + max) / 2;
PSymbol *sym = Symbols[mid];
if (sym->SymbolName == symname)
{
return sym;
}
else if (sym->SymbolName < symname)
{
min = mid + 1;
}
else
{
max = mid - 1;
}
}
if (searchparents && ParentSymbolTable != NULL)
{
return ParentSymbolTable->FindSymbol (symname, true);
}
return NULL;
}
PSymbol *PSymbolTable::AddSymbol (PSymbol *sym)
{
// Insert it in sorted order.
int min, max, mid;
min = 0;
max = (int)Symbols.Size() - 1;
while (min <= max)
{
mid = (min + max) / 2;
PSymbol *tsym = Symbols[mid];
if (tsym->SymbolName == sym->SymbolName)
{ // A symbol with this name already exists in the table
return NULL;
}
else if (tsym->SymbolName < sym->SymbolName)
{
min = mid + 1;
}
else
{
max = mid - 1;
}
}
// Good. The symbol is not in the table yet.
Symbols.Insert (MAX(min, max), sym);
return sym;
}