diff --git a/README.md b/README.md index 218d9ca07..0384aaa13 100644 --- a/README.md +++ b/README.md @@ -392,6 +392,7 @@ The scoring system is pretty straightforward. Each enemy you kill will give you * +500 if the enemy was killed with the **Deep Impact** primary (humiliation). * +300 if the enemy was killed with your butt while dashing (no, seriously). + * +600 if the enemy was killed with a blown kiss (oh my~). * x2 for an overkill (enemy was gibbed or received twice its base health in damage). * x1.5 for each combo level, up to x8 in steps of x0.5. Kills are considered combos if multiple enemies are killed within 5 seconds of each other. * +100 for killing an enemy without having taken damage since last spawn, with extra +10 boosts for consecutive kills (extra boosts taper off after 10x). diff --git a/TODO.md b/TODO.md index 00fdb1216..fbd7cb34e 100644 --- a/TODO.md +++ b/TODO.md @@ -6,7 +6,7 @@ - Omnibusting (all weapons can bust walls) - Infinite fuel - Confetti gibs - - Keen replacement (need ideas) + - Keen replacement - Achievements - Extra Demolitionist Menu tabs (radio, minigames) - Collectables diff --git a/cvarinfo.txt b/cvarinfo.txt index b7aba5b1a..6dbf46e5e 100644 --- a/cvarinfo.txt +++ b/cvarinfo.txt @@ -55,10 +55,10 @@ user int swwm_numscale = 0; // damnum scaling (0 = use GZDoom scaling) user int swwm_poiscale = 0; // point of interest scaling (0 = use GZDoom scaling) user int swwm_detscale = 0; // item sense scaling (0 = use GZDoom scaling) server bool swwm_blood = false; // custom blood/gibbing -server int swwm_maxblood = 1000; // max blood effects at any time -server int swwm_maxgibs = 200; // max gibs at any time +server int swwm_maxblood = 400; // max blood effects at any time +server int swwm_maxgibs = 100; // max gibs at any time server int swwm_maxcasings = 200; // max casings and spent mags at any time -server int swwm_maxdebris = 1000; // max chunks of debris at any time +server int swwm_maxdebris = 500; // max chunks of debris at any time user bool swwm_fuzz = true; // allows toggling the fuzz shader on the demolitionist menus, useful if you're streaming/recording since it destroys the encoding quality user bool swwm_cbtpause = true; // wallbuster menu pauses the game user noarchive int swwm_cbtmeme = 0; // easter egg, hidden cvar diff --git a/keyconf.txt b/keyconf.txt index 49a1ea06b..40e7d7205 100644 --- a/keyconf.txt +++ b/keyconf.txt @@ -3,16 +3,25 @@ addmenukey "$SWWM_PRIMARYFIRE" "+attack" addmenukey "$SWWM_SECONDARYFIRE" "+altattack" addmenukey "$SWWM_RELOADFIRE" "+reload" addmenukey "$SWWM_ZOOMFIRE" "+zoom" -//addmenukey "$SWWM_EXTRAFIRE" "+user4" +addmenukey "$SWWM_EXTRAFIRE" "+user4" addmenukey "$SWWM_MELEE" "+user1" addmenukey "$SWWM_DASH" "+user2" addmenukey "$SWWM_ITEMSENSE" "+user3" addmenukey "$SWWM_GESTURE1" "netevent swwmgesture 0" addmenukey "$SWWM_GESTURE2" "netevent swwmgesture 1" addmenukey "$SWWM_GESTURE3" "netevent swwmgesture 2" -//addmenukey "$SWWM_GESTURE4" "netevent swwmgesture 3" +addmenukey "$SWWM_GESTURE4" "netevent swwmgesture 3" addmenukey "$SWWM_KBASE" "openmenu SWWMKnowledgeBaseMenu" -defaultbind "j" "netevent swwmgesture 0" -defaultbind "k" "netevent swwmgesture 1" -defaultbind "l" "netevent swwmgesture 2" +defaultbind "mouse1" "+attack" +defaultbind "mouse2" "+altattack" +defaultbind "r" "+reload" +defaultbind "e" "+zoom" +defaultbind "g" "+user4" +defaultbind "f" "+user1" +defaultbind "alt" "+user2" +defaultbind "i" "+user3" +defaultbind "h" "netevent swwmgesture 0" +defaultbind "j" "netevent swwmgesture 1" +defaultbind "k" "netevent swwmgesture 2" +defaultbind "l" "netevent swwmgesture 3" defaultbind "q" "openmenu SWWMKnowledgeBaseMenu" diff --git a/language.def_base b/language.def_base index 41a0eb766..fc100eade 100644 --- a/language.def_base +++ b/language.def_base @@ -675,6 +675,7 @@ O_DASH = "%o was discombobulated by a very fast moving %k."; O_BUTT = "%o received a lethal impact from %k's butt."; O_JUMP = "%o was stepped on %k."; O_MELEE = "%o was K.O.'d by %k."; +O_DOKIDOKI = "%o was defeated by %k's power of love."; O_MOTH = "%%o was assaulted by %s's moths."; O_MOTH2 = "%o was assaulted by moths."; O_MASHIRO1 = "%o should have kept the lights on."; @@ -796,6 +797,7 @@ SWWM_UNIT_KILOMETER = "km"; SWWM_UNIT_KPH = "km/h"; SWWM_UNIT_LITER = "l"; SWWM_YOURSELF = "Yourself"; +SWWM_DOKIDOKI = "The Power of Love™"; // boss tags BT_BRUISERS = "Bruiser Brothers"; BT_CYBIE = "Tyrant of Babel"; @@ -842,6 +844,7 @@ SWWM_LEG = "Legendary "; SWWM_LEGPREFIX = "L"; SWWM_SHAMEFUL = "Humiliation"; SWWM_BUTTSLAM = "Buttslam"; +SWWM_LOVED = "L\cg♥\c-VE"; // score messages SWWM_FINDSECRET = "\cf%s\cf found a secret. +%d\c-"; SWWM_FINDKEY = "\cf%s\cf got the %s\cf. +%d\c-"; diff --git a/language.es_base b/language.es_base index d0ff63543..bf3e20296 100644 --- a/language.es_base +++ b/language.es_base @@ -628,6 +628,7 @@ O_DASH = "%o fue descuajeringad@[ao_esp] a todo gas por %k."; O_BUTT = "%o recibió un impacto letal del trasero de %k."; O_JUMP = "%o fue pisotead@[ao_esp] por %k."; O_MELEE = "%o fue noquead@[ao_esp] por %k."; +O_DOKIDOKI = "%o fue derrotad@[ao_esp] por el poder del amor de %k."; O_MOTH = "%%o fue asaltad@[ao_esp] por las polillas de %s."; O_MOTH2 = "%o fue asaltad@[ao_esp] por polillas."; O_MASHIRO1 = "%o debería haber dejado la luz encendida."; @@ -743,6 +744,7 @@ SWWM_YNYKRONREADY = "Artefacto Ynykron listo para disparar."; SWWM_TITLEPRESENTS = "presenta"; SWWM_TITLEMODBY = "un mod de \cxMarisa Kirisame"; SWWM_YOURSELF = "Tú"; +SWWM_DOKIDOKI = "El Poder del Amor™"; // boss tags BT_BRUISERS = "Hermanos de Guerra"; BT_CYBIE = "Tirano de Babel"; diff --git a/language.version b/language.version index 626eb3d40..32132b913 100644 --- a/language.version +++ b/language.version @@ -1,2 +1,2 @@ [default] -SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.6b r555 \cu(Sun 20 Sep 22:25:34 CEST 2020)"; +SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.7b r557 \cu(Mon 21 Sep 21:39:06 CEST 2020)"; diff --git a/modeldef.demolitionist b/modeldef.demolitionist index 2fc835d55..6d2466c8c 100644 --- a/modeldef.demolitionist +++ b/modeldef.demolitionist @@ -391,6 +391,36 @@ Model "Demolitionist" FrameIndex XZWD B 0 338 FrameIndex XZWD C 0 339 FrameIndex XZWD D 0 340 + // Blow Kiss + FrameIndex XZWD E 0 343 + FrameIndex XZWD F 0 344 + FrameIndex XZWD G 0 345 + FrameIndex XZWD H 0 346 + SurfaceSkin 0 1 "DemoFace_Blink.png" + FrameIndex XZWD I 0 347 + FrameIndex XZWD J 0 348 + FrameIndex XZWD K 0 349 + FrameIndex XZWD L 0 350 + SurfaceSkin 0 1 "DemoFace_Default.png" + FrameIndex XZWD M 0 351 + FrameIndex XZWD N 0 352 + FrameIndex XZWD O 0 353 + SurfaceSkin 0 1 "DemoFace_Wink.png" + FrameIndex XZWD P 0 354 + FrameIndex XZWD Q 0 355 + FrameIndex XZWD R 0 356 + FrameIndex XZWD S 0 357 + FrameIndex XZWD T 0 358 + FrameIndex XZWD U 0 359 + SurfaceSkin 0 1 "DemoFace_Default.png" + FrameIndex XZWD V 0 360 + FrameIndex XZWD W 0 361 + // TODO Crouched Wave + // TODO Crouched Thumbs Up + // TODO Crouched Victory + // TODO Crouched Blow Kiss + // TODO Swim + // TODO Float } // Voodoo Doll @@ -452,7 +482,6 @@ Model "SWWMGesture" SurfaceSkin 0 2 "DemoSoft.png" AngleOffset -90 Scale -0.005 0.0025 0.005 - IGNORETRANSLATION // Wave FrameIndex XZW1 A 0 0 @@ -508,4 +537,48 @@ Model "SWWMGesture" FrameIndex XZW2 W 0 51 FrameIndex XZW2 X 0 52 FrameIndex XZW2 Y 0 53 + // Blow Kiss + FrameIndex XZW2 Z 0 55 + FrameIndex XZW3 A 0 56 + FrameIndex XZW3 B 0 57 + FrameIndex XZW3 C 0 58 + FrameIndex XZW3 D 0 59 + FrameIndex XZW3 E 0 60 // smooch + FrameIndex XZW3 F 0 61 + FrameIndex XZW3 G 0 62 + FrameIndex XZW3 H 0 63 + FrameIndex XZW3 I 0 64 + FrameIndex XZW3 J 0 65 + FrameIndex XZW3 K 0 66 // blow + FrameIndex XZW3 L 0 67 + FrameIndex XZW3 M 0 68 + FrameIndex XZW3 N 0 69 + FrameIndex XZW3 O 0 70 + FrameIndex XZW3 P 0 71 + FrameIndex XZW3 Q 0 72 + FrameIndex XZW3 R 0 73 + // Quick Grenade + SurfaceSkin 0 3 "ExplodiumGun.png" + FrameIndex XZW3 S 0 75 + FrameIndex XZW3 T 0 76 + FrameIndex XZW3 U 0 77 + FrameIndex XZW3 V 0 78 + FrameIndex XZW3 W 0 79 + FrameIndex XZW3 X 0 80 // arm + FrameIndex XZW3 Y 0 81 + FrameIndex XZW3 Z 0 82 + FrameIndex XZW4 A 0 83 + FrameIndex XZW4 B 0 84 + FrameIndex XZW4 C 0 85 + FrameIndex XZW4 D 0 86 // swing + FrameIndex XZW4 E 0 87 + FrameIndex XZW4 F 0 88 + FrameIndex XZW4 G 0 89 + FrameIndex XZW4 H 0 90 + SurfaceSkin 0 3 "" + FrameIndex XZW4 I 0 91 // throw + FrameIndex XZW4 J 0 92 + FrameIndex XZW4 K 0 93 + FrameIndex XZW4 L 0 94 + FrameIndex XZW4 M 0 95 } diff --git a/models/DemolitionistPlayer.blend b/models/DemolitionistPlayer.blend index 618e1ea12..82c7f8169 100644 Binary files a/models/DemolitionistPlayer.blend and b/models/DemolitionistPlayer.blend differ diff --git a/models/DemolitionistPlayer.blend1 b/models/DemolitionistPlayer.blend1 new file mode 100644 index 000000000..d0fba79c8 Binary files /dev/null and b/models/DemolitionistPlayer.blend1 differ diff --git a/models/DemolitionistPlayer_a.3d b/models/DemolitionistPlayer_a.3d index 89b735c3a..9f724b3f2 100644 Binary files a/models/DemolitionistPlayer_a.3d and b/models/DemolitionistPlayer_a.3d differ diff --git a/models/DemolitionistPlayer_d.3d b/models/DemolitionistPlayer_d.3d index cd97ce4c8..e2dfaa0e5 100644 Binary files a/models/DemolitionistPlayer_d.3d and b/models/DemolitionistPlayer_d.3d differ diff --git a/models/GestureArms.blend b/models/GestureArms.blend index 5fb353146..f62f849e6 100644 Binary files a/models/GestureArms.blend and b/models/GestureArms.blend differ diff --git a/models/GestureArms.blend1 b/models/GestureArms.blend1 new file mode 100644 index 000000000..a6b524036 Binary files /dev/null and b/models/GestureArms.blend1 differ diff --git a/models/GestureArms_a.3d b/models/GestureArms_a.3d index 6f3d068fd..e36ecc291 100644 Binary files a/models/GestureArms_a.3d and b/models/GestureArms_a.3d differ diff --git a/models/GestureArms_d.3d b/models/GestureArms_d.3d index 7f2c720a4..2e90d0027 100644 Binary files a/models/GestureArms_d.3d and b/models/GestureArms_d.3d differ diff --git a/palettes/LovePal.pal b/palettes/LovePal.pal new file mode 100644 index 000000000..ac6a6b3a2 Binary files /dev/null and b/palettes/LovePal.pal differ diff --git a/sndinfo.txt b/sndinfo.txt index 931a4548d..c10387569 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -358,6 +358,8 @@ demolitionist/whitl1 sounds/demolitionist/demowhitl1.ogg demolitionist/whitl2 sounds/demolitionist/demowhitl2.ogg $random demolitionist/whitl { demolitionist/whitl1 demolitionist/whitl2 } demolitionist/buttslam sounds/demolitionist/demobutt.ogg +demolitionist/smooch sounds/demolitionist/demokiss.ogg +demolitionist/blowkiss sounds/demolitionist/demoblow.ogg $playersound demolitionist neutral *grunt DSEMPTY $playeralias demolitionist neutral *pain100 demolitionist/lopain @@ -838,6 +840,7 @@ misc/sundowner sounds/SUNDOWNER.ogg misc/emone sounds/EMONE.ogg misc/drumroll sounds/DRUMROLL.ogg misc/tada sounds/TADA.ogg +misc/heart sounds/KOKORO.ogg misc/gibber1 sounds/general/Gib1.ogg misc/gibber2 sounds/general/Gib2.ogg misc/gibber3 sounds/general/Gib3.ogg diff --git a/sounds/KOKORO.ogg b/sounds/KOKORO.ogg new file mode 100644 index 000000000..c6c7af828 Binary files /dev/null and b/sounds/KOKORO.ogg differ diff --git a/sounds/demolitionist/demoblow.ogg b/sounds/demolitionist/demoblow.ogg new file mode 100644 index 000000000..ef048a5fb Binary files /dev/null and b/sounds/demolitionist/demoblow.ogg differ diff --git a/sounds/demolitionist/demokiss.ogg b/sounds/demolitionist/demokiss.ogg new file mode 100644 index 000000000..7763b8ab0 Binary files /dev/null and b/sounds/demolitionist/demokiss.ogg differ diff --git a/sprites/DOKIA0.png b/sprites/DOKIA0.png new file mode 100644 index 000000000..68d322432 Binary files /dev/null and b/sprites/DOKIA0.png differ diff --git a/zscript/swwm_blazeit.zsc b/zscript/swwm_blazeit.zsc index be0c91a66..5b9846a34 100644 --- a/zscript/swwm_blazeit.zsc +++ b/zscript/swwm_blazeit.zsc @@ -1771,6 +1771,7 @@ Class Hellblazer : SWWMWeapon Reload: XZW2 A 2 { + if ( (invoker.clipcount <= 0) && (CountInv(invoker.nextammo) <= 0) ) A_PickNextAmmo(); if ( (invoker.clipcount >= invoker.LoadedCapacity()) && (invoker.loadammo == invoker.nextammo) ) return A_JumpByAmmoType("Idle_1","Idle_2","Idle_3","Idle_4","Idle_G"); return A_JumpByAmmoType("Unload_1","Unload_2","Unload_3","Unload_4","Unload_G"); diff --git a/zscript/swwm_danmaku.zsc b/zscript/swwm_danmaku.zsc index f30b7266a..684a6942e 100644 --- a/zscript/swwm_danmaku.zsc +++ b/zscript/swwm_danmaku.zsc @@ -87,9 +87,10 @@ Class EvisceratorChunkGlow : Actor override void Tick() { if ( isFrozen() ) return; - if ( !EvisceratorChunk(target) ) + if ( !EvisceratorChunk(target) || EvisceratorChunk(target).justdied ) { - Destroy(); + Scale *= .9; + A_FadeOut(); return; } SetOrigin(target.pos,true); diff --git a/zscript/swwm_handler.zsc b/zscript/swwm_handler.zsc index 7bb47d105..e65618d2a 100644 --- a/zscript/swwm_handler.zsc +++ b/zscript/swwm_handler.zsc @@ -994,6 +994,17 @@ Class SWWMHandler : EventHandler scr.xcnt = ++ofs; } } + else if ( e.DamageType == 'Love' ) + { + score += 600; + if ( scr ) + { + scr.xscore[ofs] = 0; + scr.xtcolor[ofs] = Font.FindFontColor('BlushPink'); + scr.xstr[ofs] = StringTable.Localize("$SWWM_LOVED"); + scr.xcnt = ++ofs; + } + } if ( (e.Damage >= e.Thing.GetSpawnHealth()*2) || (((e.Thing.Health <= e.Thing.GetGibHealth()) || (e.DamageSource.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageType == 'Extreme')) && !e.DamageSource.bNOEXTREMEDEATH && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH)) ) { score *= 2; @@ -1484,18 +1495,7 @@ Class SWWMHandler : EventHandler if ( (e.player == -1) || !playeringame[e.player] || !players[e.player].mo ) return; let mo = players[e.player].mo; if ( (mo.Health <= 0) || !(mo is 'Demolitionist') ) return; - switch ( e.Args[0] ) - { - case 1: - SWWMGesture.SetGesture(mo,1); - break; - case 2: - SWWMGesture.SetGesture(mo,2); - break; - default: - SWWMGesture.SetGesture(mo,0); - break; - } + SWWMGesture.SetGesture(mo,e.Args[0]); } if ( e.IsManual ) return; if ( e.Name.Left(14) ~== "swwmstoregive." ) diff --git a/zscript/swwm_menu.zsc b/zscript/swwm_menu.zsc index b26615978..8b520f153 100644 --- a/zscript/swwm_menu.zsc +++ b/zscript/swwm_menu.zsc @@ -1932,6 +1932,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu str = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_STATFAVWEAP")); if ( stats.favweapon == -1 ) str = str.."N/A"; else if ( stats.wstats[stats.favweapon].w == 'SWWMWeapon' ) str = str..StringTable.Localize("$SWWM_YOURSELF"); + else if ( stats.wstats[stats.favweapon].w == 'SWWMGesture' ) str = str..StringTable.Localize("$SWWM_DOKIDOKI"); else { let def = GetDefaultByType(stats.wstats[stats.favweapon].w); diff --git a/zscript/swwm_player.zsc b/zscript/swwm_player.zsc index 33776a832..f1b86c3de 100644 --- a/zscript/swwm_player.zsc +++ b/zscript/swwm_player.zsc @@ -100,7 +100,7 @@ Class Demolitionist : PlayerPawn health = min(health+amount,1000); player.health = health; } - else player.health = health = 1000; + else player.health = health = 200; } if ( giveall || (name ~== "backpack") ) { @@ -210,6 +210,9 @@ Class Demolitionist : PlayerPawn { let type = (class)(AllActorClasses[i]); if ( !type || (type == "Weapon") ) continue; + // Don't give already owned weapons + let owned = FindInventory(type); + if ( owned && (owned.Amount >= owned.MaxAmount) ) continue; // Don't give replaced weapons unless the replacement was done by Dehacked. let rep = GetReplacement(type); if ( (rep == type) || (rep is "DehackedPickup") ) @@ -231,6 +234,9 @@ Class Demolitionist : PlayerPawn let type = (class)(AllActorClasses[i]); if ( !type ) continue; if ( !(gameinfo.gametype&(GAME_Hexen|GAME_Strife)) && (type is 'AmmoFabricator') ) continue; // no fabricators before hexen + // Don't give maxed items + let owned = FindInventory(type); + if ( owned && (owned.Amount >= owned.MaxAmount) ) continue; let def = GetDefaultByType (type); if ( def.Icon.isValid() && !(type is "PuzzleItem") && !(type is "Powerup") && !(type is "Ammo") && !(type is "Armor") && !(type is "Key") ) @@ -495,6 +501,9 @@ Class Demolitionist : PlayerPawn if ( player.onground && (player.jumptics < -18) ) player.jumptics = 0; } + // quick grenade + if ( (player.playerstate != PST_DEAD) && (player.cmd.buttons&BT_USER4) ) + SWWMGesture.SetGesture(self,-1); } override void Tick() { @@ -690,12 +699,13 @@ Class Demolitionist : PlayerPawn Vector3 diff = level.Vec3Diff(pos,a.pos); Vector3 dirto = diff.unit(); if ( dir dot dirto < .1 ) continue; + if ( a.bACTLIKEBRIDGE && (diff.z <= -a.Height) ) continue; // don't bump bridges if hit by above // large monsters will stop the player (unless hit from above if we're going at ground pound speed) A_QuakeEx(4,4,4,10,0,128,"",QF_RELATIVE|QF_SCALEDOWN); A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); a.A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); bumptic = gametic+int(20+spd/4.); - if ( (diff.z < a.height) && (lastvelz >= -25) && (a.bDONTTHRUST || (a.Mass >= maxmass) || (!a.bSHOOTABLE && !a.bPUSHABLE && (a.Health > 0))) && a.bSOLID && (dir dot dirto > .65) ) + if ( (diff.z < a.height) && (lastvelz >= -25) && (a.bDONTTHRUST || a.bACTLIKEBRIDGE || (a.Mass >= maxmass) || (!a.bSHOOTABLE && !a.bPUSHABLE && (a.Health > 0))) && a.bSOLID && (dir dot dirto > .65) ) { if ( bumped ) continue; bumped = true; @@ -1970,6 +1980,10 @@ Class Demolitionist : PlayerPawn #### # 3; XZW6 EFGHIJKLMNOPQRSTUVW 3; Goto Spawn+1; + BlowKiss: + #### # 3; + XZWD EFGHIJKLMNOPQRSTUVW 3; + Goto Spawn+1; Missile: // attacking #### # 2; @@ -2447,6 +2461,240 @@ Class CastDemolitionist : Actor } } +Class LoveHeartTrail : Actor +{ + Default + { + RenderStyle "Add"; + Radius .1; + Height 0.; + Alpha .1; + +NOGRAVITY; + +NOBLOCKMAP; + +NOINTERACTION; + +DONTSPLASH; + +NOTELEPORT; + +FORCEXYBILLBOARD; + } + override void Tick() + { + if ( isFrozen() ) return; + A_FadeOut(.01); + scale *= .95; + } + States + { + Spawn: + DOKI A -1 Bright; + Stop; + } +} + +Class LoveHeartSparkle : Actor +{ + Default + { + Radius .1; + Height 0.; + Scale .03; + +NOGRAVITY; + +NOBLOCKMAP; + +NOINTERACTION; + +DONTSPLASH; + +NOTELEPORT; + +FORCEXYBILLBOARD; + } + override void PostBeginPlay() + { + Scale *= FRandom[ExploS](.75,1.5); + specialf1 = FRandom[ExploS](.95,.98); + specialf2 = FRandom[ExploS](.01,.03); + vel = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch))*FRandom[ExploS](2,8); + } + override void Tick() + { + if ( isFrozen() ) return; + A_SetScale(scale.x*specialf1); + A_FadeOut(specialf2); + Vector3 dir = vel; + double magvel = dir.length(); + magvel *= .99; + if ( magvel > 0. ) + { + dir /= magvel; + dir += .2*(FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)); + vel = dir.unit()*magvel; + } + SetOrigin(level.Vec3Offset(pos,vel),true); + } + States + { + Spawn: + DOKI A -1 Bright; + Stop; + } +} + +Class LoveHeartBurstLight : PaletteLight +{ + Default + { + Tag "LovePal"; + ReactionTime 15; + Args 0,0,0,150; + } +} + +Class LoveHeart : Actor +{ + Default + { + Obituary "$O_DOKIDOKI"; + DamageType 'Love'; + DamageFunction (clamp(special2,5,15)); + Radius 4; + Height 4; + Speed 10; + Scale .2; + PROJECTILE; + +BLOODLESSIMPACT; + +FORCEXYBILLBOARD; + +SEEKERMISSILE; + +FOILINVUL; + +PAINLESS; + +NODAMAGETHRUST; + } + + override int DoSpecialDamage( Actor target, int damage, Name damagetype ) + { + let raging = RagekitPower(self.target.FindInventory("RagekitPower")); + if ( target.IsFriend(self.target) || (target is 'MBFHelperDog') ) + { + int healamt = clamp(special2,5,15); + if ( raging ) + { + healamt *= 8; + raging.DoHitFX(); + } + if ( target.GiveBody(healamt,target.GetSpawnHealth()) ) + { + SWWMScoreObj.Spawn(healamt,target.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+target.Height/2),Font.CR_BLUE); + SWWMHandler.DoFlash(target,Color(32,224,128,255),10); + } + if ( target is 'MBFHelperDog' ) + { + // befriend good doggo + target.bFRIENDLY = true; + if ( deathmatch ) + target.SetFriendPlayer(self.target.player); + } + return 0; + } + let bread = target.FindState("Pain"); + if ( bread ) target.SetState(bread); + if ( raging ) + { + damage *= 8; + raging.DoHitFX(); + } + return damage; + } + + override int SpecialMissileHit( Actor victim ) + { + if ( !victim.bSHOOTABLE && (victim != tracer) ) return 1; + if ( tracer && (victim != tracer) ) return 1; + return -1; + } + + action void A_HeartTick() + { + special1++; + if ( !(special1%3) && (special2 > 0) ) special2--; + A_SetScale(.2+.02*sin(special1*.25*TICRATE)); + double magvel = vel.length(); + if ( magvel > 0 ) + { + Vector3 dir = vel/magvel; + vel = dir*min(30,magvel*1.1); + } + double steppy = vel.length()/4.; + for ( int i=2; i<6; i++ ) + { + Vector3 dir2 = vel.unit(); + let t = Spawn("LoveHeartTrail",level.Vec3Offset(pos,-dir2*steppy*i)); + t.scale = scale; + } + int numpt = Random[ExploS](1,3); + for ( int i=0; i 0) ) + if ( invoker.Owner.Health <= 0 ) return; + // crouched gestures not yet added + /*if ( (player.crouchdir == -1) && invoker.Owner.FindState(cst) ) + invoker.Owner.SetStateLabel(cst); + else */if ( invoker.Owner.FindState(st) ) invoker.Owner.SetStateLabel(st); } @@ -2492,6 +2744,88 @@ Class SWWMGesture : SWWMWeapon player.SetPSprite(PSP_WEAPON,ResolveState("Deselect")); } + action void A_ThrowMag() + { + let weap = Weapon(invoker); + if ( !weap ) return; + Vector3 x, y, z, x2, y2, z2; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-2*y-3*z); + double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.005); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("ExplodiumMagProj",origin); + p.special1 = 7; + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed; + p.vel.z += 5.; + p.vel += vel*.5; + } + + action void A_Smooch() + { + if ( (player == players[consoleplayer]) && (CVar.GetCVar('swwm_mutevoice',player).GetInt() < 4) ) + A_StartSound("demolitionist/smooch",CHAN_DEMOVOICE,CHANF_OVERLAP,.4); + } + + action void A_BlowKiss() + { + if ( (player == players[consoleplayer]) && (CVar.GetCVar('swwm_mutevoice',player).GetInt() < 4) ) + A_StartSound("demolitionist/blowkiss",CHAN_DEMOVOICE,CHANF_OVERLAP,.4); + let weap = Weapon(invoker); + if ( !weap ) return; + Vector3 x, y, z, x2, y2, z2, dir; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-1*z); + let p = Spawn("LoveHeart",origin); + p.target = self; + p.angle = angle; + p.pitch = BulletSlope(); + p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed; + // try to catch target in cone of vision + [x2, y2, z2] = swwm_CoordUtil.GetAxes(p.pitch,p.angle,0); + Array hits; + hits.Clear(); + int rings = 1; + FLineTraceData d; + for ( double i=0; i<.2; i+=.02 ) + { + for ( int j=0; j<360; j+=(360/rings) ) + { + dir = (x2+y2*cos(j)*i+z2*sin(j)*i).unit(); + LineTrace(atan2(dir.y,dir.x),8000.,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d); + if ( d.HitType != TRACE_HitActor ) continue; + bool addme = true; + for ( int k=0; k 0) && !invoker.chambered ) player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftPreSlide")); - else if ( (player.cmd.buttons&BT_RELOAD) && (ExplodiumGun(invoker.SisterWeapon).clipcount < ExplodiumGun(invoker.SisterWeapon).default.clipcount) ) + else if ( (player.cmd.buttons&BT_RELOAD) && (ExplodiumGun(invoker.SisterWeapon).clipcount < ExplodiumGun(invoker.SisterWeapon).default.clipcount) + && (ExplodiumGun(invoker.SisterWeapon).clipcount <= invoker.clipcount) ) // give priority only if less than left hand player.SetPSprite(PSP_WEAPON,ResolveState("Reload")); else if ( (player.cmd.buttons&BT_RELOAD) && (invoker.clipcount < invoker.default.clipcount) ) player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftReload")); @@ -993,7 +994,7 @@ Class DualExplodiumGun : SWWMWeapon } XZW4 STUV 1; XZW2 A 0 A_JumpIf(!ExplodiumGun(invoker.SisterWeapon).chambered,"Slide"); - XZW2 A 9 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftRaise")); } + XZW2 A 0 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftRaise")); } Goto Ready; PreSlide: XZW2 A 9 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftLower")); } @@ -1010,7 +1011,7 @@ Class DualExplodiumGun : SWWMWeapon XZW5 EFG 1; XZW5 H 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP); XZW5 IJKLM 1; - XZW2 A 9 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftRaise")); } + XZW2 A 0 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftRaise")); } Goto Ready; LeftReload: XZWB A 9 { player.SetPSPrite(PSP_WEAPON,ResolveState("Lower")); } @@ -1049,7 +1050,7 @@ Class DualExplodiumGun : SWWMWeapon } XZWD STUV 1; XZWB A 0 A_JumpIf(!invoker.chambered,"LeftSlide"); - XZWB A 9 { player.SetPSPrite(PSP_WEAPON,ResolveState("Raise")); } + XZWB A 0 { player.SetPSPrite(PSP_WEAPON,ResolveState("Raise")); } Goto LeftReady; LeftPreSlide: XZWB A 9 { player.SetPSPrite(PSP_WEAPON,ResolveState("Lower")); } @@ -1066,14 +1067,13 @@ Class DualExplodiumGun : SWWMWeapon XZWE EFG 1; XZWE H 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP); XZWE IJKLM 1; - XZWB A 9 { player.SetPSPrite(PSP_WEAPON,ResolveState("Raise")); } + XZWB A 0 { player.SetPSPrite(PSP_WEAPON,ResolveState("Raise")); } Goto LeftReady; Zoom: XZW2 A 1 A_StartSound("explodium/speen",CHAN_WEAPON,CHANF_OVERLAP); XZW9 ABCDEFG 1; XZW9 H 1 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftZoom")); } XZW9 IJKLMNOPQRSTUVW 1; - XZW2 A 9; Goto Ready; LeftZoom: XZWB A 1; diff --git a/zscript/swwm_thinkers.zsc b/zscript/swwm_thinkers.zsc index ce90c87b1..1ef565663 100644 --- a/zscript/swwm_thinkers.zsc +++ b/zscript/swwm_thinkers.zsc @@ -119,6 +119,7 @@ Class SWWMStats : Thinker } Class which = myplayer.ReadyWeapon?myplayer.ReadyWeapon.GetClass():null; if ( inflictor is 'Weapon' ) which = Weapon(inflictor).GetClass(); + if ( which is 'DualExplodiumGun' ) which = 'ExplodiumGun'; // don't credit sister weapon // properly credit some projectiles to their respective gun if ( inflictor is 'AirBullet' ) which = 'DeepImpact'; else if ( inflictor is 'PusherProjectile' ) which = 'PusherWeapon';