332 lines
10 KiB
Text
332 lines
10 KiB
Text
// inventory-related functions
|
|
extend Class SWWMUtility
|
|
{
|
|
// full reset of inventory (excluding collectibles, and optionally resetting the score)
|
|
static play void WipeInventory( Actor mo, bool resetscore = false, bool allplayers = false )
|
|
{
|
|
if ( allplayers )
|
|
{
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo ) continue;
|
|
WipeInventory(players[i].mo,resetscore,false);
|
|
}
|
|
return;
|
|
}
|
|
PlayerInfo p = mo.player;
|
|
if ( !p || !p.mo ) return;
|
|
SWWMCredits c = SWWMCredits.Find(p);
|
|
if ( resetscore && c ) c.credits = 0;
|
|
Actor last = p.mo;
|
|
while ( last.inv )
|
|
{
|
|
let inv = last.inv;
|
|
if ( !(inv is 'SWWMCollectible') )
|
|
{
|
|
inv.Destroy();
|
|
if ( !inv.bDestroyed ) last = inv;
|
|
}
|
|
else last = inv;
|
|
}
|
|
p.mo.GiveDefaultInventory();
|
|
p.mo.BringUpWeapon();
|
|
p.health = p.mo.Health = p.mo.SpawnHealth();
|
|
}
|
|
|
|
// sets all carried ammo back to zero
|
|
// resets hammerspace capacity
|
|
static play void ResetAmmo( Actor mo )
|
|
{
|
|
PlayerInfo p = mo.player;
|
|
if ( !p || !p.mo ) return;
|
|
for ( Inventory i=p.mo.inv; i; i=i.inv )
|
|
{
|
|
if ( (i is 'Ammo') || (i is 'MagAmmo') )
|
|
{
|
|
i.Amount = 0;
|
|
i.MaxAmount = i.default.MaxAmount;
|
|
}
|
|
if ( i is 'HammerspaceEmbiggener' )
|
|
i.Amount = 0;
|
|
}
|
|
// also gives back any ammo from carried weapons (provided they don't have NOFIRSTGIVE)
|
|
for ( Inventory i=p.mo.inv; i; i=i.inv )
|
|
{
|
|
if ( !(i is 'Weapon') ) continue;
|
|
let w = Weapon(i);
|
|
if ( (w is 'SWWMWeapon') && SWWMWeapon(w).bNOFIRSTGIVE ) continue;
|
|
if ( w.Ammo1 ) w.Ammo1.Amount = min(w.Ammo1.MaxAmount,w.Ammo1.Amount+w.default.AmmoGive1);
|
|
if ( w.Ammo2 && (w.Ammo2 != w.Ammo1) ) w.Ammo2.Amount = min(w.Ammo2.MaxAmount,w.Ammo2.Amount+w.default.AmmoGive2);
|
|
}
|
|
}
|
|
|
|
// removes all usable items
|
|
static play void ResetItems( Actor mo )
|
|
{
|
|
PlayerInfo p = mo.player;
|
|
if ( !p || !p.mo ) return;
|
|
Actor last = p.mo;
|
|
while ( last.inv )
|
|
{
|
|
let inv = last.inv;
|
|
if ( !inv.bINVBAR )
|
|
{
|
|
last = inv;
|
|
continue;
|
|
}
|
|
inv.Destroy();
|
|
if ( !inv.bDestroyed ) last = inv;
|
|
}
|
|
}
|
|
|
|
// resets health and removes worn armor
|
|
static play void ResetHealth( Actor mo )
|
|
{
|
|
PlayerInfo p = mo.player;
|
|
if ( !p || !p.mo ) return;
|
|
p.health = p.mo.health = 100;
|
|
for ( Inventory i=p.mo.inv; i; i=i.inv )
|
|
{
|
|
if ( !(i is 'SWWMArmor') ) continue;
|
|
i.Amount = 0;
|
|
}
|
|
}
|
|
|
|
// what RandomSpawner does, basically (simplified for items)
|
|
static play void TransferItemProp( Actor a, Actor b, bool bundlehack = false )
|
|
{
|
|
if ( bundlehack )
|
|
{
|
|
b.spawnpoint = b.pos;
|
|
b.spawnangle = int(b.angle);
|
|
}
|
|
else
|
|
{
|
|
b.spawnpoint = a.spawnpoint;
|
|
b.spawnangle = a.spawnangle;
|
|
b.angle = a.angle;
|
|
b.pitch = a.pitch;
|
|
b.roll = a.roll;
|
|
}
|
|
b.special = a.special;
|
|
b.FloatBobPhase = a.FloatBobPhase; // important
|
|
for ( int i=0; i<5; i++ ) b.args[i] = a.args[i];
|
|
b.special1 = a.special1;
|
|
b.special2 = a.special2;
|
|
b.spawnflags = a.spawnflags&~MTF_SECRET;
|
|
b.HandleSpawnFlags();
|
|
b.spawnflags = a.spawnflags;
|
|
b.bCountSecret = a.spawnflags&MTF_SECRET;
|
|
b.ChangeTid(a.tid);
|
|
b.vel = b.vel;
|
|
b.master = b.master;
|
|
b.tracer = b.tracer;
|
|
b.target = b.target;
|
|
b.bDROPPED = a.bDROPPED;
|
|
}
|
|
|
|
// check that all players can get enough of this if needed
|
|
// multi: check for multiple copies, not just single instances
|
|
// (useful e.g. for dual wieldable weapons)
|
|
static bool CheckNeedsItem( Class<Inventory> itm, bool multi = false )
|
|
{
|
|
int np = 0;
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo ) continue;
|
|
np++;
|
|
}
|
|
int required = np;
|
|
if ( multi ) required *= GetDefaultByType(itm).MaxAmount;
|
|
// subtract all that exist already (either in world or owned)
|
|
let ti = ThinkerIterator.Create(itm);
|
|
Inventory i;
|
|
while ( i = Inventory(ti.Next()) )
|
|
{
|
|
if ( multi ) required -= i.Amount;
|
|
else required--;
|
|
}
|
|
// check travelling inventory separately, as by default iterators don't check anything below STAT_FIRST_THINKING
|
|
ti = ThinkerIterator.Create(itm,Thinker.STAT_TRAVELLING);
|
|
while ( i = Inventory(ti.Next()) )
|
|
{
|
|
if ( multi ) required -= i.Amount;
|
|
else required--;
|
|
}
|
|
return (required>0);
|
|
}
|
|
|
|
// checks if instances of a certain item exist
|
|
// skipme: optionally, ignore checking for one specific instance
|
|
// (useful to check if we're the only copy of an item)
|
|
// mapstart: this function is being called during map load, so we
|
|
// should also check STAT_TRAVELLING inventory
|
|
// worldonly: only checks for items that are placed in the world
|
|
// ownedonly: only checks for items that are owned by players
|
|
// (note that this is mutually exclusive with worldonly)
|
|
static bool ItemExists( Class<Inventory> itm, Inventory skipme = null, bool mapstart = false, bool worldonly = false, bool ownedonly = false )
|
|
{
|
|
let ti = ThinkerIterator.Create(itm);
|
|
Inventory i;
|
|
while ( i = Inventory(ti.Next()) )
|
|
{
|
|
if ( i == skipme ) continue;
|
|
if ( worldonly && i.Owner ) continue;
|
|
if ( ownedonly && (!i.Owner || !i.Owner.player) ) continue;
|
|
return true;
|
|
}
|
|
if ( worldonly || !mapstart ) return false;
|
|
ti = ThinkerIterator.Create(itm,Thinker.STAT_TRAVELLING);
|
|
while ( i = Inventory(ti.Next()) )
|
|
{
|
|
if ( i == skipme ) continue;
|
|
if ( ownedonly && (!i.Owner || !i.Owner.player) ) continue;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// multi-weapon spawn stuff
|
|
|
|
static private Class<Inventory> PickPair( Class<Inventory> a, Class<Inventory> b )
|
|
{
|
|
if ( !ItemExists(a,mapstart:true) ) return a;
|
|
if ( !ItemExists(b,mapstart:true) ) return b;
|
|
return Random[Replacements](0,1)?a:b;
|
|
}
|
|
|
|
// melee weapon + extra slot 2 guns
|
|
static Class<Inventory> PickSWWMSlot1()
|
|
{
|
|
// [GROSS HACK] default to a hammer if there are no players
|
|
// (this genuinely can happen, if player starts were placed AFTER the item)
|
|
int np = 0;
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo ) continue;
|
|
np++;
|
|
}
|
|
if ( np <= 0 ) return 'ItamexHammer';
|
|
// so the player can recover it if they decided to drop it in a previous map, or they didn't start with it
|
|
if ( CheckNeedsItem('DeepImpact') ) return 'DeepImpact';
|
|
if ( CheckNeedsItem('ItamexHammer') ) return 'ItamexHammer';
|
|
return PickSWWMSlot2();
|
|
}
|
|
// pistol spawn, pretty simple
|
|
static Class<Inventory> PickSWWMSlot2()
|
|
{
|
|
return 'ExplodiumGun';
|
|
// as they are dual-wieldable, there should be a 50% chance for spares to also appear if needed
|
|
/*if ( Random[Replacements](0,1) && !CheckNeedsItem('ExplodiumGun') && CheckNeedsItem('ExplodiumGun',true) )
|
|
return 'ExplodiumGun';
|
|
if ( Random[Replacements](0,1) && !CheckNeedsItem('PlasmaBlast') && CheckNeedsItem('PlasmaBlast',true) )
|
|
return 'PlasmaBlast';
|
|
return PickPair('ExplodiumGun','PlasmaBlast');*/
|
|
}
|
|
// shotgun spawn
|
|
static Class<Inventory> PickSWWMSlot3()
|
|
{
|
|
return 'Spreadgun';
|
|
//return PickPair('Spreadgun','PuntzerBeta');
|
|
}
|
|
// super shotgun spawn
|
|
static Class<Inventory> PickSWWMSlot4()
|
|
{
|
|
return 'Wallbuster';
|
|
//return PickPair('Wallbuster','PuntzerGamma');
|
|
}
|
|
// chaingun spawn
|
|
static Class<Inventory> PickSWWMSlot5()
|
|
{
|
|
return PickPair('Eviscerator','HeavyMahSheenGun');
|
|
}
|
|
// rocket launcher spawn
|
|
static Class<Inventory> PickSWWMSlot6()
|
|
{
|
|
return PickPair('Hellblazer','Quadravol');
|
|
}
|
|
// first plasma rifle spawn
|
|
static Class<Inventory> PickSWWMSlot7()
|
|
{
|
|
return 'Sparkster';
|
|
//return PickPair('Sparkster','ModernSparkster');
|
|
}
|
|
// second plasma rifle spawn
|
|
static Class<Inventory> PickSWWMSlot8()
|
|
{
|
|
return 'SilverBullet';
|
|
//return PickPair('SilverBullet','RayKhom');
|
|
}
|
|
// first bfg spawn
|
|
static Class<Inventory> PickSWWMSlot9()
|
|
{
|
|
// 25% chance to still drop another candy gun if it's not at max capacity
|
|
if ( !Random[Replacements](0,3) && ItemExists('CandyGun') && CheckNeedsItem('CandyGunSpares',true) )
|
|
return 'CandyGun';
|
|
return PickPair('CandyGun','MisterRifle');
|
|
}
|
|
// second bfg spawn (each weapon can only exist once)
|
|
static Class<Inventory> PickSWWMSlot0( bool fallback = true )
|
|
{
|
|
if ( ItemExists('Ynykron',mapstart:true) )
|
|
{
|
|
//if ( ItemExists('RafanKos',mapstart:true) )
|
|
return fallback?PickSWWMSlot9():null;
|
|
//return 'RafanKos';
|
|
}
|
|
//if ( ItemExists('RafanKos',mapstart:true) )
|
|
return 'Ynykron';
|
|
//return Random[Replacements](0,1)?'Ynykron':'RafanKos';
|
|
}
|
|
// either plasma rifle spawn
|
|
static Class<Inventory> PickDoomSlot6()
|
|
{
|
|
bool hasslot7 = (!CheckNeedsItem('Sparkster')/*||!CheckNeedsItem('ModernSparkster')*/);
|
|
bool hasslot8 = (!CheckNeedsItem('SilverBullet')/*||!CheckNeedsItem('RayKhom')*/);
|
|
// if the player already has a slot 7 weapon...
|
|
if ( hasslot7 )
|
|
{
|
|
// ... and also has a slot 8 weapon, 33% chance of a slot 8 spawn
|
|
// otherwise, guaranteed slot 8 spawn
|
|
if ( hasslot8 && Random[Replacements](0,2) ) return PickSWWMSlot7();
|
|
else return PickSWWMSlot8();
|
|
}
|
|
// otherwise, always spawn a slot 7 weapon first
|
|
return PickSWWMSlot7();
|
|
}
|
|
// either bfg spawn
|
|
static Class<Inventory> PickDoomSlot7()
|
|
{
|
|
bool hasslot9 = (!CheckNeedsItem('CandyGun')||!CheckNeedsItem('MisterRifle'));
|
|
bool hasslot0 = (!CheckNeedsItem('Ynykron')/*||!CheckNeedsItem('RafanKos')*/);
|
|
let rep = PickSWWMSlot0(false);
|
|
// if the player already has a slot 9 weapon (and a slot 0 weapon can still spawn)...
|
|
if ( hasslot9 && rep )
|
|
{
|
|
// ... and also has a slot 0 weapon already, 33% chance of a slot 0 spawn
|
|
// otherwise, guaranteed slot 0 spawn
|
|
if ( hasslot0 && Random[Replacements](0,2) ) return PickSWWMSlot9();
|
|
else return rep;
|
|
}
|
|
// otherwise, always spawn a slot 9 weapon first
|
|
return PickSWWMSlot9();
|
|
}
|
|
// either shotgun spawn (also used for Heretic)
|
|
static Class<Inventory> PickDoomSlot3()
|
|
{
|
|
// always slot 3 after map start, prevents shotgun guys from dropping wallbusters, which is weird af
|
|
if ( level.maptime ) return PickSWWMSlot3();
|
|
bool hasslot3 = (!CheckNeedsItem('Spreadgun')/*||!CheckNeedsItem('PuntzerBeta')*/);
|
|
bool hasslot4 = (!CheckNeedsItem('Wallbuster')/*||!CheckNeedsItem('PuntzerGamma')*/);
|
|
// if the player already has a slot 3 weapon...
|
|
if ( hasslot3 )
|
|
{
|
|
// ... and also has a slot 4 weapon, 33% chance of a slot 4 spawn
|
|
// otherwise, guaranteed slot 4 spawn
|
|
if ( hasslot4 && Random[Replacements](0,2) ) return PickSWWMSlot3();
|
|
return PickSWWMSlot4();
|
|
}
|
|
// otherwise, always spawn a slot 3 weapon first
|
|
return PickSWWMSlot3();
|
|
}
|
|
}
|