Merge remote-tracking branch 'remotes/origin/weapon_scriptification' into asmjit

# Conflicts:
#	src/g_inventory/a_pickups.cpp
This commit is contained in:
Christoph Oelckers 2018-11-30 21:28:44 +01:00
commit a0c0e8bdfe
96 changed files with 4828 additions and 4759 deletions

View file

@ -998,6 +998,7 @@ set (PCH_SOURCES
s_sound.cpp
serializer.cpp
sc_man.cpp
scriptutil.cpp
st_stuff.cpp
statistics.cpp
stats.cpp

View file

@ -640,7 +640,6 @@ public:
AActor &operator= (const AActor &other);
~AActor ();
virtual void Finalize(FStateDefinitions &statedef);
virtual void OnDestroy() override;
virtual void Serialize(FSerializer &arc) override;
virtual void PostSerialize() override;
@ -685,8 +684,6 @@ public:
void LevelSpawned(); // Called after BeginPlay if this actor was spawned by the world
void HandleSpawnFlags(); // Translates SpawnFlags into in-game flags.
virtual void MarkPrecacheSounds() const; // Marks sounds used by this actor for precaching.
virtual void Activate (AActor *activator);
void CallActivate(AActor *activator);

View file

@ -47,6 +47,7 @@
#include "d_net.h"
#include "serializer.h"
#include "d_player.h"
#include "w_wad.h"
#include "vm.h"
IMPLEMENT_CLASS(DBot, false, true)
@ -246,69 +247,61 @@ CCMD (listbots)
// set the bot specific weapon information
// This is intentionally not in the weapon definition anymore.
BotInfoMap BotInfo;
void InitBotStuff()
{
static struct BotInit
int lump;
int lastlump = 0;
while (-1 != (lump = Wads.FindLump("BOTSUPP", &lastlump)))
{
const char *type;
int movecombatdist;
int weaponflags;
const char *projectile;
} botinits[] = {
{ "Pistol", 25000000, 0, NULL },
{ "Shotgun", 24000000, 0, NULL },
{ "SuperShotgun", 15000000, 0, NULL },
{ "Chaingun", 27000000, 0, NULL },
{ "RocketLauncher", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "Rocket" },
{ "PlasmaRifle", 27000000, 0, "PlasmaBall" },
{ "BFG9000", 10000000, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_BFG, "BFGBall" },
{ "GoldWand", 25000000, 0, NULL },
{ "GoldWandPowered", 25000000, 0, NULL },
{ "Crossbow", 24000000, 0, "CrossbowFX1" },
{ "CrossbowPowered", 24000000, 0, "CrossbowFX2" },
{ "Blaster", 27000000, 0, NULL },
{ "BlasterPowered", 27000000, 0, "BlasterFX1" },
{ "SkullRod", 27000000, 0, "HornRodFX1" },
{ "SkullRodPowered", 27000000, 0, "HornRodFX2" },
{ "PhoenixRod", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "PhoenixFX1" },
{ "Mace", 27000000, WIF_BOT_REACTION_SKILL_THING, "MaceFX2" },
{ "MacePowered", 27000000, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "MaceFX4" },
{ "FWeapHammer", 22000000, 0, "HammerMissile" },
{ "FWeapQuietus", 20000000, 0, "FSwordMissile" },
{ "CWeapStaff", 25000000, 0, "CStaffMissile" },
{ "CWeapFlane", 27000000, 0, "CFlameMissile" },
{ "MWeapWand", 25000000, 0, "MageWandMissile" },
{ "CWeapWraithverge", 22000000, 0, "HolyMissile" },
{ "MWeapFrost", 19000000, 0, "FrostMissile" },
{ "MWeapLightning", 23000000, 0, "LightningFloor" },
{ "MWeapBloodscourge", 20000000, 0, "MageStaffFX2" },
{ "StrifeCrossbow", 24000000, 0, "ElectricBolt" },
{ "StrifeCrossbow2", 24000000, 0, "PoisonBolt" },
{ "AssaultGun", 27000000, 0, NULL },
{ "MiniMissileLauncher", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "MiniMissile" },
{ "FlameThrower", 24000000, 0, "FlameMissile" },
{ "Mauler", 15000000, 0, NULL },
{ "Mauler2", 10000000, 0, "MaulerTorpedo" },
{ "StrifeGrenadeLauncher", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "HEGrenade" },
{ "StrifeGrenadeLauncher2", 18350080, WIF_BOT_REACTION_SKILL_THING|WIF_BOT_EXPLOSIVE, "PhosphorousGrenade" },
};
for(unsigned i=0;i<sizeof(botinits)/sizeof(botinits[0]);i++)
{
const PClass *cls = PClass::FindClass(botinits[i].type);
if (cls != NULL && cls->IsDescendantOf(NAME_Weapon))
FScanner sc(lump);
sc.SetCMode(true);
while (sc.GetString())
{
AWeapon *w = (AWeapon*)GetDefaultByType(cls);
if (w != NULL)
PClassActor *wcls = PClass::FindActor(sc.String);
if (wcls != NULL && wcls->IsDescendantOf(NAME_Weapon))
{
w->MoveCombatDist = botinits[i].movecombatdist/65536.;
w->WeaponFlags |= botinits[i].weaponflags;
w->ProjectileType = PClass::FindActor(botinits[i].projectile);
BotInfoData bi = {};
sc.MustGetStringName(",");
sc.MustGetNumber();
bi.MoveCombatDist = sc.Number;
while (sc.CheckString(","))
{
sc.MustGetString();
if (sc.Compare("BOT_REACTION_SKILL_THING"))
{
bi.flags |= BIF_BOT_REACTION_SKILL_THING;
}
else if (sc.Compare("BOT_EXPLOSIVE"))
{
bi.flags |= BIF_BOT_EXPLOSIVE;
}
else if (sc.Compare("BOT_BFG"))
{
bi.flags |= BIF_BOT_BFG;
}
else
{
PClassActor *cls = PClass::FindActor(sc.String);
bi.projectileType = cls;
if (cls == nullptr)
{
sc.ScriptError("Unknown token %s", sc.String);
}
}
}
BotInfo[wcls->TypeName] = bi;
}
else
{
sc.ScriptError("%s is not a weapon type", sc.String);
}
}
}
// Fixme: Export these, too.
static const char *warnbotmissiles[] = { "PlasmaBall", "Ripper", "HornRodFX1" };
for(unsigned i=0;i<countof(warnbotmissiles);i++)
{

View file

@ -13,6 +13,7 @@
#include "d_protocol.h"
#include "r_defs.h"
#include "a_pickups.h"
#include "a_weapons.h"
#include "stats.h"
#define FORWARDWALK 0x1900
@ -78,6 +79,34 @@ struct botinfo_t
int lastteam;
};
struct BotInfoData
{
int MoveCombatDist = 0;
int flags = 0;
PClassActor *projectileType = nullptr;
};
enum
{
BIF_BOT_REACTION_SKILL_THING = 1,
BIF_BOT_EXPLOSIVE = 2,
BIF_BOT_BFG = 4,
};
using BotInfoMap = TMap<FName, BotInfoData>;
extern BotInfoMap BotInfo;
inline BotInfoData GetBotInfo(AInventory *weap)
{
if (weap == nullptr) return BotInfoData();
auto k = BotInfo.CheckKey(weap->GetClass()->TypeName);
if (k) return *k;
return BotInfoData();
}
//Used to keep all the globally needed variables in nice order.
class FCajunMaster
{

View file

@ -188,7 +188,7 @@ void DBot::Dofire (ticcmd_t *cmd)
//Reaction skill thing.
if (first_shot &&
!(player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING))
!(GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_REACTION_SKILL_THING))
{
t_react = (100-skill.reaction+1)/((pr_botdofire()%3)+3);
}
@ -203,38 +203,21 @@ void DBot::Dofire (ticcmd_t *cmd)
Dist = player->mo->Distance2D(enemy, player->mo->Vel.X - enemy->Vel.X, player->mo->Vel.Y - enemy->Vel.Y);
//FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go.
if (player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)
if (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0)
{
if ((player->ReadyWeapon->ProjectileType != NULL))
{
if (player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true))
{
// This weapon can fire a projectile and has enough ammo to do so
goto shootmissile;
}
else if (!(player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL))
{
// Ammo is required, so don't shoot. This is for weapons that shoot
// missiles that die at close range, such as the powered-up Phoneix Rod.
return;
}
}
else
{
//*4 is for atmosphere, the chainsaws sounding and all..
no_fire = (Dist > DEFMELEERANGE*4);
}
//*4 is for atmosphere, the chainsaws sounding and all..
no_fire = (Dist > DEFMELEERANGE*4);
}
else if (player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG)
else if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_BFG)
{
//MAKEME: This should be smarter.
if ((pr_botdofire()%200)<=skill.reaction)
if(Check_LOS(enemy, SHOOTFOV))
no_fire = false;
}
else if (player->ReadyWeapon->ProjectileType != NULL)
else if (GetBotInfo(player->ReadyWeapon).projectileType != NULL)
{
if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE)
{
//Special rules for RL
an = FireRox (enemy, cmd);
@ -250,9 +233,8 @@ void DBot::Dofire (ticcmd_t *cmd)
}
}
// prediction aiming
shootmissile:
Dist = player->mo->Distance2D(enemy);
fm = Dist / GetDefaultByType (player->ReadyWeapon->ProjectileType)->Speed;
fm = Dist / GetDefaultByType (GetBotInfo(player->ReadyWeapon).projectileType)->Speed;
bglobal.SetBodyAt(enemy->Pos() + enemy->Vel.XY() * fm * 2, 1);
Angle = player->mo->AngleTo(bglobal.body1);
if (Check_LOS (enemy, SHOOTFOV))

View file

@ -345,7 +345,7 @@ void DBot::TurnToAng ()
if (player->ReadyWeapon != NULL)
{
if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE)
{
if (t_roam && !missile)
{ //Keep angle that where when shot where decided.
@ -356,7 +356,7 @@ void DBot::TurnToAng ()
if(enemy)
if(!dest) //happens when running after item in combat situations, or normal, prevents weak turns
if(player->ReadyWeapon->ProjectileType == NULL && !(player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON))
if(GetBotInfo(player->ReadyWeapon).projectileType == NULL && GetBotInfo(player->ReadyWeapon).MoveCombatDist > 0)
if(Check_LOS(enemy, SHOOTFOV+5))
maxturn = 3;
}

View file

@ -173,9 +173,9 @@ void DBot::ThinkForMove (ticcmd_t *cmd)
is (Megasphere)
) ||
dist < (GETINCOMBAT/4) ||
(player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)
(GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0)
)
&& (dist < GETINCOMBAT || (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
&& (dist < GETINCOMBAT || (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0))
&& Reachable (dest))
#undef is
{
@ -185,7 +185,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd)
dest = NULL; //To let bot turn right
if (player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
if (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0)
player->mo->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting.
if (!(enemy->flags3 & MF3_ISMONSTER))
@ -205,7 +205,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd)
if (player->ReadyWeapon == NULL ||
player->mo->Distance2D(enemy) >
player->ReadyWeapon->MoveCombatDist)
GetBotInfo(player->ReadyWeapon).MoveCombatDist)
{
// If a monster, use lower speed (just for cooler apperance while strafing down doomed monster)
cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN;
@ -273,8 +273,8 @@ void DBot::ThinkForMove (ticcmd_t *cmd)
{
if (enemy->player)
{
if (((enemy->player->ReadyWeapon != NULL && enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) ||
(pr_botmove()%100)>skill.isp) && player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
if (((enemy->player->ReadyWeapon != NULL && GetBotInfo(enemy->player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE) ||
(pr_botmove()%100)>skill.isp) && (GetBotInfo(player->ReadyWeapon).MoveCombatDist != 0))
dest = enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy.
else //hide while t_fight, but keep view at enemy.
Angle = player->mo->AngleTo(enemy);
@ -362,15 +362,17 @@ void DBot::WhatToGet (AActor *item)
if (item->IsKindOf(NAME_Weapon))
{
// FIXME
AWeapon *heldWeapon;
AInventory *heldWeapon;
heldWeapon = dyn_cast<AWeapon>(player->mo->FindInventory(item->GetClass()));
heldWeapon = player->mo->FindInventory(item->GetClass());
if (heldWeapon != NULL)
{
if (!weapgiveammo)
return;
if ((heldWeapon->Ammo1 == NULL || heldWeapon->Ammo1->Amount >= heldWeapon->Ammo1->MaxAmount) &&
(heldWeapon->Ammo2 == NULL || heldWeapon->Ammo2->Amount >= heldWeapon->Ammo2->MaxAmount))
auto ammo1 = heldWeapon->PointerVar<AInventory>(NAME_Ammo1);
auto ammo2 = heldWeapon->PointerVar<AInventory>(NAME_Ammo2);
if ((ammo1 == NULL || ammo1->Amount >= ammo1->MaxAmount) &&
(ammo2 == NULL || ammo2->Amount >= ammo2->MaxAmount))
{
return;
}

View file

@ -374,7 +374,9 @@ static void ShoveChatStr (const char *str, uint8_t who)
static bool DoSubstitution (FString &out, const char *in)
{
player_t *player = &players[consoleplayer];
AWeapon *weapon = player->ReadyWeapon;
auto weapon = player->ReadyWeapon;
auto ammo1 = weapon ? weapon->PointerVar<AInventory>(NAME_Ammo1) : nullptr;
auto ammo2 = weapon ? weapon->PointerVar<AInventory>(NAME_Ammo2) : nullptr;
const char *a, *b;
a = in;
@ -427,10 +429,10 @@ static bool DoSubstitution (FString &out, const char *in)
}
else
{
out.AppendFormat("%d", weapon->Ammo1 != NULL ? weapon->Ammo1->Amount : 0);
if (weapon->Ammo2 != NULL)
out.AppendFormat("%d", ammo1 != NULL ? ammo1->Amount : 0);
if (ammo2 != NULL)
{
out.AppendFormat("/%d", weapon->Ammo2->Amount);
out.AppendFormat("/%d", ammo2->Amount);
}
}
}
@ -439,16 +441,16 @@ static bool DoSubstitution (FString &out, const char *in)
{
if (strnicmp(a, "ammo", 4) == 0)
{
if (weapon == NULL || weapon->Ammo1 == NULL)
if (ammo1 == NULL)
{
out += "no ammo";
}
else
{
out.AppendFormat("%s", weapon->Ammo1->GetClass()->TypeName.GetChars());
if (weapon->Ammo2 != NULL)
out.AppendFormat("%s", ammo1->GetClass()->TypeName.GetChars());
if (ammo2 != NULL)
{
out.AppendFormat("/%s", weapon->Ammo2->GetClass()->TypeName.GetChars());
out.AppendFormat("/%s", ammo2->GetClass()->TypeName.GetChars());
}
}
}

View file

@ -761,8 +761,8 @@ void SetDehParams(FState *state, int codepointer)
// Let's identify the codepointer we're dealing with.
PFunction *sym;
sym = dyn_cast<PFunction>(RUNTIME_CLASS(AWeapon)->FindSymbol(FName(MBFCodePointers[codepointer].name), true));
if (sym == NULL) return;
sym = dyn_cast<PFunction>(PClass::FindActor(NAME_Weapon)->FindSymbol(FName(MBFCodePointers[codepointer].name), true));
if (sym == NULL ) return;
if (codepointer < 0 || (unsigned)codepointer >= countof(MBFCodePointerFactories))
{
@ -1580,16 +1580,18 @@ static int PatchAmmo (int ammoNum)
defaultAmmo->MaxAmount = *max;
defaultAmmo->Amount = Scale (defaultAmmo->Amount, *per, oldclip);
}
else if (type->IsDescendantOf (RUNTIME_CLASS(AWeapon)))
else if (type->IsDescendantOf (NAME_Weapon))
{
AWeapon *defWeap = (AWeapon *)GetDefaultByType (type);
if (defWeap->AmmoType1 == ammoType)
auto defWeap = GetDefaultByType (type);
if (defWeap->PointerVar<PClassActor>(NAME_AmmoType1) == ammoType)
{
defWeap->AmmoGive1 = Scale (defWeap->AmmoGive1, *per, oldclip);
auto &AmmoGive1 = defWeap->IntVar(NAME_AmmoGive1);
AmmoGive1 = Scale (AmmoGive1, *per, oldclip);
}
if (defWeap->AmmoType2 == ammoType)
if (defWeap->PointerVar<PClassActor>(NAME_AmmoType2) == ammoType)
{
defWeap->AmmoGive2 = Scale (defWeap->AmmoGive2, *per, oldclip);
auto &AmmoGive2 = defWeap->IntVar(NAME_AmmoGive2);
AmmoGive2 = Scale (AmmoGive2, *per, oldclip);
}
}
}
@ -1601,9 +1603,8 @@ static int PatchAmmo (int ammoNum)
static int PatchWeapon (int weapNum)
{
int result;
PClassActor *type = NULL;
uint8_t dummy[sizeof(AWeapon)];
AWeapon *info = (AWeapon *)&dummy;
PClassActor *type = nullptr;
AInventory *info = nullptr;
bool patchedStates = false;
FStateDefinitions statedef;
@ -1612,7 +1613,7 @@ static int PatchWeapon (int weapNum)
type = WeaponNames[weapNum];
if (type != NULL)
{
info = (AWeapon *)GetDefaultByType (type);
info = (AInventory*)GetDefaultByType (type);
DPrintf (DMSG_SPAMMY, "Weapon %d\n", weapNum);
}
}
@ -1655,13 +1656,18 @@ static int PatchWeapon (int weapNum)
{
val = 5;
}
info->AmmoType1 = AmmoNames[val];
if (info->AmmoType1 != NULL)
if (info)
{
info->AmmoGive1 = ((AInventory*)GetDefaultByType (info->AmmoType1))->Amount * 2;
if (info->AmmoUse1 == 0)
auto &AmmoType = info->PointerVar<PClassActor>(NAME_AmmoType1);
AmmoType = AmmoNames[val];
if (AmmoType != nullptr)
{
info->AmmoUse1 = 1;
info->IntVar(NAME_AmmoGive1) = ((AInventory*)GetDefaultByType(AmmoType))->Amount * 2;
auto &AmmoUse = info->IntVar(NAME_AmmoUse1);
if (AmmoUse == 0)
{
AmmoUse = 1;
}
}
}
}
@ -1676,7 +1682,7 @@ static int PatchWeapon (int weapNum)
const FDecalTemplate *decal = DecalLibrary.GetDecalByName (Line2);
if (decal != NULL)
{
info->DecalGenerator = const_cast <FDecalTemplate *>(decal);
if (info) info->DecalGenerator = const_cast <FDecalTemplate *>(decal);
}
else
{
@ -1685,12 +1691,15 @@ static int PatchWeapon (int weapNum)
}
else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0)
{
info->AmmoUse1 = val;
info->flags6 |= MF6_INTRYMOVE; // flag the weapon for postprocessing (reuse a flag that can't be set by external means)
if (info)
{
info->IntVar(NAME_AmmoUse1) = val;
info->flags6 |= MF6_INTRYMOVE; // flag the weapon for postprocessing (reuse a flag that can't be set by external means)
}
}
else if (stricmp (Line1, "Min ammo") == 0)
{
info->MinAmmo1 = val;
if (info) info->IntVar(NAME_MinSelAmmo1) = val;
}
else
{
@ -1698,14 +1707,17 @@ static int PatchWeapon (int weapNum)
}
}
if (info->AmmoType1 == NULL)
if (info)
{
info->AmmoUse1 = 0;
}
if (info->PointerVar<PClassActor>(NAME_AmmoType1) == nullptr)
{
info->IntVar(NAME_AmmoUse1) = 0;
}
if (patchedStates)
{
statedef.InstallStates(type, info);
if (patchedStates)
{
statedef.InstallStates(type, info);
}
}
return result;
@ -2117,7 +2129,7 @@ static int PatchCodePtrs (int dummy)
// This skips the action table and goes directly to the internal symbol table
// DEH compatible functions are easy to recognize.
PFunction *sym = dyn_cast<PFunction>(RUNTIME_CLASS(AWeapon)->FindSymbol(symname, true));
PFunction *sym = dyn_cast<PFunction>(PClass::FindActor(NAME_Weapon)->FindSymbol(symname, true));
if (sym == NULL)
{
Printf(TEXTCOLOR_RED "Frame %d: Unknown code pointer '%s'\n", frame, Line2);
@ -2724,6 +2736,7 @@ static bool LoadDehSupp ()
sc.OpenLumpNum(lump);
sc.SetCMode(true);
auto wcls = PClass::FindActor(NAME_Weapon);
while (sc.GetString())
{
if (sc.Compare("ActionList"))
@ -2738,11 +2751,11 @@ static bool LoadDehSupp ()
}
else
{
// all relevant code pointers are either defined in AWeapon
// or AActor so this will find all of them.
// all relevant code pointers are either defined in Weapon
// or Actor so this will find all of them.
FString name = "A_";
name << sc.String;
PFunction *sym = dyn_cast<PFunction>(RUNTIME_CLASS(AWeapon)->FindSymbol(name, true));
PFunction *sym = dyn_cast<PFunction>(wcls->FindSymbol(name, true));
if (sym == NULL)
{
sc.ScriptError("Unknown code pointer '%s'", sc.String);
@ -3084,9 +3097,10 @@ void FinishDehPatch ()
// Now it gets nasty: We have to fiddle around with the weapons' ammo use info to make Doom's original
// ammo consumption work as intended.
auto wcls = PClass::FindActor(NAME_Weapon);
for(unsigned i = 0; i < WeaponNames.Size(); i++)
{
AWeapon *weap = (AWeapon*)GetDefaultByType(WeaponNames[i]);
AInventory *weap = (AInventory*)GetDefaultByType(WeaponNames[i]);
bool found = false;
if (weap->flags6 & MF6_INTRYMOVE)
{
@ -3095,8 +3109,8 @@ void FinishDehPatch ()
}
else
{
weap->WeaponFlags |= WIF_DEHAMMO;
weap->AmmoUse1 = 0;
weap->BoolVar(NAME_bDehAmmo) = true;
weap->IntVar(NAME_AmmoUse1) = 0;
// to allow proper checks in CheckAmmo we have to find the first attack pointer in the Fire sequence
// and set its default ammo use as the weapon's AmmoUse1.
@ -3115,7 +3129,7 @@ void FinishDehPatch ()
{
if (AmmoPerAttacks[j].ptr == nullptr)
{
auto p = dyn_cast<PFunction>(RUNTIME_CLASS(AWeapon)->FindSymbol(AmmoPerAttacks[j].func, true));
auto p = dyn_cast<PFunction>(wcls->FindSymbol(AmmoPerAttacks[j].func, true));
if (p != nullptr) AmmoPerAttacks[j].ptr = p->Variants[0].Implementation;
}
if (state->ActionFunc == AmmoPerAttacks[j].ptr)
@ -3123,7 +3137,7 @@ void FinishDehPatch ()
found = true;
int use = AmmoPerAttacks[j].ammocount;
if (use < 0) use = deh.BFGCells;
weap->AmmoUse1 = use;
weap->IntVar(NAME_AmmoUse1) = use;
break;
}
}

View file

@ -2335,7 +2335,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player)
{
if (GetDefaultByType (typeinfo)->flags & MF_MISSILE)
{
P_SpawnPlayerMissile (source, typeinfo);
P_SpawnPlayerMissile (source, 0, 0, 0, typeinfo, source->Angles.Yaw);
}
else
{
@ -2536,10 +2536,10 @@ void Net_DoCommand (int type, uint8_t **stream, int player)
case DEM_MORPHEX:
{
s = ReadString (stream);
const char *msg = cht_Morph (players + player, PClass::FindActor (s), false);
FString msg = cht_Morph (players + player, PClass::FindActor (s), false);
if (player == consoleplayer)
{
Printf ("%s\n", *msg != '\0' ? msg : "Morph failed.");
Printf ("%s\n", msg[0] != '\0' ? msg.GetChars() : "Morph failed.");
}
}
break;
@ -2631,7 +2631,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player)
int count = ReadByte(stream);
if (slot < NUM_WEAPON_SLOTS)
{
players[pnum].weapons.Slots[slot].Clear();
players[pnum].weapons.ClearSlot(slot);
}
for(i = 0; i < count; ++i)
{

View file

@ -92,20 +92,15 @@ public:
virtual void AddInventory (AInventory *item) override;
virtual void RemoveInventory (AInventory *item) override;
virtual bool UseInventory (AInventory *item) override;
virtual void MarkPrecacheSounds () const override;
virtual void BeginPlay () override;
virtual void Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) override;
virtual bool UpdateWaterLevel (bool splash) override;
bool ResetAirSupply (bool playgasp = true);
int GetMaxHealth(bool withupgrades = false) const;
void ActivateMorphWeapon ();
AWeapon *PickNewWeapon (PClassActor *ammotype);
AWeapon *BestWeapon (PClassActor *ammotype);
AInventory *PickNewWeapon (PClassActor *ammotype);
AInventory *BestWeapon (PClassActor *ammotype);
void GiveDeathmatchInventory ();
void FilterCoopRespawnInventory (APlayerPawn *oldplayer);
void SetupWeaponSlots ();
void GiveDefaultInventory ();
// These are virtual on the script side only.
@ -155,6 +150,7 @@ public:
// [SP] ViewBob Multiplier
double ViewBob;
double curBob;
// Former class properties that were moved into the object to get rid of the meta class.
FName SoundClass; // Sound class
@ -235,7 +231,7 @@ enum
// The VM cannot deal with this as an invalid pointer because it performs a read barrier on every object pointer read.
// This doesn't have to point to a valid weapon, though, because WP_NOCHANGE is never dereferenced, but it must point to a valid object
// and the class descriptor just works fine for that.
extern AWeapon *WP_NOCHANGE;
extern AInventory *WP_NOCHANGE;
#define MAXPLAYERNAME 15
@ -421,8 +417,8 @@ public:
uint8_t spreecount = 0; // [RH] Keep track of killing sprees
uint16_t WeaponState = 0;
AWeapon *ReadyWeapon = nullptr;
AWeapon *PendingWeapon = nullptr; // WP_NOCHANGE if not changing
AInventory *ReadyWeapon = nullptr;
AInventory *PendingWeapon = nullptr; // WP_NOCHANGE if not changing
TObjPtr<DPSprite*> psprites = nullptr; // view sprites (gun, etc)
int cheats = 0; // bit flags
@ -446,8 +442,8 @@ public:
int morphTics = 0; // player is a chicken/pig if > 0
PClassActor *MorphedPlayerClass = nullptr; // [MH] (for SBARINFO) class # for this player instance when morphed
int MorphStyle = 0; // which effects to apply for this player instance when morphed
PClassActor *MorphExitFlash = nullptr; // flash to apply when demorphing (cache of value given to P_MorphPlayer)
TObjPtr<AWeapon*> PremorphWeapon = nullptr; // ready weapon before morphing
PClassActor *MorphExitFlash = nullptr; // flash to apply when demorphing (cache of value given to MorphPlayer)
TObjPtr<AInventory*> PremorphWeapon = nullptr; // ready weapon before morphing
int chickenPeck = 0; // chicken peck countdown
int jumpTics = 0; // delay the next jump for a moment
bool onground = 0; // Identifies if this player is on the ground or other object

View file

@ -426,6 +426,8 @@ template<class T> T *dyn_cast(DObject *p)
return NULL;
}
template<class T> const T *dyn_cast(const DObject *p)
{
return dyn_cast<T>(const_cast<DObject *>(p));

View file

@ -75,7 +75,7 @@ bool PClass::bVMOperational;
// that does not work anymore. WP_NOCHANGE needs to point to a vaild object to work as intended.
// This Object does not need to be garbage collected, though, but it needs to provide the proper structure so that the
// GC can process it.
AWeapon *WP_NOCHANGE;
AInventory *WP_NOCHANGE;
DEFINE_GLOBAL(WP_NOCHANGE);
@ -231,7 +231,7 @@ void PClass::StaticInit ()
// WP_NOCHANGE must point to a valid object, although it does not need to be a weapon.
// A simple DObject is enough to give the GC the ability to deal with it, if subjected to it.
WP_NOCHANGE = (AWeapon*)Create<DObject>();
WP_NOCHANGE = (AInventory*)Create<DObject>();
WP_NOCHANGE->Release();
}

View file

@ -48,6 +48,7 @@
#include "r_utility.h"
#include "g_levellocals.h"
#include "actorinlines.h"
#include "scriptutil.h"
static FRandom pr_script("FScript");
@ -283,6 +284,17 @@ static int T_GetPlayerNum(const svalue_t &arg)
return playernum;
}
APlayerPawn *T_GetPlayerActor(const svalue_t &arg)
{
int num = T_GetPlayerNum(arg);
return num == -1 ? nullptr : players[num].mo;
}
PClassActor *T_ClassType(const svalue_t &arg)
{
return PClass::FindActor(stringvalue(arg));
}
//==========================================================================
//
// Finds a sector from a tag. This has been extended to allow looking for
@ -2576,50 +2588,11 @@ void FParser::SF_PlayerAmmo(void)
void FParser::SF_MaxPlayerAmmo()
{
int playernum, amount;
PClassActor * ammotype;
if (CheckArgs(2))
{
playernum=T_GetPlayerNum(t_argv[0]);
if (playernum==-1) return;
ammotype=T_GetAmmo(t_argv[1]);
if (!ammotype) return;
if(t_argc == 2)
{
}
else if(t_argc >= 3)
{
auto iammo = players[playernum].mo->FindInventory(ammotype);
amount = intvalue(t_argv[2]);
if(amount < 0) amount = 0;
if (!iammo)
{
players[playernum].mo->GiveAmmo(ammotype, 1);
iammo = players[playernum].mo->FindInventory(ammotype);
iammo->Amount = 0;
}
iammo->MaxAmount = amount;
for (AInventory *item = players[playernum].mo->Inventory; item != NULL; item = item->Inventory)
{
if (item->IsKindOf(NAME_BackpackItem))
{
if (t_argc>=4) amount = intvalue(t_argv[3]);
else amount*=2;
break;
}
}
iammo->IntVar("BackpackMaxAmount") = amount;
}
t_return.type = svt_int;
AInventory * iammo = players[playernum].mo->FindInventory(ammotype);
if (iammo) t_return.value.i = iammo->MaxAmount;
else t_return.value.i = ((AInventory*)GetDefaultByType(ammotype))->MaxAmount;
t_return.value.i = ScriptUtil::Exec("MaxPlayerAmmo", ScriptUtil::Pointer, T_GetPlayerActor(t_argv[0]), ScriptUtil::Class, T_ClassType(t_argv[1]),
ScriptUtil::Int, t_argc >= 3? intvalue(t_argv[2]) : INT_MIN, ScriptUtil::Int, t_argc >= 4 ? intvalue(t_argv[3]) : INT_MIN, ScriptUtil::End);
}
}
@ -2740,7 +2713,7 @@ void FParser::SF_PlayerSelectedWeapon()
return;
}
players[playernum].PendingWeapon = (AWeapon*)players[playernum].mo->FindInventory(ti);
players[playernum].PendingWeapon = (AInventory*)players[playernum].mo->FindInventory(ti);
}
t_return.type = svt_int;
@ -2834,34 +2807,8 @@ void FParser::SF_SetWeapon()
{
if (CheckArgs(2))
{
int playernum=T_GetPlayerNum(t_argv[0]);
if (playernum!=-1)
{
AInventory *item = players[playernum].mo->FindInventory (PClass::FindActor (stringvalue(t_argv[1])));
if (item == NULL || !item->IsKindOf(NAME_Weapon))
{
}
else if (players[playernum].ReadyWeapon == item)
{
// The weapon is already selected, so setweapon succeeds by default,
// but make sure the player isn't switching away from it.
players[playernum].PendingWeapon = WP_NOCHANGE;
t_return.value.i = 1;
}
else
{
auto weap = static_cast<AWeapon *> (item);
if (weap->CheckAmmo (AWeapon::EitherFire, false))
{
// There's enough ammo, so switch to it.
t_return.value.i = 1;
players[playernum].PendingWeapon = weap;
}
}
}
t_return.value.i = 0;
t_return.type = svt_int;
t_return.value.i = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, T_GetPlayerActor(t_argv[0]), ScriptUtil::Class, T_ClassType(t_argv[1]), ScriptUtil::End);
}
}

View file

@ -281,10 +281,16 @@ CCMD (slot)
{
int slot = atoi (argv[1]);
if (slot < NUM_WEAPON_SLOTS)
auto mo = players[consoleplayer].mo;
if (slot < NUM_WEAPON_SLOTS && mo)
{
SendItemUse = players[consoleplayer].weapons.Slots[slot].PickWeapon (&players[consoleplayer],
!(dmflags2 & DF2_DONTCHECKAMMO));
// Needs to be redone
IFVIRTUALPTR(mo, APlayerPawn, PickWeapon)
{
VMValue param[] = { mo, slot, !(dmflags2 & DF2_DONTCHECKAMMO) };
VMReturn ret((void**)&SendItemUse);
VMCall(func, param, 3, &ret, 1);
}
}
}
}
@ -316,7 +322,18 @@ CCMD (turn180)
CCMD (weapnext)
{
SendItemUse = players[consoleplayer].weapons.PickNextWeapon (&players[consoleplayer]);
auto mo = players[consoleplayer].mo;
if (mo)
{
// Needs to be redone
IFVIRTUALPTR(mo, APlayerPawn, PickNextWeapon)
{
VMValue param[] = { mo };
VMReturn ret((void**)&SendItemUse);
VMCall(func, param, 1, &ret, 1);
}
}
// [BC] Option to display the name of the weapon being cycled to.
if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse)
{
@ -331,7 +348,18 @@ CCMD (weapnext)
CCMD (weapprev)
{
SendItemUse = players[consoleplayer].weapons.PickPrevWeapon (&players[consoleplayer]);
auto mo = players[consoleplayer].mo;
if (mo)
{
// Needs to be redone
IFVIRTUALPTR(mo, APlayerPawn, PickPrevWeapon)
{
VMValue param[] = { mo };
VMReturn ret((void**)&SendItemUse);
VMCall(func, param, 1, &ret, 1);
}
}
// [BC] Option to display the name of the weapon being cycled to.
if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse)
{
@ -765,19 +793,28 @@ void G_BuildTiccmd (ticcmd_t *cmd)
//[Graf Zahl] This really helps if the mouse update rate can't be increased!
CVAR (Bool, smooth_mouse, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
static int LookAdjust(int look)
{
look <<= 16;
if (players[consoleplayer].playerstate != PST_DEAD && // No adjustment while dead.
players[consoleplayer].ReadyWeapon != NULL) // No adjustment if no weapon.
{
auto scale = players[consoleplayer].ReadyWeapon->FloatVar(NAME_FOVScale);
if (scale > 0) // No adjustment if it is non-positive.
{
look = int(look * scale);
}
}
return look;
}
void G_AddViewPitch (int look, bool mouse)
{
if (gamestate == GS_TITLELEVEL)
{
return;
}
look <<= 16;
if (players[consoleplayer].playerstate != PST_DEAD && // No adjustment while dead.
players[consoleplayer].ReadyWeapon != NULL && // No adjustment if no weapon.
players[consoleplayer].ReadyWeapon->FOVScale > 0) // No adjustment if it is non-positive.
{
look = int(look * players[consoleplayer].ReadyWeapon->FOVScale);
}
look = LookAdjust(look);
if (!level.IsFreelookAllowed())
{
LocalViewPitch = 0;
@ -817,14 +854,9 @@ void G_AddViewAngle (int yaw, bool mouse)
if (gamestate == GS_TITLELEVEL)
{
return;
}
yaw <<= 16;
if (players[consoleplayer].playerstate != PST_DEAD && // No adjustment while dead.
players[consoleplayer].ReadyWeapon != NULL && // No adjustment if no weapon.
players[consoleplayer].ReadyWeapon->FOVScale > 0) // No adjustment if it is non-positive.
{
yaw = int(yaw * players[consoleplayer].ReadyWeapon->FOVScale);
}
yaw = LookAdjust(yaw);
LocalViewAngle -= yaw;
if (yaw != 0)
{
@ -1249,7 +1281,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags)
if (p->morphTics != 0)
{ // Undo morph
P_UndoPlayerMorph (p, p, 0, true);
P_UnmorphActor(p->mo, p->mo, 0, true);
}
// Strip all current powers, unless moving in a hub and the power is okay to keep.
@ -1269,8 +1301,8 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags)
item = next;
}
if (p->ReadyWeapon != NULL &&
p->ReadyWeapon->WeaponFlags&WIF_POWERED_UP &&
p->PendingWeapon == p->ReadyWeapon->SisterWeapon)
p->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_POWERED_UP &&
p->PendingWeapon == p->ReadyWeapon->PointerVar<AInventory>(NAME_SisterWeapon))
{
// Unselect powered up weapons if the unpowered counterpart is pending
p->ReadyWeapon=p->PendingWeapon;

View file

@ -54,12 +54,6 @@
EXTERN_CVAR(Bool, sv_unlimited_pickup)
void AInventory::Finalize(FStateDefinitions &statedef)
{
Super::Finalize(statedef);
flags |= MF_SPECIAL;
}
IMPLEMENT_CLASS(AInventory, false, true)
IMPLEMENT_POINTERS_START(AInventory)
@ -161,152 +155,6 @@ bool AInventory::Massacre()
return false;
}
//===========================================================================
//
// AInventory :: MarkPrecacheSounds
//
//===========================================================================
void AInventory::MarkPrecacheSounds() const
{
Super::MarkPrecacheSounds();
PickupSound.MarkUsed();
}
//===========================================================================
//
// AInventory :: BecomeItem
//
// Lets this actor know that it's about to be placed in an inventory.
//
//===========================================================================
void AInventory::BecomeItem ()
{
if (!(flags & (MF_NOBLOCKMAP|MF_NOSECTOR)))
{
UnlinkFromWorld (nullptr);
flags |= MF_NOBLOCKMAP|MF_NOSECTOR;
LinkToWorld (nullptr);
}
RemoveFromHash ();
flags &= ~MF_SPECIAL;
ChangeStatNum(STAT_INVENTORY);
// stop all sounds this item is playing.
for(int i = 1;i<=7;i++) S_StopSound(this, i);
SetState (FindState("Held"));
}
DEFINE_ACTION_FUNCTION(AInventory, BecomeItem)
{
PARAM_SELF_PROLOGUE(AInventory);
self->BecomeItem();
return 0;
}
//===========================================================================
//
// AInventory :: BecomePickup
//
// Lets this actor know it should wait to be picked up.
//
//===========================================================================
void AInventory::BecomePickup ()
{
if (Owner != NULL)
{
Owner->RemoveInventory (this);
}
if (flags & (MF_NOBLOCKMAP|MF_NOSECTOR))
{
UnlinkFromWorld (nullptr);
flags &= ~(MF_NOBLOCKMAP|MF_NOSECTOR);
LinkToWorld (nullptr);
P_FindFloorCeiling (this);
}
flags = (GetDefault()->flags | MF_DROPPED) & ~MF_COUNTITEM;
renderflags &= ~RF_INVISIBLE;
ChangeStatNum(STAT_DEFAULT);
SetState (SpawnState);
}
DEFINE_ACTION_FUNCTION(AInventory, BecomePickup)
{
PARAM_SELF_PROLOGUE(AInventory);
self->BecomePickup();
return 0;
}
//===========================================================================
//
// AInventory :: GetSpeedFactor
//
//===========================================================================
double AInventory::GetSpeedFactor()
{
double factor = 1.;
auto self = this;
while (self != nullptr)
{
IFVIRTUALPTR(self, AInventory, GetSpeedFactor)
{
VMValue params[1] = { (DObject*)self };
double retval;
VMReturn ret(&retval);
VMCall(func, params, 1, &ret, 1);
factor *= retval;
}
self = self->Inventory;
}
return factor;
}
//===========================================================================
//
// AInventory :: GetNoTeleportFreeze
//
//===========================================================================
bool AInventory::GetNoTeleportFreeze ()
{
auto self = this;
while (self != nullptr)
{
IFVIRTUALPTR(self, AInventory, GetNoTeleportFreeze)
{
VMValue params[1] = { (DObject*)self };
int retval;
VMReturn ret(&retval);
VMCall(func, params, 1, &ret, 1);
if (retval) return true;
}
self = self->Inventory;
}
return false;
}
//===========================================================================
//
// AInventory :: Use
//
//===========================================================================
bool AInventory::CallUse(bool pickup)
{
IFVIRTUAL(AInventory, Use)
{
VMValue params[2] = { (DObject*)this, pickup };
int retval;
VMReturn ret(&retval);
VMCall(func, params, 2, &ret, 1);
return !!retval;
}
return false;
}
//===========================================================================
//
//
@ -391,25 +239,6 @@ PalEntry AInventory::CallGetBlend()
else return 0;
}
//===========================================================================
//
// AInventory :: PrevItem
//
// Returns the previous item.
//
//===========================================================================
AInventory *AInventory::PrevItem ()
{
AInventory *item = Owner->Inventory;
while (item != NULL && item->Inventory != this)
{
item = item->Inventory;
}
return item;
}
//===========================================================================
//
// AInventory :: PrevInv
@ -545,10 +374,3 @@ CCMD (targetinv)
"the NOBLOCKMAP flag or have height/radius of 0.\n");
}
//===========================================================================
//===========================================================================
IMPLEMENT_CLASS(AStateProvider, false, false)

View file

@ -70,9 +70,7 @@ class AInventory : public AActor
HAS_OBJECT_POINTERS
public:
virtual void Finalize(FStateDefinitions &statedef) override;
virtual void Serialize(FSerializer &arc) override;
virtual void MarkPrecacheSounds() const override;
virtual void OnDestroy() override;
virtual void Tick() override;
virtual bool Massacre() override;
@ -80,17 +78,11 @@ public:
bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // Wrapper for script function.
void DepleteOrDestroy (); // virtual on the script side.
bool CallUse(bool pickup); // virtual on the script side.
PalEntry CallGetBlend(); // virtual on the script side.
double GetSpeedFactor(); // virtual on the script side.
bool GetNoTeleportFreeze(); // virtual on the script side.
void BecomeItem ();
void BecomePickup ();
bool DoRespawn();
AInventory *PrevItem(); // Returns the item preceding this one in the list.
AInventory *PrevInv(); // Returns the previous item with IF_INVBAR set.
AInventory *NextInv(); // Returns the next item with IF_INVBAR set.
@ -110,11 +102,4 @@ public:
FSoundIDNoInit PickupSound;
};
class AStateProvider : public AInventory
{
DECLARE_CLASS (AStateProvider, AInventory)
public:
bool CallStateChain(AActor *actor, FState *state);
};
#endif //__A_PICKUPS_H__

View file

@ -47,49 +47,6 @@
#include "serializer.h"
#include "vm.h"
IMPLEMENT_CLASS(AWeapon, false, true)
IMPLEMENT_POINTERS_START(AWeapon)
IMPLEMENT_POINTER(Ammo1)
IMPLEMENT_POINTER(Ammo2)
IMPLEMENT_POINTER(SisterWeapon)
IMPLEMENT_POINTERS_END
DEFINE_FIELD(AWeapon, AmmoType1)
DEFINE_FIELD(AWeapon, AmmoType2)
DEFINE_FIELD(AWeapon, AmmoGive1)
DEFINE_FIELD(AWeapon, AmmoGive2)
DEFINE_FIELD(AWeapon, MinAmmo1)
DEFINE_FIELD(AWeapon, MinAmmo2)
DEFINE_FIELD(AWeapon, AmmoUse1)
DEFINE_FIELD(AWeapon, AmmoUse2)
DEFINE_FIELD(AWeapon, Kickback)
DEFINE_FIELD(AWeapon, YAdjust)
DEFINE_FIELD(AWeapon, UpSound)
DEFINE_FIELD(AWeapon, ReadySound)
DEFINE_FIELD(AWeapon, SisterWeaponType)
DEFINE_FIELD(AWeapon, ProjectileType)
DEFINE_FIELD(AWeapon, AltProjectileType)
DEFINE_FIELD(AWeapon, SelectionOrder)
DEFINE_FIELD(AWeapon, MinSelAmmo1)
DEFINE_FIELD(AWeapon, MinSelAmmo2)
DEFINE_FIELD(AWeapon, MoveCombatDist)
DEFINE_FIELD(AWeapon, ReloadCounter)
DEFINE_FIELD(AWeapon, BobStyle)
DEFINE_FIELD(AWeapon, BobSpeed)
DEFINE_FIELD(AWeapon, BobRangeX)
DEFINE_FIELD(AWeapon, BobRangeY)
DEFINE_FIELD(AWeapon, Ammo1)
DEFINE_FIELD(AWeapon, Ammo2)
DEFINE_FIELD(AWeapon, SisterWeapon)
DEFINE_FIELD(AWeapon, FOVScale)
DEFINE_FIELD(AWeapon, Crosshair)
DEFINE_FIELD(AWeapon, GivenAsMorphWeapon)
DEFINE_FIELD(AWeapon, bAltFire)
DEFINE_FIELD(AWeapon, SlotNumber)
DEFINE_FIELD(AWeapon, WeaponFlags)
DEFINE_FIELD_BIT(AWeapon, WeaponFlags, bDehAmmo, WIF_DEHAMMO)
//===========================================================================
//
//
@ -105,368 +62,6 @@ TMap<PClassActor *, int> Weapons_hton;
static int ntoh_cmp(const void *a, const void *b);
//===========================================================================
//
//
//
//===========================================================================
void AWeapon::Finalize(FStateDefinitions &statedef)
{
Super::Finalize(statedef);
FState *ready = FindState(NAME_Ready);
FState *select = FindState(NAME_Select);
FState *deselect = FindState(NAME_Deselect);
FState *fire = FindState(NAME_Fire);
auto TypeName = GetClass()->TypeName;
// Consider any weapon without any valid state abstract and don't output a warning
// This is for creating base classes for weapon groups that only set up some properties.
if (ready || select || deselect || fire)
{
if (!ready)
{
I_Error("Weapon %s doesn't define a ready state.", TypeName.GetChars());
}
if (!select)
{
I_Error("Weapon %s doesn't define a select state.", TypeName.GetChars());
}
if (!deselect)
{
I_Error("Weapon %s doesn't define a deselect state.", TypeName.GetChars());
}
if (!fire)
{
I_Error("Weapon %s doesn't define a fire state.", TypeName.GetChars());
}
}
}
//===========================================================================
//
// AWeapon :: Serialize
//
//===========================================================================
void AWeapon::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
auto def = (AWeapon*)GetDefault();
arc("weaponflags", WeaponFlags, def->WeaponFlags)
("ammogive1", AmmoGive1, def->AmmoGive1)
("ammogive2", AmmoGive2, def->AmmoGive2)
("minammo1", MinAmmo1, def->MinAmmo1)
("minammo2", MinAmmo2, def->MinAmmo2)
("ammouse1", AmmoUse1, def->AmmoUse1)
("ammouse2", AmmoUse2, def->AmmoUse2)
("kickback", Kickback, Kickback)
("yadjust", YAdjust, def->YAdjust)
("upsound", UpSound, def->UpSound)
("readysound", ReadySound, def->ReadySound)
("selectionorder", SelectionOrder, def->SelectionOrder)
("ammo1", Ammo1)
("ammo2", Ammo2)
("sisterweapon", SisterWeapon)
("givenasmorphweapon", GivenAsMorphWeapon, def->GivenAsMorphWeapon)
("altfire", bAltFire, def->bAltFire)
("reloadcounter", ReloadCounter, def->ReloadCounter)
("bobstyle", BobStyle, def->BobStyle)
("bobspeed", BobSpeed, def->BobSpeed)
("bobrangex", BobRangeX, def->BobRangeX)
("bobrangey", BobRangeY, def->BobRangeY)
("fovscale", FOVScale, def->FOVScale)
("crosshair", Crosshair, def->Crosshair)
("minselammo1", MinSelAmmo1, def->MinSelAmmo1)
("minselammo2", MinSelAmmo2, def->MinSelAmmo2);
/* these can never change
("ammotype1", AmmoType1, def->AmmoType1)
("ammotype2", AmmoType2, def->AmmoType2)
("sisterweapontype", SisterWeaponType, def->SisterWeaponType)
("projectiletype", ProjectileType, def->ProjectileType)
("altprojectiletype", AltProjectileType, def->AltProjectileType)
("movecombatdist", MoveCombatDist, def->MoveCombatDist)
*/
}
//===========================================================================
//
// AWeapon :: MarkPrecacheSounds
//
//===========================================================================
void AWeapon::MarkPrecacheSounds() const
{
Super::MarkPrecacheSounds();
UpSound.MarkUsed();
ReadySound.MarkUsed();
}
//===========================================================================
//
// AWeapon :: CheckAmmo
//
// Returns true if there is enough ammo to shoot. If not, selects the
// next weapon to use.
//
//===========================================================================
bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int ammocount)
{
IFVIRTUAL(AWeapon, CheckAmmo)
{
VMValue params[] = { (DObject*)this, fireMode, autoSwitch, requireAmmo, ammocount };
VMReturn ret;
int retval;
ret.IntAt(&retval);
VMCall(func, params, 5, &ret, 1);
return !!retval;
}
return DoCheckAmmo(fireMode, autoSwitch, requireAmmo, ammocount);
}
bool AWeapon::DoCheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo, int ammocount)
{
int altFire;
int count1, count2;
int enough, enoughmask;
int lAmmoUse1;
if ((dmflags & DF_INFINITE_AMMO) || (Owner->FindInventory (PClass::FindActor(NAME_PowerInfiniteAmmo), true) != nullptr))
{
return true;
}
if (fireMode == EitherFire)
{
bool gotSome = CheckAmmo (PrimaryFire, false) || CheckAmmo (AltFire, false);
if (!gotSome && autoSwitch)
{
barrier_cast<APlayerPawn *>(Owner)->PickNewWeapon (nullptr);
}
return gotSome;
}
altFire = (fireMode == AltFire);
if (!requireAmmo && (WeaponFlags & (WIF_AMMO_OPTIONAL << altFire)))
{
return true;
}
count1 = (Ammo1 != nullptr) ? Ammo1->Amount : 0;
count2 = (Ammo2 != nullptr) ? Ammo2->Amount : 0;
if ((WeaponFlags & WIF_DEHAMMO) && (Ammo1 == nullptr))
{
lAmmoUse1 = 0;
}
else if (ammocount >= 0 && (WeaponFlags & WIF_DEHAMMO))
{
lAmmoUse1 = ammocount;
}
else
{
lAmmoUse1 = AmmoUse1;
}
enough = (count1 >= lAmmoUse1) | ((count2 >= AmmoUse2) << 1);
if (WeaponFlags & (WIF_PRIMARY_USES_BOTH << altFire))
{
enoughmask = 3;
}
else
{
enoughmask = 1 << altFire;
}
if (altFire && FindState(NAME_AltFire) == nullptr)
{ // If this weapon has no alternate fire, then there is never enough ammo for it
enough &= 1;
}
if (((enough & enoughmask) == enoughmask) || (enough && (WeaponFlags & WIF_AMMO_CHECKBOTH)))
{
return true;
}
// out of ammo, pick a weapon to change to
if (autoSwitch)
{
barrier_cast<APlayerPawn *>(Owner)->PickNewWeapon (nullptr);
}
return false;
}
DEFINE_ACTION_FUNCTION(AWeapon, CheckAmmo)
{
PARAM_SELF_PROLOGUE(AWeapon);
PARAM_INT(mode);
PARAM_BOOL(autoswitch);
PARAM_BOOL(require);
PARAM_INT(ammocnt);
ACTION_RETURN_BOOL(self->DoCheckAmmo(mode, autoswitch, require, ammocnt));
}
//===========================================================================
//
// AWeapon :: DepleteAmmo
//
// Use up some of the weapon's ammo. Returns true if the ammo was successfully
// depleted. If checkEnough is false, then the ammo will always be depleted,
// even if it drops below zero.
//
//===========================================================================
bool AWeapon::DepleteAmmo(bool altFire, bool checkEnough, int ammouse)
{
IFVIRTUAL(AWeapon, DepleteAmmo)
{
VMValue params[] = { (DObject*)this, altFire, checkEnough, ammouse };
VMReturn ret;
int retval;
ret.IntAt(&retval);
VMCall(func, params, 4, &ret, 1);
return !!retval;
}
return DoDepleteAmmo(altFire, checkEnough, ammouse);
}
bool AWeapon::DoDepleteAmmo (bool altFire, bool checkEnough, int ammouse)
{
if (!((dmflags & DF_INFINITE_AMMO) || (Owner->FindInventory (PClass::FindActor(NAME_PowerInfiniteAmmo), true) != nullptr)))
{
if (checkEnough && !CheckAmmo (altFire ? AltFire : PrimaryFire, false, false, ammouse))
{
return false;
}
if (!altFire)
{
if (Ammo1 != nullptr)
{
if (ammouse >= 0 && (WeaponFlags & WIF_DEHAMMO))
{
Ammo1->Amount -= ammouse;
}
else
{
Ammo1->Amount -= AmmoUse1;
}
}
if ((WeaponFlags & WIF_PRIMARY_USES_BOTH) && Ammo2 != nullptr)
{
Ammo2->Amount -= AmmoUse2;
}
}
else
{
if (Ammo2 != nullptr)
{
Ammo2->Amount -= AmmoUse2;
}
if ((WeaponFlags & WIF_ALT_USES_BOTH) && Ammo1 != nullptr)
{
Ammo1->Amount -= AmmoUse1;
}
}
if (Ammo1 != nullptr && Ammo1->Amount < 0)
Ammo1->Amount = 0;
if (Ammo2 != nullptr && Ammo2->Amount < 0)
Ammo2->Amount = 0;
}
return true;
}
DEFINE_ACTION_FUNCTION(AWeapon, DepleteAmmo)
{
PARAM_SELF_PROLOGUE(AWeapon);
PARAM_BOOL(altfire);
PARAM_BOOL(checkenough);
PARAM_INT(ammouse);
ACTION_RETURN_BOOL(self->DoDepleteAmmo(altfire, checkenough, ammouse));
}
//===========================================================================
//
// AWeapon :: PostMorphWeapon
//
// Bring this weapon up after a player unmorphs.
//
//===========================================================================
void AWeapon::PostMorphWeapon ()
{
DPSprite *pspr;
if (Owner == nullptr)
{
return;
}
Owner->player->PendingWeapon = WP_NOCHANGE;
Owner->player->ReadyWeapon = this;
Owner->player->refire = 0;
pspr = Owner->player->GetPSprite(PSP_WEAPON);
pspr->y = WEAPONBOTTOM;
pspr->ResetInterpolation();
pspr->SetState(GetUpState());
}
//===========================================================================
//
// AWeapon :: GetUpState
//
//===========================================================================
FState *AWeapon::GetUpState ()
{
IFVIRTUAL(AWeapon, GetUpState)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
VMCall(func, params, 1, &ret, 1);
return retval;
}
return nullptr;
}
//===========================================================================
//
// AWeapon :: GetDownState
//
//===========================================================================
FState *AWeapon::GetDownState ()
{
IFVIRTUAL(AWeapon, GetDownState)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
VMCall(func, params, 1, &ret, 1);
return retval;
}
return nullptr;
}
//===========================================================================
//
// AWeapon :: GetReadyState
//
//===========================================================================
FState *AWeapon::GetReadyState ()
{
IFVIRTUAL(AWeapon, GetReadyState)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
FState *retval;
ret.PointerAt((void**)&retval);
VMCall(func, params, 1, &ret, 1);
return retval;
}
return nullptr;
}
/* Weapon slots ***********************************************************/
@ -558,73 +153,6 @@ int FWeaponSlot::LocateWeapon(PClassActor *type)
return -1;
}
//===========================================================================
//
// FWeaponSlot :: PickWeapon
//
// Picks a weapon from this slot. If no weapon is selected in this slot,
// or the first weapon in this slot is selected, returns the last weapon.
// Otherwise, returns the previous weapon in this slot. This means
// precedence is given to the last weapon in the slot, which by convention
// is probably the strongest. Does not return weapons you have no ammo
// for or which you do not possess.
//
//===========================================================================
AWeapon *FWeaponSlot::PickWeapon(player_t *player, bool checkammo)
{
int i, j;
if (player->mo == nullptr)
{
return nullptr;
}
// Does this slot even have any weapons?
if (Weapons.Size() == 0)
{
return player->ReadyWeapon;
}
if (player->ReadyWeapon != nullptr)
{
for (i = 0; (unsigned)i < Weapons.Size(); i++)
{
if (Weapons[i].Type == player->ReadyWeapon->GetClass() ||
(player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP &&
player->ReadyWeapon->SisterWeapon != nullptr &&
player->ReadyWeapon->SisterWeapon->GetClass() == Weapons[i].Type))
{
for (j = (i == 0 ? Weapons.Size() - 1 : i - 1);
j != i;
j = (j == 0 ? Weapons.Size() - 1 : j - 1))
{
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory(Weapons[j].Type));
if (weap != nullptr && weap->IsKindOf(NAME_Weapon))
{
if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false))
{
return weap;
}
}
}
}
}
}
for (i = Weapons.Size() - 1; i >= 0; i--)
{
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory(Weapons[i].Type));
if (weap != nullptr && weap->IsKindOf(NAME_Weapon))
{
if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false))
{
return weap;
}
}
}
return player->ReadyWeapon;
}
//===========================================================================
//
// FWeaponSlot :: SetInitialPositions
@ -765,7 +293,7 @@ bool FWeaponSlots::LocateWeapon (PClassActor *type, int *const slot, int *const
DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon)
{
PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots);
PARAM_CLASS(weap, AWeapon);
PARAM_CLASS(weap, AInventory);
int slot = 0, index = 0;
bool retv = self->LocateWeapon(weap, &slot, &index);
if (numret >= 1) ret[0].SetInt(retv);
@ -774,152 +302,21 @@ DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon)
return MIN(numret, 3);
}
//===========================================================================
//
// FindMostRecentWeapon
//
// Locates the slot and index for the most recently selected weapon. If the
// player is in the process of switching to a new weapon, that is the most
// recently selected weapon. Otherwise, the current weapon is the most recent
// weapon.
//
//===========================================================================
static bool FindMostRecentWeapon(player_t *player, int *slot, int *index)
DEFINE_ACTION_FUNCTION(FWeaponSlots, GetWeapon)
{
if (player->PendingWeapon != WP_NOCHANGE)
{
return player->weapons.LocateWeapon(player->PendingWeapon->GetClass(), slot, index);
}
else if (player->ReadyWeapon != nullptr)
{
AWeapon *weap = player->ReadyWeapon;
if (!player->weapons.LocateWeapon(weap->GetClass(), slot, index))
{
// If the current weapon wasn't found and is powered up,
// look for its non-powered up version.
if (weap->WeaponFlags & WIF_POWERED_UP && weap->SisterWeaponType != nullptr)
{
return player->weapons.LocateWeapon(weap->SisterWeaponType, slot, index);
}
return false;
}
return true;
}
else
{
return false;
}
PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots);
PARAM_INT(slot);
PARAM_INT(index);
ACTION_RETURN_POINTER(self->GetWeapon(slot, index));
return 1;
}
//===========================================================================
//
// FWeaponSlots :: PickNextWeapon
//
// Returns the "next" weapon for this player. If the current weapon is not
// in a slot, then it just returns that weapon, since there's nothing to
// consider it relative to.
//
//===========================================================================
AWeapon *FWeaponSlots::PickNextWeapon(player_t *player)
DEFINE_ACTION_FUNCTION(FWeaponSlots, SlotSize)
{
int startslot, startindex;
int slotschecked = 0;
if (player->mo == nullptr)
{
return nullptr;
}
if (player->ReadyWeapon == nullptr || FindMostRecentWeapon(player, &startslot, &startindex))
{
int slot;
int index;
if (player->ReadyWeapon == nullptr)
{
startslot = NUM_WEAPON_SLOTS - 1;
startindex = Slots[startslot].Size() - 1;
}
slot = startslot;
index = startindex;
do
{
if (++index >= Slots[slot].Size())
{
index = 0;
slotschecked++;
if (++slot >= NUM_WEAPON_SLOTS)
{
slot = 0;
}
}
PClassActor *type = Slots[slot].GetWeapon(index);
AWeapon *weap = static_cast<AWeapon *>(player->mo->FindInventory(type));
if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false))
{
return weap;
}
}
while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS);
}
return player->ReadyWeapon;
}
//===========================================================================
//
// FWeaponSlots :: PickPrevWeapon
//
// Returns the "previous" weapon for this player. If the current weapon is
// not in a slot, then it just returns that weapon, since there's nothing to
// consider it relative to.
//
//===========================================================================
AWeapon *FWeaponSlots::PickPrevWeapon (player_t *player)
{
int startslot, startindex;
int slotschecked = 0;
if (player->mo == nullptr)
{
return nullptr;
}
if (player->ReadyWeapon == nullptr || FindMostRecentWeapon (player, &startslot, &startindex))
{
int slot;
int index;
if (player->ReadyWeapon == nullptr)
{
startslot = 0;
startindex = 0;
}
slot = startslot;
index = startindex;
do
{
if (--index < 0)
{
slotschecked++;
if (--slot < 0)
{
slot = NUM_WEAPON_SLOTS - 1;
}
index = Slots[slot].Size() - 1;
}
PClassActor *type = Slots[slot].GetWeapon(index);
AWeapon *weap = static_cast<AWeapon *>(player->mo->FindInventory(type));
if (weap != nullptr && weap->CheckAmmo(AWeapon::EitherFire, false))
{
return weap;
}
}
while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS);
}
return player->ReadyWeapon;
PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots);
PARAM_INT(slot);
ACTION_RETURN_INT(self->SlotSize(slot));
return 1;
}
//===========================================================================
@ -950,18 +347,23 @@ void FWeaponSlots::AddExtraWeapons()
{
continue;
}
auto weapdef = ((AWeapon*)GetDefaultByType(cls));
auto gf = cls->ActorInfo()->GameFilter;
if ((gf == GAME_Any || (gf & gameinfo.gametype)) &&
cls->ActorInfo()->Replacement == nullptr && // Replaced weapons don't get slotted.
!(weapdef->WeaponFlags & WIF_POWERED_UP) &&
!LocateWeapon(cls, nullptr, nullptr) // Don't duplicate it if it's already present.
)
if (LocateWeapon(cls, nullptr, nullptr)) // Do we already have it? Don't add it again.
{
int slot = weapdef->SlotNumber;
if ((unsigned)slot < NUM_WEAPON_SLOTS)
continue;
}
auto weapdef = ((AInventory*)GetDefaultByType(cls));
// Let the weapon decide for itself if it wants to get added to a slot.
IFVIRTUALPTRNAME(weapdef, NAME_Weapon, CheckAddToSlots)
{
VMValue param = weapdef;
int slot = -1, slotpriority;
VMReturn rets[]{ &slot, &slotpriority };
VMCall(func, &param, 1, rets, 2);
if (slot >= 0 && slot < NUM_WEAPON_SLOTS)
{
FWeaponSlot::WeaponInfo info = { cls, weapdef->SlotPriority };
FWeaponSlot::WeaponInfo info = { cls, slotpriority };
Slots[slot].Weapons.Push(info);
}
}
@ -1217,10 +619,10 @@ CCMD (setslot)
}
else if (PlayingKeyConf != nullptr)
{
PlayingKeyConf->Slots[slot].Clear();
PlayingKeyConf->ClearSlot(slot);
for (int i = 2; i < argv.argc(); ++i)
{
PlayingKeyConf->Slots[slot].AddWeapon(argv[i]);
PlayingKeyConf->AddWeapon(slot, argv[i]);
}
}
else
@ -1382,6 +784,49 @@ void P_PlaybackKeyConfWeapons(FWeaponSlots *slots)
PlayingKeyConf = nullptr;
}
//===========================================================================
//
// APlayerPawn :: SetupWeaponSlots
//
// Sets up the default weapon slots for this player. If this is also the
// local player, determines local modifications and sends those across the
// network. Ignores voodoo dolls.
//
//===========================================================================
void FWeaponSlots::SetupWeaponSlots(APlayerPawn *pp)
{
auto player = pp->player;
if (player != nullptr && player->mo == pp)
{
player->weapons.StandardSetup(pp->GetClass());
// If we're the local player, then there's a bit more work to do.
// This also applies if we're a bot and this is the net arbitrator.
if (player - players == consoleplayer ||
(player->Bot != nullptr && consoleplayer == Net_Arbitrator))
{
FWeaponSlots local_slots(player->weapons);
if (player->Bot != nullptr)
{ // Bots only need weapons from KEYCONF, not INI modifications.
P_PlaybackKeyConfWeapons(&local_slots);
}
else
{
local_slots.LocalSetup(pp->GetClass());
}
local_slots.SendDifferences(int(player - players), player->weapons);
}
}
}
DEFINE_ACTION_FUNCTION(FWeaponSlots, SetupWeaponSlots)
{
PARAM_PROLOGUE;
PARAM_OBJECT(pawn, APlayerPawn);
FWeaponSlots::SetupWeaponSlots(pawn);
return 0;
}
//===========================================================================
//
// P_SetupWeapons_ntohton

View file

@ -2,7 +2,7 @@
#include "a_pickups.h"
class PClassActor;
class AWeapon;
class APlayerPawn;
class FWeaponSlot
{
@ -14,7 +14,6 @@ public:
bool AddWeapon (const char *type);
bool AddWeapon (PClassActor *type);
void AddWeaponList (const char *list, bool clear);
AWeapon *PickWeapon (player_t *player, bool checkammo = false);
int Size () const { return (int)Weapons.Size(); }
int LocateWeapon (PClassActor *type);
@ -55,10 +54,9 @@ struct FWeaponSlots
FWeaponSlots() { Clear(); }
FWeaponSlots(const FWeaponSlots &other);
private:
FWeaponSlot Slots[NUM_WEAPON_SLOTS];
AWeapon *PickNextWeapon (player_t *player);
AWeapon *PickPrevWeapon (player_t *player);
public:
void Clear ();
bool LocateWeapon (PClassActor *type, int *const slot, int *const index);
@ -71,10 +69,53 @@ struct FWeaponSlots
void SendDifferences(int playernum, const FWeaponSlots &other);
int RestoreSlots (FConfigFile *config, const char *section);
void PrintSettings();
static void SetupWeaponSlots(APlayerPawn *pp);
void AddSlot(int slot, PClassActor *type, bool feedback);
void AddSlotDefault(int slot, PClassActor *type, bool feedback);
// Abstract access interface to the slots
void AddWeapon(int slot, PClassActor *type)
{
if (slot >= 0 && slot < NUM_WEAPON_SLOTS)
{
Slots[slot].AddWeapon(type);
}
}
void AddWeapon(int slot, const char *type)
{
if (slot >= 0 && slot < NUM_WEAPON_SLOTS)
{
Slots[slot].AddWeapon(type);
}
}
void ClearSlot(int slot)
{
if (slot >= 0 && slot < NUM_WEAPON_SLOTS)
{
Slots[slot].Weapons.Clear();
}
}
int SlotSize(int slot) const
{
if (slot >= 0 && slot < NUM_WEAPON_SLOTS)
{
return Slots[slot].Weapons.Size();
}
return 0;
}
PClassActor *GetWeapon(int slot, int index) const
{
if (slot >= 0 && slot < NUM_WEAPON_SLOTS && (unsigned)index < Slots[slot].Weapons.Size())
{
return Slots[slot].GetWeapon(index);
}
return nullptr;
}
};
void P_PlaybackKeyConfWeapons(FWeaponSlots *slots);
@ -86,74 +127,14 @@ void P_WriteDemoWeaponsChunk(uint8_t **demo);
void P_ReadDemoWeaponsChunk(uint8_t **demo);
class AWeapon : public AStateProvider
enum class EBobStyle
{
DECLARE_CLASS(AWeapon, AStateProvider)
HAS_OBJECT_POINTERS
public:
uint32_t WeaponFlags;
PClassActor *AmmoType1, *AmmoType2; // Types of ammo used by this weapon
int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon
int MinAmmo1, MinAmmo2; // Minimum ammo needed to switch to this weapon
int AmmoUse1, AmmoUse2; // How much ammo to use with each shot
int Kickback;
float YAdjust; // For viewing the weapon fullscreen (visual only so no need to be a double)
FSoundIDNoInit UpSound, ReadySound; // Sounds when coming up and idle
PClassActor *SisterWeaponType; // Another weapon to pick up with this one
PClassActor *ProjectileType; // Projectile used by primary attack
PClassActor *AltProjectileType; // Projectile used by alternate attack
int SelectionOrder; // Lower-numbered weapons get picked first
int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo
double MoveCombatDist; // Used by bots, but do they *really* need it?
int ReloadCounter; // For A_CheckForReload
int BobStyle; // [XA] Bobbing style. Defines type of bobbing (e.g. Normal, Alpha) (visual only so no need to be a double)
float BobSpeed; // [XA] Bobbing speed. Defines how quickly a weapon bobs.
float BobRangeX, BobRangeY; // [XA] Bobbing range. Defines how far a weapon bobs in either direction.
int SlotNumber;
int SlotPriority;
// In-inventory instance variables
TObjPtr<AInventory*> Ammo1, Ammo2;
TObjPtr<AWeapon*> SisterWeapon;
float FOVScale;
int Crosshair; // 0 to use player's crosshair
bool GivenAsMorphWeapon;
bool bAltFire; // Set when this weapon's alternate fire is used.
virtual void MarkPrecacheSounds() const;
void Finalize(FStateDefinitions &statedef) override;
void Serialize(FSerializer &arc) override;
void PostMorphWeapon();
// scripted virtuals.
FState *GetUpState ();
FState *GetDownState ();
FState *GetReadyState ();
enum
{
PrimaryFire,
AltFire,
EitherFire
};
bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1);
bool DoCheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int ammocount);
bool DepleteAmmo (bool altFire, bool checkEnough=true, int ammouse = -1);
bool DoDepleteAmmo(bool altFire, bool checkEnough, int ammouse);
enum
{
BobNormal,
BobInverse,
BobAlpha,
BobInverseAlpha,
BobSmooth,
BobInverseSmooth
};
BobNormal,
BobInverse,
BobAlpha,
BobInverseAlpha,
BobSmooth,
BobInverseSmooth
};
enum
@ -174,16 +155,9 @@ enum
WIF_STAFF2_KICKBACK = 0x00002000, // the powered-up Heretic staff has special kickback
WIF_NOAUTOAIM = 0x00004000, // this weapon never uses autoaim (useful for ballistic projectiles)
WIF_MELEEWEAPON = 0x00008000, // melee weapon. Used by bots and monster AI.
WIF_DEHAMMO = 0x00010000, // Uses Doom's original amount of ammo for the respective attack functions so that old DEHACKED patches work as intended.
// AmmoUse1 will be set to the first attack's ammo use so that checking for empty weapons still works
//WIF_DEHAMMO = 0x00010000,
WIF_NODEATHDESELECT = 0x00020000, // Don't jump to the Deselect state when the player dies
WIF_NODEATHINPUT = 0x00040000, // The weapon cannot be fired/reloaded/whatever when the player is dead
WIF_CHEATNOTWEAPON = 0x08000000, // Give cheat considers this not a weapon (used by Sigil)
// Flags used only by bot AI:
WIF_BOT_REACTION_SKILL_THING = 1<<31, // I don't understand this
WIF_BOT_EXPLOSIVE = 1<<30, // weapon fires an explosive
WIF_BOT_BFG = 1<<28, // this is a BFG
};

View file

@ -1513,6 +1513,7 @@ void G_InitLevelLocals ()
level.fogdensity = info->fogdensity;
level.outsidefogdensity = info->outsidefogdensity;
level.skyfog = info->skyfog;
level.deathsequence = info->deathsequence;
level.pixelstretch = info->pixelstretch;
@ -2238,6 +2239,7 @@ DEFINE_FIELD(FLevelLocals, fogdensity)
DEFINE_FIELD(FLevelLocals, outsidefogdensity)
DEFINE_FIELD(FLevelLocals, skyfog)
DEFINE_FIELD(FLevelLocals, pixelstretch)
DEFINE_FIELD(FLevelLocals, deathsequence)
DEFINE_FIELD_BIT(FLevelLocals, flags, noinventorybar, LEVEL_NOINVENTORYBAR)
DEFINE_FIELD_BIT(FLevelLocals, flags, monsterstelefrag, LEVEL_MONSTERSTELEFRAG)
DEFINE_FIELD_BIT(FLevelLocals, flags, actownspecial, LEVEL_ACTOWNSPECIAL)
@ -2304,3 +2306,12 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, ChangeSky)
R_InitSkyMap();
return 0;
}
DEFINE_ACTION_FUNCTION(FLevelLocals, StartIntermission)
{
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
PARAM_NAME(seq);
PARAM_INT(state);
F_StartIntermission(seq, (uint8_t)state);
return 0;
}

View file

@ -175,6 +175,7 @@ struct FLevelLocals
int outsidefogdensity;
int skyfog;
FName deathsequence;
float pixelstretch;
float MusicVolume;

View file

@ -1,9 +1,6 @@
//-----------------------------------------------------------------------------
//
// Copyright 1994-1996 Raven Software
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2016 Christoph Oelckers
// Copyright 2005-2008 Martin Howe
// Copyright 2018 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -38,711 +35,31 @@
static FRandom pr_morphmonst ("MorphMonster");
void EndAllPowerupEffects(AInventory *item);
void InitAllPowerupEffects(AInventory *item);
//---------------------------------------------------------------------------
//
// FUNC P_MorphPlayer
//
// Returns true if the player gets turned into a chicken/pig.
//
// TODO: Allow morphed players to receive weapon sets (not just one weapon),
// since they have their own weapon slots now.
//
//---------------------------------------------------------------------------
bool P_MorphPlayer (player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash)
bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassActor *mtype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash)
{
AInventory *item;
APlayerPawn *morphed;
APlayerPawn *actor;
actor = p->mo;
if (actor == nullptr)
IFVIRTUALPTR(victim, AActor, Morph)
{
return false;
VMValue params[] = { victim, activator, ptype, mtype, duration, style, enter_flash, exit_flash };
int retval;
VMReturn ret(&retval);
VMCall(func, params, countof(params), &ret, 1);
return !!retval;
}
if (actor->flags3 & MF3_DONTMORPH)
{
return false;
}
if ((p->mo->flags2 & MF2_INVULNERABLE) && ((p != activator) || (!(style & MORPH_WHENINVULNERABLE))))
{ // Immune when invulnerable unless this is a power we activated
return false;
}
if (p->morphTics)
{ // Player is already a beast
if ((p->mo->GetClass() == spawntype)
&& (p->mo->PlayerFlags & PPF_CANSUPERMORPH)
&& (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE))
&& (p->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true) == nullptr))
{ // Make a super chicken
p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2));
}
return false;
}
if (p->health <= 0)
{ // Dead players cannot morph
return false;
}
if (spawntype == nullptr)
{
return false;
}
if (!spawntype->IsDescendantOf (RUNTIME_CLASS(APlayerPawn)))
{
return false;
}
if (spawntype == p->mo->GetClass())
{
return false;
}
morphed = static_cast<APlayerPawn *>(Spawn (spawntype, actor->Pos(), NO_REPLACE));
EndAllPowerupEffects(actor->Inventory);
DObject::StaticPointerSubstitution (actor, morphed);
if ((style & MORPH_TRANSFERTRANSLATION) && !(morphed->flags2 & MF2_DONTTRANSLATE))
{
morphed->Translation = actor->Translation;
}
if ((actor->tid != 0) && (style & MORPH_NEWTIDBEHAVIOUR))
{
morphed->tid = actor->tid;
morphed->AddToHash ();
actor->RemoveFromHash ();
actor->tid = 0;
}
morphed->Angles.Yaw = actor->Angles.Yaw;
morphed->target = actor->target;
morphed->tracer = actor->tracer;
morphed->alternative = actor;
morphed->FriendPlayer = actor->FriendPlayer;
morphed->DesignatedTeam = actor->DesignatedTeam;
morphed->Score = actor->Score;
p->PremorphWeapon = p->ReadyWeapon;
morphed->special2 = actor->flags & ~MF_JUSTHIT;
morphed->player = p;
if (actor->renderflags & RF_INVISIBLE)
{
morphed->special2 |= MF_JUSTHIT;
}
if (morphed->ViewHeight > p->viewheight && p->deltaviewheight == 0)
{ // If the new view height is higher than the old one, start moving toward it.
p->deltaviewheight = p->GetDeltaViewHeight();
}
morphed->flags |= actor->flags & (MF_SHADOW|MF_NOGRAVITY);
morphed->flags2 |= actor->flags2 & MF2_FLY;
morphed->flags3 |= actor->flags3 & MF3_GHOST;
AActor *eflash = Spawn(((enter_flash) ? enter_flash : PClass::FindActor("TeleportFog")), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
actor->player = nullptr;
actor->alternative = morphed;
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
actor->flags |= MF_UNMORPHED;
actor->renderflags |= RF_INVISIBLE;
p->morphTics = (duration) ? duration : MORPHTICS;
// [MH] Used by SBARINFO to speed up face drawing
p->MorphedPlayerClass = spawntype;
p->MorphStyle = style;
p->MorphExitFlash = (exit_flash) ? exit_flash : PClass::FindActor("TeleportFog");
p->health = morphed->health;
p->mo = morphed;
p->Vel.X = p->Vel.Y = 0;
morphed->ObtainInventory (actor);
// Remove all armor
for (item = morphed->Inventory; item != nullptr; )
{
AInventory *next = item->Inventory;
if (item->IsKindOf (PClass::FindActor(NAME_Armor)))
{
item->DepleteOrDestroy();
}
item = next;
}
InitAllPowerupEffects(morphed->Inventory);
morphed->ActivateMorphWeapon ();
if (p->camera == actor)
{
p->camera = morphed;
}
morphed->ScoreIcon = actor->ScoreIcon; // [GRB]
if (eflash)
eflash->target = p->mo;
return true;
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, MorphPlayer)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_POINTER(activator, player_t);
PARAM_CLASS(spawntype, APlayerPawn);
PARAM_INT(duration);
PARAM_INT(style);
PARAM_CLASS(enter_flash, AActor);
PARAM_CLASS(exit_flash, AActor);
ACTION_RETURN_BOOL(P_MorphPlayer(activator, self, spawntype, duration, style, enter_flash, exit_flash));
}
//----------------------------------------------------------------------------
//
// FUNC P_UndoPlayerMorph
//
//----------------------------------------------------------------------------
bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, bool force)
{
AWeapon *beastweap;
APlayerPawn *mo;
APlayerPawn *pmo;
pmo = player->mo;
// [MH]
// Checks pmo as well; the PowerMorph destroyer will
// try to unmorph the player; if the destroyer runs
// because the level or game is ended while morphed,
// by the time it gets executed the morphed player
// pawn instance may have already been destroyed.
if (pmo == nullptr || pmo->alternative == nullptr)
{
return false;
}
bool DeliberateUnmorphIsOkay = !!(MORPH_STANDARDUNDOING & unmorphflag);
if ((pmo->flags2 & MF2_INVULNERABLE) // If the player is invulnerable
&& ((player != activator) // and either did not decide to unmorph,
|| (!((player->MorphStyle & MORPH_WHENINVULNERABLE) // or the morph style does not allow it
|| (DeliberateUnmorphIsOkay))))) // (but standard morph styles always allow it),
{ // Then the player is immune to the unmorph.
return false;
}
mo = barrier_cast<APlayerPawn *>(pmo->alternative);
mo->SetOrigin (pmo->Pos(), false);
mo->flags |= MF_SOLID;
pmo->flags &= ~MF_SOLID;
if (!force && !P_TestMobjLocation (mo))
{ // Didn't fit
mo->flags &= ~MF_SOLID;
pmo->flags |= MF_SOLID;
player->morphTics = 2*TICRATE;
return false;
}
// No longer using tracer as morph storage. That is what 'alternative' is for.
// If the tracer has changed on the morph, change the original too.
mo->target = pmo->target;
mo->tracer = pmo->tracer;
pmo->player = nullptr;
mo->alternative = pmo->alternative = nullptr;
// Remove the morph power if the morph is being undone prematurely.
auto pmtype = PClass::FindActor("PowerMorph");
for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next)
{
next = item->Inventory;
if (item->IsKindOf(pmtype))
{
item->Destroy();
}
}
EndAllPowerupEffects(pmo->Inventory);
mo->ObtainInventory (pmo);
DObject::StaticPointerSubstitution (pmo, mo);
if ((pmo->tid != 0) && (player->MorphStyle & MORPH_NEWTIDBEHAVIOUR))
{
mo->tid = pmo->tid;
mo->AddToHash ();
}
mo->Angles.Yaw = pmo->Angles.Yaw;
mo->player = player;
mo->reactiontime = 18;
mo->flags = ActorFlags::FromInt (pmo->special2) & ~MF_JUSTHIT;
mo->Vel.X = mo->Vel.Y = 0;
player->Vel.Zero();
mo->Vel.Z = pmo->Vel.Z;
mo->floorz = pmo->floorz;
if (!(pmo->special2 & MF_JUSTHIT))
{
mo->renderflags &= ~RF_INVISIBLE;
}
mo->flags = (mo->flags & ~(MF_SHADOW|MF_NOGRAVITY)) | (pmo->flags & (MF_SHADOW|MF_NOGRAVITY));
mo->flags2 = (mo->flags2 & ~MF2_FLY) | (pmo->flags2 & MF2_FLY);
mo->flags3 = (mo->flags3 & ~MF3_GHOST) | (pmo->flags3 & MF3_GHOST);
mo->Score = pmo->Score;
InitAllPowerupEffects(mo->Inventory);
PClassActor *exit_flash = player->MorphExitFlash;
bool correctweapon = !!(player->MorphStyle & MORPH_LOSEACTUALWEAPON);
bool undobydeathsaves = !!(player->MorphStyle & MORPH_UNDOBYDEATHSAVES);
player->morphTics = 0;
player->MorphedPlayerClass = 0;
player->MorphStyle = 0;
player->MorphExitFlash = nullptr;
player->viewheight = mo->ViewHeight;
AInventory *level2 = mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true);
if (level2 != nullptr)
{
level2->Destroy ();
}
if ((player->health > 0) || undobydeathsaves)
{
player->health = mo->health = mo->SpawnHealth();
}
else // killed when morphed so stay dead
{
mo->health = player->health;
}
player->mo = mo;
if (player->camera == pmo)
{
player->camera = mo;
}
// [MH]
// If the player that was morphed is the one
// taking events, reset up the face, if any;
// this is only needed for old-skool skins
// and for the original DOOM status bar.
if (player == &players[consoleplayer])
{
FName face = pmo->Face;
if (face != NAME_None)
{
// Assume root-level base skin to begin with
size_t skinindex = 0;
// If a custom skin was in use, then reload it
// or else the base skin for the player class.
if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
(unsigned)player->userinfo.GetSkin() < Skins.Size())
{
skinindex = player->userinfo.GetSkin();
}
else if (PlayerClasses.Size () > 1)
{
const PClass *whatami = player->mo->GetClass();
for (unsigned int i = 0; i < PlayerClasses.Size (); ++i)
{
if (PlayerClasses[i].Type == whatami)
{
skinindex = i;
break;
}
}
}
}
}
AActor *eflash = nullptr;
if (exit_flash != nullptr)
{
eflash = Spawn(exit_flash, pmo->Vec3Angle(20., mo->Angles.Yaw, TELEFOGHEIGHT), ALLOW_REPLACE);
if (eflash) eflash->target = mo;
}
mo->SetupWeaponSlots(); // Use original class's weapon slots.
beastweap = player->ReadyWeapon;
if (player->PremorphWeapon != nullptr)
{
player->PremorphWeapon->PostMorphWeapon ();
}
else
{
player->ReadyWeapon = player->PendingWeapon = nullptr;
}
if (correctweapon)
{ // Better "lose morphed weapon" semantics
PClassActor *morphweapon = PClass::FindActor(pmo->MorphWeapon);
if (morphweapon != nullptr && morphweapon->IsDescendantOf(NAME_Weapon))
{
AWeapon *OriginalMorphWeapon = static_cast<AWeapon *>(mo->FindInventory (morphweapon));
if ((OriginalMorphWeapon != nullptr) && (OriginalMorphWeapon->GivenAsMorphWeapon))
{ // You don't get to keep your morphed weapon.
if (OriginalMorphWeapon->SisterWeapon != nullptr)
{
OriginalMorphWeapon->SisterWeapon->Destroy ();
}
OriginalMorphWeapon->Destroy ();
}
}
}
else // old behaviour (not really useful now)
{ // Assumptions made here are no longer valid
if (beastweap != nullptr)
{ // You don't get to keep your morphed weapon.
if (beastweap->SisterWeapon != nullptr)
{
beastweap->SisterWeapon->Destroy ();
}
beastweap->Destroy ();
}
}
pmo->Destroy ();
// Restore playerclass armor to its normal amount.
auto hxarmor = mo->FindInventory(NAME_HexenArmor);
if (hxarmor != nullptr)
{
double *Slots = (double*)hxarmor->ScriptVar(NAME_Slots, nullptr);
Slots[4] = mo->HexenArmor[0];
}
return true;
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, UndoPlayerMorph)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_POINTER_NOT_NULL(player, player_t);
PARAM_INT(unmorphflag);
PARAM_BOOL(force);
ACTION_RETURN_BOOL(P_UndoPlayerMorph(self, player, unmorphflag, force));
}
//---------------------------------------------------------------------------
//
// FUNC P_MorphMonster
//
// Returns true if the monster gets turned into a chicken/pig.
//
//---------------------------------------------------------------------------
bool P_MorphMonster (AActor *actor, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash)
{
AMorphedMonster *morphed;
if (actor == NULL || actor->player || spawntype == NULL ||
actor->flags3 & MF3_DONTMORPH ||
!(actor->flags3 & MF3_ISMONSTER) ||
!spawntype->IsDescendantOf (PClass::FindActor(NAME_MorphedMonster)))
{
return false;
}
morphed = static_cast<AMorphedMonster *>(Spawn (spawntype, actor->Pos(), NO_REPLACE));
DObject::StaticPointerSubstitution (actor, morphed);
if ((style & MORPH_TRANSFERTRANSLATION) && !(morphed->flags2 & MF2_DONTTRANSLATE))
{
morphed->Translation = actor->Translation;
}
morphed->tid = actor->tid;
morphed->Angles.Yaw = actor->Angles.Yaw;
morphed->UnmorphedMe = actor;
morphed->Alpha = actor->Alpha;
morphed->RenderStyle = actor->RenderStyle;
morphed->Score = actor->Score;
morphed->UnmorphTime = level.time + ((duration) ? duration : MORPHTICS) + pr_morphmonst();
morphed->MorphStyle = style;
morphed->MorphExitFlash = (exit_flash) ? exit_flash : PClass::FindActor("TeleportFog");
morphed->FlagsSave = actor->flags & ~MF_JUSTHIT;
morphed->special = actor->special;
memcpy (morphed->args, actor->args, sizeof(actor->args));
morphed->CopyFriendliness (actor, true);
morphed->flags |= actor->flags & MF_SHADOW;
morphed->flags3 |= actor->flags3 & MF3_GHOST;
if (actor->renderflags & RF_INVISIBLE)
{
morphed->FlagsSave |= MF_JUSTHIT;
}
morphed->AddToHash ();
actor->RemoveFromHash ();
actor->special = 0;
actor->tid = 0;
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
actor->flags |= MF_UNMORPHED;
actor->renderflags |= RF_INVISIBLE;
AActor *eflash = Spawn(((enter_flash) ? enter_flash : PClass::FindActor("TeleportFog")), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
if (eflash)
eflash->target = morphed;
return true;
}
DEFINE_ACTION_FUNCTION(AActor, MorphMonster)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(spawntype, AActor);
PARAM_INT(duration);
PARAM_INT(style);
PARAM_CLASS(enter_flash, AActor);
PARAM_CLASS(exit_flash, AActor);
ACTION_RETURN_BOOL(P_MorphMonster(self, spawntype, duration, style, enter_flash, exit_flash));
}
//----------------------------------------------------------------------------
//
// FUNC P_UndoMonsterMorph
//
// Returns true if the monster unmorphs.
//
//----------------------------------------------------------------------------
bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force)
{
AActor *actor;
if (beast->UnmorphTime == 0 ||
beast->UnmorphedMe == NULL ||
beast->flags3 & MF3_STAYMORPHED ||
beast->UnmorphedMe->flags3 & MF3_STAYMORPHED)
{
return false;
}
actor = beast->UnmorphedMe;
actor->SetOrigin (beast->Pos(), false);
actor->flags |= MF_SOLID;
beast->flags &= ~MF_SOLID;
ActorFlags6 beastflags6 = beast->flags6;
beast->flags6 &= ~MF6_TOUCHY;
if (!force && !P_TestMobjLocation (actor))
{ // Didn't fit
actor->flags &= ~MF_SOLID;
beast->flags |= MF_SOLID;
beast->flags6 = beastflags6;
beast->UnmorphTime = level.time + 5*TICRATE; // Next try in 5 seconds
return false;
}
actor->Angles.Yaw = beast->Angles.Yaw;
actor->target = beast->target;
actor->FriendPlayer = beast->FriendPlayer;
actor->flags = beast->FlagsSave & ~MF_JUSTHIT;
actor->flags = (actor->flags & ~(MF_FRIENDLY|MF_SHADOW)) | (beast->flags & (MF_FRIENDLY|MF_SHADOW));
actor->flags3 = (actor->flags3 & ~(MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST))
| (beast->flags3 & (MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST));
actor->flags4 = (actor->flags4 & ~MF4_NOHATEPLAYERS) | (beast->flags4 & MF4_NOHATEPLAYERS);
if (!(beast->FlagsSave & MF_JUSTHIT))
actor->renderflags &= ~RF_INVISIBLE;
actor->health = actor->SpawnHealth();
actor->Vel = beast->Vel;
actor->tid = beast->tid;
actor->special = beast->special;
actor->Score = beast->Score;
memcpy (actor->args, beast->args, sizeof(actor->args));
actor->AddToHash ();
beast->UnmorphedMe = NULL;
DObject::StaticPointerSubstitution (beast, actor);
PClassActor *exit_flash = beast->MorphExitFlash;
beast->Destroy ();
AActor *eflash = Spawn(exit_flash, beast->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
if (eflash)
eflash->target = actor;
return true;
}
//----------------------------------------------------------------------------
//
// FUNC P_UpdateMorphedMonster
//
// Returns true if the monster unmorphs.
//
//----------------------------------------------------------------------------
bool P_UpdateMorphedMonster (AMorphedMonster *beast)
{
if (beast->UnmorphTime > level.time)
{
return false;
}
return P_UndoMonsterMorph (beast);
}
//----------------------------------------------------------------------------
//
// FUNC P_MorphedDeath
//
// Unmorphs the actor if possible.
// Returns the unmorphed actor, the style with which they were morphed and the
// health (of the AActor, not the player_t) they last had before unmorphing.
//
//----------------------------------------------------------------------------
bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth)
{
// May be a morphed player
if ((actor->player) &&
(actor->player->morphTics) &&
(actor->player->MorphStyle & MORPH_UNDOBYDEATH) &&
(actor->player->mo) &&
(actor->player->mo->alternative))
{
AActor *realme = actor->player->mo->alternative;
int realstyle = actor->player->MorphStyle;
int realhealth = actor->health;
if (P_UndoPlayerMorph(actor->player, actor->player, 0, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
{
*morphed = realme;
*morphedstyle = realstyle;
*morphedhealth = realhealth;
return true;
}
return false;
}
// May be a morphed monster
if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
{
AMorphedMonster *fakeme = static_cast<AMorphedMonster *>(actor);
AActor *realme = fakeme->UnmorphedMe;
if (realme != NULL)
{
if ((fakeme->UnmorphTime) &&
(fakeme->MorphStyle & MORPH_UNDOBYDEATH))
{
int realstyle = fakeme->MorphStyle;
int realhealth = fakeme->health;
if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
{
*morphed = realme;
*morphedstyle = realstyle;
*morphedhealth = realhealth;
return true;
}
}
if (realme->flags4 & MF4_BOSSDEATH)
{
realme->health = 0; // make sure that A_BossDeath considers it dead.
A_BossDeath(realme);
}
}
fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die()
return false;
}
// Not a morphed player or monster
return false;
}
//===========================================================================
//
// EndAllPowerupEffects
//
// Calls EndEffect() on every Powerup in the inventory list.
//
//===========================================================================
void EndAllPowerupEffects(AInventory *item)
bool P_UnmorphActor(AActor *activator, AActor *morphed, int flags, bool force)
{
auto ptype = PClass::FindActor(NAME_Powerup);
while (item != NULL)
IFVIRTUALPTR(morphed, AActor, UnMorph)
{
if (item->IsKindOf(ptype))
{
IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect)
{
VMValue params[1] = { item };
VMCall(func, params, 1, nullptr, 0);
}
}
item = item->Inventory;
}
}
//===========================================================================
//
// InitAllPowerupEffects
//
// Calls InitEffect() on every Powerup in the inventory list.
//
//===========================================================================
void InitAllPowerupEffects(AInventory *item)
{
auto ptype = PClass::FindActor(NAME_Powerup);
while (item != NULL)
{
if (item->IsKindOf(ptype))
{
IFVIRTUALPTRNAME(item, NAME_Powerup, InitEffect)
{
VMValue params[1] = { item };
VMCall(func, params, 1, nullptr, 0);
}
}
item = item->Inventory;
}
}
// Morphed Monster (you must subclass this to do something useful) ---------
IMPLEMENT_CLASS(AMorphedMonster, false, true)
IMPLEMENT_POINTERS_START(AMorphedMonster)
IMPLEMENT_POINTER(UnmorphedMe)
IMPLEMENT_POINTERS_END
DEFINE_FIELD(AMorphedMonster, UnmorphedMe)
DEFINE_FIELD(AMorphedMonster, UnmorphTime)
DEFINE_FIELD(AMorphedMonster, MorphStyle)
DEFINE_FIELD(AMorphedMonster, MorphExitFlash)
void AMorphedMonster::Serialize(FSerializer &arc)
{
Super::Serialize (arc);
arc("unmorphedme", UnmorphedMe)
("unmorphtime", UnmorphTime)
("morphstyle", MorphStyle)
("morphexitflash", MorphExitFlash)
("flagsave", FlagsSave);
}
void AMorphedMonster::OnDestroy ()
{
if (UnmorphedMe != NULL)
{
UnmorphedMe->Destroy ();
}
Super::OnDestroy();
}
void AMorphedMonster::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath)
{
// Dead things don't unmorph
// flags3 |= MF3_STAYMORPHED;
// [MH]
// But they can now, so that line above has been
// moved into P_MorphedDeath() and is now set by
// that function if and only if it is needed.
Super::Die (source, inflictor, dmgflags, MeansOfDeath);
if (UnmorphedMe != NULL && (UnmorphedMe->flags & MF_UNMORPHED))
{
UnmorphedMe->health = health;
UnmorphedMe->CallDie (source, inflictor, dmgflags, MeansOfDeath);
}
}
void AMorphedMonster::Tick ()
{
if (!P_UpdateMorphedMonster (this))
{
Super::Tick ();
VMValue params[] = { morphed, activator, flags, force };
int retval;
VMReturn ret(&retval);
VMCall(func, params, countof(params), &ret, 1);
return !!retval;
}
return false;
}
DEFINE_ACTION_FUNCTION(AActor, A_Morph)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS(type, AActor);
PARAM_INT(duration);
PARAM_INT(flags);
PARAM_CLASS(enter_flash, AActor);
PARAM_CLASS(exit_flash, AActor);
bool res = false;
if (self->player)
{
if (type->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)))
{
res = P_MorphPlayer(self->player, self->player, type, duration, flags, enter_flash, exit_flash);
}
}
else
{
if (type->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
{
res = P_MorphMonster(self, type, duration, flags, enter_flash, exit_flash);
}
}
ACTION_RETURN_BOOL(res);
}

View file

@ -33,15 +33,8 @@ enum
class PClass;
class AActor;
class player_t;
class AMorphedMonster;
bool P_MorphPlayer (player_t *activator, player_t *player, PClassActor *morphclass, int duration = 0, int style = 0,
PClassActor *enter_flash = NULL, PClassActor *exit_flash = NULL);
bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag = 0, bool force = false);
bool P_MorphMonster (AActor *actor, PClassActor *morphclass, int duration = 0, int style = 0,
PClassActor *enter_flash = NULL, PClassActor *exit_flash = NULL);
bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force = false);
bool P_UpdateMorphedMonster (AActor *actor);
bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth);
bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassActor *mtype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash);
bool P_UnmorphActor(AActor *activator, AActor *morphed, int flags = 0, bool force = false);
#endif //__A_MORPH__

View file

@ -154,21 +154,4 @@ private:
DEarthquake ();
};
class AMorphedMonster : public AActor
{
DECLARE_CLASS (AMorphedMonster, AActor)
HAS_OBJECT_POINTERS
public:
void Tick ();
void Serialize(FSerializer &arc);
void Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) override;
void OnDestroy() override;
TObjPtr<AActor*> UnmorphedMe;
int UnmorphTime, MorphStyle;
PClassActor *MorphExitFlash;
ActorFlags FlagsSave;
};
#endif //__A_SHAREDGLOBAL_H__

View file

@ -507,12 +507,12 @@ static int DrawKeys(player_t * CPlayer, int x, int y)
//---------------------------------------------------------------------------
static TArray<PClassActor *> orderedammos;
static void AddAmmoToList(AWeapon * weapdef)
static void AddAmmoToList(AInventory * weapdef)
{
for (int i = 0; i < 2; i++)
{
auto ti = i == 0 ? weapdef->AmmoType1 : weapdef->AmmoType2;
auto ti = weapdef->PointerVar<PClassActor>(i == 0 ? NAME_AmmoType1 : NAME_AmmoType2);
if (ti)
{
auto ammodef = (AInventory*)GetDefaultByType(ti);
@ -570,7 +570,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y)
char buf[256];
AInventory *inv;
AWeapon *wi=CPlayer->ReadyWeapon;
auto wi=CPlayer->ReadyWeapon;
orderedammos.Clear();
@ -582,9 +582,9 @@ static int DrawAmmo(player_t *CPlayer, int x, int y)
else
{
// Order ammo by use of weapons in the weapon slots
for (k = 0; k < NUM_WEAPON_SLOTS; k++) for(j = 0; j < CPlayer->weapons.Slots[k].Size(); j++)
for (k = 0; k < NUM_WEAPON_SLOTS; k++) for(j = 0; j < CPlayer->weapons.SlotSize(k); j++)
{
PClassActor *weap = CPlayer->weapons.Slots[k].GetWeapon(j);
PClassActor *weap = CPlayer->weapons.GetWeapon(k, j);
if (weap)
{
@ -593,7 +593,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y)
if (hud_showammo > 1 || CPlayer->mo->FindInventory(weap))
{
AddAmmoToList((AWeapon*)GetDefaultByType(weap));
AddAmmoToList((AInventory*)GetDefaultByType(weap));
}
}
}
@ -603,7 +603,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y)
{
if (inv->IsKindOf(NAME_Weapon))
{
AddAmmoToList((AWeapon*)inv);
AddAmmoToList(inv);
}
}
}
@ -643,7 +643,7 @@ static int DrawAmmo(player_t *CPlayer, int x, int y)
FTextureID icon = !AltIcon.isNull()? AltIcon : inv->Icon;
if (!icon.isValid()) continue;
double trans= (wi && (type==wi->AmmoType1 || type==wi->AmmoType2)) ? 0.75 : 0.375;
double trans= (wi && (type==wi->PointerVar<PClassActor>(NAME_AmmoType1) || type==wi->PointerVar<PClassActor>(NAME_AmmoType2))) ? 0.75 : 0.375;
int maxammo = inv->MaxAmount;
int ammo = ammoitem? ammoitem->Amount : 0;
@ -736,18 +736,19 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetInventoryIcon)
return MIN(numret, 2);
}
static void DrawOneWeapon(player_t * CPlayer, int x, int & y, AWeapon * weapon)
static void DrawOneWeapon(player_t * CPlayer, int x, int & y, AInventory * weapon)
{
double trans;
// Powered up weapons and inherited sister weapons are not displayed.
if (weapon->WeaponFlags & WIF_POWERED_UP) return;
if (weapon->SisterWeapon && weapon->IsKindOf(weapon->SisterWeapon->GetClass())) return;
if (weapon->IntVar(NAME_WeaponFlags) & WIF_POWERED_UP) return;
auto SisterWeapon = weapon->PointerVar<AInventory>(NAME_SisterWeapon);
if (SisterWeapon && weapon->IsKindOf(SisterWeapon->GetClass())) return;
trans=0.4;
if (CPlayer->ReadyWeapon)
{
if (weapon==CPlayer->ReadyWeapon || weapon==CPlayer->ReadyWeapon->SisterWeapon) trans = 0.85;
if (weapon==CPlayer->ReadyWeapon || SisterWeapon == CPlayer->ReadyWeapon) trans = 0.85;
}
FTextureID picnum = GetInventoryIcon(weapon, DI_ALTICONFIRST);
@ -775,22 +776,22 @@ static void DrawWeapons(player_t *CPlayer, int x, int y)
for(inv = CPlayer->mo->Inventory; inv; inv = inv->Inventory)
{
if (inv->IsKindOf(NAME_Weapon) &&
!CPlayer->weapons.LocateWeapon(static_cast<AWeapon*>(inv)->GetClass(), NULL, NULL))
!CPlayer->weapons.LocateWeapon(inv->GetClass(), NULL, NULL))
{
DrawOneWeapon(CPlayer, x, y, static_cast<AWeapon*>(inv));
DrawOneWeapon(CPlayer, x, y, inv);
}
}
// And now everything in the weapon slots back to front
for (k = NUM_WEAPON_SLOTS - 1; k >= 0; k--) for(j = CPlayer->weapons.Slots[k].Size() - 1; j >= 0; j--)
for (k = NUM_WEAPON_SLOTS - 1; k >= 0; k--) for(j = CPlayer->weapons.SlotSize(k) - 1; j >= 0; j--)
{
PClassActor *weap = CPlayer->weapons.Slots[k].GetWeapon(j);
PClassActor *weap = CPlayer->weapons.GetWeapon(k, j);
if (weap)
{
inv=CPlayer->mo->FindInventory(weap);
if (inv)
{
DrawOneWeapon(CPlayer, x, y, static_cast<AWeapon*>(inv));
DrawOneWeapon(CPlayer, x, y, inv);
}
}
}

View file

@ -52,8 +52,6 @@ enum EHudState
HUD_AltHud // Used for passing through popups to the alt hud
};
class AWeapon;
bool ST_IsTimeVisible();
bool ST_IsLatencyVisible();

View file

@ -1039,22 +1039,22 @@ public:
}
wrapper->ForceHUDScale(script->huds[hud]->ForceScaled());
if (CPlayer->ReadyWeapon != NULL)
if (CPlayer->ReadyWeapon != nullptr)
{
ammo1 = CPlayer->ReadyWeapon->Ammo1;
ammo2 = CPlayer->ReadyWeapon->Ammo2;
if (ammo1 == NULL)
ammo1 = CPlayer->ReadyWeapon->PointerVar<AInventory>(NAME_Ammo1);
ammo2 = CPlayer->ReadyWeapon->PointerVar<AInventory>(NAME_Ammo2);
if (ammo1 == nullptr)
{
ammo1 = ammo2;
ammo2 = NULL;
ammo2 = nullptr;
}
}
else
{
ammo1 = ammo2 = NULL;
ammo1 = ammo2 = nullptr;
}
ammocount1 = ammo1 != NULL ? ammo1->Amount : 0;
ammocount2 = ammo2 != NULL ? ammo2->Amount : 0;
ammocount1 = ammo1 != nullptr ? ammo1->Amount : 0;
ammocount2 = ammo2 != nullptr ? ammo2->Amount : 0;
//prepare ammo counts
armor = CPlayer->mo->FindInventory(NAME_BasicArmor);
@ -1467,6 +1467,12 @@ public:
return TRANSLATION(TRANSLATION_Players, int(CPlayer - players));
}
PClassActor *AmmoType(int no) const
{
auto w = StatusBar->CPlayer->ReadyWeapon;
return w == nullptr ? nullptr : (w->PointerVar<PClassActor>(no == 1 ? NAME_AmmoType1 : NAME_AmmoType2));
}
AInventory *ammo1, *ammo2;
int ammocount1, ammocount2;
AInventory *armor;

View file

@ -245,7 +245,7 @@ class CommandDrawImage : public SBarInfoCommandFlowControl
else if(type == AMMO1)
{
auto ammo = statusBar->ammo1;
if(ammo != NULL)
if(ammo != NULL)
GetIcon(ammo);
}
else if(type == AMMO2)
@ -527,9 +527,9 @@ class CommandDrawSwitchableImage : public CommandDrawImage
if(condition == WEAPONSLOT) //weaponslots
{
drawAlt = 1; //draw off state until we know we have something.
for (int i = 0; i < statusBar->CPlayer->weapons.Slots[conditionalValue[0]].Size(); i++)
for (int i = 0; i < statusBar->CPlayer->weapons.SlotSize(conditionalValue[0]); i++)
{
PClassActor *weap = statusBar->CPlayer->weapons.Slots[conditionalValue[0]].GetWeapon(i);
PClassActor *weap = statusBar->CPlayer->weapons.GetWeapon(conditionalValue[0], i);
if(weap == NULL)
{
continue;
@ -597,8 +597,9 @@ class CommandDrawSwitchableImage : public CommandDrawImage
auto armor = statusBar->CPlayer->mo->FindInventory(NAME_BasicArmor);
if(armor != NULL)
{
bool matches1 = armor->NameVar(NAME_ArmorType).GetIndex() == armorType[0] && EvaluateOperation(conditionalOperator[0], conditionalValue[0], armor->Amount);
bool matches2 = armor->NameVar(NAME_ArmorType).GetIndex() == armorType[1] && EvaluateOperation(conditionalOperator[1], conditionalValue[1], armor->Amount);
auto n = armor->NameVar(NAME_ArmorType).GetIndex();
bool matches1 = n == armorType[0] && EvaluateOperation(conditionalOperator[0], conditionalValue[0], armor->Amount);
bool matches2 = n == armorType[1] && EvaluateOperation(conditionalOperator[1], conditionalValue[1], armor->Amount);
drawAlt = 1;
if(conditionAnd)
@ -1873,7 +1874,7 @@ class CommandUsesAmmo : public SBarInfoNegatableFlowControl
{
SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged);
SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && (statusBar->CPlayer->ReadyWeapon->AmmoType1 != NULL || statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL), block, statusBar);
SetTruth(statusBar->AmmoType(1) || statusBar->AmmoType(2), block, statusBar);
}
};
@ -1890,7 +1891,7 @@ class CommandUsesSecondaryAmmo : public CommandUsesAmmo
{
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
SetTruth(statusBar->CPlayer->ReadyWeapon != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType1 != statusBar->CPlayer->ReadyWeapon->AmmoType2, block, statusBar);
SetTruth(statusBar->AmmoType(2) && statusBar->AmmoType(2) != statusBar->AmmoType(1), block, statusBar);
}
};
@ -2881,7 +2882,7 @@ class CommandIsSelected : public SBarInfoNegatableFlowControl
if(weapon[i] == NULL || !weapon[i]->IsDescendantOf(NAME_Weapon))
{
sc.ScriptMessage("'%s' is not a type of weapon.", sc.String);
weapon[i] = RUNTIME_CLASS(AWeapon);
weapon[i] = PClass::FindClass(NAME_Weapon);
}
if(sc.CheckToken(','))
@ -3034,7 +3035,7 @@ class CommandHasWeaponPiece : public SBarInfoCommandFlowControl
if (weapon == NULL || !weapon->IsDescendantOf(NAME_Weapon)) //must be a weapon
{
sc.ScriptMessage("%s is not a kind of weapon.", sc.String);
weapon = RUNTIME_CLASS(AWeapon);
weapon = PClass::FindClass(NAME_Weapon);
}
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
@ -3246,8 +3247,8 @@ class CommandWeaponAmmo : public SBarInfoNegatableFlowControl
if(statusBar->CPlayer->ReadyWeapon != NULL)
{
const PClass *AmmoType1 = statusBar->CPlayer->ReadyWeapon->AmmoType1;
const PClass *AmmoType2 = statusBar->CPlayer->ReadyWeapon->AmmoType2;
const PClass *AmmoType1 = statusBar->AmmoType(1);
const PClass *AmmoType2 = statusBar->AmmoType(2);
bool usesammo1 = (AmmoType1 != NULL);
bool usesammo2 = (AmmoType2 != NULL);
//if(!usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble.

View file

@ -175,7 +175,7 @@ void ST_LoadCrosshair(bool alwaysload)
players[consoleplayer].camera->player != NULL &&
players[consoleplayer].camera->player->ReadyWeapon != NULL)
{
num = players[consoleplayer].camera->player->ReadyWeapon->Crosshair;
num = players[consoleplayer].camera->player->ReadyWeapon->IntVar(NAME_Crosshair);
}
if (num == 0)
{

View file

@ -62,6 +62,8 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, statusscreen_coop)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, statusscreen_dm)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mSliderColor)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, defaultbloodcolor)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, telefogheight)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, defKickback)
const char *GameNames[17] =
{

View file

@ -433,21 +433,7 @@ bool HUDSprite::GetWeaponRect(HWDrawInfo *di, DPSprite *psp, float sx, float sy,
x2 += viewwindowx;
// killough 12/98: fix psprite positioning problem
ftexturemid = 100.f - sy - r.top;
AWeapon * wi = player->ReadyWeapon;
if (wi && wi->YAdjust != 0)
{
float fYAd = wi->YAdjust;
if (screenblocks >= 11)
{
ftexturemid -= fYAd;
}
else
{
ftexturemid -= float(StatusBar->GetDisplacement()) * fYAd;
}
}
ftexturemid = 100.f - sy - r.top - psp->GetYAdjust(screenblocks >= 11);
scale = (SCREENHEIGHT*vw) / (SCREENWIDTH * 200.0f);
y1 = viewwindowy + vh / 2 - (ftexturemid * scale);

View file

@ -365,29 +365,6 @@ bool PClassActor::SetReplacement(FName replaceName)
return true;
}
//==========================================================================
//
// PClassActor :: Finalize
//
// Installs the parsed states and does some sanity checking
//
//==========================================================================
void AActor::Finalize(FStateDefinitions &statedef)
{
try
{
statedef.FinishStates(GetClass());
}
catch (CRecoverableError &)
{
statedef.MakeStateDefines(nullptr);
throw;
}
statedef.InstallStates(GetClass(), this);
statedef.MakeStateDefines(nullptr);
}
//==========================================================================
//
// PClassActor :: RegisterIDs

View file

@ -872,7 +872,6 @@ void F_StartIntermission(FName seq, uint8_t state)
}
}
//==========================================================================
//
// Called by main loop.

View file

@ -90,10 +90,17 @@ void cht_DoCheat (player_t *player, int cheat)
};
PClassActor *type;
AInventory *item;
FString smsg;
const char *msg = "";
char msgbuild[32];
int i;
// No cheating when not having a pawn attached.
if (player->mo == nullptr)
{
return;
}
switch (cheat)
{
case CHT_IDDQD:
@ -188,7 +195,8 @@ void cht_DoCheat (player_t *player, int cheat)
break;
case CHT_MORPH:
msg = cht_Morph (player, PClass::FindActor (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer), true);
smsg = cht_Morph (player, PClass::FindActor (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer), true);
msg = smsg.GetChars();
break;
case CHT_NOTARGET:
@ -408,29 +416,7 @@ void cht_DoCheat (player_t *player, int cheat)
break;
case CHT_TAKEWEAPS:
if (player->morphTics || player->mo == NULL || player->mo->health <= 0)
{
return;
}
{
// Take away all weapons that are either non-wimpy or use ammo.
AInventory **invp = &player->mo->Inventory, **lastinvp;
for (item = *invp; item != NULL; item = *invp)
{
lastinvp = invp;
invp = &(*invp)->Inventory;
if (item->IsKindOf(NAME_Weapon))
{
AWeapon *weap = static_cast<AWeapon *> (item);
if (!(weap->WeaponFlags & WIF_WIMPY_WEAPON) ||
weap->AmmoType1 != NULL)
{
item->Destroy ();
invp = lastinvp;
}
}
}
}
cht_Takeweaps(player);
msg = GStrings("TXT_CHEATIDKFA");
break;
@ -494,7 +480,7 @@ void cht_DoCheat (player_t *player, int cheat)
}
else
{
player->PendingWeapon = static_cast<AWeapon *> (item);
player->PendingWeapon = item;
}
}
}
@ -546,32 +532,17 @@ void cht_DoCheat (player_t *player, int cheat)
Printf ("%s cheats: %s\n", player->userinfo.GetName(), msg);
}
const char *cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo)
FString cht_Morph(player_t *player, PClassActor *morphclass, bool quickundo)
{
if (player->mo == NULL)
{
return "";
}
auto oldclass = player->mo->GetClass();
if (player->mo == nullptr) return "";
// Set the standard morph style for the current game
int style = MORPH_UNDOBYTOMEOFPOWER;
if (gameinfo.gametype == GAME_Hexen) style |= MORPH_UNDOBYCHAOSDEVICE;
if (player->morphTics)
IFVIRTUALPTR(player->mo, APlayerPawn, CheatMorph)
{
if (P_UndoPlayerMorph (player, player))
{
if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style))
{
return GStrings("TXT_STRANGER");
}
return GStrings("TXT_NOTSTRANGE");
}
}
else if (P_MorphPlayer (player, player, morphclass, 0, style))
{
return GStrings("TXT_STRANGE");
FString message;
VMReturn msgret(&message);
VMValue params[3] = { player->mo, morphclass, quickundo };
VMCall(func, params, 3, &msgret, 1);
return message;
}
return "";
}
@ -602,8 +573,6 @@ void cht_SetInv(player_t *player, const char *string, int amount, bool beyond)
void cht_Give (player_t *player, const char *name, int amount)
{
if (player->mo == nullptr) return;
IFVIRTUALPTR(player->mo, APlayerPawn, CheatGive)
{
FString namestr = name;
@ -614,8 +583,6 @@ void cht_Give (player_t *player, const char *name, int amount)
void cht_Take (player_t *player, const char *name, int amount)
{
if (player->mo == nullptr) return;
IFVIRTUALPTR(player->mo, APlayerPawn, CheatTake)
{
FString namestr = name;
@ -624,6 +591,15 @@ void cht_Take (player_t *player, const char *name, int amount)
}
}
void cht_Takeweaps(player_t *player)
{
IFVIRTUALPTR(player->mo, APlayerPawn, CheatTakeWeaps)
{
VMValue params[3] = { player->mo };
VMCall(func, params, 1, nullptr, 0);
}
}
class DSuicider : public DThinker
{
DECLARE_CLASS(DSuicider, DThinker)

View file

@ -15,6 +15,7 @@ void cht_Give (player_t *player, const char *item, int amount=1);
void cht_Take (player_t *player, const char *item, int amount=1);
void cht_SetInv(player_t *player, const char *item, int amount = 1, bool beyondMax = false);
void cht_Suicide (player_t *player);
const char *cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo);
FString cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo);
void cht_Takeweaps(player_t *player);
#endif

View file

@ -204,6 +204,7 @@ xx(PowerDrain)
xx(Reflection)
xx(CustomInventory)
xx(Inventory)
xx(StateProvider)
xx(CallTryPickup)
xx(QuestItem25)
xx(QuestItem28)
@ -986,3 +987,26 @@ xx(snd_output)
xx(snd_output_format)
xx(snd_speakermode)
xx(snd_resampler)
// ScriptUtil entry points
xx(ScriptUtil)
xx(SetMarineWeapon)
xx(SetMarineSprite)
// Weapon member fields that need direct access
xx(Ammo1)
xx(Ammo2)
xx(AmmoType1)
xx(AmmoType2)
xx(AmmoGive1)
xx(AmmoGive2)
xx(AmmoUse1)
xx(SisterWeapon)
xx(BobStyle)
xx(Kickback)
xx(MinSelAmmo1)
xx(bDehAmmo)
xx(FOVScale)
xx(YAdjust)
xx(Crosshair)
xx(WeaponFlags)

View file

@ -74,6 +74,7 @@
#include "g_levellocals.h"
#include "actorinlines.h"
#include "types.h"
#include "scriptutil.h"
// P-codes for ACS scripts
enum
@ -6963,28 +6964,6 @@ static bool CharArrayParms(int &capacity, int &offset, int &a, FACSStackMemory&
return true;
}
static void SetMarineWeapon(AActor *marine, int weapon)
{
static VMFunction *smw = nullptr;
if (smw == nullptr) PClass::FindFunction(&smw, NAME_ScriptedMarine, NAME_SetWeapon);
if (smw)
{
VMValue params[2] = { marine, weapon };
VMCall(smw, params, 2, nullptr, 0);
}
}
static void SetMarineSprite(AActor *marine, PClassActor *source)
{
static VMFunction *sms = nullptr;
if (sms == nullptr) PClass::FindFunction(&sms, NAME_ScriptedMarine, NAME_SetSprite);
if (sms)
{
VMValue params[2] = { marine, source };
VMCall(sms, params, 2, nullptr, 0);
}
}
int DLevelScript::RunScript ()
{
DACSThinker *controller = DACSThinker::ActiveThinker;
@ -9832,93 +9811,16 @@ scriptwait:
break;
case PCD_SETWEAPON:
if (activator == NULL || activator->player == NULL)
{
STACK(1) = 0;
}
else
{
AInventory *item = activator->FindInventory (PClass::FindActor (FBehavior::StaticLookupString (STACK(1))));
if (item == NULL || !item->IsKindOf(NAME_Weapon))
{
STACK(1) = 0;
}
else if (activator->player->ReadyWeapon == item)
{
// The weapon is already selected, so setweapon succeeds by default,
// but make sure the player isn't switching away from it.
activator->player->PendingWeapon = WP_NOCHANGE;
STACK(1) = 1;
}
else
{
AWeapon *weap = static_cast<AWeapon *> (item);
if (weap->CheckAmmo (AWeapon::EitherFire, false))
{
// There's enough ammo, so switch to it.
STACK(1) = 1;
activator->player->PendingWeapon = weap;
}
else
{
STACK(1) = 0;
}
}
}
STACK(1) = ScriptUtil::Exec(NAME_SetWeapon, ScriptUtil::Pointer, activator, ScriptUtil::ACSClass, STACK(1), ScriptUtil::End);
break;
case PCD_SETMARINEWEAPON:
if (STACK(2) != 0)
{
AActor *marine;
NActorIterator iterator(NAME_ScriptedMarine, STACK(2));
while ((marine = iterator.Next()) != NULL)
{
SetMarineWeapon(marine, STACK(1));
}
}
else
{
if (activator != nullptr && activator->IsKindOf (NAME_ScriptedMarine))
{
SetMarineWeapon(activator, STACK(1));
}
}
ScriptUtil::Exec(NAME_SetMarineWeapon, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::Int, STACK(1), ScriptUtil::End);
sp -= 2;
break;
case PCD_SETMARINESPRITE:
{
PClassActor *type = PClass::FindActor(FBehavior::StaticLookupString (STACK(1)));
if (type != NULL)
{
if (STACK(2) != 0)
{
AActor *marine;
NActorIterator iterator(NAME_ScriptedMarine, STACK(2));
while ((marine = iterator.Next()) != NULL)
{
SetMarineSprite(marine, type);
}
}
else
{
if (activator != nullptr && activator->IsKindOf(NAME_ScriptedMarine))
{
SetMarineSprite(activator, type);
}
}
}
else
{
Printf ("Unknown actor type: %s\n", FBehavior::StaticLookupString (STACK(1)));
}
}
ScriptUtil::Exec(NAME_SetMarineSprite, ScriptUtil::Pointer, activator, ScriptUtil::Int, STACK(2), ScriptUtil::ACSClass, STACK(1), ScriptUtil::End);
sp -= 2;
break;
@ -10305,14 +10207,7 @@ scriptwait:
if (tag == 0)
{
if (activator != NULL && activator->player)
{
changes += P_MorphPlayer(activator->player, activator->player, playerclass, duration, style, morphflash, unmorphflash);
}
else
{
changes += P_MorphMonster(activator, monsterclass, duration, style, morphflash, unmorphflash);
}
changes = P_MorphActor(activator, activator, playerclass, monsterclass, duration, style, morphflash, unmorphflash);
}
else
{
@ -10321,15 +10216,7 @@ scriptwait:
while ( (actor = iterator.Next ()) )
{
if (actor->player)
{
changes += P_MorphPlayer(activator == NULL ? NULL : activator->player,
actor->player, playerclass, duration, style, morphflash, unmorphflash);
}
else
{
changes += P_MorphMonster(actor, monsterclass, duration, style, morphflash, unmorphflash);
}
changes += P_MorphActor(activator, actor, playerclass, monsterclass, duration, style, morphflash, unmorphflash);
}
}
@ -10346,24 +10233,7 @@ scriptwait:
if (tag == 0)
{
if (activator->player)
{
if (P_UndoPlayerMorph(activator->player, activator->player, 0, force))
{
changes++;
}
}
else
{
if (activator->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
{
AMorphedMonster *morphed_actor = barrier_cast<AMorphedMonster *>(activator);
if (P_UndoMonsterMorph(morphed_actor, force))
{
changes++;
}
}
}
changes += P_UnmorphActor(activator, activator, 0, force);
}
else
{
@ -10372,24 +10242,7 @@ scriptwait:
while ( (actor = iterator.Next ()) )
{
if (actor->player)
{
if (P_UndoPlayerMorph(activator->player, actor->player, 0, force))
{
changes++;
}
}
else
{
if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
{
AMorphedMonster *morphed_actor = static_cast<AMorphedMonster *>(actor);
if (P_UndoMonsterMorph(morphed_actor, force))
{
changes++;
}
}
}
changes += P_UnmorphActor(activator, actor, 0, force);
}
}

View file

@ -43,6 +43,7 @@
class FFont;
class FileReader;
struct line_t;
class FSerializer;
enum

File diff suppressed because it is too large Load diff

View file

@ -965,7 +965,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
if (item->GetClass()->TypeName == NAME_FlameThrower)
{
// The flame thrower gives less ammo when given in a dialog
static_cast<AWeapon*>(item)->AmmoGive1 = 40;
item->IntVar(NAME_AmmoGive1) = 40;
}
item->flags |= MF_DROPPED;
if (!item->CallTryPickup(player->mo))

View file

@ -268,71 +268,6 @@ DEFINE_ACTION_FUNCTION(AActor, SoundAlert)
return 0;
}
//============================================================================
//
// P_DaggerAlert
//
//============================================================================
void P_DaggerAlert(AActor *target, AActor *emitter)
{
AActor *looker;
sector_t *sec = emitter->Sector;
if (emitter->LastHeard != NULL)
return;
if (emitter->health <= 0)
return;
if (!(emitter->flags3 & MF3_ISMONSTER))
return;
if (emitter->flags4 & MF4_INCOMBAT)
return;
emitter->flags4 |= MF4_INCOMBAT;
emitter->target = target;
FState *painstate = emitter->FindState(NAME_Pain, NAME_Dagger);
if (painstate != NULL)
{
emitter->SetState(painstate);
}
for (looker = sec->thinglist; looker != NULL; looker = looker->snext)
{
if (looker == emitter || looker == target)
continue;
if (looker->health <= 0)
continue;
if (!(looker->flags4 & MF4_SEESDAGGERS))
continue;
if (!(looker->flags4 & MF4_INCOMBAT))
{
if (!P_CheckSight(looker, target) && !P_CheckSight(looker, emitter))
continue;
looker->target = target;
if (looker->SeeSound)
{
S_Sound(looker, CHAN_VOICE, looker->SeeSound, 1, ATTN_NORM);
}
looker->SetState(looker->SeeState);
looker->flags4 |= MF4_INCOMBAT;
}
}
}
DEFINE_ACTION_FUNCTION(AActor, DaggerAlert)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(target, AActor);
// Note that the emitter is self, not the target of the alert! Target can be NULL.
P_DaggerAlert(target, self);
return 0;
}
//----------------------------------------------------------------------------
//
// AActor :: CheckMeleeRange
@ -1094,7 +1029,7 @@ void P_NewChaseDir(AActor * actor)
{
// melee range of player weapon is a parameter of the action function and cannot be checked here.
// Add a new weapon property?
ismeleeattacker = ((target->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) && dist < 192);
ismeleeattacker = ((target->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_MELEEWEAPON) && dist < 192);
}
if (ismeleeattacker)
{
@ -3222,51 +3157,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRail)
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_Scream)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->DeathSound)
{
// Check for bosses.
if (self->flags2 & MF2_BOSS)
{
// full volume
S_Sound (self, CHAN_VOICE, self->DeathSound, 1, ATTN_NONE);
}
else
{
S_Sound (self, CHAN_VOICE, self->DeathSound, 1, ATTN_NORM);
}
}
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_XScream)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->player)
S_Sound (self, CHAN_VOICE, "*gibbed", 1, ATTN_NORM);
else
S_Sound (self, CHAN_VOICE, "misc/gibbed", 1, ATTN_NORM);
return 0;
}
//===========================================================================
//
// A_ActiveSound
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_ActiveSound)
{
PARAM_SELF_PROLOGUE(AActor);
if (self->ActiveSound)
{
S_Sound(self, CHAN_VOICE, self->ActiveSound, 1, ATTN_NORM);
}
return 0;
}
//---------------------------------------------------------------------------
//
// Modifies the drop amount of this item according to the current skill's
@ -3275,64 +3165,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_ActiveSound)
//---------------------------------------------------------------------------
void ModifyDropAmount(AInventory *inv, int dropamount)
{
auto flagmask = IF_IGNORESKILL;
double dropammofactor = G_SkillProperty(SKILLP_DropAmmoFactor);
// Default drop amount is half of regular amount * regular ammo multiplication
if (dropammofactor == -1)
IFVIRTUALPTR(inv, AInventory, ModifyDropAmount)
{
dropammofactor = 0.5;
flagmask = ItemFlag(0);
VMValue params[] = { inv, dropamount };
VMCall(func, params, 2, nullptr, 0);
}
if (dropamount > 0)
{
if (flagmask != 0 && inv->IsKindOf(NAME_Ammo))
{
inv->Amount = int(dropamount * dropammofactor);
inv->ItemFlags |= IF_IGNORESKILL;
}
else
{
inv->Amount = dropamount;
}
}
else if (inv->IsKindOf (PClass::FindActor(NAME_Ammo)))
{
// Half ammo when dropped by bad guys.
int amount = inv->IntVar("DropAmount");
if (amount <= 0)
{
amount = MAX(1, int(inv->Amount * dropammofactor));
}
inv->Amount = amount;
inv->ItemFlags |= flagmask;
}
else if (inv->IsKindOf (PClass::FindActor(NAME_WeaponGiver)))
{
inv->FloatVar("AmmoFactor") = dropammofactor;
inv->ItemFlags |= flagmask;
}
else if (inv->IsKindOf(NAME_Weapon))
{
// The same goes for ammo from a weapon.
static_cast<AWeapon *>(inv)->AmmoGive1 = int(static_cast<AWeapon *>(inv)->AmmoGive1 * dropammofactor);
static_cast<AWeapon *>(inv)->AmmoGive2 = int(static_cast<AWeapon *>(inv)->AmmoGive2 * dropammofactor);
inv->ItemFlags |= flagmask;
}
else if (inv->IsKindOf (PClass::FindClass(NAME_DehackedPickup)))
{
// For weapons and ammo modified by Dehacked we need to flag the item.
inv->BoolVar("droppedbymonster") = true;
}
}
// todo: make this a scripted virtual function so it can better deal with some of the classes involved.
DEFINE_ACTION_FUNCTION(AInventory, ModifyDropAmount)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_INT(dropamount);
ModifyDropAmount(self, dropamount);
return 0;
}
//---------------------------------------------------------------------------
@ -3343,7 +3180,7 @@ DEFINE_ACTION_FUNCTION(AInventory, ModifyDropAmount)
CVAR(Int, sv_dropstyle, 0, CVAR_SERVERINFO | CVAR_ARCHIVE);
AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int chance)
AActor *P_DropItem (AActor *source, PClassActor *type, int dropamount, int chance)
{
if (type != NULL && pr_dropitem() <= chance)
{
@ -3394,9 +3231,8 @@ AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int c
return NULL;
}
}
return inv;
}
// we can't really return an AInventory pointer to a non-inventory item here, can we?
return mo;
}
}
return NULL;
@ -3474,20 +3310,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_Pain)
return 0;
}
//
// A_Detonate
// killough 8/9/98: same as A_Explode, except that the damage is variable
//
DEFINE_ACTION_FUNCTION(AActor, A_Detonate)
{
PARAM_SELF_PROLOGUE(AActor);
int damage = self->GetMissileDamage(0, 1);
P_RadiusAttack (self, self->target, damage, damage, self->DamageType, RADF_HURTSOURCE);
P_CheckSplash(self, damage);
return 0;
}
bool CheckBossDeath (AActor *actor)
{
int i;

View file

@ -46,7 +46,6 @@ struct FLookExParams
FState *seestate;
};
void P_DaggerAlert (AActor *target, AActor *emitter);
bool P_HitFriend (AActor *self);
void P_NoiseAlert (AActor *target, AActor *emmiter, bool splash=false, double maxdist=0);
@ -54,7 +53,7 @@ bool P_CheckMeleeRange2 (AActor *actor);
bool P_Move (AActor *actor);
bool P_TryWalk (AActor *actor);
void P_NewChaseDir (AActor *actor);
AInventory *P_DropItem (AActor *source, PClassActor *type, int special, int chance);
AActor *P_DropItem (AActor *source, PClassActor *type, int special, int chance);
void P_TossItem (AActor *item);
bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params);
void A_Weave(AActor *self, int xyspeed, int zspeed, double xydist, double zdist);

View file

@ -68,7 +68,6 @@ FRandom pr_damagemobj ("ActorTakeDamage");
static FRandom pr_lightning ("LightningDamage");
static FRandom pr_poison ("PoisonDamage");
static FRandom pr_switcher ("SwitchTarget");
static FRandom pr_kickbackdir ("KickbackDir");
CVAR (Bool, cl_showsprees, true, CVAR_ARCHIVE)
CVAR (Bool, cl_showmultikills, true, CVAR_ARCHIVE)
@ -289,39 +288,41 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
{
// Handle possible unmorph on death
bool wasgibbed = (health < GetGibHealth());
AActor *realthis = NULL;
int realstyle = 0;
int realhealth = 0;
if (P_MorphedDeath(this, &realthis, &realstyle, &realhealth))
{
if (!(realstyle & MORPH_UNDOBYDEATHSAVES))
IFVIRTUAL(AActor, MorphedDeath)
{
if (wasgibbed)
AActor *realthis = NULL;
int realstyle = 0;
int realhealth = 0;
VMValue params[] = { this };
VMReturn returns[3];
returns[0].PointerAt((void**)&realthis);
returns[1].IntAt(&realstyle);
returns[2].IntAt(&realhealth);
VMCall(func, params, 1, returns, 3);
if (realthis && !(realstyle & MORPH_UNDOBYDEATHSAVES))
{
int realgibhealth = realthis->GetGibHealth();
if (realthis->health >= realgibhealth)
if (wasgibbed)
{
realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)l
int realgibhealth = realthis->GetGibHealth();
if (realthis->health >= realgibhealth)
{
realthis->health = realgibhealth - 1; // if morphed was gibbed, so must original be (where allowed)l
}
}
realthis->CallDie(source, inflictor, dmgflags, MeansOfDeath);
}
realthis->CallDie(source, inflictor, dmgflags, MeansOfDeath);
}
return;
}
// [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :)
effects &= ~FX_RESPAWNINVUL;
//flags &= ~MF_INVINCIBLE;
if (debugfile && this->player)
{
static int dieticks[MAXPLAYERS]; // [ZzZombo] not used? Except if for peeking in debugger...
int pnum = int(this->player-players);
dieticks[pnum] = gametic;
fprintf(debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic,
this->player->cheats&CF_PREDICTING ? "predicting" : "real");
}
// [RH] Notify this actor's items.
for (AInventory *item = Inventory; item != NULL; )
{
@ -597,7 +598,13 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
flags &= ~MF_SOLID;
player->playerstate = PST_DEAD;
P_DropWeapon (player);
IFVM(PlayerPawn, DropWeapon)
{
VMValue param = player->mo;
VMCall(func, &param, 1, nullptr, 0);
}
if (this == players[consoleplayer].camera && automapactive)
{
// don't die in auto map, switch view prior to dying
@ -915,9 +922,7 @@ static inline bool isFakePain(AActor *target, AActor *inflictor, int damage)
// the damage was cancelled.
static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags, DAngle angle, bool& needevent)
{
DAngle ang;
player_t *player = NULL;
double thrust;
int temp;
int painchance = 0;
FState * woundstate = NULL;
@ -1180,73 +1185,10 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
&& !(target->flags7 & MF7_DONTTHRUST)
&& (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST)))
{
int kickback;
if (inflictor && inflictor->projectileKickback)
kickback = inflictor->projectileKickback;
else if (!source || !source->player || !source->player->ReadyWeapon)
kickback = gameinfo.defKickback;
else
kickback = source->player->ReadyWeapon->Kickback;
kickback = int(kickback * G_SkillProperty(SKILLP_KickbackFactor));
if (kickback)
IFVIRTUALPTR(target, AActor, ApplyKickback)
{
AActor *origin = (source && (flags & DMG_INFLICTOR_IS_PUFF))? source : inflictor;
if (flags & DMG_USEANGLE)
{
ang = angle;
}
else if (origin->X() == target->X() && origin->Y() == target->Y())
{
// If the origin and target are in exactly the same spot, choose a random direction.
// (Most likely cause is from telefragging somebody during spawning because they
// haven't moved from their spawn spot at all.)
ang = pr_kickbackdir.GenRand_Real2() * 360.;
}
else
{
ang = origin->AngleTo(target);
}
thrust = mod == NAME_MDK ? 10 : 32;
if (target->Mass > 0)
{
thrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., thrust);
}
// Don't apply ultra-small damage thrust
if (thrust < 0.01) thrust = 0;
// make fall forwards sometimes
if ((damage < 40) && (damage > target->health)
&& (target->Z() - origin->Z() > 64)
&& (pr_damagemobj()&1)
// [RH] But only if not too fast and not flying
&& thrust < 10
&& !(target->flags & MF_NOGRAVITY)
&& (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL))
)
{
ang += 180.;
thrust *= 4;
}
if (source && source->player && (flags & DMG_INFLICTOR_IS_PUFF)
&& source->player->ReadyWeapon != NULL &&
(source->player->ReadyWeapon->WeaponFlags & WIF_STAFF2_KICKBACK))
{
// Staff power level 2
target->Thrust(ang, 10);
if (!(target->flags & MF_NOGRAVITY))
{
target->Vel.Z += 5.;
}
}
else
{
target->Thrust(ang, thrust);
}
VMValue params[] = { target, inflictor, source, damage, angle.Degrees, mod.GetIndex(), flags };
VMCall(func, params, countof(params), nullptr, 0);
}
}

View file

@ -80,7 +80,6 @@ extern int bmapnegy;
// P_PSPR
//
void P_SetupPsprites (player_t* curplayer, bool startweaponup);
void P_DropWeapon (player_t* player);
//
@ -132,9 +131,6 @@ AActor *P_SpawnMissileAngleZSpeed(AActor *source, double z, PClassActor *type, D
AActor *P_SpawnMissileZAimed(AActor *source, double z, AActor *dest, PClassActor *type);
AActor *P_SpawnPlayerMissile (AActor* source, PClassActor *type);
AActor *P_SpawnPlayerMissile (AActor *source, PClassActor *type, DAngle angle);
AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, PClassActor *type, DAngle angle,
FTranslatedLineTarget *pLineTarget = NULL, AActor **MissileActor = NULL, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0);
@ -320,6 +316,7 @@ enum // P_AimLineAttack flags
ALF_CHECKCONVERSATION = 8,
ALF_NOFRIENDS = 16,
ALF_PORTALRESTRICT = 32, // only work through portals with a global offset (to be used for stuff that cannot remember the calculated FTranslatedLineTarget info)
ALF_NOWEAPONCHECK = 64, // ignore NOAUTOAIM flag on a player's weapon.
};
enum // P_LineAttack flags
@ -360,7 +357,6 @@ void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff); // hitsc
void P_TraceBleed (int damage, AActor *target); // random direction version
bool P_HitFloor (AActor *thing);
bool P_HitWater (AActor *thing, sector_t *sec, const DVector3 &pos, bool checkabove = false, bool alert = true, bool force = false);
void P_CheckSplash(AActor *self, double distance);
struct FRailParams
{
@ -411,6 +407,7 @@ enum
RADF_SOURCEISSPOT = 4,
RADF_NODAMAGE = 8,
RADF_THRUSTZ = 16,
RADF_OLDRADIUSDAMAGE = 32
};
int P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance,
FName damageType, int flags, int fulldamagedistance=0);

View file

@ -156,6 +156,25 @@ DEFINE_FIELD_X(FCheckPosition, FCheckPosition, portalstep);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, portalgroup);
DEFINE_FIELD_X(FCheckPosition, FCheckPosition, PushTime);
DEFINE_FIELD_X(FRailParams, FRailParams, source);
DEFINE_FIELD_X(FRailParams, FRailParams, damage);
DEFINE_FIELD_X(FRailParams, FRailParams, offset_xy);
DEFINE_FIELD_X(FRailParams, FRailParams, offset_z);
DEFINE_FIELD_X(FRailParams, FRailParams, color1);
DEFINE_FIELD_X(FRailParams, FRailParams, color2);
DEFINE_FIELD_X(FRailParams, FRailParams, maxdiff);
DEFINE_FIELD_X(FRailParams, FRailParams, flags);
DEFINE_FIELD_X(FRailParams, FRailParams, puff);
DEFINE_FIELD_X(FRailParams, FRailParams, angleoffset);
DEFINE_FIELD_X(FRailParams, FRailParams, pitchoffset);
DEFINE_FIELD_X(FRailParams, FRailParams, distance);
DEFINE_FIELD_X(FRailParams, FRailParams, duration);
DEFINE_FIELD_X(FRailParams, FRailParams, sparsity);
DEFINE_FIELD_X(FRailParams, FRailParams, drift);
DEFINE_FIELD_X(FRailParams, FRailParams, spawnclass);
DEFINE_FIELD_X(FRailParams, FRailParams, SpiralOffset);
DEFINE_FIELD_X(FRailParams, FRailParams, limit);
//==========================================================================
//
// CanCollideWith
@ -4439,8 +4458,8 @@ DAngle P_AimLineAttack(AActor *t1, DAngle angle, double distance, FTranslatedLin
else
{
// [BB] Disable autoaim on weapons with WIF_NOAUTOAIM.
AWeapon *weapon = t1->player->ReadyWeapon;
if (weapon && (weapon->WeaponFlags & WIF_NOAUTOAIM))
auto weapon = t1->player->ReadyWeapon;
if ((weapon && (weapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM)) && !(flags & ALF_NOWEAPONCHECK))
{
vrange = 0.5;
}
@ -4840,38 +4859,13 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
}
if (!(puffDefaults != NULL && puffDefaults->flags3&MF3_BLOODLESSIMPACT))
{
bool bloodsplatter = (t1->flags5 & MF5_BLOODSPLATTER) ||
(t1->player != nullptr && t1->player->ReadyWeapon != nullptr &&
(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
bool axeBlood = (t1->player != nullptr &&
t1->player->ReadyWeapon != nullptr &&
(t1->player->ReadyWeapon->WeaponFlags & WIF_AXEBLOOD));
if (!bloodsplatter && !axeBlood &&
!(trace.Actor->flags & MF_NOBLOOD) &&
!(trace.Actor->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)))
IFVIRTUALPTR(trace.Actor, AActor, SpawnLineAttackBlood)
{
P_SpawnBlood(bleedpos, trace.SrcAngleFromTarget, newdam > 0 ? newdam : damage, trace.Actor);
VMValue params[] = { trace.Actor, t1, bleedpos.X, bleedpos.Y, bleedpos.Z, trace.SrcAngleFromTarget.Degrees, damage, newdam };
VMCall(func, params, countof(params), nullptr, 0);
}
if (damage)
{
if (bloodsplatter || axeBlood)
{
if (!(trace.Actor->flags&MF_NOBLOOD) &&
!(trace.Actor->flags2&(MF2_INVULNERABLE | MF2_DORMANT)))
{
if (axeBlood)
{
P_BloodSplatter2(bleedpos, trace.Actor, trace.SrcAngleFromTarget);
}
if (pr_lineattack() < 192)
{
P_BloodSplatter(bleedpos, trace.Actor, trace.SrcAngleFromTarget);
}
}
}
// [RH] Stick blood to walls
P_TraceBleed(newdam > 0 ? newdam : damage, trace.HitPos, trace.Actor, trace.SrcAngleFromTarget, pitch);
}
@ -5603,6 +5597,15 @@ void P_RailAttack(FRailParams *p)
P_DrawRailTrail(source, rail_data.PortalHits, p->color1, p->color2, p->maxdiff, p->flags, p->spawnclass, angle, p->duration, p->sparsity, p->drift, p->SpiralOffset, pitch);
}
DEFINE_ACTION_FUNCTION(AActor, RailAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_POINTER(p, FRailParams);
p->source = self;
P_RailAttack(p);
return 0;
}
//==========================================================================
//
// [RH] P_AimCamera
@ -6201,7 +6204,7 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom
// them far too "active." BossBrains also use the old code
// because some user levels require they have a height of 16,
// which can make them near impossible to hit with the new code.
if ((flags & RADF_NODAMAGE) || !((bombspot->flags5 | thing->flags5) & MF5_OLDRADIUSDMG))
if (((flags & RADF_NODAMAGE) || !((bombspot->flags5 | thing->flags5) & MF5_OLDRADIUSDMG)) && !(flags & RADF_OLDRADIUSDAMAGE))
{
double points = P_GetRadiusDamage(false, bombspot, thing, bombdamage, bombdistance, fulldamagedistance, bombsource == thing);
double check = int(points) * bombdamage;
@ -6278,6 +6281,18 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom
return count;
}
DEFINE_ACTION_FUNCTION(AActor, RadiusAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(bombsource, AActor);
PARAM_INT(bombdamage);
PARAM_INT(bombdistance);
PARAM_NAME(damagetype);
PARAM_INT(flags);
PARAM_INT(fulldamagedistance);
ACTION_RETURN_INT(P_RadiusAttack(self, bombsource, bombdamage, bombdistance, damagetype, flags, fulldamagedistance));
}
//==========================================================================
//
// SECTOR HEIGHT CHANGING

View file

@ -804,7 +804,7 @@ bool AActor::GiveInventory(PClassActor *type, int amount, bool givecheat)
if (type == nullptr || !type->IsDescendantOf(RUNTIME_CLASS(AInventory))) return false;
AWeapon *savedPendingWeap = player != NULL ? player->PendingWeapon : NULL;
auto savedPendingWeap = player != NULL ? player->PendingWeapon : NULL;
bool hadweap = player != NULL ? player->ReadyWeapon != NULL : true;
AInventory *item;
@ -1071,6 +1071,12 @@ void AActor::DestroyAllInventory ()
}
}
DEFINE_ACTION_FUNCTION(AActor, DestroyAllInventory)
{
PARAM_SELF_PROLOGUE(AActor);
self->DestroyAllInventory();
return 0;
}
//============================================================================
//
// AActor :: FirstInv
@ -1120,9 +1126,14 @@ bool AActor::UseInventory (AInventory *item)
{
return false;
}
if (!item->CallUse (false))
IFVIRTUALPTR(item, AInventory, Use)
{
return false;
VMValue params[2] = { item, false };
int retval;
VMReturn ret(&retval);
VMCall(func, params, 2, &ret, 1);
if (!retval) return false;
}
if (dmflags2 & DF2_INFINITE_INVENTORY)
@ -5319,26 +5330,6 @@ void AActor::CallPostBeginPlay()
E_WorldThingSpawned(this);
}
void AActor::MarkPrecacheSounds() const
{
SeeSound.MarkUsed();
AttackSound.MarkUsed();
PainSound.MarkUsed();
DeathSound.MarkUsed();
ActiveSound.MarkUsed();
UseSound.MarkUsed();
BounceSound.MarkUsed();
WallBounceSound.MarkUsed();
CrushPainSound.MarkUsed();
}
DEFINE_ACTION_FUNCTION(AActor, MarkPrecacheSounds)
{
PARAM_SELF_PROLOGUE(AActor);
self->MarkPrecacheSounds();
return 0;
}
bool AActor::isFast()
{
if (flags5&MF5_ALWAYSFAST) return true;
@ -5731,7 +5722,11 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags)
else if ((multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN) || sv_singleplayerrespawn ||
!!G_SkillProperty(SKILLP_PlayerRespawn)) && state == PST_REBORN && oldactor != NULL)
{ // Special inventory handling for respawning in coop
p->mo->FilterCoopRespawnInventory (oldactor);
IFVM(PlayerPawn, FilterCoopRespawnInventory)
{
VMValue params[] = { p->mo, oldactor };
VMCall(func, params, 2, nullptr, 0);
}
}
if (oldactor != NULL)
{ // Remove any inventory left from the old actor. Coop handles
@ -6797,29 +6792,6 @@ DEFINE_ACTION_FUNCTION(AActor, HitFloor)
ACTION_RETURN_BOOL(P_HitFloor(self));
}
//---------------------------------------------------------------------------
//
// P_CheckSplash
//
// Checks for splashes caused by explosions
//
//---------------------------------------------------------------------------
void P_CheckSplash(AActor *self, double distance)
{
sector_t *floorsec;
self->Sector->LowestFloorAt(self, &floorsec);
if (self->Z() <= self->floorz + distance && self->floorsector == floorsec && self->Sector->GetHeightSec() == NULL && floorsec->heightsec == NULL)
{
// Explosion splashes never alert monsters. This is because A_Explode has
// a separate parameter for that so this would get in the way of proper
// behavior.
DVector3 pos = self->PosRelative(floorsec);
pos.Z = self->floorz;
P_HitWater (self, floorsec, pos, false, false);
}
}
//---------------------------------------------------------------------------
//
// FUNC P_CheckMissileSpawn
@ -7315,20 +7287,6 @@ DEFINE_ACTION_FUNCTION(AActor, SpawnSubMissile)
================
*/
AActor *P_SpawnPlayerMissile (AActor *source, PClassActor *type)
{
if (source == NULL)
{
return NULL;
}
return P_SpawnPlayerMissile (source, 0, 0, 0, type, source->Angles.Yaw);
}
AActor *P_SpawnPlayerMissile (AActor *source, PClassActor *type, DAngle angle)
{
return P_SpawnPlayerMissile (source, 0, 0, 0, type, angle);
}
AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z,
PClassActor *type, DAngle angle, FTranslatedLineTarget *pLineTarget, AActor **pMissileActor,
bool nofreeaim, bool noautoaim, int aimflags)
@ -7346,7 +7304,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z,
DAngle vrange = nofreeaim ? 35. : 0.;
if (!pLineTarget) pLineTarget = &scratch;
if (source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->WeaponFlags & WIF_NOAUTOAIM) || noautoaim))
if (!(aimflags & ALF_NOWEAPONCHECK) && source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM) || noautoaim))
{
// Keep exactly the same angle and pitch as the player's own aim
an = angle;

View file

@ -44,6 +44,7 @@
#include "cmdlib.h"
#include "g_levellocals.h"
#include "vm.h"
#include "sbar.h"
// MACROS ------------------------------------------------------------------
@ -92,8 +93,6 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO);
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static FRandom pr_wpnreadysnd ("WpnReadySnd");
static const FGenericButtons ButtonChecks[] =
{
{ WRF_AllowZoom, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom },
@ -556,86 +555,13 @@ DEFINE_ACTION_FUNCTION(DPSprite, SetState)
void P_BringUpWeapon (player_t *player)
{
AWeapon *weapon;
if (player->PendingWeapon == WP_NOCHANGE)
IFVM(PlayerPawn, BringUpWeapon)
{
if (player->ReadyWeapon != nullptr)
{
player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP;
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetReadyState());
}
return;
}
weapon = player->PendingWeapon;
// If the player has a tome of power, use this weapon's powered up
// version, if one is available.
if (weapon != nullptr &&
weapon->SisterWeapon &&
weapon->SisterWeapon->WeaponFlags & WIF_POWERED_UP &&
player->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true))
{
weapon = weapon->SisterWeapon;
}
player->PendingWeapon = WP_NOCHANGE;
player->ReadyWeapon = weapon;
player->mo->weaponspecial = 0;
if (weapon != nullptr)
{
if (weapon->UpSound)
{
S_Sound (player->mo, CHAN_WEAPON, weapon->UpSound, 1, ATTN_NORM);
}
player->refire = 0;
player->GetPSprite(PSP_WEAPON)->y = player->cheats & CF_INSTANTWEAPSWITCH
? WEAPONTOP : WEAPONBOTTOM;
// make sure that the previous weapon's flash state is terminated.
// When coming here from a weapon drop it may still be active.
P_SetPsprite(player, PSP_FLASH, nullptr);
P_SetPsprite(player, PSP_WEAPON, weapon->GetUpState());
VMValue param = player->mo;
VMCall(func, &param, 1, nullptr, 0);
}
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, BringUpWeapon)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
P_BringUpWeapon(self);
return 0;
}
//---------------------------------------------------------------------------
//
// PROC P_DropWeapon
//
// The player died, so put the weapon away.
//
//---------------------------------------------------------------------------
void P_DropWeapon (player_t *player)
{
if (player == nullptr)
{
return;
}
// Since the weapon is dropping, stop blocking switching.
player->WeaponState &= ~WF_DISABLESWITCH;
if ((player->ReadyWeapon != nullptr) && (player->health > 0 || !(player->ReadyWeapon->WeaponFlags & WIF_NODEATHDESELECT)))
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetDownState());
}
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, DropWeapon)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
P_DropWeapon(self);
return 0;
}
//============================================================================
//
// P_BobWeapon
@ -651,219 +577,17 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, DropWeapon)
void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac)
{
static float curbob;
double xx[2], yy[2];
AWeapon *weapon;
float bobtarget;
weapon = player->ReadyWeapon;
if (weapon == nullptr || weapon->WeaponFlags & WIF_DONTBOB)
IFVIRTUALPTR(player->mo, APlayerPawn, BobWeapon)
{
*x = *y = 0;
VMValue param[] = { player->mo, ticfrac };
DVector2 result;
VMReturn ret(&result);
VMCall(func, param, 2, &ret, 1);
*x = (float)result.X;
*y = (float)result.Y;
return;
}
// [XA] Get the current weapon's bob properties.
int bobstyle = weapon->BobStyle;
float BobSpeed = (weapon->BobSpeed * 128);
float Rangex = weapon->BobRangeX;
float Rangey = weapon->BobRangeY;
for (int i = 0; i < 2; i++)
{
// Bob the weapon based on movement speed. ([SP] And user's bob speed setting)
FAngle angle = (BobSpeed * player->userinfo.GetWBobSpeed() * 35 /
TICRATE*(level.time - 1 + i)) * (360.f / 8192.f);
// [RH] Smooth transitions between bobbing and not-bobbing frames.
// This also fixes the bug where you can "stick" a weapon off-center by
// shooting it when it's at the peak of its swing.
bobtarget = float((player->WeaponState & WF_WEAPONBOBBING) ? player->bob : 0.);
if (curbob != bobtarget)
{
if (fabsf(bobtarget - curbob) <= 1)
{
curbob = bobtarget;
}
else
{
float zoom = MAX(1.f, fabsf(curbob - bobtarget) / 40);
if (curbob > bobtarget)
{
curbob -= zoom;
}
else
{
curbob += zoom;
}
}
}
if (curbob != 0)
{
//[SP] Added in decorate player.viewbob checks
float bobx = float(player->bob * Rangex * (float)player->mo->ViewBob);
float boby = float(player->bob * Rangey * (float)player->mo->ViewBob);
switch (bobstyle)
{
case AWeapon::BobNormal:
xx[i] = bobx * angle.Cos();
yy[i] = boby * fabsf(angle.Sin());
break;
case AWeapon::BobInverse:
xx[i] = bobx*angle.Cos();
yy[i] = boby * (1.f - fabsf(angle.Sin()));
break;
case AWeapon::BobAlpha:
xx[i] = bobx * angle.Sin();
yy[i] = boby * fabsf(angle.Sin());
break;
case AWeapon::BobInverseAlpha:
xx[i] = bobx * angle.Sin();
yy[i] = boby * (1.f - fabsf(angle.Sin()));
break;
case AWeapon::BobSmooth:
xx[i] = bobx*angle.Cos();
yy[i] = 0.5f * (boby * (1.f - ((angle * 2).Cos())));
break;
case AWeapon::BobInverseSmooth:
xx[i] = bobx*angle.Cos();
yy[i] = 0.5f * (boby * (1.f + ((angle * 2).Cos())));
}
}
else
{
xx[i] = 0;
yy[i] = 0;
}
}
*x = (float)(xx[0] * (1. - ticfrac) + xx[1] * ticfrac);
*y = (float)(yy[0] * (1. - ticfrac) + yy[1] * ticfrac);
}
//============================================================================
//
// PROC A_WeaponReady
//
// Readies a weapon for firing or bobbing with its three ancillary functions,
// DoReadyWeaponToSwitch(), DoReadyWeaponToFire() and DoReadyWeaponToBob().
// [XA] Added DoReadyWeaponToReload() and DoReadyWeaponToZoom()
//
//============================================================================
void DoReadyWeaponToSwitch (AActor *self, bool switchable)
{
// Prepare for switching action.
player_t *player;
if (self && (player = self->player))
{
if (switchable)
{
player->WeaponState |= WF_WEAPONSWITCHOK | WF_REFIRESWITCHOK;
}
else
{
// WF_WEAPONSWITCHOK is automatically cleared every tic by P_SetPsprite().
player->WeaponState &= ~WF_REFIRESWITCHOK;
}
}
}
void DoReadyWeaponDisableSwitch (AActor *self, INTBOOL disable)
{
// Discard all switch attempts?
player_t *player;
if (self && (player = self->player))
{
if (disable)
{
player->WeaponState |= WF_DISABLESWITCH;
player->WeaponState &= ~WF_REFIRESWITCHOK;
}
else
{
player->WeaponState &= ~WF_DISABLESWITCH;
}
}
}
void DoReadyWeaponToFire (AActor *self, bool prim, bool alt)
{
player_t *player;
AWeapon *weapon;
if (!self || !(player = self->player) || !(weapon = player->ReadyWeapon))
{
return;
}
// Change player from attack state
if (self->InStateSequence(self->state, self->MissileState) ||
self->InStateSequence(self->state, self->MeleeState))
{
static_cast<APlayerPawn *>(self)->PlayIdle ();
}
// Play ready sound, if any.
if (weapon->ReadySound && player->GetPSprite(PSP_WEAPON)->GetState() == weapon->FindState(NAME_Ready))
{
if (!(weapon->WeaponFlags & WIF_READYSNDHALF) || pr_wpnreadysnd() < 128)
{
S_Sound (self, CHAN_WEAPON, weapon->ReadySound, 1, ATTN_NORM);
}
}
// Prepare for firing action.
player->WeaponState |= ((prim ? WF_WEAPONREADY : 0) | (alt ? WF_WEAPONREADYALT : 0));
return;
}
void DoReadyWeaponToBob (AActor *self)
{
if (self && self->player && self->player->ReadyWeapon)
{
// Prepare for bobbing action.
self->player->WeaponState |= WF_WEAPONBOBBING;
self->player->GetPSprite(PSP_WEAPON)->x = 0;
self->player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP;
}
}
void DoReadyWeaponToGeneric(AActor *self, int paramflags)
{
int flags = 0;
for (size_t i = 0; i < countof(ButtonChecks); ++i)
{
if (paramflags & ButtonChecks[i].ReadyFlag)
{
flags |= ButtonChecks[i].StateFlag;
}
}
if (self != NULL && self->player != NULL)
{
self->player->WeaponState |= flags;
}
}
DEFINE_ACTION_FUNCTION(AStateProvider, A_WeaponReady)
{
PARAM_ACTION_PROLOGUE(AStateProvider);
PARAM_INT(flags);
DoReadyWeaponToSwitch(self, !(flags & WRF_NoSwitch));
if ((flags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(self, !(flags & WRF_NoPrimary), !(flags & WRF_NoSecondary));
if (!(flags & WRF_NoBob)) DoReadyWeaponToBob(self);
DoReadyWeaponToGeneric(self, flags);
DoReadyWeaponDisableSwitch(self, flags & WRF_DisableSwitch);
return 0;
*x = *y = 0;
}
//---------------------------------------------------------------------------
@ -880,7 +604,7 @@ static void P_CheckWeaponButtons (player_t *player)
{
return;
}
AWeapon *weapon = player->ReadyWeapon;
auto weapon = player->ReadyWeapon;
if (weapon == nullptr)
{
return;
@ -1333,12 +1057,13 @@ void player_t::DestroyPSprites()
//
//------------------------------------------------------------------------------------
void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int index)
void P_SetSafeFlash(AInventory *weapon, player_t *player, FState *flashstate, int index)
{
auto wcls = PClass::FindActor(NAME_Weapon);
if (flashstate != nullptr)
{
PClassActor *cls = weapon->GetClass();
while (cls != RUNTIME_CLASS(AWeapon))
while (cls != wcls)
{
if (cls->OwnsState(flashstate))
{
@ -1376,7 +1101,7 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i
DEFINE_ACTION_FUNCTION(_PlayerInfo, SetSafeFlash)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_OBJECT_NOT_NULL(weapon, AWeapon);
PARAM_OBJECT_NOT_NULL(weapon, AInventory);
PARAM_POINTER(state, FState);
PARAM_INT(index);
P_SetSafeFlash(weapon, self, state, index);
@ -1421,6 +1146,33 @@ void DPSprite::OnDestroy()
//
//------------------------------------------------------------------------
float DPSprite::GetYAdjust(bool fullscreen)
{
auto weapon = GetCaller();
if (weapon != nullptr && weapon->IsKindOf(NAME_Weapon))
{
auto fYAd = weapon->FloatVar(NAME_YAdjust);
if (fYAd != 0)
{
if (fullscreen)
{
return (float)fYAd;
}
else
{
return (float)(StatusBar->GetDisplacement() * fYAd);
}
}
}
return 0;
}
//------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------
ADD_STAT(psprites)
{
FString out;

View file

@ -92,6 +92,7 @@ public:
void ResetInterpolation() { oldx = x; oldy = y; }
void OnDestroy() override;
std::pair<FRenderStyle, float> GetRenderStyle(FRenderStyle ownerstyle, double owneralpha);
float GetYAdjust(bool fullscreen);
double x, y, alpha;
double oldx, oldy;
@ -124,7 +125,6 @@ void P_CalcSwing (player_t *player);
void P_SetPsprite(player_t *player, PSPLayers id, FState *state, bool pending = false);
void P_BringUpWeapon (player_t *player);
void P_FireWeapon (player_t *player);
void P_DropWeapon (player_t *player);
void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac);
DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget = NULL, int aimflags = 0);
AActor *P_AimTarget(AActor *mo);

View file

@ -972,6 +972,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload)
("level.fogdensity", level.fogdensity)
("level.outsidefogdensity", level.outsidefogdensity)
("level.skyfog", level.skyfog)
("level.deathsequence", level.deathsequence)
("level.bodyqueslot", level.bodyqueslot)
.Array("level.bodyque", level.bodyque, level.BODYQUESIZE);
@ -1025,7 +1026,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload)
{
if (playeringame[i] && players[i].mo != NULL)
{
players[i].mo->SetupWeaponSlots();
FWeaponSlots::SetupWeaponSlots(players[i].mo);
}
}
}

View file

@ -203,9 +203,14 @@ bool P_Teleport (AActor *thing, DVector3 pos, DAngle angle, int flags)
// [BC] && bHaltVelocity.
if (thing->player && ((flags & TELF_DESTFOG) || !(flags & TELF_KEEPORIENTATION)) && !(flags & TELF_KEEPVELOCITY))
{
// Freeze player for about .5 sec
if (thing->Inventory == NULL || !thing->Inventory->GetNoTeleportFreeze())
thing->reactiontime = 18;
int time = 18;
IFVIRTUALPTR(thing, APlayerPawn, GetTeleportFreezeTime)
{
VMValue param = thing;
VMReturn ret(&time);
VMCall(func, &param, 1, &ret, 1);
}
thing->reactiontime = time;
}
if (thing->flags & MF_MISSILE)
{

View file

@ -293,6 +293,13 @@ CCMD (playerclasses)
}
}
DEFINE_ACTION_FUNCTION(AActor, Substitute)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(replace, AActor);
DObject::StaticPointerSubstitution(self, replace);
return 0;
}
//
// Movement.
@ -648,9 +655,9 @@ void player_t::SendPitchLimits() const
bool player_t::HasWeaponsInSlot(int slot) const
{
for (int i = 0; i < weapons.Slots[slot].Size(); i++)
for (int i = 0; i < weapons.SlotSize(slot); i++)
{
PClassActor *weap = weapons.Slots[slot].GetWeapon(i);
PClassActor *weap = weapons.GetWeapon(slot, i);
if (weap != NULL && mo->FindInventory(weap)) return true;
}
return false;
@ -683,12 +690,13 @@ bool player_t::Resurrect()
}
if (ReadyWeapon != nullptr)
{
P_SetPsprite(this, PSP_WEAPON, ReadyWeapon->GetUpState());
PendingWeapon = ReadyWeapon;
P_BringUpWeapon(this);
}
if (morphTics)
{
P_UndoPlayerMorph(this, this);
P_UnmorphActor(mo, mo);
}
// player is now alive.
@ -772,6 +780,12 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, GetNoAutostartMap)
ACTION_RETURN_INT(self->userinfo.GetNoAutostartMap());
}
DEFINE_ACTION_FUNCTION(_PlayerInfo, GetWBobSpeed)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
ACTION_RETURN_FLOAT(self->userinfo.GetWBobSpeed());
}
//===========================================================================
//
@ -819,16 +833,16 @@ void APlayerPawn::Serialize(FSerializer &arc)
//===========================================================================
//
// APlayerPawn :: MarkPrecacheSounds
// APlayerPawn :: MarkPlayerSounds
//
//===========================================================================
void APlayerPawn::MarkPrecacheSounds() const
DEFINE_ACTION_FUNCTION(APlayerPawn, MarkPlayerSounds)
{
Super::MarkPrecacheSounds();
S_MarkPlayerSounds(GetSoundClass());
PARAM_SELF_PROLOGUE(APlayerPawn);
S_MarkPlayerSounds(self->GetSoundClass());
return 0;
}
//===========================================================================
//
// APlayerPawn :: BeginPlay
@ -909,7 +923,7 @@ void APlayerPawn::Tick()
void APlayerPawn::PostBeginPlay()
{
Super::PostBeginPlay();
SetupWeaponSlots();
FWeaponSlots::SetupWeaponSlots(this);
// Voodoo dolls: restore original floorz/ceilingz logic
if (player == NULL || player->mo != this)
@ -924,40 +938,6 @@ void APlayerPawn::PostBeginPlay()
}
}
//===========================================================================
//
// APlayerPawn :: SetupWeaponSlots
//
// Sets up the default weapon slots for this player. If this is also the
// local player, determines local modifications and sends those across the
// network. Ignores voodoo dolls.
//
//===========================================================================
void APlayerPawn::SetupWeaponSlots()
{
if (player != NULL && player->mo == this)
{
player->weapons.StandardSetup(GetClass());
// If we're the local player, then there's a bit more work to do.
// This also applies if we're a bot and this is the net arbitrator.
if (player - players == consoleplayer ||
(player->Bot != NULL && consoleplayer == Net_Arbitrator))
{
FWeaponSlots local_slots(player->weapons);
if (player->Bot != NULL)
{ // Bots only need weapons from KEYCONF, not INI modifications.
P_PlaybackKeyConfWeapons(&local_slots);
}
else
{
local_slots.LocalSetup(GetClass());
}
local_slots.SendDifferences(int(player - players), player->weapons);
}
}
}
//===========================================================================
//
// APlayerPawn :: AddInventory
@ -1067,73 +1047,6 @@ bool APlayerPawn::UseInventory (AInventory *item)
return true;
}
//===========================================================================
//
// APlayerPawn :: BestWeapon
//
// Returns the best weapon a player has, possibly restricted to a single
// type of ammo.
//
//===========================================================================
AWeapon *APlayerPawn::BestWeapon(PClassActor *ammotype)
{
AWeapon *bestMatch = NULL;
int bestOrder = INT_MAX;
AInventory *item;
AWeapon *weap;
bool tomed = NULL != FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true);
// Find the best weapon the player has.
for (item = Inventory; item != NULL; item = item->Inventory)
{
if (!item->IsKindOf(NAME_Weapon))
continue;
weap = static_cast<AWeapon *> (item);
// Don't select it if it's worse than what was already found.
if (weap->SelectionOrder > bestOrder)
continue;
// Don't select it if its primary fire doesn't use the desired ammo.
if (ammotype != NULL &&
(weap->Ammo1 == NULL ||
weap->Ammo1->GetClass() != ammotype))
continue;
// Don't select it if the Tome is active and this isn't the powered-up version.
if (tomed && weap->SisterWeapon != NULL && weap->SisterWeapon->WeaponFlags & WIF_POWERED_UP)
continue;
// Don't select it if it's powered-up and the Tome is not active.
if (!tomed && weap->WeaponFlags & WIF_POWERED_UP)
continue;
// Don't select it if there isn't enough ammo to use its primary fire.
if (!(weap->WeaponFlags & WIF_AMMO_OPTIONAL) &&
!weap->CheckAmmo (AWeapon::PrimaryFire, false))
continue;
// Don't select if if there isn't enough ammo as determined by the weapon's author.
if (weap->MinSelAmmo1 > 0 && (weap->Ammo1 == NULL || weap->Ammo1->Amount < weap->MinSelAmmo1))
continue;
if (weap->MinSelAmmo2 > 0 && (weap->Ammo2 == NULL || weap->Ammo2->Amount < weap->MinSelAmmo2))
continue;
// This weapon is usable!
bestOrder = weap->SelectionOrder;
bestMatch = weap;
}
return bestMatch;
}
DEFINE_ACTION_FUNCTION(APlayerPawn, BestWeapon)
{
PARAM_SELF_PROLOGUE(APlayerPawn);
PARAM_CLASS(ammo, AActor);
ACTION_RETURN_POINTER(self->BestWeapon(ammo));
}
//===========================================================================
//
// APlayerPawn :: PickNewWeapon
@ -1144,31 +1057,19 @@ DEFINE_ACTION_FUNCTION(APlayerPawn, BestWeapon)
//
//===========================================================================
AWeapon *APlayerPawn::PickNewWeapon(PClassActor *ammotype)
AInventory *APlayerPawn::PickNewWeapon(PClassActor *ammotype)
{
AWeapon *best = BestWeapon (ammotype);
if (best != NULL)
AInventory *best = nullptr;
IFVM(PlayerPawn, DropWeapon)
{
player->PendingWeapon = best;
if (player->ReadyWeapon != NULL)
{
P_DropWeapon(player);
}
else if (player->PendingWeapon != WP_NOCHANGE)
{
P_BringUpWeapon (player);
}
VMValue param = player->mo;
VMReturn ret((void**)&best);
VMCall(func, &param, 1, &ret, 1);
}
return best;
}
DEFINE_ACTION_FUNCTION(APlayerPawn, PickNewWeapon)
{
PARAM_SELF_PROLOGUE(APlayerPawn);
PARAM_CLASS(ammo, AActor);
ACTION_RETURN_POINTER(self->PickNewWeapon(ammo));
}
//===========================================================================
//
// APlayerPawn :: GiveDeathmatchInventory
@ -1197,125 +1098,6 @@ void APlayerPawn::GiveDeathmatchInventory()
}
}
//===========================================================================
//
// APlayerPawn :: FilterCoopRespawnInventory
//
// When respawning in coop, this function is called to walk through the dead
// player's inventory and modify it according to the current game flags so
// that it can be transferred to the new live player. This player currently
// has the default inventory, and the oldplayer has the inventory at the time
// of death.
//
//===========================================================================
void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer)
{
AInventory *item, *next, *defitem;
// If we're losing everything, this is really simple.
if (dmflags & DF_COOP_LOSE_INVENTORY)
{
oldplayer->DestroyAllInventory();
return;
}
if (dmflags & (DF_COOP_LOSE_KEYS |
DF_COOP_LOSE_WEAPONS |
DF_COOP_LOSE_AMMO |
DF_COOP_HALVE_AMMO |
DF_COOP_LOSE_ARMOR |
DF_COOP_LOSE_POWERUPS))
{
// Walk through the old player's inventory and destroy or modify
// according to dmflags.
for (item = oldplayer->Inventory; item != NULL; item = next)
{
next = item->Inventory;
// If this item is part of the default inventory, we never want
// to destroy it, although we might want to copy the default
// inventory amount.
defitem = FindInventory (item->GetClass());
if ((dmflags & DF_COOP_LOSE_KEYS) &&
defitem == NULL &&
item->IsKindOf(NAME_Key))
{
item->Destroy();
}
else if ((dmflags & DF_COOP_LOSE_WEAPONS) &&
defitem == NULL &&
item->IsKindOf(NAME_Weapon))
{
item->Destroy();
}
else if ((dmflags & DF_COOP_LOSE_ARMOR) &&
item->IsKindOf(NAME_Armor))
{
if (defitem == NULL)
{
item->Destroy();
}
else if (item->IsKindOf(NAME_BasicArmor))
{
item->IntVar(NAME_SavePercent) = defitem->IntVar(NAME_SavePercent);
item->Amount = defitem->Amount;
}
else if (item->IsKindOf(NAME_HexenArmor))
{
double *SlotsTo = (double*)item->ScriptVar(NAME_Slots, nullptr);
double *SlotsFrom = (double*)defitem->ScriptVar(NAME_Slots, nullptr);
memcpy(SlotsTo, SlotsFrom, 4 * sizeof(double));
}
}
else if ((dmflags & DF_COOP_LOSE_POWERUPS) &&
defitem == NULL &&
item->IsKindOf(NAME_PowerupGiver))
{
item->Destroy();
}
else if ((dmflags & (DF_COOP_LOSE_AMMO | DF_COOP_HALVE_AMMO)) &&
item->IsKindOf(NAME_Ammo))
{
if (defitem == NULL)
{
if (dmflags & DF_COOP_LOSE_AMMO)
{
// Do NOT destroy the ammo, because a weapon might reference it.
item->Amount = 0;
}
else if (item->Amount > 1)
{
item->Amount /= 2;
}
}
else
{
// When set to lose ammo, you get to keep all your starting ammo.
// When set to halve ammo, you won't be left with less than your starting amount.
if (dmflags & DF_COOP_LOSE_AMMO)
{
item->Amount = defitem->Amount;
}
else if (item->Amount > 1)
{
item->Amount = MAX(item->Amount / 2, defitem->Amount);
}
}
}
}
}
// Now destroy the default inventory this player is holding and move
// over the old player's remaining inventory.
DestroyAllInventory();
ObtainInventory (oldplayer);
player->ReadyWeapon = NULL;
PickNewWeapon (NULL);
}
//===========================================================================
//
// APlayerPawn :: GetSoundClass
@ -1449,208 +1231,13 @@ void APlayerPawn::PlayAttacking2 ()
void APlayerPawn::GiveDefaultInventory ()
{
if (player == NULL) return;
// HexenArmor must always be the first item in the inventory because
// it provides player class based protection that should not affect
// any other protection item.
auto myclass = GetClass();
GiveInventoryType(PClass::FindActor(NAME_HexenArmor));
auto harmor = FindInventory(NAME_HexenArmor);
double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr);
double *SlotsIncrement = (double*)harmor->ScriptVar(NAME_SlotsIncrement, nullptr);
Slots[4] = HexenArmor[0];
for (int i = 0; i < 4; ++i)
IFVIRTUAL(APlayerPawn, GiveDefaultInventory)
{
SlotsIncrement[i] = HexenArmor[i + 1];
}
// BasicArmor must come right after that. It should not affect any
// other protection item as well but needs to process the damage
// before the HexenArmor does.
auto barmor = (AInventory*)Spawn(NAME_BasicArmor);
barmor->BecomeItem ();
AddInventory (barmor);
// Now add the items from the DECORATE definition
auto di = GetDropItems();
while (di)
{
PClassActor *ti = PClass::FindActor (di->Name);
if (ti)
{
if (!ti->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{
Printf(TEXTCOLOR_ORANGE "%s is not an inventory item and cannot be given to a player as start item.\n", ti->TypeName.GetChars());
}
else
{
AInventory *item = FindInventory(ti);
if (item != NULL)
{
item->Amount = clamp<int>(
item->Amount + (di->Amount ? di->Amount : ((AInventory *)item->GetDefault())->Amount),
0, item->MaxAmount);
}
else
{
item = static_cast<AInventory *>(Spawn(ti));
item->ItemFlags |= IF_IGNORESKILL; // no skill multiplicators here
item->Amount = di->Amount;
if (item->IsKindOf(NAME_Weapon))
{
// To allow better control any weapon is emptied of
// ammo before being given to the player.
static_cast<AWeapon*>(item)->AmmoGive1 =
static_cast<AWeapon*>(item)->AmmoGive2 = 0;
}
AActor *check;
if (!item->CallTryPickup(this, &check))
{
if (check != this)
{
// Player was morphed. This is illegal at game start.
// This problem is only detectable when it's too late to do something about it...
I_Error("Cannot give morph items when starting a game");
}
item->Destroy();
item = NULL;
}
}
if (item != NULL && item->IsKindOf(NAME_Weapon) &&
static_cast<AWeapon*>(item)->CheckAmmo(AWeapon::EitherFire, false))
{
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *> (item);
}
}
}
di = di->Next;
VMValue params[1] = { (DObject*)this };
VMCall(func, params, 1, nullptr, 0);
}
}
void APlayerPawn::ActivateMorphWeapon ()
{
PClassActor *morphweapon = PClass::FindActor (MorphWeapon);
player->PendingWeapon = WP_NOCHANGE;
if (player->ReadyWeapon != nullptr)
{
player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP;
}
if (morphweapon == nullptr || !morphweapon->IsDescendantOf (RUNTIME_CLASS(AWeapon)))
{ // No weapon at all while morphed!
player->ReadyWeapon = nullptr;
}
else
{
player->ReadyWeapon = static_cast<AWeapon *>(player->mo->FindInventory (morphweapon));
if (player->ReadyWeapon == nullptr)
{
player->ReadyWeapon = static_cast<AWeapon *>(player->mo->GiveInventoryType (morphweapon));
if (player->ReadyWeapon != nullptr)
{
player->ReadyWeapon->GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph
}
}
if (player->ReadyWeapon != nullptr)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetReadyState());
}
}
if (player->ReadyWeapon != nullptr)
{
P_SetPsprite(player, PSP_FLASH, nullptr);
}
player->PendingWeapon = WP_NOCHANGE;
}
//===========================================================================
//
// APlayerPawn :: Die
//
//===========================================================================
void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath)
{
Super::Die (source, inflictor, dmgflags, MeansOfDeath);
if (player != NULL && player->mo == this) player->bonuscount = 0;
if (player != NULL && player->mo != this)
{ // Make the real player die, too
player->mo->CallDie (source, inflictor, dmgflags, MeansOfDeath);
}
else
{
if (player != NULL && (dmflags2 & DF2_YES_WEAPONDROP))
{ // Voodoo dolls don't drop weapons
AWeapon *weap = player->ReadyWeapon;
if (weap != NULL)
{
AInventory *item;
// kgDROP - start - modified copy from a_action.cpp
auto di = weap->GetDropItems();
if (di != NULL)
{
while (di != NULL)
{
if (di->Name != NAME_None)
{
PClassActor *ti = PClass::FindActor(di->Name);
if (ti) P_DropItem (player->mo, ti, di->Amount, di->Probability);
}
di = di->Next;
}
} else
// kgDROP - end
if (weap->SpawnState != NULL &&
weap->SpawnState != ::GetDefault<AActor>()->SpawnState)
{
item = P_DropItem (this, weap->GetClass(), -1, 256);
if (item != NULL && item->IsKindOf(NAME_Weapon))
{
if (weap->AmmoGive1 && weap->Ammo1)
{
static_cast<AWeapon *>(item)->AmmoGive1 = weap->Ammo1->Amount;
}
if (weap->AmmoGive2 && weap->Ammo2)
{
static_cast<AWeapon *>(item)->AmmoGive2 = weap->Ammo2->Amount;
}
item->ItemFlags |= IF_IGNORESKILL;
}
}
else
{
item = P_DropItem (this, weap->AmmoType1, -1, 256);
if (item != NULL)
{
item->Amount = weap->Ammo1->Amount;
item->ItemFlags |= IF_IGNORESKILL;
}
item = P_DropItem (this, weap->AmmoType2, -1, 256);
if (item != NULL)
{
item->Amount = weap->Ammo2->Amount;
item->ItemFlags |= IF_IGNORESKILL;
}
}
}
}
if (!multiplayer && level.info->deathsequence != NAME_None)
{
F_StartIntermission(level.info->deathsequence, FSTATE_EndingGame);
}
}
}
//===========================================================================
//
// A_PlayerScream
@ -2697,6 +2284,7 @@ DEFINE_FIELD(APlayerPawn, AirCapacity)
DEFINE_FIELD(APlayerPawn, FlechetteType)
DEFINE_FIELD(APlayerPawn, DamageFade)
DEFINE_FIELD(APlayerPawn, ViewBob)
DEFINE_FIELD(APlayerPawn, curBob)
DEFINE_FIELD(APlayerPawn, FullHeight)
DEFINE_FIELD(APlayerPawn, SoundClass)
DEFINE_FIELD(APlayerPawn, Face)

View file

@ -296,18 +296,7 @@ void RenderPolyPlayerSprites::RenderSprite(PolyRenderThread *thread, DPSprite *p
viewheight == renderTarget->GetHeight() ||
(renderTarget->GetWidth() > (BASEXCENTER * 2))))
{ // Adjust PSprite for fullscreen views
AWeapon *weapon = dyn_cast<AWeapon>(pspr->GetCaller());
if (weapon != nullptr && weapon->YAdjust != 0)
{
if (renderToCanvas || viewheight == renderTarget->GetHeight())
{
vis.texturemid -= weapon->YAdjust;
}
else
{
vis.texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust;
}
}
vis.texturemid -= pspr->GetYAdjust(renderToCanvas || viewheight == renderTarget->GetHeight());
}
if (pspr->GetID() < PSP_TARGETCENTER)
{ // Move the weapon down for 1280x1024.

View file

@ -2232,18 +2232,20 @@ void AAmbientSound::Serialize(FSerializer &arc)
//==========================================================================
//
// AmbientSound :: MarkPrecacheSounds
// AmbientSound :: MarkAmbientSounds
//
//==========================================================================
void AAmbientSound::MarkPrecacheSounds() const
DEFINE_ACTION_FUNCTION(AAmbientSound, MarkAmbientSounds)
{
Super::MarkPrecacheSounds();
FAmbientSound *ambient = Ambients.CheckKey(args[0]);
PARAM_SELF_PROLOGUE(AAmbientSound);
FAmbientSound *ambient = Ambients.CheckKey(self->args[0]);
if (ambient != NULL)
{
ambient->sound.MarkUsed();
}
return 0;
}
//==========================================================================
@ -2458,3 +2460,11 @@ void S_ParseMusInfo()
}
DEFINE_ACTION_FUNCTION(DObject, MarkSound)
{
PARAM_PROLOGUE;
PARAM_SOUND(sound_id);
sound_id.MarkUsed();
return 0;
}

View file

@ -494,14 +494,9 @@ void S_PrecacheLevel ()
{
IFVIRTUALPTR(actor, AActor, MarkPrecacheSounds)
{
// Without the type cast this picks the 'void *' assignment...
VMValue params[1] = { actor };
VMCall(func, params, 1, nullptr, 0);
}
else
{
actor->MarkPrecacheSounds();
}
}
for (auto snd : gameinfo.PrecachedSounds)
{

View file

@ -179,6 +179,7 @@ std2:
'none' { RET(TK_None); }
'auto' { RET(TK_Auto); }
'property' { RET(TK_Property); }
'flagdef' { RET(ParseVersion >= MakeVersion(3, 7, 0)? TK_FlagDef : TK_Identifier); }
'native' { RET(TK_Native); }
'var' { RET(TK_Var); }
'out' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Out : TK_Identifier); }

View file

@ -67,6 +67,7 @@ xx(TK_Long, "'long'")
xx(TK_ULong, "'ulong'")
xx(TK_Void, "'void'")
xx(TK_Struct, "'struct'")
xx(TK_FlagDef, "'flagdef'")
xx(TK_Property, "'property'")
xx(TK_Class, "'class'")
xx(TK_Enum, "'enum'")

View file

@ -803,7 +803,6 @@ VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, const VersionInfo
void FFunctionBuildList::Build()
{
int errorcount = 0;
int codesize = 0;
int datasize = 0;
FILE *dump = nullptr;
@ -912,7 +911,8 @@ void FFunctionBuildList::Build()
}
VMFunction::CreateRegUseInfo();
FScriptPosition::StrictErrors = false;
if (Args->CheckParm("-dumpjit")) DumpJit();
if (FScriptPosition::ErrorCounter == 0 && Args->CheckParm("-dumpjit")) DumpJit();
mItems.Clear();
mItems.ShrinkToFit();
FxAlloc.FreeAllBlocks();

View file

@ -1187,7 +1187,7 @@ static void ParseActor(FScanner &sc, PNamespace *ns)
}
try
{
GetDefaultByType(info)->Finalize(bag.statedef);
FinalizeClass(info, bag.statedef);
}
catch (CRecoverableError &err)
{

View file

@ -195,6 +195,29 @@ PProperty::PProperty(FName name, TArray<PField *> &fields)
Variables = std::move(fields);
}
/* PProperty *****************************************************************/
IMPLEMENT_CLASS(PPropFlag, false, false)
//==========================================================================
//
// PField - Default Constructor
//
//==========================================================================
PPropFlag::PPropFlag()
: PSymbol(NAME_None)
{
}
PPropFlag::PPropFlag(FName name, PField * field, int bitValue, bool forDecorate)
: PSymbol(name)
{
Offset = field;
bitval = bitValue;
decorateOnly = forDecorate;
}
//==========================================================================
//
//

View file

@ -99,10 +99,11 @@ class PPropFlag : public PSymbol
{
DECLARE_CLASS(PPropFlag, PSymbol);
public:
PPropFlag(FName name, PField *offset, int bitval);
PPropFlag(FName name, PField *offset, int bitval, bool decorateonly);
PField *Offset;
int bitval;
bool decorateOnly;
protected:
PPropFlag();

View file

@ -51,6 +51,8 @@
#include "v_text.h"
#include "backend/codegen.h"
#include "stats.h"
#include "info.h"
#include "thingdef.h"
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void InitThingdef();
@ -60,6 +62,69 @@ void InitThingdef();
static TMap<FState *, FScriptPosition> StateSourceLines;
static FScriptPosition unknownstatesource("unknown file", 0);
//==========================================================================
//
// PClassActor :: Finalize
//
// Installs the parsed states and does some sanity checking
//
//==========================================================================
void FinalizeClass(PClass *ccls, FStateDefinitions &statedef)
{
if (!ccls->IsDescendantOf(NAME_Actor)) return;
auto cls = static_cast<PClassActor*>(ccls);
try
{
statedef.FinishStates(cls);
}
catch (CRecoverableError &)
{
statedef.MakeStateDefines(nullptr);
throw;
}
auto def = GetDefaultByType(cls);
statedef.InstallStates(cls, def);
statedef.MakeStateDefines(nullptr);
if (cls->IsDescendantOf(NAME_Inventory))
{
def->flags |= MF_SPECIAL;
}
if (cls->IsDescendantOf(NAME_Weapon))
{
FState *ready = def->FindState(NAME_Ready);
FState *select = def->FindState(NAME_Select);
FState *deselect = def->FindState(NAME_Deselect);
FState *fire = def->FindState(NAME_Fire);
auto TypeName = cls->TypeName;
// Consider any weapon without any valid state abstract and don't output a warning
// This is for creating base classes for weapon groups that only set up some properties.
if (ready || select || deselect || fire)
{
if (!ready)
{
I_Error("Weapon %s doesn't define a ready state.", TypeName.GetChars());
}
if (!select)
{
I_Error("Weapon %s doesn't define a select state.", TypeName.GetChars());
}
if (!deselect)
{
I_Error("Weapon %s doesn't define a deselect state.", TypeName.GetChars());
}
if (!fire)
{
I_Error("Weapon %s doesn't define a fire state.", TypeName.GetChars());
}
}
}
}
//==========================================================================
//
// Saves the state's source lines for error messages during postprocessing
@ -186,6 +251,7 @@ PFunction *FindClassMemberFunction(PContainerType *selfcls, PContainerType *func
auto cls_target = funcsym ? PType::toClass(funcsym->OwningClass) : nullptr;
if (funcsym == nullptr)
{
if (PClass::FindClass(name)) return nullptr; // Special case when a class's member variable hides a global class name. This should still work.
sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars());
}
else if ((funcsym->Variants[0].Flags & VARF_Private) && symtable != &funccls->Symbols)
@ -244,9 +310,10 @@ static void CheckForUnsafeStates(PClassActor *obj)
TMap<FState *, bool> checked;
ENamedName *test;
if (obj->IsDescendantOf(NAME_Weapon))
auto cwtype = PClass::FindActor(NAME_Weapon);
if (obj->IsDescendantOf(cwtype))
{
if (obj->Size == RUNTIME_CLASS(AWeapon)->Size) return; // This class cannot have user variables.
if (obj->Size == cwtype->Size) return; // This class cannot have user variables.
test = weaponstates;
}
else
@ -257,7 +324,7 @@ static void CheckForUnsafeStates(PClassActor *obj)
if (obj->Size == citype->Size) return; // This class cannot have user variables.
test = pickupstates;
}
else return; // something else derived from AStateProvider. We do not know what this may be.
else return; // something else derived from StateProvider. We do not know what this may be.
}
for (; *test != NAME_None; test++)
@ -416,7 +483,7 @@ void LoadActors()
CheckStates(ti);
if (ti->bDecorateClass && ti->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
if (ti->bDecorateClass && ti->IsDescendantOf(NAME_StateProvider))
{
// either a DECORATE based weapon or CustomInventory.
// These are subject to relaxed rules for user variables in states.

View file

@ -68,6 +68,7 @@ struct FFlagDef
int varflags;
};
void FinalizeClass(PClass *cls, FStateDefinitions &statedef);
FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict = false);
void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int index);
bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index);

View file

@ -442,37 +442,6 @@ static FFlagDef InventoryFlagDefs[] =
DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP),
};
static FFlagDef WeaponFlagDefs[] =
{
// Weapon flags
DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, READYSNDHALF, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, DONTBOB, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, AXEBLOOD, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NOALERT, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, AMMO_OPTIONAL, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, ALT_AMMO_OPTIONAL, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, PRIMARY_USES_BOTH, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, WIMPY_WEAPON, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, POWERED_UP, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, STAFF2_KICKBACK, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF_BOT, EXPLOSIVE, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, MELEEWEAPON, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF_BOT, BFG, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, CHEATNOTWEAPON, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NO_AUTO_SWITCH, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, AMMO_CHECKBOTH, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags),
DEFINE_DUMMY_FLAG(NOLMS, false),
DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false),
};
static FFlagDef PlayerPawnFlagDefs[] =
{
// PlayerPawn flags
@ -505,12 +474,13 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int
{ &RUNTIME_CLASS_CASTLESS(AActor), MoreFlagDefs, countof(MoreFlagDefs), 1 },
{ &RUNTIME_CLASS_CASTLESS(AActor), InternalActorFlagDefs, countof(InternalActorFlagDefs), 2 },
{ &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(ADynamicLight),DynLightFlagDefs, countof(DynLightFlagDefs), 3 },
};
#define NUM_FLAG_LISTS (countof(FlagLists))
static FFlagDef forInternalFlags;
//==========================================================================
//
// Find a flag by name using a binary search
@ -548,6 +518,47 @@ static FFlagDef *FindFlag (FFlagDef *flags, int numflags, const char *flag)
FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict)
{
if (part2 == nullptr)
{
FStringf internalname("@flagdef@%s", part1);
FName name(internalname, true);
if (name != NAME_None)
{
auto field = dyn_cast<PPropFlag>(type->FindSymbol(name, true));
if (field != nullptr && (!strict || !field->decorateOnly))
{
forInternalFlags.fieldsize = 4;
forInternalFlags.name = "";
forInternalFlags.flagbit = field->Offset? 1 << field->bitval : DEPF_UNUSED;
forInternalFlags.structoffset = field->Offset? (int)field->Offset->Offset : -1;
forInternalFlags.varflags = 0;
return &forInternalFlags;
}
}
}
else
{
FStringf internalname("@flagdef@%s.%s", part1, part2);
FName name(internalname, true);
if (name != NAME_None)
{
auto field = dyn_cast<PPropFlag>(type->FindSymbol(name, true));
if (field != nullptr)
{
forInternalFlags.fieldsize = 4;
forInternalFlags.name = "";
forInternalFlags.flagbit = field->Offset ? 1 << field->bitval : DEPF_UNUSED;
forInternalFlags.structoffset = field->Offset ? (int)field->Offset->Offset : -1;
forInternalFlags.varflags = 0;
return &forInternalFlags;
}
}
}
// Not found. Try the internal flag definitions.
FFlagDef *def;
if (part2 == NULL)
@ -949,6 +960,11 @@ void InitThingdef()
fcp->Size = sizeof(FCheckPosition);
fcp->Align = alignof(FCheckPosition);
//This must also have its size set.
auto frp = NewStruct("FRailParams", nullptr);
frp->Size = sizeof(FRailParams);
frp->Align = alignof(FRailParams);
FieldTable.Clear();
if (FieldTable.Size() == 0)
@ -967,17 +983,6 @@ void InitThingdef()
void SynthesizeFlagFields()
{
// These are needed for inserting the flag symbols
/*
NewClassType(RUNTIME_CLASS(DObject));
NewClassType(RUNTIME_CLASS(DThinker));
NewClassType(RUNTIME_CLASS(AActor));
NewClassType(RUNTIME_CLASS(AInventory));
NewClassType(RUNTIME_CLASS(AStateProvider));
NewClassType(RUNTIME_CLASS(AWeapon));
NewClassType(RUNTIME_CLASS(APlayerPawn));
NewClassType(RUNTIME_CLASS(ADynamicLight));
*/
// synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them.
for (auto &fl : FlagLists)
{

View file

@ -1181,20 +1181,20 @@ DEFINE_CLASS_PROPERTY(pickupannouncerentry, S, Inventory)
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(defaultkickback, 0, Weapon)
DEFINE_SCRIPTED_PROPERTY(defaultkickback, 0, Weapon)
{
defaults->Kickback = gameinfo.defKickback;
defaults->IntVar(NAME_Kickback) = gameinfo.defKickback;
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(bobstyle, S, Weapon)
DEFINE_SCRIPTED_PROPERTY(bobstyle, S, Weapon)
{
static const char *names[] = { "Normal", "Inverse", "Alpha", "InverseAlpha", "Smooth", "InverseSmooth", NULL };
static const int styles[] = { AWeapon::BobNormal,
AWeapon::BobInverse, AWeapon::BobAlpha, AWeapon::BobInverseAlpha,
AWeapon::BobSmooth, AWeapon::BobInverseSmooth, };
static const EBobStyle styles[] = { EBobStyle::BobNormal,
EBobStyle::BobInverse, EBobStyle::BobAlpha, EBobStyle::BobInverseAlpha,
EBobStyle::BobSmooth, EBobStyle::BobInverseSmooth, };
PROP_STRING_PARM(id, 0);
int match = MatchString(id, names);
if (match < 0)
@ -1202,22 +1202,13 @@ DEFINE_CLASS_PROPERTY(bobstyle, S, Weapon)
I_Error("Unknown bobstyle %s", id);
match = 0;
}
defaults->BobStyle = styles[match];
defaults->IntVar(NAME_BobStyle) = (int)styles[match];
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(slotpriority, F, Weapon)
{
PROP_DOUBLE_PARM(i, 0);
defaults->SlotPriority = int(i*65536);
}
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(preferredskin, S, Weapon)
DEFINE_SCRIPTED_PROPERTY(preferredskin, S, Weapon)
{
PROP_STRING_PARM(str, 0);
// NoOp - only for Skulltag compatibility

View file

@ -179,6 +179,11 @@ struct VMReturn
Location = loc;
RegType = REGT_FLOAT;
}
void Vec2At(DVector2 *loc)
{
Location = loc;
RegType = REGT_FLOAT | REGT_MULTIREG2;
}
void StringAt(FString *loc)
{
Location = loc;
@ -192,6 +197,7 @@ struct VMReturn
VMReturn() { }
VMReturn(int *loc) { IntAt(loc); }
VMReturn(double *loc) { FloatAt(loc); }
VMReturn(DVector2 *loc) { Vec2At(loc); }
VMReturn(FString *loc) { StringAt(loc); }
VMReturn(void **loc) { PointerAt(loc); }
};
@ -691,6 +697,15 @@ VMFunction *FindVMFunction(PClass *cls, const char *name);
FString FStringFormat(VM_ARGS, int offset = 0);
#define IFVM(cls, funcname) \
static VMFunction * func = nullptr; \
if (func == nullptr) { \
PClass::FindFunction(&func, #cls, #funcname); \
assert(func); \
} \
if (func != nullptr)
unsigned GetVirtualIndex(PClass *cls, const char *funcname);

View file

@ -348,6 +348,17 @@ static void PrintProperty(FLispString &out, ZCC_TreeNode *node)
out.Close();
}
static void PrintFlagDef(FLispString &out, ZCC_TreeNode *node)
{
ZCC_FlagDef *snode = (ZCC_FlagDef *)node;
out.Break();
out.Open("flagdef");
out.AddName(snode->NodeName);
out.AddName(snode->RefName);
out.AddInt(snode->BitValue);
out.Close();
}
static void PrintStaticArrayState(FLispString &out, ZCC_TreeNode *node)
{
auto *snode = (ZCC_StaticArrayStatement *)node;
@ -959,6 +970,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *
PrintExprClassCast,
PrintStaticArrayState,
PrintProperty,
PrintFlagDef,
};
FString ZCC_PrintAST(ZCC_TreeNode *root)

View file

@ -313,6 +313,7 @@ class_innards(X) ::= . { X = NULL; }
class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); }
%type property_def{ZCC_Property *}
%type flag_def{ZCC_FlagDef *}
%type struct_def{ZCC_Struct *}
%type enum_def {ZCC_Enum *}
%type states_def {ZCC_States *}
@ -325,6 +326,7 @@ class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= property_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= flag_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= staticarray_statement(A). { X = A; /*X-overwrites-A*/ }
@ -344,6 +346,16 @@ property_def(X) ::= PROPERTY(T) IDENTIFIER(A) COLON identifier_list(B) SEMICOLON
X = def;
}
flag_def(X) ::= FLAGDEF(T) IDENTIFIER(A) COLON IDENTIFIER(B) COMMA INTCONST(C) SEMICOLON.
{
NEW_AST_NODE(FlagDef,def,T);
def->NodeName = A.Name();
def->RefName = B.Name();
def->BitValue = C.Int;
X = def;
}
identifier_list(X) ::= IDENTIFIER(A).
{
NEW_AST_NODE(Identifier,id,A);

View file

@ -175,6 +175,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode)
cls->Properties.Push(static_cast<ZCC_Property *>(node));
break;
case AST_FlagDef:
cls->FlagDefs.Push(static_cast<ZCC_FlagDef*>(node));
break;
case AST_VarDeclarator:
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
break;
@ -1375,6 +1379,10 @@ void ZCCCompiler::CompileAllProperties()
{
if (c->Properties.Size() > 0)
CompileProperties(c->ClassType(), c->Properties, c->Type()->TypeName);
if (c->FlagDefs.Size() > 0)
CompileFlagDefs(c->ClassType(), c->FlagDefs, c->Type()->TypeName);
}
}
@ -1415,20 +1423,88 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Proper
fields.Push(f);
id = (ZCC_Identifier*)id->SiblingNext;
} while (id != p->Body);
FString qualifiedname;
// Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen.
// All these will be removed from the symbol table after the compiler finishes to free up the allocated space.
FName name = FName(p->NodeName);
if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars());
else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars());
fields.ShrinkToFit();
if (!type->VMType->Symbols.AddSymbol(Create<PProperty>(qualifiedname, fields)))
{
Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars());
}
}
}
return true;
}
FString qualifiedname;
// Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen.
// All these will be removed from the symbol table after the compiler finishes to free up the allocated space.
FName name = FName(p->NodeName);
if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars());
else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars());
//==========================================================================
//
// ZCCCompiler :: CompileProperties
//
// builds the internal structure of a single class or struct
//
//==========================================================================
fields.ShrinkToFit();
if (!type->VMType->Symbols.AddSymbol(Create<PProperty>(qualifiedname, fields)))
bool ZCCCompiler::CompileFlagDefs(PClass *type, TArray<ZCC_FlagDef *> &Properties, FName prefix)
{
if (!type->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
Error(Properties[0], "Flags can only be defined for actors");
return false;
}
for (auto p : Properties)
{
PField *field;
FName referenced = FName(p->RefName);
if (FName(p->NodeName) == FName("prefix") && Wads.GetLumpFile(Lump) == 0)
{
Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars());
// only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use.
prefix = referenced;
}
else
{
if (referenced != NAME_None)
{
field = dyn_cast<PField>(type->FindSymbol(referenced, true));
if (field == nullptr)
{
Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars());
}
if (!field->Type->isInt() || field->Type->Size != 4)
{
Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars());
}
}
else field = nullptr;
FString qualifiedname;
// Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen.
// All these will be removed from the symbol table after the compiler finishes to free up the allocated space.
FName name = FName(p->NodeName);
for (int i = 0; i < 2; i++)
{
if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars());
else
{
if (prefix == NAME_None) continue;
qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars());
}
if (!type->VMType->Symbols.AddSymbol(Create<PPropFlag>(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None)))
{
Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars());
}
}
if (field != nullptr)
type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue);
}
}
return true;
}
@ -3105,7 +3181,7 @@ void ZCCCompiler::CompileStates()
}
try
{
GetDefaultByType(c->ClassType())->Finalize(statedef);
FinalizeClass(c->ClassType(), statedef);
}
catch (CRecoverableError &err)
{

View file

@ -54,6 +54,7 @@ struct ZCC_ClassWork : public ZCC_StructWork
TArray<ZCC_Default *> Defaults;
TArray<ZCC_States *> States;
TArray<ZCC_Property *> Properties;
TArray<ZCC_FlagDef *> FlagDefs;
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
{
@ -110,6 +111,7 @@ private:
bool CompileFields(PContainerType *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false);
void CompileAllProperties();
bool CompileProperties(PClass *type, TArray<ZCC_Property *> &Properties, FName prefix);
bool CompileFlagDefs(PClass *type, TArray<ZCC_FlagDef *> &FlagDefs, FName prefix);
FString FlagsToString(uint32_t flags);
PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember);
PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PContainerType *cls);

View file

@ -150,6 +150,7 @@ static void InitTokenMap()
TOKENDEF ('}', ZCC_RBRACE);
TOKENDEF (TK_Struct, ZCC_STRUCT);
TOKENDEF (TK_Property, ZCC_PROPERTY);
TOKENDEF (TK_FlagDef, ZCC_FLAGDEF);
TOKENDEF (TK_Transient, ZCC_TRANSIENT);
TOKENDEF (TK_Enum, ZCC_ENUM);
TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte);

View file

@ -134,6 +134,7 @@ enum EZCCTreeNodeType
AST_ClassCast,
AST_StaticArrayStatement,
AST_Property,
AST_FlagDef,
NUM_AST_NODE_TYPES
};
@ -226,6 +227,12 @@ struct ZCC_Property : ZCC_NamedNode
ZCC_TreeNode *Body;
};
struct ZCC_FlagDef : ZCC_NamedNode
{
ENamedName RefName;
int BitValue;
};
struct ZCC_Class : ZCC_Struct
{
ZCC_Identifier *ParentName;

104
src/scriptutil.cpp Normal file
View file

@ -0,0 +1,104 @@
//-----------------------------------------------------------------------------
//
// Copyright 2018 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
//
// DESCRIPTION: generalized interface for implementing ACS/FS functions
// in ZScript.
//
//-----------------------------------------------------------------------------
#include "i_system.h"
#include "tarray.h"
#include "dobject.h"
#include "vm.h"
#include "scriptutil.h"
#include "p_acs.h"
static TArray<VMValue> parameters;
static TMap<FName, VMFunction*> functions;
void ScriptUtil::BuildParameters(va_list ap)
{
for(int type = va_arg(ap, int); type != End; type = va_arg(ap, int))
{
switch (type)
{
case Int:
parameters.Push(VMValue(va_arg(ap, int)));
break;
case Pointer:
case Class: // this is just a pointer.
case String: // must be passed by reference to a persistent location!
parameters.Push(VMValue(va_arg(ap, void*)));
break;
case Float:
parameters.Push(VMValue(va_arg(ap, double)));
break;
case ACSClass:
parameters.Push(VMValue(PClass::FindActor(FBehavior::StaticLookupString(va_arg(ap, int)))));
break;
}
}
}
void ScriptUtil::RunFunction(FName functionname, unsigned paramstart, VMReturn &returns)
{
VMFunction *func = nullptr;
auto check = functions.CheckKey(functionname);
if (!check)
{
PClass::FindFunction(&func, NAME_ScriptUtil, functionname);
if (func == nullptr)
{
I_Error("Call to undefined function ScriptUtil.%s", functionname.GetChars());
}
functions.Insert(functionname, func);
}
else func = *check;
VMCall(func, &parameters[paramstart], parameters.Size() - paramstart, &returns, 1);
}
int ScriptUtil::Exec(FName functionname, ...)
{
unsigned paramstart = parameters.Size();
va_list ap;
va_start(ap, functionname);
try
{
BuildParameters(ap);
int ret = 0;
VMReturn returns(&ret);
RunFunction(functionname, paramstart, returns);
va_end(ap);
parameters.Clamp(paramstart);
return ret;
}
catch(...)
{
va_end(ap);
parameters.Clamp(paramstart);
throw;
}
}

27
src/scriptutil.h Normal file
View file

@ -0,0 +1,27 @@
#pragma once
#include <stdarg.h>
#include "name.h"
class ScriptUtil
{
static void BuildParameters(va_list ap);
static void RunFunction(FName function, unsigned paramstart, VMReturn &returns);
public:
enum
{
End,
Int,
Pointer,
Float,
String,
Class,
ACSString, // convenience helpers taking an ACS string index instead of a string
ACSClass,
};
static int Exec(FName functionname, ...);
};

View file

@ -295,18 +295,7 @@ namespace swrenderer
viewheight == viewport->RenderTarget->GetHeight() ||
(viewport->RenderTarget->GetWidth() > (BASEXCENTER * 2))))
{ // Adjust PSprite for fullscreen views
AWeapon *weapon = dyn_cast<AWeapon>(pspr->GetCaller());
if (weapon != nullptr && weapon->YAdjust != 0)
{
if (renderToCanvas || viewheight == viewport->RenderTarget->GetHeight())
{
vis.texturemid -= weapon->YAdjust;
}
else
{
vis.texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust;
}
}
vis.texturemid -= pspr->GetYAdjust(renderToCanvas || viewheight == viewport->RenderTarget->GetHeight());
}
if (pspr->GetID() < PSP_TARGETCENTER)
{ // Move the weapon down for 1280x1024.

View file

@ -87,11 +87,11 @@ const char *GetVersionString();
#define SAVEGAME_EXT "zds"
// MINSAVEVER is the minimum level snapshot version that can be loaded.
#define MINSAVEVER 4551
#define MINSAVEVER 4553
// Use 4500 as the base git save version, since it's higher than the
// SVN revision ever got.
#define SAVEVER 4552
#define SAVEVER 4553
// This is so that derivates can use the same savegame versions without worrying about engine compatibility
#define GAMESIG "GZDOOM"