From 9064a5b0ac0c11a86a1e6da5559e2682f29bce00 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 28 Nov 2016 18:15:18 +0100 Subject: [PATCH] - 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. --- src/dobjtype.cpp | 4 +- src/dobjtype.h | 1 + src/g_shared/a_artifacts.h | 1 + src/g_shared/a_pickups.cpp | 35 +++++++ src/g_shared/a_pickups.h | 5 +- src/g_shared/a_weapons.cpp | 2 +- src/g_strife/a_coin.cpp | 4 +- src/g_strife/a_strifeglobal.h | 10 -- src/g_strife/a_strifeitems.cpp | 2 +- src/p_conversation.cpp | 26 +++--- src/p_mobj.cpp | 2 +- src/scripting/codegeneration/codegen.cpp | 22 ++++- src/scripting/thingdef_data.cpp | 18 ++++ src/scripting/vm/vmdisasm.cpp | 4 + wadsrc/static/zscript/base.txt | 11 +++ wadsrc/static/zscript/shared/inventory.txt | 3 + wadsrc/static/zscript/strife/coin.txt | 104 ++++++++++++++++++++- 17 files changed, 221 insertions(+), 33 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 62a6a2b2d2..7377af56b7 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -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); diff --git a/src/dobjtype.h b/src/dobjtype.h index 89d3091564..ad626ee8bb 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -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; diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 95de950867..c3a0f31f08 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -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 (); diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index ad9fb70ce3..df434baae4 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -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 diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 76a3262549..8fc96cd57f 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -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); diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index c2feebec67..4f49ecd6f6 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -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 (Super::CreateTossable ()); diff --git a/src/g_strife/a_coin.cpp b/src/g_strife/a_coin.cpp index a94b1375d6..dcbbecf10b 100644 --- a/src/g_strife/a_coin.cpp +++ b/src/g_strife/a_coin.cpp @@ -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; } +*/ diff --git a/src/g_strife/a_strifeglobal.h b/src/g_strife/a_strifeglobal.h index 2561d961e5..adf8d59bf8 100644 --- a/src/g_strife/a_strifeglobal.h +++ b/src/g_strife/a_strifeglobal.h @@ -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) diff --git a/src/g_strife/a_strifeitems.cpp b/src/g_strife/a_strifeitems.cpp index 0586583ff5..0f1132cbd5 100644 --- a/src/g_strife/a_strifeitems.cpp +++ b/src/g_strife/a_strifeitems.cpp @@ -82,7 +82,7 @@ bool AHealthTraining::TryPickup (AActor *&toucher) if (Super::TryPickup (toucher)) { toucher->GiveInventoryType (PClass::FindActor("GunTraining")); - AInventory *coin = Spawn (); + AInventory *coin = (AInventory*)Spawn("Coin"); if (coin != NULL) { coin->Amount = toucher->player->mo->accuracy*5 + 300; diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 95aabe44b8..bf1c392aef 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -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; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6f0754a8fa..0f58b600c4 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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) { diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index f63629bb52..5277ad70e1 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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(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(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) { diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index d17e10420b..a1542a8a50 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -46,6 +46,8 @@ #include "p_maputl.h" #include "gi.h" #include "p_terrain.h" +#include "gstrings.h" +#include "zstring.h" static TArray properties; static TArray 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; +} \ No newline at end of file diff --git a/src/scripting/vm/vmdisasm.cpp b/src/scripting/vm/vmdisasm.cpp index 88962bf680..2158139abb 100644 --- a/src/scripting/vm/vmdisasm.cpp +++ b/src/scripting/vm/vmdisasm.cpp @@ -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)) diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index c76e4590ec..8d2e7a71bc 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -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); +} diff --git a/wadsrc/static/zscript/shared/inventory.txt b/wadsrc/static/zscript/shared/inventory.txt index d1164f2c4d..d3e50d1981 100644 --- a/wadsrc/static/zscript/shared/inventory.txt +++ b/wadsrc/static/zscript/shared/inventory.txt @@ -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(); diff --git a/wadsrc/static/zscript/strife/coin.txt b/wadsrc/static/zscript/strife/coin.txt index a930cb0ba9..b94a3f1d13 100644 --- a/wadsrc/static/zscript/strife/coin.txt +++ b/wadsrc/static/zscript/strife/coin.txt @@ -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; + } + }