- scriptified Strife's coins.

- added a String class to allow attaching methods to the builtin string type. This works by checking if the left side of the member accessor is a string and just replacing the tyoe in this one place, all the rest is automatic.
This commit is contained in:
Christoph Oelckers 2016-11-28 18:15:18 +01:00
parent d2ce78fae7
commit 9064a5b0ac
17 changed files with 221 additions and 33 deletions

View file

@ -89,6 +89,7 @@ PStateLabel *TypeStateLabel;
PStruct *TypeVector2;
PStruct *TypeVector3;
PStruct *TypeColorStruct;
PStruct *TypeStringStruct;
PPointer *TypeNullPtr;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -587,7 +588,8 @@ void PType::StaticInit()
TypeTable.AddType(TypeSpriteID = new PSpriteID);
TypeTable.AddType(TypeTextureID = new PTextureID);
TypeColorStruct = new PStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value.
TypeStringStruct = NewNativeStruct(NAME_String, nullptr);
#ifdef __BIG_ENDIAN__
TypeColorStruct->AddField(NAME_a, TypeUInt8);
TypeColorStruct->AddField(NAME_r, TypeUInt8);

View file

@ -945,6 +945,7 @@ extern PSpriteID *TypeSpriteID;
extern PStruct *TypeVector2;
extern PStruct *TypeVector3;
extern PStruct *TypeColorStruct;
extern PStruct *TypeStringStruct;
extern PStatePointer *TypeState;
extern PStateLabel *TypeStateLabel;
extern PPointer *TypeNullPtr;

View file

@ -16,6 +16,7 @@ public:
virtual bool HandlePickup (AInventory *item);
virtual AInventory *CreateCopy (AActor *other);
virtual AInventory *CreateTossable ();
virtual void Serialize(FSerializer &arc);
virtual void OwnerDied ();

View file

@ -919,6 +919,27 @@ AInventory *AInventory::CreateTossable ()
return copy;
}
DEFINE_ACTION_FUNCTION(AInventory, CreateTossable)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_OBJECT(self->CreateTossable());
}
AInventory *AInventory::CallCreateTossable()
{
IFVIRTUAL(AInventory, CreateTossable)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
VMFrameStack stack;
AInventory *retval;
ret.PointerAt((void**)&retval);
stack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
else return CreateTossable();
}
//===========================================================================
//
// AInventory :: BecomeItem
@ -946,6 +967,13 @@ void AInventory::BecomeItem ()
SetState (FindState("Held"));
}
DEFINE_ACTION_FUNCTION(AInventory, BecomeItem)
{
PARAM_SELF_PROLOGUE(AInventory);
self->BecomeItem();
return 0;
}
//===========================================================================
//
// AInventory :: BecomePickup
@ -973,6 +1001,13 @@ void AInventory::BecomePickup ()
SetState (SpawnState);
}
DEFINE_ACTION_FUNCTION(AInventory, BecomePickup)
{
PARAM_SELF_PROLOGUE(AInventory);
self->BecomePickup();
return 0;
}
//===========================================================================
//
// AInventory :: AbsorbDamage

View file

@ -196,13 +196,14 @@ public:
FSoundIDNoInit PickupSound;
virtual void BecomeItem ();
virtual void BecomePickup ();
void BecomeItem ();
void BecomePickup ();
virtual void AttachToOwner (AActor *other);
virtual void DetachFromOwner ();
virtual AInventory *CreateCopy (AActor *other);
AInventory *CallCreateCopy(AActor *other);
virtual AInventory *CreateTossable ();
AInventory *CallCreateTossable();
virtual bool GoAway ();
virtual void GoAwayAndDie ();
virtual bool HandlePickup (AInventory *item);

View file

@ -424,7 +424,7 @@ AInventory *AWeapon::CreateTossable ()
(((AWeapon*)SisterWeapon->GetDefault())->AmmoGive1 > 0 ||
((AWeapon*)SisterWeapon->GetDefault())->AmmoGive2 > 0))
{
return SisterWeapon->CreateTossable ();
return SisterWeapon->CallCreateTossable ();
}
AWeapon *copy = static_cast<AWeapon *> (Super::CreateTossable ());

View file

@ -6,9 +6,9 @@
// Coin ---------------------------------------------------------------------
/*
IMPLEMENT_CLASS(ACoin, false, false)
/*
const char *ACoin::PickupMessage ()
{
if (Amount == 1)
@ -23,7 +23,6 @@ const char *ACoin::PickupMessage ()
return msg;
}
}
*/
bool ACoin::HandlePickup (AInventory *item)
{
@ -108,3 +107,4 @@ AInventory *ACoin::CreateTossable ()
}
return tossed;
}
*/

View file

@ -13,16 +13,6 @@ public:
bool Use (bool pickup);
};
class ACoin : public AInventory
{
DECLARE_CLASS (ACoin, AInventory)
public:
//const char *PickupMessage ();
bool HandlePickup (AInventory *item);
AInventory *CreateTossable ();
AInventory *CreateCopy (AActor *other);
};
class ADummyStrifeItem : public AInventory
{
DECLARE_CLASS (ADummyStrifeItem, AInventory)

View file

@ -82,7 +82,7 @@ bool AHealthTraining::TryPickup (AActor *&toucher)
if (Super::TryPickup (toucher))
{
toucher->GiveInventoryType (PClass::FindActor("GunTraining"));
AInventory *coin = Spawn<ACoin> ();
AInventory *coin = (AInventory*)Spawn("Coin");
if (coin != NULL)
{
coin->Amount = toucher->player->mo->accuracy*5 + 300;

View file

@ -1053,18 +1053,22 @@ public:
if (ShowGold)
{
AInventory *coin = cp->ConversationPC->FindInventory (RUNTIME_CLASS(ACoin));
char goldstr[32];
auto cointype = PClass::FindActor("Coin");
if (cointype)
{
AInventory *coin = cp->ConversationPC->FindInventory(cointype);
char goldstr[32];
mysnprintf (goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0);
screen->DrawText (SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true,
DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE);
screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon),
3, 190, DTA_320x200, true,
DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE);
screen->DrawText (SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE);
screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon),
2, 189, DTA_320x200, true, TAG_DONE);
mysnprintf(goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0);
screen->DrawText(SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true,
DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE);
screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon),
3, 190, DTA_320x200, true,
DTA_FillColor, 0, DTA_AlphaF, HR_SHADOW, TAG_DONE);
screen->DrawText(SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE);
screen->DrawTexture(TexMan(((AInventory *)GetDefaultByType(cointype))->Icon),
2, 189, DTA_320x200, true, TAG_DONE);
}
}
y = mYpos;

View file

@ -1002,7 +1002,7 @@ bool AActor::UseInventory(AInventory *item)
AInventory *AActor::DropInventory (AInventory *item)
{
AInventory *drop = item->CreateTossable ();
AInventory *drop = item->CallCreateTossable ();
if (drop == NULL)
{

View file

@ -5766,9 +5766,11 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx)
// allow accessing the color channels by mapping the type to a matching struct which defines them.
if (Object->ValueType == TypeColor)
{
Object->ValueType = TypeColorStruct;
}
if (Object->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
else if (Object->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
{
auto ptype = static_cast<PPointer *>(Object->ValueType)->PointedType;
if (ptype->IsKindOf(RUNTIME_CLASS(PStruct)))
@ -7306,6 +7308,12 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
}
}
if (Self->ValueType == TypeString)
{
// same for String methods. It also uses a hidden struct type to define them.
Self->ValueType = TypeStringStruct;
}
if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
{
auto ptype = static_cast<PPointer *>(Self->ValueType)->PointedType;
@ -7876,8 +7884,16 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
{
assert(Self != nullptr);
selfemit = Self->Emit(build);
assert(selfemit.RegType == REGT_POINTER);
build->Emit(OP_PARAM, 0, selfemit.RegType, selfemit.RegNum);
assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target));
if (selfemit.Fixed && selfemit.Target)
{
// Address of a local variable.
build->Emit(OP_PARAM, 0, selfemit.RegType | REGT_ADDROF, selfemit.RegNum);
}
else
{
build->Emit(OP_PARAM, 0, selfemit.RegType, selfemit.RegNum);
}
count += 1;
if (Function->Variants[0].Flags & VARF_Action)
{

View file

@ -46,6 +46,8 @@
#include "p_maputl.h"
#include "gi.h"
#include "p_terrain.h"
#include "gstrings.h"
#include "zstring.h"
static TArray<FPropertyInfo*> properties;
static TArray<AFuncDesc> AFTable;
@ -826,3 +828,19 @@ DEFINE_ACTION_FUNCTION(DObject, GameType)
PARAM_PROLOGUE;
ACTION_RETURN_INT(gameinfo.gametype);
}
DEFINE_ACTION_FUNCTION(FStringTable, Localize)
{
PARAM_PROLOGUE;
PARAM_STRING(label);
ACTION_RETURN_STRING(GStrings(label));
}
DEFINE_ACTION_FUNCTION(FString, Replace)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(s1);
PARAM_STRING(s2);
self->Substitute(*s1, *s2);
return 0;
}

View file

@ -289,6 +289,10 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
a &= CMP_CHECK | CMP_APPROX;
cmp = true;
}
if (code[i].op == OP_PARAM && code[i].b & REGT_ADDROF)
{
name = "parama";
}
if (cmp)
{ // Comparison instruction. Modify name for inverted test.
if (!(a & CMP_CHECK))

View file

@ -98,6 +98,11 @@ struct LevelLocals native
// level_info_t *info cannot be done yet.
}
struct StringTable native
{
native static String Localize(String val);
}
// a few values of this need to be readable by the play code.
// Most are handled at load time and are omitted here.
struct DehInfo native
@ -275,3 +280,9 @@ enum EPickStart
PPS_FORCERANDOM = 1,
PPS_NOBLOCKINGCHECK = 2,
}
// Although String is a builtin type, this is a convenient way to attach methods to it.
struct String native
{
native void Replace(String pattern, String replacement);
}

View file

@ -29,10 +29,13 @@ class Inventory : Actor native
virtual native color GetBlend ();
virtual native bool HandlePickup(Inventory item);
virtual native Inventory CreateCopy(Actor other);
virtual native Inventory CreateTossable();
virtual native bool SpecialDropAction (Actor dropper);
virtual native String PickupMessage();
native void GoAwayAndDie();
native void BecomeItem();
native void BecomePickup();
// These are regular functions for the item itself.
private native void A_RestoreSpecialDoomThing();

View file

@ -1,7 +1,7 @@
// Coin ---------------------------------------------------------------------
class Coin : Inventory native
class Coin : Inventory
{
Default
{
@ -20,6 +20,108 @@ class Coin : Inventory native
COIN A -1;
Stop;
}
// Coin ---------------------------------------------------------------------
override String PickupMessage ()
{
if (Amount == 1)
{
return Super.PickupMessage();
}
else
{
String msg = StringTable.Localize("TXT_XGOLD");
msg.Replace("%d", "" .. Amount);
return msg;
}
}
override bool HandlePickup (Inventory item)
{
if (item is "Coin")
{
if (Amount < MaxAmount)
{
if (MaxAmount - Amount < item.Amount)
{
Amount = MaxAmount;
}
else
{
Amount += item.Amount;
}
item.bPickupGood = true;
}
return true;
}
return false;
}
override Inventory CreateCopy (Actor other)
{
if (GetClass() == "Coin")
{
return Super.CreateCopy (other);
}
Inventory copy = Inventory(Spawn("Coin"));
copy.Amount = Amount;
copy.BecomeItem ();
GoAwayAndDie ();
return copy;
}
//===========================================================================
//
// ACoin :: CreateTossable
//
// Gold drops in increments of 50 if you have that much, less if you don't.
//
//===========================================================================
override Inventory CreateTossable ()
{
Coin tossed;
if (bUndroppable || Owner == NULL || Amount <= 0)
{
return NULL;
}
if (Amount >= 50)
{
Amount -= 50;
tossed = Coin(Spawn("Gold50"));
}
else if (Amount >= 25)
{
Amount -= 25;
tossed = Coin(Spawn("Gold25"));
}
else if (Amount >= 10)
{
Amount -= 10;
tossed = Coin(Spawn("Gold10"));
}
else if (Amount > 1 || bKeepDepleted)
{
Amount -= 1;
tossed = Coin(Spawn("Coin"));
}
else // Amount == 1 && !(ItemFlags & IF_KEEPDEPLETED)
{
BecomePickup ();
tossed = self;
}
tossed.bSpecial = false;
tossed.bSolid = false;
tossed.DropTime = 30;
if (tossed != self && Amount <= 0)
{
Destroy ();
}
return tossed;
}
}