diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 02a4ff0e5c..49c06acbc9 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -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. diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 2bd670b08c..f4a7b5b298 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -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(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 () { diff --git a/src/dobjtype.h b/src/dobjtype.h index de51c380fd..98092d60a2 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -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 m_Types; static TArray m_RuntimeActors; diff --git a/src/p_states.cpp b/src/p_states.cpp index 6d132c92f4..68045b66bf 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -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; diff --git a/src/sc_man.cpp b/src/sc_man.cpp index 7a301584e5..9de966fd13 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -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; diff --git a/src/sc_man.h b/src/sc_man.h index f647869997..c74fca9428 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -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; + } }; diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 83fba3ef34..811bbc0367 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -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(); } diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index d843cc15ff..d2d4d9214f 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -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(size_t(int(*this))); - } - fuglyname &operator= (const PClass *foo) - { - FName *p = this; - *p = ENamedName(reinterpret_cast(foo)); - return *this; - } -}; - - //========================================================================== // // State parser diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 8b0391584d..f73e0e4c2d 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -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) { diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index b8336c2987..ddbf3851f4 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -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(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(defaults)->PickupFlash = fuglyname("PickupFlash"); + static_cast(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; } //========================================================================== diff --git a/src/thingdef/thingdef_type.h b/src/thingdef/thingdef_type.h index d09dbb520e..721cab61f9 100644 --- a/src/thingdef/thingdef_type.h +++ b/src/thingdef/thingdef_type.h @@ -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