diff --git a/FuturePlans.md b/FuturePlans.md index e94fc4df0..696cb9915 100644 --- a/FuturePlans.md +++ b/FuturePlans.md @@ -13,12 +13,19 @@ after the first release. - Super Happy Fun Ball (SWWM Z) * Additional Demolitionist Menu features - Per-monster kill tracker in stats tab - - Music player with a selection of Saya's favourite tracks + - Radio - Pong minigame + - Selling items at the store +* Fanart on the intermission screen +* 4komas on the intermission screen * Actually make a fancy titlemap * Mod trailer * Additional HUD stuff - Minimap with radar like in SWWM Z + - Fake livestream chat, with dynamic reactions to all sorts of stuff * Character and item images for the library * Full Mashiro model for Lämp easter egg +* Summonable Ibuki companion (w/ optional "stream friendly" clothing) +* Saya model, for scenes or something idk * Japanese Localization??? +* Strife support, with rewritten dialogue diff --git a/brightmaps/Nobright.png b/brightmaps/Nobright.png new file mode 100644 index 000000000..6aaccb278 Binary files /dev/null and b/brightmaps/Nobright.png differ diff --git a/cvarinfo.txt b/cvarinfo.txt index 000a1f122..5fae99c13 100644 --- a/cvarinfo.txt +++ b/cvarinfo.txt @@ -31,4 +31,4 @@ user bool swwm_scorebonus = true; // show score bonuses user bool swwm_fly6dof = true; // flying uses 6dof movement, toggleable for those who get motion sickness user bool swwm_othervoice = true; // can hear other player's voice lines in coop user bool swwm_shaders = true; // use pp shaders for some effects -user bool swwm_earbuster = false; // limits loudness of wallbuster fire sounds \ No newline at end of file +user bool swwm_earbuster = false; // limits loudness of wallbuster fire sounds diff --git a/gldefs.pickups b/gldefs.pickups index 03929d460..11f44cc86 100644 --- a/gldefs.pickups +++ b/gldefs.pickups @@ -36,22 +36,32 @@ HardwareShader Texture "models/Refresher.png" Brightmap Texture "models/GhostArtifact.png" { Map "models/GhostArtifact_bright.png" + DisableFullbright } Brightmap Texture "models/Gravity.png" { Map "models/Gravity_bright.png" + DisableFullbright } Brightmap Texture "models/Ragekit.png" { Map "models/Ragekit_bright.png" + DisableFullbright } Brightmap Texture "models/Omnisight.png" { Map "models/Omnisight_bright.png" + DisableFullbright } Brightmap Texture "models/Lamp.png" { Map "models/Lamp_bright.png" + DisableFullbright +} +Brightmap Texture "models/Invinciball.png" +{ + Map "brightmaps/nobright.png" + DisableFullbright } Brightmap Texture "models/Moth_Mashiro.png" { diff --git a/gldefs.pp b/gldefs.pp new file mode 100644 index 000000000..af9dbd7e6 --- /dev/null +++ b/gldefs.pp @@ -0,0 +1,21 @@ +HardwareShader PostProcess beforebloom +{ + Name "GhostShader" + Shader "shaders/glsl/Ghostscreen.fp" 330 +} +HardwareShader PostProcess scene +{ + Name "InvinciShader" + Shader "shaders/glsl/Invinciscreen.fp" 330 + Uniform float str +} + +HardwareShader PostProcess scene +{ + Name "RagekitShader" + Shader "shaders/glsl/Ragescreen.fp" 330 + Texture WarpTex "textures/ragewarp.png" + Texture NoiseTex "textures/graynoise.png" + Uniform float timer + Uniform float xtrastr +} diff --git a/modeldef.pickups b/modeldef.pickups index 43a1f59e8..6e77dc306 100644 --- a/modeldef.pickups +++ b/modeldef.pickups @@ -128,7 +128,6 @@ Model "GhostArtifactX" SurfaceSkin 0 0 "darkmap.png" Scale 0.05 0.05 0.05 ZOffset 16 - ROTATING FrameIndex XZW1 A 0 0 } @@ -138,39 +137,66 @@ Model "GravitySuppressor" Path "models" Model 0 "Gravity_d.3d" SurfaceSkin 0 0 "Gravity.png" - SurfaceSkin 0 1 "silvermap.png" Scale 0.06 0.06 0.06 ZOffset 16 ROTATING FrameIndex XZW1 A 0 0 } +Model "GravityX" +{ + Path "models" + Model 0 "Gravity_d.3d" + SurfaceSkin 0 1 "silvermap.png" + Scale 0.06 0.06 0.06 + ZOffset 16 + + FrameIndex XZW1 A 0 0 +} Model "FuckingInvinciball" { Path "models" Model 0 "Invinciball_d.3d" SurfaceSkin 0 0 "Invinciball.png" - SurfaceSkin 0 1 "invincimap.png" Scale 0.04 0.04 0.04 ZOffset 16 ROTATING FrameIndex XZW1 A 0 0 } +Model "InvinciballX" +{ + Path "models" + Model 0 "Invinciball_d.3d" + SurfaceSkin 0 1 "invincimap.png" + Scale 0.04 0.04 0.04 + ZOffset 16 + + FrameIndex XZW1 A 0 0 +} Model "Ragekit" { Path "models" Model 0 "Ragekit_d.3d" SurfaceSkin 0 0 "Ragekit.png" - SurfaceSkin 0 1 "ragemap.png" Scale 0.05 0.05 0.05 ZOffset 16 ROTATING FrameIndex XZW1 A 0 0 } +Model "RagekitX" +{ + Path "models" + Model 0 "Ragekit_d.3d" + SurfaceSkin 0 1 "ragemap.png" + Scale 0.05 0.05 0.05 + ZOffset 16 + + FrameIndex XZW1 A 0 0 +} Model "Omnisight" { diff --git a/shaders/glsl/Ghostscreen.fp b/shaders/glsl/Ghostscreen.fp new file mode 100644 index 000000000..f5c9bbf4c --- /dev/null +++ b/shaders/glsl/Ghostscreen.fp @@ -0,0 +1,12 @@ +void main() +{ + vec2 uv = TexCoord; + vec3 col = texture(InputTexture,uv).rgb; + float p = distance(uv,vec2(.5))/sqrt(2.); + for ( float i=0.; i<4.; i+=1. ) + { + vec2 suv = fract((.5-uv)*(1.-i*p*p)+.5); + col += texture(InputTexture,suv).rgb*pow(p,2.)*vec3(.6,.8,1.3); + } + FragColor = vec4(col,1.0); +} diff --git a/shaders/glsl/Glitch.frag b/shaders/glsl/Glitch.frag new file mode 100644 index 000000000..3e0f6c500 --- /dev/null +++ b/shaders/glsl/Glitch.frag @@ -0,0 +1,29 @@ +float rnd2( in vec2 sd ) +{ + return fract(cos(dot(sd*floor(Timer*15.0),vec2(145.34,142.55)))*2745.84); +} +float rnd( in float sd ) +{ + return rnd2(vec2(sd,1.0)); +} + +void main() +{ + vec2 coord = TexCoord; + vec2 uv_c[3] = vec2[3](coord,coord,coord); + vec2 blka = floor(coord*vec2(22.0,12.0)); + vec2 blkb = floor(coord*vec2(6.0,9.0)); + float noiz = pow(rnd2(blka),thr1)*pow(rnd2(blkb),thr2)-pow(rnd(4.53),thr3)*str2; + uv_c[0].x += str1*noiz*(rnd(3.35)-0.5); + uv_c[1].x += str1*noiz*(rnd(4.63)-0.5); + uv_c[2].x += str1*noiz*(rnd(5.62)-0.5); + uv_c[0].y += str1*noiz*(rnd(4.55)-0.5); + uv_c[1].y += str1*noiz*(rnd(3.67)-0.5); + uv_c[2].y += str1*noiz*(rnd(5.54)-0.5); + vec4 res; + res.r = texture(InputTexture,uv_c[0]).r; + res.g = texture(InputTexture,uv_c[1]).g; + res.b = texture(InputTexture,uv_c[2]).b; + res.a = 1.0; + FragColor = res; +} diff --git a/shaders/glsl/Grain.frag b/shaders/glsl/Grain.frag new file mode 100644 index 000000000..fc303dc5b --- /dev/null +++ b/shaders/glsl/Grain.frag @@ -0,0 +1,59 @@ +/* + Complex grain shader ported over from MariENB + (C)2012-2018 Marisa Kirisame +*/ +const float nf = 0.000005; +const vec3 nm1 = vec3(2.05,3.11,2.22); +const float nk = 0.04; +const vec3 nm2 = vec3(4.25,9.42,6.29); +const float ns = -0.08; +const float np = 3.95; +const float bnp = 1.7; + +#define darkmask(a,b) (a>0.5)?(2.0*a*(0.5+b)):(1.0-2.0*(1.0-a)*(1.0-((0.5+b)))) + +vec3 grain( in vec3 res, in vec2 coord ) +{ + float ts = Timer*nf; + vec2 s1 = coord+vec2(0.0,ts); + vec2 s2 = coord+vec2(ts,0.0); + vec2 s3 = coord+vec2(ts,ts); + float n1, n2, n3; + vec2 nr = textureSize(NoiseTexture,0); + s1 = mod(s1*nm1.x*nr,1.0); + s2 = mod(s2*nm1.y*nr,1.0); + s3 = mod(s3*nm1.z*nr,1.0); + n1 = texture(NoiseTexture,s1).r; + n2 = texture(NoiseTexture,s2).g; + n3 = texture(NoiseTexture,s3).b; + s1 = coord+vec2(ts+n1*nk,n2*nk); + s2 = coord+vec2(n2,ts+n3*nk); + s3 = coord+vec2(ts+n3*nk,ts+n1*nk); + s1 = mod(s1*nm2.x*nr,1.0); + s2 = mod(s2*nm2.y*nr,1.0); + s3 = mod(s3*nm2.z*nr,1.0); + n1 = texture(NoiseTexture,s1).r; + n2 = texture(NoiseTexture,s2).g; + n3 = texture(NoiseTexture,s3).b; + float n4 = (n1+n2+n3)/3.0; + vec3 ng = vec3(n4); + vec3 nc = vec3(n1,n2,n3); + vec3 nt = pow(clamp(mix(ng,nc,ns),0.0,1.0),vec3(np)); + float bn = 1.0-clamp((res.r+res.g+res.b)/3.0,0.0,1.0); + bn = pow(bn,bnp); + vec3 nn = clamp(nt*bn,vec3(0.0),vec3(1.0)); + res.r = darkmask(res.r,(nn.r*ni)); + res.g = darkmask(res.g,(nn.g*ni)); + res.b = darkmask(res.b,(nn.b*ni)); + return res; +} + +void main() +{ + vec2 coord = TexCoord; + vec4 res = texture(InputTexture,coord); + /*vec2 sfact = max(vec2(320.0,200.0),textureSize(InputTexture,0)*0.5); + coord = floor(coord*sfact)/sfact;*/ + res.rgb = grain(res.rgb,coord); + FragColor = res; +} diff --git a/shaders/glsl/Invinciscreen.fp b/shaders/glsl/Invinciscreen.fp new file mode 100644 index 000000000..586204d61 --- /dev/null +++ b/shaders/glsl/Invinciscreen.fp @@ -0,0 +1,28 @@ +vec3 sharpened( vec2 uv ) +{ + vec3 col = texture(InputTexture,uv).rgb*9.; + vec2 bresl = vec2(textureSize(InputTexture,0)); + vec2 bof = vec2(1./bresl.x,1./bresl.y); + col -= texture(InputTexture,uv+vec2(bof.x,0)).rgb; + col -= texture(InputTexture,uv+vec2(2.*bof.x,0)).rgb; + col -= texture(InputTexture,uv+vec2(-bof.x,0)).rgb; + col -= texture(InputTexture,uv+vec2(-2.*bof.x,0)).rgb; + col -= texture(InputTexture,uv+vec2(0,bof.y)).rgb; + col -= texture(InputTexture,uv+vec2(0,2.*bof.y)).rgb; + col -= texture(InputTexture,uv+vec2(0,-bof.y)).rgb; + col -= texture(InputTexture,uv+vec2(0,-2.*bof.y)).rgb; + return col; +} + +void main() +{ + vec2 uv = TexCoord; + vec3 col = texture(InputTexture,uv).rgb; + float p = distance(uv,vec2(.5)); + for ( float i=0.; i<4.; i+=1. ) + { + vec2 suv = fract((uv-.5)*(1.-.01*i*(.3+str*4.))+.5); + col += sharpened(suv)*pow(p,2.4)*vec3(1.2,.7,.2)*(.4+str); + } + FragColor = vec4(col,1.0); +} diff --git a/shaders/glsl/Ragescreen.fp b/shaders/glsl/Ragescreen.fp new file mode 100644 index 000000000..1569c5ce1 --- /dev/null +++ b/shaders/glsl/Ragescreen.fp @@ -0,0 +1,20 @@ +#define TWOPI 6.28318530718 + +void main() +{ + vec2 uv = TexCoord; + vec3 col = texture(InputTexture,uv).rgb; + col *= vec3(.5)+texture(WarpTex,vec2(fract(uv.x+.1*sin(uv.y*TWOPI)),fract(uv.y-timer*.1))).rgb*xtrastr; + col *= vec3(.5)+texture(WarpTex,2.*vec2(fract(uv.x+.1*cos(uv.y*TWOPI)),fract(uv.y-timer*.1))).rgb*xtrastr; + for ( float i=0.; i<4.; i+=1. ) + { + float str = texture(NoiseTex,vec2(fract(timer*.2),fract(timer*.02))).x*.07; + str *= pow(xtrastr,1.5); + float p = distance(uv,vec2(.5)); + vec2 suv = (uv-.5)*(1.-(str*i*p))+.5; + col += texture(InputTexture,suv).rgb+pow(p,2.)*vec3(1.8,.2,0.)*xtrastr; + } + col /= 5.; + col *= vec3(1.2,.9,.7); + FragColor = vec4(col,1.0); +} diff --git a/sndinfo.txt b/sndinfo.txt index 5b02608c7..244aaa09e 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -551,6 +551,16 @@ misc/chat sounds/menu/chatsnd.ogg misc/chat2 sounds/menu/chatsnd.ogg misc/sundowner sounds/SUNDOWNER.ogg +misc/underwater sounds/general/uWater1a.ogg +misc/underslime sounds/general/uGoop1.ogg +misc/underlava sounds/general/uLava1.ogg +misc/waterenter sounds/general/DIVE.ogg +misc/slimeenter sounds/general/GoopJ1.ogg +misc/lavaenter sounds/general/LavaJ1.ogg +misc/waterexit sounds/general/surfaceb.ogg +misc/slimeexit sounds/general/GoopE1.ogg +misc/lavaexit sounds/general/LavaE1.ogg + armor/blastsuit sounds/items/blastsuit.ogg armor/wararmor sounds/items/wararmor.ogg armor/hit1 sounds/items/hullhit.ogg @@ -575,8 +585,9 @@ powerup/invinciballend sounds/items/invincioff.ogg powerup/ragekit sounds/items/ragekiton.ogg powerup/ragekitact sounds/items/ragekitact.ogg powerup/ragekithit sounds/items/ragekithit.ogg -powerup/ragekitend sounds/items/ragekitend.ogg +powerup/ragekitend sounds/items/ragekitoff.ogg powerup/omnisight sounds/items/omnisight.ogg +powerup/embiggener sounds/items/embiggen.ogg lamp/on sounds/items/lampon.ogg lamp/off sounds/items/lampoff.ogg diff --git a/sounds/general/DIVE.ogg b/sounds/general/DIVE.ogg new file mode 100644 index 000000000..6d65b128e Binary files /dev/null and b/sounds/general/DIVE.ogg differ diff --git a/sounds/general/GoopE1.ogg b/sounds/general/GoopE1.ogg new file mode 100644 index 000000000..14a1ae39d Binary files /dev/null and b/sounds/general/GoopE1.ogg differ diff --git a/sounds/general/GoopJ1.ogg b/sounds/general/GoopJ1.ogg new file mode 100644 index 000000000..be310ea68 Binary files /dev/null and b/sounds/general/GoopJ1.ogg differ diff --git a/sounds/general/LavaE1.ogg b/sounds/general/LavaE1.ogg new file mode 100644 index 000000000..778a69d9a Binary files /dev/null and b/sounds/general/LavaE1.ogg differ diff --git a/sounds/general/LavaJ1.ogg b/sounds/general/LavaJ1.ogg new file mode 100644 index 000000000..7a9137b9b Binary files /dev/null and b/sounds/general/LavaJ1.ogg differ diff --git a/sounds/general/surfaceb.ogg b/sounds/general/surfaceb.ogg new file mode 100644 index 000000000..94eb6bbba Binary files /dev/null and b/sounds/general/surfaceb.ogg differ diff --git a/sounds/general/uGoop1.ogg b/sounds/general/uGoop1.ogg new file mode 100644 index 000000000..db0597b4a Binary files /dev/null and b/sounds/general/uGoop1.ogg differ diff --git a/sounds/general/uLava1.ogg b/sounds/general/uLava1.ogg new file mode 100644 index 000000000..be669ea84 Binary files /dev/null and b/sounds/general/uLava1.ogg differ diff --git a/sounds/general/uWater1a.ogg b/sounds/general/uWater1a.ogg new file mode 100644 index 000000000..0bae2dcea Binary files /dev/null and b/sounds/general/uWater1a.ogg differ diff --git a/sounds/items/embiggen.ogg b/sounds/items/embiggen.ogg new file mode 100644 index 000000000..a4716e429 Binary files /dev/null and b/sounds/items/embiggen.ogg differ diff --git a/textures/ragewarp.png b/textures/ragewarp.png new file mode 100644 index 000000000..641140506 Binary files /dev/null and b/textures/ragewarp.png differ diff --git a/zscript/swwm_ammo.zsc b/zscript/swwm_ammo.zsc index c429c0280..0950c7dc2 100644 --- a/zscript/swwm_ammo.zsc +++ b/zscript/swwm_ammo.zsc @@ -931,6 +931,7 @@ Class HammerspaceEmbiggener : Inventory { override Inventory CreateCopy( Actor other ) { + other.A_StartSound("powerup/embiggener",CHAN_ITEMEXTRA); // Find every unique type of ammoitem. Give it to the player if // he doesn't have it already, and increase its maximum capacity. for ( int i=0; i flashes; transient Array lastlines; + SWWMCombatTracker trackers; + SWWMScoreObj scorenums, damnums; + SWWMInterest intpoints; + int trackers_cnt, scorenums_cnt, damnums_cnt, intpoints_cnt; bool tookdamage[MAXPLAYERS]; int spreecount[MAXPLAYERS]; int lastkill[MAXPLAYERS]; @@ -1224,6 +1336,7 @@ Class SWWMHandler : EventHandler int lastitemcount[MAXPLAYERS]; transient CVar mutevoice; + transient ui CVar useshaders; static int AddOneliner( String type, int level, int delay = 5 ) { @@ -1420,10 +1533,16 @@ Class SWWMHandler : EventHandler else if ( e.IsSaveGame || e.IsReopen ) { // clear all floating numbers - let ti = ThinkerIterator.Create("SWWMScoreObj",Thinker.STAT_USER); - Thinker t; - while ( t = ti.Next() ) - t.Destroy(); + for ( SWWMScoreObj sc=scorenums; sc; sc=sc.Next ) + sc.lifespan = 0; + for ( SWWMScoreObj sc=damnums; sc; sc=sc.Next ) + sc.lifespan = 0; + // restore underwater sounds for players + for ( int i=0; i MainQueue, PickupQueue, FullHistory; - Array targets; // healthbars - Array scoreobjects; // floating scores - Array interesting; // points of interest for omnisight + // sorted arrays of various elements + Array intpoints; + Array scoreobjs; + Array trackers; + + // the event handler, holding all sorts of stuff + SWWMHandler hnd; // client cvars transient CVar safezone, maxchat[2], maxpick, chatduration, msgduration, pickduration, chatcol, teamcol, obitcol, critcol, pickcol, targetter, healthnums, scorenums, scorebonus, targettag; @@ -141,11 +145,13 @@ Class SWWMStatusBar : BaseStatusBar private bool CmpTarget( SWWMCombatTracker a, SWWMCombatTracker b ) { + if ( !a || !b ) return true; return (a.myplayer && !b.myplayer); } private bool CmpScore( SWWMScoreObj a, SWWMScoreObj b ) { + if ( !a || !b ) return true; int srt[4] = { Font.CR_GOLD, Font.CR_FIRE, Font.CR_GREEN, Font.CR_RED }; int s1 = 0, s2 = 0; for ( int i=0; i<3; i++ ) @@ -158,6 +164,7 @@ Class SWWMStatusBar : BaseStatusBar private bool CmpInterest( SWWMInterest a, SWWMInterest b ) { + if ( !a || !b ) return true; return a.type < b.type; } @@ -193,99 +200,9 @@ Class SWWMStatusBar : BaseStatusBar MainQueue.Delete(i); i--; } - // update omnisight stuff - if ( CPlayer.mo.FindInventory("Omnisight") ) - { - interesting.Clear(); - let ii = ThinkerIterator.Create("SWWMInterest",Thinker.STAT_USER); - SWWMInterest poi; // :3 - while ( poi = SWWMInterest(ii.Next()) ) interesting.Push(poi); - // sort by distance - for ( int i=0; i 0) && (CmpInterest(interesting[k-1],interesting[k]) || CmpDist(interesting[k-1].pos,interesting[k].pos)) ) - { - SWWMInterest tmp = interesting[k]; - interesting[k] = interesting[k-1]; - interesting[k-1] = tmp; - k--; - } - j++; - } - } - } - // update target stuff - targets.Clear(); - if ( targetter.GetBool() ) - { - let ti = ThinkerIterator.Create("SWWMCombatTracker",Thinker.STAT_USER); - SWWMCombatTracker ct; - int extratime = 35; - if ( CPlayer.mo.FindInventory("Omnisight") ) extratime += 105; - while ( ct = SWWMCombatTracker(ti.Next()) ) - { - // ignore player unless chasecamming - if ( (ct.mytarget == players[consoleplayer].mo) && (players[consoleplayer].Camera == players[consoleplayer].mo) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue; - if ( ct.myplayer && deathmatch ) continue; // no players in dm - if ( level.maptime > ct.updated+extratime ) continue; - targets.Push(ct); - } - // sort by distance (give priority to players) - for ( int i=0; i 0) && (CmpTarget(targets[k-1],targets[k]) || CmpDist(targets[k-1].pos,targets[k].pos)) ) - { - SWWMCombatTracker tmp = targets[k]; - targets[k] = targets[k-1]; - targets[k-1] = tmp; - k--; - } - j++; - } - } - // trim size, to unclutter - if ( targets.Size() > 40 ) targets.Resize(40); - } - // update floating scores - scoreobjects.Clear(); - let si = ThinkerIterator.Create("SWWMScoreObj",Thinker.STAT_USER); - SWWMScoreObj so; - while ( so = SWWMScoreObj(si.Next()) ) - { - if ( ((so.tcolor == Font.CR_RED) || (so.tcolor == Font.CR_GREEN)) && (!healthnums.GetBool() || (level.Vec3Diff(viewpos,so.pos).length() > 2000)) ) continue; - else if ( !scorenums.GetBool() || ((so.tcolor != Font.CR_GOLD) && !scorebonus.GetBool()) ) continue; - scoreobjects.Push(so); - // prevent slowdowns if hurting too many enemies at once - if ( scoreobjects.Size() >= 100 ) break; - } - // sort by distance - for ( int i=0; i 0) && (CmpScore(scoreobjects[k-1],scoreobjects[k]) || CmpDist(scoreobjects[k-1].pos,scoreobjects[k].pos)) ) - { - SWWMScoreObj tmp = scoreobjects[k]; - scoreobjects[k] = scoreobjects[k-1]; - scoreobjects[k-1] = tmp; - k--; - } - j++; - } - } // update interpolators HealthInter.Update(CPlayer.health); - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); ScoreInter.Update(SWWMCredits.Get(CPlayer)); let d = Demolitionist(CPlayer.mo); if ( d ) @@ -301,6 +218,98 @@ Class SWWMStatusBar : BaseStatusBar // let weapons update their own interpolators if ( CPlayer.ReadyWeapon is 'SWWMWeapon' ) SWWMWeapon(CPlayer.ReadyWeapon).HudTick(); + bool thesight = CPlayer.mo.FindInventory("Omnisight"); + if ( thesight ) + { + // update omnisight stuff + if ( intpoints.Size() != hnd.intpoints_cnt ) + intpoints.Resize(hnd.intpoints_cnt); + int i = 0; + for ( SWWMInterest poi=hnd.intpoints; poi; poi=poi.next ) + intpoints[i++] = poi; + // sort by distance + for ( int i=0; i 0) && (CmpInterest(intpoints[k-1],intpoints[k]) || CmpDist(intpoints[k-1].pos,intpoints[k].pos)) ) + { + SWWMInterest tmp = intpoints[k]; + intpoints[k] = intpoints[k-1]; + intpoints[k-1] = tmp; + k--; + } + j++; + } + } + } + if ( targetter.GetBool() ) + { + // update target stuff + if ( trackers.Size() != hnd.trackers_cnt ) + trackers.Resize(hnd.trackers_cnt); + int i = 0, actual = 0; + for ( SWWMCombatTracker trk=hnd.trackers; trk; trk=trk.next ) + { + actual++; + // ignore player unless chasecamming + if ( (trk.mytarget == players[consoleplayer].mo) && (players[consoleplayer].Camera == players[consoleplayer].mo) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue; + if ( trk.myplayer && deathmatch ) continue; // no players in dm + int mtime = 35; + if ( thesight && (trk.lasthealth > 0) ) mtime += 105; + if ( level.maptime > trk.updated+mtime ) continue; + trackers[i++] = trk; + } + // squeeze if some were discarded + if ( i != hnd.trackers_cnt ) + trackers.Resize(i); + // sort by distance (give priority to players) + for ( int i=0; i 0) && (CmpTarget(trackers[k-1],trackers[k]) || CmpDist(trackers[k-1].pos,trackers[k].pos)) ) + { + SWWMCombatTracker tmp = trackers[k]; + trackers[k] = trackers[k-1]; + trackers[k-1] = tmp; + k--; + } + j++; + } + } + } + // update floating scores, adding the scorenums first, then the damnums + int total_sz = hnd.scorenums_cnt; + total_sz += min(100,hnd.damnums_cnt); + if ( scoreobjs.Size() != total_sz ) + scoreobjs.Resize(total_sz); + int i = 0; + for ( SWWMScoreObj scr=hnd.scorenums; scr; scr=scr.next ) + scoreobjs[i++] = scr; + for ( SWWMScoreObj scr=hnd.damnums; scr && (i 0) && (CmpScore(scoreobjs[k-1],scoreobjs[k]) || CmpDist(scoreobjs[k-1].pos,scoreobjs[k].pos)) ) + { + SWWMScoreObj tmp = scoreobjs[k]; + scoreobjs[k] = scoreobjs[k-1]; + scoreobjs[k-1] = tmp; + k--; + } + j++; + } + } } override void Init() @@ -339,6 +348,7 @@ Class SWWMStatusBar : BaseStatusBar gl_proj = new("swwmLe__GLScreen"); sw_proj = new("swwmLe__SWScreen"); PrepareProjection(); + hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); } static private string FormatDist( double dist ) @@ -350,6 +360,8 @@ Class SWWMStatusBar : BaseStatusBar private void DrawTarget() { + // don't draw when dead or with automap open + if ( (CPlayer.health <= 0) || automapactive ) return; if ( !targettag ) targettag = CVar.GetCVar('swwm_targettags',players[consoleplayer]); viewport.FromHud(); proj.CacheResolution(); @@ -362,17 +374,19 @@ Class SWWMStatusBar : BaseStatusBar String tag; if ( thesight ) { - for ( int i=0; i 40 ) break; + Vector3 tdir = Level.Vec3Diff(ViewPos,targ.prevpos*(1.-fractic)+targ.pos*fractic); proj.ProjectWorldPos(ViewPos+tdir); Vector2 npos = proj.ProjectToNormal(); if ( !proj.IsInFront() ) continue; Vector2 vpos = viewport.SceneToWindow(npos); - tag = targets[i].mytag; + tag = targ.mytag; int mtime = 35; - if ( thesight && (targets[i].lasthealth > 0) ) mtime += 105; - double alph = clamp(((targets[i].updated+mtime)-level.maptime)/35.,0.,1.); + if ( thesight && (targ.lasthealth > 0) ) mtime += 105; + double alph = clamp(((targ.updated+mtime)-level.maptime)/35.,0.,1.); Vector2 barsiz = TexMan.GetScaledSize(EnemyBTex); barsiz.x *= hs.x; barsiz.y *= hs.y; Vector2 barpos = vpos-(barsiz/2.); barpos.y -= 16.; - if ( targettag.GetBool() || targets[i].myplayer && (tag != "") ) + if ( targettag.GetBool() || targ.myplayer && (tag != "") ) Screen.DrawText(mMiniwiFont.mFont,Font.CR_WHITE,(barpos.x+barsiz.x/2.-(mMiniwiFont.mFont.StringWidth(tag)*hs.x)/2.)/hs.x,(barpos.y-mMiniwiFont.mFont.GetHeight()*hs.y)/hs.y,tag,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); Screen.DrawTexture(EnemyBTex,false,barpos.x/hs.x,barpos.y/hs.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); - int ht = clamp(targets[i].intp.GetValue(),0,targets[i].maxhealth*10); - int hw = int((min(ht,targets[i].maxhealth)*50.)/targets[i].maxhealth); - if ( targets[i].mytarget && (targets[i].mytarget.bInvulnerable || (targets[i].myplayer && (targets[i].myplayer.cheats&(CF_GODMODE|CF_GODMODE2))) || targets[i].mytarget.FindInventory("InvinciballPower")) ) + int ht = clamp(targ.intp.GetValue(),0,targ.maxhealth*10); + int hw = int((min(ht,targ.maxhealth)*50.)/targ.maxhealth); + if ( targ.mytarget && (targ.mytarget.bInvulnerable || (targ.myplayer && (targ.myplayer.cheats&(CF_GODMODE|CF_GODMODE2))) || targ.mytarget.FindInventory("InvinciballPower")) ) { Screen.DrawTexture(EnemyHTex[4],false,(barpos.x+2*hs.x)/hs.x,(barpos.y+2*hs.y)/hs.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw); continue; } Screen.DrawTexture(EnemyHTex[0],false,(barpos.x+2*hs.x)/hs.x,(barpos.y+2*hs.y)/hs.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw); - if ( ht > targets[i].maxhealth ) + if ( ht > targ.maxhealth ) { - hw = int((min(ht-targets[i].maxhealth,targets[i].maxhealth)*50.)/targets[i].maxhealth); + hw = int((min(ht-targ.maxhealth,targ.maxhealth)*50.)/targ.maxhealth); Screen.DrawTexture(EnemyHTex[1],false,(barpos.x+2*hs.x)/hs.x,(barpos.y+2*hs.y)/hs.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw); } - if ( ht > targets[i].maxhealth*2 ) + if ( ht > targ.maxhealth*2 ) { - hw = int((min(ht-targets[i].maxhealth*2,targets[i].maxhealth*3)*50.)/(targets[i].maxhealth*3)); + hw = int((min(ht-targ.maxhealth*2,targ.maxhealth*3)*50.)/(targ.maxhealth*3)); Screen.DrawTexture(EnemyHTex[2],false,(barpos.x+2*hs.x)/hs.x,(barpos.y+2*hs.y)/hs.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw); } - if ( ht > targets[i].maxhealth*5 ) + if ( ht > targ.maxhealth*5 ) { - hw = int((min(ht-targets[i].maxhealth*5,targets[i].maxhealth*5)*50.)/(targets[i].maxhealth*5)); + hw = int((min(ht-targ.maxhealth*5,targ.maxhealth*5)*50.)/(targ.maxhealth*5)); Screen.DrawTexture(EnemyHTex[3],false,(barpos.x+2*hs.x)/hs.x,(barpos.y+2*hs.y)/hs.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw); } } // floating kill scores and others - for ( int i=0; i 0 ) tag.AppendFormat(" x%d",scoreobjects[i].score); + tag = StringTable.Localize(snum.str); + if ( snum.score == int.max ) tag.AppendFormat(" %s",StringTable.Localize("$SWWM_MAX")); + else if ( snum.score > 0 ) tag.AppendFormat(" x%d",snum.score); } - else tag = String.Format("%+d",scoreobjects[i].score); - double alph = clamp((scoreobjects[i].lifespan+fractic)/35.,0.,1.); + else tag = String.Format("%+d",snum.score); + double alph = clamp((snum.lifespan+fractic)/35.,0.,1.); Vector2 fo = (0,0); - if ( scoreobjects[i].tcolor == Font.CR_RED ) + if ( snum.tcolor == Font.CR_RED ) { // damage falls down - int initspd = (128-scoreobjects[i].seed); + int initspd = (128-snum.seed); if ( initspd >= 0 && initspd < 32 ) initspd = 32; if ( initspd < 0 && initspd > -32 ) initspd = -32; - int boostup = 64+scoreobjects[i].seed2/2; - fo.x = (.05*initspd)*((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**.8); - fo.y = -((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**1.5)+boostup*sin((90./scoreobjects[i].initialspan)*(level.maptime+fractic-scoreobjects[i].starttic)); + int boostup = 64+snum.seed2/2; + fo.x = (.05*initspd)*((snum.initialspan-(snum.lifespan-fractic))**.8); + fo.y = -((snum.initialspan-(snum.lifespan-fractic))**1.5)+boostup*sin((90./snum.initialspan)*(level.maptime+fractic-snum.starttic)); } - else if ( scoreobjects[i].tcolor == Font.CR_GREEN ) + else if ( snum.tcolor == Font.CR_GREEN ) { // health falls up (?) - int initspd = (128-scoreobjects[i].seed); + int initspd = (128-snum.seed); if ( initspd >= 0 && initspd < 32 ) initspd = 32; if ( initspd < 0 && initspd > -32 ) initspd = -32; - int boostup = 16+scoreobjects[i].seed2/4; - fo.x = (.15*initspd)*((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**.6); - fo.y = ((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**1.2)-boostup*sin((90./scoreobjects[i].initialspan)*(level.maptime+fractic-scoreobjects[i].starttic)); + int boostup = 16+snum.seed2/4; + fo.x = (.15*initspd)*((snum.initialspan-(snum.lifespan-fractic))**.6); + fo.y = ((snum.initialspan-(snum.lifespan-fractic))**1.2)-boostup*sin((90./snum.initialspan)*(level.maptime+fractic-snum.starttic)); } - else fo.y = scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic); // score rises linearly - fo.y += scoreobjects[i].ofs*mMiniwiFont.mFont.GetHeight(); - Screen.DrawText(mMiniwiFont.mFont,scoreobjects[i].tcolor,(vpos.x-hs.x*(fo.x+mMiniwiFont.mFont.StringWidth(tag)/2.))/hs.x,(vpos.y-hs.y*(fo.y+(mMiniwiFont.mFont.GetHeight()/2.)))/hs.y,tag,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + else fo.y = snum.initialspan-(snum.lifespan-fractic); // score rises linearly + fo.y += snum.ofs*mMiniwiFont.mFont.GetHeight(); + Screen.DrawText(mMiniwiFont.mFont,snum.tcolor,(vpos.x-hs.x*(fo.x+mMiniwiFont.mFont.StringWidth(tag)/2.))/hs.x,(vpos.y-hs.y*(fo.y+(mMiniwiFont.mFont.GetHeight()/2.)))/hs.y,tag,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); } } diff --git a/zscript/swwm_menu.zsc b/zscript/swwm_menu.zsc index 3f2075436..f740c085e 100644 --- a/zscript/swwm_menu.zsc +++ b/zscript/swwm_menu.zsc @@ -306,7 +306,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu } return true; case MKEY_ENTER: - if ( (curtab == TAB_INVENTORY) && (invlist.Size() > 0) ) + if ( (curtab == TAB_INVENTORY) && (invlist.Size() > 0) && (sel0 < invlist.Size()) ) { // can't use this if ( invlist[sel0] is 'Ammo' ) return true; @@ -317,7 +317,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu checkuse = gametic+1; EventHandler.SendNetworkEvent(String.Format("swwmuseitem.%s",invlist[sel0].GetClassName()),consoleplayer); } - else if ( (curtab == TAB_STORE) && (storelist.Size() > 0) ) + else if ( (curtab == TAB_STORE) && (storelist.Size() > 0) && (sel0 < storelist.Size()) ) { int moni = SWWMCredits.Get(players[consoleplayer]); let itm = GetDefaultByType(storelist[sel0]); @@ -359,7 +359,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu MenuSound("menu/democlose"); sub = false; } - else if ( lorelist.Size() > 0 ) + else if ( (lorelist.Size() > 0) && (sel0 < lorelist.Size()) ) { // mark as read if ( !lorelist[sel0].read ) @@ -440,7 +440,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu // alphabetically sorted inventory for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.Inv ) { - if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') ) continue; + if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) ) continue; String tag = inv.GetTag(); bool greater = false; for ( int i=0; i hsec.ceilingplane.ZAtPoint(pos.xy)) ) + headregion = hsec; + } + else // check 3D floors + { + for ( int i=0; i centerpos.z) ) continue; + if ( headpos.z <= ff_top ) + { + headregion = ff.model; + } + break; + } + } + int curunder = UNDER_NONE; + if ( headregion ) + { + switch ( headregion.damagetype ) + { + case 'Fire': + case 'Lava': + curunder = UNDER_LAVA; + break; + case 'Slime': + case 'Poison': + case 'PoisonCloud': + curunder = UNDER_SLIME; + break; + case 'Ice': + case 'Drowning': + default: + curunder = UNDER_WATER; + break; + } + } + if ( (curunder != lastunder) || restore ) + { + static const string undersnd[] = {"","misc/underwater","misc/underslime","misc/underlava"}; + static const string entersnd[] = {"","misc/waterenter","misc/slimeenter","misc/lavaenter"}; + static const string exitsnd[] = {"","misc/waterexit","misc/slimeexit","misc/lavaexit"}; + A_StopSound(CHAN_AMBEXTRA); + if ( curunder > UNDER_NONE ) + { + A_StartSound(undersnd[curunder],CHAN_AMBEXTRA,CHANF_LOOPING|CHANF_UI); + if ( !restore && (players[consoleplayer].Camera == self) ) + A_StartSound(entersnd[curunder],CHAN_FOOTSTEP,CHANF_OVERLAP|CHANF_UI); + } + if ( !restore && (lastunder > UNDER_NONE) && (players[consoleplayer].Camera == self) ) + A_StartSound(exitsnd[lastunder],CHAN_FOOTSTEP,CHANF_OVERLAP|CHANF_UI); + } + if ( curunder > UNDER_NONE ) + A_SoundVolume(CHAN_AMBEXTRA,(players[consoleplayer].Camera==self)?1.:0.); + lastunder = curunder; + } override void Tick() { Super.Tick(); if ( !player ) return; - double traveldist = level.Vec3Diff(oldpos,pos).length(); - if ( waterlevel < 3 ) + double traveldist = level.Vec3Diff(prev,pos).length(); + if ( waterlevel < 2 ) { if ( !player.onground || bNoGravity ) { @@ -309,8 +392,8 @@ Class Demolitionist : PlayerPawn mystats.grounddist += traveldist; } } - if ( traveldist > mystats.topspeed ) mystats.topspeed = traveldist; - oldpos = pos; + CheckUnderwaterAmb(); + if ( vel.length() > mystats.topspeed ) mystats.topspeed = vel.length(); if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player); if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player); if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) && (health > 0) ) @@ -334,7 +417,8 @@ Class Demolitionist : PlayerPawn if ( lastvelz < -10 ) A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP); if ( (player == players[consoleplayer]) && (lastvelz < -gruntspeed) && (mute.GetInt() < 4) ) A_StartSound(String.Format("voice/%s/grunt",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP); - A_Footstep(0,true,clamp(-lastvelz*0.05,0.01,1.0)); + if ( lastvelz < -1 ) + A_Footstep(0,true,clamp(-lastvelz*0.05,0.0,1.0)); } lastground = player.onground; lastvelz = prevvelz; @@ -381,7 +465,7 @@ Class Demolitionist : PlayerPawn double moveang = atan2(dir.y,dir.x); Vector3 sc = level.SphericalCoords(pos,a.pos,(moveang,0)); if ( abs(sc.x) > 60 ) continue; - // bosses and large monsters will stop the player + // large monsters will stop the player A_QuakeEx(1,1,1,3,0,128,"",QF_RELATIVE|QF_SCALEDOWN); a.A_StartSound("demolitionist/bump",CHAN_FOOTSTEP,CHANF_OVERLAP); if ( a.bDONTTHRUST || (a.Mass >= Mass*5) ) @@ -715,6 +799,11 @@ Class Demolitionist : PlayerPawn last_jump_held = level.maptime+1; } } + override void DeathThink() + { + // TODO reboot mechanic, death camera that doesn't move body + Super.DeathThink(); + } override void PlayIdle() { if ( !player ) diff --git a/zscript/swwm_powerup.zsc b/zscript/swwm_powerup.zsc index 565f43437..279c29961 100644 --- a/zscript/swwm_powerup.zsc +++ b/zscript/swwm_powerup.zsc @@ -136,6 +136,7 @@ Class GhostPower : PowerInvisibility { Super.InitEffect(); if ( !Owner ) return; + SWWMHandler.DoFlash(Owner,Color(96,224,192,255),20); DoEffect(); } override void EndEffect() @@ -144,6 +145,7 @@ Class GhostPower : PowerInvisibility if ( !Owner ) return; Owner.bNOTARGET = false; Owner.A_StartSound("powerup/ghostend",CHAN_ITEMEXTRA); + SWWMHandler.DoFlash(Owner,Color(96,224,192,255),20); if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_GHOSTARTI")); } @@ -334,6 +336,14 @@ Class GravityPower : Powerup } +Class GravityX : GhostArtifactX +{ + Default + { + RenderStyle "Normal"; + } +} + Class GravitySuppressor : Inventory { Mixin SWWMAutoUseFix; @@ -347,6 +357,15 @@ Class GravitySuppressor : Inventory else Owner.GiveInventory("GravityPower",1); return true; } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + tracer = Spawn("GravityX",pos); + tracer.angle = angle; + tracer.target = self; + tracer.FloatBobPhase = FloatBobPhase; + } + Default { Tag "$T_GRAVITYS"; @@ -439,6 +458,7 @@ Class InvinciballPower : Powerup { Actor l, snd; int lasteffect; + transient int lastpulse; Default { @@ -455,6 +475,8 @@ Class InvinciballPower : Powerup l = Spawn("InvinciballLight",Owner.pos); l.target = Owner; l.master = self; + lastpulse = max(lastpulse,gametic+35); + SWWMHandler.DoFlash(Owner,Color(96,255,64,0),20); } override void DoEffect() @@ -471,6 +493,7 @@ Class InvinciballPower : Powerup Super.EndEffect(); if ( !Owner ) return; Owner.A_StartSound("powerup/invinciballend",CHAN_ITEMEXTRA); + SWWMHandler.DoFlash(Owner,Color(96,255,64,0),20); if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_INVINCIBALL")); } @@ -489,11 +512,20 @@ Class InvinciballPower : Powerup SWWMHandler.DoFlash(Owner,Color(64,255,64,0),15); Owner.A_StartSound("powerup/invinciballhit",CHAN_POWERUP); lasteffect = level.maptime; + lastpulse = max(lastpulse,gametic+20); } } } } +Class InvinciballX : GhostArtifactX +{ + Default + { + RenderStyle "Normal"; + } +} + Class FuckingInvinciball : Inventory { Mixin SWWMAutoUseFix; @@ -510,10 +542,24 @@ Class FuckingInvinciball : Inventory Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); Owner.A_StartSound("misc/sundowner",CHAN_POWERUPEXTRA); let i = InvinciballPower(Owner.FindInventory("InvinciballPower")); - if ( i ) i.EffectTics = i.default.EffectTics; + if ( i ) + { + i.EffectTics = i.default.EffectTics; + i.lastpulse = max(i.lastpulse,gametic+35); + SWWMHandler.DoFlash(Owner,Color(96,255,64,0),20); + } else Owner.GiveInventory("InvinciballPower",1); return true; } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + tracer = Spawn("InvinciballX",pos); + tracer.angle = angle; + tracer.target = self; + tracer.FloatBobPhase = FloatBobPhase; + } + Default { Tag "$T_INVINCIBALL"; @@ -607,6 +653,7 @@ Class RagekitPower : Powerup { Actor l, snd; int lasteffect, lastrage; + transient int lastpulse; override double GetSpeedFactor() { @@ -629,6 +676,7 @@ Class RagekitPower : Powerup SWWMHandler.DoFlash(Owner,Color(64,255,0,0),30); Owner.A_QuakeEx(8,8,8,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.); lasteffect = int.min; + lastpulse = max(lastpulse,gametic+35); l = Spawn("RagekitLight",Owner.pos); l.target = Owner; l.master = self; @@ -647,6 +695,7 @@ Class RagekitPower : Powerup if ( (Owner.player == players[consoleplayer]) && (gametic > lastrage) && (CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() < 2) ) lastrage = SWWMHandler.AddOneliner("ragekit",2,5); Owner.A_QuakeEx(2,2,2,Random[Rage](1,2),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.5); + lastpulse = max(lastpulse,gametic+10); } } @@ -655,6 +704,8 @@ Class RagekitPower : Powerup Super.EndEffect(); if ( !Owner ) return; Owner.A_StartSound("powerup/ragekitend",CHAN_ITEMEXTRA); + SWWMHandler.DoFlash(Owner,Color(128,255,0,0),30); + Owner.A_QuakeEx(4,4,4,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.); if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_RAGEKIT")); } @@ -671,6 +722,7 @@ Class RagekitPower : Powerup lastrage = SWWMHandler.AddOneliner("ragekit",2,5); Owner.A_StartSound("powerup/ragekithit",CHAN_POWERUP); lasteffect = level.maptime; + lastpulse = max(lastpulse,gametic+35); } } else if ( passive ) @@ -678,6 +730,14 @@ Class RagekitPower : Powerup } } +Class RagekitX : GhostArtifactX +{ + Default + { + RenderStyle "Normal"; + } +} + Class Ragekit : Inventory { Mixin SWWMAutoUseFix; @@ -687,10 +747,25 @@ Class Ragekit : Inventory if ( pickup && !deathmatch ) return false; Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); let r = RagekitPower(Owner.FindInventory("RagekitPower")); - if ( r ) r.EffectTics = r.default.EffectTics; + if ( r ) + { + r.EffectTics = r.default.EffectTics; + SWWMHandler.DoFlash(Owner,Color(64,255,0,0),30); + Owner.A_QuakeEx(8,8,8,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.); + r.lastpulse = max(r.lastpulse,gametic+35); + } else Owner.GiveInventory("RagekitPower",1); return true; } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + tracer = Spawn("RagekitX",pos); + tracer.angle = angle; + tracer.target = self; + tracer.FloatBobPhase = FloatBobPhase; + } + Default { Tag "$T_RAGEKIT"; diff --git a/zscript/swwm_shot.zsc b/zscript/swwm_shot.zsc index 35929138b..dc85a0dae 100644 --- a/zscript/swwm_shot.zsc +++ b/zscript/swwm_shot.zsc @@ -525,7 +525,7 @@ Class SaltLight : PaletteLight { Tag "SaltTail"; ReactionTime 30; - Args 0,0,0,80; + Args 0,0,0,120; } } Class SaltLight2 : PaletteLight @@ -534,7 +534,7 @@ Class SaltLight2 : PaletteLight { Tag "SaltExpl"; ReactionTime 30; - Args 0,0,0,80; + Args 0,0,0,70; } } @@ -543,7 +543,7 @@ Class SaltImpact : Actor Default { Obituary "$O_SPREADGUN_BLUE"; - DamageType "Salt"; + DamageType "Electricity"; RenderStyle "Add"; Radius 0.1; Height 0; @@ -614,7 +614,7 @@ Class SaltBeam : Actor Default { Obituary "$O_SPREADGUN_BLUE"; - DamageType "Salt"; + DamageType "Electricity"; RenderStyle "Add"; Radius 0.1; Height 0; @@ -645,7 +645,7 @@ Class SaltBeam : Actor let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos); b.A_CheckTerrain(); } - for ( int i=8; i 20) && !Random[Spreadgun](0,800/args[0]) ) + { + let i = Spawn("SaltImpact",level.Vec3Offset(pos,x*32)); + i.angle = atan2(x.y,x.x); + i.pitch = asin(-x.z); + i.target = target; + return; + } // next beam - if ( !(special2%2) && !Random[Spreadgun](0,2) ) + if ( !(special2%2) && !Random[Spreadgun](0,4) ) Spawn("SaltLight",level.Vec3Offset(pos,x*16)); let next = Spawn("SaltBeam",level.Vec3Offset(pos,x*32)); double a = FRandom[Spreadgun](0,360), s = FRandom[Spreadgun](0,.06); @@ -698,6 +706,7 @@ Class SaltBeam : Actor next.pitch = asin(-dir.z); next.target = target; next.special2 = (special2+1)%10; + next.args[0] = args[0]+1; next.SetStateLabel("TrailSpawn"); } @@ -714,10 +723,10 @@ Class SaltBeam : Actor A_FadeOut(.04); if ( Random[Spreadgun](-2,GetAge()/10) == 0 ) { - SWWMHandler.DoBlast(self,48,5000,target); - A_Explode(5,48,0); + SWWMHandler.DoBlast(self,64,5000,target); + A_Explode(5,64,0); } - if ( (special2 || GetAge()) && !special1 ) SpreadOut(); + if ( ((special2%5) || GetAge()) && !special1 ) SpreadOut(); } States @@ -1311,8 +1320,7 @@ Class Spreadgun : SWWMWeapon { Super.ModifyDropAmount(dropamount); // toss some ammo while we're at it - let am = (Class)(GetReplacement("Shell")); - A_DropItem(am,Random[Spreadgun](3,5)); + A_DropItem(Random[Spreadgun](0,2)?"RedShell":"GreenShell",Random[Spreadgun](1,3)); } Default