- eliminated the native PowerupGiver class.

- scriptified the respawn invulnerability code into a virtual OnRespawn function for PlayerPawn so that custom effects can be implemented.
This commit is contained in:
Christoph Oelckers 2017-01-18 00:11:04 +01:00
parent 98f9219334
commit 232b64d332
17 changed files with 138 additions and 130 deletions

View file

@ -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.
}
}
}

View file

@ -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 T> class TObjPtr;
@ -453,6 +452,8 @@ public:
void *ScriptVar(FName field, PType *type);
protected:
public:
DObject ();
DObject (PClass *inClass);

View file

@ -1782,6 +1782,18 @@ PClassPointer::PClassPointer(PClass *restrict)
else mDescriptiveName = "ClassPointer";
}
//==========================================================================
//
// PClassPointer - isCompatible
//
//==========================================================================
bool PClassPointer::isCompatible(PType *type)
{
auto other = dyn_cast<PClassPointer>(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);

View file

@ -599,6 +599,7 @@ public:
// this is only here to block PPointer's implementation
void SetPointer(void *base, unsigned offset, TArray<size_t> *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;

View file

@ -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

View file

@ -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);
}

View file

@ -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__

View file

@ -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;

View file

@ -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<PFunction*>(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<PFunction*>(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:

View file

@ -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)

View file

@ -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;

View file

@ -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<APowerup*>(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, &param, 1, nullptr, 0);
}
}
if (StatusBar != NULL && (playernum == consoleplayer || StatusBar->GetPlayer() == playernum))

View file

@ -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();
}

View file

@ -464,10 +464,17 @@ int MatchString (const char *in, const char **strings)
//
//==========================================================================
static bool PointerCheck(PType *symtype, PType *checktype)
{
auto symptype = dyn_cast<PClassPointer>(symtype);
auto checkptype = dyn_cast<PClassPointer>(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<PField>(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<PalEntry>(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<PalEntry>(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<int>(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<double>(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<FName>(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<PClassActor*>(defaults, info, NAME_PowerupType, NewClassPointer(RUNTIME_CLASS(AActor))) = cls;
}
//==========================================================================

View file

@ -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;

View file

@ -1,11 +1,11 @@
class PowerupGiver : Inventory native
class PowerupGiver : Inventory
{
native Class<Actor> 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<Actor> 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

View file

@ -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<Inventory> item)
{
let pg = (class<PowerupGiver>)(item);
if (pg != null)
{
let powerupType = (class<Powerup>)(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);