- moved the weapon selection logic to PlayerPawn as overridable virtual functions.

This commit is contained in:
Christoph Oelckers 2018-11-24 22:22:36 +01:00
parent 51ee623b3b
commit f260709e73
5 changed files with 270 additions and 248 deletions

View File

@ -275,236 +275,22 @@ CCMD (turnspeeds)
} }
} }
//===========================================================================
//
// 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 *PickWeapon(player_t *player, int slot, bool checkammo)
{
int i, j;
if (player->mo == nullptr)
{
return nullptr;
}
int Size = player->weapons.SlotSize(slot);
// Does this slot even have any weapons?
if (Size == 0)
{
return player->ReadyWeapon;
}
if (player->ReadyWeapon != nullptr)
{
for (i = 0; (unsigned)i < Size; i++)
{
auto weapontype = player->weapons.GetWeapon(slot, i);
if (weapontype == player->ReadyWeapon->GetClass() ||
(player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP &&
player->ReadyWeapon->SisterWeapon != nullptr &&
player->ReadyWeapon->SisterWeapon->GetClass() == weapontype))
{
for (j = (i == 0 ? Size - 1 : i - 1);
j != i;
j = (j == 0 ? Size - 1 : j - 1))
{
auto weapontype2 = player->weapons.GetWeapon(slot, j);
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory(weapontype2));
if (weap != nullptr && weap->IsKindOf(NAME_Weapon))
{
if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false))
{
return weap;
}
}
}
}
}
}
for (i = Size - 1; i >= 0; i--)
{
auto weapontype = player->weapons.GetWeapon(slot, i);
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory(weapontype));
if (weap != nullptr && weap->IsKindOf(NAME_Weapon))
{
if (!checkammo || weap->CheckAmmo(AWeapon::EitherFire, false))
{
return weap;
}
}
}
return player->ReadyWeapon;
}
//===========================================================================
//
// 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)
{
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;
}
}
//===========================================================================
//
// 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 *PickNextWeapon(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 = NUM_WEAPON_SLOTS - 1;
startindex = player->weapons.SlotSize(startslot) - 1;
}
slot = startslot;
index = startindex;
do
{
if (++index >= player->weapons.SlotSize(slot))
{
index = 0;
slotschecked++;
if (++slot >= NUM_WEAPON_SLOTS)
{
slot = 0;
}
}
PClassActor *type = player->weapons.GetWeapon(slot, 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 *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 = player->weapons.SlotSize(slot) - 1;
}
PClassActor *type = player->weapons.GetWeapon(slot, 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;
}
CCMD (slot) CCMD (slot)
{ {
if (argv.argc() > 1) if (argv.argc() > 1)
{ {
int slot = atoi (argv[1]); int slot = atoi (argv[1]);
if (slot < NUM_WEAPON_SLOTS) auto mo = players[consoleplayer].mo;
if (slot < NUM_WEAPON_SLOTS && mo)
{ {
// Needs to be redone // Needs to be redone
SendItemUse = PickWeapon(&players[consoleplayer], slot, !(dmflags2 & DF2_DONTCHECKAMMO)); IFVIRTUALPTR(mo, APlayerPawn, PickWeapon)
{
VMValue param[] = { mo, slot, !(dmflags2 & DF2_DONTCHECKAMMO) };
VMReturn ret((void**)&SendItemUse);
VMCall(func, param, 3, &ret, 1);
}
} }
} }
} }
@ -536,7 +322,18 @@ CCMD (turn180)
CCMD (weapnext) CCMD (weapnext)
{ {
SendItemUse = 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. // [BC] Option to display the name of the weapon being cycled to.
if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse)
{ {
@ -551,7 +348,18 @@ CCMD (weapnext)
CCMD (weapprev) CCMD (weapprev)
{ {
SendItemUse = 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. // [BC] Option to display the name of the weapon being cycled to.
if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse)
{ {

View File

@ -204,29 +204,6 @@ void AWeapon::MarkPrecacheSounds() const
ReadySound.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 false;
}
/* Weapon slots ***********************************************************/ /* Weapon slots ***********************************************************/
//=========================================================================== //===========================================================================
@ -466,6 +443,23 @@ DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon)
return MIN(numret, 3); return MIN(numret, 3);
} }
DEFINE_ACTION_FUNCTION(FWeaponSlots, GetWeapon)
{
PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots);
PARAM_INT(slot);
PARAM_INT(index);
ACTION_RETURN_POINTER(self->GetWeapon(slot, index));
return 1;
}
DEFINE_ACTION_FUNCTION(FWeaponSlots, SlotSize)
{
PARAM_SELF_STRUCT_PROLOGUE(FWeaponSlots);
PARAM_INT(slot);
ACTION_RETURN_INT(self->SlotSize(slot));
return 1;
}
//=========================================================================== //===========================================================================
// //
// FWeaponSlots :: AddExtraWeapons // FWeaponSlots :: AddExtraWeapons

View File

@ -174,7 +174,6 @@ public:
AltFire, AltFire,
EitherFire EitherFire
}; };
bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1);
enum enum
{ {

View File

@ -961,4 +961,6 @@ struct WeaponSlots native
{ {
native bool, int, int LocateWeapon(class<Weapon> weap); native bool, int, int LocateWeapon(class<Weapon> weap);
native static void SetupWeaponSlots(PlayerPawn pp); native static void SetupWeaponSlots(PlayerPawn pp);
native class<Weapon> GetWeapon(int slot, int index);
native int SlotSize(int slot);
} }

View File

@ -1721,6 +1721,225 @@ class PlayerPawn : Actor native
return TeleportFreezeTime; return TeleportFreezeTime;
} }
//===========================================================================
//
// 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.
//
//===========================================================================
virtual Weapon PickWeapon(int slot, bool checkammo)
{
int i, j;
let player = self.player;
int Size = player.weapons.SlotSize(slot);
// Does this slot even have any weapons?
if (Size == 0)
{
return player.ReadyWeapon;
}
let ReadyWeapon = player.ReadyWeapon;
if (ReadyWeapon != null)
{
for (i = 0; i < Size; i++)
{
let weapontype = player.weapons.GetWeapon(slot, i);
if (weapontype == ReadyWeapon.GetClass() ||
(ReadyWeapon.bPOWERED_UP && ReadyWeapon.SisterWeapon != null && ReadyWeapon.SisterWeapon.GetClass() == weapontype))
{
for (j = (i == 0 ? Size - 1 : i - 1);
j != i;
j = (j == 0 ? Size - 1 : j - 1))
{
let weapontype2 = player.weapons.GetWeapon(slot, j);
let weap = Weapon(player.mo.FindInventory(weapontype2));
if (weap != null)
{
if (!checkammo || weap.CheckAmmo(Weapon.EitherFire, false))
{
return weap;
}
}
}
}
}
}
for (i = Size - 1; i >= 0; i--)
{
let weapontype = player.weapons.GetWeapon(slot, i);
let weap = Weapon(player.mo.FindInventory(weapontype));
if (weap != null)
{
if (!checkammo || weap.CheckAmmo(Weapon.EitherFire, false))
{
return weap;
}
}
}
return ReadyWeapon;
}
//===========================================================================
//
// 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.
//
//===========================================================================
bool, int, int FindMostRecentWeapon()
{
let player = self.player;
let ReadyWeapon = player.ReadyWeapon;
if (player.PendingWeapon != WP_NOCHANGE)
{
return player.weapons.LocateWeapon(player.PendingWeapon.GetClass());
}
else if (ReadyWeapon != null)
{
bool found;
int slot;
int index;
[found, slot, index] = player.weapons.LocateWeapon(ReadyWeapon.GetClass());
if (!found)
{
// If the current weapon wasn't found and is powered up,
// look for its non-powered up version.
if (ReadyWeapon.bPOWERED_UP && ReadyWeapon.SisterWeaponType != null)
{
return player.weapons.LocateWeapon(ReadyWeapon.SisterWeaponType);
}
return false, 0, 0;
}
return true, slot, index;
}
else
{
return false, 0, 0;
}
}
//===========================================================================
//
// 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.
//
//===========================================================================
const NUM_WEAPON_SLOTS = 10;
virtual Weapon PickNextWeapon()
{
let player = self.player;
bool found;
int startslot, startindex;
int slotschecked = 0;
[found, startslot, startindex] = FindMostRecentWeapon();
let ReadyWeapon = player.ReadyWeapon;
if (ReadyWeapon == null || found)
{
int slot;
int index;
if (ReadyWeapon == null)
{
startslot = NUM_WEAPON_SLOTS - 1;
startindex = player.weapons.SlotSize(startslot) - 1;
}
slot = startslot;
index = startindex;
do
{
if (++index >= player.weapons.SlotSize(slot))
{
index = 0;
slotschecked++;
if (++slot >= NUM_WEAPON_SLOTS)
{
slot = 0;
}
}
let type = player.weapons.GetWeapon(slot, index);
let weap = Weapon(FindInventory(type));
if (weap != null && weap.CheckAmmo(Weapon.EitherFire, false))
{
return weap;
}
} while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS);
}
return 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.
//
//===========================================================================
virtual Weapon PickPrevWeapon()
{
let player = self.player;
int startslot, startindex;
bool found;
int slotschecked = 0;
[found, startslot, startindex] = FindMostRecentWeapon();
if (player.ReadyWeapon == null || found)
{
int slot;
int index;
if (player.ReadyWeapon == null)
{
startslot = 0;
startindex = 0;
}
slot = startslot;
index = startindex;
do
{
if (--index < 0)
{
slotschecked++;
if (--slot < 0)
{
slot = NUM_WEAPON_SLOTS - 1;
}
index = player.weapons.SlotSize(slot) - 1;
}
let type = player.weapons.GetWeapon(slot, index);
let weap = Weapon(FindInventory(type));
if (weap != null && weap.CheckAmmo(Weapon.EitherFire, false))
{
return weap;
}
} while ((slot != startslot || index != startindex) && slotschecked <= NUM_WEAPON_SLOTS);
}
return player.ReadyWeapon;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// //
// //