1525 lines
38 KiB
Text
1525 lines
38 KiB
Text
// 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 poor sap
|
||
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();
|
||
}
|
||
}
|