diff --git a/cvarinfo.txt b/cvarinfo.txt index 4eba9a4a1..7e18bf675 100644 --- a/cvarinfo.txt +++ b/cvarinfo.txt @@ -142,6 +142,8 @@ nosave color swwm_mm_yourcolor = "80 ff 00"; server noarchive bool swwm_iseriouslywanttoplaythiswithbd = false; // self-explanatory +nosave int swwm_playtime = 0; // total playtime in seconds + // cross-session achievement cvars (0: locked, 1: unlocked, 2: notified) nosave int swwm_achievement_gcsandwich = 0; nosave int swwm_achievement_hdoom = 0; @@ -226,6 +228,7 @@ nosave int swwm_achievement_snake = 0; nosave int swwm_achievement_smb = 0; nosave int swwm_achievement_tyrian = 0; nosave int swwm_achievement_bof = 0; +nosave int swwm_achievement_wantdie = 0; // cross-session progress cvars nosave int swwm_progress_gcsandwich = 0; nosave int swwm_progress_ghost = 0; @@ -240,7 +243,7 @@ nosave int swwm_progress_gib = 0; nosave int swwm_progress_barrier = 0; nosave int swwm_progress_bossdash = 0; nosave int swwm_progress_sneeze = 0; -nosave int swwm_progress_fuel = 0; +nosave float swwm_progress_fuel = 0; // this one has to be a double nosave int swwm_progress_slayer = 0; nosave int swwm_progress_stomp = 0; nosave int swwm_progress_thruwall = 0; @@ -257,7 +260,6 @@ nosave int swwm_progress_parry = 0; nosave int swwm_progress_allkills = 0; nosave int swwm_progress_allsecrets = 0; nosave int swwm_progress_reflect = 0; -nosave int swwm_progress_wave = 0; nosave int swwm_progress_friend = 0; nosave int swwm_progress_shock = 0; nosave int swwm_progress_balls = 0; diff --git a/graphics/HUD/AchievementNotification.png b/graphics/HUD/AchievementNotification.png index 834956e54..e7a048faf 100644 Binary files a/graphics/HUD/AchievementNotification.png and b/graphics/HUD/AchievementNotification.png differ diff --git a/language.def_base b/language.def_base index a69817ae0..16773ef10 100644 --- a/language.def_base +++ b/language.def_base @@ -854,7 +854,7 @@ SWWM_ACHIEVEMENT_CLIFFYB_TAG = "Errand Boy Bullshit"; SWWM_ACHIEVEMENT_CLIFFYB_TXT = "Finish a map without collecting any keys"; SWWM_ACHIEVEMENT_PAR_TAG = "Fast"; SWWM_ACHIEVEMENT_PAR_TXT = "Beat the par time in %d maps"; -SWWM_ACHIEVEMENT_ONEGUY_TAG = "Fuck this Guy in Particular"; +SWWM_ACHIEVEMENT_ONEGUY_TAG = "Fuck that Guy in Particular"; SWWM_ACHIEVEMENT_ONEGUY_TXT = "Use the Ynykron Artifact to kill a single non-boss enemy"; SWWM_ACHIEVEMENT_GOLDEN_TAG = "Golden Shower"; SWWM_ACHIEVEMENT_GOLDEN_TXT = "Use %d golden shells"; @@ -897,9 +897,9 @@ SWWM_ACHIEVEMENT_BUSTIN_TXT = "Bust %d walls"; SWWM_ACHIEVEMENT_MOTH_TAG = "Mother"; SWWM_ACHIEVEMENT_MOTH_TXT = "Gather %d moths"; SWWM_ACHIEVEMENT_IWAD_TAG = "Mx. Worldwide"; -SWWM_ACHIEVEMENT_IWAD_TXT = "Play on all supported IWADs"; +SWWM_ACHIEVEMENT_IWAD_TXT = "Play on all supported games"; SWWM_ACHIEVEMENT_BRAKE_TAG = "No Brakes"; -SWWM_ACHIEVEMENT_BRAKE_TXT = "Consume all your fuel while dashing %d times"; +SWWM_ACHIEVEMENT_BRAKE_TXT = "Consume your full fuel supply in a single dash %d times"; SWWM_ACHIEVEMENT_RAGE_TAG = "No Talk me Angy"; SWWM_ACHIEVEMENT_RAGE_TXT = "Use %d Ragekits"; SWWM_ACHIEVEMENT_ABORT_TAG = "Oops I Broke It"; @@ -915,7 +915,7 @@ SWWM_ACHIEVEMENT_REFLECT_TXT = "Kill %d enemies with parried projectiles"; SWWM_ACHIEVEMENT_SEKIRO_TAG = "Hesitation is Defeat"; SWWM_ACHIEVEMENT_SEKIRO_TXT = "Die and reboot two times in the same map"; SWWM_ACHIEVEMENT_WAVE_TAG = "Sayonara, Scumbag"; -SWWM_ACHIEVEMENT_WAVE_TXT = "Wave at a dying enemy %d times"; +SWWM_ACHIEVEMENT_WAVE_TXT = "Wave at a dying enemy"; SWWM_ACHIEVEMENT_FRIEND_TAG = "Shaped like a Friend"; SWWM_ACHIEVEMENT_FRIEND_TXT = "Befriend %d Cacodemons, Weredragons or Ettins with headpats"; SWWM_ACHIEVEMENT_SHOCK_TAG = "Shockmaster"; @@ -941,13 +941,13 @@ SWWM_ACHIEVEMENT_DOSH_TXT = "Have ¥1,000,000,000 in bank"; SWWM_ACHIEVEMENT_JUMP_TAG = "Verticality"; SWWM_ACHIEVEMENT_JUMP_TXT = "Walljump %d times"; SWWM_ACHIEVEMENT_EZKILL_TAG = "Well That was Easy"; -SWWM_ACHIEVEMENT_EZKILL_TXT = "Kill %d enemies with a single Ynykron Artifact shot"; +SWWM_ACHIEVEMENT_EZKILL_TXT = "Kill %d enemies with a single Ynykron Artifact shot (excluding altfire)"; SWWM_ACHIEVEMENT_LIGMA_TAG = "Who's Steve Jobs?"; SWWM_ACHIEVEMENT_LIGMA_TXT = "Kill an endgame boss with the Ynykron Artifact"; SWWM_ACHIEVEMENT_YEET_TAG = "YEET"; SWWM_ACHIEVEMENT_YEET_TXT = "Throw a Candygun right into the Icon of Sin's brains"; SWWM_ACHIEVEMENT_MBF_TAG = "You can Pet the Dog"; -SWWM_ACHIEVEMENT_MBF_TXT = "Pet an MBF Helper Dog"; +SWWM_ACHIEVEMENT_MBF_TXT = "Pet a dog"; SWWM_ACHIEVEMENT_LIGHTNING_TAG = "And the Dragons were No More"; SWWM_ACHIEVEMENT_LIGHTNING_TXT = "Kill the Death Wyvern using the Rail Carbine"; SWWM_ACHIEVEMENT_DAKKA_TAG = "Dakka"; @@ -994,3 +994,5 @@ SWWM_ACHIEVEMENT_TYRIAN_TAG = "Master of SHMUPs"; SWWM_ACHIEVEMENT_TYRIAN_TXT = "Complete a full game of Interstellar Demolitionist"; SWWM_ACHIEVEMENT_BOF_TAG = "Master of RPGs"; SWWM_ACHIEVEMENT_BOF_TXT = "Complete a full game of Demolition Quest"; +SWWM_ACHIEVEMENT_WANTDIE_TAG = "E N D M I I"; +SWWM_ACHIEVEMENT_WANTDIE_TXT = "Fully clear a single map in the hardest difficulty (without dying)"; diff --git a/language.es_base b/language.es_base index cd456f0e3..b3b2190d1 100644 --- a/language.es_base +++ b/language.es_base @@ -756,7 +756,7 @@ SWWM_ACHIEVEMENT_CLIFFYB_TAG = "Mierdas de Recadero"; SWWM_ACHIEVEMENT_CLIFFYB_TXT = "Termina un mapa sin obtener ninguna llave"; SWWM_ACHIEVEMENT_PAR_TAG = "Rápido"; SWWM_ACHIEVEMENT_PAR_TXT = "Supera el tiempo par en %d mapas"; -SWWM_ACHIEVEMENT_ONEGUY_TAG = "A la Mierda este Tío en Particular"; +SWWM_ACHIEVEMENT_ONEGUY_TAG = "A la Mierda el Tío Ese"; SWWM_ACHIEVEMENT_ONEGUY_TXT = "Usa el Artefacto Ynykron para matar a un único enemigo no-jefe"; SWWM_ACHIEVEMENT_GOLDEN_TAG = "Lluvia Dorada"; SWWM_ACHIEVEMENT_GOLDEN_TXT = "Usa %d cartuchos dorados"; @@ -797,9 +797,9 @@ SWWM_ACHIEVEMENT_BUSTIN_TAG = "Me Hace Sentir Bien"; SWWM_ACHIEVEMENT_BUSTIN_TXT = "Revienta %d paredes"; SWWM_ACHIEVEMENT_MOTH_TAG = "Madre Polilla"; SWWM_ACHIEVEMENT_MOTH_TXT = "Reúne %d polillas"; -SWWM_ACHIEVEMENT_IWAD_TXT = "Juega en todos los IWADs soportados"; +SWWM_ACHIEVEMENT_IWAD_TXT = "Juega en todos los juegos soportados"; SWWM_ACHIEVEMENT_BRAKE_TAG = "Sin Frenos"; -SWWM_ACHIEVEMENT_BRAKE_TXT = "Consume todo tu combustible esprintando %d veces"; +SWWM_ACHIEVEMENT_BRAKE_TXT = "Consume toda tu capacidad de combustible en un solo esprint %d veces"; SWWM_ACHIEVEMENT_RAGE_TAG = "No Hablo me Enfado"; SWWM_ACHIEVEMENT_RAGE_TXT = "Usa %d Ragekits"; SWWM_ACHIEVEMENT_ABORT_TAG = "Uy lo He Roto"; @@ -815,7 +815,7 @@ SWWM_ACHIEVEMENT_REFLECT_TXT = "Mata %d enemigos con proyectiles desviados"; SWWM_ACHIEVEMENT_SEKIRO_TAG = "Dudar es Fracasar"; SWWM_ACHIEVEMENT_SEKIRO_TXT = "Muere y reinicia dos veces en el mismo mapa"; SWWM_ACHIEVEMENT_WAVE_TAG = "Sayonara, Capullo"; -SWWM_ACHIEVEMENT_WAVE_TXT = "Saluda a un enemigo muriéndose %d veces"; +SWWM_ACHIEVEMENT_WAVE_TXT = "Saluda a un enemigo muriéndose"; SWWM_ACHIEVEMENT_FRIEND_TAG = "Con Forma de Amigo"; SWWM_ACHIEVEMENT_FRIEND_TXT = "Entabla amistad con %d Cacodemonios, Hombres-Dragón o Ettins a base de caricias"; SWWM_ACHIEVEMENT_SHOCK_TXT = "Realiza %d combos de Biospark"; @@ -839,12 +839,12 @@ SWWM_ACHIEVEMENT_DOSH_TXT = "Ten ¥1.000.000.000 en mano"; SWWM_ACHIEVEMENT_JUMP_TAG = "Verticalidad"; SWWM_ACHIEVEMENT_JUMP_TXT = "Salta paredes %d veces"; SWWM_ACHIEVEMENT_EZKILL_TAG = "Eso Estuvo Fácil"; -SWWM_ACHIEVEMENT_EZKILL_TXT = "Mata %d enemigos con un solo disparo de Artefacto Ynykron"; +SWWM_ACHIEVEMENT_EZKILL_TXT = "Mata %d enemigos con un solo disparo de Artefacto Ynykron (excluyendo modo secundario)"; SWWM_ACHIEVEMENT_LIGMA_TAG = "¿Quien es Steve Jobs?"; SWWM_ACHIEVEMENT_LIGMA_TXT = "Mata a un jefe final con el Artefacto Ynykron"; SWWM_ACHIEVEMENT_YEET_TXT = "Lanza una Pistola Caramelo justo dentro del cerebro del Icono del Pecado"; SWWM_ACHIEVEMENT_MBF_TAG = "Puedes Acariciar el Perro"; -SWWM_ACHIEVEMENT_MBF_TXT = "Acaricia un Perro Ayudante MBF"; +SWWM_ACHIEVEMENT_MBF_TXT = "Acaricia un perro"; SWWM_ACHIEVEMENT_LIGHTNING_TAG = "Y los Dragones Desaparecieron"; SWWM_ACHIEVEMENT_LIGHTNING_TXT = "Mata al Wyvern Mortal usando la Carabina de Raíl"; SWWM_ACHIEVEMENT_DAKKA_TXT = "Dispara la Ametralladora Sheen durante un minuto entero"; @@ -888,3 +888,5 @@ SWWM_ACHIEVEMENT_TYRIAN_TAG = "Maestro de SHMUPs"; SWWM_ACHIEVEMENT_TYRIAN_TXT = "Completa una partida entera de Interstellar Demolitionist"; SWWM_ACHIEVEMENT_BOF_TAG = "Maestro de RPGs"; SWWM_ACHIEVEMENT_BOF_TXT = "Completa una partida entera de Demolition Quest"; +SWWM_ACHIEVEMENT_WANTDIE_TAG = "Mata a tu Jefe"; +SWWM_ACHIEVEMENT_WANTDIE_TXT = "Despeja un mapa en la dificultad más alta (sin morir)"; diff --git a/language.version b/language.version index 827277d0a..631ef3fdc 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r395 \cu(Tue 23 Mar 17:03:51 CET 2021)\c-"; -SWWM_SHORTVER="\cw0.9.11b-pre r395 \cu(2021-03-23 17:03:51)\c-"; +SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r396 \cu(Wed 24 Mar 02:25:38 CET 2021)\c-"; +SWWM_SHORTVER="\cw0.9.11b-pre r396 \cu(2021-03-24 02:25:38)\c-"; diff --git a/zscript/compat/swwm_hdoom.zsc b/zscript/compat/swwm_hdoom.zsc index 9d4aecb78..d11fb3d5b 100644 --- a/zscript/compat/swwm_hdoom.zsc +++ b/zscript/compat/swwm_hdoom.zsc @@ -34,6 +34,7 @@ Class SWWMHDoomHandler : StaticEventHandler "\cx┃ \cfIf you want Demo-chan to actually fuck some hot demon girls, \cx┃\n" "\cx┃ \cfjust go commission a porn artist or something, idk. \cx┃\n" "\cx┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"); + SWWMUtility.MarkAchievement('swwm_achievement_hdoom',players[consoleplayer]); } override void WorldLoaded( WorldEvent e ) diff --git a/zscript/compat/swwm_shame.zsc b/zscript/compat/swwm_shame.zsc index 67534f0e2..761b395fa 100644 --- a/zscript/compat/swwm_shame.zsc +++ b/zscript/compat/swwm_shame.zsc @@ -39,6 +39,7 @@ Class SWWMBrutalHandler : StaticEventHandler "\cx┃ \cfand trust me, it's better this way. \cx┃\n" "\cx┃ \cf \cx┃\n" "\cx┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"); + SWWMUtility.MarkAchievement('swwm_achievement_broccolidoccoli',players[consoleplayer]); } override void WorldLoaded( WorldEvent e ) diff --git a/zscript/handler/swwm_handler_cheats.zsc b/zscript/handler/swwm_handler_cheats.zsc index bd8806eb8..eb8b3c768 100644 --- a/zscript/handler/swwm_handler_cheats.zsc +++ b/zscript/handler/swwm_handler_cheats.zsc @@ -19,6 +19,7 @@ extend Class SWWMHandler S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); S_StartSound("misc/emone",CHAN_VOICE,CHANF_UI); } + SWWMCredits.HasCheated(players[e.Args[0]]); SWWMCredits.Give(players[e.Args[0]],1000000000); SWWMScoreObj.Spawn(1000000000,players[e.Args[0]].mo.Vec3Offset(0,0,players[e.Args[0]].mo.Height/2)); } @@ -341,6 +342,7 @@ extend Class SWWMHandler if ( kstr != cht[i].Left(kstr.length()) ) continue; matchany = true; if ( kstr != cht[i] ) continue; + if ( i > 4 ) SWWMUtility.MarkAchievement('swwm_achievement_cheat',players[consoleplayer]); if ( SWWMUtility.CheatsDisabled(consoleplayer) ) { kfail = true; diff --git a/zscript/handler/swwm_handler_damage.zsc b/zscript/handler/swwm_handler_damage.zsc index 0f5a63f03..54716a909 100644 --- a/zscript/handler/swwm_handler_damage.zsc +++ b/zscript/handler/swwm_handler_damage.zsc @@ -101,6 +101,7 @@ extend Class SWWMHandler s.AddDamageDealt(e.Damage); if ( e.Damage > s.topdealt ) s.topdealt = e.Damage; } + SWWMFlyTracker.Track(e.Thing,e.DamageSource); } } @@ -156,6 +157,25 @@ extend Class SWWMHandler lastcombat = AddOneliner("scorekill",1,15); } } + // achievement stuff + if ( e.Thing.IsHostile(src) ) + { + if ( e.Thing.bBOSS && ((e.DamageType == 'Dash') || (e.DamageType == 'Buttslam')) ) + SWWMUtility.AchievementProgressInc('swwm_progress_bossdash',1,src.player); + if ( e.DamageType == 'Push' ) + SWWMUtility.AchievementProgressInc('swwm_progress_sneeze',1,src.player); + else if ( e.DamageType == 'Buttslam' ) + SWWMUtility.AchievementProgressInc('swwm_progress_butts',1,src.player); + else if ( e.DamageType == 'Jump' ) + SWWMUtility.AchievementProgressInc('swwm_progress_stomp',1,src.player); + else if ( e.DamageType == 'GroundPound' ) + SWWMUtility.AchievementProgressInc('swwm_progress_thicc',1,src.player); + else if ( (e.DamageType == 'Love') && !(e.Thing is 'WolfensteinSS') ) + SWWMUtility.AchievementProgressInc('swwm_progress_love',1,src.player); + if ( e.Inflictor && e.Inflictor.FindInventory('ParriedBuff') ) + SWWMUtility.AchievementProgressInc('swwm_progress_reflect',1,src.player); + SWWMUtility.AchievementProgressInc('swwm_progress_mega',1,src.player); + } // no credits unless it's a counted kill or marine (that isn't friendly) if ( e.Thing.IsFriend(src) || (!e.Thing.default.bCountKill && !(e.Thing is 'ScriptedMarine')) ) return; @@ -236,6 +256,7 @@ extend Class SWWMHandler scr.xstr[ofs] = StringTable.Localize("$SWWM_OVERKILL"); scr.xcnt = ++ofs; } + SWWMUtility.AchievementProgressInc('swwm_progress_gib',1,src.player); } score = int(score*(1.+.5*min(multilevel[pnum],16))); if ( (multilevel[pnum] > 0) && scr ) @@ -281,6 +302,7 @@ extend Class SWWMHandler SWWMCredits.Give(src.player,1000); Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),src.player.GetUserName(),1000); SWWMScoreObj.Spawn(1000,src.Vec3Offset(0,0,src.Height/2)); + SWWMUtility.AchievementProgressInc('swwm_progress_allkills',1,src.player); } } diff --git a/zscript/handler/swwm_handler_vanillaboss.zsc b/zscript/handler/swwm_handler_vanillaboss.zsc index 7f56cd877..bd2c79b8a 100644 --- a/zscript/handler/swwm_handler_vanillaboss.zsc +++ b/zscript/handler/swwm_handler_vanillaboss.zsc @@ -1,5 +1,7 @@ // vanilla boss stuff +Class EndgameBossMarker : Inventory {} + extend Class SWWMHandler { String bosstag; @@ -131,6 +133,7 @@ extend Class SWWMHandler bossactors.Push(e.Thing); if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 6; if ( trk ) trk.bBOSS = true; + e.Thing.GiveInventory('EndgameBossMarker',1); } bosstag = "$BT_SPIDER"; } @@ -162,6 +165,7 @@ extend Class SWWMHandler bossactors.Push(e.Thing); if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 40; // goodbye, instakills if ( trk ) trk.bBOSS = true; + e.Thing.GiveInventory('EndgameBossMarker',1); } if ( e.Thing is 'BossEye' ) bossviewactor = e.Thing; @@ -195,6 +199,7 @@ extend Class SWWMHandler if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; if ( trk ) trk.bBOSS = true; bosstag = "$BT_DSPARIL"; + e.Thing.GiveInventory('EndgameBossMarker',1); } else if ( e.Thing is 'Sorcerer2' ) { @@ -205,6 +210,7 @@ extend Class SWWMHandler if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8; if ( trk ) trk.bBOSS = true; bosstag = "$BT_DSPARIL2"; + e.Thing.GiveInventory('EndgameBossMarker',1); } } else if ( bossmap == MAP_HMAP38 ) @@ -265,6 +271,7 @@ extend Class SWWMHandler if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 10; if ( trk ) trk.bBOSS = true; bosstag = "$BT_KORAX"; + e.Thing.GiveInventory('EndgameBossMarker',1); } } else if ( bossmap == MAP_HMAP60 ) @@ -276,6 +283,7 @@ extend Class SWWMHandler if ( trk ) trk.bBOSS = true; bosstag = "$BT_DEATHKINGS"; initialized = true; // healthbar shows from the start + e.Thing.GiveInventory('EndgameBossMarker',1); } } else if ( bossmap == MAP_EVMAP30 ) @@ -286,6 +294,7 @@ extend Class SWWMHandler if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; if ( trk ) trk.bBOSS = true; bosstag = "$BT_ARCHANGELUS"; + e.Thing.GiveInventory('EndgameBossMarker',1); } else if ( e.Thing.GetClassName() == "ArchangelusB" ) { @@ -296,6 +305,7 @@ extend Class SWWMHandler if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; if ( trk ) trk.bBOSS = true; bosstag = "$BT_ARCHANGELUS"; + e.Thing.GiveInventory('EndgameBossMarker',1); } } } diff --git a/zscript/handler/swwm_handler_worldload.zsc b/zscript/handler/swwm_handler_worldload.zsc index f6c64d39c..b4e0503bf 100644 --- a/zscript/handler/swwm_handler_worldload.zsc +++ b/zscript/handler/swwm_handler_worldload.zsc @@ -9,6 +9,8 @@ extend Class SWWMHandler // for minimap Array ffsectors; + Array mapkeys; + // level end stats override void WorldUnloaded( WorldEvent e ) { @@ -70,6 +72,21 @@ extend Class SWWMHandler players[i].mo.GiveInventory("InventoryWipeToken",1); } } + // did we complete this map without collecting any of its keys? (doesn't work for hubs) + if ( (mapkeys.Size() > 0) && !(level.clusterflags&LevelLocals.CLUSTER_HUB) ) + { + bool collected = false; + for ( int i=0; i 0 ) + return; + } + SWWMUtility.MarkAchievement('swwm_achievement_wantdie',players[consoleplayer]); } // "simple" tracking (used by the minimap) diff --git a/zscript/handler/swwm_statichandler_achievements.zsc b/zscript/handler/swwm_statichandler_achievements.zsc index bfb12e13d..2e803a318 100644 --- a/zscript/handler/swwm_statichandler_achievements.zsc +++ b/zscript/handler/swwm_statichandler_achievements.zsc @@ -1,56 +1,55 @@ // achievement tracking // constants for finetuning them numbers -const SA_GCSANDWICH = 40; -const SA_GHOST = 30; -const SA_ALLITEMS = 75; -const SA_CONGA = 25; -const SA_EXPLOGUN = 5; -const SA_GRAVITY = 20; -const SA_FLIGHT = 1000; -const SA_PAR = 25; +const SA_GCSANDWICH = 25; +const SA_GHOST = 25; +const SA_ALLITEMS = 30; +const SA_CONGA = 15; +const SA_EXPLOGUN = 7; +const SA_GRAVITY = 25; +const SA_FLIGHT = 2000; +const SA_PAR = 30; const SA_GOLDEN = 24; const SA_GIB = 500; -const SA_BARRIER = 30; -const SA_BOSSDASH = 20; -const SA_SNEEZE = 50; -const SA_FUEL = 200; +const SA_BARRIER = 25; +const SA_BOSSDASH = 10; +const SA_SNEEZE = 20; +const SA_FUEL = 400; const SA_SLAYER = 40; const SA_STOMP = 50; -const SA_THRUWALL = 30; +const SA_THRUWALL = 25; const SA_LEAD = 1500; -const SA_LOVE = 50; +const SA_LOVE = 20; const SA_BUTTS = 100; const SA_PLUSH = 20; const SA_BUSTIN = 40; -const SA_MOTH = 60; -const SA_BRAKE = 30; -const SA_RAGE = 40; +const SA_MOTH = 50; +const SA_BRAKE = 20; +const SA_RAGE = 25; const SA_PARRY = 100; -const SA_ALLKILLS = 100; -const SA_ALLSECRETS = 50; -const SA_REFLECT = 50; -const SA_WAVE = 20; -const SA_FRIEND = 60; -const SA_SHOCK = 50; -const SA_BALLS = 30; -const SA_REFRESH = 40; +const SA_ALLKILLS = 30; +const SA_ALLSECRETS = 30; +const SA_REFLECT = 20; +const SA_FRIEND = 30; +const SA_SHOCK = 30; +const SA_BALLS = 10; +const SA_REFRESH = 25; const SA_STEP = 60; -const SA_SUNNY = 30; +const SA_SUNNY = 25; const SA_THICC = 80; -const SA_SLEMG = 100; -const SA_JUMP = 100; -const SA_EZKILL = 200; +const SA_SLEMG = 50; +const SA_JUMP = 80; +const SA_EZKILL = 100; const SA_ROAST = 30; -const SA_DAB = 60; +const SA_DAB = 100; const SA_HND = 6; -const SA_GEPGUN = 50; +const SA_GEPGUN = 30; const SA_PENE = 20; -const SA_ACID = 200; +const SA_ACID = 100; const SA_SANIC = 800; const SA_BUNE = 150; const SA_BONK = 300; -const SA_ANOM = 40; +const SA_ANOM = 25; // achievement unlock tracking extend Class SWWMStaticHandler @@ -84,6 +83,7 @@ extend Class SWWMStaticHandler else if ( ac == 'swwm_achievement_dakka' ) maxval = 60; else if ( ac == 'swwm_achievement_dosh' ) maxval = 1000000000; else if ( ac == 'swwm_achievement_mega' ) maxval = 1000000; + else if ( ac == 'swwm_achievement_iwad' ) maxval = 7; if ( val && (prog < maxval) ) { ev.SetInt(0); @@ -194,7 +194,8 @@ extend Class SWWMStaticHandler 'swwm_achievement_snake', 'swwm_achievement_smb', 'swwm_achievement_tyrian', - 'swwm_achievement_bof' + 'swwm_achievement_bof', + 'swwm_achievement_wantdie' }; // localized name static const String names[] = @@ -280,7 +281,8 @@ extend Class SWWMStaticHandler "$SWWM_ACHIEVEMENT_SNAKE", "$SWWM_ACHIEVEMENT_SMB", "$SWWM_ACHIEVEMENT_TYRIAN", - "$SWWM_ACHIEVEMENT_BOF" + "$SWWM_ACHIEVEMENT_BOF", + "$SWWM_ACHIEVEMENT_WANTDIE" }; // has number in txt static const int number[] = @@ -328,7 +330,7 @@ extend Class SWWMStaticHandler SA_ALLSECRETS, SA_REFLECT, 0, // SEKIRO - SA_WAVE, + 0, // WAVE SA_FRIEND, SA_SHOCK, SA_BALLS, @@ -366,7 +368,8 @@ extend Class SWWMStaticHandler 0, // SNAKE 0, // SMB 0, // TYRIAN - 0 // BOF + 0, // BOF + 0 // WANTDIE }; // is ignored (hide away achievements that can't be done yet) static const bool ignoreme[] = @@ -452,93 +455,95 @@ extend Class SWWMStaticHandler true, // SNAKE true, // SMB true, // TYRIAN - true // BOF + true, // BOF + false // WANTDIE }; // progress tracking cvar (if any) static const Name pvar[] = { - 'swwm_progress_gcsandwich', // GCSANDWICH + 'swwm_progress_gcsandwich', 'None', // HDOOM 'None', // ONESTANDING 'None', // OOPSIE - 'swwm_progress_ghost', // GHOST + 'swwm_progress_ghost', 'None', // CLONK - 'swwm_progress_allitems', // ALLITEMS - 'swwm_progress_conga', // CONGA - 'swwm_progress_explogun', // EXPLOGUN - 'swwm_progress_gravity', // GRAVITY - 'swwm_progress_flight', // FLIGHT + 'swwm_progress_allitems', + 'swwm_progress_conga', + 'swwm_progress_explogun', + 'swwm_progress_gravity', + 'swwm_progress_flight', 'None', // CLIFFYB - 'swwm_progress_par', // PAR + 'swwm_progress_par', 'None', // ONEGUY - 'swwm_progress_golden', // GOLDEN - 'swwm_progress_gib', // GIB - 'swwm_progress_barrier', // BARRIER - 'swwm_progress_bossdash', // BOSSDASH - 'swwm_progress_sneeze', // SNEEZE - 'swwm_progress_fuel', // FUEL - 'swwm_progress_slayer', // SLAYER + 'swwm_progress_golden', + 'swwm_progress_gib', + 'swwm_progress_barrier', + 'swwm_progress_bossdash', + 'swwm_progress_sneeze', + 'swwm_progress_fuel', + 'swwm_progress_slayer', 'None', // BROCCOLIDOCCOLI 'None', // DIME - 'swwm_progress_stomp', // STOMP + 'swwm_progress_stomp', 'None', // CHEAT 'None', // GOD - 'swwm_progress_thruwall', // THRUWALL + 'swwm_progress_thruwall', 'None', // MASHIRO - 'swwm_progress_lead', // LEAD - 'swwm_progress_love', // LOVE - 'swwm_progress_butts', // BUTTS - 'swwm_progress_plush', // PLUSH - 'swwm_progress_bustin', // BUSTIN - 'swwm_progress_moth', // MOTH - 'None', // IWAD + 'swwm_progress_lead', + 'swwm_progress_love', + 'swwm_progress_butts', + 'swwm_progress_plush', + 'swwm_progress_bustin', + 'swwm_progress_moth', + 'swwm_progress_iwad', 'swwm_progress_brake', // BRAKE - 'swwm_progress_rage', // RAGE + 'swwm_progress_rage', 'None', // ABORT - 'swwm_progress_parry', // PARRY - 'swwm_progress_allkills', // ALLKILLS - 'swwm_progress_allsecrets', // ALLSECRETS - 'swwm_progress_reflect', // REFLECT + 'swwm_progress_parry', + 'swwm_progress_allkills', + 'swwm_progress_allsecrets', + 'swwm_progress_reflect', 'None', // SEKIRO - 'swwm_progress_wave', // WAVE - 'swwm_progress_friend', // FRIEND - 'swwm_progress_shock', // SHOCK - 'swwm_progress_balls', // BALLS - 'swwm_progress_refresh', // REFRESH - 'swwm_progress_step', // STEP - 'swwm_progress_sunny', // SUNNY - 'swwm_progress_thicc', // THICC - 'swwm_progress_allcoll', // ALLCOLL - 'swwm_progress_slemg', // SLEMG - 'swwm_progress_dosh', // DOSH - 'swwm_progress_jump', // JUMP - 'swwm_progress_ezkill', // EZKILL + 'None', // WAVE + 'swwm_progress_friend', + 'swwm_progress_shock', + 'swwm_progress_balls', + 'swwm_progress_refresh', + 'swwm_progress_step', + 'swwm_progress_sunny', + 'swwm_progress_thicc', + 'swwm_progress_allcoll', + 'swwm_progress_slemg', + 'swwm_progress_dosh', + 'swwm_progress_jump', + 'swwm_progress_ezkill', 'None', // LIGMA 'None', // YEET 'None', // MBF 'None', // LIGHTNING - 'swwm_progress_dakka', // DAKKA - 'swwm_progress_roast', // ROAST - 'swwm_progress_dab', // DAB - 'swwm_progress_hnd', // HND - 'swwm_progress_gepgun', // GEPGUN + 'swwm_progress_dakka', + 'swwm_progress_roast', + 'swwm_progress_dab', + 'swwm_progress_hnd', + 'swwm_progress_gepgun', 'None', // FULLER - 'swwm_progress_pene', // PENE - 'swwm_progress_acid', // ACID + 'swwm_progress_pene', + 'swwm_progress_acid', 'None', // SALT - 'swwm_progress_mega', // MEGA - 'swwm_progress_sanic', // SANIC + 'swwm_progress_mega', + 'swwm_progress_sanic', 'None', // TELE - 'swwm_progress_bune', // BUNE - 'swwm_progress_bonk', // BONK - 'swwm_progress_anom', // ANOM + 'swwm_progress_bune', + 'swwm_progress_bonk', + 'swwm_progress_anom', 'None', // ANONE 'None', // TETRIS 'None', // PONG 'None', // SNAKE 'None', // SMB 'None', // TYRIAN - 'None' // BOF + 'None', // BOF + 'None' // WANTDIE }; bool alldone = true; for ( int i=0; i)(AllActorClasses[i]); + if ( !c || (c == 'SWWMCollectible') ) continue; + let def = GetDefaultByType(c); + tnc++; + // check that we can collect it in this IWAD + if ( !def.ValidGame() ) continue; + nc++; + } + for ( Inventory i=other.inv; i; i=i.inv ) + { + if ( i is 'SWWMCollectible' ) + cnc++; + } + // force progress to be "total collectibles" so this doesn't get unset on other IWADs that may have more + if ( cnc == nc ) SWWMUtility.AchievementProgress('swwm_progress_allcoll',tnc,other.player); + else SWWMUtility.AchievementProgress('swwm_progress_allcoll',cnc,other.player); // we're only attaching to the other players if ( propagated ) return; diff --git a/zscript/items/swwm_collectibles_gesture.zsc b/zscript/items/swwm_collectibles_gesture.zsc index 103fd83c3..5fc81d0d6 100644 --- a/zscript/items/swwm_collectibles_gesture.zsc +++ b/zscript/items/swwm_collectibles_gesture.zsc @@ -100,11 +100,19 @@ Class SayaBeanGesture : SWWMItemGesture } A_StartSound("saya/giggle",CHAN_ITEMEXTRA,CHANF_OVERLAP); } + action void A_UsePlush() + { + if ( !Demolitionist(self) ) return; + let s = Demolitionist(self).mystats; + if ( !s ) return; + s.plushuses++; + SWWMUtility.AchievementProgress('swwm_progress_plush',s.plushuses,player); + } States { Fire: - XZW1 A 3; + XZW1 A 3 A_UsePlush(); XZW1 B 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP); XZW1 CDEFGH 3; XZW1 IJ 4; diff --git a/zscript/items/swwm_health.zsc b/zscript/items/swwm_health.zsc index 8725913ed..cc491b397 100644 --- a/zscript/items/swwm_health.zsc +++ b/zscript/items/swwm_health.zsc @@ -183,6 +183,7 @@ Class RefresherItem : SWWMHealth } override void AutoUseExtra( bool recursive ) { + SWWMUtility.AchievementProgressInc('swwm_progress_refresh',1,Owner.player); // regen effect doesn't stack if we autoactivated recursively if ( recursive ) return; let p = Powerup(Owner.FindInventory("RefresherRegen")); diff --git a/zscript/items/swwm_powerups.zsc b/zscript/items/swwm_powerups.zsc index 70fe021bb..7bd77cef1 100644 --- a/zscript/items/swwm_powerups.zsc +++ b/zscript/items/swwm_powerups.zsc @@ -26,6 +26,7 @@ Class GrilledCheeseSandwich : Inventory double lastsafeangle[5]; int safetic; int dteleport; + Actor lastdropper; override Inventory CreateCopy( Actor other ) { @@ -35,6 +36,7 @@ Class GrilledCheeseSandwich : Inventory } void DoTheThing( bool extrasafe = false ) { + SWWMUtility.AchievementProgressInc('swwm_progress_gcsandwich',1,Owner.player); SWWMHandler.DoFlash(Owner,Color(64,255,255,64),10); Owner.A_QuakeEx(9,9,9,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.); Owner.GiveBody(1000,1000); @@ -183,9 +185,14 @@ Class GrilledCheeseSandwich : Inventory other.Inv = Inv; Inv = saved; } + override void OnDrop( Actor dropper ) + { + lastdropper = dropper; + } override void PostTeleport( Vector3 destpos, double destangle, int flags ) { - // TODO "teleported bread" achievement + if ( !lastdropper ) return; + SWWMUtility.MarkAchievement('swwm_achievement_tele',lastdropper.player); } Default @@ -392,6 +399,7 @@ Class GhostArtifact : Inventory Demolitionist(Owner).lastbump *= 1.04; } else Owner.GiveInventory("GhostPower",1); + SWWMUtility.AchievementProgressInc('swwm_progress_ghost',1,Owner.player); return true; } @@ -541,6 +549,7 @@ Class GravitySuppressor : Inventory Demolitionist(Owner).lastbump *= 1.04; } else Owner.GiveInventory("GravityPower",1); + SWWMUtility.AchievementProgressInc('swwm_progress_gravity',1,Owner.player); return true; } override void Travelled() @@ -782,6 +791,7 @@ Class FuckingInvinciball : Inventory Demolitionist(Owner).lastbump *= 1.1; } else Owner.GiveInventory("InvinciballPower",1); + SWWMUtility.AchievementProgressInc('swwm_progress_sunny',1,Owner.player); return true; } override void Travelled() @@ -1064,6 +1074,7 @@ Class Ragekit : Inventory Demolitionist(Owner).lastbump *= .95; } else Owner.GiveInventory("RagekitPower",1); + SWWMUtility.AchievementProgressInc('swwm_progress_rage',1,Owner.player); return true; } override void Travelled() @@ -1560,6 +1571,7 @@ Class CompanionLamp : Actor m.lamp = self; m.trail = m.pos; moff.Push(m); + SWWMUtility.AchievementProgressInc('swwm_progress_moth',1,parent.player); } override void PostBeginPlay() { @@ -2015,6 +2027,7 @@ Class EBarrier : Inventory Demolitionist(Owner).lastbump *= 0.95; } else Owner.GiveInventory("BarrierPower",1); + SWWMUtility.AchievementProgressInc('swwm_progress_barrier',1,Owner.player); return true; } override void Travelled() @@ -2628,12 +2641,15 @@ Class Mykradvo : Inventory Amount++; return true; } + if ( (targets.Size() == 1) && targets[0] && !targets[0].bBOSS ) + SWWMUtility.MarkAchievement('swwm_achievement_anone',Owner.player); let p = Spawn("MykradvoBurst",spawnpos); p.target = Owner; MykradvoBurst(p).targets.Move(targets); targets.Clear(); if ( Owner is 'Demolitionist' ) Demolitionist(Owner).lastbump *= 1.2; + SWWMUtility.AchievementProgressInc('swwm_progress_anom',1,Owner.player); return true; } diff --git a/zscript/kbase/swwm_kbase.zsc b/zscript/kbase/swwm_kbase.zsc index d06c8739d..75442bc15 100644 --- a/zscript/kbase/swwm_kbase.zsc +++ b/zscript/kbase/swwm_kbase.zsc @@ -437,7 +437,8 @@ Class DemolitionistMenu : GenericMenu 'swwm_achievement_snake', 'swwm_achievement_smb', 'swwm_achievement_tyrian', - 'swwm_achievement_bof' + 'swwm_achievement_bof', + 'swwm_achievement_wantdie' }; // localized name static const String names[] = @@ -524,7 +525,8 @@ Class DemolitionistMenu : GenericMenu "$SWWM_ACHIEVEMENT_SNAKE", "$SWWM_ACHIEVEMENT_SMB", "$SWWM_ACHIEVEMENT_TYRIAN", - "$SWWM_ACHIEVEMENT_BOF" + "$SWWM_ACHIEVEMENT_BOF", + "$SWWM_ACHIEVEMENT_WANTDIE" }; // has number in txt static const int number[] = @@ -572,7 +574,7 @@ Class DemolitionistMenu : GenericMenu SA_ALLSECRETS, SA_REFLECT, 0, // SEKIRO - SA_WAVE, + 0, // WAVE SA_FRIEND, SA_SHOCK, SA_BALLS, @@ -611,7 +613,8 @@ Class DemolitionistMenu : GenericMenu 0, // SNAKE 0, // SMB 0, // TYRIAN - 0 // BOF + 0, // BOF + 0 // WANTDIE }; // is ignored (hide away achievements that can't be done yet) static const bool ignoreme[] = @@ -698,94 +701,96 @@ Class DemolitionistMenu : GenericMenu true, // SNAKE true, // SMB true, // TYRIAN - true // BOF + true, // BOF + false // WANTDIE }; // progress tracking cvar (if any) static const Name pvar[] = { - 'swwm_progress_gcsandwich', // GCSANDWICH + 'swwm_progress_gcsandwich', 'None', // HDOOM 'None', // ONESTANDING 'None', // OOPSIE - 'swwm_progress_ghost', // GHOST + 'swwm_progress_ghost', 'None', // CLONK - 'swwm_progress_allitems', // ALLITEMS - 'swwm_progress_conga', // CONGA - 'swwm_progress_explogun', // EXPLOGUN - 'swwm_progress_gravity', // GRAVITY - 'swwm_progress_flight', // FLIGHT + 'swwm_progress_allitems', + 'swwm_progress_conga', + 'swwm_progress_explogun', + 'swwm_progress_gravity', + 'swwm_progress_flight', 'None', // CLIFFYB - 'swwm_progress_par', // PAR + 'swwm_progress_par', 'None', // ONEGUY - 'swwm_progress_golden', // GOLDEN - 'swwm_progress_gib', // GIB - 'swwm_progress_barrier', // BARRIER - 'swwm_progress_bossdash', // BOSSDASH - 'swwm_progress_sneeze', // SNEEZE - 'swwm_progress_fuel', // FUEL - 'swwm_progress_slayer', // SLAYER + 'swwm_progress_golden', + 'swwm_progress_gib', + 'swwm_progress_barrier', + 'swwm_progress_bossdash', + 'swwm_progress_sneeze', + 'swwm_progress_fuel', + 'swwm_progress_slayer', 'None', // BROCCOLIDOCCOLI 'None', // DIME - 'swwm_progress_stomp', // STOMP + 'swwm_progress_stomp', 'None', // CHEAT 'None', // GOD - 'swwm_progress_thruwall', // THRUWALL + 'swwm_progress_thruwall', 'None', // MASHIRO - 'swwm_progress_lead', // LEAD - 'swwm_progress_love', // LOVE - 'swwm_progress_butts', // BUTTS - 'swwm_progress_plush', // PLUSH - 'swwm_progress_bustin', // BUSTIN - 'swwm_progress_moth', // MOTH - 'None', // IWAD - 'swwm_progress_brake', // BRAKE - 'swwm_progress_rage', // RAGE + 'swwm_progress_lead', + 'swwm_progress_love', + 'swwm_progress_butts', + 'swwm_progress_plush', + 'swwm_progress_bustin', + 'swwm_progress_moth', + 'swwm_progress_iwad', + 'swwm_progress_brake', + 'swwm_progress_rage', 'None', // ABORT - 'swwm_progress_parry', // PARRY - 'swwm_progress_allkills', // ALLKILLS - 'swwm_progress_allsecrets', // ALLSECRETS - 'swwm_progress_reflect', // REFLECT + 'swwm_progress_parry', + 'swwm_progress_allkills', + 'swwm_progress_allsecrets', + 'swwm_progress_reflect', 'None', // SEKIRO - 'swwm_progress_wave', // WAVE - 'swwm_progress_friend', // FRIEND - 'swwm_progress_shock', // SHOCK - 'swwm_progress_balls', // BALLS - 'swwm_progress_refresh', // REFRESH - 'swwm_progress_step', // STEP - 'swwm_progress_sunny', // SUNNY - 'swwm_progress_thicc', // THICC + 'None', // WAVE + 'swwm_progress_friend', + 'swwm_progress_shock', + 'swwm_progress_balls', + 'swwm_progress_refresh', + 'swwm_progress_step', + 'swwm_progress_sunny', + 'swwm_progress_thicc', 'None', // EVERYTHING - 'swwm_progress_allcoll', // ALLCOLL - 'swwm_progress_slemg', // SLEMG - 'swwm_progress_dosh', // DOSH - 'swwm_progress_jump', // JUMP - 'swwm_progress_ezkill', // EZKILL + 'swwm_progress_allcoll', + 'swwm_progress_slemg', + 'swwm_progress_dosh', + 'swwm_progress_jump', + 'swwm_progress_ezkill', 'None', // LIGMA 'None', // YEET 'None', // MBF 'None', // LIGHTNING - 'swwm_progress_dakka', // DAKKA - 'swwm_progress_roast', // ROAST - 'swwm_progress_dab', // DAB - 'swwm_progress_hnd', // HND - 'swwm_progress_gepgun', // GEPGUN + 'swwm_progress_dakka', + 'swwm_progress_roast', + 'swwm_progress_dab', + 'swwm_progress_hnd', + 'swwm_progress_gepgun', 'None', // FULLER - 'swwm_progress_pene', // PENE - 'swwm_progress_acid', // ACID + 'swwm_progress_pene', + 'swwm_progress_acid', 'None', // SALT - 'swwm_progress_mega', // MEGA - 'swwm_progress_sanic', // SANIC + 'swwm_progress_mega', + 'swwm_progress_sanic', 'None', // TELE - 'swwm_progress_bune', // BUNE - 'swwm_progress_bonk', // BONK - 'swwm_progress_anom', // ANOM + 'swwm_progress_bune', + 'swwm_progress_bonk', + 'swwm_progress_anom', 'None', // ANONE 'None', // TETRIS 'None', // PONG 'None', // SNAKE 'None', // SMB 'None', // TYRIAN - 'None' // BOF + 'None', // BOF + 'None' // WANTDIE }; achievements.Clear(); for ( int i=0; i= 3) && (psp.frame <= 12) && (psp.sprite == GetSpriteIndex('XZW1')) ) + CheckWave(); + } + + private void CheckWave() + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return; + for ( int i=0; i 0) || (a.tics == -1) || !(a.bISMONSTER || a.player) || !a.IsHostile(Owner) ) continue; + // check if we can see it + if ( !SWWMUtility.InPlayerFOV(Owner.player,a) ) continue; + // someone's dying + SWWMUtility.MarkAchievement('swwm_achievement_wave',Owner.player); + break; + } } static SWWMGesture SetGesture( PlayerPawn mo, int which ) @@ -232,6 +251,7 @@ Class SWWMGesture : SWWMWeapon { let s = Demolitionist(self).mystats; if ( s ) s.befriend++; + SWWMUtility.AchievementProgressInc('swwm_progress_friend',1,player); } t.bFRIENDLY = true; if ( deathmatch ) diff --git a/zscript/swwm_gesture_fx.zsc b/zscript/swwm_gesture_fx.zsc index 9dcf2bb51..4441ca624 100644 --- a/zscript/swwm_gesture_fx.zsc +++ b/zscript/swwm_gesture_fx.zsc @@ -462,6 +462,7 @@ Class HeadpatTracker : Actor patter.player.cheats |= CF_TOTALLYFROZEN; Demolitionist(patter).scriptedinvul = true; target.bDORMANT = true; + if ( SWWMUtility.IdentifyingDog(target) ) SWWMUtility.MarkAchievement('swwm_achievement_mbf',user.player); return true; } return false; diff --git a/zscript/swwm_handler.zsc b/zscript/swwm_handler.zsc index 1740314f7..8544d2837 100644 --- a/zscript/swwm_handler.zsc +++ b/zscript/swwm_handler.zsc @@ -110,9 +110,10 @@ Class SWWMHandler : EventHandler if ( !(e.ActivationType&SPAC_Use) ) return; if ( !e.Thing || !e.Thing.player ) return; let w = SWWMWeapon(e.Thing.player.ReadyWeapon); - if ( !w || !w.wallponch ) return; + if ( (!w || !w.wallponch) && (!(e.Thing is 'Demolitionist') || !Demolitionist(e.Thing).hitactivate) ) return; let s = SWWMStats.Find(e.Thing.player); if ( s ) s.wponch++; + SWWMUtility.AchievementProgressInc('swwm_progress_slemg',1,e.Thing.player); } // stuff for hud diff --git a/zscript/swwm_player.zsc b/zscript/swwm_player.zsc index 629c7609d..a3d05e534 100644 --- a/zscript/swwm_player.zsc +++ b/zscript/swwm_player.zsc @@ -5,6 +5,7 @@ Class Demolitionist : PlayerPawn Vector3 dashdir; double dashfuel, dashboost; int dashcooldown, boostcooldown, fuelcooldown; + bool fullfuel; bool sendtoground; bool key_reentrant; bool bInDefaultInventory; @@ -55,6 +56,7 @@ Class Demolitionist : PlayerPawn Actor selflight; Actor oldencroached; Vector3 oldencroachedpos; + int encroachtics; Vector3 pretelepos; @@ -63,6 +65,7 @@ Class Demolitionist : PlayerPawn int healcooldown, healtimer, oldhealth; bool scriptedinvul; + bool hitactivate; transient int lastuse, failcounter, failcooldown; @@ -445,24 +448,28 @@ Class Demolitionist : PlayerPawn } void A_Dash() { - vel += dashdir*dashboost; + vel += dashdir*dashboost*clamp(dashfuel/20.,0.,1.); player.vel *= 0.; if ( dashboost < .2 ) dashboost = 0.; else { if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:800); - dashboost *= (player.cmd.buttons&BT_USER2)?.9:.1; + if ( !(player.cmd.buttons&BT_USER2) ) dashboost *= .1; } - mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost); + double fueluse = (dashfuel-max(0.,dashfuel-dashboost))/60.; + SWWMUtility.AchievementProgressIncDouble('swwm_progress_fuel',fueluse,player); + mystats.fuelusage += fueluse; if ( !swwm_superfuel ) dashfuel = max(0.,dashfuel-dashboost); dashcooldown = min(40,max(10,int(dashcooldown*1.4))); fuelcooldown = max(30,fuelcooldown); + if ( (dashfuel <= 0.) && fullfuel ) + SWWMUtility.AchievementProgressInc('swwm_progress_brake',1,player); if ( (dashfuel <= 0.) || (dashboost <= 0.) ) SetStateLabel("DashEnd"); } void A_BoostUp( bool initial = false ) { - vel += (0,0,1)*dashboost; + vel += (0,0,1)*dashboost*clamp(dashfuel/10.,0,1.); player.vel *= 0.; if ( dashboost < .2 ) dashboost = 0.; else @@ -471,7 +478,9 @@ Class Demolitionist : PlayerPawn dashboost *= (player.cmd.buttons&BT_JUMP)?.95:.4; last_boost = level.maptime+1; } - mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost); + double fueluse = (dashfuel-max(0.,dashfuel-dashboost))/60.; + SWWMUtility.AchievementProgressIncDouble('swwm_progress_fuel',fueluse,player); + mystats.fuelusage += fueluse; if ( !swwm_superfuel ) dashfuel = max(0.,dashfuel-dashboost); if ( ((dashfuel <= 0.) || (dashboost <= 0.)) ) { @@ -863,6 +872,8 @@ Class Demolitionist : PlayerPawn CheckUnderwaterAmb(); if ( player.cmd.buttons&BT_USER3 ) SenseItems(); if ( vel.length() > mystats.topspeed ) mystats.topspeed = vel.length(); + if ( vel.length() > ((3600*GameTicRate)/32000.) ) + SWWMUtility.AchievementProgress('swwm_progress_sanic',int((vel.length()*3600*GameTicRate)/32000.),player); if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player); if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) ) { @@ -942,7 +953,11 @@ Class Demolitionist : PlayerPawn SetOrigin(newp,true); if ( !TestMobjLocation() ) SetOrigin(oldp,true); } + encroachtics++; + if ( !(encroachtics%GameTicRate) ) + SWWMUtility.AchievementProgress('swwm_progress_step',encroachtics/GameTicRate,player); } + else encroachtics = 0; oldencroached = encroached; oldencroachedpos = encroached.pos; } @@ -952,6 +967,7 @@ Class Demolitionist : PlayerPawn // make sure we're not getting launched because an enemy just died under our feet, because that can cause some issues if ( oldencroached && (dashboost <= 0.) && (lastvelz >= -25) && (!oldencroached.bISMONSTER || (oldencroached.Health > 0)) ) vel += oldencroached.vel+level.Vec3Diff(oldencroachedpos,oldencroached.pos); oldencroached = null; + encroachtics = 0; } if ( encroached && encroached.bSHOOTABLE && !encroached.bNODAMAGE && (lastvelz <= 0) && !(encroached is 'Demolitionist') ) { @@ -965,7 +981,7 @@ Class Demolitionist : PlayerPawn ps.target = self; ps.special1 = realdmg; } - if ( !encroached.bNOBLOOD && !encroached.bINVULNERABLE ) + if ( encroached && !encroached.bNOBLOOD && !encroached.bINVULNERABLE ) { encroached.TraceBleed(realdmg,self); encroached.SpawnBlood(pos,angle,realdmg); @@ -1046,6 +1062,7 @@ Class Demolitionist : PlayerPawn else if ( diff.y < -(a.Radius+Radius) ) bnorm = (0,1,0); if ( dir dot bnorm > -.6 ) continue; } + if ( !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue; // 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_AlertMonsters(swwm_uncapalert?0:800); @@ -1057,6 +1074,7 @@ Class Demolitionist : PlayerPawn { if ( bumped ) continue; bumped = true; + SWWMUtility.AchievementProgressInc('swwm_progress_bonk',1,player); A_QuakeEx(8,8,8,16,0,128,"",QF_RELATIVE|QF_SCALEDOWN); vel *= .2; vel -= dir*(10+(spd*30/mass)); @@ -1166,6 +1184,7 @@ Class Demolitionist : PlayerPawn { // headbump bumped = true; + SWWMUtility.AchievementProgressInc('swwm_progress_bonk',1,player); A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); bumptic = gametic+int(20+spd/4.); lastbump *= .8; @@ -1260,6 +1279,7 @@ Class Demolitionist : PlayerPawn } // wallbump bumped = true; + SWWMUtility.AchievementProgressInc('swwm_progress_bonk',1,player); A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP); bumptic = gametic+int(25+spd/4.); lastbump *= .8; @@ -1281,7 +1301,11 @@ Class Demolitionist : PlayerPawn // activate it int locknum = SWWMUtility.GetLineLock(BlockingLine); if ( !locknum || CheckKeys(locknum,false,true) ) + { + hitactivate = true; BlockingLine.Activate(self,lside,SPAC_Use); + hitactivate = false; + } BlockingLine.Activate(self,lside,SPAC_Impact); break; } @@ -1391,6 +1415,8 @@ Class Demolitionist : PlayerPawn A_DemoPain(); } PainChance = oldpchance; + if ( (Health <= 0) && (source == self) && (flags&DMG_EXPLOSION) ) + SWWMUtility.MarkAchievement('swwm_achievement_dime',player); return realdmg; } override void CalcHeight() @@ -1621,6 +1647,7 @@ Class Demolitionist : PlayerPawn } if ( (dodge.length() > 0) && (dashcooldown <= 0) && (dashfuel > 20.) && player.cmd.buttons&BT_USER2 && (player.onground || level.IsJumpingAllowed() || (player.cmd.buttons&BT_CROUCH)) && (gamestate == GS_LEVEL) ) { + fullfuel = (dashfuel >= default.dashfuel); dashdir = dodge.unit(); dashcooldown = 10; dashboost = 20.; @@ -1759,6 +1786,7 @@ Class Demolitionist : PlayerPawn { A_StartSound("demolitionist/kick",CHAN_FOOTSTEP,CHANF_OVERLAP); last_kick = level.maptime+1; + SWWMUtility.AchievementProgressInc('swwm_progress_jump',1,player); } } bOnMobj = false; @@ -1789,6 +1817,7 @@ Class Demolitionist : PlayerPawn // bunnyhop time if ( (player.cmd.buttons&BT_RUN) && (level.maptime < (lastairtic+10)) && !walljump && !wallclimb ) { + SWWMUtility.AchievementProgressInc('swwm_progress_bune',1,player); // bhop, z vel relative to vel size if ( vel.z < 25. ) // don't ramp up too hard vel.z += jumpvelz*(1.2+vel.length()*.01); @@ -1863,6 +1892,7 @@ Class Demolitionist : PlayerPawn // reboot (if possible) if ( !FindInventory("ReviveCooldown") && (((swwm_revivecooldown >= 0) && (G_SkillPropertyInt(SKILLP_ACSReturn) < 4)) || !hasrevived) ) { + if ( hasrevived ) SWWMUtility.MarkAchievement('swwm_achievement_sekiro',player); hasrevived = true; player.Resurrect(); player.damagecount = 0; @@ -2265,6 +2295,7 @@ Class Demolitionist : PlayerPawn if ( hnd ) hnd.allsecrets = true; score = 1000; Console.Printf(StringTable.Localize("$SWWM_LASTSECRET"),player.GetUserName(),score); + SWWMUtility.AchievementProgressInc('swwm_progress_allsecrets',1,player); } else Console.Printf(StringTable.Localize("$SWWM_FINDSECRET"),player.GetUserName(),score); if ( CheckLocalView() ) SWWMHandler.AddOneliner("findsecret",2,40); diff --git a/zscript/swwm_statichandler.zsc b/zscript/swwm_statichandler.zsc index 788cc71a6..b1c605344 100644 --- a/zscript/swwm_statichandler.zsc +++ b/zscript/swwm_statichandler.zsc @@ -51,6 +51,13 @@ Class SWWMStaticHandler : StaticEventHandler Demolitionist(players[i].mo).CheckUnderwaterAmb(true); } } + if ( players[consoleplayer].mo ) + { + // recheck progress on collectibles + int nc = 0; + for ( Inventory i=players[consoleplayer].mo.inv; i; i=i.inv ) if ( i is 'SWWMCollectible' ) nc++; + if ( !swwm_achievement_allcoll ) CVar.FindCVar('swwm_progress_allcoll').SetInt(nc); + } SWWMHandler.ClearAllShaders(players[consoleplayer]); // force a reset of the minimap zoom in case it's set beyond safe levels double mmz = swwm_mm_zoom; @@ -93,6 +100,13 @@ Class SWWMStaticHandler : StaticEventHandler Font.GetFont('TewiShaded'); Font.GetFont('TewiShadedInverse'); Font.GetFont('SWWMBigFont'); + // iwad progress + if ( gameinfo.gametype&GAME_Doom ) + SWWMUtility.AchievementProgressOr('swwm_progress_iwad',1,players[consoleplayer]); + else if ( gameinfo.gametype&GAME_Heretic ) + SWWMUtility.AchievementProgressOr('swwm_progress_iwad',2,players[consoleplayer]); + else if ( gameinfo.gametype&GAME_Hexen ) + SWWMUtility.AchievementProgressOr('swwm_progress_iwad',4,players[consoleplayer]); } override void ConsoleProcess( ConsoleEvent e ) @@ -118,6 +132,34 @@ Class SWWMStaticHandler : StaticEventHandler for ( int i=0; i 0) && !(gametic%GameTicRate) ) + { + let pt = CVar.FindCVar('swwm_playtime'); + int ct = pt.GetInt(); + pt.SetInt(ct+1); + } if ( gamestate != GS_LEVEL ) return; CheckAllAchievements(); if ( !mpsent ) diff --git a/zscript/swwm_thinkers.zsc b/zscript/swwm_thinkers.zsc index e12004757..f3474eaa2 100644 --- a/zscript/swwm_thinkers.zsc +++ b/zscript/swwm_thinkers.zsc @@ -81,7 +81,7 @@ Class SWWMDamageAccumulator : Thinker { Super.Tick(); // so many damn safeguards in this - if ( !victim ) + if ( !victim || (victim.Health <= 0) ) { Destroy(); return; @@ -396,3 +396,42 @@ Class SWWMCorpseCleaner : Thinker } } } + +Class SWWMFlyTracker : Thinker +{ + Actor tracked, instigator; + Vector3 startpos; + int gracepd; + + static void Track( Actor b, Actor whomst ) + { + if ( !b || !whomst ) return; + let ti = ThinkerIterator.Create("SWWMFlyTracker",STAT_USER); + SWWMFlyTracker ffd; + while ( ffd = SWWMFlyTracker(ti.Next()) ) + { + if ( ffd.tracked != b ) continue; + ffd.instigator = whomst; + return; + } + ffd = new("SWWMFlyTracker"); + ffd.ChangeStatNum(STAT_USER); + ffd.tracked = b; + ffd.instigator = whomst; + ffd.startpos = b.pos; + } + + override void Tick() + { + if ( !tracked || tracked.bFLOAT || tracked.bNOGRAVITY || (tracked.waterlevel > 1) || (tracked.pos.z <= tracked.floorz) || !tracked.TestMobjZ(false) ) + { + gracepd++; + if ( gracepd < 10 ) return; + if ( instigator ) SWWMUtility.AchievementProgress('swwm_progress_flight',int(level.Vec3Diff(startpos,tracked.pos).length()),instigator.player); + Destroy(); + return; + } + gracepd = 0; + } + +} diff --git a/zscript/swwm_thinkers_player.zsc b/zscript/swwm_thinkers_player.zsc index 1b6a91f47..e813d0440 100644 --- a/zscript/swwm_thinkers_player.zsc +++ b/zscript/swwm_thinkers_player.zsc @@ -43,6 +43,7 @@ Class SWWMStats : Thinker Array secretdone; // hackaround for stuff getting lost Array > ownedcollectibles; + int plushuses; // for pistol start info (to avoid it within hubs) int lastcluster; @@ -61,7 +62,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 - if ( inflictor.FindInventory("ParriedBuff") ) which = 'DoomWeapon'; // gross hack + if ( inflictor && inflictor.FindInventory("ParriedBuff") ) which = 'DoomWeapon'; // gross hack // properly credit some projectiles to their respective gun else if ( inflictor is 'AirBullet' ) which = 'DeepImpact'; else if ( inflictor is 'PusherProjectile' ) which = 'PusherWeapon'; @@ -200,6 +201,14 @@ Class SWWMCredits : Thinker { PlayerInfo myplayer; int credits, hcredits; + bool cheated; + + static void HasCheated( PlayerInfo p ) + { + let c = Find(p); + if ( !c ) return; + c.cheated = true; + } static void Give( PlayerInfo p, int amount, int hamount = 0 ) { @@ -220,6 +229,9 @@ Class SWWMCredits : Thinker s.hiscore = c.credits; s.hhiscore = c.hcredits; } + if ( c.cheated ) return; + if ( c.hcredits > 0 ) SWWMUtility.AchievementProgress('swwm_progress_dosh',1000000000,p); + else SWWMUtility.AchievementProgress('swwm_progress_dosh',c.credits,p); } static clearscope bool CanTake( PlayerInfo p, int amount, int hamount = 0 ) diff --git a/zscript/utility/swwm_utility.zsc b/zscript/utility/swwm_utility.zsc index ded1bc3a6..0b55bec80 100644 --- a/zscript/utility/swwm_utility.zsc +++ b/zscript/utility/swwm_utility.zsc @@ -9,6 +9,8 @@ enum EDoExplosionFlags DE_NOTMISSILE = 16, // instigator is the source itself (normally it'd be its target pointer) DE_EXTRAZTHRUST = 32, // applies a higher Z thrust to enemies on ground DE_HOWL = 64, // 25% chance for hit enemies to howl + DE_COUNTENEMIES = 128, // only count hits for hostiles + DE_COUNTSTEALTH = 256, // only count hits for inactive monsters }; Class SWWMUtility @@ -899,13 +901,15 @@ Class SWWMUtility if ( (flags&DE_BLAST) && a.bCANBLAST && !a.bDONTBLAST ) a.bBLASTED = true; } // hit it - nhit++; + bool inactive = (!a.player&&!a.target); + bool hostile = (Instigator&&a.IsHostile(Instigator)&&(a.bISMONSTER||a.player)); + if ( (!(flags&DE_COUNTENEMIES) || hostile) && (!(flags&DE_COUNTSTEALTH) || inactive) ) nhit++; int dmg = int(Damage*damagescale); if ( dmg <= 0 ) continue; // no harm int ndmg = a.DamageMobj(Source,Instigator,dmg,(DamageType=='')?Source.DamageType:DamageType,DMG_EXPLOSION,atan2(-dir.y,-dir.x)); if ( a && !(flags&DE_NOBLEED) ) a.TraceBleed((ndmg>0)?ndmg:dmg,Source); - if ( (flags&DE_HOWL) && a && a.bISMONSTER && !Random[DoBlast](0,3) ) a.Howl(); - if ( !a || (a.Health <= 0) ) nkill++; + if ( (flags&DE_HOWL) && a && (a.Health > 0) && a.bISMONSTER && !Random[DoBlast](0,3) ) a.Howl(); + if ( (!a || (a.Health <= 0)) && (!(flags&DE_COUNTENEMIES) || hostile) && (!(flags&DE_COUNTSTEALTH) || inactive) ) nkill++; } return nhit, nkill; } @@ -1625,6 +1629,47 @@ Class SWWMUtility return false, checkme; } + // achievement helpers + static clearscope void MarkAchievement( Name mvar, PlayerInfo p = null ) + { + if ( !p || (p != players[consoleplayer]) ) return; + let cv = CVar.FindCVar(mvar); + if ( !cv ) return; + if ( cv.GetInt() < 1 ) cv.SetInt(1); + } + static clearscope void AchievementProgress( Name pvar, int val, PlayerInfo p = null ) + { + if ( !p || (p != players[consoleplayer]) ) return; + let cv = CVar.FindCVar(pvar); + if ( !cv ) return; + int cval = cv.GetInt(); + cv.SetInt(max(cval,val)); + } + static clearscope void AchievementProgressInc( Name pvar, int inc, PlayerInfo p = null ) + { + if ( !p || (p != players[consoleplayer]) ) return; + let cv = CVar.FindCVar(pvar); + if ( !cv ) return; + int cval = cv.GetInt(); + cv.SetInt(cval+inc); + } + static clearscope void AchievementProgressIncDouble( Name pvar, double inc, PlayerInfo p = null ) + { + if ( !p || (p != players[consoleplayer]) ) return; + let cv = CVar.FindCVar(pvar); + if ( !cv ) return; + double cval = cv.GetFloat(); + cv.SetFloat(cval+inc); + } + static clearscope void AchievementProgressOr( Name pvar, int val, PlayerInfo p = null ) + { + if ( !p || (p != players[consoleplayer]) ) return; + let cv = CVar.FindCVar(pvar); + if ( !cv ) return; + int cval = cv.GetInt(); + cv.SetInt(cval|val); + } + // full reset of inventory (excluding collectibles, and optionally resetting the score) static play void WipeInventory( Actor mo, bool resetscore = false, bool allplayers = false ) { diff --git a/zscript/weapons/swwm_baseweapon_melee.zsc b/zscript/weapons/swwm_baseweapon_melee.zsc index 6fa0576c9..07c9f3830 100644 --- a/zscript/weapons/swwm_baseweapon_melee.zsc +++ b/zscript/weapons/swwm_baseweapon_melee.zsc @@ -199,6 +199,7 @@ Class ParryField : Actor critsnd = true; } if ( s ) s.parries++; + SWWMUtility.AchievementProgressInc('swwm_progress_parry',1,master.player); } if ( --special1 <= 0 ) Destroy(); } @@ -390,7 +391,10 @@ extend Class SWWMWeapon { int locknum = SWWMUtility.GetLineLock(ut.uses[i].hitline); if ( !locknum || CheckKeys(locknum,false,true) ) + { + SWWMUtility.AchievementProgressInc('swwm_progress_slemg',1,player); ut.uses[i].hitline.RemoteActivate(self,ut.uses[i].hitside,SPAC_Use,ut.uses[i].pos); + } if ( !(ut.uses[i].hitline.activation&SPAC_UseThrough) ) break; } } diff --git a/zscript/weapons/swwm_blazeit_fx.zsc b/zscript/weapons/swwm_blazeit_fx.zsc index 349e2b7ad..2a12dc361 100644 --- a/zscript/weapons/swwm_blazeit_fx.zsc +++ b/zscript/weapons/swwm_blazeit_fx.zsc @@ -546,14 +546,18 @@ Class HellblazerRavager : HellblazerMissile Class HellblazerWarhead : HellblazerMissile { + int nkill; + void A_BlazerWarheadExplode() { + nkill = 0; bForceXYBillboard = true; bRollSprite = false; A_SetRenderStyle(1.0,STYLE_Add); A_SprayDecal("WumboRocketBlast",150); A_SetScale(7.); - SWWMUtility.DoExplosion(self,1500,600000,400,200); + int nhit; + [nhit, nkill] = SWWMUtility.DoExplosion(self,1500,600000,400,200,DE_COUNTENEMIES); A_NoGravity(); A_QuakeEx(9,9,9,150,0,12000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:4000,rollIntensity:2.); A_StopSound(CHAN_BODY); @@ -594,7 +598,11 @@ Class HellblazerWarhead : HellblazerMissile void A_WarheadSub() { if ( special1 < 40 ) - SWWMUtility.DoExplosion(self,320-special1*8,600000-special1*12000,200+special1*30); + { + int nhit, nkil; + [nhit, nkil] = SWWMUtility.DoExplosion(self,320-special1*8,600000-special1*12000,200+special1*30,flags:DE_COUNTENEMIES); + nkill += nkil; + } special1++; if ( (special1 <= 30) && !(special1%2) ) { @@ -643,6 +651,7 @@ Class HellblazerWarhead : HellblazerMissile c.angle = ang; c.pitch = pt; c.target = target; + c.master = self; } } } @@ -658,9 +667,13 @@ Class HellblazerWarhead : HellblazerMissile TNT1 A 1 { A_WarheadSub(); - A_FadeOut(.05); + if ( special1 >= 40 ) + { + if ( target ) SWWMUtility.AchievementProgress('swwm_progress_slayer',invoker.nkill,target.player); + Destroy(); + } } - Stop; + Wait; } } @@ -1221,7 +1234,9 @@ Class HellblazerWarheadArm : Actor TNT1 A 1 { Spawn("HellblazerWarheadTrail",pos); - SWWMUtility.DoExplosion(self,20+reactiontime*4,3000+500*reactiontime,120+4*reactiontime); + int nhit, nkill; + [nhit, nkill] = SWWMUtility.DoExplosion(self,20+reactiontime*4,3000+500*reactiontime,120+4*reactiontime,flags:DE_COUNTENEMIES); + if ( HellblazerWarhead(master) ) HellblazerWarhead(master).nkill += nkill; Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5); let s = Spawn("SWWMHalfSmoke",pos); s.vel = pvel+vel*.2; diff --git a/zscript/weapons/swwm_cbt.zsc b/zscript/weapons/swwm_cbt.zsc index 554f9d9c9..62d030c8b 100644 --- a/zscript/weapons/swwm_cbt.zsc +++ b/zscript/weapons/swwm_cbt.zsc @@ -421,6 +421,7 @@ Class Wallbuster : SWWMWeapon [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); int alertness = 0; // second pass, play the fire effects + int salts = 0; for ( int i=0; i louds[which] ) alertness += louds[which]/4; else alertness += louds[which]; } + if ( salts >= 25 ) SWWMUtility.MarkAchievement('swwm_achievement_salt',player); if ( howmany < 3 ) player.SetPsprite(PSP_WEAPON,ResolveState("FireOne")); else if ( howmany < 15 ) player.SetPsprite(PSP_WEAPON,ResolveState("FireFive")); else player.SetPsprite(PSP_WEAPON,ResolveState("FireTwentyFive")); diff --git a/zscript/weapons/swwm_cbt_fx.zsc b/zscript/weapons/swwm_cbt_fx.zsc index 5f6617e70..1dd360bd3 100644 --- a/zscript/weapons/swwm_cbt_fx.zsc +++ b/zscript/weapons/swwm_cbt_fx.zsc @@ -343,6 +343,7 @@ Class BusterWall : Thinker { let s = SWWMStats.Find(Instigator.player); if ( s ) s.busts++; + SWWMUtility.AchievementProgressInc('swwm_progress_bustin',1,Instigator.player); } bust.busted = true; bust.busttics = 0; diff --git a/zscript/weapons/swwm_danmaku_fx.zsc b/zscript/weapons/swwm_danmaku_fx.zsc index c424fee9f..73399be23 100644 --- a/zscript/weapons/swwm_danmaku_fx.zsc +++ b/zscript/weapons/swwm_danmaku_fx.zsc @@ -445,6 +445,7 @@ Class EvisceratorProjLight : PaletteLight Class EvisceratorProj : Actor { double heat; + Vector3 startpos; Default { Obituary "$O_EVISCERATOR"; @@ -463,11 +464,17 @@ Class EvisceratorProj : Actor override void PostBeginPlay() { Super.PostBeginPlay(); + startpos = pos; if ( waterlevel <= 0 ) vel.z += 3; heat = 1.5; } action void A_EvisExplode() { + if ( target && tracer && (tracer.bIsMonster||tracer.player) && tracer.IsHostile(target) ) + { + double dist = level.Vec3Diff(pos,invoker.startpos).length(); + SWWMUtility.AchievementProgress('swwm_progress_lead',int(dist),target.player); + } bForceXYBillboard = true; A_SetRenderStyle(1.0,STYLE_Add); A_SprayDecal("BigRocketBlast",50); diff --git a/zscript/weapons/swwm_deathlydeathcannon_fx.zsc b/zscript/weapons/swwm_deathlydeathcannon_fx.zsc index 1ac470a61..f1f7cbee5 100644 --- a/zscript/weapons/swwm_deathlydeathcannon_fx.zsc +++ b/zscript/weapons/swwm_deathlydeathcannon_fx.zsc @@ -419,10 +419,20 @@ Class YnykronImpact : Actor gc.DoTheThing(true); gc.Amount--; } - else if ( !tracer.FindInventory("GrilledCheeseSafeguard") ) tracer.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS); + else if ( !tracer.FindInventory("GrilledCheeseSafeguard") ) + { + tracer.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS); + if ( target && tracer && (tracer.Health > 0) ) + SWWMUtility.MarkAchievement('swwm_achievement_god',target.player); + } if ( tracer && (tracer.Health <= 0) ) { - if ( tracer.player ) PlayerGone.FeckOff(tracer); + if ( tracer.player ) + { + if ( tracer == target ) + SWWMUtility.MarkAchievement('swwm_achievement_oopsie',tracer.player); + PlayerGone.FeckOff(tracer); + } if ( tracer.FindState("YnykronDeath",true) ) tracer.SetStateLabel("YnykronDeath"); // dedicated state else @@ -437,6 +447,10 @@ Class YnykronImpact : Actor let r = Spawn("AshenRemains",tracer.pos); r.scale *= tracer.radius/16.; } + if ( (tracer.bIsMonster || tracer.player) && tracer.IsHostile(target) && YnykronShot(master) ) + YnykronShot(master).enemykills++; + if ( target && tracer.FindInventory("EndgameBossMarker") ) + SWWMUtility.MarkAchievement('swwm_achievement_ligma',target.player); } } if ( YnykronShot(master) ) @@ -499,7 +513,10 @@ Class YnykronImpact : Actor dirto /= dist; int trad = int(max(t.radius,t.height)); if ( t && YnykronShot(master) ) + { YnykronShot(master).hitlist.Push(t); + if ( t.bBOSS ) YnykronShot(master).hitboss = true; + } // spawn blast that will propagate let b = Spawn("YnykronDelayedImpact",t.pos); Vector3 ofs = level.Vec3Diff(t.pos,level.Vec3Offset(pos,dirto*min(rad,dist))); @@ -666,7 +683,10 @@ Class YnykronBeam : Actor continue; int trad = int(max(t.hitlist[i].hitactor.radius,t.hitlist[i].hitactor.height)); if ( t.hitlist[i].hitactor && YnykronShot(master) ) + { YnykronShot(master).hitlist.Push(t.hitlist[i].hitactor); + if ( t.hitlist[i].hitactor.bBOSS ) YnykronShot(master).hitboss = true; + } // spawn blast that will propagate let b = Spawn("YnykronImpact",t.hitlist[i].hitlocation); b.tracer = t.HitList[i].hitactor; @@ -763,7 +783,10 @@ Class YnykronBeam : Actor continue; int trad = int(max(it.hitlist[i].hitactor.radius,it.hitlist[i].hitactor.height)); if ( it.hitlist[i].hitactor && YnykronShot(master) ) + { YnykronShot(master).hitlist.Push(it.hitlist[i].hitactor); + if ( it.hitlist[i].hitactor.bBOSS ) YnykronShot(master).hitboss = true; + } // spawn blast that will propagate let b = Spawn("YnykronImpact",it.hitlist[i].hitlocation); b.tracer = it.hitlist[i].hitactor; @@ -1040,6 +1063,8 @@ Class YnykronImpactRing : Actor Class YnykronShot : Actor { Array hitlist; + bool hitboss; + int enemykills; int beamcount; int blastcount; int lastimpact; @@ -1115,13 +1140,19 @@ Class YnykronShot : Actor } } // wait until we're no longer needed and all effects are over - if ( IsActorPlayingSound(CHAN_VOICE) || (beamcount > 0) || (blastcount > 0) ) - { - special1 = min(special1,50); + if ( (beamcount > 0) || (blastcount > 0) ) return; + if ( target && enemykills ) + { + if ( (enemykills == 1) && !hitboss ) + SWWMUtility.MarkAchievement('swwm_achievement_oneguy',target.player); + SWWMUtility.AchievementProgress('swwm_progress_ezkill',enemykills,target.player); + enemykills = 0; } - // we're done here, but wait for a while just in case - if ( special1 > 350 ) Destroy(); + if ( IsActorPlayingSound(CHAN_VOICE) ) + return; + // we're done here + Destroy(); } } @@ -2395,10 +2426,20 @@ Class YnykronSingularity : Actor gc.DoTheThing(true); gc.Amount--; } - else if ( !a.FindInventory("GrilledCheeseSafeguard") ) a.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS); + else if ( !a.FindInventory("GrilledCheeseSafeguard") ) + { + a.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS); + if ( target && a && (a.Health > 0) ) + SWWMUtility.MarkAchievement('swwm_achievement_god',target.player); + } if ( a && (a.Health <= 0) ) { - if ( a.player ) PlayerGone.FeckOff(a); + if ( a.player ) + { + if ( a == target ) + SWWMUtility.MarkAchievement('swwm_achievement_oopsie',a.player); + PlayerGone.FeckOff(a); + } if ( a.FindState("YnykronAltDeath",true) ) a.SetStateLabel("YnykronAltDeath"); // dedicated state else @@ -2408,6 +2449,8 @@ Class YnykronSingularity : Actor a.A_ChangeLinkFlags(false); // remove from blockmap, should guarantee archviles not raising this IDontFeelSoGood.DeletThis(a,true); // ensures corpse is deleted too } + if ( target && a.FindInventory("EndgameBossMarker") ) + SWWMUtility.MarkAchievement('swwm_achievement_ligma',target.player); } if ( !a || (a.Health <= 0) ) specialf2 += min(100.,capmass*.6); // partial absorption diff --git a/zscript/weapons/swwm_deepdarkimpact.zsc b/zscript/weapons/swwm_deepdarkimpact.zsc index b0a8d19be..9dd7ab3dd 100644 --- a/zscript/weapons/swwm_deepdarkimpact.zsc +++ b/zscript/weapons/swwm_deepdarkimpact.zsc @@ -211,6 +211,7 @@ Class DeepImpact : SWWMWeapon } let ti = ThinkerIterator.Create("Actor"); Actor m; + let s = Demolitionist(self).mystats; while ( m = Actor(ti.Next()) ) { if ( !(SWWMUtility.ValidProjectile(m) && (m.target != self)) ) continue; @@ -231,6 +232,8 @@ Class DeepImpact : SWWMWeapon let pb = Inventory(Spawn("ParriedBuff")); pb.AttachToOwner(m); } + if ( s ) s.parries++; + SWWMUtility.AchievementProgressInc('swwm_progress_parry',1,player); } int numpt = Random[Impact](7,12); for ( int i=0; i girth) && is_schutt ) { diff --git a/zscript/weapons/swwm_sparkyboi_fx.zsc b/zscript/weapons/swwm_sparkyboi_fx.zsc index 9229a75c9..df9881aca 100644 --- a/zscript/weapons/swwm_sparkyboi_fx.zsc +++ b/zscript/weapons/swwm_sparkyboi_fx.zsc @@ -1127,6 +1127,7 @@ Class BiosparkBeam : Actor s.Args[0] = t.hitlist[i].hitactor.target.special1; } t.hitlist[i].hitactor.target.Destroy(); + if ( target ) SWWMUtility.AchievementProgressInc('swwm_progress_shock',1,target.player); } else { diff --git a/zscript/weapons/swwm_splode.zsc b/zscript/weapons/swwm_splode.zsc index 41056ef1f..0f233bb53 100644 --- a/zscript/weapons/swwm_splode.zsc +++ b/zscript/weapons/swwm_splode.zsc @@ -9,6 +9,8 @@ Class ExplodiumGun : SWWMWeapon double casex, casey; transient ui TextureID WeaponBox; transient ui Font TewiFont; + transient Actor deadeye_tgt; + transient int deadeye_cnt; Property ClipCount : ClipCount; @@ -110,6 +112,17 @@ Class ExplodiumGun : SWWMWeapon SWWMBulletTrail.DoTrail(self,origin,dir,10000,2); if ( d.HitType == TRACE_HitActor ) { + if ( invoker.deadeye_tgt && (d.HitActor != invoker.deadeye_tgt) ) + { + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + } + else + { + invoker.deadeye_tgt = d.HitActor; + invoker.deadeye_cnt++; + SWWMUtility.AchievementProgress('swwm_progress_explogun',invoker.deadeye_cnt,player); + } int dmg = 15; // might as well apply explosion on top if ( dmg >= d.HitActor.Health ) dmg += 20; @@ -131,33 +144,38 @@ Class ExplodiumGun : SWWMWeapon b.pitch = asin(d.HitDir.z); b.target = self; } - else if ( d.HitType != TRACE_HitNone ) + else { - Vector3 hitnormal = -d.HitDir; - if ( d.HitType == TRACE_HitFloor ) + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + if ( d.HitType != TRACE_HitNone ) { - if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal; - else hitnormal = d.HitSector.floorplane.Normal; + 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; + } + let p = Spawn("SWWMBulletImpact",d.HitLocation+hitnormal*0.01); + p.angle = atan2(hitnormal.y,hitnormal.x); + p.pitch = asin(-hitnormal.z); + if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); + let b = Spawn("ExplodiumBulletImpact",d.HitLocation+hitnormal*4.); + b.angle = atan2(hitnormal.y,hitnormal.x); + b.pitch = asin(-hitnormal.z); + b.target = self; + if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z); } - 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; - } - let p = Spawn("SWWMBulletImpact",d.HitLocation+hitnormal*0.01); - p.angle = atan2(hitnormal.y,hitnormal.x); - p.pitch = asin(-hitnormal.z); - if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); - let b = Spawn("ExplodiumBulletImpact",d.HitLocation+hitnormal*4.); - b.angle = atan2(hitnormal.y,hitnormal.x); - b.pitch = asin(-hitnormal.z); - b.target = self; - if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z); } for ( int i=0; i<6; i++ ) { @@ -283,7 +301,12 @@ Class ExplodiumGun : SWWMWeapon XZW1 A -1; Stop; Select: - XZW2 B 2 A_FullRaise(); + XZW2 B 2 + { + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + A_FullRaise(); + } XZW2 CDEFGH 2; Goto Ready; Ready: @@ -348,6 +371,8 @@ Class ExplodiumGun : SWWMWeapon Reload: XZW2 A 1 { + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; if ( invoker.clipcount >= invoker.default.clipcount ) return ResolveState("CheckBullet"); A_PlayerReload(); if ( invoker.clipcount <= 0 ) return ResolveState("ReloadEmpty"); @@ -455,6 +480,8 @@ Class DualExplodiumGun : SWWMWeapon double casex, casey, lcasex, lcasey; transient ui TextureID WeaponBox; transient ui Font TewiFont; + transient Actor deadeye_tgt; + transient int deadeye_cnt; Property ClipCount : ClipCount; @@ -552,6 +579,17 @@ Class DualExplodiumGun : SWWMWeapon SWWMBulletTrail.DoTrail(self,origin,dir,10000,2); if ( d.HitType == TRACE_HitActor ) { + if ( invoker.deadeye_tgt && (d.HitActor != invoker.deadeye_tgt) ) + { + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + } + else + { + invoker.deadeye_tgt = d.HitActor; + invoker.deadeye_cnt++; + SWWMUtility.AchievementProgress('swwm_progress_explogun',invoker.deadeye_cnt,player); + } int dmg = 15; // might as well apply explosion on top if ( dmg >= d.HitActor.Health ) dmg += 20; @@ -573,33 +611,38 @@ Class DualExplodiumGun : SWWMWeapon b.pitch = asin(d.HitDir.z); b.target = self; } - else if ( d.HitType != TRACE_HitNone ) + else { - Vector3 hitnormal = -d.HitDir; - if ( d.HitType == TRACE_HitFloor ) + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + if ( d.HitType != TRACE_HitNone ) { - if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal; - else hitnormal = d.HitSector.floorplane.Normal; + 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; + } + let p = Spawn("SWWMBulletImpact",d.HitLocation+hitnormal*0.01); + p.angle = atan2(hitnormal.y,hitnormal.x); + p.pitch = asin(-hitnormal.z); + if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); + let b = Spawn("ExplodiumBulletImpact",d.HitLocation+hitnormal*4.); + b.angle = atan2(hitnormal.y,hitnormal.x); + b.pitch = asin(-hitnormal.z); + b.target = self; + if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z); } - 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; - } - let p = Spawn("SWWMBulletImpact",d.HitLocation+hitnormal*0.01); - p.angle = atan2(hitnormal.y,hitnormal.x); - p.pitch = asin(-hitnormal.z); - if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation); - let b = Spawn("ExplodiumBulletImpact",d.HitLocation+hitnormal*4.); - b.angle = atan2(hitnormal.y,hitnormal.x); - b.pitch = asin(-hitnormal.z); - b.target = self; - if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z); } for ( int i=0; i<6; i++ ) { @@ -674,7 +717,12 @@ Class DualExplodiumGun : SWWMWeapon States { Select: - XZW2 B 2 A_FullRaise(); + XZW2 B 2 + { + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + A_FullRaise(); + } XZW2 C 2; XZW2 D 2 { player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftSelect")); } XZW2 EFGHAA 2; @@ -789,7 +837,12 @@ Class DualExplodiumGun : SWWMWeapon XZWB A 1; Goto LeftReady; Reload: - XZW2 A 9 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftLower")); } + XZW2 A 9 + { + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftLower")); + } XZW2 A 1 { A_PlayerReload(); @@ -845,7 +898,12 @@ Class DualExplodiumGun : SWWMWeapon XZW2 A 0 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftRaise")); } Goto Ready; LeftReload: - XZWB A 9 { player.SetPSPrite(PSP_WEAPON,ResolveState("Lower")); } + XZWB A 9 + { + invoker.deadeye_tgt = null; + invoker.deadeye_cnt = 0; + player.SetPSPrite(PSP_WEAPON,ResolveState("Lower")); + } XZWB A 1 { A_PlayerReload(); diff --git a/zscript/weapons/swwm_tastytreat_fx.zsc b/zscript/weapons/swwm_tastytreat_fx.zsc index 03d3fc20e..500fbaf51 100644 --- a/zscript/weapons/swwm_tastytreat_fx.zsc +++ b/zscript/weapons/swwm_tastytreat_fx.zsc @@ -56,6 +56,7 @@ Class HitListEntry Actor hitactor; Vector3 hitlocation, x; int hitdamage; + bool pastwall; // used by silver bullet } Class CandyBeamTracer : LineTracer @@ -536,6 +537,26 @@ Class CandyGunProj : Actor } void A_BlowUp() { + bool bossbrains = false; + for ( Actor a=CurSector.thinglist; a; a=a.snext ) + { + if ( a is 'BossBrain' ) + { + let ti = ThinkerIterator.Create('BossEye'); + if ( ti.Next() ) + { + bossbrains = true; + break; + } + } + else if ( a is 'BossEye' ) + { + bossbrains = true; + break; + } + } + if ( bossbrains && target ) + SWWMUtility.MarkAchievement('swwm_achievement_yeet',target.player); angle = atan2(cvel.y,cvel.x); pitch = asin(-cvel.z); bNOGRAVITY = true; diff --git a/zscript/weapons/swwm_thiccboolet.zsc b/zscript/weapons/swwm_thiccboolet.zsc index e1b1e068d..841d1b0fb 100644 --- a/zscript/weapons/swwm_thiccboolet.zsc +++ b/zscript/weapons/swwm_thiccboolet.zsc @@ -8,6 +8,7 @@ Class WallPenetrate Line hitline; Sector hitsector; F3DFloor hitffloor; + bool pastwall; } Class AuxiliarySilverBulletTracer : LineTracer @@ -27,6 +28,7 @@ Class AuxiliarySilverBulletTracer : LineTracer Class SilverBulletTracer : SpreadSlugTracer { + bool pastwall; Array WallPenetrateList; override ETraceStatus TraceCallback() @@ -55,6 +57,7 @@ Class SilverBulletTracer : SpreadSlugTracer ent.hitactor = Results.HitActor; ent.hitlocation = Results.HitPos; ent.x = Results.HitVector; + ent.pastwall = pastwall; if ( (Results.HitActor.Health >= int(penetration)) || Results.HitActor.bNODAMAGE ) { ent.hitdamage = int(penetration); @@ -94,7 +97,9 @@ Class SilverBulletTracer : SpreadSlugTracer wp.penetration = int(penetration); wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit(); if ( !Results.Side ) wp.hitnormal *= -1; + wp.pastwall = pastwall; WallPenetrateList.Push(wp); + pastwall = true; penetration = max(0,penetration-i*4); // trace backwards to find exit surface let at = new("AuxiliarySilverBulletTracer"); @@ -124,6 +129,7 @@ Class SilverBulletTracer : SpreadSlugTracer wp2.hitnormal = at.Results.HitSector.ceilingplane.Normal; else if ( at.Results.HitType == TRACE_HitFloor ) wp2.hitnormal = at.Results.HitSector.floorplane.Normal; + wp2.pastwall = pastwall; WallPenetrateList.Push(wp2); } return TRACE_Skip; @@ -162,7 +168,9 @@ Class SilverBulletTracer : SpreadSlugTracer wp.hitnormal = Results.HitSector.ceilingplane.Normal; else if ( Results.HitType == TRACE_HitFloor ) wp.hitnormal = Results.HitSector.floorplane.Normal; + wp.pastwall = pastwall; WallPenetrateList.Push(wp); + pastwall = true; penetration = max(0,penetration-i*4); // trace backwards to find exit surface let at = new("AuxiliarySilverBulletTracer"); @@ -192,6 +200,7 @@ Class SilverBulletTracer : SpreadSlugTracer wp2.hitnormal = at.Results.HitSector.ceilingplane.Normal; else if ( at.Results.HitType == TRACE_HitFloor ) wp2.hitnormal = at.Results.HitSector.floorplane.Normal; + wp2.pastwall = pastwall; WallPenetrateList.Push(wp2); } return TRACE_Skip; @@ -249,6 +258,7 @@ Class SilverBullet : SWWMWeapon ui int lastammo; int clipcount; double casex, casey; + int nkills; transient ui TextureID WeaponBox[2], ZoomBar, BulletIcon[2], AmmoIcon[2]; transient ui Font TewiFont; @@ -433,17 +443,24 @@ Class SilverBullet : SWWMWeapon } for ( int i=0; i 0 ) p.bAMBUSH = true; if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos); if ( swwm_omnibust ) BusterWall.Bust(t.Results,int(t.penetration),self,t.Results.HitVector,t.Results.HitPos.z); } - for ( int i=0; i 0 ) { - Vector3 ofs = level.Vec3Offset(origin,dir*i); - if ( !level.IsPointInLevel(ofs) ) continue; - let s = Spawn("SilverAirRip",ofs); + Vector3 start = origin; + Vector3 end = t.WallPenetrateList[0].hitpos; + Vector3 dir = level.Vec3Diff(start,end); + double dist = dir.length(); + dir /= dist; + for ( int i=0; i= t.WallPenetrateList.Size()-1 ) end = t.Results.HitPos; + else end = t.WallPenetrateList[i+1].hitpos; + dir = level.Vec3Diff(start,end); + dist = dir.length(); + for ( int j=0; j