// basic effects // imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop Class PaletteLight : PointLight { Color pal[256]; bool IsLooping; int InitialReactionTime; Default { Tag "Explosion"; Args 0,0,0,80; ReactionTime 15; } private void UpdateLight() { int index = clamp(255-((255*ReactionTime)/InitialReactionTime),0,255); args[LIGHT_RED] = pal[index].r; args[LIGHT_GREEN] = pal[index].g; args[LIGHT_BLUE] = pal[index].b; } override void PostBeginPlay() { Super.PostBeginPlay(); String palname = GetTag(); int sep = palname.IndexOf(","); int palnum = 0; if ( sep != -1 ) { String palnumstr = palname.Mid(sep+1); palnum = palnumstr.ToInt()*768; palname.Truncate(sep); } int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",palname)); String paldat = Wads.ReadLump(lump); for ( int i=0; i<256; i++ ) { pal[i].r = paldat.ByteAt(palnum++); pal[i].g = paldat.ByteAt(palnum++); pal[i].b = paldat.ByteAt(palnum++); } if ( ReactionTime < 0 ) { ReactionTime = abs(ReactionTime)-1; IsLooping = true; } InitialReactionTime = ReactionTime; UpdateLight(); } override void Tick() { Super.Tick(); if ( isFrozen() || (freezetics > 0) ) return; ReactionTime--; if ( ReactionTime < 0 ) { if ( !IsLooping ) { Destroy(); return; } else ReactionTime = abs(InitialReactionTime); } if ( target ) SetOrigin(target.pos,true); UpdateLight(); } } // base visual thinker subclasses Class SWWMStaticSprite : VisualThinker abstract { bool bCheckWater; // checks water level, calls EnteredWater and LeftWater virtuals (perf intensive) bool lastwater; // internal, water state from previous tic bool firstwater; // internal, water state has been checked at least once bool bWallStop; // stops moving if the next tic takes us out of bounds (perf intensive) bool bWallKill; // destroy self if out of bounds (perf intensive) double speed; // multiply any initial thrust by this (used by subclasses, mainly) protected bool CheckWater() { if ( !cursector ) return false; if ( cursector.moreflags&Sector.SECMF_UNDERWATER ) { // directly underwater return true; } let hsec = cursector.GetHeightSec(); if ( hsec ) { // check for height transfer double fh = hsec.floorplane.ZAtPoint(pos.xy); if ( hsec.moreflags&Sector.SECMF_UNDERWATERMASK ) { if ( fh-pos.z > 0 ) return true; double ch = hsec.ceilingplane.ZAtPoint(pos.xy); if ( !(hsec.moreflags&Sector.SECMF_FAKEFLOORONLY) && (pos.z > ch) ) return true; } } else { // check for swimmable 3d floors for ( int i=0; i= fh) && (pos.z <= ch) ) return true; } } return false; } virtual void EnteredWater() {} virtual void LeftWater() {} virtual void OnTick() {} override void Tick() { Super.Tick(); if ( IsFrozen() ) return; if ( bCheckWater ) { bool curwater = CheckWater(); if ( curwater && (!lastwater || !firstwater) ) EnteredWater(); else if ( !curwater && (lastwater || !firstwater) ) LeftWater(); if ( bDestroyed ) // might destroy self in either virtual function return; lastwater = curwater; firstwater = true; } if ( bWallStop ) { let newpos = level.Vec3Offset(pos,vel); if ( !level.IsPointInLevel(newpos) ) vel *= 0.; } if ( bWallKill && !level.IsPointInLevel(pos) ) { Destroy(); return; } OnTick(); } virtual void SetupSprite() { // safe defaults texture = TexMan.CheckForTexture("BLPFA0"); bCheckWater = false; bWallStop = false; bWallKill = false; } static SWWMStaticSprite SpawnAt( Class type, Vector3 spawnpos ) { let t = SWWMStaticSprite(level.SpawnVisualThinker(type)); t.pos = spawnpos; t.cursector = level.PointInSector(t.pos.xy); t.SetupSprite(); t.speed = 1.; // default return t; } } Class SWWMAnimSprite : SWWMStaticSprite { string sprname; // base name of sprite uint8 sprframe; // current sprite frame (0-28) uint8 numframes; // total frame count of animation (max 29) uint8 frameskip; // how many frames to skip each step uint8 framestep; // how many tics to wait for each frame step uint8 framecnt; // internal, counter for frame stepping bool bLooping; // animation loops, otherwise die upon reaching last frame private void TickAnim( bool bFirstTick = false ) { if ( !bFirstTick ) { framecnt++; if ( framecnt < framestep ) return; framecnt = 0; sprframe += 1+frameskip; if ( sprframe >= numframes ) { if ( !bLooping ) { Destroy(); return; } else sprframe -= numframes; } } string tname = String.Format("%.4s%c0",sprname,0x41+sprframe); texture = TexMan.CheckForTexture(tname); } override void Tick() { Super.Tick(); if ( !IsFrozen() && !bDestroyed ) TickAnim(); } override void SetupSprite() { // safe defaults sprname = "XEX1"; sprframe = 0; numframes = 28; frameskip = 0; framestep = 1; framecnt = 0; bLooping = false; bCheckWater = false; bWallStop = false; bWallKill = false; } static SWWMAnimSprite SpawnAt( Class type, Vector3 spawnpos ) { let t = SWWMAnimSprite(level.SpawnVisualThinker(type)); t.pos = spawnpos; t.cursector = level.PointInSector(t.pos.xy); t.SetupSprite(); t.speed = 1.; // default t.TickAnim(true); return t; } } // Generic smoke, lightweight tick Class SWWMSmoke : SWWMNonInteractiveActor { Default { RenderStyle 'Shaded'; StencilColor "FF FF FF"; Speed 1; +FORCEXYBILLBOARD; +ROLLSPRITE; +ROLLCENTER; Scale .3; } override void PostBeginPlay() { double ang, pt; scale *= FRandom[Puff](.5,1.5); alpha = min(1.,alpha*FRandom[Puff](.5,1.5)); ang = FRandom[Puff](0,360); pt = FRandom[Puff](-90,90); vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](.2,.8)*speed; roll = Frandom[Puff](0,360); scale.x *= RandomPick[Puff](-1,1); scale.y *= RandomPick[Puff](-1,1); } override void Tick() { prev = pos; // for interpolation if ( freezetics > 0 ) { freezetics--; return; } if ( isFrozen() ) return; vel *= .96; vel.z += .01; // linetrace-based movement (hopefully more reliable than traditional methods) Vector3 dir = vel; double spd = vel.length(); dir /= spd; double dist = spd; FLineTraceData d; Vector3 newpos = pos; newpos.z = clamp(newpos.z,floorz,ceilingz); int nstep = 0; while ( dist > 0 ) { // safeguard, too many bounces if ( nstep > MAXBOUNCEPERTIC ) { Destroy(); return; } double ang = atan2(dir.y,dir.x); double pt = asin(-dir.z); LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); if ( d.HitType != TRACE_HitNone ) { Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d); dist -= d.Distance; // should only happen if we bounced dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal)); vel = dir*spd; newpos = d.HitLocation+dir; } else { dist = 0.; newpos = level.Vec3Offset(newpos,dir*spd); } nstep++; } newpos.z = clamp(newpos.z,floorz,ceilingz); SetOrigin(newpos,true); UpdateWaterLevel(); if ( (waterlevel > 0) && !bAMBUSH ) { let b = Spawn('SWWMBubble',pos); b.scale *= abs(scale.x); b.vel = vel; Destroy(); return; } if ( !CheckNoDelay() || (tics == -1) ) return; if ( tics > 0 ) tics--; while ( !tics ) { if ( !SetState(CurState.NextState) ) return; } } States { Spawn: XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1); Stop; } } // Visual thinker smoke, less overhead, used for heavier effects Class SWWMHalfSmoke : SWWMAnimSprite { override void SetupSprite() { Super.SetupSprite(); sprname = "XSMK"; numframes = 20; bCheckWater = true; SetRenderStyle(STYLE_Shaded); scolor = 0xFFFFFFFF; Scale = (.3,.3); Flags |= SPF_ROLL; } override void OnTick() { vel *= .96; vel.z += .01; } override void EnteredWater() { let b = SWWMAnimSprite.SpawnAt('SWWMHalfBubble',pos); b.scale *= abs(scale.x); b.vel = vel; Destroy(); } override void PostBeginPlay() { double ang, pt; scale *= FRandom[Puff](.5,1.5); alpha *= FRandom[Puff](.5,1.5); ang = FRandom[Puff](0,360); pt = FRandom[Puff](-90,90); vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](.2,.8)*speed; roll = FRandom[Puff](0,360); bXFlip = Random[Puff](0,1); bYFlip = Random[Puff](0,1); } } // lesser version Class SWWMSmallSmoke : SWWMHalfSmoke { override void SetupSprite() { Super.SetupSprite(); sprname = "QSM6"; numframes = 18; } override void PostBeginPlay() { double ang, pt; scale *= FRandom[Puff](.5,1.5); alpha *= FRandom[Puff](.5,1.5); ang = FRandom[Puff](0,360); pt = FRandom[Puff](-90,90); vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](.04,.16)*speed; roll = Frandom[Puff](0,360); bXFlip = Random[Puff](0,1); bYFlip = Random[Puff](0,1); } } Class SWWMBubble : SWWMNonInteractiveActor { Default { RenderStyle 'Add'; +FORCEXYBILLBOARD; Scale 0.5; } override void PostBeginPlay() { double ang, pt; scale *= FRandom[Puff](0.5,1.5); ang = FRandom[Puff](0,360); pt = FRandom[Puff](-90,90); vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](0.2,0.8); if ( (waterlevel <= 0) || (waterdepth < (scale.y*9.)) ) Destroy(); SetState(ResolveState('Spawn')+Random[Puff](0,19)); } override void Tick() { prev = pos; if ( freezetics > 0 ) { freezetics--; return; } if ( isFrozen() ) return; vel *= 0.96; vel.z += 0.05; // linetrace-based movement (hopefully more reliable than traditional methods) Vector3 dir = vel; double spd = vel.length(); dir /= spd; double dist = spd; FLineTraceData d; Vector3 newpos = pos; newpos.z = clamp(newpos.z,floorz,ceilingz); int nstep = 0; while ( dist > 0 ) { // safeguard, too many bounces if ( nstep > MAXBOUNCEPERTIC ) { Destroy(); return; } double ang = atan2(dir.y,dir.x); double pt = asin(-dir.z); LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); if ( d.HitType != TRACE_HitNone ) { Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d); dist -= d.Distance; // should only happen if we bounced dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal)); vel = dir*spd; newpos = d.HitLocation+dir; } else { dist = 0.; newpos = level.Vec3Offset(newpos,dir*spd); } nstep++; } newpos.z = clamp(newpos.z,floorz,ceilingz); SetOrigin(newpos,true); UpdateWaterLevel(); if ( (waterlevel <= 0) || (waterdepth < (scale.y*9.)) || !Random[Puff](0,100) ) Destroy(); if ( !CheckNoDelay() || (tics == -1) ) return; if ( tics > 0 ) tics--; while ( !tics ) { if ( !SetState(CurState.NextState) ) return; } } States { Spawn: XBUB ABCDEFGHIJKLMNOPQRST 1; Loop; } } // Visual thinker version of the above, much simpler checks Class SWWMHalfBubble : SWWMAnimSprite { override void SetupSprite() { Super.SetupSprite(); sprname = "XBUB"; sprframe = Random[Puff](0,19); numframes = 20; bCheckWater = true; bLooping = true; bWallKill = true; SetRenderStyle(STYLE_Add); Scale = (.5,.5); } override void OnTick() { vel *= .96; vel.z += .05; if ( !Random[Puff](0,100) ) Destroy(); } override void LeftWater() { Destroy(); } override void PostBeginPlay() { double ang, pt; scale *= FRandom[Puff](.5,1.5); ang = FRandom[Puff](0,360); pt = FRandom[Puff](-90,90); vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](.2,.8)*speed; } } Class SWWMSparkTrail : SWWMNonInteractiveActor { Default { RenderStyle 'Add'; +FORCEXYBILLBOARD; } override void Tick() { if ( freezetics > 0 ) { freezetics--; return; } if ( isFrozen() ) return; A_SetScale(scale.x*.9,scale.y); A_FadeOut(.06); } States { Spawn: XZW1 A -1 Bright; Stop; } } Class SWWMSpark : SWWMNonInteractiveActor { bool dead; Sector tracksector; int trackplane; Default { RenderStyle 'Add'; +FORCEXYBILLBOARD; Gravity .2; Scale .05; } override void Tick() { prev = pos; if ( freezetics > 0 ) { freezetics--; return; } if ( isFrozen() ) return; if ( dead ) { // do nothing but follow floor movement if ( tracksector ) { double trackz; if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); else trackz = tracksector.floorplane.ZAtPoint(pos.xy); if ( trackz != pos.z ) SetZ(trackz); } } else { vel.z -= GetGravity(); // linetrace-based movement (hopefully more reliable than traditional methods) Vector3 dir = vel; double spd = vel.length(); dir /= spd; double dist = spd; FLineTraceData d; Vector3 newpos = pos; newpos.z = clamp(newpos.z,floorz,ceilingz); int nstep = 0; while ( dist > 0 ) { Vector3 oldpos = newpos; // safeguard, too many bounces if ( nstep > MAXBOUNCEPERTIC ) { Destroy(); return; } double ang = atan2(dir.y,dir.x); double pt = asin(-dir.z); LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); if ( d.HitType != TRACE_HitNone ) { Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d); dist -= d.Distance; // should only happen if we bounced dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal)); spd *= .4; dist *= .4; vel = dir*spd; newpos = d.HitLocation+dir; } else { dist = 0.; newpos = level.Vec3Offset(newpos,dir*spd); } if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) ) { // lose speed and die if ( d.Hit3DFloor ) { newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy); tracksector = d.Hit3DFloor.model; trackplane = 1; } else { // hacky workaround if ( !d.HitSector ) d.HitSector = floorsector; newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy); tracksector = d.HitSector; trackplane = 0; } vel = (0,0,0); pitch = 0; roll = 0; dead = true; SetStateLabel('Death'); break; } nstep++; } newpos.z = clamp(newpos.z,floorz,ceilingz); Vector3 taildir = level.Vec3Diff(newpos,pos); double taillen = taildir.length(); if ( (taillen > 0.) && (alpha > .3) && (waterlevel <= 0) ) { taildir /= taillen; let t = Spawn('SWWMSparkTrail',newpos); t.alpha = alpha*.3; t.speed = taillen; [t.angle, t.pitch, t.scale.y] = SWWMUtility.CalcYBeam(taildir,taillen); } SetOrigin(newpos,true); if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid ) { Destroy(); return; } } UpdateWaterLevel(); if ( waterlevel > 0 ) { let b = Spawn('SWWMBubble',pos); b.vel = vel; b.scale *= 0.3; Destroy(); return; } if ( !CheckNoDelay() || (tics == -1) ) return; if ( tics > 0 ) tics--; while ( !tics ) { if ( !SetState(CurState.NextState) ) return; } } States { Spawn: BLPF A 1 Bright A_FadeOut(0.01); Wait; Death: BLPF A 1 Bright A_FadeOut(0.05); Wait; } } Class SWWMChip : SWWMNonInteractiveActor { SWWMChip prevchip, nextchip; bool killme; double anglevel, pitchvel, rollvel; bool dead; Sector tracksector; int trackplane; Default { +INTERPOLATEANGLES; +ROLLSPRITE; +ROLLCENTER; +FORCEXYBILLBOARD; Gravity .35; Scale .2; } override void PostBeginPlay() { anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); frame = Random[Junk](0,7); scale *= Frandom[Junk](0.8,1.2); SWWMHandler.QueueChip(self); } override void OnDestroy() { SWWMHandler.DeQueueChip(self); Super.OnDestroy(); } override void Tick() { prev = pos; // for interpolation if ( freezetics > 0 ) { freezetics--; return; } if ( isFrozen() ) return; if ( dead ) { // do nothing but follow floor movement if ( tracksector ) { double trackz; if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); else trackz = tracksector.floorplane.ZAtPoint(pos.xy); if ( trackz != pos.z ) { SetZ(trackz); UpdateWaterLevel(false); } } } else { if ( waterlevel <= 0 ) vel.z -= GetGravity(); // linetrace-based movement (hopefully more reliable than traditional methods) Vector3 dir = vel; double spd = vel.length(); dir /= spd; double dist = spd; FLineTraceData d; Vector3 newpos = pos; newpos.z = clamp(newpos.z,floorz,ceilingz); int nstep = 0; while ( dist > 0 ) { // safeguard, too many bounces if ( nstep > MAXBOUNCEPERTIC ) { Destroy(); return; } double ang = atan2(dir.y,dir.x); double pt = asin(-dir.z); LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); if ( d.HitType != TRACE_HitNone ) { Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d); dist -= d.Distance; // should only happen if we bounced dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal)); spd *= .3; dist *= .3; vel = dir*spd; newpos = d.HitLocation+dir; SetStateLabel('Bounce'); } else { dist = 0.; newpos = level.Vec3Offset(newpos,dir*spd); } newpos.z = clamp(newpos.z,floorz,ceilingz); if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) ) { // lose speed and die if ( d.Hit3DFloor ) { newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy); tracksector = d.Hit3DFloor.model; trackplane = 1; } else { // hacky workaround if ( !d.HitSector ) d.HitSector = floorsector; newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy); tracksector = d.HitSector; trackplane = 0; } vel = (0,0,0); dead = true; SetStateLabel('Death'); break; } nstep++; } SetOrigin(newpos,true); UpdateWaterLevel(); if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid ) { Destroy(); return; } } if ( killme ) A_FadeOut(.01); if ( waterlevel > 0 ) { vel *= .99; if ( pos.z > floorz ) vel.z -= gravity*.01; anglevel *= .98; pitchvel *= .98; rollvel *= .98; } if ( !CheckNoDelay() || (tics == -1) ) return; if ( tics > 0 ) tics--; while ( !tics ) { if ( !SetState(CurState.NextState) ) return; } } States { Spawn: XZW1 # 1 { angle += anglevel; pitch += pitchvel; roll += rollvel; } Loop; Bounce: XZW1 # 0 { anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); } Goto Spawn; Death: XZW1 # -1; Stop; } } Class PoofLight : PaletteLight { Default { Tag "Yellow"; ReactionTime 5; Args 0,0,0,60; } } Class PoofLight2 : PaletteLight { Default { Tag "Yellow"; ReactionTime 20; Args 0,0,0,90; } } Class SWWMItemFog : SWWMNonInteractiveActor { Default { RenderStyle 'Add'; +ROLLSPRITE; +ROLLCENTER; +FORCEXYBILLBOARD; } States { Spawn: BLPF A 2 Bright NoDelay { // offset up SetOrigin(Vec3Offset(0,0,16),false); roll = FRandom[ExploS](0,360); scale *= FRandom[ExploS](0.9,1.1); scale.x *= RandomPick[ExploS](-1,1); scale.y *= RandomPick[ExploS](-1,1); int numpt = Random[ExploS](8,12); if ( bAMBUSH ) { numpt *= 2; for ( int i=0; i 0 ) { freezetics--; return; } if ( isFrozen() ) return; if ( (level.maptime+special1)%10 ) return; if ( !flare.texture ) { flare.color1 = 0xFF88AAFF; flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png"); flare.style = STYLE_AddShaded; flare.flags = SPF_FULLBRIGHT; flare.fadestep = -1; } double dist = Distance3DSquared(players[consoleplayer].Camera); if ( dist >= 250000. ) return; double alph = clamp((250000.-dist)/250000.,0.,1.); flare.pos = pos+(0,0,28); int numpt = Random[ExploS](0,2); for ( int i=0; i 0 ) { freezetics--; return; } if ( isFrozen() ) return; if ( (level.maptime+special1)%5 ) return; Vector3 apos, bpos; apos.xy = tline.v1.p; bpos.xy = tline.v2.p; apos.z = max(tline.frontsector.floorplane.ZAtPoint(apos.xy),tline.backsector.floorplane.ZAtPoint(apos.xy)); bpos.z = max(tline.frontsector.floorplane.ZAtPoint(bpos.xy),tline.backsector.floorplane.ZAtPoint(bpos.xy)); int numpt = Random[ExploS](0,2); numpt *= int(clamp((apos-bpos).length()/32,1,8)); if ( !flare.texture ) { flare.color1 = 0xFF88AAFF; flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png"); flare.style = STYLE_AddShaded; flare.flags = SPF_FULLBRIGHT; flare.fadestep = -1; } for ( int i=0; i= 250000. ) continue; double alph = clamp((250000.-dist)/250000.,0.,1.); double ang = FRandom[ExploS](0,360); double pt = FRandom[ExploS](-90,90); Vector3 pvel = SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[ExploS](.0,.3); flare.lifetime = Random[ExploS](120,240); flare.size = FRandom[ExploS](2.,4.); flare.sizestep = FRandom[ExploS](-.02,-.01); flare.pos = ppos; flare.vel = pvel; flare.accel = (0,0,FRandom[ExploS](.05,.1)); flare.startalpha = FRandom[ExploS](.15,.3)*alph; level.SpawnParticle(flare); } } } Class SWWMTeleportFog : SWWMNonInteractiveActor { Default { RenderStyle 'Add'; +FORCEXYBILLBOARD; } override void PostBeginPlay() { Super.PostBeginPlay(); A_StartSound("misc/teleport",CHAN_VOICE); Spawn('TeleLight',pos); } States { Spawn: TNT1 A 1 { int numpt = int(Random[ExploS](6,12)*alpha); for ( int i=0; i= 160000000. ) alpha = 1.; else alpha = AlphaCubeRoot(clamp(max(0,camdist-40000.)/160000000.,0.,1.)); alpha *= FRandom[ClientSparkles](.9,1.); } // nearby sparkles if ( camdist >= 250000. ) return; double alph = clamp((250000.-camdist)/250000.,0.,1.); if ( !flare.texture ) { flare.color1 = Color(Args[1]*85,Args[2]*85,Args[3]*85); flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png"); flare.style = STYLE_AddShaded; flare.flags = SPF_FULLBRIGHT; flare.lifetime = 30; flare.accel = (0,0,.05); flare.fadestep = -1; } flare.startalpha = alph; int numpt = Random[ClientSparkles](1,3); for ( int i=0; i WaterHitList; Array ShootThroughList; static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, double dist, int bubblesparse, bool smoky = false ) { let t = new('SWWMBulletTrail'); t.WaterHitList.Clear(); t.ShootThroughList.Clear(); t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0,ignore:target); foreach ( l:t.ShootThroughList ) { // have to do both separately, for whatever reason l.Activate(target,0,SPAC_PCross); l.Activate(target,0,SPAC_Impact); } foreach ( w:t.WaterHitList ) { let b = Actor.Spawn('InvisibleSplasher',w.hitpos); b.target = target; b.A_CheckTerrain(); } Vector3 ppos; SWWMAnimSprite b; bool bInWater; for ( int i=5; i 0 ) { freezetics--; return; } Update(); } default { RenderStyle 'Shaded'; StencilColor "00 00 00"; } States { Spawn: XZW1 A -1; Stop; } } // Terrain FX (cheap) Class SWWMBaseSplash : SWWMNonInteractiveActor abstract { TextureID pufftex[8]; FSpawnParticleParams puff; abstract void DoSplash(); void Splash() { puff.style = STYLE_Shaded; puff.fadestep = -1; puff.pos = pos; for ( int i=0; i<8; i++ ) pufftex[i] = TexMan.CheckForTexture("graphics/Particles/xpuff"..i..".png"); DoSplash(); } States { Spawn: TNT1 A 1 NoDelay Splash(); Stop; } } Class SWWMWaterSplash : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 60; puff.startalpha = .5; double ang, pt, str; Vector3 dir; for ( int i=0; i<60; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-60); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](2.,12.); dir *= str*.25; dir.z += 1.; puff.color1 = SWWMUtility.LerpColor(0xFF4060FF,0xFFA0C0FF,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:1200,AMF_EMITFROMTARGET); } } Class SWWMWaterSplash2 : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 50; puff.startalpha = .5; double ang, pt, str; Vector3 dir; for ( int i=0; i<15; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](1.,6.); dir *= str*.25; dir.z += .35; puff.color1 = SWWMUtility.LerpColor(0xFF4060FF,0xFFA0C0FF,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:300,AMF_EMITFROMTARGET); } } Class SWWMBloodSplash : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 60; puff.startalpha = .5; double ang, pt, str; Vector3 dir; for ( int i=0; i<60; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-60); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](2.,12.); dir *= str*.25; dir.z += 1.; puff.color1 = SWWMUtility.LerpColor(0xFF800000,0xFF600000,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+.5; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:1200,AMF_EMITFROMTARGET); } } Class SWWMBloodSplash2 : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 50; puff.startalpha = .5; double ang, pt, str; Vector3 dir; for ( int i=0; i<15; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](1.,6.); dir *= str*.25; dir.z += .35; puff.color1 = SWWMUtility.LerpColor(0xFF800000,0xFF600000,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+.5; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:300,AMF_EMITFROMTARGET); } } Class SWWMSludgeSplash : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 40; puff.startalpha = .8; double ang, pt, str; Vector3 dir; for ( int i=0; i<60; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-60); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](2.,8.); dir *= str*.25; dir.z += .4; puff.color1 = SWWMUtility.LerpColor(0xFF405040,0xFF303030,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+2.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:1200,AMF_EMITFROMTARGET); } } Class SWWMSludgeSplash2 : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 30; puff.startalpha = .8; double ang, pt, str; Vector3 dir; for ( int i=0; i<15; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](1.,4.); dir *= str*.25; dir.z += .15; puff.color1 = SWWMUtility.LerpColor(0xFF405040,0xFF303030,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+2.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:300,AMF_EMITFROMTARGET); } } Class SWWMMudSplash : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 40; puff.startalpha = .8; double ang, pt, str; Vector3 dir; for ( int i=0; i<60; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-60); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](2.,8.); dir *= str*.25; dir.z += .4; puff.color1 = SWWMUtility.LerpColor(0xFF504020,0xFF302010,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+2.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:1200,AMF_EMITFROMTARGET); } } Class SWWMMudSplash2 : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 30; puff.startalpha = .8; double ang, pt, str; Vector3 dir; for ( int i=0; i<15; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](1.,4.); dir *= str*.25; dir.z += .15; puff.color1 = SWWMUtility.LerpColor(0xFF504020,0xFF302010,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+2.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:300,AMF_EMITFROMTARGET); } } Class SWWMSlimeSplash : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 40; puff.startalpha = .8; puff.style = STYLE_AddShaded; puff.flags = SPF_FULLBRIGHT; double ang, pt, str; Vector3 dir; for ( int i=0; i<60; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-60); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](2.,8.); dir *= str*.25; dir.z += .4; puff.color1 = SWWMUtility.LerpColor(0xFF00FF00,0xFF008000,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+2.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:1200,AMF_EMITFROMTARGET); } } Class SWWMSlimeSplash2 : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 30; puff.startalpha = .8; puff.style = STYLE_AddShaded; puff.flags = SPF_FULLBRIGHT; double ang, pt, str; Vector3 dir; for ( int i=0; i<15; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](1.,4.); dir *= str*.25; dir.z += .15; puff.color1 = SWWMUtility.LerpColor(0xFF00FF00,0xFF008000,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+2.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } A_AlertMonsters(swwm_uncapalert?0:300,AMF_EMITFROMTARGET); } } Class SWWMLavaSplash : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 40; puff.startalpha = .8; puff.style = STYLE_AddShaded; puff.flags = SPF_FULLBRIGHT; double ang, pt, str; Vector3 dir; for ( int i=0; i<60; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-60); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](2.,12.); dir *= str*.35; dir.z += .6; puff.color1 = SWWMUtility.LerpColor(0xFFFFC040,0xFFFF4020,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+1.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } let s = Spawn('SWWMSizzleSmoke',pos); s.target = target; } } Class SWWMLavaSplash2 : SWWMBaseSplash { override void DoSplash() { puff.lifetime = 30; puff.startalpha = .8; puff.style = STYLE_AddShaded; puff.flags = SPF_FULLBRIGHT; double ang, pt, str; Vector3 dir; for ( int i=0; i<15; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](1.,6.); dir *= str*.35; dir.z += .2; puff.color1 = SWWMUtility.LerpColor(0xFFFFC040,0xFFFF4020,FRandom[ExploS](0.,1.)); puff.texture = pufftex[Random[ExploS](0,7)]; puff.size = str+1.; puff.sizestep = -.02*str; puff.vel = dir; puff.accel = (0,0,-.03*str); level.SpawnParticle(puff); } let s = Spawn('SWWMSizzleSmoke2',pos); s.target = target; } } Class SWWMSizzleSmoke : SWWMBaseSplash { override void DoSplash() { double ang, pt, str; Vector3 dir; for ( int i=0; i<4; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](.5,2.); let s = SWWMAnimSprite.SpawnAt('SWWMSmallSmoke',pos); s.vel = dir*str+(0,0,.4); s.scolor = Color(1,1,1)*Random[ExploS](192,224); s.scale *= 8.; s.alpha *= .4; s.SetRenderStyle(STYLE_AddShaded); s.framestep = Random[ExploS](1,3); } A_AlertMonsters(swwm_uncapalert?0:1200,AMF_EMITFROMTARGET); } } Class SWWMSizzleSmoke2 : SWWMBaseSplash { override void DoSplash() { double ang, pt, str; Vector3 dir; for ( int i=0; i<2; i++ ) { ang = FRandom[ExploS](0,360); pt = FRandom[ExploS](-90,-30); dir = SWWMUtility.Vec3FromAngles(ang,pt); str = FRandom[ExploS](.25,1.); let s = SWWMAnimSprite.SpawnAt('SWWMSmallSmoke',pos); s.vel = dir*str+(0,0,.15); s.scolor = Color(1,1,1)*Random[ExploS](192,224); s.scale *= 4.; s.alpha *= .3; s.SetRenderStyle(STYLE_AddShaded); s.framestep = Random[ExploS](1,2); } A_AlertMonsters(swwm_uncapalert?0:300,AMF_EMITFROMTARGET); } } // Hexen thing Class SWWMCrushedSpike : Actor { Default { Radius 20; Height 16; +SOLID; +FLOORCLIP; +NOTELEPORT; } States { Spawn: TSPK X -1; Stop; } }