swwmgz_m/zscript/items/swwm_baseammo.zsc

624 lines
15 KiB
Text

// Common code for ammo division
Class SWWMAmmo : Ammo
{
Mixin SWWMOverlapPickupSound;
Mixin SWWMUseToPickup;
Mixin SWWMRespawn;
Mixin SWWMRotatingPickup;
Mixin SWWMUnrealStyleDrop;
meta String PickupTag;
meta Class<MagAmmo> MagAmmoType;
private int SAmmoFlags;
Property PickupTag : PickupTag;
Property MagAmmoType : MagAmmoType;
FlagDef UsePickupMsg : SAmmoFlags, 0; // use the set pickup message rather than generating from pickup tag
override Class<Ammo> GetParentAmmo()
{
Class<Object> type = GetClass();
while ( (type.GetParentClass() != "SWWMAmmo") && type.GetParentClass() )
type = type.GetParentClass();
return (Class<Ammo>)(type);
}
override string PickupMessage()
{
if ( bUsePickupMsg ) return Super.PickupMessage();
String tagstr = "$T_"..PickupTag;
if ( Amount > 1 )
{
tagstr = tagstr.."S";
return String.Format("%d %s",Amount,StringTable.Localize(tagstr));
}
return StringTable.Localize(tagstr);
}
private Inventory DoDrop( Class<Inventory> type )
{
let copy = Inventory(Spawn(type,Owner.Pos,NO_REPLACE));
if ( !copy ) return null;
copy.DropTime = 30;
copy.bSpecial = copy.bSolid = false;
copy.SetOrigin(Owner.Vec3Offset(0,0,10.),false);
copy.Angle = Owner.Angle;
copy.VelFromAngle(5.);
copy.Vel.Z = 1.;
copy.Vel += Owner.Vel;
copy.bNoGravity = false;
copy.ClearCounters();
copy.OnDrop(Owner);
copy.vel += (RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),Owner.angle),FRandom[Junk](2.,5.));
return copy;
}
override bool SpecialDropAction( Actor dropper )
{
if ( swwm_enemydrops >= 0 )
{
// random chance to not drop
if ( Random[DropChance](1,100) <= Accuracy ) return true;
if ( Amount == default.Amount ) return false;
// subdivide
Owner = dropper; // needed for positioning to work
CreateTossable(Amount);
return true;
}
// no ammo drops from enemies
return true;
}
private bool CmpAmmo( Class<Ammo> a, Class<Ammo> b )
{
let amta = GetDefaultByType(a).Amount;
let amtb = GetDefaultByType(b).Amount;
return (amta < amtb);
}
private int partition_ammotypes( Array<Class<Ammo> > a, int l, int h )
{
Class<Ammo> pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpAmmo(pv,a[j]) )
{
i++;
Class<Ammo> tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Class<Ammo> tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_ammotypes( Array<Class<Ammo> > a, int l, int h )
{
if ( l >= h ) return;
int p = partition_ammotypes(a,l,h);
qsort_ammotypes(a,l,p-1);
qsort_ammotypes(a,p+1,h);
}
override inventory CreateTossable( int amt )
{
if ( bUndroppable || bUntossable || !Owner || (Amount <= 0) || (amt == 0) )
return null;
// cap
amt = min(amount,amt);
// enumerate all subclasses
Array<Class<Ammo> > ammotypes;
ammotypes.Clear();
foreach ( cls:AllActorClasses )
{
if ( cls is GetParentAmmo() )
ammotypes.Push((Class<Ammo>)(cls));
}
// sort from largest to smallest
qsort_ammotypes(ammotypes,0,ammotypes.Size()-1);
// perform subdivision
Inventory last = null;
while ( amt > 0 )
{
foreach ( type:ammotypes )
{
let def = GetDefaultByType(type);
if ( amt >= def.Amount )
{
last = DoDrop(type);
amt -= def.Amount;
Amount -= def.Amount;
break;
}
}
}
return last;
}
override bool HandlePickup( Inventory item )
{
// drop excess ammo
if ( !bUNDROPPABLE && !bUNTOSSABLE && (item is 'Ammo') && (Ammo(item).GetParentAmmo() == GetParentAmmo()) )
{
int excess = Amount+item.Amount;
if ( excess > MaxAmount ) excess -= MaxAmount;
if ( excess < item.Amount )
{
// enumerate all subclasses
Array<Class<Ammo> > ammotypes;
ammotypes.Clear();
foreach ( cls:AllActorClasses )
{
if ( cls is GetParentAmmo() )
ammotypes.Push((Class<Ammo>)(cls));
}
// sort from largest to smallest
qsort_ammotypes(ammotypes,0,ammotypes.Size()-1);
// drop spares
Inventory last;
while ( excess > 0 )
{
// first of all, see if we can ADD mag ammo
if ( MagAmmoType )
{
let ma = MagAmmo(Owner.FindInventory(MagAmmoType));
if ( !ma )
{
ma = MagAmmo(Spawn(MagAmmoType));
ma.Amount = 0;
ma.AttachToOwner(Owner);
}
if ( ma.Amount <= (ma.MaxAmount-ma.ClipSize) )
{
ma.Amount += ma.ClipSize;
excess--;
continue;
}
}
foreach ( type:ammotypes )
{
let def = GetDefaultByType(type);
if ( excess >= def.Amount )
{
double ang = FRandom[Junk](0,360);
last = DoDrop(type);
last.SetOrigin(item.pos,false);
last.vel.xy = AngleToVector(ang,FRandom[Junk](2,5));
excess -= def.Amount;
break;
}
}
}
}
else if ( MagAmmoType )
{
// can we split it into mag ammo?
let ma = MagAmmo(Owner.FindInventory(MagAmmoType));
if ( !ma )
{
ma = MagAmmo(Spawn(MagAmmoType));
ma.Amount = 0;
ma.AttachToOwner(Owner);
}
if ( !GetDefaultByType(MagAmmoType).bUNDROPPABLE && !GetDefaultByType(MagAmmoType).bUNTOSSABLE )
{
if ( ma.Amount < ma.MaxAmount )
{
// split into bullets
for ( int i=0; i<item.Amount; i++ )
{
if ( Amount < MaxAmount )
{
Amount++;
continue;
}
int bul = ma.ClipSize;
int maxgiveamt = min(ma.MaxAmount-ma.Amount,bul);
int dropamt = bul-maxgiveamt;
if ( dropamt > 0 ) ma.CreateTossable(dropamt);
ma.Amount = min(ma.MaxAmount,ma.Amount+bul);
}
item.bPickupGood = true;
return true;
}
}
else if ( ma.Amount <= (ma.MaxAmount-ma.ClipSize*item.Amount) )
{
// when mag ammo is undroppable, can only divide in full mags EXACTLY
ma.Amount += ma.ClipSize*item.Amount;
item.bPickupGood = true;
return true;
}
}
}
return Super.HandlePickup(item);
}
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
// attach our mag ammo if we have none
if ( MagAmmoType && !Owner.FindInventory(MagAmmoType) )
{
let ma = Inventory(Spawn(MagAmmoType));
ma.Amount = 0;
ma.AttachToOwner(Owner);
}
}
override void ModifyDropAmount( int dropamount )
{
Super.ModifyDropAmount(dropamount);
int maxdrop = 1;
foreach ( cls:AllActorClasses )
{
if ( !(cls is GetParentAmmo()) ) continue;
let def = GetDefaultByType((Class<Ammo>)(cls));
maxdrop = max(maxdrop,def.amount);
}
Amount = Random[ShellDrop](1,clamp(dropamount,1,maxdrop));
}
default
{
+INVENTORY.IGNORESKILL;
+DONTGIB;
Inventory.PickupFlash "SWWMPickupFlash";
+FLOATBOB;
FloatBobStrength 0.25;
}
}
// Common code for individual bullets
Class MagAmmo : Inventory abstract
{
Mixin SWWMOverlapPickupSound;
Mixin SWWMUseToPickup;
Mixin SWWMRespawn;
Mixin SWWMRotatingPickup;
Mixin SWWMUnrealStyleDrop;
meta Class<Ammo> ParentAmmo;
Ammo pamo;
meta int ClipSize;
meta String PickupTag;
int BackpackAmount;
Property ParentAmmo : ParentAmmo;
Property ClipSize : ClipSize;
Property PickupTag : PickupTag;
Property BackpackAmount : BackpackAmount;
default
{
+INVENTORY.KEEPDEPLETED;
+DONTGIB;
Inventory.PickupSound "misc/bullet_pkup";
Inventory.Amount 1;
Inventory.PickupFlash "SWWMPickupFlash";
+FLOATBOB;
FloatBobStrength 0.25;
}
virtual Class<MagAmmo> GetParentMagAmmo()
{
Class<Object> type = GetClass();
while ( (type.GetParentClass() != "MagAmmo") && type.GetParentClass() )
type = type.GetParentClass();
return (Class<MagAmmo>)(type);
}
private bool CmpAmmo( Class<MagAmmo> a, Class<MagAmmo> b )
{
let amta = GetDefaultByType(a).Amount;
let amtb = GetDefaultByType(b).Amount;
return (amta < amtb);
}
private int partition_ammotypes( Array<Class<MagAmmo> > a, int l, int h )
{
Class<MagAmmo> pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpAmmo(pv,a[j]) )
{
i++;
Class<MagAmmo> tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Class<MagAmmo> tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_ammotypes( Array<Class<MagAmmo> > a, int l, int h )
{
if ( l >= h ) return;
int p = partition_ammotypes(a,l,h);
qsort_ammotypes(a,l,p-1);
qsort_ammotypes(a,p+1,h);
}
override string PickupMessage()
{
String tagstr = "$T_"..PickupTag;
if ( Amount > 1 )
{
tagstr = tagstr.."S";
return String.Format("%d %s",Amount,StringTable.Localize(tagstr));
}
return StringTable.Localize(tagstr);
}
override bool HandlePickup( Inventory item )
{
// drop excess mag ammo
if ( (item is 'MagAmmo') && (MagAmmo(item).GetParentMagAmmo() == GetClass()) )
{
if ( bUNDROPPABLE || bUNTOSSABLE )
{
// undroppable mag ammo only drops full mags.
// due to the way this works, we theoretically
// should never end up with ammo "disappearing"
while ( Amount+item.Amount >= MaxAmount )
{
if ( Amount < ClipSize ) break;
// first of all, see if we can INCREASE
// parent ammo, rather than drop a mag
if ( pamo.Amount < pamo.MaxAmount ) pamo.Amount++;
else if ( !pamo.bUNDROPPABLE && !pamo.bUNTOSSABLE ) DoDrop(ParentAmmo);
Amount -= ClipSize;
}
}
else if ( pamo.Amount < pamo.MaxAmount )
{
// see if we can fill mags with this, and drop the excess
int toadd = Amount+item.Amount;
while ( (pamo.Amount < pamo.MaxAmount) && (toadd >= ClipSize) )
{
pamo.Amount++;
toadd -= ClipSize;
}
Amount = toadd;
if ( Amount > MaxAmount ) CreateTossable(Amount-MaxAmount);
item.bPickupGood = true;
return true;
}
else
{
int excess = Amount+item.Amount;
if ( excess > MaxAmount ) excess -= MaxAmount;
if ( excess < item.Amount )
{
// enumerate all subclasses
Array<Class<MagAmmo> > ammotypes;
ammotypes.Clear();
foreach ( cls:AllActorClasses )
{
if ( cls is GetParentMagAmmo() )
ammotypes.Push((Class<MagAmmo>)(cls));
}
// sort from largest to smallest
qsort_ammotypes(ammotypes,0,ammotypes.Size()-1);
// drop spares
Inventory last;
while ( excess > 0 )
{
// drop full mag if possible
if ( excess >= ClipSize )
{
double ang = FRandom[Junk](0,360);
last = DoDrop(ParentAmmo);
last.SetOrigin(item.pos,false);
last.vel.xy = AngleToVector(ang,FRandom[Junk](2,5));
excess -= ClipSize;
continue;
}
// drop bullets otherwise
foreach ( type:ammotypes )
{
let def = GetDefaultByType(type);
if ( excess >= def.Amount )
{
double ang = FRandom[Junk](0,360);
last = DoDrop(type);
last.SetOrigin(item.pos,false);
last.vel.xy = AngleToVector(ang,FRandom[Junk](2,5));
excess -= def.Amount;
break;
}
}
}
}
}
if ( Amount < MaxAmount )
{
int receiving = item.Amount;
int oldamt = Amount;
if ( (Amount > 0) && ((Amount+item.Amount) < 0) ) Amount = int.max;
else Amount += item.Amount;
if ( Amount > MaxAmount && !sv_unlimited_pickup ) Amount = MaxAmount;
item.bPickupGood = true;
// autoswitch if needed (checks parent ammo type)
if ( (oldamt == 0) && Owner && Owner.player )
PlayerPawn(Owner).CheckWeaponSwitch(ParentAmmo);
}
return true;
}
return false;
}
override Inventory CreateCopy( Actor other )
{
Inventory copy;
int amount = Amount;
let type = GetParentMagAmmo();
if ( (GetClass() != type) && type )
{
if ( !GoAway() ) Destroy();
copy = Inventory(Spawn(type));
copy.Amount = amount;
copy.BecomeItem();
}
else
{
copy = Super.CreateCopy(other);
copy.Amount = amount;
}
if ( copy.Amount > copy.MaxAmount ) copy.Amount = copy.MaxAmount;
return copy;
}
private Inventory DoDrop( Class<Inventory> type )
{
let copy = Inventory(Spawn(type,Owner.Pos,NO_REPLACE));
if ( !copy ) return null;
copy.DropTime = 30;
copy.bSpecial = copy.bSolid = false;
copy.SetOrigin(Owner.Vec3Offset(0,0,10.),false);
copy.Angle = Owner.Angle;
copy.VelFromAngle(5.);
copy.Vel.Z = 1.;
copy.Vel += Owner.Vel;
copy.bNoGravity = false;
copy.ClearCounters();
copy.OnDrop(Owner);
copy.vel += (RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),Owner.angle),FRandom[Junk](2.,5.));
return copy;
}
override bool SpecialDropAction( Actor dropper )
{
if ( swwm_enemydrops >= 0 )
{
// random chance to not drop
if ( Random[DropChance](1,100) <= Accuracy ) return true;
if ( Amount == default.Amount ) return false;
// subdivide
Owner = dropper; // needed for positioning to work
CreateTossable(Amount);
return true;
}
// no ammo drops from enemies
return true;
}
override void DoEffect()
{
Super.DoEffect();
if ( !pamo )
{
pamo = Ammo(Owner.FindInventory(ParentAmmo));
if ( !pamo )
{
pamo = Ammo(Spawn(ParentAmmo));
pamo.AttachToOwner(Owner);
pamo.Amount = 0;
}
}
// check if we can fill a mag
if ( (Amount < ClipSize) || (pamo.Amount >= pamo.MaxAmount) )
return;
MagFill();
}
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
pamo = Ammo(other.FindInventory(ParentAmmo));
if ( !pamo )
{
pamo = Ammo(Spawn(ParentAmmo));
pamo.AttachToOwner(other);
pamo.Amount = 0;
}
}
bool MagFill()
{
// double-check that parent ammo exists
if ( !pamo )
{
pamo = Ammo(Owner.FindInventory(ParentAmmo));
if ( !pamo )
{
pamo = Ammo(Spawn(ParentAmmo));
pamo.AttachToOwner(Owner);
pamo.Amount = 0;
}
}
bool given = false;
while ( (pamo.Amount < pamo.MaxAmount) && (Amount >= ClipSize) )
{
pamo.Amount++;
Amount -= ClipSize;
given = true;
}
return given;
}
override inventory CreateTossable( int amt )
{
if ( bUndroppable || bUntossable || !Owner || (Amount <= 0) || (amt == 0) )
return null;
// cap
amt = min(amount,amt);
// enumerate all subclasses
Array<Class<MagAmmo> > ammotypes;
ammotypes.Clear();
foreach ( cls:AllActorClasses )
{
if ( cls is GetParentMagAmmo() )
ammotypes.Push((Class<MagAmmo>)(cls));
}
// sort from largest to smallest
qsort_ammotypes(ammotypes,0,ammotypes.Size()-1);
// perform subdivision
Inventory last = null;
let pammo = GetDefaultByType(ParentAmmo);
while ( amt > 0 )
{
// drop full mag if possible
if ( amt >= ClipSize )
{
last = DoDrop(ParentAmmo);
amt -= ClipSize;
Amount -= ClipSize;
continue;
}
// drop bullets otherwise
foreach ( type:ammotypes )
{
let def = GetDefaultByType(type);
if ( amt >= def.Amount )
{
last = DoDrop(type);
amt -= def.Amount;
Amount -= def.Amount;
break;
}
}
}
return last;
}
override void ModifyDropAmount( int dropamount )
{
Super.ModifyDropAmount(dropamount);
int maxdrop = 1;
foreach ( cls:AllActorClasses )
{
if ( !(cls is GetParentMagAmmo()) ) continue;
let def = GetDefaultByType((Class<MagAmmo>)(cls));
maxdrop = max(maxdrop,def.amount);
}
Amount = Random[ShellDrop](1,clamp(dropamount,1,maxdrop));
}
}