diff --git a/src/g_inventory/a_health.cpp b/src/g_inventory/a_health.cpp index 58cd86e2a..86b4b3e86 100644 --- a/src/g_inventory/a_health.cpp +++ b/src/g_inventory/a_health.cpp @@ -37,114 +37,7 @@ #include "a_morph.h" #include "a_health.h" #include "serializer.h" - -//--------------------------------------------------------------------------- -// -// FUNC P_GiveBody -// -// Returns false if the body isn't needed at all. -// -//--------------------------------------------------------------------------- - -bool P_GiveBody (AActor *actor, int num, int max) -{ - if (actor->health <= 0 || (actor->player != NULL && actor->player->playerstate == PST_DEAD)) - { // Do not heal dead things. - return false; - } - - player_t *player = actor->player; - - num = clamp(num, -65536, 65536); // prevent overflows for bad values - if (player != NULL) - { - // Max is 0 by default, preserving default behavior for P_GiveBody() - // calls while supporting AHealth. - if (max <= 0) - { - max = static_cast(actor)->GetMaxHealth() + player->mo->stamina; - // [MH] First step in predictable generic morph effects - if (player->morphTics) - { - if (player->MorphStyle & MORPH_FULLHEALTH) - { - if (!(player->MorphStyle & MORPH_ADDSTAMINA)) - { - max -= player->mo->stamina; - } - } - else // old health behaviour - { - max = MAXMORPHHEALTH; - if (player->MorphStyle & MORPH_ADDSTAMINA) - { - max += player->mo->stamina; - } - } - } - } - // [RH] For Strife: A negative body sets you up with a percentage - // of your full health. - if (num < 0) - { - num = max * -num / 100; - if (player->health < num) - { - player->health = num; - actor->health = num; - return true; - } - } - else if (num > 0) - { - if (player->health < max) - { - num = int(num * G_SkillProperty(SKILLP_HealthFactor)); - if (num < 1) num = 1; - player->health += num; - if (player->health > max) - { - player->health = max; - } - actor->health = player->health; - return true; - } - } - } - else - { - // Parameter value for max is ignored on monsters, preserving original - // behaviour on AHealth as well as on existing calls to P_GiveBody(). - max = actor->SpawnHealth(); - if (num < 0) - { - num = max * -num / 100; - if (actor->health < num) - { - actor->health = num; - return true; - } - } - else if (actor->health < max) - { - actor->health += num; - if (actor->health > max) - { - actor->health = max; - } - return true; - } - } - return false; -} - -DEFINE_ACTION_FUNCTION(AActor, GiveBody) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_INT(num); - PARAM_INT_DEF(max); - ACTION_RETURN_BOOL(P_GiveBody(self, num, max)); -} +#include "p_local.h" //=========================================================================== // @@ -155,6 +48,8 @@ DEFINE_ACTION_FUNCTION(AActor, GiveBody) IMPLEMENT_CLASS(PClassHealth, false, false) IMPLEMENT_CLASS(AHealth, false, false) DEFINE_FIELD(AHealth, PrevHealth) +DEFINE_FIELD(PClassHealth, LowHealth) +DEFINE_FIELD(PClassHealth, LowHealthMessage) //=========================================================================== // @@ -184,108 +79,10 @@ void PClassHealth::DeriveData(PClass *newclass) } -//=========================================================================== -// -// AHealth :: PickupMessage -// -//=========================================================================== -FString AHealth::PickupMessage () -{ - int threshold = GetClass()->LowHealth; - - if (PrevHealth < threshold) - { - FString message = GetClass()->LowHealthMessage; - - if (message.IsNotEmpty()) - { - return message; - } - } - return Super::PickupMessage(); -} - -//=========================================================================== -// -// AHealth :: TryPickup -// -//=========================================================================== - -bool AHealth::TryPickup (AActor *&other) -{ - PrevHealth = other->player != NULL ? other->player->health : other->health; - - // P_GiveBody adds one new feature, applied only if it is possible to pick up negative health: - // Negative values are treated as positive percentages, ie Amount -100 means 100% health, ignoring max amount. - if (P_GiveBody(other, Amount, MaxAmount)) - { - GoAwayAndDie(); - return true; - } - return false; -} - IMPLEMENT_CLASS(AHealthPickup, false, false) DEFINE_FIELD(AHealthPickup, autousemode) -//=========================================================================== -// -// AHealthPickup :: CreateCopy -// -//=========================================================================== - -AInventory *AHealthPickup::CreateCopy (AActor *other) -{ - AInventory *copy = Super::CreateCopy (other); - copy->health = health; - return copy; -} - -//=========================================================================== -// -// AHealthPickup :: CreateTossable -// -//=========================================================================== - -AInventory *AHealthPickup::CreateTossable () -{ - AInventory *copy = Super::CreateTossable (); - if (copy != NULL) - { - copy->health = health; - } - return copy; -} - -//=========================================================================== -// -// AHealthPickup :: HandlePickup -// -//=========================================================================== - -bool AHealthPickup::HandlePickup (AInventory *item) -{ - // HealthPickups that are the same type but have different health amounts - // do not count as the same item. - if (item->health == health) - { - return Super::HandlePickup (item); - } - return false; -} - -//=========================================================================== -// -// AHealthPickup :: Use -// -//=========================================================================== - -bool AHealthPickup::Use (bool pickup) -{ - return P_GiveBody (Owner, health, 0); -} - //=========================================================================== // // AHealthPickup :: Serialize diff --git a/src/g_inventory/a_health.h b/src/g_inventory/a_health.h index af282031c..826d729f9 100644 --- a/src/g_inventory/a_health.h +++ b/src/g_inventory/a_health.h @@ -21,8 +21,6 @@ class AHealth : public AInventory public: int PrevHealth; - virtual bool TryPickup (AActor *&other) override; - virtual FString PickupMessage () override; }; // HealthPickup is some item that gives the player health when used. @@ -32,11 +30,6 @@ class AHealthPickup : public AInventory public: int autousemode; - virtual void Serialize(FSerializer &arc) override; - virtual AInventory *CreateCopy (AActor *other) override; - virtual AInventory *CreateTossable () override; - virtual bool HandlePickup (AInventory *item) override; - virtual bool Use (bool pickup) override; }; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index df064c525..9ee26cd44 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -73,6 +73,7 @@ #include "a_ammo.h" #include "a_health.h" #include "g_levellocals.h" +#include "a_morph.h" // MACROS ------------------------------------------------------------------ @@ -1318,6 +1319,115 @@ DEFINE_ACTION_FUNCTION(AActor, ObtainInventory) self->ObtainInventory(other); return 0; } + +//--------------------------------------------------------------------------- +// +// FUNC P_GiveBody +// +// Returns false if the body isn't needed at all. +// +//--------------------------------------------------------------------------- + +bool P_GiveBody(AActor *actor, int num, int max) +{ + if (actor->health <= 0 || (actor->player != NULL && actor->player->playerstate == PST_DEAD)) + { // Do not heal dead things. + return false; + } + + player_t *player = actor->player; + + num = clamp(num, -65536, 65536); // prevent overflows for bad values + if (player != NULL) + { + // Max is 0 by default, preserving default behavior for P_GiveBody() + // calls while supporting AHealth. + if (max <= 0) + { + max = static_cast(actor)->GetMaxHealth() + player->mo->stamina; + // [MH] First step in predictable generic morph effects + if (player->morphTics) + { + if (player->MorphStyle & MORPH_FULLHEALTH) + { + if (!(player->MorphStyle & MORPH_ADDSTAMINA)) + { + max -= player->mo->stamina; + } + } + else // old health behaviour + { + max = MAXMORPHHEALTH; + if (player->MorphStyle & MORPH_ADDSTAMINA) + { + max += player->mo->stamina; + } + } + } + } + // [RH] For Strife: A negative body sets you up with a percentage + // of your full health. + if (num < 0) + { + num = max * -num / 100; + if (player->health < num) + { + player->health = num; + actor->health = num; + return true; + } + } + else if (num > 0) + { + if (player->health < max) + { + num = int(num * G_SkillProperty(SKILLP_HealthFactor)); + if (num < 1) num = 1; + player->health += num; + if (player->health > max) + { + player->health = max; + } + actor->health = player->health; + return true; + } + } + } + else + { + // Parameter value for max is ignored on monsters, preserving original + // behaviour on AHealth as well as on existing calls to P_GiveBody(). + max = actor->SpawnHealth(); + if (num < 0) + { + num = max * -num / 100; + if (actor->health < num) + { + actor->health = num; + return true; + } + } + else if (actor->health < max) + { + actor->health += num; + if (actor->health > max) + { + actor->health = max; + } + return true; + } + } + return false; +} + +DEFINE_ACTION_FUNCTION(AActor, GiveBody) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(num); + PARAM_INT_DEF(max); + ACTION_RETURN_BOOL(P_GiveBody(self, num, max)); +} + //============================================================================ // // AActor :: CheckLocalView diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index f47bcfff9..c072e7799 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -7526,6 +7526,13 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) else if (Self->ValueType == TypeString) { + if (MethodName == NAME_Length) // This is an intrinsic because a dedicated opcode exists for it. + { + auto x = new FxStrLen(Self); + Self = nullptr; + delete this; + return x->Resolve(ctx); + } // same for String methods. It also uses a hidden struct type to define them. Self->ValueType = TypeStringStruct; } @@ -8735,6 +8742,39 @@ ExpEmit FxVectorBuiltin::Emit(VMFunctionBuilder *build) // //========================================================================== +FxStrLen::FxStrLen(FxExpression *self) + :FxExpression(EFX_StrLen, self->ScriptPosition) +{ + Self = self; +} + +FxStrLen::~FxStrLen() +{ + SAFE_DELETE(Self); +} + +FxExpression *FxStrLen::Resolve(FCompileContext &ctx) +{ + SAFE_RESOLVE(Self, ctx); + assert(Self->ValueType == TypeString); + ValueType = TypeUInt32; + return this; +} + +ExpEmit FxStrLen::Emit(VMFunctionBuilder *build) +{ + ExpEmit to(build, REGT_INT); + ExpEmit op = Self->Emit(build); + build->Emit(OP_LENS, to.RegNum, op.RegNum); + op.Free(build); + return to; +} + +//========================================================================== +// +// +//========================================================================== + FxGetClass::FxGetClass(FxExpression *self) :FxExpression(EFX_GetClass, self->ScriptPosition) { @@ -8774,7 +8814,7 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build) //========================================================================== FxGetParentClass::FxGetParentClass(FxExpression *self) - :FxExpression(EFX_GetClass, self->ScriptPosition) + :FxExpression(EFX_GetParentClass, self->ScriptPosition) { Self = self; } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index f5fafb9e7..e7cfab662 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -289,6 +289,8 @@ enum EFxType EFX_CVar, EFX_NamedNode, EFX_GetClass, + EFX_GetParentClass, + EFX_StrLen, EFX_ColorLiteral, EFX_GetDefaultByType, EFX_COUNT @@ -1571,6 +1573,24 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxVectorBuiltin +// +//========================================================================== + +class FxStrLen : public FxExpression +{ + FxExpression *Self; + +public: + + FxStrLen(FxExpression *self); + ~FxStrLen(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxGetClass diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 6352dd0b8..16f3b6f6c 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -11,6 +11,7 @@ #include "zscript/inventory/weapons.txt" #include "zscript/inventory/armor.txt" #include "zscript/inventory/ammo.txt" +#include "zscript/inventory/health.txt" #include "zscript/inventory/powerups.txt" #include "zscript/shared/player.txt" diff --git a/wadsrc/static/zscript/inventory/ammo.txt b/wadsrc/static/zscript/inventory/ammo.txt index 2b36c287c..be9933a70 100644 --- a/wadsrc/static/zscript/inventory/ammo.txt +++ b/wadsrc/static/zscript/inventory/ammo.txt @@ -1,3 +1,37 @@ +/* +** a_ammo.cpp +** Implements ammo and backpack items. +** +**--------------------------------------------------------------------------- +** Copyright 2000-2016 Randy Heit +** Copyright 2006-2017 Cheistoph 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. +**--------------------------------------------------------------------------- +** +*/ class Ammo : Inventory native { diff --git a/wadsrc/static/zscript/inventory/health.txt b/wadsrc/static/zscript/inventory/health.txt new file mode 100644 index 000000000..f5d0f6628 --- /dev/null +++ b/wadsrc/static/zscript/inventory/health.txt @@ -0,0 +1,159 @@ +/* +** a_health.cpp +** All health items +** +**--------------------------------------------------------------------------- +** Copyright 2000-2016 Randy Heit +** Copyright 2006-2017 Cheistoph 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. +**--------------------------------------------------------------------------- +** +*/ + +class Health : Inventory native +{ + native int PrevHealth; + native meta int LowHealth; + native meta String LowHealthMessage; + + Default + { + Inventory.Amount 1; + Inventory.MaxAmount 0; + Inventory.PickupSound "misc/health_pkup"; + } + + //=========================================================================== + // + // AHealth :: PickupMessage + // + //=========================================================================== + override String PickupMessage () + { + if (PrevHealth < LowHealth) + { + String message = LowHealthMessage; + + if (message.Length() != 0) + { + return message; + } + } + return Super.PickupMessage(); + } + + //=========================================================================== + // + // TryPickup + // + //=========================================================================== + + override bool TryPickup (in out Actor other) + { + PrevHealth = other.player != NULL ? other.player.health : other.health; + + // P_GiveBody adds one new feature, applied only if it is possible to pick up negative health: + // Negative values are treated as positive percentages, ie Amount -100 means 100% health, ignoring max amount. + if (other.GiveBody(Amount, MaxAmount)) + { + GoAwayAndDie(); + return true; + } + return false; + } + + +} + +class HealthPickup : Inventory native +{ + native int autousemode; + + Default + { + Inventory.DefMaxAmount; + +INVENTORY.INVBAR + } + + //=========================================================================== + // + // CreateCopy + // + //=========================================================================== + + override Inventory CreateCopy (Actor other) + { + Inventory copy = Super.CreateCopy (other); + copy.health = health; + return copy; + } + + //=========================================================================== + // + // CreateTossable + // + //=========================================================================== + + override Inventory CreateTossable () + { + Inventory copy = Super.CreateTossable (); + if (copy != NULL) + { + copy.health = health; + } + return copy; + } + + //=========================================================================== + // + // HandlePickup + // + //=========================================================================== + + override bool HandlePickup (Inventory item) + { + // HealthPickups that are the same type but have different health amounts + // do not count as the same item. + if (item.health == health) + { + return Super.HandlePickup (item); + } + return false; + } + + //=========================================================================== + // + // Use + // + //=========================================================================== + + override bool Use (bool pickup) + { + return Owner.GiveBody (health, 0); + } + + +} diff --git a/wadsrc/static/zscript/inventory/inv_misc.txt b/wadsrc/static/zscript/inventory/inv_misc.txt index 032c9e826..3aa1a4df5 100644 --- a/wadsrc/static/zscript/inventory/inv_misc.txt +++ b/wadsrc/static/zscript/inventory/inv_misc.txt @@ -19,6 +19,8 @@ class ScoreItem : Inventory class Health : Inventory native { native int PrevHealth; + native meta int LowHealth; + native meta String LowHealthMessage; Default { @@ -26,6 +28,47 @@ class Health : Inventory native Inventory.MaxAmount 0; Inventory.PickupSound "misc/health_pkup"; } + + //=========================================================================== + // + // AHealth :: PickupMessage + // + //=========================================================================== + override String PickupMessage () + { + if (PrevHealth < LowHealth) + { + String message = LowHealthMessage; + + if (message.Length() != 0) + { + return message; + } + } + return Super.PickupMessage(); + } + + //=========================================================================== + // + // TryPickup + // + //=========================================================================== + + override bool TryPickup (in out Actor other) + { + PrevHealth = other.player != NULL ? other.player.health : other.health; + + // P_GiveBody adds one new feature, applied only if it is possible to pick up negative health: + // Negative values are treated as positive percentages, ie Amount -100 means 100% health, ignoring max amount. + if (other.GiveBody(Amount, MaxAmount)) + { + GoAwayAndDie(); + return true; + } + return false; + } + + } class HealthPickup : Inventory native @@ -37,6 +80,65 @@ class HealthPickup : Inventory native Inventory.DefMaxAmount; +INVENTORY.INVBAR } + + //=========================================================================== + // + // CreateCopy + // + //=========================================================================== + + override Inventory CreateCopy (Actor other) + { + Inventory copy = Super.CreateCopy (other); + copy.health = health; + return copy; + } + + //=========================================================================== + // + // CreateTossable + // + //=========================================================================== + + override Inventory CreateTossable () + { + Inventory copy = Super.CreateTossable (); + if (copy != NULL) + { + copy.health = health; + } + return copy; + } + + //=========================================================================== + // + // HandlePickup + // + //=========================================================================== + + override bool HandlePickup (Inventory item) + { + // HealthPickups that are the same type but have different health amounts + // do not count as the same item. + if (item.health == health) + { + return Super.HandlePickup (item); + } + return false; + } + + //=========================================================================== + // + // Use + // + //=========================================================================== + + override bool Use (bool pickup) + { + return Owner.GiveBody (health, 0); + } + + } class Key : Inventory native @@ -55,7 +157,7 @@ class MapRevealer : Inventory { //=========================================================================== // - // AMapRevealer :: TryPickup + // AMapRevealer . TryPickup // // A MapRevealer reveals the whole map for the player who picks it up. // The MapRevealer doesn't actually go in your inventory. Instead, it sets