From 4759f9a399ec9911e80d4f27b424a2e27ee7426a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 14 Jan 2017 23:34:47 +0100 Subject: [PATCH] - scriptified the backpack. - added GetParentClass builtin to compiler. --- src/fragglescript/t_func.cpp | 2 +- src/g_inventory/a_ammo.cpp | 170 ----------------------- src/g_inventory/a_ammo.h | 18 --- src/m_cheat.cpp | 2 +- src/namedef.h | 2 + src/scripting/codegeneration/codegen.cpp | 69 +++++++++ src/scripting/codegeneration/codegen.h | 18 +++ src/scripting/thingdef_data.cpp | 5 + wadsrc/static/zscript/inventory/ammo.txt | 152 +++++++++++++++++++- 9 files changed, 246 insertions(+), 192 deletions(-) diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 886b735a0..7c5adb6bc 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -2637,7 +2637,7 @@ void FParser::SF_MaxPlayerAmmo() for (AInventory *item = players[playernum].mo->Inventory; item != NULL; item = item->Inventory) { - if (item->IsKindOf(RUNTIME_CLASS(ABackpackItem))) + if (item->IsKindOf(PClass::FindClass(NAME_BackpackItem))) { if (t_argc>=4) amount = intvalue(t_argv[3]); else amount*=2; diff --git a/src/g_inventory/a_ammo.cpp b/src/g_inventory/a_ammo.cpp index ef8d8ab6e..b1765e1e4 100644 --- a/src/g_inventory/a_ammo.cpp +++ b/src/g_inventory/a_ammo.cpp @@ -89,173 +89,3 @@ DEFINE_ACTION_FUNCTION(AAmmo, GetParentAmmo) PARAM_SELF_PROLOGUE(AAmmo); ACTION_RETURN_OBJECT(self->GetParentAmmo()); } - -// Backpack ----------------------------------------------------------------- - -IMPLEMENT_CLASS(ABackpackItem, false, false) - -DEFINE_FIELD(ABackpackItem, bDepleted) - -//=========================================================================== -// -// ABackpackItem :: Serialize -// -//=========================================================================== - -void ABackpackItem::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - auto def = (ABackpackItem*)GetDefault(); - arc("bdepleted", bDepleted, def->bDepleted); -} - -EXTERN_CVAR(Bool, sv_unlimited_pickup) -//=========================================================================== -// -// ABackpackItem :: CreateCopy -// -// A backpack is being added to a player who doesn't yet have one. Give them -// every kind of ammo, and increase their max amounts. -// -//=========================================================================== - -AInventory *ABackpackItem::CreateCopy (AActor *other) -{ - // Find every unique type of ammo. Give it to the player if - // he doesn't have it already, and double its maximum capacity. - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - PClass *type = PClassActor::AllActorClasses[i]; - - if (type->ParentClass == RUNTIME_CLASS(AAmmo)) - { - PClassActor *atype = static_cast(type); - AAmmo *ammo = static_cast(other->FindInventory(atype)); - int amount = static_cast(GetDefaultByType(type))->BackpackAmount; - // extra ammo in baby mode and nightmare mode - if (!(ItemFlags&IF_IGNORESKILL)) - { - amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor)); - } - if (amount < 0) amount = 0; - if (ammo == NULL) - { // The player did not have the ammo. Add it. - ammo = static_cast(Spawn(atype)); - ammo->Amount = bDepleted ? 0 : amount; - if (ammo->BackpackMaxAmount > ammo->MaxAmount) - { - ammo->MaxAmount = ammo->BackpackMaxAmount; - } - if (ammo->Amount > ammo->MaxAmount) - { - ammo->Amount = ammo->MaxAmount; - } - ammo->AttachToOwner (other); - } - else - { // The player had the ammo. Give some more. - if (ammo->MaxAmount < ammo->BackpackMaxAmount) - { - ammo->MaxAmount = ammo->BackpackMaxAmount; - } - if (!bDepleted && ammo->Amount < ammo->MaxAmount) - { - ammo->Amount += amount; - if (ammo->Amount > ammo->MaxAmount) - { - ammo->Amount = ammo->MaxAmount; - } - } - } - } - } - return Super::CreateCopy (other); -} - -//=========================================================================== -// -// ABackpackItem :: HandlePickup -// -// When the player picks up another backpack, just give them more ammo. -// -//=========================================================================== - -bool ABackpackItem::HandlePickup (AInventory *item) -{ - // Since you already have a backpack, that means you already have every - // kind of ammo in your inventory, so we don't need to look at the - // entire PClass list to discover what kinds of ammo exist, and we don't - // have to alter the MaxAmount either. - if (item->IsKindOf (RUNTIME_CLASS(ABackpackItem))) - { - for (AInventory *probe = Owner->Inventory; probe != NULL; probe = probe->Inventory) - { - if (probe->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo)) - { - if (probe->Amount < probe->MaxAmount || sv_unlimited_pickup) - { - int amount = static_cast(probe->GetDefault())->BackpackAmount; - // extra ammo in baby mode and nightmare mode - if (!(item->ItemFlags&IF_IGNORESKILL)) - { - amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor)); - } - probe->Amount += amount; - if (probe->Amount > probe->MaxAmount && !sv_unlimited_pickup) - { - probe->Amount = probe->MaxAmount; - } - } - } - } - // The pickup always succeeds, even if you didn't get anything - item->ItemFlags |= IF_PICKUPGOOD; - return true; - } - return false; -} - -//=========================================================================== -// -// ABackpackItem :: CreateTossable -// -// The tossed backpack must not give out any more ammo, otherwise a player -// could cheat by dropping their backpack and picking it up for more ammo. -// -//=========================================================================== - -AInventory *ABackpackItem::CreateTossable () -{ - ABackpackItem *pack = static_cast(Super::CreateTossable()); - if (pack != NULL) - { - pack->bDepleted = true; - } - return pack; -} - -//=========================================================================== -// -// ABackpackItem :: DetachFromOwner -// -//=========================================================================== - -void ABackpackItem::DetachFromOwner () -{ - // When removing a backpack, drop the player's ammo maximums to normal - AInventory *item; - - for (item = Owner->Inventory; item != NULL; item = item->Inventory) - { - if (item->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo) && - item->MaxAmount == static_cast(item)->BackpackMaxAmount) - { - item->MaxAmount = static_cast(item->GetDefault())->MaxAmount; - if (item->Amount > item->MaxAmount) - { - item->Amount = item->MaxAmount; - } - } - } -} - diff --git a/src/g_inventory/a_ammo.h b/src/g_inventory/a_ammo.h index f00bbe8d2..445f44a34 100644 --- a/src/g_inventory/a_ammo.h +++ b/src/g_inventory/a_ammo.h @@ -12,21 +12,3 @@ public: int BackpackAmount, BackpackMaxAmount, DropAmount; }; - -// A backpack gives you one clip of each ammo and doubles your -// normal maximum ammo amounts. -class ABackpackItem : public AInventory -{ - DECLARE_CLASS (ABackpackItem, AInventory) -public: - - virtual void Serialize(FSerializer &arc) override; - virtual bool HandlePickup (AInventory *item) override; - virtual AInventory *CreateCopy (AActor *other) override; - virtual AInventory *CreateTossable () override; - virtual void DetachFromOwner () override; - - bool bDepleted; -}; - - diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 36c6249cd..bbf366995 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -859,7 +859,7 @@ void cht_Take (player_t *player, const char *name, int amount) { PClass *type = PClassActor::AllActorClasses[i]; - if (type->IsDescendantOf(RUNTIME_CLASS (ABackpackItem))) + if (type->IsDescendantOf(PClass::FindClass(NAME_BackpackItem))) { AInventory *pack = player->mo->FindInventory(static_cast(type)); diff --git a/src/namedef.h b/src/namedef.h index aa7969193..cfed6c63e 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -165,6 +165,7 @@ xx(FlameThrower) xx(MiniMissileLauncher) xx(StrifeGrenadeLauncher) xx(Mauler) +xx(BackpackItem) xx(AcolyteBlue) xx(SpectralLightningV1) @@ -292,6 +293,7 @@ xx(Random2) xx(RandomPick) xx(FRandomPick) xx(GetClass) +xx(GetParentClass) xx(GetDefaultByType) xx(Exp) xx(Log10) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 61d86db80..f47bcfff9 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -7265,6 +7265,13 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) } break; + case NAME_GetParentClass: + if (CheckArgSize(NAME_GetParentClass, ArgList, 0, 0, ScriptPosition)) + { + func = new FxGetParentClass(new FxSelf(ScriptPosition)); + } + break; + case NAME_GetDefaultByType: if (CheckArgSize(NAME_GetDefaultByType, ArgList, 1, 1, ScriptPosition)) { @@ -7575,6 +7582,20 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } } + + if (MethodName == NAME_GetParentClass && + (Self->IsObject() || Self->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)))) + { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } + auto x = new FxGetParentClass(Self); + return x->Resolve(ctx); + } + if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) { auto ptype = static_cast(Self->ValueType)->PointedType; @@ -7591,6 +7612,8 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) auto x = new FxGetClass(Self); return x->Resolve(ctx); } + + cls = static_cast(ptype); } else @@ -8750,6 +8773,52 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build) // //========================================================================== +FxGetParentClass::FxGetParentClass(FxExpression *self) + :FxExpression(EFX_GetClass, self->ScriptPosition) +{ + Self = self; +} + +FxGetParentClass::~FxGetParentClass() +{ + SAFE_DELETE(Self); +} + +FxExpression *FxGetParentClass::Resolve(FCompileContext &ctx) +{ + SAFE_RESOLVE(Self, ctx); + + if (!Self->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && !Self->IsObject()) + { + ScriptPosition.Message(MSG_ERROR, "GetClass() requires an object"); + delete this; + return nullptr; + } + ValueType = NewClassPointer(RUNTIME_CLASS(DObject)); // + return this; +} + +ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build) +{ + ExpEmit op = Self->Emit(build); + op.Free(build); + if (Self->IsObject()) + { + ExpEmit to(build, REGT_POINTER); + build->Emit(OP_META, to.RegNum, op.RegNum); + op = to; + op.Free(build); + } + ExpEmit to(build, REGT_POINTER); + build->Emit(OP_LO, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, ParentClass))); + return to; +} + +//========================================================================== +// +// +//========================================================================== + FxGetDefaultByType::FxGetDefaultByType(FxExpression *self) :FxExpression(EFX_GetDefaultByType, self->ScriptPosition) { diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 0a1039926..f5fafb9e7 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -1589,6 +1589,24 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxGetClass +// +//========================================================================== + +class FxGetParentClass : public FxExpression +{ + FxExpression *Self; + +public: + + FxGetParentClass(FxExpression *self); + ~FxGetParentClass(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxGetDefaultByType diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index fde56fb40..7cbaec634 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -772,6 +772,11 @@ void InitThingdef() lstruct->AddNativeField("vertexes", NewPointer(NewResizableArray(vertstruct), false), myoffsetof(FLevelLocals, vertexes), VARF_Native|VARF_ReadOnly); lstruct->AddNativeField("sectorportals", NewPointer(NewResizableArray(sectorportalstruct), false), myoffsetof(FLevelLocals, sectorPortals), VARF_Native); + + auto aact = NewPointer(NewResizableArray(NewClassPointer(RUNTIME_CLASS(AActor))), true); + PField *aacf = new PField("AllActorClasses", aact, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PClassActor::AllActorClasses); + GlobalSymbols.AddSymbol(aacf); + // set up a variable for the DEH data PStruct *dstruct = NewNativeStruct("DehInfo", nullptr); PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh); diff --git a/wadsrc/static/zscript/inventory/ammo.txt b/wadsrc/static/zscript/inventory/ammo.txt index 0a36e0b35..2b36c287c 100644 --- a/wadsrc/static/zscript/inventory/ammo.txt +++ b/wadsrc/static/zscript/inventory/ammo.txt @@ -122,8 +122,156 @@ class Ammo : Inventory native } -class BackpackItem : Inventory native +class BackpackItem : Inventory { - native bool bDepleted; + bool bDepleted; + + //=========================================================================== + // + // ABackpackItem :: CreateCopy + // + // A backpack is being added to a player who doesn't yet have one. Give them + // every kind of ammo, and increase their max amounts. + // + //=========================================================================== + + override Inventory CreateCopy (Actor other) + { + // Find every unique type of ammoitem. Give it to the player if + // he doesn't have it already, and double its maximum capacity. + uint end = AllActorClasses.Size(); + for (uint i = 0; i < end; ++i) + { + let type = AllActorClasses[i]; + + if (type.GetParentClass() == 'Ammo') + { + let ammotype = (class)(type); + let ammoitem = Ammo(other.FindInventory(ammotype)); + int amount = GetDefaultByType(ammotype).BackpackAmount; + // extra ammo in baby mode and nightmare mode + if (!bIgnoreSkill) + { + amount = int(amount * G_SkillPropertyFloat(SKILLP_AmmoFactor)); + } + if (amount < 0) amount = 0; + if (ammoitem == NULL) + { // The player did not have the ammoitem. Add it. + ammoitem = Ammo(Spawn(ammotype)); + ammoitem.Amount = bDepleted ? 0 : amount; + if (ammoitem.BackpackMaxAmount > ammoitem.MaxAmount) + { + ammoitem.MaxAmount = ammoitem.BackpackMaxAmount; + } + if (ammoitem.Amount > ammoitem.MaxAmount) + { + ammoitem.Amount = ammoitem.MaxAmount; + } + ammoitem.AttachToOwner (other); + } + else + { // The player had the ammoitem. Give some more. + if (ammoitem.MaxAmount < ammoitem.BackpackMaxAmount) + { + ammoitem.MaxAmount = ammoitem.BackpackMaxAmount; + } + if (!bDepleted && ammoitem.Amount < ammoitem.MaxAmount) + { + ammoitem.Amount += amount; + if (ammoitem.Amount > ammoitem.MaxAmount) + { + ammoitem.Amount = ammoitem.MaxAmount; + } + } + } + } + } + return Super.CreateCopy (other); + } + + //=========================================================================== + // + // ABackpackItem :: HandlePickup + // + // When the player picks up another backpack, just give them more ammoitem. + // + //=========================================================================== + + override bool HandlePickup (Inventory item) + { + // Since you already have a backpack, that means you already have every + // kind of ammo in your inventory, so we don't need to look at the + // entire PClass list to discover what kinds of ammo exist, and we don't + // have to alter the MaxAmount either. + if (item is 'BackpackItem') + { + for (let probe = Owner.Inv; probe != NULL; probe = probe.Inv) + { + if (probe.GetParentClass() == 'Ammo') + { + if (probe.Amount < probe.MaxAmount || sv_unlimited_pickup) + { + int amount = Ammo(probe).Default.BackpackAmount; + // extra ammo in baby mode and nightmare mode + if (!bIgnoreSkill) + { + amount = int(amount * G_SkillPropertyFloat(SKILLP_AmmoFactor)); + } + probe.Amount += amount; + if (probe.Amount > probe.MaxAmount && !sv_unlimited_pickup) + { + probe.Amount = probe.MaxAmount; + } + } + } + } + // The pickup always succeeds, even if you didn't get anything + item.bPickupGood = true; + return true; + } + return false; + } + + //=========================================================================== + // + // ABackpackItem :: CreateTossable + // + // The tossed backpack must not give out any more ammo, otherwise a player + // could cheat by dropping their backpack and picking it up for more ammoitem. + // + //=========================================================================== + + override Inventory CreateTossable () + { + let pack = BackpackItem(Super.CreateTossable()); + if (pack != NULL) + { + pack.bDepleted = true; + } + return pack; + } + + //=========================================================================== + // + // ABackpackItem :: DetachFromOwner + // + //=========================================================================== + + override void DetachFromOwner () + { + // When removing a backpack, drop the player's ammo maximums to normal + + for (let item = Owner.Inv; item != NULL; item = item.Inv) + { + if (item is 'Ammo' && item.MaxAmount == Ammo(item).BackpackMaxAmount) + { + item.MaxAmount = item.Default.MaxAmount; + if (item.Amount > item.MaxAmount) + { + item.Amount = item.MaxAmount; + } + } + } + } }