From 8f6571241dfe0bdc5e95e572adeca603eab6d03b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 16 Jan 2017 23:45:25 +0100 Subject: [PATCH 01/12] =?UTF-8?q?-=20scriptified=20A=C3=9CpwerInvulnerable?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/g_inventory/a_artifacts.cpp | 118 ------------------ src/g_inventory/a_artifacts.h | 9 -- src/p_mobj.cpp | 2 +- src/scripting/thingdef_data.cpp | 1 + wadsrc/static/zscript/inventory/powerups.txt | 124 ++++++++++++++++++- 5 files changed, 124 insertions(+), 130 deletions(-) diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp index 0cd4828b1..8b57d4fe2 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -422,124 +422,6 @@ 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) diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h index f947e18d5..89088a0be 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -57,15 +57,6 @@ public: 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) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 9c18ea638..48393caaa 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5373,7 +5373,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) (dmflags2 & DF2_YES_RESPAWN_INVUL) && (multiplayer || alwaysapplydmflags)) { - APowerup *invul = static_cast(p->mo->GiveInventoryType (RUNTIME_CLASS(APowerInvulnerable))); + APowerup *invul = static_cast(p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerInvulnerable))); invul->EffectTics = 3*TICRATE; invul->BlendColor = 0; // don't mess with the view invul->ItemFlags |= IF_UNDROPPABLE; // Don't drop this diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index a3f5c144c..b99d6e646 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), }; diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index b744bfc97..8ad8bdac1 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -38,14 +38,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) From bfb7b82bccfb37b4b2754a2cb61a8240badc39bb Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Mon, 16 Jan 2017 22:46:50 +0100 Subject: [PATCH 02/12] - Fixed uninitialized data for libsndfile. According to the API docs, when opening a file for read, SF_INFO::format must be set to 0. Discovered with Valgrind while running Deus Vult 2 map01. --- src/sound/sndfile_decoder.cpp | 1 + 1 file changed, 1 insertion(+) 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) { From c317a4cbf3ef424be0d7b5f8060ee43cf4b26c14 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 17 Jan 2017 09:56:45 +0100 Subject: [PATCH 03/12] - Fixed: wrong FString 'Replace' action function. The function replaced only the first character of the first argument with the first character of the second argument. --- src/scripting/thingdef_data.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index b99d6e646..8caa07292 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -926,7 +926,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; } From c4aaeef6b1f1ababff65d7465f993c36572acfcf Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Tue, 17 Jan 2017 11:26:51 +0200 Subject: [PATCH 04/12] Removed homebrew MP3 format detection It didn't work for all files so let libmpg123 handle all quirks for us See https://mantis.zdoom.org/view.php?id=60 --- src/sound/mpg123_decoder.cpp | 51 ++++-------------------------------- src/sound/mpg123_decoder.h | 1 - 2 files changed, 5 insertions(+), 47 deletions(-) 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); From 0da1142bdbf72d4d6bf46fce6f258f88a2cfe449 Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Tue, 17 Jan 2017 16:11:49 +0100 Subject: [PATCH 05/12] - Run libsndfile before libmpg123 when reading an audio lump. libmpg123 spews quite a lot of debug stuff in stdout when encountering files like WAV or Ogg Vorbis, while libsndfile is silent when encountering an MP3 file. --- src/sound/i_sound.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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); From 75d3f42d4f48f3d682a3054fde0c321ce19bf137 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 17 Jan 2017 01:50:31 +0100 Subject: [PATCH 06/12] - scriptified APowerup. --- src/dobjtype.h | 13 + src/g_inventory/a_artifacts.cpp | 291 +------------------ src/g_inventory/a_artifacts.h | 28 +- src/g_shared/a_morph.cpp | 15 +- src/p_acs.cpp | 11 + src/r_data/colormaps.h | 10 - src/scripting/thingdef.h | 1 + src/scripting/thingdef_data.cpp | 9 +- src/scripting/thingdef_properties.cpp | 17 ++ src/scripting/vm/vmframe.cpp | 10 +- wadsrc/static/zscript/inventory/powerups.txt | 242 ++++++++++++++- 11 files changed, 304 insertions(+), 343 deletions(-) diff --git a/src/dobjtype.h b/src/dobjtype.h index 156818147..dd240e071 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -639,6 +639,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_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp index 8b57d4fe2..6e5b03cc3 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -123,25 +123,7 @@ 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,257 +138,8 @@ void APowerup::Serialize(FSerializer &arc) arc("effecttics", EffectTics, def->EffectTics) ("blendcolor", BlendColor, def->BlendColor) ("mode", Mode, def->Mode) - ("strength", Strength, def->Strength); -} - -//=========================================================================== -// -// 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; + ("strength", Strength, def->Strength) + ("colormap", Colormap, def->Colormap); } //=========================================================================== @@ -422,24 +155,6 @@ void APowerup::OwnerDied () Destroy (); } -// 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) diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h index 89088a0be..f17299d46 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -11,31 +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); }; @@ -57,17 +42,6 @@ public: double Strength; // Meaning depends on powerup - currently used only by Invisibility }; -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 ) diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index d18ec7e02..40e6e420b 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"); @@ -597,7 +598,12 @@ void EndAllPowerupEffects(AInventory *item) { if (item->IsKindOf(RUNTIME_CLASS(APowerup))) { - static_cast(item)->CallEndEffect(); + IFVIRTUALPTR(item, APowerup, EndEffect) + { + VMValue params[1] = { item }; + VMFrameStack stack; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } item = item->Inventory; } @@ -617,7 +623,12 @@ void InitAllPowerupEffects(AInventory *item) { if (item->IsKindOf(RUNTIME_CLASS(APowerup))) { - static_cast(item)->CallInitEffect(); + IFVIRTUALPTR(item, APowerup, InitEffect) + { + VMValue params[1] = { item }; + VMFrameStack stack; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } } item = item->Inventory; } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 5355b10a0..50dc1eb7a 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) @@ -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/r_data/colormaps.h b/src/r_data/colormaps.h index 9db564b60..03f705b2b 100644 --- a/src/r_data/colormaps.h +++ b/src/r_data/colormaps.h @@ -63,16 +63,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/thingdef.h b/src/scripting/thingdef.h index 9d41b5265..20d25dd8e 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 diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 8caa07292..20e210f80 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -462,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[] = @@ -473,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)) @@ -548,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; } diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index db24349cb..487763d9a 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 } 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/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 8ad8bdac1..af20ed628 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -22,13 +22,232 @@ 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); + } - native virtual void InitEffect(); - native virtual void EndEffect(); - native bool isBlinking(); } @@ -881,13 +1100,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; @@ -917,7 +1135,7 @@ class PowerSpeed : Powerup native if (Owner.player.cheats & CF_PREDICTING) return; - if (SpeedFlags & PSF_NOTRAIL) + if (NoTrail) return; if (level.time & 1) @@ -928,7 +1146,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; } @@ -1678,7 +1896,7 @@ class PowerMorph : Powerup native native Class MorphFlash, UnMorphFlash; native int MorphStyle; native PlayerInfo MorphedPlayer; - + Default { Powerup.Duration -40; From 14f2c39e5863055d2d8a42de4e01bdcaeb9c0ac4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 17 Jan 2017 17:31:54 +0100 Subject: [PATCH 07/12] - scriptified cht_Give and cht_Take and made them virtual function of PlayerPawn so that this can be better configured for mods that want other options in here. - improved the class pointer to string cast to print the actual type it describes and not the class pointer's own type. - fixed: The 'is' operator created non-working code when checking the inheritance of a class pointer, it only worked for objects. --- src/c_console.cpp | 6 + src/d_dehacked.cpp | 1 + src/g_inventory/a_weapons.cpp | 13 + src/gi.cpp | 4 + src/gi.h | 2 +- src/m_cheat.cpp | 442 +----------------- src/p_actionfunctions.cpp | 16 - src/p_mobj.cpp | 18 +- src/p_user.cpp | 8 + src/scripting/codegeneration/codegen.cpp | 26 +- src/scripting/codegeneration/codegen.h | 2 +- src/scripting/thingdef_data.cpp | 8 + src/scripting/vm/vmexec.h | 34 +- src/scripting/vm/vmops.h | 2 + wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/actor.txt | 2 + wadsrc/static/zscript/actor_checks.txt | 8 +- wadsrc/static/zscript/base.txt | 15 +- wadsrc/static/zscript/inventory/weapons.txt | 4 + wadsrc/static/zscript/shared/player.txt | 6 +- wadsrc/static/zscript/shared/player_cheat.txt | 402 ++++++++++++++++ 21 files changed, 559 insertions(+), 461 deletions(-) create mode 100644 wadsrc/static/zscript/shared/player_cheat.txt diff --git a/src/c_console.cpp b/src/c_console.cpp index a3f84723b..b27674044 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1326,6 +1326,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/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/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/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_mobj.cpp b/src/p_mobj.cpp index 48393caaa..c2a72e375 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; +} //============================================================================ // diff --git a/src/p_user.cpp b/src/p_user.cpp index d3c44d46f..a9dcc7cfa 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 diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 47a10f95d..9fbfe3ef8 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; } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index e7cfab662..c31c42b07 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(); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 20e210f80..56209e114 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -788,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); @@ -796,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); 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/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/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 ef47f62d7..a5087345b 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/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 56a6dc427..99f3d9c1f 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -258,11 +258,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; */ @@ -276,5 +277,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 From 98f9219334a619d495bd3a4139c97792b4307b6f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 17 Jan 2017 20:30:17 +0100 Subject: [PATCH 08/12] - scriptified the remaining functions in a_artifacts.cpp. - added some helpers to set scripted member variables through the native property parser. Unfortunately some classes, e.g. PowerMorph, MorphProjectile and the powerup contain some that cannot be handled through the 'property' definition on the script side so they need to be done from the native side. --- src/g_inventory/a_artifacts.cpp | 86 ------------------- src/g_inventory/a_artifacts.h | 16 ---- src/g_inventory/a_pickups.cpp | 14 +-- src/g_inventory/a_pickups.h | 2 - src/namedef.h | 4 + src/p_interaction.cpp | 10 ++- src/scripting/decorate/thingdef_parse.cpp | 5 +- src/scripting/thingdef.h | 16 +++- src/scripting/thingdef_properties.cpp | 42 +++++++-- src/scripting/zscript/zcc_compile.cpp | 5 +- wadsrc/static/zscript/inventory/inventory.txt | 1 + wadsrc/static/zscript/inventory/powerups.txt | 67 +++++++++++++-- 12 files changed, 128 insertions(+), 140 deletions(-) diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp index 6e5b03cc3..fbceba95c 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -60,46 +60,6 @@ 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 @@ -142,49 +102,3 @@ void APowerup::Serialize(FSerializer &arc) ("colormap", Colormap, def->Colormap); } -//=========================================================================== -// -// APowerup :: OwnerDied -// -// Powerups don't last beyond death. -// -//=========================================================================== - -void APowerup::OwnerDied () -{ - Destroy (); -} - -// 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 f17299d46..db9429858 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -12,7 +12,6 @@ class APowerup : public AInventory DECLARE_CLASS (APowerup, AInventory) public: virtual void Serialize(FSerializer &arc) override; - virtual void OwnerDied () override; int EffectTics; PalEntry BlendColor; @@ -31,7 +30,6 @@ class APowerupGiver : public AInventory DECLARE_CLASS (APowerupGiver, AInventory) HAS_OBJECT_POINTERS public: - virtual bool Use (bool pickup) override; virtual void Serialize(FSerializer &arc) override; @@ -42,19 +40,5 @@ public: double Strength; // Meaning depends on powerup - currently used only by Invisibility }; -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/namedef.h b/src/namedef.h index c37ee5577..366676a13 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -706,6 +706,10 @@ xx(WBobSpeed) xx(PlayerClass) xx(Wi_NoAutostartMap) +xx(MorphStyle) +xx(MorphFlash) +xx(UnMorphFlash) + // Decorate compatibility functions xx(BuiltinTypeCheck) xx(BuiltinRandom) 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/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 20d25dd8e..76a209722 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -230,7 +230,7 @@ struct FPropertyInfo { const char *name; const char *params; - const PClass * const *cls; + const char *clsname; PropHandler Handler; int category; }; @@ -242,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) @@ -260,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_properties.cpp b/src/scripting/thingdef_properties.cpp index 487763d9a..170b5c49a 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -456,12 +456,38 @@ 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 void *ScriptVar(DObject *obj, PClass *cls, FName field, PType *type) +{ + auto sym = dyn_cast(cls->Symbols.FindSymbol(field, true)); + if (sym && 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 // //========================================================================== + //========================================================================== // //========================================================================== @@ -3026,37 +3052,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/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 1687983f2..c1cd274f0 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 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 af20ed628..00639ce9e 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -14,6 +14,50 @@ 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 @@ -248,6 +292,19 @@ class Powerup : Inventory native return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink); } + //=========================================================================== + // + // APowerup :: OwnerDied + // + // Powerups don't last beyond death. + // + //=========================================================================== + + override void OwnerDied () + { + Destroy (); + } + } @@ -1890,12 +1947,12 @@ 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 { From 232b64d33242bfd99bcbc2d7d435593bc289e957 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 18 Jan 2017 00:11:04 +0100 Subject: [PATCH 09/12] - eliminated the native PowerupGiver class. - scriptified the respawn invulnerability code into a virtual OnRespawn function for PlayerPawn so that custom effects can be implemented. --- src/dobject.cpp | 2 - src/dobject.h | 3 +- src/dobjtype.cpp | 26 +++++++++ src/dobjtype.h | 1 + src/g_game.cpp | 3 +- src/g_inventory/a_artifacts.cpp | 30 ---------- src/g_inventory/a_artifacts.h | 18 ------ src/g_shared/a_morph.cpp | 10 ++-- src/g_shared/sbarinfo_commands.cpp | 43 ++++++++------- src/namedef.h | 7 +++ src/p_acs.cpp | 2 +- src/p_mobj.cpp | 14 ++--- src/p_user.cpp | 2 +- src/scripting/thingdef_properties.cpp | 58 ++++++++------------ src/scripting/vm/vm.h | 5 ++ wadsrc/static/zscript/inventory/powerups.txt | 14 ++--- wadsrc/static/zscript/shared/player.txt | 30 ++++++++++ 17 files changed, 138 insertions(+), 130 deletions(-) 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 dd240e071..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; 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 fbceba95c..15895eaa4 100644 --- a/src/g_inventory/a_artifacts.cpp +++ b/src/g_inventory/a_artifacts.cpp @@ -48,35 +48,6 @@ IMPLEMENT_CLASS(APowerup, false, false) // Powerup-Giver ------------------------------------------------------------- -IMPLEMENT_CLASS(APowerupGiver, false, true) - -IMPLEMENT_POINTERS_START(APowerupGiver) -IMPLEMENT_POINTER(PowerupType) -IMPLEMENT_POINTERS_END - -DEFINE_FIELD(APowerupGiver, PowerupType) -DEFINE_FIELD(APowerupGiver, EffectTics) -DEFINE_FIELD(APowerupGiver, BlendColor) -DEFINE_FIELD(APowerupGiver, Mode) -DEFINE_FIELD(APowerupGiver, Strength) - -//=========================================================================== -// -// APowerupGiver :: Serialize -// -//=========================================================================== - -void APowerupGiver::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - auto def = (APowerupGiver*)GetDefault(); - arc("poweruptype", PowerupType, def->PowerupType) - ("effecttics", EffectTics, def->EffectTics) - ("blendcolor", BlendColor, def->BlendColor) - ("mode", Mode, def->Mode) - ("strength", Strength, def->Strength); -} - // Powerup ------------------------------------------------------------------- DEFINE_FIELD(APowerup, EffectTics) @@ -101,4 +72,3 @@ void APowerup::Serialize(FSerializer &arc) ("strength", Strength, def->Strength) ("colormap", Colormap, def->Colormap); } - diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h index db9429858..84c18505b 100644 --- a/src/g_inventory/a_artifacts.h +++ b/src/g_inventory/a_artifacts.h @@ -23,22 +23,4 @@ public: friend void EndAllPowerupEffects(AInventory *item); friend void InitAllPowerupEffects(AInventory *item); }; - -// An artifact is an item that gives the player a powerup when activated. -class APowerupGiver : public AInventory -{ - DECLARE_CLASS (APowerupGiver, AInventory) - HAS_OBJECT_POINTERS -public: - virtual void Serialize(FSerializer &arc) override; - - - PClassActor *PowerupType; - int EffectTics; // Non-0 to override the powerup's default tics - PalEntry BlendColor; // Non-0 to override the powerup's default blend - FNameNoInit Mode; // Meaning depends on powerup - used for Invulnerability and Invisibility - double Strength; // Meaning depends on powerup - currently used only by Invisibility -}; - - #endif //__A_ARTIFACTS_H__ diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 40e6e420b..12b359a14 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -594,11 +594,12 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor void EndAllPowerupEffects(AInventory *item) { + auto ptype = PClass::FindActor(NAME_Powerup); while (item != NULL) { - if (item->IsKindOf(RUNTIME_CLASS(APowerup))) + if (item->IsKindOf(ptype)) { - IFVIRTUALPTR(item, APowerup, EndEffect) + IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) { VMValue params[1] = { item }; VMFrameStack stack; @@ -619,11 +620,12 @@ void EndAllPowerupEffects(AInventory *item) void InitAllPowerupEffects(AInventory *item) { + auto ptype = PClass::FindActor(NAME_Powerup); while (item != NULL) { - if (item->IsKindOf(RUNTIME_CLASS(APowerup))) + if (item->IsKindOf(ptype)) { - IFVIRTUALPTR(item, APowerup, InitEffect) + IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) { VMValue params[1] = { item }; VMFrameStack stack; diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 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/namedef.h b/src/namedef.h index 366676a13..0a3af96cb 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -709,6 +709,13 @@ xx(Wi_NoAutostartMap) xx(MorphStyle) xx(MorphFlash) xx(UnMorphFlash) +xx(Powerup) +xx(EffectTics) +xx(PowerupGiver) +xx(BlendColor) +xx(Strength) +xx(Mode) +xx(PowerupType) // Decorate compatibility functions xx(BuiltinTypeCheck) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 50dc1eb7a..fb0de5932 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5719,7 +5719,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) if (argCount >= 2) { PClassActor *powerupclass = PClass::FindActor(FBehavior::StaticLookupString(args[1])); - if (powerupclass == NULL || !RUNTIME_CLASS(APowerup)->IsAncestorOf(powerupclass)) + if (powerupclass == NULL || !powerupclass->IsDescendantOf(PClass::FindActor(NAME_Powerup))) { Printf("'%s' is not a type of Powerup.\n", FBehavior::StaticLookupString(args[1])); return 0; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c2a72e375..e52b10a8e 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5381,15 +5381,13 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) oldactor->DestroyAllInventory(); } // [BC] Handle temporary invulnerability when respawned - if ((state == PST_REBORN || state == PST_ENTER) && - (dmflags2 & DF2_YES_RESPAWN_INVUL) && - (multiplayer || alwaysapplydmflags)) + if (state == PST_REBORN || state == PST_ENTER) { - APowerup *invul = static_cast(p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerInvulnerable))); - invul->EffectTics = 3*TICRATE; - invul->BlendColor = 0; // don't mess with the view - invul->ItemFlags |= IF_UNDROPPABLE; // Don't drop this - p->mo->effects |= FX_RESPAWNINVUL; // [RH] special effect + IFVIRTUALPTR(p->mo, APlayerPawn, OnRespawn) + { + VMValue param = p->mo; + GlobalVMStack.Call(func, ¶m, 1, nullptr, 0); + } } if (StatusBar != NULL && (playernum == consoleplayer || StatusBar->GetPlayer() == playernum)) diff --git a/src/p_user.cpp b/src/p_user.cpp index a9dcc7cfa..b04a7be01 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1166,7 +1166,7 @@ void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer) } else if ((dmflags & DF_COOP_LOSE_POWERUPS) && defitem == NULL && - item->IsKindOf(RUNTIME_CLASS(APowerupGiver))) + item->IsKindOf(PClass::FindActor(NAME_PowerupGiver))) { item->Destroy(); } diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 170b5c49a..f441f8417 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -464,10 +464,17 @@ int MatchString (const char *in, const char **strings) // //========================================================================== +static bool PointerCheck(PType *symtype, PType *checktype) +{ + auto symptype = dyn_cast(symtype); + auto checkptype = dyn_cast(checktype); + return symptype != nullptr && checkptype != nullptr && symptype->ClassRestriction->IsDescendantOf(checkptype->ClassRestriction); +} + static void *ScriptVar(DObject *obj, PClass *cls, FName field, PType *type) { auto sym = dyn_cast(cls->Symbols.FindSymbol(field, true)); - if (sym && sym->Type == type) + if (sym && (sym->Type == type || PointerCheck(sym->Type, type))) { return (((char*)obj) + sym->Offset); } @@ -2278,14 +2285,11 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) int alpha; PalEntry *pBlendColor; + bool isgiver = info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver)); - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || isgiver) { - pBlendColor = &((APowerup*)defaults)->BlendColor; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pBlendColor = &((APowerupGiver*)defaults)->BlendColor; + pBlendColor = &TypedScriptVar(defaults, info, NAME_BlendColor, TypeColor); } else { @@ -2307,7 +2311,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) *pBlendColor = MakeSpecialColormap(v); return; } - else if (!stricmp(name, "none") && info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) + else if (!stricmp(name, "none") && isgiver) { *pBlendColor = MakeSpecialColormap(65535); return; @@ -2333,13 +2337,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, colormap, FFFfff, Inventory) { PalEntry * pBlendColor; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pBlendColor = &((APowerup*)defaults)->BlendColor; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pBlendColor = &((APowerupGiver*)defaults)->BlendColor; + pBlendColor = &TypedScriptVar(defaults, info, NAME_BlendColor, TypeColor); } else { @@ -2377,13 +2377,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, duration, I, Inventory) { int *pEffectTics; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pEffectTics = &((APowerup*)defaults)->EffectTics; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pEffectTics = &((APowerupGiver*)defaults)->EffectTics; + pEffectTics = &TypedScriptVar(defaults, info, NAME_EffectTics, TypeSInt32); } else { @@ -2402,13 +2398,9 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, strength, F, Inventory) { double *pStrength; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pStrength = &((APowerup*)defaults)->Strength; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pStrength = &((APowerupGiver*)defaults)->Strength; + pStrength = &TypedScriptVar(defaults, info, NAME_Strength, TypeFloat64); } else { @@ -2426,13 +2418,10 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, mode, S, Inventory) { PROP_STRING_PARM(str, 0); FName *pMode; - if (info->IsDescendantOf(RUNTIME_CLASS(APowerup))) + + if (info->IsDescendantOf(PClass::FindActor(NAME_Powerup)) || info->IsDescendantOf(PClass::FindActor(NAME_PowerupGiver))) { - pMode = &((APowerup*)defaults)->Mode; - } - else if (info->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) - { - pMode = &((APowerupGiver*)defaults)->Mode; + pMode = &TypedScriptVar(defaults, info, NAME_Mode, TypeName); } else { @@ -2445,7 +2434,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, mode, S, Inventory) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY_PREFIX(powerup, type, S, PowerupGiver) +DEFINE_SCRIPTED_PROPERTY_PREFIX(powerup, type, S, PowerupGiver) { PROP_STRING_PARM(str, 0); @@ -2465,8 +2454,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, type, S, PowerupGiver) I_Error("Unknown powerup type %s", str); } } - - defaults->PowerupType = cls; + TypedScriptVar(defaults, info, NAME_PowerupType, NewClassPointer(RUNTIME_CLASS(AActor))) = cls; } //========================================================================== diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 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/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 00639ce9e..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 { @@ -56,8 +56,6 @@ class PowerupGiver : Inventory native power.GoAwayAndDie (); return false; } - - } class Powerup : Inventory native diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 99f3d9c1f..b7799d731 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); From ade9e4c3da2769fc8be25e4761bc1e5be1e2e367 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 18 Jan 2017 01:27:50 +0100 Subject: [PATCH 10/12] - implemented processing of multiple return values in script functions. --- src/scripting/codegeneration/codegen.cpp | 89 ++++++++++++++++++++---- src/scripting/codegeneration/codegen.h | 3 +- src/scripting/zscript/zcc_compile.cpp | 9 +-- wadsrc/static/zscript/shared/player.txt | 6 +- 4 files changed, 80 insertions(+), 27 deletions(-) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 9fbfe3ef8..fe32fbb3e 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -9956,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); @@ -9996,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--) @@ -10013,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); } @@ -10035,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; @@ -10042,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 c31c42b07..8b43d28fa 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -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/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index c1cd274f0..da10c0cfb 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -3314,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/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index b7799d731..c3f3c6768 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -116,7 +116,7 @@ class PlayerPawn : Actor native } // This is for SBARINFO. - int/*, int*/ GetEffectTicsForItem(class item) + int, int GetEffectTicsForItem(class item) { let pg = (class)(item); if (pg != null) @@ -127,10 +127,10 @@ class PlayerPawn : Actor native { let maxtics = GetDefaultByType(powerupType).EffectTics; if (maxtics == 0) maxtics = powerup.default.EffectTics; - return powerup.EffectTics/*, maxtics*/; + return powerup.EffectTics, maxtics; } } - return 0/*, 0*/; + return 0, 0; } native int GetMaxHealth(); From 207f81cc37505ac92348d9bc5a53737f5b1a2f26 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 18 Jan 2017 01:45:02 +0100 Subject: [PATCH 11/12] - fixed: There was a temporarily commented out piece of code about the Sigil which I forgot to reinstate after making some changes. --- wadsrc/static/zscript/strife/alienspectres.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } From b41d4d9f84921577319658e2c539592abff61dbf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 18 Jan 2017 01:53:15 +0100 Subject: [PATCH 12/12] - initialize variable to make Valgrind happy. --- src/r_things.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/r_things.cpp b/src/r_things.cpp index 3ddd3110a..b6b4a472a 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -1363,6 +1363,7 @@ void R_DrawPSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double vis = &avis[vispspindex]; vis->renderflags = owner->renderflags; vis->floorclip = 0; + vis->sector = nullptr; vis->texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset;