Merge separate palettes into multi-palettes, adapt PaletteLight to handle this. Added a couple extra voice sounds to the Demolitionist.
3209 lines
81 KiB
Text
3209 lines
81 KiB
Text
// Ynykron Artifact (from UnSX Series, featured in SWWM Platinum as a secret weapon)
|
||
// Slot 0, replaces BFG9000, Firemace, Wraithverge (arc)
|
||
|
||
// cheap way to let players know they just got fucking erased from existence
|
||
Class PlayerGone : PlayerChunk
|
||
{
|
||
int deadtimer;
|
||
|
||
override void DeathThink()
|
||
{
|
||
player.damagecount--;
|
||
player.poisoncount--;
|
||
deadtimer++;
|
||
if ( (deadtimer == 60) && (player == players[consoleplayer]) )
|
||
A_StartSound("demolitionist/youdied",CHAN_DEMOVOICE,CHANF_OVERLAP|CHANF_UI);
|
||
if ( multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn) )
|
||
{
|
||
// standard behaviour, respawn normally
|
||
if ( (((player.cmd.buttons&BT_USE) && !player.Bot) || ((deathmatch || alwaysapplydmflags) && sv_forcerespawn && (Level.maptime >= player.respawn_time))) && !sv_norespawn )
|
||
{
|
||
player.cls = null;
|
||
player.playerstate = PST_REBORN;
|
||
if ( special1 > 2 ) special1 = 0;
|
||
}
|
||
}
|
||
else if ( (player.cmd.buttons&BT_USE) && (deadtimer > 120) )
|
||
{
|
||
// reload save
|
||
player.cls = null;
|
||
player.playerstate = PST_ENTER;
|
||
if ( special1 > 2 ) special1 = 0;
|
||
}
|
||
// no revive (for obvious reasons)
|
||
}
|
||
|
||
static void FeckOff( Actor p )
|
||
{
|
||
// doesn't affect voodoo dolls (convenient, isn't it?)
|
||
if ( !p.player || (p.player.mo != p) ) return;
|
||
let c = PlayerGone(Spawn("PlayerGone",(65535,65535,0)));
|
||
c.player = p.player;
|
||
c.Health = p.Health;
|
||
p.player = null;
|
||
c.ObtainInventory(p);
|
||
if ( c.player ) c.player.mo = c;
|
||
for ( int i=0; i<MAXPLAYERS; i++ )
|
||
{
|
||
if ( playeringame[i] && (players[i].camera == p) )
|
||
players[i].camera = c;
|
||
}
|
||
}
|
||
}
|
||
|
||
Class DSparilHax : Sorcerer2
|
||
{
|
||
override void PostBeginPlay()
|
||
{
|
||
Thing_Destroy(0);
|
||
A_BossDeath();
|
||
Destroy();
|
||
}
|
||
}
|
||
|
||
Class IDontFeelSoGood : Thinker
|
||
{
|
||
Actor goner;
|
||
int cnt;
|
||
bool silence;
|
||
|
||
static void DeletThis( Actor whomst, bool silence = false )
|
||
{
|
||
if ( !whomst || (whomst.Health > 0) ) return;
|
||
let ti = ThinkerIterator.Create("IDontFeelSoGood",STAT_USER);
|
||
IDontFeelSoGood i;
|
||
while ( i = IDontFeelSoGood(ti.Next()) )
|
||
if ( i.goner == whomst ) return;
|
||
i = new("IDontFeelSoGood");
|
||
i.ChangeStatNum(STAT_USER);
|
||
i.goner = whomst;
|
||
i.silence = silence;
|
||
}
|
||
|
||
override void Tick()
|
||
{
|
||
if ( !goner )
|
||
{
|
||
Destroy();
|
||
return;
|
||
}
|
||
if ( goner.Health > 0 )
|
||
{
|
||
goner.bINVISIBLE = goner.default.bINVISIBLE;
|
||
goner.A_ChangeLinkFlags(goner.default.bNOBLOCKMAP);
|
||
Destroy();
|
||
return;
|
||
}
|
||
if ( silence ) goner.A_StopAllSounds();
|
||
// special handling for some bosses:
|
||
// - D'Sparil does not spawn, he's already gone
|
||
// - Korax doesn't leave ghosts
|
||
if ( (goner.tics != -1) && !(goner is 'Sorcerer1') && !(goner is 'Korax') ) return;
|
||
cnt++;
|
||
if ( cnt < 15 ) return;
|
||
if ( goner is 'Sorcerer1' )
|
||
{
|
||
let h = Actor.Spawn("DSparilHax",goner.pos);
|
||
h.CopyFriendliness(goner,true);
|
||
}
|
||
else if ( goner is 'Korax' )
|
||
level.ExecuteSpecial(80,goner,null,0,255);
|
||
goner.Destroy();
|
||
}
|
||
}
|
||
|
||
Class YnykronImpactLight : PaletteLight
|
||
{
|
||
Default
|
||
{
|
||
Tag "WhiteExpl,1";
|
||
ReactionTime 60;
|
||
Args 0,0,0,400;
|
||
}
|
||
}
|
||
|
||
Class YnykronSubImpactLight : PaletteLight
|
||
{
|
||
Default
|
||
{
|
||
Tag "WhiteExpl,1";
|
||
ReactionTime 20;
|
||
Args 0,0,0,200;
|
||
}
|
||
}
|
||
|
||
Class YnykronShotLight : PaletteLight
|
||
{
|
||
Default
|
||
{
|
||
Tag "WhiteExpl";
|
||
ReactionTime 100;
|
||
Args 0,0,0,1200;
|
||
}
|
||
}
|
||
|
||
Class YnykronBeamLight : PaletteLight
|
||
{
|
||
Default
|
||
{
|
||
Tag "WhiteExpl,1";
|
||
ReactionTime 50;
|
||
Args 0,0,0,600;
|
||
}
|
||
}
|
||
|
||
Class YnykronImpactArm : Actor
|
||
{
|
||
Default
|
||
{
|
||
PROJECTILE;
|
||
+THRUACTORS;
|
||
+BOUNCEONWALLS;
|
||
+BOUNCEONFLOORS;
|
||
+BOUNCEONCEILINGS;
|
||
+NODAMAGETHRUST;
|
||
+FORCERADIUSDMG;
|
||
-NOGRAVITY;
|
||
Gravity 0.35;
|
||
BounceFactor 1.0;
|
||
Radius 4;
|
||
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 = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[ExploS](20.,40.);
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
TNT1 A 1
|
||
{
|
||
A_CountDown();
|
||
if ( !(reactiontime%2) )
|
||
{
|
||
Spawn("YnykronImpactTrail",pos);
|
||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
|
||
let s = Spawn("SWWMHalfSmoke",pos);
|
||
s.vel = pvel+vel*.2;
|
||
s.special1 = Random[ExploS](1,3);
|
||
s.scale *= 2.4;
|
||
s.alpha *= 0.1+.4*(ReactionTime/15.);
|
||
}
|
||
}
|
||
Wait;
|
||
}
|
||
}
|
||
|
||
Class YnykronImpactTrail : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
+NOBLOCKMAP;
|
||
+NOGRAVITY;
|
||
+FORCEXYBILLBOARD;
|
||
+NOTELEPORT;
|
||
+NOINTERACTION;
|
||
Scale 2.;
|
||
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);
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
MOXP ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 1 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronDelayedImpact : Actor
|
||
{
|
||
Vector3 ofs;
|
||
|
||
Default
|
||
{
|
||
+NOBLOCKMAP;
|
||
+NOGRAVITY;
|
||
+NOTELEPORT;
|
||
+NOINTERACTION;
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
special1++;
|
||
if ( special1 < 4 )
|
||
{
|
||
if ( tracer ) SetOrigin(level.Vec3Offset(tracer.pos,ofs),false);
|
||
return;
|
||
}
|
||
let b = Spawn("YnykronImpact",pos);
|
||
b.tracer = tracer;
|
||
b.target = target;
|
||
b.master = master;
|
||
b.angle = angle;
|
||
b.pitch = pitch;
|
||
b.special1 = special2;
|
||
b.special2 = 1;
|
||
b.args[0] = args[0];
|
||
Destroy();
|
||
}
|
||
}
|
||
|
||
Class YnykronImpact : Actor
|
||
{
|
||
int rad;
|
||
|
||
Default
|
||
{
|
||
Obituary "$O_YNYKRON";
|
||
DamageType "Ynykron";
|
||
RenderStyle "Add";
|
||
Scale 5.;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+FORCEXYBILLBOARD;
|
||
+NOINTERACTION;
|
||
+NODAMAGETHRUST;
|
||
+FORCERADIUSDMG;
|
||
+EXTREMEDEATH;
|
||
}
|
||
|
||
private bool CmpDist( Actor a, Actor b )
|
||
{
|
||
double dista = Vec3To(a).length();
|
||
double distb = Vec3To(b).length();
|
||
return (dista > distb);
|
||
}
|
||
|
||
// quicksort (candidates)
|
||
private int partition_candidates( Array<Actor> a, int l, int h )
|
||
{
|
||
Actor pv = a[h];
|
||
int i = (l-1);
|
||
for ( int j=l; j<=(h-1); j++ )
|
||
{
|
||
if ( CmpDist(pv,a[j]) )
|
||
{
|
||
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_candidates( Array<Actor> a, int l, int h )
|
||
{
|
||
if ( l >= h ) return;
|
||
int p = partition_candidates(a,l,h);
|
||
qsort_candidates(a,l,p-1);
|
||
qsort_candidates(a,p+1,h);
|
||
}
|
||
|
||
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),2);
|
||
SWWMHandler.DoFlash(mo,Color(str/2,255,255,255),10);
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
Super.PostBeginPlay();
|
||
// no need to call A_AlertMonsters if all monsters on the entire map were already alerted by the initial shot
|
||
if ( swwm_ynykronalert && (!special2 || swwm_extraalert) ) A_AlertMonsters();
|
||
rad = args[0]+300+10*clamp(special1/10,0,15);
|
||
A_QuakeEx(4,4,4,50,0,rad*4,"",QF_RELATIVE|QF_SCALEDOWN,falloff:rad*2,rollintensity:.6);
|
||
FlashPlayer(60,1200);
|
||
if ( tracer )
|
||
{
|
||
// voodoo dolls just get erased (how convenient)
|
||
// otherwise instantly vaporize the fucker
|
||
if ( tracer.player && (tracer.player.mo != tracer) ) tracer.Destroy();
|
||
else if ( tracer.CountInv("GrilledCheeseSandwich") > 0 )
|
||
{
|
||
// force use the sandwich
|
||
let gc = GrilledCheeseSandwich(tracer.FindInventory("GrilledCheeseSandwich"));
|
||
tracer.A_StartSound(gc.UseSound,CHAN_ITEMEXTRA);
|
||
gc.DoTheThing(true);
|
||
gc.Amount--;
|
||
}
|
||
else if ( !tracer.FindInventory("GrilledCheeseSafeguard") ) tracer.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS);
|
||
if ( tracer && (tracer.Health <= 0) )
|
||
{
|
||
if ( tracer.player ) PlayerGone.FeckOff(tracer);
|
||
if ( tracer.FindState("YnykronDeath",true) )
|
||
tracer.SetStateLabel("YnykronDeath"); // dedicated state
|
||
else
|
||
{
|
||
// poof away manually
|
||
tracer.bINVISIBLE = true;
|
||
tracer.A_ChangeLinkFlags(true); // remove from blockmap, should guarantee archviles not raising this
|
||
IDontFeelSoGood.DeletThis(tracer); // ensures corpse is deleted too
|
||
}
|
||
}
|
||
}
|
||
if ( YnykronShot(master) )
|
||
{
|
||
if ( YnykronShot(master).lastimpact < gametic )
|
||
{
|
||
master.A_StartSound("ynykron/hit",CHAN_VOICE,CHANF_OVERLAP,special1?.4:1.,.0);
|
||
YnykronShot(master).lastimpact = gametic+(special1?2:5);
|
||
}
|
||
}
|
||
else A_StartSound("ynykron/hit",CHAN_VOICE,CHANF_OVERLAP,special1?.4:1.,.0);
|
||
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);
|
||
if ( special2 ) numpt -= 3;
|
||
for ( int i=0; i<numpt; i++ )
|
||
{
|
||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.5,2);
|
||
let s = Spawn("SWWMHalfSmoke",pos);
|
||
s.vel = pvel;
|
||
s.special1 = Random[ExploS](1,8);
|
||
s.scale *= 3.;
|
||
s.alpha *= .4;
|
||
}
|
||
if ( special2 ) Spawn("YnykronSubImpactLight",pos);
|
||
else Spawn("YnykronImpactLight",pos);
|
||
let r = Spawn("YnykronImpactRing",pos);
|
||
r.special2 = special2;
|
||
r.scale *= abs(scale.x)/5.;
|
||
numpt = Random[ExploS](special2?-4:0,3);
|
||
for ( int i=0; i<numpt; i++ )
|
||
{
|
||
let s = Spawn("YnykronImpactArm",pos);
|
||
s.target = target;
|
||
}
|
||
if ( swwm_capmcrange && (special1 > 2) ) return;
|
||
let bt = BlockThingsIterator.Create(self,rad+200);
|
||
Array<Actor> candidates;
|
||
candidates.Clear();
|
||
while ( bt.Next() )
|
||
{
|
||
let t = bt.Thing;
|
||
if ( !t || !t.bSHOOTABLE || !SWWMUtility.SphereIntersect(t,pos,rad) || (!SWWMUtility.SphereIntersect(t,pos,100) && !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) ) continue;
|
||
if ( YnykronShot(master) && (YnykronShot(master).hitlist.Find(t) < YnykronShot(master).hitlist.Size()) )
|
||
continue;
|
||
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.Height/2));
|
||
double dist = dirto.length();
|
||
if ( (dist > rad/2) && (t == target) ) continue;
|
||
candidates.Push(t);
|
||
}
|
||
qsort_candidates(candidates,0,candidates.Size()-1);
|
||
candidates.Resize(2);
|
||
for ( int i=0; i<candidates.Size(); i++ )
|
||
{
|
||
let t = candidates[i];
|
||
if ( !t ) continue;
|
||
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.Height/2));
|
||
double dist = dirto.length();
|
||
dirto /= dist;
|
||
int trad = int(max(t.radius,t.height));
|
||
if ( t && YnykronShot(master) )
|
||
YnykronShot(master).hitlist.Push(t);
|
||
// spawn blast that will propagate
|
||
let b = Spawn("YnykronDelayedImpact",t.pos);
|
||
Vector3 ofs = level.Vec3Diff(t.pos,level.Vec3Offset(pos,dirto*min(rad,dist)));
|
||
YnykronDelayedImpact(b).ofs = ofs;
|
||
b.tracer = t;
|
||
b.target = target;
|
||
b.master = master;
|
||
b.angle = atan2(dirto.y,dirto.x);
|
||
b.pitch = asin(-dirto.z);
|
||
b.special2 = special1+(Random[Ynykron](0,8)?1:0);
|
||
b.special1 = Random[Ynykron](-5,0)-min(special1/2,10);
|
||
b.args[0] = trad;
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).blastcount++;
|
||
}
|
||
}
|
||
override void OnDestroy()
|
||
{
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).blastcount--;
|
||
Super.OnDestroy();
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
MOXP ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 3 Bright A_SetTics(special2?1:3);
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronTracer : LineTracer
|
||
{
|
||
Actor ignore;
|
||
Array<Line> ShootThroughList;
|
||
Array<WaterHit> WaterHitList;
|
||
Array<HitListEntry> HitList;
|
||
|
||
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 )
|
||
{
|
||
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) && (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 YnykronInWallTracer : YnykronTracer
|
||
{
|
||
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 )
|
||
{
|
||
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;
|
||
}
|
||
}
|
||
|
||
Class YnykronBeam : Actor
|
||
{
|
||
bool nospread;
|
||
|
||
void TraceOut()
|
||
{
|
||
Vector3 x, y, z;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
let t = new("YnykronTracer");
|
||
t.ignore = target;
|
||
t.ShootThroughList.Clear();
|
||
t.WaterHitList.Clear();
|
||
t.HitList.Clear();
|
||
t.Trace(pos,cursector,x,128,TRACE_HitSky);
|
||
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
||
{
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
||
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
|
||
}
|
||
for ( int i=0; i<t.WaterHitList.Size(); i++ )
|
||
{
|
||
let b = Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
|
||
b.A_CheckTerrain();
|
||
}
|
||
for ( int i=0; i<t.Results.Distance; i+=32 )
|
||
{
|
||
if ( Random[Ynykron](0,5) ) 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.alpha *= .3;
|
||
b.vel += x*FRandom[Ynykron](-.2,.4);
|
||
}
|
||
for ( int i=0; i<t.HitList.Size(); i++ )
|
||
{
|
||
if ( YnykronShot(master) && (YnykronShot(master).hitlist.Find(t.HitList[i].hitactor) < YnykronShot(master).hitlist.Size()) )
|
||
continue;
|
||
int trad = int(max(t.hitlist[i].hitactor.radius,t.hitlist[i].hitactor.height));
|
||
if ( t.hitlist[i].hitactor && YnykronShot(master) )
|
||
YnykronShot(master).hitlist.Push(t.hitlist[i].hitactor);
|
||
// spawn blast that will propagate
|
||
let b = Spawn("YnykronImpact",t.hitlist[i].hitlocation);
|
||
b.tracer = t.HitList[i].hitactor;
|
||
b.target = target;
|
||
b.master = master;
|
||
b.angle = atan2(t.HitList[i].x.y,t.HitList[i].x.x);
|
||
b.pitch = asin(-t.HitList[i].x.z);
|
||
b.args[0] = trad;
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).blastcount++;
|
||
}
|
||
if ( t.Results.HitType == TRACE_HasHitSky )
|
||
{
|
||
// goodbye
|
||
nospread = true;
|
||
return;
|
||
}
|
||
if ( t.Results.HitType != TRACE_HitNone )
|
||
{
|
||
// hit something
|
||
Vector3 norm = -t.Results.HitVector;
|
||
if ( t.Results.HitType == TRACE_HitWall )
|
||
{
|
||
norm = (t.Results.HitLine.delta.y,-t.Results.HitLine.delta.x,0).unit();
|
||
if ( t.Results.Side ) norm *= -1;
|
||
t.Results.HitLine.RemoteActivate(tracer,t.Results.Side,SPAC_Impact,t.Results.HitPos);
|
||
}
|
||
else if ( t.Results.HitType == TRACE_HitFloor )
|
||
{
|
||
if ( t.Results.ffloor ) norm = -t.Results.ffloor.top.Normal;
|
||
else norm = t.Results.HitSector.floorplane.Normal;
|
||
}
|
||
else if ( t.Results.HitType == TRACE_HitCeiling )
|
||
{
|
||
if ( t.Results.ffloor ) norm = -t.Results.ffloor.bottom.Normal;
|
||
else norm = t.Results.HitSector.ceilingplane.Normal;
|
||
}
|
||
let b = Spawn("YnykronImpact",level.Vec3Offset(t.Results.HitPos,norm*16));
|
||
b.target = target;
|
||
b.master = master;
|
||
b.angle = atan2(norm.y,norm.x);
|
||
b.pitch = asin(-norm.z);
|
||
b.A_SprayDecal("YnykronBlast",-172);
|
||
if ( YnykronShot(master) ) YnykronShot(master).blastcount++;
|
||
if ( swwm_omnibust ) BusterWall.Bust(t.Results,int.max,target,t.Results.HitVector,t.Results.HitPos.z);
|
||
// find exit point
|
||
int maxdist = (25600-special1);
|
||
int i;
|
||
for ( i=128; i<maxdist; i++ )
|
||
{
|
||
Vector3 ofs = level.Vec3Offset(pos,x*i);
|
||
if ( !level.IsPointInLevel(ofs) ) continue;
|
||
// we got out, spawn a beam here (delayed)
|
||
let next = Spawn("DelayedWallBeam",ofs);
|
||
next.angle = atan2(x.y,x.x);
|
||
next.pitch = asin(-x.z);
|
||
next.roll = roll;
|
||
next.target = target;
|
||
next.master = master;
|
||
next.special1 = special1+i;
|
||
next.special2 = (i+Random[Ynykron](0,10))/50;
|
||
break;
|
||
}
|
||
// extra hit traces
|
||
let it = new("YnykronInWallTracer");
|
||
it.ignore = target;
|
||
it.ShootThroughList.Clear();
|
||
it.WaterHitList.Clear();
|
||
it.HitList.Clear();
|
||
it.Trace(t.Results.HitPos,level.PointInSector(t.Results.HitPos.xy),x,i,TRACE_HitSky);
|
||
for ( int i=0; i<it.ShootThroughList.Size(); i++ )
|
||
{
|
||
it.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
||
it.ShootThroughList[i].Activate(target,0,SPAC_Impact); // just in case
|
||
}
|
||
for ( int i=0; i<it.WaterHitList.Size(); i++ )
|
||
{
|
||
let b = Spawn("InvisibleSplasher",it.WaterHitList[i].hitpos);
|
||
b.A_CheckTerrain();
|
||
}
|
||
for ( int i=0; i<it.Results.Distance; i+=32 )
|
||
{
|
||
Vector3 ofs = level.Vec3Offset(pos,x*i);
|
||
if ( Random[Ynykron](0,5) || !level.IsPointInLevel(ofs) ) continue;
|
||
let b = Spawn("SWWMHalfSmoke",ofs);
|
||
b.Scale *= FRandom[Ynykron](1.6,2.8);
|
||
b.special1 = Random[Ynykron](3,5);
|
||
b.alpha *= .3;
|
||
b.vel += x*FRandom[Ynykron](-.2,.4);
|
||
}
|
||
for ( int i=0; i<it.HitList.Size(); i++ )
|
||
{
|
||
if ( YnykronShot(master) && (YnykronShot(master).hitlist.Find(it.HitList[i].hitactor) < YnykronShot(master).hitlist.Size()) )
|
||
continue;
|
||
int trad = int(max(it.hitlist[i].hitactor.radius,it.hitlist[i].hitactor.height));
|
||
if ( it.hitlist[i].hitactor && YnykronShot(master) )
|
||
YnykronShot(master).hitlist.Push(it.hitlist[i].hitactor);
|
||
// spawn blast that will propagate
|
||
let b = Spawn("YnykronImpact",it.hitlist[i].hitlocation);
|
||
b.tracer = it.hitlist[i].hitactor;
|
||
b.target = target;
|
||
b.master = master;
|
||
b.angle = atan2(it.HitList[i].x.y,it.HitList[i].x.x);
|
||
b.pitch = asin(-it.HitList[i].x.z);
|
||
b.args[0] = trad;
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).blastcount++;
|
||
}
|
||
nospread = true;
|
||
return;
|
||
}
|
||
if ( ((special1%256) < 128) && !Random[Ynykron](0,10) )
|
||
Spawn("YnykronBeamLight",level.Vec3Offset(pos,x*64));
|
||
if ( special1 >= 25600 )
|
||
{
|
||
// end of the line, dissipate
|
||
int numpt = Random[Ynykron](4,8);
|
||
for ( int i=0; i<numpt; i++ )
|
||
{
|
||
let b = Spawn("SWWMSmoke",level.Vec3Offset(pos,x*128));
|
||
b.Scale *= FRandom[Ynykron](.6,1.8);
|
||
b.special1 = Random[Ynykron](1,2);
|
||
b.A_SetRenderStyle(.3,STYLE_AddShaded);
|
||
b.SetShade(Color(255,255,255));
|
||
b.vel += x*FRandom[Ynykron](0.,5.);
|
||
}
|
||
nospread = true;
|
||
return;
|
||
}
|
||
}
|
||
void SpreadOut()
|
||
{
|
||
if ( nospread ) return;
|
||
Vector3 x, y, z;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
// propagate
|
||
let next = Spawn("YnykronBeam",level.Vec3Offset(pos,x*128));
|
||
next.angle = atan2(x.y,x.x);
|
||
next.pitch = asin(-x.z);
|
||
next.roll = roll;
|
||
next.target = target;
|
||
next.master = master;
|
||
next.special1 = special1+128;
|
||
next.SetStateLabel("TrailSpawn");
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).beamcount++;
|
||
special2 = Random[Ynykron](-1,0);
|
||
TraceOut();
|
||
}
|
||
override void Tick()
|
||
{
|
||
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;
|
||
}
|
||
}
|
||
override void OnDestroy()
|
||
{
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).beamcount--;
|
||
Super.OnDestroy();
|
||
}
|
||
Default
|
||
{
|
||
Obituary "$O_YNYKRON";
|
||
DamageType "Ynykron";
|
||
RenderStyle "Add";
|
||
Radius .1;
|
||
Height 0;
|
||
Alpha .4;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+NOCLIP;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+FORCEXYBILLBOARD;
|
||
+ROLLSPRITE;
|
||
+ROLLCENTER;
|
||
+NODAMAGETHRUST;
|
||
+FORCERADIUSDMG;
|
||
+FOILINVUL;
|
||
+EXTREMEDEATH;
|
||
+NOINTERACTION;
|
||
}
|
||
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 DelayedWallBeam : Actor
|
||
{
|
||
Default
|
||
{
|
||
Radius 0.1;
|
||
Height 0;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+NOINTERACTION;
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).beamcount++;
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
special2--;
|
||
if ( special2 > 0 ) return;
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).beamcount--;
|
||
let next = Spawn("YnykronBeam",pos);
|
||
next.angle = angle;
|
||
next.pitch = pitch;
|
||
next.roll = roll;
|
||
next.target = target;
|
||
next.master = master;
|
||
next.special1 = special1;
|
||
next.SetStateLabel("TrailSpawn");
|
||
// exit blast
|
||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||
let b = Spawn("YnykronImpact",level.Vec3Offset(pos,x*16));
|
||
b.target = target;
|
||
b.master = master;
|
||
b.angle = atan2(x.y,x.x);
|
||
b.pitch = asin(-x.z);
|
||
b.A_SprayDecal("YnykronBlast",-172);
|
||
if ( YnykronShot(master) )
|
||
YnykronShot(master).blastcount++;
|
||
// trace back to get the proper "exit surface" so we can trigger lines if needed
|
||
let at = new("AuxiliarySilverBulletTracer");
|
||
at.Trace(pos,CurSector,-x,2.,TRACE_NoSky);
|
||
if ( at.Results.HitType == TRACE_HitWall )
|
||
{
|
||
if ( at.Results.HitLine.sidedef[1] )
|
||
{
|
||
at.Results.HitLine.Activate(target,0,SPAC_PCross);
|
||
if ( at.Results.HitLine.special == GlassBreak ) // fuck glass
|
||
at.Results.HitLine.Activate(target,0,SPAC_Impact);
|
||
}
|
||
else at.Results.HitLine.Activate(tracer,at.Results.Side,SPAC_Impact);
|
||
}
|
||
if ( swwm_omnibust ) BusterWall.Bust(at.Results,int.max,target,-at.Results.HitVector,at.Results.HitPos.z);
|
||
Destroy();
|
||
}
|
||
}
|
||
|
||
Class YnykronRing : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
Radius 0.1;
|
||
Height 0;
|
||
Scale .6;
|
||
Alpha .05;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+FORCEXYBILLBOARD;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+ROLLSPRITE;
|
||
+ROLLCENTER;
|
||
+NOINTERACTION;
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
XRG4 ABCDEFGHIJKLMNOPQRSTUVWX 3 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
// non-model version, for impacts
|
||
Class YnykronImpactRing : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
Radius 0.1;
|
||
Height 0;
|
||
Scale 2.;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+FORCEXYBILLBOARD;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+NOINTERACTION;
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
A_SetScale(scale.x*(special2?1.06:1.03));
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
XRG4 ABCDEFGHIJKLMNOPQRSTUVWX 1 Bright A_SetTics(special2?1:2);
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronShot : Actor
|
||
{
|
||
Array<Actor> hitlist;
|
||
int beamcount;
|
||
int blastcount;
|
||
int lastimpact;
|
||
|
||
Default
|
||
{
|
||
Radius .1;
|
||
Height 0;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+NOINTERACTION;
|
||
}
|
||
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,255,255,255),30);
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
A_QuakeEx(6,6,6,150,0,65536,"",QF_RELATIVE|QF_SCALEDOWN,falloff:65536,rollIntensity:1.);
|
||
A_StartSound("ynykron/beam",CHAN_VOICE,CHANF_DEFAULT,1.,0.);
|
||
FlashPlayer(240,8000);
|
||
hitlist.Clear();
|
||
beamcount = 0;
|
||
blastcount = 0;
|
||
int rings = 1;
|
||
Vector3 x, y, z, dir;
|
||
double a, s;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
for ( double i=0; i<.04; i+=.006 )
|
||
{
|
||
for ( int j=0; j<360; j+=(360/rings) )
|
||
{
|
||
if ( i==0 ) dir = x; // central beam always precise
|
||
else
|
||
{
|
||
a = j+FRandom[Ynykron](-5.,5.);
|
||
s = i+FRandom[Ynykron](-.02,.04);
|
||
dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
||
}
|
||
let b = Spawn("YnykronBeam",pos);
|
||
b.target = target;
|
||
b.master = self;
|
||
b.angle = atan2(dir.y,dir.x);
|
||
b.pitch = asin(-dir.z);
|
||
b.roll = FRandom[Ynykron](0,360);
|
||
}
|
||
rings += 2;
|
||
}
|
||
Spawn("YnykronShotLight",level.Vec3Offset(pos,x*30));
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
// spawn rings
|
||
special1++;
|
||
if ( !(special1%10) && (special1 <= 30) )
|
||
{
|
||
Vector3 dir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||
for ( int i=0; i<16; i++ )
|
||
{
|
||
let r = Spawn("YnykronRing",level.Vec3Offset(pos,dir*(special1*16+i)));
|
||
r.scale *= special1/8.;
|
||
r.angle = angle;
|
||
r.pitch = pitch;
|
||
r.roll = FRandom[Ynykron](0,360);
|
||
}
|
||
}
|
||
// wait until we're no longer needed and all effects are over
|
||
if ( IsActorPlayingSound(CHAN_VOICE) || (beamcount > 0) || (blastcount > 0) )
|
||
{
|
||
special1 = min(special1,50);
|
||
return;
|
||
}
|
||
// we're done here, but wait for a while just in case
|
||
if ( special1 > 350 ) Destroy();
|
||
}
|
||
}
|
||
|
||
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 ) 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 : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
+NOGRAVITY;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+NOBLOCKMAP;
|
||
+NOINTERACTION;
|
||
+FORCEXYBILLBOARD;
|
||
Radius .1;
|
||
Height 0.;
|
||
Scale 1.5;
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
A_SetScale(scale.x+.5);
|
||
A_FadeOut(.2);
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
MHAL A -1 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronHalo : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
+NOGRAVITY;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+NOBLOCKMAP;
|
||
+NOINTERACTION;
|
||
+FORCEXYBILLBOARD;
|
||
Radius .1;
|
||
Height 0.;
|
||
Scale 1.5;
|
||
}
|
||
override void Tick()
|
||
{
|
||
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 : Actor
|
||
{
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
SetOrigin(level.Vec3Offset(pos,vel),true);
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
|
||
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,5.);
|
||
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";
|
||
Radius .1;
|
||
Height 0.;
|
||
Alpha 0.;
|
||
Scale 3.;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+NOINTERACTION;
|
||
+FORCEXYBILLBOARD;
|
||
+NOCLIP;
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
DUST ABCDEFGHIJKLMNOPQRST 1 A_Gravitate();
|
||
Wait;
|
||
}
|
||
}
|
||
|
||
Class YnykronVoidBeamTail : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
+NOGRAVITY;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+NOBLOCKMAP;
|
||
+NOINTERACTION;
|
||
+FORCEXYBILLBOARD;
|
||
Radius .1;
|
||
Height 0.;
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
A_FadeOut();
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
XZW1 # -1 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronVoidBeam : Actor
|
||
{
|
||
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";
|
||
Radius .1;
|
||
Height 0.;
|
||
Alpha 0.;
|
||
Stamina 3;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+NOINTERACTION;
|
||
+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.*(cos(baseang)*cos(basept),sin(baseang)*cos(basept),-sin(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 = swwm_CoordUtil.GetAxes(pitch,angle,0);
|
||
if ( !t ) t = new("YnykronTracer");
|
||
t.ShootThroughList.Clear();
|
||
t.WaterHitList.Clear();
|
||
t.HitList.Clear();
|
||
t.Trace(pos,cursector,x,16*scale.x,TRACE_HitSky);
|
||
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
||
{
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
||
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
|
||
}
|
||
for ( int i=0; i<t.HitList.Size(); i++ )
|
||
{
|
||
// yoink
|
||
SWWMUtility.DoKnockback(t.HitList[i].HitActor,-t.HitList[i].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*(FRandom[Ynykron](-1,1),FRandom[Ynykron](-1,1),FRandom[Ynykron](-1,1))).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;
|
||
}
|
||
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
|
||
States
|
||
{
|
||
Spawn:
|
||
XZW1 # 1 Bright A_UpdateBeam();
|
||
Wait;
|
||
}
|
||
}
|
||
|
||
Class YnykronLightningImpact : Actor
|
||
{
|
||
Default
|
||
{
|
||
Obituary "$O_YNYKRONALT";
|
||
DamageType "Electric";
|
||
Radius .1;
|
||
Height 0;
|
||
+FOILINVUL;
|
||
+FORCERADIUSDMG;
|
||
+NODAMAGETHRUST;
|
||
+NOINTERACTION;
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
Super.PostBeginPlay();
|
||
SWWMUtility.DoExplosion(self,400,120000,100,40);
|
||
A_QuakeEx(3,3,3,12,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.4);
|
||
A_SprayDecal("ShockMark",-172);
|
||
int numpt = Random[ExploS](8,16);
|
||
for ( int i=0; i<numpt; i++ )
|
||
{
|
||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](3,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 = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*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 = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
|
||
let s = Spawn("SWWMChip",pos);
|
||
s.vel = pvel;
|
||
}
|
||
Destroy();
|
||
}
|
||
}
|
||
|
||
Class YnykronLightningArc : Actor
|
||
{
|
||
Vector3 nextpos, nextdir;
|
||
Vector3 destpos;
|
||
|
||
action void A_Trace()
|
||
{
|
||
let t = new("CandyBeamTracer");
|
||
t.hitlist.Clear();
|
||
Vector3 x, y, z;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
t.ShootThroughList.Clear();
|
||
t.Trace(pos,CurSector,x,speed,TRACE_HitSky);
|
||
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
||
{
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
||
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
|
||
}
|
||
for ( int i=0; i<t.hitlist.Size(); i++ )
|
||
{
|
||
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,-t.hitlist[i].x,GetMissileDamage(0,0)*1000);
|
||
t.hitlist[i].hitactor.DamageMobj(self,target,GetMissileDamage(0,0),'Electric',DMG_THRUSTLESS);
|
||
}
|
||
Vector3 normal = -t.Results.HitVector, dir = t.Results.HitVector;
|
||
if ( t.Results.HitType != TRACE_HitNone )
|
||
{
|
||
if ( t.Results.HitType != TRACE_HasHitSky )
|
||
{
|
||
if ( t.Results.HitType == TRACE_HitWall )
|
||
{
|
||
normal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
|
||
if ( !t.Results.Side ) normal *= -1;
|
||
t.Results.HitLine.RemoteActivate(target,t.Results.Side,SPAC_Impact,t.Results.HitPos);
|
||
}
|
||
else if ( t.Results.HitType == TRACE_HitFloor )
|
||
{
|
||
if ( t.Results.ffloor ) normal = -t.Results.ffloor.top.Normal;
|
||
else normal = t.Results.HitSector.floorplane.Normal;
|
||
}
|
||
else if ( t.Results.HitType == TRACE_HitCeiling )
|
||
{
|
||
if ( t.Results.ffloor ) normal = -t.Results.ffloor.bottom.Normal;
|
||
else normal = t.Results.HitSector.ceilingplane.Normal;
|
||
}
|
||
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);
|
||
}
|
||
invoker.nextpos = t.Results.HitPos;
|
||
bAMBUSH = true;
|
||
return;
|
||
}
|
||
invoker.nextpos = level.Vec3Offset(pos,x*speed);
|
||
double a = FRandom[Ynykron](0,360), s = FRandom[Ynykron](0.,.8);
|
||
dir = (dir+cos(a)*y*s+sin(a)*z*s).unit();
|
||
if ( !special1 )
|
||
{
|
||
invoker.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 = (dir+cos(a)*y*s+sin(a)*z*s).unit();
|
||
FLineTraceData d;
|
||
LineTrace(atan2(tdir.y,tdir.x),10000,asin(-tdir.z),TRF_THRUACTORS,data:d);
|
||
strikepoints[i] = d.HitLocation;
|
||
if ( d.Distance < closest )
|
||
{
|
||
invoker.destpos = strikepoints[i];
|
||
closest = d.Distance;
|
||
}
|
||
}
|
||
}
|
||
Vector3 dirto = level.Vec3Diff(invoker.nextpos,invoker.destpos);
|
||
double dist = dirto.length();
|
||
if ( dist > 10 )
|
||
{
|
||
dirto /= dist;
|
||
dir = (dir+.8*dirto*(clamp(1.-(dist/1500.),0.,1.)**1.5)).unit();
|
||
}
|
||
invoker.nextdir = dir;
|
||
}
|
||
action void A_Spread( Sound arcsnd )
|
||
{
|
||
Vector3 tdir = level.Vec3Diff(pos,invoker.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] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
double a = FRandom[Ynykron](0,360), s = FRandom[Sparkster](0.,1.);
|
||
Vector3 sdir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
||
r.angle = atan2(sdir.y,sdir.x);
|
||
r.pitch = asin(-sdir.z);
|
||
r.target = target;
|
||
r.special1 = 1;
|
||
YnykronLightningArc(r).destpos = invoker.destpos;
|
||
r.ReactionTime += Random[Ynykron](-3,3);
|
||
}
|
||
}
|
||
if ( ((ReactionTime > 0) && (special1 > ReactionTime)) || bAMBUSH ) return;
|
||
let b = Spawn(GetClass(),invoker.nextpos);
|
||
b.angle = atan2(invoker.nextdir.y,invoker.nextdir.x);
|
||
b.pitch = asin(-invoker.nextdir.z);
|
||
b.target = target;
|
||
b.special1 = special1+1;
|
||
YnykronLightningArc(b).destpos = invoker.destpos;
|
||
b.SetState(b.FindState("Trailer"));
|
||
if ( arcsnd != "" ) A_StartSound(arcsnd,CHAN_WEAPON);
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
Super.PostBeginPlay();
|
||
frame = Random[Ynykron](0,11);
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
Default
|
||
{
|
||
Obituary "$O_YNYKRONALT";
|
||
RenderStyle "Add";
|
||
DamageFunction 1000;
|
||
Speed 128;
|
||
Radius .1;
|
||
Height 0;
|
||
Alpha 2.;
|
||
+NOGRAVITY;
|
||
+NOCLIP;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+FOILINVUL;
|
||
+NOINTERACTION;
|
||
}
|
||
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 : Actor
|
||
{
|
||
Vector3 gx, gy, gz;
|
||
double phase;
|
||
Vector3 dirto;
|
||
double frightening; // lightning flash
|
||
Color basecol;
|
||
double rollvel;
|
||
|
||
override void Tick()
|
||
{
|
||
prev = pos;
|
||
if ( isFrozen() ) return;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
|
||
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] = swwm_CoordUtil.GetAxes(FRandom[ExploS](-90,90),FRandom[ExploS](0,360),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 = (gx*cos(phase)+gy*sin(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";
|
||
Radius .1;
|
||
Height 0.;
|
||
Alpha 0.;
|
||
Scale 3.;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+NOINTERACTION;
|
||
+FORCEXYBILLBOARD;
|
||
+ROLLSPRITE;
|
||
+NOCLIP;
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
DUST ABCDEFGHIJKLMNOPQRST 1 A_Gravitate();
|
||
Wait;
|
||
}
|
||
}
|
||
|
||
Class YnykronLightningLight : PaletteLight
|
||
{
|
||
Default
|
||
{
|
||
Tag "WhiteExpl";
|
||
ReactionTime 12;
|
||
Args 0,0,0,900;
|
||
}
|
||
}
|
||
Class YnykronLightningLight2 : PaletteLight
|
||
{
|
||
Default
|
||
{
|
||
Tag "WhiteExpl";
|
||
ReactionTime 10;
|
||
Args 0,0,0,500;
|
||
}
|
||
}
|
||
|
||
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 : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
Radius 0.1;
|
||
Height 0;
|
||
Scale 4.;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+FORCEXYBILLBOARD;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+NOINTERACTION;
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
A_SetScale(scale.x*1.05);
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
XRG4 ABCDEFGHIJKLMNOPQRSTUVWX 2 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronVoidExplLight : PaletteLight
|
||
{
|
||
Default
|
||
{
|
||
Tag "Purple";
|
||
ReactionTime 60;
|
||
Args 0,0,0,1200;
|
||
}
|
||
}
|
||
|
||
Class YnykronVoidSparkle : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
Radius 0.1;
|
||
Height 0;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+DONTSPLASH;
|
||
+ROLLSPRITE;
|
||
+ROLLCENTER;
|
||
+FORCEXYBILLBOARD;
|
||
+NOINTERACTION;
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
Scale *= FRandom[ExploS](.75,1.5);
|
||
specialf1 = FRandom[ExploS](.95,.98);
|
||
specialf2 = FRandom[ExploS](.005,.015);
|
||
vel = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch))*FRandom[ExploS](1,16);
|
||
}
|
||
override void Tick()
|
||
{
|
||
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*(FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1));
|
||
vel = dir.unit()*magvel;
|
||
}
|
||
SetOrigin(level.Vec3Offset(pos,vel),true);
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
MOPF A -1 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
Class YnykronSingularityExplosionArm : Actor
|
||
{
|
||
Default
|
||
{
|
||
PROJECTILE;
|
||
+THRUACTORS;
|
||
+BOUNCEONWALLS;
|
||
+BOUNCEONFLOORS;
|
||
+BOUNCEONCEILINGS;
|
||
+NODAMAGETHRUST;
|
||
+FORCERADIUSDMG;
|
||
-NOGRAVITY;
|
||
Gravity 0.35;
|
||
BounceFactor 1.0;
|
||
Radius 4;
|
||
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 = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[ExploS](20.,40.);
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
TNT1 A 1
|
||
{
|
||
A_CountDown();
|
||
if ( !(reactiontime%2) )
|
||
{
|
||
Spawn("YnykronSingularityExplosionTrail",pos);
|
||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*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 : Actor
|
||
{
|
||
Default
|
||
{
|
||
RenderStyle "Add";
|
||
+NOBLOCKMAP;
|
||
+NOGRAVITY;
|
||
+FORCEXYBILLBOARD;
|
||
+NOTELEPORT;
|
||
+NOINTERACTION;
|
||
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);
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
XEX4 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 1 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronSingularityExplosion : Actor
|
||
{
|
||
Default
|
||
{
|
||
Obituary "$O_YNYKRONALT";
|
||
DamageType "YnykronAlt";
|
||
RenderStyle "Add";
|
||
Scale 5.;
|
||
Radius 0.1;
|
||
Height 0;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+NODAMAGETHRUST;
|
||
+FORCERADIUSDMG;
|
||
+FORCEXYBILLBOARD;
|
||
+NOTELEPORT;
|
||
+FOILINVUL;
|
||
+NOINTERACTION;
|
||
}
|
||
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](8,12);
|
||
for ( int i=0; i<numpt; i++ )
|
||
{
|
||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*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](5,10);
|
||
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*(cos(ang)*cos(pt),sin(ang)*cos(pt),sin(-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;
|
||
}
|
||
}
|
||
override void Tick()
|
||
{
|
||
if ( isFrozen() ) return;
|
||
scale *= 1.01;
|
||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||
if ( tics > 0 ) tics--;
|
||
while ( !tics )
|
||
{
|
||
if ( !SetState(CurState.NextState) )
|
||
return;
|
||
}
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
XEX4 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 2 Bright;
|
||
Stop;
|
||
}
|
||
}
|
||
|
||
Class YnykronSingularity : Actor
|
||
{
|
||
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);
|
||
}
|
||
|
||
// lightweight tick, we don't need anything else other than states
|
||
override void Tick()
|
||
{
|
||
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;
|
||
if ( d.HitType == TRACE_HitWall )
|
||
{
|
||
norm = (d.HitLine.delta.y,-d.HitLine.delta.x,0).unit();
|
||
if ( d.LineSide ) norm *= -1;
|
||
}
|
||
else if ( d.HitType == TRACE_HitFloor )
|
||
{
|
||
if ( d.Hit3DFloor ) norm = -d.Hit3DFloor.top.Normal;
|
||
else norm = d.HitSector.floorplane.Normal;
|
||
}
|
||
else if ( d.HitType == TRACE_HitCeiling )
|
||
{
|
||
if ( d.Hit3DFloor ) norm = -d.Hit3DFloor.bottom.Normal;
|
||
else norm = d.HitSector.ceilingplane.Normal;
|
||
}
|
||
else norm = (0,0,0);
|
||
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 )
|
||
{
|
||
for ( int i=0; i<hnd.SuckableActors.Size(); i++ )
|
||
{
|
||
let a = hnd.SuckableActors[i];
|
||
if ( !a )
|
||
{
|
||
hnd.SuckableActors.Delete(i--);
|
||
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.player && (a.player.cheats&CF_NOCLIP2) ) // for safety
|
||
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;
|
||
}
|
||
// delet missile
|
||
if ( a.bMISSILE )
|
||
{
|
||
specialf2 += 10.;
|
||
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 ) PlayerGone.FeckOff(a);
|
||
// poof away, always
|
||
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 ( !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*TICRATE);
|
||
}
|
||
}
|
||
}
|
||
// 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 -= (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*.02*mag;
|
||
}
|
||
double magvel = vel.length();
|
||
Vector3 dir;
|
||
if ( magvel <= double.epsilon ) dir = (cos(angle)*cos(pitch),sin(angle)+cos(pitch),-sin(pitch));
|
||
else dir = vel/magvel;
|
||
// wander
|
||
dir = (dir+(FRandom[Ynykron](-1,1),FRandom[Ynykron](-1,1),FRandom[Ynykron](-1,1))*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_AlertMonsters();
|
||
SWWMUtility.DoExplosion(self,int.max,500000,800,400);
|
||
A_QuakeEx(8,8,8,100,0,65536,"",QF_RELATIVE|QF_SCALEDOWN,falloff:65536,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](40,60);
|
||
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) ) 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(special1/6,2,8);
|
||
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 = -d.HitDir;
|
||
if ( d.HitType == TRACE_HitFloor )
|
||
{
|
||
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
||
else hitnormal = d.HitSector.floorplane.Normal;
|
||
}
|
||
else if ( d.HitType == TRACE_HitCeiling )
|
||
{
|
||
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
||
else hitnormal = d.HitSector.ceilingplane.Normal;
|
||
}
|
||
else if ( d.HitType == TRACE_HitWall )
|
||
{
|
||
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
||
if ( !d.LineSide ) hitnormal *= -1;
|
||
}
|
||
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";
|
||
+NOGRAVITY;
|
||
+MISSILE;
|
||
+DROPOFF;
|
||
+DONTSPLASH;
|
||
+NOTELEPORT;
|
||
+NOINTERACTION;
|
||
Radius .1;
|
||
Height 0.;
|
||
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 : Actor
|
||
{
|
||
bool nospread;
|
||
|
||
void TraceOut()
|
||
{
|
||
Vector3 x, y, z;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
let t = new("YnykronAltTracer");
|
||
t.ignore = target;
|
||
t.ShootThroughList.Clear();
|
||
t.WaterHitList.Clear();
|
||
t.Trace(pos,cursector,x,64,TRACE_HitSky);
|
||
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
||
{
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
||
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
|
||
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
|
||
}
|
||
for ( int i=0; i<t.WaterHitList.Size(); i++ )
|
||
{
|
||
let b = Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
|
||
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;
|
||
return;
|
||
}
|
||
if ( t.Results.HitType != TRACE_HitNone )
|
||
{
|
||
// hit something
|
||
Vector3 norm = -t.Results.HitVector;
|
||
if ( t.Results.HitType == TRACE_HitWall )
|
||
{
|
||
norm = (t.Results.HitLine.delta.y,-t.Results.HitLine.delta.x,0).unit();
|
||
if ( t.Results.Side ) norm *= -1;
|
||
t.Results.HitLine.RemoteActivate(tracer,t.Results.Side,SPAC_Impact,t.Results.HitPos);
|
||
}
|
||
else if ( t.Results.HitType == TRACE_HitFloor )
|
||
{
|
||
if ( t.Results.ffloor ) norm = -t.Results.ffloor.top.Normal;
|
||
else norm = t.Results.HitSector.floorplane.Normal;
|
||
}
|
||
else if ( t.Results.HitType == TRACE_HitCeiling )
|
||
{
|
||
if ( t.Results.ffloor ) norm = -t.Results.ffloor.bottom.Normal;
|
||
else norm = t.Results.HitSector.ceilingplane.Normal;
|
||
}
|
||
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;
|
||
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*64));
|
||
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, y, z;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
// propagate
|
||
let next = Spawn("YnykronAltBeam",level.Vec3Offset(pos,x*64));
|
||
next.angle = atan2(x.y,x.x);
|
||
next.pitch = asin(-x.z);
|
||
next.roll = roll;
|
||
next.target = target;
|
||
next.master = master;
|
||
next.special1 = special1+64;
|
||
next.SetStateLabel("TrailSpawn");
|
||
}
|
||
override void PostBeginPlay()
|
||
{
|
||
special2 = Random[Ynykron](-1,0);
|
||
TraceOut();
|
||
}
|
||
override void Tick()
|
||
{
|
||
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";
|
||
Radius .1;
|
||
Height 0;
|
||
Alpha .4;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+NOCLIP;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+FORCEXYBILLBOARD;
|
||
+ROLLSPRITE;
|
||
+ROLLCENTER;
|
||
+NOINTERACTION;
|
||
}
|
||
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 : Actor
|
||
{
|
||
Default
|
||
{
|
||
Radius .1;
|
||
Height 0;
|
||
+NOGRAVITY;
|
||
+NOBLOCKMAP;
|
||
+NOTELEPORT;
|
||
+DONTSPLASH;
|
||
+NOINTERACTION;
|
||
}
|
||
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,65536,"",QF_RELATIVE|QF_SCALEDOWN,falloff:65536,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 ( isFrozen() || IsActorPlayingSound(CHAN_VOICE) ) return;
|
||
Destroy();
|
||
}
|
||
}
|
||
|
||
Class Ynykron : SWWMWeapon
|
||
{
|
||
transient ui TextureID WeaponBox, ChargeBar[2], BoxSide[2];
|
||
transient ui Font TewiFont;
|
||
transient ui DynamicValueInterpolator ChargeInter;
|
||
|
||
enum EChargeState
|
||
{
|
||
CS_IDLE,
|
||
CS_CHARGING,
|
||
CS_READY,
|
||
CS_DISCHARGING
|
||
};
|
||
|
||
int chargestate;
|
||
double chargelevel;
|
||
bool inverted, invertreload;
|
||
|
||
double ventalpha, ventfade;
|
||
int ventcooldown;
|
||
|
||
int clipcount;
|
||
|
||
Property ClipCount : clipcount;
|
||
|
||
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
|
||
{
|
||
if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/YnykronDisplay.png",TexMan.Type_Any);
|
||
if ( !ChargeBar[0] ) ChargeBar[0] = TexMan.CheckForTexture("graphics/HUD/YnykronBarA.png",TexMan.Type_Any);
|
||
if ( !ChargeBar[1] ) ChargeBar[1] = TexMan.CheckForTexture("graphics/HUD/YnykronBarB.png",TexMan.Type_Any);
|
||
if ( !BoxSide[0] ) BoxSide[0] = TexMan.CheckForTexture("graphics/HUD/YnykronSideA.png",TexMan.Type_Any);
|
||
if ( !BoxSide[1] ) BoxSide[1] = TexMan.CheckForTexture("graphics/HUD/YnykronSideB.png",TexMan.Type_Any);
|
||
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
|
||
Screen.DrawTexture(WeaponBox,false,bx-33,by-44,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||
int chg = clamp(ChargeInter?ChargeInter.GetValue():int(chargelevel*10),0,400);
|
||
int ct = int(((by-2)-chg/10.)*hs.y);
|
||
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-30,by-16,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||
Screen.DrawTexture(BoxSide[inverted],false,bx-23,by-31,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,clipcount?Color(0,0,0,0):Color(128,0,0,0));
|
||
Screen.DrawTexture(ChargeBar[inverted],false,bx-6,by-42,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(chargestate==CS_READY)?Color(int(clamp(sin((level.maptime+TicFrac)*8)*40+24,0.,64.)),255,255,255):Color(0,0,0,0),DTA_ClipTop,ct);
|
||
}
|
||
override void HudTick()
|
||
{
|
||
Super.HudTick();
|
||
if ( !ChargeInter ) ChargeInter = DynamicValueInterpolator.Create(int(chargelevel*10),.5,1,400);
|
||
ChargeInter.Update(int(chargelevel*10));
|
||
}
|
||
|
||
override bool ReportHUDAmmo()
|
||
{
|
||
if ( clipcount > 0 ) return true;
|
||
return Super.ReportHUDAmmo();
|
||
}
|
||
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
|
||
{
|
||
if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true;
|
||
if ( (fireMode == PrimaryFire) || (fireMode == AltFire) )
|
||
return ((clipcount > 0) || (Ammo1.Amount > 0));
|
||
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
|
||
}
|
||
|
||
override void Travelled()
|
||
{
|
||
Super.Travelled();
|
||
if ( Owner.player && (Owner.player.Readyweapon == self) )
|
||
{
|
||
Owner.A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.);
|
||
if ( chargestate > CS_IDLE )
|
||
Owner.A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,(.025*chargelevel)**3.,2.);
|
||
}
|
||
}
|
||
|
||
override Vector3 GetTraceOffset()
|
||
{
|
||
return (15.,4.,-1.);
|
||
}
|
||
|
||
action void A_YnykronFire()
|
||
{
|
||
A_SWWMFlash();
|
||
A_StopSound(CHAN_WEAPONEXTRA2);
|
||
A_StartSound(invoker.inverted?"ynykron/altfire":"ynykron/fire",CHAN_WEAPON,CHANF_OVERLAP,1.,.2);
|
||
if ( !swwm_ynykronalert )
|
||
{
|
||
// global alert
|
||
int ns = level.Sectors.Size();
|
||
for ( int i=0; i<ns; i++ )
|
||
{
|
||
Sector s = level.Sectors[i];
|
||
for ( Actor a=s.thinglist; a; a=a.snext )
|
||
a.LastHeard = self;
|
||
}
|
||
}
|
||
else A_AlertMonsters(); // full range alert
|
||
A_QuakeEx(9,9,9,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:2.5);
|
||
A_ZoomFactor(.7,ZOOM_INSTANT);
|
||
A_ZoomFactor(1.);
|
||
A_PlayerFire();
|
||
SWWMHandler.DoFlash(self,Color(120,255,255,255),30);
|
||
A_Overlay(PSP_WEAPON+1,"FireSmoke");
|
||
A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true);
|
||
A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add);
|
||
A_OverlayAlpha(PSP_WEAPON+1,0.);
|
||
invoker.chargestate = CS_IDLE;
|
||
invoker.clipcount = 0;
|
||
Vector3 x, y, z, origin;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*15+y*4-z);
|
||
Actor s;
|
||
if ( invoker.inverted ) s = Spawn("YnykronAltShot",origin);
|
||
else s = Spawn("YnykronShot",origin);
|
||
s.target = self;
|
||
s.angle = angle;
|
||
s.pitch = BulletSlope();
|
||
invoker.specialf1 = 1.;
|
||
A_Overlay(PSP_WEAPON+3,"FireBlast");
|
||
}
|
||
|
||
action void A_Backblast()
|
||
{
|
||
Vector3 x, y, z, origin;
|
||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),-x*15+y*4-z);
|
||
int numpt = Random[Ynykron](10,20);
|
||
for ( int i=0; i<numpt; i++ )
|
||
{
|
||
let s = Spawn("SWWMSmoke",origin);
|
||
s.scale *= 3.;
|
||
s.alpha *= .4*invoker.specialf1;
|
||
s.special1 = Random[Ynykron](2,8);
|
||
s.vel += x*FRandom[Ynykron](-40.,-4.)*invoker.specialf1+y*FRandom[Ynykron](-1.,1.)+z*FRandom[Ynykron](-1.,1.);
|
||
}
|
||
invoker.specialf1 -= .2;
|
||
}
|
||
|
||
override void DoEffect()
|
||
{
|
||
Super.DoEffect();
|
||
if ( !Owner || !Owner.player ) return;
|
||
if ( chargestate == CS_IDLE ) chargelevel = 0.;
|
||
else if ( chargestate == CS_CHARGING )
|
||
{
|
||
chargelevel = min(chargelevel*0.997+.2,40.);
|
||
if ( chargelevel >= 40. )
|
||
{
|
||
if ( Owner.player == players[consoleplayer] )
|
||
Console.Printf(StringTable.Localize("$SWWM_YNYKRONREADY"));
|
||
chargestate = CS_READY;
|
||
}
|
||
if ( Owner.player.ReadyWeapon == self )
|
||
Owner.A_SoundVolume(CHAN_WEAPONEXTRA2,(.025*chargelevel)**3.);
|
||
}
|
||
if ( Owner.player.ReadyWeapon != self ) return;
|
||
let pspm = Owner.player.FindPSprite(PSP_WEAPON);
|
||
if ( pspm )
|
||
{
|
||
double shiver = (chargelevel*.025)**2.;
|
||
pspm.x = FRandom[Shivers](-1.,1.)*shiver;
|
||
pspm.y = 32+FRandom[Shivers](-1.,1.)*shiver;
|
||
}
|
||
let psp = Owner.player.FindPSprite(PSP_WEAPON+1);
|
||
if ( !psp ) return;
|
||
ventalpha = clamp(ventalpha+ventfade,0.,1.);
|
||
psp.alpha = ventalpha;
|
||
if ( chargestate == CS_DISCHARGING )
|
||
{
|
||
chargelevel = max(chargelevel*1.005-.24,0.);
|
||
if ( chargelevel <= 0. ) chargestate = CS_IDLE;
|
||
if ( Owner.player.ReadyWeapon == self )
|
||
Owner.A_SoundVolume(CHAN_WEAPONEXTRA2,(.025*chargelevel)**3.);
|
||
}
|
||
}
|
||
|
||
Default
|
||
{
|
||
Tag "$T_YNYKRON";
|
||
Inventory.PickupMessage "$T_YNYKRON";
|
||
Obituary "$O_YNYKRON";
|
||
Inventory.Icon "graphics/HUD/Icons/W_Ynykron.png";
|
||
Weapon.SlotNumber 0;
|
||
Weapon.SelectionOrder 9000;
|
||
Weapon.UpSound "ynykron/select";
|
||
Stamina 5000000;
|
||
Weapon.AmmoType1 "YnykronAmmo";
|
||
Weapon.AmmoGive1 1;
|
||
SWWMWeapon.DropAmmoType "YnykronAmmo";
|
||
+SWWMWEAPON.NOFIRSTGIVE;
|
||
Ynykron.ClipCount 1;
|
||
+WEAPON.BFG;
|
||
+WEAPON.EXPLOSIVE;
|
||
Radius 32;
|
||
Height 36;
|
||
}
|
||
States
|
||
{
|
||
Spawn:
|
||
XZW1 A -1;
|
||
Stop;
|
||
Select:
|
||
XZW2 L 2
|
||
{
|
||
invoker.ventcooldown = Random[Ynykron](6,15);
|
||
A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.);
|
||
if ( invoker.chargelevel > 0. )
|
||
A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,(.025*invoker.chargelevel)**3.,2.);
|
||
A_FullRaise();
|
||
}
|
||
XZW2 MNOPQRSTUVWXYZ 2;
|
||
XZW3 A 2;
|
||
Goto Ready;
|
||
Ready:
|
||
XZW2 A 1
|
||
{
|
||
if ( invoker.chargestate == CS_DISCHARGING )
|
||
return ResolveState("Discharge");
|
||
int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1;
|
||
if ( (invoker.chargestate > CS_IDLE) || ((invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true))) )
|
||
flg |= WRF_ALLOWRELOAD;
|
||
if ( (invoker.chargestate > CS_IDLE) || ((invoker.clipcount <= 0) && (invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true)) )
|
||
flg |= WRF_NOSECONDARY;
|
||
if ( invoker.chargestate == CS_CHARGING )
|
||
flg |= WRF_NOPRIMARY;
|
||
A_WeaponReady(flg);
|
||
if ( invoker.chargelevel >= 20. )
|
||
{
|
||
invoker.ventcooldown--;
|
||
if ( invoker.ventcooldown <= 0 )
|
||
return ResolveState("ReadyVent");
|
||
}
|
||
if ( player.cmd.buttons&BT_ATTACK )
|
||
invoker.CheckAmmo(EitherFire,true);
|
||
return ResolveState(null);
|
||
}
|
||
Wait;
|
||
ReadyVent:
|
||
XZW2 A 1
|
||
{
|
||
invoker.ventcooldown = Random[Ynykron](10,15)*5+2*(40-int(invoker.chargelevel));
|
||
A_Overlay(PSP_WEAPON+1,"ReadyVentSmoke");
|
||
A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true);
|
||
A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add);
|
||
A_OverlayAlpha(PSP_WEAPON+1,0.);
|
||
A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP);
|
||
}
|
||
XZWA ABCDEF 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
XZWA G 2
|
||
{
|
||
A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
A_StartSound("ynykron/ventclose",CHAN_WEAPON,CHANF_OVERLAP);
|
||
}
|
||
XZWA HI 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
Goto Ready;
|
||
ReadyVentSmoke:
|
||
XZWB D 1
|
||
{
|
||
invoker.ventfade = .3;
|
||
A_StartSound("ynykron/puff",CHAN_WEAPON,CHANF_OVERLAP);
|
||
}
|
||
XZWB EF 2;
|
||
XZWB G 2
|
||
{
|
||
invoker.ventfade = -.1;
|
||
}
|
||
XZWB HIJKLM 2;
|
||
Stop;
|
||
Fire:
|
||
XZW2 A 1
|
||
{
|
||
if ( (invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) )
|
||
return ResolveState("Reload");
|
||
if ( invoker.chargestate == CS_IDLE )
|
||
return ResolveState("Charge");
|
||
A_YnykronFire();
|
||
A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP);
|
||
return ResolveState(null);
|
||
}
|
||
XZW3 JK 2;
|
||
XZW3 LMNOP 3;
|
||
XZW3 Q 3 A_StartSound("ynykron/ventclose",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW3 R 3;
|
||
Goto Ready;
|
||
FireBlast:
|
||
TNT1 AAAAA 1 A_Backblast();
|
||
Stop;
|
||
FireSmoke:
|
||
XZWA J 1
|
||
{
|
||
invoker.ventfade = .3;
|
||
A_StartSound("ynykron/puff",CHAN_WEAPON,CHANF_OVERLAP);
|
||
}
|
||
XZWA KLMNO 2;
|
||
XZWA P 3
|
||
{
|
||
invoker.ventfade = -.1;
|
||
}
|
||
XZWA QRS 3;
|
||
Stop;
|
||
AltFire:
|
||
XZW2 A 2
|
||
{
|
||
if ( (invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) )
|
||
{
|
||
invoker.invertreload = true;
|
||
return ResolveState("Reload");
|
||
}
|
||
A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
||
return A_JumpIf(invoker.inverted,"TakeInverted");
|
||
}
|
||
TakeNormal:
|
||
XZW3 STU 2;
|
||
XZW3 V 2
|
||
{
|
||
A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP);
|
||
A_PlayerReload();
|
||
}
|
||
XZW3 WXYZ 2;
|
||
XZW4 ABCDEFGHIJKLMNO 2;
|
||
XZW4 P 0
|
||
{
|
||
invoker.inverted = true;
|
||
}
|
||
Goto PutInverted;
|
||
TakeInverted:
|
||
XZW5 PQRS 2;
|
||
XZW5 T 2
|
||
{
|
||
A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP);
|
||
A_PlayerReload();
|
||
}
|
||
XZW5 UVWXYZ 2;
|
||
XZW6 ABCDEFGHIJKLM 2;
|
||
XZW6 N 0
|
||
{
|
||
invoker.inverted = false;
|
||
}
|
||
Goto PutNormal;
|
||
PutNormal:
|
||
XZW4 PQRS 2;
|
||
XZW4 T 2 A_StartSound("ynykron/magin",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW4 UVWXYZ 2;
|
||
XZW5 A 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW5 BCDEFGHIJK 2;
|
||
XZW5 L 4;
|
||
Goto Ready;
|
||
PutInverted:
|
||
XZW6 NOPQ 2;
|
||
XZW6 R 2 A_StartSound("ynykron/magin",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW6 STUVWXY 2;
|
||
XZW6 Z 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW7 ABCDEFGHIJ 2;
|
||
XZW7 K 4;
|
||
XZW5 P 0;
|
||
Goto Ready;
|
||
Discharge:
|
||
XZW2 A 2;
|
||
XZW7 NO 2;
|
||
XZW7 P 2 A_StartSound("ynykron/latch",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW7 Q 2
|
||
{
|
||
invoker.chargestate = CS_DISCHARGING;
|
||
A_Overlay(PSP_WEAPON+1,"DischargeSmoke");
|
||
A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true);
|
||
A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add);
|
||
A_OverlayAlpha(PSP_WEAPON+1,0.);
|
||
A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP);
|
||
}
|
||
XZW7 RSTUV 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
XZW7 W 2
|
||
{
|
||
A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
return A_JumpIf(invoker.chargestate==CS_IDLE,1);
|
||
}
|
||
Wait;
|
||
XZW7 W 3 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
XZW7 X 2
|
||
{
|
||
A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
A_StartSound("ynykron/ventclose",CHAN_WEAPON,CHANF_OVERLAP);
|
||
}
|
||
XZW7 YZ 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
|
||
Goto Ready;
|
||
DischargeSmoke:
|
||
XZWA T 2
|
||
{
|
||
invoker.ventfade = .3;
|
||
A_StartSound("ynykron/puffing",CHAN_WEAPONEXTRA3,CHANF_LOOP);
|
||
}
|
||
XZWA UVWXY 2;
|
||
XZWA Z 2 A_JumpIf(invoker.chargestate==CS_IDLE,1);
|
||
Wait;
|
||
XZWA Z 3
|
||
{
|
||
invoker.ventfade = -.1;
|
||
A_SoundVolume(CHAN_WEAPONEXTRA3,.8);
|
||
A_StartSound("ynykron/puffend",CHAN_WEAPON,CHANF_OVERLAP);
|
||
}
|
||
XZWB A 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.6);
|
||
XZWB B 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.4);
|
||
XZWB C 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.2);
|
||
XZWB D 2 A_StopSound(CHAN_WEAPONEXTRA3);
|
||
Stop;
|
||
Charge:
|
||
XZW2 A 2;
|
||
XZW3 BC 2;
|
||
XZW3 D 2 A_StartSound("ynykron/latch",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW3 EFGHI 2;
|
||
XZW2 A 0
|
||
{
|
||
invoker.chargestate = CS_CHARGING;
|
||
invoker.ventcooldown = Random[Ynykron](6,15);
|
||
A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,.01,2.);
|
||
}
|
||
Goto Ready;
|
||
Reload:
|
||
XZW2 A 2
|
||
{
|
||
if ( invoker.chargestate>CS_IDLE ) return ResolveState("Discharge");
|
||
if ( invoker.inverted ) return ResolveState("UnloadInverted");
|
||
A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
||
return ResolveState(null);
|
||
}
|
||
UnloadNormal:
|
||
XZW3 STU 2;
|
||
XZW3 V 2 A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW3 WXYZ 2;
|
||
XZW4 ABCDEFGHIJKLMNO 2;
|
||
XZW4 P 0
|
||
{
|
||
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
|
||
invoker.Ammo1.Amount = max(invoker.Ammo1.Amount-1,0);
|
||
invoker.clipcount = 1;
|
||
invoker.inverted = false;
|
||
// no mag is dropped, depleted crystals are hazardous and should be disposed of properly
|
||
if ( invoker.invertreload )
|
||
{
|
||
invoker.invertreload = false;
|
||
invoker.inverted = true;
|
||
return ResolveState("PutInverted");
|
||
}
|
||
return ResolveState(null);
|
||
}
|
||
Goto PutNormal;
|
||
UnloadInverted:
|
||
XZW5 PQRS 2;
|
||
XZW5 T 2 A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW5 UVWXYZ 2;
|
||
XZW6 ABCDEFGHIJKLM 2;
|
||
XZW6 N 0
|
||
{
|
||
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
|
||
invoker.Ammo1.Amount = max(invoker.Ammo1.Amount-1,0);
|
||
invoker.clipcount = 1;
|
||
invoker.inverted = false;
|
||
// no mag is dropped, depleted crystals are hazardous and should be disposed of properly
|
||
if ( invoker.invertreload )
|
||
{
|
||
invoker.invertreload = false;
|
||
invoker.inverted = true;
|
||
return ResolveState("PutInverted");
|
||
}
|
||
return ResolveState(null);
|
||
}
|
||
Goto PutNormal;
|
||
Zoom:
|
||
XZW2 A 2
|
||
{
|
||
invoker.ventcooldown = Random[Ynykron](6,15);
|
||
A_StartSound("ynykron/checkout",CHAN_WEAPON,CHANF_OVERLAP);
|
||
A_PlayerCheckGun();
|
||
}
|
||
XZW8 ABCDEFGHIJKLMNOPQRSTUVW 2;
|
||
XZW8 X 4; // smoothen more
|
||
Goto Ready;
|
||
User1:
|
||
XZW2 A 2
|
||
{
|
||
invoker.ventcooldown = Random[Ynykron](6,15);
|
||
A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
|
||
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
|
||
A_PlayerMelee();
|
||
}
|
||
XZW8 YZ 2;
|
||
XZW9 AB 2;
|
||
XZW9 C 1 A_Parry(9);
|
||
XZW9 DE 1;
|
||
XZW9 F 1 A_Melee(100,"demolitionist/whitl",1.5);
|
||
XZW9 GHIJK 1;
|
||
XZW9 LMNO 2;
|
||
XZW9 P 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW9 QRSTUVWXYZ 2;
|
||
Goto Ready;
|
||
Deselect:
|
||
XZW2 A 2 A_StartSound("ynykron/deselect",CHAN_WEAPON,CHANF_OVERLAP);
|
||
XZW2 BCDEFGHIJK 2;
|
||
XZW2 L -1
|
||
{
|
||
A_StopSound(CHAN_WEAPONEXTRA);
|
||
A_StopSound(CHAN_WEAPONEXTRA2);
|
||
A_FullLower();
|
||
}
|
||
Stop;
|
||
Flash:
|
||
XZWZ A 2 Bright
|
||
{
|
||
let l = Spawn("SWWMWeaponLight",pos);
|
||
l.args[0] = 255;
|
||
l.args[1] = 255;
|
||
l.args[2] = 255;
|
||
l.args[3] = 500;
|
||
l.target = self;
|
||
}
|
||
Stop;
|
||
}
|
||
}
|