diff --git a/src/c_console.cpp b/src/c_console.cpp index 8bf7f4cd9..2c08d11ae 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1330,6 +1330,12 @@ void C_HideConsole () } } +DEFINE_ACTION_FUNCTION(_Console, HideConsole) +{ + C_HideConsole(); + return 0; +} + static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer) { int data1 = ev->data1; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 7b43de020..633eb466b 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -219,6 +219,7 @@ DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionStyle) DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionAlpha) DEFINE_FIELD_X(DehInfo, DehInfo, NoAutofreeze) DEFINE_FIELD_X(DehInfo, DehInfo, BFGCells) +DEFINE_FIELD_X(DehInfo, DehInfo, BlueAC) // Doom identified pickup items by their sprites. ZDoom prefers to use their // class type to identify them instead. To support the traditional Doom diff --git a/src/dobject.cpp b/src/dobject.cpp index aa3736147..6c9afb884 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 b37380342..3e8801d40 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 6e42e2919..75513c5b5 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 156818147..a7ce429bb 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; @@ -639,6 +640,19 @@ protected: PProperty(); }; +class PPropFlag : public PSymbol +{ + DECLARE_CLASS(PPropFlag, PSymbol); +public: + PPropFlag(FName name, PField *offset, int bitval); + + PField *Offset; + int bitval; + +protected: + PPropFlag(); +}; + // Compound types ----------------------------------------------------------- class PEnum : public PNamedType diff --git a/src/g_game.cpp b/src/g_game.cpp index 77e675b8a..adde5ec48 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 0cd4828b1..15895eaa4 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -48,100 +48,13 @@ 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 :: Use -// -//=========================================================================== - -bool APowerupGiver::Use (bool pickup) -{ - if (PowerupType == NULL) return true; // item is useless - if (Owner == nullptr) return true; - - APowerup *power = static_cast (Spawn (PowerupType)); - - if (EffectTics != 0) - { - power->EffectTics = EffectTics; - } - if (BlendColor != 0) - { - if (BlendColor != MakeSpecialColormap(65535)) power->BlendColor = BlendColor; - else power->BlendColor = 0; - } - if (Mode != NAME_None) - { - power->Mode = Mode; - } - if (Strength != 0) - { - power->Strength = Strength; - } - - power->ItemFlags |= ItemFlags & (IF_ALWAYSPICKUP|IF_ADDITIVETIME|IF_NOTELEPORTFREEZE); - if (power->CallTryPickup (Owner)) - { - return true; - } - power->GoAwayAndDie (); - return false; -} - -//=========================================================================== -// -// 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) DEFINE_FIELD(APowerup, BlendColor) DEFINE_FIELD(APowerup, Mode) DEFINE_FIELD(APowerup, Strength) - -//=========================================================================== -// -// APowerup :: Tick -// -//=========================================================================== - -void APowerup::Tick () -{ - // Powerups cannot exist outside an inventory - if (Owner == NULL) - { - Destroy (); - } - if (EffectTics > 0 && --EffectTics == 0) - { - Destroy (); - } -} +DEFINE_FIELD(APowerup, Colormap) //=========================================================================== // @@ -156,438 +69,6 @@ void APowerup::Serialize(FSerializer &arc) arc("effecttics", EffectTics, def->EffectTics) ("blendcolor", BlendColor, def->BlendColor) ("mode", Mode, def->Mode) - ("strength", Strength, def->Strength); + ("strength", Strength, def->Strength) + ("colormap", Colormap, def->Colormap); } - -//=========================================================================== -// -// APowerup :: GetBlend -// -//=========================================================================== - -PalEntry APowerup::GetBlend () -{ - if (isBlinking()) - return 0; - - if (IsSpecialColormap(BlendColor)) return 0; - return BlendColor; -} - -//=========================================================================== -// -// APowerup :: InitEffect -// -//=========================================================================== - -void APowerup::InitEffect () -{ -} - -DEFINE_ACTION_FUNCTION(APowerup, InitEffect) -{ - PARAM_SELF_PROLOGUE(APowerup); - self->InitEffect(); - return 0; -} - -void APowerup::CallInitEffect() -{ - IFVIRTUAL(APowerup, InitEffect) - { - VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); - } - else InitEffect(); -} - -//=========================================================================== -// -// APowerup :: isBlinking (todo: make this virtual so that child classes can configure their blinking) -// -//=========================================================================== - -bool APowerup::isBlinking() const -{ - return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !(ItemFlags & IF_NOSCREENBLINK)); -} - -DEFINE_ACTION_FUNCTION(APowerup, isBlinking) -{ - PARAM_SELF_PROLOGUE(APowerup); - ACTION_RETURN_BOOL(self->isBlinking()); -} - -//=========================================================================== -// -// APowerup :: DoEffect -// -//=========================================================================== - -void APowerup::DoEffect () -{ - if (Owner == NULL || Owner->player == NULL) - { - return; - } - - if (EffectTics > 0) - { - int Colormap = GetSpecialColormap(BlendColor); - - if (Colormap != NOFIXEDCOLORMAP) - { - if (!isBlinking()) - { - Owner->player->fixedcolormap = Colormap; - } - else if (Owner->player->fixedcolormap == Colormap) - { - // only unset if the fixed colormap comes from this item - Owner->player->fixedcolormap = NOFIXEDCOLORMAP; - } - } - } -} - -//=========================================================================== -// -// APowerup :: EndEffect -// -//=========================================================================== - -void APowerup::EndEffect () -{ - int colormap = GetSpecialColormap(BlendColor); - - if (colormap != NOFIXEDCOLORMAP && Owner && Owner->player && Owner->player->fixedcolormap == colormap) - { // only unset if the fixed colormap comes from this item - Owner->player->fixedcolormap = NOFIXEDCOLORMAP; - } -} - -DEFINE_ACTION_FUNCTION(APowerup, EndEffect) -{ - PARAM_SELF_PROLOGUE(APowerup); - self->EndEffect(); - return 0; -} - -void APowerup::CallEndEffect() -{ - IFVIRTUAL(APowerup, EndEffect) - { - VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); - } - else EndEffect(); -} - - -//=========================================================================== -// -// APowerup :: Destroy -// -//=========================================================================== - -void APowerup::OnDestroy () -{ - CallEndEffect (); - Super::OnDestroy(); -} - -//=========================================================================== -// -// APowerup :: DrawPowerup -// -//=========================================================================== - -bool APowerup::DrawPowerup (int x, int y) -{ - if (!Icon.isValid()) - { - return false; - } - if (!isBlinking()) - { - FTexture *pic = TexMan(Icon); - screen->DrawTexture (pic, x, y, - DTA_HUDRules, HUD_Normal, -// DTA_TopOffset, pic->GetHeight()/2, -// DTA_LeftOffset, pic->GetWidth()/2, - TAG_DONE); - } - return true; -} - -//=========================================================================== -// -// APowerup :: HandlePickup -// -//=========================================================================== - -bool APowerup::HandlePickup (AInventory *item) -{ - if (item->GetClass() == GetClass()) - { - APowerup *power = static_cast(item); - if (power->EffectTics == 0) - { - power->ItemFlags |= IF_PICKUPGOOD; - return true; - } - // Color gets transferred if the new item has an effect. - - // Increase the effect's duration. - if (power->ItemFlags & IF_ADDITIVETIME) - { - EffectTics += power->EffectTics; - BlendColor = power->BlendColor; - } - // If it's not blinking yet, you can't replenish the power unless the - // powerup is required to be picked up. - else if (EffectTics > BLINKTHRESHOLD && !(power->ItemFlags & IF_ALWAYSPICKUP)) - { - return true; - } - // Reset the effect duration. - else if (power->EffectTics > EffectTics) - { - EffectTics = power->EffectTics; - BlendColor = power->BlendColor; - } - power->ItemFlags |= IF_PICKUPGOOD; - return true; - } - return false; -} - -//=========================================================================== -// -// APowerup :: CreateCopy -// -//=========================================================================== - -AInventory *APowerup::CreateCopy (AActor *other) -{ - // Get the effective effect time. - EffectTics = abs (EffectTics); - // Abuse the Owner field to tell the - // InitEffect method who started it; - // this should be cleared afterwards, - // as this powerup instance is not - // properly attached to anything yet. - Owner = other; - // Actually activate the powerup. - CallInitEffect (); - // Clear the Owner field, unless it was - // changed by the activation, for example, - // if this instance is a morph powerup; - // the flag tells the caller that the - // ownership has changed so that they - // can properly handle the situation. - if (!(ItemFlags & IF_CREATECOPYMOVED)) - { - Owner = NULL; - } - // All done. - return this; -} - -//=========================================================================== -// -// APowerup :: CreateTossable -// -// Powerups are never droppable, even without IF_UNDROPPABLE set. -// -//=========================================================================== - -AInventory *APowerup::CreateTossable () -{ - return NULL; -} - -//=========================================================================== -// -// APowerup :: OwnerDied -// -// Powerups don't last beyond death. -// -//=========================================================================== - -void APowerup::OwnerDied () -{ - Destroy (); -} - -// Invulnerability Powerup --------------------------------------------------- - -IMPLEMENT_CLASS(APowerInvulnerable, false, false) - -//=========================================================================== -// -// APowerInvulnerable :: InitEffect -// -//=========================================================================== - -void APowerInvulnerable::InitEffect () -{ - Super::InitEffect(); - Owner->effects &= ~FX_RESPAWNINVUL; - Owner->flags2 |= MF2_INVULNERABLE; - if (Mode == NAME_None && Owner->IsKindOf(RUNTIME_CLASS(APlayerPawn))) - { - Mode = static_cast(Owner->GetClass())->InvulMode; - } - if (Mode == NAME_Reflective) - { - Owner->flags2 |= MF2_REFLECTIVE; - } -} - -//=========================================================================== -// -// APowerInvulnerable :: DoEffect -// -//=========================================================================== - -void APowerInvulnerable::DoEffect () -{ - Super::DoEffect (); - - if (Owner == NULL) - { - return; - } - - if (Mode == NAME_Ghost) - { - if (!(Owner->flags & MF_SHADOW)) - { - // Don't mess with the translucency settings if an - // invisibility powerup is active. - Owner->RenderStyle = STYLE_Translucent; - if (!(level.time & 7) && Owner->Alpha > 0 && Owner->Alpha < 1) - { - if (Owner->Alpha == HX_SHADOW) - { - Owner->Alpha = HX_ALTSHADOW; - } - else - { - Owner->Alpha = 0; - Owner->flags2 |= MF2_NONSHOOTABLE; - } - } - if (!(level.time & 31)) - { - if (Owner->Alpha == 0) - { - Owner->flags2 &= ~MF2_NONSHOOTABLE; - Owner->Alpha = HX_ALTSHADOW; - } - else - { - Owner->Alpha = HX_SHADOW; - } - } - } - else - { - Owner->flags2 &= ~MF2_NONSHOOTABLE; - } - } -} - -//=========================================================================== -// -// APowerInvulnerable :: EndEffect -// -//=========================================================================== - -void APowerInvulnerable::EndEffect () -{ - Super::EndEffect(); - - if (Owner == NULL) - { - return; - } - - Owner->flags2 &= ~MF2_INVULNERABLE; - Owner->effects &= ~FX_RESPAWNINVUL; - if (Mode == NAME_Ghost) - { - Owner->flags2 &= ~MF2_NONSHOOTABLE; - if (!(Owner->flags & MF_SHADOW)) - { - // Don't mess with the translucency settings if an - // invisibility powerup is active. - Owner->RenderStyle = STYLE_Normal; - Owner->Alpha = 1.; - } - } - else if (Mode == NAME_Reflective) - { - Owner->flags2 &= ~MF2_REFLECTIVE; - } - - if (Owner->player != NULL) - { - Owner->player->fixedcolormap = NOFIXEDCOLORMAP; - } -} - -// Speed Powerup ------------------------------------------------------------- - -IMPLEMENT_CLASS(APowerSpeed, false, false) - -DEFINE_FIELD(APowerSpeed, SpeedFlags) - -//=========================================================================== -// -// APowerSpeed :: Serialize -// -//=========================================================================== - -void APowerSpeed::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("speedflags", SpeedFlags); -} - -// Morph powerup ------------------------------------------------------ - -IMPLEMENT_CLASS(APowerMorph, false, true) - -IMPLEMENT_POINTERS_START(APowerMorph) - IMPLEMENT_POINTER(PlayerClass) - IMPLEMENT_POINTER(MorphFlash) - IMPLEMENT_POINTER(UnMorphFlash) -IMPLEMENT_POINTERS_END - - -DEFINE_FIELD(APowerMorph, PlayerClass) -DEFINE_FIELD(APowerMorph, MorphFlash) -DEFINE_FIELD(APowerMorph, UnMorphFlash) -DEFINE_FIELD(APowerMorph, MorphStyle) -DEFINE_FIELD(APowerMorph, MorphedPlayer) - -//=========================================================================== -// -// APowerMorph :: Serialize -// -//=========================================================================== - -void APowerMorph::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("playerclass", PlayerClass) - ("morphstyle", MorphStyle) - ("morphflash", MorphFlash) - ("unmorphflash", UnMorphFlash) - ("morphedplayer", MorphedPlayer); -} - diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h index f947e18d5..84c18505b 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -11,85 +11,16 @@ class APowerup : public AInventory { DECLARE_CLASS (APowerup, AInventory) public: - virtual void Tick () override; - virtual void OnDestroy() override; - virtual bool HandlePickup (AInventory *item) override; - virtual AInventory *CreateCopy (AActor *other) override; - virtual AInventory *CreateTossable () override; virtual void Serialize(FSerializer &arc) override; - virtual void OwnerDied () override; - virtual PalEntry GetBlend () override; - virtual bool DrawPowerup (int x, int y) override; int EffectTics; PalEntry BlendColor; FNameNoInit Mode; double Strength; + int Colormap; public: - virtual void InitEffect (); - virtual void DoEffect () override; - virtual void EndEffect (); - bool isBlinking() const; - -protected: - void CallInitEffect(); - void CallEndEffect(); - 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 bool Use (bool pickup) override; - 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 -}; - -class APowerInvulnerable : public APowerup -{ - DECLARE_CLASS (APowerInvulnerable, APowerup) -protected: - virtual void InitEffect () override; - virtual void DoEffect () override; - virtual void EndEffect () override; -}; - -class APowerSpeed : public APowerup -{ - DECLARE_CLASS (APowerSpeed, APowerup) -protected: - virtual void Serialize(FSerializer &arc) override; -public: - int SpeedFlags; -}; - -#define PSF_NOTRAIL 1 - -class APowerMorph : public APowerup -{ - DECLARE_CLASS( APowerMorph, APowerup ) - HAS_OBJECT_POINTERS -public: - - virtual void Serialize(FSerializer &arc) override; - - // Variables - PClassPlayerPawn *PlayerClass; - PClassActor *MorphFlash, *UnMorphFlash; - int MorphStyle; - player_t *MorphedPlayer; -}; - #endif //__A_ARTIFACTS_H__ diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 13842d359..7b3756f52 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -368,18 +368,6 @@ void AInventory::CallDoEffect() } -//=========================================================================== -// -// AInventory :: OwnerDied -// -// Items receive this message when their owners die. -// -//=========================================================================== - -void AInventory::OwnerDied () -{ -} - //=========================================================================== // // AInventory :: HandlePickup @@ -1348,7 +1336,7 @@ bool AInventory::TryPickup (AActor *&toucher) } // The item is placed in the inventory just long enough to be used. toucher->AddInventory (this); - bool usegood = Use (true); + bool usegood = CallUse (true); toucher->RemoveInventory (this); if (usegood) diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 9f85e482b..0d2c43fc5 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -138,8 +138,6 @@ public: // virtual on the script side only. double GetSpeedFactor(); bool GetNoTeleportFreeze(); - // Stuff for later when more features are exported. - virtual void OwnerDied(); bool GoAway(); diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 02aa8480e..242181ced 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -1288,6 +1288,19 @@ bool FWeaponSlots::LocateWeapon (PClassWeapon *type, int *const slot, int *const return false; } + +DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon) +{ + PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots); + PARAM_CLASS(weap, AWeapon); + int slot = 0, index = 0; + bool retv = self->LocateWeapon(weap, &slot, &index); + if (numret >= 1) ret[0].SetInt(retv); + if (numret >= 2) ret[1].SetInt(slot); + if (numret >= 3) ret[2].SetInt(index); + return MIN(numret, 3); +} + //=========================================================================== // // FindMostRecentWeapon diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index d18ec7e02..12b359a14 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -17,6 +17,7 @@ #include "a_armor.h" #include "r_data/sprites.h" #include "g_levellocals.h" +#include "virtual.h" static FRandom pr_morphmonst ("MorphMonster"); @@ -593,11 +594,17 @@ 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)) { - static_cast(item)->CallEndEffect(); + IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) + { + VMValue params[1] = { item }; + VMFrameStack stack; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } item = item->Inventory; } @@ -613,11 +620,17 @@ 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)) { - static_cast(item)->CallInitEffect(); + IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) + { + VMValue params[1] = { item }; + VMFrameStack stack; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } item = item->Inventory; } diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index f78c61040..4cb587bc9 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/gi.cpp b/src/gi.cpp index c82128889..a13804aad 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -45,6 +45,10 @@ gameinfo_t gameinfo; +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, backpacktype) +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, Armor2Percent) + + const char *GameNames[17] = { NULL, "Doom", "Heretic", NULL, "Hexen", NULL, NULL, NULL, "Strife", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Chex" diff --git a/src/gi.h b/src/gi.h index d9e81bcf1..bd5e84363 100644 --- a/src/gi.h +++ b/src/gi.h @@ -148,7 +148,7 @@ struct gameinfo_t FString translator; DWORD defaultbloodcolor; DWORD defaultbloodparticlecolor; - FString backpacktype; + FName backpacktype; FString statusbar; FString intermissionMusic; int intermissionOrder; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 46f18d96f..d68839359 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -50,6 +50,7 @@ #include "a_armor.h" #include "a_ammo.h" #include "g_levellocals.h" +#include "virtual.h" // [RH] Actually handle the cheat. The cheat code in st_stuff.c now just // writes some bytes to the network data stream, and the network code @@ -324,16 +325,10 @@ void cht_DoCheat (player_t *player, int cheat) } else { + player->mo->Revive(); player->playerstate = PST_LIVE; player->health = player->mo->health = player->mo->GetDefault()->health; player->viewheight = ((APlayerPawn *)player->mo->GetDefault())->ViewHeight; - player->mo->flags = player->mo->GetDefault()->flags; - player->mo->flags2 = player->mo->GetDefault()->flags2; - player->mo->flags3 = player->mo->GetDefault()->flags3; - player->mo->flags4 = player->mo->GetDefault()->flags4; - player->mo->flags5 = player->mo->GetDefault()->flags5; - player->mo->flags6 = player->mo->GetDefault()->flags6; - player->mo->flags7 = player->mo->GetDefault()->flags7; player->mo->renderflags &= ~RF_INVISIBLE; player->mo->Height = player->mo->GetDefault()->Height; player->mo->radius = player->mo->GetDefault()->radius; @@ -344,7 +339,6 @@ void cht_DoCheat (player_t *player, int cheat) { player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players)); } - player->mo->DamageType = NAME_None; if (player->ReadyWeapon != nullptr) { P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetUpState()); @@ -588,434 +582,24 @@ const char *cht_Morph (player_t *player, PClassPlayerPawn *morphclass, bool quic void cht_Give (player_t *player, const char *name, int amount) { - enum { ALL_NO, ALL_YES, ALL_YESYES } giveall; - int i; - PClassActor *type; + if (player->mo == nullptr) return; - if (player != &players[consoleplayer]) - Printf ("%s is a cheater: give %s\n", player->userinfo.GetName(), name); - - if (player->mo == NULL || player->health <= 0) + IFVIRTUALPTR(player->mo, APlayerPawn, CheatGive) { - return; + VMValue params[3] = { player->mo, FString(name), amount }; + GlobalVMStack.Call(func, params, 3, nullptr, 0); } - - giveall = ALL_NO; - if (stricmp (name, "all") == 0) - { - giveall = ALL_YES; - } - else if (stricmp (name, "everything") == 0) - { - giveall = ALL_YESYES; - } - - if (stricmp (name, "health") == 0) - { - if (amount > 0) - { - player->mo->health += amount; - player->health = player->mo->health; - } - else - { - player->health = player->mo->health = player->mo->GetMaxHealth(); - } - } - - if (giveall || stricmp (name, "backpack") == 0) - { - // Select the correct type of backpack based on the game - type = PClass::FindActor(gameinfo.backpacktype); - if (type != NULL) - { - player->mo->GiveInventory(static_cast(type), 1, true); - } - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "ammo") == 0) - { - // Find every unique type of ammo. Give it to the player if - // he doesn't have it already, and set each to its maximum. - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - PClassActor *type = PClassActor::AllActorClasses[i]; - - if (type->ParentClass == RUNTIME_CLASS(AAmmo)) - { - PClassInventory *atype = static_cast(type); - AInventory *ammo = player->mo->FindInventory(atype); - if (ammo == NULL) - { - ammo = static_cast(Spawn (atype)); - ammo->AttachToOwner (player->mo); - ammo->Amount = ammo->MaxAmount; - } - else if (ammo->Amount < ammo->MaxAmount) - { - ammo->Amount = ammo->MaxAmount; - } - } - } - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "armor") == 0) - { - if (gameinfo.gametype != GAME_Hexen) - { - ABasicArmorPickup *armor = Spawn (); - armor->SaveAmount = 100*deh.BlueAC; - armor->SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5; - if (!armor->CallTryPickup (player->mo)) - { - armor->Destroy (); - } - } - else - { - for (i = 0; i < 4; ++i) - { - AHexenArmor *armor = Spawn (); - armor->health = i; - armor->Amount = 0; - if (!armor->CallTryPickup (player->mo)) - { - armor->Destroy (); - } - } - } - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "keys") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - if (PClassActor::AllActorClasses[i]->IsDescendantOf (RUNTIME_CLASS(AKey))) - { - AKey *key = (AKey *)GetDefaultByType (PClassActor::AllActorClasses[i]); - if (key->KeyNumber != 0) - { - key = static_cast(Spawn(static_cast(PClassActor::AllActorClasses[i]))); - if (!key->CallTryPickup (player->mo)) - { - key->Destroy (); - } - } - } - } - if (!giveall) - return; - } - - if (giveall || stricmp (name, "weapons") == 0) - { - AWeapon *savedpending = player->PendingWeapon; - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - // Don't give replaced weapons unless the replacement was done by Dehacked. - if (type != RUNTIME_CLASS(AWeapon) && - type->IsDescendantOf (RUNTIME_CLASS(AWeapon)) && - (static_cast(type)->GetReplacement() == type || - static_cast(type)->GetReplacement()->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup)))) - { - // Give the weapon only if it belongs to the current game or - if (player->weapons.LocateWeapon(static_cast(type), NULL, NULL)) - { - AWeapon *def = (AWeapon*)GetDefaultByType (type); - if (giveall == ALL_YESYES || !(def->WeaponFlags & WIF_CHEATNOTWEAPON)) - { - player->mo->GiveInventory(static_cast(type), 1, true); - } - } - } - } - player->PendingWeapon = savedpending; - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "artifacts") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - if (type->IsDescendantOf (RUNTIME_CLASS(AInventory))) - { - AInventory *def = (AInventory*)GetDefaultByType (type); - if (def->Icon.isValid() && def->MaxAmount > 1 && - !type->IsDescendantOf (pitype) && - !type->IsDescendantOf (RUNTIME_CLASS(APowerup)) && - !type->IsDescendantOf (RUNTIME_CLASS(AArmor))) - { - // Do not give replaced items unless using "give everything" - if (giveall == ALL_YESYES || type->GetReplacement() == type) - { - player->mo->GiveInventory(static_cast(type), amount <= 0 ? def->MaxAmount : amount, true); - } - } - } - } - if (!giveall) - return; - } - - if (giveall || stricmp (name, "puzzlepieces") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - if (type->IsDescendantOf (pitype)) - { - AInventory *def = (AInventory*)GetDefaultByType (type); - if (def->Icon.isValid()) - { - // Do not give replaced items unless using "give everything" - if (giveall == ALL_YESYES || type->GetReplacement() == type) - { - player->mo->GiveInventory(static_cast(type), amount <= 0 ? def->MaxAmount : amount, true); - } - } - } - } - if (!giveall) - return; - } - - if (giveall) - return; - - type = PClass::FindActor(name); - if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AInventory))) - { - if (player == &players[consoleplayer]) - Printf ("Unknown item \"%s\"\n", name); - } - else - { - player->mo->GiveInventory(static_cast(type), amount, true); - } - return; } void cht_Take (player_t *player, const char *name, int amount) { - bool takeall; - PClassActor *type; + if (player->mo == nullptr) return; - if (player->mo == NULL || player->health <= 0) + IFVIRTUALPTR(player->mo, APlayerPawn, CheatTake) { - return; + VMValue params[3] = { player->mo, FString(name), amount }; + GlobalVMStack.Call(func, params, 3, nullptr, 0); } - - takeall = (stricmp (name, "all") == 0); - - if (!takeall && stricmp (name, "health") == 0) - { - if (player->mo->health - amount <= 0 - || player->health - amount <= 0 - || amount == 0) - { - - cht_Suicide (player); - - if (player == &players[consoleplayer]) - C_HideConsole (); - - return; - } - - if (amount > 0) - { - if (player->mo) - { - player->mo->health -= amount; - player->health = player->mo->health; - } - else - { - player->health -= amount; - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "backpack") == 0) - { - // Take away all types of backpacks the player might own. - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - PClass *type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf(PClass::FindClass(NAME_BackpackItem))) - { - AInventory *pack = player->mo->FindInventory(static_cast(type)); - - if (pack) - pack->Destroy(); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "ammo") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - PClass *type = PClassActor::AllActorClasses[i]; - - if (type->ParentClass == RUNTIME_CLASS (AAmmo)) - { - AInventory *ammo = player->mo->FindInventory(static_cast(type)); - - if (ammo) - ammo->DepleteOrDestroy(); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "armor") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (RUNTIME_CLASS (AArmor))) - { - AInventory *armor = player->mo->FindInventory(static_cast(type)); - - if (armor) - armor->DepleteOrDestroy(); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "keys") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (RUNTIME_CLASS (AKey))) - { - AActor *key = player->mo->FindInventory(static_cast(type)); - - if (key) - key->Destroy (); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "weapons") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type != RUNTIME_CLASS(AWeapon) && - type->IsDescendantOf (RUNTIME_CLASS (AWeapon))) - { - AActor *weapon = player->mo->FindInventory(static_cast(type)); - - if (weapon) - weapon->Destroy (); - - player->ReadyWeapon = nullptr; - player->PendingWeapon = WP_NOCHANGE; - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "artifacts") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (RUNTIME_CLASS (AInventory))) - { - if (!type->IsDescendantOf (pitype) && - !type->IsDescendantOf (RUNTIME_CLASS (APowerup)) && - !type->IsDescendantOf (RUNTIME_CLASS (AArmor)) && - !type->IsDescendantOf (RUNTIME_CLASS (AWeapon)) && - !type->IsDescendantOf (RUNTIME_CLASS (AKey))) - { - AActor *artifact = player->mo->FindInventory(static_cast(type)); - - if (artifact) - artifact->Destroy (); - } - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "puzzlepieces") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (pitype)) - { - AActor *puzzlepiece = player->mo->FindInventory(static_cast(type)); - - if (puzzlepiece) - puzzlepiece->Destroy (); - } - } - - if (!takeall) - return; - } - - if (takeall) - return; - - type = PClass::FindActor (name); - if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS (AInventory))) - { - if (player == &players[consoleplayer]) - Printf ("Unknown item \"%s\"\n", name); - } - else - { - player->mo->TakeInventory(type, amount ? amount : 1); - } - return; } class DSuicider : public DThinker @@ -1070,6 +654,12 @@ void cht_Suicide (player_t *plyr) } } +DEFINE_ACTION_FUNCTION(APlayerPawn, CheatSuicide) +{ + PARAM_SELF_PROLOGUE(APlayerPawn); + cht_Suicide(self->player); + return 0; +} CCMD (mdk) { diff --git a/src/namedef.h b/src/namedef.h index c37ee5577..0a3af96cb 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -706,6 +706,17 @@ xx(WBobSpeed) xx(PlayerClass) 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) xx(BuiltinRandom) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 5355b10a0..fb0de5932 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -87,6 +87,7 @@ #include "a_ammo.h" #include "r_data/colormaps.h" #include "g_levellocals.h" +#include "stats.h" extern FILE *Logfile; @@ -2974,8 +2975,12 @@ void DACSThinker::Serialize(FSerializer &arc) } } +cycle_t ACSTime; + void DACSThinker::Tick () { + ACSTime.Reset(); + ACSTime.Clock(); DLevelScript *script = Scripts; while (script) @@ -2993,6 +2998,7 @@ void DACSThinker::Tick () ACS_StringBuilderStack.Clear(); I_Error("Error: %d garbage entries on ACS string builder stack.", size); } + ACSTime.Unclock(); } void DACSThinker::StopScriptsFor (AActor *actor) @@ -5713,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; @@ -10269,3 +10275,8 @@ CCMD(acsprofile) ShowProfileData(ScriptProfiles, limit, sorter, false); ShowProfileData(FuncProfiles, limit, sorter, true); } + +ADD_STAT(ACS) +{ + return FStringf("ACS time: %f ms", ACSTime.TimeMS()); +} diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 3c7eeca07..172e17198 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1224,22 +1224,6 @@ DEFINE_ACTION_FUNCTION(AActor, CheckInventory) } -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, CheckArmorType) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_NAME (type); - PARAM_INT_DEF(amount); - - ABasicArmor *armor = (ABasicArmor *)self->FindInventory(NAME_BasicArmor); - - ACTION_RETURN_BOOL(armor && armor->ArmorType == type && armor->Amount >= amount); -} - //========================================================================== // // Parameterized version of A_Explode diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index a73e3d8b3..0320b0feb 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -357,15 +357,19 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) static int dieticks[MAXPLAYERS]; // [ZzZombo] not used? Except if for peeking in debugger... int pnum = int(this->player-players); dieticks[pnum] = gametic; - fprintf (debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic, - this->player->cheats&CF_PREDICTING?"predicting":"real"); + fprintf(debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic, + this->player->cheats&CF_PREDICTING ? "predicting" : "real"); } // [RH] Notify this actor's items. for (AInventory *item = Inventory; item != NULL; ) { AInventory *next = item->Inventory; - item->OwnerDied(); + IFVIRTUALPTR(item, AInventory, OwnerDied) + { + VMValue params[1] = { item }; + GlobalVMStack.Call(func, params, 1, nullptr, 0); + } item = next; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 9c18ea638..e52b10a8e 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -822,11 +822,13 @@ bool AActor::GiveInventory(PClassInventory *type, int amount, bool givecheat) return result; } -DEFINE_ACTION_FUNCTION(AActor, Inventory) +DEFINE_ACTION_FUNCTION(AActor, GiveInventory) { PARAM_SELF_PROLOGUE(AActor); - PARAM_OBJECT_NOT_NULL(item, AInventory); - ACTION_RETURN_BOOL(self->UseInventory(item)); + PARAM_CLASS(type, AInventory); + PARAM_INT(amount); + PARAM_BOOL_DEF(givecheat); + ACTION_RETURN_BOOL(self->GiveInventory(type, amount, givecheat)); } @@ -918,6 +920,16 @@ bool AActor::TakeInventory(PClassActor *itemclass, int amount, bool fromdecorate return result; } +DEFINE_ACTION_FUNCTION(AActor, TakeInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT_NOT_NULL(item, AInventory); + PARAM_INT(amount); + PARAM_BOOL_DEF(fromdecorate); + PARAM_BOOL_DEF(notakeinfinite); + self->RemoveInventory(item); + return 0; +} //============================================================================ // @@ -5369,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 (RUNTIME_CLASS(APowerInvulnerable))); - 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 952a88632..5397fa733 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -638,6 +638,14 @@ void player_t::SendPitchLimits() const } } + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetUserName) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_STRING(self->userinfo.GetName()); +} + + //=========================================================================== // // APlayerPawn @@ -1158,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/r_data/colormaps.h b/src/r_data/colormaps.h index 16e563883..675f669b9 100644 --- a/src/r_data/colormaps.h +++ b/src/r_data/colormaps.h @@ -72,16 +72,6 @@ inline uint32 MakeSpecialColormap(int index) return index | SPECIALCOLORMAP_MASK; } -inline bool IsSpecialColormap(uint32 map) -{ - return (map & 0xFFFF0000) == SPECIALCOLORMAP_MASK; -} - -inline int GetSpecialColormap(int blend) -{ - return IsSpecialColormap(blend) ? blend & 0xFFFF : NOFIXEDCOLORMAP; -} - int AddSpecialColormap(float r1, float g1, float b1, float r2, float g2, float b2); diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 47a10f95d..fe32fbb3e 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -4362,9 +4362,8 @@ ExpEmit FxDotCross::Emit(VMFunctionBuilder *build) FxTypeCheck::FxTypeCheck(FxExpression *l, FxExpression *r) : FxExpression(EFX_TypeCheck, l->ScriptPosition) { - left = new FxTypeCast(l, NewPointer(RUNTIME_CLASS(DObject)), false); - right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), r); - EmitTail = false; + left = l; + right = r; ValueType = TypeBool; } @@ -4389,9 +4388,27 @@ FxTypeCheck::~FxTypeCheck() FxExpression *FxTypeCheck::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); + // This must resolve the cast separately so that it can set the proper type for class descriptors. RESOLVE(left, ctx); RESOLVE(right, ctx); ABORT(right && left); + + if (left->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) + { + left = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), left); + ClassCheck = true; + } + else + { + left = new FxTypeCast(left, NewPointer(RUNTIME_CLASS(DObject)), false); + ClassCheck = false; + } + right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), right); + + RESOLVE(left, ctx); + RESOLVE(right, ctx); + ABORT(right && left); + return this; } @@ -4408,7 +4425,8 @@ ExpEmit FxTypeCheck::EmitCommon(VMFunctionBuilder *build) castee.Free(build); casttype.Free(build); ExpEmit ares(build, REGT_POINTER); - build->Emit(casttype.Konst ? OP_DYNCAST_K : OP_DYNCAST_R, ares.RegNum, castee.RegNum, casttype.RegNum); + if (!ClassCheck) build->Emit(casttype.Konst ? OP_DYNCAST_K : OP_DYNCAST_R, ares.RegNum, castee.RegNum, casttype.RegNum); + else build->Emit(casttype.Konst ? OP_DYNCASTC_K : OP_DYNCASTC_R, ares.RegNum, castee.RegNum, casttype.RegNum); return ares; } @@ -9938,37 +9956,76 @@ ExpEmit FxJumpStatement::Emit(VMFunctionBuilder *build) //========================================================================== FxReturnStatement::FxReturnStatement(FxExpression *value, const FScriptPosition &pos) -: FxExpression(EFX_ReturnStatement, pos), Value(value) +: FxExpression(EFX_ReturnStatement, pos) { + if (value != nullptr) Args.Push(value); + ValueType = TypeVoid; +} + +FxReturnStatement::FxReturnStatement(FArgumentList &values, const FScriptPosition &pos) + : FxExpression(EFX_ReturnStatement, pos) +{ + Args = std::move(values); ValueType = TypeVoid; } FxReturnStatement::~FxReturnStatement() { - SAFE_DELETE(Value); } FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) { + bool fail = false; CHECKRESOLVED(); - SAFE_RESOLVE_OPT(Value, ctx); + for (auto &Value : Args) + { + SAFE_RESOLVE_OPT(Value, ctx); + fail |= (Value == nullptr); + } + if (fail) + { + delete this; + return nullptr; + } PPrototype *retproto; - if (Value == nullptr) + if (Args.Size() == 0) { TArray none(0); retproto = NewPrototype(none, none); } - else + else if (Args.Size() == 1) { // If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.) if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() > 0 && ctx.Function->SymbolName != NAME_None) { - Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[0], false, false); - Value = Value->Resolve(ctx); - ABORT(Value); + Args[0] = new FxTypeCast(Args[0], ctx.ReturnProto->ReturnTypes[0], false, false); + Args[0] = Args[0]->Resolve(ctx); + ABORT(Args[0]); } - retproto = Value->ReturnProto(); + retproto = Args[0]->ReturnProto(); + } + else if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() == Args.Size()) + { + for (unsigned i = 0; i < Args.Size(); i++) + { + auto &Value = Args[0]; + Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[i], false, false); + Value = Value->Resolve(ctx); + if (Value == nullptr) fail = true; + } + if (fail) + { + delete this; + return nullptr; + } + return this; // no point calling CheckReturn here. + } + else + { + ScriptPosition.Message(MSG_ERROR, "Incorrect number of return values. Got %u, but expected %u", Args.Size(), ctx.ReturnProto->ReturnTypes.Size()); + delete this; + return nullptr; } ctx.CheckReturn(retproto, ScriptPosition); @@ -9978,8 +10035,20 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) { + TArray outs; + ExpEmit out(0, REGT_NIL); + // If there's structs to destroy here we need to emit all returns before destroying them. + if (build->ConstructedStructs.Size()) + { + for (auto ret : Args) + { + ExpEmit r = ret->Emit(build); + outs.Push(r); + } + } + // call the destructors for all structs requiring one. // go in reverse order of construction for (int i = build->ConstructedStructs.Size() - 1; i >= 0; i--) @@ -9995,19 +10064,19 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) // If we return nothing, use a regular RET opcode. // Otherwise just return the value we're given. - if (Value == nullptr) + if (Args.Size() == 0) { build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); } - else + else if (Args.Size() == 1) { - out = Value->Emit(build); + out = outs.Size() > 0? outs[0] : Args[0]->Emit(build); // Check if it is a function call that simplified itself // into a tail call in which case we don't emit anything. if (!out.Final) { - if (Value->ValueType == TypeVoid) + if (Args[0]->ValueType == TypeVoid) { // Nothing is returned. build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); } @@ -10017,6 +10086,14 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) } } } + else + { + for (unsigned i = 0; i < Args.Size(); i++) + { + out = outs.Size() > 0 ? outs[i] : Args[i]->Emit(build); + build->Emit(OP_RET, i < Args.Size() - 1 ? i : i+RET_FINAL, EncodeRegType(out), out.RegNum); + } + } out.Final = true; return out; @@ -10024,9 +10101,9 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) VMFunction *FxReturnStatement::GetDirectFunction() { - if (Value != nullptr) + if (Args.Size() == 1) { - return Value->GetDirectFunction(); + return Args[0]->GetDirectFunction(); } return nullptr; } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index e7cfab662..8b43d28fa 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -1086,7 +1086,7 @@ class FxTypeCheck : public FxExpression public: FxExpression *left; FxExpression *right; - bool EmitTail; + bool ClassCheck; FxTypeCheck(FxExpression*, FxExpression*); ~FxTypeCheck(); @@ -1911,10 +1911,11 @@ public: class FxReturnStatement : public FxExpression { - FxExpression *Value; + FArgumentList Args; public: FxReturnStatement(FxExpression *value, const FScriptPosition &pos); + FxReturnStatement(FArgumentList &args, const FScriptPosition &pos); ~FxReturnStatement(); FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index c839a2dc0..7dceceb32 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -913,13 +913,14 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag) if (prop != NULL) { - if (bag.Info->IsDescendantOf(*prop->cls)) + auto pcls = PClass::FindActor(prop->clsname); + if (bag.Info->IsDescendantOf(pcls)) { ParsePropertyParams(sc, prop, (AActor *)bag.Info->Defaults, bag); } else { - sc.ScriptMessage("'%s' requires an actor of type '%s'\n", propname.GetChars(), (*prop->cls)->TypeName.GetChars()); + sc.ScriptMessage("'%s' requires an actor of type '%s'\n", propname.GetChars(), pcls->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } } diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 9d41b5265..76a209722 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -187,6 +187,7 @@ enum DEPF_HEXENBOUNCE, DEPF_DOOMBOUNCE, DEPF_INTERHUBSTRIP, + DEPF_NOTRAIL, }; // Types of old style decorations @@ -229,7 +230,7 @@ struct FPropertyInfo { const char *name; const char *params; - const PClass * const *cls; + const char *clsname; PropHandler Handler; int category; }; @@ -241,17 +242,24 @@ int MatchString (const char *in, const char **strings); #define DEFINE_PROPERTY_BASE(name, paramlist, clas, cat) \ static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \ static FPropertyInfo Prop_##name##_##paramlist##_##clas = \ - { #name, #paramlist, &RUNTIME_CLASS_CASTLESS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ + { #name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \ static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params) #define DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \ static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \ static FPropertyInfo Prop_##name##_##paramlist##_##clas = \ -{ #prefix"."#name, #paramlist, &RUNTIME_CLASS_CASTLESS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ +{ #prefix"."#name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \ static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params) +#define DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \ + static void Handler_##name##_##paramlist##_##clas(AActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \ + static FPropertyInfo Prop_##name##_##paramlist##_##clas = \ +{ #prefix"."#name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ + MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \ + static void Handler_##name##_##paramlist##_##clas(AActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params) + #define DEFINE_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_PROPERTY) #define DEFINE_INFO_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_INFO) @@ -259,6 +267,9 @@ int MatchString (const char *in, const char **strings); #define DEFINE_CLASS_PROPERTY(name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(clas, name, paramlist, clas, CAT_PROPERTY) #define DEFINE_CLASS_PROPERTY_PREFIX(prefix, name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, CAT_PROPERTY) +#define DEFINE_SCRIPTED_PROPERTY(name, paramlist, clas) DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(clas, name, paramlist, clas, CAT_PROPERTY) +#define DEFINE_SCRIPTED_PROPERTY_PREFIX(prefix, name, paramlist, clas) DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(prefix, name, paramlist, clas, CAT_PROPERTY) + #define PROP_PARM_COUNT (params[0].i) #define PROP_STRING_PARM(var, no) \ diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index a3f5c144c..56209e114 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -100,6 +100,7 @@ static FFlagDef InternalActorFlagDefs[]= DEFINE_FLAG(MF6, INTRYMOVE, AActor, flags6), DEFINE_FLAG(MF7, HANDLENODELAY, AActor, flags7), DEFINE_FLAG(MF7, FLYCHEAT, AActor, flags7), + DEFINE_FLAG(FX, RESPAWNINVUL, AActor, effects), }; @@ -461,7 +462,7 @@ static FFlagDef PlayerPawnFlagDefs[] = static FFlagDef PowerSpeedFlagDefs[] = { // PowerSpeed flags - DEFINE_FLAG(PSF, NOTRAIL, APowerSpeed, SpeedFlags), + DEFINE_DEPRECATED_FLAG(NOTRAIL), }; static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int NumDefs; int Use; } FlagLists[] = @@ -472,7 +473,6 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int { &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs), 3 }, { &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs), 3 }, { &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs), 3 }, - { &RUNTIME_CLASS_CASTLESS(APowerSpeed), PowerSpeedFlagDefs, countof(PowerSpeedFlagDefs), 1 }, }; #define NUM_FLAG_LISTS (countof(FlagLists)) @@ -547,6 +547,12 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo } } } + + // Handle that lone PowerSpeed flag - this should be more generalized but it's just this one flag and unlikely to become more so an explicit check will do. + if ((!stricmp(part1, "NOTRAIL") && !strict) || (!stricmp(part1, "POWERSPEED") && !stricmp(part2, "NOTRAIL"))) + { + return &PowerSpeedFlagDefs[0]; + } return NULL; } @@ -782,6 +788,11 @@ void InitThingdef() PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh); GlobalSymbols.AddSymbol(dehf); + // set up a variable for the global gameinfo data + PStruct *gistruct = NewNativeStruct("GameInfoStruct", nullptr); + PField *gi = new PField("gameinfo", gistruct, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gameinfo); + GlobalSymbols.AddSymbol(gi); + // set up a variable for the global players array. PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); pstruct->Size = sizeof(player_t); @@ -790,6 +801,9 @@ void InitThingdef() PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players); GlobalSymbols.AddSymbol(playerf); + pstruct->AddNativeField("weapons", NewNativeStruct("WeaponSlots", nullptr), myoffsetof(player_t, weapons), VARF_Native); + + parray = NewArray(TypeBool, MAXPLAYERS); playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame); GlobalSymbols.AddSymbol(playerf); @@ -925,7 +939,7 @@ DEFINE_ACTION_FUNCTION(FString, Replace) PARAM_SELF_STRUCT_PROLOGUE(FString); PARAM_STRING(s1); PARAM_STRING(s2); - self->Substitute(*s1, *s2); + self->Substitute(s1, s2); return 0; } diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index db24349cb..f441f8417 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -354,6 +354,23 @@ void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int in break; case DEPF_INTERHUBSTRIP: // Old system was 0 or 1, so if the flag is cleared, assume 1. static_cast(defaults)->InterHubAmount = set ? 0 : 1; + break; + case DEPF_NOTRAIL: + { + FString propname = "@property@powerspeed.notrail"; + FName name(propname, true); + if (name != NAME_None) + { + auto propp = dyn_cast(info->Symbols.FindSymbol(name, true)); + if (propp != nullptr) + { + *((char*)defaults + propp->Variables[0]->Offset) = set ? 1 : 0; + } + } + break; + } + + default: break; // silence GCC } @@ -439,12 +456,45 @@ int MatchString (const char *in, const char **strings) return -1; } +//========================================================================== +// +// Get access to scripted fields. +// Fortunately there's only a handful that cannot be done with a +// scripted property definition, most notably the powerup and morph stuff. +// +//========================================================================== + +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 || PointerCheck(sym->Type, type))) + { + return (((char*)obj) + sym->Offset); + } + I_Error("Variable %s of type %s not found in %s\n", field.GetChars(), type->DescriptiveName(), cls->TypeName.GetChars()); + return nullptr; +} + +template +T &TypedScriptVar(DObject *obj, PClass *cls, FName field, PType *type) +{ + return *(T*)ScriptVar(obj, cls, field, type); +} + //========================================================================== // // Info Property handlers // //========================================================================== + //========================================================================== // //========================================================================== @@ -2235,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 { @@ -2264,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; @@ -2290,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 { @@ -2334,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 { @@ -2359,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 { @@ -2383,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 { @@ -2402,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); @@ -2422,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; } //========================================================================== @@ -3009,37 +3040,37 @@ DEFINE_CLASS_PROPERTY(unmorphflash, S, MorphProjectile) //========================================================================== // (non-fatal with non-existent types only in DECORATE) //========================================================================== -DEFINE_CLASS_PROPERTY(playerclass, S, PowerMorph) +DEFINE_SCRIPTED_PROPERTY(playerclass, S, PowerMorph) { PROP_STRING_PARM(str, 0); - defaults->PlayerClass = FindClassTentativePlayerPawn(str, bag.fromDecorate); + TypedScriptVar(defaults, bag.Info, NAME_PlayerClass, NewClassPointer(RUNTIME_CLASS(APlayerPawn))) = FindClassTentativePlayerPawn(str, bag.fromDecorate); } //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY(morphstyle, M, PowerMorph) +DEFINE_SCRIPTED_PROPERTY(morphstyle, M, PowerMorph) { PROP_INT_PARM(i, 0); - defaults->MorphStyle = i; + TypedScriptVar(defaults, bag.Info, NAME_MorphStyle, TypeSInt32) = i; } //========================================================================== // (non-fatal with non-existent types only in DECORATE) //========================================================================== -DEFINE_CLASS_PROPERTY(morphflash, S, PowerMorph) +DEFINE_SCRIPTED_PROPERTY(morphflash, S, PowerMorph) { PROP_STRING_PARM(str, 0); - defaults->MorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); + TypedScriptVar(defaults, bag.Info, NAME_MorphFlash, NewClassPointer(RUNTIME_CLASS(AActor))) = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); } //========================================================================== // (non-fatal with non-existent types only in DECORATE) //========================================================================== -DEFINE_CLASS_PROPERTY(unmorphflash, S, PowerMorph) +DEFINE_SCRIPTED_PROPERTY(unmorphflash, S, PowerMorph) { PROP_STRING_PARM(str, 0); - defaults->UnMorphFlash = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); + TypedScriptVar(defaults, bag.Info, NAME_UnMorphFlash, NewClassPointer(RUNTIME_CLASS(AActor))) = FindClassTentative(str, RUNTIME_CLASS(AActor), bag.fromDecorate); } diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 2a76f8e2e..a9fdf3abe 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/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 51c224649..d916658d7 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -401,7 +401,7 @@ begin: OP(MOVEA): { ASSERTA(a); ASSERTA(B); - int b = B; + b = B; reg.a[a] = reg.a[b]; reg.atag[a] = reg.atag[b]; NEXTOP; @@ -409,7 +409,7 @@ begin: OP(MOVEV2): { ASSERTF(a); ASSERTF(B); - int b = B; + b = B; reg.f[a] = reg.f[b]; reg.f[a + 1] = reg.f[b + 1]; NEXTOP; @@ -417,7 +417,7 @@ begin: OP(MOVEV3): { ASSERTF(a); ASSERTF(B); - int b = B; + b = B; reg.f[a] = reg.f[b]; reg.f[a + 1] = reg.f[b + 1]; reg.f[a + 2] = reg.f[b + 2]; @@ -435,6 +435,18 @@ begin: reg.a[a] = (reg.a[b] && ((DObject*)(reg.a[b]))->IsKindOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr; reg.atag[a] = ATAG_OBJECT; NEXTOP; + OP(DYNCASTC_R) : + ASSERTA(a); ASSERTA(B); ASSERTA(C); + b = B; + reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(reg.a[C]))) ? reg.a[b] : nullptr; + reg.atag[a] = ATAG_OBJECT; + NEXTOP; + OP(DYNCASTC_K) : + ASSERTA(a); ASSERTA(B); ASSERTKA(C); + b = B; + reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr; + reg.atag[a] = ATAG_OBJECT; + NEXTOP; OP(CAST): if (C == CAST_I2F) { @@ -1746,9 +1758,21 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c break; case CAST_P2S: + { ASSERTS(a); ASSERTA(b); - reg.s[a].Format("%s<%p>", reg.atag[b] == ATAG_OBJECT ? (reg.a[b] == nullptr? "Object" : ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars() ) : "Pointer", reg.a[b]); - break; + if (reg.a[b] == nullptr) reg.s[a] = "null"; + else if (reg.atag[b] == ATAG_OBJECT) + { + auto op = static_cast(reg.a[b]); + if (op->IsKindOf(RUNTIME_CLASS(PClass))) reg.s[a].Format("Class<%s>", static_cast(op)->TypeName.GetChars()); + else reg.s[a].Format("Object<%p>", ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars()); + } + else + { + reg.s[a].Format("%s<%p>", "Pointer", reg.a[b]); + } + break; + } case CAST_S2I: ASSERTD(a); ASSERTS(b); diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 3b97eb614..7615a4f0f 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -36,6 +36,7 @@ #include "dobject.h" #include "v_text.h" #include "stats.h" +#include "templates.h" cycle_t VMCycles[10]; int VMCalls[10]; @@ -605,11 +606,16 @@ ADD_STAT(VM) { double added = 0; int addedc = 0; - for (auto d : VMCycles) added += d.TimeMS(); + double peak = 0; + for (auto d : VMCycles) + { + added += d.TimeMS(); + peak = MAX(peak, d.TimeMS()); + } for (auto d : VMCalls) addedc += d; memmove(&VMCycles[1], &VMCycles[0], 9 * sizeof(cycle_t)); memmove(&VMCalls[1], &VMCalls[0], 9 * sizeof(int)); VMCycles[0].Reset(); VMCalls[0] = 0; - return FStringf("VM time in last 10 tics: %f ms, %d calls", added, addedc); + return FStringf("VM time in last 10 tics: %f ms, %d calls, peak = %f ms", added, addedc, peak); } diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 889706726..0829ab2c6 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -86,6 +86,8 @@ xx(CAST, cast, CAST, NOP, 0, 0), // xA = xB, conversion specified by C xx(CASTB, castb, CAST, NOP, 0, 0), // xA = !!xB, type specified by C xx(DYNCAST_R, dyncast, RPRPRP, NOP, 0, 0), // aA = dyn_cast(aB); xx(DYNCAST_K, dyncast, RPRPKP, NOP, 0, 0), // aA = dyn_cast(aB); +xx(DYNCASTC_R, dyncastc, RPRPRP, NOP, 0, 0), // aA = dyn_cast(aB); for class types +xx(DYNCASTC_K, dyncastc, RPRPKP, NOP, 0, 0), // aA = dyn_cast(aB); // Control flow. xx(TEST, test, RII16, NOP, 0, 0), // if (dA != BC) then pc++ diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 1687983f2..da10c0cfb 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2022,13 +2022,14 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro if (property != nullptr && property->category != CAT_INFO) { - if (cls->IsDescendantOf(*property->cls)) + auto pcls = PClass::FindActor(property->clsname); + if (cls->IsDescendantOf(pcls)) { DispatchProperty(property, prop, (AActor *)bag.Info->Defaults, bag); } else { - Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), (*property->cls)->TypeName.GetChars()); + Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), pcls->TypeName.GetChars()); } } else @@ -3313,16 +3314,9 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) { return new FxReturnStatement(nullptr, *ast); } - else if (args.Size() == 1) - { - auto arg = args[0]; - args[0] = nullptr; - return new FxReturnStatement(arg, *ast); - } else { - Error(ast, "Return with multiple values not implemented yet."); - return new FxReturnStatement(nullptr, *ast); + return new FxReturnStatement(args, *ast); } } diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 67f46b266..d7788ae7a 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -615,8 +615,8 @@ SoundDecoder *SoundRenderer::CreateDecoder(FileReader *reader) SoundDecoder *decoder = NULL; int pos = reader->Tell(); -#ifdef HAVE_MPG123 - decoder = new MPG123Decoder; +#ifdef HAVE_SNDFILE + decoder = new SndFileDecoder; if (decoder->open(reader)) return decoder; reader->Seek(pos, SEEK_SET); @@ -624,8 +624,8 @@ SoundDecoder *SoundRenderer::CreateDecoder(FileReader *reader) delete decoder; decoder = NULL; #endif -#ifdef HAVE_SNDFILE - decoder = new SndFileDecoder; +#ifdef HAVE_MPG123 + decoder = new MPG123Decoder; if (decoder->open(reader)) return decoder; reader->Seek(pos, SEEK_SET); diff --git a/src/sound/mpg123_decoder.cpp b/src/sound/mpg123_decoder.cpp index 9d1fff97e..0a2ba94f1 100644 --- a/src/sound/mpg123_decoder.cpp +++ b/src/sound/mpg123_decoder.cpp @@ -14,25 +14,22 @@ static bool inited = false; off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence) { - MPG123Decoder *self = reinterpret_cast(handle); - FileReader *reader = self->Reader; + FileReader *reader = reinterpret_cast(handle)->Reader; - if(whence == SEEK_SET) - offset += self->StartOffset; - else if(whence == SEEK_CUR) + if(whence == SEEK_CUR) { - if(offset < 0 && reader->Tell()+offset < self->StartOffset) + if(offset < 0 && reader->Tell()+offset < 0) return -1; } else if(whence == SEEK_END) { - if(offset < 0 && reader->GetLength()+offset < self->StartOffset) + if(offset < 0 && reader->GetLength()+offset < 0) return -1; } if(reader->Seek(offset, whence) != 0) return -1; - return reader->Tell() - self->StartOffset; + return reader->Tell(); } ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes) @@ -71,45 +68,7 @@ bool MPG123Decoder::open(FileReader *reader) } Reader = reader; - StartOffset = 0; - char data[10]; - if(file_read(this, data, 10) != 10) - return false; - - int start_offset = 0; - // Check for ID3 tags and skip them - if(memcmp(data, "ID3", 3) == 0 && - (BYTE)data[3] <= 4 && (BYTE)data[4] != 0xff && - (data[5]&0x0f) == 0 && (data[6]&0x80) == 0 && - (data[7]&0x80) == 0 && (data[8]&0x80) == 0 && - (data[9]&0x80) == 0) - { - // ID3v2 - start_offset = (data[6]<<21) | (data[7]<<14) | - (data[8]<< 7) | (data[9] ); - start_offset += ((data[5]&0x10) ? 20 : 10); - } - - StartOffset = start_offset; - if(file_lseek(this, 0, SEEK_SET) != 0) - return false; - - // Check for a frame header - bool frame_ok = false; - if(file_read(this, data, 3) == 3) - { - if((BYTE)data[0] == 0xff && - ((data[1]&0xfe) == 0xfa/*MPEG-1*/ || (data[1]&0xfe) == 0xf2/*MPEG-2*/)) - { - int brate_idx = (data[2]>>4) & 0x0f; - int srate_idx = (data[2]>>2) & 0x03; - if(brate_idx != 0 && brate_idx != 15 && srate_idx != 3) - frame_ok = (file_lseek(this, 0, SEEK_SET) == 0); - } - } - - if(frame_ok) { MPG123 = mpg123_new(NULL, NULL); if(mpg123_replace_reader_handle(MPG123, file_read, file_lseek, NULL) == MPG123_OK && diff --git a/src/sound/mpg123_decoder.h b/src/sound/mpg123_decoder.h index 1d5b883ea..051473abb 100644 --- a/src/sound/mpg123_decoder.h +++ b/src/sound/mpg123_decoder.h @@ -30,7 +30,6 @@ private: bool Done; FileReader *Reader; - int StartOffset; static off_t file_lseek(void *handle, off_t offset, int whence); static ssize_t file_read(void *handle, void *buffer, size_t bytes); diff --git a/src/sound/sndfile_decoder.cpp b/src/sound/sndfile_decoder.cpp index b0c35c2a0..02f24b9da 100644 --- a/src/sound/sndfile_decoder.cpp +++ b/src/sound/sndfile_decoder.cpp @@ -60,6 +60,7 @@ bool SndFileDecoder::open(FileReader *reader) SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell }; Reader = reader; + SndInfo.format = 0; SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this); if (SndFile) { diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 16f3b6f6c..fd8bce922 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -15,6 +15,7 @@ #include "zscript/inventory/powerups.txt" #include "zscript/shared/player.txt" +#include "zscript/shared/player_cheat.txt" #include "zscript/shared/morph.txt" #include "zscript/shared/botstuff.txt" #include "zscript/shared/sharedmisc.txt" diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 465c7f4e0..f8ee566d9 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -487,6 +487,8 @@ class Actor : Thinker native native void AddInventory(Inventory inv); native void RemoveInventory(Inventory inv); native void ClearInventory(); + native bool GiveInventory(class type, int amount, bool givecheat = false); + native bool TakeInventory(class itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); native Inventory FindInventory(class itemtype, bool subclass = false); native Inventory GiveInventoryType(class itemtype); native Inventory DropInventory (Inventory item); diff --git a/wadsrc/static/zscript/actor_checks.txt b/wadsrc/static/zscript/actor_checks.txt index a4f7da1d8..eea10b5ec 100644 --- a/wadsrc/static/zscript/actor_checks.txt +++ b/wadsrc/static/zscript/actor_checks.txt @@ -106,11 +106,15 @@ extend class Actor //========================================================================== // - // + // rather pointless these days to do it this way. // //========================================================================== - native bool CheckArmorType(name Type, int amount = 1); + bool CheckArmorType(name Type, int amount = 1) + { + let myarmor = BasicArmor(FindInventory("BasicArmor")); + return myarmor != null && myarmor.ArmorType == type && myarmor.Amount >= amount; + } action state A_JumpIfArmorType(name Type, statelabel label, int amount = 1) { diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index e9e0a0673..9cad3224e 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -32,11 +32,23 @@ struct TexMan native static TextureID CheckForTexture(String name, int usetype, int flags = TryAny); } -struct Screen +struct Screen native { native static void DrawHUDTexture(TextureID tex, double x, double y); } +struct Console native +{ + native static void HideConsole(); +} + +struct GameInfoStruct native +{ + // will be extended as needed. + native Name backpacktype; + native double Armor2Percent; +} + class Object native { native bool bDestroyed; @@ -200,6 +212,7 @@ struct DehInfo native native double ExplosionAlpha; native int NoAutofreeze; native int BFGCells; + native int BlueAC; } struct State native diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index 90ed458d0..a27240eb7 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -64,6 +64,7 @@ class Inventory : Actor native virtual bool GetNoTeleportFreeze() { return false; } virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {} virtual void AlterWeaponSprite(VisStyle vis, in out int changed) {} + virtual void OwnerDied() {} native bool GoAway(); native void GoAwayAndDie(); diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index b744bfc97..e9325c004 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 { @@ -14,6 +14,48 @@ class PowerupGiver : Inventory native +INVENTORY.FANCYPICKUPSOUND Inventory.PickupSound "misc/p_pkup"; } + + //=========================================================================== + // + // APowerupGiver :: Use + // + //=========================================================================== + + override bool Use (bool pickup) + { + if (PowerupType == NULL) return true; // item is useless + if (Owner == null) return true; + + let power = Powerup(Spawn (PowerupType)); + + if (EffectTics != 0) + { + power.EffectTics = EffectTics; + } + if (BlendColor != 0) + { + if (BlendColor != Powerup.SPECIALCOLORMAP_MASK | 65535) power.BlendColor = BlendColor; + else power.BlendColor = 0; + } + if (Mode != 'None') + { + power.Mode = Mode; + } + if (Strength != 0) + { + power.Strength = Strength; + } + + power.bAlwaysPickup |= bAlwaysPickup; + power.bAdditiveTime |= bAdditiveTime; + power.bNoTeleportFreeze |= bNoTeleportFreeze; + if (power.CallTryPickup (Owner)) + { + return true; + } + power.GoAwayAndDie (); + return false; + } } class Powerup : Inventory native @@ -22,13 +64,245 @@ class Powerup : Inventory native native color BlendColor; native Name Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility native double Strength; // Meaning depends on powerup - currently used only by Invisibility + native int Colormap; + const SPECIALCOLORMAP_MASK = 0x00b60000; // Note, that while this is an inventory flag, it only has meaning on an active powerup. - override bool GetNoTeleportFreeze() { return bNoTeleportFreeze; } + override bool GetNoTeleportFreeze() + { + return bNoTeleportFreeze; + } + + //=========================================================================== + // + // APowerup :: Tick + // + //=========================================================================== + + override void Tick () + { + // Powerups cannot exist outside an inventory + if (Owner == NULL) + { + Destroy (); + } + if (EffectTics > 0 && --EffectTics == 0) + { + Destroy (); + } + } + + //=========================================================================== + // + // APowerup :: HandlePickup + // + //=========================================================================== + + override bool HandlePickup (Inventory item) + { + if (item.GetClass() == GetClass()) + { + let power = Powerup(item); + if (power.EffectTics == 0) + { + power.bPickupGood = true; + return true; + } + // Color gets transferred if the new item has an effect. + + // Increase the effect's duration. + if (power.bAdditiveTime) + { + EffectTics += power.EffectTics; + BlendColor = power.BlendColor; + } + // If it's not blinking yet, you can't replenish the power unless the + // powerup is required to be picked up. + else if (EffectTics > BLINKTHRESHOLD && !power.bAlwaysPickup) + { + return true; + } + // Reset the effect duration. + else if (power.EffectTics > EffectTics) + { + EffectTics = power.EffectTics; + BlendColor = power.BlendColor; + } + power.bPickupGood = true; + return true; + } + return false; + } + + //=========================================================================== + // + // APowerup :: CreateCopy + // + //=========================================================================== + + override Inventory CreateCopy (Actor other) + { + // Get the effective effect time. + EffectTics = abs (EffectTics); + // Abuse the Owner field to tell the + // InitEffect method who started it; + // this should be cleared afterwards, + // as this powerup instance is not + // properly attached to anything yet. + Owner = other; + // Actually activate the powerup. + InitEffect (); + // Clear the Owner field, unless it was + // changed by the activation, for example, + // if this instance is a morph powerup; + // the flag tells the caller that the + // ownership has changed so that they + // can properly handle the situation. + if (!bCreateCopyMoved) + { + Owner = NULL; + } + // All done. + return self; + } + + //=========================================================================== + // + // APowerup :: CreateTossable + // + // Powerups are never droppable, even without IF_UNDROPPABLE set. + // + //=========================================================================== + + override Inventory CreateTossable () + { + return NULL; + } + + //=========================================================================== + // + // APowerup :: InitEffect + // + //=========================================================================== + + virtual void InitEffect() + { + // initialize this only once instead of recalculating repeatedly. + Colormap = ((BlendColor & 0xFFFF0000) == SPECIALCOLORMAP_MASK)? BlendColor & 0xffff : PlayerInfo.NOFIXEDCOLORMAP; + } + + //=========================================================================== + // + // APowerup :: DoEffect + // + //=========================================================================== + + override void DoEffect () + { + if (Owner == NULL || Owner.player == NULL) + { + return; + } + + if (EffectTics > 0) + { + if (Colormap != PlayerInfo.NOFIXEDCOLORMAP) + { + if (!isBlinking()) + { + Owner.player.fixedcolormap = Colormap; + } + else if (Owner.player.fixedcolormap == Colormap) + { + // only unset if the fixed colormap comes from this item + Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP; + } + } + } + } + + //=========================================================================== + // + // APowerup :: EndEffect + // + //=========================================================================== + + virtual void EndEffect () + { + if (colormap != PlayerInfo.NOFIXEDCOLORMAP && Owner && Owner.player && Owner.player.fixedcolormap == colormap) + { // only unset if the fixed colormap comes from this item + Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP; + } + } + + //=========================================================================== + // + // APowerup :: Destroy + // + //=========================================================================== + + override void OnDestroy () + { + EndEffect (); + Super.OnDestroy(); + } + + //=========================================================================== + // + // APowerup :: GetBlend + // + //=========================================================================== + + override color GetBlend () + { + if (Colormap != Player.NOFIXEDCOLORMAP) return 0; + if (isBlinking()) return 0; + return BlendColor; + } + + //=========================================================================== + // + // APowerup :: DrawPowerup + // + //=========================================================================== + + override bool DrawPowerup (int x, int y) + { + if (!Icon.isValid()) + { + return false; + } + if (!isBlinking()) + { + screen.DrawHUDTexture(Icon, x, y); + } + return true; + } + + //=========================================================================== + // + // APowerup :: isBlinking + // + //=========================================================================== + + virtual bool isBlinking() + { + return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink); + } + + //=========================================================================== + // + // APowerup :: OwnerDied + // + // Powerups don't last beyond death. + // + //=========================================================================== + + override void OwnerDied () + { + Destroy (); + } - native virtual void InitEffect(); - native virtual void EndEffect(); - native bool isBlinking(); } @@ -38,14 +312,134 @@ class Powerup : Inventory native // //=========================================================================== -class PowerInvulnerable : Powerup native +class PowerInvulnerable : Powerup { Default { Powerup.Duration -30; inventory.icon "SPSHLD0"; } - + + //=========================================================================== + // + // APowerInvulnerable :: InitEffect + // + //=========================================================================== + + override void InitEffect () + { + Super.InitEffect(); + Owner.bRespawnInvul = false; + Owner.bInvulnerable = true; + if (Mode == 'None' && Owner is "PlayerPawn") + { + Mode = PlayerPawn(Owner).InvulMode; + } + if (Mode == 'Reflective') + { + Owner.bReflective = true; + } + } + + //=========================================================================== + // + // APowerInvulnerable :: DoEffect + // + //=========================================================================== + + override void DoEffect () + { + Super.DoEffect (); + + if (Owner == NULL) + { + return; + } + + if (Mode == 'Ghost') + { + if (!Owner.bShadow) + { + // Don't mess with the translucency settings if an + // invisibility powerup is active. + let alpha = Owner.Alpha; + if (!(level.time & 7) && alpha > 0 && alpha < 1) + { + if (alpha == HX_SHADOW) + { + alpha = HX_ALTSHADOW; + } + else + { + alpha = 0; + Owner.bNonShootable = true; + } + } + if (!(level.time & 31)) + { + if (alpha == 0) + { + Owner.bNonShootable = false; + alpha = HX_ALTSHADOW; + } + else + { + alpha = HX_SHADOW; + } + } + Owner.A_SetRenderStyle(alpha, STYLE_Translucent); + } + else + { + Owner.bNonShootable = false; + } + } + } + + //=========================================================================== + // + // APowerInvulnerable :: EndEffect + // + //=========================================================================== + + override void EndEffect () + { + Super.EndEffect(); + + if (Owner == NULL) + { + return; + } + + Owner.bRespawnInvul = false; + Owner.bInvulnerable = false; + if (Mode == 'Ghost') + { + Owner.bNonShootable = false; + if (!bShadow) + { + // Don't mess with the translucency settings if an + // invisibility powerup is active. + Owner.A_SetRenderStyle(1, STYLE_Normal); + } + } + else if (Mode == 'Reflective') + { + Owner.bReflective = false; + } + + if (Owner.player != NULL) + { + Owner.player.fixedcolormap = PlayerInfo.NOFIXEDCOLORMAP; + } + } + + //=========================================================================== + // + // APowerInvulnerable :: AlterWeaponSprite + // + //=========================================================================== + override void AlterWeaponSprite (VisStyle vis, in out int changed) { if (Owner != NULL) @@ -761,13 +1155,12 @@ class PowerWeaponLevel2 : Powerup // //=========================================================================== -class PowerSpeed : Powerup native +class PowerSpeed : Powerup { - native int SpeedFlags; + int NoTrail; + + Property NoTrail: NoTrail; - const PSF_NOTRAIL = 1; - - Default { Powerup.Duration -45; @@ -797,7 +1190,7 @@ class PowerSpeed : Powerup native if (Owner.player.cheats & CF_PREDICTING) return; - if (SpeedFlags & PSF_NOTRAIL) + if (NoTrail) return; if (level.time & 1) @@ -808,7 +1201,7 @@ class PowerSpeed : Powerup native for (Inventory item = Inv; item != NULL; item = item.Inv) { let sitem = PowerSpeed(item); - if (sitem != null && !(sitem.SpeedFlags & PSF_NOTRAIL)) + if (sitem != null && !NoTrail) { return; } @@ -1552,13 +1945,13 @@ class PowerInfiniteAmmo : Powerup // //=========================================================================== -class PowerMorph : Powerup native +class PowerMorph : Powerup { - native Class PlayerClass; - native Class MorphFlash, UnMorphFlash; - native int MorphStyle; - native PlayerInfo MorphedPlayer; - + Class PlayerClass; + Class MorphFlash, UnMorphFlash; + int MorphStyle; + PlayerInfo MorphedPlayer; + Default { Powerup.Duration -40; diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index f1d75d5dd..ba2bf2ef5 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -125,3 +125,7 @@ class WeaponPiece : Inventory native } } +struct WeaponSlots native +{ + native bool, int, int LocateWeapon(class weap); +} \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 1d10d5895..dd6084e1d 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); @@ -260,11 +290,12 @@ struct PlayerInfo native // this is what internally is known as player_t native Actor ConversationPC; native double ConversationNPCAngle; native bool ConversationFaceTalker; + //native WeaponSlots weapons; <- defined internally + /* these are not doable yet ticcmd_t cmd; usercmd_t original_cmd; -userinfo_t userinfo; // [RH] who is this? -FWeaponSlots weapons; +userinfo_t userinfo; */ @@ -278,5 +309,6 @@ FWeaponSlots weapons; native PSprite FindPSprite(int id); native void SetLogNumber (int text); native void SetLogText (String text); + native String GetUserName(); } diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt new file mode 100644 index 000000000..30760fb5d --- /dev/null +++ b/wadsrc/static/zscript/shared/player_cheat.txt @@ -0,0 +1,402 @@ +/* +** player_cheat.txt +** +**--------------------------------------------------------------------------- +** Copyright 1999-2016 Randy Heit +** Copyright 2006-2017 Christoph Oelckers +** 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. +**--------------------------------------------------------------------------- +** +*/ + +extend class PlayerPawn +{ + enum EAll + { + ALL_NO, + ALL_YES, + ALL_YESYES + } + + native void CheatSuicide(); + + virtual void CheatGive (String name, int amount) + { + int i; + Class type; + let player = self.player; + + if (PlayerNumber() != consoleplayer) + A_Log(format ("%s is a cheater: give %s\n", player.GetUserName(), name)); + + if (player.mo == NULL || player.health <= 0) + { + return; + } + + int giveall = ALL_NO; + if (name ~== "all") + { + giveall = ALL_YES; + } + else if (name ~== "everything") + { + giveall = ALL_YESYES; + } + + if (name ~== "health") + { + if (amount > 0) + { + health += amount; + player.health = health; + } + else + { + player.health = health = GetMaxHealth(); + } + } + + if (giveall || name ~== "backpack") + { + // Select the correct type of backpack based on the game + type = (class)(gameinfo.backpacktype); + if (type != NULL) + { + GiveInventory(type, 1, true); + } + + if (!giveall) + return; + } + + if (giveall || name ~== "ammo") + { + // Find every unique type of ammo. Give it to the player if + // he doesn't have it already, and set each to its maximum. + for (i = 0; i < AllActorClasses.Size(); ++i) + { + type = (class)(AllActorClasses[i]); + + if (type != null && type.GetParentClass() == "Ammo") + { + let ammoitem = FindInventory(type); + if (ammoitem == NULL) + { + ammoitem = Inventory(Spawn (type)); + ammoitem.AttachToOwner (self); + ammoitem.Amount = ammoitem.MaxAmount; + } + else if (ammoitem.Amount < ammoitem.MaxAmount) + { + ammoitem.Amount = ammoitem.MaxAmount; + } + } + } + + if (!giveall) + return; + } + + if (giveall || name ~== "armor") + { + if (GameType() != GAME_Hexen) + { + let armoritem = BasicArmorPickup(Spawn("BasicArmorPickup")); + armoritem.SaveAmount = 100*deh.BlueAC; + armoritem.SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5; + if (!armoritem.CallTryPickup (self)) + { + armoritem.Destroy (); + } + } + else + { + for (i = 0; i < 4; ++i) + { + let armoritem = Inventory(Spawn("HexenArmor")); + armoritem.health = i; + armoritem.Amount = 0; + if (!armoritem.CallTryPickup (self)) + { + armoritem.Destroy (); + } + } + } + + if (!giveall) + return; + } + + if (giveall || name ~== "keys") + { + for (int i = 0; i < AllActorClasses.Size(); ++i) + { + if (AllActorClasses[i] is "Key") + { + readonly keyitem = GetDefaultByType ((class)(AllActorClasses[i])); + if (keyitem.KeyNumber != 0) + { + let item = Inventory(Spawn(AllActorClasses[i])); + if (!item.CallTryPickup (self)) + { + item.Destroy (); + } + } + } + } + if (!giveall) + return; + } + + if (giveall || name ~== "weapons") + { + let savedpending = player.PendingWeapon; + for (i = 0; i < AllActorClasses.Size(); ++i) + { + let type = (class)(AllActorClasses[i]); + if (type != null && type != "Weapon") + { + // Don't give replaced weapons unless the replacement was done by Dehacked. + let rep = GetReplacement(type); + if (rep == type || rep is "DehackedPickup") + { + // Give the weapon only if it is set in a weapon slot. + if (player.weapons.LocateWeapon(type)) + { + readonly def = GetDefaultByType (type); + if (giveall == ALL_YESYES || !def.bCheatNotWeapon) + { + GiveInventory(type, 1, true); + } + } + } + } + } + player.PendingWeapon = savedpending; + + if (!giveall) + return; + } + + if (giveall || name ~== "artifacts") + { + for (i = 0; i < AllActorClasses.Size(); ++i) + { + type = (class)(AllActorClasses[i]); + if (type!= null) + { + let def = GetDefaultByType (type); + if (def.Icon.isValid() && def.MaxAmount > 1 && + !(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor")) + { + // Do not give replaced items unless using "give everything" + if (giveall == ALL_YESYES || GetReplacement(type) == type) + { + GiveInventory(type, amount <= 0 ? def.MaxAmount : amount, true); + } + } + } + } + if (!giveall) + return; + } + + if (giveall || name ~== "puzzlepieces") + { + for (i = 0; i < AllActorClasses.Size(); ++i) + { + let type = (class)(AllActorClasses[i]); + if (type != null) + { + let def = GetDefaultByType (type); + if (def.Icon.isValid()) + { + // Do not give replaced items unless using "give everything" + if (giveall == ALL_YESYES || GetReplacement(type) == type) + { + GiveInventory(type, amount <= 0 ? def.MaxAmount : amount, true); + } + } + } + } + if (!giveall) + return; + } + + if (giveall) + return; + + type = name; + if (type == NULL) + { + if (PlayerNumber() == consoleplayer) + A_Log(format("Unknown item \"%s\"\n", name)); + } + else + { + GiveInventory(type, amount, true); + } + return; + } + + void CheatTakeType(class deletetype) + { + for (int i = 0; i < AllActorClasses.Size(); ++i) + { + let type = (class)(AllActorClasses[i]); + + if (type != null && type is deletetype) + { + let pack = FindInventory(type); + if (pack) pack.Destroy(); + } + } + } + + virtual void CheatTake (String name, int amount) + { + bool takeall; + Class type; + let player = self.player; + + + if (player.mo == NULL || player.health <= 0) + { + return; + } + + takeall = name ~== "all"; + + if (!takeall && name ~== "health") + { + if (player.mo.health - amount <= 0 + || player.health - amount <= 0 + || amount == 0) + { + + CheatSuicide (); + + if (PlayerNumber() == consoleplayer) + Console.HideConsole (); + + return; + } + + if (amount > 0) + { + if (player.mo) + { + player.mo.health -= amount; + player.health = player.mo.health; + } + else + { + player.health -= amount; + } + } + + if (!takeall) + return; + } + + if (takeall || name ~== "backpack") + { + CheatTakeType("BackpackItem"); + if (!takeall) + return; + } + + if (takeall || name ~== "ammo") + { + CheatTakeType("Ammo"); + if (!takeall) + return; + } + + if (takeall || name ~== "armor") + { + CheatTakeType("Armor"); + if (!takeall) + return; + } + + if (takeall || name ~== "keys") + { + CheatTakeType("Key"); + if (!takeall) + return; + } + + if (takeall || name ~== "weapons") + { + CheatTakeType("Weapon"); + CheatTakeType("WeaponHolder"); + player.ReadyWeapon = null; + player.PendingWeapon = WP_NOCHANGE; + + if (!takeall) + return; + } + + if (takeall || name ~== "artifacts") + { + for (int i = 0; i < AllActorClasses.Size(); ++i) + { + type = (class)(AllActorClasses[i]); + if (type!= null && !(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor")) + { + let pack = FindInventory(type); + if (pack) pack.Destroy(); + } + } + if (!takeall) + return; + } + + if (takeall || name ~== "puzzlepieces") + { + CheatTakeType("PuzzleItem"); + if (!takeall) + return; + } + + if (takeall) + return; + + type = name; + if (type == NULL) + { + if (PlayerNumber() == consoleplayer) + A_Log(format("Unknown item \"%s\"\n", name)); + } + else + { + TakeInventory(type, max(amount, 1)); + } + return; + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/strife/alienspectres.txt b/wadsrc/static/zscript/strife/alienspectres.txt index 791266cc0..d65142582 100644 --- a/wadsrc/static/zscript/strife/alienspectres.txt +++ b/wadsrc/static/zscript/strife/alienspectres.txt @@ -166,7 +166,7 @@ class AlienSpectre1 : SpectralMonster player.GiveInventoryType ("UpgradeAccuracy"); } Sigil sigl = Sigil(player.FindInventory("Sigil")); - if (sigl != null /*&& sigl.NumPieces == 5*/) + if (sigl != null && sigl.health == 5) { // You wield the power of the complete Sigil. log = 85; }