diff --git a/src/c_console.cpp b/src/c_console.cpp index a3f84723b..b27674044 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1326,6 +1326,12 @@ void C_HideConsole () } } +DEFINE_ACTION_FUNCTION(_Console, HideConsole) +{ + C_HideConsole(); + return 0; +} + static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer) { int data1 = ev->data1; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 7b43de020..633eb466b 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -219,6 +219,7 @@ DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionStyle) DEFINE_FIELD_X(DehInfo, DehInfo, ExplosionAlpha) DEFINE_FIELD_X(DehInfo, DehInfo, NoAutofreeze) DEFINE_FIELD_X(DehInfo, DehInfo, BFGCells) +DEFINE_FIELD_X(DehInfo, DehInfo, BlueAC) // Doom identified pickup items by their sprites. ZDoom prefers to use their // class type to identify them instead. To support the traditional Doom diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 02aa8480e..242181ced 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -1288,6 +1288,19 @@ bool FWeaponSlots::LocateWeapon (PClassWeapon *type, int *const slot, int *const return false; } + +DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon) +{ + PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots); + PARAM_CLASS(weap, AWeapon); + int slot = 0, index = 0; + bool retv = self->LocateWeapon(weap, &slot, &index); + if (numret >= 1) ret[0].SetInt(retv); + if (numret >= 2) ret[1].SetInt(slot); + if (numret >= 3) ret[2].SetInt(index); + return MIN(numret, 3); +} + //=========================================================================== // // FindMostRecentWeapon diff --git a/src/gi.cpp b/src/gi.cpp index c82128889..a13804aad 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -45,6 +45,10 @@ gameinfo_t gameinfo; +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, backpacktype) +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, Armor2Percent) + + const char *GameNames[17] = { NULL, "Doom", "Heretic", NULL, "Hexen", NULL, NULL, NULL, "Strife", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Chex" diff --git a/src/gi.h b/src/gi.h index d9e81bcf1..bd5e84363 100644 --- a/src/gi.h +++ b/src/gi.h @@ -148,7 +148,7 @@ struct gameinfo_t FString translator; DWORD defaultbloodcolor; DWORD defaultbloodparticlecolor; - FString backpacktype; + FName backpacktype; FString statusbar; FString intermissionMusic; int intermissionOrder; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 46f18d96f..d68839359 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -50,6 +50,7 @@ #include "a_armor.h" #include "a_ammo.h" #include "g_levellocals.h" +#include "virtual.h" // [RH] Actually handle the cheat. The cheat code in st_stuff.c now just // writes some bytes to the network data stream, and the network code @@ -324,16 +325,10 @@ void cht_DoCheat (player_t *player, int cheat) } else { + player->mo->Revive(); player->playerstate = PST_LIVE; player->health = player->mo->health = player->mo->GetDefault()->health; player->viewheight = ((APlayerPawn *)player->mo->GetDefault())->ViewHeight; - player->mo->flags = player->mo->GetDefault()->flags; - player->mo->flags2 = player->mo->GetDefault()->flags2; - player->mo->flags3 = player->mo->GetDefault()->flags3; - player->mo->flags4 = player->mo->GetDefault()->flags4; - player->mo->flags5 = player->mo->GetDefault()->flags5; - player->mo->flags6 = player->mo->GetDefault()->flags6; - player->mo->flags7 = player->mo->GetDefault()->flags7; player->mo->renderflags &= ~RF_INVISIBLE; player->mo->Height = player->mo->GetDefault()->Height; player->mo->radius = player->mo->GetDefault()->radius; @@ -344,7 +339,6 @@ void cht_DoCheat (player_t *player, int cheat) { player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players)); } - player->mo->DamageType = NAME_None; if (player->ReadyWeapon != nullptr) { P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetUpState()); @@ -588,434 +582,24 @@ const char *cht_Morph (player_t *player, PClassPlayerPawn *morphclass, bool quic void cht_Give (player_t *player, const char *name, int amount) { - enum { ALL_NO, ALL_YES, ALL_YESYES } giveall; - int i; - PClassActor *type; + if (player->mo == nullptr) return; - if (player != &players[consoleplayer]) - Printf ("%s is a cheater: give %s\n", player->userinfo.GetName(), name); - - if (player->mo == NULL || player->health <= 0) + IFVIRTUALPTR(player->mo, APlayerPawn, CheatGive) { - return; + VMValue params[3] = { player->mo, FString(name), amount }; + GlobalVMStack.Call(func, params, 3, nullptr, 0); } - - giveall = ALL_NO; - if (stricmp (name, "all") == 0) - { - giveall = ALL_YES; - } - else if (stricmp (name, "everything") == 0) - { - giveall = ALL_YESYES; - } - - if (stricmp (name, "health") == 0) - { - if (amount > 0) - { - player->mo->health += amount; - player->health = player->mo->health; - } - else - { - player->health = player->mo->health = player->mo->GetMaxHealth(); - } - } - - if (giveall || stricmp (name, "backpack") == 0) - { - // Select the correct type of backpack based on the game - type = PClass::FindActor(gameinfo.backpacktype); - if (type != NULL) - { - player->mo->GiveInventory(static_cast(type), 1, true); - } - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "ammo") == 0) - { - // Find every unique type of ammo. Give it to the player if - // he doesn't have it already, and set each to its maximum. - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - PClassActor *type = PClassActor::AllActorClasses[i]; - - if (type->ParentClass == RUNTIME_CLASS(AAmmo)) - { - PClassInventory *atype = static_cast(type); - AInventory *ammo = player->mo->FindInventory(atype); - if (ammo == NULL) - { - ammo = static_cast(Spawn (atype)); - ammo->AttachToOwner (player->mo); - ammo->Amount = ammo->MaxAmount; - } - else if (ammo->Amount < ammo->MaxAmount) - { - ammo->Amount = ammo->MaxAmount; - } - } - } - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "armor") == 0) - { - if (gameinfo.gametype != GAME_Hexen) - { - ABasicArmorPickup *armor = Spawn (); - armor->SaveAmount = 100*deh.BlueAC; - armor->SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5; - if (!armor->CallTryPickup (player->mo)) - { - armor->Destroy (); - } - } - else - { - for (i = 0; i < 4; ++i) - { - AHexenArmor *armor = Spawn (); - armor->health = i; - armor->Amount = 0; - if (!armor->CallTryPickup (player->mo)) - { - armor->Destroy (); - } - } - } - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "keys") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - if (PClassActor::AllActorClasses[i]->IsDescendantOf (RUNTIME_CLASS(AKey))) - { - AKey *key = (AKey *)GetDefaultByType (PClassActor::AllActorClasses[i]); - if (key->KeyNumber != 0) - { - key = static_cast(Spawn(static_cast(PClassActor::AllActorClasses[i]))); - if (!key->CallTryPickup (player->mo)) - { - key->Destroy (); - } - } - } - } - if (!giveall) - return; - } - - if (giveall || stricmp (name, "weapons") == 0) - { - AWeapon *savedpending = player->PendingWeapon; - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - // Don't give replaced weapons unless the replacement was done by Dehacked. - if (type != RUNTIME_CLASS(AWeapon) && - type->IsDescendantOf (RUNTIME_CLASS(AWeapon)) && - (static_cast(type)->GetReplacement() == type || - static_cast(type)->GetReplacement()->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup)))) - { - // Give the weapon only if it belongs to the current game or - if (player->weapons.LocateWeapon(static_cast(type), NULL, NULL)) - { - AWeapon *def = (AWeapon*)GetDefaultByType (type); - if (giveall == ALL_YESYES || !(def->WeaponFlags & WIF_CHEATNOTWEAPON)) - { - player->mo->GiveInventory(static_cast(type), 1, true); - } - } - } - } - player->PendingWeapon = savedpending; - - if (!giveall) - return; - } - - if (giveall || stricmp (name, "artifacts") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - if (type->IsDescendantOf (RUNTIME_CLASS(AInventory))) - { - AInventory *def = (AInventory*)GetDefaultByType (type); - if (def->Icon.isValid() && def->MaxAmount > 1 && - !type->IsDescendantOf (pitype) && - !type->IsDescendantOf (RUNTIME_CLASS(APowerup)) && - !type->IsDescendantOf (RUNTIME_CLASS(AArmor))) - { - // Do not give replaced items unless using "give everything" - if (giveall == ALL_YESYES || type->GetReplacement() == type) - { - player->mo->GiveInventory(static_cast(type), amount <= 0 ? def->MaxAmount : amount, true); - } - } - } - } - if (!giveall) - return; - } - - if (giveall || stricmp (name, "puzzlepieces") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - if (type->IsDescendantOf (pitype)) - { - AInventory *def = (AInventory*)GetDefaultByType (type); - if (def->Icon.isValid()) - { - // Do not give replaced items unless using "give everything" - if (giveall == ALL_YESYES || type->GetReplacement() == type) - { - player->mo->GiveInventory(static_cast(type), amount <= 0 ? def->MaxAmount : amount, true); - } - } - } - } - if (!giveall) - return; - } - - if (giveall) - return; - - type = PClass::FindActor(name); - if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AInventory))) - { - if (player == &players[consoleplayer]) - Printf ("Unknown item \"%s\"\n", name); - } - else - { - player->mo->GiveInventory(static_cast(type), amount, true); - } - return; } void cht_Take (player_t *player, const char *name, int amount) { - bool takeall; - PClassActor *type; + if (player->mo == nullptr) return; - if (player->mo == NULL || player->health <= 0) + IFVIRTUALPTR(player->mo, APlayerPawn, CheatTake) { - return; + VMValue params[3] = { player->mo, FString(name), amount }; + GlobalVMStack.Call(func, params, 3, nullptr, 0); } - - takeall = (stricmp (name, "all") == 0); - - if (!takeall && stricmp (name, "health") == 0) - { - if (player->mo->health - amount <= 0 - || player->health - amount <= 0 - || amount == 0) - { - - cht_Suicide (player); - - if (player == &players[consoleplayer]) - C_HideConsole (); - - return; - } - - if (amount > 0) - { - if (player->mo) - { - player->mo->health -= amount; - player->health = player->mo->health; - } - else - { - player->health -= amount; - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "backpack") == 0) - { - // Take away all types of backpacks the player might own. - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - PClass *type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf(PClass::FindClass(NAME_BackpackItem))) - { - AInventory *pack = player->mo->FindInventory(static_cast(type)); - - if (pack) - pack->Destroy(); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "ammo") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - PClass *type = PClassActor::AllActorClasses[i]; - - if (type->ParentClass == RUNTIME_CLASS (AAmmo)) - { - AInventory *ammo = player->mo->FindInventory(static_cast(type)); - - if (ammo) - ammo->DepleteOrDestroy(); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "armor") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (RUNTIME_CLASS (AArmor))) - { - AInventory *armor = player->mo->FindInventory(static_cast(type)); - - if (armor) - armor->DepleteOrDestroy(); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "keys") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (RUNTIME_CLASS (AKey))) - { - AActor *key = player->mo->FindInventory(static_cast(type)); - - if (key) - key->Destroy (); - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "weapons") == 0) - { - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type != RUNTIME_CLASS(AWeapon) && - type->IsDescendantOf (RUNTIME_CLASS (AWeapon))) - { - AActor *weapon = player->mo->FindInventory(static_cast(type)); - - if (weapon) - weapon->Destroy (); - - player->ReadyWeapon = nullptr; - player->PendingWeapon = WP_NOCHANGE; - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "artifacts") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (RUNTIME_CLASS (AInventory))) - { - if (!type->IsDescendantOf (pitype) && - !type->IsDescendantOf (RUNTIME_CLASS (APowerup)) && - !type->IsDescendantOf (RUNTIME_CLASS (AArmor)) && - !type->IsDescendantOf (RUNTIME_CLASS (AWeapon)) && - !type->IsDescendantOf (RUNTIME_CLASS (AKey))) - { - AActor *artifact = player->mo->FindInventory(static_cast(type)); - - if (artifact) - artifact->Destroy (); - } - } - } - - if (!takeall) - return; - } - - if (takeall || stricmp (name, "puzzlepieces") == 0) - { - auto pitype = PClass::FindActor(NAME_PuzzleItem); - for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) - { - type = PClassActor::AllActorClasses[i]; - - if (type->IsDescendantOf (pitype)) - { - AActor *puzzlepiece = player->mo->FindInventory(static_cast(type)); - - if (puzzlepiece) - puzzlepiece->Destroy (); - } - } - - if (!takeall) - return; - } - - if (takeall) - return; - - type = PClass::FindActor (name); - if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS (AInventory))) - { - if (player == &players[consoleplayer]) - Printf ("Unknown item \"%s\"\n", name); - } - else - { - player->mo->TakeInventory(type, amount ? amount : 1); - } - return; } class DSuicider : public DThinker @@ -1070,6 +654,12 @@ void cht_Suicide (player_t *plyr) } } +DEFINE_ACTION_FUNCTION(APlayerPawn, CheatSuicide) +{ + PARAM_SELF_PROLOGUE(APlayerPawn); + cht_Suicide(self->player); + return 0; +} CCMD (mdk) { diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 3c7eeca07..172e17198 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1224,22 +1224,6 @@ DEFINE_ACTION_FUNCTION(AActor, CheckInventory) } -//========================================================================== -// -// State jump function -// -//========================================================================== -DEFINE_ACTION_FUNCTION(AActor, CheckArmorType) -{ - PARAM_SELF_PROLOGUE(AActor); - PARAM_NAME (type); - PARAM_INT_DEF(amount); - - ABasicArmor *armor = (ABasicArmor *)self->FindInventory(NAME_BasicArmor); - - ACTION_RETURN_BOOL(armor && armor->ArmorType == type && armor->Amount >= amount); -} - //========================================================================== // // Parameterized version of A_Explode diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 48393caaa..c2a72e375 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -822,11 +822,13 @@ bool AActor::GiveInventory(PClassInventory *type, int amount, bool givecheat) return result; } -DEFINE_ACTION_FUNCTION(AActor, Inventory) +DEFINE_ACTION_FUNCTION(AActor, GiveInventory) { PARAM_SELF_PROLOGUE(AActor); - PARAM_OBJECT_NOT_NULL(item, AInventory); - ACTION_RETURN_BOOL(self->UseInventory(item)); + PARAM_CLASS(type, AInventory); + PARAM_INT(amount); + PARAM_BOOL_DEF(givecheat); + ACTION_RETURN_BOOL(self->GiveInventory(type, amount, givecheat)); } @@ -918,6 +920,16 @@ bool AActor::TakeInventory(PClassActor *itemclass, int amount, bool fromdecorate return result; } +DEFINE_ACTION_FUNCTION(AActor, TakeInventory) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT_NOT_NULL(item, AInventory); + PARAM_INT(amount); + PARAM_BOOL_DEF(fromdecorate); + PARAM_BOOL_DEF(notakeinfinite); + self->RemoveInventory(item); + return 0; +} //============================================================================ // diff --git a/src/p_user.cpp b/src/p_user.cpp index d3c44d46f..a9dcc7cfa 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -638,6 +638,14 @@ void player_t::SendPitchLimits() const } } + +DEFINE_ACTION_FUNCTION(_PlayerInfo, GetUserName) +{ + PARAM_SELF_STRUCT_PROLOGUE(player_t); + ACTION_RETURN_STRING(self->userinfo.GetName()); +} + + //=========================================================================== // // APlayerPawn diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 47a10f95d..9fbfe3ef8 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -4362,9 +4362,8 @@ ExpEmit FxDotCross::Emit(VMFunctionBuilder *build) FxTypeCheck::FxTypeCheck(FxExpression *l, FxExpression *r) : FxExpression(EFX_TypeCheck, l->ScriptPosition) { - left = new FxTypeCast(l, NewPointer(RUNTIME_CLASS(DObject)), false); - right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), r); - EmitTail = false; + left = l; + right = r; ValueType = TypeBool; } @@ -4389,9 +4388,27 @@ FxTypeCheck::~FxTypeCheck() FxExpression *FxTypeCheck::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); + // This must resolve the cast separately so that it can set the proper type for class descriptors. RESOLVE(left, ctx); RESOLVE(right, ctx); ABORT(right && left); + + if (left->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) + { + left = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), left); + ClassCheck = true; + } + else + { + left = new FxTypeCast(left, NewPointer(RUNTIME_CLASS(DObject)), false); + ClassCheck = false; + } + right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), right); + + RESOLVE(left, ctx); + RESOLVE(right, ctx); + ABORT(right && left); + return this; } @@ -4408,7 +4425,8 @@ ExpEmit FxTypeCheck::EmitCommon(VMFunctionBuilder *build) castee.Free(build); casttype.Free(build); ExpEmit ares(build, REGT_POINTER); - build->Emit(casttype.Konst ? OP_DYNCAST_K : OP_DYNCAST_R, ares.RegNum, castee.RegNum, casttype.RegNum); + if (!ClassCheck) build->Emit(casttype.Konst ? OP_DYNCAST_K : OP_DYNCAST_R, ares.RegNum, castee.RegNum, casttype.RegNum); + else build->Emit(casttype.Konst ? OP_DYNCASTC_K : OP_DYNCASTC_R, ares.RegNum, castee.RegNum, casttype.RegNum); return ares; } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index e7cfab662..c31c42b07 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -1086,7 +1086,7 @@ class FxTypeCheck : public FxExpression public: FxExpression *left; FxExpression *right; - bool EmitTail; + bool ClassCheck; FxTypeCheck(FxExpression*, FxExpression*); ~FxTypeCheck(); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 20e210f80..56209e114 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -788,6 +788,11 @@ void InitThingdef() PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh); GlobalSymbols.AddSymbol(dehf); + // set up a variable for the global gameinfo data + PStruct *gistruct = NewNativeStruct("GameInfoStruct", nullptr); + PField *gi = new PField("gameinfo", gistruct, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gameinfo); + GlobalSymbols.AddSymbol(gi); + // set up a variable for the global players array. PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); pstruct->Size = sizeof(player_t); @@ -796,6 +801,9 @@ void InitThingdef() PField *playerf = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players); GlobalSymbols.AddSymbol(playerf); + pstruct->AddNativeField("weapons", NewNativeStruct("WeaponSlots", nullptr), myoffsetof(player_t, weapons), VARF_Native); + + parray = NewArray(TypeBool, MAXPLAYERS); playerf = new PField("playeringame", parray, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&playeringame); GlobalSymbols.AddSymbol(playerf); diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 51c224649..d916658d7 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -401,7 +401,7 @@ begin: OP(MOVEA): { ASSERTA(a); ASSERTA(B); - int b = B; + b = B; reg.a[a] = reg.a[b]; reg.atag[a] = reg.atag[b]; NEXTOP; @@ -409,7 +409,7 @@ begin: OP(MOVEV2): { ASSERTF(a); ASSERTF(B); - int b = B; + b = B; reg.f[a] = reg.f[b]; reg.f[a + 1] = reg.f[b + 1]; NEXTOP; @@ -417,7 +417,7 @@ begin: OP(MOVEV3): { ASSERTF(a); ASSERTF(B); - int b = B; + b = B; reg.f[a] = reg.f[b]; reg.f[a + 1] = reg.f[b + 1]; reg.f[a + 2] = reg.f[b + 2]; @@ -435,6 +435,18 @@ begin: reg.a[a] = (reg.a[b] && ((DObject*)(reg.a[b]))->IsKindOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr; reg.atag[a] = ATAG_OBJECT; NEXTOP; + OP(DYNCASTC_R) : + ASSERTA(a); ASSERTA(B); ASSERTA(C); + b = B; + reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(reg.a[C]))) ? reg.a[b] : nullptr; + reg.atag[a] = ATAG_OBJECT; + NEXTOP; + OP(DYNCASTC_K) : + ASSERTA(a); ASSERTA(B); ASSERTKA(C); + b = B; + reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr; + reg.atag[a] = ATAG_OBJECT; + NEXTOP; OP(CAST): if (C == CAST_I2F) { @@ -1746,9 +1758,21 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c break; case CAST_P2S: + { ASSERTS(a); ASSERTA(b); - reg.s[a].Format("%s<%p>", reg.atag[b] == ATAG_OBJECT ? (reg.a[b] == nullptr? "Object" : ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars() ) : "Pointer", reg.a[b]); - break; + if (reg.a[b] == nullptr) reg.s[a] = "null"; + else if (reg.atag[b] == ATAG_OBJECT) + { + auto op = static_cast(reg.a[b]); + if (op->IsKindOf(RUNTIME_CLASS(PClass))) reg.s[a].Format("Class<%s>", static_cast(op)->TypeName.GetChars()); + else reg.s[a].Format("Object<%p>", ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars()); + } + else + { + reg.s[a].Format("%s<%p>", "Pointer", reg.a[b]); + } + break; + } case CAST_S2I: ASSERTD(a); ASSERTS(b); diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 889706726..0829ab2c6 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -86,6 +86,8 @@ xx(CAST, cast, CAST, NOP, 0, 0), // xA = xB, conversion specified by C xx(CASTB, castb, CAST, NOP, 0, 0), // xA = !!xB, type specified by C xx(DYNCAST_R, dyncast, RPRPRP, NOP, 0, 0), // aA = dyn_cast(aB); xx(DYNCAST_K, dyncast, RPRPKP, NOP, 0, 0), // aA = dyn_cast(aB); +xx(DYNCASTC_R, dyncastc, RPRPRP, NOP, 0, 0), // aA = dyn_cast(aB); for class types +xx(DYNCASTC_K, dyncastc, RPRPKP, NOP, 0, 0), // aA = dyn_cast(aB); // Control flow. xx(TEST, test, RII16, NOP, 0, 0), // if (dA != BC) then pc++ diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 16f3b6f6c..fd8bce922 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -15,6 +15,7 @@ #include "zscript/inventory/powerups.txt" #include "zscript/shared/player.txt" +#include "zscript/shared/player_cheat.txt" #include "zscript/shared/morph.txt" #include "zscript/shared/botstuff.txt" #include "zscript/shared/sharedmisc.txt" diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index ef47f62d7..a5087345b 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -487,6 +487,8 @@ class Actor : Thinker native native void AddInventory(Inventory inv); native void RemoveInventory(Inventory inv); native void ClearInventory(); + native bool GiveInventory(class type, int amount, bool givecheat = false); + native bool TakeInventory(class itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); native Inventory FindInventory(class itemtype, bool subclass = false); native Inventory GiveInventoryType(class itemtype); native Inventory DropInventory (Inventory item); diff --git a/wadsrc/static/zscript/actor_checks.txt b/wadsrc/static/zscript/actor_checks.txt index a4f7da1d8..eea10b5ec 100644 --- a/wadsrc/static/zscript/actor_checks.txt +++ b/wadsrc/static/zscript/actor_checks.txt @@ -106,11 +106,15 @@ extend class Actor //========================================================================== // - // + // rather pointless these days to do it this way. // //========================================================================== - native bool CheckArmorType(name Type, int amount = 1); + bool CheckArmorType(name Type, int amount = 1) + { + let myarmor = BasicArmor(FindInventory("BasicArmor")); + return myarmor != null && myarmor.ArmorType == type && myarmor.Amount >= amount; + } action state A_JumpIfArmorType(name Type, statelabel label, int amount = 1) { diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index e9e0a0673..9cad3224e 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -32,11 +32,23 @@ struct TexMan native static TextureID CheckForTexture(String name, int usetype, int flags = TryAny); } -struct Screen +struct Screen native { native static void DrawHUDTexture(TextureID tex, double x, double y); } +struct Console native +{ + native static void HideConsole(); +} + +struct GameInfoStruct native +{ + // will be extended as needed. + native Name backpacktype; + native double Armor2Percent; +} + class Object native { native bool bDestroyed; @@ -200,6 +212,7 @@ struct DehInfo native native double ExplosionAlpha; native int NoAutofreeze; native int BFGCells; + native int BlueAC; } struct State native diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index f1d75d5dd..ba2bf2ef5 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -125,3 +125,7 @@ class WeaponPiece : Inventory native } } +struct WeaponSlots native +{ + native bool, int, int LocateWeapon(class weap); +} \ No newline at end of file diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 56a6dc427..99f3d9c1f 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -258,11 +258,12 @@ struct PlayerInfo native // this is what internally is known as player_t native Actor ConversationPC; native double ConversationNPCAngle; native bool ConversationFaceTalker; + //native WeaponSlots weapons; <- defined internally + /* these are not doable yet ticcmd_t cmd; usercmd_t original_cmd; -userinfo_t userinfo; // [RH] who is this? -FWeaponSlots weapons; +userinfo_t userinfo; */ @@ -276,5 +277,6 @@ FWeaponSlots weapons; native PSprite FindPSprite(int id); native void SetLogNumber (int text); native void SetLogText (String text); + native String GetUserName(); } diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt new file mode 100644 index 000000000..30760fb5d --- /dev/null +++ b/wadsrc/static/zscript/shared/player_cheat.txt @@ -0,0 +1,402 @@ +/* +** player_cheat.txt +** +**--------------------------------------------------------------------------- +** Copyright 1999-2016 Randy Heit +** Copyright 2006-2017 Christoph 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. +**--------------------------------------------------------------------------- +** +*/ + +extend class PlayerPawn +{ + enum EAll + { + ALL_NO, + ALL_YES, + ALL_YESYES + } + + native void CheatSuicide(); + + virtual void CheatGive (String name, int amount) + { + int i; + Class type; + let player = self.player; + + if (PlayerNumber() != consoleplayer) + A_Log(format ("%s is a cheater: give %s\n", player.GetUserName(), name)); + + if (player.mo == NULL || player.health <= 0) + { + return; + } + + int giveall = ALL_NO; + if (name ~== "all") + { + giveall = ALL_YES; + } + else if (name ~== "everything") + { + giveall = ALL_YESYES; + } + + if (name ~== "health") + { + if (amount > 0) + { + health += amount; + player.health = health; + } + else + { + player.health = health = GetMaxHealth(); + } + } + + if (giveall || name ~== "backpack") + { + // Select the correct type of backpack based on the game + type = (class)(gameinfo.backpacktype); + if (type != NULL) + { + GiveInventory(type, 1, true); + } + + if (!giveall) + return; + } + + if (giveall || name ~== "ammo") + { + // Find every unique type of ammo. Give it to the player if + // he doesn't have it already, and set each to its maximum. + for (i = 0; i < AllActorClasses.Size(); ++i) + { + type = (class)(AllActorClasses[i]); + + if (type != null && type.GetParentClass() == "Ammo") + { + let ammoitem = FindInventory(type); + if (ammoitem == NULL) + { + ammoitem = Inventory(Spawn (type)); + ammoitem.AttachToOwner (self); + ammoitem.Amount = ammoitem.MaxAmount; + } + else if (ammoitem.Amount < ammoitem.MaxAmount) + { + ammoitem.Amount = ammoitem.MaxAmount; + } + } + } + + if (!giveall) + return; + } + + if (giveall || name ~== "armor") + { + if (GameType() != GAME_Hexen) + { + let armoritem = BasicArmorPickup(Spawn("BasicArmorPickup")); + armoritem.SaveAmount = 100*deh.BlueAC; + armoritem.SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : 0.5; + if (!armoritem.CallTryPickup (self)) + { + armoritem.Destroy (); + } + } + else + { + for (i = 0; i < 4; ++i) + { + let armoritem = Inventory(Spawn("HexenArmor")); + armoritem.health = i; + armoritem.Amount = 0; + if (!armoritem.CallTryPickup (self)) + { + armoritem.Destroy (); + } + } + } + + if (!giveall) + return; + } + + if (giveall || name ~== "keys") + { + for (int i = 0; i < AllActorClasses.Size(); ++i) + { + if (AllActorClasses[i] is "Key") + { + readonly keyitem = GetDefaultByType ((class)(AllActorClasses[i])); + if (keyitem.KeyNumber != 0) + { + let item = Inventory(Spawn(AllActorClasses[i])); + if (!item.CallTryPickup (self)) + { + item.Destroy (); + } + } + } + } + if (!giveall) + return; + } + + if (giveall || name ~== "weapons") + { + let savedpending = player.PendingWeapon; + for (i = 0; i < AllActorClasses.Size(); ++i) + { + let type = (class)(AllActorClasses[i]); + if (type != null && type != "Weapon") + { + // Don't give replaced weapons unless the replacement was done by Dehacked. + let rep = GetReplacement(type); + if (rep == type || rep is "DehackedPickup") + { + // Give the weapon only if it is set in a weapon slot. + if (player.weapons.LocateWeapon(type)) + { + readonly def = GetDefaultByType (type); + if (giveall == ALL_YESYES || !def.bCheatNotWeapon) + { + GiveInventory(type, 1, true); + } + } + } + } + } + player.PendingWeapon = savedpending; + + if (!giveall) + return; + } + + if (giveall || name ~== "artifacts") + { + for (i = 0; i < AllActorClasses.Size(); ++i) + { + type = (class)(AllActorClasses[i]); + if (type!= null) + { + let def = GetDefaultByType (type); + if (def.Icon.isValid() && def.MaxAmount > 1 && + !(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor")) + { + // Do not give replaced items unless using "give everything" + if (giveall == ALL_YESYES || GetReplacement(type) == type) + { + GiveInventory(type, amount <= 0 ? def.MaxAmount : amount, true); + } + } + } + } + if (!giveall) + return; + } + + if (giveall || name ~== "puzzlepieces") + { + for (i = 0; i < AllActorClasses.Size(); ++i) + { + let type = (class)(AllActorClasses[i]); + if (type != null) + { + let def = GetDefaultByType (type); + if (def.Icon.isValid()) + { + // Do not give replaced items unless using "give everything" + if (giveall == ALL_YESYES || GetReplacement(type) == type) + { + GiveInventory(type, amount <= 0 ? def.MaxAmount : amount, true); + } + } + } + } + if (!giveall) + return; + } + + if (giveall) + return; + + type = name; + if (type == NULL) + { + if (PlayerNumber() == consoleplayer) + A_Log(format("Unknown item \"%s\"\n", name)); + } + else + { + GiveInventory(type, amount, true); + } + return; + } + + void CheatTakeType(class deletetype) + { + for (int i = 0; i < AllActorClasses.Size(); ++i) + { + let type = (class)(AllActorClasses[i]); + + if (type != null && type is deletetype) + { + let pack = FindInventory(type); + if (pack) pack.Destroy(); + } + } + } + + virtual void CheatTake (String name, int amount) + { + bool takeall; + Class type; + let player = self.player; + + + if (player.mo == NULL || player.health <= 0) + { + return; + } + + takeall = name ~== "all"; + + if (!takeall && name ~== "health") + { + if (player.mo.health - amount <= 0 + || player.health - amount <= 0 + || amount == 0) + { + + CheatSuicide (); + + if (PlayerNumber() == consoleplayer) + Console.HideConsole (); + + return; + } + + if (amount > 0) + { + if (player.mo) + { + player.mo.health -= amount; + player.health = player.mo.health; + } + else + { + player.health -= amount; + } + } + + if (!takeall) + return; + } + + if (takeall || name ~== "backpack") + { + CheatTakeType("BackpackItem"); + if (!takeall) + return; + } + + if (takeall || name ~== "ammo") + { + CheatTakeType("Ammo"); + if (!takeall) + return; + } + + if (takeall || name ~== "armor") + { + CheatTakeType("Armor"); + if (!takeall) + return; + } + + if (takeall || name ~== "keys") + { + CheatTakeType("Key"); + if (!takeall) + return; + } + + if (takeall || name ~== "weapons") + { + CheatTakeType("Weapon"); + CheatTakeType("WeaponHolder"); + player.ReadyWeapon = null; + player.PendingWeapon = WP_NOCHANGE; + + if (!takeall) + return; + } + + if (takeall || name ~== "artifacts") + { + for (int i = 0; i < AllActorClasses.Size(); ++i) + { + type = (class)(AllActorClasses[i]); + if (type!= null && !(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor")) + { + let pack = FindInventory(type); + if (pack) pack.Destroy(); + } + } + if (!takeall) + return; + } + + if (takeall || name ~== "puzzlepieces") + { + CheatTakeType("PuzzleItem"); + if (!takeall) + return; + } + + if (takeall) + return; + + type = name; + if (type == NULL) + { + if (PlayerNumber() == consoleplayer) + A_Log(format("Unknown item \"%s\"\n", name)); + } + else + { + TakeInventory(type, max(amount, 1)); + } + return; + } +} \ No newline at end of file