swwmgz_m/zscript/weapons/swwm_deathlydeathcannon_altfx.zsc

1525 lines
38 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Ynykron projectiles and effects (alt-fire)
Class YnykronAltTracer : LineTracer
{
Actor ignore;
Array<Line> ShootThroughList;
Array<WaterHit> WaterHitList;
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignore ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE || (Results.HitActor is 'YnykronSingularityHitbox') ) return TRACE_Stop;
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BlockHitscan) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class YnykronHaloTail : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
Scale 1.5;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x+.5);
A_FadeOut(.2);
}
States
{
Spawn:
MHAL A -1 Bright;
Stop;
}
}
Class YnykronHalo : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
Scale 1.5;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( !target || target.InStateSequence(target.CurState,target.FindState("Death")) )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
A_SetScale(1.5*target.scale.x);
let h = Spawn("YnykronHaloTail",pos);
h.scale = scale;
}
States
{
Spawn:
MHAL A -1 Bright;
Stop;
}
}
Class GatherDust : SWWMNonInteractiveActor
{
Mixin SWWMMinimalMovingTick;
override void PostBeginPlay()
{
SetState(FindState("Spawn")+Random[ExploS](0,19));
Scale *= FRandom[ExploS](.75,1.5);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
SetShade(Color(4,3,5)*Random[ExploS](5,20));
}
void A_Gravitate()
{
if ( target && !target.InStateSequence(target.CurState,target.FindState("Death")) )
{
if ( alpha < .08 ) A_FadeIn(FRandom[ExploS](.0002,.004));
Vector3 dirto = level.Vec3Diff(pos,target.pos);
double distto = dirto.length();
if ( distto < (32.*target.scale.x) )
{
// sucked in
target.specialf2 += FRandom[ExploS](.5,2.);
Destroy();
return;
}
dirto /= distto;
double mxdist = 5000.*target.scale.x;
vel = dirto*25.*(clamp((mxdist-distto)/mxdist,.5,1.)**4.);
vel += target.vel;
return;
}
vel *= .98;
A_FadeOut(.002);
}
Default
{
RenderStyle "Shaded";
Alpha 0.;
Scale 3.;
+FORCEXYBILLBOARD;
}
States
{
Spawn:
BSMK ABCDEFGHIJKLMNOPQRST 1 A_Gravitate();
Wait;
}
}
Class YnykronVoidBeamTail : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_FadeOut();
}
States
{
Spawn:
XZW1 # -1 Bright;
Stop;
}
}
Class YnykronVoidBeam : SWWMNonInteractiveActor
{
Actor basebeam, prevbeam, nextbeam;
YnykronTracer t;
double baseang, basept;
int angledir, pitchdir;
int maxlen, curlen, segnum;
int spreadtimer;
int maxlife, lifetimer;
Default
{
DamageType 'YnykronAlt';
Obituary "$O_YNYKRONALT";
RenderStyle "Add";
Alpha 0.;
Stamina 3;
+INTERPOLATEANGLES;
+NODAMAGETHRUST;
+FOILINVUL;
}
override void PostBeginPlay()
{
angle = baseang = FRandom[Ynykron](0,360);
pitch = basept = FRandom[Ynykron](-90,90);
angledir = RandomPick[Ynykron](-1,1);
pitchdir = RandomPick[Ynykron](-1,1);
maxlen = Random[Ynykron](40,60);
maxlife = Random[Ynykron](20,30);
Scale *= FRandom[Ynykron](.75,1.5);
lifetimer = -1;
}
void A_UpdateBeam()
{
if ( !master )
{
Destroy();
return;
}
if ( basebeam == self )
{
baseang += FRandom[Ynykron](0.,5.)*angledir;
basept += FRandom[Ynykron](0.,5.)*pitchdir;
Vector3 ofs = master.scale.x*32.*SWWMUtility.Vec3FromAngles(baseang,basept);
SetOrigin(level.Vec3Offset(master.pos,ofs),true);
double da = deltaangle(angle,baseang+FRandom[Ynykron](-5,5)),
dp = deltaangle(pitch,basept+FRandom[Ynykron](-5,5));
angle += da*.2;
pitch += dp*.2;
if ( lifetimer == -1 )
{
A_FadeIn(FRandom[Ynykron](.003,.05));
if ( Alpha >= .2 )
{
Alpha = .2;
lifetimer = 0;
}
}
else if ( lifetimer == -2 ) A_FadeOut(FRandom[Ynykron](.003,.05));
else
{
lifetimer++;
if ( lifetimer > maxlife ) lifetimer = -2;
}
// check total length
int cnt = 0;
for ( YnykronVoidBeam b=self; b; b=YnykronVoidBeam(b.nextbeam) ) cnt++;
curlen = cnt;
}
else if ( !prevbeam )
{
Destroy();
return;
}
Vector3 x = SWWMUtility.Vec3FromAngles(angle,pitch);
if ( !t ) t = new("YnykronTracer");
t.ShootThroughList.Clear();
t.WaterHitList.Clear();
t.HitList.Clear();
t.Trace(pos,cursector,x,16*scale.x,TRACE_HitSky);
foreach ( l:t.ShootThroughList )
{
l.Activate(target,0,SPAC_PCross);
l.Activate(target,0,SPAC_Impact);
}
foreach ( hit:t.hitlist )
{
// yoink
SWWMUtility.DoKnockback(hit.HitActor,-hit.x,80000*scale.x*alpha);
}
spreadtimer++;
if ( (t.Results.HitType != TRACE_HitNone) || (segnum > maxlen) || (spreadtimer < stamina) )
{
// trigger walls
if ( t.Results.HitType == TRACE_HitWall )
t.Results.HitLine.RemoteActivate(target,t.Results.Side,SPAC_Impact,t.Results.HitPos);
// end of the line
if ( nextbeam ) nextbeam.Destroy();
if ( basebeam == self ) frame = 3;
else frame = 2;
}
else
{
if ( basebeam == self ) frame = 0;
else frame = 1;
// spawn (or relocate) next beam
if ( !nextbeam )
{
nextbeam = Spawn("YnykronVoidBeam",level.Vec3Offset(pos,x*16*scale.x));
nextbeam.angle = atan2(t.Results.HitVector.y,t.Results.HitVector.x);
nextbeam.pitch = asin(-t.Results.HitVector.z);
}
else nextbeam.SetOrigin(level.Vec3Offset(pos,x*16*scale.x),true);
nextbeam.target = target;
nextbeam.master = master;
YnykronVoidBeam(nextbeam).basebeam = basebeam;
YnykronVoidBeam(nextbeam).prevbeam = self;
YnykronVoidBeam(nextbeam).maxlen = maxlen;
YnykronVoidBeam(nextbeam).segnum = segnum+1;
nextbeam.alpha = alpha*(1.-(segnum/double(maxlen)));
nextbeam.scale = scale;
nextbeam.frame = 1;
Vector3 newdir = t.Results.HitVector;
newdir = (newdir+.4*SWWMUtility.Vec3FromAngles(FRandom[Ynykron](0,360),FRandom[Ynykron](-90,90))).unit();
double da = deltaangle(nextbeam.angle,atan2(newdir.y,newdir.x)),
dp = deltaangle(nextbeam.pitch,asin(-newdir.z));
nextbeam.angle += da*.2;
nextbeam.pitch += dp*.2;
}
let h = Spawn("YnykronVoidBeamTail",pos);
h.angle = angle;
h.pitch = pitch;
h.alpha = alpha*2.;
h.scale = scale;
h.frame = frame;
}
States
{
Spawn:
XZW1 # 1 Bright A_UpdateBeam();
Wait;
}
}
Class YnykronLightningImpact : SWWMNonInteractiveActor
{
Default
{
Obituary "$O_YNYKRONALT";
DamageType "Electric";
+FOILINVUL;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMUtility.DoExplosion(self,400,120000,100,40);
A_QuakeEx(3,3,3,12,0,800,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:300,rollIntensity:.4);
A_SprayDecal("ShockMark",-172);
int numpt = Random[ExploS](8,16);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](3,12);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
s.special1 = Random[ExploS](1,4);
s.scale *= 2.;
s.alpha *= .4;
}
numpt = Random[ExploS](5,10);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](10,15);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,24);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class YnykronLightningArc : SWWMNonInteractiveActor
{
Vector3 nextpos, nextdir;
Vector3 destpos;
void A_Trace()
{
let t = new("CandyBeamTracer");
t.hitlist.Clear();
Vector3 x, y, z;
[x, y, z] = SWWMUtility.GetAxes(angle,pitch,roll);
t.ShootThroughList.Clear();
t.Trace(pos,CurSector,x,speed,TRACE_HitSky);
foreach ( l:t.ShootThroughList )
{
l.Activate(target,0,SPAC_PCross);
l.Activate(target,0,SPAC_Impact);
}
foreach ( hit:t.hitlist )
{
if ( !hit.hitactor ) continue;
SWWMUtility.DoKnockback(hit.hitactor,-hit.x,GetMissileDamage(0,0)*1000);
let p = SWWMPuff.Setup(hit.hitlocation,hit.x,self,target,hit.hitactor);
hit.hitactor.DamageMobj(p,target,GetMissileDamage(0,0),'Electric',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
}
Vector3 normal = SWWMUtility.GetLineTracerHitNormal(t.Results), dir = t.Results.HitVector;
if ( t.Results.HitType != TRACE_HitNone )
{
if ( t.Results.HitType != TRACE_HasHitSky )
{
if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(target,t.Results.Side,SPAC_Impact,t.Results.HitPos);
let s = Spawn("YnykronLightningImpact",level.Vec3Offset(t.Results.HitPos,normal*8));
s.target = target;
s.angle = atan2(normal.y,normal.x);
s.pitch = asin(-normal.z);
if ( swwm_omnibust ) BusterWall.Bust(t.Results,GetMissileDamage(0,0),target,t.Results.HitVector,t.Results.HitPos.z);
}
nextpos = t.Results.HitPos;
bAMBUSH = true;
speed = t.Results.Distance; // shortens in minimap
return;
}
nextpos = level.Vec3Offset(pos,x*speed);
double a = FRandom[Ynykron](0,360), s = FRandom[Ynykron](0.,.8);
dir = SWWMUtility.ConeSpread(dir,y,z,a,s);
if ( !special1 )
{
destpos = level.Vec3Offset(pos,x*10000);
// calculate the closest strike point to home in onto
Vector3 strikepoints[32];
double closest = 10000.;
for ( int i=0; i<32; i++ )
{
a = FRandom[Ynykron](0,360);
s = FRandom[Ynykron](.2,1.);
Vector3 tdir = SWWMUtility.ConeSpread(dir,y,z,a,s);
FLineTraceData d;
LineTrace(atan2(tdir.y,tdir.x),10000,asin(-tdir.z),TRF_THRUACTORS,data:d);
strikepoints[i] = d.HitLocation;
if ( d.Distance < closest )
{
destpos = strikepoints[i];
closest = d.Distance;
}
}
}
Vector3 dirto = level.Vec3Diff(nextpos,destpos);
double dist = dirto.length();
if ( dist > 10 )
{
dirto /= dist;
dir = (dir+.8*dirto*(clamp(1.-(dist/1500.),0.,1.)**1.5)).unit();
}
nextdir = dir;
}
void A_Spread( Sound arcsnd )
{
Vector3 tdir = level.Vec3Diff(pos,nextpos);
if ( (GetClass() == 'YnykronLightningArc') && !Random[Ynykron](0,3) )
{
for ( int i=0; i<3; i++ )
{
let r = Spawn("YnykronLightningArcSub",level.Vec3Offset(pos,tdir*FRandom[Ynykron](0,1)));
Vector3 x, y, z;
[x, y, z] = SWWMUtility.GetAxes(angle,pitch,roll);
double a = FRandom[Ynykron](0,360), s = FRandom[Sparkster](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.special1 = 1;
YnykronLightningArc(r).destpos = destpos;
r.ReactionTime += Random[Ynykron](-3,3);
}
}
if ( arcsnd != "" ) A_StartSound(arcsnd,CHAN_WEAPON);
if ( ((ReactionTime > 0) && (special1 > ReactionTime)) || bAMBUSH ) return;
let b = Spawn(GetClass(),nextpos);
b.angle = atan2(nextdir.y,nextdir.x);
b.pitch = asin(-nextdir.z);
b.target = target;
b.special1 = special1+1;
YnykronLightningArc(b).destpos = destpos;
b.SetState(b.FindState("Trailer"));
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
frame = Random[Ynykron](0,11);
}
Default
{
Obituary "$O_YNYKRONALT";
RenderStyle "Add";
DamageFunction 1000;
Speed 128;
Alpha 2.;
+FOILINVUL;
}
States
{
Spawn:
XZW1 # 0 Bright;
XZW1 # 1 Bright A_Trace();
XZW1 # 1 Bright A_Spread("ynykron/vortexarc");
XZW1 # 1 Bright A_FadeOut();
Wait;
Trailer:
XZW2 # 0 Bright;
XZW2 # 1 Bright A_Trace();
XZW2 # 1 Bright A_Spread("ynykron/vortexarc");
XZW2 # 1 Bright A_FadeOut();
Wait;
}
}
Class YnykronLightningArcSub : YnykronLightningArc
{
Default
{
DamageFunction 250;
Speed 32;
ReactionTime 10;
}
States
{
Spawn:
XZW1 # 0 Bright;
XZW1 # 1 Bright A_Trace();
XZW1 # 1 Bright A_Spread("");
XZW1 # 1 Bright A_FadeOut();
Wait;
Trailer:
XZW2 # 0 Bright;
XZW2 # 1 Bright A_Trace();
XZW2 # 1 Bright A_Spread("");
XZW2 # 1 Bright A_FadeOut();
Wait;
}
}
Class YnykronCloud : SWWMNonInteractiveActor
{
Vector3 gx, gy, gz;
double phase;
Vector3 dirto;
double frightening; // lightning flash
Color basecol;
double rollvel;
override void Tick()
{
prev = pos;
Super.Tick();
}
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,255,255,255),1);
SWWMHandler.DoFlash(mo,Color(str/2,240,224,255),10);
}
override void PostBeginPlay()
{
SetState(FindState("Spawn")+Random[ExploS](0,19));
Scale *= FRandom[ExploS](.75,1.5);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
if ( master ) Scale *= master.scale.x;
// orbit axes
[gx, gy, gz] = SWWMUtility.GetAxes(FRandom[ExploS](0,360),FRandom[ExploS](-90,90),FRandom[ExploS](-90,90));
specialf1 = FRandom[ExploS](200,400);
scale *= specialf1/200.;
specialf2 = FRandom[ExploS](3.,8.)*RandomPick[ExploS](-1,1);
special2 = Random[ExploS](10,40);
basecol = Color(4,3,5)*Random[ExploS](5,20);
SetShade(basecol);
rollvel = FRandom[ExploS](.5,3.)*RandomPick[ExploS](-1,1);
}
void A_Gravitate()
{
if ( frightening > 0. )
{
alpha = max(frightening,.3);
int str = int(RandomPick[ExploS](255,240,192,254,248,128,160,204)*frightening);
Color litecol = Color(min(255,basecol.r+str),min(255,basecol.g+str),min(255,basecol.b+str));
SetShade(litecol);
frightening *= .96;
if ( frightening < .1 )
{
frightening = 0.;
SetShade(basecol);
}
}
roll += rollvel;
if ( master && !master.InStateSequence(master.CurState,master.FindState("Death")) )
{
if ( special1 == 0 )
{
A_FadeIn(FRandom[ExploS](.001,.002));
if ( alpha > .3 ) special1 = 1;
}
else if ( special1 > 0 )
{
special1++;
if ( special1 >= special2 )
special1 = -1;
}
else if ( special1 == -1 ) A_FadeOut(FRandom[ExploS](.003,.006));
dirto = level.Vec3Diff(pos,master.pos);
double distto = dirto.length();
dirto /= distto;
// orbit
Vector3 orbitdir = SWWMUtility.CircleOffset(gx,gy,phase,(specialf1+32)*master.scale.x);
SetOrigin(level.Vec3Offset(master.pos,orbitdir),true);
phase += FRandom[ExploS](.12,.24)*specialf2*(1.-specialf1/600.);
return;
}
SetOrigin(level.Vec3Offset(pos,-dirto*3.*abs(specialf2)),true);
specialf2 *= .98;
A_FadeOut(.005);
}
Default
{
DamageType 'YnykronAlt';
Obituary "$O_YNYKRONALT";
RenderStyle "Shaded";
Alpha 0.;
Scale 3.;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
}
States
{
Spawn:
BSMK ABCDEFGHIJKLMNOPQRST 1 A_Gravitate();
Wait;
}
}
Class YnykronLightningLight : PaletteLight
{
Default
{
Tag "WhiteExpl";
ReactionTime 12;
Args 0,0,0,500;
}
}
Class YnykronLightningLight2 : PaletteLight
{
Default
{
Tag "WhiteExpl";
ReactionTime 10;
Args 0,0,0,300;
}
}
Class SimpleMoveTracer : LineTracer
{
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor ) return TRACE_Skip;
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BLOCKPROJECTILE) )
return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class YnykronSingularityRing : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
Scale 4.;
+FORCEXYBILLBOARD;
}
States
{
Spawn:
XRG4 AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX 1 Bright A_SetScale(scale.x*1.05);
Stop;
}
}
Class YnykronVoidExplLight : PaletteLight
{
Default
{
Tag "Purple";
ReactionTime 60;
Args 0,0,0,1200;
}
}
Class YnykronVoidSparkleTrail : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
XScale 6.;
+FORCEXYBILLBOARD;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x*.95,scale.y);
A_FadeOut(.04);
}
States
{
Spawn:
XZW1 A -1 Bright;
Stop;
}
}
Class YnykronVoidSparkle : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Scale *= FRandom[ExploS](.75,1.5);
specialf1 = FRandom[ExploS](.95,.98);
specialf2 = FRandom[ExploS](.005,.015);
vel = SWWMUtility.Vec3FromAngles(angle,pitch)*FRandom[ExploS](4,32);
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x*specialf1);
A_FadeOut(specialf2);
Vector3 dir = vel;
double magvel = dir.length();
magvel *= .99;
if ( magvel > 0. )
{
dir /= magvel;
dir += .2*SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90));
vel = dir.unit()*magvel;
}
Vector3 newpos = level.Vec3Offset(pos,vel);
if ( (newpos.z < floorz) || (newpos.z >= ceilingz) )
{
vel.z *= -1;
newpos = level.Vec3Offset(pos,vel);
}
if ( !level.IsPointInLevel(newpos) )
{
vel.xy *= -1;
newpos = level.Vec3Offset(pos,vel);
}
dir = level.Vec3Diff(newpos,pos);
double len = dir.length();
if ( (len > 0.) && (alpha > .1) )
{
dir /= len;
let t = Spawn("YnykronVoidSparkleTrail",newpos);
t.alpha = alpha*.5;
t.scale.x *= scale.x;
t.speed = len;
[t.angle, t.pitch, t.scale.y] = SWWMUtility.CalcYBeam(dir,len);
}
SetOrigin(newpos,true);
}
States
{
Spawn:
MOPF A -1 Bright;
Stop;
}
}
Class YnykronSingularityExplosionArm : Actor
{
Default
{
PROJECTILE;
+THRUACTORS;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
-NOGRAVITY;
+NOFRICTION;
Gravity 0.35;
BounceFactor 1.0;
Radius 2;
Height 4;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
reactiontime = Random[ExploS](10,20);
double ang, pt;
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,90);
vel = SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[ExploS](20.,40.);
}
States
{
Spawn:
TNT1 A 1
{
A_CountDown();
if ( !(reactiontime%2) )
{
Spawn("YnykronSingularityExplosionTrail",pos);
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,5);
let s = Spawn("SWWMHalfSmoke",pos);
s.SetShade(Color(1,1,1)*Random[ExploS](128,160)+Color(28,0,31));
s.A_SetRenderStyle(s.alpha*(.1+.4*(ReactionTime/15.)),STYLE_AddShaded);
s.vel = pvel+vel*.2;
s.special1 = Random[ExploS](1,3);
s.scale *= 2.4;
}
}
Wait;
}
}
Class YnykronSingularityExplosionTrail : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
Scale 3.;
Alpha .2;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
}
States
{
Spawn:
XEX4 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 1 Bright;
Stop;
}
}
Class YnykronSingularityExplosion : SWWMNonInteractiveActor
{
Default
{
Obituary "$O_YNYKRONALT";
DamageType "YnykronAlt";
RenderStyle "Add";
Scale 5.;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+FOILINVUL;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !Random[ExploS](0,5) )
A_StartSound("ynykron/impact",CHAN_VOICE);
SWWMUtility.DoExplosion(self,10000,5000,400,200);
A_SprayDecal("WumboRocketBlast",-172);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](4,20);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](128,160)+Color(28,0,31));
s.A_SetRenderStyle(.2,STYLE_AddShaded);
s.special1 = Random[ExploS](2,5);
s.scale *= 8.;
}
numpt = Random[ExploS](0,4);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double ofs = FRandom[ExploS](0.,30.);
let s = Spawn("YnykronVoidSparkle",level.Vec3Offset(pos,ofs*SWWMUtility.Vec3FromAngles(ang,pt)));
s.angle = ang;
s.pitch = pt;
}
numpt = Random[ExploS](-1,3);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("YnykronSingularityExplosionArm",pos);
s.target = target;
}
}
States
{
Spawn:
XEX4 AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\ 1 Bright A_SetScale(scale.x*1.01,scale.y*1.01);
Stop;
}
}
Class YnykronSingularityHitbox : SWWMNonInteractiveActor
{
Default
{
-NOBLOCKMAP;
}
override void Tick()
{
if ( !target || !target.InStateSequence(target.CurState,target.FindState("Spawn")) )
{
Destroy();
return;
}
A_SetSize(32.*target.scale.x,64.*target.scale.y);
SetOrigin(target.Vec3Offset(0,0,-height*.5),true);
}
override bool CanCollideWith( Actor other, bool passive )
{
return false;
}
}
Class YnykronSingularity : SWWMNonInteractiveActor
{
const MAXBEAMS = 10;
const MAXCLOUDS = 300;
Actor beamers[MAXBEAMS];
Actor clouds[MAXCLOUDS];
const MION_CONST = 48538.; // miön v-force compression constant (approximate)
const YNON_CONST = .043738; // ÿnon v-force field constant (approximate)
const AXAN_CONST = 27.2761; // axan u-force field constant (approximate)
const GOAL_MASS = 26741.; // mass required to compensate Ynykron altfire negative energy release (-2.4×10¹⁵ J, approximately)
double critmass; // actual mass required (cummulative with absorbed vortices)
int litetimer;
transient SimpleMoveTracer mt;
override void PostBeginPlay()
{
let g = Spawn("YnykronHalo",pos);
g.target = self;
A_StartSound("ynykron/vortex",CHAN_BODY,CHANF_LOOP,1.,.6);
A_StartSound("ynykron/wind",CHAN_ITEM,CHANF_LOOP,1.,.0);
A_StartSound("ynykron/hit",CHAN_VOICE,CHANF_OVERLAP,1.,.0);
// initial mass and radius
specialf1 = scale.x;
specialf2 = scale.x*scale.x*MION_CONST;
critmass = GOAL_MASS;
litetimer = level.maptime+Random[Ynykron](180,240);
let h = Spawn("YnykronSingularityHitbox",pos);
h.target = self;
}
// lightweight tick, we don't need anything else other than states
override void Tick()
{
prev = pos;
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
Vector3 newpos;
if ( !mt ) mt = new("SimpleMoveTracer");
Vector3 dir = vel;
double dist = vel.length();
dir /= dist;
mt.Trace(pos,CurSector,dir,dist,0);
if ( mt.Results.HitType == TRACE_HitNone ) newpos = level.Vec3Offset(pos,vel);
else newpos = level.Vec3Offset(mt.Results.HitPos,-mt.Results.HitVector);
if ( level.IsPointInLevel(newpos) )
SetOrigin(newpos,true);
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( mod == 'Ynykron' )
return StringTable.Localize("$O_YNYKRON");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
void A_SingularityTick()
{
// gather dust particles (minor "background" mass gain)
int numpt = int(max(4,20*scale.x));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
FLineTraceData d;
double dst = FRandom[ExploS](1500,2000)*scale.x;
LineTrace(ang,dst,pt,TRF_THRUACTORS|TRF_THRUHITSCAN,data:d);
Vector3 norm = SWWMUtility.GetLineTraceHitNormal(d);
let gd = Spawn("GatherDust",level.Vec3Offset(d.HitLocation,norm*8));
gd.target = self;
}
// storm clouds
for ( int i=0; i<MAXCLOUDS; i++ )
{
if ( clouds[i] && (clouds[i].special1 > -1) ) continue;
clouds[i] = Spawn("YnykronCloud",pos);
clouds[i].target = target;
clouds[i].master = self;
}
// check dense cloud formations for lightning
if ( level.maptime > litetimer )
{
int which = Random[Ynykron](0,MAXCLOUDS-1);
if ( clouds[which] )
{
Array<Actor> contacts;
Array<double> dists;
contacts.Clear();
dists.Clear();
for ( int i=0; i<MAXCLOUDS; i++ )
{
if ( !clouds[i] || (i == which) || (clouds[i].Alpha < .1) ) continue;
double dist = clouds[which].Distance3D(clouds[i]);
if ( dist > (200*scale.x) ) continue;
contacts.Push(clouds[i]);
dists.Push(dist);
}
if ( (contacts.Size() > 12) && !Random[Ynykron](0,5) )
{
Spawn("YnykronLightningLight",clouds[which].pos);
Spawn("YnykronLightningLight2",clouds[which].pos);
A_StartSound("ynykron/vortexflash",CHAN_WEAPON,CHANF_OVERLAP,1.,0.,FRandom[Ynykron](.9,1.1));
YnykronCloud(clouds[which]).frightening = 1.;
YnykronCloud(clouds[which]).FlashPlayer(200,3000);
for ( int i=0; i<contacts.Size(); i++ )
YnykronCloud(contacts[i]).frightening = (1.-dists[i]/(200*scale.x))**.5;
litetimer = level.maptime+Random[Ynykron](120,240);
SWWMUtility.DoExplosion(clouds[which],1200,40000,500,150);
// lightning arcs
int numpt = Random[Ynykron](0,3);
Vector3 dirto = level.Vec3Diff(pos,clouds[which].pos);
dirto /= dirto.length();
for ( int i=0; i<numpt; i++ )
{
let l = Spawn("YnykronLightningArc",clouds[which].pos);
l.angle = atan2(dirto.y,dirto.x)+FRandom[Ynykron](-5,5);
l.pitch = asin(-dirto.z)+FRandom[Ynykron](-5,5);
l.target = target;
}
}
}
}
// ol' crazy purple beams from original
for ( int i=0; i<MAXBEAMS; i++ )
{
if ( beamers[i] || Random[Ynykron](0,40) ) continue;
beamers[i] = Spawn("YnykronVoidBeam",pos);
YnykronVoidBeam(beamers[i]).basebeam = beamers[i];
beamers[i].target = target;
beamers[i].master = self;
}
// suck in reachable targets
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd )
{
foreach ( a:hnd.SuckableActors )
{
if ( !a ) continue;
if ( a.bDORMANT || (a.Health <= 0) || (a == self) || !SWWMUtility.SphereIntersect(a,pos,20000.*scale.x) || !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
if ( (a is 'Inventory') && !a.bDROPPED ) // must be a dropped pickup
continue;
if ( a.player && (a.player.cheats&CF_NOCLIP2) ) // for safety
continue;
if ( (a is 'YnykronSingularity') && a.bNOBLOCKMAP ) // dead singularity, ignore
continue;
double capmass = a.mass;
// succ
if ( SWWMUtility.SphereIntersect(a,pos,32.*scale.x) )
{
// merge with other vortices
if ( a is 'YnykronSingularity' )
{
critmass += YnykronSingularity(a).critmass;
specialf2 += a.specialf2;
a.Destroy();
continue;
}
// don't delet inventory
if ( a is 'Inventory' )
continue;
// delet missile
if ( a.bMISSILE )
{
specialf2 += 5.;
a.Destroy();
continue;
}
// voodoo dolls just get erased (how convenient)
// otherwise instantly vaporize the fucker
if ( a.player && (a.player.mo != a) ) a.Destroy();
else if ( a.CountInv("GrilledCheeseSandwich") > 0 )
{
// force use the sandwich, warp to safe spot
let gc = GrilledCheeseSandwich(a.FindInventory("GrilledCheeseSandwich"));
gc.SafeTeleport(true);
a.A_StartSound(gc.UseSound,CHAN_ITEMEXTRA);
gc.DoTheThing(true);
gc.Amount--;
}
else if ( !a.FindInventory("GrilledCheeseSafeguard") )
a.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS);
if ( a && (a.Health <= 0) )
{
if ( a.player )
{
if ( a == target )
{
SWWMUtility.MarkAchievement("oopsie",a.player);
target = PlayerGone.FeckOff(a);
}
else PlayerGone.FeckOff(a);
}
if ( a.FindState("YnykronAltDeath",true) )
a.SetStateLabel("YnykronAltDeath"); // dedicated state
else
{
// poof away manually
a.bINVISIBLE = true;
a.A_ChangeLinkFlags(false); // remove from blockmap, should guarantee archviles not raising this
IDontFeelSoGood.DeletThis(a,true); // ensures corpse is deleted too
}
if ( target && a.FindInventory("EndgameBossMarker") )
SWWMUtility.MarkAchievement("ligma",target.player);
}
if ( !a || (a.Health <= 0) )
specialf2 += min(100.,capmass*.6); // partial absorption
continue;
}
capmass = max(50.,a.mass);
Vector3 dirto = level.Vec3Diff(a.Vec3Offset(0,0,a.Height/2),pos);
double dist = dirto.length();
dirto /= dist;
// rip
if ( a && a.bSHOOTABLE && SWWMUtility.SphereIntersect(a,pos,200.*scale.x) )
a.DamageMobj(self,target,int(clamp(10.-dist*.05,0.,5.)),'YnykronAlt',DMG_FORCED|DMG_THRUSTLESS);
if ( !a ) continue;
// weak gravitational force as per v-field (approximate)
double grav = (3.*YNON_CONST*specialf2*capmass)/(dist**.5);
// strong gravitational force as per u-field (approximate)
grav += (4.*AXAN_CONST*specialf2*capmass)/(dist**2.);
// account for ground friction
if ( a.pos.z <= a.floorz )
{
double frict, movef;
[frict, movef] = a.GetFriction();
grav *= movef;
if ( !a.player ) dirto.z += .1;
}
if ( a.mass < LARGE_MASS )
{
// v-force field compression (very rough approximation)
a.vel *= max(.0,1.-.03*(grav*grav)/(MION_CONST*MION_CONST));
a.vel += dirto*grav/(capmass*GameTicRate);
}
}
}
// push away from nearby geometry
double pushdist = 50+32*scale.x;
for ( int i=0; i<8; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
FLineTraceData d;
if ( !LineTrace(ang,pushdist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN,data:d) ) continue;
double mag = pushdist-d.Distance;
vel -= SWWMUtility.Vec3FromAngles(ang,pt)*.02*mag;
}
double magvel = vel.length();
Vector3 dir;
if ( magvel <= double.epsilon ) dir = SWWMUtility.Vec3FromAngles(angle,pitch);
else dir = vel/magvel;
// wander
dir = (dir+SWWMUtility.Vec3FromAngles(FRandom[Ynykron](0,360),FRandom[Ynykron](-90,90))*FRandom[Ynykron](.05,.2)).unit();
magvel *= .93;
if ( magvel > 5. ) magvel *= .95;
if ( magvel > 20. ) magvel *= .65;
if ( magvel < .1 ) magvel = .1;
vel = magvel*dir;
// calculate target scale based on radius of event horizon
specialf1 = (specialf2/MION_CONST)**.5;
// shift to target scale
// hopefully this supports interpolation someday
double diffscale = specialf1-scale.x;
if ( abs(diffscale) <= .01 ) A_SetScale(specialf1);
else A_SetScale(scale.x+diffscale*.3);
// detonate if we reach critical mass
if ( specialf2 >= critmass )
SetStateLabel("Death");
}
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,255,255,255),3);
SWWMHandler.DoFlash(mo,Color(str/2,240,224,255),30);
}
void A_SingularityBlast()
{
A_ChangeLinkFlags(true); // unlink from blockmap
A_AlertMonsters(0,AMF_EMITFROMTARGET);
SWWMUtility.DoExplosion(self,int.max,500000,800,400);
A_QuakeEx(8,8,8,100,0,65535,"",QF_RELATIVE|QF_SCALEDOWN,falloff:65535,rollIntensity:1.6);
A_StopAllSounds();
A_StartSound("ynykron/vortexend",CHAN_VOICE,attenuation:0.);
FlashPlayer(250,9000);
vel *= 0;
// kill off any leftover beams
for ( int i=0; i<MAXBEAMS; i++ )
{
if ( !beamers[i] ) continue;
YnykronVoidBeam(beamers[i]).maxlife = 0;
}
Spawn("YnykronSingularityRing",pos);
Spawn("YnykronVoidExplLight",pos);
int numpt = Random[ExploS](60,80);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("YnykronVoidSparkle",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.scale *= 8.;
s.alpha *= 5.;
}
}
void A_SingularityExtraBlast()
{
special1++;
if ( special1 > 60 )
{
if ( IsActorPlayingSound(CHAN_VOICE,-1) ) return;
for ( int i=0; i<MAXBEAMS; i++ ) if ( beamers[i] ) return;
Destroy();
return;
}
double factor = (60-special1)/60.;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,0,-50000*factor,4000*invfct);
FlashPlayer(int(min(100*factor,250)),10000);
FLineTraceData d;
Vector3 HitNormal;
Vector3 origin;
double ang, pt;
int numpt = clamp((60-special1)/4,2,12);
for ( int i=0; i<numpt; i++ )
{
double totaldist = 30+(special1**2.5)*.1;
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,90);
origin = pos;
while ( totaldist > 0 )
{
LineTrace(ang,totaldist,pt,TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
HitNormal = SWWMUtility.GetLineTraceHitNormal(d);
totaldist -= d.Distance;
if ( totaldist > 0 )
{
Vector3 bounced = d.HitDir-(1.2*hitnormal*(d.HitDir dot HitNormal));
ang = atan2(bounced.y,bounced.x);
pt = asin(-bounced.z);
origin = d.HitLocation+hitnormal;
}
}
let p = Spawn("YnykronSingularityExplosion",d.HitLocation+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = target;
p.scale *= 1.+special1/40.;
}
}
Default
{
DamageType 'YnykronAlt';
Obituary "$O_YNYKRONALT";
+MISSILE;
Scale .4;
}
States
{
Spawn:
XZW1 A 1 BRIGHT A_SingularityTick();
Wait;
Death:
TNT1 A 0 A_SingularityBlast();
TNT1 A 1 A_SingularityExtraBlast();
Wait;
}
}
Class YnykronAltBeam : SWWMNonInteractiveActor
{
bool nospread;
void TraceOut()
{
Vector3 x = SWWMUtility.Vec3FromAngles(angle,pitch);
let t = new("YnykronAltTracer");
t.ignore = target;
t.ShootThroughList.Clear();
t.WaterHitList.Clear();
t.Trace(pos,cursector,x,speed,TRACE_HitSky);
foreach ( l:t.ShootThroughList )
{
l.Activate(target,0,SPAC_PCross);
l.Activate(target,0,SPAC_Impact);
}
foreach ( w:t.WaterHitList )
{
let b = Spawn("InvisibleSplasher",w.hitpos);
b.target = target;
b.A_CheckTerrain();
}
for ( int i=0; i<t.Results.Distance; i+=16 )
{
if ( Random[Ynykron](0,2) ) continue;
let b = Spawn("SWWMHalfSmoke",level.Vec3Offset(pos,x*i));
b.Scale *= FRandom[Ynykron](1.6,2.8);
b.special1 = Random[Ynykron](3,5);
b.A_SetRenderStyle(b.alpha*.3,STYLE_Subtract);
b.vel += x*FRandom[Ynykron](-.2,.4);
}
if ( t.Results.HitType == TRACE_HasHitSky )
{
// goodbye
nospread = true;
speed = t.Results.Distance; // shortens in minimap
return;
}
if ( t.Results.HitType != TRACE_HitNone )
{
// hit something
Vector3 norm = SWWMUtility.GetLineTracerHitNormal(t.Results);
if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(tracer,t.Results.Side,SPAC_Impact,t.Results.HitPos);
let b = Spawn("YnykronSingularity",level.Vec3Offset(t.Results.HitPos,norm*16));
b.target = target;
b.angle = atan2(norm.y,norm.x);
b.pitch = asin(-norm.z);
nospread = true;
speed = t.Results.Distance; // shortens in minimap
return;
}
if ( special1 >= 25600 )
{
// end of the line, dissipate
int numpt = Random[Ynykron](16,24);
for ( int i=0; i<numpt; i++ )
{
let b = Spawn("SWWMSmoke",level.Vec3Offset(pos,x*speed));
b.Scale *= FRandom[Ynykron](.6,1.8);
b.special1 = Random[Ynykron](1,2);
b.A_SetRenderStyle(.3,STYLE_Subtract);
b.vel += x*FRandom[Ynykron](0.,5.);
}
nospread = true;
return;
}
}
void SpreadOut()
{
if ( nospread ) return;
Vector3 x = SWWMUtility.Vec3FromAngles(angle,pitch);
// propagate
let next = Spawn("YnykronAltBeam",level.Vec3Offset(pos,x*speed));
next.angle = atan2(x.y,x.x);
next.pitch = asin(-x.z);
next.roll = roll;
next.target = target;
next.master = master;
next.special1 = special1+int(speed);
next.SetStateLabel("TrailSpawn");
}
override void PostBeginPlay()
{
special2 = Random[Ynykron](-1,0);
TraceOut();
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_FadeOut(FRandom[Ynykron](.01,.02));
special2++;
if ( special2 == 1 )
SpreadOut();
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
Default
{
RenderStyle "Subtract";
Alpha .4;
Speed 64;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
}
States
{
Spawn:
XZW1 A -1 Bright NoDelay
{
return FindState("StarterDev")+Random[Ynykron](0,3)*2;
}
Stop;
TrailSpawn:
XZW2 A -1 Bright
{
return FindState("TrailerDev")+Random[Ynykron](0,3)*2;
}
Stop;
StarterDev:
#### # 50 Bright;
XZW1 B -1 Bright;
Stop;
#### # 50 Bright;
XZW1 C -1 Bright;
Stop;
#### # 50 Bright;
XZW1 D -1 Bright;
Stop;
#### # 50 Bright;
XZW1 E -1 Bright;
Stop;
TrailerDev:
#### # 50 Bright;
XZW2 B -1 Bright;
Stop;
#### # 50 Bright;
XZW2 C -1 Bright;
Stop;
#### # 50 Bright;
XZW2 D -1 Bright;
Stop;
#### # 50 Bright;
XZW2 E -1 Bright;
Stop;
}
}
Class YnykronAltShot : SWWMNonInteractiveActor
{
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,255,255,255),5);
SWWMHandler.DoFlash(mo,Color(str/2,0,0,0),15);
}
override void PostBeginPlay()
{
A_QuakeEx(4,4,4,80,0,65535,"",QF_RELATIVE|QF_SCALEDOWN,falloff:65535,rollIntensity:.8);
A_StartSound("ynykron/altbeam",CHAN_VOICE,CHANF_DEFAULT,1.,0.);
FlashPlayer(240,8000);
let b = Spawn("YnykronAltBeam",pos);
b.target = target;
b.angle = angle;
b.pitch = pitch;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() || IsActorPlayingSound(CHAN_VOICE,-1) ) return;
Destroy();
}
}