Added Lead Ball ammo type to Spreadgun.

Partial implementation of Fuck Your Shit rounds, currently in progress.
Added various shader effects to some powerups, and to player damage.
Added custom view effects to player death, disabled "face attacker" because it looks weird with model-based players.
Added "untouchable" spree tracking to the Stats tab.
Implemented "emergency reboot system" for people who want a less shameful form of the Resurrect cheat. Cooldown for consecutive reboots can be configured.
Rebalanced armors.
Small language string corrections.
Adjusted pickup model sizes of some weapons.
Fixed missing punch sound (damn typos).
Fix targetter always displaying voodoo dolls.
Fix uptime breaking when loading saves, now based on total playtime rather than gametic.
Readjusted Spreadgun ammo availability.
Flush HUD interpolators alongside messages, fixes things such as the score VERY slowly counting up when loading a save.
Spare armors now only get auto-used on pickup if there is NO armor available of that type.
Added some extra visual effects to punching walls and non-bleeding actors.
Slightly altered the melee range so it's not as awkward.
Fixed punching not using flesh sounds for bleeding actors.
Pusher primary now drags the player towards their target, like the Chainsaw.
Fixed the player having no pain sounds whatsoever.
Fixed stair step anchoring not working.
The damage dealt when walljumping on a monster now also gets boosted by the Ragekit.
This commit is contained in:
Mari the Deer 2020-03-04 16:55:45 +01:00
commit d1b1a0541d
33 changed files with 1112 additions and 89 deletions

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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"
}

View file

@ -38,3 +38,7 @@ HardwareShader Texture "models/silvermap.png"
{
Shader "shaders/glsl/Shinemap.fp"
}
HardwareShader Texture "models/leadmap.png"
{
Shader "shaders/glsl/Shinemap.fp"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -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";

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

BIN
models/ASmallPriceToPay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Binary file not shown.

BIN
models/leadmap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -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);

View file

@ -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;
}

View file

@ -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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

BIN
textures/rgbnoise.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -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"

View file

@ -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));
}

View file

@ -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<WeaponUsage> wstats;
Array<Class<Weapon> > 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 )

View file

@ -0,0 +1,5 @@
// collectable items that may drop sometimes (future feature)
Class SWWMCollectable : Inventory
{
}

View file

@ -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();
}
}

View file

@ -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<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
}
numpt = Random[Ponch](4,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Ponch](4,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class BigPunchImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_QuakeEx(8,8,8,18,0,600,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.9);
A_StartSound("pusher/althit",CHAN_VOICE);
A_SprayDecal("BigWallCrack",-20);
int numpt = Random[Ponch](9,16);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
}
numpt = Random[Ponch](9,15);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Ponch](9,16);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
// Base class for all SWWM Weapons
Class SWWMWeapon : Weapon abstract
{
@ -363,7 +451,7 @@ Class SWWMWeapon : Weapon abstract
FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count)
override void AttachToOwner (Actor other)
override void AttachToOwner( Actor other )
{
Inventory.AttachToOwner(other);
Ammo1 = AddAmmo(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1);
@ -445,16 +533,17 @@ Class SWWMWeapon : Weapon abstract
private action bool TryMelee( double angle, int dmg )
{
FTranslatedLineTarget t;
double slope = AimLineAttack(angle,2*DEFMELEERANGE,t,0.,ALF_CHECK3D);
double slope = AimLineAttack(angle,1.5*DEFMELEERANGE,t,0.,ALF_CHECK3D);
FLineTraceData d;
LineTrace(angle,2*DEFMELEERANGE,slope,0,player.viewheight,data:d);
LineTrace(angle,1.5*DEFMELEERANGE,slope,0,player.viewheight,data:d);
bool raging = CountInv("RagekitPower");
if ( d.HitType == TRACE_HitActor )
{
bool bloodless = true;
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
self.angle += clamp(diff,-5.,5.);
SWWMHandler.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000);
if ( CountInv("RagekitPower") )
if ( raging )
{
invoker.bEXTREMEDEATH = true;
invoker.bNOEXTREMEDEATH = false;
@ -468,15 +557,22 @@ Class SWWMWeapon : Weapon abstract
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25);
int quakin = raging?8:2;
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(quakin,quakin,quakin,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.125*quakin);
if ( !d.HitActor.bNOBLOOD && !d.HitActor.bINVULNERABLE )
{
d.HitActor.TraceBleed(dmg,invoker);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
bloodless = false;
}
else bloodless = false;
A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12);
A_StartSound(bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP);
else
{
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation);
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);
}
A_QuakeEx(quakin/2,quakin/2,quakin/2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.06*quakin);
if ( raging ) A_StartSound(bloodless?"pusher/althit":"pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP);
else A_StartSound(bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP);
A_AlertMonsters(300);
return true;
}
@ -484,7 +580,8 @@ Class SWWMWeapon : Weapon abstract
}
action void A_Melee( int dmg = 40 )
{
int maxang = CountInv("RagekitPower")?24:12;
bool raging = CountInv("RagekitPower");
int maxang = raging?18:12;
for ( int i=0; i<maxang; i++ ) if ( TryMelee(angle+i*(45./16),dmg) || TryMelee(angle-i*(45./16),dmg) ) return;
// check for walls instead
FTranslatedLineTarget t;
@ -492,10 +589,29 @@ Class SWWMWeapon : Weapon abstract
FLineTraceData d;
LineTrace(angle,DEFMELEERANGE,slope,TRF_THRUACTORS,player.viewheight,data:d);
if ( d.HitType == TRACE_HitNone ) return;
if ( d.HitType == TRACE_HitWall )
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12);
A_StartSound("demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
Vector3 HitNormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.top.Normal;
else HitNormal = d.HitSector.floorplane.Normal;
}
else if ( d.HitType == TRACE_HitCeiling )
{
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.bottom.Normal;
else HitNormal = d.HitSector.ceilingplane.Normal;
}
else if ( d.HitType == TRACE_HitWall )
{
HitNormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) HitNormal *= -1;
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
}
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation+HitNormal*4);
p.angle = atan2(HitNormal.y,HitNormal.x);
p.pitch = asin(-HitNormal.z);
int quakin = raging?4:1;
A_QuakeEx(quakin,quakin,quakin,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12*quakin);
A_StartSound(raging?"pusher/althit":"demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
A_AlertMonsters(100);
}
override void PlayUpSound( Actor origin )

View file

@ -331,7 +331,6 @@ Class PusherWeapon : SWWMWeapon
A_QuakeEx(3,3,3,7,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
A_AlertMonsters(1200);
int dmg = int(3+invoker.chargelevel*2);
bool bloodless = false;
if ( d.HitType == TRACE_HitActor )
{
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
@ -351,6 +350,8 @@ Class PusherWeapon : SWWMWeapon
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
d.HitActor.A_StartSound("pusher/meat",CHAN_ITEMEXTRA,CHANF_OVERLAP);
}
// move towards target
vel.xy += Vec2To(d.HitActor).unit();
}
else
{
@ -413,7 +414,6 @@ Class PusherWeapon : SWWMWeapon
A_QuakeEx(8,8,8,12,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
A_AlertMonsters(1600);
int dmg = int(80*invoker.chargelevel);
bool bloodless = false;
if ( d.HitType == TRACE_HitActor )
{
double diff = deltaangle(self.angle,AngleTo(d.HitActor));

View file

@ -430,7 +430,6 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
if ( players[consoleplayer].Health <= 0 )
{
// ded
MenuSound("menu/democlose");
Close();
return;
}
@ -440,7 +439,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') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) ) 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) && !(inv is 'HammerspaceEmbiggener') && !(inv is 'SWWMCollectable') ) continue;
String tag = inv.GetTag();
bool greater = false;
for ( int i=0; i<invlist.Size(); i++ )
@ -796,9 +795,9 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
xx = 9;
yy = 23;
// wish I could use macros for this
int thour = ((gametic-stats.lastspawn)/(3600*Thinker.TICRATE));
int tmin = ((gametic-stats.lastspawn)/(60*Thinker.TICRATE))%60;
int tsec = ((gametic-stats.lastspawn)/Thinker.TICRATE)%60;
int thour = ((level.totaltime-stats.lastspawn)/(3600*Thinker.TICRATE));
int tmin = ((level.totaltime-stats.lastspawn)/(60*Thinker.TICRATE))%60;
int tsec = ((level.totaltime-stats.lastspawn)/Thinker.TICRATE)%60;
str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec);
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
@ -852,6 +851,9 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATMKILL"),stats.mkill);
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSKILL"),stats.skill);
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_STATFAVWEAP"));
if ( stats.favweapon == -1 ) str = str.."N/A";
else

View file

@ -9,7 +9,7 @@ Class Demolitionist : PlayerPawn
bool sendtoground;
bool key_reentrant;
int lastdamage;
int lastdamage, lastdamagetic;
bool lastground;
int lastgroundtic;
double lastvelz, prevvelz;
@ -33,6 +33,7 @@ Class Demolitionist : PlayerPawn
};
int lastunder;
int deadtimer, revivefail;
Default
{
@ -503,7 +504,8 @@ Class Demolitionist : PlayerPawn
// lucky collar
if ( Health < 25 ) damage /= 4;
if ( source == self ) damage /= 2;
int lastdamage = Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
lastdamage = Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
lastdamagetic = max(lastdamagetic,gametic+clamp(lastdamage,10,80));
if ( (lastdamage > 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
}
}

View file

@ -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 )

View file

@ -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<numpt; i++ )
{
Vector3 pos = victim.Vec3Offset(FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](victim.height*0.2,victim.height*0.8));
double ang = FRandom[FlameT](0,360);
double pt = FRandom[FlameT](-90,90);
if ( amount > 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<CurSector.Get3DFloorCount(); i++ )
{
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
ff = CurSector.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.top.Normal;
else HitNormal = BlockingFloor.floorplane.Normal;
}
else if ( BlockingCeiling )
{
// find closest 3d floor for its normal
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
{
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
ff = CurSector.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.bottom.Normal;
else HitNormal = BlockingCeiling.ceilingplane.Normal;
}
else if ( BlockingLine )
{
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
HitNormal *= -1;
}
else if ( BlockingMobj )
{
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
HitNormal = diff.unit();
}
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
vel = .8*((vel dot HitNormal)*HitNormal*-1.2+vel);
gravity = .35;
if ( (vel.length() < 5) && (pos.z <= floorz) )
{
ClearBounce();
ExplodeMissile();
}
rollvel = FRandom[FlameT](10,30)*RandomPick[FlameT](-1,1);
}
Default
{
Obituary "$O_SPREADGUN_BLACK";
DamageFunction 1;
DamageType 'Fire';
Radius 4;
Height 4;
Speed 15;
Gravity 0.2;
PROJECTILE;
-NOGRAVITY;
+ROLLSPRITE;
+ROLLCENTER;
+EXPLODEONWATER;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+NODAMAGETHRUST;
+INTERPOLATEANGLES;
+RIPPER;
+BLOODLESSIMPACT;
+NOTELEPORT;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+USEBOUNCESTATE;
BounceFactor 1.0;
}
States
{
Spawn:
JUNK # 1 { roll += rollvel; }
Wait;
Bounce:
JUNK # 0 A_HandleBounce();
Goto Spawn;
Death:
JUNK # -1 { bMOVEWITHSECTOR = true; }
Stop;
Dummy:
JUNK ABCDEF -1;
Stop;
}
}
Class BallImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_QuakeEx(3,3,3,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3);
A_StartSound("spreadgun/ball",CHAN_VOICE);
A_SprayDecal("WallCrack",-20);
int numpt = Random[Spreadgun](5,10);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8))).unit()*FRandom[Spreadgun](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Spreadgun](128,192));
}
numpt = Random[Spreadgun](4,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Spreadgun](4,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class TheBall : Actor
{
double heat;
int deadtimer;
Vector3 oldvel;
Actor lasthit;
Default
{
Obituary "$O_SPREADGUN_PURPLE";
+NOBLOCKMAP;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+USEBOUNCESTATE;
+MISSILE;
+NODAMAGETHRUST;
+NOTELEPORT;
-NOGRAVITY;
Speed 80;
Gravity 0.1;
BounceFactor 1.0;
Radius 2;
Height 2;
}
override int SpecialMissileHit( Actor victim )
{
if ( (vel.length() <= 5) || ((victim == target) && !bHITOWNER) || (victim == lasthit) || (!victim.bSHOOTABLE && !victim.bSOLID) )
return 1;
// check if we should rip or bounce
// girthitude
double girth = (victim.radius+victim.height)/2.*max(50,victim.mass);
// how hard this damn thing is going to slam
double slamforce = vel.length()*350.+heat*120;
SWWMHandler.DoKnockback(victim,vel.unit(),slamforce);
bool bleeds = (victim && !victim.bINVULNERABLE && !victim.bNOBLOOD && victim.bSHOOTABLE);
int dmg = int(vel.length()*4.2+heat*80);
dmg = victim.DamageMobj(self,target,dmg,'Concussion',DMG_THRUSTLESS);
Vector3 dir = -vel.unit();
// slam jam
if ( bleeds )
{
A_StartSound("spreadgun/ballf",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/75.)**.5);
victim.TraceBleed(dmg,self);
SpawnBlood(pos,atan2(dir.y,dir.x),dmg);
}
else
{
A_StartSound("spreadgun/ball",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/75.)**.5);
if ( vel.length() > 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<CurSector.Get3DFloorCount(); i++ )
{
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
ff = CurSector.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.top.Normal;
else HitNormal = BlockingFloor.floorplane.Normal;
}
else if ( BlockingCeiling )
{
// find closest 3d floor for its normal
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
{
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
ff = CurSector.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.bottom.Normal;
else HitNormal = BlockingCeiling.ceilingplane.Normal;
}
else if ( BlockingLine )
{
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
HitNormal *= -1;
}
else if ( BlockingMobj )
{
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
HitNormal = diff.unit();
}
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
double bcefact = .9;
if ( BlockingMobj )
{
bcefact *= .7;
if ( !BlockingMobj.bINVULNERABLE && !BlockingMobj.bNOBLOOD )
bcefact *= .6;
}
vel = bcefact*((vel dot HitNormal)*HitNormal*-1.2+vel);
// slam jam
if ( !BlockingMobj )
{
A_StartSound("spreadgun/ball",CHAN_VOICE,CHANF_OVERLAP,max(0.,(vel.length()/60.-.1))**.5);
if ( vel.length() > 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-");