diff --git a/PriceTable.md b/PriceTable.md index 95d7d3f71..375b5bf68 100644 --- a/PriceTable.md +++ b/PriceTable.md @@ -73,6 +73,7 @@ be purchased from the store, they have to be found in maps. Item | Price ------------- | ----- Health Nugget | 1200 + Froggy Chair | 1440 H.Tetrahedron | 3000 H.Cube | 8000 Refresher | 160000 diff --git a/README.md b/README.md index b29027458..1c478c767 100644 --- a/README.md +++ b/README.md @@ -374,20 +374,20 @@ The upper cap for these is 200%. ### Blast Suit, replaces Green Armor, Silver Shield, Mesh Armor -The blast suit is a nice little light armor which provides a 75% reduction to +The blast suit is a nice little light armor which provides a 30% reduction to damage and an additional 50% to splash damage. -Can handle a total of 200 damage before breaking. +Can handle a total of 150 damage before breaking. ### War Armor, replaces Blue Armor, Enchanted Shield, Falcon Shield Decent armor, protects very well against all damage. Reduction factors are as follows: - * 90% reduction for elemental (fire, ice, electric, etc.) - * 80% reduction to everything else + * 80% reduction for elemental (fire, ice, electric, etc.) + * 50% reduction to everything else * 70% reduction for all splash damage (multiplicative on top of the other two) -The armor can eat up a total of 600 damage before breaking. +The armor can eat up a total of 250 damage before breaking. ### Grilled Cheese Sandwich, replaces Megasphere, Morph Ovum, Platinum Helm diff --git a/cvarinfo.txt b/cvarinfo.txt index 5fae99c13..22c7b9e23 100644 --- a/cvarinfo.txt +++ b/cvarinfo.txt @@ -32,3 +32,5 @@ user bool swwm_fly6dof = true; // flying uses 6dof movement, toggleable for th 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 +server bool swwm_revive = false; // allows the player to do an "emergency reboot" when dying +server int swwm_revivecooldown = 30; // cooldown after using a revive, in seconds (0: no limit) diff --git a/decaldef.txt b/decaldef.txt index 93efac272..fd5015461 100644 --- a/decaldef.txt +++ b/decaldef.txt @@ -25,6 +25,34 @@ decalgroup WallCrack WallCrack2 1 } +decal BigWallCrack1 +{ + pic WallCrk1 + translucent 0.75 + shade "00 00 00" + x-scale 1.1 + y-scale 1.1 + randomflipx + randomflipy +} +decal BigWallCrack2 +{ + pic WallCrk2 + translucent 0.75 + shade "00 00 00" + x-scale 1.1 + y-scale 1.1 + randomflipx + randomflipy +} + +decalgroup BigWallCrack +{ + BigWallCrack1 1 + BigWallCrack2 1 +} + + decal SaltMark { pic saltmark diff --git a/gldefs.pp b/gldefs.pp index af9dbd7e6..b43120a38 100644 --- a/gldefs.pp +++ b/gldefs.pp @@ -19,3 +19,20 @@ HardwareShader PostProcess scene Uniform float timer Uniform float xtrastr } + +HardwareShader PostProcess scene +{ + Name "Glitch" + Shader "shaders/glsl/Glitch.frag" 330 + Uniform float Timer + Uniform float str1 + Uniform float str2 +} +HardwareShader PostProcess scene +{ + Name "Grain" + Shader "shaders/glsl/Grain.frag" 330 + Uniform float Timer + Uniform float ni + Texture NoiseTexture "textures/rgbnoise.png" +} diff --git a/gldefs.shinemaps b/gldefs.shinemaps index b915f179a..6539a894b 100644 --- a/gldefs.shinemaps +++ b/gldefs.shinemaps @@ -38,3 +38,7 @@ HardwareShader Texture "models/silvermap.png" { Shader "shaders/glsl/Shinemap.fp" } +HardwareShader Texture "models/leadmap.png" +{ + Shader "shaders/glsl/Shinemap.fp" +} diff --git a/graphics/HUD/Icons/I_Revive.png b/graphics/HUD/Icons/I_Revive.png new file mode 100644 index 000000000..6766e6907 Binary files /dev/null and b/graphics/HUD/Icons/I_Revive.png differ diff --git a/language.txt b/language.txt index 3e35fb509..c4dd2de9a 100644 --- a/language.txt +++ b/language.txt @@ -70,6 +70,9 @@ SWWM_SCOREBONUS = "Show Score Bonuses"; SWWM_EARBUSTER = "Reduce Wallbuster Loudness"; SWWM_SHADERS = "Use Screen Shaders"; SWWM_OTHERVOICE = "Co-op Voice"; +SWWM_REVIVE = "Emergency Reboot System"; +SWWM_REVIVECOOLDOWN = "Reboot Cooldown"; +SWWM_UNLIMITED = "Unlimited"; TOOLTIP_SWWM_VOICETYPE = "Sets the voice pack for the player."; TOOLTIP_SWWM_MUTEVOICE = "Control what gets muted, if you'd rather have a more silent protagonist."; TOOLTIP_SWWM_FLASHSTRENGTH = "Screen flashes usually happen when firing some weapons, you can lower this if these effects are harmful for you."; @@ -96,6 +99,8 @@ TOOLTIP_SWWM_SCOREBONUS = "Show additional bonus strings above score numbers (e. TOOLTIP_SWWM_EARBUSTER = "The Wallbuster's firing sounds may be painfully loud at times. This setting will limit the loudness to something more bearable."; TOOLTIP_SWWM_SHADERS = "Use postprocess shaders for things like powerups and the Silver Bullet scope. You can disable this if you'd prefer something lighter on the eyes."; TOOLTIP_SWWM_OTHERVOICE = "Allows you to hear other player's voice lines, provided you're close enough."; +TOOLTIP_SWWM_REVIVE = "Allows the player to get back up after dying by pressing Fire. Has a configurable cooldown."; +TOOLTIP_SWWM_REVIVECOOLDOWN = "Time in seconds of downtime after using a reboot, where if you die again, you can't get back up. Set to 0 to allow unlimited reboots."; // knowledge base SWWM_COMINGSOON = "(coming soon)"; SWWM_MISSTAB = "Mission"; @@ -116,6 +121,7 @@ SWWM_STATTDEALT = "Highest Damage Dealt: "; SWWM_STATDTAKEN = "Total Damage Taken: "; SWWM_STATTTAKEN = "Highest Damage Taken: "; SWWM_STATMKILL = "Highest Kill Combo: "; +SWWM_STATSKILL = "Longest Untouchable Spree: "; SWWM_STATFAVWEAP = "Favorite Weapon: "; SWWM_STATHISCORE = "Highest Score: "; SWWM_INVTAB = "Inventory"; @@ -305,10 +311,10 @@ SWWM_LORETXT_BLASTSUIT = "Summary: A simple vest designed to protect against explosions. The main body is made of very elastic nanofabrics, and should provide adequate protection against small arms. The plating is made of a highly shock-absorbing proprietary alloy.\n" "\n" "Protection:\n" -" - 75% reduction to all types of damage.\n" +" - 30% reduction to all types of damage.\n" " - Additional 50% reduction to explosions.\n" "\n" -"Durability: Can absorb a total of 200 units of damage before breaking.\n" +"Durability: Can absorb a total of 150 units of damage before breaking.\n" "\n" "Addendum: The blast suit can be worn under other armor."; SWWM_LORETAG_CANDYGUN = "Candy Gun"; @@ -1347,11 +1353,11 @@ SWWM_LORETXT_WARARMOR = "Summary: Very robust, durable plate armor. Made of the finest almasteel in Devanikna. Originally commissioned for the third entry in Zanaveth's Instant Action arena events.\n" "\n" "Protection:\n" -" - 90% reduction to heat, cold, electricity, slime and other elemental types.\n" -" - 80% reduction to all other types of damage.\n" +" - 80% reduction to heat, cold, electricity, slime and other elemental types.\n" +" - 50% reduction to all other types of damage.\n" " - Additional 70% reduction to explosions.\n" "\n" -"Durability: Can absorb a total of 600 units of damage before breaking.\n" +"Durability: Can absorb a total of 250 units of damage before breaking.\n" "\n" "Addendum: Worn over a blast suit, you're pretty much ready for anything that could be thrown at you."; SWWM_LORETAG_YNYKRON = "Ynykron Artifact"; @@ -1690,6 +1696,11 @@ D_INVINCIBALL = "You are no longer invincible."; D_RAGEKIT = "The Ragekit has ragequit."; D_REFRESHER = "The Refresher boost has ended."; D_WARARMOR = "The War Armor is no more."; +SWWM_URDED = "Demolitionist Unit \"%s\" has fallen"; +SWWM_URDED2 = "Press \cfUse\c- to restart from the most recent save"; +SWWM_URDED3 = "...or press \cfFire\c- to attempt an emergency reboot"; +SWWM_REFAIL = "EMERGENCY REBOOT FAILED - NOT ENOUGH AUXILIARY POWER AVAILABLE"; +D_REFAIL = "The Emergency Reboot System has recharged completely."; // targetter SWWM_OVERKILL = "Overkill"; SWWM_MULTIKILL = "Multi Kill"; @@ -2028,6 +2039,9 @@ SWWM_SCOREBONUS = "Mostrar Bonificaciones de Puntuación"; SWWM_EARBUSTER = "Reducir Estruendo del Wallbuster"; SWWM_SHADERS = "Usar Shaders de Pantalla"; SWWM_OTHERVOICE = "Voz en Cooperativo"; +SWWM_REVIVE = "Sistema de Reinicio de Emergencia"; +SWWM_REVIVECOOLDOWN = "Tiempo de recarga de Reinicio"; +SWWM_UNLIMITED = "Ilimitado"; TOOLTIP_SWWM_VOICETYPE = "Selecciona el pack de voz para el jugador."; TOOLTIP_SWWM_MUTEVOICE = "Controla lo que se mutea, si prefieres tener un protagonista más silencioso."; TOOLTIP_SWWM_FLASHSTRENGTH = "Los destellos en pantalla suelen ocurrir al disparar algunas armas, puedes reducirlo si este tipo de effectos te causan malestar."; @@ -2054,6 +2068,8 @@ TOOLTIP_SWWM_SCOREBONUS = "Muestra textos de bonificación extra sobre los punto TOOLTIP_SWWM_EARBUSTER = "Los sonidos de disparo del Wallbuster pueden a veces ser dolorosamente estridentes. Esta opción limitará la intensidad a un valor más soportable."; TOOLTIP_SWWM_SHADERS = "Usa shaders de postprocesado para cosas como powerups y la mira del Silver Bullet. Puedes desactivar esto si prefieres algo más ligero para la vista."; TOOLTIP_SWWM_OTHERVOICE = "Te permite oír los comentarios de voz de otros jugadores, si estás lo suficientemente cerca."; +TOOLTIP_SWWM_REVIVE = "Permite al jugador volver a levantarse tras morir pulsando Fuego. Tiene un cooldown configurable."; +TOOLTIP_SWWM_REVIVECOOLDOWN = "Tiempo en segundos tras reiniciar, durante el cual si mueres otra vez, no puedes volver a levantarte. Pon a 0 para permitir reinicios ilimitados."; // knowledge base SWWM_COMINGSOON = "(próximamente)"; SWWM_MISSTAB = "Misión"; @@ -2074,6 +2090,7 @@ SWWM_STATTDEALT = "Mayor Daño Infligido: "; SWWM_STATDTAKEN = "Daño Total Recibido: "; SWWM_STATTTAKEN = "Mayor Daño Recibido: "; SWWM_STATMKILL = "Mayor Racha: "; +SWWM_STATSKILL = "Mayor Racha Intocable: "; SWWM_STATFAVWEAP = "Arma Favorita: "; SWWM_STATHISCORE = "Puntuación Máxima: "; SWWM_INVTAB = "Inventario"; @@ -2243,10 +2260,10 @@ SWWM_LORETXT_BLASTSUIT = "Resumen: Un simple chaleco diseñado para proteger contra explosiones. El cuerpo principal está hecho de nanotejidos muy elásticos, y deberían proveer de una protección decente contra armamento ligero. El blindaje está hecho de una aleación propietaria altamente resistente al impacto.\n" "\n" "Protección:\n" -" - Reducción de un 75% contra todo tipo de daños.\n" +" - Reducción de un 30% contra todo tipo de daños.\n" " - Reducción adicional de un 50% contra explosiones.\n" "\n" -"Durabilidad: Puede absorber un total de 200 unidades de daño antes de romperse.\n" +"Durabilidad: Puede absorber un total de 150 unidades de daño antes de romperse.\n" "\n" "Apéndice: El chaleco se puede llevar por debajo de otras armaduras."; SWWM_LORETAG_CANDYGUN = "Pistola Caramelo"; @@ -3043,7 +3060,7 @@ SWWM_LORETXT_UAC2 = "\n" "Apéndice: La compañía cayó en el control de los demonios durante la invasión (aunque algunos dicen que esto pudo ser el caso mucho antes del evento), y actualmente sigue recuperándose. Sin embargo no hay muchas esperanzas por una recuperación completa, ya que muchos de sus empleados siguen marchándose.\n" "\n" -"Nota de Saya: Si es que me da un gusto de cojones ver caer a este asqueroso gigante. Tío, todo lo de la invasión fue horrible, sabes. Pero me alegro de que al menos hubieramos podido ayudar, aunque no fuera nada comparado con lo que el tal Slayer hizo..."; +"Nota de Saya: Si es que me da un gusto de cojones ver caer a este asqueroso gigante. Tía, todo lo de la invasión fue horrible, sabes. Pero me alegro de que al menos hubieramos podido ayudar, aunque no fuera nada comparado con lo que el tal Slayer hizo..."; SWWM_LORETXT_UNISSIX = "Nombre Completo: Misa Azadeku Unissix, o \"Unissix Bokurou Azadeku\"\n" "Nacionalidad: Devanikana\n" @@ -3087,11 +3104,11 @@ SWWM_LORETXT_WARARMOR = "Resumen: Armadura robusta y durable. Hecha con el mejor almacero de Devanikna. Originalmente encargada para la tercera entrada en los eventos Instant Action de Zanaveth.\n" "\n" "Protección:\n" -" - Reducción del 90% contra calor, frío, electricidad, lodo y otros tipos elementales.\n" -" - Reducción del 80% de todos los demás tipos de daño.\n" +" - Reducción del 80% contra calor, frío, electricidad, lodo y otros tipos elementales.\n" +" - Reducción del 50% de todos los demás tipos de daño.\n" " - Reducción adicional del 70% al daño por explosión.\n" "\n" -"Durabilidad: Puede absorber un total de 600 unidades de daño antes de romperse.\n" +"Durabilidad: Puede absorber un total de 250 unidades de daño antes de romperse.\n" "\n" "Apéndice: Combinado con un chaleco antiexplosivos, estás preparada para todo lo que te puedan soltar encima."; SWWM_LORETAG_YNYKRON = "Artefacto Ynykron"; @@ -3116,7 +3133,7 @@ SWWM_LORETXT_WHITESCAR = "\n" "Resumen: En Junio de 2073, el Presidente de los Estados Unidos realizó unos comentarios muy ofensivos sobre los Nukuri, que en aquel momento estaban pasando por su séptima Guerra Mundial. No fueron muy bien recibidos. Muchos Nukuri anteriormente asentados en la Tierra emigraron en protesta, el emperador de Nahkami tuvo muchas cosas que decir, y la bola siguió pasándose. En poco tiempo, el conflicto llegó a su punto de ebullición, y el Imperio Nukuri declaró la guerra a los EEUU, listo para lanzar un ataque directo como \"castigo por su insolencia\". Se desplegaron unidades de tierra en 2074, y así empezó la guerra. Hubo muchas bajas en ambos bandos, en su intento de \"matar a la bestia desde dentro\", sin embargo, en solo tres años más, el país entero había caído. Al presidente se le dieron dos opciones: o bien disculparse públicamente por sus palabras contra el Imperio Nukuri, o si no ver como la ira del Emperador cae sobre su gente. Rechazó esta proposición, y se ordenó la retirada de todas las fuerzas Nukuri en el área. Entonces, ocurrió: Con un destello de luz cegadora, un ataque orbital alcanzó varias grandes ciudades Estadounidenses, reduciéndolas a cenizas. La mayoría de la gente que se encontraba bajo techo pudo sobrevivir, los demás no tuvieron tanta suerte.\n" "\n" -"Apéndice: Al llegar las noticias de lo que ocurrió en la tierra, estallaron las protestas por todo el Imperio Nukuri, avivando aun más las llamas de la Séptima Guerra Mundial. El caos continuó durante casi 30 años, hasta que los fundadores de la Universidad de Nos-Kora intervinieron, y pusieron fin a la guerra ellos solos. Su ultimátum fue tomado muy en serio, y así una reforma masiva comenzó, empezando con la retirada forzosa del Emperador y su consiguiente exilio, sin derecho a volver jamás. La reforma llegó a su climax en 2108, con la formación del Gobierno Nukuri Unificado. A pesar de todo esto, las relaciones con los Nukuri nunca se han recuperado, con la excepción de algunos países todavía aliados, como Escocia y Japón.\n" +"Apéndice: Al llegar las noticias de lo que ocurrió en la Tierra, estallaron las protestas por todo el Imperio Nukuri, avivando aun más las llamas de la Séptima Guerra Mundial. El caos continuó durante casi 30 años, hasta que los fundadores de la Universidad de Nos-Kora intervinieron, y pusieron fin a la guerra ellos solos. Su ultimátum fue tomado muy en serio, y así una reforma masiva comenzó, empezando con la retirada forzosa del Emperador y su consiguiente exilio, sin derecho a volver jamás. La reforma llegó a su climax en 2108, con la formación del Gobierno Nukuri Unificado. A pesar de todo esto, las relaciones con los Nukuri nunca se han recuperado, con la excepción de algunos países todavía aliados, como Escocia y Japón.\n" "\n" "Nota de Saya: Dios es que este fue uno de los momentos más oscuros de la historia. Me jode muchísimo pensar en ello otra vez, sobre todo despues de que Zana-sama me contara como fue todo al otro lado. Los Nukuri no eran gente muy pacífica en aquellos tiempos, pero solo por que el tío que mandaba era un puto supervillano megalomaníaco asqueroso, es que joder, achicharrar los Estados Unidos por UNOS TWITS. Casi todo dios ahí se puso en su contra cuando pasó. El muy capullo es que firmó su propia sentencia de muerte, vamos. Y encima lo de que los dos frikis esos fueran y... ACABARAN la guerra por su cuenta. Madre de dios, espero no cabrearlos nunca."; SWWM_LORETXT_XANIMEN = @@ -3183,7 +3200,7 @@ T_GREENSHELLS = "Slugs"; T_WHITESHELL = "Cartucho Dragon's Breath"; T_WHITESHELLS = "Cartuchos Dragon's Breath"; T_BLUESHELL = "Cartucho de Sal de Kinylum"; -T_BLUESHELLS = "Cartucho de Sal Kinylum"; +T_BLUESHELLS = "Cartucho de Sal de Kinylum"; T_BLACKSHELL = "Cartucho de Napalm"; T_BLACKSHELLS = "Cartuchos de Napalm"; T_PURPLESHELL = "Cartucho de Bola de Plomo"; @@ -3400,6 +3417,11 @@ D_INVINCIBALL = "Ya no eres invencible."; D_RAGEKIT = "El Ragekit ha hecho ragequit."; D_REFRESHER = "El boost del Refrescador ha terminado."; D_WARARMOR = "La Armadura de Guerra ya no da para más."; +SWWM_URDED = "La Unidad Demolicionista \"%s\" ha caído"; +SWWM_URDED2 = "Pulsa \cfUsar\c- para reiniciar desde la última partida guardada"; +SWWM_URDED3 = "...o pulsa \cfDisparar\c- para intentar un reinicio de emergencia"; +SWWM_REFAIL = "REINICIO DE EMERGENCIA FALLIDO - NO HAY SUFICIENTE POTENCIA AUXILIAR DISPONIBLE"; +D_REFAIL = "El Sistema de Reinicio de Emergencia se ha recargado completamente."; // targetter SWWM_MULTIKILL = "Racha"; SWWM_SPREEKILL = "Intocable"; diff --git a/menudef.txt b/menudef.txt index cb153b62b..5e64969ff 100644 --- a/menudef.txt +++ b/menudef.txt @@ -21,6 +21,8 @@ OptionMenu "SWWMOptionMenu" SWWMVoiceOption "$SWWM_VOICETYPE", "swwm_voicetype" Option "$SWWM_MUTELEVEL", "swwm_mutevoice", "SWWMVoice" Option "$SWWM_6DOF", "swwm_fly6dof", "YesNo" + Option "$SWWM_REVIVE", "swwm_revive", "YesNo" + ScaleSlider "$SWWM_REVIVECOOLDOWN", "swwm_revivecooldown", 0, 300, 30, "$SWWM_UNLIMITED" StaticText " " StaticText "$SWWM_OTITLE", "Gold" Slider "$SWWM_FLASH", "swwm_flashstrength", 0.0, 1.0, 0.1, 1 diff --git a/modeldef.candygun b/modeldef.candygun index c20a2fe11..274cea452 100644 --- a/modeldef.candygun +++ b/modeldef.candygun @@ -100,7 +100,7 @@ Model "CandyGun" Model 0 "ExplodiumGunPickup_d.3d" Skin 0 "CandyGun.png" - Scale 0.06 0.06 0.06 + Scale 0.05 0.05 0.05 ZOffset 16 ROTATING diff --git a/modeldef.explodiumgun b/modeldef.explodiumgun index d928217d2..a78063a75 100644 --- a/modeldef.explodiumgun +++ b/modeldef.explodiumgun @@ -57,7 +57,7 @@ Model "ExplodiumGun" Model 0 "ExplodiumGunPickup_d.3d" Skin 0 "ExplodiumGun.png" - Scale 0.04 0.04 0.04 + Scale 0.05 0.05 0.05 ZOffset 16 ROTATING diff --git a/modeldef.spreadgun b/modeldef.spreadgun index ec8d021b6..af796d69f 100644 --- a/modeldef.spreadgun +++ b/modeldef.spreadgun @@ -144,13 +144,23 @@ Model "SaltBeam" FrameIndex XZW2 M 0 12 } +Model "TheBall" +{ + Model 0 "models/extra/BaseSphere_d.3d" + Skin 0 "models/leadmap.png" + Scale 0.008 0.008 0.008 + ZOffset 2 + + FrameIndex XZW1 A 0 0 +} + Model "Spreadgun" { Path "models" Model 0 "SpreadgunPickup_d.3d" Skin 0 "Spreadgun.png" - Scale 0.035 0.035 0.035 + Scale 0.04 0.04 0.04 AngleOffset 180 ZOffset 16 ROTATING diff --git a/models/ASmallPriceToPay.png b/models/ASmallPriceToPay.png new file mode 100644 index 000000000..193df97fe Binary files /dev/null and b/models/ASmallPriceToPay.png differ diff --git a/models/ASmallPriceToPay_a.3d b/models/ASmallPriceToPay_a.3d new file mode 100644 index 000000000..49004bec8 Binary files /dev/null and b/models/ASmallPriceToPay_a.3d differ diff --git a/models/ASmallPriceToPay_d.3d b/models/ASmallPriceToPay_d.3d new file mode 100644 index 000000000..6c05c4eb8 Binary files /dev/null and b/models/ASmallPriceToPay_d.3d differ diff --git a/models/leadmap.png b/models/leadmap.png new file mode 100644 index 000000000..c35150c86 Binary files /dev/null and b/models/leadmap.png differ diff --git a/shaders/glsl/Glitch.frag b/shaders/glsl/Glitch.frag index 3e0f6c500..c180c0b5b 100644 --- a/shaders/glsl/Glitch.frag +++ b/shaders/glsl/Glitch.frag @@ -13,7 +13,7 @@ void main() 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; + float noiz = pow(rnd2(blka),6.4)*pow(rnd2(blkb),8.6)-pow(rnd(4.53),19.3)*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); diff --git a/shaders/glsl/Grain.frag b/shaders/glsl/Grain.frag index fc303dc5b..1456e7aff 100644 --- a/shaders/glsl/Grain.frag +++ b/shaders/glsl/Grain.frag @@ -2,13 +2,13 @@ Complex grain shader ported over from MariENB (C)2012-2018 Marisa Kirisame */ -const float nf = 0.000005; +const float nf = 0.00001; 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; +const float ns = -0.28; +const float np = 2.1; +const float bnp = 0.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)))) @@ -52,8 +52,8 @@ 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;*/ + vec2 sfact = max(vec2(640.0,400.0),textureSize(InputTexture,0)*0.5); + coord = floor(coord*sfact)/sfact; res.rgb = grain(res.rgb,coord); FragColor = res; } diff --git a/sndinfo.txt b/sndinfo.txt index 244aaa09e..a03f0ceb2 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -306,7 +306,7 @@ $random demolitionist/punch { demolitionist/punch1 demolitionist/punch2 demoliti demolitionist/punchf1 sounds/demolitionist/demopunchf1.ogg demolitionist/punchf2 sounds/demolitionist/demopunchf2.ogg demolitionist/punchf3 sounds/demolitionist/demopunchf3.ogg -$random demolitionist/punchf { demolitionist/puncfh1 demolitionist/punchf2 demolitionist/punchf3 } +$random demolitionist/punchf { demolitionist/punchf1 demolitionist/punchf2 demolitionist/punchf3 } demolitionist/bump1 sounds/demolitionist/demobump1.ogg demolitionist/bump2 sounds/demolitionist/demobump2.ogg demolitionist/bump3 sounds/demolitionist/demobump3.ogg @@ -315,6 +315,7 @@ demolitionist/kick1 sounds/demolitionist/demokick1.ogg demolitionist/kick2 sounds/demolitionist/demokick2.ogg demolitionist/kick3 sounds/demolitionist/demokick3.ogg $random demolitionist/kick { demolitionist/kick1 demolitionist/kick2 demolitionist/kick3 } +demolitionist/revive sounds/demolitionist/demorevive.ogg $playersound demolitionist neutral *grunt DSEMPTY $playeralias demolitionist neutral *pain100 demolitionist/lopain diff --git a/sounds/demolitionist/demorevive.ogg b/sounds/demolitionist/demorevive.ogg new file mode 100644 index 000000000..36c71c4a1 Binary files /dev/null and b/sounds/demolitionist/demorevive.ogg differ diff --git a/textures/ragewarp.png b/textures/ragewarp.png index 641140506..c87b3e809 100644 Binary files a/textures/ragewarp.png and b/textures/ragewarp.png differ diff --git a/textures/rgbnoise.png b/textures/rgbnoise.png new file mode 100644 index 000000000..a93edab04 Binary files /dev/null and b/textures/rgbnoise.png differ diff --git a/zscript.txt b/zscript.txt index 9c305e8a5..d58d9046a 100644 --- a/zscript.txt +++ b/zscript.txt @@ -38,3 +38,4 @@ version "4.3" #include "zscript/swwm_thiccboolet.zsc" #include "zscript/swwm_tastytreat.zsc" #include "zscript/swwm_deathlydeathcannon.zsc" +#include "zscript/swwm_funstuff.zsc" diff --git a/zscript/swwm_armor.zsc b/zscript/swwm_armor.zsc index 96ef28b59..efd8c195f 100644 --- a/zscript/swwm_armor.zsc +++ b/zscript/swwm_armor.zsc @@ -13,7 +13,7 @@ Class ArmorNugget : SWWMArmor override int HandleDamage( int damage, Name damageType, int flags ) { - double factor = amount*.01; + double factor = amount*.1; return int(ceil(damage*factor)); } } @@ -57,9 +57,9 @@ Class BlastSuit : SWWMArmor Default { Inventory.Icon "graphics/HUD/Icons/I_BlastSuit.png"; - Inventory.Amount 200; - Inventory.MaxAmount 200; - Inventory.InterHubAmount 200; + Inventory.Amount 150; + Inventory.MaxAmount 150; + Inventory.InterHubAmount 150; SWWMArmor.ArmorPriority 2; SWWMArmor.DrainMessage "$D_BLASTSUIT"; SWWMArmor.GiverArmor "BlastSuitItem"; @@ -67,7 +67,7 @@ Class BlastSuit : SWWMArmor override int HandleDamage( int damage, Name damageType, int flags ) { - double factor = .75; + double factor = .3; if ( flags&DMG_EXPLOSION ) factor = 1.-(1.-factor)*.5; return int(ceil(damage*factor)); } @@ -102,9 +102,9 @@ Class WarArmor : SWWMArmor Default { Inventory.Icon "graphics/HUD/Icons/I_WarArmor.png"; - Inventory.Amount 600; - Inventory.MaxAmount 600; - Inventory.InterHubAmount 600; + Inventory.Amount 250; + Inventory.MaxAmount 250; + Inventory.InterHubAmount 250; SWWMArmor.ArmorPriority 6; SWWMArmor.DrainMessage "$D_WARARMOR"; SWWMArmor.GiverArmor "WarArmorItem"; @@ -114,8 +114,8 @@ Class WarArmor : SWWMArmor { double factor; // should be enough "elemental" damage types I guess - if ( (damageType == 'Fire') || (damageType == 'Ice') || (damageType == 'Slime') || (damageType == 'Lightning') || (damageType == 'Wind') || (damageType == 'Water') ) factor = .9; - else factor = .8; + if ( (damageType == 'Fire') || (damageType == 'Ice') || (damageType == 'Slime') || (damageType == 'Lightning') || (damageType == 'Wind') || (damageType == 'Water') ) factor = .8; + else factor = .5; if ( flags&DMG_EXPLOSION ) factor = 1.-(1.-factor)*.7; return int(ceil(damage*factor)); } diff --git a/zscript/swwm_common.zsc b/zscript/swwm_common.zsc index fbeaafaab..df588ad3d 100644 --- a/zscript/swwm_common.zsc +++ b/zscript/swwm_common.zsc @@ -46,7 +46,7 @@ Class SWWMStats : Thinker PlayerInfo myplayer; int lastspawn, dashcount, boostcount, stompcount, airtime, kills, deaths, damagedealt, damagetaken, mkill, hiscore, topdealt, - toptaken; + toptaken, skill; double grounddist, airdist, fuelusage, topspeed; Array wstats; Array > alreadygot; @@ -596,8 +596,11 @@ Class SWWMCombatTracker : Thinker if ( newhealth != lasthealth ) updated = level.maptime+35; if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE ) { + // enemies within 2000mu that have us as target if ( mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && mytarget.CheckSight(mytarget.target) && (mytarget.Vec3To(mytarget.target).length() < 2000) ) updated = level.maptime+70; - if ( mytarget.player ) updated = level.maptime+35; + // players (but not voodoo dolls), always visible in sp/coop + if ( !deathmatch && mytarget.player && (mytarget.player.mo == mytarget) ) updated = level.maptime+35; + // enemies we're directly aiming at within 600mu if ( players[consoleplayer].mo.CheckSight(mytarget) && ((mytarget.Vec3To(players[consoleplayer].mo).length() < 600) || (players[consoleplayer].mo.AimTarget() == mytarget)) ) updated = level.maptime; } lasthealth = newhealth; @@ -1544,6 +1547,12 @@ Class SWWMHandler : EventHandler Demolitionist(players[i].mo).CheckUnderwaterAmb(true); } } + PlayerInfo p = players[consoleplayer]; + Shader.SetEnabled(p,"RagekitShader",false); + Shader.SetEnabled(p,"GhostShader",false); + Shader.SetEnabled(p,"InvinciShader",false); + Shader.SetEnabled(p,"Glitch",false); + Shader.SetEnabled(p,"Grain",false); } override void PlayerDied( PlayerEvent e ) @@ -1596,7 +1605,7 @@ Class SWWMHandler : EventHandler s = new("SWWMStats"); s.ChangeStatNum(Thinker.STAT_STATIC); s.myplayer = p; - s.lastspawn = gametic; + s.lastspawn = level.totaltime; s.favweapon = -1; } // reset some vars @@ -1640,7 +1649,7 @@ Class SWWMHandler : EventHandler } // reset uptime since player had just died SWWMStats s = SWWMStats.Find(e.Thing.player); - if ( s ) s.lastspawn = gametic; + if ( s ) s.lastspawn = level.totaltime; } override void WorldTick() @@ -1865,6 +1874,8 @@ Class SWWMHandler : EventHandler Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),e.DamageSource.player.GetUserName(),5000); } spreecount[pnum]++; + if ( s && spreecount[pnum] > s.skill ) + s.skill = spreecount[pnum]; } } @@ -2002,7 +2013,6 @@ Class SWWMHandler : EventHandler case 0: case 1: case 2: - case 3: e.Replacement = redpool[Random[Replacement](0,1)]; break; case 4: @@ -2023,67 +2033,65 @@ Class SWWMHandler : EventHandler } else if ( (e.Replacee == 'Shell') || (e.Replacee is 'CrossbowAmmo') ) { - switch( Random[Replacement](0,14) ) + switch( Random[Replacement](0,13) ) { case 0: case 1: case 2: - case 3: e.Replacement = redpool[Random[Replacement](1,2)]; break; + case 3: case 4: case 5: - case 6: e.Replacement = greenpool[Random[Replacement](1,2)]; break; + case 6: case 7: - case 8: e.Replacement = whitepool[Random[Replacement](0,1)]; break; + case 8: case 9: case 10: - case 11: e.Replacement = purplepool[Random[Replacement](0,1)]; break; + case 11: case 12: - case 13: e.Replacement = bluepool[Random[Replacement](0,2)]; break; - case 14: + case 13: e.Replacement = blackpool[0]; break; } } else if ( (e.Replacee == 'ShellBox') || (e.Replacee is 'CrossbowHefty') ) { - switch( Random[Replacement](0,15) ) + switch( Random[Replacement](0,14) ) { case 0: case 1: case 2: - case 3: e.Replacement = redpool[Random[Replacement](2,5)]; break; + case 3: case 4: case 5: - case 6: e.Replacement = greenpool[Random[Replacement](2,4)]; break; + case 6: case 7: case 8: - case 9: e.Replacement = whitepool[Random[Replacement](1,3)]; break; + case 9: case 10: case 11: - case 12: e.Replacement = purplepool[Random[Replacement](1,2)]; break; + case 12: case 13: - case 14: e.Replacement = bluepool[Random[Replacement](2,3)]; break; - case 15: + case 14: e.Replacement = blackpool[Random[Replacement](0,2)]; break; } @@ -2364,6 +2372,27 @@ Class SWWMHandler : EventHandler Shader.SetUniform1f(p,"InvinciShader","str",str); } else Shader.SetEnabled(p,"InvinciShader",false); + if ( pc && (mo is 'Demolitionist') ) + { + let demo = Demolitionist(mo); + Shader.SetEnabled(p,"Glitch",true); + Shader.SetEnabled(p,"Grain",true); + Shader.SetUniform1f(p,"Glitch","Timer",(gametic+e.FracTic)/Thinker.TICRATE); + Shader.SetUniform1f(p,"Grain","Timer",(gametic+e.FracTic)/Thinker.TICRATE); + int lastdmg = (demo.Health>0)?demo.lastdamage:Random[Flicker](60,80); + int lastdmgtic = (demo.Health>0)?demo.lastdamagetic:(gametic+Random[Flicker](30,20)); + double noiz = min(lastdmg*.3*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.7); + Shader.SetUniform1f(p,"Grain","ni",noiz); + noiz = min(lastdmg*.16*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.8); + Shader.SetUniform1f(p,"Glitch","str1",noiz); + noiz = min(lastdmg*.12*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),3.5); + Shader.SetUniform1f(p,"Glitch","str2",noiz); + } + else + { + Shader.SetEnabled(p,"Glitch",false); + Shader.SetEnabled(p,"Grain",false); + } } static void DoFlash( Actor camera, Color c, int duration ) diff --git a/zscript/swwm_funstuff.zsc b/zscript/swwm_funstuff.zsc new file mode 100644 index 000000000..04c903a0c --- /dev/null +++ b/zscript/swwm_funstuff.zsc @@ -0,0 +1,5 @@ +// collectable items that may drop sometimes (future feature) + +Class SWWMCollectable : Inventory +{ +} diff --git a/zscript/swwm_hud.zsc b/zscript/swwm_hud.zsc index 27f189e29..74dd43ca7 100644 --- a/zscript/swwm_hud.zsc +++ b/zscript/swwm_hud.zsc @@ -48,6 +48,12 @@ Class SWWMStatusBar : BaseStatusBar override void FlushNotify() { + // flush interpolators (useful since this virtual gets called + // when loading saves, too) + HealthInter.Reset(CPlayer.Health); + ScoreInter.Reset(SWWMCredits.Get(CPlayer)); + FuelInter.Reset((CPlayer.mo is 'Demolitionist')?int(Demolitionist(CPlayer.mo).dashfuel):0); + DashInter.Reset((CPlayer.mo is 'Demolitionist')?int((40-Demolitionist(CPlayer.mo).dashcooldown)*3.):0); if ( level.maptime <= 1 ) { // flush ALL messages @@ -683,12 +689,12 @@ Class SWWMStatusBar : BaseStatusBar } if ( ht > 200 ) { - hw = int(min(ht-100,400)*0.25); + hw = int(min(ht-200,300)/3.); Screen.DrawTexture(HealthTex[2],false,margin+2,ss.y-(margin+15),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw); } if ( ht > 500 ) { - hw = int(min(ht-500,500)*0.2); + hw = int(min(ht-500,500)/5.); Screen.DrawTexture(HealthTex[3],false,margin+2,ss.y-(margin+15),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw); } int hcolor = Font.CR_RED; @@ -804,6 +810,50 @@ Class SWWMStatusBar : BaseStatusBar // don't do anything } + private void DrawDeath() + { + // death prompt + let demo = Demolitionist(CPlayer.mo); + if ( !demo || (CPlayer.Health > 0) ) return; + String str; + double alph; + int len; + double xx, yy; + if ( demo.player.viewheight <= 6 ) + { + Screen.Dim("Black",min(demo.deadtimer/80.,1.),0,0,Screen.GetWidth(),Screen.GetHeight()); + alph = clamp((demo.deadtimer-60)/60.,0.,1.); + str = String.Format(StringTable.Localize("$SWWM_URDED"),CPlayer.GetUserName()); + len = mTewiFont.mFont.StringWidth(str); + xx = (ss.x-len)/2.; + yy = (ss.y-mTewiFont.mFont.GetHeight()*4)/2.; + Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + alph = clamp((demo.deadtimer-120)/60.,0.,1.); + str = String.Format(StringTable.Localize("$SWWM_URDED2"),CPlayer.GetUserName()); + len = mTewiFont.mFont.StringWidth(str); + xx = (ss.x-len)/2.; + yy = ss.y/2.; + Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + if ( !swwm_revive ) + return; + alph = clamp((demo.deadtimer-200)/60.,0.,1.); + str = String.Format(StringTable.Localize("$SWWM_URDED3"),CPlayer.GetUserName()); + len = mTewiFont.mFont.StringWidth(str); + xx = (ss.x-len)/2.; + yy = (ss.y+mTewiFont.mFont.GetHeight()*2)/2.; + Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + if ( demo.revivefail > level.maptime ) + { + str = StringTable.Localize("$SWWM_REFAIL"); + len = mTewiFont.mFont.StringWidth(str); + xx = (ss.x-len)/2.; + yy = ss.y-30; + if ( (gametic%12) < 6 ) + Screen.DrawText(mTewiFont.mFont,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + } + } + override void Draw( int state, double TicFrac ) { Super.Draw(state,TicFrac); @@ -823,5 +873,6 @@ Class SWWMStatusBar : BaseStatusBar DrawStatus(); DrawWeapon(); DrawMessages(); + DrawDeath(); } } diff --git a/zscript/swwm_inventory.zsc b/zscript/swwm_inventory.zsc index cafa1ee81..584f0af56 100644 --- a/zscript/swwm_inventory.zsc +++ b/zscript/swwm_inventory.zsc @@ -106,7 +106,7 @@ Class SWWMSpareArmor : Inventory abstract else shouldautouse = CVar.GetCVar('swwm_autousearmor',Owner.player).GetBool(); if ( pickup && !shouldautouse ) return false; let cur = Owner.FindInventory(giveme); - if ( !cur || (cur.Amount < cur.MaxAmount) ) + if ( !cur || (cur.Amount <= 0) ) { Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); @@ -356,6 +356,94 @@ Class SWWMWeaponLight : DynamicLight } } +Class PunchImpact : Actor +{ + Default + { + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3); + A_StartSound("demolitionist/punch",CHAN_VOICE); + A_SprayDecal("WallCrack",-20); + int numpt = Random[Ponch](5,10); + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + for ( int i=0; i 0) && (PainChance == 0) && (level.maptime>lastmpain) ) { lastmpain = level.maptime; @@ -627,7 +629,6 @@ Class Demolitionist : PlayerPawn double spd = vel.length(); if ( spd > 10. ) vel = (vel+accel/TICRATE).unit()*spd; else vel = vel+accel/TICRATE; - player.jumptics = -2; } if ( abs(roll) > 0. ) roll += clamp(deltaangle(roll,0),-3.,3.); } @@ -635,7 +636,7 @@ Class Demolitionist : PlayerPawn guidepitch *= .9; guideroll *= .9; // anchor to ground when going down steps - if ( !player.onground && !bFly && !bFlyCheat && (waterlevel < 2) && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) && (vel.z < 0) ) + if ( lastground && !player.onground && !bFly && !bFlyCheat && (waterlevel < 2) && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) && (vel.z < 0) ) { ssup = max(0,(pos.z-floorz)); SetOrigin(Vec2OffsetZ(0,0,floorz),true); @@ -801,8 +802,78 @@ Class Demolitionist : PlayerPawn } override void DeathThink() { - // TODO reboot mechanic, death camera that doesn't move body - Super.DeathThink(); + player.Uncrouch(); + TickPSprites(); + player.onground = (pos.Z<=floorz); + // ded (demo-chan falls faster tho) + player.deltaviewheight = 0; + if ( player.viewheight > 6 ) player.viewheight -= 3; + if ( player.viewheight < 6 ) player.viewheight = 6; + // center pitch + double dpitch = clamp(deltaangle(pitch,0),-6,6); + if ( abs(dpitch) < 3. ) pitch = 0.; + else pitch += dpitch; + // add roll + double droll = clamp(deltaangle(roll,50)*.5,-5,5); + if ( abs(droll) < 2. ) roll = 50.; + else roll += droll; + player.mo.CalcHeight(); + if ( player.damagecount ) player.damagecount--; + if ( player.poisoncount ) player.poisoncount--; + if ( player.viewheight <= 6 ) + { + deadtimer++; + if ( multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn) ) + { + // standard behaviour, respawn normally + if ( (Level.maptime >= player.respawn_time) || ((player.cmd.buttons&BT_USE) && !player.Bot) ) + { + player.cls = null; + player.playerstate = PST_REBORN; + if ( special1 > 2 ) special1 = 0; + } + } + else if ( (player.cmd.buttons&BT_USE) && (deadtimer > 150) ) + { + // reload save + player.cls = null; + player.playerstate = PST_ENTER; + if ( special1 > 2 ) special1 = 0; + } + else if ( (player.cmd.buttons&BT_ATTACK) && (deadtimer > 150) && swwm_revive ) + { + // reboot (if possible) + if ( !FindInventory("ReviveCooldown") ) + { + player.Resurrect(); + roll = 0; + let s = Spawn("DemolitionistShockwave",pos); + s.target = self; + s.special1 = 30; + ReactionTime = 17; + A_Stop(); + A_AlertMonsters(); + if ( player == players[consoleplayer] ) + { + A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP); + A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.7); + A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.4); + } + SWWMHandler.DoFlash(self,Color(255,255,255,255),10); + SWWMHandler.DoFlash(self,Color(255,128,192,255),30); + if ( special1 > 2 ) special1 = 0; + if ( swwm_revivecooldown > 0 ) + GiveInventory("ReviveCooldown",1); + } + else + { + A_StartSound("*usefail",CHAN_ITEM,CHANF_UI|CHANF_LOCAL); + SWWMHandler.DoFlash(self,Color(64,255,0,0),30); + revivefail = level.maptime+20; + } + } + } + else deadtimer = 0; } override void PlayIdle() { @@ -1480,3 +1551,38 @@ Class DemolitionistShockwave : Actor Stop; } } + +Class ReviveCooldown : Powerup +{ + Default + { + Inventory.Icon "graphics/HUD/Icons/I_Revive.png"; + Powerup.Duration -30; + } + + override void Tick() + { + if ( !Owner ) Destroy(); + if ( Owner.Health <= 0 ) return; // timer does not go down when dead + if ( (EffectTics == 0) || ((EffectTics > 0) && (--EffectTics == 0)) ) + Destroy (); + } + override void InitEffect() + { + Super.InitEffect(); + // adjust the duration + EffectTics = max(0,swwm_revivecooldown)*Thinker.TICRATE; + } + override void EndEffect() + { + Super.EndEffect(); + if ( !Owner ) return; + Owner.A_StartSound("demolitionist/revive",CHAN_ITEMEXTRA); + if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_REFAIL")); + } + override void OwnerDied() + { + // do nothing, this "powerup" is preserved on death + } +} + diff --git a/zscript/swwm_powerup.zsc b/zscript/swwm_powerup.zsc index 279c29961..34e8bede3 100644 --- a/zscript/swwm_powerup.zsc +++ b/zscript/swwm_powerup.zsc @@ -711,7 +711,7 @@ Class RagekitPower : Powerup override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags ) { - if ( !passive && ((damageType == 'Melee') || (damageType == 'Dash') || (damageType == 'GroundPound')) ) + if ( !passive && ((damageType == 'Melee') || (damageType == 'Jump') || (damageType == 'Dash') || (damageType == 'GroundPound')) ) { newdamage = damage*8; if ( level.maptime > lasteffect+5 ) diff --git a/zscript/swwm_shot.zsc b/zscript/swwm_shot.zsc index dc85a0dae..90ee82750 100644 --- a/zscript/swwm_shot.zsc +++ b/zscript/swwm_shot.zsc @@ -820,6 +820,600 @@ Class SaltBeam : Actor } } +Class OnFireLight : DynamicLight +{ + OnFire of; + + override void Tick() + { + Super.Tick(); + if ( !of || !of.victim ) + { + Destroy(); + return; + } + Args[0] = clamp(of.Amount*4,0,255); + Args[1] = clamp(of.Amount*2,0,128); + Args[3] = int(max(of.victim.radius,of.victim.height)*2.+20+clamp(of.amount/5,0,80)); + SetOrigin(of.Victim.Vec3Offset(0,0,of.Victim.Height/2),true); + } +} + +Class OnFire : Actor +{ + Actor victim, instigator, lite; + int amount, cnt, delay; + double oangle; + + override void Tick() + { + if ( !victim ) + { + Destroy(); + return; + } + if ( victim.waterlevel > 0 ) + { + if ( lite ) lite.Destroy(); + amount -= int(victim.waterlevel**2); + } + if ( (victim.Health <= 0) || ((victim is 'FlamingChunk') && !victim.bMISSILE) ) amount = min(amount,100); + if ( !(level.maptime%3) ) + amount--; + if ( victim.player ) amount -= int(abs(actor.deltaangle(victim.angle,oangle))/30); + oangle = victim.angle; + if ( amount < -30 ) + { + Destroy(); + return; + } + if ( cnt > 0 ) cnt--; + else + { + cnt = 10; + if ( victim.bSHOOTABLE && (victim.Health > 0) && (amount > 0) ) + victim.DamageMobj(self,instigator,clamp(int(amount*(victim.bBOSS?0.05:0.15)),1,20),'Fire',DMG_THRUSTLESS); // the only reason why we need to use an actor + if ( !victim ) + { + Destroy(); + return; + } + } + if ( delay > 0 ) delay--; + if ( level.maptime%5 ) return; + int numpt = clamp(int(Random[FlameT](2,4)*amount*0.02),1,4); + double mult = max(victim.radius,victim.height)/30.; + numpt = int(clamp(numpt*mult**.5,1,5)); + for ( int i=0; i 0 ) + { + let c = victim.Spawn("OnFireTrail",pos); + c.scale *= max(.3,mult*0.5); + c.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.5,2.)*c.scale.x; + } + let s = victim.Spawn("SWWMSmoke",pos); + s.scale *= max(1.,1.6*mult); + s.alpha *= min(amount+30,100)*0.02; + s.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.2,.6)*s.scale.x; + } + if ( amount <= 0 ) return; + // spread to nearby actors + let bt = BlockThingsIterator.Create(victim); + while ( bt.Next() ) + { + let t = bt.Thing; + if ( !t || !t.bSHOOTABLE || (t.Health <= 0) || (t == victim) || ((t == instigator) && (delay > 0)) || (victim.Distance3D(t) > victim.radius+t.radius+20) || !victim.CheckSight(t) ) continue; + int amt = max(1,amount/10); + if ( IsOnFire(t) ) amt = min(5,amt); + Apply(t,instigator,amt); + } + } + + static OnFire Apply( Actor victim, Actor instigator, int amount, int delay = 0 ) + { + if ( amount <= 0 ) return null; + let ti = ThinkerIterator.Create("OnFire"); + OnFire t; + while ( t = OnFire(ti.Next()) ) + { + if ( t.victim != victim ) continue; + if ( instigator ) t.instigator = instigator; + t.amount = min(500,t.amount+amount); + t.cnt = min(t.cnt,5); + return t; + } + t = OnFire(Spawn("OnFire")); + t.victim = victim; + t.instigator = instigator; + t.amount = min(500,amount); + t.cnt = 1; + // for chunks + t.delay = delay; + t.lite = Actor.Spawn("OnFireLight",victim.pos); + OnFireLight(t.lite).of = t; + t.oangle = victim.angle; + return t; + } + + static bool IsOnFire( Actor victim ) + { + let ti = ThinkerIterator.Create("OnFire"); + OnFire t; + while ( t = OnFire(ti.Next()) ) + { + if ( t.victim != victim ) continue; + return (t.amount>0); + } + return false; + } + + Default + { + +NOGRAVITY; + +NOBLOCKMAP; + +DONTSPLASH; + Obituary "$O_SPREADGUN_BLACK"; + } +} + +Class OnFireTrailLight : PaletteLight +{ + Default + { + Tag "HellExpl"; + Args 0,0,0,40; + ReactionTime 40; + } + override void Tick() + { + Super.Tick(); + Args[0] /= 10; + Args[1] /= 10; + Args[2] /= 10; + Args[3] += 3; + if ( !target || (target.waterlevel > 0) ) + { + Destroy(); + return; + } + SetOrigin(target.pos,true); + } +} + +Class OnFireTrail : Actor +{ + override void PostBeginPlay() + { + Super.PostBeginPlay(); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + roll = FRandom[ExploS](0,360); + } + action void A_Flame() + { + if ( waterlevel > 0 ) + vel *= 0.9; + else + { + vel *= 0.98; + vel.z += 0.2*abs(scale.x); + } + if ( waterlevel > 0 ) + { + let s = Spawn("SWWMSmoke",pos); + s.vel = (FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2)); + s.vel += vel*0.3; + s.alpha *= alpha*4; + s.scale *= 0.5+abs(scale.x)*(.5+GetAge()/6.); + Destroy(); + return; + } + if ( !Random[FlameT](0,int(40*(default.alpha-alpha))) ) + { + let s = Spawn("SWWMSmoke",pos); + s.vel = (FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2)); + s.vel += vel*0.3; + s.alpha *= alpha*4; + s.scale *= 0.5+abs(scale.x)*(.5+GetAge()/6.); + } + } + Default + { + RenderStyle "Add"; + Speed 2; + Radius 4; + Height 4; + Alpha 0.3; + Scale 0.6; + +NOBLOCKMAP; + +NOGRAVITY; + +NOFRICTION; + +SLIDESONWALLS; + +ACTIVATEPCROSS; + +ACTIVATEIMPACT; + +NOTELEPORT; + +FORCEXYBILLBOARD; + +ROLLSPRITE; + +ROLLCENTER; + +DROPOFF; + +NOBLOCKMONST; + +DONTSPLASH; + } + States + { + Spawn: + XFLM ABCDEFGHIJKLMNOPQRST 1 Bright + { + A_Flame(); + A_SetScale(scale.x*0.98); + A_FadeOut(0.01); + vel.z += 0.1; + } + Wait; + } +} + +Class FlamingChunk : Actor +{ + double rollvel; + OnFire myfire; + Vector3 oldvel; + int deadtimer; + Actor lasthit; + + override void PostBeginPlay() + { + Super.PostBeginPlay(); + rollvel = FRandom[FlameT](10,30)*RandomPick[FlameT](-1,1); + Scale *= FRandom[FlameT](.8,1.2); + if ( waterlevel <= 0 ) myfire = OnFire.Apply(self,target,int(120*scale.x),6); + frame = Random[FlameT](0,5); + } + override int DoSpecialDamage( Actor target, int damage, Name damagetype ) + { + if ( target != lasthit ) + { + OnFire.Apply(target,self.target,myfire?myfire.Amount:1); + lasthit = target; + } + return damage; + } + override void Tick() + { + oldvel = vel; + Super.Tick(); + if ( isFrozen() ) return; + if ( InStateSequence(CurState,ResolveState("Death")) ) + { + deadtimer++; + if ( deadtimer > 300 ) A_FadeOut(0.05); + return; + } + } + void A_HandleBounce() + { + bHITOWNER = true; + lasthit = null; + Vector3 HitNormal = -vel.unit(); + F3DFloor ff; + if ( BlockingFloor ) + { + // find closest 3d floor for its normal + for ( int i=0; i 15 ) + { + let s = Spawn("BallImpact",pos); + s.angle = atan2(dir.y,dir.x); + s.pitch = asin(-dir.z); + } + } + if ( slamforce > girth ) + { + vel *= .8; + return 1; + } + // force bounce + BlockingMobj = victim; + A_HandleBounce(); + lasthit = victim; + // pretend to pass through + return 1; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_StartSound("pusher/fly",CHAN_WEAPON,CHANF_LOOPING,.6,3.,2.); + heat = 1.; + } + override void Tick() + { + oldvel = vel; + Super.Tick(); + if ( isFrozen() ) return; + if ( InStateSequence(CurState,ResolveState("Death")) ) + { + deadtimer++; + if ( deadtimer > 300 ) A_FadeOut(0.05); + return; + } + heat -= 0.004+0.0004*vel.length(); + A_SoundVolume(CHAN_WEAPON,vel.length()/75.); + if ( heat <= 0 ) return; + let s = Spawn("SWWMSmoke",pos); + s.alpha *= heat; + } + void A_HandleBounce() + { + bHITOWNER = true; + lasthit = null; + Vector3 HitNormal = -vel.unit(); + F3DFloor ff; + if ( BlockingFloor ) + { + // find closest 3d floor for its normal + for ( int i=0; i 15 ) + { + let s = Spawn("BallImpact",pos); + s.angle = atan2(HitNormal.y,HitNormal.x); + s.pitch = asin(-HitNormal.z); + } + } + gravity = .35; + if ( (vel.length() < 5) && (pos.z <= floorz) ) + { + ClearBounce(); + ExplodeMissile(); + } + } + States + { + Spawn: + XZW1 A -1; + Stop; + Bounce: + XZW1 A 0 A_HandleBounce(); + Goto Spawn; + Death: + XZW1 A -1 + { + bMOVEWITHSECTOR = true; + A_StopSound(CHAN_WEAPON); + } + Stop; + } +} + Class Spreadgun : SWWMWeapon { bool fired; // shell was used @@ -1190,7 +1784,39 @@ Class Spreadgun : SWWMWeapon Console.Printf("\cg// TODO Napalm Rounds\c-"); break; case 5: - Console.Printf("\cg// TODO The Ball\c-"); + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.03); + let b = Spawn("TheBall",origin); + b.target = self; + b.angle = atan2(x2.y,x2.x); + b.pitch = asin(-x2.z); + b.vel = x2*b.speed; + for ( int i=0; i<4; i++ ) + { + let s = Spawn("SWWMViewSmoke",origin); + SWWMViewSmoke(s).ofs = (15,3,-3); + s.target = self; + s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); + s.alpha *= 0.4; + } + for ( int i=0; i<8; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .6; + s.alpha *= .25; + s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.); + } + for ( int i=0; i<8; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .2; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + SWWMHandler.DoKnockback(self,-x,2500.); + A_Recoil(1.); + invoker.srecoil = -.35; break; case 6: Console.Printf("\cg// TODO Golden Shell\c-");