swwmgz_m/zscript/items/swwm_ammoextra.zsc
Marisa the Magician 80db58b0d0 Bump zscript ver to 4.14.1, plus a whole lot of stuff.
- Try to get rid of all implicit casts from string to name, color or class.
 - Use FindClass where needed.
 - Used a map in a case where a dictionary was unneeded.
 - Use new bounce flags where needed.
 - Replace Legacy of Rust weapons/ammo.
2025-03-13 14:50:58 +01:00

607 lines
17 KiB
Text

// ============================================================================
// Ammo fabricator
// ============================================================================
Class AmmoFabricator : Inventory abstract
{
Mixin SWWMOverlapPickupSound;
Mixin SWWMUseToPickup;
Mixin SWWMRespawn;
Mixin SWWMRotatingPickup;
Mixin SWWMPickupGlow;
meta int budget, pertype, maxunits, maxtypes, maxunitprice, txtcol;
meta int chancediv;
String pickupmsgextra;
Property Budget : budget;
Property PerType : pertype;
Property MaxUnits : maxunits;
Property MaxTypes : maxtypes;
Property MaxUnitPrice : maxunitprice;
Property ChanceFactor : chancediv;
Property TextColor : txtcol;
override Inventory CreateCopy( Actor other )
{
// additional lore
SWWMLoreLibrary.Add(other.player,"Fabricator");
return Super.CreateCopy(other);
}
private bool CmpFabAmmo( Class<SWWMAmmo> a, Class<SWWMAmmo> b )
{
let ia = Owner.FindInventory(a);
int cnta = ia?ia.Amount:0;
int maxa = ia?ia.MaxAmount:GetDefaultByType(a).Amount;
let ib = Owner.FindInventory(b);
int cntb = ib?ib.Amount:0;
int maxb = ib?ib.MaxAmount:GetDefaultByType(b).Amount;
double facta = cnta/double(maxa);
double factb = cntb/double(maxb);
return (facta >= factb);
}
private int partition_fabammo( Array<Class<SWWMAmmo> > a, int l, int h )
{
Class<SWWMAmmo> pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpFabAmmo(pv,a[j]) )
{
i++;
Class<SWWMAmmo> tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Class<SWWMAmmo> tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_fabammo( Array<Class<SWWMAmmo> > a, int l, int h )
{
if ( l >= h ) return;
int p = partition_fabammo(a,l,h);
qsort_fabammo(a,l,p-1);
qsort_fabammo(a,p+1,h);
}
private bool CmpFabAmmo_chance( Class<SWWMAmmo> a, Class<SWWMAmmo> b )
{
int cha = GetDefaultByType(a).Accuracy;
int chb = GetDefaultByType(b).Accuracy;
return (cha >= chb);
}
private int partition_fabammo_chance( Array<Class<SWWMAmmo> > a, int l, int h )
{
Class<SWWMAmmo> pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpFabAmmo_chance(pv,a[j]) )
{
i++;
Class<SWWMAmmo> tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Class<SWWMAmmo> tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_fabammo_chance( Array<Class<SWWMAmmo> > a, int l, int h )
{
if ( l >= h ) return;
int p = partition_fabammo_chance(a,l,h);
qsort_fabammo_chance(a,l,p-1);
qsort_fabammo_chance(a,p+1,h);
}
override String PickupMessage()
{
if ( pickupmsgextra != "" ) return String.Format("\c%c%s\c-\n%s",0x61+txtcol,StringTable.Localize(pickupmsg),pickupmsgextra);
return pickupmsg;
}
bool FabricateAmmo()
{
Array<Class<SWWMAmmo> > available;
// populate ammo production list
foreach ( cls:AllActorClasses )
{
let a = (Class<SWWMAmmo>)(cls);
// skip over candy gun spares, they're "special ammo"
if ( a == 'CandyGunSpares' ) continue;
// only direct descendants of swwmammo with a set price below our max unit price
if ( !a || (a.GetParentClass() != 'SWWMAmmo') ) continue;
let def = GetDefaultByType(a);
if ( (def.Stamina <= 0) || (def.Stamina > maxunitprice) ) continue;
let f = Owner.FindInventory(a);
// don't include maxed out ammo
if ( f && (f.Amount >= f.MaxAmount) ) continue;
available.Push(a);
}
// sort by drop chance
qsort_fabammo_chance(available,0,available.Size()-1);
// discard some candidates based on their random drop chance (leaving AT LEAST one)
for ( int i=0; i<available.Size(); i++ )
{
if ( available.Size() == 1 ) break;
int chance = GetDefaultByType(available[i]).Accuracy;
if ( chancediv <= 0 ) chance = 0;
else chance /= chancediv;
if ( Random[DropChance](1,100) > chance ) continue;
available.Delete(i);
i--;
}
// sort by "need weight" (prioritize ammo that the player lacks over ammo that the player has plenty of)
qsort_fabammo(available,0,available.Size()-1);
// crop by "max types"
if ( available.Size() > maxtypes ) available.Resize(maxtypes);
// loop through until we fill the inventory or run out of budget
bool given = false;
int consumed = 0;
String fabstr = "";
bool comma = false;
int tpertype = pertype;
int ttotal = maxunits;
foreach ( type:available )
{
int amt, lim;
int cnt = 0;
SWWMAmmo cur = SWWMAmmo(Owner.FindInventory(type));
if ( cur )
{
amt = cur.Amount;
lim = cur.MaxAmount;
}
else
{
cur = SWWMAmmo(Spawn(type));
amt = cur.Amount = 0;
lim = cur.MaxAmount;
cur.AttachToOwner(Owner);
}
// percentage based on DEFAULT max amount (capped at 1 minimum)
if ( pertype < 0 ) tpertype = max(1,-int(cur.default.MaxAmount*pertype*.01));
tpertype = min(tpertype,ttotal);
while ( (amt < lim) && (consumed+cur.default.Stamina < budget) && (cnt < tpertype) )
{
consumed += cur.default.Stamina;
amt = ++cur.Amount;
cnt++;
given = true;
}
if ( cnt > 0 )
{
String tstr = String.Format("%d %s",cnt,(cnt>1)?StringTable.Localize("$T_"..cur.PickupTag.."S"):StringTable.Localize("$T_"..cur.PickupTag));
if ( comma ) fabstr = fabstr..", "..tstr;
else fabstr = tstr;
comma = true;
}
ttotal -= cnt;
if ( ttotal <= 0 ) break;
}
if ( given ) pickupmsgextra = fabstr;
else pickupmsgextra = "";
return given;
}
override bool Use( bool pickup )
{
if ( FabricateAmmo() )
{
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) && !bQUIET ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA);
return true;
}
return false;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
SetZ(floorz); // gee whizz thanks Hexen
}
Default
{
+INVENTORY.AUTOACTIVATE;
+FLOATBOB;
+DONTGIB;
Inventory.UseSound "fabricator/use";
Inventory.PickupFlash 'SWWMPickupFlash';
Inventory.MaxAmount 0;
FloatBobStrength 0.25;
}
States
{
Spawn:
XZW1 A -1;
Stop;
}
}
Class FabricatorTier1 : AmmoFabricator
{
Mixin SWWMAutoUseFix;
Default
{
Tag "$T_FABRICATOR1";
Inventory.PickupMessage "$T_FABRICATOR1";
AmmoFabricator.Budget 6000;
AmmoFabricator.PerType -15;
AmmoFabricator.MaxUnits 5;
AmmoFabricator.MaxTypes 2;
AmmoFabricator.MaxUnitPrice 2500;
AmmoFabricator.ChanceFactor 1;
AmmoFabricator.TextColor Font.CR_BLUE;
// Completely stupid hitbox, but necessary to not break compat
Radius 8;
Height 8;
}
}
Class FabricatorTier2 : AmmoFabricator
{
Mixin SWWMAutoUseFix;
Default
{
Tag "$T_FABRICATOR2";
Inventory.PickupMessage "$T_FABRICATOR2";
AmmoFabricator.Budget 20000;
AmmoFabricator.PerType -20;
AmmoFabricator.MaxUnits 10;
AmmoFabricator.MaxTypes 3;
AmmoFabricator.MaxUnitPrice 18000;
AmmoFabricator.ChanceFactor 2;
AmmoFabricator.TextColor Font.CR_GREEN;
// Completely stupid hitbox, but necessary to not break compat
Radius 8;
Height 8;
}
}
Class FabricatorTier3 : AmmoFabricator
{
Mixin SWWMAutoUseFix;
Default
{
Tag "$T_FABRICATOR3";
Inventory.PickupMessage "$T_FABRICATOR3";
AmmoFabricator.Budget 60000;
AmmoFabricator.PerType -25;
AmmoFabricator.MaxUnits 15;
AmmoFabricator.MaxTypes 4;
AmmoFabricator.MaxUnitPrice 50000;
AmmoFabricator.ChanceFactor 4;
AmmoFabricator.TextColor Font.CR_RED;
// Completely stupid hitbox, but necessary to not break compat
Radius 8;
Height 8;
}
}
Class FabricatorTier4 : AmmoFabricator
{
Mixin SWWMAutoUseFix;
Default
{
Tag "$T_FABRICATOR4";
Inventory.PickupMessage "$T_FABRICATOR4";
AmmoFabricator.Budget int.max;
AmmoFabricator.PerType -50;
AmmoFabricator.MaxUnits int.max;
AmmoFabricator.MaxTypes int.max;
AmmoFabricator.MaxUnitPrice 1000000;
AmmoFabricator.ChanceFactor 0;
AmmoFabricator.TextColor Font.CR_GOLD;
}
}
// ============================================================================
// Hammerspace embiggener
// ============================================================================
Class HammerspaceEmbiggener : Inventory
{
Mixin SWWMOverlapPickupSound;
Mixin SWWMUseToPickup;
Mixin SWWMRespawn;
Mixin SWWMRotatingPickup;
override Inventory CreateCopy( Actor other )
{
bool traded = (GetClass()=='TradedHammerspaceEmbiggener');
if ( !traded ) other.A_StartSound("powerup/embiggener",CHAN_ITEMEXTRA);
// Find every unique type of ammoitem. Give it to the player if
// they don't have it already, and increase its maximum capacity.
foreach ( cls:AllActorClasses )
{
let type = (class<Ammo>)(cls);
if ( !type || (type.GetParentClass() != 'SWWMAmmo') ) continue;
let ammoitem = Ammo(other.FindInventory(type));
int amount = GetDefaultByType(type).BackpackAmount*self.Amount;
if ( traded ) amount = 0;
if ( amount < 0 ) amount = 0;
if ( !ammoitem )
{
// The player did not have the ammoitem. Add it.
ammoitem = Ammo(Spawn(type));
ammoitem.Amount = amount;
if ( ammoitem.BackpackMaxAmount > 0 )
{
double factor = (ammoitem.BackpackMaxAmount-ammoitem.default.MaxAmount)/double(MaxAmount);
ammoitem.MaxAmount = int(ammoitem.default.MaxAmount+min(self.Amount,MaxAmount)*factor);
}
if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup )
ammoitem.Amount = ammoitem.MaxAmount;
ammoitem.AttachToOwner(other);
}
else
{
// The player had the ammoitem. Give some more.
if ( ammoitem.BackpackMaxAmount > 0 )
{
double factor = (ammoitem.BackpackMaxAmount-ammoitem.default.MaxAmount)/double(MaxAmount);
ammoitem.MaxAmount = int(ammoitem.default.MaxAmount+min(self.Amount,MaxAmount)*factor);
}
if ( ammoitem.Amount < ammoitem.MaxAmount )
{
if ( (ammoitem.Amount > 0) && (ammoitem.Amount+amount < 0) )
ammoitem.Amount = int.max;
else ammoitem.Amount += amount;
if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup )
ammoitem.Amount = ammoitem.MaxAmount;
}
}
}
// do the same for mag ammo, in a separate loop
foreach ( cls:AllActorClasses )
{
let type = (class<MagAmmo>)(cls);
if ( !type || (type.GetParentClass() != 'MagAmmo') ) continue;
let magitem = MagAmmo(other.FindInventory(type));
int amount = GetDefaultByType(type).BackpackAmount*self.Amount;
if ( traded ) amount = 0;
if ( amount < 0 ) amount = 0;
int mags = amount/GetDefaultByType(type).ClipSize;
amount = amount%GetDefaultByType(type).ClipSize;
if ( !magitem )
{
// The player did not have the magitem. Add it.
magitem = MagAmmo(Spawn(type));
magitem.Amount = amount;
magitem.AttachToOwner(other);
// by this point, we assume that the parent ammo pointer is valid, so...
let ammoitem = magitem.pamo;
// append some mags to it
if ( ammoitem.Amount < ammoitem.MaxAmount )
{
if ( (ammoitem.Amount > 0) && (ammoitem.Amount+mags < 0) )
ammoitem.Amount = int.max;
else ammoitem.Amount += mags;
if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup )
ammoitem.Amount = ammoitem.MaxAmount;
}
// we can't add extra mags, just max out the spare ammo
else if ( mags > 0 ) magitem.Amount = min(magitem.MaxAmount,magitem.Amount+magitem.ClipSize);
}
else
{
if ( magitem.Amount+amount >= magitem.MaxAmount )
{
mags++;
amount -= magitem.MaxAmount;
}
magitem.Amount += amount;
let ammoitem = magitem.pamo;
// append some mags to it
if ( ammoitem.Amount < ammoitem.MaxAmount )
{
if ( (ammoitem.Amount > 0) && (ammoitem.Amount+mags < 0) )
ammoitem.Amount = int.max;
else ammoitem.Amount += mags;
if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup )
ammoitem.Amount = ammoitem.MaxAmount;
}
// we can't add extra mags, just max out the spare ammo
else if ( mags > 0 ) magitem.Amount = min(magitem.MaxAmount,magitem.Amount+magitem.ClipSize);
}
}
self.Amount = min(self.Amount,MaxAmount);
if ( GetParentClass() == 'HammerspaceEmbiggener' )
{
if ( !GoAway() ) Destroy();
let copy = Inventory(Spawn('HammerspaceEmbiggener'));
copy.ClearCounters();
copy.Amount = self.Amount;
copy.MaxAmount = self.MaxAmount;
return copy;
}
if ( GoAway() )
{
let copy = Inventory(Spawn(GetClass()));
copy.ClearCounters();
copy.Amount = self.Amount;
copy.MaxAmount = self.MaxAmount;
return copy;
}
return self;
}
override bool HandlePickup( Inventory item )
{
if ( (item.GetClass() == GetClass()) || (item.GetParentClass() == 'HammerspaceEmbiggener') )
{
bool traded = (item.GetClass()=='TradedHammerspaceEmbiggener');
if ( !traded ) Owner.A_StartSound("powerup/embiggener",CHAN_ITEMEXTRA);
if ( (Amount > 0) && (Amount+item.Amount < 0) )
Amount = int.max;
else Amount += item.Amount;
if ( Amount > MaxAmount ) Amount = MaxAmount;
item.bPickupGood = true;
// readjust ammo values to new capacity
for ( Inventory i=Owner.Inv; i; i=i.Inv )
{
if ( !(i is 'Ammo') ) continue;
if ( Ammo(i).BackpackMaxAmount > 0 )
{
double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(MaxAmount);
i.MaxAmount = int(i.default.MaxAmount+Amount*factor);
}
int amount = Ammo(i).BackpackAmount*item.Amount;
if ( traded ) amount = 0;
if ( (i.Amount > 0) && (i.Amount+amount < 0) )
i.Amount = int.max;
else i.Amount += amount;
if ( (i.Amount > i.MaxAmount) && !sv_unlimited_pickup )
i.Amount = i.MaxAmount;
}
if ( traded ) return true;
// give spare mag ammo separately
for ( Inventory i=Owner.Inv; i; i=i.Inv )
{
if ( !(i is 'MagAmmo') ) continue;
int amount = MagAmmo(i).BackpackAmount*item.Amount;
int mags = amount/MagAmmo(i).ClipSize;
amount = amount%MagAmmo(i).ClipSize;
if ( i.Amount+amount >= MagAmmo(i).ClipSize )
{
mags++;
amount -= MagAmmo(i).ClipSize;
}
i.Amount += amount;
Ammo a = MagAmmo(i).pamo;
if ( a.Amount < a.MaxAmount )
{
if ( (a.Amount > 0) && (a.Amount+mags < 0) )
a.Amount = int.max;
else a.Amount += mags;
if ( (a.Amount > a.MaxAmount) && !sv_unlimited_pickup )
a.Amount = a.MaxAmount;
}
else if ( mags > 0 ) i.Amount = min(i.MaxAmount,i.Amount+MagAmmo(i).ClipSize);
}
return true;
}
// new ammo suddenly added? upgrade it (this shouldn't happen unless weird scripting has been involved)
if ( (item is 'Ammo') && !Owner.FindInventory(Ammo(item).GetParentAmmo()) )
{
if ( Ammo(item).BackpackMaxAmount > 0 )
{
double factor = (Ammo(item).BackpackMaxAmount-item.default.MaxAmount)/double(MaxAmount);
item.MaxAmount = int(item.default.MaxAmount+Amount*factor);
}
}
return false;
}
override void DepleteOrDestroy()
{
// reset upgrade
for ( Inventory i=Owner.Inv; i; i=i.Inv )
{
if ( !(i is 'Ammo') ) continue;
i.MaxAmount = i.default.MaxAmount;
if ( i.Amount > i.MaxAmount ) i.Amount = i.MaxAmount;
}
Super.DepleteOrDestroy();
}
// merges overlapping embiggeners into a bulk upgrade
void A_MergeEmbiggeners()
{
// while we're at it, adjust our height,
// since backpacks are taller in Doom
if ( gameinfo.gametype&GAME_DoomChex )
A_SetSize(-1,26);
int tamount = Amount;
for ( Actor t=CurSector.thinglist; t; )
{
let next = t.snext;
if ( (t == self) || !(t is 'HammerspaceEmbiggener') || !(t.spawnpoint ~== spawnpoint) )
{
t = next;
continue;
}
tamount += HammerspaceEmbiggener(t).Amount;
t.ClearCounters();
t.Destroy();
t = next;
}
if ( tamount <= 1 ) return;
tamount -= tamount%2; // always even numbered
if ( GetClass() == 'BulkHammerspaceEmbiggener' )
{
Amount = min(tamount,MaxAmount);
return;
}
let n = Spawn('BulkHammerspaceEmbiggener',pos);
Inventory(n).Amount = min(tamount,MaxAmount);
SWWMUtility.TransferItemProp(self,n);
ClearCounters();
Destroy();
}
Default
{
Tag "$T_EMBIGGENER";
Stamina -800000;
Inventory.PickupMessage "$T_EMBIGGENER";
Inventory.MaxAmount 8;
Inventory.InterHubAmount 8;
Inventory.PickupFlash 'SWWMPickupFlash';
+INVENTORY.UNDROPPABLE;
+INVENTORY.UNTOSSABLE;
+INVENTORY.ALWAYSPICKUP;
+DONTGIB;
+FLOATBOB;
FloatBobStrength 0.25;
}
States
{
Spawn:
XZW1 A 0;
XZW1 A -1 A_MergeEmbiggeners();
Stop;
}
}
// used when cheating or trading, this version does not give ammo and is meant
// only for GiveInventory, so it shouldn't be spawned in the world
Class TradedHammerspaceEmbiggener : HammerspaceEmbiggener {}
// used to denote "merged" embiggeners, changes color based on amount
// green (2+)
// blue (4+)
// purple (6+)
// black (8+)
Class BulkHammerspaceEmbiggener : HammerspaceEmbiggener
{
override string PickupMessage()
{
return String.Format("%dx %s",Amount,StringTable.Localize("$T_BULKEMBIGGENER"));
}
States
{
Spawn:
XZW1 A 0;
XZW1 A -1
{
A_MergeEmbiggeners();
if ( bDestroyed ) return ResolveState(null);
if ( Amount > 1 ) return SpawnState+1+min(4,Amount/2);
return ResolveState(null);
}
XZW1 BCDE -1;
Stop;
}
}