diff --git a/src/g_game.cpp b/src/g_game.cpp index 94b9b5a2a3..e8bddf230e 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -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 (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 (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(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(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) { if (argv.argc() > 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 - 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) { - 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. if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { @@ -551,7 +348,18 @@ CCMD (weapnext) 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. if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 60fb582b94..29579f1273 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -204,29 +204,6 @@ void AWeapon::MarkPrecacheSounds() const 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 ***********************************************************/ //=========================================================================== @@ -466,6 +443,23 @@ DEFINE_ACTION_FUNCTION(FWeaponSlots, LocateWeapon) 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 diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h index 1c4109425d..f1252f7294 100644 --- a/src/g_inventory/a_weapons.h +++ b/src/g_inventory/a_weapons.h @@ -174,7 +174,6 @@ public: AltFire, EitherFire }; - bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1); enum { diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 9f4196de2d..8612af7926 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -961,4 +961,6 @@ struct WeaponSlots native { native bool, int, int LocateWeapon(class weap); native static void SetupWeaponSlots(PlayerPawn pp); + native class GetWeapon(int slot, int index); + native int SlotSize(int slot); } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 478d3e8132..2e1cda0e99 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1721,6 +1721,225 @@ class PlayerPawn : Actor native 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; + } + + //---------------------------------------------------------------------------- // //