From c5abe83831fd48c4f5a2f400825bfb8e0153ddfd Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Sat, 27 Feb 2021 23:59:40 +0100 Subject: [PATCH] Major code refactoring. SWWMHandler could still use some more, though. --- language.version | 4 +- modeldef.misc | 15 - zscript.txt | 124 +- zscript/{ => compat}/swwm_compat.zsc | 0 zscript/{ => compat}/swwm_hdoom.zsc | 0 zscript/{ => compat}/swwm_shame.zsc | 0 ...wwm_dlcammo.zsc => swwm_ammoitems_dlc.zsc} | 0 zscript/dlc1/swwm_blackfire_fx.zsc | 1 + zscript/dlc1/swwm_blastin_fx.zsc | 1 + zscript/dlc1/swwm_hammertime_fx.zsc | 1 + zscript/dlc1/swwm_heavymahsheengun_fx.zsc | 1 + zscript/dlc1/swwm_hugeassrailgun_fx.zsc | 1 + zscript/dlc1/swwm_notashotgun_fx.zsc | 1 + zscript/dlc1/swwm_rebolber_fx.zsc | 1 + zscript/dlc1/swwm_supermarioworld_fx.zsc | 1 + zscript/dlc1/swwm_thiccbolts_fx.zsc | 1 + zscript/dlc1/swwm_veryveryfrightening_fx.zsc | 1 + zscript/handler/swwm_handler_cheats.zsc | 428 +++ zscript/handler/swwm_handler_flash.zsc | 70 + zscript/handler/swwm_handler_iwantdie.zsc | 82 + zscript/handler/swwm_handler_oneliners.zsc | 82 + zscript/handler/swwm_handler_playerevents.zsc | 144 + zscript/handler/swwm_handler_process.zsc | 602 ++++ zscript/handler/swwm_handler_queues.zsc | 195 ++ zscript/handler/swwm_handler_replacements.zsc | 536 +++ zscript/handler/swwm_handler_shaders.zsc | 146 + zscript/handler/swwm_handler_vanillaboss.zsc | 399 +++ zscript/{ => hud}/swwm_hud.zsc | 0 zscript/{ => hud}/swwm_hudextra.zsc | 0 zscript/items/swwm_ammoextra.zsc | 501 +++ zscript/items/swwm_ammoitems.zsc | 1040 ++++++ zscript/{ => items}/swwm_armor.zsc | 0 zscript/items/swwm_baseammo.zsc | 377 +++ zscript/items/swwm_basearmor.zsc | 185 + zscript/items/swwm_basehealth.zsc | 160 + zscript/items/swwm_baseitem.zsc | 88 + zscript/items/swwm_collectibles.zsc | 243 ++ zscript/items/swwm_collectibles_gesture.zsc | 12 + zscript/{ => items}/swwm_funstuff.zsc | 364 -- zscript/{ => items}/swwm_health.zsc | 0 zscript/{ => items}/swwm_keys.zsc | 141 - zscript/items/swwm_keys_gesture.zsc | 140 + .../swwm_powerups.zsc} | 0 zscript/{ => kbase}/swwm_kbase.zsc | 0 zscript/{ => menu}/swwm_credits.zsc | 0 zscript/{ => menu}/swwm_help.zsc | 0 zscript/{ => menu}/swwm_inter.zsc | 0 zscript/{ => menu}/swwm_menus.zsc | 0 zscript/{ => menu}/swwm_title.zsc | 0 zscript/swwm_ammo.zsc | 1921 ----------- zscript/swwm_common.zsc | 1574 +-------- zscript/swwm_common_fx.zsc | 1277 +++++++ zscript/swwm_gesture.zsc | 636 ++++ zscript/swwm_gesture_fx.zsc | 469 +++ zscript/swwm_handler.zsc | 3009 +---------------- zscript/swwm_inventory.zsc | 1684 --------- zscript/swwm_onfire.zsc | 293 ++ zscript/swwm_player.zsc | 1498 -------- zscript/swwm_player_fx.zsc | 335 ++ zscript/swwm_player_items.zsc | 172 + zscript/swwm_statichandler.zsc | 223 ++ zscript/swwm_thinkers.zsc | 1142 ------- zscript/swwm_thinkers_hud.zsc | 572 ++++ zscript/swwm_thinkers_player.zsc | 573 ++++ zscript/{ => utility}/swwm_coordutil.zsc | 0 zscript/{ => utility}/swwm_quaternion.zsc | 0 zscript/{ => utility}/swwm_utility.zsc | 154 +- zscript/weapons/swwm_baseweapon.zsc | 390 +++ zscript/weapons/swwm_baseweapon_fx.zsc | 291 ++ zscript/weapons/swwm_baseweapon_melee.zsc | 440 +++ .../weapons/swwm_baseweapon_precisechair.zsc | 138 + zscript/weapons/swwm_blazeit.zsc | 890 +++++ .../swwm_blazeit_fx.zsc} | 891 +---- zscript/{ => weapons}/swwm_cbt.zsc | 822 ----- zscript/weapons/swwm_cbt_fx.zsc | 456 +++ zscript/weapons/swwm_cbt_ui.zsc | 368 ++ zscript/weapons/swwm_danmaku.zsc | 505 +++ .../swwm_danmaku_fx.zsc} | 506 +-- zscript/weapons/swwm_deathlydeathcannon.zsc | 544 +++ .../swwm_deathlydeathcannon_fx.zsc} | 545 +-- zscript/{ => weapons}/swwm_deepdarkimpact.zsc | 173 - zscript/weapons/swwm_deepdarkimpact_fx.zsc | 174 + zscript/{ => weapons}/swwm_jackhammer.zsc | 370 -- zscript/weapons/swwm_jackhammer_fx.zsc | 371 ++ zscript/weapons/swwm_shot.zsc | 1184 +++++++ .../swwm_shot_fx.zsc} | 1185 +------ zscript/weapons/swwm_sparkyboi.zsc | 552 +++ .../swwm_sparkyboi_fx.zsc} | 553 +-- zscript/{ => weapons}/swwm_splode.zsc | 295 -- zscript/weapons/swwm_splode_fx.zsc | 296 ++ zscript/weapons/swwm_tastytreat.zsc | 570 ++++ .../swwm_tastytreat_fx.zsc} | 571 +--- zscript/{ => weapons}/swwm_thiccboolet.zsc | 401 --- zscript/weapons/swwm_thiccboolet_fx.zsc | 402 +++ 94 files changed, 17842 insertions(+), 17661 deletions(-) rename zscript/{ => compat}/swwm_compat.zsc (100%) rename zscript/{ => compat}/swwm_hdoom.zsc (100%) rename zscript/{ => compat}/swwm_shame.zsc (100%) rename zscript/dlc1/{swwm_dlcammo.zsc => swwm_ammoitems_dlc.zsc} (100%) create mode 100644 zscript/dlc1/swwm_blackfire_fx.zsc create mode 100644 zscript/dlc1/swwm_blastin_fx.zsc create mode 100644 zscript/dlc1/swwm_hammertime_fx.zsc create mode 100644 zscript/dlc1/swwm_heavymahsheengun_fx.zsc create mode 100644 zscript/dlc1/swwm_hugeassrailgun_fx.zsc create mode 100644 zscript/dlc1/swwm_notashotgun_fx.zsc create mode 100644 zscript/dlc1/swwm_rebolber_fx.zsc create mode 100644 zscript/dlc1/swwm_supermarioworld_fx.zsc create mode 100644 zscript/dlc1/swwm_thiccbolts_fx.zsc create mode 100644 zscript/dlc1/swwm_veryveryfrightening_fx.zsc create mode 100644 zscript/handler/swwm_handler_cheats.zsc create mode 100644 zscript/handler/swwm_handler_flash.zsc create mode 100644 zscript/handler/swwm_handler_iwantdie.zsc create mode 100644 zscript/handler/swwm_handler_oneliners.zsc create mode 100644 zscript/handler/swwm_handler_playerevents.zsc create mode 100644 zscript/handler/swwm_handler_process.zsc create mode 100644 zscript/handler/swwm_handler_queues.zsc create mode 100644 zscript/handler/swwm_handler_replacements.zsc create mode 100644 zscript/handler/swwm_handler_shaders.zsc create mode 100644 zscript/handler/swwm_handler_vanillaboss.zsc rename zscript/{ => hud}/swwm_hud.zsc (100%) rename zscript/{ => hud}/swwm_hudextra.zsc (100%) create mode 100644 zscript/items/swwm_ammoextra.zsc create mode 100644 zscript/items/swwm_ammoitems.zsc rename zscript/{ => items}/swwm_armor.zsc (100%) create mode 100644 zscript/items/swwm_baseammo.zsc create mode 100644 zscript/items/swwm_basearmor.zsc create mode 100644 zscript/items/swwm_basehealth.zsc create mode 100644 zscript/items/swwm_baseitem.zsc create mode 100644 zscript/items/swwm_collectibles.zsc create mode 100644 zscript/items/swwm_collectibles_gesture.zsc rename zscript/{ => items}/swwm_funstuff.zsc (77%) rename zscript/{ => items}/swwm_health.zsc (100%) rename zscript/{ => items}/swwm_keys.zsc (59%) create mode 100644 zscript/items/swwm_keys_gesture.zsc rename zscript/{swwm_powerup.zsc => items/swwm_powerups.zsc} (100%) rename zscript/{ => kbase}/swwm_kbase.zsc (100%) rename zscript/{ => menu}/swwm_credits.zsc (100%) rename zscript/{ => menu}/swwm_help.zsc (100%) rename zscript/{ => menu}/swwm_inter.zsc (100%) rename zscript/{ => menu}/swwm_menus.zsc (100%) rename zscript/{ => menu}/swwm_title.zsc (100%) delete mode 100644 zscript/swwm_ammo.zsc create mode 100644 zscript/swwm_common_fx.zsc create mode 100644 zscript/swwm_gesture.zsc create mode 100644 zscript/swwm_gesture_fx.zsc delete mode 100644 zscript/swwm_inventory.zsc create mode 100644 zscript/swwm_onfire.zsc create mode 100644 zscript/swwm_player_fx.zsc create mode 100644 zscript/swwm_player_items.zsc create mode 100644 zscript/swwm_statichandler.zsc create mode 100644 zscript/swwm_thinkers_hud.zsc create mode 100644 zscript/swwm_thinkers_player.zsc rename zscript/{ => utility}/swwm_coordutil.zsc (100%) rename zscript/{ => utility}/swwm_quaternion.zsc (100%) rename zscript/{ => utility}/swwm_utility.zsc (91%) create mode 100644 zscript/weapons/swwm_baseweapon.zsc create mode 100644 zscript/weapons/swwm_baseweapon_fx.zsc create mode 100644 zscript/weapons/swwm_baseweapon_melee.zsc create mode 100644 zscript/weapons/swwm_baseweapon_precisechair.zsc create mode 100644 zscript/weapons/swwm_blazeit.zsc rename zscript/{swwm_blazeit.zsc => weapons/swwm_blazeit_fx.zsc} (56%) rename zscript/{ => weapons}/swwm_cbt.zsc (61%) create mode 100644 zscript/weapons/swwm_cbt_fx.zsc create mode 100644 zscript/weapons/swwm_cbt_ui.zsc create mode 100644 zscript/weapons/swwm_danmaku.zsc rename zscript/{swwm_danmaku.zsc => weapons/swwm_danmaku_fx.zsc} (56%) create mode 100644 zscript/weapons/swwm_deathlydeathcannon.zsc rename zscript/{swwm_deathlydeathcannon.zsc => weapons/swwm_deathlydeathcannon_fx.zsc} (80%) rename zscript/{ => weapons}/swwm_deepdarkimpact.zsc (74%) create mode 100644 zscript/weapons/swwm_deepdarkimpact_fx.zsc rename zscript/{ => weapons}/swwm_jackhammer.zsc (52%) create mode 100644 zscript/weapons/swwm_jackhammer_fx.zsc create mode 100644 zscript/weapons/swwm_shot.zsc rename zscript/{swwm_shot.zsc => weapons/swwm_shot_fx.zsc} (56%) create mode 100644 zscript/weapons/swwm_sparkyboi.zsc rename zscript/{swwm_sparkyboi.zsc => weapons/swwm_sparkyboi_fx.zsc} (76%) rename zscript/{ => weapons}/swwm_splode.zsc (82%) create mode 100644 zscript/weapons/swwm_splode_fx.zsc create mode 100644 zscript/weapons/swwm_tastytreat.zsc rename zscript/{swwm_tastytreat.zsc => weapons/swwm_tastytreat_fx.zsc} (52%) rename zscript/{ => weapons}/swwm_thiccboolet.zsc (84%) create mode 100644 zscript/weapons/swwm_thiccboolet_fx.zsc diff --git a/language.version b/language.version index cff83604f..fe2ae8f34 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r313 \cu(Sat 27 Feb 17:49:58 CET 2021)\c-"; -SWWM_SHORTVER="\cw0.9.11b-pre r313 \cu(2021-02-27 17:49:58)\c-"; +SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r314 \cu(Sat 27 Feb 23:59:40 CET 2021)\c-"; +SWWM_SHORTVER="\cw0.9.11b-pre r314 \cu(2021-02-27 23:59:40)\c-"; diff --git a/modeldef.misc b/modeldef.misc index 31ae7234f..cc32625cf 100644 --- a/modeldef.misc +++ b/modeldef.misc @@ -93,21 +93,6 @@ Model "SWWMChip" FrameIndex XZW2 H 0 7 } -Model "ReflectedBullet" -{ - Path "models" - - Model 0 "Boolet_d.3d" - Skin 0 "Boolet.png" - Scale 0.008 0.008 0.008 - USEACTORPITCH - USEACTORROLL - AngleOffset -90 - ZOffset 1 - - FrameIndex XZW1 A 0 0 -} - Model "mkBloodDrop" { Path "models/extra" diff --git a/zscript.txt b/zscript.txt index 72acacfbc..9f5ba1be3 100644 --- a/zscript.txt +++ b/zscript.txt @@ -12,57 +12,115 @@ version "4.5" #include "zscript/swwm_libeye/projector gl.txt" #include "zscript/swwm_libeye/projector planar.txt" #include "zscript/swwm_libeye/viewport.txt" -#include "zscript/swwm_coordutil.zsc" -#include "zscript/swwm_quaternion.zsc" +#include "zscript/utility/swwm_coordutil.zsc" +#include "zscript/utility/swwm_quaternion.zsc" +#include "zscript/utility/swwm_utility.zsc" // base code #include "zscript/swwm_common.zsc" -#include "zscript/swwm_utility.zsc" +#include "zscript/swwm_common_fx.zsc" #include "zscript/swwm_handler.zsc" -#include "zscript/swwm_shame.zsc" -#include "zscript/swwm_hdoom.zsc" -#include "zscript/swwm_compat.zsc" +#include "zscript/swwm_statichandler.zsc" #include "zscript/swwm_thinkers.zsc" +#include "zscript/swwm_thinkers_hud.zsc" +#include "zscript/swwm_thinkers_player.zsc" #include "zscript/swwm_player.zsc" -#include "zscript/swwm_inventory.zsc" -#include "zscript/swwm_hud.zsc" -#include "zscript/swwm_hudextra.zsc" -#include "zscript/swwm_kbase.zsc" -#include "zscript/swwm_menus.zsc" -#include "zscript/swwm_title.zsc" -#include "zscript/swwm_inter.zsc" +#include "zscript/swwm_player_fx.zsc" +#include "zscript/swwm_player_items.zsc" +#include "zscript/swwm_gesture.zsc" +#include "zscript/swwm_gesture_fx.zsc" #include "zscript/swwm_blod.zsc" -#include "zscript/swwm_help.zsc" -#include "zscript/swwm_credits.zsc" +#include "zscript/swwm_onfire.zsc" +// handler code +#include "zscript/handler/swwm_handler_cheats.zsc" +#include "zscript/handler/swwm_handler_flash.zsc" +#include "zscript/handler/swwm_handler_iwantdie.zsc" +#include "zscript/handler/swwm_handler_oneliners.zsc" +#include "zscript/handler/swwm_handler_playerevents.zsc" +#include "zscript/handler/swwm_handler_process.zsc" +#include "zscript/handler/swwm_handler_queues.zsc" +#include "zscript/handler/swwm_handler_replacements.zsc" +#include "zscript/handler/swwm_handler_shaders.zsc" +#include "zscript/handler/swwm_handler_vanillaboss.zsc" +// menu code +#include "zscript/menu/swwm_menus.zsc" +#include "zscript/menu/swwm_title.zsc" +#include "zscript/menu/swwm_inter.zsc" +#include "zscript/menu/swwm_help.zsc" +#include "zscript/menu/swwm_credits.zsc" +// compat code +#include "zscript/compat/swwm_compat.zsc" +#include "zscript/compat/swwm_shame.zsc" +#include "zscript/compat/swwm_hdoom.zsc" +// hud +#include "zscript/hud/swwm_hud.zsc" +#include "zscript/hud/swwm_hudextra.zsc" +// kbase +#include "zscript/kbase/swwm_kbase.zsc" // items -#include "zscript/swwm_health.zsc" -#include "zscript/swwm_armor.zsc" -#include "zscript/swwm_powerup.zsc" -#include "zscript/swwm_ammo.zsc" -#include "zscript/swwm_jackhammer.zsc" -#include "zscript/swwm_deepdarkimpact.zsc" -#include "zscript/swwm_splode.zsc" -#include "zscript/swwm_shot.zsc" -#include "zscript/swwm_cbt.zsc" -#include "zscript/swwm_danmaku.zsc" -#include "zscript/swwm_blazeit.zsc" -#include "zscript/swwm_sparkyboi.zsc" -#include "zscript/swwm_thiccboolet.zsc" -#include "zscript/swwm_tastytreat.zsc" -#include "zscript/swwm_deathlydeathcannon.zsc" -#include "zscript/swwm_funstuff.zsc" -#include "zscript/swwm_keys.zsc" +#include "zscript/items/swwm_baseitem.zsc" +#include "zscript/items/swwm_basehealth.zsc" +#include "zscript/items/swwm_basearmor.zsc" +#include "zscript/items/swwm_baseammo.zsc" +#include "zscript/items/swwm_health.zsc" +#include "zscript/items/swwm_armor.zsc" +#include "zscript/items/swwm_powerups.zsc" +#include "zscript/items/swwm_ammoitems.zsc" +#include "zscript/items/swwm_ammoextra.zsc" +#include "zscript/items/swwm_funstuff.zsc" +#include "zscript/items/swwm_collectibles.zsc" +#include "zscript/items/swwm_collectibles_gesture.zsc" +#include "zscript/items/swwm_keys.zsc" +#include "zscript/items/swwm_keys_gesture.zsc" +// weapons +#include "zscript/weapons/swwm_baseweapon.zsc" +#include "zscript/weapons/swwm_baseweapon_fx.zsc" +#include "zscript/weapons/swwm_baseweapon_melee.zsc" +#include "zscript/weapons/swwm_baseweapon_precisechair.zsc" +#include "zscript/weapons/swwm_jackhammer.zsc" +#include "zscript/weapons/swwm_jackhammer_fx.zsc" +#include "zscript/weapons/swwm_deepdarkimpact.zsc" +#include "zscript/weapons/swwm_deepdarkimpact_fx.zsc" +#include "zscript/weapons/swwm_splode.zsc" +#include "zscript/weapons/swwm_splode_fx.zsc" +#include "zscript/weapons/swwm_shot.zsc" +#include "zscript/weapons/swwm_shot_fx.zsc" +#include "zscript/weapons/swwm_cbt.zsc" +#include "zscript/weapons/swwm_cbt_fx.zsc" +#include "zscript/weapons/swwm_cbt_ui.zsc" +#include "zscript/weapons/swwm_danmaku.zsc" +#include "zscript/weapons/swwm_danmaku_fx.zsc" +#include "zscript/weapons/swwm_blazeit.zsc" +#include "zscript/weapons/swwm_blazeit_fx.zsc" +#include "zscript/weapons/swwm_sparkyboi.zsc" +#include "zscript/weapons/swwm_sparkyboi_fx.zsc" +#include "zscript/weapons/swwm_thiccboolet.zsc" +#include "zscript/weapons/swwm_thiccboolet_fx.zsc" +#include "zscript/weapons/swwm_tastytreat.zsc" +#include "zscript/weapons/swwm_tastytreat_fx.zsc" +#include "zscript/weapons/swwm_deathlydeathcannon.zsc" +#include "zscript/weapons/swwm_deathlydeathcannon_fx.zsc" // DLC1 - Weapon Set -#include "zscript/dlc1/swwm_dlcammo.zsc" +#include "zscript/dlc1/swwm_ammoitems_dlc.zsc" #include "zscript/dlc1/swwm_hammertime.zsc" +#include "zscript/dlc1/swwm_hammertime_fx.zsc" #include "zscript/dlc1/swwm_blastin.zsc" +#include "zscript/dlc1/swwm_blastin_fx.zsc" #include "zscript/dlc1/swwm_rebolber.zsc" +#include "zscript/dlc1/swwm_rebolber_fx.zsc" #include "zscript/dlc1/swwm_supermarioworld.zsc" +#include "zscript/dlc1/swwm_supermarioworld_fx.zsc" #include "zscript/dlc1/swwm_heavymahsheengun.zsc" +#include "zscript/dlc1/swwm_heavymahsheengun_fx.zsc" #include "zscript/dlc1/swwm_notashotgun.zsc" +#include "zscript/dlc1/swwm_notashotgun_fx.zsc" #include "zscript/dlc1/swwm_blackfire.zsc" +#include "zscript/dlc1/swwm_blackfire_fx.zsc" #include "zscript/dlc1/swwm_veryveryfrightening.zsc" +#include "zscript/dlc1/swwm_veryveryfrightening_fx.zsc" #include "zscript/dlc1/swwm_thiccbolts.zsc" +#include "zscript/dlc1/swwm_thiccbolts_fx.zsc" #include "zscript/dlc1/swwm_hugeassrailgun.zsc" +#include "zscript/dlc1/swwm_hugeassrailgun_fx.zsc" // DLC2 - Game Set #include "zscript/dlc2/swwm_tetris.zsc" #include "zscript/dlc2/swwm_pong.zsc" diff --git a/zscript/swwm_compat.zsc b/zscript/compat/swwm_compat.zsc similarity index 100% rename from zscript/swwm_compat.zsc rename to zscript/compat/swwm_compat.zsc diff --git a/zscript/swwm_hdoom.zsc b/zscript/compat/swwm_hdoom.zsc similarity index 100% rename from zscript/swwm_hdoom.zsc rename to zscript/compat/swwm_hdoom.zsc diff --git a/zscript/swwm_shame.zsc b/zscript/compat/swwm_shame.zsc similarity index 100% rename from zscript/swwm_shame.zsc rename to zscript/compat/swwm_shame.zsc diff --git a/zscript/dlc1/swwm_dlcammo.zsc b/zscript/dlc1/swwm_ammoitems_dlc.zsc similarity index 100% rename from zscript/dlc1/swwm_dlcammo.zsc rename to zscript/dlc1/swwm_ammoitems_dlc.zsc diff --git a/zscript/dlc1/swwm_blackfire_fx.zsc b/zscript/dlc1/swwm_blackfire_fx.zsc new file mode 100644 index 000000000..761c98c83 --- /dev/null +++ b/zscript/dlc1/swwm_blackfire_fx.zsc @@ -0,0 +1 @@ +// Blackfire Igniter projectiles and effects diff --git a/zscript/dlc1/swwm_blastin_fx.zsc b/zscript/dlc1/swwm_blastin_fx.zsc new file mode 100644 index 000000000..f8ebd6dc2 --- /dev/null +++ b/zscript/dlc1/swwm_blastin_fx.zsc @@ -0,0 +1 @@ +// Plasma Blast projectiles and effects diff --git a/zscript/dlc1/swwm_hammertime_fx.zsc b/zscript/dlc1/swwm_hammertime_fx.zsc new file mode 100644 index 000000000..5727340da --- /dev/null +++ b/zscript/dlc1/swwm_hammertime_fx.zsc @@ -0,0 +1 @@ +// Itamex Hammer projectiles and effects diff --git a/zscript/dlc1/swwm_heavymahsheengun_fx.zsc b/zscript/dlc1/swwm_heavymahsheengun_fx.zsc new file mode 100644 index 000000000..1150517c2 --- /dev/null +++ b/zscript/dlc1/swwm_heavymahsheengun_fx.zsc @@ -0,0 +1 @@ +// Sheen HMG projectiles and effects diff --git a/zscript/dlc1/swwm_hugeassrailgun_fx.zsc b/zscript/dlc1/swwm_hugeassrailgun_fx.zsc new file mode 100644 index 000000000..54d0c3e74 --- /dev/null +++ b/zscript/dlc1/swwm_hugeassrailgun_fx.zsc @@ -0,0 +1 @@ +// Grand Lance projectiles and effects diff --git a/zscript/dlc1/swwm_notashotgun_fx.zsc b/zscript/dlc1/swwm_notashotgun_fx.zsc new file mode 100644 index 000000000..bb4f05cbd --- /dev/null +++ b/zscript/dlc1/swwm_notashotgun_fx.zsc @@ -0,0 +1 @@ +// Quadravol projectiles and effects diff --git a/zscript/dlc1/swwm_rebolber_fx.zsc b/zscript/dlc1/swwm_rebolber_fx.zsc new file mode 100644 index 000000000..dbd2ab597 --- /dev/null +++ b/zscript/dlc1/swwm_rebolber_fx.zsc @@ -0,0 +1 @@ +// Puntzer Beta projectiles and effects diff --git a/zscript/dlc1/swwm_supermarioworld_fx.zsc b/zscript/dlc1/swwm_supermarioworld_fx.zsc new file mode 100644 index 000000000..d3487b930 --- /dev/null +++ b/zscript/dlc1/swwm_supermarioworld_fx.zsc @@ -0,0 +1 @@ +// Puntzer Gamma projectiles and effects diff --git a/zscript/dlc1/swwm_thiccbolts_fx.zsc b/zscript/dlc1/swwm_thiccbolts_fx.zsc new file mode 100644 index 000000000..f00f7522e --- /dev/null +++ b/zscript/dlc1/swwm_thiccbolts_fx.zsc @@ -0,0 +1 @@ +// Ray-Khom projectiles and effects diff --git a/zscript/dlc1/swwm_veryveryfrightening_fx.zsc b/zscript/dlc1/swwm_veryveryfrightening_fx.zsc new file mode 100644 index 000000000..90771b29b --- /dev/null +++ b/zscript/dlc1/swwm_veryveryfrightening_fx.zsc @@ -0,0 +1 @@ +// EMP Rail Carbine projectiles and effects diff --git a/zscript/handler/swwm_handler_cheats.zsc b/zscript/handler/swwm_handler_cheats.zsc new file mode 100644 index 000000000..bd8806eb8 --- /dev/null +++ b/zscript/handler/swwm_handler_cheats.zsc @@ -0,0 +1,428 @@ +// cheatsydoodleing + +extend Class SWWMHandler +{ + transient ui int kcode, klinger; + transient ui String kstr, klingerstr; + transient ui bool kfail; + transient ui int rss; + + private void CheatEvent( ConsoleEvent e ) + { + if ( e.Name ~== "swwmmoneycheat" ) + { + // what's that spell? + // loadsamoney! ... probably + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyLOADSAMONEY!\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/emone",CHAN_VOICE,CHANF_UI); + } + 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)); + } + else if ( e.Name ~== "swwmlorecheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyKNOWLEDGE!\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/lamborghini",CHAN_VOICE,CHANF_UI); + } + // look up all lore files + for ( int l=0; l)(AllActorClasses[i]); + if ( !w || (w == 'SWWMWeapon') ) continue; + let def = GetDefaultByType(w); + if ( def.bCHEATNOTWEAPON ) continue; + let ow = players[e.Args[0]].mo.FindInventory(w); + if ( ow && (ow.Amount >= ow.MaxAmount) ) continue; + if ( ow ) ow.Amount = ow.MaxAmount; + else players[e.Args[0]].mo.GiveInventory(w,def.MaxAmount); + } + } + else if ( e.Name ~== "swwmhealcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyRemember to stay fit.\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/health_pkup",CHAN_VOICE,CHANF_UI); + } + players[e.Args[0]].health = players[e.Args[0]].mo.health = 1000; + } + else if ( e.Name ~== "swwmynykroncheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyYou're still crazy.\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/w_pkup",CHAN_VOICE,CHANF_UI); + } + if ( players[e.Args[0]].mo.FindInventory("Ynykron") ) + players[e.Args[0]].mo.GiveInventory("YnykronAmmo",1); + else players[e.Args[0]].mo.GiveInventory("Ynykron",1); + } + else if ( e.Name ~== "swwmgravcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyGot something floatier.\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI); + } + let g = GravityPower(players[e.Args[0]].mo.FindInventory("GravityPower")); + if ( g ) g.EffectTics += g.default.EffectTics; + else players[e.Args[0]].mo.GiveInventory("GravityPower",1); + } + else if ( e.Name ~== "swwminvischeat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyProbably because you're invisible.\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI); + } + let g = GhostPower(players[e.Args[0]].mo.FindInventory("GhostPower")); + if ( g ) g.EffectTics += g.default.EffectTics; + else players[e.Args[0]].mo.GiveInventory("GhostPower",1); + } + else if ( e.Name ~== "swwmbarriercheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cySafe from those pesky elements.\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI); + } + let b = BarrierPower(players[e.Args[0]].mo.FindInventory("BarrierPower")); + if ( b ) b.EffectTics += b.default.EffectTics; + else players[e.Args[0]].mo.GiveInventory("BarrierPower",1); + } + else if ( e.Name ~== "swwmammocheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyDon't squander it.\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("misc/ammo_pkup",CHAN_VOICE,CHANF_UI); + } + players[e.Args[0]].mo.GiveInventory("HammerspaceEmbiggener",8,true); + for ( Inventory i=players[e.Args[0]].mo.inv; i; i=i.inv ) + { + if ( !(i is 'Ammo') ) continue; + i.Amount = i.MaxAmount; + } + } + else if ( e.Name ~== "swwmbloodcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyEdgy...\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmexplocheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyThat cheat's not needed anymore.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmallcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyStill as wrappy as it's always been.\c-"); + S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("fabricator/use",CHAN_VOICE,CHANF_UI); + } + players[e.Args[0]].mo.CheatGive("all",0); + players[e.Args[0]].health = players[e.Args[0]].mo.health = 1000; + } + else if ( e.Name ~== "swwmflagcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyThere are no flags here.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmballcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cy\"Balls on your head\"? What was I even thinking...\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmsmartcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cySkittles are better anyway.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmnutcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyI'm way past that, it was bad for my health.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmweeniecheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyAlways has been.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmpunishcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyThis is a bulli free zone.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmball2cheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cy\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmfartcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyI'd rather not reimplement that one.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmsupercheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyNo, you're the Demolitionist.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmstonecheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyThe pinnacle of... wait, I misread that.\c-"); + S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); + } + } + else if ( e.Name ~== "swwmfroggycheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cdHop!\c-"); + S_StartSound("misc/buyinv",CHAN_ITEM,CHANF_UI); + } + let mo = players[e.Args[0]].mo; + Actor f = Actor(ThinkerIterator.Create("FroggyChair").Next()); + if ( !f ) f = mo.Spawn("FroggyChair"); + f.SetOrigin(mo.Vec2OffsetZ(cos(mo.angle)*40.,sin(mo.angle)*40.,mo.player.viewz-32.),false); + f.A_SetAngle(f.AngleTo(mo)); + f.Spawn("SWWMItemFog",f.pos); + f.A_StartSound("bestsound",CHAN_ITEMEXTRA); + } + else if ( e.Name ~== "swwmamnesiacheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyAmnesiacs administered.\c-"); + S_StartSound("misc/buyinv",CHAN_ITEM,CHANF_UI); + S_StartSound("bestsound",CHAN_VOICE,CHANF_UI); + } + let ti = ThinkerIterator.Create("Actor"); + Actor a; + while ( a = Actor(ti.Next()) ) + { + if ( !a.bIsMonster || a.player ) continue; + a.A_ClearTarget(); + } + } + else if ( e.Name ~== "swwmjanitorcheat" ) + { + if ( consoleplayer == e.Args[0] ) + { + Console.Printf("\cyLet's mop up that big mess over there.\c-"); + S_StartSound("misc/buyinv",CHAN_ITEM,CHANF_UI); + } + let cc = SWWMCorpseCleaner(ThinkerIterator.Create("SWWMCorpseCleaner",Thinker.STAT_USER).Next()); + if ( !cc ) + { + cc = new("SWWMCorpseCleaner"); + cc.ChangeStatNum(Thinker.STAT_USER); + cc.Init(players[e.Args[0]].mo); + } + else cc.Init(players[e.Args[0]].mo); + } + } + + private ui bool CheatInput( InputEvent e ) + { + // cheat code handling + String cht[] = + { + "swwmlodsofemone", "swwmdeeplore", "swwmfroggygang", "swwmforgetaboutit", + "swwmmisterproper", + // SWWM Platinum cheats + "swwmimstuck", "swwmarmojumbo", "swwmdangimhealthy", + "swwmwarriorofzaemonath", "swwmpowerparp", "swwmcannotseemyhands", + "swwmreflectonme", "swwmgunzmeneeds", "swwmbloodrainsfromheaven", + "swwmnotwannaboom", "swwmverywrappyoatmeal", "swwmflaggerybingo", + "swwmheadsball", "swwmsmarties", "swwmnocilla", + "swwmmarioisaweenie", "swwmpunish", "swwmboingball", + "swwmgassy", "swwmiamsuperman", "swwmtouchstone" + }; + String cmd[] = + { + "swwmmoneycheat", "swwmlorecheat", "swwmfroggycheat", "swwmamnesiacheat", + "swwmjanitorcheat", + // SWWM Platinum cheats + "swwmsafecheat", "swwmweaponcheat", "swwmhealcheat", + "swwmynykroncheat", "swwmgravcheat", "swwminvischeat", + "swwmbarriercheat", "swwmammocheat", "swwmbloodcheat", + "swwmexplocheat", "swwmallcheat", "swwmflagcheat", + "swwmballcheat", "swwmsmartcheat", "swwmnutcheat", + "swwmweeniecheat", "swwmpunishcheat", "swwmball2cheat", + "swwmfartcheat", "swwmsupercheat", "swwmstonecheat" + }; + bool matchany = false; + kstr.AppendCharacter(e.KeyChar); + if ( kstr.Length() > 0 ) + { + if ( kcode >= 4 ) + S_StartSound("misc/boink",CHAN_WEAPON,CHANF_UI|CHANF_OVERLAP,pitch:FRandom[HudStuff](.8,1.2)); + for ( int i=0; i= 4 ) + { + kfail = true; + klinger = gametic+40; + klingerstr = kstr; + S_StartSound("bruh",CHAN_VOICE,CHANF_UI); + eatit = true; + } + kcode = 0; + kstr = ""; + if ( eatit ) return true; + } + else + { + kcode++; + if ( kcode > 4 ) return true; // eat keypresses from this point + } + } + return false; + } + + private ui int GetUIRandom() + { + return (rss = (rss<<1)*35447+(rss/87)); + } + + private ui double RandomShiver() + { + int sd = GetUIRandom(); + return ((abs(sd)%11)-5)*.1; + } + + private ui int RandomFall() + { + int sd = GetUIRandom(); + return ((abs(sd)%22)+10); + } + + private ui void CheatOverlay( RenderEvent e ) + { + // cheat input + if ( (kcode <= 4) && ((klinger < gametic) || (klingerstr == "")) ) + return; + double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/266.)),1.); + Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; + String chstr = (kcode>4)?kstr.Mid(4):klingerstr.Mid(4); + double alph = clamp((klinger-(gametic+e.fractic))/20.,0.,1.); + double shine = clamp((klinger-(gametic+e.fractic+40))/20.,0.,1.); + int col = (kcode>4)?0:(kfail)?2:1; + int tlen = chstr.CodePointCount(); + let fnt = newsmallfont; + int width = fnt.StringWidth(chstr)+(tlen-1); + int xx = int((ss.x-width)/2.); + int yy = int((ss.y-newsmallfont.GetHeight())/2.); + rss = (kcode>4)?gametic:klinger; + for ( int i=0, pos=0; i flashes; + // heal/armor flashes need to be handled here so they don't stack + transient int hflash[MAXPLAYERS], aflash[MAXPLAYERS]; + + static void HealthFlash( int p ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd || (p == -1) ) return; + hnd.hflash[p] = gametic+5; + } + + static void ArmorFlash( int p ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd || (p == -1) ) return; + hnd.aflash[p] = gametic+5; + } + + static void DoFlash( Actor camera, Color c, int duration ) + { + // don't flash when paused + if ( menuactive && (menuactive != Menu.OnNoPause) ) return; + QueuedFlash qf = new("QueuedFlash"); + qf.duration = duration; + qf.c = c; + qf.tic = gametic; + qf.cam = camera; + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return; // not supposed to happen + hnd.flashes.push(qf); + } + + private void FlashTick() + { + for ( int i=0; i= gametic ) continue; + flashes.Delete(i); + i--; + } + } + private ui void FlashUITick() + { + for ( int i=0; i 1) && (Owner.tics > max(1,Owner.CurState.tics/2)) ) + Owner.tics = max(1,Owner.CurState.tics/2); + } +} + +extend Class SWWMHandler +{ + private void IWantDieSpawn( WorldEvent e ) + { + if ( iwantdie == -1 ) iwantdie = (G_SkillName() == StringTable.Localize("$SWWM_SKLUNATIC")); + if ( iwantdie ) + { + if ( e.Thing.bMISSILE && !e.Thing.FindInventory("DontDuplicate") && !e.Thing.IsZeroDamage() && (e.Thing.target && e.Thing.target.bISMONSTER && !e.Thing.target.player) ) + { + e.Thing.speed *= 2; + e.Thing.vel *= 2; + Vector3 x, y, z; + double ang = e.Thing.target.target?e.Thing.AngleTo(e.Thing.target.target):e.Thing.angle; + double pt = e.Thing.target.target?SWWMUtility.PitchTo(e.Thing,e.Thing.target.target,.5):e.Thing.pitch; + [x, y, z] = swwm_CoordUtil.GetAxes(pt,ang,e.Thing.roll); + int numpt = Random[ExtraMissiles](1,2); + for ( int i=0; i x.ceilingz ) x.SetZ(x.ceilingz-x.height); + if ( x.pos.z < x.floorz ) x.SetZ(x.floorz); + if ( !x.TestMobjLocation() || !x.TestMobjZ() || !level.IsPointInLevel(x.pos) ) + { + x.ClearCounters(); + x.Destroy(); + } + else + { + x.angle = e.Thing.angle; + x.GiveInventory("DontDuplicate",1); + break; + } + } + } + } + } + } + } +} diff --git a/zscript/handler/swwm_handler_oneliners.zsc b/zscript/handler/swwm_handler_oneliners.zsc new file mode 100644 index 000000000..d613702cd --- /dev/null +++ b/zscript/handler/swwm_handler_oneliners.zsc @@ -0,0 +1,82 @@ +// oneliner handling + +extend Class SWWMHandler +{ + transient String oneliner, onelinersnd; + transient int onelinertic, onelinerspan, onelinerlevel; + transient Array lastlines; + + static int AddOneliner( String type, int level, int delay = 5 ) + { + // only Demolitionist can play voice lines + if ( !(players[consoleplayer].mo is 'Demolitionist') ) + return 0; + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return 0; + String voicetype = CVar.FindCVar('swwm_voicetype').GetString(); + // suppress non-rage comments when ragekit is active, only screaming allowed + if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") ) return 0; + int whichline; + String testme = String.Format("SWWM_SUBS_%s_N%s",voicetype.MakeUpper(),type.MakeUpper()); + String locme = StringTable.Localize(testme,false); + int countem; + if ( testme == locme ) countem = 0; + else countem = locme.ToInt(); + if ( countem == 0 ) return 0; // voicepack doesn't have this + // check last line so we don't repeat + int last = 0, ent; + for ( int i=0; i 0 ) + { + whichline = Random[DemoLines](1,countem-1); + if ( whichline >= last ) whichline++; + hnd.lastlines[ent].lineno = whichline; + } + else + { + whichline = Random[DemoLines](1,countem); + let lst = new("LastLine"); + lst.type = type; + lst.lineno = whichline; + hnd.lastlines.Push(lst); + } + hnd.oneliner = String.Format("$SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),whichline); + hnd.onelinersnd = String.Format("voice/%s/%s%d",voicetype,type,whichline); + hnd.onelinertic = gametic+delay; + hnd.onelinerspan = int(S_GetLength(hnd.onelinersnd)*GameTicRate); + hnd.onelinerlevel = level; + return hnd.onelinertic+hnd.onelinerspan; + } + + private void OnelinerTick() + { + if ( !onelinertic || (onelinertic >= gametic) ) return; + if ( players[consoleplayer].health > 0 ) + { + if ( onelinerlevel > swwm_mutevoice ) + players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE); + SendNetworkEvent("swwmremoteliner."..onelinersnd,consoleplayer,onelinerlevel); + } + onelinertic = 0; + onelinerspan = 0; + } + + private ui void OnelinerUITick() + { + if ( (gametic != onelinertic) || (oneliner == "") || (players[consoleplayer].health <= 0) ) + return; + if ( onelinerlevel > swwm_mutevoice ) + { + let l = SWWMOneLiner.Make(oneliner,onelinerspan); + StatusBar.AttachMessage(l,-3473); + } + SendNetworkEvent("swwmremotelinertxt."..oneliner,consoleplayer,onelinerlevel); + } +} diff --git a/zscript/handler/swwm_handler_playerevents.zsc b/zscript/handler/swwm_handler_playerevents.zsc new file mode 100644 index 000000000..659f71d22 --- /dev/null +++ b/zscript/handler/swwm_handler_playerevents.zsc @@ -0,0 +1,144 @@ +// all the player* virtuals + +extend Class SWWMHandler +{ + override void PlayerDied( PlayerEvent e ) + { + let s = SWWMStats.Find(players[e.playernumber]); + if ( s ) s.deaths++; + } + + override void PlayerEntered( PlayerEvent e ) + { + PlayerInfo p = players[e.playernumber]; + // override KEYCONF-forced player classes when run with other gameplay mods (wish this was easier) + if ( !(p.mo is 'Demolitionist') ) + { + // make sure it's defined here, so special purpose classes (player chunks, scripted overrides) are respected + for ( int i=0; i 0) && ((s.lastcluster != level.cluster) || !(level.clusterflags&LevelLocals.CLUSTER_HUB))) ) + SWWMUtility.WipeInventory(p.mo,swwm_resetscore); + } + + override void PlayerRespawned( PlayerEvent e ) + { + // reset some vars + multilevel[e.playernumber] = 0; + spreecount[e.playernumber] = 0; + tookdamage[e.playernumber] = false; + lastkill[e.playernumber] = int.min; + // reset combat tracker + if ( !swwm_notrack ) + SWWMCombatTracker.Spawn(players[e.playernumber].mo); + } +} diff --git a/zscript/handler/swwm_handler_process.zsc b/zscript/handler/swwm_handler_process.zsc new file mode 100644 index 000000000..5965d4a4b --- /dev/null +++ b/zscript/handler/swwm_handler_process.zsc @@ -0,0 +1,602 @@ +// event processing +extend Class SWWMHandler +{ + // for menu events + transient Array checklist; + + override void ConsoleProcess( ConsoleEvent e ) + { + // doing it with an event because this way we can control WHEN it should be openable + if ( e.Name ~== "swwmdemomenu" ) + { + if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') ) + return; + Menu.SetMenu('DemolitionistMenu'); + } + else if ( e.Name ~== "swwmzoomin" ) + { + if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') ) + return; + double val = max(.5,swwm_mm_zoom/2.); + CVar.FindCVar('swwm_mm_zoom').SetFloat(val); + } + else if ( e.Name ~== "swwmzoomout" ) + { + if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') ) + return; + double maxval = players[consoleplayer].mo.FindInventory("Omnisight")?2.:1.; + double val = min(maxval,swwm_mm_zoom*2.); + CVar.FindCVar('swwm_mm_zoom').SetFloat(val); + } + } + + override void NetworkProcess( ConsoleEvent e ) + { + static const Class cbttypes[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; + if ( e.Name ~== "swwmgesture" ) + { + if ( (e.player == -1) || !playeringame[e.player] || !players[e.player].mo ) return; + let mo = players[e.player].mo; + switch( e.Args[0] ) + { + case 0: + SWWMGesture.SetGesture(mo,GS_Wave); + break; + case 1: + SWWMGesture.SetGesture(mo,GS_ThumbsUp); + break; + case 2: + SWWMGesture.SetGesture(mo,GS_Victory); + break; + case 3: + SWWMGesture.SetGesture(mo,GS_BlowKiss); + break; + } + return; + } + else if ( e.Name ~== "swwmfixitemcaps" ) + { + // this command is only really needed when I update item max amounts mid-playthrough + if ( multiplayer && (e.player != Net_Arbitrator) ) + { + if ( e.player == consoleplayer ) + Console.Printf("Only the net arbitrator can call this event."); + return; + } + for ( int i=0; i %d)",hams.GetTag(),hams.MaxAmount,hams.default.MaxAmount); + hams.MaxAmount = hams.default.MaxAmount; + hams.Amount = min(hams.Amount,hams.MaxAmount); + } + for ( Inventory inv=mo.inv; inv; inv=inv.inv ) + { + if ( inv is 'Ammo' ) + { + Ammo(inv).BackpackMaxAmount = Ammo(inv).default.BackpackMaxAmount; + int newmax = inv.default.MaxAmount; + if ( (hams.Amount > 0) && (Ammo(inv).BackpackMaxAmount > 0) ) + { + double factor = (Ammo(inv).BackpackMaxAmount-inv.default.MaxAmount)/double(hams.MaxAmount); + newmax = int(inv.default.MaxAmount+hams.Amount*factor); + } + if ( inv.MaxAmount != newmax ) + Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,newmax); + int dropme = max(0,inv.Amount-newmax); + if ( dropme ) + { + Console.Printf("Dropped %dx %s.",dropme,inv.GetTag()); + // non-SWWM ammos won't subdivide, but whatever, this is a debug command + inv.CreateTossable(dropme); + } + inv.MaxAmount = newmax; + } + else if ( inv is 'MagAmmo' ) + { + if ( inv.MaxAmount != inv.default.MaxAmount ) + Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,inv.default.MaxAmount); + // just drop the extras + int dropme = max(0,inv.Amount-inv.default.MaxAmount); + if ( dropme ) + { + Console.Printf("Dropped %dx %s.",dropme,inv.GetTag()); + inv.CreateTossable(dropme); + } + inv.MaxAmount = inv.default.MaxAmount; + } + else + { + if ( inv.MaxAmount != inv.default.MaxAmount ) + Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,inv.default.MaxAmount); + // only drop droppables (obviously) + if ( !inv.bUNDROPPABLE && !inv.bUNTOSSABLE ) + { + int dropme = max(0,inv.Amount-inv.default.MaxAmount); + if ( dropme ) + { + Console.Printf("Dropped %dx %s.",dropme,inv.GetTag()); + for ( int j=0; j item = e.Name.Mid(14); + if ( !item ) return; + if ( SWWMCredits.Take(players[e.Args[0]],e.Args[1]) ) + { + let def = GetDefaultByType(item); + SWWMWeapon sw; + // drop the swapweapon if we own it first + if ( swwm_swapweapons && (item is 'SWWMWeapon') && (sw = SWWMWeapon(def).HasSwapWeapon(players[e.Args[0]].mo)) ) + { + bool swapto = (sw == players[e.Args[0]].ReadyWeapon) || (sw.SisterWeapon && (sw.Sisterweapon == players[e.Args[0]].ReadyWeapon)); + int ngun = sw.Amount; + double ang = -15*(ngun-1); + for ( int i=0; i 1) ) + players[e.Args[0]].mo.A_SelectWeapon("DualExplodiumGun"); + else players[e.Args[0]].mo.A_SelectWeapon((Class)(item)); + } + } + } + else if ( e.Name.Left(14) ~== "swwmstoretake." ) + { + Class item = e.Name.Mid(14); + if ( !item ) return; + int amt = e.Args[2]; + if ( item is 'CandyGun' ) + { + // check if we can sell a spare instead, for the same price + int n = players[e.Args[0]].mo.CountInv('CandyGunSpares'); + if ( n >= amt ) + { + players[e.Args[0]].mo.TakeInventory('CandyGunSpares',amt); + SWWMCredits.Give(players[e.Args[0]],e.Args[1]); + return; + } + } + // if player currently has the dual wield weapon selected, switch over + if ( item is 'SWWMWeapon' ) + { + let c = Weapon(players[e.Args[0]].mo.FindInventory(item)); + if ( c.SisterWeapon && (players[e.Args[0]].ReadyWeapon == c.SisterWeapon) ) + { + players[e.Args[0]].ReadyWeapon = c; + players[e.Args[0]].SetPSprite(PSP_WEAPON,c.FindState("Ready")); + players[e.Args[0]].SetPSprite(PSP_WEAPON+1,null); // delete left weapon psprite + } + } + // if we're selling an embiggener, we need to readjust ammo + if ( item is 'HammerspaceEmbiggener' ) + { + let ritm = players[e.Args[0]].mo.FindInventory(item); + for ( Inventory i=players[e.Args[0]].mo.Inv; i; i=i.Inv ) + { + if ( !(i is 'Ammo') ) continue; + if ( Ammo(i).BackpackMaxAmount > 0 ) + { + double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(ritm.MaxAmount); + i.MaxAmount = int(i.default.MaxAmount+(ritm.Amount-amt)*factor); + } + // drop excess ammo + int excess = i.Amount-i.MaxAmount; + if ( excess > 0 ) i.CreateTossable(excess); + } + } + players[e.Args[0]].mo.TakeInventory(item,amt); + SWWMCredits.Give(players[e.Args[0]],e.Args[1]); + } + else if ( e.Name.Left(10) ~== "swwmtrade." ) + { + Class item = e.Name.Mid(10); + if ( !item ) return; + let def = GetDefaultByType(item); + int amt = def.Amount; + // if it's an ammo, check the largest unit givable + if ( item is 'Ammo' ) + { + for ( int i=0; i)(AllActorClasses[i]); + if ( !a || (a.GetParentClass() != item) || (GetDefaultByType(a).Amount < amt) ) continue; + amt = GetDefaultByType(a).Amount; + } + } + Inventory ritm = players[e.Args[1]].mo.FindInventory(item); + if ( ritm ) + { + int maxgive = ritm.MaxAmount-ritm.Amount; + if ( amt > maxgive ) amt = maxgive; + } + else if ( amt > def.MaxAmount ) amt = def.MaxAmount; + bool rslt = false; + Class giveitem = item; + if ( item is 'HammerspaceEmbiggener' ) giveitem = 'TradedHammerspaceEmbiggener'; + if ( (amt > 0) && players[e.Args[1]].mo.GiveInventory(giveitem,amt,true) ) + { + // if player currently has the dual wield weapon selected, switch over + if ( item is 'SWWMWeapon' ) + { + let c = Weapon(players[e.Args[0]].mo.FindInventory(item)); + if ( c.SisterWeapon && (players[e.Args[0]].ReadyWeapon == c.SisterWeapon) ) + { + players[e.Args[0]].ReadyWeapon = c; + players[e.Args[0]].SetPSprite(PSP_WEAPON,c.FindState("Ready")); + players[e.Args[0]].SetPSprite(PSP_WEAPON+1,null); // delete left weapon psprite + } + } + // if we're trading an embiggener, we need to readjust ammo + if ( item is 'HammerspaceEmbiggener' ) + { + let ritm = players[e.Args[0]].mo.FindInventory(item); + for ( Inventory i=players[e.Args[0]].mo.Inv; i; i=i.Inv ) + { + if ( !(i is 'Ammo') ) continue; + if ( Ammo(i).BackpackMaxAmount > 0 ) + { + double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(ritm.MaxAmount); + i.MaxAmount = int(i.default.MaxAmount+(ritm.Amount-amt)*factor); + } + // drop excess ammo + int excess = i.Amount-i.MaxAmount; + if ( excess > 0 ) i.CreateTossable(excess); + } + } + if ( item is 'CandyGun' ) + { + // see if we can take a fully loaded spare from us instead + int n = players[e.Args[0]].mo.CountInv('CandyGunSpares'); + int na = players[e.Args[0]].mo.CountInv('CandyGunAmmo'); + if ( (n >= amt) && (na >= amt) ) + { + players[e.Args[0]].mo.TakeInventory('CandyGunSpares',amt); + players[e.Args[0]].mo.TakeInventory('CandyGunAmmo',amt); + } + else players[e.Args[0]].mo.TakeInventory('CandyGun',amt); + } + else players[e.Args[0]].mo.TakeInventory(item,amt); + // add to history + SWWMTradeHistory.RegisterSend(players[e.Args[0]],players[e.Args[1]],item,amt); + SWWMTradeHistory.RegisterReceive(players[e.Args[1]],players[e.Args[0]],item,amt); + // add messages + if ( e.Args[0] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGSENT"),amt,def.GetTag(),players[e.Args[1]].GetUserName()); + if ( e.Args[1] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGRECV"),players[e.Args[0]].GetUserName(),amt,def.GetTag()); + rslt = true; + } + if ( e.Args[0] == consoleplayer ) + { + let t = new("MenuTransaction"); + t.uid = e.Args[2]; + t.type = MenuTransaction.TT_ITEMSEND; + t.result = rslt; + t.used = item; + t.usedup = (players[e.Args[1]].mo.CountInv(item)<=0); + checklist.Push(t); + } + } + else if ( e.Name.Left(17) ~== "swwmmarkloreread." ) + { + let l = SWWMLoreLibrary.Find(players[e.Args[0]]); + let idx = l.FindEntry(e.Name.Mid(17)); + l.MarkRead(idx); + } + else if ( e.Name.Left(12) ~== "swwmuseitem." ) + { + Class item = e.Name.Mid(12); + if ( !item ) return; + let i = players[e.Args[0]].mo.FindInventory(item); + if ( !i ) return; + bool rslt = players[e.Args[0]].mo.UseInventory(i); + if ( e.Args[0] == consoleplayer ) + { + let t = new("MenuTransaction"); + t.uid = e.Args[1]; + t.type = MenuTransaction.TT_ITEMUSE; + let w = (Class)(item); + if ( w ) + { + t.result = (players[e.Args[0]].PendingWeapon==Weapon(i)); + // dual wield gun support + if ( (i is 'ExplodiumGun') && (players[e.Args[0]].PendingWeapon==Weapon(i).SisterWeapon) ) + t.result = true; + } + else t.result = rslt; + t.used = item; + t.usedup = (!i||(i.Amount<=0)); + checklist.Push(t); + } + } + else if ( e.Name.Left(13) ~== "swwmdropitem." ) + { + Class item = e.Name.Mid(13); + if ( !item ) return; + let i = players[e.Args[0]].mo.FindInventory(item); + if ( !i ) return; + int amt = i.default.Amount; + // if it's an ammo, check the largest unit givable + if ( i is 'Ammo' ) + { + for ( int i=0; i)(AllActorClasses[i]); + if ( !a || (a.GetParentClass() != item) || (GetDefaultByType(a).Amount < amt) ) continue; + amt = GetDefaultByType(a).Amount; + } + } + if ( amt > i.Amount ) amt = i.Amount; + let drop = players[e.Args[0]].mo.DropInventory(i,amt); + // add some random velocity so multiple drops don't get bunched together + if ( drop ) drop.vel += (Actor.RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),players[e.Args[0]].mo.angle),FRandom[Junk](2.,5.)); + if ( e.Args[0] == consoleplayer ) + { + let t = new("MenuTransaction"); + t.uid = e.Args[1]; + t.type = MenuTransaction.TT_ITEMDROP; + t.used = item; + t.result = drop; + t.usedup = (!i||(i.Amount<=0)); + checklist.Push(t); + } + } + else if ( e.Name ~== "swwmkoraxline" ) + { + if ( consoleplayer != e.Args[1] ) return; + switch ( e.Args[0] ) + { + case 0: + AddOneliner("koraxgreet",3,60); + break; + case 1: + AddOneliner("koraxblood",3,150); + break; + case 2: + AddOneliner("koraxgame",3,120); + break; + case 3: + AddOneliner("koraxworship",3,80); + break; + case 4: + AddOneliner("koraxmasters",3,90); + break; + } + } + else if ( e.Name.Left(16) ~== "swwmremoteliner." ) + { + if ( consoleplayer == e.Args[0] ) return; + if ( !swwm_othervoice ) return; + if ( swwm_mutevoice >= e.Args[1] ) return; + players[e.Args[0]].mo.A_StartSound(e.Name.Mid(16),CHAN_DEMOVOICE,attenuation:.5); + } + else if ( e.Name.Left(19) ~== "swwmremotelinertxt." ) + { + if ( consoleplayer == e.Args[0] ) return; + if ( !swwm_othervoice ) return; + if ( swwm_mutevoice >= e.Args[1] ) return; + double dist = players[consoleplayer].Camera.Distance3D(players[e.Args[0]].mo); + if ( dist < 2000 ) + Console.Printf("\cx%s\cx: %s\c-",players[e.Args[0]].GetUserName(),StringTable.Localize(e.Name.Mid(19))); + } + else if ( e.Name.Left(8) ~== "swwmcbt." ) + { + // from wikipedia, the free encyclopedia + if ( !playeringame[e.Args[0]] || !players[e.Args[0]].mo ) return; + let cbt = Wallbuster(players[e.Args[0]].mo.FindInventory("Wallbuster")); + if ( !cbt ) return; + cbt.reloadqueue.Clear(); + if ( e.Name.Mid(8) ~== "EMPTY" ) cbt.clearout = true; + else + { + cbt.clearout = false; + Array qs; + qs.Clear(); + String rite = e.Name.Mid(8); + rite.Split(qs,",",TOK_SKIPEMPTY); + for ( int i=0; i 3) ) continue; + cbt.reloadqueue.Push(cbttypes[qi]); + } + } + cbt.waitreload = false; + } + else if ( e.Name ~== "swwmcleartransaction" ) + { + if ( e.Args[1] != consoleplayer ) return; + for ( int i=0; i= 0x61) && (e.KeyChar <= 0x7A) ) + { + // F + if ( e.KeyChar == 0x66 ) + { + let demo = Demolitionist(players[consoleplayer].mo); + let gone = PlayerGone(players[consoleplayer].mo); + if ( (demo && (demo.Health <= 0) && (demo.deadtimer > 40)) + || (gone && (gone.Health <= 0) && (gone.deadtimer > 40)) ) + { + // pay respects + int numf = Random[FInTheChat](1,6); + for ( int i=0; i= 0) && (hnd.casings_cnt > swwm_maxcasings) ) + DeQueueCasing(hnd.casings); + } + static void DeQueueCasing( SWWMCasing c ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd || !hnd.casings ) return; + if ( (hnd.casings != c) && !c.prevcasing && !c.nextcasing ) return; + hnd.casings_cnt--; + if ( !c.prevcasing ) hnd.casings = c.nextcasing; + else c.prevcasing.nextcasing = c.nextcasing; + if ( c == hnd.casings_end ) hnd.casings_end = c.prevcasing; + if ( c.nextcasing ) c.nextcasing.prevcasing = c.prevcasing; + c.killme = true; + c.prevcasing = null; + c.nextcasing = null; + } + static void QueueChip( SWWMChip c ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return; + hnd.chips_cnt++; + if ( !hnd.chips ) + { + // this is the initial one + hnd.chips = c; + hnd.chips_end = c; + } + else + { + hnd.chips_end.nextchip = c; + c.prevchip = hnd.chips_end; + hnd.chips_end = c; + } + while ( hnd.chips && (swwm_maxdebris >= 0) && (hnd.chips_cnt > swwm_maxdebris) ) + DeQueueChip(hnd.chips); + } + static void DeQueueChip( SWWMChip c ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd || !hnd.chips ) return; + if ( (hnd.chips != c) && !c.prevchip && !c.nextchip ) return; + hnd.chips_cnt--; + if ( !c.prevchip ) hnd.chips = c.nextchip; + else c.prevchip.nextchip = c.nextchip; + if ( c == hnd.chips_end ) hnd.chips_end = c.prevchip; + if ( c.nextchip ) c.nextchip.prevchip = c.prevchip; + c.killme = true; + c.prevchip = null; + c.nextchip = null; + } + static void QueueBlod( mkBloodDrop b ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return; + hnd.blods_cnt++; + if ( !hnd.blods ) + { + // this is the initial one + hnd.blods = b; + hnd.blods_end = b; + } + else + { + hnd.blods_end.nextblod = b; + b.prevblod = hnd.blods_end; + hnd.blods_end = b; + } + while ( hnd.blods && (swwm_maxblood >= 0) && (hnd.blods_cnt > swwm_maxblood) ) + DeQueueBlod(hnd.blods); + } + static void DeQueueBlod( mkBloodDrop b ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd || !hnd.blods ) return; + if ( (hnd.blods != b) && !b.prevblod && !b.nextblod ) return; + hnd.blods_cnt--; + if ( !b.prevblod ) hnd.blods = b.nextblod; + else b.prevblod.nextblod = b.nextblod; + if ( b == hnd.blods_end ) hnd.blods_end = b.prevblod; + if ( b.nextblod ) b.nextblod.prevblod = b.prevblod; + b.killme = true; + b.prevblod = null; + b.nextblod = null; + } + static void QueueMeat( mkFlyingGib m ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return; + hnd.meats_cnt++; + if ( !hnd.meats ) + { + // this is the initial one + hnd.meats = m; + hnd.meats_end = m; + } + else + { + hnd.meats_end.nextmeat = m; + m.prevmeat = hnd.meats_end; + hnd.meats_end = m; + } + while ( hnd.meats && (swwm_maxgibs >= 0) && (hnd.meats_cnt > swwm_maxgibs) ) + DeQueueMeat(hnd.meats); + } + static void DeQueueMeat( mkFlyingGib m ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd || !hnd.meats ) return; + if ( (hnd.meats != m) && !m.prevmeat && !m.nextmeat ) return; + hnd.meats_cnt--; + if ( !m.prevmeat ) hnd.meats = m.nextmeat; + else m.prevmeat.nextmeat = m.nextmeat; + if ( m == hnd.meats_end ) hnd.meats_end = m.prevmeat; + if ( m.nextmeat ) m.nextmeat.prevmeat = m.prevmeat; + m.killme = true; + m.prevmeat = null; + m.nextmeat = null; + } + + private void RecheckQueues() + { + while ( casings && (casings_cnt > swwm_maxcasings) ) + DeQueueCasing(casings); + while ( chips && (chips_cnt > swwm_maxdebris) ) + DeQueueChip(chips); + while ( blods && (blods_cnt > swwm_maxblood) ) + DeQueueBlod(blods); + while ( meats && (meats_cnt > swwm_maxgibs) ) + DeQueueMeat(meats); + } + + private void QueueMaintenance() + { + if ( swwm_maxcasings != oldmaxcasings ) + { + while ( casings && (swwm_maxcasings >= 0) && (casings_cnt > swwm_maxcasings) ) + DeQueueCasing(casings); + } + if ( swwm_maxdebris != oldmaxdebris ) + { + while ( chips && (swwm_maxdebris >= 0) && (chips_cnt > swwm_maxdebris) ) + DeQueueChip(chips); + } + if ( swwm_maxblood != oldmaxblood ) + { + while ( blods && (swwm_maxblood >= 0) && (blods_cnt > swwm_maxblood) ) + DeQueueBlod(blods); + } + if ( swwm_maxgibs != oldmaxgibs ) + { + while ( meats && (swwm_maxgibs >= 0) && (meats_cnt > swwm_maxgibs) ) + DeQueueMeat(meats); + } + oldmaxcasings = swwm_maxcasings; + oldmaxdebris = swwm_maxdebris; + oldmaxblood = swwm_maxblood; + oldmaxgibs = swwm_maxgibs; + if ( swwm_blood ) return; + while ( blods ) DeQueueBlod(blods); + while ( meats ) DeQueueMeat(meats); + } +} diff --git a/zscript/handler/swwm_handler_replacements.zsc b/zscript/handler/swwm_handler_replacements.zsc new file mode 100644 index 000000000..abd9f3aad --- /dev/null +++ b/zscript/handler/swwm_handler_replacements.zsc @@ -0,0 +1,536 @@ +// class replacements + +extend Class SWWMHandler +{ + bool nugflip; // h/a nugget flip-flop spawn counter + + override void CheckReplacee( ReplacedEvent e ) + { + if ( e.Replacement is 'DSparilHax' ) + e.Replacee = 'Sorcerer2'; + } + + private Class GetDRLAReplacement( Class a ) + { + static const String refpool[] = + { + "Zombieman", + "Shotgunguy", + "Chaingunguy", + "DoomImp", + "Demon", + "Spectre", + "LostSoul", + "Cacodemon", + "HellKnight", + "BaronOfHell", + "Arachnotron", + "PainElemental", + "Revenant", + "Fatso", + "Archvile", + "Cyberdemon", + "SpiderMastermind", + "BossEye", + "BossBrain" + }; + static const String babypool[] = + { + "RLFormerHumanPistol", + "RLFormerSergeantShotgun", + "RLFormerCommandoChaingun", + "RLImp", + "RLDemon", + "RLSpectre", + "RLLostSoul", + "RLCacodemon", + "RLHellKnight", + "RLBaronOfHell", + "RLArachnotron", + "RLPainElemental", + "RLRevenant", + "RLMancubus", + "RLArchvile", + "RLCyberdemon", + "RLSpiderMastermindVariantSpawner", + "RLEasyBossEye", + "RLBossBrain" + }; + static const String easypool[] = + { + "RLFormerHumanNoArmageddonSpawner", + "RLFormerSergeantNoArmageddonSpawner", + "RLFormerCommandoNoArmageddonSpawner", + "RLImpNoArmageddonSpawner", + "RLDemonNoArmageddonSpawner", + "RLSpectreNoArmageddonSpawner", + "RLLostSoulNoArmageddonSpawner", + "RLCacodemonNoArmageddonSpawner", + "RLHellKnightNoArmageddonSpawner", + "RLBaronOfHellNoArmageddonSpawner", + "RLArachnotronNoArmageddonSpawner", + "RLPainElementalNoArmageddonSpawner", + "RLRevenantNoArmageddonSpawner", + "RLMancubusNoArmageddonSpawner", + "RLArchvileNoArmageddonSpawner", + "RLCyberdemonNoArmageddonSpawner", + "RLSpiderMastermindNoArmageddonSpawner", + "RLBossEye", + "RLBossBrain" + }; + static const String normalpool[] = + { + "RLFormerHumanNoArmageddonSpawner", + "RLFormerSergeantNoArmageddonSpawner", + "RLFormerCommandoNoArmageddonSpawner", + "RLImpNoArmageddonSpawner", + "RLDemonNoArmageddonSpawner", + "RLSpectreNoArmageddonSpawner", + "RLLostSoulNoArmageddonSpawner", + "RLCacodemonNoArmageddonSpawner", + "RLHellKnightNoArmageddonSpawner", + "RLBaronOfHellNoArmageddonSpawner", + "RLArachnotronNoArmageddonSpawner", + "RLPainElementalNoArmageddonSpawner", + "RLRevenantNoArmageddonSpawner", + "RLMancubusNoArmageddonSpawner", + "RLArchvileNoArmageddonSpawner", + "RLCyberdemonNoArmageddonSpawner", + "RLSpiderMastermindNoArmageddonSpawner", + "RLBossEye", + "RLBossBrain" + }; + static const String hardpool[] = + { + "RLFormerHumanSpawner", + "RLFormerSergeantSpawner", + "RLFormerCommandoSpawner", + "RLImpSpawner", + "RLDemonSpawner", + "RLSpectreSpawner", + "RLLostSoulSpawner", + "RLCacodemonSpawner", + "RLHellKnightSpawner", + "RLBaronOfHellSpawner", + "RLArachnotronSpawner", + "RLPainElementalSpawner", + "RLRevenantSpawner", + "RLMancubusSpawner", + "RLArchvileSpawner", + "RLCyberdemonSpawner", + "RLSpiderMastermindSpawner", + "RLUVBossEye", + "RLBossBrain" + }; + static const String nightmarepool[] = + { + "RLEliteFormerHumanSpawner", + "RLEliteFormerSergeantSpawner", + "RLEliteFormerCommandoSpawner", + "RLNightmareImp", + "RLNightmareDemon", + "RLNightmareSpectre", + "RLNightmareLostSoul", + "RLNightmareCacodemon", + "RLNightmareHellKnight", + "RLNightmareBaronOfHell", + "RLNightmareArachnotron", + "RLNightmarePainElemental", + "RLNightmareRevenant", + "RLNightmareMancubus", + "RLNightmareArchvile", + "RLNightmareCyberdemonSpawner", + "RLNightmareSpiderMastermindSpawner", + "RLNightmareBossEye", + "RLNightmareBossBrain" + }; + static const String technophobiapool[] = + { + "RLFormerCyborgBattleRifle", + "RLFormerCyborgBattleRifle", + "RLFormerCyborgBattleRifle", + "RLCyberneticImp", + "RLCyberneticDemon", + "RLCyberneticSpectre", + "RLCyberneticLostSoul", + "RLCacodemon", + "RLCyberneticHellKnight", + "RLCyberneticBaronOfHell", + "RLCyberneticArachnotron", + "RLCyberneticPainElemental", + "RLCyberneticRevenant", + "RLCyberneticMancubus", + "RLCyberneticArchvile", + "RLCyberneticCyberdemonSpawner", + "RLCyberneticSpiderMastermindSpawner", + "RLTechnophobiaBossEye", + "RLTechnophobiaBossBrain" + }; + static const String armageddonpool[] = + { + "RLFormerAssaultTrooper", + "RLFormerOverwatch", + "RLFormerShocktrooper", + "RLArmageddonImp", + "RLArmageddonDemon", + "RLArmageddonSpectreSpawner", + "RLTheHungrySpawner", + "RLArmageddonCacodemon", + "RLArmageddonHellKnightSpawner", + "RLArmageddonBaronOfHell", + "RLArmageddonArachnotron", + "RLArmageddonPainElemental", + "RLArmageddonRevenant", + "RLArmageddonMancubus", + "RLArmageddonArchvileSpawner", + "RLArmageddonCyberdemonSpawner", + "RLArmageddonSpiderMastermindSpawner", + "RLArmageddonBossEye", + "RLArmageddonBossBrain" + }; + static const String adaptivepool[] = + { + "RLAdaptiveFormerHuman", + "RLAdaptiveFormerSergeant", + "RLAdaptiveFormerCommando", + "RLAdaptiveImp", + "RLAdaptiveDemon", + "RLAdaptiveSpectre", + "RLAdaptiveLostSoul", + "RLAdaptiveCacodemon", + "RLAdaptiveHellKnight", + "RLAdaptiveBaronOfHell", + "RLAdaptiveArachnotron", + "RLAdaptivePainElemental", + "RLAdaptiveRevenant", + "RLAdaptiveMancubus", + "RLAdaptiveArchvile", + "RLAdaptiveCyberdemon", + "RLAdaptiveSpiderMastermind", + "RLUVBossEye", + "RLBossBrain" + }; + switch ( swwm_drlaskill ) + { + case 0: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return babypool[i]; + } + break; + case 1: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return easypool[i]; + } + break; + case 2: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return normalpool[i]; + } + break; + case 3: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return hardpool[i]; + } + break; + case 4: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return nightmarepool[i]; + } + break; + case 5: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return technophobiapool[i]; + } + break; + case 6: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return armageddonpool[i]; + } + break; + case 7: + default: + for ( int i=0; i<18; i++ ) + { + if ( !(a is refpool[i]) ) continue; + return adaptivepool[i]; + } + break; + } + return null; + } + + override void CheckReplacement( ReplaceEvent e ) + { + // respect final replacements + if ( e.IsFinal ) return; + // shell types (sorted by rarity + static const Class redpool[] = {"RedShell","RedShell2","RedShell4"}; + static const Class greenpool[] = {"GreenShell","GreenShell2","GreenShell4"}; + static const Class whitepool[] = {"WhiteShell","WhiteShell2"}; + static const Class purplepool[] = {"PurpleShell","PurpleShell2","PurpleShell4"}; + static const Class bluepool[] = {"BlueShell","BlueShell2","BlueShell4"}; + static const Class blackpool[] = {"BlackShell","BlackShell2"}; + // DRLA Monsters stuff + if ( hasdrlamonsters ) + { + let rep = GetDRLAReplacement(e.Replacee); + if ( rep ) + { + e.Replacement = rep; + e.IsFinal = true; + return; + } + } + // only replace vanilla blood if no other gore mod is doing it + if ( (e.Replacee == "Blood") && (!e.Replacement || e.Replacement == "Blood") && swwm_blood ) e.Replacement = "mkBlood"; + else if ( e.Replacee is 'ItemFog' ) e.Replacement = 'SWWMItemFog'; + else if ( e.Replacee is 'TeleportFog' ) e.Replacement = 'SWWMTeleportFog'; + else if ( (e.Replacee is 'CommanderKeen') && (!e.Replacement || (e.Replacement == 'CommanderKeen')) ) + { + let def = GetDefaultByType(e.Replacee); + bool dehackery = false; + for ( State s=def.SpawnState; s; s=s.NextState ) + { + if ( s.bDEHACKED ) dehackery = true; + // keep checking until we hit a loop, just in case + if ( s.NextState && (s.DistanceTo(s.NextState) <= 0) ) break; + } + if ( dehackery ) return; + e.Replacement = 'SWWMHangingKeen'; + } + else if ( (e.Replacee is 'BossBrain') && (!e.Replacement || (e.Replacement == 'BossBrain')) ) + { + let def = GetDefaultByType(e.Replacee); + bool dehackery = false; + for ( State s=def.SpawnState; s; s=s.NextState ) + { + if ( s.bDEHACKED ) dehackery = true; + // keep checking until we hit a loop, just in case + if ( s.NextState && (s.DistanceTo(s.NextState) <= 0) ) break; + } + if ( dehackery ) return; + e.Replacement = 'SWWMBossBrain'; + } + else if ( e.Replacee is 'RedCard' ) + { + if ( level.GetChecksum() ~== "3805A661D5C4523AFF7BF86991071043" ) + return; // don't replace red key in Equinox MAP13 + e.Replacement = 'SWWMRedCard'; + } + else if ( e.Replacee is 'BlueCard' ) e.Replacement = 'SWWMBlueCard'; + else if ( e.Replacee is 'YellowCard' ) e.Replacement = 'SWWMYellowCard'; + else if ( e.Replacee.GetClassName() == 'KDiZDSilverKey' ) e.Replacement = 'SWWMSilverCardKDiZD'; + else if ( e.Replacee.GetClassName() == 'KDiZDGreenKey' ) e.Replacement = 'SWWMGreenCardKDiZD'; + else if ( e.Replacee.GetClassName() == 'KDiZDOrangeKey' ) e.Replacement = 'SWWMOrangeCardKDiZD'; + else if ( e.Replacee.GetClassName() == 'GreenCard' ) e.Replacement = 'SWWMGreenCard'; + else if ( e.Replacee is 'RedSkull' ) e.Replacement = 'SWWMRedSkull'; + else if ( e.Replacee is 'BlueSkull' ) e.Replacement = 'SWWMBlueSkull'; + else if ( e.Replacee is 'YellowSkull' ) e.Replacement = 'SWWMYellowSkull'; + else if ( e.Replacee is 'KeyGreen' ) e.Replacement = 'SWWMKeyGreen'; + else if ( e.Replacee is 'KeyBlue' ) e.Replacement = 'SWWMKeyBlue'; + else if ( e.Replacee is 'KeyYellow' ) e.Replacement = 'SWWMKeyYellow'; + else if ( e.Replacee.GetClassName() == 'KeyRed' ) e.Replacement = 'SWWMKeyRed'; + else if ( (e.Replacee is 'Chainsaw') || (e.Replacee is 'Gauntlets') || (e.Replacee is 'FWeapAxe') ) e.Replacement = SWWMUtility.PickSWWMSlot1(); + else if ( (e.Replacee is 'Fist') || (e.Replacee is 'Staff') ) e.Replacement = 'DeepImpact'; + else if ( (e.Replacee is 'Pistol') || (e.Replacee is 'GoldWand') || (e.Replacee is 'FWeapFist') || (e.Replacee is 'CWeapMace') || (e.Replacee is 'MWeapWand') ) e.Replacement = SWWMUtility.PickSWWMSlot2(); + else if ( (e.Replacee is 'Shotgun') || (e.Replacee is 'CWeapStaff') ) e.Replacement = SWWMUtility.IsDoomOne()?SWWMUtility.PickHereticSlot3():SWWMUtility.PickSWWMSlot3(); + else if ( (e.Replacee is 'SuperShotgun') || (e.Replacee is 'MWeapFrost') ) e.Replacement = SWWMUtility.PickSWWMSlot4(); + else if ( e.Replacee is 'Crossbow' ) e.Replacement = SWWMUtility.PickHereticSlot3(); + else if ( (e.Replacee is 'Chaingun') || (e.Replacee is 'Blaster') || (e.Replacee is 'FWeaponPiece3') ) e.Replacement = SWWMUtility.PickSWWMSlot5(); + else if ( (e.Replacee is 'RocketLauncher') || (e.Replacee is 'PhoenixRod') || (e.Replacee is 'FWeapHammer') ) e.Replacement = SWWMUtility.PickSWWMSlot6(); + else if ( (e.Replacee is 'PlasmaRifle') || (e.Replacee is 'SkullRod') ) e.Replacement = SWWMUtility.PickDoomSlot6(); + else if ( e.Replacee is 'CWeapFlame' ) e.Replacement = SWWMUtility.PickSWWMSlot7(); + else if ( e.Replacee is 'MWeapLightning' ) e.Replacement = SWWMUtility.PickSWWMSlot8(); + else if ( (e.Replacee is 'BFG9000') || (e.Replacee is 'Mace') ) e.Replacement = SWWMUtility.PickDoomSlot7(); + else if ( e.Replacee is 'CWeaponPiece2' ) e.Replacement = SWWMUtility.PickSWWMSlot9(); + else if ( e.Replacee is 'MWeaponPiece1' ) e.Replacement = SWWMUtility.PickSWWMSlot0(); + else if ( (e.Replacee is 'ShellBox') || (e.Replacee is 'CrossbowHefty') ) + { + /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SMW05SmallAmmo':'SMW05BigAmmo'; + else */switch( Random[Replacements](0,14) ) + { + case 0: + case 1: + case 2: + e.Replacement = redpool[Random[Replacements](1,2)]; + break; + case 3: + case 4: + case 5: + e.Replacement = greenpool[Random[Replacements](1,2)]; + break; + case 6: + case 7: + case 8: + e.Replacement = whitepool[Random[Replacements](0,1)]; + break; + case 9: + case 10: + case 11: + e.Replacement = purplepool[Random[Replacements](0,2)]; + break; + case 12: + case 13: + e.Replacement = bluepool[Random[Replacements](0,2)]; + break; + case 14: + e.Replacement = blackpool[Random[Replacements](0,1)]; + break; + } + } + else if ( (e.Replacee is 'Shell') || (e.Replacee is 'CrossbowAmmo') ) + { + /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SMW05SmallAmmo':'SMW05BundleSpawn'; + else */switch( Random[Replacements](0,13) ) + { + case 0: + case 1: + case 2: + e.Replacement = redpool[Random[Replacements](0,1)]; + break; + case 3: + case 4: + case 5: + e.Replacement = greenpool[Random[Replacements](0,1)]; + break; + case 6: + case 7: + e.Replacement = whitepool[0]; + break; + case 8: + case 9: + case 10: + e.Replacement = purplepool[Random[Replacements](0,1)]; + break; + case 11: + case 12: + e.Replacement = bluepool[Random[Replacements](0,1)]; + break; + case 13: + e.Replacement = blackpool[0]; + break; + } + } + else if ( e.Replacee is 'ClipBox' ) + { + /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenSmallAmmo':'SheenBigAmmo'; + else */e.Replacement = Random[Replacements](0,4)?'EvisceratorShell':Random[Replacements](0,6)?'EvisceratorTrioSpawn':'EvisceratorSixPack'; + } + else if ( (e.Replacee is 'Clip') || (e.Replacee is 'GoldWandAmmo') ) e.Replacement = /*(e.Replacee is 'GoldWandHefty')?'SheenSmallAmmo':'SheenTinyAmmo'*/'SWWMNothing'; + else if ( e.Replacee is 'BlasterHefty' ) + { + /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenBigAmmo':'SheenSmallAmmo'; + else */e.Replacement = Random[Replacements](0,6)?'EvisceratorTrioSpawn':'EvisceratorSixPack'; + } + else if ( e.Replacee is 'BlasterAmmo' ) + { + /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenSmallAmmo':'SheenTinyAmmo'; + else */e.Replacement = 'EvisceratorShell'; + } + else if ( (e.Replacee is 'RocketBox') || (e.Replacee is 'PhoenixRodHefty') || (e.Replacee is 'MaceHefty') ) + { + /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'QuadravolAmmo':'QuadravolAmmoBundleSpawn'; + else */switch ( Random[Replacements](0,11) ) + { + case 0: + case 1: + case 2: + case 3: + case 4: + if ( Random[Replacements](0,5) ) e.Replacement = 'HellblazerMissiles'; + else if ( Random[Replacements](0,4) ) e.Replacement = 'HellblazerMissileTrioSpawn'; + else e.Replacement = 'HellblazerMissileMag'; + break; + case 5: + case 6: + case 7: + case 8: + if ( Random[Replacements](0,6) ) e.Replacement = 'HellblazerCrackshots'; + else e.Replacement = 'HellblazerCrackshotMag'; + break; + case 9: + case 10: + if ( Random[Replacements](0,8) ) e.Replacement = 'HellblazerRavagers'; + else e.Replacement = 'HellblazerRavagerMag'; + break; + case 11: + if ( Random[Replacements](0,10) ) e.Replacement = 'HellblazerWarheads'; + else e.Replacement = 'HellblazerWarheadMag'; + break; + } + } + else if ( (e.Replacee is 'RocketAmmo') || (e.Replacee is 'PhoenixRodAmmo') || (e.Replacee is 'MaceAmmo') ) + { + /*if ( Random[Replacements](0,1) ) e.Replacement = 'QuadravolAmmo'; + else */e.Replacement = Random[Replacements](0,2)?'HellblazerMissiles':'HellblazerCrackshots'; + } + else if ( (e.Replacee is 'CellPack') || (e.Replacee is 'SkullRodHefty') ) + { + /*if ( Random[Replacements](0,1) ) + { + if ( !Random[Replacements](0,2) ) e.Replacement = Random[Replacements](0,3)?'EMPCoreBundleSpawn':'EMPCore'; + else if ( Random[Replacements](0,2) ) e.Replacement = 'RayBoltBundleSpawn'; + else e.Replacement = 'RayAmmo'; + } + else */if ( !Random[Replacements](0,2) ) + { + if ( Random[Replacements](0,3) ) e.Replacement = Random[Replacements](0,2)?'SilverBulletsBundleSpawn':'SilverBullets2BundleSpawn'; + else e.Replacement = Random[Replacements](0,2)?'SilverBulletAmmo':'SilverBulletAmmo2'; + } + else if ( Random[Replacements](0,2) ) e.Replacement = 'CandyGunBulletsBundleSpawn'; + else e.Replacement = 'CandyGunAmmo'; + } + else if ( (e.Replacee is 'Cell') || (e.Replacee is 'SkullRodAmmo') ) + { + if ( !Random[Replacements](0,2) ) e.Replacement = /*!Random[Replacements](0,2)?'RayBattery':*/Random[Replacements](0,2)?'HellblazerRavagers':'HellblazerWarheads'; + else if ( Random[Replacements](0,2) ) e.Replacement = /*Random[Replacements](0,1)?'DarkCanister':*/'SparkUnit'; + else if ( !Random[Replacements](0,3) ) e.Replacement = /*Random[Replacements](0,1)?'RayBolt':*/'CandyGunBullets'; + else e.Replacement = /*Random[Replacements](0,1)?'EMPCore':*/Random[Replacements](0,2)?'SilverBullets':'SilverBullets2'; + } + else if ( e.Replacee is 'Mana1' ) e.Replacement = 'FabricatorTier1'; + else if ( e.Replacee is 'Mana2' ) e.Replacement = 'FabricatorTier2'; + else if ( e.Replacee is 'Mana3' ) e.Replacement = 'FabricatorTier3'; + else if ( e.Replacee is 'ArtiBoostMana' ) e.Replacement = 'FabricatorTier4'; + else if ( (e.Replacee is 'Backpack') || (e.Replacee is 'BagOfHolding') || (e.Replacee is 'ArtiPork') ) e.Replacement = 'HammerspaceEmbiggener'; + else if ( (e.Replacee is 'FWeaponPiece1') || (e.Replacee is 'FWeaponPiece2') + || (e.Replacee is 'CWeaponPiece1') || (e.Replacee is 'CWeaponPiece3') + || (e.Replacee is 'MWeaponPiece2') || (e.Replacee is 'MWeaponPiece3') ) e.Replacement = 'SWWMNothing'; + else if ( e.Replacee is 'ArmorBonus' ) e.Replacement = 'ArmorNuggetItem'; + else if ( e.Replacee is 'HealthBonus' ) e.Replacement = 'HealthNuggetItem'; + else if ( (e.Replacee is 'ArtiTimeBomb') || (e.Replacee is 'ArtiBlastRadius') || (e.Replacee is 'ArtiPoisonBag') || (e.Replacee is 'ArtiHealingRadius') ) e.Replacement = (nugflip=!nugflip)?'HealthNuggetBundleSpawn':'ArmorNuggetBundleSpawn'; + else if ( (e.Replacee is 'HealthBonus') ) e.Replacement = 'HealthNuggetItem'; + else if ( (e.Replacee is 'Stimpack') || (e.Replacee is 'CrystalVial') ) e.Replacement = 'TetraHealthItem'; + else if ( (e.Replacee is 'Medikit') || (e.Replacee is 'ArtiHealth') ) e.Replacement = 'CubeHealthItem'; + else if ( (e.Replacee is 'Soulsphere') || (e.Replacee is 'ArtiSuperHealth') ) e.Replacement = 'RefresherItem'; + else if ( (e.Replacee is 'Megasphere') || (e.Replacee is 'ArtiEgg') || (e.Replacee is 'ArtiBoostArmor') ) e.Replacement = 'GrilledCheeseSandwich'; + else if ( (e.Replacee is 'Blursphere') || (e.Replacee is 'ArtiInvisibility') ) e.Replacement = 'GhostArtifact'; + else if ( e.Replacee is 'Radsuit' ) e.Replacement = 'EBarrier'; + else if ( (e.Replacee is 'ArtiFly') ) e.Replacement = 'GravitySuppressor'; + else if ( (e.Replacee is 'InvulnerabilitySphere') || (e.Replacee is 'ArtiInvulnerability') || (e.Replacee is 'ArtiInvulnerability2') ) e.Replacement = 'FuckingInvinciball'; + else if ( (e.Replacee is 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'Ragekit'; + else if ( (e.Replacee is 'AllMap') || (e.Replacee is 'SuperMap') ) e.Replacement = 'Omnisight'; + else if ( (e.Replacee is 'Infrared') || (e.Replacee is 'ArtiTorch') ) e.Replacement = 'SWWMLamp'; + else if ( (e.Replacee is 'GreenArmor') || (e.Replacee is 'SilverShield') || (e.Replacee is 'PlatinumHelm') || (e.Replacee is 'AmuletOfWarding') ) e.Replacement = 'BlastSuitItem'; + else if ( (e.Replacee is 'BlueArmor') || (e.Replacee is 'EnchantedShield') || (e.Replacee is 'MeshArmor') || (e.Replacee is 'FalconShield') ) e.Replacement = 'WarArmorItem'; + else if ( (e.Replacee is 'ArtiDarkServant') || (e.Replacee == 'ArtiTeleportOther') ) e.Replacement = 'ChanceboxSpawner'; + else if ( e.Replacee is 'ArtiTeleport' ) e.Replacement = (gameinfo.GameType&GAME_Hexen)?'ChanceboxSpawner':'SWWMNothing'; + else return; + // this last part is kind of ugly, but it works + // guarantees that OUR replacements are all final + e.IsFinal = true; + } +} diff --git a/zscript/handler/swwm_handler_shaders.zsc b/zscript/handler/swwm_handler_shaders.zsc new file mode 100644 index 000000000..1554014eb --- /dev/null +++ b/zscript/handler/swwm_handler_shaders.zsc @@ -0,0 +1,146 @@ +// shaders stuff + +extend Class SWWMHandler +{ + static clearscope void ClearAllShaders( PlayerInfo p ) + { + Shader.SetEnabled(p,"WaterWarp",false); + Shader.SetEnabled(p,"LavaWarp",false); + Shader.SetEnabled(p,"SlimeWarp",false); + Shader.SetEnabled(p,"ZoomBlur",false); + Shader.SetEnabled(p,"RagekitShader",false); + Shader.SetEnabled(p,"GhostShader",false); + Shader.SetEnabled(p,"InvinciShader",false); + Shader.SetEnabled(p,"Glitch",false); + Shader.SetEnabled(p,"Grain",false); + } + + private ui void RenderShaders( RenderEvent e ) + { + PlayerInfo p = players[consoleplayer]; + let mo = p.mo; + if ( !mo ) return; + bool pc = (p.camera == mo); + let rage = RagekitPower(mo.FindInventory("RagekitPower")); + if ( pc && rage && swwm_shaders ) + { + if ( swwm_rageshader ) + { + Shader.SetEnabled(p,"RagekitShader",false); + Shader.SetEnabled(p,"RagekitAltShader",true); + Shader.SetUniform1f(p,"RagekitAltShader","timer",(gametic+e.FracTic)/GameTicRate); + double xstrastr = 1.+max(0,rage.lastpulse-(gametic+e.Fractic))/35.; + Shader.SetUniform1f(p,"RagekitAltShader","xtrastr",xstrastr**2.); + } + else + { + Shader.SetEnabled(p,"RagekitAltShader",false); + Shader.SetEnabled(p,"RagekitShader",true); + Shader.SetUniform1f(p,"RagekitShader","timer",(gametic+e.FracTic)/GameTicRate); + double xstrastr = 1.+max(0,rage.lastpulse-(gametic+e.Fractic))/35.; + Shader.SetUniform1f(p,"RagekitShader","xtrastr",xstrastr**2.); + } + } + else + { + Shader.SetEnabled(p,"RagekitShader",false); + Shader.SetEnabled(p,"RagekitAltShader",false); + } + let ghost = GhostPower(mo.FindInventory("GhostPower")); + if ( pc && ghost && swwm_shaders ) Shader.SetEnabled(p,"GhostShader",true); + else Shader.SetEnabled(p,"GhostShader",false); + let sunny = InvinciballPower(mo.FindInventory("InvinciballPower")); + if ( pc && sunny && swwm_shaders ) + { + Shader.SetEnabled(p,"InvinciShader",true); + double str = max(0,sunny.lastpulse-(gametic+e.Fractic))/35.; + Shader.SetUniform1f(p,"InvinciShader","str",str); + } + else Shader.SetEnabled(p,"InvinciShader",false); + let coat = BarrierPower(mo.FindInventory("BarrierPower")); + if ( pc && coat && swwm_shaders ) + { + Shader.SetEnabled(p,"BarrierShader",true); + Shader.SetUniform1f(p,"BarrierShader","timer",(gametic+e.FracTic)/GameTicRate); + } + else Shader.SetEnabled(p,"BarrierShader",false); + if ( pc && (mo is 'Demolitionist') && swwm_shaders ) + { + let demo = Demolitionist(mo); + if ( demo.lastunder == Demolitionist.UNDER_WATER ) + { + Shader.SetEnabled(p,"WaterWarp",true); + Shader.SetUniform1f(p,"WaterWarp","timer",(gametic+e.FracTic)/GameTicRate); + Shader.SetUniform1f(p,"WaterWarp","dfact",coat?.25:1.); + Shader.SetUniform3f(p,"WaterWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.)); + + } + else Shader.SetEnabled(p,"WaterWarp",false); + if ( demo.lastunder == Demolitionist.UNDER_LAVA ) + { + Shader.SetEnabled(p,"LavaWarp",true); + Shader.SetUniform1f(p,"LavaWarp","timer",(gametic+e.FracTic)/GameTicRate); + Shader.SetUniform1f(p,"LavaWarp","dfact",coat?.25:1.); + Shader.SetUniform3f(p,"LavaWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.)); + } + else Shader.SetEnabled(p,"LavaWarp",false); + if ( demo.lastunder == Demolitionist.UNDER_SLIME ) + { + Shader.SetEnabled(p,"SlimeWarp",true); + Shader.SetUniform1f(p,"SlimeWarp","timer",(gametic+e.FracTic)/GameTicRate); + Shader.SetUniform1f(p,"SlimeWarp","dfact",coat?.25:1.); + Shader.SetUniform3f(p,"SlimeWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.)); + } + else Shader.SetEnabled(p,"SlimeWarp",false); + int lastdmg = (demo.Health>0)?demo.lastdamage:Random[Flicker](60,80); + int lastdmgtic = (demo.Health>0)?demo.lastdamagetic:(gametic+Random[Flicker](30,20)); + double noiz = min(lastdmg*.09*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.5); + Shader.SetEnabled(p,"Glitch",noiz>0); + Shader.SetEnabled(p,"Grain",noiz>0); + if ( noiz > 0 ) + { + Shader.SetUniform1f(p,"Glitch","Timer",(gametic+e.FracTic)/GameTicRate); + Shader.SetUniform1f(p,"Grain","Timer",(gametic+e.FracTic)/GameTicRate); + Shader.SetUniform1f(p,"Grain","ni",noiz); + noiz = min(lastdmg*.08*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.8); + Shader.SetUniform1f(p,"Glitch","str1",noiz); + noiz = min(lastdmg*.03*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),3.5); + Shader.SetUniform1f(p,"Glitch","str2",noiz); + } + if ( !demo.InStateSequence(demo.CurState,demo.FindState("Dash")) ) + { + Shader.SetEnabled(p,"ZoomBlur",false); + return; + } + Shader.SetEnabled(p,"ZoomBlur",true); + Vector3 vel = demo.vel+demo.dashdir*demo.dashboost; + double baumpu = max(0.,(demo.bumptic-(gametic+e.Fractic))/35.); + vel += demo.dashdir*baumpu; + double spd = vel.length(); + Vector3 worlddir = vel/spd; + Shader.SetUniform1f(p,"ZoomBlur","Fade",clamp((spd-20.)/60.,0.,1.)); + double str = min(spd/40.,15.); + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(e.ViewPitch,e.ViewAngle,e.ViewRoll); + Vector3 reldir = (worlddir dot y, worlddir dot z, worlddir dot x); + Vector2 centerspot = (.5+reldir.x*.5,.5+reldir.y*.5); + if ( reldir.z < 0 ) + { + centerspot.x = 1.-centerspot.x; + centerspot.y = 1.-centerspot.y; + str *= -1; + } + Shader.SetUniform1f(p,"ZoomBlur","Str",str); + Shader.SetUniform2f(p,"ZoomBlur","CenterSpot",centerspot); + } + else + { + Shader.SetEnabled(p,"WaterWarp",false); + Shader.SetEnabled(p,"LavaWarp",false); + Shader.SetEnabled(p,"SlimeWarp",false); + Shader.SetEnabled(p,"Glitch",false); + Shader.SetEnabled(p,"Grain",false); + Shader.SetEnabled(p,"ZoomBlur",false); + } + } +} diff --git a/zscript/handler/swwm_handler_vanillaboss.zsc b/zscript/handler/swwm_handler_vanillaboss.zsc new file mode 100644 index 000000000..7f56cd877 --- /dev/null +++ b/zscript/handler/swwm_handler_vanillaboss.zsc @@ -0,0 +1,399 @@ +// vanilla boss stuff + +extend Class SWWMHandler +{ + String bosstag; + Array bossactors; + + Actor bossbrainactor; + Actor bossviewactor; + TextureID facetex[5]; + + bool initialized; + ui bool ui_initialized; + ui TextureID bbar_f, bbar_r, bbar_d; + ui double bossalpha; + ui DynamicValueInterpolator ihealth, ihealthr; + ui int thealth, hmax; + ui int oldhealth[30]; + ui int cummdamage, lastcummtic; // please do not misread + + enum EVanillaMap + { + MAP_NONE, + MAP_DE1M8, + MAP_DE2M8, + MAP_DE3M8, + MAP_DE4M8, + MAP_HE1M8_HE4M8, + MAP_HE2M8_HE5M8, + MAP_HE3M8, + MAP_DMAP07, + MAP_DMAP30, + MAP_HMAP12, + MAP_HMAP23_HMAP27_HMAP48_HMAP55, + MAP_HMAP36, + MAP_HMAP37, + MAP_HMAP38, + MAP_HMAP40, + MAP_HMAP60, + MAP_EVMAP30 // eviternity + }; + + private int WhichVanillaBossMap() + { + String mapsum = level.GetChecksum(); + if ( (mapsum ~== "94500F4B006B316FE03AC46865AEABF8") + || (mapsum ~== "97079958C7E89C1908890730B8B9FEB7") + || (mapsum ~== "058FB092EA1B70DA1E3CBF501C4A91A1") ) + return MAP_DE1M8; + if ( mapsum ~== "EFFE91DF41AD41F6973C06F0AD67DDB9" ) + return MAP_DE2M8; + if ( mapsum ~== "EF128313112110ED6C1549AF96AF26C9" ) + return MAP_DE3M8; + if ( mapsum ~== "2DC939E508AB8EB68AF79D5B60568711" ) + return MAP_DE4M8; + if ( (mapsum ~== "27639D04F8090D57A47D354992435893") + || (mapsum ~== "30D1480A6D4F3A3153739D4CCF659C4E") ) + return MAP_HE1M8_HE4M8; + if ( (mapsum ~== "5158C22A0F30CE5E558FD2A05D67685E") + || (mapsum ~== "85AC7D20D18F9BC49B9696CC2E67F029") ) + return MAP_HE2M8_HE5M8; + if ( mapsum ~== "4719C2C71EF28F52310B889DD5A9778B" ) + return MAP_HE3M8; + if ( mapsum ~== "291F24417FB3DD411339AE82EF9B3597" ) + return MAP_DMAP07; + if ( mapsum ~== "5EECD88F4491F516D590CE4BBF45F532" ) + return MAP_DMAP30; + if ( (mapsum ~== "89C4CD26EF05E2577B10CAFE56226662") + || (mapsum ~== "441BF111747671066A10A146C03EEFC4") + || (mapsum ~== "55E321849F3699655D7E062C90682F63") ) + return MAP_HMAP12; + if ( (mapsum ~== "E3B06F44DBF6F7E7754D7B1DAEF707E4") + || (mapsum ~== "FC832437D7A2B7094A9B56C3909773D9") + || (mapsum ~== "91AD797F95CC4C6D6AE33B21F664C60B") + || (mapsum ~== "188B1B4244BD8DA501D8532696EC8654") + || (mapsum ~== "5B29D0889DF09A8250D62FA09EB2B452") + || (mapsum ~== "D3C5FA777BA52264546E6569F167AF0D") + || (mapsum ~== "15FC0991D975325556EFF71F241A4458") + || (mapsum ~== "2FAD54B58487884F06EAFA507B553921") ) + return MAP_HMAP23_HMAP27_HMAP48_HMAP55; + if ( (mapsum ~== "4444C95C2029DA6EECAC92DAA31CE665") + || (mapsum ~== "33752742BCA8E539A6EE3E5D0FDA8744") + || (mapsum ~== "3FFAF2F624C1B4BB6F581DCF7B99CBA7") ) + return MAP_HMAP36; + if ( (mapsum ~== "78979A583B1E30D94C9DAE2BCFA9A18D") + || (mapsum ~== "FDC90F44C65A71E0901C1B9FFFCF3D02") + || (mapsum ~== "088ECE0E0F3E68448FA1D901001A0084") ) + return MAP_HMAP37; + if ( (mapsum ~== "3BF62E4F9FB3CF9AF267421CE2D5F348") + || (mapsum ~== "4799E1FDB5A3C0E3AD650B5AC215A737") + || (mapsum ~== "5C63A02B0B04D9AE95CA51687DC3406F") ) + return MAP_HMAP38; + if ( (mapsum ~== "EFAFE59092DE5E613562ACF52B86C37F") + || (mapsum ~== "1C5DE5A921DEE405E98E7E09D9829387") + || (mapsum ~== "2A6C4235B942467D25FD50D5B313E67A") ) + return MAP_HMAP40; + if ( mapsum ~== "B0ADDB295A3ACCE43978AAC91FB8C58A" ) + return MAP_HMAP60; + if ( mapsum ~== "5C5E5C08AF3572F31CF27318679F2B4E" ) + return MAP_EVMAP30; + return MAP_NONE; + } + private void VanillaBossSpawn( WorldEvent e, SWWMCombatTracker trk ) + { + bool upgrademe = swwm_upgradebosses; + if ( bossmap == -1 ) bossmap = WhichVanillaBossMap(); + if ( bossmap == MAP_DE1M8 ) + { + if ( e.Thing is 'BaronOfHell' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3; + if ( trk ) trk.bBOSS = true; + } + bosstag = "$BT_BRUISERS"; + } + else if ( bossmap == MAP_DE2M8 ) + { + if ( e.Thing is 'Cyberdemon' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; + if ( trk ) trk.bBOSS = true; + } + bosstag = "$BT_CYBIE"; + } + else if ( bossmap == MAP_DE3M8 ) + { + if ( e.Thing is 'Spidermastermind' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 6; + if ( trk ) trk.bBOSS = true; + } + bosstag = "$BT_SPIDER"; + } + else if ( bossmap == MAP_DE4M8 ) + { + if ( e.Thing is 'Spidermastermind' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; + if ( trk ) trk.bBOSS = true; + } + bosstag = "$BT_SPIDER2"; + } + else if ( bossmap == MAP_DMAP07 ) + { + if ( (e.Thing is 'Fatso') || (e.Thing is 'Arachnotron') ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; + if ( trk ) trk.bBOSS = true; + } + bosstag = "$BT_DIMPLE"; + } + else if ( bossmap == MAP_DMAP30 ) + { + if ( e.Thing is 'BossBrain' ) + { + bossbrainactor = e.Thing; + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 40; // goodbye, instakills + if ( trk ) trk.bBOSS = true; + } + if ( e.Thing is 'BossEye' ) + bossviewactor = e.Thing; + bosstag = "$BT_IOS"; + } + else if ( bossmap == MAP_HE1M8_HE4M8 ) + { + if ( e.Thing is 'IronLich' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; + if ( trk ) trk.bBOSS = true; + } + bosstag = "$BT_LICHES"; + } + else if ( bossmap == MAP_HE2M8_HE5M8 ) + { + if ( e.Thing is 'Minotaur' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3; + if ( trk ) trk.bBOSS = true; + } + bosstag = "$BT_MINOTAUR"; + } + else if ( bossmap == MAP_HE3M8 ) + { + if ( e.Thing is 'Sorcerer1' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_DSPARIL"; + } + else if ( e.Thing is 'Sorcerer2' ) + { + // second phase + bossactors.Clear(); + initialized = false; + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_DSPARIL2"; + } + } + else if ( bossmap == MAP_HMAP38 ) + { + if ( e.Thing is 'ClericBoss' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_CLERIC"; + } + } + else if ( bossmap == MAP_HMAP36 ) + { + if ( e.Thing is 'FighterBoss' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_FIGHTER"; + } + } + else if ( bossmap == MAP_HMAP37 ) + { + if ( e.Thing is 'MageBoss' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_MAGE"; + } + } + else if ( bossmap == MAP_HMAP12 ) + { + if ( e.Thing is 'Dragon' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_DRAGON"; + } + } + else if ( bossmap == MAP_HMAP23_HMAP27_HMAP48_HMAP55 ) + { + if ( e.Thing is 'Heresiarch' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_HERESIARCH"; + } + } + else if ( bossmap == MAP_HMAP40 ) + { + if ( e.Thing is 'Korax' ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 10; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_KORAX"; + } + } + else if ( bossmap == MAP_HMAP60 ) + { + if ( (e.Thing is 'FighterBoss') || (e.Thing is 'ClericBoss') || (e.Thing is 'MageBoss') ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_DEATHKINGS"; + initialized = true; // healthbar shows from the start + } + } + else if ( bossmap == MAP_EVMAP30 ) + { + if ( e.Thing.GetClassName() == "ArchangelusA" ) + { + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_ARCHANGELUS"; + } + else if ( e.Thing.GetClassName() == "ArchangelusB" ) + { + // second phase + bossactors.Clear(); + initialized = false; + bossactors.Push(e.Thing); + if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; + if ( trk ) trk.bBOSS = true; + bosstag = "$BT_ARCHANGELUS"; + } + } + } + private void VanillaBossTick() + { + if ( initialized ) return; + // wait until bosses are active + for ( int i=0; i 0 ) + { + cummdamage += curcumm; + lastcummtic = gametic; + } + else if ( gametic > lastcummtic+150 ) cummdamage = 0; + thealth = newhealth; + ihealthr.Update(thealth); + if ( thealth > oldhealth[29] ) + for ( int i=29; i>0; i-- ) + oldhealth[i] = thealth; + ihealth.Update(oldhealth[29]); + for ( int i=29; i>0; i-- ) + oldhealth[i] = oldhealth[i-1]; + if ( thealth > 0 ) bossalpha = min(3.,bossalpha+1./30.); + else bossalpha = max(0,bossalpha-1./50.); + } + // called by HUD (done here for the sake of cleaner code) + ui void DrawBossBar( SWWMStatusBar bar ) + { + if ( !ui_initialized || (bossalpha <= 0.) ) return; + if ( !swwm_bosshealthbars ) return; + if ( !bbar_f ) bbar_f = TexMan.CheckForTexture("graphics/HUD/BossHealthBarBox.png",TexMan.Type_Any); + if ( !bbar_r ) bbar_r = TexMan.CheckForTexture("graphics/HUD/BossHealthBar.png",TexMan.Type_Any); + if ( !bbar_d ) bbar_d = TexMan.CheckForTexture("graphics/HUD/BossHealthBarDecay.png",TexMan.Type_Any); + Vector2 vpos = ((bar.ss.x-300)/2.,bar.ss.y-(bar.margin+35)); + Screen.DrawTexture(bbar_f,false,vpos.x-2,vpos.y-2,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha); + int rw = int(clamp((ihealthr.GetValue()*300.)/hmax,0.,300.)); + int dw = int(clamp((ihealth.GetValue()*300.)/hmax,0.,300.)); + Screen.DrawTexture(bbar_d,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,dw); + Screen.DrawTexture(bbar_r,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,rw); + Font barfnt = bar.LangFont(bar.mTewiFont); + Font dmgfnt = bar.mTewiFont.mFont; + if ( (cummdamage > 0) && (gametic < lastcummtic+150) ) + { + double calph = clamp(((lastcummtic+150)-gametic)/50.,0.,1.); + string dnum = String.Format("%d",cummdamage); + Screen.DrawText(dmgfnt,Font.CR_RED,vpos.x+300-dmgfnt.StringWidth(dnum),vpos.y-(dmgfnt.GetHeight()+2),dnum,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha*calph); + } + Screen.DrawText(barfnt,Font.CR_WHITE,vpos.x,vpos.y-(barfnt.GetHeight()+2),StringTable.Localize(swwm_funtags?(bosstag.."_FUN"):bosstag),DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha); + } + + // can't use this until I actually figure out how to make those walls damageable + /*override void WorldLineDamaged( WorldEvent e ) + { + // allow boss brain to take (reduced) damage from the facewall being shot + if ( level.mapname ~== "MAP30" ) + { + if ( !SWWMUtility.IsIOSWall(e.DamageLine) ) return; + if ( bossbrainactor ) + bossbrainactor.DamageMobj(e.Inflictor,e.DamageSource,e.Damage/3,e.DamageType,e.DamageFlags,e.DamageAngle); + e.NewDamage = 0; + } + }*/ +} diff --git a/zscript/swwm_hud.zsc b/zscript/hud/swwm_hud.zsc similarity index 100% rename from zscript/swwm_hud.zsc rename to zscript/hud/swwm_hud.zsc diff --git a/zscript/swwm_hudextra.zsc b/zscript/hud/swwm_hudextra.zsc similarity index 100% rename from zscript/swwm_hudextra.zsc rename to zscript/hud/swwm_hudextra.zsc diff --git a/zscript/items/swwm_ammoextra.zsc b/zscript/items/swwm_ammoextra.zsc new file mode 100644 index 000000000..c90a5257c --- /dev/null +++ b/zscript/items/swwm_ammoextra.zsc @@ -0,0 +1,501 @@ +// ============================================================================ +// Ammo fabricator +// ============================================================================ + +Class AmmoFabricator : Inventory abstract +{ + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + int budget, pertype, maxunitprice; + + Property Budget : budget; + Property PerType : pertype; + Property MaxUnitPrice : maxunitprice; + + override Inventory CreateCopy( Actor other ) + { + // additional lore + SWWMLoreLibrary.Add(other.player,"Fabricator"); + return Super.CreateCopy(other); + } + + private bool CmpFabAmmo( Class a, Class b ) + { + let ia = Owner.FindInventory(a); + int cnta = ia?ia.Amount:0; + int maxa = ia?ia.MaxAmount:GetDefaultByType(a).Amount; + let ib = Owner.FindInventory(b); + int cntb = ib?ib.Amount:0; + int maxb = ib?ib.MaxAmount:GetDefaultByType(b).Amount; + double facta = cnta/double(maxa); + double factb = cntb/double(maxb); + return (facta >= factb); + } + + private int partition_fabammo( Array > a, int l, int h ) + { + Class pv = a[h]; + int i = (l-1); + for ( int j=l; j<=(h-1); j++ ) + { + if ( CmpFabAmmo(pv,a[j]) ) + { + i++; + Class tmp = a[j]; + a[j] = a[i]; + a[i] = tmp; + } + } + Class tmp = a[h]; + a[h] = a[i+1]; + a[i+1] = tmp; + return i+1; + } + private void qsort_fabammo( Array > a, int l, int h ) + { + if ( l >= h ) return; + int p = partition_fabammo(a,l,h); + qsort_fabammo(a,l,p-1); + qsort_fabammo(a,p+1,h); + } + + bool FabricateAmmo() + { + Array > available; + // populate ammo production list + for ( int i=0; i)(AllActorClasses[i]); + // skip over candy gun spares, they're "special ammo" + if ( a == 'CandyGunSpares' ) continue; + // only direct descendants of ammo with a set price below our max unit price + if ( !a || (a.GetParentClass() != 'Ammo') ) continue; + let def = GetDefaultByType(a); + if ( (def.Stamina <= 0) || (def.Stamina > maxunitprice) ) continue; + // only ammo for weapons that are valid (can be used) + bool isvalid = false; + for ( int j=0; j)(AllActorClasses[j]); + if ( !type ) continue; + let rep = GetReplacement(type); + if ( (rep != type) && !(rep is "DehackedPickup") ) continue; + readonly weap = GetDefaultByType(type); + if ( !Owner.player || !Owner.player.weapons.LocateWeapon(type) || weap.bCheatNotWeapon ) continue; + let ready = weap.FindState("Ready"); + if ( !ready || !ready.ValidateSpriteFrame() ) continue; + if ( (type is 'SWWMWeapon') && SWWMWeapon(weap).UsesAmmo(a) ) + { + isvalid = true; + break; + } + if ( (weap.AmmoType1 == a) || (weap.AmmoType2 == a) ) + { + isvalid = true; + break; + } + } + if ( !isvalid ) continue; + available.Push(a); + } + // sort by "need weight" (prioritize ammo that the player lacks over ammo that the player has plenty of + qsort_fabammo(available,0,available.Size()-1); + // loop through until we fill the inventory or run out of budget + bool given = false; + int consumed = 0; + String fabstr = ""; + bool comma = false; + int tpertype = pertype; + for ( int i=0; i 0 ) + { + if ( comma ) fabstr.AppendFormat(", %dx %s",cnt,cur.GetTag()); + else fabstr.AppendFormat("%dx %s",cnt,cur.GetTag()); + comma = true; + } + } + if ( given ) PrintPickupMessage(true,fabstr); + return given; + } + + override bool Use( bool pickup ) + { + bool shouldautouse = false; + if ( swwm_enforceautouseammo == 1 ) shouldautouse = true; + else if ( swwm_enforceautouseammo == -1 ) shouldautouse = false; + else shouldautouse = CVar.GetCVar('swwm_autouseammo',Owner.player).GetBool(); + if ( pickup && !shouldautouse ) return false; + if ( FabricateAmmo() ) + { + if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); + return true; + } + return false; + } + + Default + { + +INVENTORY.INVBAR; + +INVENTORY.AUTOACTIVATE; + +FLOATBOB; + Inventory.UseSound "fabricator/use"; + Inventory.PickupFlash "SWWMPickupFlash"; + FloatBobStrength 0.25; + Radius 10; + Height 24; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + +Class FabricatorTier1 : AmmoFabricator +{ + Mixin SWWMAutoUseFix; + + Default + { + Tag "$T_FABRICATOR1"; + Stamina -2500; + Inventory.Icon "graphics/HUD/Icons/I_Fabricator1.png"; + Inventory.PickupMessage "$T_FABRICATOR1"; + Inventory.MaxAmount 20; + Inventory.InterHubAmount 20; + AmmoFabricator.Budget 3000; + AmmoFabricator.PerType 1; + AmmoFabricator.MaxUnitPrice 2500; + } +} +Class FabricatorTier2 : AmmoFabricator +{ + Mixin SWWMAutoUseFix; + + Default + { + Tag "$T_FABRICATOR2"; + Stamina -12000; + Inventory.Icon "graphics/HUD/Icons/I_Fabricator2.png"; + Inventory.PickupMessage "$T_FABRICATOR2"; + Inventory.MaxAmount 15; + Inventory.InterHubAmount 15; + AmmoFabricator.Budget 15000; + AmmoFabricator.PerType 2; + AmmoFabricator.MaxUnitPrice 12000; + } +} +Class FabricatorTier3 : AmmoFabricator +{ + Mixin SWWMAutoUseFix; + + Default + { + Tag "$T_FABRICATOR3"; + Stamina -80000; + Inventory.Icon "graphics/HUD/Icons/I_Fabricator3.png"; + Inventory.PickupMessage "$T_FABRICATOR3"; + Inventory.MaxAmount 10; + Inventory.InterHubAmount 10; + AmmoFabricator.Budget 100000; + AmmoFabricator.PerType 4; + AmmoFabricator.MaxUnitPrice 80000; + } +} +Class FabricatorTier4 : AmmoFabricator +{ + Default + { + Tag "$T_FABRICATOR4"; + Stamina -1000000; + Inventory.Icon "graphics/HUD/Icons/I_Fabricator4.png"; + Inventory.PickupMessage "$T_FABRICATOR4"; + Inventory.MaxAmount 5; + Inventory.InterHubAmount 5; + AmmoFabricator.Budget int.max; + AmmoFabricator.PerType -50; + AmmoFabricator.MaxUnitPrice 1000000; + -INVENTORY.AUTOACTIVATE; + } +} + +// ============================================================================ +// Hammerspace embiggener +// ============================================================================ + +Class HammerspaceEmbiggener : Inventory +{ + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + override Inventory CreateCopy( Actor other ) + { + bool traded = (GetClass()=='TradedHammerspaceEmbiggener'); + if ( !traded ) other.A_StartSound("powerup/embiggener",CHAN_ITEMEXTRA); + // Find every unique type of ammoitem. Give it to the player if + // he doesn't have it already, and increase its maximum capacity. + for ( int i=0; i)(AllActorClasses[i]); + if ( !type || (type.GetParentClass() != 'Ammo') ) continue; + // check that it's for a valid weapon + bool isvalid = false; + for ( int j=0; j)(AllActorClasses[j]); + if ( !type2 ) continue; + let rep = GetReplacement(type2); + if ( (rep != type2) && !(rep is "DehackedPickup") ) continue; + readonly weap = GetDefaultByType(type2); + if ( !other.player || !other.player.weapons.LocateWeapon(type2) || weap.bCheatNotWeapon ) continue; + let ready = weap.FindState("Ready"); + if ( !ready || !ready.ValidateSpriteFrame() ) continue; + if ( (type2 is 'SWWMWeapon') && SWWMWeapon(weap).UsesAmmo(type) ) + { + isvalid = true; + break; + } + if ( (weap.AmmoType1 == type) || (weap.AmmoType2 == type) ) + { + isvalid = true; + break; + } + } + if ( !isvalid ) continue; + let ammoitem = Ammo(other.FindInventory(type)); + int amount = GetDefaultByType(type).BackpackAmount*self.Amount; + if ( traded ) amount = 0; + if ( amount < 0 ) amount = 0; + if ( !ammoitem ) + { + // The player did not have the ammoitem. Add it. + ammoitem = Ammo(Spawn(type)); + ammoitem.Amount = amount; + if ( ammoitem.BackpackMaxAmount > 0 ) + { + double factor = (ammoitem.BackpackMaxAmount-ammoitem.default.MaxAmount)/double(MaxAmount); + ammoitem.MaxAmount = int(ammoitem.default.MaxAmount+min(self.Amount,MaxAmount)*factor); + } + if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup ) + ammoitem.Amount = ammoitem.MaxAmount; + ammoitem.AttachToOwner(other); + } + else + { + // The player had the ammoitem. Give some more. + if ( ammoitem.BackpackMaxAmount > 0 ) + { + double factor = (ammoitem.BackpackMaxAmount-ammoitem.default.MaxAmount)/double(MaxAmount); + ammoitem.MaxAmount = int(ammoitem.default.MaxAmount+min(self.Amount,MaxAmount)*factor); + } + if ( ammoitem.Amount < ammoitem.MaxAmount ) + { + if ( (ammoitem.Amount > 0) && (ammoitem.Amount+amount < 0) ) + ammoitem.Amount = int.max; + else ammoitem.Amount += amount; + if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup ) + ammoitem.Amount = ammoitem.MaxAmount; + } + } + } + self.Amount = min(self.Amount,MaxAmount); + if ( GetParentClass() == 'HammerspaceEmbiggener' ) + { + if ( !GoAway() ) Destroy(); + let copy = Inventory(Spawn('HammerspaceEmbiggener')); + copy.ClearCounters(); + copy.Amount = self.Amount; + copy.MaxAmount = self.MaxAmount; + return copy; + } + if ( GoAway() ) + { + let copy = Inventory(Spawn(GetClass())); + copy.ClearCounters(); + copy.Amount = self.Amount; + copy.MaxAmount = self.MaxAmount; + return copy; + } + return self; + } + + override bool HandlePickup( Inventory item ) + { + if ( (item.GetClass() == GetClass()) || (item.GetParentClass() == 'HammerspaceEmbiggener') ) + { + bool traded = (item.GetClass()=='TradedHammerspaceEmbiggener'); + if ( !traded ) Owner.A_StartSound("powerup/embiggener",CHAN_ITEMEXTRA); + if ( (Amount > 0) && (Amount+item.Amount < 0) ) + Amount = int.max; + else Amount += item.Amount; + if ( Amount > MaxAmount ) Amount = MaxAmount; + item.bPickupGood = true; + // readjust ammo values to new capacity + for ( Inventory i=Owner.Inv; i; i=i.Inv ) + { + if ( !(i is 'Ammo') ) continue; + if ( Ammo(i).BackpackMaxAmount > 0 ) + { + double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(MaxAmount); + i.MaxAmount = int(i.default.MaxAmount+Amount*factor); + } + int amount = Ammo(i).BackpackAmount*item.Amount; + if ( traded ) i.Amount = 0; + if ( (i.Amount > 0) && (i.Amount+amount < 0) ) + i.Amount = int.max; + else i.Amount += amount; + if ( (i.Amount > i.MaxAmount) && !sv_unlimited_pickup ) + i.Amount = i.MaxAmount; + } + return true; + } + // new ammo suddenly added? upgrade it (this shouldn't happen unless fucky scripting has been involved) + if ( (item is 'Ammo') && !Owner.FindInventory(Ammo(item).GetParentAmmo()) ) + { + if ( Ammo(item).BackpackMaxAmount > 0 ) + { + double factor = (Ammo(item).BackpackMaxAmount-item.default.MaxAmount)/double(MaxAmount); + item.MaxAmount = int(item.default.MaxAmount+Amount*factor); + } + } + return false; + } + + override void DepleteOrDestroy() + { + // reset upgrade + for ( Inventory i=Owner.Inv; i; i=i.Inv ) + { + if ( !(i is 'Ammo') ) continue; + i.MaxAmount = i.default.MaxAmount; + if ( i.Amount > i.MaxAmount ) i.Amount = i.MaxAmount; + } + Super.DepleteOrDestroy(); + } + + // merges overlapping embiggeners into a bulk upgrade + void A_MergeEmbiggeners() + { + let bt = BlockThingsIterator.Create(self,16); + int tamount = Amount; + while ( bt.Next() ) + { + let t = bt.Thing; + if ( !t || (t == self) || !(t is 'HammerspaceEmbiggener') || !(t.spawnpoint ~== spawnpoint) ) continue; + tamount += HammerspaceEmbiggener(t).Amount; + t.ClearCounters(); + t.Destroy(); + } + if ( tamount <= 1 ) return; + tamount -= tamount%2; // always even numbered + if ( GetClass() == 'BulkHammerspaceEmbiggener' ) + { + Amount = min(tamount,MaxAmount); + return; + } + let n = Spawn("BulkHammerspaceEmbiggener",pos); + Inventory(n).Amount = min(tamount,MaxAmount); + n.spawnpoint = spawnpoint; + n.spawnangle = spawnangle; + n.angle = angle; + n.pitch = pitch; + n.roll = roll; + n.special = special; + for ( int i=0; i<5; i++ ) n.args[i] = args[i]; + n.special1 = special1; + n.special2 = special2; + n.spawnflags = spawnflags&~MTF_SECRET; + n.HandleSpawnFlags(); + n.spawnflags = spawnflags; + n.bCountSecret = spawnflags&MTF_SECRET; + n.ChangeTid(tid); + n.vel = vel; + n.master = master; + n.tracer = tracer; + n.target = target; + if ( !bDROPPED ) n.bDROPPED = false; + ClearCounters(); + Destroy(); + } + + Default + { + Tag "$T_EMBIGGENER"; + Stamina -800000; + Inventory.PickupMessage "$T_EMBIGGENER"; + Inventory.MaxAmount 8; + Inventory.InterHubAmount 8; + Inventory.PickupFlash "SWWMPickupFlash"; + +INVENTORY.UNDROPPABLE; + +INVENTORY.UNTOSSABLE; + +INVENTORY.ALWAYSPICKUP; + +COUNTITEM; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 8; + Height 24; + } + States + { + Spawn: + XZW1 A -1 NoDelay A_MergeEmbiggeners(); + Stop; + } +} + +// used when cheating or trading, this version does not give ammo and is meant +// only for GiveInventory, so it shouldn't be spawned in the world +Class TradedHammerspaceEmbiggener : HammerspaceEmbiggener {} + +// used to denote "merged" embiggeners, changes color based on amount +// green (2+) +// blue (4+) +// purple (6+) +// black (8+) +Class BulkHammerspaceEmbiggener : HammerspaceEmbiggener +{ + override string PickupMessage() + { + return String.Format("%dx %s",Amount,StringTable.Localize("$T_BULKEMBIGGENER")); + } + States + { + Spawn: + XZW1 A -1 NoDelay + { + A_MergeEmbiggeners(); + if ( bDestroyed ) return ResolveState(null); + if ( Amount > 1 ) return SpawnState+min(4,Amount/2); + return ResolveState(null); + } + XZW1 BCDE -1; + Stop; + } +} diff --git a/zscript/items/swwm_ammoitems.zsc b/zscript/items/swwm_ammoitems.zsc new file mode 100644 index 000000000..205946618 --- /dev/null +++ b/zscript/items/swwm_ammoitems.zsc @@ -0,0 +1,1040 @@ +// ============================================================================ +// Spreadgun / Wallbuster ammo +// ============================================================================ + +Class RedShell : Ammo +{ + Mixin SWWMShellAmmo; + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_REDSHELLS"; + Stamina 500; + Inventory.Icon "graphics/HUD/Icons/A_ShellsNormal.png"; + Inventory.Amount 1; + Inventory.MaxAmount 30; + Ammo.BackpackAmount 5; + Ammo.BackpackMaxAmount 100; + Ammo.DropAmount 2; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + Height 20; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class RedShell2 : RedShell +{ + Default + { + Inventory.Amount 2; + Radius 6; + } +} +Class RedShell4 : RedShell +{ + Default + { + Inventory.Amount 4; + Radius 6; + } +} + +Class GreenShell : Ammo +{ + Mixin SWWMShellAmmo; + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_GREENSHELLS"; + Stamina 800; + Inventory.Icon "graphics/HUD/Icons/A_ShellsSlug.png"; + Inventory.Amount 1; + Inventory.MaxAmount 25; + Ammo.BackpackAmount 4; + Ammo.BackpackMaxAmount 80; + Ammo.DropAmount 2; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + Height 20; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class GreenShell2 : GreenShell +{ + Default + { + Inventory.Amount 2; + Radius 6; + } +} +Class GreenShell4 : GreenShell +{ + Default + { + Inventory.Amount 4; + Radius 6; + } +} + +Class WhiteShell : Ammo +{ + Mixin SWWMShellAmmo; + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_WHITESHELLS"; + Stamina 1000; + Inventory.Icon "graphics/HUD/Icons/A_ShellsDragon.png"; + Inventory.Amount 1; + Inventory.MaxAmount 10; + Ammo.BackpackAmount 1; + Ammo.BackpackMaxAmount 30; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class WhiteShell2 : WhiteShell +{ + Default + { + Inventory.Amount 2; + Radius 6; + } +} +Class WhiteShell4 : WhiteShell +{ + Default + { + Inventory.Amount 4; + Radius 6; + } +} + +Class BlueShell : Ammo +{ + Mixin SWWMShellAmmo; + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_BLUESHELLS"; + Stamina 2500; + Inventory.Icon "graphics/HUD/Icons/A_ShellsKinylum.png"; + Inventory.Amount 1; + Inventory.MaxAmount 15; + Ammo.BackpackAmount 2; + Ammo.BackpackMaxAmount 40; + Ammo.DropAmount 2; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + Height 20; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class BlueShell2 : BlueShell +{ + Default + { + Inventory.Amount 2; + Radius 6; + } +} +Class BlueShell4 : BlueShell +{ + Default + { + Inventory.Amount 4; + Radius 6; + } +} + +Class BlackShell : Ammo +{ + Mixin SWWMShellAmmo; + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_BLACKSHELLS"; + Stamina 4000; + Inventory.Icon "graphics/HUD/Icons/A_ShellsFuck.png"; + Inventory.Amount 1; + Inventory.MaxAmount 10; + Ammo.BackpackAmount 0; + Ammo.BackpackMaxAmount 20; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + Height 20; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class BlackShell2 : BlackShell +{ + Default + { + Inventory.Amount 2; + Radius 6; + } +} +Class BlackShell4 : BlackShell +{ + Default + { + Inventory.Amount 4; + Radius 6; + } +} + +Class PurpleShell : Ammo +{ + Mixin SWWMShellAmmo; + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_PURPLESHELLS"; + Stamina 1500; + Inventory.Icon "graphics/HUD/Icons/A_ShellsBall.png"; + Inventory.Amount 1; + Inventory.MaxAmount 20; + Ammo.BackpackAmount 3; + Ammo.BackpackMaxAmount 60; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + Height 20; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class PurpleShell2 : PurpleShell +{ + Default + { + Inventory.Amount 2; + Radius 6; + } +} +Class PurpleShell4 : PurpleShell +{ + Default + { + Inventory.Amount 4; + Radius 6; + } +} + +Class GoldShellSparkle : Actor +{ + override void PostBeginPlay() + { + Scale *= FRandom[Goldy](.5,1.5); + vel.z = FRandom[Goldy](.5,1.5); + } + override void Tick() + { + if ( isFrozen() ) return; + SetOrigin(Vec3Offset(0,0,vel.z),true); + alpha -= .02; + if ( alpha <= 0 ) Destroy(); + } + Default + { + RenderStyle "Add"; + Scale 0.05; + +NOGRAVITY; + +NOBLOCKMAP; + +NOINTERACTION; + +DONTSPLASH; + +NOTELEPORT; + +FORCEXYBILLBOARD; + FloatBobPhase 0; + } + States + { + Spawn: + BLPF A -1 Bright; + Stop; + } +} + +Class GoldShell : Ammo +{ + Mixin SWWMShellAmmo; + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + action void A_GoldShellTrail() + { + if ( Random[Goldy](0,2) ) return; + Spawn("GoldShellSparkle",Vec3Offset(FRandom[Goldy](-radius,radius)/2.,FRandom[Goldy](-radius,radius)/2.,FRandom[Goldy](10,18))); + } + + Default + { + Tag "$T_GOLDSHELLS"; + Stamina -1000000; + Inventory.Icon "graphics/HUD/Icons/A_ShellsGold.png"; + Inventory.Amount 1; + Inventory.MaxAmount 7; + Ammo.BackpackAmount 0; + Ammo.BackpackMaxAmount 7; + Ammo.DropAmount 1; + +FLOATBOB; + +DONTGIB; // don't crush these, they're valuable + FloatBobStrength 0.25; + Radius 4; + Height 20; + } + States + { + Spawn: + XZW1 A 1 A_GoldShellTrail(); + Wait; + } +} +Class GoldShell2 : GoldShell +{ + Default + { + Inventory.Amount 2; + Radius 6; + } +} +Class GoldShell4 : GoldShell +{ + Default + { + Inventory.Amount 4; + Radius 6; + } +} + +// ============================================================================ +// Eviscerator ammo +// ============================================================================ + +Class EvisceratorShell : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_EVISHELLS"; + Inventory.PickupMessage "$T_EVISHELL"; + Stamina 3000; + Inventory.Icon "graphics/HUD/Icons/A_Eviscerator.png"; + Inventory.Amount 1; + Inventory.MaxAmount 20; + Ammo.BackpackAmount 5; + Ammo.BackpackMaxAmount 90; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 8; + Height 22; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,4); + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + +Class EvisceratorSixPack : EvisceratorShell +{ + Default + { + Tag "$I_EVISHELLPAK"; + Inventory.PickupMessage "$I_EVISHELLPAK"; + Inventory.Amount 6; + Radius 20; + Height 24; + } +} + +Class EvisceratorTrioSpawn : Actor +{ + override void PostBeginPlay() + { + if ( bCOUNTSECRET ) level.total_secrets--; + for ( int i=0; i<3; i++ ) + { + let a = Spawn("EvisceratorShell",Vec3Angle(12,i*120)); + a.special = special; + a.angle = i*120; + a.FloatBobPhase = FloatBobPhase; + for ( int j=0; j<5; j++ ) a.args[j] = args[j]; + if ( bCOUNTSECRET ) + { + a.bCOUNTSECRET = true; + level.total_secrets++; + } + } + Destroy(); + } +} + +// ============================================================================ +// Hellblazer ammo +// ============================================================================ + +Class HellblazerMissiles : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_HELLMISSILES"; + Inventory.PickupMessage "$T_HELLMISSILE"; + Stamina 8000; + Inventory.Icon "graphics/HUD/Icons/A_HellblazerMissile.png"; + Inventory.Amount 1; + Inventory.MaxAmount 20; + Ammo.BackpackAmount 3; + Ammo.BackpackMaxAmount 60; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 6; + Height 22; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,3); + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class HellblazerMissileMag : HellblazerMissiles +{ + Default + { + Tag "$T_HELLMISSILEMAG"; + Inventory.PickupMessage "$T_HELLMISSILEMAG"; + Inventory.Amount 6; + Radius 12; + Height 26; + } +} +Class HellblazerMissileTrioSpawn : Actor +{ + override void PostBeginPlay() + { + if ( bCOUNTSECRET ) level.total_secrets--; + for ( int i=0; i<3; i++ ) + { + let a = Spawn("HellblazerMissiles",Vec3Angle(8,i*120)); + a.special = special; + a.angle = i*120; + a.FloatBobPhase = FloatBobPhase; + for ( int j=0; j<5; j++ ) a.args[j] = args[j]; + if ( bCOUNTSECRET ) + { + a.bCOUNTSECRET = true; + level.total_secrets++; + } + } + Destroy(); + } +} + +Class HellblazerCrackshots : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_HELLCLUSTERS"; + Inventory.PickupMessage "$T_HELLCLUSTER"; + Stamina 15000; + Inventory.Icon "graphics/HUD/Icons/A_HellblazerCrackshot.png"; + Inventory.Amount 1; + Inventory.MaxAmount 15; + Ammo.BackpackAmount 2; + Ammo.BackpackMaxAmount 30; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 6; + Height 22; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,2); + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class HellblazerCrackshotMag : HellblazerCrackshots +{ + Default + { + Tag "$T_HELLCLUSTERMAG"; + Inventory.PickupMessage "$T_HELLCLUSTERMAG"; + Inventory.Amount 3; + Radius 12; + Height 26; + } +} + +Class HellblazerRavagers : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_HELLBURNINATORS"; + Inventory.PickupMessage "$T_HELLBURNINATOR"; + Stamina 25000; + Inventory.Icon "graphics/HUD/Icons/A_HellblazerRavager.png"; + Inventory.Amount 1; + Inventory.MaxAmount 9; + Ammo.BackpackAmount 1; + Ammo.BackpackMaxAmount 18; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 6; + Height 22; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,1); + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class HellblazerRavagerMag : HellblazerRavagers +{ + Default + { + Tag "$T_HELLBURNINATORMAG"; + Inventory.PickupMessage "$T_HELLBURNINATORMAG"; + Inventory.Amount 3; + Radius 12; + Height 26; + } +} + +Class HellblazerWarheads : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_HELLNUKES"; + Inventory.PickupMessage "$T_HELLNUKE"; + Stamina 40000; + Inventory.Icon "graphics/HUD/Icons/A_HellblazerWarhead.png"; + Inventory.Amount 1; + Inventory.MaxAmount 4; + Ammo.BackpackAmount 0; + Ammo.BackpackMaxAmount 8; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 6; + Height 22; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,1); + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class HellblazerWarheadMag : HellblazerWarheads +{ + Default + { + Tag "$T_HELLNUKEMAG"; + Inventory.PickupMessage "$T_HELLNUKEMAG"; + Inventory.Amount 2; + Radius 12; + Height 26; + } +} + +// ============================================================================ +// Sparkster ammo +// ============================================================================ + +Class SparkUnit : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_SPARKUNIT"; + Inventory.PickupMessage "$T_SPARKUNIT"; + Stamina 50000; + Inventory.Icon "graphics/HUD/Icons/A_Sparkster.png"; + Inventory.Amount 1; + Inventory.MaxAmount 10; + Ammo.BackpackAmount 1; + Ammo.BackpackMaxAmount 30; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 6; + Height 22; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,1); + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + +// ============================================================================ +// Silver Bullet ammo +// ============================================================================ + +Class SilverBulletAmmo : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_XSBMAG"; + Inventory.PickupMessage "$T_XSBMAG"; + Stamina 70000; + Inventory.Icon "graphics/HUD/Icons/A_SilverBullet.png"; + Inventory.Amount 1; + Inventory.MaxAmount 3; + Ammo.BackpackAmount 0; + Ammo.BackpackMaxAmount 8; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 10; + Height 26; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,1); + } + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + // attach our mag ammo if we have none + if ( Owner.FindInventory('SilverBullets') ) + { + let ma = Inventory(Spawn('SilverBullets')); + ma.Amount = 0; + ma.AttachToOwner(Owner); + } + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + +Class SilverBulletAmmo2 : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_FCBMAG"; + Inventory.PickupMessage "$T_FCBMAG"; + Stamina 80000; + Inventory.Icon "graphics/HUD/Icons/A_SilverBullet2.png"; + Inventory.Amount 1; + Inventory.MaxAmount 2; + Ammo.BackpackAmount 0; + Ammo.BackpackMaxAmount 4; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 10; + Height 26; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,1); + } + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + // attach our mag ammo if we have none + if ( Owner.FindInventory('SilverBullets2') ) + { + let ma = Inventory(Spawn('SilverBullets2')); + ma.Amount = 0; + ma.AttachToOwner(Owner); + } + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + +Class SilverBullets : MagAmmo +{ + Default + { + Tag "$T_XSBBULLET"; + Inventory.PickupMessage "$T_XSBBULLET"; + Inventory.Icon "graphics/HUD/Icons/A_SilverBulletBullet.png"; + MagAmmo.ParentAmmo "SilverBulletAmmo"; + MagAmmo.ClipSize 5; + Inventory.MaxAmount 8; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + Height 22; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} +Class SilverBullets2 : MagAmmo +{ + Default + { + Tag "$T_FCBBULLET"; + Inventory.PickupMessage "$T_FCBBULLET"; + Inventory.Icon "graphics/HUD/Icons/A_SilverBulletBullet2.png"; + MagAmmo.ParentAmmo "SilverBulletAmmo2"; + MagAmmo.ClipSize 5; + Inventory.MaxAmount 8; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 4; + Height 22; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + +Class SilverBulletsBundleSpawn : Actor +{ + override void PostBeginPlay() + { + if ( bCOUNTSECRET ) level.total_secrets--; + int bnd = Random[Bundle](2,3); + for ( int i=0; i 0 ) + { + // drop an empty spare + let spare = Inventory(Spawn('CandyGun',Owner.Pos,NO_REPLACE)); + if ( !spare ) return null; + // spare with empty mag + CandyGun(spare).AmmoGive1 = 0; + CandyGun(spare).clipcount = 0; + CandyGun(spare).chambered = false; + spare.SetState(spare.SpawnState+1); + spare.DropTime = 30; + spare.bSpecial = spare.bSolid = false; + Amount--; + return spare; + } + return null; + } +} + +// ============================================================================ +// Ynykron ammo +// ============================================================================ + +Class YnykronAmmo : Ammo +{ + Mixin SWWMAmmo; + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Default + { + Tag "$T_YNYKRONAMMO"; + Inventory.PickupMessage "$T_YNYKRONAMMO"; + Stamina 3000000; + Inventory.Icon "graphics/HUD/Icons/A_Ynykron.png"; + Inventory.Amount 1; + Inventory.MaxAmount 1; + Ammo.BackpackAmount 0; + Ammo.BackpackMaxAmount 3; + Ammo.DropAmount 1; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 8; + Height 24; + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Amount,1); + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + diff --git a/zscript/swwm_armor.zsc b/zscript/items/swwm_armor.zsc similarity index 100% rename from zscript/swwm_armor.zsc rename to zscript/items/swwm_armor.zsc diff --git a/zscript/items/swwm_baseammo.zsc b/zscript/items/swwm_baseammo.zsc new file mode 100644 index 000000000..50309a3be --- /dev/null +++ b/zscript/items/swwm_baseammo.zsc @@ -0,0 +1,377 @@ +// Common code for ammo division +Mixin Class SWWMAmmo +{ + private Inventory DoDrop( Class type ) + { + let copy = Inventory(Spawn(type,Owner.Pos,NO_REPLACE)); + if ( !copy ) return null; + copy.DropTime = 30; + copy.bSpecial = copy.bSolid = false; + copy.SetOrigin(Owner.Vec3Offset(0,0,10.),false); + copy.Angle = Owner.Angle; + copy.VelFromAngle(5.); + copy.Vel.Z = 1.; + copy.Vel += Owner.Vel; + copy.bNoGravity = false; + copy.ClearCounters(); + copy.OnDrop(Owner); + copy.vel += (RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),Owner.angle),FRandom[Junk](2.,5.)); + return copy; + } + + override bool SpecialDropAction( Actor dropper ) + { + if ( swwm_enemydrops >= 0 ) + { + if ( Amount == default.Amount ) return false; + // subdivide + Owner = dropper; // needed for positioning to work + CreateTossable(Amount); + return true; + } + // no ammo drops from enemies + return true; + } + + private bool CmpAmmo( Class a, Class b ) + { + let amta = GetDefaultByType(a).Amount; + let amtb = GetDefaultByType(b).Amount; + return (amta < amtb); + } + + private int partition_ammotypes( Array > a, int l, int h ) + { + Class pv = a[h]; + int i = (l-1); + for ( int j=l; j<=(h-1); j++ ) + { + if ( CmpAmmo(pv,a[j]) ) + { + i++; + Class tmp = a[j]; + a[j] = a[i]; + a[i] = tmp; + } + } + Class tmp = a[h]; + a[h] = a[i+1]; + a[i+1] = tmp; + return i+1; + } + private void qsort_ammotypes( Array > a, int l, int h ) + { + if ( l >= h ) return; + int p = partition_ammotypes(a,l,h); + qsort_ammotypes(a,l,p-1); + qsort_ammotypes(a,p+1,h); + } + + override inventory CreateTossable( int amt ) + { + if ( bUndroppable || bUntossable || !Owner || (Amount <= 0) || (amt == 0) ) + return null; + // cap + amt = min(amount,amt); + // enumerate all subclasses + Array > ammotypes; + ammotypes.Clear(); + for ( int i=0; i)(AllActorClasses[i])); + } + // sort from largest to smallest + qsort_ammotypes(ammotypes,0,ammotypes.Size()-1); + // perform subdivision + Inventory last = null; + while ( amt > 0 ) + { + for ( int i=0; i= def.Amount ) + { + last = DoDrop(ammotypes[i]); + amt -= def.Amount; + Amount -= def.Amount; + break; + } + } + } + return last; + } + + override bool HandlePickup( Inventory item ) + { + // drop excess ammo + if ( (item is 'Ammo') && (Ammo(item).GetParentAmmo() == GetParentAmmo()) ) + { + int excess = Amount+item.Amount; + if ( excess > MaxAmount ) excess -= MaxAmount; + if ( excess < item.Amount ) + { + // enumerate all subclasses + Array > ammotypes; + ammotypes.Clear(); + for ( int i=0; i)(AllActorClasses[i])); + } + // sort from largest to smallest + for ( int i=0; i 0) && CmpAmmo(ammotypes[k-1],ammotypes[k]) ) + { + Class tmp = ammotypes[k]; + ammotypes[k] = ammotypes[k-1]; + ammotypes[k-1] = tmp; + k--; + } + j++; + } + } + // drop spares + Inventory last; + while ( excess > 0 ) + { + for ( int i=0; i= def.Amount ) + { + double ang = FRandom[Junk](0,360); + last = DoDrop(ammotypes[i]); + last.SetOrigin(item.pos,false); + last.vel.xy = (cos(ang),sin(ang))*FRandom[Junk](2,5); + excess -= def.Amount; + break; + } + } + } + } + } + return Super.HandlePickup(item); + } + + override void DoEffect() + { + Super.DoEffect(); + // drop excess ammo + if ( !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo') ) + { + int excess = Amount-MaxAmount; + if ( excess > 0 ) CreateTossable(excess); + } + } + + default + { + +INVENTORY.IGNORESKILL; + Inventory.PickupFlash "SWWMPickupFlash"; + } +} + +// Common code for individual bullets +Class MagAmmo : Inventory abstract +{ + Mixin SWWMOverlapPickupSound; + Mixin SWWMUseToPickup; + + Class ParentAmmo; + Ammo pamo; + int ClipSize; + int countdown; + + Property ParentAmmo : ParentAmmo; + Property ClipSize : ClipSize; + + default + { + +INVENTORY.KEEPDEPLETED; + Inventory.PickupSound "misc/bullet_pkup"; + Inventory.Amount 1; + Inventory.PickupFlash "SWWMPickupFlash"; + } + + override bool HandlePickup( Inventory item ) + { + // see if the mag can be split apart + if ( (item is 'Ammo') && (ParentAmmo == Ammo(item).GetParentAmmo()) ) + { + // double-check that parent ammo exists + if ( !pamo ) + { + pamo = Ammo(Owner.FindInventory(ParentAmmo)); + if ( !pamo ) + { + pamo = Ammo(Spawn(ParentAmmo)); + pamo.AttachToOwner(Owner); + pamo.Amount = 0; + } + } + if ( (pamo.Amount >= pamo.MaxAmount) && (Amount < MaxAmount) ) + { + // split + for ( int i=0; i 0 ) CreateTossable(dropamt); + Amount = min(MaxAmount,Amount+bul); + } + item.bPickupGood = true; + return true; + } + } + return Super.HandlePickup(item); + } + + private Inventory DoDrop( Class type ) + { + let copy = Inventory(Spawn(type,Owner.Pos,NO_REPLACE)); + if ( !copy ) return null; + copy.DropTime = 30; + copy.bSpecial = copy.bSolid = false; + copy.SetOrigin(Owner.Vec3Offset(0,0,10.),false); + copy.Angle = Owner.Angle; + copy.VelFromAngle(5.); + copy.Vel.Z = 1.; + copy.Vel += Owner.Vel; + copy.bNoGravity = false; + copy.ClearCounters(); + copy.OnDrop(Owner); + copy.vel += (RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),Owner.angle),FRandom[Junk](2.,5.)); + return copy; + } + + override bool SpecialDropAction( Actor dropper ) + { + if ( swwm_enemydrops >= 0 ) + { + if ( Amount == default.Amount ) return false; + // subdivide + Owner = dropper; // needed for positioning to work + CreateTossable(Amount); + return true; + } + // no ammo drops from enemies + return true; + } + + override void DoEffect() + { + Super.DoEffect(); + // drop excess ammo + if ( !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo') ) + { + int excess = Amount-MaxAmount; + if ( excess > 0 ) CreateTossable(excess); + } + if ( !pamo ) + { + pamo = Ammo(Owner.FindInventory(ParentAmmo)); + if ( !pamo ) + { + pamo = Ammo(Spawn(ParentAmmo)); + pamo.AttachToOwner(Owner); + pamo.Amount = 0; + } + } + // check if we can fill a mag (delayed) + if ( (Amount < ClipSize) || (pamo.Amount >= pamo.MaxAmount) ) + { + countdown = 35; + return; + } + if ( countdown-- > 0 ) return; + MagFill(); + } + + bool MagFill() + { + // double-check that parent ammo exists + if ( !pamo ) + { + pamo = Ammo(Owner.FindInventory(ParentAmmo)); + if ( !pamo ) + { + pamo = Ammo(Spawn(ParentAmmo)); + pamo.AttachToOwner(Owner); + pamo.Amount = 0; + } + } + bool given = false; + while ( (pamo.Amount < pamo.MaxAmount) && (Amount >= ClipSize) ) + { + pamo.Amount++; + Amount -= ClipSize; + given = true; + if ( Owner.CheckLocalView() ) + pamo.PrintPickupMessage(true,pamo.PickupMessage()); + } + if ( given ) pamo.PlayPickupSound(Owner); + return given; + } + + override inventory CreateTossable( int amt ) + { + if ( bUndroppable || bUntossable || !Owner || (Amount <= 0) || (amt == 0) ) + return null; + // cap + amt = min(amount,amt); + // perform subdivision + Inventory last = null; + let pammo = GetDefaultByType(ParentAmmo); + while ( amt > 0 ) + { + // drop full mag if possible + if ( amt >= ClipSize ) + { + last = DoDrop(ParentAmmo); + amt -= ClipSize; + Amount -= ClipSize; + continue; + } + // drop individual bullets + last = DoDrop(GetClass()); + amt--; + Amount--; + } + return last; + } + + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = min(Random[ShellDrop](1,ClipSize),Amount); + } +} + +// Common code for grouped shell handling and per-amount pickup messages +Mixin Class SWWMShellAmmo +{ + override string PickupMessage() + { + String tagstr = "$T_"..GetParentAmmo().GetClassName(); + tagstr.MakeUpper(); + if ( Amount > 1 ) + { + tagstr = tagstr.."S"; + return String.Format("%d %s",Amount,StringTable.Localize(tagstr)); + } + return StringTable.Localize(tagstr); + } + + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + Amount = max(1,Amount+Random[ShellDrop](-2,1)); + } +} diff --git a/zscript/items/swwm_basearmor.zsc b/zscript/items/swwm_basearmor.zsc new file mode 100644 index 000000000..4722083b8 --- /dev/null +++ b/zscript/items/swwm_basearmor.zsc @@ -0,0 +1,185 @@ +// Base class for all SWWM Armors +Class SWWMArmor : Armor abstract +{ + int priority; + String drainmsg; + Class parent; + + Property ArmorPriority : priority; + Property DrainMessage : drainmsg; + Property GiverArmor : parent; + + Default + { + +INVENTORY.AUTOACTIVATE; + +INVENTORY.UNTOSSABLE; + +INVENTORY.UNDROPPABLE; + +INVENTORY.KEEPDEPLETED; + +INVENTORY.ALWAYSPICKUP; + } + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + // find last armor that's better than us + Inventory found = null; + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( !(i is 'SWWMArmor') || (i == self) || (SWWMArmor(i).priority < priority) ) continue; + found = i; + } + if ( !found ) + { + // find first item with an armor worse than us after it + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( (i == self) || !(i.Inv is 'SWWMArmor') ) continue; + if ( SWWMArmor(i.Inv).priority > priority ) continue; + found = i; + break; + } + } + if ( !found ) + { + // is first item plating or a collar? + if ( (other.Inv is 'AlmasteelPlating') || (other.Inv is 'SayaCollar') ) + { + // we're good + return; + } + // find first item with plating or collar after it + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( (i == self) || (!(i.Inv is 'AlmasteelPlating' ) && !(i.Inv is 'SayaCollar')) ) continue; + found = i; + break; + } + } + if ( !found ) + { + // check if first item in inventory is health or a sandwich + if ( (other.Inv is 'SWWMHealth') || (other.Inv is 'GrilledCheeseSandwich') ) + { + // we're good + return; + } + // find first item with health or sandwich after it + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( (i == self) || (!(i.Inv is 'SWWMHealth' ) && !(i.Inv is 'GrilledCheeseSandwich')) ) continue; + found = i; + break; + } + } + if ( !found ) + { + // find last of either invinciball/ragekit/barrier power + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( !(i is 'InvinciballPower') && !(i is 'RagekitPower') && !(i is 'BarrierPower') ) continue; + found = i; + } + } + if ( !found ) return; + // place ourselves right after it + Inventory saved = found.Inv; + found.Inv = self; + other.Inv = Inv; + Inv = saved; + } + // for subclasses + virtual int HandleDamage( int damage, Name damageType, int flags ) + { + return damage; + } + override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) + { + int saved; + if ( (amount <= 0) || DamageTypeDefinition.IgnoreArmor(damageType) || (damage <= 0) ) + return; + SWWMHandler.DoFlash(Owner,Color(int(clamp(damage*.15,1,16)),255,224,192),3); + Owner.A_StartSound("armor/hit",CHAN_BODY,CHANF_DEFAULT,clamp(damage*.03,0.,1.),2.5); + saved = HandleDamage(damage,damageType,flags); + int healed = max(0,saved-damage); + saved = min(saved,damage); + if ( amount <= saved ) saved = amount; + newdamage -= saved; + if ( healed > 0 ) Owner.GiveBody(healed); + if ( (swwm_strictuntouchable == 1) && (saved > 0) && Owner.player ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( hnd ) hnd.tookdamage[Owner.PlayerNumber()] = true; + } + amount -= saved; + damage = newdamage; + bool shouldautouse = false; + if ( swwm_enforceautousearmor == 1 ) shouldautouse = true; + else if ( swwm_enforceautousearmor == -1 ) shouldautouse = false; + else shouldautouse = CVar.GetCVar('swwm_autousearmor',Owner.player).GetBool(); + if ( (amount <= (MaxAmount-default.Amount)) && (Owner.CountInv(parent) > 0) && shouldautouse ) + { + if ( GetDefaultByType(parent).UseSound ) Owner.A_StartSound(GetDefaultByType(parent).UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); + int tgive = 0; + while ( (amount <= (MaxAmount-default.Amount)) && (Owner.CountInv(parent) > 0) ) + { + if ( swwm_accdamage ) tgive += default.Amount; + else SWWMScoreObj.Spawn(default.Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor); + Amount += default.Amount; + Owner.TakeInventory(parent,1); + // absorb the extra damage too + saved = HandleDamage(damage,damageType,flags); + healed = max(0,saved-damage); + saved = min(saved,damage); + if ( amount <= saved ) saved = amount; + newdamage -= saved; + if ( healed > 0 ) Owner.GiveBody(healed); + amount -= saved; + damage = newdamage; + } + if ( swwm_accdamage ) SWWMScoreObj.Spawn(tgive,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor); + } + else if ( amount <= 0 ) + { + if ( Owner.CheckLocalView() && (drainmsg != "") ) Console.Printf(StringTable.Localize(drainmsg)); + DepleteOrDestroy(); + return; + } + } +} + +// gives armor when used +Class SWWMSpareArmor : Inventory abstract +{ + Mixin SWWMAutoUseFix; + Mixin SWWMUseToPickup; + Mixin SWWMOverlapPickupSound; + + Class giveme; + + Property GiveArmor : giveme; + + override bool Use( bool pickup ) + { + let cur = Owner.FindInventory(giveme); + if ( !cur || (!pickup && (cur.Amount < cur.MaxAmount)) || (GetDefaultByType(giveme).Amount+cur.Amount <= cur.MaxAmount) ) + { + if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); + Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); + SWWMHandler.ArmorFlash(Owner.PlayerNumber()); + SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor); + return true; + } + return false; + } + + Default + { + +INVENTORY.INVBAR; + +INVENTORY.ISARMOR; + +INVENTORY.AUTOACTIVATE; + Inventory.MaxAmount 5; + Inventory.InterHubAmount 5; + Inventory.PickupFlash "SWWMGreenPickupFlash"; + +FLOATBOB; + FloatBobStrength 0.25; + } +} diff --git a/zscript/items/swwm_basehealth.zsc b/zscript/items/swwm_basehealth.zsc new file mode 100644 index 000000000..4ad3859c8 --- /dev/null +++ b/zscript/items/swwm_basehealth.zsc @@ -0,0 +1,160 @@ +// Base class for all SWWM Health +Class SWWMHealth : Inventory abstract +{ + Mixin SWWMAutoUseFix; + Mixin SWWMUseToPickup; + Mixin SWWMOverlapPickupSound; + + // can't use the Health class for whatever reason + // nice parser you got there I guess? + Class giveme; + + Property GiveHealth : giveme; + + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + // find last health item that's better than us + Inventory found = null; + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( !(i is 'SWWMHealth') || (i == self) || (GetDefaultByType(SWWMHealth(i).giveme).Amount < GetDefaultByType(giveme).Amount) ) continue; + found = i; + } + if ( !found ) + { + // find first item with health that's worse than us after it + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( (i == self) || !(i.Inv is 'SWWMHealth') ) continue; + if ( GetDefaultByType(SWWMHealth(i.Inv).giveme).Amount > GetDefaultByType(giveme).Amount ) continue; + found = i; + break; + } + } + if ( !found ) + { + // find last armor item, plating or collar + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( !(i is 'SWWMArmor') && !(i is 'AlmasteelPlating') && !(i is 'SayaCollar') ) continue; + found = i; + } + } + if ( !found ) + { + // check if the first item in inventory is a sandwich + if ( other.Inv is 'GrilledCheeseSandwich' ) + { + // we're good + return; + } + // find first item next to a sandwich + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( (i == self) || !(i.Inv is 'GrilledCheeseSandwich') ) continue; + found = i; + break; + } + } + if ( !found ) + { + // find last of either invinciball/ragekit/barrier power + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( !(i is 'InvinciballPower') && !(i is 'RagekitPower') && !(i is 'BarrierPower') ) continue; + found = i; + } + } + if ( !found ) return; + // place ourselves right after it + Inventory saved = found.Inv; + found.Inv = self; + other.Inv = Inv; + Inv = saved; + } + + override bool Use( bool pickup ) + { + if ( Owner.Health >= GetDefaultByType(giveme).MaxAmount ) return false; + // healing items won't get auto-used on pickup if their healing could "be wasted", unless they're powerup health (e.g. Refresher) + if ( pickup && !bBIGPOWERUP && (Owner.Health+GetDefaultByType(giveme).Amount > GetDefaultByType(giveme).MaxAmount) ) return false; + if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); + SWWMHandler.HealthFlash(Owner.PlayerNumber()); + Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); + SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health); + AutoUseExtra(false); + return true; + } + + // additional effects when automatically used + // recursive: if true, the auto-use was called on another copy of the + // item in a "stacked" heal. can be used to prevent certain + // effects from happening multiple times in one go + virtual void AutoUseExtra( bool recursive ) + { + } + + override void DoEffect() + { + Super.DoEffect(); + if ( Amount <= 0 ) DepleteOrDestroy(); + } + + override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) + { + if ( Owner.ApplyDamageFactor(damageType,damage) <= 0 ) + return; // this damage type is ignored by the player, so it does not affect us + if ( damageType == 'EndLevel' ) + return; // don't trigger on endlevel damage + bool shouldautouse = false; + if ( swwm_enforceautousehealth == 1 ) shouldautouse = true; + else if ( swwm_enforceautousehealth == -1 ) shouldautouse = false; + else shouldautouse = CVar.GetCVar('swwm_autousehealth',Owner.player).GetBool(); + if ( !shouldautouse && !bBIGPOWERUP ) return; // powerup health is always auto-used + if ( (Owner.Health-damage <= (GetDefaultByType(giveme).MaxAmount-GetDefaultByType(giveme).Amount)) ) + { + newdamage = damage; + // lesser healing items can't prevent lethal damage + // bigger healing items only autoactivate on lethal damage + if ( !bBIGPOWERUP && (Owner.Health-damage <= 0) ) + return; + else if ( bBIGPOWERUP && (Owner.Health-damage > 0) ) + return; + if ( (swwm_strictuntouchable == 1) && Owner.player ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( hnd ) hnd.tookdamage[Owner.PlayerNumber()] = true; + } + if ( ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); + int tgive = 0; + bool morethanonce = false; + while ( (Amount > 0) && (newdamage > 0) ) + { + if ( swwm_accdamage ) tgive += GetDefaultByType(giveme).Amount; + else SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health); + newdamage = newdamage-GetDefaultByType(giveme).Amount; + if ( newdamage < 0 ) Owner.GiveBody(-newdamage,GetDefaultByType(giveme).MaxAmount); + newdamage = max(0,newdamage); + AutoUseExtra(morethanonce); + morethanonce = true; + Amount--; + } + if ( swwm_accdamage ) SWWMScoreObj.Spawn(tgive,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health); + } + else newdamage = damage; + } + + Default + { + +INVENTORY.INVBAR; + +INVENTORY.ISHEALTH; + +INVENTORY.AUTOACTIVATE; + Inventory.MaxAmount 5; + Inventory.InterHubAmount 5; + Inventory.UseSound "misc/health_pkup"; + Inventory.PickupFlash "SWWMBluePickupFlash"; + +FLOATBOB; + FloatBobStrength 0.25; + } +} diff --git a/zscript/items/swwm_baseitem.zsc b/zscript/items/swwm_baseitem.zsc new file mode 100644 index 000000000..9756b1d61 --- /dev/null +++ b/zscript/items/swwm_baseitem.zsc @@ -0,0 +1,88 @@ +// Inventory stuff +Mixin Class SWWMAutoUseFix +{ + override bool HandlePickup( Inventory item ) + { + if ( GetClass() == item.GetClass() ) + { + if ( Use(true) ) Amount--; + // sell excess if there's a price + if ( bALWAYSPICKUP && (Amount+item.Amount > MaxAmount) && (Stamina != 0) ) + { + int sellprice = int(abs(Stamina)*.5); + SWWMScoreObj.Spawn(sellprice,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2)); + SWWMCredits.Give(Owner.player,sellprice); + if ( Owner.player ) + Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),Owner.player.GetUserName(),GetTag(),sellprice); + } + } + return Super.HandlePickup(item); + } +} + +Class CrossLineFinder : LineTracer +{ + Array clines; + Array csides; + + override ETraceStatus TraceCallback() + { + if ( (Results.HitType == TRACE_HitWall) && (Results.HitLine.activation&SPAC_Cross) ) + { + clines.Push(Results.HitLine); + csides.Push(Results.Side); + } + return TRACE_Skip; + } +} + +Mixin Class SWWMUseToPickup +{ + // allow pickup by use + override bool Used( Actor user ) + { + Vector3 itempos = Vec3Offset(0,0,Height/2), + userpos = user.Vec2OffsetZ(0,0,user.player.viewz); + // test vertical range + Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); + double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); + if ( abs(diff.z) > rang ) return false; + Touch(user); + // we got picked up + if ( bDestroyed || Owner || !bSPECIAL ) + { + Vector3 tracedir = level.Vec3Diff(userpos,itempos); + double dist = tracedir.length(); + tracedir /= dist; + let cf = new("CrossLineFinder"); + cf.Trace(userpos,level.PointInSector(userpos.xy),tracedir,dist,0); + // trigger all player cross lines found between user and item + for ( int i=0; i gesture; + + Property Availability : avail; + Property GestureWeapon : gesture; + + // minimum gametype requirements + enum EAvailability + { + AVAIL_Strife = GAME_Strife, + AVAIL_Hexen = AVAIL_Strife|GAME_Hexen, + AVAIL_Heretic = AVAIL_Hexen|GAME_Heretic, + AVAIL_All = AVAIL_Heretic|GAME_DoomChex + }; + + Default + { + Inventory.PickupSound "menu/buyinv"; + Inventory.Amount 1; + Inventory.MaxAmount 1; + Inventory.PickupFlash "SWWMCyanPickupFlash"; + SWWMCollectible.Availability AVAIL_All; + +INVENTORY.UNTOSSABLE; + +INVENTORY.UNDROPPABLE; + +INVENTORY.UNCLEARABLE; + +INVENTORY.AUTOACTIVATE; + +FLOATBOB; + FloatBobStrength 0.25; + Radius 8; + Height 24; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + // delet ourselves if wrong iwad + if ( !(gameinfo.gametype&avail) ) + Destroy(); + } + override bool CanPickup( Actor toucher ) + { + // no pickup if wrong iwad + if ( !(gameinfo.gametype&avail) ) return false; + return Super.CanPickup(toucher); + } + override string PickupMessage() + { + if ( Stamina > 0 ) + return StringTable.Localize(PickupMsg)..String.Format(" \cj(\cg¥\cf%d\cj)\c-",Stamina); + return Super.PickupMessage(); + } + override bool Use( bool pickup ) + { + if ( Owner.player && !propagated ) + { + if ( pickup && CVar.GetCVar('swwm_collectanim',Owner.player).GetBool() ) + SWWMGesture.SetSpecialGesture(Owner.player.mo,gesture); + else if ( !pickup ) + SWWMGesture.SetSpecialGesture(Owner.player.mo,gesture,true); + } + // clean up the flag + propagated = false; + return false; + } + + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + // we're only attaching to the other players + if ( propagated ) + return; + // give credit + if ( other.player && (Stamina > 0) ) + { + SWWMScoreObj.Spawn(Stamina,other.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+other.Height/2)); + SWWMCredits.Give(other.player,Stamina); + } + // send to all other players + for ( int i=0; i gesture; - - Property Availability : avail; - Property GestureWeapon : gesture; - - // minimum gametype requirements - enum EAvailability - { - AVAIL_Strife = GAME_Strife, - AVAIL_Hexen = AVAIL_Strife|GAME_Hexen, - AVAIL_Heretic = AVAIL_Hexen|GAME_Heretic, - AVAIL_All = AVAIL_Heretic|GAME_DoomChex - }; - - Default - { - Inventory.PickupSound "menu/buyinv"; - Inventory.Amount 1; - Inventory.MaxAmount 1; - Inventory.PickupFlash "SWWMCyanPickupFlash"; - SWWMCollectible.Availability AVAIL_All; - +INVENTORY.UNTOSSABLE; - +INVENTORY.UNDROPPABLE; - +INVENTORY.UNCLEARABLE; - +INVENTORY.AUTOACTIVATE; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 8; - Height 24; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - // delet ourselves if wrong iwad - if ( !(gameinfo.gametype&avail) ) - Destroy(); - } - override bool CanPickup( Actor toucher ) - { - // no pickup if wrong iwad - if ( !(gameinfo.gametype&avail) ) return false; - return Super.CanPickup(toucher); - } - override string PickupMessage() - { - if ( Stamina > 0 ) - return StringTable.Localize(PickupMsg)..String.Format(" \cj(\cg¥\cf%d\cj)\c-",Stamina); - return Super.PickupMessage(); - } - override bool Use( bool pickup ) - { - if ( Owner.player && !propagated ) - { - if ( pickup && CVar.GetCVar('swwm_collectanim',Owner.player).GetBool() ) - SWWMGesture.SetSpecialGesture(Owner.player.mo,gesture); - else if ( !pickup ) - SWWMGesture.SetSpecialGesture(Owner.player.mo,gesture,true); - } - // clean up the flag - propagated = false; - return false; - } - - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - // we're only attaching to the other players - if ( propagated ) - return; - // give credit - if ( other.player && (Stamina > 0) ) - { - SWWMScoreObj.Spawn(Stamina,other.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+other.Height/2)); - SWWMCredits.Give(other.player,Stamina); - } - // send to all other players - for ( int i=0; i 0 ) - { - gest.whichgesture = GS_Null; - gest.whichweapon = gest.sweapon[0]; - gest.whichuse = gest.suse[0]; - // push back - gest.sweapon.Delete(0); - gest.suse.Delete(0); - // go back to the main gesture - player.ReadyWeapon = gest; - player.SetPSPrite(PSP_WEAPON,gest.ResolveState("Ready")); - invoker.gotused = true; - return; - } - if ( gest.queued ) - { - gest.whichweapon = null; - gest.whichgesture = gest.nextgesture; - gest.queued = false; - // go back to the main gesture - player.ReadyWeapon = gest; - player.SetPSPrite(PSP_WEAPON,gest.ResolveState("Ready")); - invoker.gotused = true; - return; - } - // switch to old weapon - player.ReadyWeapon = gest; - player.PendingWeapon = gest.formerweapon; - player.SetPSPrite(PSP_WEAPON,gest.ResolveState("Deselect")); - invoker.gotused = true; - } - - Default - { - +WEAPON.CHEATNOTWEAPON; - +WEAPON.NO_AUTO_SWITCH; - +WEAPON.WIMPY_WEAPON; - +SWWMWEAPON.HIDEINMENU; - +INVENTORY.UNDROPPABLE; - +INVENTORY.UNTOSSABLE; - +INVENTORY.UNCLEARABLE; - Weapon.SelectionOrder int.max; - } - States - { - Select: - XZW1 A 1 A_FullRaise(); - Goto Ready; - Ready: - Fire: - XZW1 A 1 A_Log("\cgUnimplemented pickup sequence for "..invoker.GetClassName().."\c-"); - XZW1 A -1 A_FinishGesture(); - Stop; - AltFire: - XZW1 A 1 A_Log("\cgUnimplemented use sequence for "..invoker.GetClassName().."\c-"); - XZW1 A -1 A_FinishGesture(); - Stop; - Deselect: - XZW1 A -1 A_FullLower(); - Stop; - } -} - // April Fools 2020 Class FroggyChair : Actor { @@ -366,160 +156,6 @@ Class FroggyChair : Actor } } -// The collectibles -Class GenericCube : SWWMCollectible -{ - Default - { - Tag "$T_PERFECTLYGENERIC"; - Inventory.PickupMessage "$T_PERFECTLYGENERIC"; - SWWMCollectible.GestureWeapon "GenericCubeGesture"; - Stamina 1000; - } -} -Class AkariProject : SWWMCollectible -{ - Default - { - Tag "$T_AKARIPROJECT"; - Inventory.PickupMessage "$T_AKARIPROJECT"; - SWWMCollectible.GestureWeapon "AkariProjectGesture"; - Stamina 2000; - Radius 4; - Height 22; - } -} -Class LoveSignalsCD : SWWMCollectible -{ - Default - { - Tag "$T_LOVESIGNALS"; - Inventory.PickupMessage "$T_LOVESIGNALS"; - SWWMCollectible.GestureWeapon "LoveSignalsCDGesture"; - Stamina 3000; - Radius 4; - Height 21; - } -} -Class NutatcoBar : SWWMCollectible -{ - Default - { - Tag "$T_NUTATCO"; - Inventory.PickupMessage "$T_NUTATCO"; - SWWMCollectible.GestureWeapon "NutatcoBarGesture"; - Stamina 200; - Radius 3; - Height 22; - } -} -Class FrispyCorn : SWWMCollectible -{ - Default - { - Tag "$T_FRISPYCORN"; - Inventory.PickupMessage "$T_FRISPYCORN"; - SWWMCollectible.GestureWeapon "FrispyCornGesture"; - Stamina 400; - Radius 5; - Height 23; - } -} -Class SayaBean : SWWMCollectible -{ - bool callout; // already called the player a perv for loading h-doom - - Default - { - Tag "$T_SAYABEAN"; - Inventory.PickupMessage "$T_SAYABEAN"; - SWWMCollectible.GestureWeapon "SayaBeanGesture"; - Stamina 5000; - Radius 6; - Height 23; - } -} -// Heretic -Class DemoPlush : SWWMCollectible -{ - Default - { - Tag "$T_DEMOPLUSH"; - Inventory.PickupMessage "$T_DEMOPLUSH"; - SWWMCollectible.Availability AVAIL_Heretic; - SWWMCollectible.GestureWeapon "DemoPlushGesture"; - Stamina 6000; - Radius 12; - Height 36; - } -} -// Hexen -Class KirinCummies : SWWMCollectible -{ - Default - { - Tag "$T_PEACH"; - Inventory.PickupMessage "$T_PEACH"; - SWWMCollectible.Availability AVAIL_Hexen; - SWWMCollectible.GestureWeapon "KirinCummiesGesture"; - Stamina 300; - Radius 3; - Height 21; - } -} -Class MilkBreads : SWWMCollectible -{ - Default - { - Tag "$T_MILKBREAD"; - Inventory.PickupMessage "$T_MILKBREAD"; - SWWMCollectible.Availability AVAIL_Hexen; - SWWMCollectible.GestureWeapon "MilkBreadsGesture"; - Stamina 900; - Radius 4; - Height 21; - } -} -Class KirinManga : SWWMCollectible -{ - Default - { - Tag "$T_KIRINMANGA"; - Inventory.PickupMessage "$T_KIRINMANGA"; - SWWMCollectible.Availability AVAIL_Hexen; - SWWMCollectible.GestureWeapon "KirinMangaGesture"; - Stamina 1600; - Radius 4; - Height 22; - } -} -Class KirinPlush : SWWMCollectible -{ - Default - { - Tag "$T_KIRINPLUSH"; - Inventory.PickupMessage "$T_KIRINPLUSH"; - SWWMCollectible.Availability AVAIL_Hexen; - SWWMCollectible.GestureWeapon "KirinPlushGesture"; - Stamina 8000; - Radius 14; - Height 37; - } -} - -// TODO them gestures -Class GenericCubeGesture : SWWMItemGesture {} -Class AkariProjectGesture : SWWMItemGesture {} -Class LoveSignalsCDGesture : SWWMItemGesture {} -Class NutatcoBarGesture : SWWMItemGesture {} -Class FrispyCornGesture : SWWMItemGesture {} -Class DemoPlushGesture : SWWMItemGesture {} -Class SayaBeanGesture : SWWMItemGesture {} -Class KirinCummiesGesture : SWWMItemGesture {} -Class MilkBreadsGesture : SWWMItemGesture {} -Class KirinMangaGesture : SWWMItemGesture {} -Class KirinPlushGesture : SWWMItemGesture {} - // yay! Class FancyConfetti : Actor { diff --git a/zscript/swwm_health.zsc b/zscript/items/swwm_health.zsc similarity index 100% rename from zscript/swwm_health.zsc rename to zscript/items/swwm_health.zsc diff --git a/zscript/swwm_keys.zsc b/zscript/items/swwm_keys.zsc similarity index 59% rename from zscript/swwm_keys.zsc rename to zscript/items/swwm_keys.zsc index 631b5a6b0..ef7335b98 100644 --- a/zscript/swwm_keys.zsc +++ b/zscript/items/swwm_keys.zsc @@ -332,147 +332,6 @@ Class SWWMKeyRed : SWWMKey } } -// Key gestures -// (they all use the same exact animations, just with the object changed) -// (yeah, I'm lazy, and there's a lot of keys) -Class SWWMKeyGesture : SWWMItemGesture abstract -{ - // due to specifics™ we have to handle the punching here - override void DoEffect() - { - Super.DoEffect(); - if ( !Owner || !Owner.player || (Owner.player.Health <= 0) || (Owner.player.ReadyWeapon != self) ) - return; - PSprite psp = Owner.player.FindPSPrite(PSP_WEAPON+1); - if ( !(Owner.player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1)) && !psp ) - { - // not punching, move weapon back - psp = Owner.player.FindPSPrite(PSP_WEAPON); - if ( psp ) - { - psp.oldx = psp.x; - psp.x = max(0,psp.x-8); - psp.oldy = psp.y; - psp.y = min(32,psp.y+4); - } - return; - } - if ( psp ) - { - // already punching, let's shift the weapon away - psp = Owner.player.FindPSPrite(PSP_WEAPON); - if ( psp ) - { - // shift away from center to center - psp.oldx = psp.x; - psp.x = min(70,psp.x+8); - psp.oldy = psp.y; - psp.y = max(16,psp.y-4); - } - return; - } - // start punch - if ( Owner.player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1) ) - { - psp = Owner.player.FindPSPrite(PSP_WEAPON); - if ( psp && (psp.CurState == FindState("WaitingForEnd")) ) - return; - Owner.player.SetPSprite(PSP_WEAPON+1,FindState("Punch")); - psp = Owner.player.FindPSPrite(PSP_WEAPON+1); - if ( psp ) - { - psp.bAddWeapon = false; - psp.bAddBob = false; - psp.x = -50; - psp.y = 32; - } - } - } - - States - { - Fire: - XZW1 A 3 A_Jump(128,"AltFire","AltFire2"); - XZW1 B 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP); - XZW1 CDEF 3; - XZW1 GHIJKLMNO 4; - XZW1 P 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); - XZW1 QRST 3; - Goto WaitingForEnd; - AltFire: - XZW1 A 3 A_Jump(128,"Fire","AltFire2"); - XZW1 U 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP); - XZW1 VWX 3; - XZW1 YZ 4; - XZW2 ABC 4; - XZW2 D 4 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP,pitch:.7); - XZW2 EFGHIJ 4; - XZW2 KL 3; - XZW2 M 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); - XZW2 NOPQR 3; - Goto WaitingForEnd; - AltFire2: - XZW1 A 3; - XZW2 S 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP); - XZW2 TUVWX 3; - XZW2 YZ 4; - XZW3 ABCDE 4; - XZW3 F 2 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 GHI 2; - XZW3 JKL 3; - XZW3 MN 2; - XZW3 O 2 A_StartSound("demolitionist/petting",CHAN_WEAPON,CHANF_OVERLAP,.4); - XZW3 PQ 2; - XZW3 RS 4; - XZW3 T 4 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 UV 4; - XZW3 WXY 3; - Goto WaitingForEnd; - WaitingForEnd: - XZW1 A 1 A_JumpIf(!player.FindPSprite(PSP_WEAPON+1),1); - Wait; - XZW1 A -1 A_FinishGesture(); - Stop; - // overlay for melee - Punch: - XZW0 ABC 1; - PunchHold: - XZW0 D 1 - { - A_PlayerMelee(true); - A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); - A_Parry(9); - } - XZW0 EF 1; - XZW0 G 1 A_Melee(); - XZW0 HIJKLMN 2; - XZW0 A 0 - { - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1) ) - { - let psp = player.FindPSprite(PSP_WEAPON); - if ( psp && (psp.CurState != ResolveState("WaitingForEnd")) ) - return ResolveState("PunchHold"); - } - return ResolveState(null); - } - Stop; - } -} -Class SWWMRedCardGesture : SWWMKeyGesture {} -Class SWWMYellowCardGesture : SWWMKeyGesture {} -Class SWWMBlueCardGesture : SWWMKeyGesture {} -Class SWWMSilverCardGesture : SWWMKeyGesture {} -Class SWWMGreenCardGesture : SWWMKeyGesture {} -Class SWWMOrangeCardGesture : SWWMKeyGesture {} -Class SWWMRedSkullGesture : SWWMKeyGesture {} -Class SWWMYellowSkullGesture : SWWMKeyGesture {} -Class SWWMBlueSkullGesture : SWWMKeyGesture {} -Class SWWMGreenKeyGesture : SWWMKeyGesture {} -Class SWWMBlueKeyGesture : SWWMKeyGesture {} -Class SWWMYellowKeyGesture : SWWMKeyGesture {} -Class SWWMRedKeyGesture : SWWMKeyGesture {} - // HEXDD thingy Class SWWMChaosSphere : Key { diff --git a/zscript/items/swwm_keys_gesture.zsc b/zscript/items/swwm_keys_gesture.zsc new file mode 100644 index 000000000..00757fb5a --- /dev/null +++ b/zscript/items/swwm_keys_gesture.zsc @@ -0,0 +1,140 @@ +// Key gestures +// (they all use the same exact animations, just with the object changed) +// (yeah, I'm lazy, and there's a lot of keys) +Class SWWMKeyGesture : SWWMItemGesture abstract +{ + // due to specifics™ we have to handle the punching here + override void DoEffect() + { + Super.DoEffect(); + if ( !Owner || !Owner.player || (Owner.player.Health <= 0) || (Owner.player.ReadyWeapon != self) ) + return; + PSprite psp = Owner.player.FindPSPrite(PSP_WEAPON+1); + if ( !(Owner.player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1)) && !psp ) + { + // not punching, move weapon back + psp = Owner.player.FindPSPrite(PSP_WEAPON); + if ( psp ) + { + psp.oldx = psp.x; + psp.x = max(0,psp.x-8); + psp.oldy = psp.y; + psp.y = min(32,psp.y+4); + } + return; + } + if ( psp ) + { + // already punching, let's shift the weapon away + psp = Owner.player.FindPSPrite(PSP_WEAPON); + if ( psp ) + { + // shift away from center to center + psp.oldx = psp.x; + psp.x = min(70,psp.x+8); + psp.oldy = psp.y; + psp.y = max(16,psp.y-4); + } + return; + } + // start punch + if ( Owner.player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1) ) + { + psp = Owner.player.FindPSPrite(PSP_WEAPON); + if ( psp && (psp.CurState == FindState("WaitingForEnd")) ) + return; + Owner.player.SetPSprite(PSP_WEAPON+1,FindState("Punch")); + psp = Owner.player.FindPSPrite(PSP_WEAPON+1); + if ( psp ) + { + psp.bAddWeapon = false; + psp.bAddBob = false; + psp.x = -50; + psp.y = 32; + } + } + } + + States + { + Fire: + XZW1 A 3 A_Jump(128,"AltFire","AltFire2"); + XZW1 B 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP); + XZW1 CDEF 3; + XZW1 GHIJKLMNO 4; + XZW1 P 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); + XZW1 QRST 3; + Goto WaitingForEnd; + AltFire: + XZW1 A 3 A_Jump(128,"Fire","AltFire2"); + XZW1 U 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP); + XZW1 VWX 3; + XZW1 YZ 4; + XZW2 ABC 4; + XZW2 D 4 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP,pitch:.7); + XZW2 EFGHIJ 4; + XZW2 KL 3; + XZW2 M 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); + XZW2 NOPQR 3; + Goto WaitingForEnd; + AltFire2: + XZW1 A 3; + XZW2 S 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP); + XZW2 TUVWX 3; + XZW2 YZ 4; + XZW3 ABCDE 4; + XZW3 F 2 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 GHI 2; + XZW3 JKL 3; + XZW3 MN 2; + XZW3 O 2 A_StartSound("demolitionist/petting",CHAN_WEAPON,CHANF_OVERLAP,.4); + XZW3 PQ 2; + XZW3 RS 4; + XZW3 T 4 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 UV 4; + XZW3 WXY 3; + Goto WaitingForEnd; + WaitingForEnd: + XZW1 A 1 A_JumpIf(!player.FindPSprite(PSP_WEAPON+1),1); + Wait; + XZW1 A -1 A_FinishGesture(); + Stop; + // overlay for melee + Punch: + XZW0 ABC 1; + PunchHold: + XZW0 D 1 + { + A_PlayerMelee(true); + A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); + A_Parry(9); + } + XZW0 EF 1; + XZW0 G 1 A_Melee(); + XZW0 HIJKLMN 2; + XZW0 A 0 + { + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1) ) + { + let psp = player.FindPSprite(PSP_WEAPON); + if ( psp && (psp.CurState != ResolveState("WaitingForEnd")) ) + return ResolveState("PunchHold"); + } + return ResolveState(null); + } + Stop; + } +} +Class SWWMRedCardGesture : SWWMKeyGesture {} +Class SWWMYellowCardGesture : SWWMKeyGesture {} +Class SWWMBlueCardGesture : SWWMKeyGesture {} +Class SWWMSilverCardGesture : SWWMKeyGesture {} +Class SWWMGreenCardGesture : SWWMKeyGesture {} +Class SWWMOrangeCardGesture : SWWMKeyGesture {} +Class SWWMRedSkullGesture : SWWMKeyGesture {} +Class SWWMYellowSkullGesture : SWWMKeyGesture {} +Class SWWMBlueSkullGesture : SWWMKeyGesture {} +Class SWWMGreenKeyGesture : SWWMKeyGesture {} +Class SWWMBlueKeyGesture : SWWMKeyGesture {} +Class SWWMYellowKeyGesture : SWWMKeyGesture {} +Class SWWMRedKeyGesture : SWWMKeyGesture {} diff --git a/zscript/swwm_powerup.zsc b/zscript/items/swwm_powerups.zsc similarity index 100% rename from zscript/swwm_powerup.zsc rename to zscript/items/swwm_powerups.zsc diff --git a/zscript/swwm_kbase.zsc b/zscript/kbase/swwm_kbase.zsc similarity index 100% rename from zscript/swwm_kbase.zsc rename to zscript/kbase/swwm_kbase.zsc diff --git a/zscript/swwm_credits.zsc b/zscript/menu/swwm_credits.zsc similarity index 100% rename from zscript/swwm_credits.zsc rename to zscript/menu/swwm_credits.zsc diff --git a/zscript/swwm_help.zsc b/zscript/menu/swwm_help.zsc similarity index 100% rename from zscript/swwm_help.zsc rename to zscript/menu/swwm_help.zsc diff --git a/zscript/swwm_inter.zsc b/zscript/menu/swwm_inter.zsc similarity index 100% rename from zscript/swwm_inter.zsc rename to zscript/menu/swwm_inter.zsc diff --git a/zscript/swwm_menus.zsc b/zscript/menu/swwm_menus.zsc similarity index 100% rename from zscript/swwm_menus.zsc rename to zscript/menu/swwm_menus.zsc diff --git a/zscript/swwm_title.zsc b/zscript/menu/swwm_title.zsc similarity index 100% rename from zscript/swwm_title.zsc rename to zscript/menu/swwm_title.zsc diff --git a/zscript/swwm_ammo.zsc b/zscript/swwm_ammo.zsc deleted file mode 100644 index 0e263e977..000000000 --- a/zscript/swwm_ammo.zsc +++ /dev/null @@ -1,1921 +0,0 @@ -// All the ammo items go here - -// Common code for ammo division -Mixin Class SWWMAmmo -{ - private Inventory DoDrop( Class type ) - { - let copy = Inventory(Spawn(type,Owner.Pos,NO_REPLACE)); - if ( !copy ) return null; - copy.DropTime = 30; - copy.bSpecial = copy.bSolid = false; - copy.SetOrigin(Owner.Vec3Offset(0,0,10.),false); - copy.Angle = Owner.Angle; - copy.VelFromAngle(5.); - copy.Vel.Z = 1.; - copy.Vel += Owner.Vel; - copy.bNoGravity = false; - copy.ClearCounters(); - copy.OnDrop(Owner); - copy.vel += (RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),Owner.angle),FRandom[Junk](2.,5.)); - return copy; - } - - override bool SpecialDropAction( Actor dropper ) - { - if ( swwm_enemydrops >= 0 ) - { - if ( Amount == default.Amount ) return false; - // subdivide - Owner = dropper; // needed for positioning to work - CreateTossable(Amount); - return true; - } - // no ammo drops from enemies - return true; - } - - private bool CmpAmmo( Class a, Class b ) - { - let amta = GetDefaultByType(a).Amount; - let amtb = GetDefaultByType(b).Amount; - return (amta < amtb); - } - - private int partition_ammotypes( Array > a, int l, int h ) - { - Class pv = a[h]; - int i = (l-1); - for ( int j=l; j<=(h-1); j++ ) - { - if ( CmpAmmo(pv,a[j]) ) - { - i++; - Class tmp = a[j]; - a[j] = a[i]; - a[i] = tmp; - } - } - Class tmp = a[h]; - a[h] = a[i+1]; - a[i+1] = tmp; - return i+1; - } - private void qsort_ammotypes( Array > a, int l, int h ) - { - if ( l >= h ) return; - int p = partition_ammotypes(a,l,h); - qsort_ammotypes(a,l,p-1); - qsort_ammotypes(a,p+1,h); - } - - override inventory CreateTossable( int amt ) - { - if ( bUndroppable || bUntossable || !Owner || (Amount <= 0) || (amt == 0) ) - return null; - // cap - amt = min(amount,amt); - // enumerate all subclasses - Array > ammotypes; - ammotypes.Clear(); - for ( int i=0; i)(AllActorClasses[i])); - } - // sort from largest to smallest - qsort_ammotypes(ammotypes,0,ammotypes.Size()-1); - // perform subdivision - Inventory last = null; - while ( amt > 0 ) - { - for ( int i=0; i= def.Amount ) - { - last = DoDrop(ammotypes[i]); - amt -= def.Amount; - Amount -= def.Amount; - break; - } - } - } - return last; - } - - override bool HandlePickup( Inventory item ) - { - // drop excess ammo - if ( (item is 'Ammo') && (Ammo(item).GetParentAmmo() == GetParentAmmo()) ) - { - int excess = Amount+item.Amount; - if ( excess > MaxAmount ) excess -= MaxAmount; - if ( excess < item.Amount ) - { - // enumerate all subclasses - Array > ammotypes; - ammotypes.Clear(); - for ( int i=0; i)(AllActorClasses[i])); - } - // sort from largest to smallest - for ( int i=0; i 0) && CmpAmmo(ammotypes[k-1],ammotypes[k]) ) - { - Class tmp = ammotypes[k]; - ammotypes[k] = ammotypes[k-1]; - ammotypes[k-1] = tmp; - k--; - } - j++; - } - } - // drop spares - Inventory last; - while ( excess > 0 ) - { - for ( int i=0; i= def.Amount ) - { - double ang = FRandom[Junk](0,360); - last = DoDrop(ammotypes[i]); - last.SetOrigin(item.pos,false); - last.vel.xy = (cos(ang),sin(ang))*FRandom[Junk](2,5); - excess -= def.Amount; - break; - } - } - } - } - } - return Super.HandlePickup(item); - } - - override void DoEffect() - { - Super.DoEffect(); - // drop excess ammo - if ( !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo') ) - { - int excess = Amount-MaxAmount; - if ( excess > 0 ) CreateTossable(excess); - } - } - - default - { - +INVENTORY.IGNORESKILL; - Inventory.PickupFlash "SWWMPickupFlash"; - } -} - -// Common code for individual bullets -Class MagAmmo : Inventory abstract -{ - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Class ParentAmmo; - Ammo pamo; - int ClipSize; - int countdown; - - Property ParentAmmo : ParentAmmo; - Property ClipSize : ClipSize; - - default - { - +INVENTORY.KEEPDEPLETED; - Inventory.PickupSound "misc/bullet_pkup"; - Inventory.Amount 1; - Inventory.PickupFlash "SWWMPickupFlash"; - } - - override bool HandlePickup( Inventory item ) - { - // see if the mag can be split apart - if ( (item is 'Ammo') && (ParentAmmo == Ammo(item).GetParentAmmo()) ) - { - // double-check that parent ammo exists - if ( !pamo ) - { - pamo = Ammo(Owner.FindInventory(ParentAmmo)); - if ( !pamo ) - { - pamo = Ammo(Spawn(ParentAmmo)); - pamo.AttachToOwner(Owner); - pamo.Amount = 0; - } - } - if ( (pamo.Amount >= pamo.MaxAmount) && (Amount < MaxAmount) ) - { - // split - for ( int i=0; i 0 ) CreateTossable(dropamt); - Amount = min(MaxAmount,Amount+bul); - } - item.bPickupGood = true; - return true; - } - } - return Super.HandlePickup(item); - } - - private Inventory DoDrop( Class type ) - { - let copy = Inventory(Spawn(type,Owner.Pos,NO_REPLACE)); - if ( !copy ) return null; - copy.DropTime = 30; - copy.bSpecial = copy.bSolid = false; - copy.SetOrigin(Owner.Vec3Offset(0,0,10.),false); - copy.Angle = Owner.Angle; - copy.VelFromAngle(5.); - copy.Vel.Z = 1.; - copy.Vel += Owner.Vel; - copy.bNoGravity = false; - copy.ClearCounters(); - copy.OnDrop(Owner); - copy.vel += (RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),Owner.angle),FRandom[Junk](2.,5.)); - return copy; - } - - override bool SpecialDropAction( Actor dropper ) - { - if ( swwm_enemydrops >= 0 ) - { - if ( Amount == default.Amount ) return false; - // subdivide - Owner = dropper; // needed for positioning to work - CreateTossable(Amount); - return true; - } - // no ammo drops from enemies - return true; - } - - override void DoEffect() - { - Super.DoEffect(); - // drop excess ammo - if ( !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo') ) - { - int excess = Amount-MaxAmount; - if ( excess > 0 ) CreateTossable(excess); - } - if ( !pamo ) - { - pamo = Ammo(Owner.FindInventory(ParentAmmo)); - if ( !pamo ) - { - pamo = Ammo(Spawn(ParentAmmo)); - pamo.AttachToOwner(Owner); - pamo.Amount = 0; - } - } - // check if we can fill a mag (delayed) - if ( (Amount < ClipSize) || (pamo.Amount >= pamo.MaxAmount) ) - { - countdown = 35; - return; - } - if ( countdown-- > 0 ) return; - MagFill(); - } - - bool MagFill() - { - // double-check that parent ammo exists - if ( !pamo ) - { - pamo = Ammo(Owner.FindInventory(ParentAmmo)); - if ( !pamo ) - { - pamo = Ammo(Spawn(ParentAmmo)); - pamo.AttachToOwner(Owner); - pamo.Amount = 0; - } - } - bool given = false; - while ( (pamo.Amount < pamo.MaxAmount) && (Amount >= ClipSize) ) - { - pamo.Amount++; - Amount -= ClipSize; - given = true; - if ( Owner.CheckLocalView() ) - pamo.PrintPickupMessage(true,pamo.PickupMessage()); - } - if ( given ) pamo.PlayPickupSound(Owner); - return given; - } - - override inventory CreateTossable( int amt ) - { - if ( bUndroppable || bUntossable || !Owner || (Amount <= 0) || (amt == 0) ) - return null; - // cap - amt = min(amount,amt); - // perform subdivision - Inventory last = null; - let pammo = GetDefaultByType(ParentAmmo); - while ( amt > 0 ) - { - // drop full mag if possible - if ( amt >= ClipSize ) - { - last = DoDrop(ParentAmmo); - amt -= ClipSize; - Amount -= ClipSize; - continue; - } - // drop individual bullets - last = DoDrop(GetClass()); - amt--; - Amount--; - } - return last; - } - - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Random[ShellDrop](1,ClipSize),Amount); - } -} - -// ============================================================================ -// Spreadgun / Wallbuster ammo -// ============================================================================ - -// Common code for grouped shell handling and per-amount pickup messages -Mixin Class SWWMShellAmmo -{ - override string PickupMessage() - { - String tagstr = "$T_"..GetParentAmmo().GetClassName(); - tagstr.MakeUpper(); - if ( Amount > 1 ) - { - tagstr = tagstr.."S"; - return String.Format("%d %s",Amount,StringTable.Localize(tagstr)); - } - return StringTable.Localize(tagstr); - } - - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = max(1,Amount+Random[ShellDrop](-2,1)); - } -} - -Class RedShell : Ammo -{ - Mixin SWWMShellAmmo; - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_REDSHELLS"; - Stamina 500; - Inventory.Icon "graphics/HUD/Icons/A_ShellsNormal.png"; - Inventory.Amount 1; - Inventory.MaxAmount 30; - Ammo.BackpackAmount 5; - Ammo.BackpackMaxAmount 100; - Ammo.DropAmount 2; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - Height 20; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class RedShell2 : RedShell -{ - Default - { - Inventory.Amount 2; - Radius 6; - } -} -Class RedShell4 : RedShell -{ - Default - { - Inventory.Amount 4; - Radius 6; - } -} - -Class GreenShell : Ammo -{ - Mixin SWWMShellAmmo; - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_GREENSHELLS"; - Stamina 800; - Inventory.Icon "graphics/HUD/Icons/A_ShellsSlug.png"; - Inventory.Amount 1; - Inventory.MaxAmount 25; - Ammo.BackpackAmount 4; - Ammo.BackpackMaxAmount 80; - Ammo.DropAmount 2; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - Height 20; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class GreenShell2 : GreenShell -{ - Default - { - Inventory.Amount 2; - Radius 6; - } -} -Class GreenShell4 : GreenShell -{ - Default - { - Inventory.Amount 4; - Radius 6; - } -} - -Class WhiteShell : Ammo -{ - Mixin SWWMShellAmmo; - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_WHITESHELLS"; - Stamina 1000; - Inventory.Icon "graphics/HUD/Icons/A_ShellsDragon.png"; - Inventory.Amount 1; - Inventory.MaxAmount 10; - Ammo.BackpackAmount 1; - Ammo.BackpackMaxAmount 30; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class WhiteShell2 : WhiteShell -{ - Default - { - Inventory.Amount 2; - Radius 6; - } -} -Class WhiteShell4 : WhiteShell -{ - Default - { - Inventory.Amount 4; - Radius 6; - } -} - -Class BlueShell : Ammo -{ - Mixin SWWMShellAmmo; - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_BLUESHELLS"; - Stamina 2500; - Inventory.Icon "graphics/HUD/Icons/A_ShellsKinylum.png"; - Inventory.Amount 1; - Inventory.MaxAmount 15; - Ammo.BackpackAmount 2; - Ammo.BackpackMaxAmount 40; - Ammo.DropAmount 2; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - Height 20; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class BlueShell2 : BlueShell -{ - Default - { - Inventory.Amount 2; - Radius 6; - } -} -Class BlueShell4 : BlueShell -{ - Default - { - Inventory.Amount 4; - Radius 6; - } -} - -Class BlackShell : Ammo -{ - Mixin SWWMShellAmmo; - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_BLACKSHELLS"; - Stamina 4000; - Inventory.Icon "graphics/HUD/Icons/A_ShellsFuck.png"; - Inventory.Amount 1; - Inventory.MaxAmount 10; - Ammo.BackpackAmount 0; - Ammo.BackpackMaxAmount 20; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - Height 20; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class BlackShell2 : BlackShell -{ - Default - { - Inventory.Amount 2; - Radius 6; - } -} -Class BlackShell4 : BlackShell -{ - Default - { - Inventory.Amount 4; - Radius 6; - } -} - -Class PurpleShell : Ammo -{ - Mixin SWWMShellAmmo; - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_PURPLESHELLS"; - Stamina 1500; - Inventory.Icon "graphics/HUD/Icons/A_ShellsBall.png"; - Inventory.Amount 1; - Inventory.MaxAmount 20; - Ammo.BackpackAmount 3; - Ammo.BackpackMaxAmount 60; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - Height 20; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class PurpleShell2 : PurpleShell -{ - Default - { - Inventory.Amount 2; - Radius 6; - } -} -Class PurpleShell4 : PurpleShell -{ - Default - { - Inventory.Amount 4; - Radius 6; - } -} - -Class GoldShellSparkle : Actor -{ - override void PostBeginPlay() - { - Scale *= FRandom[Goldy](.5,1.5); - vel.z = FRandom[Goldy](.5,1.5); - } - override void Tick() - { - if ( isFrozen() ) return; - SetOrigin(Vec3Offset(0,0,vel.z),true); - alpha -= .02; - if ( alpha <= 0 ) Destroy(); - } - Default - { - RenderStyle "Add"; - Scale 0.05; - +NOGRAVITY; - +NOBLOCKMAP; - +NOINTERACTION; - +DONTSPLASH; - +NOTELEPORT; - +FORCEXYBILLBOARD; - FloatBobPhase 0; - } - States - { - Spawn: - BLPF A -1 Bright; - Stop; - } -} - -Class GoldShell : Ammo -{ - Mixin SWWMShellAmmo; - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - action void A_GoldShellTrail() - { - if ( Random[Goldy](0,2) ) return; - Spawn("GoldShellSparkle",Vec3Offset(FRandom[Goldy](-radius,radius)/2.,FRandom[Goldy](-radius,radius)/2.,FRandom[Goldy](10,18))); - } - - Default - { - Tag "$T_GOLDSHELLS"; - Stamina -1000000; - Inventory.Icon "graphics/HUD/Icons/A_ShellsGold.png"; - Inventory.Amount 1; - Inventory.MaxAmount 7; - Ammo.BackpackAmount 0; - Ammo.BackpackMaxAmount 7; - Ammo.DropAmount 1; - +FLOATBOB; - +DONTGIB; // don't crush these, they're valuable - FloatBobStrength 0.25; - Radius 4; - Height 20; - } - States - { - Spawn: - XZW1 A 1 A_GoldShellTrail(); - Wait; - } -} -Class GoldShell2 : GoldShell -{ - Default - { - Inventory.Amount 2; - Radius 6; - } -} -Class GoldShell4 : GoldShell -{ - Default - { - Inventory.Amount 4; - Radius 6; - } -} - -// ============================================================================ -// Eviscerator ammo -// ============================================================================ - -Class EvisceratorShell : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_EVISHELLS"; - Inventory.PickupMessage "$T_EVISHELL"; - Stamina 3000; - Inventory.Icon "graphics/HUD/Icons/A_Eviscerator.png"; - Inventory.Amount 1; - Inventory.MaxAmount 20; - Ammo.BackpackAmount 5; - Ammo.BackpackMaxAmount 90; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 8; - Height 22; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,4); - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} - -Class EvisceratorSixPack : EvisceratorShell -{ - Default - { - Tag "$I_EVISHELLPAK"; - Inventory.PickupMessage "$I_EVISHELLPAK"; - Inventory.Amount 6; - Radius 20; - Height 24; - } -} - -Class EvisceratorTrioSpawn : Actor -{ - override void PostBeginPlay() - { - if ( bCOUNTSECRET ) level.total_secrets--; - for ( int i=0; i<3; i++ ) - { - let a = Spawn("EvisceratorShell",Vec3Angle(12,i*120)); - a.special = special; - a.angle = i*120; - a.FloatBobPhase = FloatBobPhase; - for ( int j=0; j<5; j++ ) a.args[j] = args[j]; - if ( bCOUNTSECRET ) - { - a.bCOUNTSECRET = true; - level.total_secrets++; - } - } - Destroy(); - } -} - -// ============================================================================ -// Hellblazer ammo -// ============================================================================ - -Class HellblazerMissiles : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_HELLMISSILES"; - Inventory.PickupMessage "$T_HELLMISSILE"; - Stamina 8000; - Inventory.Icon "graphics/HUD/Icons/A_HellblazerMissile.png"; - Inventory.Amount 1; - Inventory.MaxAmount 20; - Ammo.BackpackAmount 3; - Ammo.BackpackMaxAmount 60; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 6; - Height 22; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,3); - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class HellblazerMissileMag : HellblazerMissiles -{ - Default - { - Tag "$T_HELLMISSILEMAG"; - Inventory.PickupMessage "$T_HELLMISSILEMAG"; - Inventory.Amount 6; - Radius 12; - Height 26; - } -} -Class HellblazerMissileTrioSpawn : Actor -{ - override void PostBeginPlay() - { - if ( bCOUNTSECRET ) level.total_secrets--; - for ( int i=0; i<3; i++ ) - { - let a = Spawn("HellblazerMissiles",Vec3Angle(8,i*120)); - a.special = special; - a.angle = i*120; - a.FloatBobPhase = FloatBobPhase; - for ( int j=0; j<5; j++ ) a.args[j] = args[j]; - if ( bCOUNTSECRET ) - { - a.bCOUNTSECRET = true; - level.total_secrets++; - } - } - Destroy(); - } -} - -Class HellblazerCrackshots : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_HELLCLUSTERS"; - Inventory.PickupMessage "$T_HELLCLUSTER"; - Stamina 15000; - Inventory.Icon "graphics/HUD/Icons/A_HellblazerCrackshot.png"; - Inventory.Amount 1; - Inventory.MaxAmount 15; - Ammo.BackpackAmount 2; - Ammo.BackpackMaxAmount 30; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 6; - Height 22; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,2); - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class HellblazerCrackshotMag : HellblazerCrackshots -{ - Default - { - Tag "$T_HELLCLUSTERMAG"; - Inventory.PickupMessage "$T_HELLCLUSTERMAG"; - Inventory.Amount 3; - Radius 12; - Height 26; - } -} - -Class HellblazerRavagers : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_HELLBURNINATORS"; - Inventory.PickupMessage "$T_HELLBURNINATOR"; - Stamina 25000; - Inventory.Icon "graphics/HUD/Icons/A_HellblazerRavager.png"; - Inventory.Amount 1; - Inventory.MaxAmount 9; - Ammo.BackpackAmount 1; - Ammo.BackpackMaxAmount 18; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 6; - Height 22; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,1); - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class HellblazerRavagerMag : HellblazerRavagers -{ - Default - { - Tag "$T_HELLBURNINATORMAG"; - Inventory.PickupMessage "$T_HELLBURNINATORMAG"; - Inventory.Amount 3; - Radius 12; - Height 26; - } -} - -Class HellblazerWarheads : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_HELLNUKES"; - Inventory.PickupMessage "$T_HELLNUKE"; - Stamina 40000; - Inventory.Icon "graphics/HUD/Icons/A_HellblazerWarhead.png"; - Inventory.Amount 1; - Inventory.MaxAmount 4; - Ammo.BackpackAmount 0; - Ammo.BackpackMaxAmount 8; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 6; - Height 22; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,1); - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class HellblazerWarheadMag : HellblazerWarheads -{ - Default - { - Tag "$T_HELLNUKEMAG"; - Inventory.PickupMessage "$T_HELLNUKEMAG"; - Inventory.Amount 2; - Radius 12; - Height 26; - } -} - -// ============================================================================ -// Sparkster ammo -// ============================================================================ - -Class SparkUnit : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_SPARKUNIT"; - Inventory.PickupMessage "$T_SPARKUNIT"; - Stamina 50000; - Inventory.Icon "graphics/HUD/Icons/A_Sparkster.png"; - Inventory.Amount 1; - Inventory.MaxAmount 10; - Ammo.BackpackAmount 1; - Ammo.BackpackMaxAmount 30; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 6; - Height 22; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,1); - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} - -// ============================================================================ -// Silver Bullet ammo -// ============================================================================ - -Class SilverBulletAmmo : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_XSBMAG"; - Inventory.PickupMessage "$T_XSBMAG"; - Stamina 70000; - Inventory.Icon "graphics/HUD/Icons/A_SilverBullet.png"; - Inventory.Amount 1; - Inventory.MaxAmount 3; - Ammo.BackpackAmount 0; - Ammo.BackpackMaxAmount 8; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 10; - Height 26; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,1); - } - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - // attach our mag ammo if we have none - if ( Owner.FindInventory('SilverBullets') ) - { - let ma = Inventory(Spawn('SilverBullets')); - ma.Amount = 0; - ma.AttachToOwner(Owner); - } - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} - -Class SilverBulletAmmo2 : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_FCBMAG"; - Inventory.PickupMessage "$T_FCBMAG"; - Stamina 80000; - Inventory.Icon "graphics/HUD/Icons/A_SilverBullet2.png"; - Inventory.Amount 1; - Inventory.MaxAmount 2; - Ammo.BackpackAmount 0; - Ammo.BackpackMaxAmount 4; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 10; - Height 26; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,1); - } - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - // attach our mag ammo if we have none - if ( Owner.FindInventory('SilverBullets2') ) - { - let ma = Inventory(Spawn('SilverBullets2')); - ma.Amount = 0; - ma.AttachToOwner(Owner); - } - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} - -Class SilverBullets : MagAmmo -{ - Default - { - Tag "$T_XSBBULLET"; - Inventory.PickupMessage "$T_XSBBULLET"; - Inventory.Icon "graphics/HUD/Icons/A_SilverBulletBullet.png"; - MagAmmo.ParentAmmo "SilverBulletAmmo"; - MagAmmo.ClipSize 5; - Inventory.MaxAmount 8; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - Height 22; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} -Class SilverBullets2 : MagAmmo -{ - Default - { - Tag "$T_FCBBULLET"; - Inventory.PickupMessage "$T_FCBBULLET"; - Inventory.Icon "graphics/HUD/Icons/A_SilverBulletBullet2.png"; - MagAmmo.ParentAmmo "SilverBulletAmmo2"; - MagAmmo.ClipSize 5; - Inventory.MaxAmount 8; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 4; - Height 22; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} - -Class SilverBulletsBundleSpawn : Actor -{ - override void PostBeginPlay() - { - if ( bCOUNTSECRET ) level.total_secrets--; - int bnd = Random[Bundle](2,3); - for ( int i=0; i 0 ) - { - // drop an empty spare - let spare = Inventory(Spawn('CandyGun',Owner.Pos,NO_REPLACE)); - if ( !spare ) return null; - // spare with empty mag - CandyGun(spare).AmmoGive1 = 0; - CandyGun(spare).clipcount = 0; - CandyGun(spare).chambered = false; - spare.SetState(spare.SpawnState+1); - spare.DropTime = 30; - spare.bSpecial = spare.bSolid = false; - Amount--; - return spare; - } - return null; - } -} - -// ============================================================================ -// Ynykron ammo -// ============================================================================ - -Class YnykronAmmo : Ammo -{ - Mixin SWWMAmmo; - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - Default - { - Tag "$T_YNYKRONAMMO"; - Inventory.PickupMessage "$T_YNYKRONAMMO"; - Stamina 3000000; - Inventory.Icon "graphics/HUD/Icons/A_Ynykron.png"; - Inventory.Amount 1; - Inventory.MaxAmount 1; - Ammo.BackpackAmount 0; - Ammo.BackpackMaxAmount 3; - Ammo.DropAmount 1; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 8; - Height 24; - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - Amount = min(Amount,1); - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} - -// ============================================================================ -// Ammo fabricator -// ============================================================================ - -Class AmmoFabricator : Inventory abstract -{ - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - int budget, pertype, maxunitprice; - - Property Budget : budget; - Property PerType : pertype; - Property MaxUnitPrice : maxunitprice; - - override Inventory CreateCopy( Actor other ) - { - // additional lore - SWWMLoreLibrary.Add(other.player,"Fabricator"); - return Super.CreateCopy(other); - } - - private bool CmpFabAmmo( Class a, Class b ) - { - let ia = Owner.FindInventory(a); - int cnta = ia?ia.Amount:0; - int maxa = ia?ia.MaxAmount:GetDefaultByType(a).Amount; - let ib = Owner.FindInventory(b); - int cntb = ib?ib.Amount:0; - int maxb = ib?ib.MaxAmount:GetDefaultByType(b).Amount; - double facta = cnta/double(maxa); - double factb = cntb/double(maxb); - return (facta >= factb); - } - - private int partition_fabammo( Array > a, int l, int h ) - { - Class pv = a[h]; - int i = (l-1); - for ( int j=l; j<=(h-1); j++ ) - { - if ( CmpFabAmmo(pv,a[j]) ) - { - i++; - Class tmp = a[j]; - a[j] = a[i]; - a[i] = tmp; - } - } - Class tmp = a[h]; - a[h] = a[i+1]; - a[i+1] = tmp; - return i+1; - } - private void qsort_fabammo( Array > a, int l, int h ) - { - if ( l >= h ) return; - int p = partition_fabammo(a,l,h); - qsort_fabammo(a,l,p-1); - qsort_fabammo(a,p+1,h); - } - - bool FabricateAmmo() - { - Array > available; - // populate ammo production list - for ( int i=0; i)(AllActorClasses[i]); - // skip over candy gun spares, they're "special ammo" - if ( a == 'CandyGunSpares' ) continue; - // only direct descendants of ammo with a set price below our max unit price - if ( !a || (a.GetParentClass() != 'Ammo') ) continue; - let def = GetDefaultByType(a); - if ( (def.Stamina <= 0) || (def.Stamina > maxunitprice) ) continue; - // only ammo for weapons that are valid (can be used) - bool isvalid = false; - for ( int j=0; j)(AllActorClasses[j]); - if ( !type ) continue; - let rep = GetReplacement(type); - if ( (rep != type) && !(rep is "DehackedPickup") ) continue; - readonly weap = GetDefaultByType(type); - if ( !Owner.player || !Owner.player.weapons.LocateWeapon(type) || weap.bCheatNotWeapon ) continue; - let ready = weap.FindState("Ready"); - if ( !ready || !ready.ValidateSpriteFrame() ) continue; - if ( (type is 'SWWMWeapon') && SWWMWeapon(weap).UsesAmmo(a) ) - { - isvalid = true; - break; - } - if ( (weap.AmmoType1 == a) || (weap.AmmoType2 == a) ) - { - isvalid = true; - break; - } - } - if ( !isvalid ) continue; - available.Push(a); - } - // sort by "need weight" (prioritize ammo that the player lacks over ammo that the player has plenty of - qsort_fabammo(available,0,available.Size()-1); - // loop through until we fill the inventory or run out of budget - bool given = false; - int consumed = 0; - String fabstr = ""; - bool comma = false; - int tpertype = pertype; - for ( int i=0; i 0 ) - { - if ( comma ) fabstr.AppendFormat(", %dx %s",cnt,cur.GetTag()); - else fabstr.AppendFormat("%dx %s",cnt,cur.GetTag()); - comma = true; - } - } - if ( given ) PrintPickupMessage(true,fabstr); - return given; - } - - override bool Use( bool pickup ) - { - bool shouldautouse = false; - if ( swwm_enforceautouseammo == 1 ) shouldautouse = true; - else if ( swwm_enforceautouseammo == -1 ) shouldautouse = false; - else shouldautouse = CVar.GetCVar('swwm_autouseammo',Owner.player).GetBool(); - if ( pickup && !shouldautouse ) return false; - if ( FabricateAmmo() ) - { - if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); - return true; - } - return false; - } - - Default - { - +INVENTORY.INVBAR; - +INVENTORY.AUTOACTIVATE; - +FLOATBOB; - Inventory.UseSound "fabricator/use"; - Inventory.PickupFlash "SWWMPickupFlash"; - FloatBobStrength 0.25; - Radius 10; - Height 24; - } - States - { - Spawn: - XZW1 A -1; - Stop; - } -} - -Class FabricatorTier1 : AmmoFabricator -{ - Mixin SWWMAutoUseFix; - - Default - { - Tag "$T_FABRICATOR1"; - Stamina -2500; - Inventory.Icon "graphics/HUD/Icons/I_Fabricator1.png"; - Inventory.PickupMessage "$T_FABRICATOR1"; - Inventory.MaxAmount 20; - Inventory.InterHubAmount 20; - AmmoFabricator.Budget 3000; - AmmoFabricator.PerType 1; - AmmoFabricator.MaxUnitPrice 2500; - } -} -Class FabricatorTier2 : AmmoFabricator -{ - Mixin SWWMAutoUseFix; - - Default - { - Tag "$T_FABRICATOR2"; - Stamina -12000; - Inventory.Icon "graphics/HUD/Icons/I_Fabricator2.png"; - Inventory.PickupMessage "$T_FABRICATOR2"; - Inventory.MaxAmount 15; - Inventory.InterHubAmount 15; - AmmoFabricator.Budget 15000; - AmmoFabricator.PerType 2; - AmmoFabricator.MaxUnitPrice 12000; - } -} -Class FabricatorTier3 : AmmoFabricator -{ - Mixin SWWMAutoUseFix; - - Default - { - Tag "$T_FABRICATOR3"; - Stamina -80000; - Inventory.Icon "graphics/HUD/Icons/I_Fabricator3.png"; - Inventory.PickupMessage "$T_FABRICATOR3"; - Inventory.MaxAmount 10; - Inventory.InterHubAmount 10; - AmmoFabricator.Budget 100000; - AmmoFabricator.PerType 4; - AmmoFabricator.MaxUnitPrice 80000; - } -} -Class FabricatorTier4 : AmmoFabricator -{ - Default - { - Tag "$T_FABRICATOR4"; - Stamina -1000000; - Inventory.Icon "graphics/HUD/Icons/I_Fabricator4.png"; - Inventory.PickupMessage "$T_FABRICATOR4"; - Inventory.MaxAmount 5; - Inventory.InterHubAmount 5; - AmmoFabricator.Budget int.max; - AmmoFabricator.PerType -50; - AmmoFabricator.MaxUnitPrice 1000000; - -INVENTORY.AUTOACTIVATE; - } -} - -// ============================================================================ -// Hammerspace embiggener -// ============================================================================ - -Class HammerspaceEmbiggener : Inventory -{ - Mixin SWWMOverlapPickupSound; - Mixin SWWMUseToPickup; - - override Inventory CreateCopy( Actor other ) - { - bool traded = (GetClass()=='TradedHammerspaceEmbiggener'); - if ( !traded ) other.A_StartSound("powerup/embiggener",CHAN_ITEMEXTRA); - // Find every unique type of ammoitem. Give it to the player if - // he doesn't have it already, and increase its maximum capacity. - for ( int i=0; i)(AllActorClasses[i]); - if ( !type || (type.GetParentClass() != 'Ammo') ) continue; - // check that it's for a valid weapon - bool isvalid = false; - for ( int j=0; j)(AllActorClasses[j]); - if ( !type2 ) continue; - let rep = GetReplacement(type2); - if ( (rep != type2) && !(rep is "DehackedPickup") ) continue; - readonly weap = GetDefaultByType(type2); - if ( !other.player || !other.player.weapons.LocateWeapon(type2) || weap.bCheatNotWeapon ) continue; - let ready = weap.FindState("Ready"); - if ( !ready || !ready.ValidateSpriteFrame() ) continue; - if ( (type2 is 'SWWMWeapon') && SWWMWeapon(weap).UsesAmmo(type) ) - { - isvalid = true; - break; - } - if ( (weap.AmmoType1 == type) || (weap.AmmoType2 == type) ) - { - isvalid = true; - break; - } - } - if ( !isvalid ) continue; - let ammoitem = Ammo(other.FindInventory(type)); - int amount = GetDefaultByType(type).BackpackAmount*self.Amount; - if ( traded ) amount = 0; - if ( amount < 0 ) amount = 0; - if ( !ammoitem ) - { - // The player did not have the ammoitem. Add it. - ammoitem = Ammo(Spawn(type)); - ammoitem.Amount = amount; - if ( ammoitem.BackpackMaxAmount > 0 ) - { - double factor = (ammoitem.BackpackMaxAmount-ammoitem.default.MaxAmount)/double(MaxAmount); - ammoitem.MaxAmount = int(ammoitem.default.MaxAmount+min(self.Amount,MaxAmount)*factor); - } - if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup ) - ammoitem.Amount = ammoitem.MaxAmount; - ammoitem.AttachToOwner(other); - } - else - { - // The player had the ammoitem. Give some more. - if ( ammoitem.BackpackMaxAmount > 0 ) - { - double factor = (ammoitem.BackpackMaxAmount-ammoitem.default.MaxAmount)/double(MaxAmount); - ammoitem.MaxAmount = int(ammoitem.default.MaxAmount+min(self.Amount,MaxAmount)*factor); - } - if ( ammoitem.Amount < ammoitem.MaxAmount ) - { - if ( (ammoitem.Amount > 0) && (ammoitem.Amount+amount < 0) ) - ammoitem.Amount = int.max; - else ammoitem.Amount += amount; - if ( (ammoitem.Amount > ammoitem.MaxAmount) && !sv_unlimited_pickup ) - ammoitem.Amount = ammoitem.MaxAmount; - } - } - } - self.Amount = min(self.Amount,MaxAmount); - if ( GetParentClass() == 'HammerspaceEmbiggener' ) - { - if ( !GoAway() ) Destroy(); - let copy = Inventory(Spawn('HammerspaceEmbiggener')); - copy.ClearCounters(); - copy.Amount = self.Amount; - copy.MaxAmount = self.MaxAmount; - return copy; - } - if ( GoAway() ) - { - let copy = Inventory(Spawn(GetClass())); - copy.ClearCounters(); - copy.Amount = self.Amount; - copy.MaxAmount = self.MaxAmount; - return copy; - } - return self; - } - - override bool HandlePickup( Inventory item ) - { - if ( (item.GetClass() == GetClass()) || (item.GetParentClass() == 'HammerspaceEmbiggener') ) - { - bool traded = (item.GetClass()=='TradedHammerspaceEmbiggener'); - if ( !traded ) Owner.A_StartSound("powerup/embiggener",CHAN_ITEMEXTRA); - if ( (Amount > 0) && (Amount+item.Amount < 0) ) - Amount = int.max; - else Amount += item.Amount; - if ( Amount > MaxAmount ) Amount = MaxAmount; - item.bPickupGood = true; - // readjust ammo values to new capacity - for ( Inventory i=Owner.Inv; i; i=i.Inv ) - { - if ( !(i is 'Ammo') ) continue; - if ( Ammo(i).BackpackMaxAmount > 0 ) - { - double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(MaxAmount); - i.MaxAmount = int(i.default.MaxAmount+Amount*factor); - } - int amount = Ammo(i).BackpackAmount*item.Amount; - if ( traded ) i.Amount = 0; - if ( (i.Amount > 0) && (i.Amount+amount < 0) ) - i.Amount = int.max; - else i.Amount += amount; - if ( (i.Amount > i.MaxAmount) && !sv_unlimited_pickup ) - i.Amount = i.MaxAmount; - } - return true; - } - // new ammo suddenly added? upgrade it (this shouldn't happen unless fucky scripting has been involved) - if ( (item is 'Ammo') && !Owner.FindInventory(Ammo(item).GetParentAmmo()) ) - { - if ( Ammo(item).BackpackMaxAmount > 0 ) - { - double factor = (Ammo(item).BackpackMaxAmount-item.default.MaxAmount)/double(MaxAmount); - item.MaxAmount = int(item.default.MaxAmount+Amount*factor); - } - } - return false; - } - - override void DepleteOrDestroy() - { - // reset upgrade - for ( Inventory i=Owner.Inv; i; i=i.Inv ) - { - if ( !(i is 'Ammo') ) continue; - i.MaxAmount = i.default.MaxAmount; - if ( i.Amount > i.MaxAmount ) i.Amount = i.MaxAmount; - } - Super.DepleteOrDestroy(); - } - - // merges overlapping embiggeners into a bulk upgrade - void A_MergeEmbiggeners() - { - let bt = BlockThingsIterator.Create(self,16); - int tamount = Amount; - while ( bt.Next() ) - { - let t = bt.Thing; - if ( !t || (t == self) || !(t is 'HammerspaceEmbiggener') || !(t.spawnpoint ~== spawnpoint) ) continue; - tamount += HammerspaceEmbiggener(t).Amount; - t.ClearCounters(); - t.Destroy(); - } - if ( tamount <= 1 ) return; - tamount -= tamount%2; // always even numbered - if ( GetClass() == 'BulkHammerspaceEmbiggener' ) - { - Amount = min(tamount,MaxAmount); - return; - } - let n = Spawn("BulkHammerspaceEmbiggener",pos); - Inventory(n).Amount = min(tamount,MaxAmount); - n.spawnpoint = spawnpoint; - n.spawnangle = spawnangle; - n.angle = angle; - n.pitch = pitch; - n.roll = roll; - n.special = special; - for ( int i=0; i<5; i++ ) n.args[i] = args[i]; - n.special1 = special1; - n.special2 = special2; - n.spawnflags = spawnflags&~MTF_SECRET; - n.HandleSpawnFlags(); - n.spawnflags = spawnflags; - n.bCountSecret = spawnflags&MTF_SECRET; - n.ChangeTid(tid); - n.vel = vel; - n.master = master; - n.tracer = tracer; - n.target = target; - if ( !bDROPPED ) n.bDROPPED = false; - ClearCounters(); - Destroy(); - } - - Default - { - Tag "$T_EMBIGGENER"; - Stamina -800000; - Inventory.PickupMessage "$T_EMBIGGENER"; - Inventory.MaxAmount 8; - Inventory.InterHubAmount 8; - Inventory.PickupFlash "SWWMPickupFlash"; - +INVENTORY.UNDROPPABLE; - +INVENTORY.UNTOSSABLE; - +INVENTORY.ALWAYSPICKUP; - +COUNTITEM; - +FLOATBOB; - FloatBobStrength 0.25; - Radius 8; - Height 24; - } - States - { - Spawn: - XZW1 A -1 NoDelay A_MergeEmbiggeners(); - Stop; - } -} - -// used when cheating or trading, this version does not give ammo and is meant -// only for GiveInventory, so it shouldn't be spawned in the world -Class TradedHammerspaceEmbiggener : HammerspaceEmbiggener {} - -// used to denote "merged" embiggeners, changes color based on amount -// green (2+) -// blue (4+) -// purple (6+) -// black (8+) -Class BulkHammerspaceEmbiggener : HammerspaceEmbiggener -{ - override string PickupMessage() - { - return String.Format("%dx %s",Amount,StringTable.Localize("$T_BULKEMBIGGENER")); - } - States - { - Spawn: - XZW1 A -1 NoDelay - { - A_MergeEmbiggeners(); - if ( bDestroyed ) return ResolveState(null); - if ( Amount > 1 ) return SpawnState+min(4,Amount/2); - return ResolveState(null); - } - XZW1 BCDE -1; - Stop; - } -} diff --git a/zscript/swwm_common.zsc b/zscript/swwm_common.zsc index 6ea0c2521..79df9464b 100644 --- a/zscript/swwm_common.zsc +++ b/zscript/swwm_common.zsc @@ -1,4 +1,6 @@ // common code goes here + +// extra sound channels for the mod enum ESWWMGZChannels { CHAN_YOUDONEFUCKEDUP = 63200, // exception handler @@ -15,6 +17,9 @@ enum ESWWMGZChannels CHAN_AMBEXTRA = 63211 // player ambience when submerged }; +const FallbackTag = "AWESOME IT'S PENIS"; // used on tag processing, please don't mind the actual string used) +const MaxBouncePerTic = 40; // maximum simultaneous bounces in one tic for a lightweight actor before we consider it's stuck + // Future planning, will be filled out with AI stuff and whatnot someday Class SWWMMonster : Actor abstract { @@ -376,763 +381,6 @@ Class SWWMBossBrain : BossBrain } } -// imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop -Class PaletteLight : PointLight -{ - Color pal[256]; - bool IsLooping; - int InitialReactionTime; - - Default - { - Tag "Explosion"; - Args 0,0,0,80; - ReactionTime 15; - } - private void UpdateLight() - { - int index = clamp(255-((255*ReactionTime)/abs(InitialReactionTime)),0,255); - args[LIGHT_RED] = pal[index].r; - args[LIGHT_GREEN] = pal[index].g; - args[LIGHT_BLUE] = pal[index].b; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - InitialReactionTime = ReactionTime; - String palname = GetTag(); - int sep = palname.IndexOf(","); - int palnum = 0; - if ( sep != -1 ) - { - String palnumstr = palname.Mid(sep+1); - palnum = palnumstr.ToInt()*768; - palname.Truncate(sep); - } - int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",palname)); - String paldat = Wads.ReadLump(lump); - for ( int i=0; i<256; i++ ) - { - pal[i].r = paldat.ByteAt(palnum++); - pal[i].g = paldat.ByteAt(palnum++); - pal[i].b = paldat.ByteAt(palnum++); - } - if ( ReactionTime < 0 ) - { - ReactionTime = -ReactionTime; - IsLooping = true; - } - UpdateLight(); - } - override void Tick() - { - Super.Tick(); - if ( isFrozen() ) return; - ReactionTime--; - if ( ReactionTime < 0 ) - { - if ( !IsLooping ) - { - Destroy(); - return; - } - else ReactionTime = abs(InitialReactionTime); - } - if ( target ) SetOrigin(target.pos,true); - UpdateLight(); - } -} - -// Generic smoke, lightweight tick -Class SWWMSmoke : Actor -{ - Default - { - RenderStyle "Shaded"; - StencilColor "FFFFFF"; - Radius 2; - Height 2; - Speed 1; - +NOBLOCKMAP; - +NOGRAVITY; - +DONTSPLASH; - +FORCEXYBILLBOARD; - +ROLLSPRITE; - +ROLLCENTER; - +THRUACTORS; - +NOTELEPORT; - +NOINTERACTION; - Scale .3; - FloatBobPhase 0; - } - - override void PostBeginPlay() - { - double ang, pt; - scale *= FRandom[Puff](.5,1.5); - alpha = min(1.,alpha*FRandom[Puff](.5,1.5)); - ang = FRandom[Puff](0,360); - pt = FRandom[Puff](-90,90); - vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](.2,.8)*speed; - roll = Frandom[Puff](0,360); - scale.x *= RandomPick[Puff](-1,1); - scale.y *= RandomPick[Puff](-1,1); - } - override void Tick() - { - prev = pos; // for interpolation - if ( isFrozen() ) return; - vel *= .96; - vel.z += .01; - // linetrace-based movement (hopefully more reliable than traditional methods) - Vector3 dir = vel; - double spd = vel.length(); - dir /= spd; - double dist = spd; - FLineTraceData d; - Vector3 newpos = pos; - newpos.z = clamp(newpos.z,floorz,ceilingz); - int nstep = 0; - while ( dist > 0 ) - { - // safeguard, too many bounces - if ( nstep > MAXBOUNCEPERTIC ) - { - Destroy(); - return; - } - double ang = atan2(dir.y,dir.x); - double pt = asin(-dir.z); - LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); - 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; - } - if ( d.HitType != TRACE_HitNone ) - { - dist -= d.Distance; - // should only happen if we bounced - dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal)); - vel = dir*spd; - newpos = d.HitLocation+dir; - } - else - { - dist = 0.; - newpos = level.Vec3Offset(newpos,dir*spd); - } - nstep++; - } - newpos.z = clamp(newpos.z,floorz,ceilingz); - SetOrigin(newpos,true); - UpdateWaterLevel(); - if ( (waterlevel > 0) && !bAMBUSH ) - { - let b = Spawn("SWWMBubble",pos); - b.scale *= abs(scale.x); - b.vel = vel; - Destroy(); - return; - } - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - - States - { - Spawn: - XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1); - Stop; - } -} - -// strictly non-interacting smoke, much lighter tick, used for heavier effects -Class SWWMHalfSmoke : Actor -{ - Default - { - RenderStyle "Shaded"; - StencilColor "FFFFFF"; - Radius 0.1; - Height 0; - Speed 1; - +NOBLOCKMAP; - +NOGRAVITY; - +DONTSPLASH; - +FORCEXYBILLBOARD; - +ROLLSPRITE; - +ROLLCENTER; - +NOTELEPORT; - +NOINTERACTION; - Scale 0.3; - FloatBobPhase 0; - } - override void PostBeginPlay() - { - double ang, pt; - scale *= FRandom[Puff](0.5,1.5); - alpha *= FRandom[Puff](0.5,1.5); - ang = FRandom[Puff](0,360); - pt = FRandom[Puff](-90,90); - vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8)*speed; - roll = Frandom[Puff](0,360); - scale.x *= RandomPick[Puff](-1,1); - scale.y *= RandomPick[Puff](-1,1); - } - override void Tick() - { - prev = pos; // for interpolation - if ( isFrozen() ) return; - vel *= 0.96; - vel.z += 0.01; - SetOrigin(level.Vec3Offset(pos,vel),true); - UpdateWaterLevel(); - if ( (waterlevel > 0) && !bAMBUSH ) - { - let b = Spawn("SWWMBubble",pos); - b.scale *= abs(scale.x); - b.vel = vel; - Destroy(); - return; - } - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - - States - { - Spawn: - XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1); - Stop; - } -} - -Class SWWMSmallSmoke : SWWMHalfSmoke -{ - override void PostBeginPlay() - { - double ang, pt; - scale *= FRandom[Puff](0.1,0.3); - alpha *= FRandom[Puff](0.5,1.5); - ang = FRandom[Puff](0,360); - pt = FRandom[Puff](-90,90); - vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16); - roll = Frandom[Puff](0,360); - scale.x *= RandomPick[Puff](-1,1); - scale.y *= RandomPick[Puff](-1,1); - } - - States - { - Spawn: - QSM6 ABCDEFGHIJKLMNOPQR 1 A_SetTics(1+special1); - Stop; - } -} - -Class SWWMBubble : Actor -{ - Default - { - RenderStyle "Add"; - Radius 2; - Height 2; - +NOBLOCKMAP; - +NOGRAVITY; - +DONTSPLASH; - +FORCEXYBILLBOARD; - +NOTELEPORT; - +THRUACTORS; - +NOINTERACTION; - Scale 0.5; - FloatBobPhase 0; - } - override void PostBeginPlay() - { - double ang, pt; - scale *= FRandom[Puff](0.5,1.5); - ang = FRandom[Puff](0,360); - pt = FRandom[Puff](-90,90); - vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8); - if ( waterlevel <= 0 ) Destroy(); - SetState(ResolveState("Spawn")+Random[Puff](0,19)); - } - override void Tick() - { - prev = pos; - if ( isFrozen() ) return; - vel *= 0.96; - vel.z += 0.05; - // linetrace-based movement (hopefully more reliable than traditional methods) - Vector3 dir = vel; - double spd = vel.length(); - dir /= spd; - double dist = spd; - FLineTraceData d; - Vector3 newpos = pos; - newpos.z = clamp(newpos.z,floorz,ceilingz); - int nstep = 0; - while ( dist > 0 ) - { - // safeguard, too many bounces - if ( nstep > MAXBOUNCEPERTIC ) - { - Destroy(); - return; - } - double ang = atan2(dir.y,dir.x); - double pt = asin(-dir.z); - LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); - 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; - } - if ( d.HitType != TRACE_HitNone ) - { - dist -= d.Distance; - // should only happen if we bounced - dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal)); - vel = dir*spd; - newpos = d.HitLocation+dir; - } - else - { - dist = 0.; - newpos = level.Vec3Offset(newpos,dir*spd); - } - nstep++; - } - newpos.z = clamp(newpos.z,floorz,ceilingz); - SetOrigin(newpos,true); - UpdateWaterLevel(); - if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy(); - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XBUB ABCDEFGHIJKLMNOPQRST 1; - Loop; - } -} - -Class SWWMSparkTrail : Actor -{ - Default - { - RenderStyle "Add"; - Radius .1; - Height 0.; - +FORCEXYBILLBOARD; - +NOGRAVITY; - +NOBLOCKMAP; - +NOINTERACTION; - +DONTSPLASH; - +NOTELEPORT; - } - override void Tick() - { - if ( isFrozen() ) return; - A_SetScale(scale.x*.9,scale.y); - A_FadeOut(.06); - } - States - { - Spawn: - XZW1 A -1 Bright; - Stop; - } -} - -Class SWWMSpark : Actor -{ - bool dead; - Sector tracksector; - int trackplane; - - Default - { - RenderStyle "Add"; - Radius 2; - Height 2; - +NOBLOCKMAP; - +FORCEXYBILLBOARD; - +THRUACTORS; - +NOTELEPORT; - +DONTSPLASH; - +NOINTERACTION; - Gravity 0.2; - Scale 0.05; - FloatBobPhase 0; - } - override void Tick() - { - prev = pos; - if ( isFrozen() ) return; - if ( dead ) - { - // do nothing but follow floor movement - if ( tracksector ) - { - double trackz; - if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); - else trackz = tracksector.floorplane.ZAtPoint(pos.xy); - if ( trackz != pos.z ) SetZ(trackz); - } - } - else - { - vel.z -= GetGravity(); - // linetrace-based movement (hopefully more reliable than traditional methods) - Vector3 dir = vel; - double spd = vel.length(); - dir /= spd; - double dist = spd; - FLineTraceData d; - Vector3 newpos = pos; - newpos.z = clamp(newpos.z,floorz,ceilingz); - int nstep = 0; - while ( dist > 0 ) - { - Vector3 oldpos = newpos; - // safeguard, too many bounces - if ( nstep > MAXBOUNCEPERTIC ) - { - Destroy(); - return; - } - double ang = atan2(dir.y,dir.x); - double pt = asin(-dir.z); - LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); - 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; - } - if ( d.HitType != TRACE_HitNone ) - { - dist -= d.Distance; - // should only happen if we bounced - dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal)); - spd *= .4; - dist *= .4; - vel = dir*spd; - newpos = d.HitLocation+dir; - } - else - { - dist = 0.; - newpos = level.Vec3Offset(newpos,dir*spd); - } - if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) ) - { - // lose speed and die - if ( d.Hit3DFloor ) - { - newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy); - tracksector = d.Hit3DFloor.model; - trackplane = 1; - } - else - { - // hacky workaround - if ( !d.HitSector ) d.HitSector = floorsector; - newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy); - tracksector = d.HitSector; - trackplane = 0; - } - vel = (0,0,0); - pitch = 0; - roll = 0; - dead = true; - SetStateLabel("Death"); - break; - } - nstep++; - } - newpos.z = clamp(newpos.z,floorz,ceilingz); - Vector3 taildir = level.Vec3Diff(newpos,pos); - double taillen = taildir.length(); - if ( (taillen > 0.) && (alpha > .3) && (waterlevel <= 0) ) - { - taildir /= taillen; - let t = Spawn("SWWMSparkTrail",newpos); - t.alpha = alpha*.3; - t.scale.y = taillen; - t.angle = atan2(taildir.y,taildir.x); - t.pitch = asin(-taildir.z)+90; - } - SetOrigin(newpos,true); - if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid ) - { - Destroy(); - return; - } - } - UpdateWaterLevel(); - if ( waterlevel > 0 ) - { - let b = Spawn("SWWMBubble",pos); - b.vel = vel; - b.scale *= 0.3; - Destroy(); - return; - } - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - BLPF A 1 Bright A_FadeOut(0.01); - Wait; - Death: - BLPF A 1 Bright A_FadeOut(0.05); - Wait; - } -} - -Class SWWMChip : Actor -{ - SWWMChip prevchip, nextchip; - bool killme; - double anglevel, pitchvel, rollvel; - bool dead; - Sector tracksector; - int trackplane; - - Default - { - Radius 1; - Height 1; - +NOBLOCKMAP; - +THRUACTORS; - +NOTELEPORT; - +DONTSPLASH; - +INTERPOLATEANGLES; - +ROLLSPRITE; - +ROLLCENTER; - +FORCEXYBILLBOARD; - +NOINTERACTION; - Gravity 0.35; - Scale 0.2; - FloatBobPhase 0; - } - override void PostBeginPlay() - { - anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - frame = Random[Junk](0,7); - scale *= Frandom[Junk](0.8,1.2); - SWWMHandler.QueueChip(self); - } - override void OnDestroy() - { - SWWMHandler.DeQueueChip(self); - Super.OnDestroy(); - } - override void Tick() - { - prev = pos; // for interpolation - if ( isFrozen() ) return; - if ( dead ) - { - // do nothing but follow floor movement - if ( tracksector ) - { - double trackz; - if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); - else trackz = tracksector.floorplane.ZAtPoint(pos.xy); - if ( trackz != pos.z ) - { - SetZ(trackz); - UpdateWaterLevel(false); - } - } - } - else - { - if ( waterlevel <= 0 ) vel.z -= GetGravity(); - // linetrace-based movement (hopefully more reliable than traditional methods) - Vector3 dir = vel; - double spd = vel.length(); - dir /= spd; - double dist = spd; - FLineTraceData d; - Vector3 newpos = pos; - newpos.z = clamp(newpos.z,floorz,ceilingz); - int nstep = 0; - while ( dist > 0 ) - { - // safeguard, too many bounces - if ( nstep > MAXBOUNCEPERTIC ) - { - Destroy(); - return; - } - double ang = atan2(dir.y,dir.x); - double pt = asin(-dir.z); - LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); - 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; - } - if ( d.HitType != TRACE_HitNone ) - { - dist -= d.Distance; - // should only happen if we bounced - dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal)); - spd *= .3; - dist *= .3; - vel = dir*spd; - newpos = d.HitLocation+dir; - SetStateLabel("Bounce"); - } - else - { - dist = 0.; - newpos = level.Vec3Offset(newpos,dir*spd); - } - newpos.z = clamp(newpos.z,floorz,ceilingz); - if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) ) - { - // lose speed and die - if ( d.Hit3DFloor ) - { - newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy); - tracksector = d.Hit3DFloor.model; - trackplane = 1; - } - else - { - // hacky workaround - if ( !d.HitSector ) d.HitSector = floorsector; - newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy); - tracksector = d.HitSector; - trackplane = 0; - } - vel = (0,0,0); - pitch = 0; - roll = 0; - dead = true; - SetStateLabel("Death"); - break; - } - nstep++; - } - SetOrigin(newpos,true); - UpdateWaterLevel(); - if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid ) - { - Destroy(); - return; - } - } - if ( killme ) A_FadeOut(.01); - if ( waterlevel > 0 ) - { - vel *= .98; - anglevel *= .98; - pitchvel *= .98; - rollvel *= .98; - } - if ( !CheckNoDelay() || (tics == -1) ) return; - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XZW1 # 1 - { - angle += anglevel; - pitch += pitchvel; - roll += rollvel; - } - Loop; - Bounce: - XZW1 # 0 - { - anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - } - Goto Spawn; - Death: - XZW2 # -1; - Stop; - } -} - Class SWWMNothing : Actor { States @@ -1142,815 +390,3 @@ Class SWWMNothing : Actor Stop; } } - -Class PoofLight : PaletteLight -{ - Default - { - Tag "Yellow"; - ReactionTime 5; - Args 0,0,0,60; - } -} -Class PoofLight2 : PaletteLight -{ - Default - { - Tag "Yellow"; - ReactionTime 20; - Args 0,0,0,90; - } -} - -Class SWWMItemFog : Actor -{ - Default - { - RenderStyle "Add"; - +NOGRAVITY; - +NOBLOCKMAP; - +DONTSPLASH; - +ROLLSPRITE; - +ROLLCENTER; - +NOINTERACTION; - +FORCEXYBILLBOARD; - FloatBobPhase 0; - } - States - { - Spawn: - BLPF A 2 Bright NoDelay - { - // offset up - SetOrigin(Vec3Offset(0,0,16),false); - roll = FRandom[ExploS](0,360); - scale *= FRandom[ExploS](0.9,1.1); - scale.x *= RandomPick[ExploS](-1,1); - scale.y *= RandomPick[ExploS](-1,1); - int numpt = Random[ExploS](8,12); - if ( bAMBUSH ) numpt *= 2; - for ( int i=0; i WaterHitList; - Array ShootThroughList; - Actor ignoreme; - - static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, double dist, int bubblechance, bool smoky = false ) - { - let t = new("SWWMBulletTrail"); - t.ignoreme = target; - t.WaterHitList.Clear(); - t.ShootThroughList.Clear(); - t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0); - for ( int i=0; i 0 ) - { - if ( lite ) lite.Destroy(); - amount -= int(victim.waterlevel**2); - } - if ( victim.Health <= 0 ) amount = min(amount,100); - if ( !(level.maptime%3) ) - amount--; - if ( victim.player ) amount -= int(abs(actor.deltaangle(victim.angle,oangle))/30); - oangle = victim.angle; - if ( amount < -30 ) - { - A_StopSound(CHAN_5); - Destroy(); - return; - } - if ( cnt > 0 ) cnt--; - else - { - cnt = min(10,30-int(29*(min(1.,amount/500.)**3.))); - if ( victim.bSHOOTABLE && (victim.Health > 0) && (amount > 0) ) - { - int flg = DMG_THRUSTLESS; - if ( victim is 'Centaur' ) flg |= DMG_FOILINVUL; // you're on fire, that shield is worthless - victim.DamageMobj(self,instigator,clamp(int(amount*.06),1,20),'Fire',flg); // need to use this actor as inflictor to have a proper obituary - if ( victim.bISMONSTER && !Random[FlameT](0,3) ) - victim.Howl(); - } - if ( !victim ) - { - A_StopSound(CHAN_5); - Destroy(); - return; - } - else SWWMUtility.DoExplosion(self,clamp(int(amount*.06),1,20),0,victim.radius+40,victim.radius,DE_NOBLEED|DE_NOSPLASH|DE_HOWL,'Fire',victim); // radius fire damage - } - double mult = max(victim.radius,victim.height)/30.; - if ( delay > 0 ) delay--; - if ( (level.maptime+special1)%6 ) return; - A_SoundVolume(CHAN_5,min(1.,mult*amount/80.)); - int numpt = clamp(int(Random[FlameT](2,4)*amount*.01),1,4); - numpt = int(clamp(numpt*mult**.5,1,3)); - for ( int i=0; i 0 ) - { - let c = victim.Spawn("OnFireTrail",pos); - c.special1 = Random[FlameT](-2,2); - c.scale *= max(.3,mult*0.5); - c.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.5,2.)*c.scale.x; - } - if ( !(i%2) ) - { - let s = victim.Spawn("SWWMHalfSmoke",pos); - s.scale *= max(1.,1.6*mult); - s.alpha *= min(amount+30,100)*.01; - s.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.2,.6)*s.scale.x; - } - } - } - - static OnFire Apply( Actor victim, Actor instigator, int amount, int delay = 0 ) - { - if ( amount <= 0 ) return null; - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return null; - OnFire t; - for ( t=hnd.fires; t; t=t.nextfire ) - { - if ( t.victim != victim ) continue; - if ( instigator ) t.instigator = instigator; - t.amount = min(500,t.amount+amount); - t.cnt = min(t.cnt,5); - return t; - } - t = OnFire(Spawn("OnFire",victim.pos)); - t.victim = victim; - t.instigator = instigator; - t.amount = min(500,amount); - t.cnt = 1; - t.special1 = Random[FlameT](0,10); - t.A_StartSound("misc/flame",CHAN_5,CHANF_LOOP); - double mult = max(victim.radius,victim.height)/30.; - t.A_SoundVolume(CHAN_5,min(1.,mult*amount/80.)); - // for chunks - t.delay = delay; - t.lite = Actor.Spawn("OnFireLight",victim.pos); - OnFireLight(t.lite).of = t; - t.oangle = victim.angle; - // append - t.nextfire = hnd.fires; - if ( hnd.fires ) hnd.fires.prevfire = t; - hnd.fires = t; - hnd.fires_cnt++; - return t; - } - - static OnFire IsOnFire( Actor victim ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return null; - OnFire t; - for ( t=hnd.fires; t; t=t.nextfire ) - { - if ( t.victim != victim ) continue; - if ( t.amount <= 0 ) return null; - return t; - } - return null; - } - - Default - { - +NOGRAVITY; - +NOBLOCKMAP; - +DONTSPLASH; - +NOEXTREMEDEATH; - +NOINTERACTION; - Obituary "$O_ONFIRE"; - } -} - -Class OnFireTrailLight : PaletteLight -{ - Default - { - Tag "HellExpl"; - Args 0,0,0,40; - ReactionTime 40; - } - override void Tick() - { - Super.Tick(); - Args[0] /= 10; - Args[1] /= 10; - Args[2] /= 10; - Args[3] += 3; - if ( !target || (target.waterlevel > 0) ) - { - Destroy(); - return; - } - SetOrigin(target.pos,true); - } -} - -Class OnFireTrail : Actor -{ - override void PostBeginPlay() - { - Super.PostBeginPlay(); - Scale.x *= RandomPick[ExploS](-1,1); - Scale.y *= RandomPick[ExploS](-1,1); - roll = FRandom[ExploS](0,360); - } - action void A_Flame() - { - special1++; - if ( waterlevel > 0 ) - vel *= .9; - else - { - vel *= .98; - vel.z += .1+.2*abs(scale.x); - } - if ( waterlevel > 0 ) - { - let s = Spawn("SWWMSmoke",pos); - s.vel = (FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2)); - s.vel += vel*.3; - s.alpha *= alpha*2; - s.scale *= .5+abs(scale.x)*(.5+special1/6.); - Destroy(); - return; - } - if ( !Random[FlameT](0,int(40*(default.alpha-alpha))) ) - { - let s = Spawn("SWWMHalfSmoke",pos); - s.vel = (FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2)); - s.vel += vel*.3; - s.alpha *= alpha*1.5; - s.scale *= .5+abs(scale.x)*(.5+special1/6.); - } - } - override void Tick() - { - if ( isFrozen() ) return; - SetOrigin(level.Vec3Offset(pos,vel),true); - UpdateWaterLevel(); - if ( !CheckNoDelay() || (tics == -1) ) return; - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - Default - { - RenderStyle "Add"; - Speed 2; - Radius 4; - Height 4; - Alpha .6; - Scale .8; - +NOBLOCKMAP; - +NOGRAVITY; - +NOFRICTION; - +SLIDESONWALLS; - +NOTELEPORT; - +FORCEXYBILLBOARD; - +ROLLSPRITE; - +ROLLCENTER; - +DROPOFF; - +NOBLOCKMONST; - +DONTSPLASH; - +NOINTERACTION; - } - States - { - Spawn: - XFLM ABCDEFGHIJKLMNOPQRST 1 Bright - { - A_Flame(); - A_SetScale(scale.x*0.98); - A_FadeOut(0.02); - } - Wait; - } -} - diff --git a/zscript/swwm_common_fx.zsc b/zscript/swwm_common_fx.zsc new file mode 100644 index 000000000..5bca6d5eb --- /dev/null +++ b/zscript/swwm_common_fx.zsc @@ -0,0 +1,1277 @@ +// basic effects + +// imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop +Class PaletteLight : PointLight +{ + Color pal[256]; + bool IsLooping; + int InitialReactionTime; + + Default + { + Tag "Explosion"; + Args 0,0,0,80; + ReactionTime 15; + } + private void UpdateLight() + { + int index = clamp(255-((255*ReactionTime)/abs(InitialReactionTime)),0,255); + args[LIGHT_RED] = pal[index].r; + args[LIGHT_GREEN] = pal[index].g; + args[LIGHT_BLUE] = pal[index].b; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + InitialReactionTime = ReactionTime; + String palname = GetTag(); + int sep = palname.IndexOf(","); + int palnum = 0; + if ( sep != -1 ) + { + String palnumstr = palname.Mid(sep+1); + palnum = palnumstr.ToInt()*768; + palname.Truncate(sep); + } + int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",palname)); + String paldat = Wads.ReadLump(lump); + for ( int i=0; i<256; i++ ) + { + pal[i].r = paldat.ByteAt(palnum++); + pal[i].g = paldat.ByteAt(palnum++); + pal[i].b = paldat.ByteAt(palnum++); + } + if ( ReactionTime < 0 ) + { + ReactionTime = -ReactionTime; + IsLooping = true; + } + UpdateLight(); + } + override void Tick() + { + Super.Tick(); + if ( isFrozen() ) return; + ReactionTime--; + if ( ReactionTime < 0 ) + { + if ( !IsLooping ) + { + Destroy(); + return; + } + else ReactionTime = abs(InitialReactionTime); + } + if ( target ) SetOrigin(target.pos,true); + UpdateLight(); + } +} + +// Generic smoke, lightweight tick +Class SWWMSmoke : Actor +{ + Default + { + RenderStyle "Shaded"; + StencilColor "FFFFFF"; + Radius 2; + Height 2; + Speed 1; + +NOBLOCKMAP; + +NOGRAVITY; + +DONTSPLASH; + +FORCEXYBILLBOARD; + +ROLLSPRITE; + +ROLLCENTER; + +THRUACTORS; + +NOTELEPORT; + +NOINTERACTION; + Scale .3; + FloatBobPhase 0; + } + + override void PostBeginPlay() + { + double ang, pt; + scale *= FRandom[Puff](.5,1.5); + alpha = min(1.,alpha*FRandom[Puff](.5,1.5)); + ang = FRandom[Puff](0,360); + pt = FRandom[Puff](-90,90); + vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](.2,.8)*speed; + roll = Frandom[Puff](0,360); + scale.x *= RandomPick[Puff](-1,1); + scale.y *= RandomPick[Puff](-1,1); + } + override void Tick() + { + prev = pos; // for interpolation + if ( isFrozen() ) return; + vel *= .96; + vel.z += .01; + // linetrace-based movement (hopefully more reliable than traditional methods) + Vector3 dir = vel; + double spd = vel.length(); + dir /= spd; + double dist = spd; + FLineTraceData d; + Vector3 newpos = pos; + newpos.z = clamp(newpos.z,floorz,ceilingz); + int nstep = 0; + while ( dist > 0 ) + { + // safeguard, too many bounces + if ( nstep > MAXBOUNCEPERTIC ) + { + Destroy(); + return; + } + double ang = atan2(dir.y,dir.x); + double pt = asin(-dir.z); + LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); + 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; + } + if ( d.HitType != TRACE_HitNone ) + { + dist -= d.Distance; + // should only happen if we bounced + dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal)); + vel = dir*spd; + newpos = d.HitLocation+dir; + } + else + { + dist = 0.; + newpos = level.Vec3Offset(newpos,dir*spd); + } + nstep++; + } + newpos.z = clamp(newpos.z,floorz,ceilingz); + SetOrigin(newpos,true); + UpdateWaterLevel(); + if ( (waterlevel > 0) && !bAMBUSH ) + { + let b = Spawn("SWWMBubble",pos); + b.scale *= abs(scale.x); + b.vel = vel; + Destroy(); + return; + } + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + + States + { + Spawn: + XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1); + Stop; + } +} + +// strictly non-interacting smoke, much lighter tick, used for heavier effects +Class SWWMHalfSmoke : Actor +{ + Default + { + RenderStyle "Shaded"; + StencilColor "FFFFFF"; + Radius 0.1; + Height 0; + Speed 1; + +NOBLOCKMAP; + +NOGRAVITY; + +DONTSPLASH; + +FORCEXYBILLBOARD; + +ROLLSPRITE; + +ROLLCENTER; + +NOTELEPORT; + +NOINTERACTION; + Scale 0.3; + FloatBobPhase 0; + } + override void PostBeginPlay() + { + double ang, pt; + scale *= FRandom[Puff](0.5,1.5); + alpha *= FRandom[Puff](0.5,1.5); + ang = FRandom[Puff](0,360); + pt = FRandom[Puff](-90,90); + vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8)*speed; + roll = Frandom[Puff](0,360); + scale.x *= RandomPick[Puff](-1,1); + scale.y *= RandomPick[Puff](-1,1); + } + override void Tick() + { + prev = pos; // for interpolation + if ( isFrozen() ) return; + vel *= 0.96; + vel.z += 0.01; + SetOrigin(level.Vec3Offset(pos,vel),true); + UpdateWaterLevel(); + if ( (waterlevel > 0) && !bAMBUSH ) + { + let b = Spawn("SWWMBubble",pos); + b.scale *= abs(scale.x); + b.vel = vel; + Destroy(); + return; + } + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + + States + { + Spawn: + XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1); + Stop; + } +} + +Class SWWMSmallSmoke : SWWMHalfSmoke +{ + override void PostBeginPlay() + { + double ang, pt; + scale *= FRandom[Puff](0.1,0.3); + alpha *= FRandom[Puff](0.5,1.5); + ang = FRandom[Puff](0,360); + pt = FRandom[Puff](-90,90); + vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16); + roll = Frandom[Puff](0,360); + scale.x *= RandomPick[Puff](-1,1); + scale.y *= RandomPick[Puff](-1,1); + } + + States + { + Spawn: + QSM6 ABCDEFGHIJKLMNOPQR 1 A_SetTics(1+special1); + Stop; + } +} + +Class SWWMBubble : Actor +{ + Default + { + RenderStyle "Add"; + Radius 2; + Height 2; + +NOBLOCKMAP; + +NOGRAVITY; + +DONTSPLASH; + +FORCEXYBILLBOARD; + +NOTELEPORT; + +THRUACTORS; + +NOINTERACTION; + Scale 0.5; + FloatBobPhase 0; + } + override void PostBeginPlay() + { + double ang, pt; + scale *= FRandom[Puff](0.5,1.5); + ang = FRandom[Puff](0,360); + pt = FRandom[Puff](-90,90); + vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8); + if ( waterlevel <= 0 ) Destroy(); + SetState(ResolveState("Spawn")+Random[Puff](0,19)); + } + override void Tick() + { + prev = pos; + if ( isFrozen() ) return; + vel *= 0.96; + vel.z += 0.05; + // linetrace-based movement (hopefully more reliable than traditional methods) + Vector3 dir = vel; + double spd = vel.length(); + dir /= spd; + double dist = spd; + FLineTraceData d; + Vector3 newpos = pos; + newpos.z = clamp(newpos.z,floorz,ceilingz); + int nstep = 0; + while ( dist > 0 ) + { + // safeguard, too many bounces + if ( nstep > MAXBOUNCEPERTIC ) + { + Destroy(); + return; + } + double ang = atan2(dir.y,dir.x); + double pt = asin(-dir.z); + LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); + 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; + } + if ( d.HitType != TRACE_HitNone ) + { + dist -= d.Distance; + // should only happen if we bounced + dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal)); + vel = dir*spd; + newpos = d.HitLocation+dir; + } + else + { + dist = 0.; + newpos = level.Vec3Offset(newpos,dir*spd); + } + nstep++; + } + newpos.z = clamp(newpos.z,floorz,ceilingz); + SetOrigin(newpos,true); + UpdateWaterLevel(); + if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy(); + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XBUB ABCDEFGHIJKLMNOPQRST 1; + Loop; + } +} + +Class SWWMSparkTrail : Actor +{ + Default + { + RenderStyle "Add"; + Radius .1; + Height 0.; + +FORCEXYBILLBOARD; + +NOGRAVITY; + +NOBLOCKMAP; + +NOINTERACTION; + +DONTSPLASH; + +NOTELEPORT; + } + override void Tick() + { + if ( isFrozen() ) return; + A_SetScale(scale.x*.9,scale.y); + A_FadeOut(.06); + } + States + { + Spawn: + XZW1 A -1 Bright; + Stop; + } +} + +Class SWWMSpark : Actor +{ + bool dead; + Sector tracksector; + int trackplane; + + Default + { + RenderStyle "Add"; + Radius 2; + Height 2; + +NOBLOCKMAP; + +FORCEXYBILLBOARD; + +THRUACTORS; + +NOTELEPORT; + +DONTSPLASH; + +NOINTERACTION; + Gravity 0.2; + Scale 0.05; + FloatBobPhase 0; + } + override void Tick() + { + prev = pos; + if ( isFrozen() ) return; + if ( dead ) + { + // do nothing but follow floor movement + if ( tracksector ) + { + double trackz; + if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); + else trackz = tracksector.floorplane.ZAtPoint(pos.xy); + if ( trackz != pos.z ) SetZ(trackz); + } + } + else + { + vel.z -= GetGravity(); + // linetrace-based movement (hopefully more reliable than traditional methods) + Vector3 dir = vel; + double spd = vel.length(); + dir /= spd; + double dist = spd; + FLineTraceData d; + Vector3 newpos = pos; + newpos.z = clamp(newpos.z,floorz,ceilingz); + int nstep = 0; + while ( dist > 0 ) + { + Vector3 oldpos = newpos; + // safeguard, too many bounces + if ( nstep > MAXBOUNCEPERTIC ) + { + Destroy(); + return; + } + double ang = atan2(dir.y,dir.x); + double pt = asin(-dir.z); + LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); + 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; + } + if ( d.HitType != TRACE_HitNone ) + { + dist -= d.Distance; + // should only happen if we bounced + dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal)); + spd *= .4; + dist *= .4; + vel = dir*spd; + newpos = d.HitLocation+dir; + } + else + { + dist = 0.; + newpos = level.Vec3Offset(newpos,dir*spd); + } + if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) ) + { + // lose speed and die + if ( d.Hit3DFloor ) + { + newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy); + tracksector = d.Hit3DFloor.model; + trackplane = 1; + } + else + { + // hacky workaround + if ( !d.HitSector ) d.HitSector = floorsector; + newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy); + tracksector = d.HitSector; + trackplane = 0; + } + vel = (0,0,0); + pitch = 0; + roll = 0; + dead = true; + SetStateLabel("Death"); + break; + } + nstep++; + } + newpos.z = clamp(newpos.z,floorz,ceilingz); + Vector3 taildir = level.Vec3Diff(newpos,pos); + double taillen = taildir.length(); + if ( (taillen > 0.) && (alpha > .3) && (waterlevel <= 0) ) + { + taildir /= taillen; + let t = Spawn("SWWMSparkTrail",newpos); + t.alpha = alpha*.3; + t.scale.y = taillen; + t.angle = atan2(taildir.y,taildir.x); + t.pitch = asin(-taildir.z)+90; + } + SetOrigin(newpos,true); + if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid ) + { + Destroy(); + return; + } + } + UpdateWaterLevel(); + if ( waterlevel > 0 ) + { + let b = Spawn("SWWMBubble",pos); + b.vel = vel; + b.scale *= 0.3; + Destroy(); + return; + } + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + BLPF A 1 Bright A_FadeOut(0.01); + Wait; + Death: + BLPF A 1 Bright A_FadeOut(0.05); + Wait; + } +} + +Class SWWMChip : Actor +{ + SWWMChip prevchip, nextchip; + bool killme; + double anglevel, pitchvel, rollvel; + bool dead; + Sector tracksector; + int trackplane; + + Default + { + Radius 1; + Height 1; + +NOBLOCKMAP; + +THRUACTORS; + +NOTELEPORT; + +DONTSPLASH; + +INTERPOLATEANGLES; + +ROLLSPRITE; + +ROLLCENTER; + +FORCEXYBILLBOARD; + +NOINTERACTION; + Gravity 0.35; + Scale 0.2; + FloatBobPhase 0; + } + override void PostBeginPlay() + { + anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + frame = Random[Junk](0,7); + scale *= Frandom[Junk](0.8,1.2); + SWWMHandler.QueueChip(self); + } + override void OnDestroy() + { + SWWMHandler.DeQueueChip(self); + Super.OnDestroy(); + } + override void Tick() + { + prev = pos; // for interpolation + if ( isFrozen() ) return; + if ( dead ) + { + // do nothing but follow floor movement + if ( tracksector ) + { + double trackz; + if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy); + else trackz = tracksector.floorplane.ZAtPoint(pos.xy); + if ( trackz != pos.z ) + { + SetZ(trackz); + UpdateWaterLevel(false); + } + } + } + else + { + if ( waterlevel <= 0 ) vel.z -= GetGravity(); + // linetrace-based movement (hopefully more reliable than traditional methods) + Vector3 dir = vel; + double spd = vel.length(); + dir /= spd; + double dist = spd; + FLineTraceData d; + Vector3 newpos = pos; + newpos.z = clamp(newpos.z,floorz,ceilingz); + int nstep = 0; + while ( dist > 0 ) + { + // safeguard, too many bounces + if ( nstep > MAXBOUNCEPERTIC ) + { + Destroy(); + return; + } + double ang = atan2(dir.y,dir.x); + double pt = asin(-dir.z); + LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d); + 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; + } + if ( d.HitType != TRACE_HitNone ) + { + dist -= d.Distance; + // should only happen if we bounced + dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal)); + spd *= .3; + dist *= .3; + vel = dir*spd; + newpos = d.HitLocation+dir; + SetStateLabel("Bounce"); + } + else + { + dist = 0.; + newpos = level.Vec3Offset(newpos,dir*spd); + } + newpos.z = clamp(newpos.z,floorz,ceilingz); + if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) ) + { + // lose speed and die + if ( d.Hit3DFloor ) + { + newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy); + tracksector = d.Hit3DFloor.model; + trackplane = 1; + } + else + { + // hacky workaround + if ( !d.HitSector ) d.HitSector = floorsector; + newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy); + tracksector = d.HitSector; + trackplane = 0; + } + vel = (0,0,0); + pitch = 0; + roll = 0; + dead = true; + SetStateLabel("Death"); + break; + } + nstep++; + } + SetOrigin(newpos,true); + UpdateWaterLevel(); + if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid ) + { + Destroy(); + return; + } + } + if ( killme ) A_FadeOut(.01); + if ( waterlevel > 0 ) + { + vel *= .98; + anglevel *= .98; + pitchvel *= .98; + rollvel *= .98; + } + if ( !CheckNoDelay() || (tics == -1) ) return; + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XZW1 # 1 + { + angle += anglevel; + pitch += pitchvel; + roll += rollvel; + } + Loop; + Bounce: + XZW1 # 0 + { + anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + } + Goto Spawn; + Death: + XZW2 # -1; + Stop; + } +} + +Class PoofLight : PaletteLight +{ + Default + { + Tag "Yellow"; + ReactionTime 5; + Args 0,0,0,60; + } +} +Class PoofLight2 : PaletteLight +{ + Default + { + Tag "Yellow"; + ReactionTime 20; + Args 0,0,0,90; + } +} + +Class SWWMItemFog : Actor +{ + Default + { + RenderStyle "Add"; + +NOGRAVITY; + +NOBLOCKMAP; + +DONTSPLASH; + +ROLLSPRITE; + +ROLLCENTER; + +NOINTERACTION; + +FORCEXYBILLBOARD; + FloatBobPhase 0; + } + States + { + Spawn: + BLPF A 2 Bright NoDelay + { + // offset up + SetOrigin(Vec3Offset(0,0,16),false); + roll = FRandom[ExploS](0,360); + scale *= FRandom[ExploS](0.9,1.1); + scale.x *= RandomPick[ExploS](-1,1); + scale.y *= RandomPick[ExploS](-1,1); + int numpt = Random[ExploS](8,12); + if ( bAMBUSH ) numpt *= 2; + for ( int i=0; i WaterHitList; + Array ShootThroughList; + Actor ignoreme; + + static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, double dist, int bubblechance, bool smoky = false ) + { + let t = new("SWWMBulletTrail"); + t.ignoreme = target; + t.WaterHitList.Clear(); + t.ShootThroughList.Clear(); + t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0); + for ( int i=0; i whichweapon; + Array suse; + Array > sweapon; + int gonect; + HeadpatTracker pats; // for headpat gesture, our current tracker + + // these should prevent autoswitch when out of ammo + override bool ReportHUDAmmo() + { + return false; + } + override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) + { + return false; + } + override bool Use( bool pickup ) + { + return false; + } + override void DoEffect() + { + Super.DoEffect(); + if ( !Owner || !Owner.player || (Owner.player.ReadyWeapon != self) ) + return; + let psp = Owner.player.FindPSprite(PSP_WEAPON); + if ( !psp ) return; + if ( (Owner.Health <= 0) && (psp.CurState != ResolveState("Deselect")) ) + Owner.player.SetPSprite(PSP_WEAPON,ResolveState("Deselect")); + } + + static SWWMGesture SetGesture( PlayerPawn mo, int which ) + { + if ( !mo || !(mo is 'Demolitionist') ) return null; // only Demo + if ( mo.Health <= 0 ) return null; // dead + if ( mo.player.cheats&CF_TOTALLYFROZEN ) return null; // frozen today + SWWMGesture w = SWWMGesture(mo.FindInventory("SWWMGesture")); + if ( w && ((mo.player.PendingWeapon is 'SWWMGesture') || (mo.player.ReadyWeapon is 'SWWMGesture') + || (mo.player.PendingWeapon is 'SWWMItemGesture') || (mo.player.ReadyWeapon is 'SWWMItemGesture')) ) + { + // already gesturing + // just queue another one + if ( which <= 0 ) return null; // these gestures can't be queued + else + { + w.nextgesture = which; + w.queued = true; + } + return null; + } + if ( !w ) + { + w = SWWMGesture(Spawn("SWWMGesture")); + mo.AddInventory(w); + } + if ( mo.player.PendingWeapon != WP_NOCHANGE ) w.formerweapon = mo.player.PendingWeapon; + else w.formerweapon = mo.player.ReadyWeapon; + w.whichweapon = null; + w.whichgesture = which; + mo.player.PendingWeapon = w; + return w; + } + + // "special" gestures are run by switching to another "weapon" + static SWWMGesture SetSpecialGesture( PlayerPawn mo, Class a, bool used = false ) + { + if ( !mo || !(mo is 'Demolitionist') ) return null; // only Demo + if ( mo.Health <= 0 ) return null; // dead + if ( mo.player.cheats&CF_TOTALLYFROZEN ) return null; // frozen today + if ( !a ) return null; + SWWMGesture w = SWWMGesture(mo.FindInventory("SWWMGesture")); + if ( w && ((mo.player.PendingWeapon is 'SWWMGesture') || (mo.player.ReadyWeapon is 'SWWMGesture') + || (mo.player.PendingWeapon is 'SWWMItemGesture') || (mo.player.ReadyWeapon is 'SWWMItemGesture')) ) + { + // already gesturing + // queue if unique + for ( int i=0; i 0 ) + { + invoker.whichgesture = GS_Null; + invoker.whichweapon = invoker.sweapon[0]; + invoker.whichuse = invoker.suse[0]; + // push back + invoker.sweapon.Delete(0); + invoker.suse.Delete(0); + player.SetPSprite(PSP_WEAPON,ResolveState("Ready")); + return; + } + if ( invoker.queued ) + { + invoker.whichweapon = null; + invoker.whichgesture = invoker.nextgesture; + invoker.queued = false; + player.SetPSprite(PSP_WEAPON,ResolveState("Ready")); + return; + } + player.PendingWeapon = invoker.formerweapon; + player.SetPSprite(PSP_WEAPON,ResolveState("Deselect")); + } + + action void A_Headpat() + { + A_StartSound("demolitionist/petting",CHAN_WEAPON,CHANF_OVERLAP,.4); + let pt = invoker.pats; + if ( !pt ) return; + int numpt = Random[ExploS](6,9); + Vector3 dir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + Vector3 patpos = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz-4),dir*30.); + for ( int i=0; i 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 ) + { + gest.whichgesture = GS_Null; + gest.whichweapon = gest.sweapon[0]; + gest.whichuse = gest.suse[0]; + // push back + gest.sweapon.Delete(0); + gest.suse.Delete(0); + // go back to the main gesture + player.ReadyWeapon = gest; + player.SetPSPrite(PSP_WEAPON,gest.ResolveState("Ready")); + invoker.gotused = true; + return; + } + if ( gest.queued ) + { + gest.whichweapon = null; + gest.whichgesture = gest.nextgesture; + gest.queued = false; + // go back to the main gesture + player.ReadyWeapon = gest; + player.SetPSPrite(PSP_WEAPON,gest.ResolveState("Ready")); + invoker.gotused = true; + return; + } + // switch to old weapon + player.ReadyWeapon = gest; + player.PendingWeapon = gest.formerweapon; + player.SetPSPrite(PSP_WEAPON,gest.ResolveState("Deselect")); + invoker.gotused = true; + } + + Default + { + +WEAPON.CHEATNOTWEAPON; + +WEAPON.NO_AUTO_SWITCH; + +WEAPON.WIMPY_WEAPON; + +SWWMWEAPON.HIDEINMENU; + +INVENTORY.UNDROPPABLE; + +INVENTORY.UNTOSSABLE; + +INVENTORY.UNCLEARABLE; + Weapon.SelectionOrder int.max; + } + States + { + Select: + XZW1 A 1 A_FullRaise(); + Goto Ready; + Ready: + Fire: + XZW1 A 1 A_Log("\cgUnimplemented pickup sequence for "..invoker.GetClassName().."\c-"); + XZW1 A -1 A_FinishGesture(); + Stop; + AltFire: + XZW1 A 1 A_Log("\cgUnimplemented use sequence for "..invoker.GetClassName().."\c-"); + XZW1 A -1 A_FinishGesture(); + Stop; + Deselect: + XZW1 A -1 A_FullLower(); + Stop; + } +} diff --git a/zscript/swwm_gesture_fx.zsc b/zscript/swwm_gesture_fx.zsc new file mode 100644 index 000000000..11eb670c4 --- /dev/null +++ b/zscript/swwm_gesture_fx.zsc @@ -0,0 +1,469 @@ +// gesture effects + +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 ) + { + SWWMStats s = null; + if ( Demolitionist(self.target) ) s = Demolitionist(self.target).mystats; + if ( s ) s.smooch++; + let raging = RagekitPower(self.target.FindInventory("RagekitPower")); + if ( raging ) + { + bEXTREMEDEATH = true; + bNOEXTREMEDEATH = false; + } + else + { + bEXTREMEDEATH = false; + bNOEXTREMEDEATH = true; + } + if ( (target is 'WolfensteinSS') || (target.Species == 'WolfensteinSS') ) target.bFRIENDLY = false; + if ( target.IsFriend(self.target) || SWWMUtility.IdentifyingDog(target) ) + { + 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),ST_Health); + SWWMHandler.DoFlash(target,Color(32,224,128,255),10); + } + if ( SWWMUtility.IdentifyingDog(target) ) + { + // befriend good doggo + if ( target.bCOUNTKILL ) + { + target.bCOUNTKILL = false; + level.total_monsters--; + } + if ( !target.bFRIENDLY && s ) + s.befriend++; + target.bFRIENDLY = true; + if ( deathmatch ) + target.SetFriendPlayer(self.target.player); + } + return 0; + } + Vector3 dirto = level.Vec3Diff(pos,target.Vec3Offset(0,0,target.Height/2)).unit(); + SWWMUtility.DoKnockback(target,dirto,1500.*damage); + let bread = target.FindState("Pain"); + if ( bread ) target.SetState(bread); + if ( raging ) + { + damage *= 8; + raging.DoHitFX(); + } + if ( (target is 'WolfensteinSS') || (target.Species == 'WolfensteinSS') ) + { + damage = int.max; + bEXTREMEDEATH = true; + bNOEXTREMEDEATH = false; + } + else if ( target is 'SWWMHangingKeen' ) + damage = max(target.Health,damage); // rescued by love :3 + else if ( SWWMHDoomHandler.IsCuteGirl(target) ) + { + // no cute demon girl can resist demo's charm + damage = max(target.Health,damage); + bEXTREMEDEATH = false; + bNOEXTREMEDEATH = true; + } + 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*GameTicRate)); + 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 cl ) LinePart = 2; + else LinePart = 1; + } + else LinePart = 1; // always middle + if ( Blocking3DFloor ) + { + for ( int i=0; i PlayerPawn(user.player.mo).UseRange ) return false; + if ( user is 'Demolitionist' ) + { + patter = user; + let g = SWWMGesture.SetGesture(Demolitionist(patter),GS_Headpat); + if ( !g ) return false; // can't headpat at the moment + patting = true; + g.pats = self; + oldtargettics = target.tics; + target.tics = -1; + patter.player.cheats |= CF_TOTALLYFROZEN; + Demolitionist(patter).scriptedinvul = true; + target.bDORMANT = true; + return true; + } + return false; + } +} + diff --git a/zscript/swwm_handler.zsc b/zscript/swwm_handler.zsc index 4dd0e67e6..d04798b11 100644 --- a/zscript/swwm_handler.zsc +++ b/zscript/swwm_handler.zsc @@ -1,252 +1,14 @@ -// event handlers and whatnot - -// save version holder -Class SWWMSaveVerData : Thinker -{ - String ver; -} - -// Static handler responsible for some special stuff -Class SWWMStaticHandler : StaticEventHandler -{ - // crash handler - ui bool wasinmap; - ui int timer; - // versioning - bool tainted; - String taintver; - bool mptaint[MAXPLAYERS]; - bool mprecv[MAXPLAYERS]; - String mpver[MAXPLAYERS]; - int checktic; - ui bool mpsent, checked; - - override void NewGame() - { - // set save version every new session - let svd = new("SWWMSaveVerData"); - svd.ChangeStatNum(Thinker.STAT_STATIC); - svd.ver = StringTable.Localize("$SWWM_SHORTVER"); - } - - override void WorldLoaded( WorldEvent e ) - { - // save version checker - if ( !e.IsSaveGame ) return; - checktic = gametic+5; - let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC); - let svd = SWWMSaveVerData(ti.Next()); - if ( !svd ) - { - tainted = true; - taintver = "\cg(no version info)\c-"; - return; - } - String cver = StringTable.Localize("$SWWM_SHORTVER"); - if ( svd.ver != cver ) - { - tainted = true; - taintver = svd.ver; - } - } - - override void OnRegister() - { - // preload various fonts - Font.GetFont('k6x8'); - Font.GetFont('k6x8Shaded'); - Font.GetFont('k6x8ShadedInverse'); - Font.GetFont('Miniwi'); - Font.GetFont('MiniwiShaded'); - Font.GetFont('MiniwiShadedInverse'); - Font.GetFont('MPlus'); - Font.GetFont('MPlusShaded'); - Font.GetFont('MPlusShadedInverse'); - Font.GetFont('Tewi'); - Font.GetFont('TewiShaded'); - Font.GetFont('TewiShadedInverse'); - Font.GetFont('SWWMBigFont'); - } - - override void ConsoleProcess( ConsoleEvent e ) - { - static const Name mmvars[] = - { - 'swwm_mm_backcolor', 'swwm_mm_cdwallcolor', - 'swwm_mm_efwallcolor', 'swwm_mm_fdwallcolor', - 'swwm_mm_gridcolor', 'swwm_mm_interlevelcolor', - 'swwm_mm_intralevelcolor', 'swwm_mm_lockedcolor', - 'swwm_mm_notseencolor', 'swwm_mm_portalcolor', - 'swwm_mm_secretsectorcolor', 'swwm_mm_secretwallcolor', - 'swwm_mm_specialwallcolor', 'swwm_mm_thingcolor', - 'swwm_mm_thingcolor_citem', 'swwm_mm_thingcolor_friend', - 'swwm_mm_thingcolor_item', 'swwm_mm_thingcolor_monster', - 'swwm_mm_thingcolor_ncmonster', 'swwm_mm_thingcolor_shootable', - 'swwm_mm_thingcolor_vipitem', 'swwm_mm_tswallcolor', - 'swwm_mm_unexploredsecretcolor', 'swwm_mm_wallcolor', - 'swwm_mm_xhaircolor', 'swwm_mm_yourcolor' - }; - if ( e.Name ~== "swwmresetmmcolors" ) - { - for ( int i=0; i 0)) ) - { - wasinmap = false; - if ( timer == 1 ) - { - Console.Printf("\cfOopsie Woopsie!\c-"); - let hnd = SWWMBrutalHandler(StaticEventHandler.Find("SWWMBrutalHandler")); - if ( hnd && hnd.detected ) - { - S_StartSound("crash/glass",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); - S_StartSound("crash/glass",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); - } - else S_StartSound("crash/crash",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); - } - else if ( timer == 70 ) - { - Console.Printf("\cfLooks like GZDoom made a fucky wucky! owo\c-"); - S_StartSound("crash/curb",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); - } - else if ( timer == 140 ) - { - let hnd = SWWMBrutalHandler(StaticEventHandler.Find("SWWMBrutalHandler")); - if ( hnd && hnd.detected ) Console.Printf("\cfDon't blame me. Shouldn't have tried running this with Brutal Doom.\c-"); - else Console.Printf("\cfIf you didn't trigger it manually, it's best if you take a screenshot and show it to Marisa.\c-"); - Console.Printf("\cfLoaded Version:\n \cj%s\c-",StringTable.Localize("$SWWM_SHORTVER")); - if ( tainted ) Console.Printf("\cfSavegame Version:\n \cj%s\c-",taintver); - } - timer++; - } - } -} - -// fuck -Class DontDuplicate : Inventory {} -Class DontDuplicate2 : Inventory {} -Class HOLYCOWIMTOTALLYGOINGSOFASTOHFUCK : Inventory -{ - override void DoEffect() - { - Super.DoEffect(); - if ( !Owner || (Owner.Health <= 0) ) return; - if ( (Owner.tics > 1) && (Owner.tics > max(1,Owner.CurState.tics/2)) ) - Owner.tics = max(1,Owner.CurState.tics/2); - } -} - // Handler responsible for item replacements and whatever else + +// Note: There's still a lot of code here that could be split to other files +// I'll get that done eventually, it's a complicated process + Class SWWMHandler : EventHandler { - transient String oneliner, onelinersnd; - transient int onelinertic, onelinerspan, onelinerlevel; transient int lastlock, lastcombat; transient Array combatactors; transient Array combattics; transient int highesttic; - transient Array flashes; - transient Array lastlines; transient int lastpickuptic[MAXPLAYERS]; SWWMCombatTracker trackers; SWWMScoreObj scorenums, damnums; @@ -265,17 +27,6 @@ Class SWWMHandler : EventHandler String lastmus; int lastorder; bool lastloop; - // for custom cheats - transient ui int kcode, klinger; - transient ui String kstr, klingerstr; - transient ui bool kfail; - transient ui int rss; - - // heal/armor flashes need to be handled here so they don't stack - transient int hflash[MAXPLAYERS], aflash[MAXPLAYERS]; - - // for menu events - transient Array checklist; transient String curlang; transient bool curfuntags; @@ -285,17 +36,6 @@ Class SWWMHandler : EventHandler OnFire fires; int fires_cnt; - // junk - SWWMCasing casings, casings_end; - int casings_cnt, oldmaxcasings; - SWWMChip chips, chips_end; - int chips_cnt, oldmaxdebris; - // gore - mkBloodDrop blods, blods_end; - int blods_cnt, oldmaxblood; - mkFlyingGib meats, meats_end; - int meats_cnt, oldmaxgibs; - // prevents revived monsters from spawning in more golden shells Array alreadygold; @@ -306,318 +46,17 @@ Class SWWMHandler : EventHandler // used to ease some portal-aware functions Array psectors; - // vanilla boss stuff - String bosstag; - Array bossactors; - - Actor bossbrainactor; - Actor bossviewactor; - TextureID facetex[5]; - - bool initialized; - ui bool ui_initialized; - ui TextureID bbar_f, bbar_r, bbar_d; - ui double bossalpha; - ui DynamicValueInterpolator ihealth, ihealthr; - ui int thealth, hmax; - ui int oldhealth[30]; - ui int cummdamage, lastcummtic; // please do not misread - - bool nugflip; // h/a nugget flip-flop spawn counter - // stuff to reduce worldthingspawned overhead int bossmap; int iwantdie; int indoomvacation; + // for checkreplacement bool hasdrlamonsters; // for minimap Array ffsectors; - enum EVanillaMap - { - MAP_NONE, - MAP_DE1M8, - MAP_DE2M8, - MAP_DE3M8, - MAP_DE4M8, - MAP_HE1M8_HE4M8, - MAP_HE2M8_HE5M8, - MAP_HE3M8, - MAP_DMAP07, - MAP_DMAP30, - MAP_HMAP12, - MAP_HMAP23_HMAP27_HMAP48_HMAP55, - MAP_HMAP36, - MAP_HMAP37, - MAP_HMAP38, - MAP_HMAP40, - MAP_HMAP60, - MAP_EVMAP30 // eviternity - }; - - private int WhichVanillaBossMap() - { - String mapsum = level.GetChecksum(); - if ( (mapsum ~== "94500F4B006B316FE03AC46865AEABF8") - || (mapsum ~== "97079958C7E89C1908890730B8B9FEB7") - || (mapsum ~== "058FB092EA1B70DA1E3CBF501C4A91A1") ) - return MAP_DE1M8; - if ( mapsum ~== "EFFE91DF41AD41F6973C06F0AD67DDB9" ) - return MAP_DE2M8; - if ( mapsum ~== "EF128313112110ED6C1549AF96AF26C9" ) - return MAP_DE3M8; - if ( mapsum ~== "2DC939E508AB8EB68AF79D5B60568711" ) - return MAP_DE4M8; - if ( (mapsum ~== "27639D04F8090D57A47D354992435893") - || (mapsum ~== "30D1480A6D4F3A3153739D4CCF659C4E") ) - return MAP_HE1M8_HE4M8; - if ( (mapsum ~== "5158C22A0F30CE5E558FD2A05D67685E") - || (mapsum ~== "85AC7D20D18F9BC49B9696CC2E67F029") ) - return MAP_HE2M8_HE5M8; - if ( mapsum ~== "4719C2C71EF28F52310B889DD5A9778B" ) - return MAP_HE3M8; - if ( mapsum ~== "291F24417FB3DD411339AE82EF9B3597" ) - return MAP_DMAP07; - if ( mapsum ~== "5EECD88F4491F516D590CE4BBF45F532" ) - return MAP_DMAP30; - if ( (mapsum ~== "89C4CD26EF05E2577B10CAFE56226662") - || (mapsum ~== "441BF111747671066A10A146C03EEFC4") - || (mapsum ~== "55E321849F3699655D7E062C90682F63") ) - return MAP_HMAP12; - if ( (mapsum ~== "E3B06F44DBF6F7E7754D7B1DAEF707E4") - || (mapsum ~== "FC832437D7A2B7094A9B56C3909773D9") - || (mapsum ~== "91AD797F95CC4C6D6AE33B21F664C60B") - || (mapsum ~== "188B1B4244BD8DA501D8532696EC8654") - || (mapsum ~== "5B29D0889DF09A8250D62FA09EB2B452") - || (mapsum ~== "D3C5FA777BA52264546E6569F167AF0D") - || (mapsum ~== "15FC0991D975325556EFF71F241A4458") - || (mapsum ~== "2FAD54B58487884F06EAFA507B553921") ) - return MAP_HMAP23_HMAP27_HMAP48_HMAP55; - if ( (mapsum ~== "4444C95C2029DA6EECAC92DAA31CE665") - || (mapsum ~== "33752742BCA8E539A6EE3E5D0FDA8744") - || (mapsum ~== "3FFAF2F624C1B4BB6F581DCF7B99CBA7") ) - return MAP_HMAP36; - if ( (mapsum ~== "78979A583B1E30D94C9DAE2BCFA9A18D") - || (mapsum ~== "FDC90F44C65A71E0901C1B9FFFCF3D02") - || (mapsum ~== "088ECE0E0F3E68448FA1D901001A0084") ) - return MAP_HMAP37; - if ( (mapsum ~== "3BF62E4F9FB3CF9AF267421CE2D5F348") - || (mapsum ~== "4799E1FDB5A3C0E3AD650B5AC215A737") - || (mapsum ~== "5C63A02B0B04D9AE95CA51687DC3406F") ) - return MAP_HMAP38; - if ( (mapsum ~== "EFAFE59092DE5E613562ACF52B86C37F") - || (mapsum ~== "1C5DE5A921DEE405E98E7E09D9829387") - || (mapsum ~== "2A6C4235B942467D25FD50D5B313E67A") ) - return MAP_HMAP40; - if ( mapsum ~== "B0ADDB295A3ACCE43978AAC91FB8C58A" ) - return MAP_HMAP60; - if ( mapsum ~== "5C5E5C08AF3572F31CF27318679F2B4E" ) - return MAP_EVMAP30; - return MAP_NONE; - } - - static void QueueCasing( SWWMCasing c ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return; - hnd.casings_cnt++; - if ( !hnd.casings ) - { - // this is the initial one - hnd.casings = c; - hnd.casings_end = c; - } - else - { - hnd.casings_end.nextcasing = c; - c.prevcasing = hnd.casings_end; - hnd.casings_end = c; - } - while ( hnd.casings && (swwm_maxcasings >= 0) && (hnd.casings_cnt > swwm_maxcasings) ) - DeQueueCasing(hnd.casings); - } - static void DeQueueCasing( SWWMCasing c ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd || !hnd.casings ) return; - if ( (hnd.casings != c) && !c.prevcasing && !c.nextcasing ) return; - hnd.casings_cnt--; - if ( !c.prevcasing ) hnd.casings = c.nextcasing; - else c.prevcasing.nextcasing = c.nextcasing; - if ( c == hnd.casings_end ) hnd.casings_end = c.prevcasing; - if ( c.nextcasing ) c.nextcasing.prevcasing = c.prevcasing; - c.killme = true; - c.prevcasing = null; - c.nextcasing = null; - } - static void QueueChip( SWWMChip c ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return; - hnd.chips_cnt++; - if ( !hnd.chips ) - { - // this is the initial one - hnd.chips = c; - hnd.chips_end = c; - } - else - { - hnd.chips_end.nextchip = c; - c.prevchip = hnd.chips_end; - hnd.chips_end = c; - } - while ( hnd.chips && (swwm_maxdebris >= 0) && (hnd.chips_cnt > swwm_maxdebris) ) - DeQueueChip(hnd.chips); - } - static void DeQueueChip( SWWMChip c ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd || !hnd.chips ) return; - if ( (hnd.chips != c) && !c.prevchip && !c.nextchip ) return; - hnd.chips_cnt--; - if ( !c.prevchip ) hnd.chips = c.nextchip; - else c.prevchip.nextchip = c.nextchip; - if ( c == hnd.chips_end ) hnd.chips_end = c.prevchip; - if ( c.nextchip ) c.nextchip.prevchip = c.prevchip; - c.killme = true; - c.prevchip = null; - c.nextchip = null; - } - static void QueueBlod( mkBloodDrop b ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return; - hnd.blods_cnt++; - if ( !hnd.blods ) - { - // this is the initial one - hnd.blods = b; - hnd.blods_end = b; - } - else - { - hnd.blods_end.nextblod = b; - b.prevblod = hnd.blods_end; - hnd.blods_end = b; - } - while ( hnd.blods && (swwm_maxblood >= 0) && (hnd.blods_cnt > swwm_maxblood) ) - DeQueueBlod(hnd.blods); - } - static void DeQueueBlod( mkBloodDrop b ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd || !hnd.blods ) return; - if ( (hnd.blods != b) && !b.prevblod && !b.nextblod ) return; - hnd.blods_cnt--; - if ( !b.prevblod ) hnd.blods = b.nextblod; - else b.prevblod.nextblod = b.nextblod; - if ( b == hnd.blods_end ) hnd.blods_end = b.prevblod; - if ( b.nextblod ) b.nextblod.prevblod = b.prevblod; - b.killme = true; - b.prevblod = null; - b.nextblod = null; - } - static void QueueMeat( mkFlyingGib m ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return; - hnd.meats_cnt++; - if ( !hnd.meats ) - { - // this is the initial one - hnd.meats = m; - hnd.meats_end = m; - } - else - { - hnd.meats_end.nextmeat = m; - m.prevmeat = hnd.meats_end; - hnd.meats_end = m; - } - while ( hnd.meats && (swwm_maxgibs >= 0) && (hnd.meats_cnt > swwm_maxgibs) ) - DeQueueMeat(hnd.meats); - } - static void DeQueueMeat( mkFlyingGib m ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd || !hnd.meats ) return; - if ( (hnd.meats != m) && !m.prevmeat && !m.nextmeat ) return; - hnd.meats_cnt--; - if ( !m.prevmeat ) hnd.meats = m.nextmeat; - else m.prevmeat.nextmeat = m.nextmeat; - if ( m == hnd.meats_end ) hnd.meats_end = m.prevmeat; - if ( m.nextmeat ) m.nextmeat.prevmeat = m.prevmeat; - m.killme = true; - m.prevmeat = null; - m.nextmeat = null; - } - - static void HealthFlash( int p ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd || (p == -1) ) return; - hnd.hflash[p] = gametic+5; - } - - static void ArmorFlash( int p ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd || (p == -1) ) return; - hnd.aflash[p] = gametic+5; - } - - static int AddOneliner( String type, int level, int delay = 5 ) - { - // only Demolitionist can play voice lines - if ( !(players[consoleplayer].mo is 'Demolitionist') ) - return 0; - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return 0; - String voicetype = CVar.FindCVar('swwm_voicetype').GetString(); - // suppress non-rage comments when ragekit is active, only screaming allowed - if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") ) return 0; - int whichline; - String testme = String.Format("SWWM_SUBS_%s_N%s",voicetype.MakeUpper(),type.MakeUpper()); - String locme = StringTable.Localize(testme,false); - int countem; - if ( testme == locme ) countem = 0; - else countem = locme.ToInt(); - if ( countem == 0 ) return 0; // voicepack doesn't have this - // check last line so we don't repeat - int last = 0, ent; - for ( int i=0; i 0 ) - { - whichline = Random[DemoLines](1,countem-1); - if ( whichline >= last ) whichline++; - hnd.lastlines[ent].lineno = whichline; - } - else - { - whichline = Random[DemoLines](1,countem); - let lst = new("LastLine"); - lst.type = type; - lst.lineno = whichline; - hnd.lastlines.Push(lst); - } - hnd.oneliner = String.Format("$SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),whichline); - hnd.onelinersnd = String.Format("voice/%s/%s%d",voicetype,type,whichline); - hnd.onelinertic = gametic+delay; - hnd.onelinerspan = int(S_GetLength(hnd.onelinersnd)*GameTicRate); - hnd.onelinerlevel = level; - return hnd.onelinertic+hnd.onelinerspan; - } - override void OnRegister() { // oneliner RNG must be relative to consoleplayer @@ -635,170 +74,6 @@ Class SWWMHandler : EventHandler } } - private static Vector3 UseLinePos( Line l ) - { - Vector3 al, ah, bl, bh; - if ( !l.sidedef[1] ) - { - // just the whole line - al = (l.v1.p,l.frontsector.floorplane.ZatPoint(l.v1.p)); - ah = (l.v1.p,l.frontsector.ceilingplane.ZatPoint(l.v1.p)); - bl = (l.v2.p,l.frontsector.floorplane.ZatPoint(l.v2.p)); - bh = (l.v2.p,l.frontsector.ceilingplane.ZatPoint(l.v2.p)); - return (al+ah+bl+bh)*.25; - } - SecPlane highestfloor, lowestfloor, lowestceiling, highestceiling; - if ( (l.frontsector.floorplane.ZatPoint(l.v1.p) > l.backsector.floorplane.ZatPoint(l.v1.p)) - && (l.frontsector.floorplane.ZatPoint(l.v2.p) > l.backsector.floorplane.ZatPoint(l.v2.p)) ) - { - highestfloor = l.frontsector.floorplane; - lowestfloor = l.backsector.floorplane; - } - else - { - highestfloor = l.backsector.floorplane; - lowestfloor = l.frontsector.floorplane; - } - if ( (l.frontsector.ceilingplane.ZatPoint(l.v1.p) < l.backsector.ceilingplane.ZatPoint(l.v1.p)) - && (l.frontsector.ceilingplane.ZatPoint(l.v2.p) < l.backsector.ceilingplane.ZatPoint(l.v2.p)) ) - { - lowestceiling = l.frontsector.ceilingplane; - highestceiling = l.backsector.ceilingplane; - } - else - { - lowestceiling = l.backsector.ceilingplane; - highestceiling = l.frontsector.ceilingplane; - } - // try to guess what the part that triggers this is - if ( l.Activation&SPAC_Cross ) - { - // pick the "intersection" - al = (l.v1.p,highestfloor.ZatPoint(l.v1.p)); - ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p)); - bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p)); - bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p)); - return (al+ah+bl+bh)*.25; - } - // check if lower part available - al = (l.v1.p,lowestfloor.ZatPoint(l.v1.p)); - ah = (l.v1.p,highestfloor.ZatPoint(l.v1.p)); - bl = (l.v2.p,lowestfloor.ZatPoint(l.v2.p)); - bh = (l.v2.p,highestfloor.ZatPoint(l.v2.p)); - if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) ) - return (al+ah+bl+bh)*.25; - // check if upper part available - al = (l.v1.p,lowestceiling.ZatPoint(l.v1.p)); - ah = (l.v1.p,highestceiling.ZatPoint(l.v1.p)); - bl = (l.v2.p,lowestceiling.ZatPoint(l.v2.p)); - bh = (l.v2.p,highestceiling.ZatPoint(l.v2.p)); - if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) ) - return (al+ah+bl+bh)*.25; - // check for 3d floors - bool floorfound = false; - Vector3 fal, fah, fbl, fbh; - for ( int i=0; i ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue; - al = fal; - ah = fah; - bl = fbl; - bh = fbh; - floorfound = true; - } - if ( floorfound ) return (al+ah+bl+bh)*.25; - for ( int i=0; i ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue; - al = fal; - ah = fah; - bl = fbl; - bh = fbh; - floorfound = true; - } - if ( floorfound ) return (al+ah+bl+bh)*.25; - // check for midtex - if ( !l.sidedef[0].GetTexture(1).IsNull() ) - { - double ofs = l.sidedef[0].GetTextureYOffset(1); - Vector2 siz = TexMan.GetScaledSize(l.sidedef[0].GetTexture(1)); - Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[0].GetTexture(1)); - ofs += tofs.y; - ofs *= l.sidedef[0].GetTextureYScale(1); - siz.y *= l.sidedef[0].GetTextureYScale(1); - if ( l.flags&Line.ML_DONTPEGBOTTOM ) - { - al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs); - bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs); - ah = al+(0,0,siz.y); - bh = bl+(0,0,siz.y); - } - else - { - ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs); - bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs); - al = ah-(0,0,siz.y); - bl = bh-(0,0,siz.y); - } - return (al+ah+bl+bh)*.25; - } - if ( !l.sidedef[1].GetTexture(1).IsNull() ) - { - double ofs = l.sidedef[1].GetTextureYOffset(1); - Vector2 siz = TexMan.GetScaledSize(l.sidedef[1].GetTexture(1)); - Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[1].GetTexture(1)); - ofs += tofs.y; - ofs *= l.sidedef[1].GetTextureYScale(1); - siz.y *= l.sidedef[1].GetTextureYScale(1); - if ( l.flags&Line.ML_DONTPEGBOTTOM ) - { - al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs); - bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs); - ah = al+(0,0,siz.y); - bh = bl+(0,0,siz.y); - } - else - { - ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs); - bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs); - al = ah-(0,0,siz.y); - bl = bh-(0,0,siz.y); - } - return (al+ah+bl+bh)*.25; - } - // just use the intersection - al = (l.v1.p,highestfloor.ZatPoint(l.v1.p)); - ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p)); - bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p)); - bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p)); - return (al+ah+bl+bh)*.25; - } - - static clearscope void ClearAllShaders( PlayerInfo p ) - { - Shader.SetEnabled(p,"WaterWarp",false); - Shader.SetEnabled(p,"LavaWarp",false); - Shader.SetEnabled(p,"SlimeWarp",false); - Shader.SetEnabled(p,"ZoomBlur",false); - Shader.SetEnabled(p,"RagekitShader",false); - Shader.SetEnabled(p,"GhostShader",false); - Shader.SetEnabled(p,"InvinciShader",false); - Shader.SetEnabled(p,"Glitch",false); - Shader.SetEnabled(p,"Grain",false); - } - // level end stats override void WorldUnloaded( WorldEvent e ) { @@ -989,7 +264,7 @@ Class SWWMHandler : EventHandler if ( !SWWMUtility.IsExitLine(l) ) continue; if ( skipme.Find(l) < skipme.Size() ) continue; - Vector3 lpos = UseLinePos(l); + Vector3 lpos = SWWMUtility.UseLinePos(l); // look for connected lines int xcnt = 1; if ( l.frontsector ) @@ -1013,7 +288,7 @@ Class SWWMHandler : EventHandler } skipme.Push(l2); xcnt++; - lpos += UseLinePos(l2); + lpos += SWWMUtility.UseLinePos(l2); } } if ( l.backsector ) @@ -1037,7 +312,7 @@ Class SWWMHandler : EventHandler } skipme.Push(l2); xcnt++; - lpos += UseLinePos(l2); + lpos += SWWMUtility.UseLinePos(l2); } } lpos /= xcnt; @@ -1062,154 +337,7 @@ Class SWWMHandler : EventHandler } ClearAllShaders(players[consoleplayer]); // recheck queues in case limits changed - while ( casings && (casings_cnt > swwm_maxcasings) ) - DeQueueCasing(casings); - while ( chips && (chips_cnt > swwm_maxdebris) ) - DeQueueChip(chips); - while ( blods && (blods_cnt > swwm_maxblood) ) - DeQueueBlod(blods); - while ( meats && (meats_cnt > swwm_maxgibs) ) - DeQueueMeat(meats); - } - - override void PlayerDied( PlayerEvent e ) - { - let s = SWWMStats.Find(players[e.playernumber]); - if ( s ) s.deaths++; - } - - override void PlayerEntered( PlayerEvent e ) - { - PlayerInfo p = players[e.playernumber]; - // override KEYCONF-forced player classes when run with other gameplay mods (wish this was easier) - if ( !(p.mo is 'Demolitionist') ) - { - // make sure it's defined here, so special purpose classes (player chunks, scripted overrides) are respected - for ( int i=0; i 0) && ((s.lastcluster != level.cluster) || !(level.clusterflags&LevelLocals.CLUSTER_HUB))) ) - SWWMUtility.WipeInventory(p.mo,swwm_resetscore); - } - - override void PlayerRespawned( PlayerEvent e ) - { - // reset some vars - multilevel[e.playernumber] = 0; - spreecount[e.playernumber] = 0; - tookdamage[e.playernumber] = false; - lastkill[e.playernumber] = int.min; - // reset combat tracker - if ( !swwm_notrack ) - SWWMCombatTracker.Spawn(players[e.playernumber].mo); + RecheckQueues(); } override void WorldThingRevived( WorldEvent e ) @@ -1262,37 +390,6 @@ Class SWWMHandler : EventHandler curfuntags = swwm_funtags; } - private void QueueMaintenance() - { - if ( swwm_maxcasings != oldmaxcasings ) - { - while ( casings && (swwm_maxcasings >= 0) && (casings_cnt > swwm_maxcasings) ) - DeQueueCasing(casings); - } - if ( swwm_maxdebris != oldmaxdebris ) - { - while ( chips && (swwm_maxdebris >= 0) && (chips_cnt > swwm_maxdebris) ) - DeQueueChip(chips); - } - if ( swwm_maxblood != oldmaxblood ) - { - while ( blods && (swwm_maxblood >= 0) && (blods_cnt > swwm_maxblood) ) - DeQueueBlod(blods); - } - if ( swwm_maxgibs != oldmaxgibs ) - { - while ( meats && (swwm_maxgibs >= 0) && (meats_cnt > swwm_maxgibs) ) - DeQueueMeat(meats); - } - oldmaxcasings = swwm_maxcasings; - oldmaxdebris = swwm_maxdebris; - oldmaxblood = swwm_maxblood; - oldmaxgibs = swwm_maxgibs; - if ( swwm_blood ) return; - while ( blods ) DeQueueBlod(blods); - while ( meats ) DeQueueMeat(meats); - } - // countable item scoring private void ItemCountTrack() { @@ -1578,37 +675,13 @@ Class SWWMHandler : EventHandler Console.Printf(StringTable.Localize("$SWWM_NEWMISSION")); } } - if ( onelinertic && (onelinertic < gametic) ) - { - if ( players[consoleplayer].health > 0 ) - { - if ( onelinerlevel > swwm_mutevoice ) - players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE); - SendNetworkEvent("swwmremoteliner."..onelinersnd,consoleplayer,onelinerlevel); - } - onelinertic = 0; - onelinerspan = 0; - } - for ( int i=0; i= gametic ) continue; - flashes.Delete(i); - i--; - } + OnelinerTick(); + FlashTick(); ItemCountTrack(); CombatTrack(); OneHundredPercentCheck(); SimpleTracking(); - if ( initialized ) return; - // wait until bosses are active - for ( int i=0; i x.ceilingz ) x.SetZ(x.ceilingz-x.height); - if ( x.pos.z < x.floorz ) x.SetZ(x.floorz); - if ( !x.TestMobjLocation() || !x.TestMobjZ() || !level.IsPointInLevel(x.pos) ) - { - x.ClearCounters(); - x.Destroy(); - } - else - { - x.angle = e.Thing.angle; - x.GiveInventory("DontDuplicate",1); - break; - } - } - } - } - } - } + IWantDieSpawn(e); if ( (e.Thing is 'TeleportDest') || (e.Thing is 'BossTarget') ) { let d = Actor.Spawn("SWWMTeleportDest",e.Thing.pos); @@ -2278,203 +1291,7 @@ Class SWWMHandler : EventHandler } else if ( e.Thing.GetClass() == "LostSoul" ) e.Thing.bNOBLOOD = true; - // vanilla boss stuff - bool upgrademe = swwm_upgradebosses; - if ( bossmap == -1 ) bossmap = WhichVanillaBossMap(); - if ( bossmap == MAP_DE1M8 ) - { - if ( e.Thing is 'BaronOfHell' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3; - if ( trk ) trk.bBOSS = true; - } - bosstag = "$BT_BRUISERS"; - } - else if ( bossmap == MAP_DE2M8 ) - { - if ( e.Thing is 'Cyberdemon' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; - if ( trk ) trk.bBOSS = true; - } - bosstag = "$BT_CYBIE"; - } - else if ( bossmap == MAP_DE3M8 ) - { - if ( e.Thing is 'Spidermastermind' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 6; - if ( trk ) trk.bBOSS = true; - } - bosstag = "$BT_SPIDER"; - } - else if ( bossmap == MAP_DE4M8 ) - { - if ( e.Thing is 'Spidermastermind' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; - if ( trk ) trk.bBOSS = true; - } - bosstag = "$BT_SPIDER2"; - } - else if ( bossmap == MAP_DMAP07 ) - { - if ( (e.Thing is 'Fatso') || (e.Thing is 'Arachnotron') ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; - if ( trk ) trk.bBOSS = true; - } - bosstag = "$BT_DIMPLE"; - } - else if ( bossmap == MAP_DMAP30 ) - { - if ( e.Thing is 'BossBrain' ) - { - bossbrainactor = e.Thing; - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 40; // goodbye, instakills - if ( trk ) trk.bBOSS = true; - } - if ( e.Thing is 'BossEye' ) - bossviewactor = e.Thing; - bosstag = "$BT_IOS"; - } - else if ( bossmap == MAP_HE1M8_HE4M8 ) - { - if ( e.Thing is 'IronLich' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; - if ( trk ) trk.bBOSS = true; - } - bosstag = "$BT_LICHES"; - } - else if ( bossmap == MAP_HE2M8_HE5M8 ) - { - if ( e.Thing is 'Minotaur' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 3; - if ( trk ) trk.bBOSS = true; - } - bosstag = "$BT_MINOTAUR"; - } - else if ( bossmap == MAP_HE3M8 ) - { - if ( e.Thing is 'Sorcerer1' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_DSPARIL"; - } - else if ( e.Thing is 'Sorcerer2' ) - { - // second phase - bossactors.Clear(); - initialized = false; - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_DSPARIL2"; - } - } - else if ( bossmap == MAP_HMAP38 ) - { - if ( e.Thing is 'ClericBoss' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_CLERIC"; - } - } - else if ( bossmap == MAP_HMAP36 ) - { - if ( e.Thing is 'FighterBoss' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_FIGHTER"; - } - } - else if ( bossmap == MAP_HMAP37 ) - { - if ( e.Thing is 'MageBoss' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 2; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_MAGE"; - } - } - else if ( bossmap == MAP_HMAP12 ) - { - if ( e.Thing is 'Dragon' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_DRAGON"; - } - } - else if ( bossmap == MAP_HMAP23_HMAP27_HMAP48_HMAP55 ) - { - if ( e.Thing is 'Heresiarch' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 8; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_HERESIARCH"; - } - } - else if ( bossmap == MAP_HMAP40 ) - { - if ( e.Thing is 'Korax' ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 10; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_KORAX"; - } - } - else if ( bossmap == MAP_HMAP60 ) - { - if ( (e.Thing is 'FighterBoss') || (e.Thing is 'ClericBoss') || (e.Thing is 'MageBoss') ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 4; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_DEATHKINGS"; - initialized = true; // healthbar shows from the start - } - } - else if ( bossmap == MAP_EVMAP30 ) - { - if ( e.Thing.GetClassName() == "ArchangelusA" ) - { - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_ARCHANGELUS"; - } - else if ( e.Thing.GetClassName() == "ArchangelusB" ) - { - // second phase - bossactors.Clear(); - initialized = false; - bossactors.Push(e.Thing); - if ( upgrademe ) e.Thing.StartHealth = e.Thing.Health *= 5; - if ( trk ) trk.bBOSS = true; - bosstag = "$BT_ARCHANGELUS"; - } - } + VanillaBossSpawn(e,trk); // inflation check if ( trk ) { @@ -2485,165 +1302,9 @@ Class SWWMHandler : EventHandler override void PostUiTick() { - if ( (gametic == onelinertic) && (oneliner != "") && (players[consoleplayer].health > 0) ) - { - if ( onelinerlevel > swwm_mutevoice ) - { - let l = SWWMOneLiner.Make(oneliner,onelinerspan); - StatusBar.AttachMessage(l,-3473); - } - SendNetworkEvent("swwmremotelinertxt."..oneliner,consoleplayer,onelinerlevel); - } - for ( int i=0; i 0 ) - { - cummdamage += curcumm; - lastcummtic = gametic; - } - else if ( gametic > lastcummtic+150 ) cummdamage = 0; - thealth = newhealth; - ihealthr.Update(thealth); - if ( thealth > oldhealth[29] ) - for ( int i=29; i>0; i-- ) - oldhealth[i] = thealth; - ihealth.Update(oldhealth[29]); - for ( int i=29; i>0; i-- ) - oldhealth[i] = oldhealth[i-1]; - if ( thealth > 0 ) bossalpha = min(3.,bossalpha+1./30.); - else bossalpha = max(0,bossalpha-1./50.); - } - - override bool InputProcess( InputEvent e ) - { - if ( (e.Type == InputEvent.TYPE_KeyDown) && (e.KeyChar >= 0x61) && (e.KeyChar <= 0x7A) ) - { - // cheat code handling - String cht[] = - { - "swwmlodsofemone", "swwmdeeplore", "swwmfroggygang", "swwmforgetaboutit", - "swwmmisterproper", - // SWWM Platinum cheats - "swwmimstuck", "swwmarmojumbo", "swwmdangimhealthy", - "swwmwarriorofzaemonath", "swwmpowerparp", "swwmcannotseemyhands", - "swwmreflectonme", "swwmgunzmeneeds", "swwmbloodrainsfromheaven", - "swwmnotwannaboom", "swwmverywrappyoatmeal", "swwmflaggerybingo", - "swwmheadsball", "swwmsmarties", "swwmnocilla", - "swwmmarioisaweenie", "swwmpunish", "swwmboingball", - "swwmgassy", "swwmiamsuperman", "swwmtouchstone" - }; - String cmd[] = - { - "swwmmoneycheat", "swwmlorecheat", "swwmfroggycheat", "swwmamnesiacheat", - "swwmjanitorcheat", - // SWWM Platinum cheats - "swwmsafecheat", "swwmweaponcheat", "swwmhealcheat", - "swwmynykroncheat", "swwmgravcheat", "swwminvischeat", - "swwmbarriercheat", "swwmammocheat", "swwmbloodcheat", - "swwmexplocheat", "swwmallcheat", "swwmflagcheat", - "swwmballcheat", "swwmsmartcheat", "swwmnutcheat", - "swwmweeniecheat", "swwmpunishcheat", "swwmball2cheat", - "swwmfartcheat", "swwmsupercheat", "swwmstonecheat" - }; - bool matchany = false; - kstr.AppendCharacter(e.KeyChar); - if ( kstr.Length() > 0 ) - { - if ( kcode >= 4 ) - S_StartSound("misc/boink",CHAN_WEAPON,CHANF_UI|CHANF_OVERLAP,pitch:FRandom[HudStuff](.8,1.2)); - for ( int i=0; i= 4 ) - { - kfail = true; - klinger = gametic+40; - klingerstr = kstr; - S_StartSound("bruh",CHAN_VOICE,CHANF_UI); - eatit = true; - } - kcode = 0; - kstr = ""; - if ( eatit ) return true; - } - else - { - kcode++; - if ( kcode > 4 ) return true; // eat keypresses from this point - } - } - // F - if ( e.KeyChar == 0x66 ) - { - let demo = Demolitionist(players[consoleplayer].mo); - let gone = PlayerGone(players[consoleplayer].mo); - if ( (demo && (demo.Health <= 0) && (demo.deadtimer > 40)) - || (gone && (gone.Health <= 0) && (gone.deadtimer > 40)) ) - { - // pay respects - int numf = Random[FInTheChat](1,6); - for ( int i=0; i GetDRLAReplacement( Class a ) - { - static const String refpool[] = - { - "Zombieman", - "Shotgunguy", - "Chaingunguy", - "DoomImp", - "Demon", - "Spectre", - "LostSoul", - "Cacodemon", - "HellKnight", - "BaronOfHell", - "Arachnotron", - "PainElemental", - "Revenant", - "Fatso", - "Archvile", - "Cyberdemon", - "SpiderMastermind", - "BossEye", - "BossBrain" - }; - static const String babypool[] = - { - "RLFormerHumanPistol", - "RLFormerSergeantShotgun", - "RLFormerCommandoChaingun", - "RLImp", - "RLDemon", - "RLSpectre", - "RLLostSoul", - "RLCacodemon", - "RLHellKnight", - "RLBaronOfHell", - "RLArachnotron", - "RLPainElemental", - "RLRevenant", - "RLMancubus", - "RLArchvile", - "RLCyberdemon", - "RLSpiderMastermindVariantSpawner", - "RLEasyBossEye", - "RLBossBrain" - }; - static const String easypool[] = - { - "RLFormerHumanNoArmageddonSpawner", - "RLFormerSergeantNoArmageddonSpawner", - "RLFormerCommandoNoArmageddonSpawner", - "RLImpNoArmageddonSpawner", - "RLDemonNoArmageddonSpawner", - "RLSpectreNoArmageddonSpawner", - "RLLostSoulNoArmageddonSpawner", - "RLCacodemonNoArmageddonSpawner", - "RLHellKnightNoArmageddonSpawner", - "RLBaronOfHellNoArmageddonSpawner", - "RLArachnotronNoArmageddonSpawner", - "RLPainElementalNoArmageddonSpawner", - "RLRevenantNoArmageddonSpawner", - "RLMancubusNoArmageddonSpawner", - "RLArchvileNoArmageddonSpawner", - "RLCyberdemonNoArmageddonSpawner", - "RLSpiderMastermindNoArmageddonSpawner", - "RLBossEye", - "RLBossBrain" - }; - static const String normalpool[] = - { - "RLFormerHumanNoArmageddonSpawner", - "RLFormerSergeantNoArmageddonSpawner", - "RLFormerCommandoNoArmageddonSpawner", - "RLImpNoArmageddonSpawner", - "RLDemonNoArmageddonSpawner", - "RLSpectreNoArmageddonSpawner", - "RLLostSoulNoArmageddonSpawner", - "RLCacodemonNoArmageddonSpawner", - "RLHellKnightNoArmageddonSpawner", - "RLBaronOfHellNoArmageddonSpawner", - "RLArachnotronNoArmageddonSpawner", - "RLPainElementalNoArmageddonSpawner", - "RLRevenantNoArmageddonSpawner", - "RLMancubusNoArmageddonSpawner", - "RLArchvileNoArmageddonSpawner", - "RLCyberdemonNoArmageddonSpawner", - "RLSpiderMastermindNoArmageddonSpawner", - "RLBossEye", - "RLBossBrain" - }; - static const String hardpool[] = - { - "RLFormerHumanSpawner", - "RLFormerSergeantSpawner", - "RLFormerCommandoSpawner", - "RLImpSpawner", - "RLDemonSpawner", - "RLSpectreSpawner", - "RLLostSoulSpawner", - "RLCacodemonSpawner", - "RLHellKnightSpawner", - "RLBaronOfHellSpawner", - "RLArachnotronSpawner", - "RLPainElementalSpawner", - "RLRevenantSpawner", - "RLMancubusSpawner", - "RLArchvileSpawner", - "RLCyberdemonSpawner", - "RLSpiderMastermindSpawner", - "RLUVBossEye", - "RLBossBrain" - }; - static const String nightmarepool[] = - { - "RLEliteFormerHumanSpawner", - "RLEliteFormerSergeantSpawner", - "RLEliteFormerCommandoSpawner", - "RLNightmareImp", - "RLNightmareDemon", - "RLNightmareSpectre", - "RLNightmareLostSoul", - "RLNightmareCacodemon", - "RLNightmareHellKnight", - "RLNightmareBaronOfHell", - "RLNightmareArachnotron", - "RLNightmarePainElemental", - "RLNightmareRevenant", - "RLNightmareMancubus", - "RLNightmareArchvile", - "RLNightmareCyberdemonSpawner", - "RLNightmareSpiderMastermindSpawner", - "RLNightmareBossEye", - "RLNightmareBossBrain" - }; - static const String technophobiapool[] = - { - "RLFormerCyborgBattleRifle", - "RLFormerCyborgBattleRifle", - "RLFormerCyborgBattleRifle", - "RLCyberneticImp", - "RLCyberneticDemon", - "RLCyberneticSpectre", - "RLCyberneticLostSoul", - "RLCacodemon", - "RLCyberneticHellKnight", - "RLCyberneticBaronOfHell", - "RLCyberneticArachnotron", - "RLCyberneticPainElemental", - "RLCyberneticRevenant", - "RLCyberneticMancubus", - "RLCyberneticArchvile", - "RLCyberneticCyberdemonSpawner", - "RLCyberneticSpiderMastermindSpawner", - "RLTechnophobiaBossEye", - "RLTechnophobiaBossBrain" - }; - static const String armageddonpool[] = - { - "RLFormerAssaultTrooper", - "RLFormerOverwatch", - "RLFormerShocktrooper", - "RLArmageddonImp", - "RLArmageddonDemon", - "RLArmageddonSpectreSpawner", - "RLTheHungrySpawner", - "RLArmageddonCacodemon", - "RLArmageddonHellKnightSpawner", - "RLArmageddonBaronOfHell", - "RLArmageddonArachnotron", - "RLArmageddonPainElemental", - "RLArmageddonRevenant", - "RLArmageddonMancubus", - "RLArmageddonArchvileSpawner", - "RLArmageddonCyberdemonSpawner", - "RLArmageddonSpiderMastermindSpawner", - "RLArmageddonBossEye", - "RLArmageddonBossBrain" - }; - static const String adaptivepool[] = - { - "RLAdaptiveFormerHuman", - "RLAdaptiveFormerSergeant", - "RLAdaptiveFormerCommando", - "RLAdaptiveImp", - "RLAdaptiveDemon", - "RLAdaptiveSpectre", - "RLAdaptiveLostSoul", - "RLAdaptiveCacodemon", - "RLAdaptiveHellKnight", - "RLAdaptiveBaronOfHell", - "RLAdaptiveArachnotron", - "RLAdaptivePainElemental", - "RLAdaptiveRevenant", - "RLAdaptiveMancubus", - "RLAdaptiveArchvile", - "RLAdaptiveCyberdemon", - "RLAdaptiveSpiderMastermind", - "RLUVBossEye", - "RLBossBrain" - }; - switch ( swwm_drlaskill ) - { - case 0: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return babypool[i]; - } - break; - case 1: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return easypool[i]; - } - break; - case 2: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return normalpool[i]; - } - break; - case 3: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return hardpool[i]; - } - break; - case 4: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return nightmarepool[i]; - } - break; - case 5: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return technophobiapool[i]; - } - break; - case 6: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return armageddonpool[i]; - } - break; - case 7: - default: - for ( int i=0; i<18; i++ ) - { - if ( !(a is refpool[i]) ) continue; - return adaptivepool[i]; - } - break; - } - return null; - } - - override void CheckReplacement( ReplaceEvent e ) - { - // respect final replacements - if ( e.IsFinal ) return; - // shell types (sorted by rarity - static const Class redpool[] = {"RedShell","RedShell2","RedShell4"}; - static const Class greenpool[] = {"GreenShell","GreenShell2","GreenShell4"}; - static const Class whitepool[] = {"WhiteShell","WhiteShell2"}; - static const Class purplepool[] = {"PurpleShell","PurpleShell2","PurpleShell4"}; - static const Class bluepool[] = {"BlueShell","BlueShell2","BlueShell4"}; - static const Class blackpool[] = {"BlackShell","BlackShell2"}; - // DRLA Monsters stuff - if ( hasdrlamonsters ) - { - let rep = GetDRLAReplacement(e.Replacee); - if ( rep ) - { - e.Replacement = rep; - e.IsFinal = true; - return; - } - } - // only replace vanilla blood if no other gore mod is doing it - if ( (e.Replacee == "Blood") && (!e.Replacement || e.Replacement == "Blood") && swwm_blood ) e.Replacement = "mkBlood"; - else if ( e.Replacee is 'ItemFog' ) e.Replacement = 'SWWMItemFog'; - else if ( e.Replacee is 'TeleportFog' ) e.Replacement = 'SWWMTeleportFog'; - else if ( (e.Replacee is 'CommanderKeen') && (!e.Replacement || (e.Replacement == 'CommanderKeen')) ) - { - let def = GetDefaultByType(e.Replacee); - bool dehackery = false; - for ( State s=def.SpawnState; s; s=s.NextState ) - { - if ( s.bDEHACKED ) dehackery = true; - // keep checking until we hit a loop, just in case - if ( s.NextState && (s.DistanceTo(s.NextState) <= 0) ) break; - } - if ( dehackery ) return; - e.Replacement = 'SWWMHangingKeen'; - } - else if ( (e.Replacee is 'BossBrain') && (!e.Replacement || (e.Replacement == 'BossBrain')) ) - { - let def = GetDefaultByType(e.Replacee); - bool dehackery = false; - for ( State s=def.SpawnState; s; s=s.NextState ) - { - if ( s.bDEHACKED ) dehackery = true; - // keep checking until we hit a loop, just in case - if ( s.NextState && (s.DistanceTo(s.NextState) <= 0) ) break; - } - if ( dehackery ) return; - e.Replacement = 'SWWMBossBrain'; - } - else if ( e.Replacee is 'RedCard' ) - { - if ( level.GetChecksum() ~== "3805A661D5C4523AFF7BF86991071043" ) - return; // don't replace red key in Equinox MAP13 - e.Replacement = 'SWWMRedCard'; - } - else if ( e.Replacee is 'BlueCard' ) e.Replacement = 'SWWMBlueCard'; - else if ( e.Replacee is 'YellowCard' ) e.Replacement = 'SWWMYellowCard'; - else if ( e.Replacee.GetClassName() == 'KDiZDSilverKey' ) e.Replacement = 'SWWMSilverCardKDiZD'; - else if ( e.Replacee.GetClassName() == 'KDiZDGreenKey' ) e.Replacement = 'SWWMGreenCardKDiZD'; - else if ( e.Replacee.GetClassName() == 'KDiZDOrangeKey' ) e.Replacement = 'SWWMOrangeCardKDiZD'; - else if ( e.Replacee.GetClassName() == 'GreenCard' ) e.Replacement = 'SWWMGreenCard'; - else if ( e.Replacee is 'RedSkull' ) e.Replacement = 'SWWMRedSkull'; - else if ( e.Replacee is 'BlueSkull' ) e.Replacement = 'SWWMBlueSkull'; - else if ( e.Replacee is 'YellowSkull' ) e.Replacement = 'SWWMYellowSkull'; - else if ( e.Replacee is 'KeyGreen' ) e.Replacement = 'SWWMKeyGreen'; - else if ( e.Replacee is 'KeyBlue' ) e.Replacement = 'SWWMKeyBlue'; - else if ( e.Replacee is 'KeyYellow' ) e.Replacement = 'SWWMKeyYellow'; - else if ( e.Replacee.GetClassName() == 'KeyRed' ) e.Replacement = 'SWWMKeyRed'; - else if ( (e.Replacee is 'Chainsaw') || (e.Replacee is 'Gauntlets') || (e.Replacee is 'FWeapAxe') ) e.Replacement = SWWMUtility.PickSWWMSlot1(); - else if ( (e.Replacee is 'Fist') || (e.Replacee is 'Staff') ) e.Replacement = 'DeepImpact'; - else if ( (e.Replacee is 'Pistol') || (e.Replacee is 'GoldWand') || (e.Replacee is 'FWeapFist') || (e.Replacee is 'CWeapMace') || (e.Replacee is 'MWeapWand') || (e.Replacee.GetClassName() == 'TangoPistol') ) e.Replacement = SWWMUtility.PickSWWMSlot2(); - else if ( (e.Replacee is 'Shotgun') || (e.Replacee is 'CWeapStaff') || (e.Replacee.GetClassName() == 'TangoShotgun') ) e.Replacement = SWWMUtility.IsDoomOne()?SWWMUtility.PickHereticSlot3():SWWMUtility.PickSWWMSlot3(); - else if ( (e.Replacee is 'SuperShotgun') || (e.Replacee is 'MWeapFrost') || (e.Replacee.GetClassName() == 'TangoSuperShotgun') ) e.Replacement = SWWMUtility.PickSWWMSlot4(); - else if ( (e.Replacee is 'Crossbow') || (e.Replacee.GetClassName() == 'TangoScrapGun') ) e.Replacement = SWWMUtility.PickHereticSlot3(); - else if ( (e.Replacee is 'Chaingun') || (e.Replacee is 'Blaster') || (e.Replacee is 'FWeaponPiece3') || (e.Replacee.GetClassName() == 'TangoAssaultRifle') || (e.Replacee.GetClassName() == 'TangoChaingun') ) e.Replacement = SWWMUtility.PickSWWMSlot5(); - else if ( (e.Replacee is 'RocketLauncher') || (e.Replacee is 'PhoenixRod') || (e.Replacee is 'FWeapHammer') || (e.Replacee.GetClassName() == 'TangoStomper') ) e.Replacement = SWWMUtility.PickSWWMSlot6(); - else if ( (e.Replacee is 'PlasmaRifle') || (e.Replacee is 'SkullRod') ) e.Replacement = SWWMUtility.PickDoomSlot6(); - else if ( e.Replacee is 'CWeapFlame' ) e.Replacement = SWWMUtility.PickSWWMSlot7(); - else if ( e.Replacee is 'MWeapLightning' ) e.Replacement = SWWMUtility.PickSWWMSlot8(); - else if ( (e.Replacee is 'BFG9000') || (e.Replacee is 'Mace') ) e.Replacement = SWWMUtility.PickDoomSlot7(); - else if ( e.Replacee is 'CWeaponPiece2' ) e.Replacement = SWWMUtility.PickSWWMSlot9(); - else if ( e.Replacee is 'MWeaponPiece1' ) e.Replacement = SWWMUtility.PickSWWMSlot0(); - else if ( (e.Replacee is 'ShellBox') || (e.Replacee is 'CrossbowHefty') || (e.Replacee.GetClassName() == 'TangoShellBox') || (e.Replacee.GetClassName() == 'TangoScrapDrumPack') ) - { - /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SMW05SmallAmmo':'SMW05BigAmmo'; - else */switch( Random[Replacements](0,14) ) - { - case 0: - case 1: - case 2: - e.Replacement = redpool[Random[Replacements](1,2)]; - break; - case 3: - case 4: - case 5: - e.Replacement = greenpool[Random[Replacements](1,2)]; - break; - case 6: - case 7: - case 8: - e.Replacement = whitepool[Random[Replacements](0,1)]; - break; - case 9: - case 10: - case 11: - e.Replacement = purplepool[Random[Replacements](0,2)]; - break; - case 12: - case 13: - e.Replacement = bluepool[Random[Replacements](0,2)]; - break; - case 14: - e.Replacement = blackpool[Random[Replacements](0,1)]; - break; - } - } - else if ( (e.Replacee is 'Shell') || (e.Replacee is 'CrossbowAmmo') || (e.Replacee.GetClassName() == 'TangoShell') || (e.Replacee.GetClassName() == 'TangoScrapDrum') ) - { - /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SMW05SmallAmmo':'SMW05BundleSpawn'; - else */switch( Random[Replacements](0,13) ) - { - case 0: - case 1: - case 2: - e.Replacement = redpool[Random[Replacements](0,1)]; - break; - case 3: - case 4: - case 5: - e.Replacement = greenpool[Random[Replacements](0,1)]; - break; - case 6: - case 7: - e.Replacement = whitepool[0]; - break; - case 8: - case 9: - case 10: - e.Replacement = purplepool[Random[Replacements](0,1)]; - break; - case 11: - case 12: - e.Replacement = bluepool[Random[Replacements](0,1)]; - break; - case 13: - e.Replacement = blackpool[0]; - break; - } - } - else if ( e.Replacee is 'ClipBox' ) - { - /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenSmallAmmo':'SheenBigAmmo'; - else */e.Replacement = Random[Replacements](0,4)?'EvisceratorShell':Random[Replacements](0,6)?'EvisceratorTrioSpawn':'EvisceratorSixPack'; - } - else if ( (e.Replacee is 'Clip') || (e.Replacee is 'GoldWandAmmo') ) e.Replacement = /*(e.Replacee is 'GoldWandHefty')?'SheenSmallAmmo':'SheenTinyAmmo'*/'SWWMNothing'; - else if ( e.Replacee is 'BlasterHefty' ) - { - /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenBigAmmo':'SheenSmallAmmo'; - else */e.Replacement = Random[Replacements](0,6)?'EvisceratorTrioSpawn':'EvisceratorSixPack'; - } - else if ( (e.Replacee is 'BlasterAmmo') || (e.Replacee.GetClassName() == 'TangoBulletClipHalf') ) - { - /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'SheenSmallAmmo':'SheenTinyAmmo'; - else */e.Replacement = 'EvisceratorShell'; - } - else if ( (e.Replacee is 'RocketBox') || (e.Replacee is 'PhoenixRodHefty') || (e.Replacee is 'MaceHefty')|| (e.Replacee.GetClassName() == 'TangoStomperBox') ) - { - /*if ( Random[Replacements](0,1) ) e.Replacement = Random[Replacements](0,2)?'QuadravolAmmo':'QuadravolAmmoBundleSpawn'; - else */switch ( Random[Replacements](0,11) ) - { - case 0: - case 1: - case 2: - case 3: - case 4: - if ( Random[Replacements](0,5) ) e.Replacement = 'HellblazerMissiles'; - else if ( Random[Replacements](0,4) ) e.Replacement = 'HellblazerMissileTrioSpawn'; - else e.Replacement = 'HellblazerMissileMag'; - break; - case 5: - case 6: - case 7: - case 8: - if ( Random[Replacements](0,6) ) e.Replacement = 'HellblazerCrackshots'; - else e.Replacement = 'HellblazerCrackshotMag'; - break; - case 9: - case 10: - if ( Random[Replacements](0,8) ) e.Replacement = 'HellblazerRavagers'; - else e.Replacement = 'HellblazerRavagerMag'; - break; - case 11: - if ( Random[Replacements](0,10) ) e.Replacement = 'HellblazerWarheads'; - else e.Replacement = 'HellblazerWarheadMag'; - break; - } - } - else if ( (e.Replacee is 'RocketAmmo') || (e.Replacee is 'PhoenixRodAmmo') || (e.Replacee is 'MaceAmmo') || (e.Replacee.GetClassName() == 'TangoStomperAmmo') ) - { - /*if ( Random[Replacements](0,1) ) e.Replacement = 'QuadravolAmmo'; - else */e.Replacement = Random[Replacements](0,2)?'HellblazerMissiles':'HellblazerCrackshots'; - } - else if ( (e.Replacee is 'CellPack') || (e.Replacee is 'SkullRodHefty') ) - { - /*if ( Random[Replacements](0,1) ) - { - if ( !Random[Replacements](0,2) ) e.Replacement = Random[Replacements](0,3)?'EMPCoreBundleSpawn':'EMPCore'; - else if ( Random[Replacements](0,2) ) e.Replacement = 'RayBoltBundleSpawn'; - else e.Replacement = 'RayAmmo'; - } - else */if ( !Random[Replacements](0,2) ) - { - if ( Random[Replacements](0,3) ) e.Replacement = Random[Replacements](0,2)?'SilverBulletsBundleSpawn':'SilverBullets2BundleSpawn'; - else e.Replacement = Random[Replacements](0,2)?'SilverBulletAmmo':'SilverBulletAmmo2'; - } - else if ( Random[Replacements](0,2) ) e.Replacement = 'CandyGunBulletsBundleSpawn'; - else e.Replacement = 'CandyGunAmmo'; - } - else if ( (e.Replacee is 'Cell') || (e.Replacee is 'SkullRodAmmo') ) - { - if ( !Random[Replacements](0,2) ) e.Replacement = /*!Random[Replacements](0,2)?'RayBattery':*/Random[Replacements](0,2)?'HellblazerRavagers':'HellblazerWarheads'; - else if ( Random[Replacements](0,2) ) e.Replacement = /*Random[Replacements](0,1)?'DarkCanister':*/'SparkUnit'; - else if ( !Random[Replacements](0,3) ) e.Replacement = /*Random[Replacements](0,1)?'RayBolt':*/'CandyGunBullets'; - else e.Replacement = /*Random[Replacements](0,1)?'EMPCore':*/Random[Replacements](0,2)?'SilverBullets':'SilverBullets2'; - } - else if ( e.Replacee is 'Mana1' ) e.Replacement = 'FabricatorTier1'; - else if ( e.Replacee is 'Mana2' ) e.Replacement = 'FabricatorTier2'; - else if ( e.Replacee is 'Mana3' ) e.Replacement = 'FabricatorTier3'; - else if ( e.Replacee is 'ArtiBoostMana' ) e.Replacement = 'FabricatorTier4'; - else if ( (e.Replacee is 'Backpack') || (e.Replacee is 'BagOfHolding') || (e.Replacee is 'ArtiPork') ) e.Replacement = 'HammerspaceEmbiggener'; - else if ( (e.Replacee is 'FWeaponPiece1') || (e.Replacee is 'FWeaponPiece2') - || (e.Replacee is 'CWeaponPiece1') || (e.Replacee is 'CWeaponPiece3') - || (e.Replacee is 'MWeaponPiece2') || (e.Replacee is 'MWeaponPiece3') ) e.Replacement = 'SWWMNothing'; - else if ( e.Replacee is 'ArmorBonus' ) e.Replacement = 'ArmorNuggetItem'; - else if ( e.Replacee is 'HealthBonus' ) e.Replacement = 'HealthNuggetItem'; - else if ( (e.Replacee is 'ArtiTimeBomb') || (e.Replacee is 'ArtiBlastRadius') || (e.Replacee is 'ArtiPoisonBag') || (e.Replacee is 'ArtiHealingRadius') ) e.Replacement = (nugflip=!nugflip)?'HealthNuggetBundleSpawn':'ArmorNuggetBundleSpawn'; - else if ( (e.Replacee is 'HealthBonus') ) e.Replacement = 'HealthNuggetItem'; - else if ( (e.Replacee is 'Stimpack') || (e.Replacee is 'CrystalVial') ) e.Replacement = 'TetraHealthItem'; - else if ( (e.Replacee is 'Medikit') || (e.Replacee is 'ArtiHealth') ) e.Replacement = 'CubeHealthItem'; - else if ( (e.Replacee is 'Soulsphere') || (e.Replacee is 'ArtiSuperHealth') ) e.Replacement = 'RefresherItem'; - else if ( (e.Replacee is 'Megasphere') || (e.Replacee is 'ArtiEgg') || (e.Replacee is 'ArtiBoostArmor') ) e.Replacement = 'GrilledCheeseSandwich'; - else if ( (e.Replacee is 'Blursphere') || (e.Replacee is 'ArtiInvisibility') ) e.Replacement = 'GhostArtifact'; - else if ( e.Replacee is 'Radsuit' ) e.Replacement = 'EBarrier'; - else if ( (e.Replacee is 'ArtiFly') ) e.Replacement = 'GravitySuppressor'; - else if ( (e.Replacee is 'InvulnerabilitySphere') || (e.Replacee is 'ArtiInvulnerability') || (e.Replacee is 'ArtiInvulnerability2') ) e.Replacement = 'FuckingInvinciball'; - else if ( (e.Replacee is 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'Ragekit'; - else if ( (e.Replacee is 'AllMap') || (e.Replacee is 'SuperMap') ) e.Replacement = 'Omnisight'; - else if ( (e.Replacee is 'Infrared') || (e.Replacee is 'ArtiTorch') ) e.Replacement = 'SWWMLamp'; - else if ( (e.Replacee is 'GreenArmor') || (e.Replacee is 'SilverShield') || (e.Replacee is 'PlatinumHelm') || (e.Replacee is 'AmuletOfWarding') ) e.Replacement = 'BlastSuitItem'; - else if ( (e.Replacee is 'BlueArmor') || (e.Replacee is 'EnchantedShield') || (e.Replacee is 'MeshArmor') || (e.Replacee is 'FalconShield') ) e.Replacement = 'WarArmorItem'; - else if ( (e.Replacee is 'ArtiDarkServant') || (e.Replacee == 'ArtiTeleportOther') ) e.Replacement = 'ChanceboxSpawner'; - else if ( e.Replacee is 'ArtiTeleport' ) e.Replacement = (gameinfo.GameType&GAME_Hexen)?'ChanceboxSpawner':'SWWMNothing'; - else return; - // this last part is kind of ugly, but it works - // guarantees that OUR replacements are all final - e.IsFinal = true; - } - - override void ConsoleProcess( ConsoleEvent e ) - { - // doing it with an event because this way we can control WHEN it should be openable - if ( e.Name ~== "swwmdemomenu" ) - { - if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') ) - return; - Menu.SetMenu('DemolitionistMenu'); - } - else if ( e.Name ~== "swwmzoomin" ) - { - if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') ) - return; - double val = max(.5,swwm_mm_zoom/2.); - CVar.FindCVar('swwm_mm_zoom').SetFloat(val); - } - else if ( e.Name ~== "swwmzoomout" ) - { - if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') ) - return; - double maxval = players[consoleplayer].mo.FindInventory("Omnisight")?2.:1.; - double val = min(maxval,swwm_mm_zoom*2.); - CVar.FindCVar('swwm_mm_zoom').SetFloat(val); - } - } - - override void NetworkProcess( ConsoleEvent e ) - { - static const Class cbttypes[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; - if ( e.Name ~== "swwmgesture" ) - { - if ( (e.player == -1) || !playeringame[e.player] || !players[e.player].mo ) return; - let mo = players[e.player].mo; - switch( e.Args[0] ) - { - case 0: - SWWMGesture.SetGesture(mo,GS_Wave); - break; - case 1: - SWWMGesture.SetGesture(mo,GS_ThumbsUp); - break; - case 2: - SWWMGesture.SetGesture(mo,GS_Victory); - break; - case 3: - SWWMGesture.SetGesture(mo,GS_BlowKiss); - break; - } - return; - } - else if ( e.Name ~== "swwmfixitemcaps" ) - { - // this command is only really needed when I update item max amounts mid-playthrough - if ( multiplayer && (e.player != Net_Arbitrator) ) - { - if ( e.player == consoleplayer ) - Console.Printf("Only the net arbitrator can call this event."); - return; - } - for ( int i=0; i %d)",hams.GetTag(),hams.MaxAmount,hams.default.MaxAmount); - hams.MaxAmount = hams.default.MaxAmount; - hams.Amount = min(hams.Amount,hams.MaxAmount); - } - for ( Inventory inv=mo.inv; inv; inv=inv.inv ) - { - if ( inv is 'Ammo' ) - { - Ammo(inv).BackpackMaxAmount = Ammo(inv).default.BackpackMaxAmount; - int newmax = inv.default.MaxAmount; - if ( (hams.Amount > 0) && (Ammo(inv).BackpackMaxAmount > 0) ) - { - double factor = (Ammo(inv).BackpackMaxAmount-inv.default.MaxAmount)/double(hams.MaxAmount); - newmax = int(inv.default.MaxAmount+hams.Amount*factor); - } - if ( inv.MaxAmount != newmax ) - Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,newmax); - int dropme = max(0,inv.Amount-newmax); - if ( dropme ) - { - Console.Printf("Dropped %dx %s.",dropme,inv.GetTag()); - // non-SWWM ammos won't subdivide, but whatever, this is a debug command - inv.CreateTossable(dropme); - } - inv.MaxAmount = newmax; - } - else if ( inv is 'MagAmmo' ) - { - if ( inv.MaxAmount != inv.default.MaxAmount ) - Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,inv.default.MaxAmount); - // just drop the extras - int dropme = max(0,inv.Amount-inv.default.MaxAmount); - if ( dropme ) - { - Console.Printf("Dropped %dx %s.",dropme,inv.GetTag()); - inv.CreateTossable(dropme); - } - inv.MaxAmount = inv.default.MaxAmount; - } - else - { - if ( inv.MaxAmount != inv.default.MaxAmount ) - Console.Printf("Adjust %s capacity (%d -> %d)",inv.GetTag(),inv.MaxAmount,inv.default.MaxAmount); - // only drop droppables (obviously) - if ( !inv.bUNDROPPABLE && !inv.bUNTOSSABLE ) - { - int dropme = max(0,inv.Amount-inv.default.MaxAmount); - if ( dropme ) - { - Console.Printf("Dropped %dx %s.",dropme,inv.GetTag()); - for ( int j=0; j item = e.Name.Mid(14); - if ( !item ) return; - if ( SWWMCredits.Take(players[e.Args[0]],e.Args[1]) ) - { - let def = GetDefaultByType(item); - SWWMWeapon sw; - // drop the swapweapon if we own it first - if ( swwm_swapweapons && (item is 'SWWMWeapon') && (sw = SWWMWeapon(def).HasSwapWeapon(players[e.Args[0]].mo)) ) - { - bool swapto = (sw == players[e.Args[0]].ReadyWeapon) || (sw.SisterWeapon && (sw.Sisterweapon == players[e.Args[0]].ReadyWeapon)); - int ngun = sw.Amount; - double ang = -15*(ngun-1); - for ( int i=0; i 1) ) - players[e.Args[0]].mo.A_SelectWeapon("DualExplodiumGun"); - else players[e.Args[0]].mo.A_SelectWeapon((Class)(item)); - } - } - } - else if ( e.Name.Left(14) ~== "swwmstoretake." ) - { - Class item = e.Name.Mid(14); - if ( !item ) return; - int amt = e.Args[2]; - if ( item is 'CandyGun' ) - { - // check if we can sell a spare instead, for the same price - int n = players[e.Args[0]].mo.CountInv('CandyGunSpares'); - if ( n >= amt ) - { - players[e.Args[0]].mo.TakeInventory('CandyGunSpares',amt); - SWWMCredits.Give(players[e.Args[0]],e.Args[1]); - return; - } - } - // if player currently has the dual wield weapon selected, switch over - if ( item is 'SWWMWeapon' ) - { - let c = Weapon(players[e.Args[0]].mo.FindInventory(item)); - if ( c.SisterWeapon && (players[e.Args[0]].ReadyWeapon == c.SisterWeapon) ) - { - players[e.Args[0]].ReadyWeapon = c; - players[e.Args[0]].SetPSprite(PSP_WEAPON,c.FindState("Ready")); - players[e.Args[0]].SetPSprite(PSP_WEAPON+1,null); // delete left weapon psprite - } - } - // if we're selling an embiggener, we need to readjust ammo - if ( item is 'HammerspaceEmbiggener' ) - { - let ritm = players[e.Args[0]].mo.FindInventory(item); - for ( Inventory i=players[e.Args[0]].mo.Inv; i; i=i.Inv ) - { - if ( !(i is 'Ammo') ) continue; - if ( Ammo(i).BackpackMaxAmount > 0 ) - { - double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(ritm.MaxAmount); - i.MaxAmount = int(i.default.MaxAmount+(ritm.Amount-amt)*factor); - } - // drop excess ammo - int excess = i.Amount-i.MaxAmount; - if ( excess > 0 ) i.CreateTossable(excess); - } - } - players[e.Args[0]].mo.TakeInventory(item,amt); - SWWMCredits.Give(players[e.Args[0]],e.Args[1]); - } - else if ( e.Name.Left(10) ~== "swwmtrade." ) - { - Class item = e.Name.Mid(10); - if ( !item ) return; - let def = GetDefaultByType(item); - int amt = def.Amount; - // if it's an ammo, check the largest unit givable - if ( item is 'Ammo' ) - { - for ( int i=0; i)(AllActorClasses[i]); - if ( !a || (a.GetParentClass() != item) || (GetDefaultByType(a).Amount < amt) ) continue; - amt = GetDefaultByType(a).Amount; - } - } - Inventory ritm = players[e.Args[1]].mo.FindInventory(item); - if ( ritm ) - { - int maxgive = ritm.MaxAmount-ritm.Amount; - if ( amt > maxgive ) amt = maxgive; - } - else if ( amt > def.MaxAmount ) amt = def.MaxAmount; - bool rslt = false; - Class giveitem = item; - if ( item is 'HammerspaceEmbiggener' ) giveitem = 'TradedHammerspaceEmbiggener'; - if ( (amt > 0) && players[e.Args[1]].mo.GiveInventory(giveitem,amt,true) ) - { - // if player currently has the dual wield weapon selected, switch over - if ( item is 'SWWMWeapon' ) - { - let c = Weapon(players[e.Args[0]].mo.FindInventory(item)); - if ( c.SisterWeapon && (players[e.Args[0]].ReadyWeapon == c.SisterWeapon) ) - { - players[e.Args[0]].ReadyWeapon = c; - players[e.Args[0]].SetPSprite(PSP_WEAPON,c.FindState("Ready")); - players[e.Args[0]].SetPSprite(PSP_WEAPON+1,null); // delete left weapon psprite - } - } - // if we're trading an embiggener, we need to readjust ammo - if ( item is 'HammerspaceEmbiggener' ) - { - let ritm = players[e.Args[0]].mo.FindInventory(item); - for ( Inventory i=players[e.Args[0]].mo.Inv; i; i=i.Inv ) - { - if ( !(i is 'Ammo') ) continue; - if ( Ammo(i).BackpackMaxAmount > 0 ) - { - double factor = (Ammo(i).BackpackMaxAmount-i.default.MaxAmount)/double(ritm.MaxAmount); - i.MaxAmount = int(i.default.MaxAmount+(ritm.Amount-amt)*factor); - } - // drop excess ammo - int excess = i.Amount-i.MaxAmount; - if ( excess > 0 ) i.CreateTossable(excess); - } - } - if ( item is 'CandyGun' ) - { - // see if we can take a fully loaded spare from us instead - int n = players[e.Args[0]].mo.CountInv('CandyGunSpares'); - int na = players[e.Args[0]].mo.CountInv('CandyGunAmmo'); - if ( (n >= amt) && (na >= amt) ) - { - players[e.Args[0]].mo.TakeInventory('CandyGunSpares',amt); - players[e.Args[0]].mo.TakeInventory('CandyGunAmmo',amt); - } - else players[e.Args[0]].mo.TakeInventory('CandyGun',amt); - } - else players[e.Args[0]].mo.TakeInventory(item,amt); - // add to history - SWWMTradeHistory.RegisterSend(players[e.Args[0]],players[e.Args[1]],item,amt); - SWWMTradeHistory.RegisterReceive(players[e.Args[1]],players[e.Args[0]],item,amt); - // add messages - if ( e.Args[0] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGSENT"),amt,def.GetTag(),players[e.Args[1]].GetUserName()); - if ( e.Args[1] == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_MSGRECV"),players[e.Args[0]].GetUserName(),amt,def.GetTag()); - rslt = true; - } - if ( e.Args[0] == consoleplayer ) - { - let t = new("MenuTransaction"); - t.uid = e.Args[2]; - t.type = MenuTransaction.TT_ITEMSEND; - t.result = rslt; - t.used = item; - t.usedup = (players[e.Args[1]].mo.CountInv(item)<=0); - checklist.Push(t); - } - } - else if ( e.Name.Left(17) ~== "swwmmarkloreread." ) - { - let l = SWWMLoreLibrary.Find(players[e.Args[0]]); - let idx = l.FindEntry(e.Name.Mid(17)); - l.MarkRead(idx); - } - else if ( e.Name.Left(12) ~== "swwmuseitem." ) - { - Class item = e.Name.Mid(12); - if ( !item ) return; - let i = players[e.Args[0]].mo.FindInventory(item); - if ( !i ) return; - bool rslt = players[e.Args[0]].mo.UseInventory(i); - if ( e.Args[0] == consoleplayer ) - { - let t = new("MenuTransaction"); - t.uid = e.Args[1]; - t.type = MenuTransaction.TT_ITEMUSE; - let w = (Class)(item); - if ( w ) - { - t.result = (players[e.Args[0]].PendingWeapon==Weapon(i)); - // dual wield gun support - if ( (i is 'ExplodiumGun') && (players[e.Args[0]].PendingWeapon==Weapon(i).SisterWeapon) ) - t.result = true; - } - else t.result = rslt; - t.used = item; - t.usedup = (!i||(i.Amount<=0)); - checklist.Push(t); - } - } - else if ( e.Name.Left(13) ~== "swwmdropitem." ) - { - Class item = e.Name.Mid(13); - if ( !item ) return; - let i = players[e.Args[0]].mo.FindInventory(item); - if ( !i ) return; - int amt = i.default.Amount; - // if it's an ammo, check the largest unit givable - if ( i is 'Ammo' ) - { - for ( int i=0; i)(AllActorClasses[i]); - if ( !a || (a.GetParentClass() != item) || (GetDefaultByType(a).Amount < amt) ) continue; - amt = GetDefaultByType(a).Amount; - } - } - if ( amt > i.Amount ) amt = i.Amount; - let drop = players[e.Args[0]].mo.DropInventory(i,amt); - // add some random velocity so multiple drops don't get bunched together - if ( drop ) drop.vel += (Actor.RotateVector((FRandom[Junk](-1.5,.5),FRandom[Junk](-2.5,2.5)),players[e.Args[0]].mo.angle),FRandom[Junk](2.,5.)); - if ( e.Args[0] == consoleplayer ) - { - let t = new("MenuTransaction"); - t.uid = e.Args[1]; - t.type = MenuTransaction.TT_ITEMDROP; - t.used = item; - t.result = drop; - t.usedup = (!i||(i.Amount<=0)); - checklist.Push(t); - } - } - else if ( e.Name ~== "swwmkoraxline" ) - { - if ( consoleplayer != e.Args[1] ) return; - switch ( e.Args[0] ) - { - case 0: - AddOneliner("koraxgreet",3,60); - break; - case 1: - AddOneliner("koraxblood",3,150); - break; - case 2: - AddOneliner("koraxgame",3,120); - break; - case 3: - AddOneliner("koraxworship",3,80); - break; - case 4: - AddOneliner("koraxmasters",3,90); - break; - } - } - else if ( e.Name.Left(16) ~== "swwmremoteliner." ) - { - if ( consoleplayer == e.Args[0] ) return; - if ( !swwm_othervoice ) return; - if ( swwm_mutevoice >= e.Args[1] ) return; - players[e.Args[0]].mo.A_StartSound(e.Name.Mid(16),CHAN_DEMOVOICE,attenuation:.5); - } - else if ( e.Name.Left(19) ~== "swwmremotelinertxt." ) - { - if ( consoleplayer == e.Args[0] ) return; - if ( !swwm_othervoice ) return; - if ( swwm_mutevoice >= e.Args[1] ) return; - double dist = players[consoleplayer].Camera.Distance3D(players[e.Args[0]].mo); - if ( dist < 2000 ) - Console.Printf("\cx%s\cx: %s\c-",players[e.Args[0]].GetUserName(),StringTable.Localize(e.Name.Mid(19))); - } - else if ( e.Name.Left(8) ~== "swwmcbt." ) - { - // from wikipedia, the free encyclopedia - if ( !playeringame[e.Args[0]] || !players[e.Args[0]].mo ) return; - let cbt = Wallbuster(players[e.Args[0]].mo.FindInventory("Wallbuster")); - if ( !cbt ) return; - cbt.reloadqueue.Clear(); - if ( e.Name.Mid(8) ~== "EMPTY" ) cbt.clearout = true; - else - { - cbt.clearout = false; - Array qs; - qs.Clear(); - String rite = e.Name.Mid(8); - rite.Split(qs,",",TOK_SKIPEMPTY); - for ( int i=0; i 3) ) continue; - cbt.reloadqueue.Push(cbttypes[qi]); - } - } - cbt.waitreload = false; - } - else if ( e.Name ~== "swwmcleartransaction" ) - { - if ( e.Args[1] != consoleplayer ) return; - for ( int i=0; i)(AllActorClasses[i]); - if ( !w || (w == 'SWWMWeapon') ) continue; - let def = GetDefaultByType(w); - if ( def.bCHEATNOTWEAPON ) continue; - let ow = players[e.Args[0]].mo.FindInventory(w); - if ( ow && (ow.Amount >= ow.MaxAmount) ) continue; - if ( ow ) ow.Amount = ow.MaxAmount; - else players[e.Args[0]].mo.GiveInventory(w,def.MaxAmount); - } - } - else if ( e.Name ~== "swwmhealcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyRemember to stay fit.\c-"); - S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("misc/health_pkup",CHAN_VOICE,CHANF_UI); - } - players[e.Args[0]].health = players[e.Args[0]].mo.health = 1000; - } - else if ( e.Name ~== "swwmynykroncheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyYou're still crazy.\c-"); - S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("misc/w_pkup",CHAN_VOICE,CHANF_UI); - } - if ( players[e.Args[0]].mo.FindInventory("Ynykron") ) - players[e.Args[0]].mo.GiveInventory("YnykronAmmo",1); - else players[e.Args[0]].mo.GiveInventory("Ynykron",1); - } - else if ( e.Name ~== "swwmgravcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyGot something floatier.\c-"); - S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI); - } - let g = GravityPower(players[e.Args[0]].mo.FindInventory("GravityPower")); - if ( g ) g.EffectTics += g.default.EffectTics; - else players[e.Args[0]].mo.GiveInventory("GravityPower",1); - } - else if ( e.Name ~== "swwminvischeat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyProbably because you're invisible.\c-"); - S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI); - } - let g = GhostPower(players[e.Args[0]].mo.FindInventory("GhostPower")); - if ( g ) g.EffectTics += g.default.EffectTics; - else players[e.Args[0]].mo.GiveInventory("GhostPower",1); - } - else if ( e.Name ~== "swwmbarriercheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cySafe from those pesky elements.\c-"); - S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("misc/p_pkup",CHAN_VOICE,CHANF_UI); - } - let b = BarrierPower(players[e.Args[0]].mo.FindInventory("BarrierPower")); - if ( b ) b.EffectTics += b.default.EffectTics; - else players[e.Args[0]].mo.GiveInventory("BarrierPower",1); - } - else if ( e.Name ~== "swwmammocheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyDon't squander it.\c-"); - S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("misc/ammo_pkup",CHAN_VOICE,CHANF_UI); - } - players[e.Args[0]].mo.GiveInventory("HammerspaceEmbiggener",8,true); - for ( Inventory i=players[e.Args[0]].mo.inv; i; i=i.inv ) - { - if ( !(i is 'Ammo') ) continue; - i.Amount = i.MaxAmount; - } - } - else if ( e.Name ~== "swwmbloodcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyEdgy...\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmexplocheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyThat cheat's not needed anymore.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmallcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyStill as wrappy as it's always been.\c-"); - S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("fabricator/use",CHAN_VOICE,CHANF_UI); - } - players[e.Args[0]].mo.CheatGive("all",0); - players[e.Args[0]].health = players[e.Args[0]].mo.health = 1000; - } - else if ( e.Name ~== "swwmflagcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyThere are no flags here.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmballcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cy\"Balls on your head\"? What was I even thinking...\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmsmartcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cySkittles are better anyway.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmnutcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyI'm way past that, it was bad for my health.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmweeniecheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyAlways has been.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmpunishcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyThis is a bulli free zone.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmball2cheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cy\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmfartcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyI'd rather not reimplement that one.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmsupercheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyNo, you're the Demolitionist.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmstonecheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyThe pinnacle of... wait, I misread that.\c-"); - S_StartSound("misc/nocheat",CHAN_ITEM,CHANF_UI); - } - } - else if ( e.Name ~== "swwmfroggycheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cdHop!\c-"); - S_StartSound("misc/buyinv",CHAN_ITEM,CHANF_UI); - } - let mo = players[e.Args[0]].mo; - Actor f = Actor(ThinkerIterator.Create("FroggyChair").Next()); - if ( !f ) f = mo.Spawn("FroggyChair"); - f.SetOrigin(mo.Vec2OffsetZ(cos(mo.angle)*40.,sin(mo.angle)*40.,mo.player.viewz-32.),false); - f.A_SetAngle(f.AngleTo(mo)); - f.Spawn("SWWMItemFog",f.pos); - f.A_StartSound("bestsound",CHAN_ITEMEXTRA); - } - else if ( e.Name ~== "swwmamnesiacheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyAmnesiacs administered.\c-"); - S_StartSound("misc/buyinv",CHAN_ITEM,CHANF_UI); - S_StartSound("bestsound",CHAN_VOICE,CHANF_UI); - } - let ti = ThinkerIterator.Create("Actor"); - Actor a; - while ( a = Actor(ti.Next()) ) - { - if ( !a.bIsMonster || a.player ) continue; - a.A_ClearTarget(); - } - } - else if ( e.Name ~== "swwmjanitorcheat" ) - { - if ( consoleplayer == e.Args[0] ) - { - Console.Printf("\cyLet's mop up that big mess over there.\c-"); - S_StartSound("misc/buyinv",CHAN_ITEM,CHANF_UI); - } - let cc = SWWMCorpseCleaner(ThinkerIterator.Create("SWWMCorpseCleaner",Thinker.STAT_USER).Next()); - if ( !cc ) - { - cc = new("SWWMCorpseCleaner"); - cc.ChangeStatNum(Thinker.STAT_USER); - cc.Init(players[e.Args[0]].mo); - } - else cc.Init(players[e.Args[0]].mo); - } - } - // stuff for hud override void RenderUnderlay( RenderEvent e ) { // armor/health flashes - int camplayer = players[consoleplayer].Camera.PlayerNumber(); - if ( camplayer != -1 ) - { - if ( gametic < hflash[camplayer] ) - { - double fstr = (hflash[camplayer]-(gametic+e.FracTic))/5.; - Screen.Dim(Color(64,128,255),.1875*fstr,0,0,Screen.GetWidth(),Screen.GetHeight()); - } - if ( gametic < aflash[camplayer] ) - { - double fstr = (aflash[camplayer]-(gametic+e.FracTic))/5.; - Screen.Dim(Color(96,255,64),.1875*fstr,0,0,Screen.GetWidth(),Screen.GetHeight()); - } - } - // weapon underlays - if ( players[consoleplayer].ReadyWeapon is 'SWWMWeapon' ) - SWWMWeapon(players[consoleplayer].ReadyWeapon).RenderUnderlay(e); - if ( !statusbar || !(statusbar is 'SWWMStatusBar') ) return; - SWWMStatusBar(statusbar).viewpos = e.viewpos; - SWWMStatusBar(statusbar).viewrot = (e.viewangle,e.viewpitch,e.viewroll); + FlashRender(e); if ( slotstrictwarn && (gametic < slotstrictwarn) ) { String str = StringTable.Localize("$SWWM_SETSLOTSTRICT"); @@ -4099,229 +1353,18 @@ Class SWWMHandler : EventHandler yy += newsmallfont.GetHeight(); } } - } - - // called by HUD (done here for the sake of cleaner code) - ui void DrawBossBar( SWWMStatusBar bar ) - { - if ( !ui_initialized || (bossalpha <= 0.) ) return; - if ( !swwm_bosshealthbars ) return; - if ( !bbar_f ) bbar_f = TexMan.CheckForTexture("graphics/HUD/BossHealthBarBox.png",TexMan.Type_Any); - if ( !bbar_r ) bbar_r = TexMan.CheckForTexture("graphics/HUD/BossHealthBar.png",TexMan.Type_Any); - if ( !bbar_d ) bbar_d = TexMan.CheckForTexture("graphics/HUD/BossHealthBarDecay.png",TexMan.Type_Any); - Vector2 vpos = ((bar.ss.x-300)/2.,bar.ss.y-(bar.margin+35)); - Screen.DrawTexture(bbar_f,false,vpos.x-2,vpos.y-2,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha); - int rw = int(clamp((ihealthr.GetValue()*300.)/hmax,0.,300.)); - int dw = int(clamp((ihealth.GetValue()*300.)/hmax,0.,300.)); - Screen.DrawTexture(bbar_d,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,dw); - Screen.DrawTexture(bbar_r,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,rw); - Font barfnt = bar.LangFont(bar.mTewiFont); - Font dmgfnt = bar.mTewiFont.mFont; - if ( (cummdamage > 0) && (gametic < lastcummtic+150) ) - { - double calph = clamp(((lastcummtic+150)-gametic)/50.,0.,1.); - string dnum = String.Format("%d",cummdamage); - Screen.DrawText(dmgfnt,Font.CR_RED,vpos.x+300-dmgfnt.StringWidth(dnum),vpos.y-(dmgfnt.GetHeight()+2),dnum,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha*calph); - } - Screen.DrawText(barfnt,Font.CR_WHITE,vpos.x,vpos.y-(barfnt.GetHeight()+2),StringTable.Localize(swwm_funtags?(bosstag.."_FUN"):bosstag),DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha); - } - - private ui int GetUIRandom() - { - return (rss = (rss<<1)*35447+(rss/87)); - } - - private ui double RandomShiver() - { - int sd = GetUIRandom(); - return ((abs(sd)%11)-5)*.1; - } - - private ui int RandomFall() - { - int sd = GetUIRandom(); - return ((abs(sd)%22)+10); + // weapon underlays + if ( players[consoleplayer].ReadyWeapon is 'SWWMWeapon' ) + SWWMWeapon(players[consoleplayer].ReadyWeapon).RenderUnderlay(e); + if ( !statusbar || !(statusbar is 'SWWMStatusBar') ) return; + SWWMStatusBar(statusbar).viewpos = e.viewpos; + SWWMStatusBar(statusbar).viewrot = (e.viewangle,e.viewpitch,e.viewroll); } // various shaders override void RenderOverlay( RenderEvent e ) { - // cheat input - if ( (kcode > 4) || ((klinger > gametic) && (klingerstr != "")) ) - { - double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/266.)),1.); - Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; - String chstr = (kcode>4)?kstr.Mid(4):klingerstr.Mid(4); - double alph = clamp((klinger-(gametic+e.fractic))/20.,0.,1.); - double shine = clamp((klinger-(gametic+e.fractic+40))/20.,0.,1.); - int col = (kcode>4)?0:(kfail)?2:1; - int tlen = chstr.CodePointCount(); - let fnt = newsmallfont; - int width = fnt.StringWidth(chstr)+(tlen-1); - int xx = int((ss.x-width)/2.); - int yy = int((ss.y-newsmallfont.GetHeight())/2.); - rss = (kcode>4)?gametic:klinger; - for ( int i=0, pos=0; i0)?demo.lastdamage:Random[Flicker](60,80); - int lastdmgtic = (demo.Health>0)?demo.lastdamagetic:(gametic+Random[Flicker](30,20)); - double noiz = min(lastdmg*.09*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.5); - Shader.SetEnabled(p,"Glitch",noiz>0); - Shader.SetEnabled(p,"Grain",noiz>0); - if ( noiz > 0 ) - { - Shader.SetUniform1f(p,"Glitch","Timer",(gametic+e.FracTic)/GameTicRate); - Shader.SetUniform1f(p,"Grain","Timer",(gametic+e.FracTic)/GameTicRate); - Shader.SetUniform1f(p,"Grain","ni",noiz); - noiz = min(lastdmg*.08*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),.8); - Shader.SetUniform1f(p,"Glitch","str1",noiz); - noiz = min(lastdmg*.03*max(0,(lastdmgtic-(gametic+e.Fractic))/35.),3.5); - Shader.SetUniform1f(p,"Glitch","str2",noiz); - } - if ( !demo.InStateSequence(demo.CurState,demo.FindState("Dash")) ) - { - Shader.SetEnabled(p,"ZoomBlur",false); - return; - } - Shader.SetEnabled(p,"ZoomBlur",true); - Vector3 vel = demo.vel+demo.dashdir*demo.dashboost; - double baumpu = max(0.,(demo.bumptic-(gametic+e.Fractic))/35.); - vel += demo.dashdir*baumpu; - double spd = vel.length(); - Vector3 worlddir = vel/spd; - Shader.SetUniform1f(p,"ZoomBlur","Fade",clamp((spd-20.)/60.,0.,1.)); - double str = min(spd/40.,15.); - Vector3 x, y, z; - [x, y, z] = swwm_CoordUtil.GetAxes(e.ViewPitch,e.ViewAngle,e.ViewRoll); - Vector3 reldir = (worlddir dot y, worlddir dot z, worlddir dot x); - Vector2 centerspot = (.5+reldir.x*.5,.5+reldir.y*.5); - if ( reldir.z < 0 ) - { - centerspot.x = 1.-centerspot.x; - centerspot.y = 1.-centerspot.y; - str *= -1; - } - Shader.SetUniform1f(p,"ZoomBlur","Str",str); - Shader.SetUniform2f(p,"ZoomBlur","CenterSpot",centerspot); - } - else - { - Shader.SetEnabled(p,"WaterWarp",false); - Shader.SetEnabled(p,"LavaWarp",false); - Shader.SetEnabled(p,"SlimeWarp",false); - Shader.SetEnabled(p,"Glitch",false); - Shader.SetEnabled(p,"Grain",false); - Shader.SetEnabled(p,"ZoomBlur",false); - } + CheatOverlay(e); + RenderShaders(e); } - - static void DoFlash( Actor camera, Color c, int duration ) - { - // don't flash when paused - if ( menuactive && (menuactive != Menu.OnNoPause) ) return; - QueuedFlash qf = new("QueuedFlash"); - qf.duration = duration; - qf.c = c; - qf.tic = gametic; - qf.cam = camera; - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return; // not supposed to happen - hnd.flashes.push(qf); - } - - // can't use this until I actually figure out how to make those walls damageable - /*override void WorldLineDamaged( WorldEvent e ) - { - // allow boss brain to take (reduced) damage from the facewall being shot - if ( level.mapname ~== "MAP30" ) - { - if ( !SWWMUtility.IsIOSWall(e.DamageLine) ) return; - if ( bossbrainactor ) - bossbrainactor.DamageMobj(e.Inflictor,e.DamageSource,e.Damage/3,e.DamageType,e.DamageFlags,e.DamageAngle); - e.NewDamage = 0; - } - }*/ } diff --git a/zscript/swwm_inventory.zsc b/zscript/swwm_inventory.zsc deleted file mode 100644 index be92756f7..000000000 --- a/zscript/swwm_inventory.zsc +++ /dev/null @@ -1,1684 +0,0 @@ -// Inventory stuff -Mixin Class SWWMAutoUseFix -{ - override bool HandlePickup( Inventory item ) - { - if ( GetClass() == item.GetClass() ) - { - if ( Use(true) ) Amount--; - // sell excess if there's a price - if ( bALWAYSPICKUP && (Amount+item.Amount > MaxAmount) && (Stamina != 0) ) - { - int sellprice = int(abs(Stamina)*.5); - SWWMScoreObj.Spawn(sellprice,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2)); - SWWMCredits.Give(Owner.player,sellprice); - if ( Owner.player ) - Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),Owner.player.GetUserName(),GetTag(),sellprice); - } - } - return Super.HandlePickup(item); - } -} - -Class CrossLineFinder : LineTracer -{ - Array clines; - Array csides; - - override ETraceStatus TraceCallback() - { - if ( (Results.HitType == TRACE_HitWall) && (Results.HitLine.activation&SPAC_Cross) ) - { - clines.Push(Results.HitLine); - csides.Push(Results.Side); - } - return TRACE_Skip; - } -} - -Mixin Class SWWMUseToPickup -{ - // allow pickup by use - override bool Used( Actor user ) - { - Vector3 itempos = Vec3Offset(0,0,Height/2), - userpos = user.Vec2OffsetZ(0,0,user.player.viewz); - // test vertical range - Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); - double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); - if ( abs(diff.z) > rang ) return false; - Touch(user); - // we got picked up - if ( bDestroyed || Owner || !bSPECIAL ) - { - Vector3 tracedir = level.Vec3Diff(userpos,itempos); - double dist = tracedir.length(); - tracedir /= dist; - let cf = new("CrossLineFinder"); - cf.Trace(userpos,level.PointInSector(userpos.xy),tracedir,dist,0); - // trigger all player cross lines found between user and item - for ( int i=0; i parent; - - Property ArmorPriority : priority; - Property DrainMessage : drainmsg; - Property GiverArmor : parent; - - Default - { - +INVENTORY.AUTOACTIVATE; - +INVENTORY.UNTOSSABLE; - +INVENTORY.UNDROPPABLE; - +INVENTORY.KEEPDEPLETED; - +INVENTORY.ALWAYSPICKUP; - } - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - // find last armor that's better than us - Inventory found = null; - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( !(i is 'SWWMArmor') || (i == self) || (SWWMArmor(i).priority < priority) ) continue; - found = i; - } - if ( !found ) - { - // find first item with an armor worse than us after it - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( (i == self) || !(i.Inv is 'SWWMArmor') ) continue; - if ( SWWMArmor(i.Inv).priority > priority ) continue; - found = i; - break; - } - } - if ( !found ) - { - // is first item plating or a collar? - if ( (other.Inv is 'AlmasteelPlating') || (other.Inv is 'SayaCollar') ) - { - // we're good - return; - } - // find first item with plating or collar after it - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( (i == self) || (!(i.Inv is 'AlmasteelPlating' ) && !(i.Inv is 'SayaCollar')) ) continue; - found = i; - break; - } - } - if ( !found ) - { - // check if first item in inventory is health or a sandwich - if ( (other.Inv is 'SWWMHealth') || (other.Inv is 'GrilledCheeseSandwich') ) - { - // we're good - return; - } - // find first item with health or sandwich after it - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( (i == self) || (!(i.Inv is 'SWWMHealth' ) && !(i.Inv is 'GrilledCheeseSandwich')) ) continue; - found = i; - break; - } - } - if ( !found ) - { - // find last of either invinciball/ragekit/barrier power - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( !(i is 'InvinciballPower') && !(i is 'RagekitPower') && !(i is 'BarrierPower') ) continue; - found = i; - } - } - if ( !found ) return; - // place ourselves right after it - Inventory saved = found.Inv; - found.Inv = self; - other.Inv = Inv; - Inv = saved; - } - // for subclasses - virtual int HandleDamage( int damage, Name damageType, int flags ) - { - return damage; - } - override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) - { - int saved; - if ( (amount <= 0) || DamageTypeDefinition.IgnoreArmor(damageType) || (damage <= 0) ) - return; - SWWMHandler.DoFlash(Owner,Color(int(clamp(damage*.15,1,16)),255,224,192),3); - Owner.A_StartSound("armor/hit",CHAN_BODY,CHANF_DEFAULT,clamp(damage*.03,0.,1.),2.5); - saved = HandleDamage(damage,damageType,flags); - int healed = max(0,saved-damage); - saved = min(saved,damage); - if ( amount <= saved ) saved = amount; - newdamage -= saved; - if ( healed > 0 ) Owner.GiveBody(healed); - if ( (swwm_strictuntouchable == 1) && (saved > 0) && Owner.player ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( hnd ) hnd.tookdamage[Owner.PlayerNumber()] = true; - } - amount -= saved; - damage = newdamage; - bool shouldautouse = false; - if ( swwm_enforceautousearmor == 1 ) shouldautouse = true; - else if ( swwm_enforceautousearmor == -1 ) shouldautouse = false; - else shouldautouse = CVar.GetCVar('swwm_autousearmor',Owner.player).GetBool(); - if ( (amount <= (MaxAmount-default.Amount)) && (Owner.CountInv(parent) > 0) && shouldautouse ) - { - if ( GetDefaultByType(parent).UseSound ) Owner.A_StartSound(GetDefaultByType(parent).UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); - int tgive = 0; - while ( (amount <= (MaxAmount-default.Amount)) && (Owner.CountInv(parent) > 0) ) - { - if ( swwm_accdamage ) tgive += default.Amount; - else SWWMScoreObj.Spawn(default.Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor); - Amount += default.Amount; - Owner.TakeInventory(parent,1); - // absorb the extra damage too - saved = HandleDamage(damage,damageType,flags); - healed = max(0,saved-damage); - saved = min(saved,damage); - if ( amount <= saved ) saved = amount; - newdamage -= saved; - if ( healed > 0 ) Owner.GiveBody(healed); - amount -= saved; - damage = newdamage; - } - if ( swwm_accdamage ) SWWMScoreObj.Spawn(tgive,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor); - } - else if ( amount <= 0 ) - { - if ( Owner.CheckLocalView() && (drainmsg != "") ) Console.Printf(StringTable.Localize(drainmsg)); - DepleteOrDestroy(); - return; - } - } -} - -// gives armor when used -Class SWWMSpareArmor : Inventory abstract -{ - Mixin SWWMAutoUseFix; - Mixin SWWMUseToPickup; - Mixin SWWMOverlapPickupSound; - - Class giveme; - - Property GiveArmor : giveme; - - override bool Use( bool pickup ) - { - let cur = Owner.FindInventory(giveme); - if ( !cur || (!pickup && (cur.Amount < cur.MaxAmount)) || (GetDefaultByType(giveme).Amount+cur.Amount <= cur.MaxAmount) ) - { - if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); - Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); - SWWMHandler.ArmorFlash(Owner.PlayerNumber()); - SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor); - return true; - } - return false; - } - - Default - { - +INVENTORY.INVBAR; - +INVENTORY.ISARMOR; - +INVENTORY.AUTOACTIVATE; - Inventory.MaxAmount 5; - Inventory.InterHubAmount 5; - Inventory.PickupFlash "SWWMGreenPickupFlash"; - +FLOATBOB; - FloatBobStrength 0.25; - } -} - -Class SWWMHealth : Inventory abstract -{ - Mixin SWWMAutoUseFix; - Mixin SWWMUseToPickup; - Mixin SWWMOverlapPickupSound; - - // can't use the Health class for whatever reason - // nice parser you got there I guess? - Class giveme; - - Property GiveHealth : giveme; - - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - // find last health item that's better than us - Inventory found = null; - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( !(i is 'SWWMHealth') || (i == self) || (GetDefaultByType(SWWMHealth(i).giveme).Amount < GetDefaultByType(giveme).Amount) ) continue; - found = i; - } - if ( !found ) - { - // find first item with health that's worse than us after it - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( (i == self) || !(i.Inv is 'SWWMHealth') ) continue; - if ( GetDefaultByType(SWWMHealth(i.Inv).giveme).Amount > GetDefaultByType(giveme).Amount ) continue; - found = i; - break; - } - } - if ( !found ) - { - // find last armor item, plating or collar - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( !(i is 'SWWMArmor') && !(i is 'AlmasteelPlating') && !(i is 'SayaCollar') ) continue; - found = i; - } - } - if ( !found ) - { - // check if the first item in inventory is a sandwich - if ( other.Inv is 'GrilledCheeseSandwich' ) - { - // we're good - return; - } - // find first item next to a sandwich - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( (i == self) || !(i.Inv is 'GrilledCheeseSandwich') ) continue; - found = i; - break; - } - } - if ( !found ) - { - // find last of either invinciball/ragekit/barrier power - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( !(i is 'InvinciballPower') && !(i is 'RagekitPower') && !(i is 'BarrierPower') ) continue; - found = i; - } - } - if ( !found ) return; - // place ourselves right after it - Inventory saved = found.Inv; - found.Inv = self; - other.Inv = Inv; - Inv = saved; - } - - override bool Use( bool pickup ) - { - if ( Owner.Health >= GetDefaultByType(giveme).MaxAmount ) return false; - // healing items won't get auto-used on pickup if their healing could "be wasted", unless they're powerup health (e.g. Refresher) - if ( pickup && !bBIGPOWERUP && (Owner.Health+GetDefaultByType(giveme).Amount > GetDefaultByType(giveme).MaxAmount) ) return false; - if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); - SWWMHandler.HealthFlash(Owner.PlayerNumber()); - Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); - SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health); - AutoUseExtra(false); - return true; - } - - // additional effects when automatically used - // recursive: if true, the auto-use was called on another copy of the - // item in a "stacked" heal. can be used to prevent certain - // effects from happening multiple times in one go - virtual void AutoUseExtra( bool recursive ) - { - } - - override void DoEffect() - { - Super.DoEffect(); - if ( Amount <= 0 ) DepleteOrDestroy(); - } - - override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) - { - if ( Owner.ApplyDamageFactor(damageType,damage) <= 0 ) - return; // this damage type is ignored by the player, so it does not affect us - if ( damageType == 'EndLevel' ) - return; // don't trigger on endlevel damage - bool shouldautouse = false; - if ( swwm_enforceautousehealth == 1 ) shouldautouse = true; - else if ( swwm_enforceautousehealth == -1 ) shouldautouse = false; - else shouldautouse = CVar.GetCVar('swwm_autousehealth',Owner.player).GetBool(); - if ( !shouldautouse && !bBIGPOWERUP ) return; // powerup health is always auto-used - if ( (Owner.Health-damage <= (GetDefaultByType(giveme).MaxAmount-GetDefaultByType(giveme).Amount)) ) - { - newdamage = damage; - // lesser healing items can't prevent lethal damage - // bigger healing items only autoactivate on lethal damage - if ( !bBIGPOWERUP && (Owner.Health-damage <= 0) ) - return; - else if ( bBIGPOWERUP && (Owner.Health-damage > 0) ) - return; - if ( (swwm_strictuntouchable == 1) && Owner.player ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( hnd ) hnd.tookdamage[Owner.PlayerNumber()] = true; - } - if ( ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); - int tgive = 0; - bool morethanonce = false; - while ( (Amount > 0) && (newdamage > 0) ) - { - if ( swwm_accdamage ) tgive += GetDefaultByType(giveme).Amount; - else SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health); - newdamage = newdamage-GetDefaultByType(giveme).Amount; - if ( newdamage < 0 ) Owner.GiveBody(-newdamage,GetDefaultByType(giveme).MaxAmount); - newdamage = max(0,newdamage); - AutoUseExtra(morethanonce); - morethanonce = true; - Amount--; - } - if ( swwm_accdamage ) SWWMScoreObj.Spawn(tgive,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health); - } - else newdamage = damage; - } - - Default - { - +INVENTORY.INVBAR; - +INVENTORY.ISHEALTH; - +INVENTORY.AUTOACTIVATE; - Inventory.MaxAmount 5; - Inventory.InterHubAmount 5; - Inventory.UseSound "misc/health_pkup"; - Inventory.PickupFlash "SWWMBluePickupFlash"; - +FLOATBOB; - FloatBobStrength 0.25; - } -} - -// Base casing classes -Class SWWMCasing : Actor abstract -{ - SWWMCasing prevcasing, nextcasing; - bool killme; - int numbounces; - double pitchvel, anglevel; - double heat; - - Default - { - Radius 2; - Height 2; - +NOBLOCKMAP; - +MISSILE; - +DROPOFF; - +MOVEWITHSECTOR; - +THRUACTORS; - +USEBOUNCESTATE; - +INTERPOLATEANGLES; - +NOTELEPORT; - +ROLLSPRITE; - +ROLLCENTER; - Mass 1; - Gravity 0.35; - BounceType "Hexen"; - WallBounceFactor 0.65; - BounceFactor 0.65; - BounceSound "explodium/casing"; - FloatBobPhase 0; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - heat = 1.0; - SWWMHandler.QueueCasing(self); - } - override void OnDestroy() - { - SWWMHandler.DeQueueCasing(self); - Super.OnDestroy(); - } - override void Tick() - { - Super.Tick(); - if ( isFrozen() ) return; - if ( killme ) A_FadeOut(.01); - if ( waterlevel > 0 ) - { - vel.xy *= .98; - anglevel *= .98; - pitchvel *= .98; - } - if ( heat <= 0 ) return; - let s = Spawn("SWWMSmallSmoke",pos); - s.alpha *= heat; - heat -= 0.05; - } - States - { - Spawn: - XZW1 A 1 - { - angle += anglevel; - pitch += pitchvel; - } - Loop; - Bounce: - XZW1 A 0 - { - pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - vel = (vel.unit()+(FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2))).unit()*vel.length(); - if ( numbounces && ((numbounces > 3) || (Random[Junk](1,20) < 17) || (vel.z > -1.4)) ) - { - ClearBounce(); - ExplodeMissile(); - } - numbounces++; - } - Goto Spawn; - Death: - XZW1 B -1 - { - pitch = roll = 0; - angle = FRandom[Junk](0,360); - } - Stop; - } -} - -Class SWWMBulletImpact : Actor -{ - Default - { - RenderStyle "Add"; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOBLOCKMAP; - +DONTSPLASH; - +NOTELEPORT; - +NOINTERACTION; - Scale 0.25; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - A_SprayDecal("Pock",-20); - int numpt = int(Random[Junk](5,10)*scale.x*4); - Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); - for ( int i=0; i 2 ) Destroy(); - } -} - -Class PunchImpact : Actor -{ - Default - { - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +NOTELEPORT; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3); - A_StartSound("demolitionist/punch",CHAN_VOICE,CHANF_DEFAULT,bAMBUSH?.6:1.); - A_SprayDecal("WallCrack",-20); - int numpt = Random[Ponch](5,10); - if ( bAMBUSH ) numpt /= 3; - Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); - for ( int i=0; i 1 ) - { - s = Spawn("SWWMHalfSmoke",Owner.pos); - s.vel = Owner.vel*.3+(FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](.1,1.2); - s.scale *= 2.; - s.A_SetRenderStyle(s.alpha,STYLE_AddShaded); - s.SetShade(Color(4,2,1)*Random[Ponch](32,63)); - } - } - override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags ) - { - // increase blast damage (way too tiny normally for lost souls) - if ( Owner.bBLASTED && (damageType == 'Melee') && !inflictor && !source ) - { - newdamage = 20; - if ( special1&1 ) newdamage *= 2; - if ( special2 > 1 ) newdamage *= 8; - } - } -} - -// amplifies damage of parried projectiles -Class ParryDamageChecker : Inventory -{ - Default - { - +Inventory.UNDROPPABLE; - +Inventory.UNTOSSABLE; - +Inventory.UNCLEARABLE; - Inventory.Amount 1; - Inventory.MaxAmount 1; - } - - override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags ) - { - Inventory buff; - if ( inflictor && (buff=inflictor.FindInventory("ParriedBuff")) ) - { - double mult; - if ( buff.special1 <= 1 ) mult = 1.5; - else if ( buff.special1 >= 2 ) mult = 8.; - if ( buff.special1&1 ) mult *= 2.; - newdamage = int(damage*mult); - } - } -} - -Class ParryRing : Actor -{ - Default - { - RenderStyle "Add"; - Scale .1; - Alpha .3; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOBLOCKMAP; - +FORCEXYBILLBOARD; - +NOTELEPORT; - +NOINTERACTION; - } - States - { - Spawn: - XRG4 ABCDEFGHIJKLMNOPQRSTUVWX 1 A_SetScale(scale.x*(1+specialf1)); - Stop; - } -} - -Class ParryField : Actor -{ - bool critsnd; - - Default - { - Radius .1; - Height 0.; - +NOGRAVITY; - +NOCLIP; - +DONTSPLASH; - +NOTELEPORT; - +NOINTERACTION; - } - - override void Tick() - { - if ( !master ) - { - Destroy(); - return; - } - Vector3 x, y, z, origin; - [x, y, z] = swwm_CoordUtil.GetAxes(master.pitch,master.angle,master.roll); - origin = level.Vec3Offset(master.Vec2OffsetZ(0,0,master.player.viewz),x*20); - SetOrigin(origin,false); - // check for projectiles to deflect - let ti = ThinkerIterator.Create("Actor"); - Actor a; - while ( a = Actor(ti.Next()) ) - { - if ( !((a.bMISSILE && !a.IsZeroDamage() && (a.target != master)) || a.bSKULLFLY) || a.bTHRUACTORS || (level.Vec3Diff(a.pos,pos).length() > 80) ) continue; - Vector3 vdir = a.vel; - Vector3 dir = level.Vec3Diff(master.Vec2OffsetZ(0,0,pos.z),a.pos).unit(); - Vector3 hdir = dir; - if ( a.bMISSILE ) - { - // deflect directly to target - if ( a.target ) - { - hdir = level.Vec3Diff(a.pos,a.target.Vec3Offset(0,0,a.target.height/2)).unit(); - double theta = max(FRandom[Parry](0.,1.)**2.,.1); - dir = dir*(1.-theta)+hdir*theta; - } - // push away - if ( a.bSEEKERMISSILE ) a.tracer = a.target; - a.target = master; - } - if ( a.bSKULLFLY ) a.bBLASTED = true; // blast lost souls - a.GiveInventory("ParriedBuff",1); - let buff = a.FindInventory("ParriedBuff"); - double mvel = a.vel.length(); - double nspeed = min(100,mvel*FRandom[Parry](1.2,1.4)+20); - a.angle = atan2(dir.y,dir.x); - a.pitch = asin(-dir.z); - let raging = RagekitPower(master.FindInventory("RagekitPower")); - if ( raging ) - { - buff.special1 = 2; - nspeed = min(100,nspeed*2.); - raging.DoHitFX(); - } - a.vel = dir*nspeed; - if ( a.bMISSILE ) a.speed = nspeed; - let i = Spawn(raging?"BigPunchImpact":"PunchImpact",a.pos); - i.target = master; - i.angle = atan2(dir.y,dir.x); - i.pitch = asin(-dir.z); - i.bAMBUSH = true; - A_QuakeEx(3,3,3,10,0,64,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.2); - A_StartSound("demolitionist/parry",CHAN_WEAPON); - let s = Demolitionist(master).mystats; - if ( special1 >= special2 ) // perfect parry - { - // increased homing - dir = dir*.4+hdir*.6; - nspeed = min(100,nspeed*1.5); - a.vel = dir*nspeed; - for ( int i=1; i<6; i++ ) - { - let r = Spawn("ParryRing",a.pos); - r.specialf1 = i*.04; - } - buff.special1++; - if ( !critsnd ) - { - A_StartSound("misc/soulsparry",CHAN_ITEM,CHANF_OVERLAP,1.,.5); - if ( s ) s.pparries++; - } - critsnd = true; - } - if ( s ) s.parries++; - } - if ( --special1 <= 0 ) Destroy(); - } -} - -Class UseList -{ - Line hitline; - int hitside, hitpart; - Actor hitactor; - Vector3 pos; -} - -Class UseLineTracer : LineTracer -{ - Array uses; - - static play bool TangibleLine( UseList u ) - { - if ( u.hitpart != TIER_MIDDLE ) return true; // lower/upper/ffloor - Line l = u.HitLine; - if ( !l.sidedef[1] ) return true; // onesided line - Side s = l.sidedef[u.hitside]; - if ( s.GetTexture(1).IsNull() ) return false; // no midtex - double ofs = s.GetTextureYOffset(1); - Vector2 siz = TexMan.GetScaledSize(s.GetTexture(1)); - Vector2 tofs = TexMan.GetScaledOffset(s.GetTexture(1)); - ofs += tofs.y; - ofs *= s.GetTextureYScale(1); - siz.y *= s.GetTextureYScale(1); - SecPlane ceil, flor; - if ( (l.frontsector.floorplane.ZatPoint(l.v1.p) > l.backsector.floorplane.ZatPoint(l.v1.p)) - && (l.frontsector.floorplane.ZatPoint(l.v2.p) > l.backsector.floorplane.ZatPoint(l.v2.p)) ) - flor = l.frontsector.floorplane; - else flor = l.backsector.floorplane; - if ( (l.frontsector.ceilingplane.ZatPoint(l.v1.p) < l.backsector.ceilingplane.ZatPoint(l.v1.p)) - && (l.frontsector.ceilingplane.ZatPoint(l.v2.p) < l.backsector.ceilingplane.ZatPoint(l.v2.p)) ) - ceil = l.frontsector.ceilingplane; - else ceil = l.backsector.ceilingplane; - double ceilpoint = max(ceil.ZatPoint(l.v1.p),ceil.ZatPoint(l.v2.p)); - double florpoint = min(flor.ZatPoint(l.v1.p),flor.ZatPoint(l.v2.p)); - if ( l.flags&Line.ML_DONTPEGBOTTOM ) - { - if ( u.pos.z > florpoint+ofs+siz.y ) return false; - if ( u.pos.z < florpoint+ofs ) return false; - return true; - } - else - { - if ( u.pos.z > ceilpoint+ofs ) return false; - if ( u.pos.z < (ceilpoint+ofs)-siz.y ) return false; - return true; - } - return false; - } - - override ETraceStatus TraceCallback() - { - if ( Results.HitType == TRACE_HitActor ) - { - let u = new("UseList"); - u.hitline = null; - u.hitactor = Results.HitActor; - u.pos = Results.HitPos; - uses.Push(u); - return TRACE_Skip; - } - if ( Results.HitType == TRACE_HitWall ) - { - if ( Results.HitLine.Activation&(SPAC_Use|SPAC_UseThrough) ) - { - let u = new("UseList"); - u.hitline = Results.HitLine; - u.hitside = Results.Side; - u.hitpart = Results.FFloor?TIER_FFLOOR:Results.Tier; - u.hitactor = null; - u.pos = Results.HitPos; - uses.Push(u); - } - if ( Results.Tier == TIER_Middle ) - { - if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything|Line.ML_BlockUse)) ) - return TRACE_Stop; - return TRACE_Skip; - } - } - return TRACE_Stop; - } -} - -Class SWWMCrosshairTracer : LineTracer -{ - Actor ignoreme; - - override ETraceStatus TraceCallback() - { - if ( Results.HitType == TRACE_HitActor ) - { - if ( Results.HitActor == ignoreme ) return TRACE_Skip; - if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop; - return TRACE_Skip; - } - else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) - { - if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) - return TRACE_Stop; - return TRACE_Skip; - } - return TRACE_Stop; - } -} - -// Base class for all SWWM Weapons -Class SWWMWeapon : Weapon abstract -{ - Mixin SWWMOverlapPickupSound; - - bool wasused; - private int SWeaponFlags; - Actor pfield; // instance of parry field for current melee attack - bool wallponch; // is punching a wall (for activation checks) - - Class dropammotype; - - Property DropAmmoType : dropammotype; - - FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count) - FlagDef HideInMenu : SWeaponFlags, 1; // don't show in inventory menu (usually for sister weapons) - FlagDef NoSwapWeapon : SWeaponFlags, 2; // weapon is not affected by slot swapping - - ui SWWMCrosshairTracer ctr; - ui Vector3 cpos; - ui Color ccol; - ui Vector2 lagvpos; - transient ui uint prevms; - - bool IsSwapWeapon( Inventory i ) const - { - if ( bNoSwapWeapon || (i.GetClass() == GetClass()) ) return false; - let w = SWWMWeapon(i); - if ( w && !w.bNoSwapWeapon && (SlotNumber != -1) && (w.SlotNumber == SlotNumber) ) - return true; - return false; - } - - SWWMWeapon HasSwapWeapon( Actor other ) const - { - if ( bNoSwapWeapon ) return null; - for ( Inventory i=other.inv; i; i=i.inv ) - { - if ( IsSwapWeapon(i) ) - return SWWMWeapon(i); - } - return null; - } - - override void Touch( Actor toucher ) - { - // cannot pick up swapweapon unless explicitly pressing use - SWWMWeapon sw; - if ( swwm_swapweapons && (sw = HasSwapWeapon(toucher)) ) - { - if ( toucher.CheckLocalView() ) - { - // use sisterweapon tag for dual wield (slot 2 weapons) - if ( sw.SisterWeapon && (sw.Amount > 1) ) - Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.SisterWeapon.GetTag(),GetTag())); - else Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),GetTag())); - } - return; - } - Super.Touch(toucher); - } - // allow pickup by use + swap weapon support - override bool Used( Actor user ) - { - Vector3 itempos = Vec3Offset(0,0,Height/2), - userpos = user.Vec2OffsetZ(0,0,user.player.viewz); - // test vertical range - Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); - double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); - if ( abs(diff.z) > rang ) return false; - // if the toucher owns our SwapWeapon, drop it before picking us up - bool swapto = false; - SWWMWeapon sw; - if ( swwm_swapweapons && (sw = HasSwapWeapon(user)) ) - { - if ( (sw == user.player.ReadyWeapon) || (sw.SisterWeapon && (sw.SisterWeapon == user.player.ReadyWeapon)) ) - swapto = true; - int ngun = sw.Amount; - double ang = -15*(ngun-1); - for ( int i=0; i 0 ) gotstuff = AddExistingAmmo(ownedWeapon.Ammo1,AmmoGive1); - if ( AmmoGive2 > 0 ) gotstuff |= AddExistingAmmo(ownedWeapon.Ammo2,AmmoGive2); - let Owner = ownedWeapon.Owner; - if ( gotstuff && Owner && Owner.player ) - { - if ( ownedWeapon.Ammo1 && (oldamount1 == 0) ) - PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass()); - else if ( ownedWeapon.Ammo2 && (oldamount2 == 0) ) - PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo2.GetClass()); - } - if ( ownedWeapon.Ammo1 ) - { - // subtract price of ammo we don't give - int ammonotgiven = default.AmmoGive1-AmmoGive1; - if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammonotgiven-1))); - // subtract price of given ammo - int ammogiven = ownedWeapon.Ammo1.Amount-oldamount1; - if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammogiven-1))); - // drop excess - int dropme = AmmoGive1-ammogiven; - if ( dropme > 0 ) - { - // hacky, but it works - ownedWeapon.Ammo1.CreateTossable(dropme); - ownedWeapon.Ammo1.Amount += dropme; - } - } - if ( ownedWeapon.Ammo2 ) - { - // subtract price of ammo we don't give - int ammonotgiven = default.AmmoGive2-AmmoGive2; - if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo2.Stamina*(1.+.75*(ammonotgiven-1))); - // subtract price of given ammo - int ammogiven = ownedWeapon.Ammo2.Amount-oldamount2; - if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo2.Stamina*(1.+.75*(ammogiven-1))); - // drop excess - int dropme = AmmoGive2-ammogiven; - if ( dropme > 0 ) - { - // hacky, but it works - ownedWeapon.Ammo2.CreateTossable(dropme); - ownedWeapon.Ammo2.Amount += dropme; - } - } - return gotstuff; - } - override bool HandlePickup( Inventory item ) - { - // can't hold both weapons at once - if ( swwm_swapweapons && IsSwapWeapon(item) ) - return true; - if ( (GetClass() == item.GetClass()) && !item.ShouldStay() ) - { - if ( SWWMWeapon(item).PickupForAmmoSWWM(self) ) - item.bPickupGood = true; - if ( (Amount+item.Amount > MaxAmount) && (item.Stamina > 0) ) - { - // sell excess - int sellprice = item.Stamina/2; - SWWMScoreObj.Spawn(sellprice,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2)); - SWWMCredits.Give(Owner.player,sellprice); - if ( Owner.player ) - Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),Owner.player.GetUserName(),GetTag(),sellprice); - item.bPickupGood = true; - } - // reset the price in case it has to respawn - item.Stamina = item.default.Stamina; - return true; - } - return Super.HandlePickup(item); - } - override void AttachToOwner( Actor other ) - { - Inventory.AttachToOwner(other); - Ammo1 = AddAmmo(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1); - Ammo2 = AddAmmo(Owner,AmmoType2,bNoFirstGive?0:AmmoGive2); - SisterWeapon = AddWeapon(SisterWeaponType); - if ( Owner.player ) - { - if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch ) - Owner.player.PendingWeapon = self; - if ( Owner.player.mo == players[consoleplayer].camera ) - StatusBar.ReceivedWeapon(self); - } - GivenAsMorphWeapon = false; - } - override void DetachFromOwner() - { - Owner.A_StopSound(CHAN_WEAPON); - Owner.A_StopSound(CHAN_WEAPONEXTRA); - Owner.A_StopSound(CHAN_WEAPONEXTRA2); - Owner.A_StopSound(CHAN_WEAPONEXTRA3); - Super.DetachFromOwner(); - } - override void OwnerDied() - { - if ( Owner.player && (Owner.player.ReadyWeapon == self) ) - { - Owner.A_StopSound(CHAN_WEAPON); - Owner.A_StopSound(CHAN_WEAPONEXTRA); - Owner.A_StopSound(CHAN_WEAPONEXTRA2); - Owner.A_StopSound(CHAN_WEAPONEXTRA3); - } - A_ClearRefire(); - Super.OwnerDied(); - } - override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) - { - if ( mod == 'Melee' ) return StringTable.Localize("$O_MELEE"); - return Super.GetObituary(victim,inflictor,mod,playerattack); - } - // draw ammo on hud above weapon box - virtual ui void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) - { - } - // HUD-side ticking - virtual ui void HudTick() - { - if ( !Owner ) return; - [cpos, ccol] = TraceForCrosshair(); - // avoid jumpy switching - if ( Owner.player.PendingWeapon is 'SWWMWeapon' ) - { - SWWMWeapon(Owner.player.PendingWeapon).cpos = cpos; - SWWMWeapon(Owner.player.PendingWeapon).lagvpos = lagvpos; - SWWMWeapon(Owner.player.PendingWeapon).prevms = prevms; - } - } - // extra drawing, usually scopes - virtual ui void RenderUnderlay( RenderEvent e ) - { - // draw custom crosshair - if ( automapactive || !(players[consoleplayer].Camera is 'PlayerPawn') ) return; - if ( !swwm_precisecrosshair ) return; - if ( !crosshairon ) return; - if ( crosshairforce ) return; - let sb = SWWMStatusBar(StatusBar); - if ( !sb ) return; - sb.viewport.FromHud(); - sb.proj.CacheResolution(); - sb.proj.CacheFov(players[consoleplayer].fov); - sb.proj.OrientForRenderUnderlay(e); - sb.proj.BeginProjection(); - Vector3 tdir = level.Vec3Diff(e.ViewPos,cpos); - // project - sb.proj.ProjectWorldPos(e.ViewPos+tdir); - Vector2 vpos; - if ( !sb.proj.IsInFront() ) return; - Vector2 npos = sb.proj.ProjectToNormal(); - vpos = sb.viewport.SceneToWindow(npos); - double frametime = (MSTime()-prevms)/1000.; - double theta = clamp(15.*frametime,0.,1.); // naive, but whatever - if ( !prevms || (lagvpos == (0,0)) ) lagvpos = vpos; - else lagvpos = lagvpos*(1.-theta)+vpos*theta; - prevms = MSTime(); - int cnum = abs(CVar.FindCVar('crosshair').GetInt()); - if ( !cnum ) return; - String tn = String.Format("XHAIR%s%d",(Screen.GetWidth()<640)?"S":"B",cnum); - TextureID ctex = TexMan.CheckForTexture(tn,TexMan.Type_MiscPatch); - if ( !ctex.IsValid() ) ctex = TexMan.CheckForTexture(String.Format("XHAIR%s1",(Screen.GetWidth()<640)?"S":"B"),TexMan.Type_MiscPatch); - if ( !ctex.IsValid() ) ctex = TexMan.CheckForTexture("XHAIRS1",TexMan.Type_MiscPatch); - Vector2 ts = TexMan.GetScaledSize(ctex); - double cs = crosshairscale; - double sz = 1.; - if ( cs > 0. ) sz = Screen.GetHeight()*cs/200.; - if ( crosshairgrow ) sz *= sb.CrosshairSize; - Screen.DrawTexture(ctex,false,int(lagvpos.x),int(lagvpos.y),DTA_DestWidthF,ts.x*sz,DTA_DestHeightF,ts.y*sz,DTA_AlphaChannel,true,DTA_FillColor,ccol); - } - ui Vector3, Color TraceForCrosshair() - { - if ( !ctr ) ctr = new("SWWMCrosshairTracer"); - ctr.ignoreme = Owner; - Vector3 x, y, z, ofs; - double s; - [x, y, z] = swwm_CoordUtil.GetAxes(Owner.pitch,Owner.angle,Owner.roll); - ofs = GetTraceOffset(); - Vector3 origin = level.Vec3Offset(Owner.Vec2OffsetZ(0,0,Owner.player.viewz),ofs.x*x+ofs.y*y+ofs.z*z); - ctr.Trace(origin,level.PointInSector(origin.xy),x,10000.,0); - Color col = crosshaircolor; - int chp = crosshairhealth; - if ( chp >= 2 ) - { - int hp = Clamp(Owner.Health,0,200); - double sat = (hp<150)?1.:(1.-(hp-150)/100.); - Vector3 rgb = SWWMUtility.HSVtoRGB((hp/300.,sat,1.)); - col = Color(int(rgb.x*255),int(rgb.y*255),int(rgb.z*255)); - } - else if ( chp == 1 ) - { - double hp = Clamp(Owner.Health,0,100)/100.; - if ( hp <= 0 ) col = Color(255,0,0); - else if ( hp < .3 ) col = Color(255,int(hp*255/.3),0); - else if ( hp < .85 ) col = Color(int((.6-hp)*255/.3),255,0); - else col = Color(0,255,0); - } - else if ( (ctr.Results.HitType == TRACE_HitActor) && ctr.Results.HitActor.bSHOOTABLE ) - { - // show target health, rather than our own - double hp = ctr.Results.HitActor.Health/double(ctr.Results.HitActor.GetSpawnHealth()); - if ( hp <= 0 ) col = Color(255,0,0); - else if ( hp < .3 ) col = Color(255,int(hp*255/.3),0); - else if ( hp < .85 ) col = Color(int((.6-hp)*255/.3),255,0); - else col = Color(0,255,0); - } - if ( ctr.Results.HitType == TRACE_HitNone ) return level.Vec3Offset(origin,x*10000.), col; - else return ctr.Results.HitPos, col; - } - // where the trace is coming from relative to eyes - virtual clearscope Vector3 GetTraceOffset() const - { - return (0.,0.,0.); - } - // animations - action void A_PlayerFire() - { - let demo = Demolitionist(player.mo); - if ( demo && (demo.Health > 0) ) demo.PlayFire(); - } - action void A_PlayerMelee( bool bFast = false ) - { - let demo = Demolitionist(player.mo); - if ( demo && (demo.Health > 0) ) - { - if ( bFast ) demo.PlayFastMelee(); - else demo.PlayMelee(); - } - } - action void A_PlayerReload() - { - let demo = Demolitionist(player.mo); - if ( demo && (demo.Health > 0) ) demo.PlayReload(); - } - action void A_PlayerCheckGun() - { - let demo = Demolitionist(player.mo); - if ( demo && (demo.Health > 0) ) demo.PlayCheckGun(); - } - // instant raise/lower - action void A_FullRaise() - { - if ( !player ) return; - if ( player.PendingWeapon != WP_NOCHANGE ) - { - player.mo.DropWeapon(); - return; - } - if ( !player.ReadyWeapon ) return; - let psp = player.GetPSprite(PSP_WEAPON); - if ( !psp ) return; - ResetPSprite(psp); - psp.y = WEAPONTOP; - // do not jump to ready state here, the weapon should do that - // directly once it finishes playing its select animation - } - action void A_FullLower() - { - if ( !player ) return; - if ( !player.ReadyWeapon ) - { - player.mo.BringUpWeapon(); - return; - } - let psp = player.GetPSprite(PSP_WEAPON); - if ( !psp ) return; - psp.y = WEAPONBOTTOM; - ResetPSprite(psp); - if ( player.playerstate == PST_DEAD ) - { - // Player is dead, so don't bring up a pending weapon - // Player is dead, so keep the weapon off screen - player.SetPSprite(PSP_FLASH,null); - psp.SetState(player.ReadyWeapon.FindState('DeadLowered')); - return; - } - // [RH] Clear the flash state. Only needed for Strife. - player.SetPSprite(PSP_FLASH,null); - player.mo.BringUpWeapon(); - } - action void A_Parry( int duration ) - { - Vector3 x, y, z, origin; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*20-(0,0,20)); - if ( invoker.pfield ) invoker.pfield.Destroy(); - invoker.pfield = Spawn("ParryField",origin); - invoker.pfield.master = self; - invoker.pfield.special1 = duration; - invoker.pfield.special2 = duration; - if ( !FindInventory("ParryDamageChecker") ) - GiveInventory("ParryDamageChecker",1); // need this so parried projectiles deal extra damage - } - private action bool TryMelee( double angle, int dmg, String hitsound = "", double rangemul = 1. ) - { - FTranslatedLineTarget t; - double slope = AimLineAttack(angle,1.5*DEFMELEERANGE*rangemul,t,0.,ALF_CHECK3D); - FLineTraceData d; - LineTrace(angle,1.5*DEFMELEERANGE*rangemul,slope,0,player.viewheight,data:d); - bool raging = CountInv("RagekitPower"); - if ( (d.HitType == TRACE_HitActor) && !d.HitActor.FindInventory("ParriedBuff") ) - { - bool bloodless = true; - double diff = deltaangle(self.angle,AngleTo(d.HitActor)); - self.angle += clamp(diff,-5.,5.); - SWWMUtility.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000); - if ( raging ) - { - invoker.bEXTREMEDEATH = true; - invoker.bNOEXTREMEDEATH = false; - } - else - { - invoker.bEXTREMEDEATH = false; - invoker.bNOEXTREMEDEATH = true; - } - if ( !d.HitActor.bDORMANT ) // lol oops - d.HitActor.DaggerAlert(self); - int flg = DMG_USEANGLE|DMG_THRUSTLESS; - if ( raging ) flg |= DMG_FOILINVUL; - dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',flg,atan2(d.HitDir.y,d.HitDir.x)); - invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH; - invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH; - int quakin = raging?8:2; - if ( d.HitActor.player ) d.HitActor.A_QuakeEx(quakin,quakin,quakin,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.125*quakin); - if ( !d.HitActor.bNOBLOOD && !d.HitActor.bDORMANT && (raging || !d.HitActor.bINVULNERABLE) ) - { - d.HitActor.TraceBleed(dmg,invoker); - d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); - bloodless = false; - } - else - { - let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation); - p.angle = atan2(-d.HitDir.y,-d.HitDir.x); - } - if ( raging ) - { - let ps = Spawn("BigPunchSplash",d.HitLocation); - ps.target = self; - ps.special1 = dmg; - } - A_QuakeEx(quakin/2,quakin/2,quakin/2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.06*quakin); - if ( raging ) A_StartSound(bloodless?"pusher/althit":"pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP); - else A_StartSound((hitsound!="")?hitsound:bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP); - A_AlertMonsters(swwm_uncapalert?0:300); - return true; - } - return false; - } - action void A_Melee( int dmg = 40, String hitsound = "", double rangemul = 1. ) - { - Vector3 origin = Vec3Offset(0,0,player.viewheight); - Vector3 dir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)); - // check for usables - let ut = new("UseLineTracer"); - ut.uses.Clear(); - ut.Trace(origin,CurSector,dir,DEFMELEERANGE*rangemul,0); - invoker.wallponch = true; - for ( int i=0; i0)||(Ammo2&&(Ammo2.Amount>0))); - } - // tells the Embiggener that this weapon uses the specified ammo type - // even if it is not its primary one - virtual clearscope bool UsesAmmo( Class kind ) - { - return (AmmoType1&&(kind is AmmoType1))||(AmmoType2&&(kind is AmmoType2)); - } - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - if ( (AmmoGive1 <= 0) && (default.AmmoGive1 > 0) ) - AmmoGive1 = 1; - if ( (AmmoGive2 <= 0) && (default.AmmoGive2 > 0) ) - AmmoGive2 = 1; - } - override bool SpecialDropAction( Actor dropper ) - { - if ( swwm_enemydrops > 0 ) return false; - else if ( swwm_enemydrops == 0 ) - { - // first, check if no others exist in the map, just in - // case this weapon drop MAY be needed - if ( !SWWMUtility.ItemExists(GetClass(),self) ) - return false; // drop us - // drop our corresponding ammo - if ( !DropAmmoType ) return true; - let a = Inventory(Spawn(DropAmmoType,pos,ALLOW_REPLACE)); - if ( !a ) return true; - a.bDROPPED = true; - a.bNOGRAVITY = false; - if ( !(level.compatflags&COMPATF_NOTOSSDROPS) ) - a.TossItem(); - if ( a is 'Ammo' ) - a.ModifyDropAmount(Ammo(a).DropAmount); - a.bTOSSED = true; - if ( a.SpecialDropAction(dropper) ) - a.Destroy(); - return true; - } - // no weapon drops from enemies - return true; - } - override Inventory CreateTossable( int amt ) - { - // disallow dropping if weapon isn't ready for switching - if ( (Owner.player.ReadyWeapon == self) && (!(Owner.player.WeaponState&WF_WEAPONSWITCHOK) || (Owner.player.WeaponState&WF_DISABLESWITCH)) ) - return null; - return Super.CreateTossable(amt); - } - Default - { - Weapon.BobStyle "Alpha"; - Weapon.BobSpeed 3.0; - Weapon.BobRangeX 0.5; - Weapon.BobRangeY 0.2; - Weapon.YAdjust 0; - Weapon.SlotPriority 1.; - Inventory.RestrictedTo "Demolitionist"; - Inventory.PickupFlash "SWWMRedPickupFlash"; - +INVENTORY.IGNORESKILL; - +WEAPON.NOALERT; - +WEAPON.NODEATHINPUT; - +FLOATBOB; - FloatBobStrength 0.25; - } -} diff --git a/zscript/swwm_onfire.zsc b/zscript/swwm_onfire.zsc new file mode 100644 index 000000000..9b87661ff --- /dev/null +++ b/zscript/swwm_onfire.zsc @@ -0,0 +1,293 @@ +// discarded after FYS shell removal, but will be used by Quadravol eventually +// kept in this file in the meantime + +Class OnFireLight : DynamicLight +{ + OnFire of; + + override void Tick() + { + Super.Tick(); + if ( !of || !of.victim ) + { + Destroy(); + return; + } + Args[0] = clamp(of.Amount*4,0,255); + Args[1] = clamp(of.Amount*2,0,160); + Args[2] = clamp(of.Amount/2,0,24); + Args[3] = int(max(of.victim.default.radius,of.victim.default.height)*(of.victim.scale.x+of.victim.scale.y)*1.2+40+clamp(of.amount/5,0,120)); + SetOrigin(of.Victim.Vec3Offset(0,0,of.Victim.Height/2),true); + } +} + +Class OnFire : Actor +{ + OnFire prevfire, nextfire; + Actor victim, instigator, lite; + int amount, cnt, delay; + double oangle; + + override void OnDestroy() + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( hnd ) + { + hnd.fires_cnt--; + if ( !prevfire ) + { + hnd.fires = nextfire; + if ( nextfire ) nextfire.prevfire = null; + } + else + { + prevfire.nextfire = nextfire; + if ( nextfire ) nextfire.prevfire = prevfire; + } + } + Super.OnDestroy(); + } + + override void Tick() + { + if ( isFrozen() ) return; + if ( !victim ) + { + A_StopSound(CHAN_5); + Destroy(); + return; + } + SetOrigin(victim.pos,false); + if ( victim.waterlevel > 0 ) + { + if ( lite ) lite.Destroy(); + amount -= int(victim.waterlevel**2); + } + if ( victim.Health <= 0 ) amount = min(amount,100); + if ( !(level.maptime%3) ) + amount--; + if ( victim.player ) amount -= int(abs(actor.deltaangle(victim.angle,oangle))/30); + oangle = victim.angle; + if ( amount < -30 ) + { + A_StopSound(CHAN_5); + Destroy(); + return; + } + if ( cnt > 0 ) cnt--; + else + { + cnt = min(10,30-int(29*(min(1.,amount/500.)**3.))); + if ( victim.bSHOOTABLE && (victim.Health > 0) && (amount > 0) ) + { + int flg = DMG_THRUSTLESS; + if ( victim is 'Centaur' ) flg |= DMG_FOILINVUL; // you're on fire, that shield is worthless + victim.DamageMobj(self,instigator,clamp(int(amount*.06),1,20),'Fire',flg); // need to use this actor as inflictor to have a proper obituary + if ( victim.bISMONSTER && !Random[FlameT](0,3) ) + victim.Howl(); + } + if ( !victim ) + { + A_StopSound(CHAN_5); + Destroy(); + return; + } + else SWWMUtility.DoExplosion(self,clamp(int(amount*.06),1,20),0,victim.radius+40,victim.radius,DE_NOBLEED|DE_NOSPLASH|DE_HOWL,'Fire',victim); // radius fire damage + } + double mult = max(victim.radius,victim.height)/30.; + if ( delay > 0 ) delay--; + if ( (level.maptime+special1)%6 ) return; + A_SoundVolume(CHAN_5,min(1.,mult*amount/80.)); + int numpt = clamp(int(Random[FlameT](2,4)*amount*.01),1,4); + numpt = int(clamp(numpt*mult**.5,1,3)); + for ( int i=0; i 0 ) + { + let c = victim.Spawn("OnFireTrail",pos); + c.special1 = Random[FlameT](-2,2); + c.scale *= max(.3,mult*0.5); + c.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.5,2.)*c.scale.x; + } + if ( !(i%2) ) + { + let s = victim.Spawn("SWWMHalfSmoke",pos); + s.scale *= max(1.,1.6*mult); + s.alpha *= min(amount+30,100)*.01; + s.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.2,.6)*s.scale.x; + } + } + } + + static OnFire Apply( Actor victim, Actor instigator, int amount, int delay = 0 ) + { + if ( amount <= 0 ) return null; + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return null; + OnFire t; + for ( t=hnd.fires; t; t=t.nextfire ) + { + if ( t.victim != victim ) continue; + if ( instigator ) t.instigator = instigator; + t.amount = min(500,t.amount+amount); + t.cnt = min(t.cnt,5); + return t; + } + t = OnFire(Spawn("OnFire",victim.pos)); + t.victim = victim; + t.instigator = instigator; + t.amount = min(500,amount); + t.cnt = 1; + t.special1 = Random[FlameT](0,10); + t.A_StartSound("misc/flame",CHAN_5,CHANF_LOOP); + double mult = max(victim.radius,victim.height)/30.; + t.A_SoundVolume(CHAN_5,min(1.,mult*amount/80.)); + // for chunks + t.delay = delay; + t.lite = Actor.Spawn("OnFireLight",victim.pos); + OnFireLight(t.lite).of = t; + t.oangle = victim.angle; + // append + t.nextfire = hnd.fires; + if ( hnd.fires ) hnd.fires.prevfire = t; + hnd.fires = t; + hnd.fires_cnt++; + return t; + } + + static OnFire IsOnFire( Actor victim ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return null; + OnFire t; + for ( t=hnd.fires; t; t=t.nextfire ) + { + if ( t.victim != victim ) continue; + if ( t.amount <= 0 ) return null; + return t; + } + return null; + } + + Default + { + +NOGRAVITY; + +NOBLOCKMAP; + +DONTSPLASH; + +NOEXTREMEDEATH; + +NOINTERACTION; + Obituary "$O_ONFIRE"; + } +} + +Class OnFireTrailLight : PaletteLight +{ + Default + { + Tag "HellExpl"; + Args 0,0,0,40; + ReactionTime 40; + } + override void Tick() + { + Super.Tick(); + Args[0] /= 10; + Args[1] /= 10; + Args[2] /= 10; + Args[3] += 3; + if ( !target || (target.waterlevel > 0) ) + { + Destroy(); + return; + } + SetOrigin(target.pos,true); + } +} + +Class OnFireTrail : Actor +{ + override void PostBeginPlay() + { + Super.PostBeginPlay(); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + roll = FRandom[ExploS](0,360); + } + action void A_Flame() + { + special1++; + if ( waterlevel > 0 ) + vel *= .9; + else + { + vel *= .98; + vel.z += .1+.2*abs(scale.x); + } + if ( waterlevel > 0 ) + { + let s = Spawn("SWWMSmoke",pos); + s.vel = (FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2)); + s.vel += vel*.3; + s.alpha *= alpha*2; + s.scale *= .5+abs(scale.x)*(.5+special1/6.); + Destroy(); + return; + } + if ( !Random[FlameT](0,int(40*(default.alpha-alpha))) ) + { + let s = Spawn("SWWMHalfSmoke",pos); + s.vel = (FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2)); + s.vel += vel*.3; + s.alpha *= alpha*1.5; + s.scale *= .5+abs(scale.x)*(.5+special1/6.); + } + } + override void Tick() + { + if ( isFrozen() ) return; + SetOrigin(level.Vec3Offset(pos,vel),true); + UpdateWaterLevel(); + if ( !CheckNoDelay() || (tics == -1) ) return; + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + Default + { + RenderStyle "Add"; + Speed 2; + Radius 4; + Height 4; + Alpha .6; + Scale .8; + +NOBLOCKMAP; + +NOGRAVITY; + +NOFRICTION; + +SLIDESONWALLS; + +NOTELEPORT; + +FORCEXYBILLBOARD; + +ROLLSPRITE; + +ROLLCENTER; + +DROPOFF; + +NOBLOCKMONST; + +DONTSPLASH; + +NOINTERACTION; + } + States + { + Spawn: + XFLM ABCDEFGHIJKLMNOPQRST 1 Bright + { + A_Flame(); + A_SetScale(scale.x*0.98); + A_FadeOut(0.02); + } + Wait; + } +} diff --git a/zscript/swwm_player.zsc b/zscript/swwm_player.zsc index ed01ecad1..ce4e4ee17 100644 --- a/zscript/swwm_player.zsc +++ b/zscript/swwm_player.zsc @@ -2595,511 +2595,6 @@ Class Demolitionist : PlayerPawn } } -// lucky collar -// made by Ashley Knox, given to you and Ibuki by Saya -Class SayaCollar : Inventory -{ - Default - { - +INVENTORY.UNDROPPABLE; - +INVENTORY.UNTOSSABLE; - +INVENTORY.UNCLEARABLE; - } - override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) - { - if ( (damage <= 0) || (flags&(DMG_FORCED|DMG_NO_ARMOR)) ) return; - newdamage = damage; - // oopsies are halved - if ( source == Owner ) newdamage = max(1,newdamage/2); - // in danger? reduce to a quarter - if ( (Owner.Health-newdamage < 25) ) - { - int splitdmg[2]; - splitdmg[0] = max(0,Owner.Health-25); // non-reduced part (>=25% health) - splitdmg[1] = max(1,(newdamage-splitdmg[0])/4); // reduced part (<25% health) - newdamage = splitdmg[0]+splitdmg[1]; - } - } - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - // if first item is health or sandwich, ignore - if ( (other.Inv is 'SWWMHealth') || (other.Inv is 'GrilledCheeseSandwich') ) - return; - // if there's items before health/sandwich, squeeze right in - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( (i == self) || (!(i.Inv is 'SWWMHealth' ) && !(i.Inv is 'GrilledCheeseSandwich')) ) continue; - Inventory saved = i.Inv; - i.Inv = self; - other.Inv = Inv; - Inv = saved; - break; - } - } -} - -// high-resonant almasteel plating -// EXTRA THICC as Saya requested -Class AlmasteelPlating : Inventory -{ - Inventory dbf; - - Default - { - +INVENTORY.UNDROPPABLE; - +INVENTORY.UNTOSSABLE; - +INVENTORY.UNCLEARABLE; - Inventory.RestrictedTo "Demolitionist"; - } - override void DoEffect() - { - Super.DoEffect(); - if ( !dbf ) return; - dbf.Amount = int(dbf.Amount*.95-1); // rapidly dissipate Telebrium corrosion - } - override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) - { - if ( inflictor && (inflictor is 'CorrodeDebuff') ) dbf = Inventory(inflictor); - if ( (damage <= 0) || (flags&(DMG_FORCED|DMG_NO_ARMOR)) ) return; - newdamage = damage; - // 80% reduction for explosions - if ( flags&DMG_EXPLOSION ) newdamage = newdamage/5; - // 50% reduction for crushing - if ( damageType == 'Crush' ) - { - newdamage = newdamage/2; - // additionally, check if we can break any active crushers - double gaph = (Owner.ceilingz-Owner.floorz); - if ( gaph > Owner.height*.6 ) return; - // the smaller the gap, the more likely the crusher will snap - if ( Random[Demolitionist](0,3) && (FRandom[Demolitionist](0,gaph/Owner.height) > .1) ) return; - double diffh = 8.+(Owner.default.height-gaph); // how much the crusher will have to "snap" after breaking - let ceil = Owner.ceilingsector; - let flor = Owner.floorsector; - let ceilse = ceil.ceilingdata; - let florse = flor.floordata; - if ( ceilse && florse ) - { - // snap both planes - let q = Spawn("BustedQuake",(ceil.centerspot.x,ceil.centerspot.y,ceil.ceilingplane.ZAtPoint(ceil.centerspot))); - q.special1 = 6; - q = Spawn("BustedQuake",(flor.centerspot.x,flor.centerspot.y,flor.floorplane.ZAtPoint(flor.centerspot))); - q.special1 = 6; - SWWMCrusherBroken.Create(flor,ceil,diffh/2.); - } - else if ( ceilse ) - { - // snap ceiling - let q = Spawn("BustedQuake",(ceil.centerspot.x,ceil.centerspot.y,ceil.ceilingplane.ZAtPoint(ceil.centerspot))); - q.special1 = 10; - SWWMCrusherBroken.Create(null,ceil,diffh); - } - else if ( florse ) - { - // snap floor - let q = Spawn("BustedQuake",(flor.centerspot.x,flor.centerspot.y,flor.floorplane.ZAtPoint(flor.centerspot))); - q.special1 = 10; - SWWMCrusherBroken.Create(flor,null,diffh); - } - } - } - override bool HandlePickup( Inventory item ) - { - // disallow vanilla armors - if ( (item is 'BasicArmor') || (item is 'BasicArmorBonus') || (item is 'BasicArmorPickup') || (item is 'HexenArmor') ) - { - item.bPickupGood = true; // but act as if we picked them up - return true; - } - return false; - } - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - // if first item is the collar, just ignore - if ( other.Inv is 'SayaCollar' ) return; - // if there's items before collar, squeeze right in - for ( Inventory i=other.Inv; i; i=i.Inv ) - { - if ( (i == self) || !(i.Inv is 'SayaCollar') ) continue; - Inventory saved = i.Inv; - i.Inv = self; - other.Inv = Inv; - Inv = saved; - break; - } - } -} - -Class DashTrail : Actor -{ - Default - { - RenderStyle "Add"; - Radius 2; - Height 2; - Scale 0.3; - +NOGRAVITY; - +NOBLOCKMAP; - +DONTSPLASH; - +NOTELEPORT; - +NOINTERACTION; - FloatBobPhase 0; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - SetState(FindState("Spawn")+Random[ExploS](0,7)); - let t = Spawn("DashTrail2",level.Vec3Offset(pos,vel*.3)); - t.target = target; - t.vel = vel*1.2; - let s = Spawn("SWWMSmoke",level.Vec3Offset(pos,vel*1.6)); - s.vel = vel*.8; - s.SetShade(Color(1,1,1)*Random[ExploS](64,128)); - s.special1 = Random[ExploS](2,4); - s.scale *= 1.4; - s.alpha *= .3; - } - override void Tick() - { - Super.Tick(); - // hack - if ( target && (players[consoleplayer].Camera == target) ) Warp(target,pos.x,pos.y,pos.z,0,WARPF_ABSOLUTEPOSITION|WARPF_COPYINTERPOLATION); - } - States - { - Spawn: - JFLB ABCDEFGH 1 Bright - { - A_FadeOut(.2); - A_SetScale(scale.x*.95); - } - Loop; - } -} - -Class DashTrail2 : Actor -{ - Default - { - RenderStyle "Add"; - Radius 2; - Height 2; - Scale 0.2; - Alpha 0.4; - +NOGRAVITY; - +NOBLOCKMAP; - +DONTSPLASH; - +NOTELEPORT; - +NOINTERACTION; - FloatBobPhase 0; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - SetState(FindState("Spawn")+Random[ExploS](0,7)); - } - override void Tick() - { - Super.Tick(); - // hack - if ( target && (players[consoleplayer].Camera == target) ) Warp(target,pos.x,pos.y,pos.z,0,WARPF_ABSOLUTEPOSITION|WARPF_COPYINTERPOLATION); - } - States - { - Spawn: - JFLR ABCDEFGH 1 Bright - { - A_FadeOut(.02); - A_SetScale(scale.x*1.04); - if ( waterlevel > 0 ) - { - let b = Spawn("SWWMBubble",pos); - b.vel = vel; - b.scale *= scale.x; - Destroy(); - } - } - Loop; - } -} - -Class DemolitionistRadiusShockwaveTail : Actor -{ - Default - { - RenderStyle "Add"; - Radius 16; - Height 8; - +NOBLOCKMAP; - +NOGRAVITY; - +DONTSPLASH; - +NOTELEPORT; - +NOINTERACTION; - } - States - { - Spawn: - XZW1 A 1 - { - pitch = min(85,(pitch+2)*1.05); - A_FadeOut(.02); - A_SetScale(scale.x*1.08,scale.y); - vel *= .98; - } - Wait; - } -} - -Class DemolitionistRadiusShockwave : Actor -{ - Actor lasthit; - - Default - { - RenderStyle "Add"; - Speed 15; - DamageFunction int(200*alpha); - DamageType "GroundPound"; - Radius 16; - Height 8; - Alpha .4; - XScale .65; - YScale 3.; - PROJECTILE; - +DONTSPLASH; - +STEPMISSILE; - +NOEXPLODEFLOOR; - +FLATSPRITE; - +RIPPER; - +BLOODLESSIMPACT; - -NOGRAVITY; - } - override int DoSpecialDamage( Actor target, int damage, Name damagetype ) - { - if ( target == lasthit ) return 0; - lasthit = target; - if ( damage <= 0 ) return damage; - if ( (target.mass < LARGE_MASS) && !target.bDONTTHRUST ) - { - target.vel.xy += vel.xy.unit()*(30000./max(50,target.mass))*alpha; - if ( (target.pos.z <= floorz) || !target.TestMobjZ() ) - target.vel.z += (4000./max(50,target.mass))*alpha; - } - return damage; - } - States - { - Spawn: - XZW1 A 1 - { - SetZ(floorz); - pitch = min(85,(pitch+2)*1.05); - if ( !Random[ExploS](0,3) ) - Spawn("InvisibleSplasher",Vec3Offset(0,0,2)); - let s = Spawn("DemolitionistRadiusShockwaveTail",pos); - s.vel = vel*.35; - s.scale = scale; - s.alpha = alpha*.4; - s.angle = angle; - s.pitch = pitch; - s.roll = roll; - A_FadeOut(.015); - A_SetScale(scale.x*1.08,scale.y); - vel *= .98; - } - Wait; - Death: - XZW1 A 1 - { - SetZ(floorz); - A_FadeOut(.05); - A_SetScale(scale.x*1.1,scale.y*.97); - } - Wait; - } -} - -Class DemolitionistShockwave : Actor -{ - Default - { - +NOGRAVITY; - +NOBLOCKMAP; - +NOTELEPORT; - +NODAMAGETHRUST; - +FORCERADIUSDMG; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - A_QuakeEx(7,7,7,30,0,300+min(special1,50)*4,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:1.5); - if ( target.player != players[consoleplayer] ) - { - A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.3); - A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.2,pitch:.7); - A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.1,pitch:.4); - } - SWWMUtility.DoExplosion(self,40+min(special1,120),100000+min(special1*2000,150000),100+min(special1*2,130),80,DE_BLAST|DE_EXTRAZTHRUST,'GroundPound',target); - for ( int i=0; i<360; i+=5 ) - { - Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3); - let s = Spawn("SWWMSmoke",Vec3Angle(4,i,8)); - s.vel = pvel+(cos(i),sin(i),0)*7.; - s.SetShade(Color(1,1,1)*Random[ExploS](64,224)); - s.special1 = Random[ExploS](1,4); - s.scale *= 1.5; - s.alpha *= .4; - } - if ( pos.z > floorz+16 ) return; - for ( int i=0; i<360; i+=5 ) - { - let r = Spawn("DemolitionistRadiusShockwave",Vec3Angle(5,i)); - r.target = target; - r.angle = i; - r.vel.xy = (cos(i),sin(i))*(r.speed+min(special1*.15,30)); - r.alpha *= .1+min(special1*.03,.9); - } - int numpt = Random[ExploS](10,20); - for ( int i=0; i 0) && (--EffectTics == 0)) ) - Destroy (); - } - override void InitEffect() - { - Super.InitEffect(); - // adjust the duration - EffectTics = max(0,swwm_revivecooldown)*GameTicRate; - } - override void EndEffect() - { - Super.EndEffect(); - if ( !Owner ) return; - Owner.A_StartSound("demolitionist/revive",CHAN_ITEMEXTRA); - if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_REFAIL")); - } - override void OwnerDied() - { - // do nothing, this "powerup" is preserved on death - } -} - -// not an actual light, just handles the attach/detach -Class DemolitionistSelfLight : Actor -{ - bool oldactive; - bool oldglow; - transient Color tagcolor; - - Default - { - +NOGRAVITY; - +NOBLOCKMAP; - +DONTSPLASH; - +NOINTERACTION; - FloatBobPhase 0; - } - bool activelight() - { - // active all the time except when invisible or in certain - // animation frames - if ( target.bINVISIBLE || (target.alpha <= double.epsilon) ) return false; - if ( target.InStateSequence(target.CurState,target.FindState("Death")) && ((target.frame == 20) || (target.frame == 22) || (target.frame == 23) || (target.frame == 25) || (target.frame == 27) || (target.frame == 28) || (target.frame == 30) || (target.frame == 31) || (target.frame == 32) || (target.frame == 33) || (target.frame == 34)) ) - return false; - if ( target.InStateSequence(target.CurState,target.FindState("CrouchDeath")) && ((target.frame == 7) || (target.frame == 10) || (target.frame == 11)) ) - return false; - if ( target.InStateSequence(target.CurState,target.FindState("VoodooDeath")) && ((target.frame == 9) || (target.frame == 11) || (target.frame == 12) || (target.frame == 14) || (target.frame == 16) || (target.frame == 18)) ) - return false; - return true; - } - override void Tick() - { - if ( !target || !(target is 'Demolitionist') || (Demolitionist(target).selflight != self) ) - { - Destroy(); - return; - } - if ( tagcolor.a == 0 ) - { - let lmp = Wads.FindLump("tagcolor.txt"); - if ( lmp != -1 ) - { - String str = Wads.ReadLump(lmp); - Array rgb; - str.Split(rgb,",",0); - tagcolor = Color(255,rgb[0].ToInt(),rgb[1].ToInt(),rgb[2].ToInt()); - } - else tagcolor = Color(255,32,48,24); - } - bool curactive = activelight(); - // have to re-attach it repeatedly to update the pitch (wow great thanks) - if ( curactive ) - target.A_AttachLight('DemoSelfLight',DynamicLight.PointLight,Color(112,144,176),200,0,DynamicLight.LF_DONTLIGHTSELF|DynamicLight.LF_ATTENUATE|DynamicLight.LF_SPOT,(5,0,target.player?(target.player.viewz-target.pos.z):(target.height*.93)),0,15,60,target.pitch); - else if ( !curactive && oldactive ) - target.A_AttachLight('DemoSelfLight',DynamicLight.PointLight,0,0,0); - oldactive = curactive; - bool curglow = !(target.bINVISIBLE||(target.alpha <= double.epsilon)); - if ( curglow && !oldglow ) target.A_AttachLight('DemoSelfLight2',DynamicLight.PointLight,tagcolor,80,0,DynamicLight.LF_DONTLIGHTSELF|DynamicLight.LF_ATTENUATE,(0,0,target.height/2)); - else if ( !curglow && oldglow ) target.A_AttachLight('DemoSelfLight2',DynamicLight.PointLight,0,0,0); - oldglow = curglow; - } -} - // for the doom 2 cast Class CastDemolitionist : Actor { @@ -3124,996 +2619,3 @@ Class CastDemolitionist : Actor Stop; } } - -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 ) - { - SWWMStats s = null; - if ( Demolitionist(self.target) ) s = Demolitionist(self.target).mystats; - if ( s ) s.smooch++; - let raging = RagekitPower(self.target.FindInventory("RagekitPower")); - if ( raging ) - { - bEXTREMEDEATH = true; - bNOEXTREMEDEATH = false; - } - else - { - bEXTREMEDEATH = false; - bNOEXTREMEDEATH = true; - } - if ( (target is 'WolfensteinSS') || (target.Species == 'WolfensteinSS') ) target.bFRIENDLY = false; - if ( target.IsFriend(self.target) || SWWMUtility.IdentifyingDog(target) ) - { - 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),ST_Health); - SWWMHandler.DoFlash(target,Color(32,224,128,255),10); - } - if ( SWWMUtility.IdentifyingDog(target) ) - { - // befriend good doggo - if ( target.bCOUNTKILL ) - { - target.bCOUNTKILL = false; - level.total_monsters--; - } - if ( !target.bFRIENDLY && s ) - s.befriend++; - target.bFRIENDLY = true; - if ( deathmatch ) - target.SetFriendPlayer(self.target.player); - } - return 0; - } - Vector3 dirto = level.Vec3Diff(pos,target.Vec3Offset(0,0,target.Height/2)).unit(); - SWWMUtility.DoKnockback(target,dirto,1500.*damage); - let bread = target.FindState("Pain"); - if ( bread ) target.SetState(bread); - if ( raging ) - { - damage *= 8; - raging.DoHitFX(); - } - if ( (target is 'WolfensteinSS') || (target.Species == 'WolfensteinSS') ) - { - damage = int.max; - bEXTREMEDEATH = true; - bNOEXTREMEDEATH = false; - } - else if ( target is 'SWWMHangingKeen' ) - damage = max(target.Health,damage); // rescued by love :3 - else if ( SWWMHDoomHandler.IsCuteGirl(target) ) - { - // no cute demon girl can resist demo's charm - damage = max(target.Health,damage); - bEXTREMEDEATH = false; - bNOEXTREMEDEATH = true; - } - 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*GameTicRate)); - 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 cl ) LinePart = 2; - else LinePart = 1; - } - else LinePart = 1; // always middle - if ( Blocking3DFloor ) - { - for ( int i=0; i PlayerPawn(user.player.mo).UseRange ) return false; - if ( user is 'Demolitionist' ) - { - patter = user; - let g = SWWMGesture.SetGesture(Demolitionist(patter),GS_Headpat); - if ( !g ) return false; // can't headpat at the moment - patting = true; - g.pats = self; - oldtargettics = target.tics; - target.tics = -1; - patter.player.cheats |= CF_TOTALLYFROZEN; - Demolitionist(patter).scriptedinvul = true; - target.bDORMANT = true; - return true; - } - return false; - } -} - -Class HHitList -{ - Actor a; - Vector3 dir; -} - -enum EGestureSlot -{ - // special use - GS_Headpat = -50, - GS_Grenade, - GS_EmptyMelee, - // no gesture - GS_Null = 0, - // general gestures - GS_Wave = 1, - GS_ThumbsUp, - GS_Victory, - GS_BlowKiss -}; - -// First person gestures -Class SWWMGesture : SWWMWeapon -{ - Weapon formerweapon; - int whichgesture, nextgesture; - bool deaded, queued; - bool whichuse; - Class whichweapon; - Array suse; - Array > sweapon; - int gonect; - HeadpatTracker pats; // for headpat gesture, our current tracker - - // these should prevent autoswitch when out of ammo - override bool ReportHUDAmmo() - { - return false; - } - override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) - { - return false; - } - override bool Use( bool pickup ) - { - return false; - } - override void DoEffect() - { - Super.DoEffect(); - if ( !Owner || !Owner.player || (Owner.player.ReadyWeapon != self) ) - return; - let psp = Owner.player.FindPSprite(PSP_WEAPON); - if ( !psp ) return; - if ( (Owner.Health <= 0) && (psp.CurState != ResolveState("Deselect")) ) - Owner.player.SetPSprite(PSP_WEAPON,ResolveState("Deselect")); - } - - static SWWMGesture SetGesture( PlayerPawn mo, int which ) - { - if ( !mo || !(mo is 'Demolitionist') ) return null; // only Demo - if ( mo.Health <= 0 ) return null; // dead - if ( mo.player.cheats&CF_TOTALLYFROZEN ) return null; // frozen today - SWWMGesture w = SWWMGesture(mo.FindInventory("SWWMGesture")); - if ( w && ((mo.player.PendingWeapon is 'SWWMGesture') || (mo.player.ReadyWeapon is 'SWWMGesture') - || (mo.player.PendingWeapon is 'SWWMItemGesture') || (mo.player.ReadyWeapon is 'SWWMItemGesture')) ) - { - // already gesturing - // just queue another one - if ( which <= 0 ) return null; // these gestures can't be queued - else - { - w.nextgesture = which; - w.queued = true; - } - return null; - } - if ( !w ) - { - w = SWWMGesture(Spawn("SWWMGesture")); - mo.AddInventory(w); - } - if ( mo.player.PendingWeapon != WP_NOCHANGE ) w.formerweapon = mo.player.PendingWeapon; - else w.formerweapon = mo.player.ReadyWeapon; - w.whichweapon = null; - w.whichgesture = which; - mo.player.PendingWeapon = w; - return w; - } - - // "special" gestures are run by switching to another "weapon" - static SWWMGesture SetSpecialGesture( PlayerPawn mo, Class a, bool used = false ) - { - if ( !mo || !(mo is 'Demolitionist') ) return null; // only Demo - if ( mo.Health <= 0 ) return null; // dead - if ( mo.player.cheats&CF_TOTALLYFROZEN ) return null; // frozen today - if ( !a ) return null; - SWWMGesture w = SWWMGesture(mo.FindInventory("SWWMGesture")); - if ( w && ((mo.player.PendingWeapon is 'SWWMGesture') || (mo.player.ReadyWeapon is 'SWWMGesture') - || (mo.player.PendingWeapon is 'SWWMItemGesture') || (mo.player.ReadyWeapon is 'SWWMItemGesture')) ) - { - // already gesturing - // queue if unique - for ( int i=0; i 0 ) - { - invoker.whichgesture = GS_Null; - invoker.whichweapon = invoker.sweapon[0]; - invoker.whichuse = invoker.suse[0]; - // push back - invoker.sweapon.Delete(0); - invoker.suse.Delete(0); - player.SetPSprite(PSP_WEAPON,ResolveState("Ready")); - return; - } - if ( invoker.queued ) - { - invoker.whichweapon = null; - invoker.whichgesture = invoker.nextgesture; - invoker.queued = false; - player.SetPSprite(PSP_WEAPON,ResolveState("Ready")); - return; - } - player.PendingWeapon = invoker.formerweapon; - player.SetPSprite(PSP_WEAPON,ResolveState("Deselect")); - } - - action void A_Headpat() - { - A_StartSound("demolitionist/petting",CHAN_WEAPON,CHANF_OVERLAP,.4); - let pt = invoker.pats; - if ( !pt ) return; - int numpt = Random[ExploS](6,9); - Vector3 dir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); - Vector3 patpos = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz-4),dir*30.); - for ( int i=0; i 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 ) + { + let b = Spawn("SWWMBubble",pos); + b.vel = vel; + b.scale *= scale.x; + Destroy(); + } + } + Loop; + } +} + +Class DemolitionistRadiusShockwaveTail : Actor +{ + Default + { + RenderStyle "Add"; + Radius 16; + Height 8; + +NOBLOCKMAP; + +NOGRAVITY; + +DONTSPLASH; + +NOTELEPORT; + +NOINTERACTION; + } + States + { + Spawn: + XZW1 A 1 + { + pitch = min(85,(pitch+2)*1.05); + A_FadeOut(.02); + A_SetScale(scale.x*1.08,scale.y); + vel *= .98; + } + Wait; + } +} + +Class DemolitionistRadiusShockwave : Actor +{ + Actor lasthit; + + Default + { + RenderStyle "Add"; + Speed 15; + DamageFunction int(200*alpha); + DamageType "GroundPound"; + Radius 16; + Height 8; + Alpha .4; + XScale .65; + YScale 3.; + PROJECTILE; + +DONTSPLASH; + +STEPMISSILE; + +NOEXPLODEFLOOR; + +FLATSPRITE; + +RIPPER; + +BLOODLESSIMPACT; + -NOGRAVITY; + } + override int DoSpecialDamage( Actor target, int damage, Name damagetype ) + { + if ( target == lasthit ) return 0; + lasthit = target; + if ( damage <= 0 ) return damage; + if ( (target.mass < LARGE_MASS) && !target.bDONTTHRUST ) + { + target.vel.xy += vel.xy.unit()*(30000./max(50,target.mass))*alpha; + if ( (target.pos.z <= floorz) || !target.TestMobjZ() ) + target.vel.z += (4000./max(50,target.mass))*alpha; + } + return damage; + } + States + { + Spawn: + XZW1 A 1 + { + SetZ(floorz); + pitch = min(85,(pitch+2)*1.05); + if ( !Random[ExploS](0,3) ) + Spawn("InvisibleSplasher",Vec3Offset(0,0,2)); + let s = Spawn("DemolitionistRadiusShockwaveTail",pos); + s.vel = vel*.35; + s.scale = scale; + s.alpha = alpha*.4; + s.angle = angle; + s.pitch = pitch; + s.roll = roll; + A_FadeOut(.015); + A_SetScale(scale.x*1.08,scale.y); + vel *= .98; + } + Wait; + Death: + XZW1 A 1 + { + SetZ(floorz); + A_FadeOut(.05); + A_SetScale(scale.x*1.1,scale.y*.97); + } + Wait; + } +} + +Class DemolitionistShockwave : Actor +{ + Default + { + +NOGRAVITY; + +NOBLOCKMAP; + +NOTELEPORT; + +NODAMAGETHRUST; + +FORCERADIUSDMG; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_QuakeEx(7,7,7,30,0,300+min(special1,50)*4,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:1.5); + if ( target.player != players[consoleplayer] ) + { + A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.3); + A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.2,pitch:.7); + A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,attenuation:.1,pitch:.4); + } + SWWMUtility.DoExplosion(self,40+min(special1,120),100000+min(special1*2000,150000),100+min(special1*2,130),80,DE_BLAST|DE_EXTRAZTHRUST,'GroundPound',target); + for ( int i=0; i<360; i+=5 ) + { + Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3); + let s = Spawn("SWWMSmoke",Vec3Angle(4,i,8)); + s.vel = pvel+(cos(i),sin(i),0)*7.; + s.SetShade(Color(1,1,1)*Random[ExploS](64,224)); + s.special1 = Random[ExploS](1,4); + s.scale *= 1.5; + s.alpha *= .4; + } + if ( pos.z > floorz+16 ) return; + for ( int i=0; i<360; i+=5 ) + { + let r = Spawn("DemolitionistRadiusShockwave",Vec3Angle(5,i)); + r.target = target; + r.angle = i; + r.vel.xy = (cos(i),sin(i))*(r.speed+min(special1*.15,30)); + r.alpha *= .1+min(special1*.03,.9); + } + int numpt = Random[ExploS](10,20); + for ( int i=0; i rgb; + str.Split(rgb,",",0); + tagcolor = Color(255,rgb[0].ToInt(),rgb[1].ToInt(),rgb[2].ToInt()); + } + else tagcolor = Color(255,32,48,24); + } + bool curactive = activelight(); + // have to re-attach it repeatedly to update the pitch (wow great thanks) + if ( curactive ) + target.A_AttachLight('DemoSelfLight',DynamicLight.PointLight,Color(112,144,176),200,0,DynamicLight.LF_DONTLIGHTSELF|DynamicLight.LF_ATTENUATE|DynamicLight.LF_SPOT,(5,0,target.player?(target.player.viewz-target.pos.z):(target.height*.93)),0,15,60,target.pitch); + else if ( !curactive && oldactive ) + target.A_AttachLight('DemoSelfLight',DynamicLight.PointLight,0,0,0); + oldactive = curactive; + bool curglow = !(target.bINVISIBLE||(target.alpha <= double.epsilon)); + if ( curglow && !oldglow ) target.A_AttachLight('DemoSelfLight2',DynamicLight.PointLight,tagcolor,80,0,DynamicLight.LF_DONTLIGHTSELF|DynamicLight.LF_ATTENUATE,(0,0,target.height/2)); + else if ( !curglow && oldglow ) target.A_AttachLight('DemoSelfLight2',DynamicLight.PointLight,0,0,0); + oldglow = curglow; + } +} diff --git a/zscript/swwm_player_items.zsc b/zscript/swwm_player_items.zsc new file mode 100644 index 000000000..0cde432dd --- /dev/null +++ b/zscript/swwm_player_items.zsc @@ -0,0 +1,172 @@ +// player-specific item stuff + +// lucky collar +// made by Ashley Knox, given to you and Ibuki by Saya +Class SayaCollar : Inventory +{ + Default + { + +INVENTORY.UNDROPPABLE; + +INVENTORY.UNTOSSABLE; + +INVENTORY.UNCLEARABLE; + } + override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) + { + if ( (damage <= 0) || (flags&(DMG_FORCED|DMG_NO_ARMOR)) ) return; + newdamage = damage; + // oopsies are halved + if ( source == Owner ) newdamage = max(1,newdamage/2); + // in danger? reduce to a quarter + if ( (Owner.Health-newdamage < 25) ) + { + int splitdmg[2]; + splitdmg[0] = max(0,Owner.Health-25); // non-reduced part (>=25% health) + splitdmg[1] = max(1,(newdamage-splitdmg[0])/4); // reduced part (<25% health) + newdamage = splitdmg[0]+splitdmg[1]; + } + } + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + // if first item is health or sandwich, ignore + if ( (other.Inv is 'SWWMHealth') || (other.Inv is 'GrilledCheeseSandwich') ) + return; + // if there's items before health/sandwich, squeeze right in + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( (i == self) || (!(i.Inv is 'SWWMHealth' ) && !(i.Inv is 'GrilledCheeseSandwich')) ) continue; + Inventory saved = i.Inv; + i.Inv = self; + other.Inv = Inv; + Inv = saved; + break; + } + } +} + +// high-resonant almasteel plating +// EXTRA THICC as Saya requested +Class AlmasteelPlating : Inventory +{ + Inventory dbf; + + Default + { + +INVENTORY.UNDROPPABLE; + +INVENTORY.UNTOSSABLE; + +INVENTORY.UNCLEARABLE; + Inventory.RestrictedTo "Demolitionist"; + } + override void DoEffect() + { + Super.DoEffect(); + if ( !dbf ) return; + dbf.Amount = int(dbf.Amount*.95-1); // rapidly dissipate Telebrium corrosion + } + override void AbsorbDamage( int damage, Name damageType, out int newdamage, Actor inflictor, Actor source, int flags ) + { + if ( inflictor && (inflictor is 'CorrodeDebuff') ) dbf = Inventory(inflictor); + if ( (damage <= 0) || (flags&(DMG_FORCED|DMG_NO_ARMOR)) ) return; + newdamage = damage; + // 80% reduction for explosions + if ( flags&DMG_EXPLOSION ) newdamage = newdamage/5; + // 50% reduction for crushing + if ( damageType == 'Crush' ) + { + newdamage = newdamage/2; + // additionally, check if we can break any active crushers + double gaph = (Owner.ceilingz-Owner.floorz); + if ( gaph > Owner.height*.6 ) return; + // the smaller the gap, the more likely the crusher will snap + if ( Random[Demolitionist](0,3) && (FRandom[Demolitionist](0,gaph/Owner.height) > .1) ) return; + double diffh = 8.+(Owner.default.height-gaph); // how much the crusher will have to "snap" after breaking + let ceil = Owner.ceilingsector; + let flor = Owner.floorsector; + let ceilse = ceil.ceilingdata; + let florse = flor.floordata; + if ( ceilse && florse ) + { + // snap both planes + let q = Spawn("BustedQuake",(ceil.centerspot.x,ceil.centerspot.y,ceil.ceilingplane.ZAtPoint(ceil.centerspot))); + q.special1 = 6; + q = Spawn("BustedQuake",(flor.centerspot.x,flor.centerspot.y,flor.floorplane.ZAtPoint(flor.centerspot))); + q.special1 = 6; + SWWMCrusherBroken.Create(flor,ceil,diffh/2.); + } + else if ( ceilse ) + { + // snap ceiling + let q = Spawn("BustedQuake",(ceil.centerspot.x,ceil.centerspot.y,ceil.ceilingplane.ZAtPoint(ceil.centerspot))); + q.special1 = 10; + SWWMCrusherBroken.Create(null,ceil,diffh); + } + else if ( florse ) + { + // snap floor + let q = Spawn("BustedQuake",(flor.centerspot.x,flor.centerspot.y,flor.floorplane.ZAtPoint(flor.centerspot))); + q.special1 = 10; + SWWMCrusherBroken.Create(flor,null,diffh); + } + } + } + override bool HandlePickup( Inventory item ) + { + // disallow vanilla armors + if ( (item is 'BasicArmor') || (item is 'BasicArmorBonus') || (item is 'BasicArmorPickup') || (item is 'HexenArmor') ) + { + item.bPickupGood = true; // but act as if we picked them up + return true; + } + return false; + } + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + // if first item is the collar, just ignore + if ( other.Inv is 'SayaCollar' ) return; + // if there's items before collar, squeeze right in + for ( Inventory i=other.Inv; i; i=i.Inv ) + { + if ( (i == self) || !(i.Inv is 'SayaCollar') ) continue; + Inventory saved = i.Inv; + i.Inv = self; + other.Inv = Inv; + Inv = saved; + break; + } + } +} + +Class ReviveCooldown : Powerup +{ + Default + { + Inventory.Icon "graphics/HUD/Icons/I_Revive.png"; + Powerup.Duration -30; + } + + override void Tick() + { + if ( !Owner ) Destroy(); + if ( Owner.Health <= 0 ) return; // timer does not go down when dead + if ( (EffectTics == 0) || ((EffectTics > 0) && (--EffectTics == 0)) ) + Destroy (); + } + override void InitEffect() + { + Super.InitEffect(); + // adjust the duration + EffectTics = max(0,swwm_revivecooldown)*GameTicRate; + } + override void EndEffect() + { + Super.EndEffect(); + if ( !Owner ) return; + Owner.A_StartSound("demolitionist/revive",CHAN_ITEMEXTRA); + if ( (EffectTics <= 0) && Owner && Owner.CheckLocalView() ) Console.Printf(StringTable.Localize("$D_REFAIL")); + } + override void OwnerDied() + { + // do nothing, this "powerup" is preserved on death + } +} diff --git a/zscript/swwm_statichandler.zsc b/zscript/swwm_statichandler.zsc new file mode 100644 index 000000000..d6abd88d0 --- /dev/null +++ b/zscript/swwm_statichandler.zsc @@ -0,0 +1,223 @@ +// Static handler responsible for some special stuff + +// save version holder +Class SWWMSaveVerData : Thinker +{ + String ver; +} + +Class SWWMStaticHandler : StaticEventHandler +{ + // crash handler + ui bool wasinmap; + ui int timer; + // versioning + bool tainted; + String taintver; + bool mptaint[MAXPLAYERS]; + bool mprecv[MAXPLAYERS]; + String mpver[MAXPLAYERS]; + int checktic; + ui bool mpsent, checked; + + override void NewGame() + { + // set save version every new session + let svd = new("SWWMSaveVerData"); + svd.ChangeStatNum(Thinker.STAT_STATIC); + svd.ver = StringTable.Localize("$SWWM_SHORTVER"); + } + + override void WorldLoaded( WorldEvent e ) + { + // save version checker + if ( !e.IsSaveGame ) return; + checktic = gametic+5; + let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC); + let svd = SWWMSaveVerData(ti.Next()); + if ( !svd ) + { + tainted = true; + taintver = "\cg(no version info)\c-"; + return; + } + String cver = StringTable.Localize("$SWWM_SHORTVER"); + if ( svd.ver != cver ) + { + tainted = true; + taintver = svd.ver; + } + } + + override void OnRegister() + { + // preload various fonts + Font.GetFont('k6x8'); + Font.GetFont('k6x8Shaded'); + Font.GetFont('k6x8ShadedInverse'); + Font.GetFont('Miniwi'); + Font.GetFont('MiniwiShaded'); + Font.GetFont('MiniwiShadedInverse'); + Font.GetFont('MPlus'); + Font.GetFont('MPlusShaded'); + Font.GetFont('MPlusShadedInverse'); + Font.GetFont('Tewi'); + Font.GetFont('TewiShaded'); + Font.GetFont('TewiShadedInverse'); + Font.GetFont('SWWMBigFont'); + } + + override void ConsoleProcess( ConsoleEvent e ) + { + static const Name mmvars[] = + { + 'swwm_mm_backcolor', 'swwm_mm_cdwallcolor', + 'swwm_mm_efwallcolor', 'swwm_mm_fdwallcolor', + 'swwm_mm_gridcolor', 'swwm_mm_interlevelcolor', + 'swwm_mm_intralevelcolor', 'swwm_mm_lockedcolor', + 'swwm_mm_notseencolor', 'swwm_mm_portalcolor', + 'swwm_mm_secretsectorcolor', 'swwm_mm_secretwallcolor', + 'swwm_mm_specialwallcolor', 'swwm_mm_thingcolor', + 'swwm_mm_thingcolor_citem', 'swwm_mm_thingcolor_friend', + 'swwm_mm_thingcolor_item', 'swwm_mm_thingcolor_monster', + 'swwm_mm_thingcolor_ncmonster', 'swwm_mm_thingcolor_shootable', + 'swwm_mm_thingcolor_vipitem', 'swwm_mm_tswallcolor', + 'swwm_mm_unexploredsecretcolor', 'swwm_mm_wallcolor', + 'swwm_mm_xhaircolor', 'swwm_mm_yourcolor' + }; + if ( e.Name ~== "swwmresetmmcolors" ) + { + for ( int i=0; i 0)) ) + { + wasinmap = false; + if ( timer == 1 ) + { + Console.Printf("\cfOopsie Woopsie!\c-"); + let hnd = SWWMBrutalHandler(StaticEventHandler.Find("SWWMBrutalHandler")); + if ( hnd && hnd.detected ) + { + S_StartSound("crash/glass",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); + S_StartSound("crash/glass",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); + } + else S_StartSound("crash/crash",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); + } + else if ( timer == 70 ) + { + Console.Printf("\cfLooks like GZDoom made a fucky wucky! owo\c-"); + S_StartSound("crash/curb",CHAN_YOUDONEFUCKEDUP,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE); + } + else if ( timer == 140 ) + { + let hnd = SWWMBrutalHandler(StaticEventHandler.Find("SWWMBrutalHandler")); + if ( hnd && hnd.detected ) Console.Printf("\cfDon't blame me. Shouldn't have tried running this with Brutal Doom.\c-"); + else Console.Printf("\cfIf you didn't trigger it manually, it's best if you take a screenshot and show it to Marisa.\c-"); + Console.Printf("\cfLoaded Version:\n \cj%s\c-",StringTable.Localize("$SWWM_SHORTVER")); + if ( tainted ) Console.Printf("\cfSavegame Version:\n \cj%s\c-",taintver); + } + timer++; + } + } +} + diff --git a/zscript/swwm_thinkers.zsc b/zscript/swwm_thinkers.zsc index 64bd7aa2a..68eb1d0b4 100644 --- a/zscript/swwm_thinkers.zsc +++ b/zscript/swwm_thinkers.zsc @@ -1,1045 +1,5 @@ // various stat tracking thinkers and others -// Stats -Class WeaponUsage -{ - Class w; - int kills; -} - -Class MonsterKill -{ - Class m; - int kills; -} - -Class LevelStat -{ - bool hub; - String levelname, mapname; - int kcount, ktotal; - int icount, itotal; - int scount, stotal; - int time, par, suck; -} - -Class SWWMStats : Thinker -{ - PlayerInfo myplayer; - int lastspawn, dashcount, boostcount, stompcount, airtime, kills, - deaths, damagedealt, hdamagedealt, damagetaken, hdamagetaken, - mkill, hiscore, hhiscore, topdealt, toptaken, skill, wponch, - busts, buttslams, secrets, items, parries, pparries, pats, - befriend, smooch; - double grounddist, airdist, swimdist, fuelusage, topspeed, teledist; - Array wstats; - Array mstats; - Array lstats; - Array > alreadygot; - int favweapon; - // these two are used for mission updates - Array clustervisit; - Array secretdone; - // [Strife] preserve previous mission logs - String oldlogtext; - Array questbacklog; - // hackaround for stuff getting lost - Array > ownedcollectibles; - // for pistol start info (to avoid it within hubs) - int lastcluster; - - bool GotWeapon( Class which ) - { - for ( int i=0; i WeaponFromInflictor( Actor inflictor, Name damagetype ) - { - 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'; - else if ( (inflictor is 'ExplodiumMagArm') || (inflictor is 'ExplodiumMagProj') || (inflictor is 'ExplodiumBulletImpact') ) which = 'ExplodiumGun'; - else if ( (inflictor is 'DragonBreathArm') || ((inflictor is 'SaltImpact') && !inflictor.Args[0]) || ((inflictor is 'SaltBeam') && !inflictor.Args[1]) || (inflictor is 'CorrodeDebuff') || (inflictor is 'CorrosiveFlechette') || ((inflictor is 'TheBall') && !inflictor.special1) || (inflictor is 'GoldenImpact') || (inflictor is 'GoldenSubImpact') || (inflictor is 'GoldenSubSubImpact') ) which = 'Spreadgun'; - else if ( ((inflictor is 'SaltImpact') && inflictor.Args[0]) || ((inflictor is 'SaltBeam') && inflictor.Args[1]) || ((inflictor is 'TheBall') && inflictor.special1) ) which = 'Wallbuster'; - else if ( (inflictor is 'EvisceratorChunk') || (inflictor is 'EvisceratorProj') ) which = 'Eviscerator'; - else if ( (inflictor is 'HellblazerRavagerArm') || (inflictor is 'HellblazerWarheadArm') ) which = 'Hellblazer'; - else if ( (inflictor is 'BigBiospark') || (inflictor is 'BiosparkBall') || (inflictor is 'BiosparkBeamImpact') || (inflictor is 'BiosparkComboImpact') || (inflictor is 'BiosparkComboImpactSub') || (inflictor is 'BiosparkBeam') || (inflictor is 'BiosparkArc') ) which = 'Sparkster'; - else if ( (inflictor is 'CandyBeam') || (inflictor is 'CandyPop') || (inflictor is 'CandyMagArm') || (inflictor is 'CandyGunProj') || (inflictor is 'CandyMagProj') || (inflictor is 'CandyBulletImpact') ) which = 'CandyGun'; - else if ( (inflictor is 'YnykronBeam') || (inflictor is 'YnykronImpact') || (inflictor is 'YnykronSingularity') || (inflictor is 'YnykronCloud') || (inflictor is 'YnykronVoidBeam') || (inflictor is 'YnykronLightningArc') || (inflictor is 'YnykronLightningImpact') ) which = 'Ynykron'; - else if ( (inflictor is 'Demolitionist') || (inflictor is 'DemolitionistShockwave') || (inflictor is 'DemolitionistRadiusShockwave') || (inflictor is 'SWWMGesture') - || (inflictor is 'SWWMItemGesture') ) which = 'SWWMWeapon'; // hack to assume Demolitionist as weapon - else if ( inflictor is 'BigPunchSplash' ) - { - // guess from damagetype - if ( (damagetype == 'Jump') || (damagetype == 'Dash') || (damagetype == 'Buttslam') || (damagetype == 'GroundPound') ) - which = 'SWWMWeapon'; - else if ( damagetype == 'Love' ) - which = 'SWWMGesture'; - // others are just weapon melee, so keep the readyweapon - } - else if ( inflictor is 'FroggyChair' ) - which = 'SWWMItemGesture'; // more gross hacks - if ( damagetype == 'Falling' ) - which = 'Weapon'; // the gross hacks continue - return which; - } - - void AddDamageDealt( int dmg ) - { - int upper = dmg/1000000000; - int lower = dmg%1000000000; - if ( hdamagedealt+upper > 999999999 ) hdamagedealt = 999999999; - else hdamagedealt += upper; - damagedealt += lower; - if ( damagedealt > 999999999 ) - { - upper = damagedealt/1000000000; - lower = damagedealt%1000000000; - if ( hdamagedealt+upper > 999999999 ) hdamagedealt = 999999999; - else hdamagedealt += upper; - damagedealt = lower; - } - } - void AddDamageTaken( int dmg ) - { - int upper = dmg/1000000000; - int lower = dmg%1000000000; - if ( hdamagetaken+upper > 999999999 ) hdamagetaken = 999999999; - else hdamagetaken += upper; - damagetaken += lower; - if ( damagetaken > 999999999 ) - { - upper = damagetaken/1000000000; - lower = damagetaken%1000000000; - if ( hdamagetaken+upper > 999999999 ) hdamagetaken = 999999999; - else hdamagetaken += upper; - damagetaken = lower; - } - } - - void AddLevelStats() - { - let ls = new("LevelStat"); - ls.hub = !!(level.clusterflags&level.CLUSTER_HUB); - ls.levelname = level.levelname; - int iof = ls.levelname.IndexOf(" - by: "); - if ( iof != -1 ) ls.levelname.Truncate(iof); - ls.mapname = level.mapname; - ls.kcount = level.killed_monsters; - ls.ktotal = level.total_monsters; - ls.icount = level.found_items; - ls.itotal = level.total_items; - ls.scount = level.found_secrets; - ls.stotal = level.total_secrets; - ls.time = level.maptime; - ls.par = level.partime; - ls.suck = level.sucktime; - lstats.Push(ls); - } - - void AddWeaponKill( Actor inflictor, Actor victim, Name damagetype ) - { - if ( victim ) - { - bool found = false; - for ( int i=0; i which = WeaponFromInflictor(inflictor,damagetype); - if ( !which ) return; - for ( int i=0; i 999999999 ) - { - c.credits -= 1000000000; - c.hcredits++; - } - if ( (c.hcredits+hamount < c.hcredits) || (c.hcredits+hamount > 999999999) ) c.hcredits = 999999999; - else c.hcredits += hamount; - let s = SWWMStats.Find(p); - if ( s && ((c.hcredits > s.hhiscore) || ((c.credits > s.hiscore) && (c.hcredits >= s.hhiscore))) ) - { - s.hiscore = c.credits; - s.hhiscore = c.hcredits; - } - } - - static clearscope bool CanTake( PlayerInfo p, int amount, int hamount = 0 ) - { - let c = Find(p); - if ( !c ) return false; - int req = amount, hreq = hamount; - while ( req > 999999999 ) - { - req -= 1000000000; - hreq++; - } - // waaaaay too much - if ( (c.hcredits-hreq < 0) || (c.hcredits-hreq > c.hcredits) ) return false; - // too much! - if ( ((c.credits-amount < 0) || (c.credits-amount > c.credits)) && (c.hcredits-hreq <= 0) ) return false; - return true; - } - - static bool Take( PlayerInfo p, int amount, int hamount = 0 ) - { - let c = Find(p); - if ( !c ) return false; - int req = amount, hreq = hamount; - while ( req > 999999999 ) - { - req -= 1000000000; - hreq++; - } - // waaaaay too much - if ( (c.hcredits-hreq < 0) || (c.hcredits-hreq > c.hcredits) ) return false; - // too much! - if ( ((c.credits-amount < 0) || (c.credits-amount > c.credits)) && (c.hcredits-hreq <= 0) ) return false; - c.hcredits -= hreq; - c.credits -= req; - while ( c.credits < 0 ) - { - c.credits += 1000000000; - c.hcredits--; - } - return true; - } - - static clearscope int, int Get( PlayerInfo p ) - { - let c = Find(p); - if ( !c ) return 0; - return c.credits, c.hcredits; - } - - static clearscope SWWMCredits Find( PlayerInfo p ) - { - let ti = ThinkerIterator.Create("SWWMCredits",STAT_STATIC); - SWWMCredits t; - while ( t = SWWMCredits(ti.Next()) ) - { - if ( t.myplayer != p ) continue; - return t; - } - return null; - } -} - -// Trading history between players -Class SWWMTrade -{ - int timestamp, type, amt; - String other; - Class what; -} - -Class SWWMTradeHistory : Thinker -{ - PlayerInfo myplayer; - Array ent; - - static void RegisterSend( PlayerInfo p, PlayerInfo other, Class what, int amt ) - { - let th = Find(p); - if ( !th ) return; - SWWMTrade t = new("SWWMTrade"); - t.timestamp = level.totaltime; - t.type = 0; - t.other = other.GetUserName(); - t.what = what; - t.amt = amt; - th.ent.Push(t); - } - static void RegisterReceive( PlayerInfo p, PlayerInfo other, Class what, int amt ) - { - let th = Find(p); - if ( !th ) return; - SWWMTrade t = new("SWWMTrade"); - t.timestamp = level.totaltime; - t.type = 1; - t.other = other.GetUserName(); - t.what = what; - t.amt = amt; - th.ent.Push(t); - } - - static clearscope SWWMTradeHistory Find( PlayerInfo p ) - { - let ti = ThinkerIterator.Create("SWWMTradeHistory",STAT_STATIC); - SWWMTradeHistory th; - while ( th = SWWMTradeHistory(ti.Next()) ) - { - if ( th.myplayer != p ) continue; - return th; - } - return Null; - } -} - -// Lore holder -enum ELoreTab -{ - LORE_ITEM, - LORE_PEOPLE, - LORE_LORE // lol -}; - -Class SWWMLore -{ - String tag, text, assoc; - int tab; - bool read; -} - -Class SWWMLoreLibrary : Thinker -{ - PlayerInfo myplayer; - Array ent; - int lastaddtic; - - static bool PreVerify( String ref ) - { - // restrictions - if ( !(gameinfo.gametype&(GAME_Raven|GAME_Strife)) ) - { - if ( ref ~== "Parthoris" ) return true; - if ( ref ~== "Sidhe" ) return true; - if ( ref ~== "SerpentRiders" ) return true; - } - if ( !(gameinfo.gametype&(GAME_Hexen|GAME_Strife)) ) - { - if ( ref ~== "Cronos" ) return true; - if ( ref ~== "Kirin" ) return true; // not met - if ( ref ~== "Fabricator" ) return true; // not yet introduced - if ( ref ~== "Administrators" ) return true; // not met - } - if ( !(gameinfo.gametype&GAME_Strife) ) - { - if ( ref ~== "TheOrder" ) return true; - if ( ref ~== "TheFront" ) return true; - } - // check if entry is for a collectible - for ( int i=0; i)(AllActorClasses[i]); - if ( !c || (c == 'SWWMCollectible') ) continue; - let def = GetDefaultByType(c); - // skip if we match and it's not for this game - if ( (c.GetClassName() == ref) && !(gameinfo.gametype&def.avail) ) - return true; - } - ref = ref.MakeUpper(); - String tag = String.Format("SWWM_LORETAG_%s",ref); - String tab = String.Format("SWWM_LORETAB_%s",ref); - String text = String.Format("SWWM_LORETXT_%s",ref); - // check that it's valid - if ( StringTable.Localize(tag,false) == tag ) return true; - if ( StringTable.Localize(tab,false) == tab ) - { - Console.Printf("Entry \"%s\" defines no tab.",ref); - return true; - } - if ( StringTable.Localize(text,false) == text ) - { - Console.Printf("Entry \"%s\" defines no text.",ref); - return true; - } - return false; - } - - bool DirectAdd( String ref ) - { - if ( PreVerify(ref) ) return true; - return InternalAdd(ref); - } - - private bool InternalAdd( String ref ) - { - ref = ref.MakeUpper(); - String tag = String.Format("SWWM_LORETAG_%s",ref); - String tab = String.Format("SWWM_LORETAB_%s",ref); - String text = String.Format("SWWM_LORETXT_%s",ref); - String assoc = String.Format("SWWM_LOREREL_%s",ref); - // redirects - if ( gameinfo.gametype&GAME_Strife ) - { - if ( text ~== "SWWM_LORETXT_KIRIN" ) - text = "SWWM_LORETXT_KIRIN2"; // married alakir - else if ( text ~== "SWWM_LORETXT_SERPENTRIDERS" ) - text = "SWWM_LORETXT_SERPENTRIDERS3"; // all riders gone - } - if ( gameinfo.gametype&(GAME_Hexen|GAME_Strife) ) - { - if ( text ~== "SWWM_LORETXT_SAYA" ) - text = "SWWM_LORETXT_SAYA3"; // married kirin - else if ( text ~== "SWWM_LORETXT_ANARUKON" ) - text = "SWWM_LORETXT_ANARUKON2"; // comments from miyamoto-xanai wedding - else if ( text ~== "SWWM_LORETXT_HELL" ) - text = "SWWM_LORETXT_HELL3"; // met father nostros during the wedding - else if ( text ~== "SWWM_LORETXT_NANA" ) - text = "SWWM_LORETXT_NANA3"; // stuff that happened at the wedding - else if ( text ~== "SWWM_LORETXT_GHOULHUNT" ) - text = "SWWM_LORETXT_GHOULHUNT2"; // met anthon anderken during the wedding - else if ( text ~== "SWWM_LORETXT_RAGEKIT" ) - text = "SWWM_LORETXT_RAGEKIT2"; // kirin's reactions to demo using this item - else if ( text ~== "SWWM_LORETXT_SANKAIDERIHA" ) - text = "SWWM_LORETXT_SANKAIDERIHA2"; // comments about kirin - else if ( text ~== "SWWM_LORETXT_SERPENTRIDERS" ) - text = "SWWM_LORETXT_SERPENTRIDERS2"; // defeated d'sparil - else if ( text ~== "SWWM_LORETXT_XANIMEN" ) - text = "SWWM_LORETXT_XANIMEN2"; // footnote about nuoma - else if ( text ~== "SWWM_LORETXT_ZANAVETH2" ) - text = "SWWM_LORETXT_ZANAVETH22"; // met at wedding - else if ( text ~== "SWWM_LORETXT_YNYKRON" ) - text = "SWWM_LORETXT_YNYKRON2"; // confirmed to harm (but not kill) gods - else if ( text ~== "SWWM_LORETXT_AKARIPROJECT" ) - text = "SWWM_LORETXT_AKARIPROJECT3"; // mentions kirin - else if ( text ~== "SWWM_LORETXT_GODS" ) - text = "SWWM_LORETXT_GODS2"; // beyond gods - } - if ( gameinfo.gametype&(GAME_Raven|GAME_Strife) ) - { - if ( text ~== "SWWM_LORETXT_SAYA" ) - text = "SWWM_LORETXT_SAYA2"; // dating demo - else if ( text ~== "SWWM_LORETXT_AKARILABS" ) - text = "SWWM_LORETXT_AKARILABS2"; // demo won, akari project announced - else if ( text ~== "SWWM_LORETXT_DEMOLITIONIST" ) - text = "SWWM_LORETXT_DEMOLITIONIST2"; // demo rewarded with maidbot frame - else if ( text ~== "SWWM_LORETXT_DOOMGUY" ) - text = "SWWM_LORETXT_DOOMGUY2"; // he gone - else if ( text ~== "SWWM_LORETXT_UAC" ) - text = "SWWM_LORETXT_UAC2"; // uac "reformed" - else if ( text ~== "SWWM_LORETXT_HELL" ) - text = "SWWM_LORETXT_HELL2"; // invasion was a thing of the past - else if ( text ~== "SWWM_LORETXT_NANA" ) - text = "SWWM_LORETXT_NANA2"; // demo met nana - else if ( text ~== "SWWM_LORETXT_ZANAVETH3" ) - text = "SWWM_LORETXT_ZANAVETH32"; // iagb happened - else if ( text ~== "SWWM_LORETXT_BIGSHOT" ) - text = "SWWM_LORETXT_BIGSHOT2"; // predictions about crimes_m - else if ( text ~== "SWWM_LORETXT_AKARIPROJECT" ) - text = "SWWM_LORETXT_AKARIPROJECT2"; // fiction becomes reality - } - // check if existing - for ( int i=0; i 0) && (gametic > lastaddtic) && (myplayer == players[consoleplayer]) && (!menuactive || (menuactive == Menu.OnNoPause)) && (myplayer.mo is 'Demolitionist') ) - Console.Printf(StringTable.Localize("$SWWM_NEWLORE")); - lastaddtic = gametic; - ent.Push(e); - return true; - } - - static void Add( PlayerInfo p, String ref ) - { - if ( PreVerify(ref) ) return; - SWWMLoreLibrary ll = Find(p); - if ( !ll ) - { - ll = new("SWWMLoreLibrary"); - ll.ChangeStatNum(STAT_STATIC); - ll.myplayer = p; - } - ll.InternalAdd(ref); - } - - void MarkRead( int idx ) - { - if ( (idx < 0) || (idx >= ent.Size()) ) return; - if ( !ent[idx].read ) - { - ent[idx].read = true; - // add associated entries - Array rel; - rel.Clear(); - String assocstr = StringTable.Localize(ent[idx].assoc); - assocstr.Split(rel,";",0); - for ( int i=0; i ST_Score); - o.xcnt = 0; - for ( int i=0; i<6; i++ ) o.xtcolor[i] = swwm_numcolor_bonus; - o.acc = acc; - if ( o.damnum ) - { - o.next = hnd.damnums; - if ( hnd.damnums ) hnd.damnums.prev = o; - hnd.damnums = o; - hnd.damnums_cnt++; - } - else - { - o.next = hnd.scorenums; - if ( hnd.scorenums ) hnd.scorenums.prev = o; - hnd.scorenums = o; - hnd.scorenums_cnt++; - } - return o; - } - - override void OnDestroy() - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( hnd ) - { - if ( damnum ) - { - hnd.damnums_cnt--; - if ( !prev ) hnd.damnums = next; - } - else - { - hnd.scorenums_cnt--; - if ( !prev ) hnd.scorenums = next; - } - if ( !prev ) - { - if ( next ) next.prev = null; - } - else - { - prev.next = next; - if ( next ) next.prev = prev; - } - } - Super.OnDestroy(); - } - - override void Tick() - { - lifespan--; - if ( lifespan <= 0 ) Destroy(); - } -} - -enum EInterestType -{ - INT_Key, - INT_Exit -}; - -Class SWWMInterest : Thinker -{ - int type; - Key trackedkey; - Line trackedline; - Vector3 pos; - SWWMInterest prev, next; - String keytag; - - static SWWMInterest Spawn( Vector3 pos = (0,0,0), Key thekey = null, Line theline = null ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return null; - if ( (!thekey && !theline) || (thekey && theline) ) return null; - let i = new("SWWMInterest"); - i.ChangeStatNum(STAT_USER); - i.trackedkey = thekey; - i.trackedline = theline; - if ( thekey ) - { - i.type = INT_Key; - i.keytag = thekey.GetTag(); - } - else if ( theline ) i.type = INT_Exit; - else - { - i.Destroy(); - return null; - } - i.pos = thekey?thekey.Vec3Offset(0,0,thekey.height/2):pos; - i.next = hnd.intpoints; - if ( hnd.intpoints ) hnd.intpoints.prev = i; - hnd.intpoints = i; - hnd.intpoints_cnt++; - return i; - } - - override void OnDestroy() - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( hnd ) - { - hnd.intpoints_cnt--; - if ( !prev ) - { - hnd.intpoints = next; - if ( next ) next.prev = null; - } - else - { - prev.next = next; - if ( next ) next.prev = prev; - } - } - Super.OnDestroy(); - } - - override void Tick() - { - // update - if ( (type == INT_Key) && (!trackedkey || trackedkey.Owner) ) Destroy(); - else if ( trackedkey ) pos = trackedkey.Vec3Offset(0,0,trackedkey.height/2); - } -} - -Class SWWMItemSense : Thinker -{ - Actor item; - String tag; - int updated; - bool scoreitem, vipitem; - Demolitionist parent; - SWWMItemSense prev, next; - Vector3 pos; - - static SWWMItemSense Spawn( Demolitionist parent, Actor item ) - { - if ( !parent || !item ) return null; - // only refresh the updated time if existing - for ( SWWMItemSense s=parent.itemsense; s; s=s.next ) - { - if ( s.item != item ) continue; - s.updated = level.maptime+35; - s.pos = item.Vec3Offset(0,0,item.height); - return s; - } - let i = new("SWWMItemSense"); - i.ChangeStatNum(STAT_USER); - i.item = item; - i.scoreitem = SWWMUtility.IsScoreItem(item); - i.vipitem = SWWMUtility.IsVipItem(item); - i.parent = parent; - i.updated = level.maptime+35; - i.UpdateTag(); - i.pos = item.Vec3Offset(0,0,item.height); - i.next = parent.itemsense; - if ( parent.itemsense ) parent.itemsense.prev = i; - parent.itemsense = i; - parent.itemsense_cnt++; - return i; - } - - void UpdateTag() - { - if ( !item ) return; - // certain ammo types use the pickup message as it's amount-aware - if ( (item is 'RedShell') || (item is 'GreenShell') - || (item is 'WhiteShell') || (item is 'BlueShell') - || (item is 'BlackShell') || (item is 'PurpleShell') - || (item is 'GoldShell') || (item is 'SMW05Ammo') - || (item is 'SheenAmmo') ) - tag = Inventory(item).PickupMessage(); - else tag = item.GetTag(); - } - - override void OnDestroy() - { - if ( parent ) - { - parent.itemsense_cnt--; - if ( !prev ) - { - parent.itemsense = next; - if ( next ) next.prev = null; - } - else - { - prev.next = next; - if ( next ) next.prev = prev; - } - } - Super.OnDestroy(); - } - - override void Tick() - { - if ( !parent ) - { - Destroy(); - return; - } - // expire - if ( level.maptime > updated+70 ) Destroy(); - } -} - -// enemy combat tracker -Class SWWMCombatTracker : Thinker -{ - Actor mytarget; - String mytag; - int updated, lasthealth, maxhealth; - DynamicValueInterpolator intp; - Vector3 pos, prevpos, oldpos, oldprev; - PlayerInfo myplayer; - SWWMCombatTracker prev, next; - bool legged, mutated; - int tcnt; - double height; - int mxdist, dbar; - bool bBOSS, bFRIENDLY; - bool firsthit; - - void UpdateTag() - { - if ( mytarget && (mytarget.player || mytarget.bISMONSTER || (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen') || (mytarget is 'Demolitionist')) ) - { - String realtag = swwm_funtags?SWWMUtility.GetFunTag(mytarget,FallbackTag):mytarget.GetTag(FallbackTag); - if ( realtag == FallbackTag ) - { - realtag = mytarget.GetClassName(); - SWWMUtility.BeautifyClassName(realtag); - } - mytag = mytarget.player?(mytarget.player.mo!=mytarget)?String.Format(StringTable.Localize("$FN_VOODOO"),mytarget.player.GetUserName()):mytarget.player.GetUserName():realtag; - } - else mytag = ""; - } - - static SWWMCombatTracker Spawn( Actor target ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return null; - SWWMCombatTracker t; - for ( t=hnd.trackers; t; t=t.next ) - { - if ( t.mytarget != target ) continue; - return t; - } - t = new("SWWMCombatTracker"); - t.ChangeStatNum(STAT_USER); - t.mytarget = target; - t.UpdateTag(); - if ( target.player ) - { - t.lasthealth = target.health; - t.maxhealth = target.default.health; - } - else t.lasthealth = t.maxhealth = target.health; - t.updated = int.min; - t.height = target.height; - t.pos = level.Vec3Offset(target.pos,(0,0,t.height)); - t.prevpos = level.Vec3Offset(target.prev,(0,0,t.height)); - t.oldpos = target.pos; - t.oldprev = target.prev; - t.intp = DynamicValueInterpolator.Create(t.lasthealth,.5,1,100); - t.myplayer = target.player; - t.next = hnd.trackers; - t.bBOSS = target.bBOSS; - t.bFRIENDLY = target.bFRIENDLY; - if ( hnd.trackers ) - { - hnd.trackers.prev = t; - // propagate cvar values - t.mxdist = hnd.trackers.mxdist; - t.dbar = hnd.trackers.dbar; - } - hnd.trackers = t; - hnd.trackers_cnt++; - return t; - } - - override void OnDestroy() - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( hnd ) - { - hnd.trackers_cnt--; - if ( !prev ) - { - hnd.trackers = next; - if ( next ) next.prev = null; - } - else - { - prev.next = next; - if ( next ) next.prev = prev; - } - } - Super.OnDestroy(); - } - - override void Tick() - { - // only the first tracker accesses the CVars, saves on perf - if ( !prev ) - { - dbar = swwm_damagetarget; - mxdist = swwm_maxtargetdist; - } - if ( next ) - { - next.dbar = dbar; - next.mxdist = mxdist; - } - // is target gone or dead? - if ( !mytarget || (mytarget.Health <= 0) ) - { - // we're done - if ( updated > level.maptime ) updated = level.maptime; - lasthealth = 0; - prevpos = pos; // prevent stuttering - intp.Update(lasthealth); - if ( level.maptime > updated+35 ) Destroy(); - return; - } - // don't update dormant targets - if ( mytarget.bDORMANT ) - return; - // in deathmatch, don't update for hostile players - if ( deathmatch && mytarget.player && (mytarget.player.mo == mytarget) ) - { - if ( teamplay && (mytarget.player.GetTeam() != players[consoleplayer].GetTeam()) ) - return; - else if ( !teamplay ) return; - } - // only update height/position while alive - bool heightchanged = false; - if ( height != mytarget.height ) heightchanged = true; - height = mytarget.height; - if ( heightchanged || (mytarget.pos != oldpos) || (mytarget.prev != oldprev) ) - { - oldpos = mytarget.pos; - oldprev = mytarget.prev; - pos = level.Vec3Offset(mytarget.pos,(0,0,height)); - prevpos = level.Vec3Offset(mytarget.prev,(0,0,height)); - } - tcnt++; - if ( (tcnt == 1) && !mytarget.player ) - { - // post-spawn health inflation check - if ( lasthealth > maxhealth ) - { - maxhealth = lasthealth; - intp.Reset(lasthealth); - } - } - if ( (tcnt == 6) && !mytarget.player ) - { - // legendoom check - for ( Inventory i=mytarget.inv; i; i=i.inv ) - { - if ( i.GetClassName() != "LDLegendaryMonsterToken" ) continue; - legged = true; - // adjust for health inflation - if ( lasthealth > maxhealth ) - { - maxhealth = lasthealth; - intp.Reset(lasthealth); - } - } - } - if ( legged && !mutated ) - { - // check inventory regularly to mark as mutated - for ( Inventory i=mytarget.inv; i; i=i.inv ) - { - if ( i.GetClassName() != "LDLegendaryMonsterTransformed" ) continue; - mutated = true; - Console.Printf(StringTable.Localize("$SWWM_LTFORM"),mytag); - } - } - bFRIENDLY = mytarget.bFRIENDLY; - if ( mytarget.Health < lasthealth ) firsthit = true; - lasthealth = mytarget.Health; - intp.Update(lasthealth); - // special update conditions - if ( dbar ) - { - if ( (dbar == 2) && (lasthealth >= maxhealth) ) - return; - else if ( (dbar == 1) && !firsthit ) - return; - } - if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE && !mytarget.bCORPSE ) - { - bool straifu = false; - if ( (gameinfo.gametype&GAME_Strife) && (!mytarget.bINCOMBAT && !mytarget.bJUSTATTACKED) || (mytarget is 'Beggar') || (mytarget is 'Peasant') ) - straifu = true; - // players (but not voodoo dolls), always visible - if ( mytarget.player && (mytarget.player.mo == mytarget) ) updated = level.maptime+35; - // friendlies within a set distance - else if ( mytarget.bFRIENDLY && ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < mxdist)) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+35; - // enemies within a set distance that have us as target - else if ( !straifu && mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < mxdist)) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+70; - // any visible enemies within one quarter of the set distance - else if ( ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < (mxdist/4))) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime; - } - else if ( (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen') ) - { - // special stuff, only if visible - if ( ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < (mxdist/4))) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime; - } - } -} - // Korax instakill handler Class UglyBoyGetsFuckedUp : Thinker { @@ -1422,105 +382,3 @@ Class SWWMCorpseCleaner : Thinker } } } - -// ultralight trackers for certain things -Class SWWMSimpleTracker : Thinker -{ - Actor target; - double radius; - double angle; - Vector3 pos; - bool isplayer; - Color playercol; - bool ismonster; - bool friendly; - bool countkill; - bool shootable; - bool isitem; - bool countitem; - bool vipitem; - bool expired; - int lastupdate; - ui double smoothalpha; // smoothened alpha, for ui - SWWMSimpleTracker prev, next; - - void Update() - { - if ( !target ) return; - radius = target.radius; - angle = target.angle; - pos = target.pos; - isplayer = target.player; - if ( isplayer ) playercol = target.player.GetColor(); - ismonster = target.bISMONSTER; - friendly = target.IsFriend(players[consoleplayer].mo); - countkill = target.bCOUNTKILL; - shootable = target.default.bSHOOTABLE; - isitem = (target is 'Inventory'); - countitem = SWWMUtility.IsScoreItem(target); - vipitem = SWWMUtility.IsVipItem(target); - lastupdate = level.maptime; - if ( isitem ) - { - if ( !target.bSPECIAL || Inventory(target).Owner ) - expired = true; - else - { - expired = false; - lastupdate += 35; - if ( countitem ) lastupdate += 35; - if ( vipitem ) lastupdate += 70; - } - } - else if ( vipitem ) - { - if ( (target is 'Chancebox') && (target.CurState != target.SpawnState) ) - expired = true; - else - { - expired = false; - lastupdate += 70; - } - } - else if ( friendly ) - { - expired = target.bKILLED; - if ( expired ) lastupdate += 35; - else lastupdate += 140; - } - else if ( ismonster ) - { - expired = target.bKILLED; - if ( !expired ) - { - lastupdate += 35; - if ( target.target == players[consoleplayer].mo ) - lastupdate += 70; - } - } - else if ( target.default.bSHOOTABLE ) - expired = (target.Health<=0); - } - - static SWWMSimpleTracker Track( Actor target ) - { - let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); - if ( !hnd ) return null; - SWWMSimpleTracker t; - for ( t=hnd.strackers; t; t=t.next ) - { - if ( t.target != target ) continue; - t.Update(); - return t; - } - t = new("SWWMSimpleTracker"); - t.ChangeStatNum(STAT_INFO); - t.target = target; - t.Update(); - t.next = hnd.strackers; - if ( hnd.strackers ) hnd.strackers.prev = t; - hnd.strackers = t; - hnd.strackers_cnt++; - return t; - } -} diff --git a/zscript/swwm_thinkers_hud.zsc b/zscript/swwm_thinkers_hud.zsc new file mode 100644 index 000000000..8bab7f7f6 --- /dev/null +++ b/zscript/swwm_thinkers_hud.zsc @@ -0,0 +1,572 @@ +// thinkers related to the hud + +Enum EScoreObjType +{ + ST_Score, + ST_Damage, + ST_Health, + ST_Armor +}; + +// floating scores +Class SWWMScoreObj : Thinker +{ + int xcnt; + int xtcolor[6]; + int xscore[6]; + String xstr[6]; + int tcolor; + int score; + Vector3 pos; + int lifespan, initialspan; + int starttic, seed, seed2; + SWWMScoreObj prev, next; + bool damnum; + Actor acc; + + static SWWMScoreObj Spawn( int score, Vector3 pos, int type = ST_Score, Actor acc = null, int tcolor = -1 ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return null; + let o = new("SWWMScoreObj"); + o.ChangeStatNum(STAT_USER); + o.score = score; + o.pos = pos; + o.lifespan = o.initialspan = 60; + if ( tcolor != -1 ) o.tcolor = tcolor; + else switch ( type ) + { + case ST_Score: + o.tcolor = swwm_numcolor_scr; + break; + case ST_Damage: + o.tcolor = swwm_numcolor_dmg; + break; + case ST_Health: + o.tcolor = swwm_numcolor_hp; + break; + case ST_Armor: + o.tcolor = swwm_numcolor_ap; + break; + } + o.starttic = level.maptime; + o.seed = Random[ScoreBits](); + o.seed2 = Random[ScoreBits](); + o.damnum = (type > ST_Score); + o.xcnt = 0; + for ( int i=0; i<6; i++ ) o.xtcolor[i] = swwm_numcolor_bonus; + o.acc = acc; + if ( o.damnum ) + { + o.next = hnd.damnums; + if ( hnd.damnums ) hnd.damnums.prev = o; + hnd.damnums = o; + hnd.damnums_cnt++; + } + else + { + o.next = hnd.scorenums; + if ( hnd.scorenums ) hnd.scorenums.prev = o; + hnd.scorenums = o; + hnd.scorenums_cnt++; + } + return o; + } + + override void OnDestroy() + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( hnd ) + { + if ( damnum ) + { + hnd.damnums_cnt--; + if ( !prev ) hnd.damnums = next; + } + else + { + hnd.scorenums_cnt--; + if ( !prev ) hnd.scorenums = next; + } + if ( !prev ) + { + if ( next ) next.prev = null; + } + else + { + prev.next = next; + if ( next ) next.prev = prev; + } + } + Super.OnDestroy(); + } + + override void Tick() + { + lifespan--; + if ( lifespan <= 0 ) Destroy(); + } +} + +enum EInterestType +{ + INT_Key, + INT_Exit +}; + +Class SWWMInterest : Thinker +{ + int type; + Key trackedkey; + Line trackedline; + Vector3 pos; + SWWMInterest prev, next; + String keytag; + + static SWWMInterest Spawn( Vector3 pos = (0,0,0), Key thekey = null, Line theline = null ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return null; + if ( (!thekey && !theline) || (thekey && theline) ) return null; + let i = new("SWWMInterest"); + i.ChangeStatNum(STAT_USER); + i.trackedkey = thekey; + i.trackedline = theline; + if ( thekey ) + { + i.type = INT_Key; + i.keytag = thekey.GetTag(); + } + else if ( theline ) i.type = INT_Exit; + else + { + i.Destroy(); + return null; + } + i.pos = thekey?thekey.Vec3Offset(0,0,thekey.height/2):pos; + i.next = hnd.intpoints; + if ( hnd.intpoints ) hnd.intpoints.prev = i; + hnd.intpoints = i; + hnd.intpoints_cnt++; + return i; + } + + override void OnDestroy() + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( hnd ) + { + hnd.intpoints_cnt--; + if ( !prev ) + { + hnd.intpoints = next; + if ( next ) next.prev = null; + } + else + { + prev.next = next; + if ( next ) next.prev = prev; + } + } + Super.OnDestroy(); + } + + override void Tick() + { + // update + if ( (type == INT_Key) && (!trackedkey || trackedkey.Owner) ) Destroy(); + else if ( trackedkey ) pos = trackedkey.Vec3Offset(0,0,trackedkey.height/2); + } +} + +Class SWWMItemSense : Thinker +{ + Actor item; + String tag; + int updated; + bool scoreitem, vipitem; + Demolitionist parent; + SWWMItemSense prev, next; + Vector3 pos; + + static SWWMItemSense Spawn( Demolitionist parent, Actor item ) + { + if ( !parent || !item ) return null; + // only refresh the updated time if existing + for ( SWWMItemSense s=parent.itemsense; s; s=s.next ) + { + if ( s.item != item ) continue; + s.updated = level.maptime+35; + s.pos = item.Vec3Offset(0,0,item.height); + return s; + } + let i = new("SWWMItemSense"); + i.ChangeStatNum(STAT_USER); + i.item = item; + i.scoreitem = SWWMUtility.IsScoreItem(item); + i.vipitem = SWWMUtility.IsVipItem(item); + i.parent = parent; + i.updated = level.maptime+35; + i.UpdateTag(); + i.pos = item.Vec3Offset(0,0,item.height); + i.next = parent.itemsense; + if ( parent.itemsense ) parent.itemsense.prev = i; + parent.itemsense = i; + parent.itemsense_cnt++; + return i; + } + + void UpdateTag() + { + if ( !item ) return; + // certain ammo types use the pickup message as it's amount-aware + if ( (item is 'RedShell') || (item is 'GreenShell') + || (item is 'WhiteShell') || (item is 'BlueShell') + || (item is 'BlackShell') || (item is 'PurpleShell') + || (item is 'GoldShell') || (item is 'SMW05Ammo') + || (item is 'SheenAmmo') ) + tag = Inventory(item).PickupMessage(); + else tag = item.GetTag(); + } + + override void OnDestroy() + { + if ( parent ) + { + parent.itemsense_cnt--; + if ( !prev ) + { + parent.itemsense = next; + if ( next ) next.prev = null; + } + else + { + prev.next = next; + if ( next ) next.prev = prev; + } + } + Super.OnDestroy(); + } + + override void Tick() + { + if ( !parent ) + { + Destroy(); + return; + } + // expire + if ( level.maptime > updated+70 ) Destroy(); + } +} + +// enemy combat tracker +Class SWWMCombatTracker : Thinker +{ + Actor mytarget; + String mytag; + int updated, lasthealth, maxhealth; + DynamicValueInterpolator intp; + Vector3 pos, prevpos, oldpos, oldprev; + PlayerInfo myplayer; + SWWMCombatTracker prev, next; + bool legged, mutated; + int tcnt; + double height; + int mxdist, dbar; + bool bBOSS, bFRIENDLY; + bool firsthit; + + void UpdateTag() + { + if ( mytarget && (mytarget.player || mytarget.bISMONSTER || (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen') || (mytarget is 'Demolitionist')) ) + { + String realtag = swwm_funtags?SWWMUtility.GetFunTag(mytarget,FallbackTag):mytarget.GetTag(FallbackTag); + if ( realtag == FallbackTag ) + { + realtag = mytarget.GetClassName(); + SWWMUtility.BeautifyClassName(realtag); + } + mytag = mytarget.player?(mytarget.player.mo!=mytarget)?String.Format(StringTable.Localize("$FN_VOODOO"),mytarget.player.GetUserName()):mytarget.player.GetUserName():realtag; + } + else mytag = ""; + } + + static SWWMCombatTracker Spawn( Actor target ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return null; + SWWMCombatTracker t; + for ( t=hnd.trackers; t; t=t.next ) + { + if ( t.mytarget != target ) continue; + return t; + } + t = new("SWWMCombatTracker"); + t.ChangeStatNum(STAT_USER); + t.mytarget = target; + t.UpdateTag(); + if ( target.player ) + { + t.lasthealth = target.health; + t.maxhealth = target.default.health; + } + else t.lasthealth = t.maxhealth = target.health; + t.updated = int.min; + t.height = target.height; + t.pos = level.Vec3Offset(target.pos,(0,0,t.height)); + t.prevpos = level.Vec3Offset(target.prev,(0,0,t.height)); + t.oldpos = target.pos; + t.oldprev = target.prev; + t.intp = DynamicValueInterpolator.Create(t.lasthealth,.5,1,100); + t.myplayer = target.player; + t.next = hnd.trackers; + t.bBOSS = target.bBOSS; + t.bFRIENDLY = target.bFRIENDLY; + if ( hnd.trackers ) + { + hnd.trackers.prev = t; + // propagate cvar values + t.mxdist = hnd.trackers.mxdist; + t.dbar = hnd.trackers.dbar; + } + hnd.trackers = t; + hnd.trackers_cnt++; + return t; + } + + override void OnDestroy() + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( hnd ) + { + hnd.trackers_cnt--; + if ( !prev ) + { + hnd.trackers = next; + if ( next ) next.prev = null; + } + else + { + prev.next = next; + if ( next ) next.prev = prev; + } + } + Super.OnDestroy(); + } + + override void Tick() + { + // only the first tracker accesses the CVars, saves on perf + if ( !prev ) + { + dbar = swwm_damagetarget; + mxdist = swwm_maxtargetdist; + } + if ( next ) + { + next.dbar = dbar; + next.mxdist = mxdist; + } + // is target gone or dead? + if ( !mytarget || (mytarget.Health <= 0) ) + { + // we're done + if ( updated > level.maptime ) updated = level.maptime; + lasthealth = 0; + prevpos = pos; // prevent stuttering + intp.Update(lasthealth); + if ( level.maptime > updated+35 ) Destroy(); + return; + } + // don't update dormant targets + if ( mytarget.bDORMANT ) + return; + // in deathmatch, don't update for hostile players + if ( deathmatch && mytarget.player && (mytarget.player.mo == mytarget) ) + { + if ( teamplay && (mytarget.player.GetTeam() != players[consoleplayer].GetTeam()) ) + return; + else if ( !teamplay ) return; + } + // only update height/position while alive + bool heightchanged = false; + if ( height != mytarget.height ) heightchanged = true; + height = mytarget.height; + if ( heightchanged || (mytarget.pos != oldpos) || (mytarget.prev != oldprev) ) + { + oldpos = mytarget.pos; + oldprev = mytarget.prev; + pos = level.Vec3Offset(mytarget.pos,(0,0,height)); + prevpos = level.Vec3Offset(mytarget.prev,(0,0,height)); + } + tcnt++; + if ( (tcnt == 1) && !mytarget.player ) + { + // post-spawn health inflation check + if ( lasthealth > maxhealth ) + { + maxhealth = lasthealth; + intp.Reset(lasthealth); + } + } + if ( (tcnt == 6) && !mytarget.player ) + { + // legendoom check + for ( Inventory i=mytarget.inv; i; i=i.inv ) + { + if ( i.GetClassName() != "LDLegendaryMonsterToken" ) continue; + legged = true; + // adjust for health inflation + if ( lasthealth > maxhealth ) + { + maxhealth = lasthealth; + intp.Reset(lasthealth); + } + } + } + if ( legged && !mutated ) + { + // check inventory regularly to mark as mutated + for ( Inventory i=mytarget.inv; i; i=i.inv ) + { + if ( i.GetClassName() != "LDLegendaryMonsterTransformed" ) continue; + mutated = true; + Console.Printf(StringTable.Localize("$SWWM_LTFORM"),mytag); + } + } + bFRIENDLY = mytarget.bFRIENDLY; + if ( mytarget.Health < lasthealth ) firsthit = true; + lasthealth = mytarget.Health; + intp.Update(lasthealth); + // special update conditions + if ( dbar ) + { + if ( (dbar == 2) && (lasthealth >= maxhealth) ) + return; + else if ( (dbar == 1) && !firsthit ) + return; + } + if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE && !mytarget.bCORPSE ) + { + bool straifu = false; + if ( (gameinfo.gametype&GAME_Strife) && (!mytarget.bINCOMBAT && !mytarget.bJUSTATTACKED) || (mytarget is 'Beggar') || (mytarget is 'Peasant') ) + straifu = true; + // players (but not voodoo dolls), always visible + if ( mytarget.player && (mytarget.player.mo == mytarget) ) updated = level.maptime+35; + // friendlies within a set distance + else if ( mytarget.bFRIENDLY && ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < mxdist)) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+35; + // enemies within a set distance that have us as target + else if ( !straifu && mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < mxdist)) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+70; + // any visible enemies within one quarter of the set distance + else if ( ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < (mxdist/4))) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime; + } + else if ( (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen') ) + { + // special stuff, only if visible + if ( ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].Camera).length() < (mxdist/4))) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime; + } + } +} + +// ultralight trackers for certain things +Class SWWMSimpleTracker : Thinker +{ + Actor target; + double radius; + double angle; + Vector3 pos; + bool isplayer; + Color playercol; + bool ismonster; + bool friendly; + bool countkill; + bool shootable; + bool isitem; + bool countitem; + bool vipitem; + bool expired; + int lastupdate; + ui double smoothalpha; // smoothened alpha, for ui + SWWMSimpleTracker prev, next; + + void Update() + { + if ( !target ) return; + radius = target.radius; + angle = target.angle; + pos = target.pos; + isplayer = target.player; + if ( isplayer ) playercol = target.player.GetColor(); + ismonster = target.bISMONSTER; + friendly = target.IsFriend(players[consoleplayer].mo); + countkill = target.bCOUNTKILL; + shootable = target.default.bSHOOTABLE; + isitem = (target is 'Inventory'); + countitem = SWWMUtility.IsScoreItem(target); + vipitem = SWWMUtility.IsVipItem(target); + lastupdate = level.maptime; + if ( isitem ) + { + if ( !target.bSPECIAL || Inventory(target).Owner ) + expired = true; + else + { + expired = false; + lastupdate += 35; + if ( countitem ) lastupdate += 35; + if ( vipitem ) lastupdate += 70; + } + } + else if ( vipitem ) + { + if ( (target is 'Chancebox') && (target.CurState != target.SpawnState) ) + expired = true; + else + { + expired = false; + lastupdate += 70; + } + } + else if ( friendly ) + { + expired = target.bKILLED; + if ( expired ) lastupdate += 35; + else lastupdate += 140; + } + else if ( ismonster ) + { + expired = target.bKILLED; + if ( !expired ) + { + lastupdate += 35; + if ( target.target == players[consoleplayer].mo ) + lastupdate += 70; + } + } + else if ( target.default.bSHOOTABLE ) + expired = (target.Health<=0); + } + + static SWWMSimpleTracker Track( Actor target ) + { + let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); + if ( !hnd ) return null; + SWWMSimpleTracker t; + for ( t=hnd.strackers; t; t=t.next ) + { + if ( t.target != target ) continue; + t.Update(); + return t; + } + t = new("SWWMSimpleTracker"); + t.ChangeStatNum(STAT_INFO); + t.target = target; + t.Update(); + t.next = hnd.strackers; + if ( hnd.strackers ) hnd.strackers.prev = t; + hnd.strackers = t; + hnd.strackers_cnt++; + return t; + } +} diff --git a/zscript/swwm_thinkers_player.zsc b/zscript/swwm_thinkers_player.zsc new file mode 100644 index 000000000..8cb784d99 --- /dev/null +++ b/zscript/swwm_thinkers_player.zsc @@ -0,0 +1,573 @@ +// player-specific thinkers + + +// Stats +Class WeaponUsage +{ + Class w; + int kills; +} + +Class MonsterKill +{ + Class m; + int kills; +} + +Class LevelStat +{ + bool hub; + String levelname, mapname; + int kcount, ktotal; + int icount, itotal; + int scount, stotal; + int time, par, suck; +} + +Class SWWMStats : Thinker +{ + PlayerInfo myplayer; + int lastspawn, dashcount, boostcount, stompcount, airtime, kills, + deaths, damagedealt, hdamagedealt, damagetaken, hdamagetaken, + mkill, hiscore, hhiscore, topdealt, toptaken, skill, wponch, + busts, buttslams, secrets, items, parries, pparries, pats, + befriend, smooch; + double grounddist, airdist, swimdist, fuelusage, topspeed, teledist; + Array wstats; + Array mstats; + Array lstats; + Array > alreadygot; + int favweapon; + // these two are used for mission updates + Array clustervisit; + Array secretdone; + // [Strife] preserve previous mission logs + String oldlogtext; + Array questbacklog; + // hackaround for stuff getting lost + Array > ownedcollectibles; + // for pistol start info (to avoid it within hubs) + int lastcluster; + + bool GotWeapon( Class which ) + { + for ( int i=0; i WeaponFromInflictor( Actor inflictor, Name damagetype ) + { + 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'; + else if ( (inflictor is 'ExplodiumMagArm') || (inflictor is 'ExplodiumMagProj') || (inflictor is 'ExplodiumBulletImpact') ) which = 'ExplodiumGun'; + else if ( (inflictor is 'DragonBreathArm') || ((inflictor is 'SaltImpact') && !inflictor.Args[0]) || ((inflictor is 'SaltBeam') && !inflictor.Args[1]) || (inflictor is 'CorrodeDebuff') || (inflictor is 'CorrosiveFlechette') || ((inflictor is 'TheBall') && !inflictor.special1) || (inflictor is 'GoldenImpact') || (inflictor is 'GoldenSubImpact') || (inflictor is 'GoldenSubSubImpact') ) which = 'Spreadgun'; + else if ( ((inflictor is 'SaltImpact') && inflictor.Args[0]) || ((inflictor is 'SaltBeam') && inflictor.Args[1]) || ((inflictor is 'TheBall') && inflictor.special1) ) which = 'Wallbuster'; + else if ( (inflictor is 'EvisceratorChunk') || (inflictor is 'EvisceratorProj') ) which = 'Eviscerator'; + else if ( (inflictor is 'HellblazerRavagerArm') || (inflictor is 'HellblazerWarheadArm') ) which = 'Hellblazer'; + else if ( (inflictor is 'BigBiospark') || (inflictor is 'BiosparkBall') || (inflictor is 'BiosparkBeamImpact') || (inflictor is 'BiosparkComboImpact') || (inflictor is 'BiosparkComboImpactSub') || (inflictor is 'BiosparkBeam') || (inflictor is 'BiosparkArc') ) which = 'Sparkster'; + else if ( (inflictor is 'CandyBeam') || (inflictor is 'CandyPop') || (inflictor is 'CandyMagArm') || (inflictor is 'CandyGunProj') || (inflictor is 'CandyMagProj') || (inflictor is 'CandyBulletImpact') ) which = 'CandyGun'; + else if ( (inflictor is 'YnykronBeam') || (inflictor is 'YnykronImpact') || (inflictor is 'YnykronSingularity') || (inflictor is 'YnykronCloud') || (inflictor is 'YnykronVoidBeam') || (inflictor is 'YnykronLightningArc') || (inflictor is 'YnykronLightningImpact') ) which = 'Ynykron'; + else if ( (inflictor is 'Demolitionist') || (inflictor is 'DemolitionistShockwave') || (inflictor is 'DemolitionistRadiusShockwave') || (inflictor is 'SWWMGesture') + || (inflictor is 'SWWMItemGesture') ) which = 'SWWMWeapon'; // hack to assume Demolitionist as weapon + else if ( inflictor is 'BigPunchSplash' ) + { + // guess from damagetype + if ( (damagetype == 'Jump') || (damagetype == 'Dash') || (damagetype == 'Buttslam') || (damagetype == 'GroundPound') ) + which = 'SWWMWeapon'; + else if ( damagetype == 'Love' ) + which = 'SWWMGesture'; + // others are just weapon melee, so keep the readyweapon + } + else if ( inflictor is 'FroggyChair' ) + which = 'SWWMItemGesture'; // more gross hacks + if ( damagetype == 'Falling' ) + which = 'Weapon'; // the gross hacks continue + return which; + } + + void AddDamageDealt( int dmg ) + { + int upper = dmg/1000000000; + int lower = dmg%1000000000; + if ( hdamagedealt+upper > 999999999 ) hdamagedealt = 999999999; + else hdamagedealt += upper; + damagedealt += lower; + if ( damagedealt > 999999999 ) + { + upper = damagedealt/1000000000; + lower = damagedealt%1000000000; + if ( hdamagedealt+upper > 999999999 ) hdamagedealt = 999999999; + else hdamagedealt += upper; + damagedealt = lower; + } + } + void AddDamageTaken( int dmg ) + { + int upper = dmg/1000000000; + int lower = dmg%1000000000; + if ( hdamagetaken+upper > 999999999 ) hdamagetaken = 999999999; + else hdamagetaken += upper; + damagetaken += lower; + if ( damagetaken > 999999999 ) + { + upper = damagetaken/1000000000; + lower = damagetaken%1000000000; + if ( hdamagetaken+upper > 999999999 ) hdamagetaken = 999999999; + else hdamagetaken += upper; + damagetaken = lower; + } + } + + void AddLevelStats() + { + let ls = new("LevelStat"); + ls.hub = !!(level.clusterflags&level.CLUSTER_HUB); + ls.levelname = level.levelname; + int iof = ls.levelname.IndexOf(" - by: "); + if ( iof != -1 ) ls.levelname.Truncate(iof); + ls.mapname = level.mapname; + ls.kcount = level.killed_monsters; + ls.ktotal = level.total_monsters; + ls.icount = level.found_items; + ls.itotal = level.total_items; + ls.scount = level.found_secrets; + ls.stotal = level.total_secrets; + ls.time = level.maptime; + ls.par = level.partime; + ls.suck = level.sucktime; + lstats.Push(ls); + } + + void AddWeaponKill( Actor inflictor, Actor victim, Name damagetype ) + { + if ( victim ) + { + bool found = false; + for ( int i=0; i which = WeaponFromInflictor(inflictor,damagetype); + if ( !which ) return; + for ( int i=0; i 999999999 ) + { + c.credits -= 1000000000; + c.hcredits++; + } + if ( (c.hcredits+hamount < c.hcredits) || (c.hcredits+hamount > 999999999) ) c.hcredits = 999999999; + else c.hcredits += hamount; + let s = SWWMStats.Find(p); + if ( s && ((c.hcredits > s.hhiscore) || ((c.credits > s.hiscore) && (c.hcredits >= s.hhiscore))) ) + { + s.hiscore = c.credits; + s.hhiscore = c.hcredits; + } + } + + static clearscope bool CanTake( PlayerInfo p, int amount, int hamount = 0 ) + { + let c = Find(p); + if ( !c ) return false; + int req = amount, hreq = hamount; + while ( req > 999999999 ) + { + req -= 1000000000; + hreq++; + } + // waaaaay too much + if ( (c.hcredits-hreq < 0) || (c.hcredits-hreq > c.hcredits) ) return false; + // too much! + if ( ((c.credits-amount < 0) || (c.credits-amount > c.credits)) && (c.hcredits-hreq <= 0) ) return false; + return true; + } + + static bool Take( PlayerInfo p, int amount, int hamount = 0 ) + { + let c = Find(p); + if ( !c ) return false; + int req = amount, hreq = hamount; + while ( req > 999999999 ) + { + req -= 1000000000; + hreq++; + } + // waaaaay too much + if ( (c.hcredits-hreq < 0) || (c.hcredits-hreq > c.hcredits) ) return false; + // too much! + if ( ((c.credits-amount < 0) || (c.credits-amount > c.credits)) && (c.hcredits-hreq <= 0) ) return false; + c.hcredits -= hreq; + c.credits -= req; + while ( c.credits < 0 ) + { + c.credits += 1000000000; + c.hcredits--; + } + return true; + } + + static clearscope int, int Get( PlayerInfo p ) + { + let c = Find(p); + if ( !c ) return 0; + return c.credits, c.hcredits; + } + + static clearscope SWWMCredits Find( PlayerInfo p ) + { + let ti = ThinkerIterator.Create("SWWMCredits",STAT_STATIC); + SWWMCredits t; + while ( t = SWWMCredits(ti.Next()) ) + { + if ( t.myplayer != p ) continue; + return t; + } + return null; + } +} + +// Trading history between players +Class SWWMTrade +{ + int timestamp, type, amt; + String other; + Class what; +} + +Class SWWMTradeHistory : Thinker +{ + PlayerInfo myplayer; + Array ent; + + static void RegisterSend( PlayerInfo p, PlayerInfo other, Class what, int amt ) + { + let th = Find(p); + if ( !th ) return; + SWWMTrade t = new("SWWMTrade"); + t.timestamp = level.totaltime; + t.type = 0; + t.other = other.GetUserName(); + t.what = what; + t.amt = amt; + th.ent.Push(t); + } + static void RegisterReceive( PlayerInfo p, PlayerInfo other, Class what, int amt ) + { + let th = Find(p); + if ( !th ) return; + SWWMTrade t = new("SWWMTrade"); + t.timestamp = level.totaltime; + t.type = 1; + t.other = other.GetUserName(); + t.what = what; + t.amt = amt; + th.ent.Push(t); + } + + static clearscope SWWMTradeHistory Find( PlayerInfo p ) + { + let ti = ThinkerIterator.Create("SWWMTradeHistory",STAT_STATIC); + SWWMTradeHistory th; + while ( th = SWWMTradeHistory(ti.Next()) ) + { + if ( th.myplayer != p ) continue; + return th; + } + return Null; + } +} + +// Lore holder +enum ELoreTab +{ + LORE_ITEM, + LORE_PEOPLE, + LORE_LORE // lol +}; + +Class SWWMLore +{ + String tag, text, assoc; + int tab; + bool read; +} + +Class SWWMLoreLibrary : Thinker +{ + PlayerInfo myplayer; + Array ent; + int lastaddtic; + + static bool PreVerify( String ref ) + { + // restrictions + if ( !(gameinfo.gametype&(GAME_Raven|GAME_Strife)) ) + { + if ( ref ~== "Parthoris" ) return true; + if ( ref ~== "Sidhe" ) return true; + if ( ref ~== "SerpentRiders" ) return true; + } + if ( !(gameinfo.gametype&(GAME_Hexen|GAME_Strife)) ) + { + if ( ref ~== "Cronos" ) return true; + if ( ref ~== "Kirin" ) return true; // not met + if ( ref ~== "Fabricator" ) return true; // not yet introduced + if ( ref ~== "Administrators" ) return true; // not met + } + if ( !(gameinfo.gametype&GAME_Strife) ) + { + if ( ref ~== "TheOrder" ) return true; + if ( ref ~== "TheFront" ) return true; + } + // check if entry is for a collectible + for ( int i=0; i)(AllActorClasses[i]); + if ( !c || (c == 'SWWMCollectible') ) continue; + let def = GetDefaultByType(c); + // skip if we match and it's not for this game + if ( (c.GetClassName() == ref) && !(gameinfo.gametype&def.avail) ) + return true; + } + ref = ref.MakeUpper(); + String tag = String.Format("SWWM_LORETAG_%s",ref); + String tab = String.Format("SWWM_LORETAB_%s",ref); + String text = String.Format("SWWM_LORETXT_%s",ref); + // check that it's valid + if ( StringTable.Localize(tag,false) == tag ) return true; + if ( StringTable.Localize(tab,false) == tab ) + { + Console.Printf("Entry \"%s\" defines no tab.",ref); + return true; + } + if ( StringTable.Localize(text,false) == text ) + { + Console.Printf("Entry \"%s\" defines no text.",ref); + return true; + } + return false; + } + + bool DirectAdd( String ref ) + { + if ( PreVerify(ref) ) return true; + return InternalAdd(ref); + } + + private bool InternalAdd( String ref ) + { + ref = ref.MakeUpper(); + String tag = String.Format("SWWM_LORETAG_%s",ref); + String tab = String.Format("SWWM_LORETAB_%s",ref); + String text = String.Format("SWWM_LORETXT_%s",ref); + String assoc = String.Format("SWWM_LOREREL_%s",ref); + // redirects + if ( gameinfo.gametype&GAME_Strife ) + { + if ( text ~== "SWWM_LORETXT_KIRIN" ) + text = "SWWM_LORETXT_KIRIN2"; // married alakir + else if ( text ~== "SWWM_LORETXT_SERPENTRIDERS" ) + text = "SWWM_LORETXT_SERPENTRIDERS3"; // all riders gone + } + if ( gameinfo.gametype&(GAME_Hexen|GAME_Strife) ) + { + if ( text ~== "SWWM_LORETXT_SAYA" ) + text = "SWWM_LORETXT_SAYA3"; // married kirin + else if ( text ~== "SWWM_LORETXT_ANARUKON" ) + text = "SWWM_LORETXT_ANARUKON2"; // comments from miyamoto-xanai wedding + else if ( text ~== "SWWM_LORETXT_HELL" ) + text = "SWWM_LORETXT_HELL3"; // met father nostros during the wedding + else if ( text ~== "SWWM_LORETXT_NANA" ) + text = "SWWM_LORETXT_NANA3"; // stuff that happened at the wedding + else if ( text ~== "SWWM_LORETXT_GHOULHUNT" ) + text = "SWWM_LORETXT_GHOULHUNT2"; // met anthon anderken during the wedding + else if ( text ~== "SWWM_LORETXT_RAGEKIT" ) + text = "SWWM_LORETXT_RAGEKIT2"; // kirin's reactions to demo using this item + else if ( text ~== "SWWM_LORETXT_SANKAIDERIHA" ) + text = "SWWM_LORETXT_SANKAIDERIHA2"; // comments about kirin + else if ( text ~== "SWWM_LORETXT_SERPENTRIDERS" ) + text = "SWWM_LORETXT_SERPENTRIDERS2"; // defeated d'sparil + else if ( text ~== "SWWM_LORETXT_XANIMEN" ) + text = "SWWM_LORETXT_XANIMEN2"; // footnote about nuoma + else if ( text ~== "SWWM_LORETXT_ZANAVETH2" ) + text = "SWWM_LORETXT_ZANAVETH22"; // met at wedding + else if ( text ~== "SWWM_LORETXT_YNYKRON" ) + text = "SWWM_LORETXT_YNYKRON2"; // confirmed to harm (but not kill) gods + else if ( text ~== "SWWM_LORETXT_AKARIPROJECT" ) + text = "SWWM_LORETXT_AKARIPROJECT3"; // mentions kirin + else if ( text ~== "SWWM_LORETXT_GODS" ) + text = "SWWM_LORETXT_GODS2"; // beyond gods + } + if ( gameinfo.gametype&(GAME_Raven|GAME_Strife) ) + { + if ( text ~== "SWWM_LORETXT_SAYA" ) + text = "SWWM_LORETXT_SAYA2"; // dating demo + else if ( text ~== "SWWM_LORETXT_AKARILABS" ) + text = "SWWM_LORETXT_AKARILABS2"; // demo won, akari project announced + else if ( text ~== "SWWM_LORETXT_DEMOLITIONIST" ) + text = "SWWM_LORETXT_DEMOLITIONIST2"; // demo rewarded with maidbot frame + else if ( text ~== "SWWM_LORETXT_DOOMGUY" ) + text = "SWWM_LORETXT_DOOMGUY2"; // he gone + else if ( text ~== "SWWM_LORETXT_UAC" ) + text = "SWWM_LORETXT_UAC2"; // uac "reformed" + else if ( text ~== "SWWM_LORETXT_HELL" ) + text = "SWWM_LORETXT_HELL2"; // invasion was a thing of the past + else if ( text ~== "SWWM_LORETXT_NANA" ) + text = "SWWM_LORETXT_NANA2"; // demo met nana + else if ( text ~== "SWWM_LORETXT_ZANAVETH3" ) + text = "SWWM_LORETXT_ZANAVETH32"; // iagb happened + else if ( text ~== "SWWM_LORETXT_BIGSHOT" ) + text = "SWWM_LORETXT_BIGSHOT2"; // predictions about crimes_m + else if ( text ~== "SWWM_LORETXT_AKARIPROJECT" ) + text = "SWWM_LORETXT_AKARIPROJECT2"; // fiction becomes reality + } + // check if existing + for ( int i=0; i 0) && (gametic > lastaddtic) && (myplayer == players[consoleplayer]) && (!menuactive || (menuactive == Menu.OnNoPause)) && (myplayer.mo is 'Demolitionist') ) + Console.Printf(StringTable.Localize("$SWWM_NEWLORE")); + lastaddtic = gametic; + ent.Push(e); + return true; + } + + static void Add( PlayerInfo p, String ref ) + { + if ( PreVerify(ref) ) return; + SWWMLoreLibrary ll = Find(p); + if ( !ll ) + { + ll = new("SWWMLoreLibrary"); + ll.ChangeStatNum(STAT_STATIC); + ll.myplayer = p; + } + ll.InternalAdd(ref); + } + + void MarkRead( int idx ) + { + if ( (idx < 0) || (idx >= ent.Size()) ) return; + if ( !ent[idx].read ) + { + ent[idx].read = true; + // add associated entries + Array rel; + rel.Clear(); + String assocstr = StringTable.Localize(ent[idx].assoc); + assocstr.Split(rel,";",0); + for ( int i=0; i l.backsector.floorplane.ZatPoint(l.v1.p)) + && (l.frontsector.floorplane.ZatPoint(l.v2.p) > l.backsector.floorplane.ZatPoint(l.v2.p)) ) + { + highestfloor = l.frontsector.floorplane; + lowestfloor = l.backsector.floorplane; + } + else + { + highestfloor = l.backsector.floorplane; + lowestfloor = l.frontsector.floorplane; + } + if ( (l.frontsector.ceilingplane.ZatPoint(l.v1.p) < l.backsector.ceilingplane.ZatPoint(l.v1.p)) + && (l.frontsector.ceilingplane.ZatPoint(l.v2.p) < l.backsector.ceilingplane.ZatPoint(l.v2.p)) ) + { + lowestceiling = l.frontsector.ceilingplane; + highestceiling = l.backsector.ceilingplane; + } + else + { + lowestceiling = l.backsector.ceilingplane; + highestceiling = l.frontsector.ceilingplane; + } + // try to guess what the part that triggers this is + if ( l.Activation&SPAC_Cross ) + { + // pick the "intersection" + al = (l.v1.p,highestfloor.ZatPoint(l.v1.p)); + ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p)); + bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p)); + bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p)); + return (al+ah+bl+bh)*.25; + } + // check if lower part available + al = (l.v1.p,lowestfloor.ZatPoint(l.v1.p)); + ah = (l.v1.p,highestfloor.ZatPoint(l.v1.p)); + bl = (l.v2.p,lowestfloor.ZatPoint(l.v2.p)); + bh = (l.v2.p,highestfloor.ZatPoint(l.v2.p)); + if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) ) + return (al+ah+bl+bh)*.25; + // check if upper part available + al = (l.v1.p,lowestceiling.ZatPoint(l.v1.p)); + ah = (l.v1.p,highestceiling.ZatPoint(l.v1.p)); + bl = (l.v2.p,lowestceiling.ZatPoint(l.v2.p)); + bh = (l.v2.p,highestceiling.ZatPoint(l.v2.p)); + if ( ((al-ah).length() > 0) && ((bl-bh).length() > 0) ) + return (al+ah+bl+bh)*.25; + // check for 3d floors + bool floorfound = false; + Vector3 fal, fah, fbl, fbh; + for ( int i=0; i ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue; + al = fal; + ah = fah; + bl = fbl; + bh = fbh; + floorfound = true; + } + if ( floorfound ) return (al+ah+bl+bh)*.25; + for ( int i=0; i ah.z) && (fbh.z > bh.z) && (fal.z > al.z) && (fbl.z > bl.z) ) continue; + al = fal; + ah = fah; + bl = fbl; + bh = fbh; + floorfound = true; + } + if ( floorfound ) return (al+ah+bl+bh)*.25; + // check for midtex + if ( !l.sidedef[0].GetTexture(1).IsNull() ) + { + double ofs = l.sidedef[0].GetTextureYOffset(1); + Vector2 siz = TexMan.GetScaledSize(l.sidedef[0].GetTexture(1)); + Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[0].GetTexture(1)); + ofs += tofs.y; + ofs *= l.sidedef[0].GetTextureYScale(1); + siz.y *= l.sidedef[0].GetTextureYScale(1); + if ( l.flags&Line.ML_DONTPEGBOTTOM ) + { + al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs); + bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs); + ah = al+(0,0,siz.y); + bh = bl+(0,0,siz.y); + } + else + { + ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs); + bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs); + al = ah-(0,0,siz.y); + bl = bh-(0,0,siz.y); + } + return (al+ah+bl+bh)*.25; + } + if ( !l.sidedef[1].GetTexture(1).IsNull() ) + { + double ofs = l.sidedef[1].GetTextureYOffset(1); + Vector2 siz = TexMan.GetScaledSize(l.sidedef[1].GetTexture(1)); + Vector2 tofs = TexMan.GetScaledOffset(l.sidedef[1].GetTexture(1)); + ofs += tofs.y; + ofs *= l.sidedef[1].GetTextureYScale(1); + siz.y *= l.sidedef[1].GetTextureYScale(1); + if ( l.flags&Line.ML_DONTPEGBOTTOM ) + { + al = (l.v1.p,highestfloor.ZAtPoint(l.v1.p)+ofs); + bl = (l.v2.p,highestfloor.ZAtPoint(l.v2.p)+ofs); + ah = al+(0,0,siz.y); + bh = bl+(0,0,siz.y); + } + else + { + ah = (l.v1.p,lowestceiling.ZAtPoint(l.v1.p)+ofs); + bh = (l.v2.p,lowestceiling.ZAtPoint(l.v2.p)+ofs); + al = ah-(0,0,siz.y); + bl = bh-(0,0,siz.y); + } + return (al+ah+bl+bh)*.25; + } + // just use the intersection + al = (l.v1.p,highestfloor.ZatPoint(l.v1.p)); + ah = (l.v1.p,lowestceiling.ZatPoint(l.v1.p)); + bl = (l.v2.p,highestfloor.ZatPoint(l.v2.p)); + bh = (l.v2.p,lowestceiling.ZatPoint(l.v2.p)); + return (al+ah+bl+bh)*.25; + } + // get how much a sector's physical position is offset by portals static Vector2 PortalDisplacement( Sector a, Sector b ) { diff --git a/zscript/weapons/swwm_baseweapon.zsc b/zscript/weapons/swwm_baseweapon.zsc new file mode 100644 index 000000000..d6b4fdb69 --- /dev/null +++ b/zscript/weapons/swwm_baseweapon.zsc @@ -0,0 +1,390 @@ +// Base class for all SWWM Weapons +Class SWWMWeapon : Weapon abstract +{ + Mixin SWWMOverlapPickupSound; + + bool wasused; + private int SWeaponFlags; + + Class dropammotype; + + Property DropAmmoType : dropammotype; + + FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count) + FlagDef HideInMenu : SWeaponFlags, 1; // don't show in inventory menu (usually for sister weapons) + FlagDef NoSwapWeapon : SWeaponFlags, 2; // weapon is not affected by slot swapping + + bool IsSwapWeapon( Inventory i ) const + { + if ( bNoSwapWeapon || (i.GetClass() == GetClass()) ) return false; + let w = SWWMWeapon(i); + if ( w && !w.bNoSwapWeapon && (SlotNumber != -1) && (w.SlotNumber == SlotNumber) ) + return true; + return false; + } + + SWWMWeapon HasSwapWeapon( Actor other ) const + { + if ( bNoSwapWeapon ) return null; + for ( Inventory i=other.inv; i; i=i.inv ) + { + if ( IsSwapWeapon(i) ) + return SWWMWeapon(i); + } + return null; + } + + override void Touch( Actor toucher ) + { + // cannot pick up swapweapon unless explicitly pressing use + SWWMWeapon sw; + if ( swwm_swapweapons && (sw = HasSwapWeapon(toucher)) ) + { + if ( toucher.CheckLocalView() ) + { + // use sisterweapon tag for dual wield (slot 2 weapons) + if ( sw.SisterWeapon && (sw.Amount > 1) ) + Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.SisterWeapon.GetTag(),GetTag())); + else Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),GetTag())); + } + return; + } + Super.Touch(toucher); + } + // allow pickup by use + swap weapon support + override bool Used( Actor user ) + { + Vector3 itempos = Vec3Offset(0,0,Height/2), + userpos = user.Vec2OffsetZ(0,0,user.player.viewz); + // test vertical range + Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); + double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); + if ( abs(diff.z) > rang ) return false; + // if the toucher owns our SwapWeapon, drop it before picking us up + bool swapto = false; + SWWMWeapon sw; + if ( swwm_swapweapons && (sw = HasSwapWeapon(user)) ) + { + if ( (sw == user.player.ReadyWeapon) || (sw.SisterWeapon && (sw.SisterWeapon == user.player.ReadyWeapon)) ) + swapto = true; + int ngun = sw.Amount; + double ang = -15*(ngun-1); + for ( int i=0; i 0 ) gotstuff = AddExistingAmmo(ownedWeapon.Ammo1,AmmoGive1); + if ( AmmoGive2 > 0 ) gotstuff |= AddExistingAmmo(ownedWeapon.Ammo2,AmmoGive2); + let Owner = ownedWeapon.Owner; + if ( gotstuff && Owner && Owner.player ) + { + if ( ownedWeapon.Ammo1 && (oldamount1 == 0) ) + PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass()); + else if ( ownedWeapon.Ammo2 && (oldamount2 == 0) ) + PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo2.GetClass()); + } + if ( ownedWeapon.Ammo1 ) + { + // subtract price of ammo we don't give + int ammonotgiven = default.AmmoGive1-AmmoGive1; + if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammonotgiven-1))); + // subtract price of given ammo + int ammogiven = ownedWeapon.Ammo1.Amount-oldamount1; + if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammogiven-1))); + // drop excess + int dropme = AmmoGive1-ammogiven; + if ( dropme > 0 ) + { + // hacky, but it works + ownedWeapon.Ammo1.CreateTossable(dropme); + ownedWeapon.Ammo1.Amount += dropme; + } + } + if ( ownedWeapon.Ammo2 ) + { + // subtract price of ammo we don't give + int ammonotgiven = default.AmmoGive2-AmmoGive2; + if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo2.Stamina*(1.+.75*(ammonotgiven-1))); + // subtract price of given ammo + int ammogiven = ownedWeapon.Ammo2.Amount-oldamount2; + if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo2.Stamina*(1.+.75*(ammogiven-1))); + // drop excess + int dropme = AmmoGive2-ammogiven; + if ( dropme > 0 ) + { + // hacky, but it works + ownedWeapon.Ammo2.CreateTossable(dropme); + ownedWeapon.Ammo2.Amount += dropme; + } + } + return gotstuff; + } + override bool HandlePickup( Inventory item ) + { + // can't hold both weapons at once + if ( swwm_swapweapons && IsSwapWeapon(item) ) + return true; + if ( (GetClass() == item.GetClass()) && !item.ShouldStay() ) + { + if ( SWWMWeapon(item).PickupForAmmoSWWM(self) ) + item.bPickupGood = true; + if ( (Amount+item.Amount > MaxAmount) && (item.Stamina > 0) ) + { + // sell excess + int sellprice = item.Stamina/2; + SWWMScoreObj.Spawn(sellprice,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2)); + SWWMCredits.Give(Owner.player,sellprice); + if ( Owner.player ) + Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),Owner.player.GetUserName(),GetTag(),sellprice); + item.bPickupGood = true; + } + // reset the price in case it has to respawn + item.Stamina = item.default.Stamina; + return true; + } + return Super.HandlePickup(item); + } + override void AttachToOwner( Actor other ) + { + Inventory.AttachToOwner(other); + Ammo1 = AddAmmo(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1); + Ammo2 = AddAmmo(Owner,AmmoType2,bNoFirstGive?0:AmmoGive2); + SisterWeapon = AddWeapon(SisterWeaponType); + if ( Owner.player ) + { + if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch ) + Owner.player.PendingWeapon = self; + if ( Owner.player.mo == players[consoleplayer].camera ) + StatusBar.ReceivedWeapon(self); + } + GivenAsMorphWeapon = false; + } + override void DetachFromOwner() + { + Owner.A_StopSound(CHAN_WEAPON); + Owner.A_StopSound(CHAN_WEAPONEXTRA); + Owner.A_StopSound(CHAN_WEAPONEXTRA2); + Owner.A_StopSound(CHAN_WEAPONEXTRA3); + Super.DetachFromOwner(); + } + override void OwnerDied() + { + if ( Owner.player && (Owner.player.ReadyWeapon == self) ) + { + Owner.A_StopSound(CHAN_WEAPON); + Owner.A_StopSound(CHAN_WEAPONEXTRA); + Owner.A_StopSound(CHAN_WEAPONEXTRA2); + Owner.A_StopSound(CHAN_WEAPONEXTRA3); + } + A_ClearRefire(); + Super.OwnerDied(); + } + override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) + { + if ( mod == 'Melee' ) return StringTable.Localize("$O_MELEE"); + return Super.GetObituary(victim,inflictor,mod,playerattack); + } + // draw ammo on hud above weapon box + virtual ui void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + } + // animations + action void A_PlayerFire() + { + let demo = Demolitionist(player.mo); + if ( demo && (demo.Health > 0) ) demo.PlayFire(); + } + action void A_PlayerMelee( bool bFast = false ) + { + let demo = Demolitionist(player.mo); + if ( demo && (demo.Health > 0) ) + { + if ( bFast ) demo.PlayFastMelee(); + else demo.PlayMelee(); + } + } + action void A_PlayerReload() + { + let demo = Demolitionist(player.mo); + if ( demo && (demo.Health > 0) ) demo.PlayReload(); + } + action void A_PlayerCheckGun() + { + let demo = Demolitionist(player.mo); + if ( demo && (demo.Health > 0) ) demo.PlayCheckGun(); + } + // instant raise/lower + action void A_FullRaise() + { + if ( !player ) return; + if ( player.PendingWeapon != WP_NOCHANGE ) + { + player.mo.DropWeapon(); + return; + } + if ( !player.ReadyWeapon ) return; + let psp = player.GetPSprite(PSP_WEAPON); + if ( !psp ) return; + ResetPSprite(psp); + psp.y = WEAPONTOP; + // do not jump to ready state here, the weapon should do that + // directly once it finishes playing its select animation + } + action void A_FullLower() + { + if ( !player ) return; + if ( !player.ReadyWeapon ) + { + player.mo.BringUpWeapon(); + return; + } + let psp = player.GetPSprite(PSP_WEAPON); + if ( !psp ) return; + psp.y = WEAPONBOTTOM; + ResetPSprite(psp); + if ( player.playerstate == PST_DEAD ) + { + // Player is dead, so don't bring up a pending weapon + // Player is dead, so keep the weapon off screen + player.SetPSprite(PSP_FLASH,null); + psp.SetState(player.ReadyWeapon.FindState('DeadLowered')); + return; + } + // [RH] Clear the flash state. Only needed for Strife. + player.SetPSprite(PSP_FLASH,null); + player.mo.BringUpWeapon(); + } + override void PlayUpSound( Actor origin ) + { + if ( UpSound ) origin.A_StartSound(UpSound,CHAN_WEAPON,CHANF_OVERLAP); + } + action void A_SWWMFlash( StateLabel flashlabel = null ) + { + if ( !player || !player.ReadyWeapon ) + return; + Weapon weap = player.ReadyWeapon; + State flashstate = null; + if ( !flashlabel ) + { + if ( weap.bAltFire ) + flashstate = weap.FindState('AltFlash'); + if ( !flashstate ) + flashstate = weap.FindState('Flash'); + } + else flashstate = weap.FindState(flashlabel); + player.SetPSprite(PSP_FLASH,flashstate); + A_OverlayFlags(PSP_FLASH,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); + A_OverlayRenderStyle(PSP_FLASH,STYLE_Add); + } + // tells the SWWM HUD that this weapon has ammo available + virtual clearscope bool ReportHUDAmmo() + { + return (!Ammo1||(Ammo1.Amount>0)||(Ammo2&&(Ammo2.Amount>0))); + } + // tells the Embiggener that this weapon uses the specified ammo type + // even if it is not its primary one + virtual clearscope bool UsesAmmo( Class kind ) + { + return (AmmoType1&&(kind is AmmoType1))||(AmmoType2&&(kind is AmmoType2)); + } + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + if ( (AmmoGive1 <= 0) && (default.AmmoGive1 > 0) ) + AmmoGive1 = 1; + if ( (AmmoGive2 <= 0) && (default.AmmoGive2 > 0) ) + AmmoGive2 = 1; + } + override bool SpecialDropAction( Actor dropper ) + { + if ( swwm_enemydrops > 0 ) return false; + else if ( swwm_enemydrops == 0 ) + { + // first, check if no others exist in the map, just in + // case this weapon drop MAY be needed + if ( !SWWMUtility.ItemExists(GetClass(),self) ) + return false; // drop us + // drop our corresponding ammo + if ( !DropAmmoType ) return true; + let a = Inventory(Spawn(DropAmmoType,pos,ALLOW_REPLACE)); + if ( !a ) return true; + a.bDROPPED = true; + a.bNOGRAVITY = false; + if ( !(level.compatflags&COMPATF_NOTOSSDROPS) ) + a.TossItem(); + if ( a is 'Ammo' ) + a.ModifyDropAmount(Ammo(a).DropAmount); + a.bTOSSED = true; + if ( a.SpecialDropAction(dropper) ) + a.Destroy(); + return true; + } + // no weapon drops from enemies + return true; + } + override Inventory CreateTossable( int amt ) + { + // disallow dropping if weapon isn't ready for switching + if ( (Owner.player.ReadyWeapon == self) && (!(Owner.player.WeaponState&WF_WEAPONSWITCHOK) || (Owner.player.WeaponState&WF_DISABLESWITCH)) ) + return null; + return Super.CreateTossable(amt); + } + Default + { + Weapon.BobStyle "Alpha"; + Weapon.BobSpeed 3.0; + Weapon.BobRangeX 0.5; + Weapon.BobRangeY 0.2; + Weapon.YAdjust 0; + Weapon.SlotPriority 1.; + Inventory.RestrictedTo "Demolitionist"; + Inventory.PickupFlash "SWWMRedPickupFlash"; + +INVENTORY.IGNORESKILL; + +WEAPON.NOALERT; + +WEAPON.NODEATHINPUT; + +FLOATBOB; + FloatBobStrength 0.25; + } +} diff --git a/zscript/weapons/swwm_baseweapon_fx.zsc b/zscript/weapons/swwm_baseweapon_fx.zsc new file mode 100644 index 000000000..2ca80aff3 --- /dev/null +++ b/zscript/weapons/swwm_baseweapon_fx.zsc @@ -0,0 +1,291 @@ +// Base casing classes + +Class SWWMCasing : Actor abstract +{ + SWWMCasing prevcasing, nextcasing; + bool killme; + int numbounces; + double pitchvel, anglevel; + double heat; + + Default + { + Radius 2; + Height 2; + +NOBLOCKMAP; + +MISSILE; + +DROPOFF; + +MOVEWITHSECTOR; + +THRUACTORS; + +USEBOUNCESTATE; + +INTERPOLATEANGLES; + +NOTELEPORT; + +ROLLSPRITE; + +ROLLCENTER; + Mass 1; + Gravity 0.35; + BounceType "Hexen"; + WallBounceFactor 0.65; + BounceFactor 0.65; + BounceSound "explodium/casing"; + FloatBobPhase 0; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + heat = 1.0; + SWWMHandler.QueueCasing(self); + } + override void OnDestroy() + { + SWWMHandler.DeQueueCasing(self); + Super.OnDestroy(); + } + override void Tick() + { + Super.Tick(); + if ( isFrozen() ) return; + if ( killme ) A_FadeOut(.01); + if ( waterlevel > 0 ) + { + vel.xy *= .98; + anglevel *= .98; + pitchvel *= .98; + } + if ( heat <= 0 ) return; + let s = Spawn("SWWMSmallSmoke",pos); + s.alpha *= heat; + heat -= 0.05; + } + States + { + Spawn: + XZW1 A 1 + { + angle += anglevel; + pitch += pitchvel; + } + Loop; + Bounce: + XZW1 A 0 + { + pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + vel = (vel.unit()+(FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2))).unit()*vel.length(); + if ( numbounces && ((numbounces > 3) || (Random[Junk](1,20) < 17) || (vel.z > -1.4)) ) + { + ClearBounce(); + ExplodeMissile(); + } + numbounces++; + } + Goto Spawn; + Death: + XZW1 B -1 + { + pitch = roll = 0; + angle = FRandom[Junk](0,360); + } + Stop; + } +} + +Class SWWMBulletImpact : Actor +{ + Default + { + RenderStyle "Add"; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOBLOCKMAP; + +DONTSPLASH; + +NOTELEPORT; + +NOINTERACTION; + Scale 0.25; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_SprayDecal("Pock",-20); + int numpt = int(Random[Junk](5,10)*scale.x*4); + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + for ( int i=0; i 2 ) Destroy(); + } +} + +Class PunchImpact : Actor +{ + Default + { + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +NOTELEPORT; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3); + A_StartSound("demolitionist/punch",CHAN_VOICE,CHANF_DEFAULT,bAMBUSH?.6:1.); + A_SprayDecal("WallCrack",-20); + int numpt = Random[Ponch](5,10); + if ( bAMBUSH ) numpt /= 3; + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + for ( int i=0; i 1 ) + { + s = Spawn("SWWMHalfSmoke",Owner.pos); + s.vel = Owner.vel*.3+(FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](.1,1.2); + s.scale *= 2.; + s.A_SetRenderStyle(s.alpha,STYLE_AddShaded); + s.SetShade(Color(4,2,1)*Random[Ponch](32,63)); + } + } + override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags ) + { + // increase blast damage (way too tiny normally for lost souls) + if ( Owner.bBLASTED && (damageType == 'Melee') && !inflictor && !source ) + { + newdamage = 20; + if ( special1&1 ) newdamage *= 2; + if ( special2 > 1 ) newdamage *= 8; + } + } +} + +// amplifies damage of parried projectiles +Class ParryDamageChecker : Inventory +{ + Default + { + +Inventory.UNDROPPABLE; + +Inventory.UNTOSSABLE; + +Inventory.UNCLEARABLE; + Inventory.Amount 1; + Inventory.MaxAmount 1; + } + + override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags ) + { + Inventory buff; + if ( inflictor && (buff=inflictor.FindInventory("ParriedBuff")) ) + { + double mult; + if ( buff.special1 <= 1 ) mult = 1.5; + else if ( buff.special1 >= 2 ) mult = 8.; + if ( buff.special1&1 ) mult *= 2.; + newdamage = int(damage*mult); + } + } +} + +Class ParryRing : Actor +{ + Default + { + RenderStyle "Add"; + Scale .1; + Alpha .3; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOBLOCKMAP; + +FORCEXYBILLBOARD; + +NOTELEPORT; + +NOINTERACTION; + } + States + { + Spawn: + XRG4 ABCDEFGHIJKLMNOPQRSTUVWX 1 A_SetScale(scale.x*(1+specialf1)); + Stop; + } +} + +Class ParryField : Actor +{ + bool critsnd; + + Default + { + Radius .1; + Height 0.; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + +NOINTERACTION; + } + + override void Tick() + { + if ( !master ) + { + Destroy(); + return; + } + Vector3 x, y, z, origin; + [x, y, z] = swwm_CoordUtil.GetAxes(master.pitch,master.angle,master.roll); + origin = level.Vec3Offset(master.Vec2OffsetZ(0,0,master.player.viewz),x*20); + SetOrigin(origin,false); + // check for projectiles to deflect + let ti = ThinkerIterator.Create("Actor"); + Actor a; + while ( a = Actor(ti.Next()) ) + { + if ( !((a.bMISSILE && !a.IsZeroDamage() && (a.target != master)) || a.bSKULLFLY) || a.bTHRUACTORS || (level.Vec3Diff(a.pos,pos).length() > 80) ) continue; + Vector3 vdir = a.vel; + Vector3 dir = level.Vec3Diff(master.Vec2OffsetZ(0,0,pos.z),a.pos).unit(); + Vector3 hdir = dir; + if ( a.bMISSILE ) + { + // deflect directly to target + if ( a.target ) + { + hdir = level.Vec3Diff(a.pos,a.target.Vec3Offset(0,0,a.target.height/2)).unit(); + double theta = max(FRandom[Parry](0.,1.)**2.,.1); + dir = dir*(1.-theta)+hdir*theta; + } + // push away + if ( a.bSEEKERMISSILE ) a.tracer = a.target; + a.target = master; + } + if ( a.bSKULLFLY ) a.bBLASTED = true; // blast lost souls + a.GiveInventory("ParriedBuff",1); + let buff = a.FindInventory("ParriedBuff"); + double mvel = a.vel.length(); + double nspeed = min(100,mvel*FRandom[Parry](1.2,1.4)+20); + a.angle = atan2(dir.y,dir.x); + a.pitch = asin(-dir.z); + let raging = RagekitPower(master.FindInventory("RagekitPower")); + if ( raging ) + { + buff.special1 = 2; + nspeed = min(100,nspeed*2.); + raging.DoHitFX(); + } + a.vel = dir*nspeed; + if ( a.bMISSILE ) a.speed = nspeed; + let i = Spawn(raging?"BigPunchImpact":"PunchImpact",a.pos); + i.target = master; + i.angle = atan2(dir.y,dir.x); + i.pitch = asin(-dir.z); + i.bAMBUSH = true; + A_QuakeEx(3,3,3,10,0,64,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.2); + A_StartSound("demolitionist/parry",CHAN_WEAPON); + let s = Demolitionist(master).mystats; + if ( special1 >= special2 ) // perfect parry + { + // increased homing + dir = dir*.4+hdir*.6; + nspeed = min(100,nspeed*1.5); + a.vel = dir*nspeed; + for ( int i=1; i<6; i++ ) + { + let r = Spawn("ParryRing",a.pos); + r.specialf1 = i*.04; + } + buff.special1++; + if ( !critsnd ) + { + A_StartSound("misc/soulsparry",CHAN_ITEM,CHANF_OVERLAP,1.,.5); + if ( s ) s.pparries++; + } + critsnd = true; + } + if ( s ) s.parries++; + } + if ( --special1 <= 0 ) Destroy(); + } +} + +Class UseList +{ + Line hitline; + int hitside, hitpart; + Actor hitactor; + Vector3 pos; +} + +Class UseLineTracer : LineTracer +{ + Array uses; + + static play bool TangibleLine( UseList u ) + { + if ( u.hitpart != TIER_MIDDLE ) return true; // lower/upper/ffloor + Line l = u.HitLine; + if ( !l.sidedef[1] ) return true; // onesided line + Side s = l.sidedef[u.hitside]; + if ( s.GetTexture(1).IsNull() ) return false; // no midtex + double ofs = s.GetTextureYOffset(1); + Vector2 siz = TexMan.GetScaledSize(s.GetTexture(1)); + Vector2 tofs = TexMan.GetScaledOffset(s.GetTexture(1)); + ofs += tofs.y; + ofs *= s.GetTextureYScale(1); + siz.y *= s.GetTextureYScale(1); + SecPlane ceil, flor; + if ( (l.frontsector.floorplane.ZatPoint(l.v1.p) > l.backsector.floorplane.ZatPoint(l.v1.p)) + && (l.frontsector.floorplane.ZatPoint(l.v2.p) > l.backsector.floorplane.ZatPoint(l.v2.p)) ) + flor = l.frontsector.floorplane; + else flor = l.backsector.floorplane; + if ( (l.frontsector.ceilingplane.ZatPoint(l.v1.p) < l.backsector.ceilingplane.ZatPoint(l.v1.p)) + && (l.frontsector.ceilingplane.ZatPoint(l.v2.p) < l.backsector.ceilingplane.ZatPoint(l.v2.p)) ) + ceil = l.frontsector.ceilingplane; + else ceil = l.backsector.ceilingplane; + double ceilpoint = max(ceil.ZatPoint(l.v1.p),ceil.ZatPoint(l.v2.p)); + double florpoint = min(flor.ZatPoint(l.v1.p),flor.ZatPoint(l.v2.p)); + if ( l.flags&Line.ML_DONTPEGBOTTOM ) + { + if ( u.pos.z > florpoint+ofs+siz.y ) return false; + if ( u.pos.z < florpoint+ofs ) return false; + return true; + } + else + { + if ( u.pos.z > ceilpoint+ofs ) return false; + if ( u.pos.z < (ceilpoint+ofs)-siz.y ) return false; + return true; + } + return false; + } + + override ETraceStatus TraceCallback() + { + if ( Results.HitType == TRACE_HitActor ) + { + let u = new("UseList"); + u.hitline = null; + u.hitactor = Results.HitActor; + u.pos = Results.HitPos; + uses.Push(u); + return TRACE_Skip; + } + if ( Results.HitType == TRACE_HitWall ) + { + if ( Results.HitLine.Activation&(SPAC_Use|SPAC_UseThrough) ) + { + let u = new("UseList"); + u.hitline = Results.HitLine; + u.hitside = Results.Side; + u.hitpart = Results.FFloor?TIER_FFLOOR:Results.Tier; + u.hitactor = null; + u.pos = Results.HitPos; + uses.Push(u); + } + if ( Results.Tier == TIER_Middle ) + { + if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything|Line.ML_BlockUse)) ) + return TRACE_Stop; + return TRACE_Skip; + } + } + return TRACE_Stop; + } +} + +extend Class SWWMWeapon +{ + Actor pfield; // instance of parry field for current melee attack + bool wallponch; // is punching a wall (for activation checks) + + action void A_Parry( int duration ) + { + Vector3 x, y, z, origin; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*20-(0,0,20)); + if ( invoker.pfield ) invoker.pfield.Destroy(); + invoker.pfield = Spawn("ParryField",origin); + invoker.pfield.master = self; + invoker.pfield.special1 = duration; + invoker.pfield.special2 = duration; + if ( !FindInventory("ParryDamageChecker") ) + GiveInventory("ParryDamageChecker",1); // need this so parried projectiles deal extra damage + } + private action bool TryMelee( double angle, int dmg, String hitsound = "", double rangemul = 1. ) + { + FTranslatedLineTarget t; + double slope = AimLineAttack(angle,1.5*DEFMELEERANGE*rangemul,t,0.,ALF_CHECK3D); + FLineTraceData d; + LineTrace(angle,1.5*DEFMELEERANGE*rangemul,slope,0,player.viewheight,data:d); + bool raging = CountInv("RagekitPower"); + if ( (d.HitType == TRACE_HitActor) && !d.HitActor.FindInventory("ParriedBuff") ) + { + bool bloodless = true; + double diff = deltaangle(self.angle,AngleTo(d.HitActor)); + self.angle += clamp(diff,-5.,5.); + SWWMUtility.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000); + if ( raging ) + { + invoker.bEXTREMEDEATH = true; + invoker.bNOEXTREMEDEATH = false; + } + else + { + invoker.bEXTREMEDEATH = false; + invoker.bNOEXTREMEDEATH = true; + } + if ( !d.HitActor.bDORMANT ) // lol oops + d.HitActor.DaggerAlert(self); + int flg = DMG_USEANGLE|DMG_THRUSTLESS; + if ( raging ) flg |= DMG_FOILINVUL; + dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',flg,atan2(d.HitDir.y,d.HitDir.x)); + invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH; + invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH; + int quakin = raging?8:2; + if ( d.HitActor.player ) d.HitActor.A_QuakeEx(quakin,quakin,quakin,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.125*quakin); + if ( !d.HitActor.bNOBLOOD && !d.HitActor.bDORMANT && (raging || !d.HitActor.bINVULNERABLE) ) + { + d.HitActor.TraceBleed(dmg,invoker); + d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); + bloodless = false; + } + else + { + let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation); + p.angle = atan2(-d.HitDir.y,-d.HitDir.x); + } + if ( raging ) + { + let ps = Spawn("BigPunchSplash",d.HitLocation); + ps.target = self; + ps.special1 = dmg; + } + A_QuakeEx(quakin/2,quakin/2,quakin/2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.06*quakin); + if ( raging ) A_StartSound(bloodless?"pusher/althit":"pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP); + else A_StartSound((hitsound!="")?hitsound:bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP); + A_AlertMonsters(swwm_uncapalert?0:300); + return true; + } + return false; + } + action void A_Melee( int dmg = 40, String hitsound = "", double rangemul = 1. ) + { + Vector3 origin = Vec3Offset(0,0,player.viewheight); + Vector3 dir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)); + // check for usables + let ut = new("UseLineTracer"); + ut.uses.Clear(); + ut.Trace(origin,CurSector,dir,DEFMELEERANGE*rangemul,0); + invoker.wallponch = true; + for ( int i=0; i 0. ) sz = Screen.GetHeight()*cs/200.; + if ( crosshairgrow ) sz *= sb.CrosshairSize; + Screen.DrawTexture(ctex,false,int(lagvpos.x),int(lagvpos.y),DTA_DestWidthF,ts.x*sz,DTA_DestHeightF,ts.y*sz,DTA_AlphaChannel,true,DTA_FillColor,ccol); + } + ui Vector3, Color TraceForCrosshair() + { + if ( !ctr ) ctr = new("SWWMCrosshairTracer"); + ctr.ignoreme = Owner; + Vector3 x, y, z, ofs; + double s; + [x, y, z] = swwm_CoordUtil.GetAxes(Owner.pitch,Owner.angle,Owner.roll); + ofs = GetTraceOffset(); + Vector3 origin = level.Vec3Offset(Owner.Vec2OffsetZ(0,0,Owner.player.viewz),ofs.x*x+ofs.y*y+ofs.z*z); + ctr.Trace(origin,level.PointInSector(origin.xy),x,10000.,0); + Color col = crosshaircolor; + int chp = crosshairhealth; + if ( chp >= 2 ) + { + int hp = Clamp(Owner.Health,0,200); + double sat = (hp<150)?1.:(1.-(hp-150)/100.); + Vector3 rgb = SWWMUtility.HSVtoRGB((hp/300.,sat,1.)); + col = Color(int(rgb.x*255),int(rgb.y*255),int(rgb.z*255)); + } + else if ( chp == 1 ) + { + double hp = Clamp(Owner.Health,0,100)/100.; + if ( hp <= 0 ) col = Color(255,0,0); + else if ( hp < .3 ) col = Color(255,int(hp*255/.3),0); + else if ( hp < .85 ) col = Color(int((.6-hp)*255/.3),255,0); + else col = Color(0,255,0); + } + else if ( (ctr.Results.HitType == TRACE_HitActor) && ctr.Results.HitActor.bSHOOTABLE ) + { + // show target health, rather than our own + double hp = ctr.Results.HitActor.Health/double(ctr.Results.HitActor.GetSpawnHealth()); + if ( hp <= 0 ) col = Color(255,0,0); + else if ( hp < .3 ) col = Color(255,int(hp*255/.3),0); + else if ( hp < .85 ) col = Color(int((.6-hp)*255/.3),255,0); + else col = Color(0,255,0); + } + if ( ctr.Results.HitType == TRACE_HitNone ) return level.Vec3Offset(origin,x*10000.), col; + else return ctr.Results.HitPos, col; + } + // where the trace is coming from relative to eyes + virtual clearscope Vector3 GetTraceOffset() const + { + return (0.,0.,0.); + } +} diff --git a/zscript/weapons/swwm_blazeit.zsc b/zscript/weapons/swwm_blazeit.zsc new file mode 100644 index 000000000..34e79c7f4 --- /dev/null +++ b/zscript/weapons/swwm_blazeit.zsc @@ -0,0 +1,890 @@ +// Imanaki Corp Hellfire Cannon Mk3, aka "Hellblazer" (from SWWM series, originally inspired by the Hellraiser from OMGWEAPONS) +// Slot 6, replaces Rocket Launcher, Phoenix Rod, Firestorm + +Class HellblazerX : GhostArtifactX +{ + States + { + Spawn: + XZW1 A -1; // not fullbright + Stop; + } +} + +Class HellblazerXSub : GhostArtifactX +{ + Hellblazer weap; + int ridx; + + Default + { + RenderStyle "Normal"; + } + void UpdateMe() + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + int curtype = -1; + for ( int i=0; i<4; i++ ) + { + if ( weap.loadammo != types[i] ) continue; + curtype = i; + break; + } + static const int sofs[] = {1,7,10,13}; // offsets from SpawnState for each ammo type state label + int idx; + switch ( curtype ) + { + case 0: + idx = ridx-weap.magpos; + if ( idx > 5 ) idx -= 6; + else if ( idx < 0 ) idx += 6; + if ( weap.magstate[ridx] ) SetState(SpawnState); + else SetState(SpawnState+sofs[0]+idx); + break; + case 1: + idx = ridx-weap.magpos; + if ( idx > 2 ) idx -= 3; + else if ( idx < 0 ) idx += 3; + if ( (ridx > 2) || weap.magstate[ridx] ) SetState(SpawnState); + else SetState(SpawnState+sofs[1]+idx); + break; + case 2: + idx = ridx-weap.magpos; + if ( idx > 2 ) idx -= 3; + else if ( idx < 0 ) idx += 3; + if ( (ridx > 2) || weap.magstate[ridx] ) SetState(SpawnState); + else SetState(SpawnState+sofs[2]+idx); + break; + case 3: + idx = ridx-weap.magpos; + if ( idx > 1 ) idx -= 2; + else if ( idx < 0 ) idx += 2; + if ( (ridx > 1) || weap.magstate[ridx] ) SetState(SpawnState); + else SetState(SpawnState+sofs[3]+idx); + break; + default: + // uninitialized pickup (3 blazers) + idx = ridx; + if ( idx > 2 ) SetState(SpawnState); + else SetState(SpawnState+sofs[0]+idx); + break; + } + } + States + { + Spawn: + TNT1 A -1; + Missiles: + XZW1 ABCDEF -1; + Crackshots: + XZW2 ABC -1; + Ravagers: + XZW3 ABC -1; + Nukes: + XZW4 AB -1; + Stop; + } +} + +Class Hellblazer : SWWMWeapon +{ + int clipcount; + bool magstate[6]; // true: rocket was spent + int magpos; // current rotation + Class loadammo, nextammo; + int spinskipped; + HellblazerXSub pickuprockets[6]; + + Property ClipCount : clipcount; + + transient ui TextureID WeaponBox, AmmoIcon[4], LoadedIcon[4]; + transient ui Font TewiFont; + + override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + if ( !WeaponBox ) + { + WeaponBox = TexMan.CheckForTexture("graphics/HUD/HellblazerDisplay.png",TexMan.Type_Any); + AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/HellblazerMissile.png",TexMan.Type_Any); + AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/HellblazerCrackshot.png",TexMan.Type_Any); + AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/HellblazerRavager.png",TexMan.Type_Any); + AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/HellblazerWarhead.png",TexMan.Type_Any); + LoadedIcon[0] = TexMan.CheckForTexture("graphics/HUD/HellblazerMissileLoaded.png",TexMan.Type_Any); + LoadedIcon[1] = TexMan.CheckForTexture("graphics/HUD/HellblazerCrackshotLoaded.png",TexMan.Type_Any); + LoadedIcon[2] = TexMan.CheckForTexture("graphics/HUD/HellblazerRavagerLoaded.png",TexMan.Type_Any); + LoadedIcon[3] = TexMan.CheckForTexture("graphics/HUD/HellblazerWarheadLoaded.png",TexMan.Type_Any); + } + double xx = -56, yy = -49; + Screen.DrawTexture(WeaponBox,false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int curtype = 0; + for ( int i=0; i<4; i++ ) + { + if ( loadammo != types[i] ) continue; + curtype = i; + break; + } + xx += 2; + yy += 1; + for ( int i=0; i<4; i++ ) + { + int amt = Owner.CountInv(types[i]); + String amtstr = String.Format("%3d",amt); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx+xx,by+yy,amtstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); + Screen.DrawTexture(AmmoIcon[i],false,bx+xx+19,by+yy+1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); + yy += 13; + if ( i%2 ) + { + yy -= 26; + xx += 28; + } + } + yy = -18; + switch ( curtype ) + { + case 0: + xx = -54; + for ( int i=0; i<6; i++ ) + { + Screen.DrawTexture(LoadedIcon[0],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); + xx += 9; + } + break; + case 1: + xx = -49; + for ( int i=0; i<3; i++ ) + { + Screen.DrawTexture(LoadedIcon[1],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); + xx += 18; + } + break; + case 2: + xx = -49; + for ( int i=0; i<3; i++ ) + { + Screen.DrawTexture(LoadedIcon[2],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); + xx += 18; + } + break; + case 3: + xx = -45; + for ( int i=0; i<2; i++ ) + { + Screen.DrawTexture(LoadedIcon[3],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); + xx += 27; + } + break; + } + } + + override Vector3 GetTraceOffset() + { + return (10.,3.5,-5.); + } + + action void A_HellblazerFire( int type = 0, bool bAlt = false ) + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + static const Class projs[] = {"HellblazerMissile","HellblazerCrackshot","HellblazerRavager","HellblazerWarhead", + "HellblazerMissile2","HellblazerCrackshot2","HellblazerRavager2","HellblazerWarhead2"}; + static const Color cols[] = {Color(4,3,2),Color(2,4,2),Color(4,2,2),Color(3,2,4)}; + A_StartSound(bAlt?"hellblazer/altfire":"hellblazer/fire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:(bAlt?1.7:.8)); + A_AlertMonsters(swwm_uncapalert?0:bAlt?400:1200); + int qstr = bAlt?4:5; + A_QuakeEx(qstr,qstr,qstr,bAlt?4:12,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.12*qstr); + A_ZoomFactor(bAlt?.96:.93,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_PlayerFire(); + invoker.clipcount = max(0,invoker.clipcount-1); + invoker.magstate[invoker.magpos] = true; + invoker.spinskipped++; + Vector3 x, y, z, x2, y2, z2, dir, origin; + double a, s; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + SWWMUtility.DoKnockback(self,-x,bAlt?22000.:32000.); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3.5*y-5*z); + a = FRandom[Hellblazer](0,360); + s = FRandom[Hellblazer](0,bAlt?.02:.005); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn(projs[type+4*bAlt],origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed; + if ( bAlt ) + { + if ( p.waterlevel <= 0 ) p.vel.z += 3.5; + return; + } + for ( int i=0; i<5; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= 2.4; + s.alpha *= .4; + s.SetShade(cols[type]*Random[Hellblazer](48,63)); + s.vel += vel*.5+x*FRandom[Hellblazer](6.,10.)+y*FRandom[Hellblazer](-2,2)+z*FRandom[Hellblazer](-2,2); + } + for ( int i=0; i<6; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .7; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Hellblazer](4.,8.)+y*FRandom[Hellblazer](-2,2)+z*FRandom[Hellblazer](-2,2); + } + } + + override bool ReportHUDAmmo() + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + for ( int i=0; i<4; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; + return (clipcount>0); + } + + override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + if ( (firemode == PrimaryFire) || (firemode == AltFire) ) + { + if ( clipcount > 0 ) return true; + for ( int i=0; i<4; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; + return false; + } + return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); + } + + override bool UsesAmmo( Class kind ) + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + for ( int i=0; i<4; i++ ) if ( kind is types[i] ) return true; + return false; + } + + action void A_GlassOverlay( StateLabel g ) + { + player.SetPSprite(PSP_WEAPON+1,invoker.FindState(g)); + // we have to still use A_Overlay* functions for these + A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); + A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); + } + + action state A_JumpByAmmoType( StateLabel a, StateLabel b, StateLabel c, StateLabel d, StateLabel g, StateLabel o = null ) + { + A_Overlay(-9999,o); + A_GlassOverlay(g); + if ( invoker.loadammo is "HellblazerMissiles" ) return invoker.FindState(a); + if ( invoker.loadammo is "HellblazerCrackshots" ) return invoker.FindState(b); + if ( invoker.loadammo is "HellblazerRavagers" ) return invoker.FindState(c); + if ( invoker.loadammo is "HellblazerWarheads" ) return invoker.FindState(d); + return invoker.FindState(a); + } + + override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) + { + bool good = Super.PickupForAmmoSWWM(ownedWeapon); + let Owner = ownedWeapon.Owner; + if ( (AmmoGive1 == 0) && loadammo ) + { + // let's get this bread + Inventory cur = Owner.FindInventory(loadammo); + if ( !cur ) + { + cur = Inventory(Spawn(loadammo)); + cur.Amount = 0; + cur.AttachToOwner(Owner); + } + int maxgiveamt = min(cur.MaxAmount-cur.Amount,clipcount); + int dropamt = clipcount-maxgiveamt; + if ( dropamt > 0 ) cur.CreateTossable(dropamt); + cur.Amount = min(cur.MaxAmount,cur.Amount+clipcount); + good = true; + } + return good; + } + + override void AttachToOwner( Actor other ) + { + Super.AttachToOwner(other); + if ( !loadammo ) + { + // 3 hellblazer missiles loaded + loadammo = "HellblazerMissiles"; + clipcount = 3; + magpos = 0; + for ( int i=0; i<6; i++ ) + magstate[i] = (i>2); + } + nextammo = loadammo; + } + + clearscope int LoadedCapacity() const + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + static const int typeclipcount[] = {6,3,3,2}; + for ( int i=0; i<4; i++ ) + { + if ( loadammo != types[i] ) continue; + let a = Owner.FindInventory(types[i]); + return min(a.Amount+clipcount,typeclipcount[i]); + break; + } + return 0; + } + + action void A_PickNextAmmo() + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + int curidx = 0; + for ( int i=0; i<4; i++ ) + { + if ( invoker.nextammo != types[i] ) continue; + curidx = (i+1)%4; + break; + } + Class oldammo = invoker.nextammo, newammo = invoker.loadammo; + for ( int i=0; i<4; i++ ) + { + int nidx = (i+curidx)%4; + if ( CountInv(types[nidx]) <= 0 ) continue; + newammo = types[nidx]; + break; + } + if ( newammo != oldammo ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL); + invoker.nextammo = newammo; + } + + action void A_ZoomHold() + { + A_WeaponReady(WRF_NOFIRE); + if ( player.cmd.buttons&BT_ZOOM ) return; + player.SetPSPrite(PSP_WEAPON,invoker.FindState("Ready")); + } + + action void A_SwapAmmo() + { + let amo = FindInventory(invoker.loadammo); + // if we're loading the same ammo type, we only need to remove the needed ammo + if ( invoker.loadammo == invoker.nextammo ) + { + int takeamt = invoker.LoadedCapacity()-invoker.clipcount; + invoker.clipcount = invoker.LoadedCapacity(); + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + amo.Amount = max(0,amo.Amount-takeamt); + invoker.magpos = 0; + for ( int i=0; i<6; i++ ) + invoker.magstate[i] = !(invoker.clipcount > i); + return; + } + // re-add/drop any still loaded + int maxgiveamt = min(amo.MaxAmount-amo.Amount,invoker.clipcount); + int dropamt = invoker.clipcount-maxgiveamt; + if ( (dropamt > 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) amo.CreateTossable(dropamt); + amo.Amount = min(amo.MaxAmount,amo.Amount+invoker.clipcount); + // swap + invoker.clipcount = 0; + invoker.loadammo = invoker.nextammo; + invoker.clipcount = invoker.LoadedCapacity(); + invoker.magpos = 0; + for ( int i=0; i<6; i++ ) + invoker.magstate[i] = !(invoker.clipcount > i); + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + { + let namo = FindInventory(invoker.loadammo); + namo.Amount = max(0,namo.Amount-invoker.clipcount); + } + } + + action void A_HellblazerReady() + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; + // can we fire? + bool canfire = (invoker.clipcount > 0); + for ( int i=0; i<4; i++ ) + { + if ( CountInv(types[i]) <= 0 ) continue; + canfire = true; + break; + } + if ( !canfire ) flg |= WRF_NOPRIMARY|WRF_NOSECONDARY; + A_WeaponReady(flg); + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) ) + invoker.CheckAmmo(EitherFire,true); + } + + // check if weapon was dropped or interrupted in some way before the mag spin could be done + action void A_CheckSpinSkip() + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + static const int magcap[] = {6,3,3,2}; + for ( int i=0; i<4; i++ ) + { + if ( invoker.loadammo != types[i] ) continue; + invoker.magpos = (invoker.magpos+invoker.spinskipped)%magcap[i]; + invoker.spinskipped = 0; + break; + } + } + + action void A_UpdatePickup() + { + static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; + for ( int i=0; i<6; i++ ) + { + if ( !invoker.pickuprockets[i] ) + { + invoker.pickuprockets[i] = HellblazerXSub(Spawn("HellblazerXSub",pos)); + invoker.pickuprockets[i].angle = angle; + invoker.pickuprockets[i].target = invoker; + invoker.pickuprockets[i].weap = invoker; + invoker.pickuprockets[i].FloatBobPhase = FloatBobPhase; + invoker.pickuprockets[i].ridx = i; + } + invoker.pickuprockets[i].UpdateMe(); + } + int curtype = 0; + for ( int i=0; i<4; i++ ) + { + if ( invoker.loadammo != types[i] ) continue; + curtype = i; + break; + } + SetState(SpawnState+curtype+1); + } + + override void Travelled() + { + Super.Travelled(); + if ( !tracer ) + { + tracer = Spawn("HellblazerX",pos); + tracer.angle = angle; + tracer.target = self; + tracer.FloatBobPhase = FloatBobPhase; + } + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + tracer = Spawn("HellblazerX",pos); + tracer.angle = angle; + tracer.target = self; + tracer.FloatBobPhase = FloatBobPhase; + } + + Default + { + Tag "$T_HELLBLAZER"; + Inventory.PickupMessage "$I_HELLBLAZER"; + Obituary "$O_HELLBLAZER"; + Inventory.Icon "graphics/HUD/Icons/W_Hellblazer.png"; + Weapon.SlotNumber 6; + Weapon.SelectionOrder 700; + Weapon.UpSound "hellblazer/select"; + Stamina 90000; + Weapon.AmmoType1 "HellblazerMissiles"; + Weapon.AmmoGive1 3; + SWWMWeapon.DropAmmoType "RocketAmmo"; + Hellblazer.ClipCount 6; + +SWWMWEAPON.NOFIRSTGIVE; + +WEAPON.EXPLOSIVE; + Radius 24; + Height 32; + } + States + { + Spawn: + XZW1 A -1 NoDelay A_UpdatePickup(); + XZW1 ABCD -1; + Stop; + Select: + XZW2 I 0 + { + A_CheckSpinSkip(); + A_FullRaise(); + return A_JumpByAmmoType("Select_1","Select_2","Select_3","Select_4","Select_G"); + } + Select_1: + XZW2 IJKLMNOP 2; + Goto Ready_1; + Select_2: + XZW7 DEFGHIJK 2; + Goto Ready_2; + Select_3: + XZWC BCDEFGHI 2; + Goto Ready_3; + Select_4: + XZWG Z 2; + XZWH ABCDEFG 2; + Goto Ready_4; + Select_G: + XZWM ABCDEFGH 2; + Goto Ready_G; + Deselect: + XZW2 A 0 + { + A_StartSound("hellblazer/deselect",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpByAmmoType("Deselect_1","Deselect_2","Deselect_3","Deselect_4","Deselect_G"); + } + Deselect_1: + XZW2 ABCDEFGHI 2; + XZW2 I -1 A_FullLower(); + Stop; + Deselect_2: + XZW6 VWXYZ 2; + XZW7 ABCD 2; + XZW7 D -1 A_FullLower(); + Stop; + Deselect_3: + XZWB TUVWXYZ 2; + XZWC AB 2; + XZWC B -1 A_FullLower(); + Stop; + Deselect_4: + XZWG RSTUVWXYZ 2; + XZWG Z -1 A_FullLower(); + Stop; + Deselect_G: + XZWL STUVWXYZ 2; + XZWM A 2; + Stop; + Ready: + XZW2 A 0 A_JumpByAmmoType("Ready_1","Ready_2","Ready_3","Ready_4","Ready_G"); + Ready_1: + XZW2 A 1 A_HellblazerReady(); + Wait; + Ready_2: + XZW6 V 1 A_HellblazerReady(); + Wait; + Ready_3: + XZWB T 1 A_HellblazerReady(); + Wait; + Ready_4: + XZWG R 1 A_HellblazerReady(); + Wait; + Ready_G: + XZWL S 1; + Wait; + Fire: + XZW2 A 0 + { + if ( invoker.clipcount <= 0 ) + { + if ( CountInv(invoker.nextammo) <= 0 ) A_PickNextAmmo(); + return A_JumpByAmmoType("Unload_1","Unload_2","Unload_3","Unload_4","Unload_G"); + } + return A_JumpByAmmoType("Fire_1","Fire_2","Fire_3","Fire_4","Fire_G"); + } + Fire_1: + XZW2 A 1 A_HellblazerFire(0); + XZW2 QRSTUVW 2; + Goto Cycle_1; + Fire_2: + XZW6 V 1 A_HellblazerFire(1); + XZW7 LMNOPQR 2; + Goto Cycle_2; + Fire_3: + XZWB T 1 A_HellblazerFire(2); + XZWC JKLMNOP 2; + Goto Cycle_3; + Fire_4: + XZWG R 1 A_HellblazerFire(3); + XZWH HIJKLMN 2; + Goto Cycle_4; + Fire_G: + XZWL S 1; + XZWM IJKLMNO 2; + Goto Ready_G; // state jump to cycling is done elsewhere + AltFire: + XZW2 A 0 + { + if ( invoker.clipcount <= 0 ) + { + if ( CountInv(invoker.nextammo) <= 0 ) A_PickNextAmmo(); + return A_JumpByAmmoType("Unload_1","Unload_2","Unload_3","Unload_4","Unload_G"); + } + return A_JumpByAmmoType("AltFire_1","AltFire_2","AltFire_3","AltFire_4","AltFire_G"); + } + AltFire_1: + XZW2 A 1 A_HellblazerFire(0,true); + XZW2 XYZ 2; + XZW3 ABCD 2; + Goto Cycle_1; + AltFire_2: + XZW6 V 1 A_HellblazerFire(1,true); + XZW7 STUVWXY 2; + Goto Cycle_2; + AltFire_3: + XZWB T 1 A_HellblazerFire(2,true); + XZWC QRSTUVW 2; + Goto Cycle_3; + AltFire_4: + XZWG R 1 A_HellblazerFire(3,true); + XZWH OPQRSTU 2; + Goto Cycle_4; + AltFire_G: + XZWL S 1; + XZWM PQRSTUV 2; + Goto Ready_G; // state jump to cycling is done elsewhere + Cycle_1: + XZW2 A 2 + { + invoker.spinskipped--; + invoker.magpos = (invoker.magpos+1)%6; + A_GlassOverlay("Cycle_G1"); + } + XZW3 E 2; + XZW3 FGHI 2; + XZW3 I 0; + XZW3 FE 2; + Goto Ready_1; + Cycle_2: + XZW6 V 2 + { + invoker.spinskipped--; + invoker.magpos = (invoker.magpos+1)%3; + A_GlassOverlay("Cycle_G2"); + } + XZW7 Z 2; + XZW8 ABCDEFG 2; + XZW8 G 0; + XZW8 A 2; + XZW7 Z 2; + Goto Ready_2; + Cycle_3: + XZWB T 2 + { + invoker.spinskipped--; + invoker.magpos = (invoker.magpos+1)%3; + A_GlassOverlay("Cycle_G2"); + } + XZWC X 2; + XZWC YZ 2; + XZWD ABCDE 2; + XZWD E 0; + XZWC YX 2; + Goto Ready_3; + Cycle_4: + XZWG R 2 + { + invoker.spinskipped--; + invoker.magpos = (invoker.magpos+1)%2; + A_GlassOverlay("Cycle_G3"); + } + XZWH V 2; + XZWH WXYZ 2; + XZWI ABCDEF 2; + XZWI F 0; + XZWH WV 2; + Goto Ready_4; + Cycle_G1: + XZWL S 2 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); + XZWM W 2; + XZWM X 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); + XZWM YZ 2; + XZWN A 2; + XZWN A 0 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); + XZWM XW 2; + Goto Ready_G; + Cycle_G2: + XZWL S 2 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); + XZWM W 2; + XZWN B 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); + XZWN CD 2; + XZWN E 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); + XZWN FGH 2; + XZWN H 0 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); + XZWN B 2; + XZWM W 2; + Goto Ready_G; + Cycle_G3: + XZWL S 2 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); + XZWM W 2; + XZWN I 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); + XZWN JK 2; + XZWN L 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); + XZWN MN 2; + XZWN O 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); + XZWN PQR 2; + XZWN R 0 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); + XZWN I 2; + XZWM W 2; + Goto Ready_G; + 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"); + } + Goto Ready; + Unload_1: + XZW2 A 2; + XZW3 JKLMNOPQRSTUVWXYZ 2; + XZW4 ABCDEFGHIJ 2; + XZWR JKLMN 3; + XZW4 J 2 + { + A_SwapAmmo(); + return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); + } + Goto Load_1; + Unload_2: + XZW6 V 2; + XZW8 HIJKLMNOPQRSTUVWXYZ 2; + XZW9 ABCDEFGH 2; + XZWR JKLMN 3; + XZW9 H 2 + { + A_SwapAmmo(); + return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); + } + Goto Load_2; + Unload_3: + XZWB T 2; + XZWD FGHIJKLMNOPQRSTUVWXYZ 2; + XZWE ABCDEF 2; + XZWR JKLMN 3; + XZWE F 2 + { + A_SwapAmmo(); + return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); + } + Goto Load_3; + Unload_4: + XZWG R 2; + XZWI GHIJKLMNOPQRSTUVWXYZ 2; + XZWJ ABCDEFG 2; + XZWR JKLMN 3; + XZWJ G 2 + { + A_SwapAmmo(); + return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); + } + Goto Load_4; + Unload_G: + XZWL S 2 A_StartSound("hellblazer/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + XZWN STU 2; + XZWN V 2 A_StartSound("hellblazer/open",CHAN_WEAPON,CHANF_OVERLAP); + XZWN WXYZ 2; + XZWO ABCDEFGH 2; + XZWO I 2 A_StartSound("hellblazer/magout",CHAN_WEAPON,CHANF_OVERLAP); + XZWO JKLMNOPQRS 2; + XZWR EFGHI 3; + Goto Load_G; + Load_1: + XZW4 JKLMNOPQRSTUVWXYZ 2; + XZW5 ABCDEFGHIJ 2; + Goto Ready_1; + Load_2: + XZW9 HIJKLMNOPQRSTUVWXYZ 2; + XZWA ABCDEFGH 2; + Goto Ready_2; + Load_3: + XZWE FGHIJKLMNOPQRSTUVWXYZ 2; + XZWF ABCDEF 2; + Goto Ready_3; + Load_4: + XZWJ GHIJKLMNOPQRSTUVWXYZ 2; + XZWK ABCDEFG 2; + Goto Ready_4; + Load_G: + XZWO S 2 A_PlayerReload(); + XZWO TUVWXY 2; + XZWO Z 2 A_StartSound("hellblazer/magin",CHAN_WEAPON,CHANF_OVERLAP); + XZWP ABCDEFG 2; + XZWP H 2 A_StartSound("hellblazer/close",CHAN_WEAPON,CHANF_OVERLAP); + XZWP IJKL 2; + XZWP M 2 A_StartSound("hellblazer/meleestop",CHAN_WEAPON,CHANF_OVERLAP); + XZWP NOPQRS 2; + Goto Ready_G; + Idle_1: + XZW2 A 2; + XZW5 KLMNOPQRSTUVWXYZ 2; + XZW6 A 2; + Goto Ready_1; + Idle_2: + XZW6 V 2; + XZWA IJKLMNOPQRSTUVWXY 2; + Goto Ready_2; + Idle_3: + XZWB T 2; + XZWF GHIJKLMNOPQRSTUVW 2; + Goto Ready_3; + Idle_4: + XZWG R 2; + XZWK HIJKLMNOPQRSTUVWX 2; + Goto Ready_4; + Idle_G: + XZWL S 2 + { + A_StartSound("hellblazer/idle",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerCheckGun(); + } + XZWP TUVWX 2; + XZWP Y 2 A_StartSound("hellblazer/dustoff",CHAN_WEAPON,CHANF_OVERLAP); + XZWP Z 2; + XZWQ ABCDEFGHIJ 2; + Goto Ready_G; + Zoom: + #### # 1 + { + A_PickNextAmmo(); + A_WeaponReady(WRF_NOFIRE); + } + #### # 1 A_ZoomHold(); + Wait; + User1: + XZW2 A 0 A_JumpByAmmoType("User1_1","User1_2","User1_3","User1_4","User1_G"); + User1_1: + XZW2 A 2; + XZW6 BCDE 2; + XZW6 FGH 1; + XZW6 IJK 2; + XZW6 LMNOPQRSTU 2; + Goto Ready_1; + User1_2: + XZW6 V 2; + XZWA Z 2; + XZWB ABC 2; + XZWB DEF 1; + XZWB GHI 2; + XZWB JKLMNOPQRS 2; + Goto Ready_2; + User1_3: + XZWB T 2; + XZWF XYZ 2; + XZWG A 2; + XZWG BCD 1; + XZWG EFG 2; + XZWG HIJKLMNOPQ 2; + Goto Ready_3; + User1_4: + XZWG R 2; + XZWK YZ 2; + XZWL AB 2; + XZWL CDE 1; + XZWL FGH 2; + XZWL IJKLMNOPQR 2; + Goto Ready_4; + User1_G: + XZWL S 2 + { + A_StartSound("hellblazer/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerMelee(); + } + XZWQ KLM 2; + XZWQ N 2 A_Parry(9); + XZWQ OP 1; + XZWQ Q 1 A_Melee(75,"demolitionist/whitl",1.05); + XZWQ RSTUV 2; + XZWQ W 2 A_StartSound("hellblazer/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZWQ XYZ 2; + XZWR ABCD 2; + Goto Ready_G; + } +} diff --git a/zscript/swwm_blazeit.zsc b/zscript/weapons/swwm_blazeit_fx.zsc similarity index 56% rename from zscript/swwm_blazeit.zsc rename to zscript/weapons/swwm_blazeit_fx.zsc index 8e90397ab..ae5a381f4 100644 --- a/zscript/swwm_blazeit.zsc +++ b/zscript/weapons/swwm_blazeit_fx.zsc @@ -1,5 +1,4 @@ -// Imanaki Corp Hellfire Cannon Mk3, aka "Hellblazer" (from SWWM series, originally inspired by the Hellraiser from OMGWEAPONS) -// Slot 6, replaces Rocket Launcher, Phoenix Rod, Firestorm +// Hellblazer projectiles and effects Class HellblazerExplLight : PaletteLight { @@ -1233,891 +1232,3 @@ Class HellblazerWarheadArm : Actor Wait; } } - -Class HellblazerX : GhostArtifactX -{ - States - { - Spawn: - XZW1 A -1; // not fullbright - Stop; - } -} - -Class HellblazerXSub : GhostArtifactX -{ - Hellblazer weap; - int ridx; - - Default - { - RenderStyle "Normal"; - } - void UpdateMe() - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - int curtype = -1; - for ( int i=0; i<4; i++ ) - { - if ( weap.loadammo != types[i] ) continue; - curtype = i; - break; - } - static const int sofs[] = {1,7,10,13}; // offsets from SpawnState for each ammo type state label - int idx; - switch ( curtype ) - { - case 0: - idx = ridx-weap.magpos; - if ( idx > 5 ) idx -= 6; - else if ( idx < 0 ) idx += 6; - if ( weap.magstate[ridx] ) SetState(SpawnState); - else SetState(SpawnState+sofs[0]+idx); - break; - case 1: - idx = ridx-weap.magpos; - if ( idx > 2 ) idx -= 3; - else if ( idx < 0 ) idx += 3; - if ( (ridx > 2) || weap.magstate[ridx] ) SetState(SpawnState); - else SetState(SpawnState+sofs[1]+idx); - break; - case 2: - idx = ridx-weap.magpos; - if ( idx > 2 ) idx -= 3; - else if ( idx < 0 ) idx += 3; - if ( (ridx > 2) || weap.magstate[ridx] ) SetState(SpawnState); - else SetState(SpawnState+sofs[2]+idx); - break; - case 3: - idx = ridx-weap.magpos; - if ( idx > 1 ) idx -= 2; - else if ( idx < 0 ) idx += 2; - if ( (ridx > 1) || weap.magstate[ridx] ) SetState(SpawnState); - else SetState(SpawnState+sofs[3]+idx); - break; - default: - // uninitialized pickup (3 blazers) - idx = ridx; - if ( idx > 2 ) SetState(SpawnState); - else SetState(SpawnState+sofs[0]+idx); - break; - } - } - States - { - Spawn: - TNT1 A -1; - Missiles: - XZW1 ABCDEF -1; - Crackshots: - XZW2 ABC -1; - Ravagers: - XZW3 ABC -1; - Nukes: - XZW4 AB -1; - Stop; - } -} - -Class Hellblazer : SWWMWeapon -{ - int clipcount; - bool magstate[6]; // true: rocket was spent - int magpos; // current rotation - Class loadammo, nextammo; - int spinskipped; - HellblazerXSub pickuprockets[6]; - - Property ClipCount : clipcount; - - transient ui TextureID WeaponBox, AmmoIcon[4], LoadedIcon[4]; - transient ui Font TewiFont; - - override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); - if ( !WeaponBox ) - { - WeaponBox = TexMan.CheckForTexture("graphics/HUD/HellblazerDisplay.png",TexMan.Type_Any); - AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/HellblazerMissile.png",TexMan.Type_Any); - AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/HellblazerCrackshot.png",TexMan.Type_Any); - AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/HellblazerRavager.png",TexMan.Type_Any); - AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/HellblazerWarhead.png",TexMan.Type_Any); - LoadedIcon[0] = TexMan.CheckForTexture("graphics/HUD/HellblazerMissileLoaded.png",TexMan.Type_Any); - LoadedIcon[1] = TexMan.CheckForTexture("graphics/HUD/HellblazerCrackshotLoaded.png",TexMan.Type_Any); - LoadedIcon[2] = TexMan.CheckForTexture("graphics/HUD/HellblazerRavagerLoaded.png",TexMan.Type_Any); - LoadedIcon[3] = TexMan.CheckForTexture("graphics/HUD/HellblazerWarheadLoaded.png",TexMan.Type_Any); - } - double xx = -56, yy = -49; - Screen.DrawTexture(WeaponBox,false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int curtype = 0; - for ( int i=0; i<4; i++ ) - { - if ( loadammo != types[i] ) continue; - curtype = i; - break; - } - xx += 2; - yy += 1; - for ( int i=0; i<4; i++ ) - { - int amt = Owner.CountInv(types[i]); - String amtstr = String.Format("%3d",amt); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx+xx,by+yy,amtstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); - Screen.DrawTexture(AmmoIcon[i],false,bx+xx+19,by+yy+1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); - yy += 13; - if ( i%2 ) - { - yy -= 26; - xx += 28; - } - } - yy = -18; - switch ( curtype ) - { - case 0: - xx = -54; - for ( int i=0; i<6; i++ ) - { - Screen.DrawTexture(LoadedIcon[0],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); - xx += 9; - } - break; - case 1: - xx = -49; - for ( int i=0; i<3; i++ ) - { - Screen.DrawTexture(LoadedIcon[1],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); - xx += 18; - } - break; - case 2: - xx = -49; - for ( int i=0; i<3; i++ ) - { - Screen.DrawTexture(LoadedIcon[2],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); - xx += 18; - } - break; - case 3: - xx = -45; - for ( int i=0; i<2; i++ ) - { - Screen.DrawTexture(LoadedIcon[3],false,bx+xx,by+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==magpos)?magstate[i]?Color(192,0,0,0):Color(0,0,0,0):magstate[i]?Color(224,0,0,0):Color(96,0,0,0),DTA_Desaturate,magstate[i]?192:0); - xx += 27; - } - break; - } - } - - override Vector3 GetTraceOffset() - { - return (10.,3.5,-5.); - } - - action void A_HellblazerFire( int type = 0, bool bAlt = false ) - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - static const Class projs[] = {"HellblazerMissile","HellblazerCrackshot","HellblazerRavager","HellblazerWarhead", - "HellblazerMissile2","HellblazerCrackshot2","HellblazerRavager2","HellblazerWarhead2"}; - static const Color cols[] = {Color(4,3,2),Color(2,4,2),Color(4,2,2),Color(3,2,4)}; - A_StartSound(bAlt?"hellblazer/altfire":"hellblazer/fire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:(bAlt?1.7:.8)); - A_AlertMonsters(swwm_uncapalert?0:bAlt?400:1200); - int qstr = bAlt?4:5; - A_QuakeEx(qstr,qstr,qstr,bAlt?4:12,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.12*qstr); - A_ZoomFactor(bAlt?.96:.93,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_PlayerFire(); - invoker.clipcount = max(0,invoker.clipcount-1); - invoker.magstate[invoker.magpos] = true; - invoker.spinskipped++; - Vector3 x, y, z, x2, y2, z2, dir, origin; - double a, s; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - SWWMUtility.DoKnockback(self,-x,bAlt?22000.:32000.); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3.5*y-5*z); - a = FRandom[Hellblazer](0,360); - s = FRandom[Hellblazer](0,bAlt?.02:.005); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let p = Spawn(projs[type+4*bAlt],origin); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed; - if ( bAlt ) - { - if ( p.waterlevel <= 0 ) p.vel.z += 3.5; - return; - } - for ( int i=0; i<5; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.special1 = 1; - s.scale *= 2.4; - s.alpha *= .4; - s.SetShade(cols[type]*Random[Hellblazer](48,63)); - s.vel += vel*.5+x*FRandom[Hellblazer](6.,10.)+y*FRandom[Hellblazer](-2,2)+z*FRandom[Hellblazer](-2,2); - } - for ( int i=0; i<6; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .7; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Hellblazer](4.,8.)+y*FRandom[Hellblazer](-2,2)+z*FRandom[Hellblazer](-2,2); - } - } - - override bool ReportHUDAmmo() - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - for ( int i=0; i<4; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; - return (clipcount>0); - } - - override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - if ( (firemode == PrimaryFire) || (firemode == AltFire) ) - { - if ( clipcount > 0 ) return true; - for ( int i=0; i<4; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; - return false; - } - return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); - } - - override bool UsesAmmo( Class kind ) - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - for ( int i=0; i<4; i++ ) if ( kind is types[i] ) return true; - return false; - } - - action void A_GlassOverlay( StateLabel g ) - { - player.SetPSprite(PSP_WEAPON+1,invoker.FindState(g)); - // we have to still use A_Overlay* functions for these - A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); - A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); - } - - action state A_JumpByAmmoType( StateLabel a, StateLabel b, StateLabel c, StateLabel d, StateLabel g, StateLabel o = null ) - { - A_Overlay(-9999,o); - A_GlassOverlay(g); - if ( invoker.loadammo is "HellblazerMissiles" ) return invoker.FindState(a); - if ( invoker.loadammo is "HellblazerCrackshots" ) return invoker.FindState(b); - if ( invoker.loadammo is "HellblazerRavagers" ) return invoker.FindState(c); - if ( invoker.loadammo is "HellblazerWarheads" ) return invoker.FindState(d); - return invoker.FindState(a); - } - - override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) - { - bool good = Super.PickupForAmmoSWWM(ownedWeapon); - let Owner = ownedWeapon.Owner; - if ( (AmmoGive1 == 0) && loadammo ) - { - // let's get this bread - Inventory cur = Owner.FindInventory(loadammo); - if ( !cur ) - { - cur = Inventory(Spawn(loadammo)); - cur.Amount = 0; - cur.AttachToOwner(Owner); - } - int maxgiveamt = min(cur.MaxAmount-cur.Amount,clipcount); - int dropamt = clipcount-maxgiveamt; - if ( dropamt > 0 ) cur.CreateTossable(dropamt); - cur.Amount = min(cur.MaxAmount,cur.Amount+clipcount); - good = true; - } - return good; - } - - override void AttachToOwner( Actor other ) - { - Super.AttachToOwner(other); - if ( !loadammo ) - { - // 3 hellblazer missiles loaded - loadammo = "HellblazerMissiles"; - clipcount = 3; - magpos = 0; - for ( int i=0; i<6; i++ ) - magstate[i] = (i>2); - } - nextammo = loadammo; - } - - clearscope int LoadedCapacity() const - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - static const int typeclipcount[] = {6,3,3,2}; - for ( int i=0; i<4; i++ ) - { - if ( loadammo != types[i] ) continue; - let a = Owner.FindInventory(types[i]); - return min(a.Amount+clipcount,typeclipcount[i]); - break; - } - return 0; - } - - action void A_PickNextAmmo() - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - int curidx = 0; - for ( int i=0; i<4; i++ ) - { - if ( invoker.nextammo != types[i] ) continue; - curidx = (i+1)%4; - break; - } - Class oldammo = invoker.nextammo, newammo = invoker.loadammo; - for ( int i=0; i<4; i++ ) - { - int nidx = (i+curidx)%4; - if ( CountInv(types[nidx]) <= 0 ) continue; - newammo = types[nidx]; - break; - } - if ( newammo != oldammo ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL); - invoker.nextammo = newammo; - } - - action void A_ZoomHold() - { - A_WeaponReady(WRF_NOFIRE); - if ( player.cmd.buttons&BT_ZOOM ) return; - player.SetPSPrite(PSP_WEAPON,invoker.FindState("Ready")); - } - - action void A_SwapAmmo() - { - let amo = FindInventory(invoker.loadammo); - // if we're loading the same ammo type, we only need to remove the needed ammo - if ( invoker.loadammo == invoker.nextammo ) - { - int takeamt = invoker.LoadedCapacity()-invoker.clipcount; - invoker.clipcount = invoker.LoadedCapacity(); - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - amo.Amount = max(0,amo.Amount-takeamt); - invoker.magpos = 0; - for ( int i=0; i<6; i++ ) - invoker.magstate[i] = !(invoker.clipcount > i); - return; - } - // re-add/drop any still loaded - int maxgiveamt = min(amo.MaxAmount-amo.Amount,invoker.clipcount); - int dropamt = invoker.clipcount-maxgiveamt; - if ( (dropamt > 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) amo.CreateTossable(dropamt); - amo.Amount = min(amo.MaxAmount,amo.Amount+invoker.clipcount); - // swap - invoker.clipcount = 0; - invoker.loadammo = invoker.nextammo; - invoker.clipcount = invoker.LoadedCapacity(); - invoker.magpos = 0; - for ( int i=0; i<6; i++ ) - invoker.magstate[i] = !(invoker.clipcount > i); - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - { - let namo = FindInventory(invoker.loadammo); - namo.Amount = max(0,namo.Amount-invoker.clipcount); - } - } - - action void A_HellblazerReady() - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; - // can we fire? - bool canfire = (invoker.clipcount > 0); - for ( int i=0; i<4; i++ ) - { - if ( CountInv(types[i]) <= 0 ) continue; - canfire = true; - break; - } - if ( !canfire ) flg |= WRF_NOPRIMARY|WRF_NOSECONDARY; - A_WeaponReady(flg); - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) ) - invoker.CheckAmmo(EitherFire,true); - } - - // check if weapon was dropped or interrupted in some way before the mag spin could be done - action void A_CheckSpinSkip() - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - static const int magcap[] = {6,3,3,2}; - for ( int i=0; i<4; i++ ) - { - if ( invoker.loadammo != types[i] ) continue; - invoker.magpos = (invoker.magpos+invoker.spinskipped)%magcap[i]; - invoker.spinskipped = 0; - break; - } - } - - action void A_UpdatePickup() - { - static const Class types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"}; - for ( int i=0; i<6; i++ ) - { - if ( !invoker.pickuprockets[i] ) - { - invoker.pickuprockets[i] = HellblazerXSub(Spawn("HellblazerXSub",pos)); - invoker.pickuprockets[i].angle = angle; - invoker.pickuprockets[i].target = invoker; - invoker.pickuprockets[i].weap = invoker; - invoker.pickuprockets[i].FloatBobPhase = FloatBobPhase; - invoker.pickuprockets[i].ridx = i; - } - invoker.pickuprockets[i].UpdateMe(); - } - int curtype = 0; - for ( int i=0; i<4; i++ ) - { - if ( invoker.loadammo != types[i] ) continue; - curtype = i; - break; - } - SetState(SpawnState+curtype+1); - } - - override void Travelled() - { - Super.Travelled(); - if ( !tracer ) - { - tracer = Spawn("HellblazerX",pos); - tracer.angle = angle; - tracer.target = self; - tracer.FloatBobPhase = FloatBobPhase; - } - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - tracer = Spawn("HellblazerX",pos); - tracer.angle = angle; - tracer.target = self; - tracer.FloatBobPhase = FloatBobPhase; - } - - Default - { - Tag "$T_HELLBLAZER"; - Inventory.PickupMessage "$I_HELLBLAZER"; - Obituary "$O_HELLBLAZER"; - Inventory.Icon "graphics/HUD/Icons/W_Hellblazer.png"; - Weapon.SlotNumber 6; - Weapon.SelectionOrder 700; - Weapon.UpSound "hellblazer/select"; - Stamina 90000; - Weapon.AmmoType1 "HellblazerMissiles"; - Weapon.AmmoGive1 3; - SWWMWeapon.DropAmmoType "RocketAmmo"; - Hellblazer.ClipCount 6; - +SWWMWEAPON.NOFIRSTGIVE; - +WEAPON.EXPLOSIVE; - Radius 24; - Height 32; - } - States - { - Spawn: - XZW1 A -1 NoDelay A_UpdatePickup(); - XZW1 ABCD -1; - Stop; - Select: - XZW2 I 0 - { - A_CheckSpinSkip(); - A_FullRaise(); - return A_JumpByAmmoType("Select_1","Select_2","Select_3","Select_4","Select_G"); - } - Select_1: - XZW2 IJKLMNOP 2; - Goto Ready_1; - Select_2: - XZW7 DEFGHIJK 2; - Goto Ready_2; - Select_3: - XZWC BCDEFGHI 2; - Goto Ready_3; - Select_4: - XZWG Z 2; - XZWH ABCDEFG 2; - Goto Ready_4; - Select_G: - XZWM ABCDEFGH 2; - Goto Ready_G; - Deselect: - XZW2 A 0 - { - A_StartSound("hellblazer/deselect",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpByAmmoType("Deselect_1","Deselect_2","Deselect_3","Deselect_4","Deselect_G"); - } - Deselect_1: - XZW2 ABCDEFGHI 2; - XZW2 I -1 A_FullLower(); - Stop; - Deselect_2: - XZW6 VWXYZ 2; - XZW7 ABCD 2; - XZW7 D -1 A_FullLower(); - Stop; - Deselect_3: - XZWB TUVWXYZ 2; - XZWC AB 2; - XZWC B -1 A_FullLower(); - Stop; - Deselect_4: - XZWG RSTUVWXYZ 2; - XZWG Z -1 A_FullLower(); - Stop; - Deselect_G: - XZWL STUVWXYZ 2; - XZWM A 2; - Stop; - Ready: - XZW2 A 0 A_JumpByAmmoType("Ready_1","Ready_2","Ready_3","Ready_4","Ready_G"); - Ready_1: - XZW2 A 1 A_HellblazerReady(); - Wait; - Ready_2: - XZW6 V 1 A_HellblazerReady(); - Wait; - Ready_3: - XZWB T 1 A_HellblazerReady(); - Wait; - Ready_4: - XZWG R 1 A_HellblazerReady(); - Wait; - Ready_G: - XZWL S 1; - Wait; - Fire: - XZW2 A 0 - { - if ( invoker.clipcount <= 0 ) - { - if ( CountInv(invoker.nextammo) <= 0 ) A_PickNextAmmo(); - return A_JumpByAmmoType("Unload_1","Unload_2","Unload_3","Unload_4","Unload_G"); - } - return A_JumpByAmmoType("Fire_1","Fire_2","Fire_3","Fire_4","Fire_G"); - } - Fire_1: - XZW2 A 1 A_HellblazerFire(0); - XZW2 QRSTUVW 2; - Goto Cycle_1; - Fire_2: - XZW6 V 1 A_HellblazerFire(1); - XZW7 LMNOPQR 2; - Goto Cycle_2; - Fire_3: - XZWB T 1 A_HellblazerFire(2); - XZWC JKLMNOP 2; - Goto Cycle_3; - Fire_4: - XZWG R 1 A_HellblazerFire(3); - XZWH HIJKLMN 2; - Goto Cycle_4; - Fire_G: - XZWL S 1; - XZWM IJKLMNO 2; - Goto Ready_G; // state jump to cycling is done elsewhere - AltFire: - XZW2 A 0 - { - if ( invoker.clipcount <= 0 ) - { - if ( CountInv(invoker.nextammo) <= 0 ) A_PickNextAmmo(); - return A_JumpByAmmoType("Unload_1","Unload_2","Unload_3","Unload_4","Unload_G"); - } - return A_JumpByAmmoType("AltFire_1","AltFire_2","AltFire_3","AltFire_4","AltFire_G"); - } - AltFire_1: - XZW2 A 1 A_HellblazerFire(0,true); - XZW2 XYZ 2; - XZW3 ABCD 2; - Goto Cycle_1; - AltFire_2: - XZW6 V 1 A_HellblazerFire(1,true); - XZW7 STUVWXY 2; - Goto Cycle_2; - AltFire_3: - XZWB T 1 A_HellblazerFire(2,true); - XZWC QRSTUVW 2; - Goto Cycle_3; - AltFire_4: - XZWG R 1 A_HellblazerFire(3,true); - XZWH OPQRSTU 2; - Goto Cycle_4; - AltFire_G: - XZWL S 1; - XZWM PQRSTUV 2; - Goto Ready_G; // state jump to cycling is done elsewhere - Cycle_1: - XZW2 A 2 - { - invoker.spinskipped--; - invoker.magpos = (invoker.magpos+1)%6; - A_GlassOverlay("Cycle_G1"); - } - XZW3 E 2; - XZW3 FGHI 2; - XZW3 I 0; - XZW3 FE 2; - Goto Ready_1; - Cycle_2: - XZW6 V 2 - { - invoker.spinskipped--; - invoker.magpos = (invoker.magpos+1)%3; - A_GlassOverlay("Cycle_G2"); - } - XZW7 Z 2; - XZW8 ABCDEFG 2; - XZW8 G 0; - XZW8 A 2; - XZW7 Z 2; - Goto Ready_2; - Cycle_3: - XZWB T 2 - { - invoker.spinskipped--; - invoker.magpos = (invoker.magpos+1)%3; - A_GlassOverlay("Cycle_G2"); - } - XZWC X 2; - XZWC YZ 2; - XZWD ABCDE 2; - XZWD E 0; - XZWC YX 2; - Goto Ready_3; - Cycle_4: - XZWG R 2 - { - invoker.spinskipped--; - invoker.magpos = (invoker.magpos+1)%2; - A_GlassOverlay("Cycle_G3"); - } - XZWH V 2; - XZWH WXYZ 2; - XZWI ABCDEF 2; - XZWI F 0; - XZWH WV 2; - Goto Ready_4; - Cycle_G1: - XZWL S 2 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); - XZWM W 2; - XZWM X 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); - XZWM YZ 2; - XZWN A 2; - XZWN A 0 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); - XZWM XW 2; - Goto Ready_G; - Cycle_G2: - XZWL S 2 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); - XZWM W 2; - XZWN B 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); - XZWN CD 2; - XZWN E 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); - XZWN FGH 2; - XZWN H 0 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); - XZWN B 2; - XZWM W 2; - Goto Ready_G; - Cycle_G3: - XZWL S 2 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); - XZWM W 2; - XZWN I 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); - XZWN JK 2; - XZWN L 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); - XZWN MN 2; - XZWN O 2 A_StartSound("hellblazer/spin",CHAN_WEAPON,CHANF_OVERLAP); - XZWN PQR 2; - XZWN R 0 A_StartSound("hellblazer/shift",CHAN_WEAPON,CHANF_OVERLAP); - XZWN I 2; - XZWM W 2; - Goto Ready_G; - 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"); - } - Goto Ready; - Unload_1: - XZW2 A 2; - XZW3 JKLMNOPQRSTUVWXYZ 2; - XZW4 ABCDEFGHIJ 2; - XZWR JKLMN 3; - XZW4 J 2 - { - A_SwapAmmo(); - return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); - } - Goto Load_1; - Unload_2: - XZW6 V 2; - XZW8 HIJKLMNOPQRSTUVWXYZ 2; - XZW9 ABCDEFGH 2; - XZWR JKLMN 3; - XZW9 H 2 - { - A_SwapAmmo(); - return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); - } - Goto Load_2; - Unload_3: - XZWB T 2; - XZWD FGHIJKLMNOPQRSTUVWXYZ 2; - XZWE ABCDEF 2; - XZWR JKLMN 3; - XZWE F 2 - { - A_SwapAmmo(); - return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); - } - Goto Load_3; - Unload_4: - XZWG R 2; - XZWI GHIJKLMNOPQRSTUVWXYZ 2; - XZWJ ABCDEFG 2; - XZWR JKLMN 3; - XZWJ G 2 - { - A_SwapAmmo(); - return A_JumpByAmmoType("Load_1","Load_2","Load_3","Load_4","Load_G"); - } - Goto Load_4; - Unload_G: - XZWL S 2 A_StartSound("hellblazer/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - XZWN STU 2; - XZWN V 2 A_StartSound("hellblazer/open",CHAN_WEAPON,CHANF_OVERLAP); - XZWN WXYZ 2; - XZWO ABCDEFGH 2; - XZWO I 2 A_StartSound("hellblazer/magout",CHAN_WEAPON,CHANF_OVERLAP); - XZWO JKLMNOPQRS 2; - XZWR EFGHI 3; - Goto Load_G; - Load_1: - XZW4 JKLMNOPQRSTUVWXYZ 2; - XZW5 ABCDEFGHIJ 2; - Goto Ready_1; - Load_2: - XZW9 HIJKLMNOPQRSTUVWXYZ 2; - XZWA ABCDEFGH 2; - Goto Ready_2; - Load_3: - XZWE FGHIJKLMNOPQRSTUVWXYZ 2; - XZWF ABCDEF 2; - Goto Ready_3; - Load_4: - XZWJ GHIJKLMNOPQRSTUVWXYZ 2; - XZWK ABCDEFG 2; - Goto Ready_4; - Load_G: - XZWO S 2 A_PlayerReload(); - XZWO TUVWXY 2; - XZWO Z 2 A_StartSound("hellblazer/magin",CHAN_WEAPON,CHANF_OVERLAP); - XZWP ABCDEFG 2; - XZWP H 2 A_StartSound("hellblazer/close",CHAN_WEAPON,CHANF_OVERLAP); - XZWP IJKL 2; - XZWP M 2 A_StartSound("hellblazer/meleestop",CHAN_WEAPON,CHANF_OVERLAP); - XZWP NOPQRS 2; - Goto Ready_G; - Idle_1: - XZW2 A 2; - XZW5 KLMNOPQRSTUVWXYZ 2; - XZW6 A 2; - Goto Ready_1; - Idle_2: - XZW6 V 2; - XZWA IJKLMNOPQRSTUVWXY 2; - Goto Ready_2; - Idle_3: - XZWB T 2; - XZWF GHIJKLMNOPQRSTUVW 2; - Goto Ready_3; - Idle_4: - XZWG R 2; - XZWK HIJKLMNOPQRSTUVWX 2; - Goto Ready_4; - Idle_G: - XZWL S 2 - { - A_StartSound("hellblazer/idle",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerCheckGun(); - } - XZWP TUVWX 2; - XZWP Y 2 A_StartSound("hellblazer/dustoff",CHAN_WEAPON,CHANF_OVERLAP); - XZWP Z 2; - XZWQ ABCDEFGHIJ 2; - Goto Ready_G; - Zoom: - #### # 1 - { - A_PickNextAmmo(); - A_WeaponReady(WRF_NOFIRE); - } - #### # 1 A_ZoomHold(); - Wait; - User1: - XZW2 A 0 A_JumpByAmmoType("User1_1","User1_2","User1_3","User1_4","User1_G"); - User1_1: - XZW2 A 2; - XZW6 BCDE 2; - XZW6 FGH 1; - XZW6 IJK 2; - XZW6 LMNOPQRSTU 2; - Goto Ready_1; - User1_2: - XZW6 V 2; - XZWA Z 2; - XZWB ABC 2; - XZWB DEF 1; - XZWB GHI 2; - XZWB JKLMNOPQRS 2; - Goto Ready_2; - User1_3: - XZWB T 2; - XZWF XYZ 2; - XZWG A 2; - XZWG BCD 1; - XZWG EFG 2; - XZWG HIJKLMNOPQ 2; - Goto Ready_3; - User1_4: - XZWG R 2; - XZWK YZ 2; - XZWL AB 2; - XZWL CDE 1; - XZWL FGH 2; - XZWL IJKLMNOPQR 2; - Goto Ready_4; - User1_G: - XZWL S 2 - { - A_StartSound("hellblazer/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerMelee(); - } - XZWQ KLM 2; - XZWQ N 2 A_Parry(9); - XZWQ OP 1; - XZWQ Q 1 A_Melee(75,"demolitionist/whitl",1.05); - XZWQ RSTUV 2; - XZWQ W 2 A_StartSound("hellblazer/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZWQ XYZ 2; - XZWR ABCD 2; - Goto Ready_G; - } -} diff --git a/zscript/swwm_cbt.zsc b/zscript/weapons/swwm_cbt.zsc similarity index 61% rename from zscript/swwm_cbt.zsc rename to zscript/weapons/swwm_cbt.zsc index 7898b95fb..b095e82ac 100644 --- a/zscript/swwm_cbt.zsc +++ b/zscript/weapons/swwm_cbt.zsc @@ -1,828 +1,6 @@ // Blackmann Arms "Wallbuster" Heavy Armor Perforator Shotgun (planned for unreleased Total Destruction UT mod as the "Armor Perforator") // Slot 3, replaces Super Shotgun, Ethereal Crossbow, Frost Shards -Class WallbusterReloadMenu : GenericMenu -{ - transient Font TewiFont, MPlusFont, MiniwiFont, k6x8Font; - TextureID MainWindow, AmmoIcon[4]; - int sel0; - Array queue; - int AmmoSets[4]; - bool isrclick, ismclick; - - // if playing in Japanese, returns an alternate font of the same height - // Tewi -> MPlus - // Miniwi -> k6x8 - Font LangFont( Font req ) - { - if ( language ~== "jp" ) - { - if ( req == MiniwiFont ) return k6x8Font; - return MPlusFont; - } - return req; - } - - override void Init( Menu parent ) - { - Super.Init(parent); - if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].ReadyWeapon is 'Wallbuster') ) - { - EventHandler.SendNetworkEvent("swwmcbt.",consoleplayer); - Close(); - return; - } - TewiFont = Font.GetFont('TewiShaded'); - MPlusFont = Font.GetFont('MPlusShaded'); - MiniwiFont = Font.GetFont('MiniwiShaded'); - k6x8Font = Font.GetFont('k6x8Shaded'); - MainWindow = TexMan.CheckForTexture("graphics/HUD/WallbusterMenu.png",TexMan.Type_Any); - AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/RedShell.png",TexMan.Type_Any); - AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/GreenShell.png",TexMan.Type_Any); - AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/BlueShell.png",TexMan.Type_Any); - AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/PurpleShell.png",TexMan.Type_Any); - MenuSound("menu/demotab"); - queue.Clear(); - sel0 = swwm_cbtlast; - } - - override void Ticker() - { - Super.Ticker(); - if ( swwm_cbtpause ) menuactive = Menu.On; - else menuactive = Menu.OnNoPause; - if ( (players[consoleplayer].Health > 0) && (players[consoleplayer].ReadyWeapon is 'Wallbuster') && (gamestate == GS_LEVEL) ) return; - MenuEvent(MKEY_BACK,false); - } - - private bool IsDone() - { - static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; - if ( queue.Size() >= 25 ) return true; - for ( int i=0; i<4; i++ ) - { - if ( (players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]) > 0 ) - return false; - } - return true; - } - - private bool PushAmmo( bool autoshift = false ) - { - static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; - if ( queue.Size() >= 25 ) return true; - if ( (players[consoleplayer].mo.CountInv(types[sel0])-AmmoSets[sel0]) <= 0 ) - { - if ( autoshift ) - { - // switch to next available ammo - for ( int i=0; i<4; i++ ) - { - int idx = (sel0+i)%4; - if ( (players[consoleplayer].mo.CountInv(types[idx])-AmmoSets[idx]) > 0 ) - { - sel0 = idx; - CVar.FindCVar('swwm_cbtlast').SetInt(sel0); - return PushAmmo(true); - } - } - } - MenuSound("menu/noinvuse"); - return false; - } - if ( !autoshift ) MenuSound("menu/demosel"); - AmmoSets[sel0]++; - queue.Push(sel0); - return true; - } - - private void ShuffleAmmo() - { - static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; - // there's probably a better way to do this but I'm lazy - Array candidates; - candidates.Clear(); - for ( int i=0; i<4; i++ ) - { - if ( (players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]) <= 0 ) - continue; - candidates.Push(i); - } - if ( candidates.Size() <= 0 ) return; - sel0 = candidates[Random[WallbusterMenu](0,candidates.Size()-1)]; - CVar.FindCVar('swwm_cbtlast').SetInt(sel0); - AmmoSets[sel0]++; - queue.Push(sel0); - } - - private bool PopAmmo() - { - if ( queue.Size() <= 0 ) return false; - AmmoSets[queue[queue.Size()-1]]--; - queue.Pop(); - return true; - } - - override bool MenuEvent( int mkey, bool fromcontroller ) - { - switch ( mkey ) - { - case MKEY_BACK: - queue.Clear(); - for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; - MenuSound("menu/democlose"); - EventHandler.SendNetworkEvent("swwmcbt.",consoleplayer); - Close(); - return true; - case MKEY_ENTER: - if ( queue.Size() <= 0 ) - { - while ( queue.Size() < 25 ) - { - if ( !PushAmmo(true) ) - break; - } - } - String cbt = "swwmcbt."; - for ( int i=0; i 3 ) sel0 = 0; - CVar.FindCVar('swwm_cbtlast').SetInt(sel0); - return true; - case MKEY_LEFT: - MenuSound("menu/demotab"); - sel0--; - if ( sel0 < 0 ) sel0 = 3; - CVar.FindCVar('swwm_cbtlast').SetInt(sel0); - return true; - case MKEY_PAGEUP: - if ( queue.Size() <= 0 ) - { - MenuSound("menu/noinvuse"); - return true; - } - int i = 0; - while ( (queue.Size() > 0) && (i++ < 5) ) - { - if ( !PopAmmo() ) - break; - } - MenuSound("menu/demoscroll"); - return true; - case MKEY_PAGEDOWN: - if ( IsDone() ) - { - MenuSound("menu/noinvuse"); - return true; - } - int j = 0; - while ( (queue.Size() < 25) && (j++ < 5) ) - { - if ( !PushAmmo(true) ) - return true; - } - MenuSound("menu/demosel"); - return true; - case MKEY_CLEAR: - if ( queue.Size() <= 0 ) MenuSound("menu/noinvuse"); - else - { - MenuSound("menu/demoscroll"); - queue.Clear(); - for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; - } - return true; - } - return Super.MenuEvent(mkey,fromcontroller); - } - - override bool OnUiEvent( UIEvent ev ) - { - int y; - bool res; - switch ( ev.type ) - { - case UIEvent.Type_KeyDown: - if ( ev.keychar == UiEvent.Key_Tab ) - { - // shuffle! - queue.Clear(); - for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; - bool didsomething = false; - while ( !IsDone() ) - { - ShuffleAmmo(); - didsomething = true; - } - MenuSound(didsomething?"menu/demosel":"menu/noinvuse"); - } - else if ( ev.keychar == UiEvent.Key_Del ) - { - // empty it out - queue.Clear(); - for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; - MenuSound("menu/democlose"); - EventHandler.SendNetworkEvent("swwmcbt.EMPTY",consoleplayer); - Close(); - } - break; - case UIEvent.Type_LButtonDown: - isrclick = false; - ismclick = false; - return Super.OnUIEvent(ev); - break; - case UIEvent.Type_RButtonDown: - isrclick = true; - ismclick = false; - // copy over what base menus do for L click - y = ev.MouseY; - res = MouseEventBack(MOUSE_Click,ev.MouseX,y); - if ( res ) y = -1; - res |= MouseEvent(MOUSE_Click,ev.MouseX,y); - if ( res ) SetCapture(true); - return false; - break; - case UIEvent.Type_MButtonDown: - isrclick = false; - ismclick = true; - // copy over what base menus do for L click - y = ev.MouseY; - res = MouseEventBack(MOUSE_Click,ev.MouseX,y); - if ( res ) y = -1; - res |= MouseEvent(MOUSE_Click,ev.MouseX,y); - if ( res ) SetCapture(true); - return false; - break; - case UIEvent.Type_RButtonUp: - case UIEvent.Type_MButtonUp: - // copy over what base menus do for L release - if ( mMouseCapture ) - { - SetCapture(false); - y = ev.MouseY; - res = MouseEventBack(MOUSE_Release,ev.MouseX,y); - if ( res ) y = -1; - res |= MouseEvent(MOUSE_Release,ev.MouseX,y); - } - return false; - break; - } - return Super.OnUIEvent(ev); - } - - override void Drawer() - { - static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; - Super.Drawer(); - double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.); - Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; - Vector2 origin = (ss.x-132,ss.y-26)/2.; - Screen.DrawTexture(MainWindow,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int ox = 27, oy = 2; - for ( int i=0; i<4; i++ ) - { - Screen.DrawTexture(AmmoIcon[i],false,origin.x+ox,origin.y+oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==sel0)?Color(0,0,0,0):Color(128,0,0,0)); - String astr = String.Format("%3d",players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]); - Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+ox-(TewiFont.StringWidth(astr)+1),origin.y+oy-2,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==sel0)?Color(0,0,0,0):Color(128,0,0,0)); - ox += 33; - } - // pointer (▸) - Screen.DrawChar(TewiFont,Font.CR_GREEN,origin.x+2+33*sel0,origin.y,0x25B8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int siz = queue.Size()-1; - ox = 2+siz*5+(siz/5); - oy = 15; - for ( int i=0; i<=siz; i++ ) - { - Screen.DrawTexture(AmmoIcon[queue[i]],false,origin.x+ox,origin.y+oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - ox -= 5; - if ( !((i+1)%5) ) ox--; - } - // text stuff - String str; - Font fnt; - int boxw, sw; - double x, y; - fnt = LangFont(TewiFont); - str = StringTable.Localize("$SWWM_BUSTERTITLE"); - sw = fnt.StringWidth(str); - boxw = sw; - fnt = LangFont(MiniwiFont); - str = "(C)2148 Akari Labs"; - sw = fnt.StringWidth(str); - if ( sw > boxw ) boxw = sw; - x = floor((ss.x-boxw)/2.); - y = origin.y-30; - Screen.Dim("Black",.8,int((x-2)*hs),int((y-1)*hs),int((boxw+4)*hs),int(25*hs)); - fnt = LangFont(TewiFont); - str = StringTable.Localize("$SWWM_BUSTERTITLE"); - sw = fnt.StringWidth(str); - x = floor((ss.x-sw)/2.); - Screen.DrawText(fnt,Font.CR_FIRE,x,y,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - y += 14; - fnt = LangFont(MiniwiFont); - str = "(C)2148 Akari Labs"; - sw = fnt.StringWidth(str); - x = floor((ss.x-sw)/2.); - Screen.DrawText(fnt,Font.CR_GOLD,x,y,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - y = origin.y+36; - fnt = LangFont(MiniwiFont); - str = StringTable.Localize("$SWWM_BUSTERKEYS"); - BrokenLines l = fnt.BreakLines(str,300); - boxw = 0; - for ( int i=0; i boxw ) boxw = sw; - } - x = floor((ss.x-boxw)/2.); - Screen.Dim("Black",.8,int((x-2)*hs),int((y-2)*hs),int((boxw+4)*hs),int((9*l.Count()+2)*hs)); - for ( int i=0; i acchits; - int hitplane; - bool busted; - Vector3 bustdir; - int busttics, delay; - double cutheight; - // cached - Vector3 boundsmin, boundsmax, step; - - override void Tick() - { - if ( busted ) - { - busttics++; - if ( busttics > 12 ) - { - Destroy(); - return; - } - SpawnDebris(); - return; - } - // fade out damage - if ( delay > 0 ) - { - delay--; - return; - } - accdamage = int(accdamage*.9-5); - if ( accdamage <= 0 ) - { - Destroy(); - return; - } - } - - private void SpawnDebris( bool initial = false ) - { - double x, y, z; - for ( z=boundsmin.z; z 4) ) continue; - int numpt = Random[Wallbuster](0,3); - for ( int i=0; i= ceil ) faketracer.Results.Tier = TIER_Upper; - else if ( (a.pos.z+a.Height) <= flor ) faketracer.Results.Tier = TIER_Lower; - } - } - else if ( a.BlockingMobj ) - { - Vector3 diff = level.Vec3Diff(a.BlockingMobj.Vec3Offset(0,0,a.BlockingMobj.Height/2),a.pos); - HitNormal = diff.unit(); - faketracer.Results.HitType = TRACE_HitActor; - } - return Bust(faketracer.Results,accdamage,a.target,x,a.pos.z+a.Height/2.); - } - - static bool BustLinetrace( FLineTraceData d, int accdamage, Actor instigator, Vector3 x, double hitz ) - { - LineTracer faketracer = new("LineTracer"); - faketracer.Results.HitType = d.HitType; - faketracer.Results.HitSector = d.HitSector; - faketracer.Results.HitLine = d.HitLine; - faketracer.Results.ffloor = d.Hit3DFloor; - faketracer.Results.Side = d.LineSide; - faketracer.Results.Tier = (d.LinePart==Side.Top)?TIER_UPPER:(d.LinePart==Side.Bottom)?TIER_LOWER:TIER_Middle; - return Bust(faketracer.Results,accdamage,instigator,x,hitz); - } - - static bool Bust( TraceResults d, int accdamage, Actor instigator, Vector3 x, double hitz ) - { - // we can't blow up 3D floors - if ( d.ffloor ) return false; - Sector hs = d.HitSector; - int hp; - if ( d.HitType == TRACE_HitWall ) - { - // no busting the goat - if ( IsIOSWall(d.HitLine) ) return false; - // onesided wall? no bust - if ( !d.HitLine.sidedef[1] ) return false; - // sector is opposite of side hit - hs = d.HitLine.sidedef[!d.Side].sector; - // what part we hit? - if ( d.Tier == TIER_Upper ) hp = 1; // ceiling - else if ( d.Tier == TIER_Lower ) hp = 0; // floor - else return false; // middle ignored - } - else if ( d.HitType == TRACE_HitCeiling ) - { - // no busting the goat - for ( int i=0; i>4)); - bust.accdamage += accdamage; - bust.acchits.Push(accdamage); - bust.bustdir = (bust.bustdir+x)*.5; - double extracut = FRandom[Wallbuster](.01,.04)*bust.accdamage; - // is this actually sticking out? - double thisheight, othersheight, partheight, cutheight; - if ( hp ) - { - thisheight = hs.FindLowestCeilingPoint(); - othersheight = hs.FindHighestCeilingSurrounding(); - if ( (thisheight-othersheight) >= -4. ) return false; - cutheight = min(hitz+extracut,othersheight-4); - } - else - { - thisheight = hs.FindHighestFloorPoint(); - othersheight = hs.FindLowestFloorSurrounding(); - if ( (thisheight-othersheight) <= 4. ) return false; - cutheight = max(hitz-extracut,othersheight+4); - } - if ( hp ) bust.cutheight = mnew?cutheight:max(bust.cutheight,cutheight); - else bust.cutheight = mnew?cutheight:min(bust.cutheight,cutheight); - partheight = abs(thisheight-bust.cutheight); - // skip if we don't cut off enough - if ( partheight < 4. ) return false; - // skip if already busted - if ( bust.busted ) return true; - // not enough total damage - if ( bust.accdamage < 100 ) return false; - // estimate sector volume - Vector2 a = (32767,32767), b = (-32768,-32768); - for ( int i=0; i b.x ) b.x = l.v1.p.x; - if ( l.v2.p.x > b.x ) b.x = l.v2.p.x; - if ( l.v1.p.y > b.y ) b.y = l.v1.p.y; - if ( l.v2.p.y > b.y ) b.y = l.v2.p.y; - } - double girthitude = (b.x-a.x)*(b.y-a.y)*partheight; - // do a grid check to approximate "real" volume, useful for diagonal doors - double ystep = (b.y-a.y)/64.; - double xstep = (b.x-a.x)/64.; - int inspot = 0, allspot = 0; - for ( double y=a.y; y<=b.y; y+=ystep ) for ( double x=a.x; x<=b.x; x+=xstep ) - { - allspot++; - if ( level.PointInSector((x,y)) == hs ) inspot++; - } - if ( allspot <= 0 ) return false; // what the fuck? - girthitude = (girthitude*inspot)/allspot; - // too fucking huge - if ( (girthitude > 16777216) || (max(partheight,max(b.x-a.x,b.y-a.y)) > 1024) ) return false; - // not strong enough to bust - if ( bust.accdamage < girthitude/300. ) return false; - // report bust - if ( Instigator && Instigator.player ) - { - let s = SWWMStats.Find(Instigator.player); - if ( s ) s.busts++; - } - bust.busted = true; - bust.busttics = 0; - // shush - hs.flags |= Sector.SECF_SILENTMOVE; - // filler texture - TextureID rubble = TexMan.CheckForTexture("ASHWALL2",TexMan.Type_Any); - // equivalents for other iwads - if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("ASHWALL",TexMan.Type_Any); - if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("LOOSERCK",TexMan.Type_Any); - if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("WASTE03",TexMan.Type_Any); - if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("SCHWAL01",TexMan.Type_Any); // Strife has a dedicated "destroyed wall" texture, nice - // activate all shoot/use specials (not locked) associated with this sector's two-sided lines - for ( int i=0; i loaded[25]; diff --git a/zscript/weapons/swwm_cbt_fx.zsc b/zscript/weapons/swwm_cbt_fx.zsc new file mode 100644 index 000000000..b0ae3cd70 --- /dev/null +++ b/zscript/weapons/swwm_cbt_fx.zsc @@ -0,0 +1,456 @@ +// Wallbuster effects + +Class BustedQuake : Actor +{ + Default + { + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + +NOINTERACTION; + } + override void PostBeginPlay() + { + if ( special1 < 3 ) A_StartSound("wallbuster/smallbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.32),1./max(1.,special1*.35),1.-special1*.05); + else A_StartSound("wallbuster/bigbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.16),1./max(1.,special1*.35),1.-special1*.03); + A_QuakeEx(special1,special1,special1,20+special1*5,0,300+special1*90,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:special1*.1); + A_AlertMonsters(swwm_uncapalert?0:2500); + } + override void Tick() + { + if ( isFrozen() ) return; + tics--; + if ( tics <= 0 ) Destroy(); + } + States + { + Spawn: + TNT1 A 700; + Stop; + } +} + +// Bustin' makes me feel good +Class BusterWall : Thinker +{ + Sector hitsector; + int accdamage; + Array acchits; + int hitplane; + bool busted; + Vector3 bustdir; + int busttics, delay; + double cutheight; + // cached + Vector3 boundsmin, boundsmax, step; + + override void Tick() + { + if ( busted ) + { + busttics++; + if ( busttics > 12 ) + { + Destroy(); + return; + } + SpawnDebris(); + return; + } + // fade out damage + if ( delay > 0 ) + { + delay--; + return; + } + accdamage = int(accdamage*.9-5); + if ( accdamage <= 0 ) + { + Destroy(); + return; + } + } + + private void SpawnDebris( bool initial = false ) + { + double x, y, z; + for ( z=boundsmin.z; z 4) ) continue; + int numpt = Random[Wallbuster](0,3); + for ( int i=0; i= ceil ) faketracer.Results.Tier = TIER_Upper; + else if ( (a.pos.z+a.Height) <= flor ) faketracer.Results.Tier = TIER_Lower; + } + } + else if ( a.BlockingMobj ) + { + Vector3 diff = level.Vec3Diff(a.BlockingMobj.Vec3Offset(0,0,a.BlockingMobj.Height/2),a.pos); + HitNormal = diff.unit(); + faketracer.Results.HitType = TRACE_HitActor; + } + return Bust(faketracer.Results,accdamage,a.target,x,a.pos.z+a.Height/2.); + } + + static bool BustLinetrace( FLineTraceData d, int accdamage, Actor instigator, Vector3 x, double hitz ) + { + LineTracer faketracer = new("LineTracer"); + faketracer.Results.HitType = d.HitType; + faketracer.Results.HitSector = d.HitSector; + faketracer.Results.HitLine = d.HitLine; + faketracer.Results.ffloor = d.Hit3DFloor; + faketracer.Results.Side = d.LineSide; + faketracer.Results.Tier = (d.LinePart==Side.Top)?TIER_UPPER:(d.LinePart==Side.Bottom)?TIER_LOWER:TIER_Middle; + return Bust(faketracer.Results,accdamage,instigator,x,hitz); + } + + static bool Bust( TraceResults d, int accdamage, Actor instigator, Vector3 x, double hitz ) + { + // we can't blow up 3D floors + if ( d.ffloor ) return false; + Sector hs = d.HitSector; + int hp; + if ( d.HitType == TRACE_HitWall ) + { + // no busting the goat + if ( IsIOSWall(d.HitLine) ) return false; + // onesided wall? no bust + if ( !d.HitLine.sidedef[1] ) return false; + // sector is opposite of side hit + hs = d.HitLine.sidedef[!d.Side].sector; + // what part we hit? + if ( d.Tier == TIER_Upper ) hp = 1; // ceiling + else if ( d.Tier == TIER_Lower ) hp = 0; // floor + else return false; // middle ignored + } + else if ( d.HitType == TRACE_HitCeiling ) + { + // no busting the goat + for ( int i=0; i>4)); + bust.accdamage += accdamage; + bust.acchits.Push(accdamage); + bust.bustdir = (bust.bustdir+x)*.5; + double extracut = FRandom[Wallbuster](.01,.04)*bust.accdamage; + // is this actually sticking out? + double thisheight, othersheight, partheight, cutheight; + if ( hp ) + { + thisheight = hs.FindLowestCeilingPoint(); + othersheight = hs.FindHighestCeilingSurrounding(); + if ( (thisheight-othersheight) >= -4. ) return false; + cutheight = min(hitz+extracut,othersheight-4); + } + else + { + thisheight = hs.FindHighestFloorPoint(); + othersheight = hs.FindLowestFloorSurrounding(); + if ( (thisheight-othersheight) <= 4. ) return false; + cutheight = max(hitz-extracut,othersheight+4); + } + if ( hp ) bust.cutheight = mnew?cutheight:max(bust.cutheight,cutheight); + else bust.cutheight = mnew?cutheight:min(bust.cutheight,cutheight); + partheight = abs(thisheight-bust.cutheight); + // skip if we don't cut off enough + if ( partheight < 4. ) return false; + // skip if already busted + if ( bust.busted ) return true; + // not enough total damage + if ( bust.accdamage < 100 ) return false; + // estimate sector volume + Vector2 a = (32767,32767), b = (-32768,-32768); + for ( int i=0; i b.x ) b.x = l.v1.p.x; + if ( l.v2.p.x > b.x ) b.x = l.v2.p.x; + if ( l.v1.p.y > b.y ) b.y = l.v1.p.y; + if ( l.v2.p.y > b.y ) b.y = l.v2.p.y; + } + double girthitude = (b.x-a.x)*(b.y-a.y)*partheight; + // do a grid check to approximate "real" volume, useful for diagonal doors + double ystep = (b.y-a.y)/64.; + double xstep = (b.x-a.x)/64.; + int inspot = 0, allspot = 0; + for ( double y=a.y; y<=b.y; y+=ystep ) for ( double x=a.x; x<=b.x; x+=xstep ) + { + allspot++; + if ( level.PointInSector((x,y)) == hs ) inspot++; + } + if ( allspot <= 0 ) return false; // what the fuck? + girthitude = (girthitude*inspot)/allspot; + // too fucking huge + if ( (girthitude > 16777216) || (max(partheight,max(b.x-a.x,b.y-a.y)) > 1024) ) return false; + // not strong enough to bust + if ( bust.accdamage < girthitude/300. ) return false; + // report bust + if ( Instigator && Instigator.player ) + { + let s = SWWMStats.Find(Instigator.player); + if ( s ) s.busts++; + } + bust.busted = true; + bust.busttics = 0; + // shush + hs.flags |= Sector.SECF_SILENTMOVE; + // filler texture + TextureID rubble = TexMan.CheckForTexture("ASHWALL2",TexMan.Type_Any); + // equivalents for other iwads + if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("ASHWALL",TexMan.Type_Any); + if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("LOOSERCK",TexMan.Type_Any); + if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("WASTE03",TexMan.Type_Any); + if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("SCHWAL01",TexMan.Type_Any); // Strife has a dedicated "destroyed wall" texture, nice + // activate all shoot/use specials (not locked) associated with this sector's two-sided lines + for ( int i=0; i queue; + int AmmoSets[4]; + bool isrclick, ismclick; + + // if playing in Japanese, returns an alternate font of the same height + // Tewi -> MPlus + // Miniwi -> k6x8 + Font LangFont( Font req ) + { + if ( language ~== "jp" ) + { + if ( req == MiniwiFont ) return k6x8Font; + return MPlusFont; + } + return req; + } + + override void Init( Menu parent ) + { + Super.Init(parent); + if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].ReadyWeapon is 'Wallbuster') ) + { + EventHandler.SendNetworkEvent("swwmcbt.",consoleplayer); + Close(); + return; + } + TewiFont = Font.GetFont('TewiShaded'); + MPlusFont = Font.GetFont('MPlusShaded'); + MiniwiFont = Font.GetFont('MiniwiShaded'); + k6x8Font = Font.GetFont('k6x8Shaded'); + MainWindow = TexMan.CheckForTexture("graphics/HUD/WallbusterMenu.png",TexMan.Type_Any); + AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/RedShell.png",TexMan.Type_Any); + AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/GreenShell.png",TexMan.Type_Any); + AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/BlueShell.png",TexMan.Type_Any); + AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/PurpleShell.png",TexMan.Type_Any); + MenuSound("menu/demotab"); + queue.Clear(); + sel0 = swwm_cbtlast; + } + + override void Ticker() + { + Super.Ticker(); + if ( swwm_cbtpause ) menuactive = Menu.On; + else menuactive = Menu.OnNoPause; + if ( (players[consoleplayer].Health > 0) && (players[consoleplayer].ReadyWeapon is 'Wallbuster') && (gamestate == GS_LEVEL) ) return; + MenuEvent(MKEY_BACK,false); + } + + private bool IsDone() + { + static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; + if ( queue.Size() >= 25 ) return true; + for ( int i=0; i<4; i++ ) + { + if ( (players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]) > 0 ) + return false; + } + return true; + } + + private bool PushAmmo( bool autoshift = false ) + { + static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; + if ( queue.Size() >= 25 ) return true; + if ( (players[consoleplayer].mo.CountInv(types[sel0])-AmmoSets[sel0]) <= 0 ) + { + if ( autoshift ) + { + // switch to next available ammo + for ( int i=0; i<4; i++ ) + { + int idx = (sel0+i)%4; + if ( (players[consoleplayer].mo.CountInv(types[idx])-AmmoSets[idx]) > 0 ) + { + sel0 = idx; + CVar.FindCVar('swwm_cbtlast').SetInt(sel0); + return PushAmmo(true); + } + } + } + MenuSound("menu/noinvuse"); + return false; + } + if ( !autoshift ) MenuSound("menu/demosel"); + AmmoSets[sel0]++; + queue.Push(sel0); + return true; + } + + private void ShuffleAmmo() + { + static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; + // there's probably a better way to do this but I'm lazy + Array candidates; + candidates.Clear(); + for ( int i=0; i<4; i++ ) + { + if ( (players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]) <= 0 ) + continue; + candidates.Push(i); + } + if ( candidates.Size() <= 0 ) return; + sel0 = candidates[Random[WallbusterMenu](0,candidates.Size()-1)]; + CVar.FindCVar('swwm_cbtlast').SetInt(sel0); + AmmoSets[sel0]++; + queue.Push(sel0); + } + + private bool PopAmmo() + { + if ( queue.Size() <= 0 ) return false; + AmmoSets[queue[queue.Size()-1]]--; + queue.Pop(); + return true; + } + + override bool MenuEvent( int mkey, bool fromcontroller ) + { + switch ( mkey ) + { + case MKEY_BACK: + queue.Clear(); + for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; + MenuSound("menu/democlose"); + EventHandler.SendNetworkEvent("swwmcbt.",consoleplayer); + Close(); + return true; + case MKEY_ENTER: + if ( queue.Size() <= 0 ) + { + while ( queue.Size() < 25 ) + { + if ( !PushAmmo(true) ) + break; + } + } + String cbt = "swwmcbt."; + for ( int i=0; i 3 ) sel0 = 0; + CVar.FindCVar('swwm_cbtlast').SetInt(sel0); + return true; + case MKEY_LEFT: + MenuSound("menu/demotab"); + sel0--; + if ( sel0 < 0 ) sel0 = 3; + CVar.FindCVar('swwm_cbtlast').SetInt(sel0); + return true; + case MKEY_PAGEUP: + if ( queue.Size() <= 0 ) + { + MenuSound("menu/noinvuse"); + return true; + } + int i = 0; + while ( (queue.Size() > 0) && (i++ < 5) ) + { + if ( !PopAmmo() ) + break; + } + MenuSound("menu/demoscroll"); + return true; + case MKEY_PAGEDOWN: + if ( IsDone() ) + { + MenuSound("menu/noinvuse"); + return true; + } + int j = 0; + while ( (queue.Size() < 25) && (j++ < 5) ) + { + if ( !PushAmmo(true) ) + return true; + } + MenuSound("menu/demosel"); + return true; + case MKEY_CLEAR: + if ( queue.Size() <= 0 ) MenuSound("menu/noinvuse"); + else + { + MenuSound("menu/demoscroll"); + queue.Clear(); + for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; + } + return true; + } + return Super.MenuEvent(mkey,fromcontroller); + } + + override bool OnUiEvent( UIEvent ev ) + { + int y; + bool res; + switch ( ev.type ) + { + case UIEvent.Type_KeyDown: + if ( ev.keychar == UiEvent.Key_Tab ) + { + // shuffle! + queue.Clear(); + for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; + bool didsomething = false; + while ( !IsDone() ) + { + ShuffleAmmo(); + didsomething = true; + } + MenuSound(didsomething?"menu/demosel":"menu/noinvuse"); + } + else if ( ev.keychar == UiEvent.Key_Del ) + { + // empty it out + queue.Clear(); + for ( int i=0; i<4; i++ ) AmmoSets[i] = 0; + MenuSound("menu/democlose"); + EventHandler.SendNetworkEvent("swwmcbt.EMPTY",consoleplayer); + Close(); + } + break; + case UIEvent.Type_LButtonDown: + isrclick = false; + ismclick = false; + return Super.OnUIEvent(ev); + break; + case UIEvent.Type_RButtonDown: + isrclick = true; + ismclick = false; + // copy over what base menus do for L click + y = ev.MouseY; + res = MouseEventBack(MOUSE_Click,ev.MouseX,y); + if ( res ) y = -1; + res |= MouseEvent(MOUSE_Click,ev.MouseX,y); + if ( res ) SetCapture(true); + return false; + break; + case UIEvent.Type_MButtonDown: + isrclick = false; + ismclick = true; + // copy over what base menus do for L click + y = ev.MouseY; + res = MouseEventBack(MOUSE_Click,ev.MouseX,y); + if ( res ) y = -1; + res |= MouseEvent(MOUSE_Click,ev.MouseX,y); + if ( res ) SetCapture(true); + return false; + break; + case UIEvent.Type_RButtonUp: + case UIEvent.Type_MButtonUp: + // copy over what base menus do for L release + if ( mMouseCapture ) + { + SetCapture(false); + y = ev.MouseY; + res = MouseEventBack(MOUSE_Release,ev.MouseX,y); + if ( res ) y = -1; + res |= MouseEvent(MOUSE_Release,ev.MouseX,y); + } + return false; + break; + } + return Super.OnUIEvent(ev); + } + + override void Drawer() + { + static const Class types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"}; + Super.Drawer(); + double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.); + Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; + Vector2 origin = (ss.x-132,ss.y-26)/2.; + Screen.DrawTexture(MainWindow,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int ox = 27, oy = 2; + for ( int i=0; i<4; i++ ) + { + Screen.DrawTexture(AmmoIcon[i],false,origin.x+ox,origin.y+oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==sel0)?Color(0,0,0,0):Color(128,0,0,0)); + String astr = String.Format("%3d",players[consoleplayer].mo.CountInv(types[i])-AmmoSets[i]); + Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+ox-(TewiFont.StringWidth(astr)+1),origin.y+oy-2,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i==sel0)?Color(0,0,0,0):Color(128,0,0,0)); + ox += 33; + } + // pointer (▸) + Screen.DrawChar(TewiFont,Font.CR_GREEN,origin.x+2+33*sel0,origin.y,0x25B8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int siz = queue.Size()-1; + ox = 2+siz*5+(siz/5); + oy = 15; + for ( int i=0; i<=siz; i++ ) + { + Screen.DrawTexture(AmmoIcon[queue[i]],false,origin.x+ox,origin.y+oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + ox -= 5; + if ( !((i+1)%5) ) ox--; + } + // text stuff + String str; + Font fnt; + int boxw, sw; + double x, y; + fnt = LangFont(TewiFont); + str = StringTable.Localize("$SWWM_BUSTERTITLE"); + sw = fnt.StringWidth(str); + boxw = sw; + fnt = LangFont(MiniwiFont); + str = "(C)2148 Akari Labs"; + sw = fnt.StringWidth(str); + if ( sw > boxw ) boxw = sw; + x = floor((ss.x-boxw)/2.); + y = origin.y-30; + Screen.Dim("Black",.8,int((x-2)*hs),int((y-1)*hs),int((boxw+4)*hs),int(25*hs)); + fnt = LangFont(TewiFont); + str = StringTable.Localize("$SWWM_BUSTERTITLE"); + sw = fnt.StringWidth(str); + x = floor((ss.x-sw)/2.); + Screen.DrawText(fnt,Font.CR_FIRE,x,y,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + y += 14; + fnt = LangFont(MiniwiFont); + str = "(C)2148 Akari Labs"; + sw = fnt.StringWidth(str); + x = floor((ss.x-sw)/2.); + Screen.DrawText(fnt,Font.CR_GOLD,x,y,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + y = origin.y+36; + fnt = LangFont(MiniwiFont); + str = StringTable.Localize("$SWWM_BUSTERKEYS"); + BrokenLines l = fnt.BreakLines(str,300); + boxw = 0; + for ( int i=0; i boxw ) boxw = sw; + } + x = floor((ss.x-boxw)/2.); + Screen.Dim("Black",.8,int((x-2)*hs),int((y-2)*hs),int((boxw+4)*hs),int((9*l.Count()+2)*hs)); + for ( int i=0; i 0) || chambered ) return true; + return false; + } + + override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) + { + if ( (firemode == PrimaryFire) || (firemode == AltFire) ) + { + if ( (Ammo1.Amount > 0) || chambered ) return true; + return false; + } + return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); + } + + override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) + { + // add the chambered shell in + if ( chambered ) AmmoGive1++; + return Super.PickupForAmmoSWWM(ownedWeapon); + } + + override void DoEffect() + { + Super.DoEffect(); + if ( chambered || !pendingload || ((loadtics < 20) && (Ammo1.Amount <= 0)) ) + { + loadtics = 0; + return; + } + loadtics++; + if ( (loadtics == 10) && Owner && Owner.player && (Owner.player.ReadyWeapon == self) ) + Owner.A_StartSound("eviscerator/load",CHAN_WEAPON,CHANF_OVERLAP); + if ( (loadtics == 20) && !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo',true) ) + Ammo1.Amount = max(0,Ammo1.Amount-1); + if ( loadtics == 25 ) + { + pendingload = false; + chambered = true; + } + } + + action void A_StartLoad( int delay = 0 ) + { + invoker.pendingload = true; + invoker.loadtics = -delay; + } + + override Vector3 GetTraceOffset() + { + return (10.,4.,-5.); + } + + action void A_EvisceratorFire() + { + let weap = Weapon(invoker); + if ( !weap ) return; + invoker.isfiring = true; + A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP); + A_QuakeEx(6,6,6,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.5); + A_ZoomFactor(.94,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_SWWMFlash(); + A_PlayerFire(); + SWWMHandler.DoFlash(self,Color(64,255,224,96),3); + A_AlertMonsters(swwm_uncapalert?0:4500); + Vector3 x, y, z, x2, y2, z2, dir, origin; + double a, s; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + SWWMUtility.DoKnockback(self,-x,25000.); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+4*y-5*z); + int trail = CVar.GetCVar('swwm_funtrails',player).GetInt(); + for ( int i=0; i<40; i++ ) + { + a = FRandom[Eviscerator](0,360); + s = FRandom[Eviscerator](0,invoker.extended?.06:.3); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = EvisceratorChunk(Spawn("EvisceratorChunk",origin)); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed*FRandom[Eviscerator](.9,1.1); + if ( invoker.extended ) p.vel *= 1.4; + if ( trail < 8 ) p.trailcolor = max(0,trail); + else if ( trail == 8 ) p.trailcolor = (i%6)+2; + else if ( trail == 9 ) + { + switch ( i%5 ) + { + case 0: + case 3: + p.trailcolor = 8; + break; + case 1: + case 4: + p.trailcolor = 9; + break; + case 2: + p.trailcolor = 1; + break; + } + } + } + for ( int i=0; i<8; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= .9; + s.alpha *= .3; + s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); + s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-1,1)+z*FRandom[Eviscerator](-1,1); + } + for ( int i=0; i<9; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); + } + } + + action void A_EvisceratorAltFire() + { + let weap = Weapon(invoker); + if ( !weap ) return; + invoker.chambered = false; + invoker.isfiring = true; + A_StartSound("eviscerator/altfire",CHAN_WEAPON,CHANF_OVERLAP); + A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP); + A_QuakeEx(4,4,4,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.9); + A_ZoomFactor(.91,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_SWWMFlash(); + A_PlayerFire(); + SWWMHandler.DoFlash(self,Color(16,255,224,96),3); + A_AlertMonsters(swwm_uncapalert?0:4000); + Vector3 x, y, z, x2, y2, z2, dir, origin; + double a, s; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + SWWMUtility.DoKnockback(self,-x,32000.); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z); + a = FRandom[Eviscerator](0,360); + s = FRandom[Eviscerator](0,invoker.extended?.003:.02); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("EvisceratorProj",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed*(invoker.extended?1.6:.8); + for ( int i=0; i<6; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= .9; + s.alpha *= .2; + s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); + s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); + } + for ( int i=0; i<5; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); + } + } + + action void A_EvisceratorEject() + { + Vector3 x, y, z, origin; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*10-y*10-z*10); + let c = Spawn("EvisceratorCasing",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.5,.5)-y*FRandom[Junk](3,6)-(0,0,FRandom[Junk](4,6)); + c.vel += vel*.5; + invoker.chambered = false; + } + + action void A_EvisceratorCasingSmoke( Vector3 ofs ) + { + Vector3 x, y, z, origin; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*ofs.x+y*ofs.y+z*ofs.z); + let s = Spawn("SWWMHalfSmoke",origin); + s.scale *= .2; + s.alpha *= .4; + s.speed *= .1; + } + + Default + { + Tag "$T_EVISCERATOR"; + Inventory.PickupMessage "$I_EVISCERATOR"; + Obituary "$O_EVISCERATOR"; + Inventory.Icon "graphics/HUD/Icons/W_Eviscerator.png"; + Weapon.SlotNumber 5; + Weapon.UpSound "eviscerator/select"; + Weapon.SelectionOrder 300; + Stamina 50000; + Weapon.AmmoType1 "EvisceratorShell"; + Weapon.AmmoGive1 4; + SWWMWeapon.DropAmmoType "EvisceratorShell"; + +WEAPON.EXPLOSIVE; + Radius 20; + Height 32; + } + States + { + Spawn: + XZW1 A -1; + Stop; + Deselect: + XZW2 A 2 + { + A_StartSound("eviscerator/deselect",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.extended,"DeselectExt"); + } + XZW2 BCDEFGH 2; + XZW2 H -1 A_FullLower(); + Stop; + DeselectExt: + XZW4 Z 2; + XZW5 ABCDEFG 2; + XZW5 G -1 A_FullLower(); + Stop; + Select: + XZW2 H 2 + { + invoker.isfiring = false; + A_FullRaise(); + return A_JumpIf(invoker.extended,"SelectExt"); + } + XZW2 IJKLMNOPQR 2; + Goto Ready; + SelectExt: + XZW5 GHIJKLMNOPQ 2; + Goto ReadyExt; + Ready: + XZW2 A 1 + { + invoker.isfiring = false; + int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; + if ( !invoker.chambered ) + { + flg |= WRF_NOFIRE; + // autoloader + if ( !invoker.pendingload ) + invoker.pendingload = true; + } + A_WeaponReady(flg); + // avoid the check while still chambering + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) && (invoker.loadtics < 20) ) + invoker.CheckAmmo(EitherFire,true); + } + Wait; + ReadyExt: + XZW4 Z 1 + { + invoker.isfiring = false; + int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; + if ( !invoker.chambered ) + { + flg |= WRF_NOFIRE; + // autoloader + if ( !invoker.pendingload ) + invoker.pendingload = true; + } + A_WeaponReady(flg); + // avoid the check while still chambering + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) && (invoker.loadtics < 20) ) + invoker.CheckAmmo(EitherFire,true); + } + Wait; + Fire: + XZW2 A 1 + { + A_EvisceratorFire(); + return A_JumpIf(invoker.extended,"FireExt"); + } + XZW3 EFGHIJKLMNOPQR 1; + Goto Eject; + FireExt: + XZW4 Z 1; + XZW6 DEFGHIJKLMNOPQ 1; + Goto EjectExt; + Eject: + XZW2 A 4; + XZW3 STUV 2; + XZW3 W 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 X 1 + { + int layer = PSP_WEAPON+1; + while ( player.FindPSprite(layer) ) layer++; + A_Overlay(layer,"EjectCasing"); + A_Overlay(-9999,"EjectSmoke"); + } + XZW3 YZ 1; + XZW4 AB 1; + XZW4 C 1 + { + A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); + A_StartLoad(); + } + XZW4 DEF 1; + XZW4 GHI 2; + Goto Ready; + EjectExt: + XZW4 Z 4; + XZW6 RSTU 2; + XZW6 V 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP); + XZW6 W 1 + { + int layer = PSP_WEAPON+1; + while ( player.FindPSprite(layer) ) layer++; + A_Overlay(layer,"EjectCasing"); + A_Overlay(-9999,"EjectSmoke"); + } + XZW6 XYZ 1; + XZW7 A 1; + XZW7 B 1 + { + A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); + A_StartLoad(); + } + XZW7 CDE 1; + XZW7 FGH 2; + Goto ReadyExt; + EjectCasing: + XZWB D 1 + { + A_OverlayOffset(OverlayID(),0,0); + invoker.casex = FRandom[Eviscerator](-2.,2.); + invoker.casey = FRandom[Eviscerator](-2.,2.); + } + XZWB EFGHIJKLM 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); + TNT1 A 1 A_EvisceratorEject(); + Stop; + EjectSmoke: + TNT1 A 1 A_EvisceratorCasingSmoke((10,2,-3)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-1,-2)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-3,-1.5)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-5,-2)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-7,-3.5)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-8.5,-5)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-10,-9)); + TNT1 A 1 A_EvisceratorCasingSmoke((10,-11,-14)); + Stop; + AltFire: + XZW2 A 2 + { + A_EvisceratorAltFire(); + return A_JumpIf(invoker.extended,"AltFireExt"); + } + XZW2 STUVW 1; + XZW2 XYZ 2; + XZW3 ABCD 2; + XZW2 A 1 A_StartLoad(5); + Goto Ready; + AltFireExt: + XZW4 Z 2; + XZW5 RSTUV 1; + XZW5 WXY 2; + XZW5 Z 2; + XZW6 ABC 2; + XZW4 Z 1 A_StartLoad(5); + Goto ReadyExt; + Zoom: + XZW2 A 2 + { + A_StartSound("eviscerator/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.extended,"ZoomExt"); + } + XZW4 JKLMN 2; + XZW4 O 1 A_StartSound("eviscerator/switch"); + XZW4 PQR 1; + XZW4 S 2 + { + invoker.extended = !invoker.extended; + A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + } + XZW4 TUVWY 2; + Goto ReadyExt; + ZoomExt: + XZW4 Z 2; + XZW7 IJK 3; + XZW7 L 1 A_StartSound("eviscerator/switch"); + XZW7 MNO 1; + XZW7 P 2 + { + invoker.extended = !invoker.extended; + A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + } + XZW7 QRSTU 2; + Goto Ready; + Reload: + XZW2 A 2 + { + A_StartSound("eviscerator/checkgun",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerCheckGun(); + return A_JumpIf(invoker.extended,"ReloadExt"); + } + XZW7 VWXYZ 2; + XZW8 A 2; + XZW8 BCDEF 3; + XZW8 GHIJK 2; + XZW8 LMNO 3; + XZW8 PQRSTU 2; + XZW8 V 3; + Goto Ready; + ReloadExt: + XZW4 Z 2; + XZW9 MNOPQR 2; + XZW9 STUVW 3; + XZW9 XYZ 2; + XZWA AB 2; + XZWA CDEF 3; + XZWA GHIJKL 2; + XZWA M 3; + Goto ReadyExt; + User1: + XZW2 A 2 + { + A_StartSound("eviscerator/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerMelee(); + return A_JumpIf(invoker.extended,"User1Ext"); + } + XZW8 WXY 2; + XZW8 Z 1; + XZW9 AB 1; + XZW9 C 1 A_Parry(9); + XZW9 D 1; + XZW9 E 2 A_Melee(60,"demolitionist/whitm",1.1); + XZW9 FGH 2; + XZW9 I 2 A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW9 JKL 2; + Goto Ready; + User1Ext: + XZW4 Z 2; + XZWA NOP 2; + XZWA QRS 1; + XZWA T 1 A_Parry(9); + XZWA U 1; + XZWA V 2 A_Melee(60,"demolitionist/whitm",1.1); + XZWA WXY 2; + XZWA Z 2 A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZWB ABC 2; + Goto ReadyExt; + Flash: + XZWZ A 2 + { + let psp = player.GetPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](0,3); + let l = Spawn("SWWMWeaponLight",pos); + l.target = self; + } + Stop; + AltFlash: + XZWZ A 2 + { + let psp = player.GetPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](4,7); + let l = Spawn("SWWMWeaponLight",pos); + l.target = self; + l.args[3] -= 20; + } + Stop; + } +} diff --git a/zscript/swwm_danmaku.zsc b/zscript/weapons/swwm_danmaku_fx.zsc similarity index 56% rename from zscript/swwm_danmaku.zsc rename to zscript/weapons/swwm_danmaku_fx.zsc index 1b2e516a3..c424fee9f 100644 --- a/zscript/swwm_danmaku.zsc +++ b/zscript/weapons/swwm_danmaku_fx.zsc @@ -1,5 +1,4 @@ -// Mr. BIG SHOT Industries "Eviscerator" High Load Flak Cannon (from SWWM series) -// Slot 5, replaces Chaingun, Dragon Claw, Hammer of Retribution +// Eviscerator projectiles and effects Class EvisceratorChunkLight : PointLightAttenuated { @@ -702,506 +701,3 @@ Class EvisceratorCasing : SWWMCasing Stop; } } - -Class Eviscerator : SWWMWeapon -{ - double casex, casey; - bool isfiring; - // barrel is extended - bool extended; - // pending shell load - bool pendingload; - // has shell chambered - bool chambered; - // countdown to loading new shell - int loadtics; - - transient ui TextureID WeaponBox, AmmoIcon; - transient ui Font TewiFont; - - override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) - { - if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/EvisceratorDisplay.png",TexMan.Type_Any); - if ( !AmmoIcon ) AmmoIcon = TexMan.CheckForTexture("graphics/HUD/EvisceratorShell.png",TexMan.Type_Any); - if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); - Screen.DrawTexture(WeaponBox,false,bx-46,by-16,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - String astr = String.Format("%d",Ammo1.Amount); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-14-(TewiFont.StringWidth(astr)+1),by-15,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawTexture(AmmoIcon,false,bx-14,by-14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,chambered?Color(0,0,0,0):Color(128,0,0,0)); - Screen.DrawText(TewiFont,Font.CR_WHITE,bx-44,by-15,extended?"►":"",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - } - - override bool ReportHUDAmmo() - { - if ( (Ammo1.Amount > 0) || chambered ) return true; - return false; - } - - override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) - { - if ( (firemode == PrimaryFire) || (firemode == AltFire) ) - { - if ( (Ammo1.Amount > 0) || chambered ) return true; - return false; - } - return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); - } - - override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) - { - // add the chambered shell in - if ( chambered ) AmmoGive1++; - return Super.PickupForAmmoSWWM(ownedWeapon); - } - - override void DoEffect() - { - Super.DoEffect(); - if ( chambered || !pendingload || ((loadtics < 20) && (Ammo1.Amount <= 0)) ) - { - loadtics = 0; - return; - } - loadtics++; - if ( (loadtics == 10) && Owner && Owner.player && (Owner.player.ReadyWeapon == self) ) - Owner.A_StartSound("eviscerator/load",CHAN_WEAPON,CHANF_OVERLAP); - if ( (loadtics == 20) && !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo',true) ) - Ammo1.Amount = max(0,Ammo1.Amount-1); - if ( loadtics == 25 ) - { - pendingload = false; - chambered = true; - } - } - - action void A_StartLoad( int delay = 0 ) - { - invoker.pendingload = true; - invoker.loadtics = -delay; - } - - override Vector3 GetTraceOffset() - { - return (10.,4.,-5.); - } - - action void A_EvisceratorFire() - { - let weap = Weapon(invoker); - if ( !weap ) return; - invoker.isfiring = true; - A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP); - A_QuakeEx(6,6,6,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.5); - A_ZoomFactor(.94,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_SWWMFlash(); - A_PlayerFire(); - SWWMHandler.DoFlash(self,Color(64,255,224,96),3); - A_AlertMonsters(swwm_uncapalert?0:4500); - Vector3 x, y, z, x2, y2, z2, dir, origin; - double a, s; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - SWWMUtility.DoKnockback(self,-x,25000.); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+4*y-5*z); - int trail = CVar.GetCVar('swwm_funtrails',player).GetInt(); - for ( int i=0; i<40; i++ ) - { - a = FRandom[Eviscerator](0,360); - s = FRandom[Eviscerator](0,invoker.extended?.06:.3); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let p = EvisceratorChunk(Spawn("EvisceratorChunk",origin)); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed*FRandom[Eviscerator](.9,1.1); - if ( invoker.extended ) p.vel *= 1.4; - if ( trail < 8 ) p.trailcolor = max(0,trail); - else if ( trail == 8 ) p.trailcolor = (i%6)+2; - else if ( trail == 9 ) - { - switch ( i%5 ) - { - case 0: - case 3: - p.trailcolor = 8; - break; - case 1: - case 4: - p.trailcolor = 9; - break; - case 2: - p.trailcolor = 1; - break; - } - } - } - for ( int i=0; i<8; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.special1 = 1; - s.scale *= .9; - s.alpha *= .3; - s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); - s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-1,1)+z*FRandom[Eviscerator](-1,1); - } - for ( int i=0; i<9; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .3; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); - } - } - - action void A_EvisceratorAltFire() - { - let weap = Weapon(invoker); - if ( !weap ) return; - invoker.chambered = false; - invoker.isfiring = true; - A_StartSound("eviscerator/altfire",CHAN_WEAPON,CHANF_OVERLAP); - A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP); - A_QuakeEx(4,4,4,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.9); - A_ZoomFactor(.91,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_SWWMFlash(); - A_PlayerFire(); - SWWMHandler.DoFlash(self,Color(16,255,224,96),3); - A_AlertMonsters(swwm_uncapalert?0:4000); - Vector3 x, y, z, x2, y2, z2, dir, origin; - double a, s; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - SWWMUtility.DoKnockback(self,-x,32000.); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z); - a = FRandom[Eviscerator](0,360); - s = FRandom[Eviscerator](0,invoker.extended?.003:.02); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let p = Spawn("EvisceratorProj",origin); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed*(invoker.extended?1.6:.8); - for ( int i=0; i<6; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.special1 = 1; - s.scale *= .9; - s.alpha *= .2; - s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255)); - s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); - } - for ( int i=0; i<5; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .3; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2); - } - } - - action void A_EvisceratorEject() - { - Vector3 x, y, z, origin; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*10-y*10-z*10); - let c = Spawn("EvisceratorCasing",origin); - c.angle = angle; - c.pitch = pitch; - c.vel = x*FRandom[Junk](-.5,.5)-y*FRandom[Junk](3,6)-(0,0,FRandom[Junk](4,6)); - c.vel += vel*.5; - invoker.chambered = false; - } - - action void A_EvisceratorCasingSmoke( Vector3 ofs ) - { - Vector3 x, y, z, origin; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*ofs.x+y*ofs.y+z*ofs.z); - let s = Spawn("SWWMHalfSmoke",origin); - s.scale *= .2; - s.alpha *= .4; - s.speed *= .1; - } - - Default - { - Tag "$T_EVISCERATOR"; - Inventory.PickupMessage "$I_EVISCERATOR"; - Obituary "$O_EVISCERATOR"; - Inventory.Icon "graphics/HUD/Icons/W_Eviscerator.png"; - Weapon.SlotNumber 5; - Weapon.UpSound "eviscerator/select"; - Weapon.SelectionOrder 300; - Stamina 50000; - Weapon.AmmoType1 "EvisceratorShell"; - Weapon.AmmoGive1 4; - SWWMWeapon.DropAmmoType "EvisceratorShell"; - +WEAPON.EXPLOSIVE; - Radius 20; - Height 32; - } - States - { - Spawn: - XZW1 A -1; - Stop; - Deselect: - XZW2 A 2 - { - A_StartSound("eviscerator/deselect",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.extended,"DeselectExt"); - } - XZW2 BCDEFGH 2; - XZW2 H -1 A_FullLower(); - Stop; - DeselectExt: - XZW4 Z 2; - XZW5 ABCDEFG 2; - XZW5 G -1 A_FullLower(); - Stop; - Select: - XZW2 H 2 - { - invoker.isfiring = false; - A_FullRaise(); - return A_JumpIf(invoker.extended,"SelectExt"); - } - XZW2 IJKLMNOPQR 2; - Goto Ready; - SelectExt: - XZW5 GHIJKLMNOPQ 2; - Goto ReadyExt; - Ready: - XZW2 A 1 - { - invoker.isfiring = false; - int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; - if ( !invoker.chambered ) - { - flg |= WRF_NOFIRE; - // autoloader - if ( !invoker.pendingload ) - invoker.pendingload = true; - } - A_WeaponReady(flg); - // avoid the check while still chambering - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) && (invoker.loadtics < 20) ) - invoker.CheckAmmo(EitherFire,true); - } - Wait; - ReadyExt: - XZW4 Z 1 - { - invoker.isfiring = false; - int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; - if ( !invoker.chambered ) - { - flg |= WRF_NOFIRE; - // autoloader - if ( !invoker.pendingload ) - invoker.pendingload = true; - } - A_WeaponReady(flg); - // avoid the check while still chambering - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) && (invoker.loadtics < 20) ) - invoker.CheckAmmo(EitherFire,true); - } - Wait; - Fire: - XZW2 A 1 - { - A_EvisceratorFire(); - return A_JumpIf(invoker.extended,"FireExt"); - } - XZW3 EFGHIJKLMNOPQR 1; - Goto Eject; - FireExt: - XZW4 Z 1; - XZW6 DEFGHIJKLMNOPQ 1; - Goto EjectExt; - Eject: - XZW2 A 4; - XZW3 STUV 2; - XZW3 W 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 X 1 - { - int layer = PSP_WEAPON+1; - while ( player.FindPSprite(layer) ) layer++; - A_Overlay(layer,"EjectCasing"); - A_Overlay(-9999,"EjectSmoke"); - } - XZW3 YZ 1; - XZW4 AB 1; - XZW4 C 1 - { - A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); - A_StartLoad(); - } - XZW4 DEF 1; - XZW4 GHI 2; - Goto Ready; - EjectExt: - XZW4 Z 4; - XZW6 RSTU 2; - XZW6 V 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP); - XZW6 W 1 - { - int layer = PSP_WEAPON+1; - while ( player.FindPSprite(layer) ) layer++; - A_Overlay(layer,"EjectCasing"); - A_Overlay(-9999,"EjectSmoke"); - } - XZW6 XYZ 1; - XZW7 A 1; - XZW7 B 1 - { - A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP); - A_StartLoad(); - } - XZW7 CDE 1; - XZW7 FGH 2; - Goto ReadyExt; - EjectCasing: - XZWB D 1 - { - A_OverlayOffset(OverlayID(),0,0); - invoker.casex = FRandom[Eviscerator](-2.,2.); - invoker.casey = FRandom[Eviscerator](-2.,2.); - } - XZWB EFGHIJKLM 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); - TNT1 A 1 A_EvisceratorEject(); - Stop; - EjectSmoke: - TNT1 A 1 A_EvisceratorCasingSmoke((10,2,-3)); - TNT1 A 1 A_EvisceratorCasingSmoke((10,-1,-2)); - TNT1 A 1 A_EvisceratorCasingSmoke((10,-3,-1.5)); - TNT1 A 1 A_EvisceratorCasingSmoke((10,-5,-2)); - TNT1 A 1 A_EvisceratorCasingSmoke((10,-7,-3.5)); - TNT1 A 1 A_EvisceratorCasingSmoke((10,-8.5,-5)); - TNT1 A 1 A_EvisceratorCasingSmoke((10,-10,-9)); - TNT1 A 1 A_EvisceratorCasingSmoke((10,-11,-14)); - Stop; - AltFire: - XZW2 A 2 - { - A_EvisceratorAltFire(); - return A_JumpIf(invoker.extended,"AltFireExt"); - } - XZW2 STUVW 1; - XZW2 XYZ 2; - XZW3 ABCD 2; - XZW2 A 1 A_StartLoad(5); - Goto Ready; - AltFireExt: - XZW4 Z 2; - XZW5 RSTUV 1; - XZW5 WXY 2; - XZW5 Z 2; - XZW6 ABC 2; - XZW4 Z 1 A_StartLoad(5); - Goto ReadyExt; - Zoom: - XZW2 A 2 - { - A_StartSound("eviscerator/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.extended,"ZoomExt"); - } - XZW4 JKLMN 2; - XZW4 O 1 A_StartSound("eviscerator/switch"); - XZW4 PQR 1; - XZW4 S 2 - { - invoker.extended = !invoker.extended; - A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - } - XZW4 TUVWY 2; - Goto ReadyExt; - ZoomExt: - XZW4 Z 2; - XZW7 IJK 3; - XZW7 L 1 A_StartSound("eviscerator/switch"); - XZW7 MNO 1; - XZW7 P 2 - { - invoker.extended = !invoker.extended; - A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - } - XZW7 QRSTU 2; - Goto Ready; - Reload: - XZW2 A 2 - { - A_StartSound("eviscerator/checkgun",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerCheckGun(); - return A_JumpIf(invoker.extended,"ReloadExt"); - } - XZW7 VWXYZ 2; - XZW8 A 2; - XZW8 BCDEF 3; - XZW8 GHIJK 2; - XZW8 LMNO 3; - XZW8 PQRSTU 2; - XZW8 V 3; - Goto Ready; - ReloadExt: - XZW4 Z 2; - XZW9 MNOPQR 2; - XZW9 STUVW 3; - XZW9 XYZ 2; - XZWA AB 2; - XZWA CDEF 3; - XZWA GHIJKL 2; - XZWA M 3; - Goto ReadyExt; - User1: - XZW2 A 2 - { - A_StartSound("eviscerator/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerMelee(); - return A_JumpIf(invoker.extended,"User1Ext"); - } - XZW8 WXY 2; - XZW8 Z 1; - XZW9 AB 1; - XZW9 C 1 A_Parry(9); - XZW9 D 1; - XZW9 E 2 A_Melee(60,"demolitionist/whitm",1.1); - XZW9 FGH 2; - XZW9 I 2 A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW9 JKL 2; - Goto Ready; - User1Ext: - XZW4 Z 2; - XZWA NOP 2; - XZWA QRS 1; - XZWA T 1 A_Parry(9); - XZWA U 1; - XZWA V 2 A_Melee(60,"demolitionist/whitm",1.1); - XZWA WXY 2; - XZWA Z 2 A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZWB ABC 2; - Goto ReadyExt; - Flash: - XZWZ A 2 - { - let psp = player.GetPSprite(PSP_FLASH); - psp.frame = Random[GunFlash](0,3); - let l = Spawn("SWWMWeaponLight",pos); - l.target = self; - } - Stop; - AltFlash: - XZWZ A 2 - { - let psp = player.GetPSprite(PSP_FLASH); - psp.frame = Random[GunFlash](4,7); - let l = Spawn("SWWMWeaponLight",pos); - l.target = self; - l.args[3] -= 20; - } - Stop; - } -} diff --git a/zscript/weapons/swwm_deathlydeathcannon.zsc b/zscript/weapons/swwm_deathlydeathcannon.zsc new file mode 100644 index 000000000..f5979fd9b --- /dev/null +++ b/zscript/weapons/swwm_deathlydeathcannon.zsc @@ -0,0 +1,544 @@ +// Ynykron Artifact (from UnSX Series, featured in SWWM Platinum as a secret weapon) +// Slot 0, replaces BFG9000, Firemace, Wraithverge (arc) + +Class Ynykron : SWWMWeapon +{ + transient ui TextureID WeaponBox, ChargeBar[2], BoxSide[2]; + transient ui Font TewiFont; + transient ui DynamicValueInterpolator ChargeInter; + + enum EChargeState + { + CS_IDLE, + CS_CHARGING, + CS_READY, + CS_DISCHARGING, + CS_POSTFIRE + }; + + int chargestate; + double chargelevel; + bool inverted, invertreload; + + double ventalpha, ventfade; + int ventcooldown; + + int clipcount; + + Property ClipCount : clipcount; + + override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/YnykronDisplay.png",TexMan.Type_Any); + if ( !ChargeBar[0] ) ChargeBar[0] = TexMan.CheckForTexture("graphics/HUD/YnykronBarA.png",TexMan.Type_Any); + if ( !ChargeBar[1] ) ChargeBar[1] = TexMan.CheckForTexture("graphics/HUD/YnykronBarB.png",TexMan.Type_Any); + if ( !BoxSide[0] ) BoxSide[0] = TexMan.CheckForTexture("graphics/HUD/YnykronSideA.png",TexMan.Type_Any); + if ( !BoxSide[1] ) BoxSide[1] = TexMan.CheckForTexture("graphics/HUD/YnykronSideB.png",TexMan.Type_Any); + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + Screen.DrawTexture(WeaponBox,false,bx-33,by-44,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int chg = clamp(ChargeInter?ChargeInter.GetValue():int(chargelevel*10),0,400); + int ct = int(((by-2)-chg/10.)*hs.y); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-30,by-16,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawTexture(BoxSide[inverted],false,bx-23,by-31,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,clipcount?Color(0,0,0,0):Color(128,0,0,0)); + Screen.DrawTexture(ChargeBar[inverted],false,bx-6,by-42,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(chargestate==CS_READY)?Color(int(clamp(sin((level.maptime+TicFrac)*8)*40+24,0.,64.)),255,255,255):Color(0,0,0,0),DTA_ClipTop,ct); + } + override void HudTick() + { + Super.HudTick(); + if ( !ChargeInter ) ChargeInter = DynamicValueInterpolator.Create(int(chargelevel*10),.5,1,400); + ChargeInter.Update(int(chargelevel*10)); + } + + override bool ReportHUDAmmo() + { + if ( clipcount > 0 ) return true; + return Super.ReportHUDAmmo(); + } + override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) + { + if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true; + if ( (fireMode == PrimaryFire) || (fireMode == AltFire) ) + return ((clipcount > 0) || (Ammo1.Amount > 0)); + return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); + } + + override void Travelled() + { + Super.Travelled(); + if ( Owner.player && (Owner.player.Readyweapon == self) ) + { + Owner.A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); + if ( chargestate > CS_IDLE ) + Owner.A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,(.025*chargelevel)**3.,2.); + } + } + + override Vector3 GetTraceOffset() + { + return (15.,4.,-1.); + } + + action void A_YnykronFire() + { + A_SWWMFlash(); + A_StopSound(CHAN_WEAPONEXTRA2); + A_StartSound(invoker.inverted?"ynykron/altfire":"ynykron/fire",CHAN_WEAPON,CHANF_OVERLAP,1.,.2); + if ( !swwm_ynykronalert ) + { + // global alert + int ns = level.Sectors.Size(); + for ( int i=0; i= 40. ) + { + if ( Owner.player == players[consoleplayer] ) + Console.Printf(StringTable.Localize("$SWWM_YNYKRONREADY")); + chargestate = CS_READY; + } + if ( Owner.player.ReadyWeapon == self ) + Owner.A_SoundVolume(CHAN_WEAPONEXTRA2,(.025*chargelevel)**3.); + } + if ( Owner.player.ReadyWeapon != self ) return; + let pspm = Owner.player.FindPSprite(PSP_WEAPON); + if ( pspm ) + { + double shiver = (chargelevel*.025)**2.; + pspm.x = FRandom[Shivers](-1.,1.)*shiver; + pspm.y = 32+FRandom[Shivers](-1.,1.)*shiver; + } + let psp = Owner.player.FindPSprite(PSP_WEAPON+1); + if ( !psp ) return; + ventalpha = clamp(ventalpha+ventfade,0.,1.); + psp.alpha = ventalpha; + if ( chargestate >= CS_DISCHARGING ) + { + if ( chargestate == CS_POSTFIRE ) chargelevel = max(chargelevel*1.024-1.,0.); + else chargelevel = max(chargelevel*1.005-.24,0.); + if ( chargelevel <= 0. ) chargestate = CS_IDLE; + if ( Owner.player.ReadyWeapon == self ) + Owner.A_SoundVolume(CHAN_WEAPONEXTRA2,(.025*chargelevel)**3.); + } + } + + override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) + { + // add the loaded box + if ( !AmmoGive1 && clipcount ) AmmoGive1++; + return Super.PickupForAmmoSWWM(ownedWeapon); + } + + Default + { + Tag "$T_YNYKRON"; + Inventory.PickupMessage "$T_YNYKRON"; + Obituary "$O_YNYKRON"; + Inventory.Icon "graphics/HUD/Icons/W_Ynykron.png"; + Weapon.SlotNumber 0; + Weapon.SelectionOrder 9000; + Weapon.UpSound "ynykron/select"; + Stamina 5000000; + Weapon.AmmoType1 "YnykronAmmo"; + Weapon.AmmoGive1 1; + SWWMWeapon.DropAmmoType "YnykronAmmo"; + +SWWMWEAPON.NOFIRSTGIVE; + Ynykron.ClipCount 1; + +WEAPON.BFG; + +WEAPON.EXPLOSIVE; + Radius 32; + Height 36; + } + States + { + Spawn: + XZW1 A -1; + Stop; + Select: + XZW2 L 2 + { + invoker.ventcooldown = Random[Ynykron](6,15); + A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); + if ( invoker.chargelevel > 0. ) + A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,(.025*invoker.chargelevel)**3.,2.); + A_FullRaise(); + } + XZW2 MNOPQRSTUVWXYZ 2; + XZW3 A 2; + Goto Ready; + Ready: + XZW2 A 1 + { + if ( invoker.chargestate == CS_DISCHARGING ) + return ResolveState("Discharge"); + int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; + if ( (invoker.chargestate > CS_IDLE) || ((invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true))) ) + flg |= WRF_ALLOWRELOAD; + if ( (invoker.chargestate > CS_IDLE) || ((invoker.clipcount <= 0) && (invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true)) ) + flg |= WRF_NOSECONDARY; + if ( invoker.chargestate == CS_CHARGING ) + flg |= WRF_NOPRIMARY; + A_WeaponReady(flg); + if ( invoker.chargelevel >= 20. ) + { + invoker.ventcooldown--; + if ( invoker.ventcooldown <= 0 ) + return ResolveState("ReadyVent"); + } + if ( player.cmd.buttons&BT_ATTACK ) + invoker.CheckAmmo(EitherFire,true); + return ResolveState(null); + } + Wait; + ReadyVent: + XZW2 A 1 + { + invoker.ventcooldown = Random[Ynykron](10,15)*5+2*(40-int(invoker.chargelevel)); + A_Overlay(PSP_WEAPON+1,"ReadyVentSmoke"); + A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true); + A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); + A_OverlayAlpha(PSP_WEAPON+1,0.); + A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP); + } + XZWA ABCDEF 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + XZWA G 2 + { + A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + A_StartSound("ynykron/ventclose",CHAN_WEAPON,CHANF_OVERLAP); + } + XZWA HI 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + Goto Ready; + ReadyVentSmoke: + XZWB D 1 + { + invoker.ventfade = .3; + A_StartSound("ynykron/puff",CHAN_WEAPON,CHANF_OVERLAP); + } + XZWB EF 2; + XZWB G 2 + { + invoker.ventfade = -.1; + } + XZWB HIJKLM 2; + Stop; + Fire: + XZW2 A 1 + { + if ( (invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) ) + return ResolveState("Reload"); + if ( invoker.chargestate == CS_IDLE ) + return ResolveState("Charge"); + A_YnykronFire(); + A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP); + return ResolveState(null); + } + XZW3 JK 2; + XZW3 LMNOPQR 3; + Goto Discharging; + FireBlast: + TNT1 AAAAA 1 A_Backblast(); + Stop; + FireSmoke: + XZWA J 1 + { + invoker.ventfade = .3; + A_StartSound("ynykron/puffing",CHAN_WEAPONEXTRA3,CHANF_LOOP); + } + XZWA KL 2; + XZWA MNOPQRS 3; + Goto DischargingSmoke; + AltFire: + XZW2 A 2 + { + if ( (invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) ) + { + invoker.invertreload = true; + return ResolveState("Reload"); + } + A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.inverted,"TakeInverted"); + } + TakeNormal: + XZW2 A 2; + XZW3 STU 2; + XZW3 V 2 + { + A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerReload(); + } + XZW3 WXYZ 2; + XZW4 A 2; + XZW4 B 2 A_StopSound(CHAN_WEAPONEXTRA); + XZW4 CDEFGHIJKLMNO 2; + XZW4 P 0 + { + invoker.inverted = true; + } + Goto PutInverted; + TakeInverted: + XZW5 P 2; + XZW5 QRS 2; + XZW5 T 2 + { + A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerReload(); + } + XZW5 UVWXY 2; + XZW5 Z 2 A_StopSound(CHAN_WEAPONEXTRA); + XZW6 ABCDEFGHIJKLM 2; + XZW6 N 0 + { + invoker.inverted = false; + } + Goto PutNormal; + PutNormal: + XZW4 PQRS 2; + XZW4 T 2 A_StartSound("ynykron/magin",CHAN_WEAPON,CHANF_OVERLAP); + XZW4 UVWXYZ 2; + XZW5 A 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW5 B 2 A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); + XZW5 CDEFGHIJK 2; + XZW5 L 4; + Goto Ready; + PutInverted: + XZW6 NOPQ 2; + XZW6 R 2 A_StartSound("ynykron/magin",CHAN_WEAPON,CHANF_OVERLAP); + XZW6 STUVWX 2; + XZW6 Y 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW6 Z 2 A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); + XZW7 ABCDEFGHI 2; + XZW7 J 4; + XZW5 P 0; + Goto Ready; + Discharge: + XZW2 A 2; + XZW7 NO 2; + XZW7 P 2 A_StartSound("ynykron/latch",CHAN_WEAPON,CHANF_OVERLAP); + XZW7 Q 2 + { + invoker.chargestate = CS_DISCHARGING; + A_Overlay(PSP_WEAPON+1,"DischargeSmoke"); + A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true); + A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); + A_OverlayAlpha(PSP_WEAPON+1,0.); + A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP); + } + XZW7 RSTUV 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + Discharging: + XZW7 W 2 + { + A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + return A_JumpIf(invoker.chargestate==CS_IDLE,1); + } + Wait; + XZW7 W 3 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + XZW7 X 2 + { + A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + A_StartSound("ynykron/ventclose",CHAN_WEAPON,CHANF_OVERLAP); + } + XZW7 YZ 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); + Goto Ready; + DischargeSmoke: + XZWA T 2 + { + invoker.ventfade = .3; + A_StartSound("ynykron/puffing",CHAN_WEAPONEXTRA3,CHANF_LOOP); + } + XZWA UVWXY 2; + DischargingSmoke: + XZWA Z 2 A_JumpIf(invoker.chargestate==CS_IDLE,1); + Wait; + XZWA Z 3 + { + invoker.ventfade = -.1; + A_SoundVolume(CHAN_WEAPONEXTRA3,.8); + A_StartSound("ynykron/puffend",CHAN_WEAPON,CHANF_OVERLAP); + } + XZWB A 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.6); + XZWB B 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.4); + XZWB C 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.2); + XZWB D 2 A_StopSound(CHAN_WEAPONEXTRA3); + Stop; + Charge: + XZW2 A 2; + XZW3 BC 2; + XZW3 D 2 A_StartSound("ynykron/latch",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 EFGHI 2; + XZW2 A 0 + { + if ( gameinfo.gametype&GAME_Strife ) + { + A_StartSound("ynykron/locked",CHAN_WEAPON,CHANF_OVERLAP); + if ( player == players[consoleplayer] ) + Console.Printf(StringTable.Localize("$SWWM_YNYKRONLOCKED")); + } + else + { + invoker.chargestate = CS_CHARGING; + invoker.ventcooldown = Random[Ynykron](6,15); + A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,.01,2.); + } + } + Goto Ready; + Reload: + XZW2 A 2 + { + if ( invoker.chargestate>CS_IDLE ) return ResolveState("Discharge"); + if ( invoker.inverted ) return ResolveState("UnloadInverted"); + A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + return ResolveState(null); + } + UnloadNormal: + XZW2 A 2; + XZW3 STU 2; + XZW3 V 2 A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 WXYZ 2; + XZW4 A 2; + XZW4 B 2 A_StopSound(CHAN_WEAPONEXTRA); + XZW4 CDEFGHIJKLMNO 2; + XZW4 P 0 + { + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + invoker.Ammo1.Amount = max(invoker.Ammo1.Amount-1,0); + invoker.clipcount = 1; + invoker.inverted = false; + // no mag is dropped, depleted crystals are hazardous and should be disposed of properly + if ( invoker.invertreload ) + { + invoker.invertreload = false; + invoker.inverted = true; + return ResolveState("PutInverted"); + } + return ResolveState(null); + } + Goto PutNormal; + UnloadInverted: + XZW5 P 2; + XZW5 QRS 2; + XZW5 T 2 A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); + XZW5 UVWXY 2; + XZW5 Z 2 A_StopSound(CHAN_WEAPONEXTRA); + XZW6 ABCDEFGHIJKLM 2; + XZW6 N 0 + { + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + invoker.Ammo1.Amount = max(invoker.Ammo1.Amount-1,0); + invoker.clipcount = 1; + invoker.inverted = false; + // no mag is dropped, depleted crystals are hazardous and should be disposed of properly + if ( invoker.invertreload ) + { + invoker.invertreload = false; + invoker.inverted = true; + return ResolveState("PutInverted"); + } + return ResolveState(null); + } + Goto PutNormal; + Zoom: + XZW2 A 2 + { + invoker.ventcooldown = Random[Ynykron](6,15); + A_StartSound("ynykron/checkout",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerCheckGun(); + } + XZW8 ABCDEFGHIJKLMNOPQRSTUVW 2; + XZW8 X 4; // smoothen more + Goto Ready; + User1: + XZW2 A 2 + { + invoker.ventcooldown = Random[Ynykron](6,15); + A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerMelee(); + } + XZW8 YZ 2; + XZW9 AB 2; + XZW9 C 1 A_Parry(9); + XZW9 DE 1; + XZW9 F 1 A_Melee(100,"demolitionist/whitl",1.5); + XZW9 GHIJK 1; + XZW9 LMNO 2; + XZW9 P 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW9 QRSTUVWXYZ 2; + Goto Ready; + Deselect: + XZW2 A 2 A_StartSound("ynykron/deselect",CHAN_WEAPON,CHANF_OVERLAP); + XZW2 BCDEFGHIJK 2; + XZW2 L -1 + { + A_StopSound(CHAN_WEAPONEXTRA); + A_StopSound(CHAN_WEAPONEXTRA2); + A_FullLower(); + } + Stop; + Flash: + XZWZ A 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[0] = 255; + l.args[1] = 255; + l.args[2] = 255; + l.args[3] = 500; + l.target = self; + } + Stop; + } +} diff --git a/zscript/swwm_deathlydeathcannon.zsc b/zscript/weapons/swwm_deathlydeathcannon_fx.zsc similarity index 80% rename from zscript/swwm_deathlydeathcannon.zsc rename to zscript/weapons/swwm_deathlydeathcannon_fx.zsc index 20b72deb3..afc8c6393 100644 --- a/zscript/swwm_deathlydeathcannon.zsc +++ b/zscript/weapons/swwm_deathlydeathcannon_fx.zsc @@ -1,5 +1,4 @@ -// Ynykron Artifact (from UnSX Series, featured in SWWM Platinum as a secret weapon) -// Slot 0, replaces BFG9000, Firemace, Wraithverge (arc) +// Ynykron projectiles and effects // cheap way to let players know they just got fucking erased from existence Class PlayerGone : PlayerChunk @@ -2751,545 +2750,3 @@ Class YnykronAltShot : Actor Destroy(); } } - -Class Ynykron : SWWMWeapon -{ - transient ui TextureID WeaponBox, ChargeBar[2], BoxSide[2]; - transient ui Font TewiFont; - transient ui DynamicValueInterpolator ChargeInter; - - enum EChargeState - { - CS_IDLE, - CS_CHARGING, - CS_READY, - CS_DISCHARGING, - CS_POSTFIRE - }; - - int chargestate; - double chargelevel; - bool inverted, invertreload; - - double ventalpha, ventfade; - int ventcooldown; - - int clipcount; - - Property ClipCount : clipcount; - - override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) - { - if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/YnykronDisplay.png",TexMan.Type_Any); - if ( !ChargeBar[0] ) ChargeBar[0] = TexMan.CheckForTexture("graphics/HUD/YnykronBarA.png",TexMan.Type_Any); - if ( !ChargeBar[1] ) ChargeBar[1] = TexMan.CheckForTexture("graphics/HUD/YnykronBarB.png",TexMan.Type_Any); - if ( !BoxSide[0] ) BoxSide[0] = TexMan.CheckForTexture("graphics/HUD/YnykronSideA.png",TexMan.Type_Any); - if ( !BoxSide[1] ) BoxSide[1] = TexMan.CheckForTexture("graphics/HUD/YnykronSideB.png",TexMan.Type_Any); - if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); - Screen.DrawTexture(WeaponBox,false,bx-33,by-44,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int chg = clamp(ChargeInter?ChargeInter.GetValue():int(chargelevel*10),0,400); - int ct = int(((by-2)-chg/10.)*hs.y); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-30,by-16,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawTexture(BoxSide[inverted],false,bx-23,by-31,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,clipcount?Color(0,0,0,0):Color(128,0,0,0)); - Screen.DrawTexture(ChargeBar[inverted],false,bx-6,by-42,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(chargestate==CS_READY)?Color(int(clamp(sin((level.maptime+TicFrac)*8)*40+24,0.,64.)),255,255,255):Color(0,0,0,0),DTA_ClipTop,ct); - } - override void HudTick() - { - Super.HudTick(); - if ( !ChargeInter ) ChargeInter = DynamicValueInterpolator.Create(int(chargelevel*10),.5,1,400); - ChargeInter.Update(int(chargelevel*10)); - } - - override bool ReportHUDAmmo() - { - if ( clipcount > 0 ) return true; - return Super.ReportHUDAmmo(); - } - override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) - { - if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true; - if ( (fireMode == PrimaryFire) || (fireMode == AltFire) ) - return ((clipcount > 0) || (Ammo1.Amount > 0)); - return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); - } - - override void Travelled() - { - Super.Travelled(); - if ( Owner.player && (Owner.player.Readyweapon == self) ) - { - Owner.A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); - if ( chargestate > CS_IDLE ) - Owner.A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,(.025*chargelevel)**3.,2.); - } - } - - override Vector3 GetTraceOffset() - { - return (15.,4.,-1.); - } - - action void A_YnykronFire() - { - A_SWWMFlash(); - A_StopSound(CHAN_WEAPONEXTRA2); - A_StartSound(invoker.inverted?"ynykron/altfire":"ynykron/fire",CHAN_WEAPON,CHANF_OVERLAP,1.,.2); - if ( !swwm_ynykronalert ) - { - // global alert - int ns = level.Sectors.Size(); - for ( int i=0; i= 40. ) - { - if ( Owner.player == players[consoleplayer] ) - Console.Printf(StringTable.Localize("$SWWM_YNYKRONREADY")); - chargestate = CS_READY; - } - if ( Owner.player.ReadyWeapon == self ) - Owner.A_SoundVolume(CHAN_WEAPONEXTRA2,(.025*chargelevel)**3.); - } - if ( Owner.player.ReadyWeapon != self ) return; - let pspm = Owner.player.FindPSprite(PSP_WEAPON); - if ( pspm ) - { - double shiver = (chargelevel*.025)**2.; - pspm.x = FRandom[Shivers](-1.,1.)*shiver; - pspm.y = 32+FRandom[Shivers](-1.,1.)*shiver; - } - let psp = Owner.player.FindPSprite(PSP_WEAPON+1); - if ( !psp ) return; - ventalpha = clamp(ventalpha+ventfade,0.,1.); - psp.alpha = ventalpha; - if ( chargestate >= CS_DISCHARGING ) - { - if ( chargestate == CS_POSTFIRE ) chargelevel = max(chargelevel*1.024-1.,0.); - else chargelevel = max(chargelevel*1.005-.24,0.); - if ( chargelevel <= 0. ) chargestate = CS_IDLE; - if ( Owner.player.ReadyWeapon == self ) - Owner.A_SoundVolume(CHAN_WEAPONEXTRA2,(.025*chargelevel)**3.); - } - } - - override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) - { - // add the loaded box - if ( !AmmoGive1 && clipcount ) AmmoGive1++; - return Super.PickupForAmmoSWWM(ownedWeapon); - } - - Default - { - Tag "$T_YNYKRON"; - Inventory.PickupMessage "$T_YNYKRON"; - Obituary "$O_YNYKRON"; - Inventory.Icon "graphics/HUD/Icons/W_Ynykron.png"; - Weapon.SlotNumber 0; - Weapon.SelectionOrder 9000; - Weapon.UpSound "ynykron/select"; - Stamina 5000000; - Weapon.AmmoType1 "YnykronAmmo"; - Weapon.AmmoGive1 1; - SWWMWeapon.DropAmmoType "YnykronAmmo"; - +SWWMWEAPON.NOFIRSTGIVE; - Ynykron.ClipCount 1; - +WEAPON.BFG; - +WEAPON.EXPLOSIVE; - Radius 32; - Height 36; - } - States - { - Spawn: - XZW1 A -1; - Stop; - Select: - XZW2 L 2 - { - invoker.ventcooldown = Random[Ynykron](6,15); - A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); - if ( invoker.chargelevel > 0. ) - A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,(.025*invoker.chargelevel)**3.,2.); - A_FullRaise(); - } - XZW2 MNOPQRSTUVWXYZ 2; - XZW3 A 2; - Goto Ready; - Ready: - XZW2 A 1 - { - if ( invoker.chargestate == CS_DISCHARGING ) - return ResolveState("Discharge"); - int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; - if ( (invoker.chargestate > CS_IDLE) || ((invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true))) ) - flg |= WRF_ALLOWRELOAD; - if ( (invoker.chargestate > CS_IDLE) || ((invoker.clipcount <= 0) && (invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true)) ) - flg |= WRF_NOSECONDARY; - if ( invoker.chargestate == CS_CHARGING ) - flg |= WRF_NOPRIMARY; - A_WeaponReady(flg); - if ( invoker.chargelevel >= 20. ) - { - invoker.ventcooldown--; - if ( invoker.ventcooldown <= 0 ) - return ResolveState("ReadyVent"); - } - if ( player.cmd.buttons&BT_ATTACK ) - invoker.CheckAmmo(EitherFire,true); - return ResolveState(null); - } - Wait; - ReadyVent: - XZW2 A 1 - { - invoker.ventcooldown = Random[Ynykron](10,15)*5+2*(40-int(invoker.chargelevel)); - A_Overlay(PSP_WEAPON+1,"ReadyVentSmoke"); - A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true); - A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); - A_OverlayAlpha(PSP_WEAPON+1,0.); - A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP); - } - XZWA ABCDEF 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - XZWA G 2 - { - A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - A_StartSound("ynykron/ventclose",CHAN_WEAPON,CHANF_OVERLAP); - } - XZWA HI 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - Goto Ready; - ReadyVentSmoke: - XZWB D 1 - { - invoker.ventfade = .3; - A_StartSound("ynykron/puff",CHAN_WEAPON,CHANF_OVERLAP); - } - XZWB EF 2; - XZWB G 2 - { - invoker.ventfade = -.1; - } - XZWB HIJKLM 2; - Stop; - Fire: - XZW2 A 1 - { - if ( (invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) ) - return ResolveState("Reload"); - if ( invoker.chargestate == CS_IDLE ) - return ResolveState("Charge"); - A_YnykronFire(); - A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP); - return ResolveState(null); - } - XZW3 JK 2; - XZW3 LMNOPQR 3; - Goto Discharging; - FireBlast: - TNT1 AAAAA 1 A_Backblast(); - Stop; - FireSmoke: - XZWA J 1 - { - invoker.ventfade = .3; - A_StartSound("ynykron/puffing",CHAN_WEAPONEXTRA3,CHANF_LOOP); - } - XZWA KL 2; - XZWA MNOPQRS 3; - Goto DischargingSmoke; - AltFire: - XZW2 A 2 - { - if ( (invoker.clipcount <= 0) && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) ) - { - invoker.invertreload = true; - return ResolveState("Reload"); - } - A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.inverted,"TakeInverted"); - } - TakeNormal: - XZW2 A 2; - XZW3 STU 2; - XZW3 V 2 - { - A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerReload(); - } - XZW3 WXYZ 2; - XZW4 A 2; - XZW4 B 2 A_StopSound(CHAN_WEAPONEXTRA); - XZW4 CDEFGHIJKLMNO 2; - XZW4 P 0 - { - invoker.inverted = true; - } - Goto PutInverted; - TakeInverted: - XZW5 P 2; - XZW5 QRS 2; - XZW5 T 2 - { - A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerReload(); - } - XZW5 UVWXY 2; - XZW5 Z 2 A_StopSound(CHAN_WEAPONEXTRA); - XZW6 ABCDEFGHIJKLM 2; - XZW6 N 0 - { - invoker.inverted = false; - } - Goto PutNormal; - PutNormal: - XZW4 PQRS 2; - XZW4 T 2 A_StartSound("ynykron/magin",CHAN_WEAPON,CHANF_OVERLAP); - XZW4 UVWXYZ 2; - XZW5 A 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW5 B 2 A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); - XZW5 CDEFGHIJK 2; - XZW5 L 4; - Goto Ready; - PutInverted: - XZW6 NOPQ 2; - XZW6 R 2 A_StartSound("ynykron/magin",CHAN_WEAPON,CHANF_OVERLAP); - XZW6 STUVWX 2; - XZW6 Y 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW6 Z 2 A_StartSound("ynykron/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.3,4.); - XZW7 ABCDEFGHI 2; - XZW7 J 4; - XZW5 P 0; - Goto Ready; - Discharge: - XZW2 A 2; - XZW7 NO 2; - XZW7 P 2 A_StartSound("ynykron/latch",CHAN_WEAPON,CHANF_OVERLAP); - XZW7 Q 2 - { - invoker.chargestate = CS_DISCHARGING; - A_Overlay(PSP_WEAPON+1,"DischargeSmoke"); - A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true); - A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); - A_OverlayAlpha(PSP_WEAPON+1,0.); - A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - A_StartSound("ynykron/ventopen",CHAN_WEAPON,CHANF_OVERLAP); - } - XZW7 RSTUV 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - Discharging: - XZW7 W 2 - { - A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - return A_JumpIf(invoker.chargestate==CS_IDLE,1); - } - Wait; - XZW7 W 3 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - XZW7 X 2 - { - A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - A_StartSound("ynykron/ventclose",CHAN_WEAPON,CHANF_OVERLAP); - } - XZW7 YZ 2 A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); - Goto Ready; - DischargeSmoke: - XZWA T 2 - { - invoker.ventfade = .3; - A_StartSound("ynykron/puffing",CHAN_WEAPONEXTRA3,CHANF_LOOP); - } - XZWA UVWXY 2; - DischargingSmoke: - XZWA Z 2 A_JumpIf(invoker.chargestate==CS_IDLE,1); - Wait; - XZWA Z 3 - { - invoker.ventfade = -.1; - A_SoundVolume(CHAN_WEAPONEXTRA3,.8); - A_StartSound("ynykron/puffend",CHAN_WEAPON,CHANF_OVERLAP); - } - XZWB A 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.6); - XZWB B 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.4); - XZWB C 2 A_SoundVolume(CHAN_WEAPONEXTRA3,.2); - XZWB D 2 A_StopSound(CHAN_WEAPONEXTRA3); - Stop; - Charge: - XZW2 A 2; - XZW3 BC 2; - XZW3 D 2 A_StartSound("ynykron/latch",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 EFGHI 2; - XZW2 A 0 - { - if ( gameinfo.gametype&GAME_Strife ) - { - A_StartSound("ynykron/locked",CHAN_WEAPON,CHANF_OVERLAP); - if ( player == players[consoleplayer] ) - Console.Printf(StringTable.Localize("$SWWM_YNYKRONLOCKED")); - } - else - { - invoker.chargestate = CS_CHARGING; - invoker.ventcooldown = Random[Ynykron](6,15); - A_StartSound("ynykron/ready",CHAN_WEAPONEXTRA2,CHANF_LOOP,.01,2.); - } - } - Goto Ready; - Reload: - XZW2 A 2 - { - if ( invoker.chargestate>CS_IDLE ) return ResolveState("Discharge"); - if ( invoker.inverted ) return ResolveState("UnloadInverted"); - A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - return ResolveState(null); - } - UnloadNormal: - XZW2 A 2; - XZW3 STU 2; - XZW3 V 2 A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 WXYZ 2; - XZW4 A 2; - XZW4 B 2 A_StopSound(CHAN_WEAPONEXTRA); - XZW4 CDEFGHIJKLMNO 2; - XZW4 P 0 - { - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - invoker.Ammo1.Amount = max(invoker.Ammo1.Amount-1,0); - invoker.clipcount = 1; - invoker.inverted = false; - // no mag is dropped, depleted crystals are hazardous and should be disposed of properly - if ( invoker.invertreload ) - { - invoker.invertreload = false; - invoker.inverted = true; - return ResolveState("PutInverted"); - } - return ResolveState(null); - } - Goto PutNormal; - UnloadInverted: - XZW5 P 2; - XZW5 QRS 2; - XZW5 T 2 A_StartSound("ynykron/magout",CHAN_WEAPON,CHANF_OVERLAP); - XZW5 UVWXY 2; - XZW5 Z 2 A_StopSound(CHAN_WEAPONEXTRA); - XZW6 ABCDEFGHIJKLM 2; - XZW6 N 0 - { - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - invoker.Ammo1.Amount = max(invoker.Ammo1.Amount-1,0); - invoker.clipcount = 1; - invoker.inverted = false; - // no mag is dropped, depleted crystals are hazardous and should be disposed of properly - if ( invoker.invertreload ) - { - invoker.invertreload = false; - invoker.inverted = true; - return ResolveState("PutInverted"); - } - return ResolveState(null); - } - Goto PutNormal; - Zoom: - XZW2 A 2 - { - invoker.ventcooldown = Random[Ynykron](6,15); - A_StartSound("ynykron/checkout",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerCheckGun(); - } - XZW8 ABCDEFGHIJKLMNOPQRSTUVW 2; - XZW8 X 4; // smoothen more - Goto Ready; - User1: - XZW2 A 2 - { - invoker.ventcooldown = Random[Ynykron](6,15); - A_StartSound("ynykron/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerMelee(); - } - XZW8 YZ 2; - XZW9 AB 2; - XZW9 C 1 A_Parry(9); - XZW9 DE 1; - XZW9 F 1 A_Melee(100,"demolitionist/whitl",1.5); - XZW9 GHIJK 1; - XZW9 LMNO 2; - XZW9 P 2 A_StartSound("ynykron/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW9 QRSTUVWXYZ 2; - Goto Ready; - Deselect: - XZW2 A 2 A_StartSound("ynykron/deselect",CHAN_WEAPON,CHANF_OVERLAP); - XZW2 BCDEFGHIJK 2; - XZW2 L -1 - { - A_StopSound(CHAN_WEAPONEXTRA); - A_StopSound(CHAN_WEAPONEXTRA2); - A_FullLower(); - } - Stop; - Flash: - XZWZ A 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[0] = 255; - l.args[1] = 255; - l.args[2] = 255; - l.args[3] = 500; - l.target = self; - } - Stop; - } -} diff --git a/zscript/swwm_deepdarkimpact.zsc b/zscript/weapons/swwm_deepdarkimpact.zsc similarity index 74% rename from zscript/swwm_deepdarkimpact.zsc rename to zscript/weapons/swwm_deepdarkimpact.zsc index 32f294d8b..791407df9 100644 --- a/zscript/swwm_deepdarkimpact.zsc +++ b/zscript/weapons/swwm_deepdarkimpact.zsc @@ -1,179 +1,6 @@ // Dr. Locke's Mighty Wolf Breath Airgun aka "Deep Impact" Airblaster (from SWWM series) // Slot 1, replaces Fist, Staff, Hexen starting weapons -Class AirBullet : FastProjectile -{ - int tcnt; - Actor lasthit; - - Default - { - Obituary "$O_DEEPIMPACT"; - Radius 2; - Height 4; - DamageFunction 100; - DamageType 'AirRip'; - Speed 400; - PROJECTILE; - +FORCERADIUSDMG; - +NODAMAGETHRUST; - +RIPPER; - } - override void PostBeginPlay() - { - A_StartSound("deepimpact/bullet",CHAN_BODY,CHANF_LOOP,1.,1.5); - } - override int DoSpecialDamage( Actor target, int damage, Name damagetype ) - { - if ( target.bBOSS ) damage = int(damage*.4); - if ( !bAMBUSH ) - { - if ( target == lasthit ) return 0; - lasthit = target; - } - Vector3 dirto = level.Vec3Offset(pos,target.Vec3Offset(0,0,target.Height/2.)); - dirto /= dirto.length(); - SWWMUtility.DoKnockback(target,vel.unit()*.6+dirto*.4,120000); - if ( !target.player ) target.bBLASTED = true; - return damage; - } - override void Effect() - { - let r = Spawn("AirBulletRing",pos); - r.angle = angle; - r.pitch = pitch; - r.roll = FRandom[Impact](0,360); - r.scale *= .3; - r.alpha *= .6; - int numpt = Random[ExploS](2,4); - for ( int i=0; i target.floorz) && target.TestMobjZ() ) mm *= 1.6; - SWWMUtility.DoKnockback(target,dir,mm); - } - SWWMUtility.DoExplosion(self,0,150000,150,ignoreme:target); - A_QuakeEx(6,6,6,20,0,250,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.); - A_StartSound("deepimpact/bullethit",CHAN_VOICE,CHANF_DEFAULT,1.,.3); - A_SprayDecal("ImpactMark"); - Spawn("AirBulletLight",pos); - int numpt = Random[ExploS](10,20); - for ( int i=0; i 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XRG1 ABCDEFGHIJKLMNOPQRSTUVWX 2; - Stop; - } -} - Class DeepTracer : LineTracer { Actor ignoreme; diff --git a/zscript/weapons/swwm_deepdarkimpact_fx.zsc b/zscript/weapons/swwm_deepdarkimpact_fx.zsc new file mode 100644 index 000000000..0f493504f --- /dev/null +++ b/zscript/weapons/swwm_deepdarkimpact_fx.zsc @@ -0,0 +1,174 @@ +// Deep Impact projectiles and effects + +Class AirBullet : FastProjectile +{ + int tcnt; + Actor lasthit; + + Default + { + Obituary "$O_DEEPIMPACT"; + Radius 2; + Height 4; + DamageFunction 100; + DamageType 'AirRip'; + Speed 400; + PROJECTILE; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + +RIPPER; + } + override void PostBeginPlay() + { + A_StartSound("deepimpact/bullet",CHAN_BODY,CHANF_LOOP,1.,1.5); + } + override int DoSpecialDamage( Actor target, int damage, Name damagetype ) + { + if ( target.bBOSS ) damage = int(damage*.4); + if ( !bAMBUSH ) + { + if ( target == lasthit ) return 0; + lasthit = target; + } + Vector3 dirto = level.Vec3Offset(pos,target.Vec3Offset(0,0,target.Height/2.)); + dirto /= dirto.length(); + SWWMUtility.DoKnockback(target,vel.unit()*.6+dirto*.4,120000); + if ( !target.player ) target.bBLASTED = true; + return damage; + } + override void Effect() + { + let r = Spawn("AirBulletRing",pos); + r.angle = angle; + r.pitch = pitch; + r.roll = FRandom[Impact](0,360); + r.scale *= .3; + r.alpha *= .6; + int numpt = Random[ExploS](2,4); + for ( int i=0; i target.floorz) && target.TestMobjZ() ) mm *= 1.6; + SWWMUtility.DoKnockback(target,dir,mm); + } + SWWMUtility.DoExplosion(self,0,150000,150,ignoreme:target); + A_QuakeEx(6,6,6,20,0,250,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.); + A_StartSound("deepimpact/bullethit",CHAN_VOICE,CHANF_DEFAULT,1.,.3); + A_SprayDecal("ImpactMark"); + Spawn("AirBulletLight",pos); + int numpt = Random[ExploS](10,20); + for ( int i=0; i 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XRG1 ABCDEFGHIJKLMNOPQRSTUVWX 2; + Stop; + } +} diff --git a/zscript/swwm_jackhammer.zsc b/zscript/weapons/swwm_jackhammer.zsc similarity index 52% rename from zscript/swwm_jackhammer.zsc rename to zscript/weapons/swwm_jackhammer.zsc index 1fa141ff0..c326ef31f 100644 --- a/zscript/swwm_jackhammer.zsc +++ b/zscript/weapons/swwm_jackhammer.zsc @@ -1,376 +1,6 @@ // Tach-Engine Technologies Microfusion Rotary Hammer aka "Pusher" (planned for unreleased Zanaveth Ultra Suite 2) // Slot 1, replaces Chainsaw, Gauntlets, Timon's Axe -Class PusherImpact : Actor -{ - Default - { - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +NOTELEPORT; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3); - A_StartSound("pusher/hit",CHAN_VOICE); - A_SprayDecal("WallCrack",-20); - int numpt = Random[Pusher](1,3); - Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); - for ( int i=0; i= (BlockingMobj.pos.x+BlockingMobj.radius) ) - HitNormal = (1,0,0); - else if ( (pos.y+radius) <= (BlockingMobj.pos.y-BlockingMobj.radius) ) - HitNormal = (0,-1,0); - else if ( (pos.y-radius) >= (BlockingMobj.pos.y+BlockingMobj.radius) ) - HitNormal = (0,1,0); - else if ( pos.z >= (BlockingMobj.pos.z+BlockingMobj.height) ) - HitNormal = (0,0,1); - else if ( (pos.z+height) <= BlockingMobj.pos.z ) - HitNormal = (0,0,-1); - } - // undo the bounce, we need to hook in our own - angle = oldangle; - pitch = oldpitch; - vel = oldvel; - // try to guess if we hit the sky - if ( HitSkyLine(BlockingLine,lineside) || (BlockingCeiling && (ceilingpic == skyflatnum)) || (BlockingFloor && (floorpic == skyflatnum)) ) - { - special1 = 0; - ExplodeMissile(); - return; - } - // re-do the bounce with our formula - vel = .8*((vel dot HitNormal)*HitNormal*(-1.8+FRandom[Pusher](.0,.8))+vel); - A_StartSound("pusher/bounce",volume:.3); - A_AlertMonsters(swwm_uncapalert?0:300); - if ( vel.length() < 5 ) - { - special1 = 0; - ExplodeMissile(); - } - } - void A_BecomePickup() - { - if ( special1 ) - { - // stuff from direct hit - FLineTraceData d; - LineTrace(angle,40,pitch,0,5,data:d); - 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("BigPusherImpact",d.HitLocation+HitNormal*4); - p.angle = atan2(HitNormal.y,HitNormal.x); - p.pitch = asin(-HitNormal.z); - bool busted = false; - if ( swwm_omnibust ) - { - if ( BusterWall.BustLinetrace(d,100,target,d.HitDir,d.HitLocation.z) ) - busted = true; - } - if ( busted ) pitch = 0.; - else bNOGRAVITY = true; - } - else pitch = 0; - gravity = 1.; - ClearBounce(); - bSPECIAL = true; - A_SetSize(20,16); - A_ChangeLinkFlags(0); - A_StopSound(CHAN_BODY); - } - override int DoSpecialDamage( Actor target, int damage, Name damagetype ) - { - if ( target == lasthit ) return 0; - lasthit = target; - if ( target.bNOBLOOD || target.bDORMANT || target.bINVULNERABLE ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP); - else A_StartSound("pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP); - target.A_QuakeEx(6,6,6,10,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.7); - SWWMUtility.DoKnockback(target,vel.unit(),85000); - return damage; - } - override void Touch( Actor toucher ) - { - // cannot pick up swapweapon unless explicitly pressing use - let pw = GetDefaultByType("PusherWeapon"); - SWWMWeapon sw; - if ( swwm_swapweapons && (sw = pw.HasSwapWeapon(toucher)) ) - { - if ( toucher.CheckLocalView() ) - Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),StringTable.Localize("$T_PUSHER"))); - return; - } - let w = toucher.FindInventory("PusherWeapon"); - if ( toucher.player && w ) - { - let psp = toucher.player.GetPSPrite(PSP_WEAPON); - if ( psp && psp.CurState.InStateSequence(w.FindState("AltMiss")) ) - return; - } - if ( !toucher.player || !toucher.GiveInventory("PusherWeapon",1) ) return; - if ( toucher.CheckLocalView() ) - { - toucher.A_StartSound("misc/w_pkup",CHAN_ITEM,CHANF_NOPAUSE|CHANF_MAYBE_LOCAL); - let w = toucher.FindInventory("PusherWeapon"); - if ( w ) w.PrintPickupMessage(true,w.PickupMessage()); - } - else toucher.A_StartSound("misc/w_pkup",CHAN_ITEM,CHANF_MAYBE_LOCAL); - toucher.A_SelectWeapon("PusherWeapon"); - Spawn("SWWMRedPickupFlash",pos); - Destroy(); - } - override bool Used( Actor user ) - { - // test vertical range - Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); - double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); - if ( abs(diff.z) > rang ) return false; - // if the toucher owns our SwapWeapon, drop it before picking us up - let pw = GetDefaultByType("PusherWeapon"); - SWWMWeapon sw; - if ( swwm_swapweapons && (sw = pw.HasSwapWeapon(user)) ) - { - bool swapto = false; - if ( sw == user.player.ReadyWeapon ) swapto = true; - user.DropInventory(sw); - // don't autoswitch just yet (hacky) - if ( swapto ) - { - user.player.ReadyWeapon = null; - user.player.PendingWeapon = WP_NOCHANGE; - } - } - Touch(user); - return bDestroyed; - } - States - { - Spawn: - XZW1 A 1 A_Reorient(); - Wait; - Bounce: - XZW1 A 0 A_HandleBounce(); - Goto Spawn; - Death: - XZW1 A 0 A_BecomePickup(); - XZW1 A 1 A_JumpIf(pos.z<=floorz,1); - Wait; - XZW1 A -1 A_StartSound("pusher/bounce"); - Stop; - } -} - Class PusherWeapon : SWWMWeapon { double chargelevel, vibe; diff --git a/zscript/weapons/swwm_jackhammer_fx.zsc b/zscript/weapons/swwm_jackhammer_fx.zsc new file mode 100644 index 000000000..96e2a3611 --- /dev/null +++ b/zscript/weapons/swwm_jackhammer_fx.zsc @@ -0,0 +1,371 @@ +// Pusher projectiles and effects + +Class PusherImpact : Actor +{ + Default + { + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +NOTELEPORT; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3); + A_StartSound("pusher/hit",CHAN_VOICE); + A_SprayDecal("WallCrack",-20); + int numpt = Random[Pusher](1,3); + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + for ( int i=0; i= (BlockingMobj.pos.x+BlockingMobj.radius) ) + HitNormal = (1,0,0); + else if ( (pos.y+radius) <= (BlockingMobj.pos.y-BlockingMobj.radius) ) + HitNormal = (0,-1,0); + else if ( (pos.y-radius) >= (BlockingMobj.pos.y+BlockingMobj.radius) ) + HitNormal = (0,1,0); + else if ( pos.z >= (BlockingMobj.pos.z+BlockingMobj.height) ) + HitNormal = (0,0,1); + else if ( (pos.z+height) <= BlockingMobj.pos.z ) + HitNormal = (0,0,-1); + } + // undo the bounce, we need to hook in our own + angle = oldangle; + pitch = oldpitch; + vel = oldvel; + // try to guess if we hit the sky + if ( HitSkyLine(BlockingLine,lineside) || (BlockingCeiling && (ceilingpic == skyflatnum)) || (BlockingFloor && (floorpic == skyflatnum)) ) + { + special1 = 0; + ExplodeMissile(); + return; + } + // re-do the bounce with our formula + vel = .8*((vel dot HitNormal)*HitNormal*(-1.8+FRandom[Pusher](.0,.8))+vel); + A_StartSound("pusher/bounce",volume:.3); + A_AlertMonsters(swwm_uncapalert?0:300); + if ( vel.length() < 5 ) + { + special1 = 0; + ExplodeMissile(); + } + } + void A_BecomePickup() + { + if ( special1 ) + { + // stuff from direct hit + FLineTraceData d; + LineTrace(angle,40,pitch,0,5,data:d); + 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("BigPusherImpact",d.HitLocation+HitNormal*4); + p.angle = atan2(HitNormal.y,HitNormal.x); + p.pitch = asin(-HitNormal.z); + bool busted = false; + if ( swwm_omnibust ) + { + if ( BusterWall.BustLinetrace(d,100,target,d.HitDir,d.HitLocation.z) ) + busted = true; + } + if ( busted ) pitch = 0.; + else bNOGRAVITY = true; + } + else pitch = 0; + gravity = 1.; + ClearBounce(); + bSPECIAL = true; + A_SetSize(20,16); + A_ChangeLinkFlags(0); + A_StopSound(CHAN_BODY); + } + override int DoSpecialDamage( Actor target, int damage, Name damagetype ) + { + if ( target == lasthit ) return 0; + lasthit = target; + if ( target.bNOBLOOD || target.bDORMANT || target.bINVULNERABLE ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP); + else A_StartSound("pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP); + target.A_QuakeEx(6,6,6,10,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.7); + SWWMUtility.DoKnockback(target,vel.unit(),85000); + return damage; + } + override void Touch( Actor toucher ) + { + // cannot pick up swapweapon unless explicitly pressing use + let pw = GetDefaultByType("PusherWeapon"); + SWWMWeapon sw; + if ( swwm_swapweapons && (sw = pw.HasSwapWeapon(toucher)) ) + { + if ( toucher.CheckLocalView() ) + Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),StringTable.Localize("$T_PUSHER"))); + return; + } + let w = toucher.FindInventory("PusherWeapon"); + if ( toucher.player && w ) + { + let psp = toucher.player.GetPSPrite(PSP_WEAPON); + if ( psp && psp.CurState.InStateSequence(w.FindState("AltMiss")) ) + return; + } + if ( !toucher.player || !toucher.GiveInventory("PusherWeapon",1) ) return; + if ( toucher.CheckLocalView() ) + { + toucher.A_StartSound("misc/w_pkup",CHAN_ITEM,CHANF_NOPAUSE|CHANF_MAYBE_LOCAL); + let w = toucher.FindInventory("PusherWeapon"); + if ( w ) w.PrintPickupMessage(true,w.PickupMessage()); + } + else toucher.A_StartSound("misc/w_pkup",CHAN_ITEM,CHANF_MAYBE_LOCAL); + toucher.A_SelectWeapon("PusherWeapon"); + Spawn("SWWMRedPickupFlash",pos); + Destroy(); + } + override bool Used( Actor user ) + { + // test vertical range + Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); + double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); + if ( abs(diff.z) > rang ) return false; + // if the toucher owns our SwapWeapon, drop it before picking us up + let pw = GetDefaultByType("PusherWeapon"); + SWWMWeapon sw; + if ( swwm_swapweapons && (sw = pw.HasSwapWeapon(user)) ) + { + bool swapto = false; + if ( sw == user.player.ReadyWeapon ) swapto = true; + user.DropInventory(sw); + // don't autoswitch just yet (hacky) + if ( swapto ) + { + user.player.ReadyWeapon = null; + user.player.PendingWeapon = WP_NOCHANGE; + } + } + Touch(user); + return bDestroyed; + } + States + { + Spawn: + XZW1 A 1 A_Reorient(); + Wait; + Bounce: + XZW1 A 0 A_HandleBounce(); + Goto Spawn; + Death: + XZW1 A 0 A_BecomePickup(); + XZW1 A 1 A_JumpIf(pos.z<=floorz,1); + Wait; + XZW1 A -1 A_StartSound("pusher/bounce"); + Stop; + } +} diff --git a/zscript/weapons/swwm_shot.zsc b/zscript/weapons/swwm_shot.zsc new file mode 100644 index 000000000..38b0757d3 --- /dev/null +++ b/zscript/weapons/swwm_shot.zsc @@ -0,0 +1,1184 @@ +// Blackmann "Rhino Stopper" Spreadgun (from Instant Action 3, also planned for Zanaveth Ultra Suite 2) +// Slot 3, replaces Shotgun, Ethereal Crossbow, Serpent Staff + +Class SpreadgunTracer : LineTracer +{ + Actor ignoreme; + Array hitlist; + Array shootthroughlist; + Array waterhitlist; + + override ETraceStatus TraceCallback() + { + // liquid splashes + if ( Results.CrossedWater ) + { + let hl = new("WaterHit"); + hl.sect = Results.CrossedWater; + hl.hitpos = Results.CrossedWaterPos; + WaterHitList.Push(hl); + } + else if ( Results.Crossed3DWater ) + { + let hl = new("WaterHit"); + hl.sect = Results.Crossed3DWater; + hl.hitpos = Results.Crossed3DWaterPos; + WaterHitList.Push(hl); + } + if ( Results.HitType == TRACE_HitActor ) + { + if ( Results.HitActor == ignoreme ) return TRACE_Skip; + if ( Results.HitActor.bSHOOTABLE ) + { + int amt = SWWMDamageAccumulator.GetAmount(Results.HitActor); + // getgibhealth isn't clearscope, fuck + int gibhealth = -int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor); + if ( Results.HitActor.GibHealth != int.min ) gibhealth = -abs(Results.HitActor.GibHealth); + // if gibbed, go through without dealing more damage + if ( Results.HitActor.health-amt <= gibhealth ) return TRACE_Skip; + let ent = new("HitListEntry"); + ent.hitactor = Results.HitActor; + ent.hitlocation = Results.HitPos; + ent.x = Results.HitVector; + hitlist.Push(ent); + // go right on through if dead + if ( Results.HitActor.health-amt <= 0 ) return TRACE_Skip; + // stap + return TRACE_Stop; + } + return TRACE_Skip; + } + else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) + { + if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) + return TRACE_Stop; + ShootThroughList.Push(Results.HitLine); + return TRACE_Skip; + } + return TRACE_Stop; + } +} + +Class SpreadSlugTracer : SpreadgunTracer +{ + double penetration; // please don't laugh + + override ETraceStatus TraceCallback() + { + // liquid splashes + if ( Results.CrossedWater ) + { + let hl = new("WaterHit"); + hl.sect = Results.CrossedWater; + hl.hitpos = Results.CrossedWaterPos; + WaterHitList.Push(hl); + } + else if ( Results.Crossed3DWater ) + { + let hl = new("WaterHit"); + hl.sect = Results.Crossed3DWater; + hl.hitpos = Results.Crossed3DWaterPos; + WaterHitList.Push(hl); + } + if ( Results.HitType == TRACE_HitActor ) + { + if ( Results.HitActor == ignoreme ) return TRACE_Skip; + if ( Results.HitActor.bSHOOTABLE ) + { + let ent = new("HitListEntry"); + ent.hitactor = Results.HitActor; + ent.hitlocation = Results.HitPos; + ent.x = Results.HitVector; + if ( (Results.HitActor.Health >= int(penetration)) || Results.HitActor.bNODAMAGE ) + { + ent.hitdamage = int(penetration); + penetration = 0; + } + else + { + ent.hitdamage = min(Results.HitActor.health+int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor),int(penetration)); + penetration = max(0,penetration-ent.hitdamage); + } + hitlist.Push(ent); + if ( penetration <= 0 ) return TRACE_Stop; + return TRACE_Skip; + } + return TRACE_Skip; + } + else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) + { + if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) + return TRACE_Stop; + ShootThroughList.Push(Results.HitLine); + return TRACE_Skip; + } + return TRACE_Stop; + } +} + +Class Spreadgun : SWWMWeapon +{ + bool fired; // shell was used + bool chambered; // a shell is actually loaded + bool emptyup; // next reload will keep the chamber empty + Class loadammo, nextammo; // currently loaded shell, next shell to load + bool initialized; + + transient ui TextureID WeaponBox, AmmoIcon[7], LoadedIcon[7]; + transient ui Font TewiFont; + + override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) + { + if ( loadammo is 'RedShell' ) return StringTable.Localize("$O_SPREADGUN_RED"); + if ( loadammo is 'GreenShell' ) return StringTable.Localize("$O_SPREADGUN_GREEN"); + if ( loadammo is 'WhiteShell' ) return StringTable.Localize("$O_SPREADGUN_WHITE"); + if ( loadammo is 'BlueShell' ) return StringTable.Localize("$O_SPREADGUN_BLUE"); + if ( loadammo is 'BlackShell' ) return StringTable.Localize("$O_SPREADGUN_BLACK"); + if ( loadammo is 'PurpleShell' ) return StringTable.Localize("$O_SPREADGUN_PURPLE"); + if ( loadammo is 'GoldShell' ) return StringTable.Localize("$O_SPREADGUN_GOLD"); + return Super.GetObituary(victim,inflictor,mod,playerattack); + } + + override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + if ( !WeaponBox ) + { + WeaponBox = TexMan.CheckForTexture("graphics/HUD/SpreadgunDisplay.png",TexMan.Type_Any); + AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/RedShell.png",TexMan.Type_Any); + AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/GreenShell.png",TexMan.Type_Any); + AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/WhiteShell.png",TexMan.Type_Any); + AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/BlueShell.png",TexMan.Type_Any); + AmmoIcon[4] = TexMan.CheckForTexture("graphics/HUD/BlackShell.png",TexMan.Type_Any); + AmmoIcon[5] = TexMan.CheckForTexture("graphics/HUD/PurpleShell.png",TexMan.Type_Any); + AmmoIcon[6] = TexMan.CheckForTexture("graphics/HUD/GoldShell.png",TexMan.Type_Any); + LoadedIcon[0] = TexMan.CheckForTexture("graphics/HUD/LoadedRedShell.png",TexMan.Type_Any); + LoadedIcon[1] = TexMan.CheckForTexture("graphics/HUD/LoadedGreenShell.png",TexMan.Type_Any); + LoadedIcon[2] = TexMan.CheckForTexture("graphics/HUD/LoadedWhiteShell.png",TexMan.Type_Any); + LoadedIcon[3] = TexMan.CheckForTexture("graphics/HUD/LoadedBlueShell.png",TexMan.Type_Any); + LoadedIcon[4] = TexMan.CheckForTexture("graphics/HUD/LoadedBlackShell.png",TexMan.Type_Any); + LoadedIcon[5] = TexMan.CheckForTexture("graphics/HUD/LoadedPurpleShell.png",TexMan.Type_Any); + LoadedIcon[6] = TexMan.CheckForTexture("graphics/HUD/LoadedGoldShell.png",TexMan.Type_Any); + } + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + Screen.DrawTexture(WeaponBox,false,bx-54,by-43,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int ox = 6; + int oy = 11; + for ( int i=0; i<7; i++ ) + { + Screen.DrawTexture(AmmoIcon[i],false,bx-ox,by-oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); + String astr = String.Format("%3d",Owner.CountInv(types[i])); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-ox-(TewiFont.StringWidth(astr)+1),by-oy-2,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); + oy += 10; + if ( i == 3 ) + { + oy = 21; + ox = 33; + } + } + if ( !chambered ) return; + for ( int i=0; i<7; i++ ) + { + if ( loadammo != types[i] ) continue; + Screen.DrawTexture(LoadedIcon[i],false,bx-48,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fired?Color(128,0,0,0):Color(0,0,0,0)); + break; + } + } + + override bool ReportHUDAmmo() + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + for ( int i=0; i<7; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; + return (!fired && chambered); + } + + override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + if ( (firemode == PrimaryFire) || (firemode == AltFire) ) + { + if ( !fired && chambered ) return true; + for ( int i=0; i<7; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; + return false; + } + return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); + } + + override bool UsesAmmo( Class kind ) + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + for ( int i=0; i<7; i++ ) if ( kind is types[i] ) return true; + return false; + } + + action void A_SelectUnloadState() + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + static const statelabel primedstates[] = {"UnloadRed", "UnloadGreen", "UnloadWhite", "UnloadBlue", "UnloadBlack", "UnloadPurple", "UnloadGold"}; + static const statelabel firedstates[] = {"UnloadRedFired", "UnloadGreenFired", "UnloadWhiteFired", "UnloadBlueFired", "UnloadBlackFired", "UnloadPurpleFired", "UnloadGoldFired"}; + int amidx = 0; + for ( int i=0; i<7; i++ ) + { + if ( invoker.loadammo != types[i] ) continue; + amidx = i; + break; + } + if ( !invoker.chambered ) player.SetPSprite(PSP_WEAPON,invoker.FindState("UnloadEmpty")); // no "fired" one for this, as it can never happen + else if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState(primedstates[amidx])); + else player.SetPSprite(PSP_WEAPON,invoker.FindState(firedstates[amidx])); + if ( invoker.chambered ) A_Overlay(-9999,"UnloadDummy"); + else A_Overlay(-9999,"UnloadDummyEmpty"); + A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP); + } + + action void A_SelectLoadState() + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + static const statelabel primedstates[] = {"LoadRed", "LoadGreen", "LoadWhite", "LoadBlue", "LoadBlack", "LoadPurple", "LoadGold"}; + static const statelabel firedstates[] = {"LoadRedFired", "LoadGreenFired", "LoadWhiteFired", "LoadBlueFired", "LoadBlackFired", "LoadPurpleFired", "LoadGoldFired"}; + int amidx = 0; + for ( int i=0; i<7; i++ ) + { + if ( invoker.nextammo != types[i] ) continue; + amidx = i; + break; + } + if ( invoker.emptyup ) + { + if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState("LoadEmpty")); + else player.SetPSprite(PSP_WEAPON,invoker.FindState("LoadEmptyFired")); + } + else if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState(primedstates[amidx])); + else player.SetPSprite(PSP_WEAPON,invoker.FindState(firedstates[amidx])); + if ( !invoker.emptyup && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + { + let amo = FindInventory(invoker.nextammo); + if ( amo && (amo.Amount > 0) ) amo.Amount--; + } + if ( invoker.emptyup ) A_Overlay(-9999,"LoadDummyEmpty"); + else A_Overlay(-9999,"LoadDummy"); + invoker.emptyup = false; + } + + action void A_DropShell() + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + static const Class casetypes[] = {"RedShellCasing","GreenShellCasing","WhiteShellCasing","BlueShellCasing","BlackShellCasing","PurpleShellCasing","GoldShellCasing"}; + if ( !invoker.fired ) + { + for ( int i=0; i<7; i++ ) + { + if ( invoker.loadammo != types[i] ) continue; + let amo = FindInventory(types[i]); + if ( !amo ) + { + amo = Inventory(Spawn(types[i])); + amo.AttachToOwner(self); + amo.Amount = 0; + } + if ( (amo.Amount >= amo.MaxAmount) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + amo.CreateTossable(1); + amo.Amount++; + break; + } + } + else + { + for ( int i=0; i<7; i++ ) + { + if ( invoker.loadammo != types[i] ) continue; + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-10*z); + let c = Spawn(casetypes[i],origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2,3)); + c.vel += vel*.5; + break; + } + } + } + + action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class impact = "SpreadImpact", int bc = 1, bool large = false ) + { + if ( swwm_omnibust ) + { + // Wall busting + int bustdmg = dmg; + if ( t is 'SpreadSlugTracer' ) bustdmg = int(SpreadSlugTracer(t).penetration); + BusterWall.Bust(t.Results,bustdmg,self,t.Results.HitVector,t.Results.HitPos.z); + } + for ( int i=0; i types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + static const statelabel flashes[] = {"FlashRed","FlashGreen","FlashWhite","FlashBlue","FlashBlack","FlashPurple","FlashGold"}; + static const String sounds[] = {"spreadgun/redfire","spreadgun/greenfire","spreadgun/whitefire","spreadgun/bluefire","spreadgun/blackfire","spreadgun/purplefire","spreadgun/goldfire"}; + static const int louds[] = {800,1000,1100,1200,1400,600,2500}; + static const int quakes[] = {3,4,2,4,3,1,6}; + static const Color cols[] = {Color(40,255,192,64),Color(36,255,192,80),Color(64,255,160,32),Color(48,32,176,255),Color(72,255,128,16),Color(24,255,224,96),Color(96,255,224,16)}; + for ( int i=0; i<7; i++ ) + { + if ( invoker.loadammo != types[i] ) continue; + A_SWWMFlash(flashes[i]); + A_StartSound(sounds[i],CHAN_WEAPON,CHANF_OVERLAP,attenuation:.6); + A_AlertMonsters(swwm_uncapalert?0:louds[i]); + A_QuakeEx(quakes[i],quakes[i],quakes[i],9,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.2*quakes[i]); + A_ZoomFactor(1.-quakes[i]*.04,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_PlayerFire(); + SWWMHandler.DoFlash(self,cols[i],5); + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z); + Vector3 x2, y2, z2; + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + double a, s; + Vector3 dir; + SpreadgunTracer st; + SpreadSlugTracer sst; + switch ( i ) + { + case 1: + sst = new("SpreadSlugTracer"); + sst.ignoreme = self; + sst.penetration = 150.; + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.01); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + sst.hitlist.Clear(); + sst.shootthroughlist.Clear(); + sst.waterhitlist.Clear(); + sst.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky); + ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1,true); + for ( int i=0; i<6; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .8; + s.alpha *= .3; + s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.); + } + for ( int i=0; i<10; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .2; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + SWWMUtility.DoKnockback(self,-x,25000.); + break; + case 2: + for ( int j=0; j<3; j++ ) + { + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.24); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("DragonBreathArm",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + } + for ( int i=0; i<15; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= .9; + s.alpha *= .3; + s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + for ( int i=0; i<12; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); + } + SWWMUtility.DoKnockback(self,-x,13000.); + break; + case 3: + for ( int j=0; j<8; j++ ) + { + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.8); + let b = Spawn("SaltBeam",level.Vec3Offset(origin,y*cos(a)*s+z*sin(a)*s)); + b.target = self; + b.angle = atan2(x2.y,x2.x); + b.pitch = asin(-x2.z); + } + for ( int i=0; i<16; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= .9; + s.SetShade(Color(1,3,4)*Random[Spreadgun](32,63)); + s.A_SetRenderStyle(.3,STYLE_AddShaded); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + for ( int i=0; i<20; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); + } + SWWMUtility.DoKnockback(self,-x,23000.); + break; + case 4: + for ( int j=0; j<50; j++ ) + { + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.2); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("CorrosiveFlechette",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed*FRandom[Spreadgun](1.,1.5); + } + for ( int i=0; i<10; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .7; + s.alpha *= .3; + s.SetShade(Color(1,1,1)*Random[Spreadgun](48,128)); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,12.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + for ( int i=0; i<20; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,12.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); + } + SWWMUtility.DoKnockback(self,-x,15000.); + break; + case 5: + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.03); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let b = Spawn("TheBall",origin); + b.target = self; + b.angle = atan2(dir.y,dir.x); + b.pitch = asin(-dir.z); + b.vel = dir*b.speed; + for ( int i=0; i<8; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .6; + s.alpha *= .25; + s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.); + } + for ( int i=0; i<8; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .2; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + SWWMUtility.DoKnockback(self,-x,9500.); + break; + case 6: + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.01); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + FLineTraceData d; + LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d); + SWWMBulletTrail.DoTrail(self,origin,dir,10000,2,true); + if ( d.HitType != TRACE_HitNone ) + { + 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("SlugImpact",d.HitLocation+hitnormal); + 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("GoldenImpact",d.HitLocation+hitnormal*4.); + b.angle = atan2(hitnormal.y,hitnormal.x); + b.pitch = asin(-hitnormal.z); + b.target = self; + } + for ( int i=0; i<6; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .8; + s.alpha *= .3; + s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.); + } + for ( int i=0; i<10; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .2; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + for ( int i=0; i<50; i++ ) + { + let s = Spawn("FancyConfetti",origin); + s.bAMBUSH = true; + s.vel += vel*.5+x*FRandom[Spreadgun](1.,20.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); + } + SWWMUtility.DoKnockback(self,-x,30000.); + break; + default: + st = new("SpreadgunTracer"); + st.ignoreme = self; + for ( int j=0; j<20; j++ ) + { + a = FRandom[Spreadgun](0,360); + s = FRandom[Spreadgun](0,.22); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + st.hitlist.Clear(); + st.shootthroughlist.Clear(); + st.waterhitlist.Clear(); + st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky); + ProcessTraceHit(st,origin,dir,6,7000,bc:5); + } + for ( int i=0; i<16; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.special1 = 1; + s.scale *= .9; + s.alpha *= .3; + s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); + s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); + } + for ( int i=0; i<20; i++ ) + { + let s = Spawn("SWWMSpark",origin); + s.scale *= .3; + s.alpha *= .4; + s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); + } + SWWMUtility.DoKnockback(self,-x,20000.); + break; + } + break; + } + A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP); + invoker.fired = true; + } + + action void A_LoadShell() + { + A_StartSound("spreadgun/shellin",CHAN_WEAPON,CHANF_OVERLAP); + invoker.chambered = true; + invoker.loadammo = invoker.nextammo; + } + + action void A_Prime() + { + if ( invoker.fired ) + { + A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP); + invoker.fired = false; + } + } + + override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) + { + bool good = Super.PickupForAmmoSWWM(ownedWeapon); + let Owner = ownedWeapon.Owner; + if ( (AmmoGive1 == 0) && loadammo && !fired && chambered ) + { + let cur = Owner.FindInventory(loadammo); + if ( !cur ) + { + cur = Inventory(Spawn(loadammo)); + cur.Amount = 0; + cur.AttachToOwner(Owner); + } + // give the loaded shell (or drop) + if ( cur.Amount >= cur.MaxAmount ) cur.CreateTossable(1); + cur.Amount++; + good = true; + } + return good; + } + + override void AttachToOwner( Actor other ) + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + Super.AttachToOwner(other); + if ( !initialized ) + { + initialized = true; + if ( !loadammo ) loadammo = "RedShell"; + fired = false; + chambered = true; + } + for ( int i=0; i<7; i++ ) + { + Ammo a = Ammo(other.FindInventory(types[i])); + if ( !a ) continue; + nextammo = types[i]; + return; + } + nextammo = AmmoType1; + } + + action void A_SwitchAmmoType( bool rev = false ) + { + static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; + int cur = 0, next = 0; + for ( int i=0; i<7; i++ ) + { + if ( invoker.nextammo != types[i] ) continue; + cur = i; + break; + } + int ridx = -1; + if ( rev ) + { + // check backwards from what we currently had + for ( int i=cur; i>=0; i-- ) + { + if ( CountInv(types[i]) <= 0 ) continue; + ridx = i; + break; + } + if ( ridx == -1 ) + { + // check forwards instead, but avoid golden shells + for ( int i=0; i<6; i++ ) + { + if ( CountInv(types[i]) <= 0 ) continue; + ridx = i; + break; + } + } + if ( ridx != -1 ) next = ridx; + } + else + { + for ( int i=0; i<7; i++ ) + { + ridx = (i+cur+1)%7; + if ( CountInv(types[ridx]) <= 0 ) continue; + next = ridx; + break; + } + } + if ( invoker.nextammo != types[next] ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL); + invoker.nextammo = types[next]; + A_WeaponReady(WRF_NOFIRE); + } + + action void A_AltHold() + { + A_WeaponReady(WRF_NOFIRE); + // tap fire to unload round + if ( invoker.chambered && (player.cmd.buttons&BT_ATTACK) ) + { + invoker.emptyup = true; + player.SetPSPrite(PSP_WEAPON,invoker.FindState("Reload")); + return; + } + if ( player.cmd.buttons&BT_ALTATTACK ) return; + A_SwitchAmmoType(); + if ( !invoker.fired ) player.SetPSPrite(PSP_WEAPON,invoker.FindState("Ready")); + else player.SetPSPrite(PSP_WEAPON,invoker.FindState("ReadyFired")); + } + + override void ModifyDropAmount( int dropamount ) + { + Super.ModifyDropAmount(dropamount); + // toss some ammo while we're at it + if ( Random[Spreadgun](0,1) ) + A_DropItem(Random[Spreadgun](0,2)?"RedShell":"GreenShell",Random[Spreadgun](1,2)); + } + + Default + { + Tag "$T_SPREADGUN"; + Inventory.PickupMessage "$I_SPREADGUN"; + Obituary "$O_SPREADGUN"; + Inventory.Icon "graphics/HUD/Icons/W_Spreadgun.png"; + Weapon.UpSound "spreadgun/select"; + Weapon.SlotNumber 3; + Weapon.SelectionOrder 500; + Weapon.AmmoType1 "RedShell"; + Weapon.AmmoGive1 1; + SWWMWeapon.DropAmmoType "Shell"; + Stamina 15000; + +SWWMWEAPON.NOFIRSTGIVE; + Radius 10; + Height 24; + } + + States + { + Spawn: + XZW1 A -1; + Stop; + Deselect: + XZW2 A 1 + { + A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.fired,"DeselectFired"); + } + XZW2 BCDEFGHI 1; + XZW2 I -1 A_FullLower(); + Stop; + DeselectFired: + XZW2 Z 1; + XZW3 ABCDEFGH 1; + XZW3 H -1 A_FullLower(); + Stop; + Select: + XZW2 I 1 + { + A_FullRaise(); + return A_JumpIf(invoker.fired,"SelectFired"); + } + XZW2 JKLMNOPQ 1; + Goto Ready; + SelectFired: + XZW3 HIJKLMNOP 1; + Goto ReadyFired; + Ready: + XZW2 A 1 + { + if ( CountInv(invoker.nextammo) <= 0 ) A_SwitchAmmoType(true); + int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; + if ( invoker.nextammo && (CountInv(invoker.nextammo) > 0) && ((invoker.loadammo != invoker.nextammo) || !invoker.chambered) ) + flg |= WRF_ALLOWRELOAD; + A_WeaponReady(flg); + return ResolveState(null); + } + Wait; + ReadyFired: + XZW2 Z 1 + { + if ( CountInv(invoker.nextammo) <= 0 ) A_SwitchAmmoType(true); + int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; + if ( invoker.nextammo && (CountInv(invoker.nextammo) > 0) ) + flg |= WRF_ALLOWRELOAD; + else flg |= WRF_NOPRIMARY; + A_WeaponReady(flg); + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) ) + invoker.CheckAmmo(EitherFire,true); + return ResolveState(null); + } + Wait; + Fire: + #### # 1 + { + if ( invoker.fired || !invoker.chambered ) return ResolveState("Reload"); + A_FireShell(); + return ResolveState(null); + } + XZW2 RSTU 1; + XZW2 VWXY 2; + Goto ReadyFired; + AltFire: + #### # 1 A_AltHold(); + Wait; + Reload: + #### # 1 + { + A_PlayerReload(); + A_SelectUnloadState(); + } + Stop; + UnloadDummy: // overlay with shared functions for all unload anims + TNT1 A 11; + TNT1 A 14 + { + invoker.chambered = false; + A_StartSound("spreadgun/open",CHAN_WEAPON,CHANF_OVERLAP); + } + TNT1 A 1 A_DropShell(); + Stop; + UnloadDummyEmpty: + TNT1 A 11; + TNT1 A 14 A_StartSound("spreadgun/open",CHAN_WEAPON,CHANF_OVERLAP); + Stop; + UnloadRedFired: + XZW2 Z 2; + XZW3 QRST 2; + XZW3 UVWXYZ 1; + XZW4 ABCDEFGH 1; + XZW8 M 1; + Goto Reload2; + UnloadGreenFired: + XZW2 Z 2; + XZW4 IJKL 2; + XZW4 MNOPQRSTUVWXYZ 1; + XZW9 T 1; + Goto Reload2; + UnloadWhiteFired: + XZW2 Z 2; + XZW5 ABCD 2; + XZW5 EFGHIJKLMNOPQR 1; + XZWB A 1; + Goto Reload2; + UnloadBlueFired: + XZW2 Z 2; + XZW5 STUV 2; + XZW5 WXYZ 1; + XZW6 ABCDEFGHIJ 1; + XZWC H 1; + Goto Reload2; + UnloadBlackFired: + XZW2 Z 2; + XZW6 KLMN 2; + XZW6 OPQRSTUVWXYZ 1; + XZW7 AB 1; + XZWD O 1; + Goto Reload2; + UnloadPurpleFired: + XZW2 Z 2; + XZW7 CDEF 2; + XZW7 GHIJKLMNOPQRST 1; + XZWE V 1; + Goto Reload2; + UnloadGoldFired: + XZW2 Z 2; + XZW7 UVWX 2; + XZW7 YZ 1; + XZW8 ABCDEFGHIJKL 1; + XZWG C 1; + Goto Reload2; + UnloadRed: + XZW2 A 2; + XZWK JKLM 2; + XZWK NOPQRSTUVWXYZ 1; + XZWL A 1; + XZWP F 1; + Goto Reload2; + UnloadGreen: + XZW2 A 2; + XZWL BCDE 2; + XZWL FGHIJKLMNOPQRS 1; + XZWQ M 1; + Goto Reload2; + UnloadWhite: + XZW2 A 2; + XZWL TUVW 2; + XZWL XYZ 1; + XZWM ABCDEFGHIJK 1; + XZWR T 1; + Goto Reload2; + UnloadBlue: + XZW2 A 2; + XZWM LMNO 2; + XZWM PQRSTUVWXYZ 1; + XZWN ABC 1; + XZWT A 1; + Goto Reload2; + UnloadBlack: + XZW2 A 2; + XZWN DEFG 2; + XZWN HIJKLMNOPQRSTU 1; + XZWU H 1; + Goto Reload2; + UnloadPurple: + XZW2 A 2; + XZWN VWXY 2; + XZWN Z 1; + XZWO ABCDEFGHIJKLM 1; + XZWV O 1; + Goto Reload2; + UnloadGold: + XZW2 A 2; + XZWO NOPQ 2; + XZWO RSTUVWXYZ 1; + XZWP ABCDE 1; + XZWW V 1; + Goto Reload2; + UnloadEmpty: + XZW2 A 2; + XZWY CDEF 2; + XZWY GHIJKLMNOPQRSTU 1; + Goto Reload2; + Reload2: + #### # 1 A_SelectLoadState(); + Stop; + LoadDummy: // overlay with shared functions for all load anims + TNT1 A 9; + TNT1 A 12 A_LoadShell(); + TNT1 A 2 A_StartSound("spreadgun/close",CHAN_WEAPON,CHANF_OVERLAP); + TNT1 A 2 A_Prime(); + TNT1 A 1 { invoker.PlayUpSound(self); } + Stop; + LoadDummyEmpty: + TNT1 A 9; + TNT1 A 2 A_StartSound("spreadgun/close",CHAN_WEAPON,CHANF_OVERLAP); + TNT1 A 2 A_Prime(); + TNT1 A 1 { invoker.PlayUpSound(self); } + Stop; + LoadRedFired: + XZW8 MNOPQRSTUVWXYZ 1; + XZW9 ABCDEFGHIJKLMNOPQRS 1; + Goto Ready; + LoadGreenFired: + XZW9 TUVWXYZ 1; + XZWA ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; + Goto Ready; + LoadWhiteFired: + XZWB ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; + XZWC ABCDEFG 1; + Goto Ready; + LoadBlueFired: + XZWC HIJKLMNOPQRSTUVWXYZ 1; + XZWD ABCDEFGHIJKLMN 1; + Goto Ready; + LoadBlackFired: + XZWD OPQRSTUVWXYZ 1; + XZWE ABCDEFGHIJKLMNOPQRSTU 1; + Goto Ready; + LoadPurpleFired: + XZWE VWXYZ 1; + XZWF ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; + XZWG AB 1; + Goto Ready; + LoadGoldFired: + XZWG CDEFGHIJKLMNOPQRSTUVWXYZ 1; + XZWH ABCDEFGHI 1; + Goto Ready; + LoadRed: + XZWP FGHIJKLMNOPQRSTUVWXYZ 1; + XZWQ ABCDEFGHIJKL 1; + Goto Ready; + LoadGreen: + XZWQ MNOPQRSTUVWXYZ 1; + XZWR ABCDEFGHIJKLMNOPQRS 1; + Goto Ready; + LoadWhite: + XZWR TUVWXYZ 1; + XZWS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; + Goto Ready; + LoadBlue: + XZWT ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; + XZWU ABCDEFG 1; + Goto Ready; + LoadBlack: + XZWU HIJKLMNOPQRSTUVWXYZ 1; + XZWV ABCDEFGHIJKLMN 1; + Goto Ready; + LoadPurple: + XZWV OPQRSTUVWXYZ 1; + XZWW ABCDEFGHIJKLMNOPQRSTU 1; + Goto Ready; + LoadGold: + XZWW VWXYZ 1; + XZWX ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; + XZWY AB 1; + Goto Ready; + LoadEmpty: + XZWY UVWXYZ 1; + XZWZ ABCDEFGHIJKLMNO 1; + Goto Ready; + LoadEmptyFired: + XZWZ PQRSTUVWXYZ 1; + XZW0 HIJKLMNOPQ 1; + Goto Ready; + Zoom: + XZW2 A 1 + { + A_StartSound("spreadgun/checkgun",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerCheckGun(); + return A_JumpIf(invoker.fired,"ZoomFired"); + } + XZWH JKLMNOPQRST 1; + XZWH UVWXYZ 2; + XZWI ABC 2; + XZWI DEFGHI 1; + Goto Ready; + ZoomFired: + XZW2 Z 1; + XZWI WXYZ 1; + XZWJ ABCDEFG 1; + XZWJ HIJKLMNOP 2; + XZWJ QRSTUV 1; + Goto ReadyFired; + DummyMelee: + TNT1 A 3 + { + A_Parry(9); + A_PlayerMelee(true); + } + TNT1 A 1 A_Melee(); + Stop; + User1: + XZW2 A 2 + { + A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.fired,"User1Fired"); + } + XZWI JK 2; + User1Hold: + XZWI L 1 + { + A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); + A_Overlay(-9999,"DummyMelee"); + } + XZWI MNOP 2; + XZWI QR 3; + XZWI S 0 A_JumpIf(player.cmd.buttons&BT_USER1,"User1Hold"); + XZWI S 0 { invoker.PlayUpSound(self); } + XZWI STUV 2; + Goto Ready; + User1Fired: + XZW2 Z 2; + XZWJ WX 2; + User1FiredHold: + XZWJ Y 1 + { + A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); + A_Overlay(-9999,"DummyMelee"); + } + XZWJ Z 2; + XZWK ABC 2; + XZWK DE 3; + XZWK F 0 A_JumpIf(player.cmd.buttons&BT_USER1,"User1FiredHold"); + XZWK F 0 { invoker.PlayUpSound(self); } + XZWK FGHI 2; + Goto ReadyFired; + FlashRed: + XZW0 A 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[3] = 120; + l.target = self; + } + Stop; + FlashGreen: + XZW0 B 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[3] = 90; + l.target = self; + } + Stop; + FlashWhite: + XZW0 C 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[1] = 176; + l.args[2] = 32; + l.args[3] = 160; + l.target = self; + } + Stop; + FlashBlue: + XZW0 D 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[0] = 96; + l.args[1] = 224; + l.args[2] = 255; + l.args[3] = 160; + l.target = self; + } + Stop; + FlashBlack: + XZW0 E 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[3] = 60; + l.target = self; + } + Stop; + FlashPurple: + XZW0 F 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[3] = 60; + l.target = self; + } + Stop; + FlashGold: + XZW0 G 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[3] = 300; + l.target = self; + } + Stop; + } +} diff --git a/zscript/swwm_shot.zsc b/zscript/weapons/swwm_shot_fx.zsc similarity index 56% rename from zscript/swwm_shot.zsc rename to zscript/weapons/swwm_shot_fx.zsc index 73bfef623..5636a9a94 100644 --- a/zscript/swwm_shot.zsc +++ b/zscript/weapons/swwm_shot_fx.zsc @@ -1,5 +1,4 @@ -// Blackmann "Rhino Stopper" Spreadgun (from Instant Action 3, also planned for Zanaveth Ultra Suite 2) -// Slot 3, replaces Shotgun, Ethereal Crossbow, Serpent Staff +// Spreadgun projectiles and effects Class RedShellCasing : SWWMCasing { @@ -26,121 +25,6 @@ Class GoldShellCasing : RedShellCasing } } -Class SpreadgunTracer : LineTracer -{ - Actor ignoreme; - Array hitlist; - Array shootthroughlist; - Array waterhitlist; - - override ETraceStatus TraceCallback() - { - // liquid splashes - if ( Results.CrossedWater ) - { - let hl = new("WaterHit"); - hl.sect = Results.CrossedWater; - hl.hitpos = Results.CrossedWaterPos; - WaterHitList.Push(hl); - } - else if ( Results.Crossed3DWater ) - { - let hl = new("WaterHit"); - hl.sect = Results.Crossed3DWater; - hl.hitpos = Results.Crossed3DWaterPos; - WaterHitList.Push(hl); - } - if ( Results.HitType == TRACE_HitActor ) - { - if ( Results.HitActor == ignoreme ) return TRACE_Skip; - if ( Results.HitActor.bSHOOTABLE ) - { - int amt = SWWMDamageAccumulator.GetAmount(Results.HitActor); - // getgibhealth isn't clearscope, fuck - int gibhealth = -int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor); - if ( Results.HitActor.GibHealth != int.min ) gibhealth = -abs(Results.HitActor.GibHealth); - // if gibbed, go through without dealing more damage - if ( Results.HitActor.health-amt <= gibhealth ) return TRACE_Skip; - let ent = new("HitListEntry"); - ent.hitactor = Results.HitActor; - ent.hitlocation = Results.HitPos; - ent.x = Results.HitVector; - hitlist.Push(ent); - // go right on through if dead - if ( Results.HitActor.health-amt <= 0 ) return TRACE_Skip; - // stap - return TRACE_Stop; - } - return TRACE_Skip; - } - else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) - { - if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) - return TRACE_Stop; - ShootThroughList.Push(Results.HitLine); - return TRACE_Skip; - } - return TRACE_Stop; - } -} - -Class SpreadSlugTracer : SpreadgunTracer -{ - double penetration; // please don't laugh - - override ETraceStatus TraceCallback() - { - // liquid splashes - if ( Results.CrossedWater ) - { - let hl = new("WaterHit"); - hl.sect = Results.CrossedWater; - hl.hitpos = Results.CrossedWaterPos; - WaterHitList.Push(hl); - } - else if ( Results.Crossed3DWater ) - { - let hl = new("WaterHit"); - hl.sect = Results.Crossed3DWater; - hl.hitpos = Results.Crossed3DWaterPos; - WaterHitList.Push(hl); - } - if ( Results.HitType == TRACE_HitActor ) - { - if ( Results.HitActor == ignoreme ) return TRACE_Skip; - if ( Results.HitActor.bSHOOTABLE ) - { - let ent = new("HitListEntry"); - ent.hitactor = Results.HitActor; - ent.hitlocation = Results.HitPos; - ent.x = Results.HitVector; - if ( (Results.HitActor.Health >= int(penetration)) || Results.HitActor.bNODAMAGE ) - { - ent.hitdamage = int(penetration); - penetration = 0; - } - else - { - ent.hitdamage = min(Results.HitActor.health+int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor),int(penetration)); - penetration = max(0,penetration-ent.hitdamage); - } - hitlist.Push(ent); - if ( penetration <= 0 ) return TRACE_Stop; - return TRACE_Skip; - } - return TRACE_Skip; - } - else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) - { - if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) - return TRACE_Stop; - ShootThroughList.Push(Results.HitLine); - return TRACE_Skip; - } - return TRACE_Stop; - } -} - Class SpreadImpact : Actor { Default @@ -1853,1070 +1737,3 @@ Class GoldenSubSubImpact : Actor Stop; } } - -Class Spreadgun : SWWMWeapon -{ - bool fired; // shell was used - bool chambered; // a shell is actually loaded - bool emptyup; // next reload will keep the chamber empty - Class loadammo, nextammo; // currently loaded shell, next shell to load - bool initialized; - - transient ui TextureID WeaponBox, AmmoIcon[7], LoadedIcon[7]; - transient ui Font TewiFont; - - override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) - { - if ( loadammo is 'RedShell' ) return StringTable.Localize("$O_SPREADGUN_RED"); - if ( loadammo is 'GreenShell' ) return StringTable.Localize("$O_SPREADGUN_GREEN"); - if ( loadammo is 'WhiteShell' ) return StringTable.Localize("$O_SPREADGUN_WHITE"); - if ( loadammo is 'BlueShell' ) return StringTable.Localize("$O_SPREADGUN_BLUE"); - if ( loadammo is 'BlackShell' ) return StringTable.Localize("$O_SPREADGUN_BLACK"); - if ( loadammo is 'PurpleShell' ) return StringTable.Localize("$O_SPREADGUN_PURPLE"); - if ( loadammo is 'GoldShell' ) return StringTable.Localize("$O_SPREADGUN_GOLD"); - return Super.GetObituary(victim,inflictor,mod,playerattack); - } - - override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - if ( !WeaponBox ) - { - WeaponBox = TexMan.CheckForTexture("graphics/HUD/SpreadgunDisplay.png",TexMan.Type_Any); - AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/RedShell.png",TexMan.Type_Any); - AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/GreenShell.png",TexMan.Type_Any); - AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/WhiteShell.png",TexMan.Type_Any); - AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/BlueShell.png",TexMan.Type_Any); - AmmoIcon[4] = TexMan.CheckForTexture("graphics/HUD/BlackShell.png",TexMan.Type_Any); - AmmoIcon[5] = TexMan.CheckForTexture("graphics/HUD/PurpleShell.png",TexMan.Type_Any); - AmmoIcon[6] = TexMan.CheckForTexture("graphics/HUD/GoldShell.png",TexMan.Type_Any); - LoadedIcon[0] = TexMan.CheckForTexture("graphics/HUD/LoadedRedShell.png",TexMan.Type_Any); - LoadedIcon[1] = TexMan.CheckForTexture("graphics/HUD/LoadedGreenShell.png",TexMan.Type_Any); - LoadedIcon[2] = TexMan.CheckForTexture("graphics/HUD/LoadedWhiteShell.png",TexMan.Type_Any); - LoadedIcon[3] = TexMan.CheckForTexture("graphics/HUD/LoadedBlueShell.png",TexMan.Type_Any); - LoadedIcon[4] = TexMan.CheckForTexture("graphics/HUD/LoadedBlackShell.png",TexMan.Type_Any); - LoadedIcon[5] = TexMan.CheckForTexture("graphics/HUD/LoadedPurpleShell.png",TexMan.Type_Any); - LoadedIcon[6] = TexMan.CheckForTexture("graphics/HUD/LoadedGoldShell.png",TexMan.Type_Any); - } - if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); - Screen.DrawTexture(WeaponBox,false,bx-54,by-43,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int ox = 6; - int oy = 11; - for ( int i=0; i<7; i++ ) - { - Screen.DrawTexture(AmmoIcon[i],false,bx-ox,by-oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); - String astr = String.Format("%3d",Owner.CountInv(types[i])); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-ox-(TewiFont.StringWidth(astr)+1),by-oy-2,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0)); - oy += 10; - if ( i == 3 ) - { - oy = 21; - ox = 33; - } - } - if ( !chambered ) return; - for ( int i=0; i<7; i++ ) - { - if ( loadammo != types[i] ) continue; - Screen.DrawTexture(LoadedIcon[i],false,bx-48,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fired?Color(128,0,0,0):Color(0,0,0,0)); - break; - } - } - - override bool ReportHUDAmmo() - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - for ( int i=0; i<7; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; - return (!fired && chambered); - } - - override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - if ( (firemode == PrimaryFire) || (firemode == AltFire) ) - { - if ( !fired && chambered ) return true; - for ( int i=0; i<7; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true; - return false; - } - return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); - } - - override bool UsesAmmo( Class kind ) - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - for ( int i=0; i<7; i++ ) if ( kind is types[i] ) return true; - return false; - } - - action void A_SelectUnloadState() - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - static const statelabel primedstates[] = {"UnloadRed", "UnloadGreen", "UnloadWhite", "UnloadBlue", "UnloadBlack", "UnloadPurple", "UnloadGold"}; - static const statelabel firedstates[] = {"UnloadRedFired", "UnloadGreenFired", "UnloadWhiteFired", "UnloadBlueFired", "UnloadBlackFired", "UnloadPurpleFired", "UnloadGoldFired"}; - int amidx = 0; - for ( int i=0; i<7; i++ ) - { - if ( invoker.loadammo != types[i] ) continue; - amidx = i; - break; - } - if ( !invoker.chambered ) player.SetPSprite(PSP_WEAPON,invoker.FindState("UnloadEmpty")); // no "fired" one for this, as it can never happen - else if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState(primedstates[amidx])); - else player.SetPSprite(PSP_WEAPON,invoker.FindState(firedstates[amidx])); - if ( invoker.chambered ) A_Overlay(-9999,"UnloadDummy"); - else A_Overlay(-9999,"UnloadDummyEmpty"); - A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP); - } - - action void A_SelectLoadState() - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - static const statelabel primedstates[] = {"LoadRed", "LoadGreen", "LoadWhite", "LoadBlue", "LoadBlack", "LoadPurple", "LoadGold"}; - static const statelabel firedstates[] = {"LoadRedFired", "LoadGreenFired", "LoadWhiteFired", "LoadBlueFired", "LoadBlackFired", "LoadPurpleFired", "LoadGoldFired"}; - int amidx = 0; - for ( int i=0; i<7; i++ ) - { - if ( invoker.nextammo != types[i] ) continue; - amidx = i; - break; - } - if ( invoker.emptyup ) - { - if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState("LoadEmpty")); - else player.SetPSprite(PSP_WEAPON,invoker.FindState("LoadEmptyFired")); - } - else if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState(primedstates[amidx])); - else player.SetPSprite(PSP_WEAPON,invoker.FindState(firedstates[amidx])); - if ( !invoker.emptyup && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - { - let amo = FindInventory(invoker.nextammo); - if ( amo && (amo.Amount > 0) ) amo.Amount--; - } - if ( invoker.emptyup ) A_Overlay(-9999,"LoadDummyEmpty"); - else A_Overlay(-9999,"LoadDummy"); - invoker.emptyup = false; - } - - action void A_DropShell() - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - static const Class casetypes[] = {"RedShellCasing","GreenShellCasing","WhiteShellCasing","BlueShellCasing","BlackShellCasing","PurpleShellCasing","GoldShellCasing"}; - if ( !invoker.fired ) - { - for ( int i=0; i<7; i++ ) - { - if ( invoker.loadammo != types[i] ) continue; - let amo = FindInventory(types[i]); - if ( !amo ) - { - amo = Inventory(Spawn(types[i])); - amo.AttachToOwner(self); - amo.Amount = 0; - } - if ( (amo.Amount >= amo.MaxAmount) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - amo.CreateTossable(1); - amo.Amount++; - break; - } - } - else - { - for ( int i=0; i<7; i++ ) - { - if ( invoker.loadammo != types[i] ) continue; - Vector3 x, y, z; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-10*z); - let c = Spawn(casetypes[i],origin); - c.angle = angle; - c.pitch = pitch; - c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2,3)); - c.vel += vel*.5; - break; - } - } - } - - action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class impact = "SpreadImpact", int bc = 1, bool large = false ) - { - if ( swwm_omnibust ) - { - // Wall busting - int bustdmg = dmg; - if ( t is 'SpreadSlugTracer' ) bustdmg = int(SpreadSlugTracer(t).penetration); - BusterWall.Bust(t.Results,bustdmg,self,t.Results.HitVector,t.Results.HitPos.z); - } - for ( int i=0; i types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - static const statelabel flashes[] = {"FlashRed","FlashGreen","FlashWhite","FlashBlue","FlashBlack","FlashPurple","FlashGold"}; - static const String sounds[] = {"spreadgun/redfire","spreadgun/greenfire","spreadgun/whitefire","spreadgun/bluefire","spreadgun/blackfire","spreadgun/purplefire","spreadgun/goldfire"}; - static const int louds[] = {800,1000,1100,1200,1400,600,2500}; - static const int quakes[] = {3,4,2,4,3,1,6}; - static const Color cols[] = {Color(40,255,192,64),Color(36,255,192,80),Color(64,255,160,32),Color(48,32,176,255),Color(72,255,128,16),Color(24,255,224,96),Color(96,255,224,16)}; - for ( int i=0; i<7; i++ ) - { - if ( invoker.loadammo != types[i] ) continue; - A_SWWMFlash(flashes[i]); - A_StartSound(sounds[i],CHAN_WEAPON,CHANF_OVERLAP,attenuation:.6); - A_AlertMonsters(swwm_uncapalert?0:louds[i]); - A_QuakeEx(quakes[i],quakes[i],quakes[i],9,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.2*quakes[i]); - A_ZoomFactor(1.-quakes[i]*.04,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_PlayerFire(); - SWWMHandler.DoFlash(self,cols[i],5); - Vector3 x, y, z; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z); - Vector3 x2, y2, z2; - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - double a, s; - Vector3 dir; - SpreadgunTracer st; - SpreadSlugTracer sst; - switch ( i ) - { - case 1: - sst = new("SpreadSlugTracer"); - sst.ignoreme = self; - sst.penetration = 150.; - a = FRandom[Spreadgun](0,360); - s = FRandom[Spreadgun](0,.01); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - sst.hitlist.Clear(); - sst.shootthroughlist.Clear(); - sst.waterhitlist.Clear(); - sst.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky); - ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1,true); - for ( int i=0; i<6; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.scale *= .8; - s.alpha *= .3; - s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); - s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.); - } - for ( int i=0; i<10; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .2; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); - } - SWWMUtility.DoKnockback(self,-x,25000.); - break; - case 2: - for ( int j=0; j<3; j++ ) - { - a = FRandom[Spreadgun](0,360); - s = FRandom[Spreadgun](0,.24); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let p = Spawn("DragonBreathArm",origin); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - } - for ( int i=0; i<15; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.special1 = 1; - s.scale *= .9; - s.alpha *= .3; - s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); - s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); - } - for ( int i=0; i<12; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .3; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); - } - SWWMUtility.DoKnockback(self,-x,13000.); - break; - case 3: - for ( int j=0; j<8; j++ ) - { - a = FRandom[Spreadgun](0,360); - s = FRandom[Spreadgun](0,.8); - let b = Spawn("SaltBeam",level.Vec3Offset(origin,y*cos(a)*s+z*sin(a)*s)); - b.target = self; - b.angle = atan2(x2.y,x2.x); - b.pitch = asin(-x2.z); - } - for ( int i=0; i<16; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.special1 = 1; - s.scale *= .9; - s.SetShade(Color(1,3,4)*Random[Spreadgun](32,63)); - s.A_SetRenderStyle(.3,STYLE_AddShaded); - s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); - } - for ( int i=0; i<20; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .3; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); - } - SWWMUtility.DoKnockback(self,-x,23000.); - break; - case 4: - for ( int j=0; j<50; j++ ) - { - a = FRandom[Spreadgun](0,360); - s = FRandom[Spreadgun](0,.2); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let p = Spawn("CorrosiveFlechette",origin); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed*FRandom[Spreadgun](1.,1.5); - } - for ( int i=0; i<10; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.scale *= .7; - s.alpha *= .3; - s.SetShade(Color(1,1,1)*Random[Spreadgun](48,128)); - s.vel += vel*.5+x*FRandom[Spreadgun](3.,12.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); - } - for ( int i=0; i<20; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .3; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Spreadgun](4.,12.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); - } - SWWMUtility.DoKnockback(self,-x,15000.); - break; - case 5: - a = FRandom[Spreadgun](0,360); - s = FRandom[Spreadgun](0,.03); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let b = Spawn("TheBall",origin); - b.target = self; - b.angle = atan2(dir.y,dir.x); - b.pitch = asin(-dir.z); - b.vel = dir*b.speed; - for ( int i=0; i<8; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.scale *= .6; - s.alpha *= .25; - s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); - s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.); - } - for ( int i=0; i<8; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .2; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); - } - SWWMUtility.DoKnockback(self,-x,9500.); - break; - case 6: - a = FRandom[Spreadgun](0,360); - s = FRandom[Spreadgun](0,.01); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - FLineTraceData d; - LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d); - SWWMBulletTrail.DoTrail(self,origin,dir,10000,2,true); - if ( d.HitType != TRACE_HitNone ) - { - 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("SlugImpact",d.HitLocation+hitnormal); - 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("GoldenImpact",d.HitLocation+hitnormal*4.); - b.angle = atan2(hitnormal.y,hitnormal.x); - b.pitch = asin(-hitnormal.z); - b.target = self; - } - for ( int i=0; i<6; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.scale *= .8; - s.alpha *= .3; - s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); - s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.); - } - for ( int i=0; i<10; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .2; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); - } - for ( int i=0; i<50; i++ ) - { - let s = Spawn("FancyConfetti",origin); - s.bAMBUSH = true; - s.vel += vel*.5+x*FRandom[Spreadgun](1.,20.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); - } - SWWMUtility.DoKnockback(self,-x,30000.); - break; - default: - st = new("SpreadgunTracer"); - st.ignoreme = self; - for ( int j=0; j<20; j++ ) - { - a = FRandom[Spreadgun](0,360); - s = FRandom[Spreadgun](0,.22); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - st.hitlist.Clear(); - st.shootthroughlist.Clear(); - st.waterhitlist.Clear(); - st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky); - ProcessTraceHit(st,origin,dir,6,7000,bc:5); - } - for ( int i=0; i<16; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.special1 = 1; - s.scale *= .9; - s.alpha *= .3; - s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192)); - s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1); - } - for ( int i=0; i<20; i++ ) - { - let s = Spawn("SWWMSpark",origin); - s.scale *= .3; - s.alpha *= .4; - s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2); - } - SWWMUtility.DoKnockback(self,-x,20000.); - break; - } - break; - } - A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP); - invoker.fired = true; - } - - action void A_LoadShell() - { - A_StartSound("spreadgun/shellin",CHAN_WEAPON,CHANF_OVERLAP); - invoker.chambered = true; - invoker.loadammo = invoker.nextammo; - } - - action void A_Prime() - { - if ( invoker.fired ) - { - A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP); - invoker.fired = false; - } - } - - override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) - { - bool good = Super.PickupForAmmoSWWM(ownedWeapon); - let Owner = ownedWeapon.Owner; - if ( (AmmoGive1 == 0) && loadammo && !fired && chambered ) - { - let cur = Owner.FindInventory(loadammo); - if ( !cur ) - { - cur = Inventory(Spawn(loadammo)); - cur.Amount = 0; - cur.AttachToOwner(Owner); - } - // give the loaded shell (or drop) - if ( cur.Amount >= cur.MaxAmount ) cur.CreateTossable(1); - cur.Amount++; - good = true; - } - return good; - } - - override void AttachToOwner( Actor other ) - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - Super.AttachToOwner(other); - if ( !initialized ) - { - initialized = true; - if ( !loadammo ) loadammo = "RedShell"; - fired = false; - chambered = true; - } - for ( int i=0; i<7; i++ ) - { - Ammo a = Ammo(other.FindInventory(types[i])); - if ( !a ) continue; - nextammo = types[i]; - return; - } - nextammo = AmmoType1; - } - - action void A_SwitchAmmoType( bool rev = false ) - { - static const Class types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"}; - int cur = 0, next = 0; - for ( int i=0; i<7; i++ ) - { - if ( invoker.nextammo != types[i] ) continue; - cur = i; - break; - } - int ridx = -1; - if ( rev ) - { - // check backwards from what we currently had - for ( int i=cur; i>=0; i-- ) - { - if ( CountInv(types[i]) <= 0 ) continue; - ridx = i; - break; - } - if ( ridx == -1 ) - { - // check forwards instead, but avoid golden shells - for ( int i=0; i<6; i++ ) - { - if ( CountInv(types[i]) <= 0 ) continue; - ridx = i; - break; - } - } - if ( ridx != -1 ) next = ridx; - } - else - { - for ( int i=0; i<7; i++ ) - { - ridx = (i+cur+1)%7; - if ( CountInv(types[ridx]) <= 0 ) continue; - next = ridx; - break; - } - } - if ( invoker.nextammo != types[next] ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL); - invoker.nextammo = types[next]; - A_WeaponReady(WRF_NOFIRE); - } - - action void A_AltHold() - { - A_WeaponReady(WRF_NOFIRE); - // tap fire to unload round - if ( invoker.chambered && (player.cmd.buttons&BT_ATTACK) ) - { - invoker.emptyup = true; - player.SetPSPrite(PSP_WEAPON,invoker.FindState("Reload")); - return; - } - if ( player.cmd.buttons&BT_ALTATTACK ) return; - A_SwitchAmmoType(); - if ( !invoker.fired ) player.SetPSPrite(PSP_WEAPON,invoker.FindState("Ready")); - else player.SetPSPrite(PSP_WEAPON,invoker.FindState("ReadyFired")); - } - - override void ModifyDropAmount( int dropamount ) - { - Super.ModifyDropAmount(dropamount); - // toss some ammo while we're at it - if ( Random[Spreadgun](0,1) ) - A_DropItem(Random[Spreadgun](0,2)?"RedShell":"GreenShell",Random[Spreadgun](1,2)); - } - - Default - { - Tag "$T_SPREADGUN"; - Inventory.PickupMessage "$I_SPREADGUN"; - Obituary "$O_SPREADGUN"; - Inventory.Icon "graphics/HUD/Icons/W_Spreadgun.png"; - Weapon.UpSound "spreadgun/select"; - Weapon.SlotNumber 3; - Weapon.SelectionOrder 500; - Weapon.AmmoType1 "RedShell"; - Weapon.AmmoGive1 1; - SWWMWeapon.DropAmmoType "Shell"; - Stamina 15000; - +SWWMWEAPON.NOFIRSTGIVE; - Radius 10; - Height 24; - } - - States - { - Spawn: - XZW1 A -1; - Stop; - Deselect: - XZW2 A 1 - { - A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.fired,"DeselectFired"); - } - XZW2 BCDEFGHI 1; - XZW2 I -1 A_FullLower(); - Stop; - DeselectFired: - XZW2 Z 1; - XZW3 ABCDEFGH 1; - XZW3 H -1 A_FullLower(); - Stop; - Select: - XZW2 I 1 - { - A_FullRaise(); - return A_JumpIf(invoker.fired,"SelectFired"); - } - XZW2 JKLMNOPQ 1; - Goto Ready; - SelectFired: - XZW3 HIJKLMNOP 1; - Goto ReadyFired; - Ready: - XZW2 A 1 - { - if ( CountInv(invoker.nextammo) <= 0 ) A_SwitchAmmoType(true); - int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; - if ( invoker.nextammo && (CountInv(invoker.nextammo) > 0) && ((invoker.loadammo != invoker.nextammo) || !invoker.chambered) ) - flg |= WRF_ALLOWRELOAD; - A_WeaponReady(flg); - return ResolveState(null); - } - Wait; - ReadyFired: - XZW2 Z 1 - { - if ( CountInv(invoker.nextammo) <= 0 ) A_SwitchAmmoType(true); - int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; - if ( invoker.nextammo && (CountInv(invoker.nextammo) > 0) ) - flg |= WRF_ALLOWRELOAD; - else flg |= WRF_NOPRIMARY; - A_WeaponReady(flg); - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) ) - invoker.CheckAmmo(EitherFire,true); - return ResolveState(null); - } - Wait; - Fire: - #### # 1 - { - if ( invoker.fired || !invoker.chambered ) return ResolveState("Reload"); - A_FireShell(); - return ResolveState(null); - } - XZW2 RSTU 1; - XZW2 VWXY 2; - Goto ReadyFired; - AltFire: - #### # 1 A_AltHold(); - Wait; - Reload: - #### # 1 - { - A_PlayerReload(); - A_SelectUnloadState(); - } - Stop; - UnloadDummy: // overlay with shared functions for all unload anims - TNT1 A 11; - TNT1 A 14 - { - invoker.chambered = false; - A_StartSound("spreadgun/open",CHAN_WEAPON,CHANF_OVERLAP); - } - TNT1 A 1 A_DropShell(); - Stop; - UnloadDummyEmpty: - TNT1 A 11; - TNT1 A 14 A_StartSound("spreadgun/open",CHAN_WEAPON,CHANF_OVERLAP); - Stop; - UnloadRedFired: - XZW2 Z 2; - XZW3 QRST 2; - XZW3 UVWXYZ 1; - XZW4 ABCDEFGH 1; - XZW8 M 1; - Goto Reload2; - UnloadGreenFired: - XZW2 Z 2; - XZW4 IJKL 2; - XZW4 MNOPQRSTUVWXYZ 1; - XZW9 T 1; - Goto Reload2; - UnloadWhiteFired: - XZW2 Z 2; - XZW5 ABCD 2; - XZW5 EFGHIJKLMNOPQR 1; - XZWB A 1; - Goto Reload2; - UnloadBlueFired: - XZW2 Z 2; - XZW5 STUV 2; - XZW5 WXYZ 1; - XZW6 ABCDEFGHIJ 1; - XZWC H 1; - Goto Reload2; - UnloadBlackFired: - XZW2 Z 2; - XZW6 KLMN 2; - XZW6 OPQRSTUVWXYZ 1; - XZW7 AB 1; - XZWD O 1; - Goto Reload2; - UnloadPurpleFired: - XZW2 Z 2; - XZW7 CDEF 2; - XZW7 GHIJKLMNOPQRST 1; - XZWE V 1; - Goto Reload2; - UnloadGoldFired: - XZW2 Z 2; - XZW7 UVWX 2; - XZW7 YZ 1; - XZW8 ABCDEFGHIJKL 1; - XZWG C 1; - Goto Reload2; - UnloadRed: - XZW2 A 2; - XZWK JKLM 2; - XZWK NOPQRSTUVWXYZ 1; - XZWL A 1; - XZWP F 1; - Goto Reload2; - UnloadGreen: - XZW2 A 2; - XZWL BCDE 2; - XZWL FGHIJKLMNOPQRS 1; - XZWQ M 1; - Goto Reload2; - UnloadWhite: - XZW2 A 2; - XZWL TUVW 2; - XZWL XYZ 1; - XZWM ABCDEFGHIJK 1; - XZWR T 1; - Goto Reload2; - UnloadBlue: - XZW2 A 2; - XZWM LMNO 2; - XZWM PQRSTUVWXYZ 1; - XZWN ABC 1; - XZWT A 1; - Goto Reload2; - UnloadBlack: - XZW2 A 2; - XZWN DEFG 2; - XZWN HIJKLMNOPQRSTU 1; - XZWU H 1; - Goto Reload2; - UnloadPurple: - XZW2 A 2; - XZWN VWXY 2; - XZWN Z 1; - XZWO ABCDEFGHIJKLM 1; - XZWV O 1; - Goto Reload2; - UnloadGold: - XZW2 A 2; - XZWO NOPQ 2; - XZWO RSTUVWXYZ 1; - XZWP ABCDE 1; - XZWW V 1; - Goto Reload2; - UnloadEmpty: - XZW2 A 2; - XZWY CDEF 2; - XZWY GHIJKLMNOPQRSTU 1; - Goto Reload2; - Reload2: - #### # 1 A_SelectLoadState(); - Stop; - LoadDummy: // overlay with shared functions for all load anims - TNT1 A 9; - TNT1 A 12 A_LoadShell(); - TNT1 A 2 A_StartSound("spreadgun/close",CHAN_WEAPON,CHANF_OVERLAP); - TNT1 A 2 A_Prime(); - TNT1 A 1 { invoker.PlayUpSound(self); } - Stop; - LoadDummyEmpty: - TNT1 A 9; - TNT1 A 2 A_StartSound("spreadgun/close",CHAN_WEAPON,CHANF_OVERLAP); - TNT1 A 2 A_Prime(); - TNT1 A 1 { invoker.PlayUpSound(self); } - Stop; - LoadRedFired: - XZW8 MNOPQRSTUVWXYZ 1; - XZW9 ABCDEFGHIJKLMNOPQRS 1; - Goto Ready; - LoadGreenFired: - XZW9 TUVWXYZ 1; - XZWA ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; - Goto Ready; - LoadWhiteFired: - XZWB ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; - XZWC ABCDEFG 1; - Goto Ready; - LoadBlueFired: - XZWC HIJKLMNOPQRSTUVWXYZ 1; - XZWD ABCDEFGHIJKLMN 1; - Goto Ready; - LoadBlackFired: - XZWD OPQRSTUVWXYZ 1; - XZWE ABCDEFGHIJKLMNOPQRSTU 1; - Goto Ready; - LoadPurpleFired: - XZWE VWXYZ 1; - XZWF ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; - XZWG AB 1; - Goto Ready; - LoadGoldFired: - XZWG CDEFGHIJKLMNOPQRSTUVWXYZ 1; - XZWH ABCDEFGHI 1; - Goto Ready; - LoadRed: - XZWP FGHIJKLMNOPQRSTUVWXYZ 1; - XZWQ ABCDEFGHIJKL 1; - Goto Ready; - LoadGreen: - XZWQ MNOPQRSTUVWXYZ 1; - XZWR ABCDEFGHIJKLMNOPQRS 1; - Goto Ready; - LoadWhite: - XZWR TUVWXYZ 1; - XZWS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; - Goto Ready; - LoadBlue: - XZWT ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; - XZWU ABCDEFG 1; - Goto Ready; - LoadBlack: - XZWU HIJKLMNOPQRSTUVWXYZ 1; - XZWV ABCDEFGHIJKLMN 1; - Goto Ready; - LoadPurple: - XZWV OPQRSTUVWXYZ 1; - XZWW ABCDEFGHIJKLMNOPQRSTU 1; - Goto Ready; - LoadGold: - XZWW VWXYZ 1; - XZWX ABCDEFGHIJKLMNOPQRSTUVWXYZ 1; - XZWY AB 1; - Goto Ready; - LoadEmpty: - XZWY UVWXYZ 1; - XZWZ ABCDEFGHIJKLMNO 1; - Goto Ready; - LoadEmptyFired: - XZWZ PQRSTUVWXYZ 1; - XZW0 HIJKLMNOPQ 1; - Goto Ready; - Zoom: - XZW2 A 1 - { - A_StartSound("spreadgun/checkgun",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerCheckGun(); - return A_JumpIf(invoker.fired,"ZoomFired"); - } - XZWH JKLMNOPQRST 1; - XZWH UVWXYZ 2; - XZWI ABC 2; - XZWI DEFGHI 1; - Goto Ready; - ZoomFired: - XZW2 Z 1; - XZWI WXYZ 1; - XZWJ ABCDEFG 1; - XZWJ HIJKLMNOP 2; - XZWJ QRSTUV 1; - Goto ReadyFired; - DummyMelee: - TNT1 A 3 - { - A_Parry(9); - A_PlayerMelee(true); - } - TNT1 A 1 A_Melee(); - Stop; - User1: - XZW2 A 2 - { - A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.fired,"User1Fired"); - } - XZWI JK 2; - User1Hold: - XZWI L 1 - { - A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); - A_Overlay(-9999,"DummyMelee"); - } - XZWI MNOP 2; - XZWI QR 3; - XZWI S 0 A_JumpIf(player.cmd.buttons&BT_USER1,"User1Hold"); - XZWI S 0 { invoker.PlayUpSound(self); } - XZWI STUV 2; - Goto Ready; - User1Fired: - XZW2 Z 2; - XZWJ WX 2; - User1FiredHold: - XZWJ Y 1 - { - A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); - A_Overlay(-9999,"DummyMelee"); - } - XZWJ Z 2; - XZWK ABC 2; - XZWK DE 3; - XZWK F 0 A_JumpIf(player.cmd.buttons&BT_USER1,"User1FiredHold"); - XZWK F 0 { invoker.PlayUpSound(self); } - XZWK FGHI 2; - Goto ReadyFired; - FlashRed: - XZW0 A 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[3] = 120; - l.target = self; - } - Stop; - FlashGreen: - XZW0 B 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[3] = 90; - l.target = self; - } - Stop; - FlashWhite: - XZW0 C 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[1] = 176; - l.args[2] = 32; - l.args[3] = 160; - l.target = self; - } - Stop; - FlashBlue: - XZW0 D 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[0] = 96; - l.args[1] = 224; - l.args[2] = 255; - l.args[3] = 160; - l.target = self; - } - Stop; - FlashBlack: - XZW0 E 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[3] = 60; - l.target = self; - } - Stop; - FlashPurple: - XZW0 F 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[3] = 60; - l.target = self; - } - Stop; - FlashGold: - XZW0 G 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[3] = 300; - l.target = self; - } - Stop; - } -} diff --git a/zscript/weapons/swwm_sparkyboi.zsc b/zscript/weapons/swwm_sparkyboi.zsc new file mode 100644 index 000000000..f9b0a8530 --- /dev/null +++ b/zscript/weapons/swwm_sparkyboi.zsc @@ -0,0 +1,552 @@ +// Decade Mechanics Model S-5 Biospark Carbine aka "Legacy Sparkster" (from UnSX series, also featured in SWWM series) +// Slot 7, replaces Plasma Rifle, Hellstaff, Arc of Death + +Class Sparkster : SWWMWeapon +{ + int clipcount; + bool doublestacc; + bool nomag; + + Property ClipCount : clipcount; + + transient ui TextureID WeaponBox, AmmoBar[6]; + transient ui Font TewiFont; + transient ui DynamicValueInterpolator iclip; + transient int failtime; + + override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + if ( !WeaponBox ) + { + WeaponBox = TexMan.CheckForTexture("graphics/HUD/BiosparkDisplay.png",TexMan.Type_Any); + AmmoBar[0] = TexMan.CheckForTexture("graphics/HUD/BiosparkBar.png",TexMan.Type_Any); + AmmoBar[1] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarRed.png",TexMan.Type_Any); + AmmoBar[2] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarGray.png",TexMan.Type_Any); + AmmoBar[3] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarSlim.png",TexMan.Type_Any); + AmmoBar[4] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarSlimRed.png",TexMan.Type_Any); + AmmoBar[5] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarSlimGray.png",TexMan.Type_Any); + } + Screen.DrawTexture(WeaponBox,false,bx-28,by-28,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-26,by-26,String.Format("%2d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( nomag ) return; + bool blinking = (failtime>gametic)&&((failtime-gametic)%16>=8); + if ( doublestacc ) + { + Screen.DrawTexture(AmmoBar[5],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawTexture(AmmoBar[5],false,bx-14,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawTexture(AmmoBar[3+(clipcount<2)],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,iclip?(iclip.GetValue()/10.):0.,DTA_ColorOverlay,(blinking&&(clipcount<=4))?Color(128,0,0,0):Color(0,0,0,0)); + if ( clipcount > 4 ) + Screen.DrawTexture(AmmoBar[3+(clipcount<6)],false,bx-14,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,iclip?(((iclip.GetValue()/10.))-12.):0.,DTA_ColorOverlay,blinking?Color(128,0,0,0):Color(0,0,0,0)); + } + else + { + Screen.DrawTexture(AmmoBar[2],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawTexture(AmmoBar[clipcount<2],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,iclip?(iclip.GetValue()/5.):0.,DTA_ColorOverlay,blinking?Color(128,0,0,0):Color(0,0,0,0)); + } + } + + override void HudTick() + { + Super.HudTick(); + if ( !iclip ) iclip = DynamicValueInterpolator.Create(clipcount*30,.25,1,10); + else iclip.Update(clipcount*30); + } + + override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) + { + if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true; + if ( (fireMode == PrimaryFire) || (fireMode == AltFire) ) return ((clipcount > 0) || (Ammo1.Amount > 0)); + return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); + } + + override bool ReportHUDAmmo() + { + if ( clipcount > 0 ) return true; + return Super.ReportHUDAmmo(); + } + + override Vector3 GetTraceOffset() + { + return (10.,4.5,-5.); + } + + action void A_FireSpark( int mode ) + { + int scnt = invoker.clipcount%4; + if ( mode == 2 ) + { + if ( invoker.clipcount > 4 ) invoker.clipcount = 4; + else invoker.clipcount = 0; + } + else invoker.clipcount = max(0,invoker.clipcount-1); + 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+4.5*y-5*z); + double a, s; + Actor p; + switch ( mode ) + { + case 0: + // spark + A_StartSound("biospark/fire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.7); + A_QuakeEx(2,2,2,5,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.2); + A_ZoomFactor(.96,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_SWWMFlash(); + SWWMHandler.DoFlash(self,Color(64,192,255,96),3); + A_AlertMonsters(swwm_uncapalert?0:5000); + A_PlayerFire(); + a = FRandom[Spread](0,360); + s = FRandom[Spread](0,.007); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + p = Spawn("BiosparkBall",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed; + break; + case 1: + // beam + A_StartSound("biospark/altfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.8); + A_QuakeEx(3,3,3,5,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.3); + A_ZoomFactor(.95,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_SWWMFlash(); + SWWMHandler.DoFlash(self,Color(64,192,255,96),3); + A_AlertMonsters(swwm_uncapalert?0:4000); + A_PlayerFire(); + a = FRandom[Spread](0,360); + s = FRandom[Spread](0,.003); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + p = Spawn("BiosparkBeam",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.frame = 0; + break; + case 2: + // big spark + A_StartSound("biospark/thirdfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:1.5); + A_QuakeEx(5,5,5,10,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.6); + A_ZoomFactor(.94,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_AlertMonsters(swwm_uncapalert?0:1200); + A_PlayerFire(); + SWWMUtility.DoKnockback(self,-x,2500.); + a = FRandom[Spread](0,360); + s = FRandom[Spread](0,.012); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + p = Spawn("BiosparkCore",origin); + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed; + p.vel.z += 3.5; + p.special1 = scnt; + break; + } + } + + action void A_DropMag( bool stacc = false ) + { + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-2*y-10*z); + let c = Spawn("SparksterMag",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3)); + c.vel += vel*.5; + if ( !stacc ) return; + origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),8*x-2*y-12*z); + c = Spawn("SparksterMag",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3)); + c.vel += vel*.5; + } + + override void Travelled() + { + Super.Travelled(); + if ( Owner.player && (Owner.player.Readyweapon == self) ) + Owner.A_StartSound("biospark/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.4,4.); + } + + action void A_FailZoom() + { + invoker.failtime = gametic+48; + A_StartSound("biospark/fail",CHAN_WEAPON,CHANF_OVERLAP); + } + + Default + { + Tag "$T_SPARKSTER"; + Inventory.PickupMessage "$I_SPARKSTER"; + Obituary "$O_SPARKSTER"; + Inventory.Icon "graphics/HUD/Icons/W_Sparkster.png"; + Weapon.SlotNumber 7; + Weapon.UpSound "biospark/select"; + Weapon.SelectionOrder 600; + Stamina 200000; + Weapon.AmmoType1 "SparkUnit"; + Weapon.AmmoGive1 1; + SWWMWeapon.DropAmmoType "SparkUnit"; + Sparkster.ClipCount 4; + +SWWMWEAPON.NOFIRSTGIVE; + +WEAPON.EXPLOSIVE; + Radius 22; + Height 24; + } + States + { + Spawn: + XZW1 A -1 NoDelay A_JumpIf(invoker.doublestacc,1); + XZW1 B -1; + Stop; + Select: + XZW2 G 2 + { + invoker.nomag = false; + A_FullRaise(); + A_StartSound("biospark/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.4,4.); + return A_JumpIf(invoker.doublestacc,"DoubleSelect"); + } + XZW2 HIJKLM 2; + Goto Ready; + DoubleSelect: + XZW6 XYZ 2; + XZW7 ABCD 2; + Goto DoubleReady; + Deselect: + XZW2 A 2 + { + A_StartSound("biospark/deselect",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.doublestacc,"DoubleDeselect"); + } + XZW2 BCDEFG 2; + XZW2 G -1 + { + A_StopSound(CHAN_WEAPONEXTRA); + A_FullLower(); + } + Stop; + DoubleDeselect: + XZW5 Z 2; + XZW6 STUVWX 2; + XZW6 X -1 + { + A_StopSound(CHAN_WEAPONEXTRA); + A_FullLower(); + } + Stop; + Ready: + XZW2 A 1 + { + A_WeaponReady(WRF_ALLOWZOOM|WRF_ALLOWUSER1|WRF_ALLOWRELOAD); + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_ZOOM) ) + invoker.CheckAmmo(EitherFire,true); + } + Wait; + DoubleReady: + XZW5 Z 1 + { + A_WeaponReady(WRF_ALLOWZOOM|WRF_ALLOWUSER1|WRF_ALLOWRELOAD); + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_ZOOM) ) + invoker.CheckAmmo(EitherFire,true); + } + Wait; + Fire: + XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"Reload"); + XZW2 A 1 + { + A_StartSound("biospark/prefire",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.doublestacc,"DoubleFire"); + } + XZW4 Y 12; + XZW4 Y 1 A_FireSpark(0); + XZW2 NOPQ 2; + Goto PreVent; + DoubleFire: + XZW5 Z 1; + XZW8 L 12; + XZW8 L 1 A_FireSpark(0); + XZW7 EFGH 2; + Goto DoublePreVent; + AltFire: + XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"Reload"); + XZW2 A 1 + { + A_StartSound("biospark/prefire",CHAN_WEAPON,CHANF_OVERLAP); + return A_JumpIf(invoker.doublestacc,"DoubleAltFire"); + } + XZW4 Z 12; + XZW4 Z 1 A_FireSpark(1); + XZW2 RSTU 2; + Goto PreVent; + DoubleAltFire: + XZW5 Z 1; + XZW8 M 12; + XZW8 M 1 A_FireSpark(1); + XZW7 IJKL 2; + Goto DoublePreVent; + PreVent: + XZW2 AAAAAAAA 1 + { + if ( invoker.ClipCount > 0 ) + { + if ( player.cmd.buttons&BT_ATTACK ) + return ResolveState("Refire"); + if ( player.cmd.buttons&BT_ALTATTACK ) + return ResolveState("AltRefire"); + } + return ResolveState(null); + } + Goto Vent; + DoublePreVent: + XZW5 ZZZZZZZZ 1 + { + if ( invoker.ClipCount > 0 ) + { + if ( player.cmd.buttons&BT_ATTACK ) + return ResolveState("DoubleRefire"); + if ( player.cmd.buttons&BT_ALTATTACK ) + return ResolveState("DoubleAltRefire"); + } + return ResolveState(null); + } + Goto DoubleVent; + Refire: + XZW2 A 1; + XZW4 Y 3; + Goto Fire+3; + DoubleRefire: + XZW5 Z 1; + XZW8 L 3; + Goto DoubleFire+2; + AltRefire: + XZW2 A 1; + XZW4 Y 3; + Goto AltFire+3; + DoubleAltRefire: + XZW5 Z 1; + XZW8 M 3; + Goto DoubleAltFire+2; + Vent: + XZW2 A 8 + { + A_Overlay(PSP_WEAPON+2,"VentFlash"); + A_OverlayFlags(PSP_WEAPON+2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); + A_OverlayRenderStyle(PSP_WEAPON+2,STYLE_Add); + A_StartSound("biospark/hiss",CHAN_WEAPON,CHANF_OVERLAP); + } + Goto Ready; + DoubleVent: + XZW5 Z 8 + { + A_Overlay(PSP_WEAPON+2,"VentFlash"); + A_OverlayFlags(PSP_WEAPON+2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); + A_OverlayRenderStyle(PSP_WEAPON+2,STYLE_Add); + A_StartSound("biospark/hiss",CHAN_WEAPON,CHANF_OVERLAP); + } + Goto DoubleReady; + VentFlash: + XZWY ABCDEFGHIJKLMNOPQRSTUVWX 1; + Stop; + Zoom: + XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"Reload"); + XZW2 A 0 A_JumpIf(invoker.doublestacc,"DoubleZoom"); + XZW2 A 0 A_JumpIf(invoker.ClipCount<2,"NoZoom"); + XZW2 A 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); + XZW5 A 12; + XZW5 A 1 A_FireSpark(2); + XZW2 VWXYZ 2; + Goto Ready; + DoubleZoom: + XZW5 Z 0 A_JumpIf((invoker.ClipCount<2)||((invoker.ClipCount>4)&&(invoker.ClipCount<6)),"DoubleNoZoom"); + XZW5 Z 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); + XZW8 N 12; + XZW8 N 1 A_FireSpark(2); + XZW7 MNOPQ 2; + Goto DoubleReady; + NoZoom: + XZW2 A 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); + XZW5 A 12; + XZW5 A 8 A_FailZoom(); + XZW2 A 20; + Goto Ready; + DoubleNoZoom: + XZW5 Z 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); + XZW8 N 12; + XZW8 N 8 A_FailZoom(); + XZW5 Z 20; + Goto DoubleReady; + Reload: + XZW2 A 0 + { + if ( invoker.clipcount > 0 ) + { + if ( !invoker.doublestacc && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo')) ) + return ResolveState("AttachExtra"); + return ResolveState("Idle"); + } + if ( (invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo') ) + return ResolveState("Idle"); + if ( invoker.doublestacc ) + return ResolveState("UnloadExtra"); + return ResolveState(null); + } + XZW2 A 2 + { + A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerReload(); + } + XZW3 ABCDE 2; + XZW3 F 2 + { + invoker.clipcount = 0; + invoker.nomag = true; + A_StartSound("biospark/magout",CHAN_WEAPON,CHANF_OVERLAP); + A_StopSound(CHAN_WEAPONEXTRA); + } + XZW3 GHIJKL 2; + XZW3 M 2 A_DropMag(); + Goto Reload2; + UnloadExtra: + XZW5 Z 2 + { + A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerReload(); + } + XZW9 MNOPQ 2; + XZW9 R 2 + { + invoker.clipcount = 0; + invoker.nomag = true; + invoker.doublestacc = false; + A_StartSound("biospark/magout",CHAN_WEAPON,CHANF_OVERLAP); + A_StopSound(CHAN_WEAPONEXTRA); + } + XZW9 STUVWX 2; + XZW9 Y 2 A_DropMag(true); + XZW9 Z 0; + Goto Reload2; + Reload2: + XZW3 NOPQR 2; + XZW3 S 2 + { + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); + invoker.clipcount = invoker.default.clipcount; + invoker.nomag = false; + A_StartSound("biospark/magin",CHAN_WEAPON,CHANF_OVERLAP); + A_StartSound("biospark/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.4,4.); + } + XZW3 UV 2; + XZW3 W 0 A_JumpIf((player.cmd.buttons&BT_RELOAD)&&((invoker.Ammo1.Amount>0)||sv_infiniteammo||FindInventory('PowerInfiniteAmmo')),"DoubleStacc"); + Goto Reload3; + Reload3: + XZW3 W 2; + XZW3 X 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 YZ 2; + XZW4 ABCD 2; + Goto Vent; + DoubleStacc: + XZWA ABCDEFG 2; + XZWA H 2 + { + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); + invoker.clipcount += invoker.default.clipcount; + invoker.doublestacc = true; + A_StartSound("biospark/magin",CHAN_WEAPON,CHANF_OVERLAP); + } + XZWA IJKL 2; + XZWA M 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZWA NOPQRS 2; + Goto DoubleVent; + AttachExtra: + XZW2 A 2 + { + A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerReload(); + } + XZW6 ABCDEFGHI 2; + XZW6 J 2 + { + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); + invoker.clipcount += invoker.default.clipcount; + invoker.doublestacc = true; + A_StartSound("biospark/magin",CHAN_WEAPON,CHANF_OVERLAP); + } + XZW6 K 2; + XZW6 L 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW6 MNOPQR 2; + Goto DoubleVent; + Idle: + XZW2 A 2 + { + A_StartSound("biospark/checkout",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerCheckGun(); + return A_JumpIf(invoker.doublestacc,"DoubleIdle"); + } + XZW5 BCDE 2; + XZW5 FGHI 3; + XZW5 JKLM 2; + XZW5 NOPQ 3; + XZW5 RST 2; + XZW5 UVWXY 3; + Goto Ready; + DoubleIdle: + XZW5 Z 2; + XZW8 OPQR 2; + XZW8 STUV 3; + XZW8 WXYZ 2; + XZW9 ABCD 3; + XZW9 EFG 2; + XZW9 HIJKL 3; + Goto DoubleReady; + User1: + XZW2 A 2 + { + A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); + A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); + A_PlayerMelee(); + return A_JumpIf(invoker.doublestacc,"DoubleUser1"); + } + XZW4 EFG 2; + XZW4 H 1 A_Parry(9); + XZW4 IJ 1; + XZW4 K 2 A_Melee(60,"demolitionist/whitm"); + XZW4 LMNOPQ 2; + XZW4 R 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW4 STUVWX 2; + Goto Ready; + DoubleUser1: + XZW5 Z 2; + XZW7 RST 2; + XZW7 U 1 A_Parry(9); + XZW7 VW 1; + XZW7 X 2 A_Melee(60,"demolitionist/whitm"); + XZW7 YZ 2; + XZW8 ABCD 2; + XZW8 E 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); + XZW8 FGHIJK 2; + Goto DoubleReady; + Flash: + XZWZ A 2 Bright + { + let l = Spawn("SWWMWeaponLight",pos); + l.args[0] = 192; + l.args[1] = 255; + l.args[2] = 96; + l.target = self; + } + Stop; + } +} diff --git a/zscript/swwm_sparkyboi.zsc b/zscript/weapons/swwm_sparkyboi_fx.zsc similarity index 76% rename from zscript/swwm_sparkyboi.zsc rename to zscript/weapons/swwm_sparkyboi_fx.zsc index 582f9f6e1..3a7522d13 100644 --- a/zscript/swwm_sparkyboi.zsc +++ b/zscript/weapons/swwm_sparkyboi_fx.zsc @@ -1,5 +1,4 @@ -// Decade Mechanics Model S-5 Biospark Carbine aka "Legacy Sparkster" (from UnSX series, also featured in SWWM series) -// Slot 7, replaces Plasma Rifle, Hellstaff, Arc of Death +// Biospark Carbine projectiles and effects Class BiosparkExplLight : PaletteLight { @@ -1964,553 +1963,3 @@ Class SparksterMag : SWWMCasing Stop; } } - -Class Sparkster : SWWMWeapon -{ - int clipcount; - bool doublestacc; - bool nomag; - - Property ClipCount : clipcount; - - transient ui TextureID WeaponBox, AmmoBar[6]; - transient ui Font TewiFont; - transient ui DynamicValueInterpolator iclip; - transient int failtime; - - override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) - { - if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); - if ( !WeaponBox ) - { - WeaponBox = TexMan.CheckForTexture("graphics/HUD/BiosparkDisplay.png",TexMan.Type_Any); - AmmoBar[0] = TexMan.CheckForTexture("graphics/HUD/BiosparkBar.png",TexMan.Type_Any); - AmmoBar[1] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarRed.png",TexMan.Type_Any); - AmmoBar[2] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarGray.png",TexMan.Type_Any); - AmmoBar[3] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarSlim.png",TexMan.Type_Any); - AmmoBar[4] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarSlimRed.png",TexMan.Type_Any); - AmmoBar[5] = TexMan.CheckForTexture("graphics/HUD/BiosparkBarSlimGray.png",TexMan.Type_Any); - } - Screen.DrawTexture(WeaponBox,false,bx-28,by-28,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-26,by-26,String.Format("%2d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( nomag ) return; - bool blinking = (failtime>gametic)&&((failtime-gametic)%16>=8); - if ( doublestacc ) - { - Screen.DrawTexture(AmmoBar[5],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawTexture(AmmoBar[5],false,bx-14,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawTexture(AmmoBar[3+(clipcount<2)],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,iclip?(iclip.GetValue()/10.):0.,DTA_ColorOverlay,(blinking&&(clipcount<=4))?Color(128,0,0,0):Color(0,0,0,0)); - if ( clipcount > 4 ) - Screen.DrawTexture(AmmoBar[3+(clipcount<6)],false,bx-14,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,iclip?(((iclip.GetValue()/10.))-12.):0.,DTA_ColorOverlay,blinking?Color(128,0,0,0):Color(0,0,0,0)); - } - else - { - Screen.DrawTexture(AmmoBar[2],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawTexture(AmmoBar[clipcount<2],false,bx-26,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,iclip?(iclip.GetValue()/5.):0.,DTA_ColorOverlay,blinking?Color(128,0,0,0):Color(0,0,0,0)); - } - } - - override void HudTick() - { - Super.HudTick(); - if ( !iclip ) iclip = DynamicValueInterpolator.Create(clipcount*30,.25,1,10); - else iclip.Update(clipcount*30); - } - - override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) - { - if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true; - if ( (fireMode == PrimaryFire) || (fireMode == AltFire) ) return ((clipcount > 0) || (Ammo1.Amount > 0)); - return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); - } - - override bool ReportHUDAmmo() - { - if ( clipcount > 0 ) return true; - return Super.ReportHUDAmmo(); - } - - override Vector3 GetTraceOffset() - { - return (10.,4.5,-5.); - } - - action void A_FireSpark( int mode ) - { - int scnt = invoker.clipcount%4; - if ( mode == 2 ) - { - if ( invoker.clipcount > 4 ) invoker.clipcount = 4; - else invoker.clipcount = 0; - } - else invoker.clipcount = max(0,invoker.clipcount-1); - 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+4.5*y-5*z); - double a, s; - Actor p; - switch ( mode ) - { - case 0: - // spark - A_StartSound("biospark/fire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.7); - A_QuakeEx(2,2,2,5,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.2); - A_ZoomFactor(.96,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_SWWMFlash(); - SWWMHandler.DoFlash(self,Color(64,192,255,96),3); - A_AlertMonsters(swwm_uncapalert?0:5000); - A_PlayerFire(); - a = FRandom[Spread](0,360); - s = FRandom[Spread](0,.007); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - p = Spawn("BiosparkBall",origin); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed; - break; - case 1: - // beam - A_StartSound("biospark/altfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.8); - A_QuakeEx(3,3,3,5,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.3); - A_ZoomFactor(.95,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_SWWMFlash(); - SWWMHandler.DoFlash(self,Color(64,192,255,96),3); - A_AlertMonsters(swwm_uncapalert?0:4000); - A_PlayerFire(); - a = FRandom[Spread](0,360); - s = FRandom[Spread](0,.003); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - p = Spawn("BiosparkBeam",origin); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.frame = 0; - break; - case 2: - // big spark - A_StartSound("biospark/thirdfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:1.5); - A_QuakeEx(5,5,5,10,0,8,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.6); - A_ZoomFactor(.94,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_AlertMonsters(swwm_uncapalert?0:1200); - A_PlayerFire(); - SWWMUtility.DoKnockback(self,-x,2500.); - a = FRandom[Spread](0,360); - s = FRandom[Spread](0,.012); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - p = Spawn("BiosparkCore",origin); - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed; - p.vel.z += 3.5; - p.special1 = scnt; - break; - } - } - - action void A_DropMag( bool stacc = false ) - { - Vector3 x, y, z; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-2*y-10*z); - let c = Spawn("SparksterMag",origin); - c.angle = angle; - c.pitch = pitch; - c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3)); - c.vel += vel*.5; - if ( !stacc ) return; - origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),8*x-2*y-12*z); - c = Spawn("SparksterMag",origin); - c.angle = angle; - c.pitch = pitch; - c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3)); - c.vel += vel*.5; - } - - override void Travelled() - { - Super.Travelled(); - if ( Owner.player && (Owner.player.Readyweapon == self) ) - Owner.A_StartSound("biospark/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.4,4.); - } - - action void A_FailZoom() - { - invoker.failtime = gametic+48; - A_StartSound("biospark/fail",CHAN_WEAPON,CHANF_OVERLAP); - } - - Default - { - Tag "$T_SPARKSTER"; - Inventory.PickupMessage "$I_SPARKSTER"; - Obituary "$O_SPARKSTER"; - Inventory.Icon "graphics/HUD/Icons/W_Sparkster.png"; - Weapon.SlotNumber 7; - Weapon.UpSound "biospark/select"; - Weapon.SelectionOrder 600; - Stamina 200000; - Weapon.AmmoType1 "SparkUnit"; - Weapon.AmmoGive1 1; - SWWMWeapon.DropAmmoType "SparkUnit"; - Sparkster.ClipCount 4; - +SWWMWEAPON.NOFIRSTGIVE; - +WEAPON.EXPLOSIVE; - Radius 22; - Height 24; - } - States - { - Spawn: - XZW1 A -1 NoDelay A_JumpIf(invoker.doublestacc,1); - XZW1 B -1; - Stop; - Select: - XZW2 G 2 - { - invoker.nomag = false; - A_FullRaise(); - A_StartSound("biospark/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.4,4.); - return A_JumpIf(invoker.doublestacc,"DoubleSelect"); - } - XZW2 HIJKLM 2; - Goto Ready; - DoubleSelect: - XZW6 XYZ 2; - XZW7 ABCD 2; - Goto DoubleReady; - Deselect: - XZW2 A 2 - { - A_StartSound("biospark/deselect",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.doublestacc,"DoubleDeselect"); - } - XZW2 BCDEFG 2; - XZW2 G -1 - { - A_StopSound(CHAN_WEAPONEXTRA); - A_FullLower(); - } - Stop; - DoubleDeselect: - XZW5 Z 2; - XZW6 STUVWX 2; - XZW6 X -1 - { - A_StopSound(CHAN_WEAPONEXTRA); - A_FullLower(); - } - Stop; - Ready: - XZW2 A 1 - { - A_WeaponReady(WRF_ALLOWZOOM|WRF_ALLOWUSER1|WRF_ALLOWRELOAD); - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_ZOOM) ) - invoker.CheckAmmo(EitherFire,true); - } - Wait; - DoubleReady: - XZW5 Z 1 - { - A_WeaponReady(WRF_ALLOWZOOM|WRF_ALLOWUSER1|WRF_ALLOWRELOAD); - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_ZOOM) ) - invoker.CheckAmmo(EitherFire,true); - } - Wait; - Fire: - XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"Reload"); - XZW2 A 1 - { - A_StartSound("biospark/prefire",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.doublestacc,"DoubleFire"); - } - XZW4 Y 12; - XZW4 Y 1 A_FireSpark(0); - XZW2 NOPQ 2; - Goto PreVent; - DoubleFire: - XZW5 Z 1; - XZW8 L 12; - XZW8 L 1 A_FireSpark(0); - XZW7 EFGH 2; - Goto DoublePreVent; - AltFire: - XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"Reload"); - XZW2 A 1 - { - A_StartSound("biospark/prefire",CHAN_WEAPON,CHANF_OVERLAP); - return A_JumpIf(invoker.doublestacc,"DoubleAltFire"); - } - XZW4 Z 12; - XZW4 Z 1 A_FireSpark(1); - XZW2 RSTU 2; - Goto PreVent; - DoubleAltFire: - XZW5 Z 1; - XZW8 M 12; - XZW8 M 1 A_FireSpark(1); - XZW7 IJKL 2; - Goto DoublePreVent; - PreVent: - XZW2 AAAAAAAA 1 - { - if ( invoker.ClipCount > 0 ) - { - if ( player.cmd.buttons&BT_ATTACK ) - return ResolveState("Refire"); - if ( player.cmd.buttons&BT_ALTATTACK ) - return ResolveState("AltRefire"); - } - return ResolveState(null); - } - Goto Vent; - DoublePreVent: - XZW5 ZZZZZZZZ 1 - { - if ( invoker.ClipCount > 0 ) - { - if ( player.cmd.buttons&BT_ATTACK ) - return ResolveState("DoubleRefire"); - if ( player.cmd.buttons&BT_ALTATTACK ) - return ResolveState("DoubleAltRefire"); - } - return ResolveState(null); - } - Goto DoubleVent; - Refire: - XZW2 A 1; - XZW4 Y 3; - Goto Fire+3; - DoubleRefire: - XZW5 Z 1; - XZW8 L 3; - Goto DoubleFire+2; - AltRefire: - XZW2 A 1; - XZW4 Y 3; - Goto AltFire+3; - DoubleAltRefire: - XZW5 Z 1; - XZW8 M 3; - Goto DoubleAltFire+2; - Vent: - XZW2 A 8 - { - A_Overlay(PSP_WEAPON+2,"VentFlash"); - A_OverlayFlags(PSP_WEAPON+2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); - A_OverlayRenderStyle(PSP_WEAPON+2,STYLE_Add); - A_StartSound("biospark/hiss",CHAN_WEAPON,CHANF_OVERLAP); - } - Goto Ready; - DoubleVent: - XZW5 Z 8 - { - A_Overlay(PSP_WEAPON+2,"VentFlash"); - A_OverlayFlags(PSP_WEAPON+2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); - A_OverlayRenderStyle(PSP_WEAPON+2,STYLE_Add); - A_StartSound("biospark/hiss",CHAN_WEAPON,CHANF_OVERLAP); - } - Goto DoubleReady; - VentFlash: - XZWY ABCDEFGHIJKLMNOPQRSTUVWX 1; - Stop; - Zoom: - XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"Reload"); - XZW2 A 0 A_JumpIf(invoker.doublestacc,"DoubleZoom"); - XZW2 A 0 A_JumpIf(invoker.ClipCount<2,"NoZoom"); - XZW2 A 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); - XZW5 A 12; - XZW5 A 1 A_FireSpark(2); - XZW2 VWXYZ 2; - Goto Ready; - DoubleZoom: - XZW5 Z 0 A_JumpIf((invoker.ClipCount<2)||((invoker.ClipCount>4)&&(invoker.ClipCount<6)),"DoubleNoZoom"); - XZW5 Z 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); - XZW8 N 12; - XZW8 N 1 A_FireSpark(2); - XZW7 MNOPQ 2; - Goto DoubleReady; - NoZoom: - XZW2 A 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); - XZW5 A 12; - XZW5 A 8 A_FailZoom(); - XZW2 A 20; - Goto Ready; - DoubleNoZoom: - XZW5 Z 1 A_StartSound("biospark/prethird",CHAN_WEAPON,CHANF_OVERLAP); - XZW8 N 12; - XZW8 N 8 A_FailZoom(); - XZW5 Z 20; - Goto DoubleReady; - Reload: - XZW2 A 0 - { - if ( invoker.clipcount > 0 ) - { - if ( !invoker.doublestacc && ((invoker.Ammo1.Amount > 0) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo')) ) - return ResolveState("AttachExtra"); - return ResolveState("Idle"); - } - if ( (invoker.Ammo1.Amount <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo') ) - return ResolveState("Idle"); - if ( invoker.doublestacc ) - return ResolveState("UnloadExtra"); - return ResolveState(null); - } - XZW2 A 2 - { - A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerReload(); - } - XZW3 ABCDE 2; - XZW3 F 2 - { - invoker.clipcount = 0; - invoker.nomag = true; - A_StartSound("biospark/magout",CHAN_WEAPON,CHANF_OVERLAP); - A_StopSound(CHAN_WEAPONEXTRA); - } - XZW3 GHIJKL 2; - XZW3 M 2 A_DropMag(); - Goto Reload2; - UnloadExtra: - XZW5 Z 2 - { - A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerReload(); - } - XZW9 MNOPQ 2; - XZW9 R 2 - { - invoker.clipcount = 0; - invoker.nomag = true; - invoker.doublestacc = false; - A_StartSound("biospark/magout",CHAN_WEAPON,CHANF_OVERLAP); - A_StopSound(CHAN_WEAPONEXTRA); - } - XZW9 STUVWX 2; - XZW9 Y 2 A_DropMag(true); - XZW9 Z 0; - Goto Reload2; - Reload2: - XZW3 NOPQR 2; - XZW3 S 2 - { - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); - invoker.clipcount = invoker.default.clipcount; - invoker.nomag = false; - A_StartSound("biospark/magin",CHAN_WEAPON,CHANF_OVERLAP); - A_StartSound("biospark/idle",CHAN_WEAPONEXTRA,CHANF_LOOP,.4,4.); - } - XZW3 UV 2; - XZW3 W 0 A_JumpIf((player.cmd.buttons&BT_RELOAD)&&((invoker.Ammo1.Amount>0)||sv_infiniteammo||FindInventory('PowerInfiniteAmmo')),"DoubleStacc"); - Goto Reload3; - Reload3: - XZW3 W 2; - XZW3 X 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 YZ 2; - XZW4 ABCD 2; - Goto Vent; - DoubleStacc: - XZWA ABCDEFG 2; - XZWA H 2 - { - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); - invoker.clipcount += invoker.default.clipcount; - invoker.doublestacc = true; - A_StartSound("biospark/magin",CHAN_WEAPON,CHANF_OVERLAP); - } - XZWA IJKL 2; - XZWA M 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZWA NOPQRS 2; - Goto DoubleVent; - AttachExtra: - XZW2 A 2 - { - A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerReload(); - } - XZW6 ABCDEFGHI 2; - XZW6 J 2 - { - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); - invoker.clipcount += invoker.default.clipcount; - invoker.doublestacc = true; - A_StartSound("biospark/magin",CHAN_WEAPON,CHANF_OVERLAP); - } - XZW6 K 2; - XZW6 L 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW6 MNOPQR 2; - Goto DoubleVent; - Idle: - XZW2 A 2 - { - A_StartSound("biospark/checkout",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerCheckGun(); - return A_JumpIf(invoker.doublestacc,"DoubleIdle"); - } - XZW5 BCDE 2; - XZW5 FGHI 3; - XZW5 JKLM 2; - XZW5 NOPQ 3; - XZW5 RST 2; - XZW5 UVWXY 3; - Goto Ready; - DoubleIdle: - XZW5 Z 2; - XZW8 OPQR 2; - XZW8 STUV 3; - XZW8 WXYZ 2; - XZW9 ABCD 3; - XZW9 EFG 2; - XZW9 HIJKL 3; - Goto DoubleReady; - User1: - XZW2 A 2 - { - A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); - A_StartSound("biospark/meleestart",CHAN_WEAPON,CHANF_OVERLAP); - A_PlayerMelee(); - return A_JumpIf(invoker.doublestacc,"DoubleUser1"); - } - XZW4 EFG 2; - XZW4 H 1 A_Parry(9); - XZW4 IJ 1; - XZW4 K 2 A_Melee(60,"demolitionist/whitm"); - XZW4 LMNOPQ 2; - XZW4 R 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW4 STUVWX 2; - Goto Ready; - DoubleUser1: - XZW5 Z 2; - XZW7 RST 2; - XZW7 U 1 A_Parry(9); - XZW7 VW 1; - XZW7 X 2 A_Melee(60,"demolitionist/whitm"); - XZW7 YZ 2; - XZW8 ABCD 2; - XZW8 E 2 A_StartSound("biospark/meleeend",CHAN_WEAPON,CHANF_OVERLAP); - XZW8 FGHIJK 2; - Goto DoubleReady; - Flash: - XZWZ A 2 Bright - { - let l = Spawn("SWWMWeaponLight",pos); - l.args[0] = 192; - l.args[1] = 255; - l.args[2] = 96; - l.target = self; - } - Stop; - } -} diff --git a/zscript/swwm_splode.zsc b/zscript/weapons/swwm_splode.zsc similarity index 82% rename from zscript/swwm_splode.zsc rename to zscript/weapons/swwm_splode.zsc index 25d370137..24f1eb675 100644 --- a/zscript/swwm_splode.zsc +++ b/zscript/weapons/swwm_splode.zsc @@ -1,301 +1,6 @@ // Munch Innovations Explodium Gun (from SWWM series) // Slot 2, replaces Pistol, Elven Wand, Hexen starting weapons -Class ExplodiumCasing : SWWMCasing {} - -Class ExplodiumMag : SWWMCasing -{ - Default - { - Mass 10; - BounceFactor 0.4; - WallBounceFactor 0.4; - BounceSound "explodium/mag"; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - heat = 0; - } - States - { - Death: - XZW1 BC -1 - { - pitch = roll = 0; - angle = FRandom[Junk](0,360); - frame = RandomPick[Junk](1,2); - } - Stop; - } -} - -Class ExplodiumMagArm : Actor -{ - Default - { - Obituary "$O_EXPLODIUM"; - PROJECTILE; - +THRUACTORS; - +BOUNCEONWALLS; - +BOUNCEONFLOORS; - +BOUNCEONCEILINGS; - +NODAMAGETHRUST; - +FORCERADIUSDMG; - -NOGRAVITY; - Gravity 0.35; - BounceFactor 1.0; - Radius 4; - Height 4; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - reactiontime = Random[ExploS](10,15); - double ang, pt; - ang = FRandom[ExploS](0,360); - pt = FRandom[ExploS](-90,90); - vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[ExploS](8.,20.); - } - States - { - Spawn: - TNT1 A 1 - { - Spawn("ExplodiumMagTrail",pos); - if ( !(ReactionTime%2) ) - SWWMUtility.DoExplosion(self,2+reactiontime/2,3000+500*reactiontime,40+3*reactiontime,20); - 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; - s.SetShade(Color(1,1,1)*Random[ExploS](64,224)); - s.special1 = Random[ExploS](1,3); - s.scale *= 2.4; - s.alpha *= 0.1+.4*(ReactionTime/15.); - A_CountDown(); - } - Wait; - } -} - -Class ExplodiumMagTrail : Actor -{ - Default - { - RenderStyle "Add"; - +NOBLOCKMAP; - +NOGRAVITY; - +FORCEXYBILLBOARD; - +NOTELEPORT; - +NOINTERACTION; - Scale 1.1; - } - override void Tick() - { - if ( isFrozen() ) return; - if ( !CheckNoDelay() || (tics == -1) ) return; - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XEX1 ADGJMPSVY\ 1 Bright; - Stop; - } -} - -Class ExplodiumMagProj : Actor -{ - double pitchvel, anglevel; - Vector3 cvel; - - Default - { - Obituary "$O_EXPLODIUM"; - DamageType "Explodium"; - Radius 4; - Height 4; - Gravity 0.5; - Speed 30; - PROJECTILE; - -NOGRAVITY; - +ROLLSPRITE; - +ROLLCENTER; - +INTERPOLATEANGLES; - +NODAMAGETHRUST; - +FORCERADIUSDMG; - +FORCEXYBILLBOARD; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); - } - void A_BlowUp() - { - angle = atan2(cvel.y,cvel.x); - pitch = asin(-cvel.z); - bNOGRAVITY = true; - A_SetRenderStyle(1.,STYLE_Add); - Scale *= 2.+.2*special1; - A_AlertMonsters(swwm_uncapalert?0:6000); - SWWMUtility.DoExplosion(self,15+25*special1,80000+8000*special1,90+10*special1,60,DE_EXTRAZTHRUST); - A_QuakeEx(9,9,9,30,0,400+80*special1,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollintensity:2.); - A_StartSound("explodium/maghit",CHAN_VOICE,attenuation:.35); - A_StartSound("explodium/maghit",CHAN_WEAPON,attenuation:.2); - A_SprayDecal("BigRocketBlast",172); - Scale *= FRandom[ExploS](0.8,1.1); - Scale.x *= RandomPick[ExploS](-1,1); - Scale.y *= RandomPick[ExploS](-1,1); - int numpt = Random[ExploS](16,32); - for ( int i=0; i 0. ) cvel = vel.unit(); - } - Wait; - Death: - TNT1 A 0 A_BlowUp(); - XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright; - Stop; - } -} - -Class ExploLight : PaletteLight -{ - Default - { - ReactionTime 15; - } -} -Class ExploLight2 : PaletteLight -{ - Default - { - ReactionTime 30; - Args 0,0,0,120; - } -} - -Class ExplodiumBulletImpact : Actor -{ - Default - { - Obituary "$O_EXPLODIUM"; - DamageType "Explodium"; - RenderStyle "Add"; - Radius .1; - Height 0.; - Scale 1.2; - +NOGRAVITY; - +NOBLOCKMAP; - +NODAMAGETHRUST; - +FORCERADIUSDMG; - +FORCEXYBILLBOARD; - +NOTELEPORT; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - A_AlertMonsters(swwm_uncapalert?0:3000); - SWWMUtility.DoExplosion(self,25,80000,90,40,DE_EXTRAZTHRUST); - A_QuakeEx(4,4,4,10,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:150,rollintensity:0.2); - A_StartSound("explodium/hit",CHAN_VOICE,attenuation:.6); - A_StartSound("explodium/hit",CHAN_WEAPON,attenuation:.3); - A_SprayDecal("RocketBlast",-172); - Scale *= FRandom[ExploS](0.8,1.1); - Scale.x *= RandomPick[ExploS](-1,1); - Scale.y *= RandomPick[ExploS](-1,1); - int numpt = Random[ExploS](10,20); - for ( int i=0; i 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XEX1 ACEGIKMOQSUWY[] 1 Bright; - Stop; - } -} - Class ExplodiumGun : SWWMWeapon { int clipcount; diff --git a/zscript/weapons/swwm_splode_fx.zsc b/zscript/weapons/swwm_splode_fx.zsc new file mode 100644 index 000000000..35820c61a --- /dev/null +++ b/zscript/weapons/swwm_splode_fx.zsc @@ -0,0 +1,296 @@ +// Explodium Gun projectiles and effects + +Class ExplodiumCasing : SWWMCasing {} + +Class ExplodiumMag : SWWMCasing +{ + Default + { + Mass 10; + BounceFactor 0.4; + WallBounceFactor 0.4; + BounceSound "explodium/mag"; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + heat = 0; + } + States + { + Death: + XZW1 BC -1 + { + pitch = roll = 0; + angle = FRandom[Junk](0,360); + frame = RandomPick[Junk](1,2); + } + Stop; + } +} + +Class ExplodiumMagArm : Actor +{ + Default + { + Obituary "$O_EXPLODIUM"; + PROJECTILE; + +THRUACTORS; + +BOUNCEONWALLS; + +BOUNCEONFLOORS; + +BOUNCEONCEILINGS; + +NODAMAGETHRUST; + +FORCERADIUSDMG; + -NOGRAVITY; + Gravity 0.35; + BounceFactor 1.0; + Radius 4; + Height 4; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + reactiontime = Random[ExploS](10,15); + double ang, pt; + ang = FRandom[ExploS](0,360); + pt = FRandom[ExploS](-90,90); + vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[ExploS](8.,20.); + } + States + { + Spawn: + TNT1 A 1 + { + Spawn("ExplodiumMagTrail",pos); + if ( !(ReactionTime%2) ) + SWWMUtility.DoExplosion(self,2+reactiontime/2,3000+500*reactiontime,40+3*reactiontime,20); + 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; + s.SetShade(Color(1,1,1)*Random[ExploS](64,224)); + s.special1 = Random[ExploS](1,3); + s.scale *= 2.4; + s.alpha *= 0.1+.4*(ReactionTime/15.); + A_CountDown(); + } + Wait; + } +} + +Class ExplodiumMagTrail : Actor +{ + Default + { + RenderStyle "Add"; + +NOBLOCKMAP; + +NOGRAVITY; + +FORCEXYBILLBOARD; + +NOTELEPORT; + +NOINTERACTION; + Scale 1.1; + } + override void Tick() + { + if ( isFrozen() ) return; + if ( !CheckNoDelay() || (tics == -1) ) return; + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XEX1 ADGJMPSVY\ 1 Bright; + Stop; + } +} + +Class ExplodiumMagProj : Actor +{ + double pitchvel, anglevel; + Vector3 cvel; + + Default + { + Obituary "$O_EXPLODIUM"; + DamageType "Explodium"; + Radius 4; + Height 4; + Gravity 0.5; + Speed 30; + PROJECTILE; + -NOGRAVITY; + +ROLLSPRITE; + +ROLLCENTER; + +INTERPOLATEANGLES; + +NODAMAGETHRUST; + +FORCERADIUSDMG; + +FORCEXYBILLBOARD; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1); + } + void A_BlowUp() + { + angle = atan2(cvel.y,cvel.x); + pitch = asin(-cvel.z); + bNOGRAVITY = true; + A_SetRenderStyle(1.,STYLE_Add); + Scale *= 2.+.2*special1; + A_AlertMonsters(swwm_uncapalert?0:6000); + SWWMUtility.DoExplosion(self,15+25*special1,80000+8000*special1,90+10*special1,60,DE_EXTRAZTHRUST); + A_QuakeEx(9,9,9,30,0,400+80*special1,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollintensity:2.); + A_StartSound("explodium/maghit",CHAN_VOICE,attenuation:.35); + A_StartSound("explodium/maghit",CHAN_WEAPON,attenuation:.2); + A_SprayDecal("BigRocketBlast",172); + Scale *= FRandom[ExploS](0.8,1.1); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + int numpt = Random[ExploS](16,32); + for ( int i=0; i 0. ) cvel = vel.unit(); + } + Wait; + Death: + TNT1 A 0 A_BlowUp(); + XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright; + Stop; + } +} + +Class ExploLight : PaletteLight +{ + Default + { + ReactionTime 15; + } +} +Class ExploLight2 : PaletteLight +{ + Default + { + ReactionTime 30; + Args 0,0,0,120; + } +} + +Class ExplodiumBulletImpact : Actor +{ + Default + { + Obituary "$O_EXPLODIUM"; + DamageType "Explodium"; + RenderStyle "Add"; + Radius .1; + Height 0.; + Scale 1.2; + +NOGRAVITY; + +NOBLOCKMAP; + +NODAMAGETHRUST; + +FORCERADIUSDMG; + +FORCEXYBILLBOARD; + +NOTELEPORT; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + A_AlertMonsters(swwm_uncapalert?0:3000); + SWWMUtility.DoExplosion(self,25,80000,90,40,DE_EXTRAZTHRUST); + A_QuakeEx(4,4,4,10,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:150,rollintensity:0.2); + A_StartSound("explodium/hit",CHAN_VOICE,attenuation:.6); + A_StartSound("explodium/hit",CHAN_WEAPON,attenuation:.3); + A_SprayDecal("RocketBlast",-172); + Scale *= FRandom[ExploS](0.8,1.1); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + int numpt = Random[ExploS](10,20); + for ( int i=0; i 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XEX1 ACEGIKMOQSUWY[] 1 Bright; + Stop; + } +} diff --git a/zscript/weapons/swwm_tastytreat.zsc b/zscript/weapons/swwm_tastytreat.zsc new file mode 100644 index 000000000..9720843c1 --- /dev/null +++ b/zscript/weapons/swwm_tastytreat.zsc @@ -0,0 +1,570 @@ +// Munch Innovations "Taste the Sweetness" Candy Gun (from unreleased "Weird Weapons" UT minimod) +// Slot 9, replaces BFG9000, Firemace, Bloodscourge (stub) + +Class CandyGun : SWWMWeapon +{ + int clipcount; + bool chambered; + double casex, casey; + transient ui TextureID WeaponBox; + transient ui Font TewiFont; + bool tospecial; + + Property ClipCount : ClipCount; + + // re-edit to allow picking up spares in coop + override bool HandlePickup( Inventory item ) + { + if ( (item.GetClass() == GetClass()) && !item.ShouldStay() ) + { + bool ammoget, spareget; + [ammoget, spareget] = CandyGun(item).PickupForAmmoAndSpares(self); + if ( ammoget || spareget ) + item.bPickupGood = true; + if ( !spareget ) + { + // sell excess + int sellprice = item.Stamina/2; + SWWMScoreObj.Spawn(sellprice,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2)); + SWWMCredits.Give(Owner.player,sellprice); + if ( Owner.player ) + Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),Owner.player.GetUserName(),GetTag(),sellprice); + item.bPickupGood = true; + } + // reset the price in case it has to respawn + item.Stamina = item.default.Stamina; + return true; + } + return Weapon.HandlePickup(item); + } + protected bool, bool PickupForAmmoAndSpares( Weapon ownedWeapon ) + { + bool gotstuff = false, gotspares = false; + int oldamount = ownedWeapon.Ammo1.Amount; + gotstuff = AddExistingAmmo(ownedWeapon.Ammo1,AmmoGive1); + gotspares = AddExistingAmmo(ownedWeapon.Ammo2,AmmoGive2); + let Owner = ownedWeapon.Owner; + if ( gotstuff && Owner && Owner.player && (oldamount == 0) ) + PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass()); + if ( ownedWeapon.Ammo1 ) + { + // subtract price of ammo we don't give + int ammonotgiven = default.AmmoGive1-AmmoGive1; + if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammonotgiven-1))); + // subtract price of given ammo + int ammogiven = ownedWeapon.Ammo1.Amount-oldamount; + if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammogiven-1))); + // drop excess + int dropme = AmmoGive1-ammogiven; + if ( dropme > 0 ) + { + // hacky, but it works + ownedWeapon.Ammo1.Amount += dropme; + ownedWeapon.Ammo1.CreateTossable(dropme); + } + } + if ( (AmmoGive1 == 0) && ((clipcount > 0) || chambered) ) + { + // we were dropped, see if we can add the bullets we contain + int bul = clipcount+chambered; + MagAmmo ma = MagAmmo(Owner.FindInventory("CandyGunBullets")); + if ( !ma ) + { + ma = MagAmmo(Spawn("CandyGunBullets")); + ma.Amount = 0; + ma.AttachToOwner(Owner); + } + int maxgiveamt = min(ma.MaxAmount-ma.Amount,bul); + int dropamt = bul-maxgiveamt; + if ( dropamt > 0 ) ma.CreateTossable(dropamt); + ma.Amount = min(ma.MaxAmount,ma.Amount+bul); + gotstuff = true; + } + return gotstuff, gotspares; + } + + override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/CandygunDisplay.png",TexMan.Type_Any); + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + Screen.DrawTexture(WeaponBox,false,bx-51,by-44,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( chambered ) Screen.DrawText(TewiFont,Font.CR_FIRE,bx-22,by-21,"⁺¹",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-19,by-14,String.Format("%d",max(clipcount,0)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + int cx = (Ammo1.Amount>9)?48:45; + int sb = Owner.CountInv("CandyGunBullets"); + if ( sb > 0 ) + { + int cbx = (sb>9)?50:47; + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cbx,by-21,String.Format("⁺%s",SWWMUtility.SuperscriptNum(sb)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-14,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + else Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-18,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-37,by-40,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + + override Vector3 GetTraceOffset() + { + return (10.,3,-2.); + } + + action void A_Schutt() + { + let weap = Weapon(invoker); + if ( !weap ) return; + invoker.chambered = invoker.clipcount; + invoker.clipcount = max(invoker.clipcount-1,0); + A_StartSound("candygun/fire",CHAN_WEAPON,CHANF_OVERLAP); + A_QuakeEx(5,5,5,5,0,15,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:2.); + A_ZoomFactor(.94,ZOOM_INSTANT); + A_ZoomFactor(1.); + A_SWWMFlash(); + SWWMHandler.DoFlash(self,Color(64,224,64,255),5); + A_AlertMonsters(swwm_uncapalert?0:9000); + A_PlayerFire(); + Vector3 x, y, z, x2, y2, z2; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + SWWMUtility.DoKnockback(self,-x,18000.); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-2*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(); + FLineTraceData d; + LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d); + SWWMBulletTrail.DoTrail(self,origin,dir,10000,2); + if ( d.HitType == TRACE_HitActor ) + { + int dmg = 300; + // might as well apply explosion on top + if ( dmg >= d.HitActor.Health ) dmg += 900; + SWWMUtility.DoKnockback(d.HitActor,d.HitDir,72000); + dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS|DMG_FOILINVUL,atan2(d.HitDir.y,d.HitDir.x)); + if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT ) + { + let p = Spawn("SWWMBulletImpact",d.HitLocation); + p.angle = atan2(d.HitDir.y,d.HitDir.x)+180; + p.pitch = asin(d.HitDir.z); + } + else + { + d.HitActor.TraceBleed(dmg,self); + d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); + } + let b = Spawn("CandyBulletImpact",d.HitLocation-d.HitDir*4.); + b.angle = atan2(d.HitDir.y,d.HitDir.x)+180; + b.pitch = asin(d.HitDir.z); + b.target = self; + } + else if ( d.HitType != TRACE_HitNone ) + { + 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("CandyBulletImpact",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,1700,self,d.HitDir,d.HitLocation.z); + } + for ( int i=0; i<6; i++ ) + { + let s = Spawn("SWWMSmoke",origin); + s.scale *= .15; + s.alpha *= .5; + s.speed *= .2; + s.vel += vel*.5+x*FRandom[Explodium](.2,.5); + } + } + + 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,.01); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("CandyMagProj",origin); + p.special1 = invoker.special1; + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed; + if ( p.waterlevel <= 0 ) p.vel.z += 5.; + p.vel += vel*.5; + } + + action void A_ThrowGun() + { + let weap = Weapon(invoker); + if ( !weap ) return; + if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) + weap.Ammo2.Amount = max(0,weap.Ammo2.Amount-1); + 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,.015); + [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); + Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); + let p = Spawn("CandyGunProj",origin); + p.special1 = invoker.clipcount+invoker.chambered; + invoker.clipcount = 0; + invoker.chambered = false; + p.target = self; + p.angle = atan2(dir.y,dir.x); + p.pitch = asin(-dir.z); + p.vel = dir*p.speed; + if ( p.waterlevel <= 0 ) p.vel.z += 5.; + p.vel += vel*.5; + } + + action void A_DropMag() + { + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-2*y-10*z); + let c = Spawn("CandyMag",origin); + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3)); + c.vel += vel*.5; + } + + action void A_EmptyMag() + { + MagAmmo ma = MagAmmo(FindInventory("CandyGunBullets")); + if ( !ma ) + { + ma = MagAmmo(Spawn("CandyGunBullets")); + ma.Amount = 0; + ma.AttachToOwner(self); + } + int maxgiveamt = min(ma.MaxAmount-ma.Amount,invoker.clipcount); + int dropamt = invoker.clipcount-maxgiveamt; + if ( dropamt > 0 ) ma.CreateTossable(dropamt); + ma.Amount = min(ma.MaxAmount,ma.Amount+invoker.clipcount); + ma.MagFill(); + if ( CheckLocalView() ) for ( int i=0; i 0 ) ma.PlayPickupSound(self); + invoker.clipcount = 0; + } + + action void A_DropCasing() + { + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+8*y-10*z); + let c = Spawn("CandyCasing",origin); + c.special1 = special1; + c.angle = angle; + c.pitch = pitch; + c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](2,4)-(0,0,FRandom[Junk](2,3)); + c.vel += vel*.5; + } + + override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) + { + if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true; + if ( fireMode == PrimaryFire ) return (chambered || (clipcount > 0) || (Ammo1.Amount > 0) || (Owner.CountInv("CandyGunBullets") > 0)); + if ( fireMode == AltFire ) return ((Ammo1.Amount > 0) || (Owner.CountInv("CandyGunBullets") > 0)); + return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); + } + + override bool ReportHUDAmmo() + { + if ( chambered || (clipcount > 0) || (Owner.CountInv("CandyGunBullets") > 0) ) return true; + if ( Ammo1.Amount <= 0 ) return false; + return Super.ReportHUDAmmo(); + } + + override Inventory CreateTossable( int amt ) + { + if ( Ammo2.Amount > 0 ) + { + // drop an empty spare + let spare = Inventory(Spawn('CandyGun',Owner.Pos,NO_REPLACE)); + if ( !spare ) return null; + // spare with empty mag + CandyGun(spare).AmmoGive1 = 0; + CandyGun(spare).clipcount = 0; + CandyGun(spare).chambered = false; + spare.SetState(spare.SpawnState+1); + spare.DropTime = 30; + spare.bSpecial = spare.bSolid = false; + Ammo2.Amount--; + return spare; + } + return Super.CreateTossable(amt); + } + Default + { + Tag "$T_CANDYGUN"; + Inventory.PickupMessage "$I_CANDYGUN"; + Obituary "$O_CANDYGUN"; + Inventory.Icon "graphics/HUD/Icons/W_CandyGun.png"; + Weapon.UpSound "explodium/select"; + Weapon.SlotNumber 9; + Weapon.SelectionOrder 900; + Stamina 1000000; + Weapon.AmmoType1 "CandyGunAmmo"; + Weapon.AmmoType2 "CandyGunSpares"; + Weapon.AmmoGive1 1; + Weapon.AmmoGive2 1; + Weapon.AmmoUse2 0; + SWWMWeapon.DropAmmoType "CandyGunAmmo"; + CandyGun.ClipCount 7; + +SWWMWEAPON.NOFIRSTGIVE; + +WEAPON.EXPLOSIVE; + +WEAPON.BFG; + Radius 12; + Height 24; + } + States + { + Spawn: + XZW1 AB -1; + Stop; + Select: + XZW2 B 2 A_FullRaise(); + XZW2 CDEFGH 2; + Goto Ready; + Ready: + XZW2 A 1 + { + if ( (invoker.clipcount <= 0) && !invoker.chambered && ((invoker.Ammo1.Amount > 0) || (CountInv("CandyGunBullets") > 0)) ) player.SetPSprite(PSP_WEAPON,ResolveState("Reload")); + else if ( (invoker.clipcount > 0) && !invoker.chambered ) player.SetPSprite(PSP_WEAPON,ResolveState("Slide")); + else + { + int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; + if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) || (invoker.Ammo1.Amount > 0) || invoker.chambered ) flg |= WRF_ALLOWRELOAD; + if ( (invoker.Ammo1.Amount <= 0) && (CountInv("CandyGunBullets") <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) flg |= WRF_NOSECONDARY; + A_WeaponReady(flg); + if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) ) + invoker.CheckAmmo(EitherFire,true); + } + } + Wait; + Fire: + XZW2 A 1 A_Schutt(); + XZW2 I 1; + XZW2 J 1 + { + int layer = PSP_WEAPON+1; + while ( player.FindPSprite(layer) ) layer++; + A_Overlay(layer,"Casing"); + } + XZW2 KLMNOP 1; + XZW2 Q 2; + Goto Ready; + Casing: + XZWC A 1 + { + A_OverlayOffset(OverlayID(),0,0); + invoker.casex = FRandom[Explodium](-1.,5.); + invoker.casey = FRandom[Explodium](-2.,1.); + } + XZWC BCDEFGHIJ 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); + TNT1 A 1 A_DropCasing(); + Stop; + AltFire: + XZW2 A 2 + { + A_PlayerReload(); + return A_JumpIf(invoker.clipcount<=0,"Reload"); + } + XZW5 NO 2; + XZW5 P 1 A_StartSound("explodium/magpin",CHAN_WEAPON,CHANF_OVERLAP); + XZW5 Q 1; + XZW5 R 1 + { + if ( player.cmd.buttons&BT_ATTACK && ((((invoker.Ammo1.Amount > 0) || (CountInv("CandyGunBullets") > 0)) && (invoker.Ammo2.Amount > 0)) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) ) + player.SetPSprite(PSP_WEAPON,ResolveState("SpecialFire")); + } + XZW5 STUVWXYZ 1; + XZW6 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP); + XZW6 B 1; + XZW6 C 1 + { + A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); + invoker.special1 = invoker.clipcount; + invoker.clipcount = 0; + } + XZW6 D 1 + { + A_PlayerMelee(); + A_StartSound("explodium/throwmag",CHAN_WEAPON,CHANF_OVERLAP); + } + XZW6 EFGHIJKLMNOPRS 1; + XZW6 T 1 A_ThrowMag(); + XZW6 UV 2; + XZW6 W 2 A_PlayerReload(); + XZW6 XY 2; + XZW6 Z 4; + Goto ReloadEnd; + SpecialFire: + #### # 1; + XZWA Z 1; + XZWB ABC 1; + XZWB D 1 + { + A_PlayerMelee(); + A_StartSound("explodium/throwmag",CHAN_WEAPON,CHANF_OVERLAP); + } + XZWB EFGHIJKLMNOPQR 1; + XZWB S 1 A_ThrowGun(); + XZWB TUVWXYZ 2; + XZW1 B 0 + { + invoker.PlayUpSound(self); + if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) ) + invoker.clipcount = invoker.default.clipcount; + else if ( invoker.Ammo1.Amount <= 0 ) + { + MagAmmo sb = MagAmmo(FindInventory("CandyGunBullets")); + int takeamt = min(sb.Amount,sb.ClipSize); + invoker.clipcount = takeamt; + sb.Amount -= takeamt; + } + else + { + invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); + invoker.clipcount = invoker.default.clipcount; + } + } + Goto Select; + Reload: + XZW2 A 1 + { + if ( ((invoker.Ammo1.Amount <= 0) && (CountInv("CandyGunBullets") <= 0)) || (invoker.clipcount >= invoker.default.clipcount) ) return ResolveState("CheckBullet"); + A_PlayerReload(); + if ( invoker.clipcount <= 0 ) return ResolveState("ReloadEmpty"); + return ResolveState(null); + } + XZW2 TUVWXYZ 1; + XZW3 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP,.3); + XZW3 B 1 A_EmptyMag(); + XZW3 C 1; + XZW3 D 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 EFGH 1; + XZW3 I 1 A_DropMag(); + Goto ReloadEnd; + ReloadEmpty: + XZW2 A 1; + XZW3 JKLMNOP 1; + XZW3 Q 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP,.3); + XZW3 RS 1; + XZW3 T 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 UVWX 1; + XZW3 Y 1 A_DropMag(); + Goto ReloadEnd; + ReloadEnd: + XZW3 Z 1; + XZW4 ABCDE 1; + XZW4 F 1 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP); + XZW4 GHIJKLMNOPQ 1; + XZW4 R 1 + { + A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); + if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) ) + invoker.clipcount = invoker.default.clipcount; + else if ( invoker.Ammo1.Amount <= 0 ) + { + MagAmmo sb = MagAmmo(FindInventory("CandyGunBullets")); + int takeamt = min(sb.Amount,sb.ClipSize); + invoker.clipcount = takeamt; + sb.Amount -= takeamt; + } + else + { + invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); + invoker.clipcount = invoker.default.clipcount; + } + } + XZW4 STUV 1; + XZW2 A 1 A_JumpIf(!invoker.chambered,"Slide"); + Goto Ready; + Slide: + XZW2 A 1; + XZW4 WXY 1; + XZW5 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP,.3); + XZW5 BC 1; + XZW5 D 1 { invoker.chambered = true; invoker.clipcount--; } + XZW5 EFG 1; + XZW5 H 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP); + XZW5 IJKLM 1; + Goto Ready; + Zoom: + XZW2 A 1 + { + A_PlayerCheckGun(); + return A_Jump(256,"Zoom1","Zoom2","Zoom2"); + } + Goto Ready; + CheckBullet: + XZW2 A 1; + XZW7 ABCDE 1; + XZW7 F 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP); + XZW7 GHIJKLMNOP 1; + XZW7 Q 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP); + XZW7 RS 1; + Goto Ready; + User1: + XZW2 A 1; + XZW7 TU 1; + User1Hold: + XZW7 V 1 + { + A_PlayerMelee(true); + A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); + A_Parry(9); + } + XZW7 WX 1; + XZW7 Y 1 A_Melee(); + XZW7 Z 2; + XZW8 ABCDE 2; + XZW8 F 1 A_JumpIf(player.cmd.buttons&BT_USER1,"User1Hold"); + XZW2 B 0 { invoker.PlayUpSound(self); } + Goto Select; + Zoom1: + XZW2 A 2 A_StartSound("explodium/checkout",CHAN_WEAPON,CHANF_OVERLAP); + XZW8 GHIJKLMNOPQRSTUVWXYZ 2; + Goto Ready; + Zoom2: + XZW2 A 1 A_StartSound("explodium/speen",CHAN_WEAPON,CHANF_OVERLAP); + XZW9 ABCDEFGHIJKLMNOPQRSTUVW 1; + Goto Ready; + Deselect: + XZW2 A 2 A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP); + XZWA TUVW 2; + XZW2 B 2; + XZW2 B -1 A_FullLower(); + Stop; + Flash: + XZWZ A 2 + { + let psp = player.GetPSprite(PSP_FLASH); + psp.frame = Random[GunFlash](0,9); + let l = Spawn("CandyWeaponLight",pos); + l.target = self; + } + Stop; + } +} diff --git a/zscript/swwm_tastytreat.zsc b/zscript/weapons/swwm_tastytreat_fx.zsc similarity index 52% rename from zscript/swwm_tastytreat.zsc rename to zscript/weapons/swwm_tastytreat_fx.zsc index 7779c94ab..b602b09e4 100644 --- a/zscript/swwm_tastytreat.zsc +++ b/zscript/weapons/swwm_tastytreat_fx.zsc @@ -1,5 +1,4 @@ -// Munch Innovations "Taste the Sweetness" Candy Gun (from unreleased "Weird Weapons" UT minimod) -// Slot 9, replaces BFG9000, Firemace, Bloodscourge (stub) +// Candygun projectiles and effects Class CandyCasing : SWWMCasing {} @@ -886,571 +885,3 @@ Class CandyWeaponLight : SWWMWeaponLight args 255,64,224,150; } } - -Class CandyGun : SWWMWeapon -{ - int clipcount; - bool chambered; - double casex, casey; - transient ui TextureID WeaponBox; - transient ui Font TewiFont; - bool tospecial; - - Property ClipCount : ClipCount; - - // re-edit to allow picking up spares in coop - override bool HandlePickup( Inventory item ) - { - if ( (item.GetClass() == GetClass()) && !item.ShouldStay() ) - { - bool ammoget, spareget; - [ammoget, spareget] = CandyGun(item).PickupForAmmoAndSpares(self); - if ( ammoget || spareget ) - item.bPickupGood = true; - if ( !spareget ) - { - // sell excess - int sellprice = item.Stamina/2; - SWWMScoreObj.Spawn(sellprice,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2)); - SWWMCredits.Give(Owner.player,sellprice); - if ( Owner.player ) - Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),Owner.player.GetUserName(),GetTag(),sellprice); - item.bPickupGood = true; - } - // reset the price in case it has to respawn - item.Stamina = item.default.Stamina; - return true; - } - return Weapon.HandlePickup(item); - } - protected bool, bool PickupForAmmoAndSpares( Weapon ownedWeapon ) - { - bool gotstuff = false, gotspares = false; - int oldamount = ownedWeapon.Ammo1.Amount; - gotstuff = AddExistingAmmo(ownedWeapon.Ammo1,AmmoGive1); - gotspares = AddExistingAmmo(ownedWeapon.Ammo2,AmmoGive2); - let Owner = ownedWeapon.Owner; - if ( gotstuff && Owner && Owner.player && (oldamount == 0) ) - PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass()); - if ( ownedWeapon.Ammo1 ) - { - // subtract price of ammo we don't give - int ammonotgiven = default.AmmoGive1-AmmoGive1; - if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammonotgiven-1))); - // subtract price of given ammo - int ammogiven = ownedWeapon.Ammo1.Amount-oldamount; - if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammogiven-1))); - // drop excess - int dropme = AmmoGive1-ammogiven; - if ( dropme > 0 ) - { - // hacky, but it works - ownedWeapon.Ammo1.Amount += dropme; - ownedWeapon.Ammo1.CreateTossable(dropme); - } - } - if ( (AmmoGive1 == 0) && ((clipcount > 0) || chambered) ) - { - // we were dropped, see if we can add the bullets we contain - int bul = clipcount+chambered; - MagAmmo ma = MagAmmo(Owner.FindInventory("CandyGunBullets")); - if ( !ma ) - { - ma = MagAmmo(Spawn("CandyGunBullets")); - ma.Amount = 0; - ma.AttachToOwner(Owner); - } - int maxgiveamt = min(ma.MaxAmount-ma.Amount,bul); - int dropamt = bul-maxgiveamt; - if ( dropamt > 0 ) ma.CreateTossable(dropamt); - ma.Amount = min(ma.MaxAmount,ma.Amount+bul); - gotstuff = true; - } - return gotstuff, gotspares; - } - - override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) - { - if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/CandygunDisplay.png",TexMan.Type_Any); - if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); - Screen.DrawTexture(WeaponBox,false,bx-51,by-44,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( chambered ) Screen.DrawText(TewiFont,Font.CR_FIRE,bx-22,by-21,"⁺¹",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-19,by-14,String.Format("%d",max(clipcount,0)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - int cx = (Ammo1.Amount>9)?48:45; - int sb = Owner.CountInv("CandyGunBullets"); - if ( sb > 0 ) - { - int cbx = (sb>9)?50:47; - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cbx,by-21,String.Format("⁺%s",SWWMUtility.SuperscriptNum(sb)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-14,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - } - else Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-18,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawText(TewiFont,Font.CR_FIRE,bx-37,by-40,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - } - - override Vector3 GetTraceOffset() - { - return (10.,3,-2.); - } - - action void A_Schutt() - { - let weap = Weapon(invoker); - if ( !weap ) return; - invoker.chambered = invoker.clipcount; - invoker.clipcount = max(invoker.clipcount-1,0); - A_StartSound("candygun/fire",CHAN_WEAPON,CHANF_OVERLAP); - A_QuakeEx(5,5,5,5,0,15,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:2.); - A_ZoomFactor(.94,ZOOM_INSTANT); - A_ZoomFactor(1.); - A_SWWMFlash(); - SWWMHandler.DoFlash(self,Color(64,224,64,255),5); - A_AlertMonsters(swwm_uncapalert?0:9000); - A_PlayerFire(); - Vector3 x, y, z, x2, y2, z2; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - SWWMUtility.DoKnockback(self,-x,18000.); - Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-2*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(); - FLineTraceData d; - LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d); - SWWMBulletTrail.DoTrail(self,origin,dir,10000,2); - if ( d.HitType == TRACE_HitActor ) - { - int dmg = 300; - // might as well apply explosion on top - if ( dmg >= d.HitActor.Health ) dmg += 900; - SWWMUtility.DoKnockback(d.HitActor,d.HitDir,72000); - dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS|DMG_FOILINVUL,atan2(d.HitDir.y,d.HitDir.x)); - if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT ) - { - let p = Spawn("SWWMBulletImpact",d.HitLocation); - p.angle = atan2(d.HitDir.y,d.HitDir.x)+180; - p.pitch = asin(d.HitDir.z); - } - else - { - d.HitActor.TraceBleed(dmg,self); - d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg); - } - let b = Spawn("CandyBulletImpact",d.HitLocation-d.HitDir*4.); - b.angle = atan2(d.HitDir.y,d.HitDir.x)+180; - b.pitch = asin(d.HitDir.z); - b.target = self; - } - else if ( d.HitType != TRACE_HitNone ) - { - 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("CandyBulletImpact",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,1700,self,d.HitDir,d.HitLocation.z); - } - for ( int i=0; i<6; i++ ) - { - let s = Spawn("SWWMSmoke",origin); - s.scale *= .15; - s.alpha *= .5; - s.speed *= .2; - s.vel += vel*.5+x*FRandom[Explodium](.2,.5); - } - } - - 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,.01); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let p = Spawn("CandyMagProj",origin); - p.special1 = invoker.special1; - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed; - if ( p.waterlevel <= 0 ) p.vel.z += 5.; - p.vel += vel*.5; - } - - action void A_ThrowGun() - { - let weap = Weapon(invoker); - if ( !weap ) return; - if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) - weap.Ammo2.Amount = max(0,weap.Ammo2.Amount-1); - 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,.015); - [x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll); - Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit(); - let p = Spawn("CandyGunProj",origin); - p.special1 = invoker.clipcount+invoker.chambered; - invoker.clipcount = 0; - invoker.chambered = false; - p.target = self; - p.angle = atan2(dir.y,dir.x); - p.pitch = asin(-dir.z); - p.vel = dir*p.speed; - if ( p.waterlevel <= 0 ) p.vel.z += 5.; - p.vel += vel*.5; - } - - action void A_DropMag() - { - Vector3 x, y, z; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-2*y-10*z); - let c = Spawn("CandyMag",origin); - c.angle = angle; - c.pitch = pitch; - c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3)); - c.vel += vel*.5; - } - - action void A_EmptyMag() - { - MagAmmo ma = MagAmmo(FindInventory("CandyGunBullets")); - if ( !ma ) - { - ma = MagAmmo(Spawn("CandyGunBullets")); - ma.Amount = 0; - ma.AttachToOwner(self); - } - int maxgiveamt = min(ma.MaxAmount-ma.Amount,invoker.clipcount); - int dropamt = invoker.clipcount-maxgiveamt; - if ( dropamt > 0 ) ma.CreateTossable(dropamt); - ma.Amount = min(ma.MaxAmount,ma.Amount+invoker.clipcount); - ma.MagFill(); - if ( CheckLocalView() ) for ( int i=0; i 0 ) ma.PlayPickupSound(self); - invoker.clipcount = 0; - } - - action void A_DropCasing() - { - Vector3 x, y, z; - [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); - Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+8*y-10*z); - let c = Spawn("CandyCasing",origin); - c.special1 = special1; - c.angle = angle; - c.pitch = pitch; - c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](2,4)-(0,0,FRandom[Junk](2,3)); - c.vel += vel*.5; - } - - override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount ) - { - if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true; - if ( fireMode == PrimaryFire ) return (chambered || (clipcount > 0) || (Ammo1.Amount > 0) || (Owner.CountInv("CandyGunBullets") > 0)); - if ( fireMode == AltFire ) return ((Ammo1.Amount > 0) || (Owner.CountInv("CandyGunBullets") > 0)); - return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); - } - - override bool ReportHUDAmmo() - { - if ( chambered || (clipcount > 0) || (Owner.CountInv("CandyGunBullets") > 0) ) return true; - if ( Ammo1.Amount <= 0 ) return false; - return Super.ReportHUDAmmo(); - } - - override Inventory CreateTossable( int amt ) - { - if ( Ammo2.Amount > 0 ) - { - // drop an empty spare - let spare = Inventory(Spawn('CandyGun',Owner.Pos,NO_REPLACE)); - if ( !spare ) return null; - // spare with empty mag - CandyGun(spare).AmmoGive1 = 0; - CandyGun(spare).clipcount = 0; - CandyGun(spare).chambered = false; - spare.SetState(spare.SpawnState+1); - spare.DropTime = 30; - spare.bSpecial = spare.bSolid = false; - Ammo2.Amount--; - return spare; - } - return Super.CreateTossable(amt); - } - Default - { - Tag "$T_CANDYGUN"; - Inventory.PickupMessage "$I_CANDYGUN"; - Obituary "$O_CANDYGUN"; - Inventory.Icon "graphics/HUD/Icons/W_CandyGun.png"; - Weapon.UpSound "explodium/select"; - Weapon.SlotNumber 9; - Weapon.SelectionOrder 900; - Stamina 1000000; - Weapon.AmmoType1 "CandyGunAmmo"; - Weapon.AmmoType2 "CandyGunSpares"; - Weapon.AmmoGive1 1; - Weapon.AmmoGive2 1; - Weapon.AmmoUse2 0; - SWWMWeapon.DropAmmoType "CandyGunAmmo"; - CandyGun.ClipCount 7; - +SWWMWEAPON.NOFIRSTGIVE; - +WEAPON.EXPLOSIVE; - +WEAPON.BFG; - Radius 12; - Height 24; - } - States - { - Spawn: - XZW1 AB -1; - Stop; - Select: - XZW2 B 2 A_FullRaise(); - XZW2 CDEFGH 2; - Goto Ready; - Ready: - XZW2 A 1 - { - if ( (invoker.clipcount <= 0) && !invoker.chambered && ((invoker.Ammo1.Amount > 0) || (CountInv("CandyGunBullets") > 0)) ) player.SetPSprite(PSP_WEAPON,ResolveState("Reload")); - else if ( (invoker.clipcount > 0) && !invoker.chambered ) player.SetPSprite(PSP_WEAPON,ResolveState("Slide")); - else - { - int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1; - if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) || (invoker.Ammo1.Amount > 0) || invoker.chambered ) flg |= WRF_ALLOWRELOAD; - if ( (invoker.Ammo1.Amount <= 0) && (CountInv("CandyGunBullets") <= 0) && !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) ) flg |= WRF_NOSECONDARY; - A_WeaponReady(flg); - if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) ) - invoker.CheckAmmo(EitherFire,true); - } - } - Wait; - Fire: - XZW2 A 1 A_Schutt(); - XZW2 I 1; - XZW2 J 1 - { - int layer = PSP_WEAPON+1; - while ( player.FindPSprite(layer) ) layer++; - A_Overlay(layer,"Casing"); - } - XZW2 KLMNOP 1; - XZW2 Q 2; - Goto Ready; - Casing: - XZWC A 1 - { - A_OverlayOffset(OverlayID(),0,0); - invoker.casex = FRandom[Explodium](-1.,5.); - invoker.casey = FRandom[Explodium](-2.,1.); - } - XZWC BCDEFGHIJ 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); - TNT1 A 1 A_DropCasing(); - Stop; - AltFire: - XZW2 A 2 - { - A_PlayerReload(); - return A_JumpIf(invoker.clipcount<=0,"Reload"); - } - XZW5 NO 2; - XZW5 P 1 A_StartSound("explodium/magpin",CHAN_WEAPON,CHANF_OVERLAP); - XZW5 Q 1; - XZW5 R 1 - { - if ( player.cmd.buttons&BT_ATTACK && ((((invoker.Ammo1.Amount > 0) || (CountInv("CandyGunBullets") > 0)) && (invoker.Ammo2.Amount > 0)) || sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true)) ) - player.SetPSprite(PSP_WEAPON,ResolveState("SpecialFire")); - } - XZW5 STUVWXYZ 1; - XZW6 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP); - XZW6 B 1; - XZW6 C 1 - { - A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); - invoker.special1 = invoker.clipcount; - invoker.clipcount = 0; - } - XZW6 D 1 - { - A_PlayerMelee(); - A_StartSound("explodium/throwmag",CHAN_WEAPON,CHANF_OVERLAP); - } - XZW6 EFGHIJKLMNOPRS 1; - XZW6 T 1 A_ThrowMag(); - XZW6 UV 2; - XZW6 W 2 A_PlayerReload(); - XZW6 XY 2; - XZW6 Z 4; - Goto ReloadEnd; - SpecialFire: - #### # 1; - XZWA Z 1; - XZWB ABC 1; - XZWB D 1 - { - A_PlayerMelee(); - A_StartSound("explodium/throwmag",CHAN_WEAPON,CHANF_OVERLAP); - } - XZWB EFGHIJKLMNOPQR 1; - XZWB S 1 A_ThrowGun(); - XZWB TUVWXYZ 2; - XZW1 B 0 - { - invoker.PlayUpSound(self); - if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) ) - invoker.clipcount = invoker.default.clipcount; - else if ( invoker.Ammo1.Amount <= 0 ) - { - MagAmmo sb = MagAmmo(FindInventory("CandyGunBullets")); - int takeamt = min(sb.Amount,sb.ClipSize); - invoker.clipcount = takeamt; - sb.Amount -= takeamt; - } - else - { - invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); - invoker.clipcount = invoker.default.clipcount; - } - } - Goto Select; - Reload: - XZW2 A 1 - { - if ( ((invoker.Ammo1.Amount <= 0) && (CountInv("CandyGunBullets") <= 0)) || (invoker.clipcount >= invoker.default.clipcount) ) return ResolveState("CheckBullet"); - A_PlayerReload(); - if ( invoker.clipcount <= 0 ) return ResolveState("ReloadEmpty"); - return ResolveState(null); - } - XZW2 TUVWXYZ 1; - XZW3 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP,.3); - XZW3 B 1 A_EmptyMag(); - XZW3 C 1; - XZW3 D 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 EFGH 1; - XZW3 I 1 A_DropMag(); - Goto ReloadEnd; - ReloadEmpty: - XZW2 A 1; - XZW3 JKLMNOP 1; - XZW3 Q 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP,.3); - XZW3 RS 1; - XZW3 T 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); - XZW3 UVWX 1; - XZW3 Y 1 A_DropMag(); - Goto ReloadEnd; - ReloadEnd: - XZW3 Z 1; - XZW4 ABCDE 1; - XZW4 F 1 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP); - XZW4 GHIJKLMNOPQ 1; - XZW4 R 1 - { - A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); - if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) ) - invoker.clipcount = invoker.default.clipcount; - else if ( invoker.Ammo1.Amount <= 0 ) - { - MagAmmo sb = MagAmmo(FindInventory("CandyGunBullets")); - int takeamt = min(sb.Amount,sb.ClipSize); - invoker.clipcount = takeamt; - sb.Amount -= takeamt; - } - else - { - invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); - invoker.clipcount = invoker.default.clipcount; - } - } - XZW4 STUV 1; - XZW2 A 1 A_JumpIf(!invoker.chambered,"Slide"); - Goto Ready; - Slide: - XZW2 A 1; - XZW4 WXY 1; - XZW5 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP,.3); - XZW5 BC 1; - XZW5 D 1 { invoker.chambered = true; invoker.clipcount--; } - XZW5 EFG 1; - XZW5 H 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP); - XZW5 IJKLM 1; - Goto Ready; - Zoom: - XZW2 A 1 - { - A_PlayerCheckGun(); - return A_Jump(256,"Zoom1","Zoom2","Zoom2"); - } - Goto Ready; - CheckBullet: - XZW2 A 1; - XZW7 ABCDE 1; - XZW7 F 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP); - XZW7 GHIJKLMNOP 1; - XZW7 Q 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP); - XZW7 RS 1; - Goto Ready; - User1: - XZW2 A 1; - XZW7 TU 1; - User1Hold: - XZW7 V 1 - { - A_PlayerMelee(true); - A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); - A_Parry(9); - } - XZW7 WX 1; - XZW7 Y 1 A_Melee(); - XZW7 Z 2; - XZW8 ABCDE 2; - XZW8 F 1 A_JumpIf(player.cmd.buttons&BT_USER1,"User1Hold"); - XZW2 B 0 { invoker.PlayUpSound(self); } - Goto Select; - Zoom1: - XZW2 A 2 A_StartSound("explodium/checkout",CHAN_WEAPON,CHANF_OVERLAP); - XZW8 GHIJKLMNOPQRSTUVWXYZ 2; - Goto Ready; - Zoom2: - XZW2 A 1 A_StartSound("explodium/speen",CHAN_WEAPON,CHANF_OVERLAP); - XZW9 ABCDEFGHIJKLMNOPQRSTUVW 1; - Goto Ready; - Deselect: - XZW2 A 2 A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP); - XZWA TUVW 2; - XZW2 B 2; - XZW2 B -1 A_FullLower(); - Stop; - Flash: - XZWZ A 2 - { - let psp = player.GetPSprite(PSP_FLASH); - psp.frame = Random[GunFlash](0,9); - let l = Spawn("CandyWeaponLight",pos); - l.target = self; - } - Stop; - } -} diff --git a/zscript/swwm_thiccboolet.zsc b/zscript/weapons/swwm_thiccboolet.zsc similarity index 84% rename from zscript/swwm_thiccboolet.zsc rename to zscript/weapons/swwm_thiccboolet.zsc index 8de8c7cf2..60fbf2678 100644 --- a/zscript/swwm_thiccboolet.zsc +++ b/zscript/weapons/swwm_thiccboolet.zsc @@ -1,164 +1,6 @@ // Blackmann-Forx Silver Bullet JET (successor to Silver Bullet from Zanaveth Ultra Suite) // Slot 8, replaces Plasma Rifle, Hellstaff, Quietus (hilt) -Class SilverBulletCasing : SWWMCasing -{ - Default - { - BounceSound "silverbullet/casing"; - } -} -Class SilverBulletCasing2 : SilverBulletCasing {} - -Class SilverBulletMag : SWWMCasing -{ - Default - { - Mass 10; - BounceFactor 0.4; - WallBounceFactor 0.4; - BounceSound "silverbullet/mag"; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - heat = 0; - } - States - { - Death: - XZW1 BC -1 - { - pitch = roll = 0; - angle = FRandom[Junk](0,360); - frame = RandomPick[Junk](1,2); - } - Stop; - } -} -Class SilverBulletMag2 : SilverBulletMag {} - -Class SilverAirRip : Actor -{ - Default - { - Obituary "$O_SILVERBULLET"; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +DONTSPLASH; - +NOTELEPORT; - +FORCERADIUSDMG; - +NODAMAGETHRUST; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - SWWMUtility.DoExplosion(self,40,2000,40,ignoreme:target); - Destroy(); - } -} -Class SilverAirRip2 : Actor -{ - Default - { - Obituary "$O_SILVERBULLET2"; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +DONTSPLASH; - +NOTELEPORT; - +FORCERADIUSDMG; - +NODAMAGETHRUST; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - SWWMUtility.DoExplosion(self,20,2000,30,ignoreme:target); - Destroy(); - } -} - -Class SilverImpact : Actor -{ - Default - { - Obituary "$O_SILVERBULLET"; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +DONTSPLASH; - +NOTELEPORT; - +FORCERADIUSDMG; - +NODAMAGETHRUST; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - SWWMUtility.DoExplosion(self,100,8000,100,20); - A_AlertMonsters(swwm_uncapalert?0:2500); - A_QuakeEx(4,4,4,20,0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.9); - if ( special1 ) - { - Destroy(); - return; - } - A_StartSound("silverbullet/hit",CHAN_VOICE,CHANF_DEFAULT,1.,.7); - A_SprayDecal("BigPock",-64); - A_SprayDecal("HugeWallCrack",-64); - int numpt = Random[Silverbullet](15,25); - Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); - for ( int i=0; i 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - TNT1 A 20; - Stop; - } -} - Class WallPenetrate { Vector3 hitpos, hitdir, hitnormal, bustdir; @@ -400,249 +242,6 @@ Class FatChodeTracer : LineTracer } } -Class ExploLight3 : PaletteLight -{ - Default - { - ReactionTime 30; - Args 0,0,0,250; - } -} - -Class FatChodeRing : Actor -{ - Default - { - RenderStyle "Add"; - Scale 3.; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOBLOCKMAP; - +FORCEXYBILLBOARD; - +NOTELEPORT; - +NOINTERACTION; - } - override void Tick() - { - if ( isFrozen() ) return; - if ( !CheckNoDelay() || (tics == -1) ) return; - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XRG0 ABCDEFGHIJKLMNOPQRSTUVWX 1 Bright A_SetScale(scale.x*1.06); - Stop; - } -} - -Class FatChodeImpact : Actor -{ - double realangle, realpitch; - - Default - { - Obituary "$O_SILVERBULLET2"; - DamageType 'Explodium'; - RenderStyle "Add"; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +DONTSPLASH; - +NOBLOCKMAP; - +FORCEXYBILLBOARD; - +FORCERADIUSDMG; - +NODAMAGETHRUST; - +NOTELEPORT; - +NOINTERACTION; - Scale 4.5; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - SWWMUtility.DoExplosion(self,600,40000,250,120,DE_THRUWALLS|DE_EXTRAZTHRUST); - A_AlertMonsters(swwm_uncapalert?0:8000); - A_QuakeEx(7,7,7,50,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:800,rollIntensity:1.); - A_StartSound("silverbullet/chode",CHAN_VOICE,CHANF_DEFAULT,1.,.35); - A_SprayDecal("BigPock",-64); - A_SprayDecal("HugeWallCrack",-64); - A_SprayDecal("WumboRocketBlast",-64); - Scale *= FRandom[ExploS](0.8,1.1); - Scale.x *= RandomPick[ExploS](-1,1); - Scale.y *= RandomPick[ExploS](-1,1); - int numpt = Random[Silverbullet](15,25); - Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); - for ( int i=0; i 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 1 Bright; - Stop; - } -} - -Class FatChodeExtraArm : ExplodiumMagArm -{ - Default - { - Obituary "$O_SILVERBULLET2"; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - vel *= 1.5; - } -} - -Class FatChodeExplosionTrail : Actor -{ - Default - { - RenderStyle "Add"; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +DONTSPLASH; - +NOTELEPORT; - +NOBLOCKMAP; - +FORCEXYBILLBOARD; - +NOTELEPORT; - +NOINTERACTION; - Scale 3.; - } - override void Tick() - { - if ( isFrozen() ) return; - if ( !CheckNoDelay() || (tics == -1) ) return; - if ( tics > 0 ) tics--; - while ( !tics ) - { - if ( !SetState(CurState.NextState) ) - return; - } - } - States - { - Spawn: - XEX1 ACEGIKMOQSUWY[ 1 Bright; - Stop; - } -} - -Class FatChodeExplosionArm : Actor -{ - Default - { - Obituary "$O_SILVERBULLET2"; - DamageType 'Explodium'; - Radius 0.1; - Height 0; - +NOGRAVITY; - +NOCLIP; - +DONTSPLASH; - +NOTELEPORT; - +NOBLOCKMAP; - +FORCERADIUSDMG; - +NODAMAGETHRUST; - +NOINTERACTION; - } - override void PostBeginPlay() - { - Super.PostBeginPlay(); - reactiontime = Random[ExploS](15,20); - vel = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*FRandom[ExploS](20.,30.); - } - States - { - Spawn: - TNT1 A 1 - { - if ( !(ReactionTime%2) ) - SWWMUtility.DoExplosion(self,10+reactiontime,8000+1500*reactiontime,80+5*reactiontime,50,DE_THRUWALLS); - if ( level.IsPointInLevel(pos) ) - { - A_SprayDecal("HugeRocketBlast",-32); - Spawn("FatChodeExplosionTrail",pos); - 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; - s.SetShade(Color(1,1,1)*Random[ExploS](64,224)); - s.special1 = Random[ExploS](1,4); - s.scale *= 2.4; - s.alpha *= .1+.4*(ReactionTime/15.); - } - A_CountDown(); - } - Wait; - } -} - Class SilverBullet : SWWMWeapon { bool chambered, fired; diff --git a/zscript/weapons/swwm_thiccboolet_fx.zsc b/zscript/weapons/swwm_thiccboolet_fx.zsc new file mode 100644 index 000000000..1d59f96f0 --- /dev/null +++ b/zscript/weapons/swwm_thiccboolet_fx.zsc @@ -0,0 +1,402 @@ +// Silver Bullet projectiles and effects + +Class SilverBulletCasing : SWWMCasing +{ + Default + { + BounceSound "silverbullet/casing"; + } +} +Class SilverBulletCasing2 : SilverBulletCasing {} + +Class SilverBulletMag : SWWMCasing +{ + Default + { + Mass 10; + BounceFactor 0.4; + WallBounceFactor 0.4; + BounceSound "silverbullet/mag"; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + heat = 0; + } + States + { + Death: + XZW1 BC -1 + { + pitch = roll = 0; + angle = FRandom[Junk](0,360); + frame = RandomPick[Junk](1,2); + } + Stop; + } +} +Class SilverBulletMag2 : SilverBulletMag {} + +Class SilverAirRip : Actor +{ + Default + { + Obituary "$O_SILVERBULLET"; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + SWWMUtility.DoExplosion(self,40,2000,40,ignoreme:target); + Destroy(); + } +} +Class SilverAirRip2 : Actor +{ + Default + { + Obituary "$O_SILVERBULLET2"; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + SWWMUtility.DoExplosion(self,20,2000,30,ignoreme:target); + Destroy(); + } +} + +Class SilverImpact : Actor +{ + Default + { + Obituary "$O_SILVERBULLET"; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + SWWMUtility.DoExplosion(self,100,8000,100,20); + A_AlertMonsters(swwm_uncapalert?0:2500); + A_QuakeEx(4,4,4,20,0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.9); + if ( special1 ) + { + Destroy(); + return; + } + A_StartSound("silverbullet/hit",CHAN_VOICE,CHANF_DEFAULT,1.,.7); + A_SprayDecal("BigPock",-64); + A_SprayDecal("HugeWallCrack",-64); + int numpt = Random[Silverbullet](15,25); + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + for ( int i=0; i 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + TNT1 A 20; + Stop; + } +} + +Class ExploLight3 : PaletteLight +{ + Default + { + ReactionTime 30; + Args 0,0,0,250; + } +} + +Class FatChodeRing : Actor +{ + Default + { + RenderStyle "Add"; + Scale 3.; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOBLOCKMAP; + +FORCEXYBILLBOARD; + +NOTELEPORT; + +NOINTERACTION; + } + override void Tick() + { + if ( isFrozen() ) return; + if ( !CheckNoDelay() || (tics == -1) ) return; + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XRG0 ABCDEFGHIJKLMNOPQRSTUVWX 1 Bright A_SetScale(scale.x*1.06); + Stop; + } +} + +Class FatChodeImpact : Actor +{ + double realangle, realpitch; + + Default + { + Obituary "$O_SILVERBULLET2"; + DamageType 'Explodium'; + RenderStyle "Add"; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOBLOCKMAP; + +FORCEXYBILLBOARD; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + +NOTELEPORT; + +NOINTERACTION; + Scale 4.5; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + SWWMUtility.DoExplosion(self,600,40000,250,120,DE_THRUWALLS|DE_EXTRAZTHRUST); + A_AlertMonsters(swwm_uncapalert?0:8000); + A_QuakeEx(7,7,7,50,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:800,rollIntensity:1.); + A_StartSound("silverbullet/chode",CHAN_VOICE,CHANF_DEFAULT,1.,.35); + A_SprayDecal("BigPock",-64); + A_SprayDecal("HugeWallCrack",-64); + A_SprayDecal("WumboRocketBlast",-64); + Scale *= FRandom[ExploS](0.8,1.1); + Scale.x *= RandomPick[ExploS](-1,1); + Scale.y *= RandomPick[ExploS](-1,1); + int numpt = Random[Silverbullet](15,25); + Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch)); + for ( int i=0; i 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ 1 Bright; + Stop; + } +} + +Class FatChodeExtraArm : ExplodiumMagArm +{ + Default + { + Obituary "$O_SILVERBULLET2"; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + vel *= 1.5; + } +} + +Class FatChodeExplosionTrail : Actor +{ + Default + { + RenderStyle "Add"; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + +NOBLOCKMAP; + +FORCEXYBILLBOARD; + +NOTELEPORT; + +NOINTERACTION; + Scale 3.; + } + override void Tick() + { + if ( isFrozen() ) return; + if ( !CheckNoDelay() || (tics == -1) ) return; + if ( tics > 0 ) tics--; + while ( !tics ) + { + if ( !SetState(CurState.NextState) ) + return; + } + } + States + { + Spawn: + XEX1 ACEGIKMOQSUWY[ 1 Bright; + Stop; + } +} + +Class FatChodeExplosionArm : Actor +{ + Default + { + Obituary "$O_SILVERBULLET2"; + DamageType 'Explodium'; + Radius 0.1; + Height 0; + +NOGRAVITY; + +NOCLIP; + +DONTSPLASH; + +NOTELEPORT; + +NOBLOCKMAP; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + +NOINTERACTION; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + reactiontime = Random[ExploS](15,20); + vel = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*FRandom[ExploS](20.,30.); + } + States + { + Spawn: + TNT1 A 1 + { + if ( !(ReactionTime%2) ) + SWWMUtility.DoExplosion(self,10+reactiontime,8000+1500*reactiontime,80+5*reactiontime,50,DE_THRUWALLS); + if ( level.IsPointInLevel(pos) ) + { + A_SprayDecal("HugeRocketBlast",-32); + Spawn("FatChodeExplosionTrail",pos); + 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; + s.SetShade(Color(1,1,1)*Random[ExploS](64,224)); + s.special1 = Random[ExploS](1,4); + s.scale *= 2.4; + s.alpha *= .1+.4*(ReactionTime/15.); + } + A_CountDown(); + } + Wait; + } +}