swwmgz_m/zscript/items/swwm_powerups_vip.zsc

1289 lines
31 KiB
Text

// VIP powerups
Class TendrilTracer : LineTracer
{
Actor ignore;
Array<Line> ShootThroughList;
Array<HitListEntry> HitList;
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignore ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
hitlist.Push(ent);
}
return TRACE_Skip;
}
else if ( Results.HitType == TRACE_HitWall )
{
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Skip;
}
}
// main heatseeker
Class MykradvoTendril : SWWMNonInteractiveActor
{
Vector3 nextpos, nextdir;
void A_Trace()
{
tics = bMISSILEMORE?2:1;
Vector3 x, y, z;
[x, y, z] = SWWMUtility.GetAxes(angle,pitch,roll);
if ( !bSTANDSTILL )
{
let t = new("TendrilTracer");
t.ignore = target;
t.hitlist.Clear();
t.ShootThroughList.Clear();
t.Trace(pos,CurSector,x,speed,0);
foreach ( l:t.ShootThroughList )
{
l.Activate(target,0,SPAC_PCross);
l.Activate(target,0,SPAC_Impact);
}
foreach ( hit:t.hitlist )
{
if ( hit.hitactor.IsFriend(target) ) continue;
if ( (hit.hitactor == tracer) && bMISSILEMORE ) bMISSILEEVENMORE = true; // we split
int dmg = (hit.hitactor.bBOSS||hit.hitactor.FindInventory("BossMarker"))?(GetMissileDamage(0,0)*4):max(hit.hitactor.Health,GetMissileDamage(0,0));
SWWMUtility.DoKnockback(hit.hitactor,-hit.x+(0,0,.5),((hit.hitactor.Health-dmg)<=0)?60000:8000);
let p = SWWMPuff.Setup(hit.hitlocation,hit.x,self,target,hit.hitactor);
hit.hitactor.DamageMobj(p,target,dmg,'Plasma',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
if ( hit.hitactor && hit.hitactor.bISMONSTER && !Random[Mykradvo](0,3) )
hit.hitactor.Howl();
}
}
nextpos = level.Vec3Offset(pos,x*speed);
if ( !bSTANDSTILL && (!tracer || !tracer.bSHOOTABLE || (tracer.Health <= 0) || ((tracer.bBOSS || tracer.FindInventory("BossMarker")) && !bMISSILEMORE)) )
{
ReactionTime--;
if ( ReactionTime <= 0 )
{
bAMBUSH = true;
return;
}
}
double a = FRandom[Mykradvo](0,360), s = FRandom[Mykradvo](0.,bSTANDSTILL?3.:bMISSILEMORE?.75:1.5);
Vector3 dir = SWWMUtility.ConeSpread(x,y,z,a,s);
if ( tracer )
{
Vector3 destofs = bMISSILEMORE?tracer.Vec3Offset(0,0,tracer.Height/2.):tracer.Vec3Offset(FRandom[Mykradvo](-1.2,1.2)*tracer.Radius,FRandom[Mykradvo](-1.2,1.2)*tracer.Radius,FRandom[Mykradvo](-.1,1.1)*tracer.height);
Vector3 dirto = level.Vec3Diff(nextpos,destofs);
double dist = dirto.length();
if ( dist > 1 )
{
dirto /= dist;
dir = (dir+dirto*(clamp(1.-(dist/4000.),.25,1.)**1.5)).unit();
}
// early split
if ( dist < speed ) bMISSILEEVENMORE = true;
}
nextdir = dir;
}
void A_Spread()
{
if ( bMISSILEMORE && bMISSILEEVENMORE )
{
// spread into sub-tendrils
Vector3 x, y, z;
[x, y, z] = SWWMUtility.GetAxes(angle,pitch,roll);
int ntendies = tracer?clamp(tracer.GetSpawnHealth()/400,2,10):2;
for ( int i=0; i<ntendies; i++ )
{
let r = Spawn("MykradvoSmallTendril",nextpos);
double a = FRandom[Mykradvo](0,360), s = FRandom[Mykradvo](0.,1.);
Vector3 sdir = SWWMUtility.ConeSpread(x,y,z,a,s);
r.angle = atan2(sdir.y,sdir.x);
r.pitch = asin(-sdir.z);
r.target = target;
r.tracer = tracer;
if ( tracer && (tracer.bBOSS || tracer.FindInventory("BossMarker")) ) r.ReactionTime -= Random[ExploS](5,15);
else r.ReactionTime += Random[ExploS](0,20);
}
return;
}
if ( bAMBUSH || (bSTANDSTILL && (special1 > ReactionTime)) )
{
if ( !bSTANDSTILL )
{
int numpt = bMISSILEMORE?9:3;
Vector3 x, y, z;
[x, y, z] = SWWMUtility.GetAxes(angle,pitch,roll);
for ( int i=0; i<numpt; i++ )
{
double a = FRandom[ExploS](0,360), s = FRandom[ExploS](0,1.);
Vector3 sdir = SWWMUtility.ConeSpread(x,y,z,a,s);
let r = Spawn("MykradvoSmallNullTendril",nextpos);
r.angle = atan2(sdir.y,sdir.x);
r.pitch = asin(-sdir.z);
r.target = target;
if ( bMISSILEMORE ) r.ReactionTime += Random[ExploS](0,4);
else r.ReactionTime -= Random[ExploS](0,4);
}
}
return;
}
let b = Spawn(GetClass(),nextpos);
b.angle = atan2(nextdir.y,nextdir.x);
b.pitch = asin(-nextdir.z);
b.target = target;
b.tracer = tracer;
b.special1 = special1+1;
b.special2 = special2;
b.ReactionTime = ReactionTime;
if ( !bSTANDSTILL && !((special1+special2)%4) && !Random[Mykradvo](0,4) )
A_StartSound(bMISSILEMORE?"mykradvo/arc":"mykradvo/smallarc",CHAN_WEAPON,attenuation:(bMISSILEMORE?.5:1.5),pitch:FRandom[Mykradvo](.75,1.25));
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !special1 ) special2 = Random[Mykradvo](0,8);
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_FadeOut(bMISSILEMORE?.05:bSTANDSTILL?.2:.1);
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
Default
{
Obituary "$O_MYKRADVO";
RenderStyle "Add";
DamageFunction 100;
ReactionTime 8;
Speed 64;
+INTERPOLATEANGLES;
+FOILINVUL;
+MISSILEMORE;
}
States
{
Spawn:
TNT1 A 0 Bright;
XZW1 A 1 Bright A_Trace();
XZW1 A 1 Bright
{
A_Spread();
return FindState("Fade")+Random[Mykradvo](0,11)*2;
}
Stop;
Fade:
#### # 20 Bright;
XZW1 B -1 Bright;
Stop;
#### # 20 Bright;
XZW1 C -1 Bright;
Stop;
#### # 20 Bright;
XZW1 D -1 Bright;
Stop;
#### # 20 Bright;
XZW1 E -1 Bright;
Stop;
#### # 20 Bright;
XZW1 F -1 Bright;
Stop;
#### # 20 Bright;
XZW1 G -1 Bright;
Stop;
#### # 20 Bright;
XZW1 H -1 Bright;
Stop;
#### # 20 Bright;
XZW1 I -1 Bright;
Stop;
#### # 20 Bright;
XZW1 J -1 Bright;
Stop;
#### # 20 Bright;
XZW1 K -1 Bright;
Stop;
#### # 20 Bright;
XZW1 L -1 Bright;
Stop;
#### # 20 Bright;
XZW1 M -1 Bright;
Stop;
}
}
// sub seekers
Class MykradvoSmallTendril : MykradvoTendril
{
Default
{
Speed 16;
DamageFunction 10;
ReactionTime 20;
-MISSILEMORE;
}
States
{
Fade:
#### # 10 Bright;
XZW1 B -1 Bright;
Stop;
#### # 10 Bright;
XZW1 C -1 Bright;
Stop;
#### # 10 Bright;
XZW1 D -1 Bright;
Stop;
#### # 10 Bright;
XZW1 E -1 Bright;
Stop;
#### # 10 Bright;
XZW1 F -1 Bright;
Stop;
#### # 10 Bright;
XZW1 G -1 Bright;
Stop;
#### # 10 Bright;
XZW1 H -1 Bright;
Stop;
#### # 10 Bright;
XZW1 I -1 Bright;
Stop;
#### # 10 Bright;
XZW1 J -1 Bright;
Stop;
#### # 10 Bright;
XZW1 K -1 Bright;
Stop;
#### # 10 Bright;
XZW1 L -1 Bright;
Stop;
#### # 10 Bright;
XZW1 M -1 Bright;
Stop;
}
}
// non-hurting non-seekers
Class MykradvoSmallNullTendril : MykradvoSmallTendril
{
Default
{
Speed 8;
ReactionTime 6;
+STANDSTILL;
}
States
{
Fade:
#### # 5 Bright;
XZW1 B -1 Bright;
Stop;
#### # 5 Bright;
XZW1 C -1 Bright;
Stop;
#### # 5 Bright;
XZW1 D -1 Bright;
Stop;
#### # 5 Bright;
XZW1 E -1 Bright;
Stop;
#### # 5 Bright;
XZW1 F -1 Bright;
Stop;
#### # 5 Bright;
XZW1 G -1 Bright;
Stop;
#### # 5 Bright;
XZW1 H -1 Bright;
Stop;
#### # 5 Bright;
XZW1 I -1 Bright;
Stop;
#### # 5 Bright;
XZW1 J -1 Bright;
Stop;
#### # 5 Bright;
XZW1 K -1 Bright;
Stop;
#### # 5 Bright;
XZW1 L -1 Bright;
Stop;
#### # 5 Bright;
XZW1 M -1 Bright;
Stop;
}
}
Class MykradvoBurstLight : PaletteLight
{
Default
{
Tag "Purple";
ReactionTime 60;
Args 0,0,0,400;
}
}
// 'splode
Class MykradvoBurst : SWWMNonInteractiveActor
{
Array<Actor> targets;
int nstep;
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
Scale 1.4;
}
void FlashPlayer( int str, double rad )
{
if ( !SWWMUtility.InPlayerFOV(players[consoleplayer],self,rad) ) return;
let mo = players[consoleplayer].Camera;
double dist = Distance3D(mo);
str = int(str*(1.-(dist/rad)));
SWWMHandler.DoFlash(mo,Color(str,250,240,255),5);
SWWMHandler.DoFlash(mo,Color(str,128,0,255),15);
}
override void PostBeginPlay()
{
nstep = clamp(targets.Size()/10,1,5);
A_AlertMonsters(swwm_uncapalert?0:8000);
A_QuakeEx(9,9,9,80,0,3000,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:1000,rollintensity:2.);
A_StartSound("powerup/mykradvo",CHAN_BODY,CHANF_DEFAULT,1.,.25);
A_StartSound("powerup/mykradvo",CHAN_VOICE,CHANF_DEFAULT,1.,.25);
FlashPlayer(100,1500);
int numpt = Random[ExploS](20,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](.25,8);
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](128,160)+Color(28,0,31));
s.A_SetRenderStyle(.4,STYLE_AddShaded);
s.special1 = Random[ExploS](1,5);
s.scale *= 2.;
}
numpt = Random[ExploS](8,12);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("MykradvoSmallNullTendril",pos);
s.angle = ang;
s.pitch = pt;
s.target = target;
s.ReactionTime += Random[ExploS](0,8);
}
Spawn("MykradvoBurstLight",pos);
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( (special1++)%3 )
{
if ( targets.Size() > 0 )
{
int numpt = Random[ExploS](2,4);
for ( int j=0; j<numpt; j++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](.25,8);
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](128,160)+Color(28,0,31));
s.A_SetRenderStyle(.4,STYLE_AddShaded);
s.special1 = Random[ExploS](1,5);
s.scale *= 2.;
}
numpt = Random[ExploS](1,2);
for ( int j=0; j<numpt; j++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("MykradvoSmallNullTendril",pos);
s.angle = ang;
s.pitch = pt;
s.target = target;
s.ReactionTime += Random[ExploS](0,8);
}
}
for ( int i=0; i<nstep; i++ )
{
if ( targets.Size() <= 0 ) break;
let targ = targets[0];
if ( targ && targ.bSHOOTABLE && (targ.Health > 0) )
{
let t = Spawn("MykradvoTendril",pos);
t.angle = t.AngleTo(targ);
t.pitch = t.PitchTo(targ,0,targ.Height/2);
t.target = target;
t.tracer = targ;
}
else i--;
targets.Delete(0);
}
}
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XEX4 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 2 Bright;
TNT1 A 1 A_JumpIf(!IsActorPlayingSound(CHAN_VOICE,-1)&&!(targets.Size()),1);
Wait;
TNT1 A 1;
Stop;
}
}
Class Mykradvo : Inventory
{
Mixin SWWMAutoUseFix;
Mixin SWWMOverlapPickupSound;
Mixin SWWMUseToPickup;
Mixin SWWMRespawn;
Mixin SWWMPickupGlow;
Actor ringa[2];
Array<Actor> targets;
// quicksort (targets)
private bool CmpDist( Actor ref, Vector3 a, Vector3 b )
{
double dista = level.Vec3Diff(ref.pos,a).length();
double distb = level.Vec3Diff(ref.pos,b).length();
return (dista < distb);
}
private int partition_targets( Array<Actor> a, int l, int h, Actor ref )
{
Actor pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpDist(ref,a[j].pos,pv.pos) )
{
i++;
Actor tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Actor tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_targets( Array<Actor> a, int l, int h, Actor ref )
{
if ( l >= h ) return;
int p = partition_targets(a,l,h,ref);
qsort_targets(a,l,p-1,ref);
qsort_targets(a,p+1,h,ref);
}
bool FindTargets( Actor t )
{
targets.Clear();
// search all actively hostile enemies within 50m
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a=Actor(ti.Next()) )
{
// must be an active, shootable live monster
if ( !a.bISMONSTER || !a.bSHOOTABLE || a.bDORMANT || (a.Health <= 0) ) continue;
// skip non-hostiles
if ( a.IsFriend(t) ) continue;
// is targetting us and is within 10m
// or
// is visible and is within 100m
if ( ((a.target == t) && SWWMUtility.SphereIntersect(a,t.pos,320))
|| (t.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) && SWWMUtility.SphereIntersect(a,t.pos,3200)) )
targets.Push(a);
}
// sorted, so the closest take priority
qsort_targets(targets,0,targets.Size()-1,t);
return (targets.Size() > 0);
}
override bool Use( bool pickup )
{
if ( pickup && !deathmatch ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_OVERLAP);
Vector3 spawnpos = Owner.Vec3Angle(15,Owner.angle,Owner.Height*.7);
if ( !FindTargets(Owner) )
{
int numpt = Random[ExploS](8,12);
let f = Spawn("SWWMPurplePickupFlash",spawnpos-(0,0,16));
f.Scale *= .5;
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("MykradvoSmallNullTendril",spawnpos+SWWMUtility.Vec3Fromangles(ang,pt)*FRandom[Mykradvo](2,4));
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[ExploS](0,8);
}
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= 0.98;
Owner.A_QuakeEx(1,1,1,4,0,8,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D);
// don't consume on failure
Amount++;
return true;
}
if ( (targets.Size() == 1) && targets[0] && !targets[0].bBOSS && !targets[0].FindInventory("BossMarker") )
SWWMUtility.MarkAchievement("anone",Owner.player);
let p = Spawn("MykradvoBurst",spawnpos);
p.target = Owner;
MykradvoBurst(p).targets.Move(targets);
targets.Clear();
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= 1.2;
SWWMUtility.AchievementProgressInc("anom",1,Owner.player);
return true;
}
void A_Zap()
{
int numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("MykradvoSmallNullTendril",pos+(0,0,16+GetBobOffset())+SWWMUtility.Vec3Fromangles(ang,pt)*FRandom[Mykradvo](4,8));
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[ExploS](-2,2);
}
A_StartSound("mykradvo/smallarc",CHAN_WEAPON,CHANF_OVERLAP,attenuation:3.);
A_SetTics(Random[Mykradvo](10,50));
}
override bool CanPickup( Actor toucher )
{
// in DM, we can't be picked up unless we can be used
if ( deathmatch && !FindTargets(toucher) )
return false;
return Super.CanPickup(toucher);
}
override void PreTravelled()
{
if ( tracer ) tracer.Destroy();
for ( int i=0; i<2; i++ )
{
if ( !ringa[i] ) continue;
ringa[i].Destroy();
}
}
override void Travelled()
{
Super.Travelled();
if ( !tracer )
{
tracer = Spawn("MykradvoX",pos);
tracer.angle = angle;
tracer.target = self;
tracer.FloatBobPhase = FloatBobPhase;
}
for ( int i=0; i<2; i++ )
{
if ( ringa[i] ) continue;
ringa[i] = Spawn("MykradvoX2",pos);
ringa[i].angle = angle;
ringa[i].target = self;
ringa[i].FloatBobPhase = FloatBobPhase;
ringa[i].special1 = -1+2*i;
ringa[i].special2 = -2+4*i;
ringa[i].frame = i;
}
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
tracer = Spawn("MykradvoX",pos);
tracer.angle = angle;
tracer.target = self;
tracer.FloatBobPhase = FloatBobPhase;
for ( int i=0; i<2; i++ )
{
ringa[i] = Spawn("MykradvoX2",pos);
ringa[i].angle = angle;
ringa[i].target = self;
ringa[i].FloatBobPhase = FloatBobPhase;
ringa[i].special1 = -1+2*i;
ringa[i].special2 = -2+4*i;
ringa[i].frame = i;
}
}
Default
{
Tag "$T_MYKRADVO";
Stamina -1200000;
Inventory.Icon "graphics/HUD/Icons/I_Mykradvo.png";
Inventory.PickupSound "misc/p_pkup_vip";
Inventory.UseSound "mykradvo/arc";
Inventory.PickupMessage "$T_MYKRADVO";
Inventory.MaxAmount 3;
Inventory.InterHubAmount 3;
Inventory.PickupFlash "SWWMPurplePickupFlash";
+INVENTORY.ALWAYSPICKUP;
+INVENTORY.AUTOACTIVATE;
+INVENTORY.INVBAR;
+COUNTITEM;
+INVENTORY.BIGPOWERUP;
+FLOATBOB;
+DONTGIB;
FloatBobStrength 0.25;
}
States
{
Spawn:
XZW0 A 10 A_Zap();
Wait;
}
}
Class MykradvoX : SWWMItemOverlay
{
Default
{
Scale .16;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("powerup/mykradvoamb",CHAN_VOICE,CHANF_LOOP,attenuation:2.);
WorldOffset = (0,0,16);
}
override void Tick()
{
if ( !target )
{
Destroy();
return;
}
prev = target.prev;
if ( (target.pos != pos) || (target.vel != (0,0,0)) ) SetOrigin(target.pos+vel,true);
if ( angle != target.angle ) A_SetAngle(target.angle,SPF_INTERPOLATE);
FloatBobPhase = target.FloatBobPhase;
A_SetScale(.16+.01*sin(GetAge()*4));
bool bOldInvis = bInvisible;
bInvisible = target.bInvisible||Inventory(target).Owner;
if ( bInvisible != bOldInvis )
{
SetState(SpawnState+bInvisible);
A_SoundVolume(CHAN_VOICE,bInvisible?0.:1.);
}
}
States
{
Spawn:
MKRV A -1 Bright;
TNT1 A -1;
Stop;
}
}
Class MykradvoX2 : SWWMItemOverlay
{
Default
{
+ROLLSPRITE;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
WorldOffset = (0,0,16+special2);
}
override void Tick()
{
if ( !target )
{
Destroy();
return;
}
prev = target.prev;
if ( (target.pos != pos) || (target.vel != (0,0,0)) ) SetOrigin(target.pos+vel,true);
if ( angle != target.angle ) A_SetAngle(target.angle,SPF_INTERPOLATE);
FloatBobPhase = target.FloatBobPhase;
A_SetPitch(sin(GetAge()*special1*8)*5,SPF_INTERPOLATE);
A_SetRoll(cos(GetAge()*special1*8)*5,SPF_INTERPOLATE);
A_SetScale(1.+.05*cos(GetAge()*4)*special1);
bool bOldInvis = bInvisible;
bInvisible = target.bInvisible||Inventory(target).Owner;
if ( bInvisible != bOldInvis )
SetState(SpawnState+bInvisible);
}
States
{
Spawn:
XZW1 # -1 Bright;
TNT1 # -1;
Stop;
}
}
// additions from 1.1
Class AngeryLight : PointLightAttenuated
{
Default
{
Args 224,0,255,80;
}
override void Tick()
{
Super.Tick();
if ( !target || !master )
{
Destroy();
return;
}
if ( target.player ) SetOrigin(SWWMUtility.GetFireOffset(target,10,0,0),true);
else SetOrigin(target.Vec3Angle(10,target.angle,target.missileheight),true);
args[LIGHT_INTENSITY] = Random[Invinciball](10,12)*8;
bDORMANT = Powerup(master).isBlinking();
}
}
Class AngerySnd : SWWMNonInteractiveActor
{
override void Tick()
{
if ( !target || !master )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
if ( players[consoleplayer].Camera == target )
{
A_SoundVolume(CHAN_VOICE,0.);
A_SoundVolume(CHAN_7,.5);
}
else
{
A_SoundVolume(CHAN_VOICE,.4);
A_SoundVolume(CHAN_7,0.);
}
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("powerup/devastationact",CHAN_VOICE,CHANF_LOOP,.4,1.5);
A_StartSound("powerup/devastationact",CHAN_7,CHANF_LOOP,.5,ATTN_NONE);
}
override void OnDestroy()
{
Super.OnDestroy();
A_StopSound(CHAN_VOICE);
A_StopSound(CHAN_7);
}
}
Class AngeryPower : Powerup
{
Mixin SWWMShadedPowerup;
Actor l, snd;
int lasteffect;
transient int lastpulse;
Default
{
Powerup.Duration -50;
Inventory.Icon "graphics/HUD/Icons/I_Devastation.png";
Powerup.Color "C0 00 FF", 0.2;
+INVENTORY.ADDITIVETIME;
}
override void InitEffect()
{
Super.InitEffect();
if ( !Owner ) return;
Owner.A_AlertMonsters(swwm_uncapalert?0:5000);
SWWMHandler.DoFlash(Owner,Color(64,224,0,255),30);
Owner.A_QuakeEx(8,8,8,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.);
lasteffect = int.min;
lastpulse = max(lastpulse,gametic+35);
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= .95;
l = Spawn("AngeryLight",Owner.pos);
l.target = Owner;
l.master = self;
}
override void DoEffect()
{
Super.DoEffect();
if ( !Owner ) return;
if ( !snd ) snd = Spawn("AngerySnd",Owner.pos);
snd.target = Owner;
snd.master = self;
}
override void EndEffect()
{
Super.EndEffect();
if ( !Owner ) return;
Owner.A_StartSound("powerup/devastationend",CHAN_ITEMEXTRA,CHANF_OVERLAP);
SWWMHandler.DoFlash(Owner,Color(128,224,0,255),30);
Owner.A_QuakeEx(4,4,4,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.);
Owner.A_AlertMonsters(swwm_uncapalert?0:2000);
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= .9;
if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_DEVASTATION"));
}
void DoHitFX()
{
if ( level.maptime <= lasteffect+5 ) return;
Owner.A_AlertMonsters(swwm_uncapalert?0:5000);
SWWMHandler.DoFlash(Owner,Color(64,224,0,255),10);
Owner.A_QuakeEx(8,8,8,Random[Rage](3,8),0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.);
Owner.A_StartSound("powerup/devastationhit",CHAN_POWERUP,CHANF_OVERLAP);
lasteffect = level.maptime;
lastpulse = max(lastpulse,gametic+35);
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= .9;
}
override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags )
{
if ( passive || (damage <= 0) ) return;
if ( damageType == 'Mortal' ) // can only be in 4s
{
// max cap is the closest combination of 4s smaller than (2^31-1)
if ( damage > 44444444 ) newdamage = 444444444;
else newdamage = damage*10+4;
}
else
{
// (2^31-1)/25 : guarantee that it caps rather than overflowing
if ( damage > 85899345 ) newdamage = int.max;
else newdamage = damage*25;
}
// don't play hit fx for wall busting, as it'll be done manually if the bust goes through
if ( damageType != 'Wallbust' ) DoHitFX();
}
}
Class AngerySigil : Inventory
{
Mixin SWWMAutoUseFix;
Mixin SWWMOverlapPickupSound;
Mixin SWWMUseToPickup;
Mixin SWWMRespawn;
Mixin SWWMPickupGlow;
override bool Use( bool pickup )
{
if ( pickup && !deathmatch ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_OVERLAP);
SWWMUtility.AchievementProgressInc("deva",1,Owner.player);
let r = AngeryPower(Owner.FindInventory("AngeryPower"));
if ( r )
{
r.EffectTics += r.default.EffectTics;
SWWMHandler.DoFlash(Owner,Color(64,224,0,255),30);
Owner.A_QuakeEx(8,8,8,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.);
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= .95;
}
else Owner.GiveInventory("AngeryPower",1);
return true;
}
override void PreTravelled()
{
if ( tracer ) tracer.Destroy();
}
override void Travelled()
{
Super.Travelled();
if ( tracer ) return;
tracer = Spawn("AngerySigilX",pos);
tracer.angle = angle;
tracer.target = self;
tracer.FloatBobPhase = FloatBobPhase;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
tracer = Spawn("AngerySigilX",pos);
tracer.angle = angle;
tracer.target = self;
tracer.FloatBobPhase = FloatBobPhase;
}
Default
{
Tag "$T_DEVASTATION";
Stamina -1500000;
Inventory.Icon "graphics/HUD/Icons/I_Devastation.png";
Inventory.PickupSound "misc/p_pkup_vip";
Inventory.UseSound "powerup/devastation";
Inventory.PickupMessage "$T_DEVASTATION";
Inventory.MaxAmount 3;
Inventory.InterHubAmount 3;
Inventory.PickupFlash "SWWMPurplePickupFlash";
+INVENTORY.ALWAYSPICKUP;
+INVENTORY.AUTOACTIVATE;
+INVENTORY.INVBAR;
+COUNTITEM;
+INVENTORY.BIGPOWERUP;
+FLOATBOB;
+DONTGIB;
FloatBobStrength 0.25;
}
States
{
Spawn:
XZW1 A -1;
Stop;
}
}
Class AngerySigilX : SWWMItemOverlay
{
Default
{
Scale .5;
Alpha .35;
RenderStyle "Subtract";
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("powerup/devastationamb",CHAN_VOICE,CHANF_LOOP,attenuation:2.);
WorldOffset = (0,0,20);
}
override void Tick()
{
if ( !target )
{
Destroy();
return;
}
prev = target.prev;
if ( (target.pos != pos) || (target.vel != (0,0,0)) ) SetOrigin(target.pos+vel,true);
if ( angle != target.angle ) A_SetAngle(target.angle,SPF_INTERPOLATE);
FloatBobPhase = target.FloatBobPhase;
bool bOldInvis = bInvisible;
bInvisible = target.bInvisible||Inventory(target).Owner;
if ( bInvisible != bOldInvis )
{
SetState(SpawnState+bInvisible);
A_SoundVolume(CHAN_VOICE,bInvisible?0.:1.);
}
}
States
{
Spawn:
BLPS C -1 Bright;
TNT1 A -1;
Stop;
}
}
Class DivineSpriteLight : PointLightAttenuated
{
Default
{
Args 255,255,255,100;
}
override void Tick()
{
Super.Tick();
if ( !target || !master )
{
Destroy();
return;
}
if ( target.player ) SetOrigin(SWWMUtility.GetFireOffset(target,10,0,0),true);
else SetOrigin(target.Vec3Angle(10,target.angle,target.missileheight),true);
double vol = clamp((target.Health-1000)/6000.,0.,1.);
int lv = clamp(int(vol*255),0,255);
args[LIGHT_RED] = lv;
args[LIGHT_GREEN] = lv;
args[LIGHT_BLUE] = lv;
args[LIGHT_INTENSITY] = Random[Invinciball](10,12)*10;
}
}
Class DivineSpriteSnd : SWWMNonInteractiveActor
{
override void Tick()
{
if ( !target || !master )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
double vol = clamp((DivineSpriteEffect(master).AlphInter.GetValue()-1000.)/9000.,0.,1.);
if ( players[consoleplayer].Camera == target )
{
A_SoundVolume(CHAN_VOICE,0.);
A_SoundVolume(CHAN_7,.8*vol);
}
else
{
A_SoundVolume(CHAN_VOICE,.4*vol);
A_SoundVolume(CHAN_7,0.);
}
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("powerup/divineact",CHAN_VOICE,CHANF_LOOP,.4,1.5);
A_StartSound("powerup/divineact",CHAN_7,CHANF_LOOP,.8,ATTN_NONE);
}
override void OnDestroy()
{
Super.OnDestroy();
A_StopSound(CHAN_VOICE);
A_StopSound(CHAN_7);
}
}
Class DivineSpriteEffect : Inventory
{
int healcnt;
int healtim;
bool bHealDone;
Actor l, snd;
DynamicValueInterpolator AlphInter;
FSpawnParticleParams flare;
Property HealTimer : healtim;
default
{
Inventory.Icon "graphics/HUD/Icons/I_Divine.png";
DivineSpriteEffect.HealTimer 1750;
+INVENTORY.UNDROPPABLE;
+INVENTORY.UNTOSSABLE;
}
clearscope bool isBlinking() const
{
return ( (healtim <= BLINKTHRESHOLD) && (healtim&8) );
}
override Color GetBlend()
{
if ( swwm_shaders ) return 0;
if ( !AlphInter ) AlphInter = DynamicValueInterpolator.Create(Owner.Health,.1,1,100);
double alph = clamp((AlphInter.GetValue()-1000.)/6000.,0.,1.);
return Color(int(64*alph),255,255,255);
}
override void Travelled()
{
Super.Travelled();
bHealDone = true;
}
override void DoEffect()
{
Super.DoEffect();
if ( !l ) l = Spawn("DivineSpriteLight",Owner.pos);
l.target = Owner;
l.master = self;
if ( !snd ) snd = Spawn("DivineSpriteSnd",Owner.pos);
snd.target = Owner;
snd.master = self;
int numpt = Random[ExploS](5,10);
if ( !AlphInter ) AlphInter = DynamicValueInterpolator.Create(Owner.Health,.1,1,100);
AlphInter.Update(Owner.Health);
double alph = clamp((AlphInter.GetValue()-1000.)/6000.,0.,1.);
double scl = clamp((AlphInter.GetValue()-1000.)/6000.,2.,4.);
if ( !flare.texture )
{
flare.color1 = "White";
flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png");
flare.style = STYLE_AddShaded;
flare.flags = SPF_FULLBRIGHT;
flare.lifetime = 30;
flare.fadestep = -1;
flare.accel = (0,0,.05);
}
flare.size = scl;
flare.sizestep = -scl/30.;
flare.startalpha = alph;
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dst = FRandom[ExploS](.25,.75)*Owner.height;
Vector3 dir = SWWMUtility.Vec3Fromangles(ang,pt);
flare.pos = Owner.Vec3Offset(0,0,Owner.height/2)+dir*dst;
flare.vel = dir*.2;
level.SpawnParticle(flare);
}
if ( bHealDone || (Owner.Health <= 0) )
{
if ( AlphInter.GetValue() <= 1000 ) DepleteOrDestroy();
return;
}
healcnt++;
healtim--;
if ( (healtim <= 0) || (Owner.Health <= 0) )
{
Owner.A_StartSound("powerup/divineend",CHAN_ITEMEXTRA,CHANF_OVERLAP);
SWWMHandler.DoFlash(Owner,Color(80,255,255,255),40);
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= 0.95;
bHealDone = true;
if ( (healtim <= 0) && Owner && Owner.CheckLocalView() )
Console.Printf(StringTable.Localize("$D_SPRITE"));
}
else if ( Owner.Health < 1000 )
{
healcnt = 0;
Owner.GiveBody(1000,1000);
SWWMHandler.DoFlash(Owner,Color(40,255,255,255),20);
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= 0.97;
Owner.A_StartSound("powerup/divinehit",CHAN_ITEMEXTRA,CHANF_OVERLAP);
}
else if ( !(healcnt%5) )
{
Owner.GiveBody(500,10000);
Owner.A_StartSound("powerup/divinehit",CHAN_ITEMEXTRA,CHANF_OVERLAP);
}
}
}
Class DivineSprite : Inventory
{
Mixin SWWMAutoUseFix;
Mixin SWWMOverlapPickupSound;
Mixin SWWMUseToPickup;
Mixin SWWMRespawn;
Mixin SWWMPickupGlow;
override bool Use( bool pickup )
{
// never get auto-used on pickup unless we're in deathmatch
if ( pickup && !deathmatch ) return false;
let p = Owner.FindInventory("DivineSpriteEffect");
if ( p ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_OVERLAP);
Owner.GiveInventory("DivineSpriteEffect",1);
SWWMUtility.AchievementProgressInc("divine",1,Owner.player);
return true;
}
override void PreTravelled()
{
if ( tracer ) tracer.Destroy();
}
override void Travelled()
{
if ( tracer ) return;
tracer = Spawn("DivineSpriteX",pos);
tracer.angle = angle;
tracer.target = self;
tracer.FloatBobPhase = FloatBobPhase;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
tracer = Spawn("DivineSpriteX",pos);
tracer.angle = angle;
tracer.target = self;
tracer.FloatBobPhase = FloatBobPhase;
}
Default
{
Tag "$T_DIVINE";
Stamina -3600000;
Inventory.Icon "graphics/HUD/Icons/I_Divine.png";
Inventory.PickupSound "misc/p_pkup_vip";
Inventory.UseSound "powerup/divineuse";
Inventory.PickupMessage "$T_DIVINE";
Inventory.MaxAmount 3;
Inventory.InterHubAmount 3;
Inventory.PickupFlash "SWWMPurplePickupFlash";
+INVENTORY.ALWAYSPICKUP;
+INVENTORY.AUTOACTIVATE;
+INVENTORY.INVBAR;
+COUNTITEM;
+INVENTORY.BIGPOWERUP;
+FLOATBOB;
+DONTGIB;
FloatBobStrength 0.25;
}
States
{
Spawn:
XZW1 A -1;
Stop;
}
}
Class DivineSpriteX : SWWMItemOverlay
{
Default
{
Scale .5;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("powerup/divineamb",CHAN_VOICE,CHANF_LOOP,attenuation:2.);
WorldOffset = (0,0,16);
}
override void Tick()
{
if ( !target )
{
Destroy();
return;
}
prev = target.prev;
if ( (target.pos != pos) || (target.vel != (0,0,0)) ) SetOrigin(target.pos+vel,true);
if ( angle != target.angle ) A_SetAngle(target.angle,SPF_INTERPOLATE);
FloatBobPhase = target.FloatBobPhase;
bool bOldInvis = bInvisible;
bInvisible = target.bInvisible||Inventory(target).Owner;
if ( bOldInvis != bInvisible )
{
SetState(SpawnState+bInvisible);
A_SoundVolume(CHAN_VOICE,bInvisible?0.:1.);
}
}
States
{
Spawn:
DVSP A -1 Bright;
TNT1 A -1;
Stop;
}
}