gzdoom/src/g_shared/a_weapons.cpp
Christoph Oelckers 3637c878cd - Changed: Replaced weapons should not be given by generic cheats, only
when explicitly giving them.
- Changed 'give weapon' cheat so that in single player it only gives weapons
  belonging to the current game or are placed in a weapon slot to avoid
  giving the Chex Quest weapons in Doom and vice versa.
- Fixed: The texture manager must be the first thing to be initialized
  because MAPINFO and DECORATE both can reference textures and letting them
  create their own textures is not safe.


SVN r1230 (trunk)
2008-09-17 00:14:33 +00:00

1082 lines
25 KiB
C++

#include <string.h>
#include "a_pickups.h"
#include "gi.h"
#include "d_player.h"
#include "s_sound.h"
#include "i_system.h"
#include "r_state.h"
#include "p_pspr.h"
#include "c_dispatch.h"
#include "m_misc.h"
#include "gameconfigfile.h"
#include "cmdlib.h"
#include "templates.h"
#include "sbar.h"
#include "thingdef/thingdef.h"
#include "doomstat.h"
#include "g_level.h"
#define BONUSADD 6
IMPLEMENT_POINTY_CLASS (AWeapon)
DECLARE_POINTER (Ammo1)
DECLARE_POINTER (Ammo2)
DECLARE_POINTER (SisterWeapon)
END_POINTERS
//===========================================================================
//
// AWeapon :: Serialize
//
//===========================================================================
void AWeapon::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << WeaponFlags
<< AmmoType1 << AmmoType2
<< AmmoGive1 << AmmoGive2
<< MinAmmo1 << MinAmmo2
<< AmmoUse1 << AmmoUse2
<< Kickback
<< YAdjust
<< UpSound << ReadySound
<< SisterWeaponType
<< ProjectileType << AltProjectileType
<< SelectionOrder
<< MoveCombatDist
<< Ammo1 << Ammo2 << SisterWeapon << GivenAsMorphWeapon
<< bAltFire
<< ReloadCounter;
}
//===========================================================================
//
// AWeapon :: TryPickup
//
// If you can't see the weapon when it's active, then you can't pick it up.
//
//===========================================================================
bool AWeapon::TryPickup (AActor *&toucher)
{
FState * ReadyState = FindState(NAME_Ready);
if (ReadyState != NULL &&
ReadyState->GetFrame() < sprites[ReadyState->sprite].numframes)
{
return Super::TryPickup (toucher);
}
return false;
}
//===========================================================================
//
// AWeapon :: Use
//
// Make the player switch to this weapon.
//
//===========================================================================
bool AWeapon::Use (bool pickup)
{
AWeapon *useweap = this;
// Powered up weapons cannot be used directly.
if (WeaponFlags & WIF_POWERED_UP) return false;
// If the player is powered-up, use the alternate version of the
// weapon, if one exists.
if (SisterWeapon != NULL &&
SisterWeapon->WeaponFlags & WIF_POWERED_UP &&
Owner->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2)))
{
useweap = SisterWeapon;
}
if (Owner->player != NULL && Owner->player->ReadyWeapon != useweap)
{
Owner->player->PendingWeapon = useweap;
}
// Return false so that the weapon is not removed from the inventory.
return false;
}
//===========================================================================
//
// AWeapon :: Destroy
//
//===========================================================================
void AWeapon::Destroy()
{
if (SisterWeapon != NULL)
{
// avoid recursion
SisterWeapon->SisterWeapon = NULL;
SisterWeapon->Destroy();
}
Super::Destroy();
}
//===========================================================================
//
// AWeapon :: HandlePickup
//
// Try to leach ammo from the weapon if you have it already.
//
//===========================================================================
bool AWeapon::HandlePickup (AInventory *item)
{
if (item->GetClass() == GetClass())
{
if (static_cast<AWeapon *>(item)->PickupForAmmo (this))
{
item->ItemFlags |= IF_PICKUPGOOD;
}
return true;
}
if (Inventory != NULL)
{
return Inventory->HandlePickup (item);
}
return false;
}
//===========================================================================
//
// AWeapon :: PickupForAmmo
//
// The player already has this weapon, so try to pick it up for ammo.
//
//===========================================================================
bool AWeapon::PickupForAmmo (AWeapon *ownedWeapon)
{
bool gotstuff = false;
// Don't take ammo if the weapon sticks around.
if (!ShouldStay ())
{
if (AmmoGive1 > 0) gotstuff = AddExistingAmmo (ownedWeapon->Ammo1, AmmoGive1);
if (AmmoGive2 > 0) gotstuff |= AddExistingAmmo (ownedWeapon->Ammo2, AmmoGive2);
}
return gotstuff;
}
//===========================================================================
//
// AWeapon :: CreateCopy
//
//===========================================================================
AInventory *AWeapon::CreateCopy (AActor *other)
{
AWeapon *copy = static_cast<AWeapon*>(Super::CreateCopy (other));
if (copy != this)
{
copy->AmmoGive1 = AmmoGive1;
copy->AmmoGive2 = AmmoGive2;
}
return copy;
}
//===========================================================================
//
// AWeapon :: CreateTossable
//
// A weapon that's tossed out should contain no ammo, so you can't cheat
// by dropping it and then picking it back up.
//=======================
AInventory *AWeapon::CreateTossable ()
{
// Only drop the weapon that is meant to be placed in a level. That is,
// only drop the weapon that normally gives you ammo.
if (SisterWeapon != NULL &&
((AWeapon*)GetDefault())->AmmoGive1 == 0 &&
((AWeapon*)GetDefault())->AmmoGive2 == 0 &&
(((AWeapon*)SisterWeapon->GetDefault())->AmmoGive1 > 0 ||
((AWeapon*)SisterWeapon->GetDefault())->AmmoGive2 > 0))
{
return SisterWeapon->CreateTossable ();
}
AWeapon *copy = static_cast<AWeapon *> (Super::CreateTossable ());
if (copy != NULL)
{
// If this weapon has a sister, remove it from the inventory too.
if (SisterWeapon != NULL)
{
SisterWeapon->SisterWeapon = NULL;
SisterWeapon->Destroy ();
}
// To avoid exploits, the tossed weapon must not have any ammo.
copy->AmmoGive1 = 0;
copy->AmmoGive2 = 0;
}
return copy;
}
//===========================================================================
//
// AWeapon :: AttachToOwner
//
//===========================================================================
void AWeapon::AttachToOwner (AActor *other)
{
Super::AttachToOwner (other);
Ammo1 = AddAmmo (Owner, AmmoType1, AmmoGive1);
Ammo2 = AddAmmo (Owner, AmmoType2, AmmoGive2);
SisterWeapon = AddWeapon (SisterWeaponType);
if (Owner->player != NULL)
{
if (!Owner->player->userinfo.neverswitch && !(WeaponFlags & WIF_NO_AUTO_SWITCH))
{
Owner->player->PendingWeapon = this;
}
if (Owner->player->mo == players[consoleplayer].camera)
{
StatusBar->ReceivedWeapon (this);
}
}
GivenAsMorphWeapon = false; // will be set explicitly by morphing code
}
//===========================================================================
//
// AWeapon :: AddAmmo
//
// Give some ammo to the owner, even if it's just 0.
//
//===========================================================================
AAmmo *AWeapon::AddAmmo (AActor *other, const PClass *ammotype, int amount)
{
AAmmo *ammo;
if (ammotype == NULL)
{
return NULL;
}
// [BC] This behavior is from the original Doom. Give 5/2 times as much ammo when
// we pick up a weapon in deathmatch.
if (( deathmatch ) && ( gameinfo.gametype & GAME_DoomChex ))
amount = amount * 5 / 2;
// extra ammo in baby mode and nightmare mode
if (!(this->ItemFlags&IF_IGNORESKILL))
{
amount = FixedMul(amount, G_SkillProperty(SKILLP_AmmoFactor));
}
ammo = static_cast<AAmmo *>(other->FindInventory (ammotype));
if (ammo == NULL)
{
ammo = static_cast<AAmmo *>(Spawn (ammotype, 0, 0, 0, NO_REPLACE));
ammo->Amount = MIN (amount, ammo->MaxAmount);
ammo->AttachToOwner (other);
}
else if (ammo->Amount < ammo->MaxAmount)
{
ammo->Amount += amount;
if (ammo->Amount > ammo->MaxAmount)
{
ammo->Amount = ammo->MaxAmount;
}
}
return ammo;
}
//===========================================================================
//
// AWeapon :: AddExistingAmmo
//
// Give the owner some more ammo he already has.
//
//===========================================================================
bool AWeapon::AddExistingAmmo (AAmmo *ammo, int amount)
{
if (ammo != NULL && ammo->Amount < ammo->MaxAmount)
{
// extra ammo in baby mode and nightmare mode
if (!(ItemFlags&IF_IGNORESKILL))
{
amount = FixedMul(amount, G_SkillProperty(SKILLP_AmmoFactor));
}
ammo->Amount += amount;
if (ammo->Amount > ammo->MaxAmount)
{
ammo->Amount = ammo->MaxAmount;
}
return true;
}
return false;
}
//===========================================================================
//
// AWeapon :: AddWeapon
//
// Give the owner a weapon if they don't have it already.
//
//===========================================================================
AWeapon *AWeapon::AddWeapon (const PClass *weapontype)
{
AWeapon *weap;
if (weapontype == NULL || !weapontype->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
return NULL;
}
weap = static_cast<AWeapon *>(Owner->FindInventory (weapontype));
if (weap == NULL)
{
weap = static_cast<AWeapon *>(Spawn (weapontype, 0, 0, 0, NO_REPLACE));
weap->AttachToOwner (Owner);
}
return weap;
}
//===========================================================================
//
// AWeapon :: ShouldStay
//
//===========================================================================
bool AWeapon::ShouldStay ()
{
if (((multiplayer &&
(!deathmatch && !alwaysapplydmflags)) || (dmflags & DF_WEAPONS_STAY)) &&
!(flags & MF_DROPPED))
{
return true;
}
return false;
}
//===========================================================================
//
// 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 altFire;
int count1, count2;
int enough, enoughmask;
if (dmflags & DF_INFINITE_AMMO)
{
return true;
}
if (fireMode == EitherFire)
{
bool gotSome = CheckAmmo (PrimaryFire, false) || CheckAmmo (AltFire, false);
if (!gotSome && autoSwitch)
{
barrier_cast<APlayerPawn *>(Owner)->PickNewWeapon (NULL);
}
return gotSome;
}
altFire = (fireMode == AltFire);
if (!requireAmmo && (WeaponFlags & (WIF_AMMO_OPTIONAL << altFire)))
{
return true;
}
count1 = (Ammo1 != NULL) ? Ammo1->Amount : 0;
count2 = (Ammo2 != NULL) ? Ammo2->Amount : 0;
enough = (count1 >= AmmoUse1) | ((count2 >= AmmoUse2) << 1);
if (WeaponFlags & (WIF_PRIMARY_USES_BOTH << altFire))
{
enoughmask = 3;
}
else
{
enoughmask = 1 << altFire;
}
if (altFire && FindState(NAME_AltFire) == NULL)
{ // If this weapon has no alternate fire, then there is never enough ammo for it
enough &= 1;
}
if ((enough & enoughmask) == enoughmask)
{
return true;
}
// out of ammo, pick a weapon to change to
if (autoSwitch)
{
barrier_cast<APlayerPawn *>(Owner)->PickNewWeapon (NULL);
}
return false;
}
//===========================================================================
//
// 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)
{
if (!(dmflags & DF_INFINITE_AMMO))
{
if (checkEnough && !CheckAmmo (altFire ? AltFire : PrimaryFire, false))
{
return false;
}
if (!altFire)
{
if (Ammo1 != NULL)
{
Ammo1->Amount -= AmmoUse1;
}
if ((WeaponFlags & WIF_PRIMARY_USES_BOTH) && Ammo2 != NULL)
{
Ammo2->Amount -= AmmoUse2;
}
}
else
{
if (Ammo2 != NULL)
{
Ammo2->Amount -= AmmoUse2;
}
if ((WeaponFlags & WIF_ALT_USES_BOTH) && Ammo1 != NULL)
{
Ammo1->Amount -= AmmoUse1;
}
}
if (Ammo1 != NULL && Ammo1->Amount < 0)
Ammo1->Amount = 0;
if (Ammo2 != NULL && Ammo2->Amount < 0)
Ammo2->Amount = 0;
}
return true;
}
//===========================================================================
//
// AWeapon :: PostMorphWeapon
//
// Bring this weapon up after a player unmorphs.
//
//===========================================================================
void AWeapon::PostMorphWeapon ()
{
Owner->player->PendingWeapon = WP_NOCHANGE;
Owner->player->ReadyWeapon = this;
Owner->player->psprites[ps_weapon].sy = WEAPONBOTTOM;
P_SetPsprite (Owner->player, ps_weapon, GetUpState());
}
//===========================================================================
//
// AWeapon :: EndPowerUp
//
// The Tome of Power just expired.
//
//===========================================================================
void AWeapon::EndPowerup ()
{
if (SisterWeapon != NULL && WeaponFlags&WIF_POWERED_UP)
{
if (GetReadyState() != SisterWeapon->GetReadyState())
{
if (Owner->player->PendingWeapon == NULL ||
Owner->player->PendingWeapon == WP_NOCHANGE)
Owner->player->PendingWeapon = SisterWeapon;
}
else
{
Owner->player->ReadyWeapon = SisterWeapon;
}
}
}
//===========================================================================
//
// AWeapon :: GetUpState
//
//===========================================================================
FState *AWeapon::GetUpState ()
{
return FindState(NAME_Select);
}
//===========================================================================
//
// AWeapon :: GetDownState
//
//===========================================================================
FState *AWeapon::GetDownState ()
{
return FindState(NAME_Deselect);
}
//===========================================================================
//
// AWeapon :: GetReadyState
//
//===========================================================================
FState *AWeapon::GetReadyState ()
{
return FindState(NAME_Ready);
}
//===========================================================================
//
// AWeapon :: GetAtkState
//
//===========================================================================
FState *AWeapon::GetAtkState (bool hold)
{
FState * state=NULL;
if (hold) state = FindState(NAME_Hold);
if (state == NULL) state = FindState(NAME_Fire);
return state;
}
//===========================================================================
//
// AWeapon :: GetAtkState
//
//===========================================================================
FState *AWeapon::GetAltAtkState (bool hold)
{
FState * state=NULL;
if (hold) state = FindState(NAME_AltHold);
if (state == NULL) state = FindState(NAME_AltFire);
return state;
}
/* Weapon giver ***********************************************************/
class AWeaponGiver : public AWeapon
{
DECLARE_CLASS(AWeaponGiver, AWeapon)
public:
bool TryPickup(AActor *&toucher);
};
IMPLEMENT_CLASS(AWeaponGiver)
bool AWeaponGiver::TryPickup(AActor *&toucher)
{
FDropItem *di = GetDropItems(GetClass());
if (di != NULL)
{
const PClass *ti = PClass::FindClass(di->Name);
if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
AWeapon *weap = static_cast<AWeapon*>(Spawn(di->Name, 0, 0, 0, NO_REPLACE));
if (weap != NULL)
{
weap->ItemFlags &= ~IF_ALWAYSPICKUP; // use the flag of this item only.
bool res = weap->CallTryPickup(toucher);
if (!res) weap->Destroy();
else GoAwayAndDie();
return res;
}
}
}
return false;
}
/* Weapon slots ***********************************************************/
FWeaponSlots LocalWeapons;
FWeaponSlot::FWeaponSlot ()
{
Clear ();
}
void FWeaponSlot::Clear ()
{
for (int i = 0; i < MAX_WEAPONS_PER_SLOT; i++)
{
Weapons[i] = NULL;
}
}
bool FWeaponSlot::AddWeapon (const char *type)
{
return AddWeapon (PClass::FindClass (type));
}
bool FWeaponSlot::AddWeapon (const PClass *type)
{
int i;
if (type == NULL)
{
return false;
}
if (!type->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
Printf("Can't add non-weapon %s to weapon slots\n", type->TypeName.GetChars());
return false;
}
for (i = 0; i < MAX_WEAPONS_PER_SLOT; i++)
{
if (Weapons[i] == type)
return true; // Already present
if (Weapons[i] == NULL)
break;
}
if (i == MAX_WEAPONS_PER_SLOT)
{ // This slot is full
return false;
}
Weapons[i] = type;
return true;
}
AWeapon *FWeaponSlot::PickWeapon (player_t *player)
{
int i, j;
if (player->ReadyWeapon != NULL)
{
for (i = 0; i < MAX_WEAPONS_PER_SLOT; i++)
{
if (Weapons[i] == player->ReadyWeapon->GetClass() ||
(player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP &&
player->ReadyWeapon->SisterWeapon != NULL &&
player->ReadyWeapon->SisterWeapon->GetClass() == Weapons[i]))
{
for (j = (unsigned)(i - 1) % MAX_WEAPONS_PER_SLOT;
j != i;
j = (unsigned)(j - 1) % MAX_WEAPONS_PER_SLOT)
{
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory (Weapons[j]));
if (weap != NULL && weap->IsKindOf(RUNTIME_CLASS(AWeapon)) && weap->CheckAmmo (AWeapon::EitherFire, false))
{
return weap;
}
}
}
}
}
for (i = MAX_WEAPONS_PER_SLOT - 1; i >= 0; i--)
{
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory (Weapons[i]));
if (weap != NULL && weap->IsKindOf(RUNTIME_CLASS(AWeapon)) && weap->CheckAmmo (AWeapon::EitherFire, false))
{
return weap;
}
}
return player->ReadyWeapon;
}
void FWeaponSlots::Clear ()
{
for (int i = 0; i < NUM_WEAPON_SLOTS; ++i)
{
Slots[i].Clear ();
}
}
// If the weapon already exists in a slot, don't add it. If it doesn't,
// then add it to the specified slot. False is returned if the weapon was
// not in a slot and could not be added. True is returned otherwise.
ESlotDef FWeaponSlots::AddDefaultWeapon (int slot, const PClass *type)
{
int currSlot, index;
if (!LocateWeapon (type, &currSlot, &index))
{
if (slot >= 0 && slot < NUM_WEAPON_SLOTS)
{
bool added = Slots[slot].AddWeapon (type);
return added ? SLOTDEF_Added : SLOTDEF_Full;
}
return SLOTDEF_Full;
}
return SLOTDEF_Exists;
}
bool FWeaponSlots::LocateWeapon (const PClass *type, int *const slot, int *const index)
{
int i, j;
for (i = 0; i < NUM_WEAPON_SLOTS; i++)
{
for (j = 0; j < MAX_WEAPONS_PER_SLOT; j++)
{
if (Slots[i].Weapons[j] == type)
{
if (slot != NULL) *slot = i;
if (index != NULL) *index = j;
return true;
}
else if (Slots[i].Weapons[j] == NULL)
{ // No more weapons in this slot, so try the next
break;
}
}
}
return false;
}
static bool FindMostRecentWeapon (player_t *player, int *slot, int *index)
{
if (player->PendingWeapon != WP_NOCHANGE)
{
return LocalWeapons.LocateWeapon (player->PendingWeapon->GetClass(), slot, index);
}
else if (player->ReadyWeapon != NULL)
{
AWeapon *weap = player->ReadyWeapon;
if (!LocalWeapons.LocateWeapon (weap->GetClass(), slot, index))
{
if (weap->WeaponFlags & WIF_POWERED_UP && weap->SisterWeaponType != NULL)
{
return LocalWeapons.LocateWeapon (weap->SisterWeaponType, slot, index);
}
return false;
}
return true;
}
else
{
return false;
}
}
AWeapon *PickNextWeapon (player_t *player)
{
int startslot, startindex;
if (player->mo == NULL)
{
return NULL;
}
if (player->ReadyWeapon == NULL || FindMostRecentWeapon (player, &startslot, &startindex))
{
int start;
int i;
if (player->ReadyWeapon == NULL)
{
startslot = NUM_WEAPON_SLOTS - 1;
startindex = MAX_WEAPONS_PER_SLOT - 1;
}
start = startslot * MAX_WEAPONS_PER_SLOT + startindex;
for (i = 1; i < NUM_WEAPON_SLOTS * MAX_WEAPONS_PER_SLOT + 1; i++)
{
int slot = (unsigned)((start + i) / MAX_WEAPONS_PER_SLOT) % NUM_WEAPON_SLOTS;
int index = (unsigned)(start + i) % MAX_WEAPONS_PER_SLOT;
const PClass *type = LocalWeapons.Slots[slot].Weapons[index];
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory (type));
if (weap != NULL && weap->CheckAmmo (AWeapon::EitherFire, false))
{
return weap;
}
}
}
return player->ReadyWeapon;
}
AWeapon *PickPrevWeapon (player_t *player)
{
int startslot, startindex;
if (player->mo == NULL)
{
return NULL;
}
if (player->ReadyWeapon == NULL || FindMostRecentWeapon (player, &startslot, &startindex))
{
int start;
int i;
if (player->ReadyWeapon == NULL)
{
startslot = 0;
startindex = 0;
}
start = startslot * MAX_WEAPONS_PER_SLOT + startindex;
for (i = 1; i < NUM_WEAPON_SLOTS * MAX_WEAPONS_PER_SLOT + 1; i++)
{
int slot = start - i;
if (slot < 0)
slot += NUM_WEAPON_SLOTS * MAX_WEAPONS_PER_SLOT;
int index = slot % MAX_WEAPONS_PER_SLOT;
slot /= MAX_WEAPONS_PER_SLOT;
const PClass *type = LocalWeapons.Slots[slot].Weapons[index];
AWeapon *weap = static_cast<AWeapon *> (player->mo->FindInventory (type));
if (weap != NULL && weap->CheckAmmo (AWeapon::EitherFire, false))
{
return weap;
}
}
}
return player->ReadyWeapon;
}
CCMD (setslot)
{
int slot, i;
if (ParsingKeyConf && WeaponSection.IsEmpty())
{
Printf ("You need to use weaponsection before using setslot\n");
return;
}
if (argv.argc() < 2 || (slot = atoi (argv[1])) >= NUM_WEAPON_SLOTS)
{
Printf ("Usage: setslot [slot] [weapons]\nCurrent slot assignments:\n");
for (slot = 0; slot < NUM_WEAPON_SLOTS; ++slot)
{
Printf (" Slot %d:", slot);
for (i = 0;
i < MAX_WEAPONS_PER_SLOT && LocalWeapons.Slots[slot].GetWeapon(i) != NULL;
++i)
{
Printf (" %s", LocalWeapons.Slots[slot].GetWeapon(i)->TypeName.GetChars());
}
Printf ("\n");
}
return;
}
LocalWeapons.Slots[slot].Clear();
if (argv.argc() == 2)
{
Printf ("Slot %d cleared\n", slot);
}
else
{
for (i = 2; i < argv.argc(); ++i)
{
if (!LocalWeapons.Slots[slot].AddWeapon (argv[i]))
{
Printf ("Could not add %s to slot %d\n", argv[i], slot);
}
}
}
}
CCMD (addslot)
{
size_t slot;
if (argv.argc() != 3 || (slot = atoi (argv[1])) >= NUM_WEAPON_SLOTS)
{
Printf ("Usage: addslot <slot> <weapon>\n");
return;
}
if (!LocalWeapons.Slots[slot].AddWeapon (argv[2]))
{
Printf ("Could not add %s to slot %zu\n", argv[2], slot);
}
}
CCMD (weaponsection)
{
if (argv.argc() != 2)
{
Printf ("Usage: weaponsection <ini name>\n");
}
else
{
// Limit the section name to 32 chars
if (strlen(argv[1]) > 32)
{
argv[1][32] = 0;
}
WeaponSection = argv[1];
// If the ini already has definitions for this section, load them
char fullSection[32*3];
char *tackOn;
if (gameinfo.gametype == GAME_Hexen)
{
strcpy (fullSection, "Hexen");
tackOn = fullSection + 5;
}
else if (gameinfo.gametype == GAME_Heretic)
{
strcpy (fullSection, "Heretic");
tackOn = fullSection + 7;
}
else if (gameinfo.gametype == GAME_Strife)
{
strcpy (fullSection, "Strife");
tackOn = fullSection + 6;
}
else
{
strcpy (fullSection, "Doom");
tackOn = fullSection + 4;
}
mysnprintf (tackOn, countof(fullSection) - (tackOn - fullSection),
".%s.WeaponSlots", WeaponSection.GetChars());
if (GameConfig->SetSection (fullSection))
{
LocalWeapons.RestoreSlots (*GameConfig);
}
}
}
CCMD (addslotdefault)
{
const PClass *type;
unsigned int slot;
if (argv.argc() != 3 || (slot = atoi (argv[1])) >= NUM_WEAPON_SLOTS)
{
Printf ("Usage: addslotdefault <slot> <weapon>\n");
return;
}
if (ParsingKeyConf && WeaponSection.IsEmpty())
{
Printf ("You need to use weaponsection before using addslotdefault\n");
return;
}
type = PClass::FindClass (argv[2]);
if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AWeapon)))
{
Printf ("%s is not a weapon\n", argv[2]);
}
switch (LocalWeapons.AddDefaultWeapon (slot, type))
{
case SLOTDEF_Full:
Printf ("Could not add %s to slot %d\n", argv[2], slot);
break;
case SLOTDEF_Added:
break;
case SLOTDEF_Exists:
break;
}
}
int FWeaponSlots::RestoreSlots (FConfigFile &config)
{
char buff[MAX_WEAPONS_PER_SLOT*64];
const char *key, *value;
int slot;
int slotsread = 0;
buff[sizeof(buff)-1] = 0;
for (slot = 0; slot < NUM_WEAPON_SLOTS; ++slot)
{
Slots[slot].Clear ();
}
while (config.NextInSection (key, value))
{
if (strnicmp (key, "Slot[", 5) != 0 ||
key[5] < '0' ||
key[5] > '0'+NUM_WEAPON_SLOTS ||
key[6] != ']' ||
key[7] != 0)
{
continue;
}
slot = key[5] - '0';
strncpy (buff, value, sizeof(buff)-1);
char *tok;
Slots[slot].Clear ();
tok = strtok (buff, " ");
while (tok != NULL)
{
Slots[slot].AddWeapon (tok);
tok = strtok (NULL, " ");
}
slotsread++;
}
return slotsread;
}
void FWeaponSlots::SaveSlots (FConfigFile &config)
{
char buff[MAX_WEAPONS_PER_SLOT*64];
char keyname[16];
for (int i = 0; i < NUM_WEAPON_SLOTS; ++i)
{
int index = 0;
for (int j = 0; j < MAX_WEAPONS_PER_SLOT; ++j)
{
if (Slots[i].Weapons[j] == NULL)
{
break;
}
if (index > 0)
{
buff[index++] = ' ';
}
const char *name = Slots[i].Weapons[j]->TypeName.GetChars();
strcpy (buff+index, name);
index += (int)strlen (name);
}
if (index > 0)
{
mysnprintf (keyname, countof(keyname), "Slot[%d]", i);
config.SetValueForKey (keyname, buff);
}
}
}
int FWeaponSlot::CountWeapons ()
{
int i;
for (i = 0; i < MAX_WEAPONS_PER_SLOT; ++i)
{
if (Weapons[i] == NULL)
{
break;
}
}
return i;
}