diff --git a/src/dobject.cpp b/src/dobject.cpp index aa37361475..6c9afb8848 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -456,9 +456,7 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld, bool s auto def = GetDefaultByType(p); if (def != nullptr) { - def->Class = p; def->DObject::PointerSubstitution(old, notOld); - def->Class = nullptr; // reset pointer. Defaults should not have a valid class pointer. } } } diff --git a/src/dobject.h b/src/dobject.h index b373803420..3e8801d404 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -208,7 +208,6 @@ enum EObjectFlags OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk) - OF_SuperCall = 1 << 12, // A super call from the VM is about to be performed }; template class TObjPtr; @@ -453,6 +452,8 @@ public: void *ScriptVar(FName field, PType *type); +protected: + public: DObject (); DObject (PClass *inClass); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 6e42e29195..75513c5b53 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -1782,6 +1782,18 @@ PClassPointer::PClassPointer(PClass *restrict) else mDescriptiveName = "ClassPointer"; } +//========================================================================== +// +// PClassPointer - isCompatible +// +//========================================================================== + +bool PClassPointer::isCompatible(PType *type) +{ + auto other = dyn_cast(type); + return (other != nullptr && other->ClassRestriction->IsDescendantOf(ClassRestriction)); +} + //========================================================================== // // PClassPointer :: IsMatch @@ -3333,6 +3345,20 @@ void PClass::InitializeDefaults() { assert(Defaults == NULL); Defaults = (BYTE *)M_Malloc(Size); + + // run the constructor on the defaults to set the vtbl pointer which is needed to run class-aware functions on them. + // bSerialOverride prevents linking into the thinker chains. + auto s = DThinker::bSerialOverride; + DThinker::bSerialOverride = true; + ConstructNative(Defaults); + DThinker::bSerialOverride = s; + // We must unlink the defaults from the class list because it's just a static block of data to the engine. + DObject *optr = (DObject*)Defaults; + GC::Root = optr->ObjNext; + optr->ObjNext = nullptr; + optr->SetClass(this); + + if (ParentClass->Defaults != NULL) { memcpy(Defaults, ParentClass->Defaults, ParentClass->Size); diff --git a/src/dobjtype.h b/src/dobjtype.h index dd240e0718..a7ce429bbc 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -599,6 +599,7 @@ public: // this is only here to block PPointer's implementation void SetPointer(void *base, unsigned offset, TArray *special = NULL) const override {} + bool isCompatible(PType *type); virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; diff --git a/src/g_game.cpp b/src/g_game.cpp index 77e675b8a8..adde5ec485 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1263,10 +1263,11 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags) // Strip all current powers, unless moving in a hub and the power is okay to keep. item = p->mo->Inventory; + auto ptype = PClass::FindActor(NAME_Powerup); while (item != NULL) { next = item->Inventory; - if (item->IsKindOf (RUNTIME_CLASS(APowerup))) + if (item->IsKindOf (ptype)) { if (deathmatch || ((mode != FINISH_SameHub || !(item->ItemFlags & IF_HUBPOWER)) && !(item->ItemFlags & IF_PERSISTENTPOWER))) // Keep persistent powers in non-deathmatch games diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp index fbceba95ce..15895eaa4f 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -48,35 +48,6 @@ IMPLEMENT_CLASS(APowerup, false, false) // Powerup-Giver ------------------------------------------------------------- -IMPLEMENT_CLASS(APowerupGiver, false, true) - -IMPLEMENT_POINTERS_START(APowerupGiver) -IMPLEMENT_POINTER(PowerupType) -IMPLEMENT_POINTERS_END - -DEFINE_FIELD(APowerupGiver, PowerupType) -DEFINE_FIELD(APowerupGiver, EffectTics) -DEFINE_FIELD(APowerupGiver, BlendColor) -DEFINE_FIELD(APowerupGiver, Mode) -DEFINE_FIELD(APowerupGiver, Strength) - -//=========================================================================== -// -// APowerupGiver :: Serialize -// -//=========================================================================== - -void APowerupGiver::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - auto def = (APowerupGiver*)GetDefault(); - arc("poweruptype", PowerupType, def->PowerupType) - ("effecttics", EffectTics, def->EffectTics) - ("blendcolor", BlendColor, def->BlendColor) - ("mode", Mode, def->Mode) - ("strength", Strength, def->Strength); -} - // Powerup ------------------------------------------------------------------- DEFINE_FIELD(APowerup, EffectTics) @@ -101,4 +72,3 @@ void APowerup::Serialize(FSerializer &arc) ("strength", Strength, def->Strength) ("colormap", Colormap, def->Colormap); } - diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h index db9429858d..84c18505bb 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -23,22 +23,4 @@ public: friend void EndAllPowerupEffects(AInventory *item); friend void InitAllPowerupEffects(AInventory *item); }; - -// An artifact is an item that gives the player a powerup when activated. -class APowerupGiver : public AInventory -{ - DECLARE_CLASS (APowerupGiver, AInventory) - HAS_OBJECT_POINTERS -public: - virtual void Serialize(FSerializer &arc) override; - - - PClassActor *PowerupType; - int EffectTics; // Non-0 to override the powerup's default tics - PalEntry BlendColor; // Non-0 to override the powerup's default blend - FNameNoInit Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility - double Strength; // Meaning depends on powerup - currently used only by Invisibility -}; - - #endif //__A_ARTIFACTS_H__ diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 40e6e420b6..12b359a14d 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -594,11 +594,12 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor void EndAllPowerupEffects(AInventory *item) { + auto ptype = PClass::FindActor(NAME_Powerup); while (item != NULL) { - if (item->IsKindOf(RUNTIME_CLASS(APowerup))) + if (item->IsKindOf(ptype)) { - IFVIRTUALPTR(item, APowerup, EndEffect) + IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) { VMValue params[1] = { item }; VMFrameStack stack; @@ -619,11 +620,12 @@ void EndAllPowerupEffects(AInventory *item) void InitAllPowerupEffects(AInventory *item) { + auto ptype = PClass::FindActor(NAME_Powerup); while (item != NULL) { - if (item->IsKindOf(RUNTIME_CLASS(APowerup))) + if (item->IsKindOf(ptype)) { - IFVIRTUALPTR(item, APowerup, InitEffect) + IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) { VMValue params[1] = { item }; VMFrameStack stack; diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index f78c610404..4cb587bc9a 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -1158,10 +1158,10 @@ class CommandDrawNumber : public CommandDrawString if(!parenthesized || !sc.CheckToken(TK_StringConst)) sc.MustGetToken(TK_Identifier); inventoryItem = PClass::FindActor(sc.String); - if(inventoryItem == NULL || !RUNTIME_CLASS(APowerupGiver)->IsAncestorOf(inventoryItem)) + if(inventoryItem == NULL || !PClass::FindActor(NAME_PowerupGiver)->IsAncestorOf(inventoryItem)) { sc.ScriptMessage("'%s' is not a type of PowerupGiver.", sc.String); - inventoryItem = RUNTIME_CLASS(APowerupGiver); + inventoryItem = PClass::FindActor(NAME_PowerupGiver); } if(parenthesized) sc.MustGetToken(')'); @@ -1433,11 +1433,14 @@ class CommandDrawNumber : public CommandDrawString break; case POWERUPTIME: { - //Get the PowerupType and check to see if the player has any in inventory. - PClassActor* powerupType = ((APowerupGiver*) GetDefaultByType(inventoryItem))->PowerupType; - APowerup* powerup = (APowerup*) statusBar->CPlayer->mo->FindInventory(powerupType); - if(powerup != NULL) - num = powerup->EffectTics / TICRATE + 1; + // num = statusBar.CPlayer.mo.GetEffectTicsForItem(inventoryItem) / TICRATE + 1; + static VMFunction *func = nullptr; + if (func == nullptr) func = static_cast(RUNTIME_CLASS(APlayerPawn)->Symbols.FindSymbol("GetEffectTicsForItem", false))->Variants[0].Implementation; + VMValue params[] = { statusBar->CPlayer->mo, inventoryItem }; + int retv; + VMReturn ret(&retv); + GlobalVMStack.Call(func, params, 2, &ret, 1); + num = retv / TICRATE + 1; break; } case INVENTORY: @@ -2655,10 +2658,10 @@ class CommandDrawBar : public SBarInfoCommand if(!parenthesized || !sc.CheckToken(TK_StringConst)) sc.MustGetToken(TK_Identifier); data.inventoryItem = PClass::FindActor(sc.String); - if(data.inventoryItem == NULL || !RUNTIME_CLASS(APowerupGiver)->IsAncestorOf(data.inventoryItem)) + if(data.inventoryItem == NULL || !PClass::FindActor(NAME_PowerupGiver)->IsAncestorOf(data.inventoryItem)) { sc.ScriptMessage("'%s' is not a type of PowerupGiver.", sc.String); - data.inventoryItem = RUNTIME_CLASS(APowerupGiver); + data.inventoryItem = PClass::FindActor(NAME_PowerupGiver); } if(parenthesized) sc.MustGetToken(')'); @@ -2822,18 +2825,16 @@ class CommandDrawBar : public SBarInfoCommand break; case POWERUPTIME: { - //Get the PowerupType and check to see if the player has any in inventory. - APowerupGiver *powerupGiver = (APowerupGiver*) GetDefaultByType(data.inventoryItem); - PClassActor *powerupType = powerupGiver->PowerupType; - APowerup *powerup = (APowerup*) statusBar->CPlayer->mo->FindInventory(powerupType); - if(powerup != NULL && powerupType != NULL && powerupGiver != NULL) - { - value = powerup->EffectTics + 1; - if(powerupGiver->EffectTics == 0) //if 0 we need to get the default from the powerup - max = ((APowerup*) GetDefaultByType(powerupType))->EffectTics + 1; - else - max = powerupGiver->EffectTics + 1; - } + static VMFunction *func = nullptr; + if (func == nullptr) func = static_cast(RUNTIME_CLASS(APlayerPawn)->Symbols.FindSymbol("GetEffectTicsForItem", false))->Variants[0].Implementation; + VMValue params[] = { statusBar->CPlayer->mo, data.inventoryItem }; + VMReturn ret[2]; + int ival; + ret[0].IntAt(&ival); + ret[1].IntAt(&max); + GlobalVMStack.Call(func, params, 2, ret, 2); + value = ival + 1; + max++; break; } case SAVEPERCENT: diff --git a/src/namedef.h b/src/namedef.h index 366676a13a..0a3af96cbb 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -709,6 +709,13 @@ xx(Wi_NoAutostartMap) xx(MorphStyle) xx(MorphFlash) xx(UnMorphFlash) +xx(Powerup) +xx(EffectTics) +xx(PowerupGiver) +xx(BlendColor) +xx(Strength) +xx(Mode) +xx(PowerupType) // Decorate compatibility functions xx(BuiltinTypeCheck) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 50dc1eb7a7..fb0de59328 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5719,7 +5719,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) if (argCount >= 2) { PClassActor *powerupclass = PClass::FindActor(FBehavior::StaticLookupString(args[1])); - if (powerupclass == NULL || !RUNTIME_CLASS(APowerup)->IsAncestorOf(powerupclass)) + if (powerupclass == NULL || !powerupclass->IsDescendantOf(PClass::FindActor(NAME_Powerup))) { Printf("'%s' is not a type of Powerup.\n", FBehavior::StaticLookupString(args[1])); return 0; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c2a72e3751..e52b10a8ed 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5381,15 +5381,13 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) oldactor->DestroyAllInventory(); } // [BC] Handle temporary invulnerability when respawned - if ((state == PST_REBORN || state == PST_ENTER) && - (dmflags2 & DF2_YES_RESPAWN_INVUL) && - (multiplayer || alwaysapplydmflags)) + if (state == PST_REBORN || state == PST_ENTER) { - APowerup *invul = static_cast(p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerInvulnerable))); - invul->EffectTics = 3*TICRATE; - invul->BlendColor = 0; // don't mess with the view - invul->ItemFlags |= IF_UNDROPPABLE; // Don't drop this - p->mo->effects |= FX_RESPAWNINVUL; // [RH] special effect + IFVIRTUALPTR(p->mo, APlayerPawn, OnRespawn) + { + VMValue param = p->mo; + GlobalVMStack.Call(func, ¶m, 1, nullptr, 0); + } } if (StatusBar != NULL && (playernum == consoleplayer || StatusBar->GetPlayer() == playernum)) diff --git a/src/p_user.cpp b/src/p_user.cpp index a9dcc7cfa1..b04a7be01a 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1166,7 +1166,7 @@ void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer) } else if ((dmflags & DF_COOP_LOSE_POWERUPS) && defitem == NULL && - item->IsKindOf(RUNTIME_CLASS(APowerupGiver))) + item->IsKindOf(PClass::FindActor(NAME_PowerupGiver))) { item->Destroy(); } diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 170b5c49af..f441f84177 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -464,10 +464,17 @@ int MatchString (const char *in, const char **strings) // //========================================================================== +static bool PointerCheck(PType *symtype, PType *checktype) +{ + auto symptype = dyn_cast(symtype); + auto checkptype = dyn_cast(checktype); + return symptype != nullptr && checkptype != nullptr && symptype->ClassRestriction->IsDescendantOf(checkptype->ClassRestriction); +} + static void *ScriptVar(DObject *obj, PClass *cls, FName field, PType *type) { auto sym = dyn_cast(cls->Symbols.FindSymbol(field, true)); - if (sym && sym->Type == type) + if (sym && (sym->Type == type || PointerCheck(sym->Type, type))) { return (((char*)obj) + sym->Offset); } @@ -2278,14 +2285,11 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) int alpha; PalEntry *pBlendColor; + bool isgiver = info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver)); - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || isgiver) { - pBlendColor = &((APowerup*)defaults)->BlendColor; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pBlendColor = &((APowerupGiver*)defaults)->BlendColor; + pBlendColor = &TypedScriptVar(defaults, info, NAME_BlendColor, TypeColor); } else { @@ -2307,7 +2311,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) *pBlendColor = MakeSpecialColormap(v); return; } - else if (!stricmp(name, "none") && info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) + else if (!stricmp(name, "none") && isgiver) { *pBlendColor = MakeSpecialColormap(65535); return; @@ -2333,13 +2337,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, colormap, FFFfff, Inventory) { PalEntry * pBlendColor; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pBlendColor = &((APowerup*)defaults)->BlendColor; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pBlendColor = &((APowerupGiver*)defaults)->BlendColor; + pBlendColor = &TypedScriptVar(defaults, info, NAME_BlendColor, TypeColor); } else { @@ -2377,13 +2377,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, duration, I, Inventory) { int *pEffectTics; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pEffectTics = &((APowerup*)defaults)->EffectTics; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pEffectTics = &((APowerupGiver*)defaults)->EffectTics; + pEffectTics = &TypedScriptVar(defaults, info, NAME_EffectTics, TypeSInt32); } else { @@ -2402,13 +2398,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, strength, F, Inventory) { double *pStrength; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pStrength = &((APowerup*)defaults)->Strength; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pStrength = &((APowerupGiver*)defaults)->Strength; + pStrength = &TypedScriptVar(defaults, info, NAME_Strength, TypeFloat64); } else { @@ -2426,13 +2418,10 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, mode, S, Inventory) { PROP_STRING_PARM(str, 0); FName *pMode; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pMode = &((APowerup*)defaults)->Mode; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pMode = &((APowerupGiver*)defaults)->Mode; + pMode = &TypedScriptVar(defaults, info, NAME_Mode, TypeName); } else { @@ -2445,7 +2434,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, mode, S, Inventory) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY_PREFIX(powerup, type, S, PowerupGiver) +DEFINE_SCRIPTED_PROPERTY_PREFIX(powerup, type, S, PowerupGiver) { PROP_STRING_PARM(str, 0); @@ -2465,8 +2454,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, type, S, PowerupGiver) I_Error("Unknown powerup type %s", str); } } - - defaults->PowerupType = cls; + TypedScriptVar(defaults, info, NAME_PowerupType, NewClassPointer(RUNTIME_CLASS(AActor))) = cls; } //========================================================================== diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 2a76f8e2ea..a9fdf3abeb 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -393,6 +393,11 @@ struct VMReturn TagOfs = 0; RegType = REGT_POINTER; } + VMReturn() { } + VMReturn(int *loc) { IntAt(loc); } + VMReturn(double *loc) { FloatAt(loc); } + VMReturn(FString *loc) { StringAt(loc); } + VMReturn(void **loc) { PointerAt(loc); } }; struct VMRegisters; diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 00639ce9e7..e9325c0048 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -1,11 +1,11 @@ -class PowerupGiver : Inventory native +class PowerupGiver : Inventory { - native Class PowerupType; - native int EffectTics; // Non-0 to override the powerup's default tics - native color BlendColor; // Non-0 to override the powerup's default blend - native Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility - native double Strength; // Meaning depends on powerup - currently used only by Invisibility + Class PowerupType; + int EffectTics; // Non-0 to override the powerup's default tics + color BlendColor; // Non-0 to override the powerup's default blend + Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility + double Strength; // Meaning depends on powerup - currently used only by Invisibility Default { @@ -56,8 +56,6 @@ class PowerupGiver : Inventory native power.GoAwayAndDie (); return false; } - - } class Powerup : Inventory native diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 99f3d9c1ff..b7799d7315 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -102,6 +102,36 @@ class PlayerPawn : Actor native virtual void MorphPlayerThink() { } + + virtual void OnRespawn() + { + if (sv_respawnprotect && (multiplayer || alwaysapplydmflags)) + { + let invul = Powerup(Spawn("PowerInvulnerable")); + invul.EffectTics = 3 * TICRATE; + invul.BlendColor = 0; // don't mess with the view + invul.bUndroppable = true; // Don't drop this + bRespawnInvul = true; // [RH] special effect + } + } + + // This is for SBARINFO. + int/*, int*/ GetEffectTicsForItem(class item) + { + let pg = (class)(item); + if (pg != null) + { + let powerupType = (class)(GetDefaultByType(pg).PowerupType); + let powerup = Powerup(FindInventory(powerupType)); + if(powerup != null) + { + let maxtics = GetDefaultByType(powerupType).EffectTics; + if (maxtics == 0) maxtics = powerup.default.EffectTics; + return powerup.EffectTics/*, maxtics*/; + } + } + return 0/*, 0*/; + } native int GetMaxHealth(); native bool ResetAirSupply (bool playgasp = false);