From d2d6e5d486f438c6a2838048963f25088c7459a4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 15 Jan 2017 23:21:38 +0100 Subject: [PATCH] - scriptified PowerFlight and PowerWeaponLevel2. --- src/doomtype.h | 7 + src/g_inventory/a_artifacts.cpp | 224 ------------------ src/g_inventory/a_artifacts.h | 24 -- src/g_inventory/a_pickups.cpp | 8 + src/g_inventory/a_weapons.cpp | 14 +- src/g_inventory/a_weapons.h | 1 - src/g_level.cpp | 1 + src/g_shared/a_morph.cpp | 6 +- src/g_shared/shared_sbar.cpp | 20 +- src/gl/scene/gl_scene.cpp | 6 +- src/m_cheat.cpp | 4 +- src/namedef.h | 9 + src/p_interaction.cpp | 2 +- src/p_lnspec.cpp | 34 +-- src/p_pspr.cpp | 2 +- src/p_sectors.cpp | 6 - src/p_user.cpp | 2 +- src/scripting/codegeneration/codegen.cpp | 26 +- src/textures/texturemanager.cpp | 9 + src/textures/textures.h | 2 + src/v_draw.cpp | 10 + wadsrc/static/zscript/base.txt | 40 ++++ wadsrc/static/zscript/inventory/inventory.txt | 2 + wadsrc/static/zscript/inventory/powerups.txt | 214 ++++++++++++++++- wadsrc/static/zscript/shared/player.txt | 2 +- 25 files changed, 369 insertions(+), 306 deletions(-) diff --git a/src/doomtype.h b/src/doomtype.h index 264713d1b..248aee33b 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -186,6 +186,13 @@ private: int texnum; }; +// This is for the script interface which needs to do casts from int to texture. +class FSetTextureID : public FTextureID +{ +public: + FSetTextureID(int v) : FTextureID(v) {} +}; + // Screenshot buffer image data types diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp index 5105ddf83..7ffdab6c8 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -1008,230 +1008,6 @@ void APowerTorch::DoEffect () } } -// Flight (aka Wings of Wrath) powerup --------------------------------------- - -IMPLEMENT_CLASS(APowerFlight, false, false) - -//=========================================================================== -// -// APowerFlight :: Serialize -// -//=========================================================================== - -void APowerFlight::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("hitcenterframe", HitCenterFrame); -} - -//=========================================================================== -// -// APowerFlight :: InitEffect -// -//=========================================================================== - -void APowerFlight::InitEffect () -{ - Super::InitEffect(); - Owner->flags2 |= MF2_FLY; - Owner->flags |= MF_NOGRAVITY; - if (Owner->Z() <= Owner->floorz) - { - Owner->Vel.Z = 4;; // thrust the player in the air a bit - } - if (Owner->Vel.Z <= -35) - { // stop falling scream - S_StopSound (Owner, CHAN_VOICE); - } -} - -//=========================================================================== -// -// APowerFlight :: DoEffect -// -//=========================================================================== - -void APowerFlight::Tick () -{ - // The Wings of Wrath only expire in multiplayer and non-hub games - if (!multiplayer && (level.flags2 & LEVEL2_INFINITE_FLIGHT)) - { - assert(EffectTics < INT_MAX); // I can't see a game lasting nearly two years, but... - EffectTics++; - } - - Super::Tick (); - -// Owner->flags |= MF_NOGRAVITY; -// Owner->flags2 |= MF2_FLY; -} - -//=========================================================================== -// -// APowerFlight :: EndEffect -// -//=========================================================================== - -void APowerFlight::EndEffect () -{ - Super::EndEffect(); - if (Owner == NULL || Owner->player == NULL) - { - return; - } - - if (!(Owner->flags7 & MF7_FLYCHEAT)) - { - if (Owner->Z() != Owner->floorz) - { - Owner->player->centering = true; - } - Owner->flags2 &= ~MF2_FLY; - Owner->flags &= ~MF_NOGRAVITY; - } -// BorderTopRefresh = screen->GetPageCount (); //make sure the sprite's cleared out -} - -//=========================================================================== -// -// APowerFlight :: DrawPowerup -// -//=========================================================================== - -bool APowerFlight::DrawPowerup (int x, int y) -{ - // If this item got a valid icon use that instead of the default spinning wings. - if (Icon.isValid()) - { - return Super::DrawPowerup(x, y); - } - - if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16)) - { - FTextureID picnum = TexMan.CheckForTexture ("SPFLY0", FTexture::TEX_MiscPatch); - int frame = (level.time/3) & 15; - - if (!picnum.isValid()) - { - return false; - } - if (Owner->flags & MF_NOGRAVITY) - { - if (HitCenterFrame && (frame != 15 && frame != 0)) - { - screen->DrawTexture (TexMan[picnum+15], x, y, - DTA_HUDRules, HUD_Normal, TAG_DONE); - } - else - { - screen->DrawTexture (TexMan[picnum+frame], x, y, - DTA_HUDRules, HUD_Normal, TAG_DONE); - HitCenterFrame = false; - } - } - else - { - if (!HitCenterFrame && (frame != 15 && frame != 0)) - { - screen->DrawTexture (TexMan[picnum+frame], x, y, - DTA_HUDRules, HUD_Normal, TAG_DONE); - HitCenterFrame = false; - } - else - { - screen->DrawTexture (TexMan[picnum+15], x, y, - DTA_HUDRules, HUD_Normal, TAG_DONE); - HitCenterFrame = true; - } - } - } - return true; -} - -// Weapon Level 2 (aka Tome of Power) Powerup -------------------------------- - -IMPLEMENT_CLASS(APowerWeaponLevel2, false, false) - -//=========================================================================== -// -// APowerWeaponLevel2 :: InitEffect -// -//=========================================================================== - -void APowerWeaponLevel2::InitEffect () -{ - AWeapon *weapon, *sister; - - Super::InitEffect(); - - if (Owner->player == nullptr) - return; - - weapon = Owner->player->ReadyWeapon; - - if (weapon == nullptr) - return; - - sister = weapon->SisterWeapon; - - if (sister == nullptr) - return; - - if (!(sister->WeaponFlags & WIF_POWERED_UP)) - return; - - assert (sister->SisterWeapon == weapon); - - - if (weapon->GetReadyState() != sister->GetReadyState()) - { - Owner->player->ReadyWeapon = sister; - P_SetPsprite(Owner->player, PSP_WEAPON, sister->GetReadyState()); - } - else - { - DPSprite *psp = Owner->player->FindPSprite(PSP_WEAPON); - if (psp != nullptr && psp->GetCaller() == Owner->player->ReadyWeapon) - { - // If the weapon changes but the state does not, we have to manually change the PSprite's caller here. - psp->SetCaller(sister); - Owner->player->ReadyWeapon = sister; - } - else - { - // Something went wrong. Initiate a regular weapon change. - Owner->player->PendingWeapon = sister; - } - } -} - -//=========================================================================== -// -// APowerWeaponLevel2 :: EndEffect -// -//=========================================================================== - -void APowerWeaponLevel2::EndEffect () -{ - player_t *player = Owner != NULL ? Owner->player : NULL; - - Super::EndEffect(); - if (player != NULL) - { - if (player->ReadyWeapon != NULL && - player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP) - { - player->ReadyWeapon->CallEndPowerup (); - } - if (player->PendingWeapon != NULL && player->PendingWeapon != WP_NOCHANGE && - player->PendingWeapon->WeaponFlags & WIF_POWERED_UP && - player->PendingWeapon->SisterWeapon != NULL) - { - player->PendingWeapon = player->PendingWeapon->SisterWeapon; - } - } -} - // Speed Powerup ------------------------------------------------------------- IMPLEMENT_CLASS(APowerSpeed, false, false) diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h index b3143cfaa..65f8d7cea 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -124,30 +124,6 @@ protected: int NewTorch, NewTorchDelta; }; -class APowerFlight : public APowerup -{ - DECLARE_CLASS (APowerFlight, APowerup) -public: - virtual bool DrawPowerup (int x, int y) override; - virtual void Serialize(FSerializer &arc) override; - -protected: - virtual void InitEffect () override; - virtual void Tick () override; - virtual void EndEffect () override; - -private: - bool HitCenterFrame; -}; - -class APowerWeaponLevel2 : public APowerup -{ - DECLARE_CLASS (APowerWeaponLevel2, APowerup) -protected: - virtual void InitEffect () override; - virtual void EndEffect () override; -}; - class APowerSpeed : public APowerup { DECLARE_CLASS (APowerSpeed, APowerup) diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 6f51bb6b5..72e51b0c9 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -1279,6 +1279,14 @@ bool AInventory::DrawPowerup (int x, int y) return false; } +DEFINE_ACTION_FUNCTION(AInventory, DrawPowerup) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_INT(x); + PARAM_INT(y); + ACTION_RETURN_BOOL(self->DrawPowerup(x, y)); +} + //=========================================================================== // // AInventory :: DoRespawn diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 750aad952..02aa8480e 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -311,7 +311,7 @@ bool AWeapon::Use (bool pickup) // weapon, if one exists. if (SisterWeapon != NULL && SisterWeapon->WeaponFlags & WIF_POWERED_UP && - Owner->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true)) + Owner->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true)) { useweap = SisterWeapon; } @@ -824,18 +824,6 @@ DEFINE_ACTION_FUNCTION(AWeapon, EndPowerup) return 0; } -void AWeapon::CallEndPowerup() -{ - IFVIRTUAL(AWeapon, EndPowerup) - { - // Without the type cast this picks the 'void *' assignment... - VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); - } - else EndPowerup(); -} - - //=========================================================================== // // AWeapon :: GetUpState diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 564c0df02..55354c139 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -161,7 +161,6 @@ public: virtual void EndPowerup (); - void CallEndPowerup(); enum { diff --git a/src/g_level.cpp b/src/g_level.cpp index 0c7326c78..030720a88 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1897,6 +1897,7 @@ DEFINE_FIELD_BIT(FLevelLocals, flags2, checkswitchrange, LEVEL2_CHECKSWITCHRANGE DEFINE_FIELD_BIT(FLevelLocals, flags2, polygrind, LEVEL2_POLYGRIND) DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS) DEFINE_FIELD_BIT(FLevelLocals, flags2, frozen, LEVEL2_FROZEN) +DEFINE_FIELD_BIT(FLevelLocals, flags2, infinite_flight, LEVEL2_INFINITE_FLIGHT) //========================================================================== // diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index bb03ff489..d18ec7e02 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -58,9 +58,9 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp if ((p->mo->GetClass() == spawntype) && (p->mo->PlayerFlags & PPF_CANSUPERMORPH) && (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE)) - && (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == nullptr)) + && (p->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true) == nullptr)) { // Make a super chicken - p->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); + p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2)); } return false; } @@ -263,7 +263,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, player->MorphStyle = 0; player->MorphExitFlash = nullptr; player->viewheight = mo->ViewHeight; - AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true); + AInventory *level2 = mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true); if (level2 != nullptr) { level2->Destroy (); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 009b480b7..6a790f845 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -56,6 +56,7 @@ #include "r_utility.h" #include "cmdlib.h" #include "g_levellocals.h" +#include "virtual.h" #include "../version.h" @@ -1529,13 +1530,22 @@ void DBaseStatusBar::DrawPowerups () + (ST_IsLatencyVisible() ? yshift : 0); for (item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory) { - if (item->DrawPowerup (x, y)) + IFVIRTUALPTR(item, AInventory, DrawPowerup) { - x -= POWERUPICONSIZE; - if (x < -POWERUPICONSIZE*5) + VMValue params[3] = { item, x, y }; + VMReturn ret; + int retv; + + ret.IntAt(&retv); + GlobalVMStack.Call(func, params, 3, &ret, 1); + if (retv) { - x = -20; - y += POWERUPICONSIZE*2; + x -= POWERUPICONSIZE; + if (x < -POWERUPICONSIZE * 5) + { + x = -20; + y += POWERUPICONSIZE * 2; + } } } } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index cfd3f017e..b1510d420 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -767,16 +767,18 @@ void FGLRenderer::SetFixedColormap (player_t *player) } else if (cplayer->fixedlightlevel != -1) { + auto torchtype = PClass::FindActor(NAME_PowerTorch); + auto litetype = PClass::FindActor(NAME_PowerLightAmp); for(AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory) { PalEntry color = in->GetBlend (); // Need special handling for light amplifiers - if (in->IsKindOf(RUNTIME_CLASS(APowerTorch))) + if (in->IsKindOf(torchtype)) { gl_fixedcolormap = cplayer->fixedlightlevel + CM_TORCH; } - else if (in->IsKindOf(RUNTIME_CLASS(APowerLightAmp))) + else if (in->IsKindOf(litetype)) { gl_fixedcolormap = CM_LITE; } diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 3af00c012..46f18d96f 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -218,7 +218,7 @@ void cht_DoCheat (player_t *player, int cheat) case CHT_POWER: if (player->mo != NULL && player->health >= 0) { - item = player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true); + item = player->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true); if (item != NULL) { item->Destroy (); @@ -226,7 +226,7 @@ void cht_DoCheat (player_t *player, int cheat) } else { - player->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); + player->mo->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2)); msg = GStrings("TXT_CHEATPOWERON"); } } diff --git a/src/namedef.h b/src/namedef.h index 9e89a1acf..c37ee5577 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -172,6 +172,15 @@ xx(HealthPickup) xx(autousemode) xx(Ammo) xx(PowerTargeter) +xx(PowerInvulnerable) +xx(PowerStrength) +xx(PowerInvisibility) +xx(PowerIronFeet) +xx(PowerLightAmp) +xx(PowerWeaponLevel2) +xx(PowerFlight) +xx(PowerSpeed) +xx(PowerTorch) xx(AcolyteBlue) xx(SpectralLightningV1) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index f61e77630..97787fd9c 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -476,7 +476,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) if (source->player->morphTics) { // Make a super chicken - source->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); + source->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2)); } if (deathmatch && cl_showsprees) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index ec8c8d2fa..f26f617a9 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2841,23 +2841,23 @@ FUNC(LS_SetPlayerProperty) // Add or remove a power if (arg2 >= PROP_INVULNERABILITY && arg2 <= PROP_SPEED) { - static PClass * const *powers[11] = + static ENamedName powers[11] = { - &RUNTIME_CLASS_CASTLESS(APowerInvulnerable), - &RUNTIME_CLASS_CASTLESS(APowerStrength), - &RUNTIME_CLASS_CASTLESS(APowerInvisibility), - &RUNTIME_CLASS_CASTLESS(APowerIronFeet), - NULL, // MapRevealer - &RUNTIME_CLASS_CASTLESS(APowerLightAmp), - &RUNTIME_CLASS_CASTLESS(APowerWeaponLevel2), - &RUNTIME_CLASS_CASTLESS(APowerFlight), - NULL, - NULL, - &RUNTIME_CLASS_CASTLESS(APowerSpeed) + NAME_PowerInvulnerable, + NAME_PowerStrength, + NAME_PowerInvisibility, + NAME_PowerIronFeet, + NAME_None, + NAME_PowerLightAmp, + NAME_PowerWeaponLevel2, + NAME_PowerFlight, + NAME_None, + NAME_None, + NAME_PowerSpeed }; int power = arg2 - PROP_INVULNERABILITY; - if (power > 4 && powers[power] == NULL) + if (power > 4 && powers[power] == NAME_None) { return false; } @@ -2868,7 +2868,7 @@ FUNC(LS_SetPlayerProperty) { // Give power to activator if (power != 4) { - APowerup *item = static_cast(it->GiveInventoryType(static_cast(*powers[power]))); + APowerup *item = static_cast(it->GiveInventoryType(PClass::FindActor(powers[power]))); if (item != NULL && power == 0 && arg1 == 1) { item->BlendColor = MakeSpecialColormap(INVERSECOLORMAP); @@ -2883,7 +2883,7 @@ FUNC(LS_SetPlayerProperty) { // Take power from activator if (power != 4) { - AInventory *item = it->FindInventory(static_cast(*powers[power]), true); + AInventory *item = it->FindInventory(PClass::FindActor(powers[power]), true); if (item != NULL) { item->Destroy (); @@ -2908,7 +2908,7 @@ FUNC(LS_SetPlayerProperty) { // Give power if (power != 4) { - APowerup *item = static_cast(players[i].mo->GiveInventoryType (static_cast(*powers[power]))); + APowerup *item = static_cast(players[i].mo->GiveInventoryType ((PClass::FindActor(powers[power])))); if (item != NULL && power == 0 && arg1 == 1) { item->BlendColor = MakeSpecialColormap(INVERSECOLORMAP); @@ -2923,7 +2923,7 @@ FUNC(LS_SetPlayerProperty) { // Take power if (power != 4) { - AInventory *item = players[i].mo->FindInventory (static_cast(*powers[power])); + AInventory *item = players[i].mo->FindInventory (PClass::FindActor(powers[power])); if (item != NULL) { item->Destroy (); diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 850c8ef0d..2e5201fa1 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -481,7 +481,7 @@ void P_BringUpWeapon (player_t *player) if (weapon != nullptr && weapon->SisterWeapon && weapon->SisterWeapon->WeaponFlags & WIF_POWERED_UP && - player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true)) + player->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true)) { weapon = weapon->SisterWeapon; } diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index b1385b079..e85fd744f 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -1673,12 +1673,6 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt) ACTION_RETURN_INT(self->GetPlaneLight(pos)); } - class FSetTextureID : public FTextureID - { - public: - FSetTextureID(int v) : FTextureID(v) {} - }; - DEFINE_ACTION_FUNCTION(_Sector, SetTexture) { PARAM_SELF_STRUCT_PROLOGUE(sector_t); diff --git a/src/p_user.cpp b/src/p_user.cpp index ba187ea29..d3c44d46f 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -948,7 +948,7 @@ AWeapon *APlayerPawn::BestWeapon(PClassInventory *ammotype) int bestOrder = INT_MAX; AInventory *item; AWeapon *weap; - bool tomed = NULL != FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true); + bool tomed = NULL != FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true); // Find the best weapon the player has. for (item = Inventory; item != NULL; item = item->Inventory) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 7f401720b..cd63636f6 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -2676,7 +2676,11 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx) return nullptr; } - if (left->ValueType == TypeState && right->IsInteger() && Operator == '+' && !left->isConstant()) + if (left->ValueType == TypeTextureID && right->IsInteger()) + { + ValueType = TypeTextureID; + } + else if (left->ValueType == TypeState && right->IsInteger() && Operator == '+' && !left->isConstant()) { // This is the only special case of pointer addition that will be accepted - because it is used quite often in the existing game code. ValueType = TypeState; @@ -2755,6 +2759,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) assert(Operator == '+' || Operator == '-'); ExpEmit op1 = left->Emit(build); ExpEmit op2 = right->Emit(build); + ExpEmit to; if (Operator == '+') { if (op1.RegType == REGT_POINTER) @@ -2775,7 +2780,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) assert(!op1.Konst); op1.Free(build); op2.Free(build); - ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount()); + to = ExpEmit(build, ValueType->GetRegType(), ValueType->GetRegCount()); if (IsVector()) { assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); @@ -2798,6 +2803,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) assert(ValueType->GetRegType() == REGT_INT); assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT); build->Emit(op2.Konst ? OP_ADD_RK : OP_ADD_RR, to.RegNum, op1.RegNum, op2.RegNum); + if (ValueType == TypeTextureID) goto texcheck; return to; } } @@ -2807,7 +2813,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) assert(!op1.Konst || !op2.Konst); op1.Free(build); op2.Free(build); - ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount()); + to = ExpEmit(build, ValueType->GetRegType(), ValueType->GetRegCount()); if (IsVector()) { assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT); @@ -2825,9 +2831,23 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build) assert(ValueType->GetRegType() == REGT_INT); assert(op1.RegType == REGT_INT && op2.RegType == REGT_INT); build->Emit(op1.Konst ? OP_SUB_KR : op2.Konst ? OP_SUB_RK : OP_SUB_RR, to.RegNum, op1.RegNum, op2.RegNum); + if (ValueType == TypeTextureID) goto texcheck; return to; } } + +texcheck: + // Do a bounds check for the texture index. Note that count can change at run time so this needs to read the value from the texture manager. + auto * ptr = (FArray*)&TexMan.Textures; + auto * countptr = &ptr->Count; + ExpEmit bndp(build, REGT_POINTER); + ExpEmit bndc(build, REGT_INT); + build->Emit(OP_LKP, bndp.RegNum, build->GetConstantAddress(countptr, ATAG_GENERIC)); + build->Emit(OP_LW, bndc.RegNum, bndp.RegNum, build->GetConstantInt(0)); + build->Emit(OP_BOUND_R, to.RegNum, bndc.RegNum); + bndp.Free(build); + bndc.Free(build); + return to; } //========================================================================== diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index aa760c221..c94f4f467 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -261,6 +261,15 @@ FTextureID FTextureManager::CheckForTexture (const char *name, int usetype, BITF return FTextureID(-1); } +DEFINE_ACTION_FUNCTION(_TexMan, CheckForTexture) +{ + PARAM_PROLOGUE; + PARAM_STRING(name); + PARAM_INT(type); + PARAM_INT_DEF(flags); + ACTION_RETURN_INT(TexMan.CheckForTexture(name, type, flags).GetIndex()); +} + //========================================================================== // // FTextureManager :: ListTextures diff --git a/src/textures/textures.h b/src/textures/textures.h index 20ee5aec2..3e441b7bc 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -339,9 +339,11 @@ public: bool ProcessData(unsigned char * buffer, int w, int h, bool ispatch); }; +class FxAddSub; // Texture manager class FTextureManager { + friend class FxAddSub; // needs access to do a bounds check on the texture ID. public: FTextureManager (); ~FTextureManager (); diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 16b0e405a..fce5f50b4 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -130,6 +130,16 @@ void DCanvas::DrawTexture (FTexture *img, double x, double y, int tags_first, .. DrawTextureParms(img, parms); } +DEFINE_ACTION_FUNCTION(_Screen, DrawHUDTexture) +{ + PARAM_PROLOGUE; + PARAM_INT(texid); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + screen->DrawTexture(TexMan(FSetTextureID(texid)), x, y, DTA_HUDRules, HUD_Normal, TAG_END); + return 0; +} + void DCanvas::DrawTextureParms(FTexture *img, DrawParms &parms) { #ifndef NO_SWRENDER diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 0ed230186..e9e0a0673 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -1,3 +1,42 @@ +struct TexMan +{ + enum EUseTypes + { + Type_Any, + Type_Wall, + Type_Flat, + Type_Sprite, + Type_WallPatch, + Type_Build, + Type_SkinSprite, + Type_Decal, + Type_MiscPatch, + Type_FontChar, + Type_Override, // For patches between TX_START/TX_END + Type_Autopage, // Automap background - used to enable the use of FAutomapTexture + Type_SkinGraphic, + Type_Null, + Type_FirstDefined, + }; + + enum EFlags + { + TryAny = 1, + Overridable = 2, + ReturnFirst = 4, + AllowSkins = 8, + ShortNameOnly = 16, + DontCreate = 32 + }; + + native static TextureID CheckForTexture(String name, int usetype, int flags = TryAny); +} + +struct Screen +{ + native static void DrawHUDTexture(TextureID tex, double x, double y); +} + class Object native { native bool bDestroyed; @@ -143,6 +182,7 @@ struct LevelLocals native native bool polygrind; native bool nomonsters; native bool frozen; + native bool infinite_flight; // level_info_t *info cannot be done yet. } diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index 051f62bb3..d3f6bfef5 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -1,5 +1,6 @@ class Inventory : Actor native { + const BLINKTHRESHOLD = (4*32); native Actor Owner; // Who owns this item? NULL if it's still a pickup. native int Amount; // Amount of item this instance has @@ -37,6 +38,7 @@ class Inventory : Actor native virtual native void PlayPickupSound(Actor user); virtual native void AttachToOwner(Actor user); virtual native void DetachFromOwner(); + virtual native bool DrawPowerup(int x, int y); //=========================================================================== // diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 12a1cc408..adee447f5 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -114,16 +114,146 @@ class PowerLightAmp : Powerup native class PowerTorch : PowerLightAmp native {} -class PowerFlight : Powerup native +//=========================================================================== +// +// Flight +// +//=========================================================================== + +class PowerFlight : Powerup { Default { Powerup.Duration -60; +INVENTORY.HUBPOWER } + + bool HitCenterFrame; + + //=========================================================================== + // + // APowerFlight :: InitEffect + // + //=========================================================================== + + override void InitEffect () + { + Super.InitEffect(); + Owner.bFly = true; + Owner.bNoGravity = true; + if (Owner.pos.Z <= Owner.floorz) + { + Owner.Vel.Z = 4;; // thrust the player in the air a bit + } + if (Owner.Vel.Z <= -35) + { // stop falling scream + Owner.A_StopSound (CHAN_VOICE); + } + } + + //=========================================================================== + // + // APowerFlight :: DoEffect + // + //=========================================================================== + + override void Tick () + { + // The Wings of Wrath only expire in multiplayer and non-hub games + if (!multiplayer && level.infinite_flight) + { + EffectTics++; + } + Super.Tick (); + } + + //=========================================================================== + // + // APowerFlight :: EndEffect + // + //=========================================================================== + + override void EndEffect () + { + Super.EndEffect(); + if (Owner == NULL || Owner.player == NULL) + { + return; + } + + if (!(Owner.bFlyCheat)) + { + if (Owner.pos.Z != Owner.floorz) + { + Owner.player.centering = true; + } + Owner.bFly = false; + Owner.bNoGravity = false; + } + } + + //=========================================================================== + // + // APowerFlight :: DrawPowerup + // + //=========================================================================== + + override bool DrawPowerup (int x, int y) + { + // If this item got a valid icon use that instead of the default spinning wings. + if (Icon.isValid()) + { + return Super.DrawPowerup(x, y); + } + + if (EffectTics > BLINKTHRESHOLD || !(EffectTics & 16)) + { + TextureID picnum = TexMan.CheckForTexture ("SPFLY0", TexMan.Type_MiscPatch); + int frame = (level.time/3) & 15; + + if (!picnum.isValid()) + { + return false; + } + if (Owner.bNoGravity) + { + if (HitCenterFrame && (frame != 15 && frame != 0)) + { + screen.DrawHUDTexture (picnum + 15, x, y); + } + else + { + screen.DrawHUDTexture (picnum + frame, x, y); + HitCenterFrame = false; + } + } + else + { + if (!HitCenterFrame && (frame != 15 && frame != 0)) + { + screen.DrawHUDTexture (picnum + frame, x, y); + HitCenterFrame = false; + } + else + { + screen.DrawHUDTexture (picnum+15, x, y); + HitCenterFrame = true; + } + } + } + return true; + } + + } -class PowerWeaponLevel2 : Powerup native +//=========================================================================== +// +// WeaponLevel2 +// +//=========================================================================== + +class PowerWeaponLevel2 : Powerup { Default { @@ -131,6 +261,86 @@ class PowerWeaponLevel2 : Powerup native Inventory.Icon "SPINBK0"; +INVENTORY.NOTELEPORTFREEZE } + + //=========================================================================== + // + // APowerWeaponLevel2 :: InitEffect + // + //=========================================================================== + + override void InitEffect () + { + + Super.InitEffect(); + + let player = Owner.player; + + if (player == null) + return; + + let weap = player.ReadyWeapon; + + if (weap == null) + return; + + let sister = weap.SisterWeapon; + + if (sister == null) + return; + + if (!sister.bPowered_Up) + return; + + let ready = sister.GetReadyState(); + if (weap.GetReadyState() != ready) + { + player.ReadyWeapon = sister; + player.SetPsprite(PSP_WEAPON, ready); + } + else + { + PSprite psp = player.FindPSprite(PSprite.WEAPON); + if (psp != null && psp.Caller == player.ReadyWeapon) + { + // If the weapon changes but the state does not, we have to manually change the PSprite's caller here. + psp.Caller = sister; + player.ReadyWeapon = sister; + } + else + { + // Something went wrong. Initiate a regular weapon change. + player.PendingWeapon = sister; + } + } + } + + //=========================================================================== + // + // APowerWeaponLevel2 :: EndEffect + // + //=========================================================================== + + override void EndEffect () + { + Super.EndEffect(); + if (Owner == null) return; + let player = Owner.player; + if (player != NULL) + { + if (player.ReadyWeapon != NULL && player.ReadyWeapon.bPowered_Up) + { + player.ReadyWeapon.EndPowerup (); + } + if (player.PendingWeapon != NULL && player.PendingWeapon != WP_NOCHANGE && + player.PendingWeapon.bPowered_Up && + player.PendingWeapon.SisterWeapon != NULL) + { + player.PendingWeapon = player.PendingWeapon.SisterWeapon; + } + } + } + + } //=========================================================================== diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index e2f01d866..1648a8a40 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -139,7 +139,7 @@ class PSprite : Object native }; native readonly State CurState; - native readonly Actor Caller; + native Actor Caller; native readonly PSprite Next; native readonly PlayerInfo Owner; native SpriteID Sprite;