- Made several DECORATE errors which do not involve parsing non-fatal.

- Added a static error counter to FScriptPosition class.
- Changed initialization of actor class type properties: fuglyname is gone as
  is the postprocessing in FinishThingdef. Instead an empty placeholder class
  is now created when a class is first referenced and this placeholder is later
  filled in. 
- Added option to replace backslash with '^' in state frame definitions because
  the backslash is just causing too many problems because it's also an escape
  character.


SVN r1334 (trunk)
This commit is contained in:
Christoph Oelckers 2008-12-29 23:03:38 +00:00
parent 8f686a5e02
commit f8c38e5f54
12 changed files with 211 additions and 186 deletions

View file

@ -1,3 +1,14 @@
December 30, 2008 (Changes by Graf Zahl)
- Made several DECORATE errors which do not involve parsing non-fatal.
- Added a static error counter to FScriptPosition class.
- Changed initialization of actor class type properties: fuglyname is gone as
is the postprocessing in FinishThingdef. Instead an empty placeholder class
is now created when a class is first referenced and this placeholder is later
filled in.
- Added option to replace backslash with '^' in state frame definitions because
the backslash is just causing too many problems because it's also an escape
character.
December 28, 2008 (Changes by Graf Zahl)
- Added Karate Chris's new DMFlags submission.

View file

@ -216,7 +216,7 @@ const PClass *PClass::FindClass (FName zaname)
}
else if (lexx == 0)
{
return cls;
return cls->Size<0? NULL : cls;
}
else
{
@ -247,15 +247,34 @@ DObject *PClass::CreateNew () const
PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
{
assert (size >= Size);
PClass *type;
bool notnew;
PClass *type = new PClass;
const PClass *existclass = FindClass(name);
// This is a placeholder so fill it in
if (existclass != NULL && existclass->Size == -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());
}
Printf("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;
type->ClassIndex = m_Types.Push (type);
if (!notnew) type->ClassIndex = m_Types.Push (type);
type->Meta = Meta;
type->Defaults = new BYTE[size];
memcpy (type->Defaults, Defaults, Size);
@ -267,7 +286,7 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
type->bRuntimeClass = true;
type->ActorInfo = NULL;
type->Symbols.SetParentTable (&this->Symbols);
type->InsertIntoHash();
if (!notnew) type->InsertIntoHash();
// If this class has an actor info, then any classes derived from it
// also need an actor info.
@ -290,6 +309,51 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
return type;
}
// 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;
Printf("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 ()
{

View file

@ -169,6 +169,7 @@ struct PClass
static const PClass *FindClass (const FString &name) { return FindClass (FName (name, true)); }
static const PClass *FindClass (ENamedName name) { return FindClass (FName (name)); }
static const PClass *FindClass (FName name);
const PClass *FindClassTentative (FName name); // not static!
static TArray<PClass *> m_Types;
static TArray<PClass *> m_RuntimeActors;

View file

@ -826,8 +826,14 @@ bool FStateDefinitions::AddStates(FState *state, const char *framechars)
bool error = false;
while (*framechars)
{
int frame=((*framechars++)&223)-'A';
int frame;
if (*framechars == '^')
frame = '\\'-'A';
else
frame = ((*framechars)&223)-'A';
framechars++;
if (frame<0 || frame>28)
{
frame = 0;

View file

@ -1030,6 +1030,7 @@ void FScanner::CheckOpen()
// a class that remembers a parser position
//
//==========================================================================
int FScriptPosition::ErrorCounter;
FScriptPosition::FScriptPosition(const FScriptPosition &other)
{
@ -1092,6 +1093,7 @@ void STACK_ARGS FScriptPosition::Message (int severity, const char *message, ...
break;
case MSG_ERROR:
ErrorCounter++;
type = "error";
break;

View file

@ -238,6 +238,7 @@ enum
struct FScriptPosition
{
static int ErrorCounter;
FString FileName;
int ScriptLine;
@ -250,6 +251,10 @@ struct FScriptPosition
FScriptPosition(FScanner &sc);
FScriptPosition &operator=(const FScriptPosition &other);
void Message(int severity, const char *message,...) const;
static void ResetErrorCounter()
{
ErrorCounter = 0;
}
};

View file

@ -184,7 +184,8 @@ void SetReplacement(FActorInfo *info, FName replaceName)
void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag)
{
AActor *defaults = (AActor*)info->Class->Defaults;
PClass *ti = info->Class;
AActor *defaults = (AActor*)ti->Defaults;
try
{
@ -192,7 +193,9 @@ void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag)
}
catch (CRecoverableError &err)
{
sc.Message(MSG_FATAL, "%s", err.GetMessage());
sc.Message(MSG_ERROR, "%s", err.GetMessage());
bag.statedef.MakeStateDefines(NULL);
return;
}
bag.statedef.InstallStates (info, defaults);
bag.statedef.MakeStateDefines(NULL);
@ -200,69 +203,78 @@ void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag)
{
if (bag.DropItemList == NULL)
{
if (info->Class->Meta.GetMetaInt (ACMETA_DropItems) != 0)
if (ti->Meta.GetMetaInt (ACMETA_DropItems) != 0)
{
info->Class->Meta.SetMetaInt (ACMETA_DropItems, 0);
ti->Meta.SetMetaInt (ACMETA_DropItems, 0);
}
}
else
{
info->Class->Meta.SetMetaInt (ACMETA_DropItems,
ti->Meta.SetMetaInt (ACMETA_DropItems,
StoreDropItemChain(bag.DropItemList));
}
}
if (info->Class->IsDescendantOf (RUNTIME_CLASS(AInventory)))
if (ti->IsDescendantOf (RUNTIME_CLASS(AInventory)))
{
defaults->flags |= MF_SPECIAL;
}
// Weapons must be checked for all relevant states. They may crash the game otherwise.
if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
FState * ready = ti->ActorInfo->FindState(NAME_Ready);
FState * select = ti->ActorInfo->FindState(NAME_Select);
FState * deselect = ti->ActorInfo->FindState(NAME_Deselect);
FState * fire = ti->ActorInfo->FindState(NAME_Fire);
// Consider any weapon without any valid state abstract and don't output a warning
// This is for creating base classes for weapon groups that only set up some properties.
if (ready || select || deselect || fire)
{
if (!ready)
{
sc.Message(MSG_ERROR, "Weapon %s doesn't define a ready state.\n", ti->TypeName.GetChars());
}
if (!select)
{
sc.Message(MSG_ERROR, "Weapon %s doesn't define a select state.\n", ti->TypeName.GetChars());
}
if (!deselect)
{
sc.Message(MSG_ERROR, "Weapon %s doesn't define a deselect state.\n", ti->TypeName.GetChars());
}
if (!fire)
{
sc.Message(MSG_ERROR, "Weapon %s doesn't define a fire state.\n", ti->TypeName.GetChars());
}
}
}
}
//==========================================================================
//
// Do some postprocessing after everything has been defined
// This also processes all the internal actors to adjust the type
// fields in the weapons
//
//==========================================================================
static int ResolvePointer(const PClass **pPtr, const PClass *owner, const PClass *destclass, const char *description)
{
fuglyname v;
v = *pPtr;
if (v != NAME_None && v.IsValidName())
{
*pPtr = PClass::FindClass(v);
if (!*pPtr)
{
Printf("Unknown %s '%s' in '%s'\n", description, v.GetChars(), owner->TypeName.GetChars());
return 1;
}
else if (!(*pPtr)->IsDescendantOf(destclass))
{
*pPtr = NULL;
Printf("Invalid %s '%s' in '%s'\n", description, v.GetChars(), owner->TypeName.GetChars());
return 1;
}
}
return 0;
}
static void FinishThingdef()
{
unsigned int i;
int errorcount;
int errorcount = StateParams.ResolveAll();
errorcount = StateParams.ResolveAll();
for (i = 0;i < PClass::m_Types.Size(); i++)
for (unsigned i = 0;i < PClass::m_Types.Size(); i++)
{
PClass * ti = PClass::m_Types[i];
// Skip non-actors
if (!ti->IsDescendantOf(RUNTIME_CLASS(AActor))) continue;
if (ti->Size == -1)
{
Printf("Class %s referenced but not defined\n", ti->TypeName.GetChars());
errorcount++;
continue;
}
AActor *def = GetDefaultByType(ti);
if (!def)
@ -271,100 +283,6 @@ static void FinishThingdef()
errorcount++;
continue;
}
// Friendlies never count as kills!
if (def->flags & MF_FRIENDLY)
{
def->flags &=~MF_COUNTKILL;
}
if (ti->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{
AInventory * defaults=(AInventory *)def;
errorcount += ResolvePointer(&defaults->PickupFlash, ti, RUNTIME_CLASS(AActor), "pickup flash");
}
if (ti->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)) && ti != RUNTIME_CLASS(APowerupGiver))
{
FString typestr;
APowerupGiver * defaults=(APowerupGiver *)def;
fuglyname v;
v = defaults->PowerupType;
if (v != NAME_None && v.IsValidName())
{
typestr.Format ("Power%s", v.GetChars());
const PClass * powertype=PClass::FindClass(typestr);
if (!powertype) powertype=PClass::FindClass(v.GetChars());
if (!powertype)
{
Printf("Unknown powerup type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
else if (!powertype->IsDescendantOf(RUNTIME_CLASS(APowerup)))
{
Printf("Invalid powerup type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
else
{
defaults->PowerupType=powertype;
}
}
else if (v == NAME_None)
{
Printf("No powerup type specified in '%s'\n", ti->TypeName.GetChars());
errorcount++;
}
}
// the typeinfo properties of weapons have to be fixed here after all actors have been declared
if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
AWeapon * defaults=(AWeapon *)def;
errorcount += ResolvePointer(&defaults->AmmoType1, ti, RUNTIME_CLASS(AAmmo), "ammo type");
errorcount += ResolvePointer(&defaults->AmmoType2, ti, RUNTIME_CLASS(AAmmo), "ammo type");
errorcount += ResolvePointer(&defaults->SisterWeaponType, ti, RUNTIME_CLASS(AWeapon), "sister weapon type");
FState * ready = ti->ActorInfo->FindState(NAME_Ready);
FState * select = ti->ActorInfo->FindState(NAME_Select);
FState * deselect = ti->ActorInfo->FindState(NAME_Deselect);
FState * fire = ti->ActorInfo->FindState(NAME_Fire);
// Consider any weapon without any valid state abstract and don't output a warning
// This is for creating base classes for weapon groups that only set up some properties.
if (ready || select || deselect || fire)
{
// Do some consistency checks. If these states are undefined the weapon cannot work!
if (!ready)
{
Printf("Weapon %s doesn't define a ready state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!select)
{
Printf("Weapon %s doesn't define a select state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!deselect)
{
Printf("Weapon %s doesn't define a deselect state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!fire)
{
Printf("Weapon %s doesn't define a fire state.\n", ti->TypeName.GetChars());
errorcount++;
}
}
}
// same for the weapon type of weapon pieces.
else if (ti->IsDescendantOf(RUNTIME_CLASS(AWeaponPiece)))
{
AWeaponPiece * defaults=(AWeaponPiece *)def;
errorcount += ResolvePointer(&defaults->WeaponClass, ti, RUNTIME_CLASS(AWeapon), "weapon type");
}
}
if (errorcount > 0)
{
@ -378,7 +296,6 @@ static void FinishThingdef()
mysnprintf(fmt, countof(fmt), "QuestItem%d", i+1);
QuestItemClasses[i] = PClass::FindClass(fmt);
}
}
@ -395,6 +312,7 @@ void LoadActors ()
{
int lastlump, lump;
FScriptPosition::ResetErrorCounter();
InitThingdef();
lastlump = 0;
while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1)
@ -402,6 +320,10 @@ void LoadActors ()
FScanner sc(lump);
ParseDecorate (sc);
}
if (FScriptPosition::ErrorCounter > 0)
{
I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter);
}
FinishThingdef();
}

View file

@ -27,31 +27,6 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2);
void HandleDeprecatedFlags(AActor *defaults, FActorInfo *info, bool set, int index);
//==========================================================================
//
// This class is for storing a name inside a const PClass* field without
// generating compiler warnings. It does not manipulate data in any other
// way.
//
//==========================================================================
class fuglyname : public FName
{
public:
fuglyname() : FName() {}
fuglyname(const char *foo) : FName(foo) {}
operator const PClass *()
{
return reinterpret_cast<const PClass *>(size_t(int(*this)));
}
fuglyname &operator= (const PClass *foo)
{
FName *p = this;
*p = ENamedName(reinterpret_cast<size_t>(foo));
return *this;
}
};
//==========================================================================
//
// State parser

View file

@ -62,6 +62,12 @@ struct FCompileContext
bool lax;
bool isconst;
FCompileContext(const PClass *_cls = NULL, bool _lax = false, bool _isconst = false)
{
cls = _cls;
lax = _lax;
isconst = _isconst;
}
PSymbol *FindInClass(FName identifier)
{

View file

@ -232,7 +232,7 @@ static ExpVal GetVariableValue (void *address, FExpressionType &type)
ExpVal FxExpression::EvalExpression (AActor *self)
{
I_Error("Unresolved expression found");
ScriptPosition.Message(MSG_ERROR, "Unresolved expression found");
ExpVal val;
val.Type = VAL_Int;
@ -2635,23 +2635,27 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
}
if (scope != NULL)
{
FState *destination = NULL;
// If the label is class specific we can resolve it right here
if (scope->ActorInfo == NULL)
if (names[1] != NAME_None)
{
ScriptPosition.Message(MSG_ERROR, "'%s' has no actorinfo", names[0].GetChars());
delete this;
return NULL;
}
FState *destination = scope->ActorInfo->FindState(names.Size()-1, &names[1], false);
if (destination == NULL)
{
ScriptPosition.Message(ctx.lax? MSG_WARNING:MSG_ERROR, "Unknown state jump destination");
if (!ctx.lax)
if (scope->ActorInfo == NULL)
{
ScriptPosition.Message(MSG_ERROR, "'%s' has no actorinfo", names[0].GetChars());
delete this;
return NULL;
}
return this;
destination = scope->ActorInfo->FindState(names.Size()-1, &names[1], false);
if (destination == NULL)
{
ScriptPosition.Message(ctx.lax? MSG_WARNING:MSG_ERROR, "Unknown state jump destination");
if (!ctx.lax)
{
delete this;
return NULL;
}
return this;
}
}
FxExpression *x = new FxConstant(destination, ScriptPosition);
delete this;
@ -2707,7 +2711,7 @@ FStateExpressions StateParams;
FStateExpressions::~FStateExpressions()
{
for(int i=0; i<Size(); i++)
for(unsigned i=0; i<Size(); i++)
{
if (expressions[i].expr != NULL && !expressions[i].cloned)
{
@ -2797,7 +2801,7 @@ int FStateExpressions::ResolveAll()
FCompileContext ctx;
ctx.lax = true;
for(int i=0; i<Size(); i++)
for(unsigned i=0; i<Size(); i++)
{
if (expressions[i].cloned)
{
@ -2822,7 +2826,7 @@ int FStateExpressions::ResolveAll()
}
}
for(int i=0; i<Size(); i++)
for(unsigned i=0; i<Size(); i++)
{
if (expressions[i].expr != NULL)
{

View file

@ -69,6 +69,25 @@
#include "colormatcher.h"
#include "autosegs.h"
//==========================================================================
//
// Gets a class pointer and performs an error check for correct type
//
//==========================================================================
static const PClass *FindClassTentative(const char *name, const char *ancestor)
{
const PClass *anc = PClass::FindClass(ancestor);
assert(anc != NULL); // parent classes used here should always be natively defined
const PClass *cls = const_cast<PClass*>(anc)->FindClassTentative(name);
assert (cls != NULL); // cls can not ne NULL here
if (!cls->IsDescendantOf(anc))
{
I_Error("%s does not inherit from %s\n", name, ancestor);
}
return cls;
}
//===========================================================================
//
// HandleDeprecatedFlags
@ -107,7 +126,7 @@ void HandleDeprecatedFlags(AActor *defaults, FActorInfo *info, bool set, int ind
case DEPF_PICKUPFLASH:
if (set)
{
static_cast<AInventory*>(defaults)->PickupFlash = fuglyname("PickupFlash");
static_cast<AInventory*>(defaults)->PickupFlash = FindClassTentative("PickupFlash", "Actor");
}
else
{
@ -1160,7 +1179,7 @@ DEFINE_CLASS_PROPERTY(defmaxamount, 0, Inventory)
DEFINE_CLASS_PROPERTY(pickupflash, S, Inventory)
{
PROP_STRING_PARM(str, 0);
defaults->PickupFlash = fuglyname(str);
defaults->PickupFlash = FindClassTentative(str, "Actor");
}
//==========================================================================
@ -1270,7 +1289,7 @@ DEFINE_CLASS_PROPERTY(ammogive2, I, Weapon)
DEFINE_CLASS_PROPERTY(ammotype, S, Weapon)
{
PROP_STRING_PARM(str, 0);
defaults->AmmoType1 = fuglyname(str);
defaults->AmmoType1 = FindClassTentative(str, "Ammo");
}
//==========================================================================
@ -1279,7 +1298,7 @@ DEFINE_CLASS_PROPERTY(ammotype, S, Weapon)
DEFINE_CLASS_PROPERTY(ammotype1, S, Weapon)
{
PROP_STRING_PARM(str, 0);
defaults->AmmoType1 = fuglyname(str);
defaults->AmmoType1 = FindClassTentative(str, "Ammo");
}
//==========================================================================
@ -1288,7 +1307,7 @@ DEFINE_CLASS_PROPERTY(ammotype1, S, Weapon)
DEFINE_CLASS_PROPERTY(ammotype2, S, Weapon)
{
PROP_STRING_PARM(str, 0);
defaults->AmmoType2 = fuglyname(str);
defaults->AmmoType2 = FindClassTentative(str, "Ammo");
}
//==========================================================================
@ -1359,7 +1378,7 @@ DEFINE_CLASS_PROPERTY(selectionorder, I, Weapon)
DEFINE_CLASS_PROPERTY(sisterweapon, S, Weapon)
{
PROP_STRING_PARM(str, 0);
defaults->SisterWeaponType = fuglyname(str);
defaults->SisterWeaponType = FindClassTentative(str, "Weapon");
}
//==========================================================================
@ -1395,7 +1414,7 @@ DEFINE_CLASS_PROPERTY(number, I, WeaponPiece)
DEFINE_CLASS_PROPERTY(weapon, S, WeaponPiece)
{
PROP_STRING_PARM(str, 0);
defaults->WeaponClass = fuglyname(str);
defaults->WeaponClass = FindClassTentative(str, "Weapon");
}
//==========================================================================
@ -1505,7 +1524,18 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, mode, S, PowerupGiver)
DEFINE_CLASS_PROPERTY_PREFIX(powerup, type, S, PowerupGiver)
{
PROP_STRING_PARM(str, 0);
defaults->PowerupType = fuglyname(str);
// Yuck! What was I thinking when I decided to prepend "Power" to the name?
// Now it's too late to change it...
const PClass *cls = PClass::FindClass(str);
if (cls == NULL || !cls->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)))
{
FString st;
st.Format("%s%s", strnicmp(str, "power", 5)? "Power" : "", str);
cls = FindClassTentative(st, "Powerup");
}
defaults->PowerupType = cls;
}
//==========================================================================

View file

@ -17,7 +17,6 @@ enum ExpValType
VAL_Pointer, // Dereferenced variable (only used for addressing arrays for now.)
VAL_Sound, // Sound identifier. Internally it's an int.
VAL_Name, // A Name
VAL_MultiName, // Multiple names for multi-label states
VAL_Color, // A color.
VAL_State, // A State pointer