Spreadgun buckshot and slug implemented.

Tweaks here and there to other stuff.
Refined wall jumping/climbing, also works on actors (including other players).
Refined how boosting/dashing handles falling speeds.
Added improved air control. It was very much needed.
Added "kick" sounds to wall jumps.
Add option to hear other player's voices in mp.
Fix some broken localization.
Fix invulnerable monsters bleeding from some attacks.
Fix desync when jumping on top of another player with prediction enabled.
Make moths immune to your damage, so you can stop accidentally killing them.
Make normal ammo buyable in Hexen again.
This commit is contained in:
Mari the Deer 2020-02-27 02:00:17 +01:00
commit 7a01fdc4e8
57 changed files with 979 additions and 115 deletions

View file

@ -29,3 +29,6 @@ user bool swwm_healthnums = true; // show damage/healing numbers
user bool swwm_scorenums = true; // show score numbers
user bool swwm_scorebonus = true; // show score bonuses
user bool swwm_fly6dof = true; // flying uses 6dof movement, toggleable for those who get motion sickness
user bool swwm_othervoice = true; // can hear other player's voice lines in coop
user bool swwm_shaders = true; // use pp shaders for some effects
user bool swwm_earbuster = false; // limits loudness of wallbuster fire sounds

View file

@ -93,3 +93,67 @@ decal WumboScorch
randomflipx
randomflipy
}
decal TinyBulletChip1
{
pic CHIP1
translucent 0.85
shade "00 00 00"
x-scale 0.22
y-scale 0.22
randomflipx
randomflipy
}
decal TinyBulletChip2
{
pic CHIP2
translucent 0.85
shade "00 00 00"
x-scale 0.22
y-scale 0.22
randomflipx
randomflipy
}
decal TinyBulletChip3
{
pic CHIP3
translucent 0.85
shade "00 00 00"
x-scale 0.22
y-scale 0.22
randomflipx
randomflipy
}
decal TinyBulletChip4
{
pic CHIP4
translucent 0.85
shade "00 00 00"
x-scale 0.22
y-scale 0.22
randomflipx
randomflipy
}
decal TinyBulletChip5
{
pic CHIP5
translucent 0.85
shade "00 00 00"
x-scale 0.22
y-scale 0.22
randomflipx
randomflipy
}
decalgroup TinyBulletChip
{
TinyBulletChip1 1
TinyBulletChip2 1
TinyBulletChip3 1
TinyBulletChip4 1
TinyBulletChip5 1
}

View file

@ -1,8 +1,8 @@
addkeysection "$SWWM_KEYS" "SWWM_GZ"
addmenukey "$SWWM_PRIMARYFIRE" "+attack"
addmenukey "$SWWM_SECONDARYFIRE" "+altattack"
addmenukey "$SWWM_TERTIARYFIRE" "+reload"
addmenukey "$SWWM_QUATERNARYFIRE" "+zoom"
addmenukey "$SWWM_RELOADFIRE" "+reload"
addmenukey "$SWWM_ZOOMFIREFIRE" "+zoom"
addmenukey "$SWWM_MELEE" "+user1"
addmenukey "$SWWM_DASH" "+user2"
addmenukey "$SWWM_GESTURE1" "netevent swwmgesture 0"

View file

@ -95,7 +95,7 @@ TOOLTIP_SWWM_SCORENUMS = "Show score kill numbers.";
TOOLTIP_SWWM_SCOREBONUS = "Show additional bonus strings above score numbers (e.g.: multikill counters).";
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. If a player is using a voice pack you don't have, they will use the default voice.";
TOOLTIP_SWWM_OTHERVOICE = "Allows you to hear other player's voice lines, provided you're close enough.";
// knowledge base
SWWM_COMINGSOON = "(coming soon)";
SWWM_MISSTAB = "Mission";
@ -275,7 +275,7 @@ SWWM_LORETAG_BIGSHOT = "Mr. BIG SHOT";
SWWM_LORETAB_BIGSHOT = "People";
SWWM_LOREREL_BIGSHOT = "Blackmann;Saya";
SWWM_LORETXT_BIGSHOT =
"Full Name: John \"Bigshot\" Gunns"
"Full Name: John \"Bigshot\" Gunns\n"
"Nationality: American\n"
"Date Of Birth: 2110-11-12\n"
"\n"
@ -874,7 +874,7 @@ SWWM_LORETXT_MAIDBOT =
"\n"
"Summary: The first maidbot ever created by Saya, with all others being based on her. Although Maidbot works for the Miyamotos as a servant, she is also considered part of the family, and lives with them, even having her own bedroom. On the side, she's had a successful career as a J-Pop singer and idol, under the nickname of \"Meidobot\".\n"
"\n"
"Addendum: Maidbot was once fitted into an experimental \"combat body\" early on in order to assist the Red Oni in her mission on the US. She proved effective, but was not comfortable with the task and preferred to go back to being a normal girl afterwards.\n"
"Addendum: Maidbot was once fitted into an experimental \"combat body\" in order to assist the Red Oni in her mission on the US. She proved effective, but was not comfortable with the task and preferred to go back to being a normal girl afterwards.\n"
"\n"
"Saya's Note: You know... all my maidbots, are fully equipped, hm? You know what I mean there... they're fully capable of \"loving\". Maidbot is no exception herself, and let me tell you... it feels like heaven to do things with her... ♥";
SWWM_LORETAG_MIXOM = "Mixom";
@ -1545,9 +1545,9 @@ T_KEYCASTLE = "\cxCastle Key\c-";
GOTBLUECARD = "Blue Keycard";
GOTYELWCARD = "Yellow Keycard";
GOTREDCARD = "Red Keycard";
GOTBLUESKULL = "Blue Skull Key";
GOTYELWSKULL = "Yellow Skull Key";
GOTREDSKULL = "Red Skull Key";
GOTBLUESKUL = "Blue Skull Key";
GOTYELWSKUL = "Yellow Skull Key";
GOTREDSKUL = "Red Skull Key";
// edited vanilla pickup messages
TXT_DEFAULTPICKUPMSG = "Unidentified Item";
// other edited messages
@ -1648,6 +1648,7 @@ O_YNYKRON = "%o was instantly removed by %k.";
O_YNYKRONALT = "%o was spaghettified by %k.";
O_POUND = "%o was very impressed by %k's landing.";
O_DASH = "%o was discombobulated by a very fast moving %k.";
O_JUMP = "%o was stepped on %k.";
O_MELEE = "%o was K.O.'d by %k.";
O_MOTH = "%%o was assaulted by %s's moths.";
O_MOTH2 = "%o was assaulted by moths.";
@ -1700,7 +1701,7 @@ SWWM_NEXIT = "Exit";
SWWM_SEXIT = "Secret Exit";
// score messages
SWWM_FINDSECRET = "\cf%s\cf found a secret. +%d\c-";
SWWM_FINDKEY = "\cf%s\cf got the %s. +%d\c-";
SWWM_FINDKEY = "\cf%s\cf got the %s\cf. +%d\c-";
SWWM_LASTSECRET = "\cf%s\cf found the last secret. +%d\c-";
SWWM_LASTITEM = "\cf%s\cf got the last item. +%d\c-";
SWWM_LASTMONSTER = "\cf%s\cf killed the last monster. +%d\c-";
@ -1727,7 +1728,7 @@ SWWM_INTERTIP18 = "All weapons have a quick melee attack available through a ded
SWWM_INTERTIP19 = "With the right combination of dashing, boosting and wall jumping, it is perfectly possible to remain in the air for extended periods of time.";
SWWM_INTERTIP20 = "In multiplayer, any keys you pick up will be immediately shared with other players.";
SWWM_INTERTIP21 = "Aim for a high score! The more enemies you kill the better. Don't forget to put that cash to use at the in-game store when you can.";
SWWM_INTERTIP22 = "Although chance boxes may sound like a terrible idea, there is a high chance to obtain very valuable items for a much lower cost.";
SWWM_INTERTIP22 = "Although chance boxes may sound like a terrible idea, there is a high probability of obtaining very valuable items for a much lower cost.";
/* SUBTITLES */
// voice name
@ -2052,7 +2053,7 @@ TOOLTIP_SWWM_SCORENUMS = "Muestra números de puntuación con cada baja.";
TOOLTIP_SWWM_SCOREBONUS = "Muestra textos de bonificación extra sobre los puntos (ej.: contadores de multikill).";
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. Si un jugador está usando un pack de voz que no tienes, usarán la voz por defecto.";
TOOLTIP_SWWM_OTHERVOICE = "Te permite oír los comentarios de voz de otros jugadores, si estás lo suficientemente cerca.";
// knowledge base
SWWM_COMINGSOON = "(próximamente)";
SWWM_MISSTAB = "Misión";
@ -2123,7 +2124,7 @@ SWWM_MISSION_DOOM =
"\n"
"No hemos podido proveerte de todo el equipamiento disponible, no hay tiempo. En vez de eso, vete fijando por si encuentras provisiones colocadas estratégicamente por el camino (si hay suerte los demonios no tocarán nada). Se que suena muy a videojuego, y si te oigo quejarte me puedes comer los calzones, la decisión no fue mía. Pues eso.\n"
"\n"
"Eres nuestra última esperanza, asi que lucha, Demo-chan, lucha por aquellos que quieres proteger, por todos nosotros... Haz que esos bastardos lo paguen.\n"
"Eres nuestra última esperanza, así que lucha, Demo-chan, lucha por aquellos que quieres proteger, por todos nosotros... Haz que esos bastardos lo paguen.\n"
"\n"
" — Saya";
SWWM_MISSION_HERETIC =
@ -3262,9 +3263,9 @@ T_KEYCASTLE = "\cxLlave de Castillo\c-";
GOTBLUECARD = "Tarjeta Llave Azul";
GOTYELWCARD = "Tarjeta Llave Amarilla";
GOTREDCARD = "Tarjeta Llave Roja";
GOTBLUESKULL = "Calavera Llave Azul";
GOTYELWSKULL = "Calavera Llave Amarilla";
GOTREDSKULL = "Calavera Llave Roja";
GOTBLUESKUL = "Calavera Llave Azul";
GOTYELWSKUL = "Calavera Llave Amarilla";
GOTREDSKUL = "Calavera Llave Roja";
// edited vanilla pickup messages
TXT_DEFAULTPICKUPMSG = "Item No Identificado";
// other edited messages
@ -3312,7 +3313,7 @@ FN_WOLFSS = "Votante de VOX";
FN_DOG = "Perrete";
FN_CHICKEN = "Gallina";
FN_CLINK = "Rasguñitos";
FN_DSPARIL = "Nosequé de Dos Perales";
FN_DSPARIL = "El Brujo Ese";
FN_HERETICIMP = "Bicho con Alas";
FN_IRONLICH = "Risitas";
FN_BONEKNIGHT = "Huesitos";
@ -3357,6 +3358,7 @@ O_YNYKRON = "%o fue borrad@[ao_esp] instantáneamente por %k.";
O_YNYKRONALT = "%o fue espaguetificad@[ao_esp] por %k.";
O_POUND = "%o se llevó una gran impresión del aterrizaje de %k.";
O_DASH = "%o fue descuajeringad@[ao_esp] a todo gas por %k.";
O_JUMP = "%o fue pisotead@[ao_esp] por %k.";
O_MELEE = "%o fue noquead@[ao_esp] por %k.";
O_MOTH = "%%o fue asaltad@[ao_esp] por las polillas de %s.";
O_MOTH2 = "%o fue asaltad@[ao_esp] por polillas.";
@ -3407,7 +3409,7 @@ SWWM_NEXIT = "Salida";
SWWM_SEXIT = "Salida Secreta";
// score messages
SWWM_FINDSECRET = "\cf%s\cf encontró un secreto. +%d\c-";
SWWM_FINDKEY = "\cf%s\cf obtuvo la %s. +%d\c-";
SWWM_FINDKEY = "\cf%s\cf obtuvo la %s\cf. +%d\c-";
SWWM_LASTSECRET = "\cf%s\cf encontró el último secreto. +%d\c-";
SWWM_LASTITEM = "\cf%s\cf obtuvo el último item. +%d\c-";
SWWM_LASTMONSTER = "\cf%s\cf mató al último enemigo. +%d\c-";

View file

@ -36,17 +36,20 @@ OptionMenu "SWWMOptionMenu"
Option "$SWWM_DAMNUMS", "swwm_healthnums", "YesNo"
Option "$SWWM_SCORENUMS", "swwm_scorenums", "YesNo"
Option "$SWWM_SCOREBONUS", "swwm_scorebonus", "YesNo"
Option "$SWWM_SHADERS", "swwm_shaders", "YesNo"
StaticText " "
StaticText "$SWWM_ITITLE", "Gold"
Option "$SWWM_ARMORUSE", "swwm_autousearmor", "YesNo"
Option "$SWWM_HEALTHUSE", "swwm_autousehealth", "YesNo"
Option "$SWWM_AMMOUSE", "swwm_autouseammo", "YesNo"
Option "$SWWM_EARBUSTER", "swwm_earbuster", "YesNo"
StaticText " "
StaticText "$SWWM_CTITLE", "Gold"
Option "$SWWM_SKEYS", "swwm_sharekeys", "YesNo"
Option "$SWWM_SVARMORUSE", "swwm_enforceautousearmor", "SWWMEnforce"
Option "$SWWM_SVHEALTHUSE", "swwm_enforceautousehealth", "SWWMEnforce"
Option "$SWWM_SVAMMOUSE", "swwm_enforceautouseammo", "SWWMEnforce"
Option "$SWWM_OTHERVOICE", "swwm_othervoice", "YesNo"
}
OptionMenu "SWWMCreditsMenu"
{

View file

@ -1,3 +1,95 @@
Model "RedShellCasing"
{
Path "models"
Model 0 "ShotShell_d.3d"
Skin 0 "Shell_Normal_Used.png"
Scale 0.15 0.15 0.15
ZOffset 1
USEACTORPITCH
USEACTORROLL
FrameIndex XZW1 A 0 0
}
Model "GreenShellCasing"
{
Path "models"
Model 0 "ShotShell_d.3d"
Skin 0 "Shell_Slug_Used.png"
Scale 0.15 0.15 0.15
ZOffset 1
USEACTORPITCH
USEACTORROLL
FrameIndex XZW1 A 0 0
}
Model "WhiteShellCasing"
{
Path "models"
Model 0 "ShotShell_d.3d"
Skin 0 "Shell_Dragon_Used.png"
Scale 0.15 0.15 0.15
ZOffset 1
USEACTORPITCH
USEACTORROLL
FrameIndex XZW1 A 0 0
}
Model "BlueShellCasing"
{
Path "models"
Model 0 "ShotShell_d.3d"
Skin 0 "Shell_Kinylum_Used.png"
Scale 0.15 0.15 0.15
ZOffset 1
USEACTORPITCH
USEACTORROLL
FrameIndex XZW1 A 0 0
}
Model "BlackShellCasing"
{
Path "models"
Model 0 "ShotShell_d.3d"
Skin 0 "Shell_Fuck_Used.png"
Scale 0.15 0.15 0.15
ZOffset 1
USEACTORPITCH
USEACTORROLL
FrameIndex XZW1 A 0 0
}
Model "PurpleShellCasing"
{
Path "models"
Model 0 "ShotShell_d.3d"
Skin 0 "Shell_Ball_Used.png"
Scale 0.15 0.15 0.15
ZOffset 1
USEACTORPITCH
USEACTORROLL
FrameIndex XZW1 A 0 0
}
Model "GoldShellCasing"
{
Path "models"
Model 0 "ShotShell_d.3d"
Skin 0 "Shell_Gold_Used.png"
Scale 0.15 0.15 0.15
ZOffset 1
USEACTORPITCH
USEACTORROLL
FrameIndex XZW1 A 0 0
}
Model "Spreadgun"
{
Path "models"

View file

@ -311,6 +311,10 @@ demolitionist/bump1 sounds/demolitionist/demobump1.ogg
demolitionist/bump2 sounds/demolitionist/demobump2.ogg
demolitionist/bump3 sounds/demolitionist/demobump3.ogg
$random demolitionist/bump { demolitionist/bump1 demolitionist/bump2 demolitionist/bump3 }
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 }
$playersound demolitionist neutral *grunt DSEMPTY
$playeralias demolitionist neutral *pain100 demolitionist/lopain
@ -447,6 +451,52 @@ spreadgun/goldfire1 sounds/spreadgun/spread_goldfire1.ogg
spreadgun/goldfire2 sounds/spreadgun/spread_goldfire2.ogg
$random spreadgun/goldfire { spreadgun/goldfire1 spreadgun/goldfire2 }
spreadgun/checkgun sounds/spreadgun/spread_idle.ogg
spreadgun/casing1 sounds/spreadgun/spread_casing1.ogg
spreadgun/casing2 sounds/spreadgun/spread_casing2.ogg
spreadgun/casing3 sounds/spreadgun/spread_casing3.ogg
spreadgun/casing4 sounds/spreadgun/spread_casing4.ogg
spreadgun/casing5 sounds/spreadgun/spread_casing5.ogg
spreadgun/casing6 sounds/spreadgun/spread_casing6.ogg
$random spreadgun/casing { spreadgun/casing1 spreadgun/casing2 spreadgun/casing3 spreadgun/casing4 spreadgun/casing5 spreadgun/casing6 }
spreadgun/gcasing1 sounds/spreadgun/spread_gcasing1.ogg
spreadgun/gcasing2 sounds/spreadgun/spread_gcasing2.ogg
spreadgun/gcasing3 sounds/spreadgun/spread_gcasing3.ogg
$random spreadgun/gcasing { spreadgun/gcasing1 spreadgun/gcasing2 spreadgun/gcasing3 }
spreadgun/pellet1 sounds/spreadgun/spread_pellet1.ogg
spreadgun/pellet2 sounds/spreadgun/spread_pellet2.ogg
spreadgun/pellet3 sounds/spreadgun/spread_pellet3.ogg
spreadgun/pellet4 sounds/spreadgun/spread_pellet4.ogg
spreadgun/pellet5 sounds/spreadgun/spread_pellet5.ogg
spreadgun/pellet6 sounds/spreadgun/spread_pellet6.ogg
spreadgun/pellet7 sounds/spreadgun/spread_pellet7.ogg
spreadgun/pellet8 sounds/spreadgun/spread_pellet8.ogg
$random spreadgun/pellet { spreadgun/pellet1 spreadgun/pellet2 spreadgun/pellet3 spreadgun/pellet4 spreadgun/pellet5 spreadgun/pellet6 spreadgun/pellet7 spreadgun/pellet8 }
spreadgun/pelletf1 sounds/spreadgun/spread_pelletf1.ogg
spreadgun/pelletf2 sounds/spreadgun/spread_pelletf2.ogg
spreadgun/pelletf3 sounds/spreadgun/spread_pelletf3.ogg
spreadgun/pelletf4 sounds/spreadgun/spread_pelletf4.ogg
spreadgun/pelletf5 sounds/spreadgun/spread_pelletf5.ogg
spreadgun/pelletf6 sounds/spreadgun/spread_pelletf6.ogg
$random spreadgun/pelletf { spreadgun/pelletf1 spreadgun/pelletf2 spreadgun/pelletf3 spreadgun/pelletf4 spreadgun/pelletf5 spreadgun/pelletf6 }
spreadgun/slug1 sounds/spreadgun/spread_slug1.ogg
spreadgun/slug2 sounds/spreadgun/spread_slug2.ogg
$random spreadgun/slug { spreadgun/slug1 spreadgun/slug2 }
spreadgun/slugf1 sounds/spreadgun/spread_slugf1.ogg
spreadgun/slugf2 sounds/spreadgun/spread_slugf2.ogg
$random spreadgun/slugf { spreadgun/slugf1 spreadgun/slugf2 }
spreadgun/ball1 sounds/spreadgun/spread_ball1.ogg
spreadgun/ball2 sounds/spreadgun/spread_ball2.ogg
spreadgun/ball3 sounds/spreadgun/spread_ball3.ogg
$random spreadgun/ball { spreadgun/ball1 spreadgun/ball2 spreadgun/ball3 }
spreadgun/ballf1 sounds/spreadgun/spread_ballf1.ogg
spreadgun/ballf2 sounds/spreadgun/spread_ballf2.ogg
spreadgun/ballf3 sounds/spreadgun/spread_ballf3.ogg
$random spreadgun/ballf { spreadgun/ballf1 spreadgun/ballf2 spreadgun/ballf3 }
spreadgun/salt1 sounds/spreadgun/spread_salt1.ogg
spreadgun/salt2 sounds/spreadgun/spread_salt2.ogg
spreadgun/salt3 sounds/spreadgun/spread_salt3.ogg
spreadgun/salt4 sounds/spreadgun/spread_salt4.ogg
$random spreadgun/salt { spreadgun/salt1 spreadgun/salt2 spreadgun/salt3 spreadgun/salt4 }
candygun/fire1 sounds/candygun/candy_fire1.ogg
candygun/fire2 sounds/candygun/candy_fire2.ogg

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -874,7 +874,7 @@ Class FabricatorTier1 : AmmoFabricator
Inventory.PickupMessage "$T_FABRICATOR1";
Inventory.MaxAmount 30;
AmmoFabricator.Budget 2000;
AmmoFabricator.PerType 4;
AmmoFabricator.PerType 2;
Stamina 3000;
}
}
@ -887,7 +887,7 @@ Class FabricatorTier2 : AmmoFabricator
Inventory.PickupMessage "$T_FABRICATOR2";
Inventory.MaxAmount 20;
AmmoFabricator.Budget 15000;
AmmoFabricator.PerType 8;
AmmoFabricator.PerType 4;
Stamina 12000;
}
}
@ -900,7 +900,7 @@ Class FabricatorTier3 : AmmoFabricator
Inventory.PickupMessage "$T_FABRICATOR3";
Inventory.MaxAmount 10;
AmmoFabricator.Budget 100000;
AmmoFabricator.PerType 16;
AmmoFabricator.PerType 8;
Stamina 150000;
}
}

View file

@ -515,6 +515,7 @@ Class SWWMOneLiner : HUDMessageBase
override bool Tick()
{
if ( players[consoleplayer].Health <= 0 ) curtime = int.min;
curtime--;
return (curtime<-20);
}
@ -818,6 +819,7 @@ Class SWWMChip : Actor
+INTERPOLATEANGLES;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
BounceType "Doom";
BounceFactor 0.3;
Gravity 0.35;
@ -1019,14 +1021,14 @@ Class SWWMBulletTrail : LineTracer
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[0].hitpos);
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
b.A_CheckTerrain();
}
for ( int i=5; i<t.Results.Distance; i+=10 )
{
if ( !Random[Boolet](0,bubblechance) ) continue;
let b = Actor.Spawn(smoky?"SWWMSmallSmoke":"SWWMBubble",level.Vec3Offset(pos,dir*i));
b.Scale *= FRandom[Boolet](0.4,0.6);
b.Scale *= FRandom[Boolet](.4,.6);
}
t.Destroy();
}
@ -1177,7 +1179,7 @@ Class LastLine
Class SWWMHandler : EventHandler
{
transient String oneliner, onelinersnd;
transient int onelinertic, onelinerspan;
transient int onelinertic, onelinerspan, onelinerlevel;
transient int lastlock, lastcombat;
transient Array<Actor> combatactors;
transient Array<Int> combattics;
@ -1192,25 +1194,40 @@ Class SWWMHandler : EventHandler
transient CVar mutevoice;
static int AddOneliner( String type, int delay = 5 )
static int AddOneliner( String type, int level, int delay = 5 )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return 0;
CVar voicetype = CVar.GetCVar('swwm_voicetype',players[consoleplayer]);
String voicetype = CVar.GetCVar('swwm_voicetype',players[consoleplayer]).GetString();
// suppress non-rage comments when ragekit is active, only screaming allowed
if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") && (CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() < 2) ) return 0;
if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") ) return 0;
int whichline;
int countem = 0, i = 1;
String testme, locme;
do
{
testme = String.Format("SWWM_SUBS_%s_%s%d",voicetype.GetString().MakeUpper(),type.MakeUpper(),i);
testme = String.Format("SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),i);
locme = StringTable.Localize(testme,false);
if ( testme != locme ) countem++;
i++;
}
while ( (testme != locme) && (i < 100) ); // gotta prevent infinite loops
if ( countem == 0 ) return 0;
if ( countem == 0 )
{
if ( voicetype ~== "default" ) return 0;
// retry with the default voicetype
voicetype = "default";
i = 1;
do
{
testme = String.Format("SWWM_SUBS_DEFAULT_%s%d",type.MakeUpper(),i);
locme = StringTable.Localize(testme,false);
if ( testme != locme ) countem++;
i++;
}
while ( (testme != locme) && (i < 100) ); // gotta prevent infinite loops
if ( countem == 0 ) return 0;
}
// check last line so we don't repeat
int last = 0, ent;
for ( int i=0; i<hnd.lastlines.Size(); i++ )
@ -1234,10 +1251,11 @@ Class SWWMHandler : EventHandler
lst.lineno = whichline;
hnd.lastlines.Push(lst);
}
hnd.oneliner = String.Format("$SWWM_SUBS_%s_%s%d",voicetype.GetString().MakeUpper(),type.MakeUpper(),whichline);
hnd.onelinersnd = String.Format("voice/%s/%s%d",voicetype.GetString(),type,whichline);
hnd.oneliner = String.Format("$SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),whichline);
hnd.onelinersnd = String.Format("voice/%s/%s%d",voicetype,type,whichline);
hnd.onelinertic = gametic+delay;
hnd.onelinerspan = int(S_GetLength(hnd.onelinersnd)*Thinker.TICRATE);
hnd.onelinerlevel = level;
return hnd.onelinertic+hnd.onelinerspan;
}
@ -1325,8 +1343,8 @@ Class SWWMHandler : EventHandler
override void WorldLoaded( WorldEvent e )
{
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
if ( !e.IsSaveGame && !e.IsReopen && (gamestate != GS_TITLELEVEL) && (mutevoice.GetInt() < 3) )
AddOneliner("mapstart");
if ( !e.IsSaveGame && !e.IsReopen && (gamestate != GS_TITLELEVEL) )
AddOneliner("mapstart",3);
if ( !e.IsSaveGame && !e.IsReopen )
{
// for skipping over merged exit lines (sharing vertices)
@ -1467,7 +1485,11 @@ Class SWWMHandler : EventHandler
if ( onelinertic && (onelinertic < gametic) )
{
if ( players[consoleplayer].health > 0 )
players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE);
{
if ( onelinerlevel > mutevoice.GetInt() )
players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE);
SendNetworkEvent("swwmremoteliner."..onelinersnd,consoleplayer,onelinerlevel);
}
onelinertic = 0;
onelinerspan = 0;
}
@ -1540,8 +1562,8 @@ Class SWWMHandler : EventHandler
enteredcombat = true;
}
}
if ( enteredcombat && (!highesttic || (gametic > highesttic+100)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner("fightstart",10);
if ( enteredcombat && (!highesttic || (gametic > highesttic+100)) )
lastcombat = AddOneliner("fightstart",1,10);
}
override void WorldThingDied( WorldEvent e )
@ -1557,7 +1579,6 @@ Class SWWMHandler : EventHandler
override void WorldThingDamaged( WorldEvent e )
{
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
if ( e.Damage > 0 )
SWWMScoreObj.Spawn(-e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),Font.CR_RED);
if ( e.Thing.player )
@ -1577,8 +1598,8 @@ Class SWWMHandler : EventHandler
{
if ( (e.DamageSource.bISMONSTER || e.DamageSource.player) && (e.Thing == players[consoleplayer].mo) && (e.Thing.Health > 0) )
{
if ( (!lastcombat || (gametic > lastcombat+20)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner(e.Thing.IsFriend(e.DamageSource)?"friendhit":"gethit",15);
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner(e.Thing.IsFriend(e.DamageSource)?"friendhit":"gethit",1,15);
highesttic = gametic;
}
if ( (e.DamageSource == players[consoleplayer].mo) && (e.Thing.bISMONSTER || e.Thing.player) )
@ -1586,8 +1607,8 @@ Class SWWMHandler : EventHandler
// make sure it's not a moth, because otherwise they won't shut up about accidentally hurting them (it happens a lot)
if ( e.Thing.IsFriend(e.DamageSource) && !(e.Thing is 'LampMoth') )
{
if ( (!lastcombat || (gametic > lastcombat+20)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner("hitfriend",15);
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner("hitfriend",1,15);
highesttic = gametic;
}
}
@ -1606,8 +1627,8 @@ Class SWWMHandler : EventHandler
if ( e.DamageSource == players[consoleplayer].mo )
{
highesttic = gametic;
if ( (!lastcombat || (gametic > lastcombat+20)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner("scorekill",15);
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner("scorekill",1,15);
}
if ( !e.Thing.default.bCountKill ) // no credits
return;
@ -1621,7 +1642,7 @@ Class SWWMHandler : EventHandler
// scoring
int score = min(2000,int(ceil(e.Thing.SpawnHealth()*.5)*10));
int ofs = 0;
if ( e.Thing.Health <= e.Thing.GetGibHealth() )
if ( ((e.Thing.Health <= e.Thing.GetGibHealth()) || (e.DamageSource.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH)) && !e.DamageSource.bNOEXTREMEDEATH && !(e.Inflictor && e.Inflictor.bNOEXTREMEDEATH) )
{
score = int(score*1.25);
if ( e.DamageSource.player == players[consoleplayer] )
@ -1640,7 +1661,7 @@ Class SWWMHandler : EventHandler
{
score += 10000;
if ( e.DamageSource.player == players[consoleplayer] )
SWWMScoreObj.Spawn(score,e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_BOSSKILL",++ofs);
SWWMScoreObj.Spawn(0,e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_BOSSKILL",++ofs);
}
SWWMCredits.Give(e.DamageSource.player,score);
if ( e.DamageSource.player == players[consoleplayer] )
@ -1702,6 +1723,7 @@ Class SWWMHandler : EventHandler
{
let l = SWWMOneLiner.Make(oneliner,onelinerspan);
StatusBar.AttachMessage(l,-3473);
SendNetworkEvent("swwmremotelinertxt."..oneliner,consoleplayer,onelinerlevel);
}
for ( int i=0; i<flashes.size(); i++ )
{
@ -1738,8 +1760,8 @@ Class SWWMHandler : EventHandler
if ( !locknum ) return;
if ( e.Thing.CheckLocalView() && !e.Thing.CheckKeys(locknum,false,true) )
{
if ( (!lastlock || (gametic > lastlock+20)) && (mutevoice.GetInt() < 2) )
lastlock = AddOneliner("locked");
if ( !lastlock || (gametic > lastlock+20) )
lastlock = AddOneliner("locked",2);
}
}
@ -2066,26 +2088,41 @@ Class SWWMHandler : EventHandler
else if ( e.Name ~== "swwmkoraxline" )
{
if ( consoleplayer != e.Args[1] ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= 3 ) return;
switch ( e.Args[0] )
{
case 0:
AddOneliner("koraxgreet",60);
AddOneliner("koraxgreet",3,60);
break;
case 1:
AddOneliner("koraxblood",150);
AddOneliner("koraxblood",3,150);
break;
case 2:
AddOneliner("koraxgame",120);
AddOneliner("koraxgame",3,120);
break;
case 3:
AddOneliner("koraxworship",80);
AddOneliner("koraxworship",3,80);
break;
case 4:
AddOneliner("koraxmasters",90);
AddOneliner("koraxmasters",3,90);
break;
}
}
else if ( e.Name.Left(16) ~== "swwmremoteliner." )
{
if ( consoleplayer == e.Args[0] ) return;
if ( !CVar.GetCVar('swwm_othervoice',players[consoleplayer]).GetBool() ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= e.Args[1] ) return;
players[e.Args[0]].mo.A_StartSound(e.Name.Mid(16),CHAN_DEMOVOICE,attenuation:.5);
}
else if ( e.Name.Left(19) ~== "swwmremotelinertxt." )
{
if ( consoleplayer == e.Args[0] ) return;
if ( !CVar.GetCVar('swwm_othervoice',players[consoleplayer]).GetBool() ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= e.Args[1] ) return;
double dist = players[consoleplayer].Camera.Distance3D(players[e.Args[0]].mo);
if ( dist < 2000 )
Console.Printf("\cx%s\cx: %s\c-",players[e.Args[0]].GetUserName(),StringTable.Localize(e.Name.Mid(19)));
}
}
// stuff for hud

View file

@ -269,9 +269,8 @@ Class SWWMCasing : Actor abstract
Death:
#### # -1
{
pitch = 0;
pitch = roll = 0;
angle = FRandom[Junk](0,360);
roll = FRandom[Junk](0,360);
}
Stop;
}
@ -470,7 +469,7 @@ Class SWWMWeapon : Weapon abstract
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);
if ( !d.HitActor.bNOBLOOD )
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);
@ -531,6 +530,14 @@ Class SWWMWeapon : Weapon abstract
{
return (AmmoType1&&(kind is AmmoType1))||(AmmoType2&&(kind is AmmoType2));
}
override void ModifyDropAmount( int dropamount )
{
Super.ModifyDropAmount(dropamount);
if ( (AmmoGive1 <= 0) && (default.AmmoGive1 > 0) )
AmmoGive1 = 1;
if ( (AmmoGive2 <= 0) && (default.AmmoGive2 > 0) )
AmmoGive2 = 1;
}
Default
{
Weapon.BobStyle "Alpha";

View file

@ -252,7 +252,7 @@ Class PusherProjectile : Actor
{
if ( target == lasthit ) return 0;
lasthit = target;
if ( target.bNOBLOOD ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP);
if ( target.bNOBLOOD || target.bINVULNERABLE ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP);
else A_StartSound("pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP);
target.A_QuakeEx(6,6,6,10,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.7);
SWWMHandler.DoKnockback(target,vel.unit(),85000);
@ -339,7 +339,7 @@ Class PusherWeapon : SWWMWeapon
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,8500);
d.HitActor.A_QuakeEx(4,4,4,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("PusherImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);
@ -421,7 +421,7 @@ Class PusherWeapon : SWWMWeapon
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,85000);
d.HitActor.A_QuakeEx(9,9,9,15,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("BigPusherImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);

View file

@ -504,8 +504,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
if ( !type ) continue;
// skip chanceboxes, since they're not yet implemented
if ( type is 'Chancebox' ) continue;
// no fabricators outside of hexen, otherwise no ammo
if ( (gameinfo.gametype&GAME_Hexen) && (type is 'Ammo') ) continue;
// no fabricators outside of hexen
if ( !(gameinfo.gametype&GAME_Hexen) && (type is 'AmmoFabricator') ) continue;
// skip maxed items
let cur = players[consoleplayer].mo.FindInventory(type);

View file

@ -1,7 +1,7 @@
// The Demolitionist
Class Demolitionist : PlayerPawn
{
int last_jump_held;
int last_jump_held, last_boost;
Vector3 dashdir;
double dashfuel, dashboost;
int dashcooldown, boostcooldown, fuelcooldown;
@ -225,6 +225,7 @@ Class Demolitionist : PlayerPawn
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( mod == 'Jump' ) return StringTable.Localize("$O_JUMP");
if ( mod == 'Dash' ) return StringTable.Localize("$O_DASH");
if ( mod == 'GroundPound' ) return StringTable.Localize("$O_POUND");
return Super.GetObituary(victim,inflictor,mod,playerattack);
@ -275,6 +276,7 @@ Class Demolitionist : PlayerPawn
{
A_AlertMonsters(800);
dashboost *= (player.cmd.buttons&BT_JUMP)?.9:.4;
last_boost = level.maptime+1;
}
mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost);
dashfuel = max(0.,dashfuel-dashboost);
@ -531,6 +533,19 @@ Class Demolitionist : PlayerPawn
else
{
Super.MovePlayer();
// override air control because we REALLY need the extra mobility
if ( !player.onground && !bNOGRAVITY && (waterlevel < 2) )
{
double fs = TweakSpeeds(1.,0.);
fs *= max(abs(player.cmd.forwardmove/12800.),abs(player.cmd.sidemove/10240.));
if ( CanCrouch() && (player.crouchfactor != -1) ) fs *= player.crouchfactor;
Vector3 accel = (RotateVector((player.cmd.forwardmove,-player.cmd.sidemove),angle),0);
accel *= fs/960.;
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.);
}
guideangle *= .9;
@ -568,7 +583,7 @@ Class Demolitionist : PlayerPawn
player.cheats &= ~CF_REVERTPLEASE;
player.camera = player.mo;
}
vel *= 0;
vel.z += clamp(-vel.z*.4,0.,30.);
player.jumptics = -1;
SetStateLabel("Dash");
A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP);
@ -578,8 +593,53 @@ Class Demolitionist : PlayerPawn
override void CheckJump()
{
if ( InStateSequence(CurState,FindState("Dash")) ) return; // do not
bool walljump = LineTrace(angle-180,Radius*2,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,height*.2);
bool wallclimb = LineTrace(angle,Radius*2,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,height*.2);
Vector2 walldir = (cos(angle),sin(angle));
bool walljump = false, wallclimb = false;
FLineTraceData d;
Actor jumpactor = null;
for ( int i=-4; i<int(height*.8); i++ )
{
if ( !wallclimb )
{
for ( int k=0; k<30; k+=10 )
{
for ( int j=-1; j<=1; j+=2 )
{
double ang = angle+k*j;
wallclimb = LineTrace(ang,Radius+12,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,i,data:d);
if ( wallclimb )
{
jumpactor = d.HitActor;
walldir = (walldir*.7+(cos(ang),sin(ang))*.3);
break;
}
}
if ( wallclimb ) break;
}
if ( wallclimb ) break;
}
if ( wallclimb ) break;
if ( !walljump )
{
for ( int k=0; k<60; k+=10 )
{
for ( int j=-1; j<=1; j+=2 )
{
double ang = (angle-180)+k*j;
walljump = LineTrace(ang,Radius+12,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,i,data:d);
if ( walljump )
{
jumpactor = d.HitActor;
walldir = (walldir*.7+(cos(ang+180),sin(ang+180))*.3);
break;
}
}
if ( walljump ) break;
}
if ( walljump ) break;
}
if ( walljump ) break;
}
if ( player.cmd.buttons&BT_JUMP )
{
if ( player.crouchoffset ) player.crouching = 1;
@ -590,6 +650,7 @@ Class Demolitionist : PlayerPawn
&& ((player.onground && (player.jumptics == 0))
|| (!player.onground && (level.maptime > last_jump_held) && (((dashfuel > 10.) && (boostcooldown <= 0)) || walljump || wallclimb))) )
{
if ( !player.onground && (level.maptime < last_boost+8) ) return;
double jumpvelz = JumpZ*35./TICRATE;
double jumpfac = 0;
for ( let p=Inv; p; p=p.Inv )
@ -603,21 +664,27 @@ Class Demolitionist : PlayerPawn
}
if ( jumpfac > 0 ) jumpvelz *= jumpfac;
double pvelz = vel.z;
if ( !player.onground )
if ( !player.onground && !(player.cheats&CF_PREDICTING) )
{
// check for wall stuff
if ( walljump )
{
vel.z = max(-abs(vel.z)*.1,vel.z);
vel.z += jumpvelz;
vel.xy = (cos(angle),sin(angle))*20*Speed;
if ( vel.z < 10. )
vel.z += jumpvelz+clamp(-pvelz*.6,0.,30.);
vel.xy += walldir*20*Speed;
}
else if ( wallclimb )
{
vel.z = max(-abs(vel.z)*.1,vel.z);
vel.z += jumpvelz;
vel.xy = (cos(angle),sin(angle))*5*Speed;
if ( vel.z < 10. )
vel.z += jumpvelz+clamp(-pvelz*.8,0.,30.);
vel.xy += walldir*5*Speed;
}
if ( jumpactor && jumpactor.bSHOOTABLE )
{
SWWMHandler.DoKnockback(jumpactor,(-walldir,0),12000);
jumpactor.DamageMobj(self,self,10,'Jump');
}
if ( walljump || wallclimb ) A_StartSound("demolitionist/kick",CHAN_FOOTSTEP,CHANF_OVERLAP);
}
bOnMobj = false;
player.jumpTics = -1;
@ -627,16 +694,19 @@ Class Demolitionist : PlayerPawn
{
dashboost = 3.;
boostcooldown = 20;
vel.z += jumpvelz+clamp(-pvelz,0.,30.);
if ( vel.z < 10. )
vel.z += jumpvelz+clamp(-pvelz*.4,0.,30.);
A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP);
mystats.boostcount++;
}
else
{
dashboost = 0.;
vel.z += jumpvelz*1.25;
if ( vel.z < 10. )
vel.z += jumpvelz*1.25;
}
SetStateLabel("Jump");
if ( !player.onground ) last_boost = level.maptime+1;
}
last_jump_held = level.maptime+1;
}
@ -819,6 +889,7 @@ Class Demolitionist : PlayerPawn
}
void A_DemoScream()
{
A_StopSound(CHAN_DEMOVOICE);
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
A_PlayerScream();
@ -828,7 +899,6 @@ Class Demolitionist : PlayerPawn
override bool OnGiveSecret( bool printmsg, bool playsound )
{
if ( !player ) return false;
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
int score = 500;
// last secret (this is called before counting it up, so have to subtract)
if ( level.found_secrets == level.total_secrets-1 )
@ -837,7 +907,7 @@ Class Demolitionist : PlayerPawn
Console.Printf(StringTable.Localize("$SWWM_LASTSECRET"),player.GetUserName(),score);
}
else Console.Printf(StringTable.Localize("$SWWM_FINDSECRET"),player.GetUserName(),score);
if ( CheckLocalView() && (mute.GetInt() < 2) ) SWWMHandler.AddOneliner("findsecret",40);
if ( CheckLocalView() ) SWWMHandler.AddOneliner("findsecret",2,40);
SWWMCredits.Give(player,score);
return true;
}
@ -847,9 +917,8 @@ Class Demolitionist : PlayerPawn
if ( !player ) return;
// add lore if any
SWWMLoreLibrary.Add(player,item.GetClassName());
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
if ( (item is 'Weapon') && mystats && !mystats.GotWeapon(Weapon(item).GetClass()) && CheckLocalView() && (mute.GetInt() < 2) )
SWWMHandler.AddOneliner("getweapon");
if ( (item is 'Weapon') && mystats && !mystats.GotWeapon(Weapon(item).GetClass()) && CheckLocalView() )
SWWMHandler.AddOneliner("getweapon",2);
if ( (item is 'Key') && !key_reentrant )
{
// score
@ -871,15 +940,14 @@ Class Demolitionist : PlayerPawn
override bool UseInventory( Inventory item )
{
if ( !player ) return Super.UseInventory(item);
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
bool res = Super.UseInventory(item);
if ( CheckLocalView() )
{
if ( !res && !(item is 'Weapon') ) A_StartSound("menu/noinvuse",CHAN_ITEM);
if ( (item is 'PuzzleItem') && (mute.GetInt() < 2) )
if ( item is 'PuzzleItem' )
{
if ( res ) SWWMHandler.AddOneliner("puzzsucc",10);
else SWWMHandler.AddOneliner("puzzfail",20);
if ( res ) SWWMHandler.AddOneliner("puzzsucc",2,10);
else SWWMHandler.AddOneliner("puzzfail",2,20);
}
}
return res;
@ -902,9 +970,11 @@ Class Demolitionist : PlayerPawn
override bool Used( Actor user )
{
if ( !(user is 'Demolitionist') || !user.player ) return false;
CVar othermute = CVar.GetCVar('swwm_mutevoice',user.player);
if ( (user.player == players[consoleplayer]) && (othermute.GetInt() < 2) )
SWWMHandler.AddOneliner("greet");
if ( (user.player == players[consoleplayer]) && (health > 0) )
{
SWWMHandler.AddOneliner("greet",2);
return true;
}
return false;
}
States

View file

@ -576,7 +576,7 @@ Class RagekitPower : Powerup
Super.InitEffect();
if ( !Owner ) return;
if ( Owner.player == players[consoleplayer] )
lastrage = SWWMHandler.AddOneliner("ragekit",20);
lastrage = SWWMHandler.AddOneliner("ragekit",2,20);
SWWMHandler.DoFlash(Owner,Color(64,255,0,0),30);
Owner.A_QuakeEx(8,8,8,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
lasteffect = int.min;
@ -596,7 +596,7 @@ Class RagekitPower : Powerup
{
SWWMHandler.DoFlash(Owner,Color(16,255,0,0),5);
if ( (Owner.player == players[consoleplayer]) && (gametic > lastrage) && (CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() < 2) )
lastrage = SWWMHandler.AddOneliner("ragekit",5);
lastrage = SWWMHandler.AddOneliner("ragekit",2,5);
Owner.A_QuakeEx(2,2,2,Random[Rage](1,2),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.5);
}
}
@ -619,7 +619,7 @@ Class RagekitPower : Powerup
SWWMHandler.DoFlash(Owner,Color(64,255,0,0),10);
Owner.A_QuakeEx(8,8,8,Random[Rage](3,8),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
if ( (Owner.player == players[consoleplayer]) && (gametic > lastrage) )
lastrage = SWWMHandler.AddOneliner("ragekit",5);
lastrage = SWWMHandler.AddOneliner("ragekit",2,5);
Owner.A_StartSound("powerup/ragekithit",CHAN_POWERUP);
lasteffect = level.maptime;
}
@ -748,6 +748,12 @@ Class LampMoth : Actor
if ( master && master.player ) SetFriendPlayer(master.player);
else bFRIENDLY = false;
}
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
{
// no hurt moff
if ( source && IsFriend(source) ) damage = 0;
return Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
}
bool isEntranced()
{
if ( !lamp )
@ -880,7 +886,7 @@ Class LampMoth : Actor
A_FaceTarget(0,0);
lifespan -= 5;
Vector3 awaydir = level.Vec3Diff(target.Vec3Offset(0,0,target.height),pos).unit();
vel += awaydir*32.;
vel += awaydir*8.;
int dmg = target.DamageMobj(self,self,GetMissileDamage(0,0),'Melee',Random[Moth](0,8)?DMG_NO_PAIN:0);
if ( !target.bNOBLOOD )
{
@ -1003,6 +1009,7 @@ Class CompanionLamp : Actor
{
Vector3 Trail;
Array<LampMoth> moff;
Actor parent;
Default
{
@ -1046,7 +1053,7 @@ Class CompanionLamp : Actor
}
let s = Spawn("SWWMSmallSmoke",m.pos);
s.alpha *= .3;
m.master = target;
m.master = parent;
m.lamp = self;
m.trail = m.pos;
moff.Push(m);
@ -1054,7 +1061,7 @@ Class CompanionLamp : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !target || !SWWMLamp(master) )
if ( !parent || !SWWMLamp(master) )
{
Destroy();
return;
@ -1080,7 +1087,7 @@ Class CompanionLamp : Actor
override void Tick()
{
Super.Tick();
if ( !target || !SWWMLamp(master) )
if ( !parent || !SWWMLamp(master) )
{
Destroy();
return;
@ -1092,8 +1099,8 @@ Class CompanionLamp : Actor
{
for ( int j=1; j>=-1; j-=2 )
{
double ang = (target.angle-180)+i*j;
Vector3 testpos = level.Vec3Offset(target.pos,(cos(ang)*32,sin(ang)*32,target.height-8+1.5*sin(level.maptime*3.)));
double ang = (parent.angle-180)+i*j;
Vector3 testpos = level.Vec3Offset(parent.pos,(cos(ang)*32,sin(ang)*32,parent.height-8+1.5*sin(level.maptime*3.)));
if ( !level.IsPointInLevel(testpos) ) continue;
Vector3 oldpos = pos;
Vector3 oldprev = prev;
@ -1123,19 +1130,19 @@ Class CompanionLamp : Actor
// check at most for a 45 degree offset
if ( foundspot && (i > 45) ) break;
}
Vector3 diff = level.Vec3Diff(pos,target.pos);
Vector3 diff = level.Vec3Diff(pos,parent.pos);
if ( diff.length() > 400 )
{
Actor f = Spawn("SWWMItemFog",pos);
f.A_StartSound("lamp/disappear",CHAN_VOICE);
SetOrigin(trail,false);
angle = AngleTo(target);
angle = AngleTo(parent);
vel *= 0.;
f = Spawn("SWWMItemFog",pos);
f.A_StartSound("lamp/appear",CHAN_VOICE);
return;
}
angle += Clamp(deltaangle(angle,AngleTo(target)),-5.,5.);
angle += Clamp(deltaangle(angle,AngleTo(parent)),-5.,5.);
vel *= .8;
bool blocked = false;
if ( BlockingLine && BlockingLineIsBlocking() )
@ -1174,7 +1181,7 @@ Class CompanionLamp : Actor
vel += 4.*normal;
blocked = true;
}
if ( (diff.x > -16) && (diff.x < 16) && (diff.y > -16) && (diff.y < 16) && (diff.z > -16) && (diff.z < target.height+8) )
if ( (diff.x > -16) && (diff.x < 16) && (diff.y > -16) && (diff.y < 16) && (diff.z > -16) && (diff.z < parent.height+8) )
{
if ( diff.x < 0 ) vel.x -= .2;
else vel.x += .2;
@ -1277,7 +1284,7 @@ Class SWWMLamp : Inventory
if ( !thelamp )
{
thelamp = Spawn("CompanionLamp",Owner.Vec3Offset(cos(Owner.angle)*20,sin(Owner.angle)*20,24));
thelamp.target = Owner;
CompanionLamp(thelamp).parent = Owner;
thelamp.master = self;
let f = Spawn("SWWMItemFog",thelamp.pos);
f.A_StartSound("lamp/appear",CHAN_VOICE);

View file

@ -1,6 +1,303 @@
// Blackmann "Rhino Stopper" Spreadgun (from Instant Action 3, also planned for Zanaveth Ultra Suite 2)
// Slot 3, replaces Shotgun, Ethereal Crossbow, Serpent Staff
Class RedShellCasing : SWWMCasing
{
Default
{
BounceSound "spreadgun/casing";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
heat = 0;
}
}
Class GreenShellCasing : RedShellCasing {}
Class WhiteShellCasing : RedShellCasing {}
Class BlueShellCasing : RedShellCasing {}
Class BlackShellCasing : RedShellCasing {}
Class PurpleShellCasing : RedShellCasing {}
Class GoldShellCasing : RedShellCasing
{
Default
{
BounceSound "spreadgun/gcasing";
}
}
Class SWWMDamageAccumulator : Thinker
{
Actor victim, inflictor, source;
Array<Int> amounts;
int total;
Name type;
bool dontgib;
override void Tick()
{
Super.Tick();
// so many damn safeguards in this
if ( !victim )
{
Destroy();
return;
}
int gibhealth = victim.GetGibHealth();
// おまえはもう死んでいる
if ( (victim.health-total <= gibhealth) && !dontgib )
{
// safeguard for inflictors that have somehow ceased to exist, which apparently STILL CAN HAPPEN
if ( inflictor ) inflictor.bEXTREMEDEATH = true;
else type = 'Extreme';
}
// make sure accumulation isn't reentrant
// 何?
for ( int i=0; i<amounts.Size(); i++ )
{
if ( !victim ) break;
victim.DamageMobj(inflictor,source,amounts[i],type,DMG_THRUSTLESS);
}
// clean up
if ( inflictor ) inflictor.bEXTREMEDEATH = false;
Destroy();
}
static void Accumulate( Actor victim, int amount, Actor inflictor, Actor source, Name type, bool dontgib = false )
{
if ( !victim ) return;
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
match = a;
break;
}
if ( !match )
{
match = new("SWWMDamageAccumulator");
match.ChangeStatNum(STAT_USER);
match.victim = victim;
match.amounts.Clear();
}
match.amounts.Push(amount);
match.total += amount;
match.inflictor = inflictor;
match.source = source;
match.type = type;
match.dontgib = dontgib;
}
static clearscope int GetAmount( Actor victim )
{
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
return a.total;
}
return 0;
}
}
Class SpreadgunTracer : LineTracer
{
Actor ignoreme;
Array<HitListEntry> hitlist;
Array<Line> shootthroughlist;
Array<WaterHit> waterhitlist;
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
int amt = SWWMDamageAccumulator.GetAmount(Results.HitActor);
// getgibhealth isn't clearscope, fuck
int gibhealth = -int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor);
if ( Results.HitActor.GibHealth != int.min ) gibhealth = -abs(Results.HitActor.GibHealth);
// if gibbed, go through without dealing more damage
if ( Results.HitActor.health-amt <= gibhealth ) return TRACE_Skip;
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
hitlist.Push(ent);
// go right on through if dead
if ( Results.HitActor.health-amt <= 0 ) return TRACE_Skip;
// stap
return TRACE_Abort;
}
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class SpreadSlugTracer : SpreadgunTracer
{
double penetration; // please don't laugh
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
ent.hitdamage = min(Results.HitActor.health+int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor),int(penetration));
hitlist.Push(ent);
penetration = max(0,penetration-ent.hitdamage);
if ( penetration <= 0 ) return TRACE_Abort;
return TRACE_Skip;
}
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class SpreadImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("spreadgun/pellet",CHAN_VOICE,CHANF_DEFAULT,.4,4.);
A_SprayDecal("TinyBulletChip",-20);
int numpt = Random[Spreadgun](-2,2);
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.scale *= .6;
s.special1 = Random[Spreadgun](0,1);
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
}
numpt = Random[Spreadgun](-3,3);
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](-2,2);
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 SlugImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("spreadgun/slug",CHAN_VOICE,CHANF_DEFAULT,1.,2.);
A_SprayDecal("BulletChip",-20);
int numpt = Random[Spreadgun](6,12);
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.special1 = Random[Spreadgun](0,2);
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
}
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("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Spreadgun](10,15);
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 Spreadgun : SWWMWeapon
{
bool fired; // shell was used
@ -9,6 +306,18 @@ Class Spreadgun : SWWMWeapon
transient ui TextureID WeaponBox, AmmoIcon[7], LoadedIcon[7];
transient ui Font TewiFont;
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( loadammo is 'RedShell' ) return StringTable.Localize("$O_SPREADGUN_RED");
if ( loadammo is 'GreenShell' ) return StringTable.Localize("$O_SPREADGUN_GREEN");
if ( loadammo is 'WhiteShell' ) return StringTable.Localize("$O_SPREADGUN_WHITE");
if ( loadammo is 'BlueShell' ) return StringTable.Localize("$O_SPREADGUN_BLUE");
if ( loadammo is 'BlackShell' ) return StringTable.Localize("$O_SPREADGUN_BLACK");
if ( loadammo is 'PurpleShell' ) return StringTable.Localize("$O_SPREADGUN_PURPLE");
if ( loadammo is 'GoldShell' ) return StringTable.Localize("$O_SPREADGUN_GOLD");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
@ -122,6 +431,7 @@ Class Spreadgun : SWWMWeapon
action void A_DropShell()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
static const Class<Actor> casetypes[] = {"RedShellCasing","GreenShellCasing","WhiteShellCasing","BlueShellCasing","BlackShellCasing","PurpleShellCasing","GoldShellCasing"};
if ( !invoker.fired )
{
for ( int i=0; i<7; i++ )
@ -131,7 +441,81 @@ Class Spreadgun : SWWMWeapon
break;
}
}
else Console.Printf("// TODO drop spent shell");
else
{
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-10*z);
let c = Spawn(casetypes[i],origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2,3));
c.vel += vel*.5;
break;
}
}
}
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class<Actor> impact = "SpreadImpact", int bc = 1, bool large = false )
{
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
t.ShootThroughList[i].Activate(self,0,SPAC_PCross);
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Spawn(large?"InvisibleSplasher":"SmolInvisibleSplasher",t.WaterHitList[i].hitpos);
b.A_CheckTerrain();
}
for ( int i=5; i<t.Results.Distance; i+=10 )
{
if ( !Random[Boolet](0,bc) ) continue;
let b = Actor.Spawn("SWWMBubble",level.Vec3Offset(origin,dir*i));
b.Scale *= FRandom[Boolet](.1,.3);
}
for ( int i=0; i<t.HitList.Size(); i++ )
{
int realdmg = dmg?dmg:t.HitList[i].HitDamage;
SWWMDamageAccumulator.Accumulate(t.HitList[i].HitActor,realdmg,invoker,self,'shot',!large);
SWWMHandler.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Spreadgun](0.4,1.2));
if ( t.HitList[i].HitActor.bNOBLOOD || t.HitList[i].HitActor.bINVULNERABLE )
{
let p = Spawn(impact,t.HitList[i].HitLocation);
p.angle = atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180;
p.pitch = asin(t.HitList[i].x.z);
}
else
{
t.HitList[i].HitActor.TraceBleed(realdmg,self);
t.HitList[i].HitActor.SpawnBlood(t.HitList[i].HitLocation,atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180,realdmg);
if ( large ) t.HitList[i].HitActor.A_StartSound("spreadgun/slugf",CHAN_BODY,CHANF_OVERLAP,1.,2.);
else t.HitList[i].HitActor.A_StartSound("spreadgun/pelletf",CHAN_BODY,CHANF_OVERLAP,.4,4.);
}
}
if ( t.Results.HitType != TRACE_HitNone )
{
Vector3 hitnormal = -t.Results.HitVector;
if ( t.Results.HitType == TRACE_HitFloor )
{
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.top.Normal;
else hitnormal = t.Results.HitSector.floorplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.bottom.Normal;
else hitnormal = t.Results.HitSector.ceilingplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitWall )
{
hitnormal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
if ( !t.Results.Side ) hitnormal *= -1;
}
let p = Spawn(impact,t.Results.HitPos+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
if ( t.Results.HitLine ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
}
}
action void A_FireShell()
@ -142,7 +526,6 @@ Class Spreadgun : SWWMWeapon
static const int louds[] = {800,1000,1100,1200,1400,600,2500};
static const int quakes[] = {3,4,2,4,3,1,6};
static const Color cols[] = {Color(40,255,192,64),Color(36,255,192,80),Color(64,255,160,32),Color(48,32,176,255),Color(72,255,128,16),Color(24,255,224,96),Color(96,255,224,16)};
Console.Printf("// TODO fire shell");
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
@ -153,6 +536,108 @@ Class Spreadgun : SWWMWeapon
A_ZoomFactor(1.+quakes[i]*.04,ZOOM_INSTANT);
A_ZoomFactor(1.);
SWWMHandler.DoFlash(self,cols[i],5);
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z);
Vector3 x2, y2, z2;
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
double a, s;
Vector3 dir;
SpreadgunTracer st;
SpreadSlugTracer sst;
switch ( i )
{
case 1:
sst = new("SpreadSlugTracer");
sst.ignoreme = self;
sst.penetration = 250.;
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.01);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
sst.hitlist.Clear();
sst.shootthroughlist.Clear();
sst.waterhitlist.Clear();
sst.Trace(origin,level.PointInSector(origin.xy),dir,8000.,0);
ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1,true);
for ( int i=0; i<3; 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.5;
}
for ( int i=0; i<6; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .8;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.);
}
for ( int i=0; i<10; 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);
}
break;
case 2:
Console.Printf("\cg// TODO Dragon's Breath\c-");
break;
case 3:
Console.Printf("\cg// TODO Kinylum Saltshot\c-");
break;
case 4:
Console.Printf("\cg// TODO Napalm Rounds\c-");
break;
case 5:
Console.Printf("\cg// TODO The Ball\c-");
break;
case 6:
Console.Printf("\cg// TODO Golden Shell\c-");
break;
default:
st = new("SpreadgunTracer");
st.ignoreme = self;
for ( int j=0; j<30; j++ )
{
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.22);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
st.hitlist.Clear();
st.shootthroughlist.Clear();
st.waterhitlist.Clear();
st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,0);
ProcessTraceHit(st,origin,dir,6,7000,bc:5);
}
for ( int i=0; i<9; 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 *= .2;
}
for ( int i=0; i<16; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
for ( int i=0; i<20; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
break;
}
break;
}
A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP);
@ -185,7 +670,6 @@ Class Spreadgun : SWWMWeapon
Ammo a = Ammo(other.FindInventory(types[i]));
if ( !a ) continue;
nextammo = types[i];
Ammo1 = a;
return;
}
nextammo = AmmoType1;
@ -206,17 +690,21 @@ Class Spreadgun : SWWMWeapon
int ridx;
if ( rev )
{
ridx = (cur-i)%7;
if ( ridx < 0 ) ridx = 6;
ridx = cur-i;
if ( ridx < 0 ) break;
}
else ridx = (i+cur+1)%7;
if ( CountInv(types[ridx]) <= 0 ) continue;
next = ridx;
break;
}
if ( rev && (invoker.nextammo == types[next]) )
{
A_SwitchAmmoType(false); // recheck forward
return;
}
if ( invoker.nextammo != types[next] ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL);
invoker.nextammo = types[next];
invoker.Ammo1 = Ammo(FindInventory(invoker.nextammo));
A_WeaponReady(WRF_NOFIRE);
}
@ -532,25 +1020,67 @@ Class Spreadgun : SWWMWeapon
XZWK FGHI 2;
Goto ReadyFired;
FlashRed:
XZWZ A 2 Bright;
XZWZ A 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 120;
l.target = self;
}
Stop;
FlashGreen:
XZWZ B 2 Bright;
XZWZ B 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 90;
l.target = self;
}
Stop;
FlashWhite:
XZWZ C 2 Bright;
XZWZ C 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[1] = 176;
l.args[2] = 32;
l.args[3] = 160;
l.target = self;
}
Stop;
FlashBlue:
XZWZ D 2 Bright;
XZWZ D 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[0] = 96;
l.args[1] = 224;
l.args[2] = 255;
l.args[3] = 160;
l.target = self;
}
Stop;
FlashBlack:
XZWZ E 2 Bright;
XZWZ E 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[1] = 144;
l.args[2] = 16;
l.args[3] = 280;
l.target = self;
}
Stop;
FlashPurple:
XZWZ F 2 Bright;
XZWZ F 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 60;
l.target = self;
}
Stop;
FlashGold:
XZWZ G 2 Bright;
XZWZ G 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 300;
l.target = self;
}
Stop;
}
}
}

View file

@ -318,7 +318,7 @@ Class ExplodiumGun : SWWMWeapon
int dmg = 40;
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,48000);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("SWWMBulletImpact",d.HitLocation);
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;

View file

@ -825,7 +825,7 @@ Class CandyGun : SWWMWeapon
int dmg = 350;
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,72000);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("SWWMBulletImpact",d.HitLocation);
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;