- scriptified the health items.

This commit is contained in:
Christoph Oelckers 2017-01-15 01:02:38 +01:00
parent 917b36c6ae
commit 9f9cea4b4a
9 changed files with 471 additions and 215 deletions

View File

@ -37,114 +37,7 @@
#include "a_morph.h" #include "a_morph.h"
#include "a_health.h" #include "a_health.h"
#include "serializer.h" #include "serializer.h"
#include "p_local.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<APlayerPawn*>(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));
}
//=========================================================================== //===========================================================================
// //
@ -155,6 +48,8 @@ DEFINE_ACTION_FUNCTION(AActor, GiveBody)
IMPLEMENT_CLASS(PClassHealth, false, false) IMPLEMENT_CLASS(PClassHealth, false, false)
IMPLEMENT_CLASS(AHealth, false, false) IMPLEMENT_CLASS(AHealth, false, false)
DEFINE_FIELD(AHealth, PrevHealth) 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) IMPLEMENT_CLASS(AHealthPickup, false, false)
DEFINE_FIELD(AHealthPickup, autousemode) 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 // AHealthPickup :: Serialize

View File

@ -21,8 +21,6 @@ class AHealth : public AInventory
public: public:
int PrevHealth; int PrevHealth;
virtual bool TryPickup (AActor *&other) override;
virtual FString PickupMessage () override;
}; };
// HealthPickup is some item that gives the player health when used. // HealthPickup is some item that gives the player health when used.
@ -32,11 +30,6 @@ class AHealthPickup : public AInventory
public: public:
int autousemode; int autousemode;
virtual void Serialize(FSerializer &arc) override; 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;
}; };

View File

@ -73,6 +73,7 @@
#include "a_ammo.h" #include "a_ammo.h"
#include "a_health.h" #include "a_health.h"
#include "g_levellocals.h" #include "g_levellocals.h"
#include "a_morph.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -1318,6 +1319,115 @@ DEFINE_ACTION_FUNCTION(AActor, ObtainInventory)
self->ObtainInventory(other); self->ObtainInventory(other);
return 0; 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<APlayerPawn*>(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 // AActor :: CheckLocalView

View File

@ -7526,6 +7526,13 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
else if (Self->ValueType == TypeString) 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. // same for String methods. It also uses a hidden struct type to define them.
Self->ValueType = TypeStringStruct; 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) FxGetClass::FxGetClass(FxExpression *self)
:FxExpression(EFX_GetClass, self->ScriptPosition) :FxExpression(EFX_GetClass, self->ScriptPosition)
{ {
@ -8774,7 +8814,7 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build)
//========================================================================== //==========================================================================
FxGetParentClass::FxGetParentClass(FxExpression *self) FxGetParentClass::FxGetParentClass(FxExpression *self)
:FxExpression(EFX_GetClass, self->ScriptPosition) :FxExpression(EFX_GetParentClass, self->ScriptPosition)
{ {
Self = self; Self = self;
} }

View File

@ -289,6 +289,8 @@ enum EFxType
EFX_CVar, EFX_CVar,
EFX_NamedNode, EFX_NamedNode,
EFX_GetClass, EFX_GetClass,
EFX_GetParentClass,
EFX_StrLen,
EFX_ColorLiteral, EFX_ColorLiteral,
EFX_GetDefaultByType, EFX_GetDefaultByType,
EFX_COUNT EFX_COUNT
@ -1571,6 +1573,24 @@ public:
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
}; };
//==========================================================================
//
// FxVectorBuiltin
//
//==========================================================================
class FxStrLen : public FxExpression
{
FxExpression *Self;
public:
FxStrLen(FxExpression *self);
~FxStrLen();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//========================================================================== //==========================================================================
// //
// FxGetClass // FxGetClass

View File

@ -11,6 +11,7 @@
#include "zscript/inventory/weapons.txt" #include "zscript/inventory/weapons.txt"
#include "zscript/inventory/armor.txt" #include "zscript/inventory/armor.txt"
#include "zscript/inventory/ammo.txt" #include "zscript/inventory/ammo.txt"
#include "zscript/inventory/health.txt"
#include "zscript/inventory/powerups.txt" #include "zscript/inventory/powerups.txt"
#include "zscript/shared/player.txt" #include "zscript/shared/player.txt"

View File

@ -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 class Ammo : Inventory native
{ {

View File

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

View File

@ -19,6 +19,8 @@ class ScoreItem : Inventory
class Health : Inventory native class Health : Inventory native
{ {
native int PrevHealth; native int PrevHealth;
native meta int LowHealth;
native meta String LowHealthMessage;
Default Default
{ {
@ -26,6 +28,47 @@ class Health : Inventory native
Inventory.MaxAmount 0; Inventory.MaxAmount 0;
Inventory.PickupSound "misc/health_pkup"; 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 class HealthPickup : Inventory native
@ -37,6 +80,65 @@ class HealthPickup : Inventory native
Inventory.DefMaxAmount; Inventory.DefMaxAmount;
+INVENTORY.INVBAR +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 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. // 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 // The MapRevealer doesn't actually go in your inventory. Instead, it sets