- New fun options implemented (omnibusting, unlimited fuel, party time) - Biospark Carbine gets a requested nerf - Candygun combo fire has been buffed (watch out for that splash damage) - All powerup effects are additive (stacc 'em) - Automap hud respects gzdoom's cvars for toggling certain elements - Automap hud shows stats and times in gold when 100% / under par - Weapons and ammo are no longer affected by skill amount modifiers, for balance (and to avoid any weird glitches) - Sorting improvements for menu (weapons by slot, ammo by weapons, other items by value, etc.) - Grilled Cheese Sandwich now saves you from lethal falls properly - Blown kisses instakill nazis - Added non-violent Keen replacement (based on "Less mean-spirited Keen replacement" by SiFi270) - Added gib deaths for hell nobles, pinkies, cacos, revs and viles (sprites by Amuscaria and Ryan Cordell) - Blown kisses can activate use switches - Gestures can be chained by pressing a gesture button while another is playing - Fixed Grilled Cheese Sandwich not avoiding telefrags properly (now also works with voodoo dolls) - More precise weapon kill tracking (fixes some ragekit quirks) - Merge both DLC weaponsets, removing redundant weapons (see FuturePlans.md) - Discarded some collectables for the next updates, to save time - Preparation work for collectables update, including some (partial) lore files - Remove ammo fabricators from store, makes no sense to have them when you can just buy ammo directly - Cosmetic Boss Brain sprite replacements, just for fun - 10 more intermission tips, because yes - Added option to reduce distance at which enemy healthbars are picked - Various minor bugfixes and adjustments (and also some tiny typo fixes) - Ragekit now heals over time and with each hit (so it's more rewarding to go wild) - PNG optimization pass (again lol) - Fix crouched gestures having no facial animation
837 lines
18 KiB
Text
837 lines
18 KiB
Text
// collectable items that may drop sometimes
|
|
|
|
Class SWWMCollectable : Inventory abstract
|
|
{
|
|
int avail;
|
|
bool propagated;
|
|
|
|
Property Availability : avail;
|
|
|
|
// minimum gametype requirements
|
|
enum EAvailability
|
|
{
|
|
AVAIL_Strife = GAME_Strife,
|
|
AVAIL_Hexen = AVAIL_Strife|GAME_Hexen,
|
|
AVAIL_Heretic = AVAIL_Hexen|GAME_Heretic,
|
|
AVAIL_All = AVAIL_Heretic|GAME_DoomChex
|
|
};
|
|
|
|
Default
|
|
{
|
|
Inventory.PickupSound "menu/buyinv";
|
|
Inventory.Amount 1;
|
|
Inventory.MaxAmount 1;
|
|
SWWMCollectable.Availability AVAIL_All;
|
|
+INVENTORY.UNTOSSABLE;
|
|
+INVENTORY.UNDROPPABLE;
|
|
+INVENTORY.UNCLEARABLE;
|
|
+FLOATBOB;
|
|
FloatBobStrength 0.25;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
// delet ourselves if wrong iwad
|
|
if ( !(gameinfo.gametype&avail) )
|
|
Destroy();
|
|
}
|
|
override bool CanPickup( Actor toucher )
|
|
{
|
|
// no pickup if wrong iwad
|
|
if ( !(gameinfo.gametype&avail) ) return false;
|
|
return Super.CanPickup(toucher);
|
|
}
|
|
override string PickupMessage()
|
|
{
|
|
if ( Stamina > 0 )
|
|
return StringTable.Localize(PickupMsg)..String.Format(" \cj(\cg¥\cf%d\cj)\c-",Stamina);
|
|
return Super.PickupMessage();
|
|
}
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
Super.AttachToOwner(other);
|
|
// we're only attaching to the other players
|
|
if ( propagated )
|
|
return;
|
|
// give credit
|
|
if ( other.player && (Stamina > 0) )
|
|
{
|
|
SWWMScoreObj.Spawn(Stamina,other.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+other.Height/2),Font.CR_GOLD);
|
|
SWWMCredits.Give(other.player,Stamina);
|
|
}
|
|
// send to all other players
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] || !players[i].mo || (i == other.PlayerNumber()) )
|
|
continue;
|
|
let c = SWWMCollectable(Spawn(GetClass(),pos));
|
|
c.propagated = true;
|
|
if ( !c.CallTryPickup(players[i].mo) )
|
|
c.Destroy();
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// April Fools 2020
|
|
Class FroggyChair : SWWMCollectable
|
|
{
|
|
int cdown;
|
|
|
|
Default
|
|
{
|
|
Tag "$T_FROGGY";
|
|
Inventory.PickupMessage "$T_FROGGY";
|
|
Stamina 1440;
|
|
}
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
Super.AttachToOwner(other);
|
|
if ( !propagated )
|
|
cdown = 150;
|
|
}
|
|
override void DoEffect()
|
|
{
|
|
Super.DoEffect();
|
|
if ( cdown <= 0 ) return;
|
|
if ( Owner && Owner.CheckLocalView() )
|
|
{
|
|
if ( cdown == 140 )
|
|
Console.MidPrint(newsmallfont,"$D_FROGGY1");
|
|
else if ( cdown == 70 )
|
|
Console.MidPrint(newsmallfont,"$D_FROGGY2");
|
|
}
|
|
cdown--;
|
|
}
|
|
}
|
|
|
|
// The other collectables, which will be implemented eventually
|
|
Class MMiaSRVol1 : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_SEXROBOT1";
|
|
Inventory.PickupMessage "$T_SEXROBOT1";
|
|
Stamina 2000;
|
|
}
|
|
}
|
|
Class MMiaSRVol2 : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_SEXROBOT2";
|
|
Inventory.PickupMessage "$T_SEXROBOT2";
|
|
Stamina 2000;
|
|
}
|
|
}
|
|
Class MMiaSRVol3 : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_SEXROBOT3";
|
|
Inventory.PickupMessage "$T_SEXROBOT3";
|
|
Stamina 2000;
|
|
}
|
|
}
|
|
Class MothPlushy : SWWMCollectable
|
|
{
|
|
int cdown;
|
|
|
|
Default
|
|
{
|
|
Tag "$T_MOTHPLUSH";
|
|
Inventory.PickupMessage "$T_MOTHPLUSH";
|
|
Stamina 5000;
|
|
}
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
Super.AttachToOwner(other);
|
|
if ( !propagated )
|
|
cdown = 175;
|
|
}
|
|
override void DoEffect()
|
|
{
|
|
Super.DoEffect();
|
|
if ( cdown <= 0 ) return;
|
|
if ( Owner && Owner.CheckLocalView() )
|
|
{
|
|
if ( cdown == 105 )
|
|
A_StartSound("mashiro/giggle",CHAN_ITEMEXTRA,CHANF_OVERLAP|CHANF_UI,1.,0.);
|
|
if ( cdown == 35 )
|
|
Console.MidPrint(newsmallfont,"$D_MASHIRO");
|
|
}
|
|
cdown--;
|
|
}
|
|
}
|
|
Class BRCDebutLP : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_BLACKRATS";
|
|
Inventory.PickupMessage "$T_BLACKRATS";
|
|
Stamina 6000;
|
|
}
|
|
}
|
|
Class AkariProject : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_AKARIPROJECT";
|
|
Inventory.PickupMessage "$T_AKARIPROJECT";
|
|
Stamina 3000;
|
|
}
|
|
}
|
|
Class CatCatcherPoster : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_CATCATCHER";
|
|
Inventory.PickupMessage "$T_CATCATCHER";
|
|
Stamina 1500;
|
|
}
|
|
}
|
|
Class LoveSignalsCD : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_LOVESIGNALS";
|
|
Inventory.PickupMessage "$T_LOVESIGNALS";
|
|
Stamina 4000;
|
|
}
|
|
}
|
|
Class NutatcoBar : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_NUTATCO";
|
|
Inventory.PickupMessage "$T_NUTATCO";
|
|
Stamina 200;
|
|
}
|
|
}
|
|
Class HegeCactus : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_HEGE";
|
|
Inventory.PickupMessage "$T_HEGE";
|
|
Stamina 8000;
|
|
}
|
|
}
|
|
Class FrispyCorn : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_FRISPYCORN";
|
|
Inventory.PickupMessage "$T_FRISPYCORN";
|
|
Stamina 500;
|
|
}
|
|
}
|
|
Class LithiumVol1 : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_LITHIUM";
|
|
Inventory.PickupMessage "$T_LITHIUM";
|
|
Stamina 3500;
|
|
}
|
|
}
|
|
// Heretic
|
|
Class DemoPlush : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_DEMOPLUSH";
|
|
Inventory.PickupMessage "$T_DEMOPLUSH";
|
|
SWWMCollectable.Availability AVAIL_Heretic;
|
|
Stamina 6400;
|
|
}
|
|
}
|
|
Class TomeOfPower : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_TOMEPOWER";
|
|
Inventory.PickupMessage "$T_TOMEPOWER";
|
|
SWWMCollectable.Availability AVAIL_Heretic;
|
|
Stamina 16000;
|
|
}
|
|
}
|
|
Class QuartzFlask : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_YEFLASK";
|
|
Inventory.PickupMessage "$T_YEFLASK";
|
|
SWWMCollectable.Availability AVAIL_Heretic;
|
|
Stamina 4000;
|
|
}
|
|
}
|
|
Class SayaBean : SWWMCollectable
|
|
{
|
|
int cdown;
|
|
|
|
Default
|
|
{
|
|
Tag "$T_SAYABEAN";
|
|
Inventory.PickupMessage "$T_SAYABEAN";
|
|
SWWMCollectable.Availability AVAIL_Heretic;
|
|
Stamina 12000;
|
|
}
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
Super.AttachToOwner(other);
|
|
if ( !propagated )
|
|
cdown = 70;
|
|
}
|
|
override void DoEffect()
|
|
{
|
|
Super.DoEffect();
|
|
if ( cdown <= 0 ) return;
|
|
if ( Owner && Owner.CheckLocalView() )
|
|
{
|
|
if ( cdown == 35 )
|
|
A_StartSound("saya/giggle",CHAN_ITEMEXTRA,CHANF_OVERLAP|CHANF_UI,1.,0.);
|
|
}
|
|
cdown--;
|
|
}
|
|
}
|
|
// Hexen
|
|
Class KirinCummies : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_PEACH";
|
|
Inventory.PickupMessage "$T_PEACH";
|
|
SWWMCollectable.Availability AVAIL_Hexen;
|
|
Stamina 400;
|
|
}
|
|
}
|
|
Class MilkBreads : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_MILKBREAD";
|
|
Inventory.PickupMessage "$T_MILKBREAD";
|
|
SWWMCollectable.Availability AVAIL_Hexen;
|
|
Stamina 900;
|
|
}
|
|
}
|
|
Class EyeObelisk : SWWMCollectable
|
|
{
|
|
int cdown;
|
|
|
|
Default
|
|
{
|
|
Tag "$T_OBELISK";
|
|
Inventory.PickupMessage "$T_OBELISK";
|
|
SWWMCollectable.Availability AVAIL_Hexen;
|
|
Stamina 24000;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
if ( InStateSequence(CurState,FindState("Spawn")) )
|
|
A_StartSound("obelisk/idle",CHAN_BODY,CHANF_LOOPING);
|
|
}
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
Super.AttachToOwner(other);
|
|
if ( propagated ) return;
|
|
// nauseatingly fast trip through images of the future
|
|
if ( other.player != players[consoleplayer] ) return;
|
|
let hnd = EyeObeliskFlash(StaticEventHandler.Find("EyeObeliskFlash"));
|
|
if ( hnd ) hnd.doit = true;
|
|
}
|
|
}
|
|
Class KirinManga : SWWMCollectable
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_KIRINMANGA";
|
|
Inventory.PickupMessage "$T_KIRINMANGA";
|
|
SWWMCollectable.Availability AVAIL_Hexen;
|
|
Stamina 1600;
|
|
}
|
|
}
|
|
|
|
Class EyeObeliskFlash : StaticEventHandler
|
|
{
|
|
bool doit;
|
|
int cd;
|
|
ui bool doit_ui;
|
|
ui int timeme;
|
|
ui TextureID ftex[29];
|
|
|
|
override void UiTick()
|
|
{
|
|
if ( !doit && !doit_ui )
|
|
{
|
|
timeme = 0;
|
|
return;
|
|
}
|
|
if ( timeme == 0 )
|
|
{
|
|
// do not trigger if the user doesn't want this
|
|
if ( CVar.GetCVar('swwm_noscare',players[consoleplayer]).GetBool() )
|
|
return;
|
|
doit_ui = true;
|
|
for ( int i=0; i<29; i++ )
|
|
ftex[i] = TexMan.CheckForTexture(String.Format("graphics/obelisk/O_SIGHT%d.png",i),TexMan.Type_Any);
|
|
S_StartSound("obelisk/sight",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE,1.,0.);
|
|
}
|
|
if ( timeme == 210 ) Console.MidPrint(newsmallfont,"$D_OBELISK");
|
|
if ( timeme == 240 ) doit_ui = false;
|
|
timeme++;
|
|
}
|
|
override void WorldTick()
|
|
{
|
|
if ( !doit ) return;
|
|
cd++;
|
|
if ( cd < 1 ) return;
|
|
cd = 0;
|
|
doit = false;
|
|
}
|
|
override void RenderOverlay( RenderEvent e )
|
|
{
|
|
if ( !doit_ui ) return;
|
|
double secs = (timeme+e.FracTic)/Thinker.TICRATE;
|
|
PlayerInfo p = players[consoleplayer];
|
|
if ( secs < .25 ) Screen.Dim("White",(secs*4.)**2.,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
else if ( secs < 2.25 )
|
|
{
|
|
Shader.SetEnabled(p,"ObPass0",true);
|
|
Shader.SetEnabled(p,"ObPass1",true);
|
|
Shader.SetEnabled(p,"ObPass2",true);
|
|
Shader.SetEnabled(p,"ObPass3",true);
|
|
Shader.SetUniform1f(p,"ObPass0","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"ObPass1","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"ObPass2","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
|
|
Shader.SetUniform1f(p,"ObPass3","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
|
|
secs -= .25;
|
|
Screen.Dim("Black",1.,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
// flash images:
|
|
// 0. mirror man (thievery at the forsaken manor redux)
|
|
// 1. viridescent lord (hexmas)
|
|
// 2. nightmare lord (spooktober)
|
|
// 3. dreadful lady (strife 25th anniversary project)
|
|
// 4. shinnovre (unsx 6)
|
|
// 5. hiikya (the hiikyan war)
|
|
// 6. rebirth of azadeku (project abyss dlc1: forgotten times)
|
|
// 7. heart of assimilation (project abyss dlc2: the void lords)
|
|
// 8. sage of blue (project abyss dlc3: assault on blue tower)
|
|
// 9. ser (project convergence)
|
|
int whichimg = clamp(int((10.*secs)/2.),0,9);
|
|
if ( !ftex[whichimg] ) return;
|
|
double ar = Screen.GetAspectRatio();
|
|
Vector2 tsize = TexMan.GetScaledSize(ftex[whichimg]);
|
|
double sar = tsize.x/tsize.y;
|
|
Vector2 vsize;
|
|
if ( sar > ar ) vsize = (tsize.x,tsize.x/ar);
|
|
else if ( sar < ar ) vsize = (tsize.y*ar,tsize.y);
|
|
else vsize = tsize;
|
|
vsize *= .75;
|
|
Screen.DrawTexture(ftex[whichimg],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true);
|
|
}
|
|
else if ( secs < 6.25 )
|
|
{
|
|
Shader.SetEnabled(p,"ObPass0",false);
|
|
Shader.SetEnabled(p,"ObPass1",false);
|
|
Shader.SetEnabled(p,"ObPass2",false);
|
|
Shader.SetEnabled(p,"ObPass3",false);
|
|
secs -= 2.25;
|
|
Screen.Dim("White",(1.-secs*.25)**1.5,0,0,Screen.GetWidth(),Screen.GetHeight());
|
|
}
|
|
}
|
|
}
|
|
|
|
// yay!
|
|
Class FancyConfetti : Actor
|
|
{
|
|
int deadtimer;
|
|
double anglevel, pitchvel, rollvel;
|
|
|
|
Default
|
|
{
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+DROPOFF;
|
|
+MOVEWITHSECTOR;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+INTERPOLATEANGLES;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+SLIDESONWALLS;
|
|
Gravity 0.05;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
deadtimer = Random[Junk](-30,30);
|
|
anglevel = FRandom[Junk](3,12)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](3,12)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](3,12)*RandomPick[Junk](-1,1);
|
|
if ( bAMBUSH ) frame = 0;
|
|
else frame = Random[Junk](0,9);
|
|
scale *= Frandom[Junk](0.8,1.2);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
vel.xy *= 0.98;
|
|
if ( vel.z > 0 ) vel.z *= 0.98;
|
|
if ( CurState == ResolveState("Death") )
|
|
{
|
|
deadtimer++;
|
|
if ( deadtimer > 300 ) A_FadeOut(0.05);
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 # 1
|
|
{
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
roll += rollvel;
|
|
return A_JumpIf(pos.z<=floorz,"Death");
|
|
}
|
|
Loop;
|
|
Death:
|
|
XZW1 # -1
|
|
{
|
|
A_Stop();
|
|
pitch = roll = 0;
|
|
}
|
|
Stop;
|
|
Dummy:
|
|
XZW1 ABCDEFGHIJ -1;
|
|
Stop;
|
|
}
|
|
}
|
|
Class PartyTime : Actor
|
|
{
|
|
bool ignite;
|
|
|
|
Default
|
|
{
|
|
Radius .1;
|
|
Height 0.;
|
|
+NOBLOCKMAP;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
if ( target ) specialf1 = target.Height/2.;
|
|
else specialf1 = 16.;
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( ignite )
|
|
{
|
|
// wait for the sound to stop
|
|
if ( !IsActorPlayingSound(CHAN_ITEM) )
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( !target || (target.tics == -1) )
|
|
{
|
|
// burst into treats!
|
|
ignite = true;
|
|
A_Confetti();
|
|
return;
|
|
}
|
|
SetOrigin(target.pos,false);
|
|
}
|
|
action void A_Confetti()
|
|
{
|
|
A_StartSound("misc/tada",CHAN_ITEM);
|
|
double ang, pt;
|
|
int numpt = Random[ExploS](100,120);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[ExploS](0,360);
|
|
pt = FRandom[ExploS](-90,30);
|
|
let c = Spawn("FancyConfetti",Vec3Offset(0,0,specialf1));
|
|
c.vel = (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[ExploS](2,8);
|
|
}
|
|
}
|
|
}
|
|
|
|
Class ChanceboxSpawner : Actor
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
int numcol = 0;
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let c = (Class<SWWMCollectable>)(AllActorClasses[i]);
|
|
if ( !c || (c == 'SWWMCollectable') ) continue;
|
|
let def = GetDefaultByType(c);
|
|
// check that we can collect it in this IWAD
|
|
if ( !(gameinfo.gametype&def.avail) ) continue;
|
|
numcol++;
|
|
}
|
|
let ti = ThinkerIterator.Create("SWWMCollectable");
|
|
while ( ti.Next() ) numcol--;
|
|
ti = ThinkerIterator.Create("Chancebox");
|
|
while ( ti.Next() ) numcol--;
|
|
if ( numcol <= 0 )
|
|
{
|
|
// can't spawn a box, everything's been collected
|
|
Destroy();
|
|
return;
|
|
}
|
|
let b = Spawn("Chancebox",pos);
|
|
// copy all our stuff
|
|
b.spawnangle = spawnangle;
|
|
b.angle = angle;
|
|
b.pitch = pitch;
|
|
b.roll = roll;
|
|
b.spawnpoint = spawnpoint;
|
|
b.special = special;
|
|
for ( int i=0; i<5; i++ )
|
|
b.Args[i] = Args[i];
|
|
b.spawnflags = spawnflags&~MTF_SECRET;
|
|
b.HandleSpawnFlags();
|
|
b.bCOUNTSECRET = spawnflags&MTF_SECRET;
|
|
b.ChangeTid(tid);
|
|
}
|
|
}
|
|
|
|
// Collectable box (recycling of discarded "chance box" item)
|
|
Class Chancebox : Actor
|
|
{
|
|
static void SpawnChanceboxes()
|
|
{
|
|
// TODO the whole damn deal of finding secret sectors and whatnot
|
|
// this will be huge, and get called from the event handler on first level load
|
|
}
|
|
|
|
action void A_DropSomething()
|
|
{
|
|
Array<Class <SWWMCollectable> > candidates;
|
|
candidates.Clear();
|
|
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
|
{
|
|
let c = (Class<SWWMCollectable>)(AllActorClasses[i]);
|
|
if ( !c || (c == 'SWWMCollectable') ) continue;
|
|
let def = GetDefaultByType(c);
|
|
// check that we can collect it in this IWAD
|
|
if ( !(gameinfo.gametype&def.avail) ) continue;
|
|
candidates.Push(c);
|
|
}
|
|
let ti = ThinkerIterator.Create("SWWMCollectable");
|
|
SWWMCollectable c;
|
|
while ( c = SWWMCollectable(ti.Next()) )
|
|
{
|
|
int f = candidates.Find(c.GetClass());
|
|
if ( f < candidates.Size() )
|
|
candidates.Delete(f);
|
|
}
|
|
if ( candidates.Size() <= 0 )
|
|
{
|
|
// no candidates? just burst into treats
|
|
for ( int i=0; i<=30; i++ )
|
|
{
|
|
let a = Spawn((i==0)?"RefresherItem":(i%3)?"HealthNuggetItem":"ArmorNuggetItem",pos);
|
|
a.bDROPPED = true;
|
|
a.bNOGRAVITY = false;
|
|
a.vel.z = FRandom[Chancebox](2,4);
|
|
if ( i > 0 ) a.vel.xy = (cos(i*12),sin(i*12))*FRandom[Chancebox](4,12);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pop one at random
|
|
let a = Spawn(candidates[Random[Chancebox](0,candidates.Size()-1)],pos);
|
|
a.bDROPPED = true;
|
|
a.bNOGRAVITY = false;
|
|
a.vel.z = FRandom[Chancebox](2,4);
|
|
}
|
|
int numpt = Random[ExploS](16,32);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8);
|
|
let s = Spawn("SWWMHalfSmoke",Vec3Offset(0,0,16));
|
|
s.vel = pvel;
|
|
s.SetShade(Color(2,1,3)*Random[ExploS](64,85));
|
|
s.special1 = Random[ExploS](1,4);
|
|
s.alpha *= .4;
|
|
s.scale *= 2.4;
|
|
}
|
|
}
|
|
action void A_Confetti()
|
|
{
|
|
A_StartSound("misc/tada",CHAN_ITEM);
|
|
double ang, pt;
|
|
int numpt = Random[ExploS](100,120);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[ExploS](0,360);
|
|
pt = FRandom[ExploS](-90,30);
|
|
let c = Spawn("FancyConfetti",Vec3Offset(0,0,16));
|
|
c.vel = (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[ExploS](2,8);
|
|
}
|
|
}
|
|
override bool Used( Actor user )
|
|
{
|
|
if ( CurState != FindState("Spawn") ) return false;
|
|
if ( bCountSecret )
|
|
{
|
|
user.GiveSecret();
|
|
bCountSecret = false;
|
|
}
|
|
if ( special )
|
|
{
|
|
user.A_CallSpecial(special,args[0],args[1],args[2],args[3],args[4]);
|
|
special = 0;
|
|
}
|
|
SWWMLoreLibrary.Add(user.player,"Chancebox");
|
|
specialf2 = AngleTo(user);
|
|
SetStateLabel("PreActive");
|
|
return true;
|
|
}
|
|
Default
|
|
{
|
|
Radius 16;
|
|
Height 32;
|
|
+MOVEWITHSECTOR;
|
|
+ROLLSPRITE;
|
|
+SOLID;
|
|
+INTERPOLATEANGLES;
|
|
Species "Chancebox";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
PreActive:
|
|
XZW1 A 1
|
|
{
|
|
double delta = deltaangle(angle,specialf2);
|
|
int sign = (delta>=0.)?1:-1;
|
|
delta = clamp(abs(delta)*.15,.1,10.)*sign;
|
|
angle += delta;
|
|
return A_JumpIf(abs(deltaangle(angle,specialf2))<1.,"Active");
|
|
}
|
|
Wait;
|
|
Active:
|
|
XZW1 A 1
|
|
{
|
|
angle = specialf2;
|
|
specialf1 = angle;
|
|
A_StartSound("misc/drumroll",CHAN_WEAPON);
|
|
}
|
|
XZW1 A 1
|
|
{
|
|
bINTERPOLATEANGLES = false;
|
|
angle = specialf1+FRandom[Chancebox](-5,5);
|
|
pitch = FRandom[Chancebox](-5,5);
|
|
roll = FRandom[Chancebox](-5,5);
|
|
special1++;
|
|
return A_JumpIf(special1>40,"BlowUp");
|
|
}
|
|
Wait;
|
|
BlowUp:
|
|
XZW2 A 1
|
|
{
|
|
A_SetSize(16,4);
|
|
A_QuakeEx(2,2,2,9,0,500,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.2);
|
|
A_StartSound("chancebox/explode",CHAN_VOICE);
|
|
angle = specialf1;
|
|
pitch = roll = 0;
|
|
let t = Spawn("ChanceboxTop",Vec3Offset(0,0,32));
|
|
t.angle = angle;
|
|
let s1 = Spawn("ChanceboxSide",level.Vec3Offset(pos,(RotateVector((16,0),angle+90),0)));
|
|
s1.angle = angle+90;
|
|
let s2 = Spawn("ChanceboxSide",level.Vec3Offset(pos,(RotateVector((16,0),angle-90),0)));
|
|
s2.angle = angle-90;
|
|
A_DropSomething();
|
|
}
|
|
XZW2 BCDEFGHIJKLMNO 1;
|
|
XZW2 P -1 A_Confetti();
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// top side of chancebox, shoots upwards
|
|
Class ChanceboxTop : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 16;
|
|
Height 4;
|
|
Species "Chancebox";
|
|
PROJECTILE;
|
|
+THRUSPECIES;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
vel = (0,0,20);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A 1
|
|
{
|
|
double magvel = vel.length();
|
|
if ( magvel > 0. )
|
|
{
|
|
magvel = min(60,magvel*1.2);
|
|
vel = vel.unit()*magvel;
|
|
}
|
|
}
|
|
Wait;
|
|
Death:
|
|
TNT1 A 1 A_SpawnItemEx("ExplodiumBulletImpact");
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// left/right side of chancebox, shoots forward
|
|
Class ChanceboxSide : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 16;
|
|
Height 32;
|
|
Species "Chancebox";
|
|
PROJECTILE;
|
|
+THRUSPECIES;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
vel = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*20;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A 1
|
|
{
|
|
double magvel = vel.length();
|
|
if ( magvel > 0. )
|
|
{
|
|
magvel = min(60,magvel*1.2);
|
|
vel = vel.unit()*magvel;
|
|
}
|
|
}
|
|
Wait;
|
|
Death:
|
|
TNT1 A 1 A_SpawnItemEx("ExplodiumBulletImpact");
|
|
Stop;
|
|
}
|
|
}
|