Major code refactoring. SWWMHandler could still use some more, though.
This commit is contained in:
parent
0eec40e723
commit
c5abe83831
94 changed files with 17859 additions and 17678 deletions
|
|
@ -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-";
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
124
zscript.txt
124
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"
|
||||
|
|
|
|||
1
zscript/dlc1/swwm_blackfire_fx.zsc
Normal file
1
zscript/dlc1/swwm_blackfire_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Blackfire Igniter projectiles and effects
|
||||
1
zscript/dlc1/swwm_blastin_fx.zsc
Normal file
1
zscript/dlc1/swwm_blastin_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Plasma Blast projectiles and effects
|
||||
1
zscript/dlc1/swwm_hammertime_fx.zsc
Normal file
1
zscript/dlc1/swwm_hammertime_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Itamex Hammer projectiles and effects
|
||||
1
zscript/dlc1/swwm_heavymahsheengun_fx.zsc
Normal file
1
zscript/dlc1/swwm_heavymahsheengun_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Sheen HMG projectiles and effects
|
||||
1
zscript/dlc1/swwm_hugeassrailgun_fx.zsc
Normal file
1
zscript/dlc1/swwm_hugeassrailgun_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Grand Lance projectiles and effects
|
||||
1
zscript/dlc1/swwm_notashotgun_fx.zsc
Normal file
1
zscript/dlc1/swwm_notashotgun_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Quadravol projectiles and effects
|
||||
1
zscript/dlc1/swwm_rebolber_fx.zsc
Normal file
1
zscript/dlc1/swwm_rebolber_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Puntzer Beta projectiles and effects
|
||||
1
zscript/dlc1/swwm_supermarioworld_fx.zsc
Normal file
1
zscript/dlc1/swwm_supermarioworld_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Puntzer Gamma projectiles and effects
|
||||
1
zscript/dlc1/swwm_thiccbolts_fx.zsc
Normal file
1
zscript/dlc1/swwm_thiccbolts_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Ray-Khom projectiles and effects
|
||||
1
zscript/dlc1/swwm_veryveryfrightening_fx.zsc
Normal file
1
zscript/dlc1/swwm_veryveryfrightening_fx.zsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
// EMP Rail Carbine projectiles and effects
|
||||
428
zscript/handler/swwm_handler_cheats.zsc
Normal file
428
zscript/handler/swwm_handler_cheats.zsc
Normal file
|
|
@ -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<Wads.GetNumLumps(); l++ )
|
||||
{
|
||||
String fn = Wads.GetLumpFullName(l);
|
||||
if ( fn.Left(13) != "lore/default/" ) continue;
|
||||
int ext = fn.IndexOf(".txt");
|
||||
if ( ext != fn.Length()-4 ) continue;
|
||||
SWWMLoreLibrary.Add(players[e.Args[0]],fn.Mid(13,ext-13));
|
||||
}
|
||||
}
|
||||
else if ( e.Name ~== "swwmsafecheat" )
|
||||
{
|
||||
if ( consoleplayer == e.Args[0] )
|
||||
{
|
||||
Console.Printf("\cyStay out of trouble.\c-");
|
||||
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
|
||||
}
|
||||
Vector3 safepos;
|
||||
double safeangle;
|
||||
[safepos, safeangle] = level.PickPlayerStart(e.Args[0]);
|
||||
players[e.Args[0]].mo.Teleport(safepos,safeangle,TF_TELEFRAG|TF_FORCED|TF_USESPOTZ);
|
||||
}
|
||||
else if ( e.Name ~== "swwmweaponcheat" )
|
||||
{
|
||||
if ( consoleplayer == e.Args[0] )
|
||||
{
|
||||
Console.Printf("\cyYou better be happy now\c-");
|
||||
S_StartSound("menu/buyinv",CHAN_ITEM,CHANF_UI);
|
||||
S_StartSound("misc/w_pkup",CHAN_VOICE,CHANF_UI);
|
||||
}
|
||||
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
||||
{
|
||||
let w = (Class<SWWMWeapon>)(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<insert amiga boing ball here>\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<cht.Size(); i++ )
|
||||
{
|
||||
if ( kstr != cht[i].Left(kstr.length()) ) continue;
|
||||
matchany = true;
|
||||
if ( kstr != cht[i] ) continue;
|
||||
if ( SWWMUtility.CheatsDisabled(consoleplayer) )
|
||||
{
|
||||
kfail = true;
|
||||
klinger = gametic+40;
|
||||
}
|
||||
else
|
||||
{
|
||||
kfail = false;
|
||||
klinger = gametic+60;
|
||||
SendNetworkEvent(cmd[i],consoleplayer);
|
||||
}
|
||||
klingerstr = kstr;
|
||||
kcode = 0;
|
||||
kstr = "";
|
||||
return true;
|
||||
}
|
||||
if ( !matchany )
|
||||
{
|
||||
bool eatit = false;
|
||||
if ( kcode >= 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<tlen; i++ )
|
||||
{
|
||||
int ch;
|
||||
[ch, pos] = chstr.GetNextCodePoint(pos);
|
||||
if ( col == 0 ) Screen.DrawChar(fnt,Font.CR_DARKGRAY,xx+RandomShiver(),yy+RandomShiver(),ch,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||||
else if ( col == 1 ) Screen.DrawChar(fnt,Font.CR_SAPPHIRE,xx,yy,ch,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ColorOverlay,Color(int(shine*255),255,255,255));
|
||||
else if ( col == 2 ) Screen.DrawChar(fnt,Font.CR_RED,xx,yy+RandomFall()*(1.-alph),ch,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
|
||||
xx += newsmallfont.GetCharWidth(ch)+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
zscript/handler/swwm_handler_flash.zsc
Normal file
70
zscript/handler/swwm_handler_flash.zsc
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// screen flashes
|
||||
|
||||
extend Class SWWMHandler
|
||||
{
|
||||
transient Array<QueuedFlash> 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<flashes.size(); i++ )
|
||||
{
|
||||
if ( flashes[i].tic >= gametic ) continue;
|
||||
flashes.Delete(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
private ui void FlashUITick()
|
||||
{
|
||||
for ( int i=0; i<flashes.size(); i++ )
|
||||
{
|
||||
if ( flashes[i].tic < gametic ) continue;
|
||||
GenericFlash gf = new("GenericFlash").Setup(flashes[i].cam,flashes[i].c,flashes[i].duration);
|
||||
StatusBar.AttachMessage(gf,0,BaseStatusBar.HUDMSGLayer_UnderHUD);
|
||||
}
|
||||
}
|
||||
private ui void FlashRender( RenderEvent e )
|
||||
{
|
||||
int camplayer = players[consoleplayer].Camera.PlayerNumber();
|
||||
if ( camplayer == -1 ) return;
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
82
zscript/handler/swwm_handler_iwantdie.zsc
Normal file
82
zscript/handler/swwm_handler_iwantdie.zsc
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// I WANT DIE
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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<numpt; i++ )
|
||||
{
|
||||
double a = FRandom[ExtraMissiles](0,360);
|
||||
double s = FRandom[ExtraMissiles](0,.1);
|
||||
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
||||
let p = Actor.Spawn(e.Thing.GetClass(),e.Thing.pos);
|
||||
p.GiveInventory("DontDuplicate",1);
|
||||
p.target = e.Thing.target;
|
||||
p.tracer = e.Thing.tracer;
|
||||
p.master = e.Thing.master;
|
||||
p.speed *= FRandom[ExtraMissiles](1.,3.);
|
||||
p.vel = dir*p.speed;
|
||||
p.angle = atan2(dir.y,dir.x);
|
||||
p.pitch = asin(-dir.z);
|
||||
p.roll = e.Thing.roll;
|
||||
}
|
||||
}
|
||||
if ( e.Thing.bISMONSTER && !(e.Thing is 'PlayerPawn') )
|
||||
{
|
||||
e.Thing.GiveInventory("HOLYCOWIMTOTALLYGOINGSOFASTOHFUCK",1);
|
||||
// random chance to spawn doubles
|
||||
if ( !e.Thing.FindInventory("DontDuplicate") && !Random[ExtraMissiles](0,2) )
|
||||
{
|
||||
int numpt = Random[ExtraMissiles](1,2);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
// three attempts for each
|
||||
for ( int j=0; j<3; j++ )
|
||||
{
|
||||
let x = Actor.Spawn(e.Thing.GetClass(),e.Thing.Vec3Angle(e.Thing.Radius*FRandom[ExtraMissiles](1.5,4.),FRandom[ExtraMissiles](0,360)));
|
||||
if ( x.pos.z+x.height > 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
zscript/handler/swwm_handler_oneliners.zsc
Normal file
82
zscript/handler/swwm_handler_oneliners.zsc
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// oneliner handling
|
||||
|
||||
extend Class SWWMHandler
|
||||
{
|
||||
transient String oneliner, onelinersnd;
|
||||
transient int onelinertic, onelinerspan, onelinerlevel;
|
||||
transient Array<LastLine> 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<hnd.lastlines.Size(); i++ )
|
||||
{
|
||||
if ( hnd.lastlines[i].type != type ) continue;
|
||||
last = hnd.lastlines[i].lineno;
|
||||
ent = i;
|
||||
break;
|
||||
}
|
||||
if ( countem == 1 ) whichline = 1;
|
||||
else if ( last > 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);
|
||||
}
|
||||
}
|
||||
144
zscript/handler/swwm_handler_playerevents.zsc
Normal file
144
zscript/handler/swwm_handler_playerevents.zsc
Normal file
|
|
@ -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<PlayerClasses.Size(); i++ )
|
||||
{
|
||||
if ( !(p.mo is PlayerClasses[i].Type) ) continue;
|
||||
// perform a hotswap, code adapted from my .flow player morph in spooktober
|
||||
let n = PlayerPawn(Actor.Spawn("Demolitionist",p.mo.pos));
|
||||
n.player = p;
|
||||
n.angle = p.mo.angle;
|
||||
n.pitch = p.mo.pitch;
|
||||
p.camera = n;
|
||||
p.mo.Destroy();
|
||||
p.mo = n;
|
||||
n.GiveDefaultInventory();
|
||||
let e = Weapon(n.FindInventory("ExplodiumGun"));
|
||||
if ( e )
|
||||
{
|
||||
p.ReadyWeapon = null;
|
||||
p.PendingWeapon = e;
|
||||
n.BringUpWeapon();
|
||||
}
|
||||
// warn if strict KEYCONF is enabled
|
||||
if ( setslotstrict && (p == players[consoleplayer]) )
|
||||
slotstrictwarn = gametic+300;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// create some static thinkers for this player if needed
|
||||
SWWMTradeHistory th = SWWMTradeHistory.Find(p);
|
||||
if ( !th )
|
||||
{
|
||||
th = new("SWWMTradeHistory");
|
||||
th.ChangeStatNum(Thinker.STAT_STATIC);
|
||||
th.myplayer = p;
|
||||
}
|
||||
SWWMCredits c = SWWMCredits.Find(p);
|
||||
if ( !c )
|
||||
{
|
||||
c = new("SWWMCredits");
|
||||
c.ChangeStatNum(Thinker.STAT_STATIC);
|
||||
c.myplayer = p;
|
||||
}
|
||||
SWWMLoreLibrary l = SWWMLoreLibrary.Find(p);
|
||||
if ( !l )
|
||||
{
|
||||
l = new("SWWMLoreLibrary");
|
||||
l.ChangeStatNum(Thinker.STAT_STATIC);
|
||||
l.myplayer = p;
|
||||
}
|
||||
// pre-add some entries to start with
|
||||
l.DirectAdd("Demolitionist");
|
||||
l.DirectAdd("KnowledgeBase");
|
||||
l.DirectAdd("Saya");
|
||||
l.DirectAdd("UAC");
|
||||
if ( SWWMUtility.IsEviternity() )
|
||||
{
|
||||
l.DirectAdd("Gods");
|
||||
l.DirectAdd("SUSAN");
|
||||
}
|
||||
if ( gameinfo.gametype&(GAME_Raven|GAME_Strife) )
|
||||
{
|
||||
l.DirectAdd("Parthoris");
|
||||
l.DirectAdd("SerpentRiders");
|
||||
l.DirectAdd("Sidhe");
|
||||
}
|
||||
if ( gameinfo.gametype&(GAME_Hexen|GAME_Strife) )
|
||||
l.DirectAdd("Cronos");
|
||||
if ( gameinfo.gametype&GAME_Strife )
|
||||
{
|
||||
l.DirectAdd("TheOrder");
|
||||
l.DirectAdd("TheFront");
|
||||
}
|
||||
// starting weapons (if owned)
|
||||
if ( p.mo.FindInventory('DeepImpact') )
|
||||
l.DirectAdd("DeepImpact");
|
||||
if ( p.mo.FindInventory('ExplodiumGun') )
|
||||
l.DirectAdd("ExplodiumGun");
|
||||
SWWMStats s = SWWMStats.Find(p);
|
||||
if ( !s )
|
||||
{
|
||||
s = new("SWWMStats");
|
||||
s.ChangeStatNum(Thinker.STAT_STATIC);
|
||||
s.myplayer = p;
|
||||
s.lastspawn = level.totaltime;
|
||||
s.favweapon = -1;
|
||||
for ( Inventory i=p.mo.Inv; i; i=i.inv )
|
||||
{
|
||||
if ( i is 'Weapon' )
|
||||
s.GotWeapon(Weapon(i).GetClass());
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
// reset score (optional) if inventory should be cleared
|
||||
if ( swwm_resetscore && level.removeitems && !e.IsReturn )
|
||||
c.credits = c.hcredits = 0;
|
||||
// re-add any missing collectibles after a death exit (yes, this happens)
|
||||
for ( int i=0; i<s.ownedcollectibles.Size(); i++ )
|
||||
{
|
||||
if ( p.mo.FindInventory(s.ownedcollectibles[i]) ) continue;
|
||||
let c = SWWMCollectible(Actor.Spawn(s.ownedcollectibles[i],p.mo.pos));
|
||||
c.propagated = true;
|
||||
if ( !c.CallTryPickup(p.mo) )
|
||||
c.Destroy();
|
||||
}
|
||||
// reset inventory (including unclearables) on forced pistol starts (must have visited at least one map, though)
|
||||
// known bug: the previous weapon will play its select sound regardless, this is ENTIRELY IMPOSSIBLE to fix
|
||||
if ( p.mo.FindInventory("InventoryWipeToken") || (swwm_pistolstart && (s.lstats.Size() > 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);
|
||||
}
|
||||
}
|
||||
602
zscript/handler/swwm_handler_process.zsc
Normal file
602
zscript/handler/swwm_handler_process.zsc
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
// event processing
|
||||
extend Class SWWMHandler
|
||||
{
|
||||
// for menu events
|
||||
transient Array<MenuTransaction> 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<Ammo> 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<MAXPLAYERS; i++ )
|
||||
{
|
||||
if ( !playeringame[i] || !players[i].mo ) continue;
|
||||
let mo = players[i].mo;
|
||||
Inventory hams = mo.FindInventory("HammerspaceEmbiggener");
|
||||
if ( hams )
|
||||
{
|
||||
if ( hams.MaxAmount != hams.default.MaxAmount )
|
||||
Console.Printf("Adjust %s capacity (%d -> %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<dropme; j++ ) inv.CreateTossable(j);
|
||||
}
|
||||
}
|
||||
inv.MaxAmount = inv.default.MaxAmount;
|
||||
inv.Amount = min(inv.Amount,inv.MaxAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( e.Name ~== "swwmupdatetrackers" )
|
||||
{
|
||||
if ( multiplayer && (e.player != Net_Arbitrator) )
|
||||
{
|
||||
if ( e.player == consoleplayer )
|
||||
Console.Printf("Only the net arbitrator can call this event.");
|
||||
return;
|
||||
}
|
||||
if ( swwm_notrack )
|
||||
{
|
||||
int n = trackers_cnt;
|
||||
while ( trackers ) trackers.Destroy(); // wow that's simple, all in one line
|
||||
Console.Printf("%d trackers removed.",n);
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = trackers_cnt;
|
||||
let ti = ThinkerIterator.Create("Actor");
|
||||
Actor a;
|
||||
while ( a = Actor(ti.Next()) )
|
||||
{
|
||||
if ( (!a.bSHOOTABLE && !a.bISMONSTER) || (a is 'LampMoth') || (a is 'CompanionLamp') ) continue;
|
||||
let trk = SWWMCombatTracker.Spawn(a);
|
||||
if ( !a.player ) trk.maxhealth = max(a.health,a.GetSpawnHealth());
|
||||
}
|
||||
n = (trackers_cnt-n);
|
||||
Console.Printf("%d trackers added.",n);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ( e.Name ~== "swwmtrimsuckables" )
|
||||
{
|
||||
if ( multiplayer && (e.player != Net_Arbitrator) )
|
||||
{
|
||||
if ( e.player == consoleplayer )
|
||||
Console.Printf("Only the net arbitrator can call this event.");
|
||||
return;
|
||||
}
|
||||
int n = 0;
|
||||
for ( int i=0; i<suckableactors.Size(); i++ )
|
||||
{
|
||||
if ( suckableactors[i] && (suckableactors[i].bSHOOTABLE || suckableactors[i].bMISSILE) ) continue;
|
||||
suckableactors.Delete(i);
|
||||
i--;
|
||||
n++;
|
||||
}
|
||||
Console.Printf("%d suckable actors trimmed.",n);
|
||||
return;
|
||||
}
|
||||
else if ( e.Name ~== "swwmdebugdumprng" )
|
||||
{
|
||||
if ( multiplayer && (e.player != Net_Arbitrator) )
|
||||
{
|
||||
if ( e.player == consoleplayer )
|
||||
Console.Printf("Only the net arbitrator can call this event.");
|
||||
return;
|
||||
}
|
||||
// dump the values of all mod RNGs (might help someday to track down what desyncs)
|
||||
Console.Printf("\cxSWWM GZ RNG dump for player %d (\c-%s\cx):\c-",consoleplayer,players[consoleplayer].GetUserName());
|
||||
Console.Printf("bdscreen: %d",Random2[bdscreen]());
|
||||
Console.Printf("Blood: %d",Random2[Blood]());
|
||||
Console.Printf("Boolet: %d",Random2[Boolet]());
|
||||
Console.Printf("BrainExplode: %d",Random2[BrainExplode]());
|
||||
Console.Printf("Bundle: %d",Random2[Bundle]());
|
||||
Console.Printf("Candy: %d",Random2[Candy]());
|
||||
Console.Printf("Chancebox: %d",Random2[Chancebox]());
|
||||
Console.Printf("ClientSparkles: %d",Random2[ClientSparkles]());
|
||||
Console.Printf("Corrode: %d",Random2[Corrode]());
|
||||
Console.Printf("DemoLines: %d",Random2[DemoLines]());
|
||||
Console.Printf("DoBlast: %d",Random2[DoBlast]());
|
||||
Console.Printf("Eviscerator: %d",Random2[Eviscerator]());
|
||||
Console.Printf("Explodium: %d",Random2[Explodium]());
|
||||
Console.Printf("Explos: %d",Random2[Explos]());
|
||||
Console.Printf("ExtraMissiles: %d",Random2[ExtraMissiles]());
|
||||
Console.Printf("FInTheChat: %d",Random2[FInTheChat]());
|
||||
Console.Printf("FlameT: %d",Random2[FlameT]());
|
||||
Console.Printf("Flicker: %d",Random2[Flicker]());
|
||||
Console.Printf("FunTags: %d",Random2[FunTags]());
|
||||
Console.Printf("Gibs: %d",Random2[Gibs]());
|
||||
Console.Printf("GoldDrop: %d",Random2[GoldDrop]());
|
||||
Console.Printf("Goldy: %d",Random2[Goldy]());
|
||||
Console.Printf("GunFlash: %d",Random2[GunFlash]());
|
||||
Console.Printf("hdscreen: %d",Random2[hdscreen]());
|
||||
Console.Printf("Hellblazer: %d",Random2[Hellblazer]());
|
||||
Console.Printf("HudStuff: %d",Random2[HudStuff]());
|
||||
Console.Printf("Impact: %d",Random2[Impact]());
|
||||
Console.Printf("InterArt: %d",Random2[InterArt]());
|
||||
Console.Printf("Invinciball: %d",Random2[Invinciball]());
|
||||
Console.Printf("Junk: %d",Random2[Junk]());
|
||||
Console.Printf("Moth: %d",Random2[Moth]());
|
||||
Console.Printf("Nugget: %d",Random2[Nugget]());
|
||||
Console.Printf("Parry: %d",Random2[Parry]());
|
||||
Console.Printf("Ponch: %d",Random2[Ponch]());
|
||||
Console.Printf("Puff: %d",Random2[Puff]());
|
||||
Console.Printf("Pusher: %d",Random2[Pusher]());
|
||||
Console.Printf("Rage: %d",Random2[Rage]());
|
||||
Console.Printf("Replacements: %d",Random2[Replacements]());
|
||||
Console.Printf("ScoreBits: %d",Random2[ScoreBits]());
|
||||
Console.Printf("ShellDrop: %d",Random2[ShellDrop]());
|
||||
Console.Printf("Shivers: %d",Random2[Shivers]());
|
||||
Console.Printf("Silverbullet: %d",Random2[Silverbullet]());
|
||||
Console.Printf("SpareShells: %d",Random2[SpareShells]());
|
||||
Console.Printf("Sparkster: %d",Random2[Sparkster]());
|
||||
Console.Printf("Spread: %d",Random2[Spread]());
|
||||
Console.Printf("Spreadgun: %d",Random2[Spreadgun]());
|
||||
Console.Printf("TUID: %d",Random2[TUID]());
|
||||
Console.Printf("Wallbuster: %d",Random2[Wallbuster]());
|
||||
Console.Printf("WallbusterMenu: %d",Random2[WallbusterMenu]());
|
||||
Console.Printf("Ynykron: %d",Random2[Ynykron]());
|
||||
return;
|
||||
}
|
||||
if ( e.IsManual ) return;
|
||||
if ( e.Name.Left(14) ~== "swwmstoregive." )
|
||||
{
|
||||
Class<Inventory> 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<ngun; i++ )
|
||||
{
|
||||
let d = players[e.Args[0]].mo.DropInventory(sw);
|
||||
if ( !d || (ngun <= 1) ) continue;
|
||||
// adjust angle for multi-drops
|
||||
d.angle = players[e.Args[0]].mo.angle+ang;
|
||||
d.vel.xy = Actor.RotateVector((5,0),d.angle);
|
||||
d.vel.z = 1;
|
||||
d.vel += players[e.Args[0]].mo.vel;
|
||||
ang += 30;
|
||||
}
|
||||
// don't autoswitch just yet (hacky)
|
||||
if ( swapto )
|
||||
{
|
||||
players[e.Args[0]].ReadyWeapon = null;
|
||||
players[e.Args[0]].PendingWeapon = WP_NOCHANGE;
|
||||
}
|
||||
}
|
||||
if ( (item is 'ArmorNuggetItem') || (item is 'HealthNuggetItem') )
|
||||
{
|
||||
// these have to be given in a loop because fun reasons
|
||||
for ( int i=0; i<e.Args[2]; i++ )
|
||||
players[e.Args[0]].mo.GiveInventory(item,1,true);
|
||||
}
|
||||
else players[e.Args[0]].mo.GiveInventory(item,e.Args[2],true);
|
||||
// fucky workaround
|
||||
let inv = players[e.Args[0]].mo.FindInventory(item);
|
||||
if ( inv && (inv.Amount <= 0) && !inv.bKEEPDEPLETED ) inv.Destroy();
|
||||
if ( item is 'Weapon' )
|
||||
{
|
||||
// special case, select dual guns if we bought a second one
|
||||
if ( (item is 'ExplodiumGun') && (players[e.Args[0]].mo.CountInv("ExplodiumGun") > 1) )
|
||||
players[e.Args[0]].mo.A_SelectWeapon("DualExplodiumGun");
|
||||
else players[e.Args[0]].mo.A_SelectWeapon((Class<Weapon>)(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( e.Name.Left(14) ~== "swwmstoretake." )
|
||||
{
|
||||
Class<Inventory> 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<Inventory> 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.Size(); i++ )
|
||||
{
|
||||
let a = (Class<Ammo>)(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<Inventory> 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<Inventory> 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<Weapon>)(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<Inventory> 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.Size(); i++ )
|
||||
{
|
||||
let a = (Class<Ammo>)(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<String> qs;
|
||||
qs.Clear();
|
||||
String rite = e.Name.Mid(8);
|
||||
rite.Split(qs,",",TOK_SKIPEMPTY);
|
||||
for ( int i=0; i<qs.Size(); i++ )
|
||||
{
|
||||
int qi = qs[i].ToInt();
|
||||
if ( (qi < 0) || (qi > 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<checklist.Size(); i++ )
|
||||
{
|
||||
if ( checklist[i].uid != e.Args[0] ) continue;
|
||||
checklist.Delete(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
else if ( e.Name ~== "swwmclearalltransactions" )
|
||||
{
|
||||
if ( e.Args[0] != consoleplayer ) return;
|
||||
checklist.Clear();
|
||||
}
|
||||
// cheats go here
|
||||
else CheatEvent(e);
|
||||
}
|
||||
|
||||
override bool InputProcess( InputEvent e )
|
||||
{
|
||||
if ( (e.Type == InputEvent.TYPE_KeyDown) && (e.KeyChar >= 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<numf; i++ )
|
||||
{
|
||||
let f = PayRespects.PressF();
|
||||
StatusBar.AttachMessage(f,0,layer:StatusBar.HUDMSGLayer_OverHUD);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( CheatInput(e) ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
195
zscript/handler/swwm_handler_queues.zsc
Normal file
195
zscript/handler/swwm_handler_queues.zsc
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
// various actor budget queues
|
||||
|
||||
extend Class SWWMHandler
|
||||
{
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
536
zscript/handler/swwm_handler_replacements.zsc
Normal file
536
zscript/handler/swwm_handler_replacements.zsc
Normal file
|
|
@ -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<Actor> GetDRLAReplacement( Class<Actor> 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<Actor> redpool[] = {"RedShell","RedShell2","RedShell4"};
|
||||
static const Class<Actor> greenpool[] = {"GreenShell","GreenShell2","GreenShell4"};
|
||||
static const Class<Actor> whitepool[] = {"WhiteShell","WhiteShell2"};
|
||||
static const Class<Actor> purplepool[] = {"PurpleShell","PurpleShell2","PurpleShell4"};
|
||||
static const Class<Actor> bluepool[] = {"BlueShell","BlueShell2","BlueShell4"};
|
||||
static const Class<Actor> 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;
|
||||
}
|
||||
}
|
||||
146
zscript/handler/swwm_handler_shaders.zsc
Normal file
146
zscript/handler/swwm_handler_shaders.zsc
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
399
zscript/handler/swwm_handler_vanillaboss.zsc
Normal file
399
zscript/handler/swwm_handler_vanillaboss.zsc
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
// vanilla boss stuff
|
||||
|
||||
extend Class SWWMHandler
|
||||
{
|
||||
String bosstag;
|
||||
Array<Actor> 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<bossactors.Size(); i++ )
|
||||
{
|
||||
if ( !bossactors[i] ) continue;
|
||||
if ( (!bossactors[i].target || !bossactors[i].CheckSight(bossactors[i].target,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY))
|
||||
&& (!bossviewactor || (bossviewactor && !bossviewactor.target)) ) continue;
|
||||
initialized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
private ui void VanillaBossUiTick()
|
||||
{
|
||||
if ( (!ui_initialized && initialized) || (ui_initialized && !initialized) )
|
||||
{
|
||||
ui_initialized = true;
|
||||
thealth = 0;
|
||||
for ( int i=0; i<bossactors.Size(); i++ )
|
||||
{
|
||||
if ( !bossactors[i] ) continue;
|
||||
thealth += max(0,bossactors[i].Health);
|
||||
}
|
||||
hmax = thealth;
|
||||
for ( int i=0; i<30; i++ ) oldhealth[i] = thealth;
|
||||
cummdamage = 0;
|
||||
if ( !ihealth ) ihealth = DynamicValueInterpolator.Create(thealth,.1,1,1000);
|
||||
else ihealth.Reset(thealth);
|
||||
if ( !ihealthr ) ihealthr = DynamicValueInterpolator.Create(thealth,.5,1,1000);
|
||||
else ihealthr.Reset(thealth);
|
||||
return;
|
||||
}
|
||||
if ( !ui_initialized ) return;
|
||||
// update healthbar
|
||||
int newhealth = 0;
|
||||
for ( int i=0; i<bossactors.Size(); i++ )
|
||||
{
|
||||
if ( !bossactors[i] ) continue;
|
||||
newhealth += max(0,bossactors[i].Health);
|
||||
}
|
||||
oldhealth[0] = newhealth;
|
||||
int curcumm = max(0,thealth-newhealth);
|
||||
if ( curcumm > 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;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
501
zscript/items/swwm_ammoextra.zsc
Normal file
501
zscript/items/swwm_ammoextra.zsc
Normal file
|
|
@ -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<Ammo> a, Class<Ammo> 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<Class<Ammo> > a, int l, int h )
|
||||
{
|
||||
Class<Ammo> pv = a[h];
|
||||
int i = (l-1);
|
||||
for ( int j=l; j<=(h-1); j++ )
|
||||
{
|
||||
if ( CmpFabAmmo(pv,a[j]) )
|
||||
{
|
||||
i++;
|
||||
Class<Ammo> tmp = a[j];
|
||||
a[j] = a[i];
|
||||
a[i] = tmp;
|
||||
}
|
||||
}
|
||||
Class<Ammo> tmp = a[h];
|
||||
a[h] = a[i+1];
|
||||
a[i+1] = tmp;
|
||||
return i+1;
|
||||
}
|
||||
private void qsort_fabammo( Array<Class<Ammo> > 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<Class<Ammo> > available;
|
||||
// populate ammo production list
|
||||
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
||||
{
|
||||
let a = (Class<Ammo>)(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.Size(); j++ )
|
||||
{
|
||||
let type = (class<Weapon>)(AllActorClasses[j]);
|
||||
if ( !type ) continue;
|
||||
let rep = GetReplacement(type);
|
||||
if ( (rep != type) && !(rep is "DehackedPickup") ) continue;
|
||||
readonly<Weapon> 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<available.Size(); i++ )
|
||||
{
|
||||
int amt, lim;
|
||||
int cnt = 0;
|
||||
Ammo cur = Ammo(Owner.FindInventory(available[i]));
|
||||
if ( cur )
|
||||
{
|
||||
amt = cur.Amount;
|
||||
lim = cur.MaxAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur = Ammo(Spawn(available[i]));
|
||||
amt = cur.Amount = 0;
|
||||
lim = cur.MaxAmount;
|
||||
cur.AttachToOwner(Owner);
|
||||
}
|
||||
// percentage based on DEFAULT max amount (capped at 1 minimum)
|
||||
if ( pertype < 0 ) tpertype = max(1,-int(cur.default.MaxAmount*pertype*.01));
|
||||
while ( (amt < lim) && (consumed+cur.default.Stamina < budget) && (cnt < tpertype) )
|
||||
{
|
||||
consumed += cur.default.Stamina;
|
||||
amt = ++cur.Amount;
|
||||
cnt++;
|
||||
given = true;
|
||||
}
|
||||
if ( cnt > 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.Size(); i++ )
|
||||
{
|
||||
let type = (class<Ammo>)(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.Size(); j++ )
|
||||
{
|
||||
let type2 = (class<Weapon>)(AllActorClasses[j]);
|
||||
if ( !type2 ) continue;
|
||||
let rep = GetReplacement(type2);
|
||||
if ( (rep != type2) && !(rep is "DehackedPickup") ) continue;
|
||||
readonly<Weapon> 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;
|
||||
}
|
||||
}
|
||||
1040
zscript/items/swwm_ammoitems.zsc
Normal file
1040
zscript/items/swwm_ammoitems.zsc
Normal file
File diff suppressed because it is too large
Load diff
377
zscript/items/swwm_baseammo.zsc
Normal file
377
zscript/items/swwm_baseammo.zsc
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
// Common code for ammo division
|
||||
Mixin Class SWWMAmmo
|
||||
{
|
||||
private Inventory DoDrop( Class<Inventory> 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<Ammo> a, Class<Ammo> b )
|
||||
{
|
||||
let amta = GetDefaultByType(a).Amount;
|
||||
let amtb = GetDefaultByType(b).Amount;
|
||||
return (amta < amtb);
|
||||
}
|
||||
|
||||
private int partition_ammotypes( Array<Class<Ammo> > a, int l, int h )
|
||||
{
|
||||
Class<Ammo> pv = a[h];
|
||||
int i = (l-1);
|
||||
for ( int j=l; j<=(h-1); j++ )
|
||||
{
|
||||
if ( CmpAmmo(pv,a[j]) )
|
||||
{
|
||||
i++;
|
||||
Class<Ammo> tmp = a[j];
|
||||
a[j] = a[i];
|
||||
a[i] = tmp;
|
||||
}
|
||||
}
|
||||
Class<Ammo> tmp = a[h];
|
||||
a[h] = a[i+1];
|
||||
a[i+1] = tmp;
|
||||
return i+1;
|
||||
}
|
||||
private void qsort_ammotypes( Array<Class<Ammo> > 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<Class<Ammo> > ammotypes;
|
||||
ammotypes.Clear();
|
||||
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
||||
{
|
||||
if ( AllActorClasses[i] is GetParentAmmo() )
|
||||
ammotypes.Push((Class<Ammo>)(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<ammotypes.Size(); i++ )
|
||||
{
|
||||
let def = GetDefaultByType(ammotypes[i]);
|
||||
if ( amt >= 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<Class<Ammo> > ammotypes;
|
||||
ammotypes.Clear();
|
||||
for ( int i=0; i<AllActorClasses.Size(); i++ )
|
||||
{
|
||||
if ( AllActorClasses[i] is GetParentAmmo() )
|
||||
ammotypes.Push((Class<Ammo>)(AllActorClasses[i]));
|
||||
}
|
||||
// sort from largest to smallest
|
||||
for ( int i=0; i<ammotypes.Size(); i++ )
|
||||
{
|
||||
int j = 1;
|
||||
while ( j < ammotypes.Size() )
|
||||
{
|
||||
int k = j;
|
||||
while ( (k > 0) && CmpAmmo(ammotypes[k-1],ammotypes[k]) )
|
||||
{
|
||||
Class<Ammo> 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<ammotypes.Size(); i++ )
|
||||
{
|
||||
let def = GetDefaultByType(ammotypes[i]);
|
||||
if ( excess >= 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<Ammo> 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<item.Amount; i++ )
|
||||
{
|
||||
int bul = ClipSize;
|
||||
int maxgiveamt = min(MaxAmount-Amount,bul);
|
||||
int dropamt = bul-maxgiveamt;
|
||||
if ( dropamt > 0 ) CreateTossable(dropamt);
|
||||
Amount = min(MaxAmount,Amount+bul);
|
||||
}
|
||||
item.bPickupGood = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return Super.HandlePickup(item);
|
||||
}
|
||||
|
||||
private Inventory DoDrop( Class<Inventory> 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));
|
||||
}
|
||||
}
|
||||
185
zscript/items/swwm_basearmor.zsc
Normal file
185
zscript/items/swwm_basearmor.zsc
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
// Base class for all SWWM Armors
|
||||
Class SWWMArmor : Armor abstract
|
||||
{
|
||||
int priority;
|
||||
String drainmsg;
|
||||
Class<SWWMSpareArmor> 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<SWWMArmor> 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;
|
||||
}
|
||||
}
|
||||
160
zscript/items/swwm_basehealth.zsc
Normal file
160
zscript/items/swwm_basehealth.zsc
Normal file
|
|
@ -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<Inventory> 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;
|
||||
}
|
||||
}
|
||||
88
zscript/items/swwm_baseitem.zsc
Normal file
88
zscript/items/swwm_baseitem.zsc
Normal file
|
|
@ -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<Line> clines;
|
||||
Array<int> 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<cf.clines.Size(); i++ )
|
||||
cf.clines[i].Activate(user,cf.csides[i],SPAC_Cross);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Mixin Class SWWMOverlapPickupSound
|
||||
{
|
||||
// overlap sounds
|
||||
override void PlayPickupSound( Actor toucher )
|
||||
{
|
||||
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
||||
if ( hnd )
|
||||
{
|
||||
if ( hnd.lastpickuptic[toucher.PlayerNumber()] == gametic )
|
||||
return; // don't play if picked up on the same exact tic (overlapping items)
|
||||
hnd.lastpickuptic[toucher.PlayerNumber()] = gametic;
|
||||
}
|
||||
double atten;
|
||||
int flags = CHANF_OVERLAP|CHANF_MAYBE_LOCAL;
|
||||
if ( bNoAttenPickupSound ) atten = ATTN_NONE;
|
||||
else atten = ATTN_NORM;
|
||||
if ( toucher && toucher.CheckLocalView() )
|
||||
flags |= CHANF_NOPAUSE;
|
||||
toucher.A_StartSound(PickupSound,CHAN_ITEM,flags,1.,atten);
|
||||
}
|
||||
}
|
||||
243
zscript/items/swwm_collectibles.zsc
Normal file
243
zscript/items/swwm_collectibles.zsc
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
// collectible items that may drop sometimes
|
||||
|
||||
Class SWWMCollectible : Inventory abstract
|
||||
{
|
||||
Mixin SWWMUseToPickup;
|
||||
|
||||
int avail;
|
||||
bool propagated;
|
||||
Class<SWWMItemGesture> 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<MAXPLAYERS; i++ )
|
||||
{
|
||||
if ( !playeringame[i] || !players[i].mo || (i == other.PlayerNumber()) )
|
||||
continue;
|
||||
let c = SWWMCollectible(Spawn(GetClass(),pos));
|
||||
c.propagated = true;
|
||||
if ( !c.CallTryPickup(players[i].mo) )
|
||||
c.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A -1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
12
zscript/items/swwm_collectibles_gesture.zsc
Normal file
12
zscript/items/swwm_collectibles_gesture.zsc
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// 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 {}
|
||||
|
|
@ -1,213 +1,3 @@
|
|||
// collectible items that may drop sometimes
|
||||
|
||||
Class SWWMCollectible : Inventory abstract
|
||||
{
|
||||
Mixin SWWMUseToPickup;
|
||||
|
||||
int avail;
|
||||
bool propagated;
|
||||
Class<SWWMItemGesture> 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<MAXPLAYERS; i++ )
|
||||
{
|
||||
if ( !playeringame[i] || !players[i].mo || (i == other.PlayerNumber()) )
|
||||
continue;
|
||||
let c = SWWMCollectible(Spawn(GetClass(),pos));
|
||||
c.propagated = true;
|
||||
if ( !c.CallTryPickup(players[i].mo) )
|
||||
c.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A -1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class SWWMItemGesture : SWWMWeapon abstract
|
||||
{
|
||||
SWWMGesture gest; // the base gesture weapon that we got picked from
|
||||
bool gotused;
|
||||
|
||||
// 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 ( gotused )
|
||||
{
|
||||
// clear ourselves after use (don't pollute inventory list)
|
||||
DepleteOrDestroy();
|
||||
return;
|
||||
}
|
||||
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"));
|
||||
}
|
||||
|
||||
action void A_FinishGesture()
|
||||
{
|
||||
A_WeaponOffset(0,32); // fix key punch offset
|
||||
let gest = invoker.gest;
|
||||
if ( !gest )
|
||||
{
|
||||
ThrowAbortException("Call to A_FinishGesture() without owned SWWMGesture");
|
||||
return;
|
||||
}
|
||||
if ( gest.sweapon.Size() > 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
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
140
zscript/items/swwm_keys_gesture.zsc
Normal file
140
zscript/items/swwm_keys_gesture.zsc
Normal file
|
|
@ -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 {}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1277
zscript/swwm_common_fx.zsc
Normal file
1277
zscript/swwm_common_fx.zsc
Normal file
File diff suppressed because it is too large
Load diff
636
zscript/swwm_gesture.zsc
Normal file
636
zscript/swwm_gesture.zsc
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
// stuff related to gestures
|
||||
|
||||
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<SWWMItemGesture> whichweapon;
|
||||
Array<bool> suse;
|
||||
Array<Class<SWWMItemGesture> > 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<SWWMItemGesture> 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<w.sweapon.Size(); i++ )
|
||||
{
|
||||
if ( (w.sweapon[i] != a) || (w.suse[i] != used) ) continue;
|
||||
return null;
|
||||
}
|
||||
w.sweapon.Push(a);
|
||||
w.suse.Push(used);
|
||||
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.whichgesture = GS_Null;
|
||||
w.whichweapon = a;
|
||||
w.whichuse = used;
|
||||
mo.player.PendingWeapon = w;
|
||||
return w;
|
||||
}
|
||||
|
||||
action void A_CallPlayerGesture( statelabel st, statelabel cst )
|
||||
{
|
||||
if ( invoker.Owner.Health <= 0 ) return;
|
||||
if ( (player.crouchdir == -1) && invoker.Owner.FindState(cst) )
|
||||
invoker.Owner.SetStateLabel(cst);
|
||||
else if ( invoker.Owner.FindState(st) )
|
||||
invoker.Owner.SetStateLabel(st);
|
||||
}
|
||||
|
||||
action void A_FinishGesture()
|
||||
{
|
||||
if ( invoker.sweapon.Size() > 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<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("LoveHeartSparkle",patpos);
|
||||
s.angle = FRandom[ExploS](0,360);
|
||||
s.pitch = FRandom[ExploS](-90,90);
|
||||
}
|
||||
if ( pt.target && pt.target.bSHOOTABLE )
|
||||
{
|
||||
int healamt = 10;
|
||||
let raging = RagekitPower(FindInventory("RagekitPower"));
|
||||
if ( raging )
|
||||
{
|
||||
healamt *= 8;
|
||||
raging.DoHitFX();
|
||||
}
|
||||
if ( pt.target.GiveBody(healamt,pt.target.GetSpawnHealth()) )
|
||||
{
|
||||
SWWMScoreObj.Spawn(healamt,pt.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+pt.target.Height/2),ST_Health);
|
||||
SWWMHandler.DoFlash(pt.target,Color(32,224,128,255),10);
|
||||
}
|
||||
}
|
||||
let s = Demolitionist(self).mystats;
|
||||
if ( s ) s.pats++;
|
||||
}
|
||||
|
||||
action void A_HeadpatEnd()
|
||||
{
|
||||
player.cheats &= ~CF_TOTALLYFROZEN;
|
||||
Demolitionist(player.mo).scriptedinvul = false;
|
||||
let pt = invoker.pats;
|
||||
if ( !pt ) return;
|
||||
pt.patting = false;
|
||||
let t = pt.target;
|
||||
if ( t )
|
||||
{
|
||||
if ( pt.patstate ) t.SetState(pt.patstate);
|
||||
else t.tics = pt.oldtargettics;
|
||||
t.bDORMANT = false;
|
||||
if ( t.bISMONSTER )
|
||||
{
|
||||
if ( pt.lethalpat )
|
||||
{
|
||||
t.DamageMobj(invoker,self,t.Health,'Love',DMG_FORCED|DMG_THRUSTLESS);
|
||||
pt.Destroy();
|
||||
return;
|
||||
}
|
||||
// befriend
|
||||
if ( t.bCOUNTKILL )
|
||||
{
|
||||
t.bCOUNTKILL = false;
|
||||
level.total_monsters--;
|
||||
}
|
||||
if ( t.special && !(t.ActivationType&THINGSPEC_NoDeathSpecial) )
|
||||
{
|
||||
Actor whomst = level.actownspecial?t:self;
|
||||
if ( t.ActivationType&THINGSPEC_ThingActs ) whomst = t;
|
||||
else if ( t.ActivationType&THINGSPEC_TriggerActs ) whomst = self;
|
||||
level.ExecuteSpecial(t.special,whomst,null,false,t.args[0],t.args[1],t.args[2],t.args[3],t.args[4]);
|
||||
t.special = 0;
|
||||
}
|
||||
if ( !t.bFRIENDLY )
|
||||
{
|
||||
let s = Demolitionist(self).mystats;
|
||||
if ( s ) s.befriend++;
|
||||
}
|
||||
t.bFRIENDLY = true;
|
||||
if ( deathmatch )
|
||||
t.SetFriendPlayer(player);
|
||||
// cancel any attacks
|
||||
if ( t.InStateSequence(t.CurState,t.FindState("Missile"))
|
||||
|| t.InStateSequence(t.CurState,t.FindState("Melee")) )
|
||||
t.SetState(t.FindState("See"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action void A_ThrowMag()
|
||||
{
|
||||
let weap = Weapon(invoker);
|
||||
if ( !weap ) return;
|
||||
Vector3 x, y, z, x2, y2, z2;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-2*y-3*z);
|
||||
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.005);
|
||||
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
|
||||
Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
|
||||
let p = Spawn("ExplodiumMagProj",origin);
|
||||
p.special1 = 7;
|
||||
p.target = self;
|
||||
p.angle = atan2(dir.y,dir.x);
|
||||
p.pitch = asin(-dir.z);
|
||||
p.vel = dir*p.speed;
|
||||
if ( p.waterlevel <= 0 ) p.vel.z += 5.;
|
||||
p.vel += vel*.5;
|
||||
}
|
||||
|
||||
action void A_Smooch()
|
||||
{
|
||||
if ( swwm_mutevoice < 4 )
|
||||
A_StartSound("demolitionist/smooch",CHAN_DEMOVOICE,CHANF_OVERLAP,.4);
|
||||
}
|
||||
|
||||
action void A_BlowKiss()
|
||||
{
|
||||
if ( swwm_mutevoice < 4 )
|
||||
A_StartSound("demolitionist/blowkiss",CHAN_DEMOVOICE,CHANF_OVERLAP,.4);
|
||||
let weap = Weapon(invoker);
|
||||
if ( !weap ) return;
|
||||
Vector3 x, y, z, x2, y2, z2, dir;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-1*z);
|
||||
let p = Spawn("LoveHeart",origin);
|
||||
p.target = self;
|
||||
p.angle = angle;
|
||||
p.pitch = BulletSlope();
|
||||
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
||||
// try to catch target in cone of vision
|
||||
[x2, y2, z2] = swwm_CoordUtil.GetAxes(p.pitch,p.angle,0);
|
||||
Array<HHitList> 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<hits.Size(); k++ )
|
||||
{
|
||||
if ( hits[k].a != d.HitActor ) continue;
|
||||
if ( (hits[k].dir dot x2) < (dir dot x2) )
|
||||
hits[k].dir = dir; // closer to centerpoint
|
||||
addme = false;
|
||||
break;
|
||||
}
|
||||
if ( !addme ) continue;
|
||||
let nhit = new("HHitList");
|
||||
nhit.a = d.HitActor;
|
||||
nhit.dir = dir;
|
||||
hits.Push(nhit);
|
||||
}
|
||||
rings += 5;
|
||||
}
|
||||
int closest = -1;
|
||||
double closestdot = -1;
|
||||
for ( int i=0; i<hits.Size(); i++ )
|
||||
{
|
||||
double thisdot = (hits[i].dir dot x2);
|
||||
if ( thisdot < closestdot ) continue;
|
||||
closest = i;
|
||||
closestdot = thisdot;
|
||||
}
|
||||
if ( closest != -1 ) p.tracer = hits[closest].a;
|
||||
}
|
||||
|
||||
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:
|
||||
XZW1 A 1
|
||||
{
|
||||
if ( invoker.whichweapon )
|
||||
{
|
||||
SWWMItemGesture g = SWWMItemGesture(FindInventory(invoker.whichweapon));
|
||||
if ( !g )
|
||||
{
|
||||
g = SWWMItemGesture(Spawn(invoker.whichweapon));
|
||||
AddInventory(g);
|
||||
}
|
||||
g.gest = invoker;
|
||||
player.ReadyWeapon = g;
|
||||
if ( invoker.whichuse ) player.SetPSPrite(PSP_WEAPON,g.FindState("AltFire"));
|
||||
else player.SetPSPrite(PSP_WEAPON,g.FindState("Fire"));
|
||||
return ResolveState(null);
|
||||
}
|
||||
switch ( invoker.whichgesture )
|
||||
{
|
||||
case GS_Headpat:
|
||||
return ResolveState("Headpat");
|
||||
case GS_Grenade:
|
||||
return ResolveState("QuickGrenade");
|
||||
case GS_EmptyMelee:
|
||||
return ResolveState("EmptyMelee");
|
||||
case GS_Wave:
|
||||
return ResolveState("Wave");
|
||||
case GS_ThumbsUp:
|
||||
return ResolveState("Approve");
|
||||
case GS_Victory:
|
||||
return ResolveState("Victory");
|
||||
case GS_BlowKiss:
|
||||
return ResolveState("BlowKiss");
|
||||
}
|
||||
return ResolveState("NoGesture");
|
||||
}
|
||||
Wait;
|
||||
Fire:
|
||||
XZW1 A 1;
|
||||
Goto Ready;
|
||||
Headpat:
|
||||
XZW1 A 0 A_JumpIf(CountInv("RagekitPower"),"Ragepat");
|
||||
XZW1 A 3 A_CallPlayerGesture("Headpat","Headpat");
|
||||
XZW3 TU 3;
|
||||
XZW3 V 2 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW3 WX 2;
|
||||
XZW3 YZ 1;
|
||||
XZW4 AB 1;
|
||||
XZW4 C 2;
|
||||
XZW4 D 2 A_Headpat();
|
||||
XZW4 EF 2;
|
||||
XZW4 GHI 1;
|
||||
XZW3 YZ 1;
|
||||
XZW4 AB 1;
|
||||
XZW4 C 2;
|
||||
XZW4 D 2 A_Headpat();
|
||||
XZW4 EF 2;
|
||||
XZW4 GHI 1;
|
||||
XZW4 JK 2;
|
||||
XZW4 L 2 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW4 MNOP 3;
|
||||
XZW1 A 0 A_HeadpatEnd();
|
||||
XZW1 A -1 A_FinishGesture();
|
||||
Stop;
|
||||
Ragepat:
|
||||
XZW1 A 3 A_CallPlayerGesture("Ragepat","Ragepat");
|
||||
XZW3 TU 2;
|
||||
XZW3 V 1 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW3 WX 1;
|
||||
XZW3 Y 1;
|
||||
XZW4 AC 1;
|
||||
XZW4 D 1 A_Headpat();
|
||||
XZW4 EF 1;
|
||||
XZW4 GI 1;
|
||||
XZW3 Y 1;
|
||||
XZW4 AC 1;
|
||||
XZW4 D 1 A_Headpat();
|
||||
XZW4 EF 1;
|
||||
XZW4 GI 1;
|
||||
XZW3 Y 1;
|
||||
XZW4 AC 1;
|
||||
XZW4 D 1 A_Headpat();
|
||||
XZW4 EF 1;
|
||||
XZW4 GI 1;
|
||||
XZW3 Y 1;
|
||||
XZW4 AC 1;
|
||||
XZW4 D 1 A_Headpat();
|
||||
XZW4 EF 1;
|
||||
XZW4 GIJK 1;
|
||||
XZW4 L 1 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW4 MNOP 2;
|
||||
XZW1 A 0 A_HeadpatEnd();
|
||||
XZW1 A -1 A_FinishGesture();
|
||||
Stop;
|
||||
QuickGrenade:
|
||||
XZW4 Q 3;
|
||||
XZW4 R 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW4 ST 3;
|
||||
XZW4 U 3 A_PlayerReload();
|
||||
XZW4 V 2 A_StartSound("explodium/magpin",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW4 WX 2;
|
||||
XZW4 Y 3;
|
||||
XZW4 Z 2
|
||||
{
|
||||
A_StartSound("explodium/throwmag",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
A_PlayerMelee();
|
||||
}
|
||||
XZW5 AB 2;
|
||||
XZW5 CDEF 2;
|
||||
XZW5 G 2 A_ThrowMag();
|
||||
XZW5 HIJ 3;
|
||||
XZW5 K 4;
|
||||
XZW4 Q 0 A_JumpIf(player.cmd.buttons&BT_USER4,"QuickGrenade");
|
||||
XZW4 Q -1 A_FinishGesture();
|
||||
Stop;
|
||||
EmptyMelee:
|
||||
XZW0 ABC 1;
|
||||
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 -1
|
||||
{
|
||||
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK|BT_USER1) )
|
||||
return ResolveState("EmptyMelee")+3;
|
||||
A_FinishGesture();
|
||||
return ResolveState(null);
|
||||
}
|
||||
Stop;
|
||||
Wave:
|
||||
XZW1 A 3 A_CallPlayerGesture("Wave","CrouchWave");
|
||||
XZW1 B 3;
|
||||
XZW1 C 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW1 DEFGHIJ 3;
|
||||
XZW1 K 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW1 LMNOP 3;
|
||||
XZW1 A -1 A_FinishGesture();
|
||||
Stop;
|
||||
Approve:
|
||||
XZW1 A 3 A_CallPlayerGesture("Approve","CrouchApprove");
|
||||
XZW1 QR 3;
|
||||
XZW1 S 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW1 TUVWXY 3;
|
||||
XZW1 Z 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW2 A 3;
|
||||
XZW2 BCDEFG 3;
|
||||
XZW1 A -1 A_FinishGesture();
|
||||
Stop;
|
||||
Victory:
|
||||
XZW1 A 3 A_CallPlayerGesture("Victory","CrouchVictory");
|
||||
XZW2 HI 3;
|
||||
XZW2 J 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW2 KLMNOPQR 3;
|
||||
XZW2 S 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW2 TUVWXYZ 3;
|
||||
XZW1 A -1 A_FinishGesture();
|
||||
Stop;
|
||||
BlowKiss:
|
||||
XZW1 A 3 A_CallPlayerGesture("BlowKiss","CrouchBlowKiss");
|
||||
XZW3 A 3;
|
||||
XZW3 B 3;
|
||||
XZW3 C 3 A_StartSound("demolitionist/handsup",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW3 DE 3;
|
||||
XZW3 F 3 A_Smooch();
|
||||
XZW3 GHIJ 3;
|
||||
XZW3 K 3 A_StartSound("demolitionist/handsdown",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
XZW3 L 3 A_BlowKiss();
|
||||
XZW3 MNOPQRS 3;
|
||||
XZW1 A -1 A_FinishGesture();
|
||||
Stop;
|
||||
NoGesture:
|
||||
XZW1 A -1 A_FinishGesture();
|
||||
Stop;
|
||||
Deselect:
|
||||
XZW1 A -1 A_FullLower();
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// item gestures
|
||||
|
||||
Class SWWMItemGesture : SWWMWeapon abstract
|
||||
{
|
||||
SWWMGesture gest; // the base gesture weapon that we got picked from
|
||||
bool gotused;
|
||||
|
||||
// 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 ( gotused )
|
||||
{
|
||||
// clear ourselves after use (don't pollute inventory list)
|
||||
DepleteOrDestroy();
|
||||
return;
|
||||
}
|
||||
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"));
|
||||
}
|
||||
|
||||
action void A_FinishGesture()
|
||||
{
|
||||
A_WeaponOffset(0,32); // fix key punch offset
|
||||
let gest = invoker.gest;
|
||||
if ( !gest )
|
||||
{
|
||||
ThrowAbortException("Call to A_FinishGesture() without owned SWWMGesture");
|
||||
return;
|
||||
}
|
||||
if ( gest.sweapon.Size() > 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;
|
||||
}
|
||||
}
|
||||
469
zscript/swwm_gesture_fx.zsc
Normal file
469
zscript/swwm_gesture_fx.zsc
Normal file
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("LoveHeartSparkle",pos);
|
||||
s.angle = FRandom[ExploS](0,360);
|
||||
s.pitch = FRandom[ExploS](-90,90);
|
||||
}
|
||||
if ( !tracer || (tracer.Health <= 0) ) return;
|
||||
double mag = vel.length();
|
||||
vel = mag*(level.Vec3Diff(pos,tracer.Vec3Offset(0,0,tracer.height/2)).unit()*mag*6./GameTicRate+vel).unit();
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_StartSound("misc/heart",CHAN_WEAPON);
|
||||
A_AttachLight('LOVELIGHT',DynamicLight.PointLight,Color(255,176,208),80,80,DYNAMICLIGHT.LF_ATTENUATE);
|
||||
special2 = 25;
|
||||
}
|
||||
void CheckNaziRemove()
|
||||
{
|
||||
TextureID HitTexture;
|
||||
Line HitLine;
|
||||
int LineSide, LinePart;
|
||||
Sector HitSector;
|
||||
F3DFloor Hit3DFloor;
|
||||
bool HitCeiling;
|
||||
if ( BlockingLine )
|
||||
{
|
||||
HitLine = BlockingLine;
|
||||
// which side and part we hit?
|
||||
LineSide = SWWMUtility.PointOnLineSide(pos.xy,BlockingLine);
|
||||
double fl, cl;
|
||||
if ( BlockingLine.sidedef[1] )
|
||||
{
|
||||
fl = min(BlockingLine.frontsector.floorplane.ZAtPoint(pos.xy),BlockingLine.backsector.floorplane.ZAtPoint(pos.xy));
|
||||
cl = max(BlockingLine.frontsector.ceilingplane.ZAtPoint(pos.xy),BlockingLine.backsector.ceilingplane.ZAtPoint(pos.xy));
|
||||
if ( max(floorz,pos.z) < fl ) LinePart = 0;
|
||||
else if ( min(ceilingz,pos.z+height) > cl ) LinePart = 2;
|
||||
else LinePart = 1;
|
||||
}
|
||||
else LinePart = 1; // always middle
|
||||
if ( Blocking3DFloor )
|
||||
{
|
||||
for ( int i=0; i<BlockingLine.backsector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
F3DFloor ff = BlockingLine.backsector.Get3DFloor(i);
|
||||
if ( ff.model != Blocking3DFloor ) continue;
|
||||
Hit3DFloor = ff;
|
||||
break;
|
||||
}
|
||||
if ( Hit3DFloor.flags&F3DFloor.FF_UPPERTEXTURE ) HitTexture = HitLine.sidedef[LineSide].GetTexture(0);
|
||||
else if ( Hit3DFloor.flags&F3DFloor.FF_LOWERTEXTURE ) HitTexture = HitLine.sidedef[LineSide].GetTexture(2);
|
||||
}
|
||||
else HitTexture = HitLine.sidedef[LineSide].GetTexture(LinePart);
|
||||
}
|
||||
else if ( BlockingCeiling )
|
||||
{
|
||||
if ( Blocking3DFloor )
|
||||
{
|
||||
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
|
||||
{
|
||||
F3DFloor ff = BlockingCeiling.Get3DFloor(i);
|
||||
if ( ff.model != Blocking3DFloor ) continue;
|
||||
Hit3DFloor = ff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HitSector = BlockingCeiling;
|
||||
HitCeiling = true;
|
||||
HitTexture = ceilingpic;
|
||||
}
|
||||
else if ( BlockingFloor )
|
||||
{
|
||||
if ( Blocking3DFloor )
|
||||
{
|
||||
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
|
||||
{
|
||||
F3DFloor ff = BlockingFloor.Get3DFloor(i);
|
||||
if ( ff.model != Blocking3DFloor ) continue;
|
||||
Hit3DFloor = ff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HitSector = BlockingFloor;
|
||||
HitCeiling = false;
|
||||
HitTexture = floorpic;
|
||||
}
|
||||
bool remove;
|
||||
TextureID replacewith;
|
||||
[remove, replacewith] = SWWMUtility.NaziTexRemover(HitTexture);
|
||||
if ( !remove ) return;
|
||||
A_StartSound("bestsound",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
if ( HitLine )
|
||||
{
|
||||
if ( Hit3DFloor )
|
||||
{
|
||||
if ( Hit3DFloor.flags&F3DFloor.FF_UPPERTEXTURE ) HitLine.sidedef[LineSide].SetTexture(0,replacewith);
|
||||
else if ( Hit3DFloor.flags&F3DFloor.FF_LOWERTEXTURE ) HitLine.sidedef[LineSide].SetTexture(2,replacewith);
|
||||
else Hit3DFloor.master.sidedef[0].SetTexture(1,replacewith);
|
||||
}
|
||||
else HitLine.sidedef[LineSide].SetTexture(LinePart,replacewith);
|
||||
}
|
||||
else if ( HitSector && HitCeiling )
|
||||
{
|
||||
if ( Hit3DFloor )
|
||||
{
|
||||
if ( Hit3DFloor.flags&F3DFloor.FF_INVERTSECTOR ) Hit3DFloor.model.SetTexture(1,replacewith);
|
||||
else Hit3DFloor.model.SetTexture(0,replacewith);
|
||||
}
|
||||
else HitSector.SetTexture(1,replacewith);
|
||||
}
|
||||
else if ( HitSector && !HitCeiling )
|
||||
{
|
||||
if ( Hit3DFloor )
|
||||
{
|
||||
if ( Hit3DFloor.flags&F3DFloor.FF_INVERTSECTOR ) Hit3DFloor.model.SetTexture(0,replacewith);
|
||||
else Hit3DFloor.model.SetTexture(1,replacewith);
|
||||
}
|
||||
else HitSector.SetTexture(0,replacewith);
|
||||
}
|
||||
}
|
||||
action void A_HeartBurst()
|
||||
{
|
||||
invoker.CheckNaziRemove();
|
||||
// use line
|
||||
if ( BlockingLine )
|
||||
{
|
||||
int s = SWWMUtility.PointOnLineSide(pos.xy,BlockingLine);
|
||||
int locknum = SWWMUtility.GetLineLock(BlockingLine);
|
||||
if ( !locknum || (target && target.CheckKeys(locknum,false,true)) )
|
||||
BlockingLine.RemoteActivate(target,s,SPAC_Use,pos);
|
||||
}
|
||||
if ( swwm_omnibust )
|
||||
{
|
||||
int dmg = GetMissileDamage(0,0);
|
||||
let raging = RagekitPower(self.target.FindInventory("RagekitPower"));
|
||||
if ( raging ) dmg *= 8;
|
||||
if ( BusterWall.ProjectileBust(self,dmg,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch))) )
|
||||
raging.DoHitFX();
|
||||
}
|
||||
A_SetRenderStyle(1.,STYLE_Add);
|
||||
// RemoveLight causes heavy performance issues, just overwrite with a "blank light"
|
||||
A_AttachLight('LOVELIGHT',DynamicLight.PointLight,0,0,0);
|
||||
//A_RemoveLight('LOVELIGHT');
|
||||
CheckSplash(40);
|
||||
A_QuakeEx(2,2,2,8,0,300,"",QF_RELATIVE|QF_SCALEDOWN);
|
||||
A_StartSound("bestsound",CHAN_VOICE);
|
||||
Spawn("LoveHeartBurstLight",pos);
|
||||
int numpt = Random[ExploS](10,15);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.5,4);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(10,8,9)*Random[ExploS](20,25));
|
||||
s.special1 = Random[ExploS](1,3);
|
||||
s.scale *= 2.;
|
||||
s.alpha *= .6;
|
||||
}
|
||||
numpt = Random[ExploS](40,50);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("LoveHeartSparkle",pos);
|
||||
s.angle = FRandom[ExploS](0,360);
|
||||
s.pitch = FRandom[ExploS](-90,90);
|
||||
s.scale *= RandomPick[ExploS](1,3);
|
||||
s.alpha *= 2;
|
||||
}
|
||||
}
|
||||
action void A_HeartDie()
|
||||
{
|
||||
scale *= 1.2;
|
||||
A_FadeOut();
|
||||
}
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
DOKI A 1 Bright A_HeartTick();
|
||||
Wait;
|
||||
Death:
|
||||
DOKI A 0 Bright A_HeartBurst();
|
||||
DOKI A 1 Bright A_HeartDie();
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
Class HeadpatTracker : Actor
|
||||
{
|
||||
State patstate; // state the target will jump to on headpat
|
||||
// if null, will simply resume current state
|
||||
State deathstate; // if the actor enters this state, we can't headpat no more
|
||||
int oldtargettics; // previous tics left in target's pre-pat state
|
||||
bool patting; // currently in pat
|
||||
Actor patter; // who's patting
|
||||
double hdoomheightfix; // certain hdoom monsters kneel down, so their heads are lower than expected
|
||||
double hdoomangfix; // fix for imp in a chair
|
||||
bool lethalpat; // ending headpat immediately drops enemy health to 0
|
||||
bool dvacationarghack; // hackfix for some girls in doom vacation, disallows headpats if args[0] is 1
|
||||
|
||||
default
|
||||
{
|
||||
+NOGRAVITY;
|
||||
+NOTELEPORT;
|
||||
+DONTSPLASH;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( !target || (target.Health <= 0) || (deathstate && target.InStateSequence(target.CurState,deathstate)) )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if ( patting )
|
||||
{
|
||||
// keep bolted in here
|
||||
target.vel *= 0;
|
||||
patter.vel *= 0;
|
||||
patter.player.vel *= 0;
|
||||
target.SetOrigin(pos,true);
|
||||
// keep aim
|
||||
double delta = deltaangle(target.angle,target.AngleTo(patter));
|
||||
if ( abs(delta) < 1. ) target.angle = target.AngleTo(patter);
|
||||
else target.angle += .3*delta;
|
||||
delta = deltaangle(patter.angle,patter.AngleTo(target)+hdoomangfix);
|
||||
if ( abs(delta) < 1. ) patter.A_SetAngle(patter.AngleTo(target)+hdoomangfix,SPF_INTERPOLATE);
|
||||
else patter.A_SetAngle(patter.angle+.3*delta,SPF_INTERPOLATE);
|
||||
double hfact = 1.2-hdoomheightfix;
|
||||
delta = deltaangle(patter.pitch,SWWMUtility.PitchTo(patter,target,hfact));
|
||||
if ( abs(delta) < 1. ) patter.A_SetPitch(SWWMUtility.PitchTo(patter,target,hfact),SPF_INTERPOLATE);
|
||||
else patter.A_SetPitch(patter.pitch+.3*delta,SPF_INTERPOLATE);
|
||||
return;
|
||||
}
|
||||
if ( (radius != target.radius+8) || (height != target.height+8) )
|
||||
A_SetSize(target.radius+8,target.height+8);
|
||||
if ( pos != target.pos ) SetOrigin(target.pos,false);
|
||||
}
|
||||
override bool Used( Actor user )
|
||||
{
|
||||
if ( !target ) return false;
|
||||
if ( patting ) return false; // already on it
|
||||
if ( user.player.crouchdir == -1 ) return false; // need to be standing up
|
||||
if ( !user.player.onground ) return false; // need to be on solid ground
|
||||
if ( dvacationarghack && (target.args[0] == 1) ) return false; // can't pat at the moment
|
||||
// check use range
|
||||
Vector3 diff = level.Vec3Diff(user.Vec2OffsetZ(0,0,user.player.viewz),Vec3Offset(0,0,target.Height));
|
||||
if ( abs(diff.z) > 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;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
293
zscript/swwm_onfire.zsc
Normal file
293
zscript/swwm_onfire.zsc
Normal file
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pos = victim.Vec3Offset(FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](victim.height*0.2,victim.height*0.8));
|
||||
double ang = FRandom[FlameT](0,360);
|
||||
double pt = FRandom[FlameT](-90,90);
|
||||
if ( amount > 0 )
|
||||
{
|
||||
let c = victim.Spawn("OnFireTrail",pos);
|
||||
c.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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
335
zscript/swwm_player_fx.zsc
Normal file
335
zscript/swwm_player_fx.zsc
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
// player effects
|
||||
|
||||
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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
let raging = RagekitPower(target.FindInventory("RagekitPower"));
|
||||
if ( raging || swwm_omnibust )
|
||||
{
|
||||
// bust the floor if we can
|
||||
let tempme = new("LineTracer"); // gross hack to pass needed data
|
||||
int dmg = 40+min(special1,120);
|
||||
if ( raging ) dmg *= 8;
|
||||
F3DFloor ff;
|
||||
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(FloorSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
||||
ff = FloorSector.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) tempme.Results.ffloor = ff;
|
||||
tempme.Results.HitSector = FloorSector;
|
||||
tempme.Results.HitType = TRACE_HitFloor;
|
||||
BusterWall.Bust(tempme.Results,dmg,target,(0,0,-1),pos.z);
|
||||
if ( raging )
|
||||
{
|
||||
let ps = Spawn("BigPunchSplash",pos);
|
||||
ps.damagetype = 'GroundPound';
|
||||
ps.target = target;
|
||||
ps.special1 = dmg;
|
||||
raging.DoHitFX();
|
||||
}
|
||||
}
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
TNT1 A 140;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// 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<String> 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;
|
||||
}
|
||||
}
|
||||
172
zscript/swwm_player_items.zsc
Normal file
172
zscript/swwm_player_items.zsc
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
223
zscript/swwm_statichandler.zsc
Normal file
223
zscript/swwm_statichandler.zsc
Normal file
|
|
@ -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<mmvars.Size(); i++ )
|
||||
CVar.FindCVar(mmvars[i]).ResetToDefault();
|
||||
}
|
||||
}
|
||||
|
||||
override void NetworkProcess( ConsoleEvent e )
|
||||
{
|
||||
if ( e.Name ~== "swwmgetversion" )
|
||||
{
|
||||
let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC);
|
||||
let svd = SWWMSaveVerData(ti.Next());
|
||||
if ( svd ) Console.Printf("\cj%s\c-",svd.ver);
|
||||
else Console.Printf("\cg(no version data)\c-");
|
||||
if ( tainted ) Console.Printf("\cgversion mismatched\c-");
|
||||
else Console.Printf("\cdversion not mismatched\c-");
|
||||
return;
|
||||
}
|
||||
if ( e.IsManual ) return;
|
||||
if ( e.Name.Left(12) ~== "swwmversion." )
|
||||
{
|
||||
String verstr = e.Name.Mid(12);
|
||||
mprecv[e.Player] = true;
|
||||
mpver[e.Player] = verstr;
|
||||
if ( verstr != StringTable.Localize("$SWWM_SHORTVER") )
|
||||
mptaint[e.Player] = true;
|
||||
}
|
||||
}
|
||||
|
||||
override void PostUiTick()
|
||||
{
|
||||
// TODO achievement update code would go in here
|
||||
if ( gamestate != GS_LEVEL ) return;
|
||||
if ( !mpsent )
|
||||
{
|
||||
EventHandler.SendNetworkEvent("swwmversion."..StringTable.Localize("$SWWM_SHORTVER"));
|
||||
mpsent = true;
|
||||
return;
|
||||
}
|
||||
if ( checked || (gametic < checktic) ) return;
|
||||
if ( multiplayer )
|
||||
{
|
||||
for ( int i=0; i<MAXPLAYERS; i++ )
|
||||
{
|
||||
if ( !playeringame[i] || mprecv[i] ) continue;
|
||||
// waiting for version info from all players
|
||||
return;
|
||||
}
|
||||
}
|
||||
checked = true;
|
||||
String cver = StringTable.Localize("$SWWM_SHORTVER");
|
||||
if ( tainted )
|
||||
{
|
||||
let ti = ThinkerIterator.Create("SWWMSaveVerData",Thinker.STAT_STATIC);
|
||||
let svd = SWWMSaveVerData(ti.Next());
|
||||
if ( !svd ) Console.Printf("\cgWARNING:\n \cjSave contains no version data. Issues may happen.\c-");
|
||||
else
|
||||
{
|
||||
Console.Printf("\cgWARNING:\n \cjVersion mismatch with save data. Issues may happen.\c-");
|
||||
Console.Printf("\cgSaved:\n \cj"..svd.ver.."\c-");
|
||||
Console.Printf("\cgCurrent:\n \cj"..cver.."\c-");
|
||||
}
|
||||
}
|
||||
if ( multiplayer )
|
||||
{
|
||||
bool found = false;
|
||||
for ( int i=0; i<MAXPLAYERS; i++ )
|
||||
{
|
||||
if ( !playeringame[i] || (i == consoleplayer) || (!mptaint[i] && (mpver[i] != "")) ) continue;
|
||||
if ( !found )
|
||||
{
|
||||
Console.Printf("\cfWARNING:\n \cjVersion mismatch between players. Desyncs will happen.\c-");
|
||||
Console.Printf("\cgYou:\n \cj"..cver.."\c-");
|
||||
}
|
||||
found = true;
|
||||
Console.Printf("\cgPlayer %d (\c-%s\cg):\n \cj%s\c-",i+1,players[i].GetUserName(),(mpver[i]=="")?"\cg(no version data)\c-":mpver[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override void UiTick()
|
||||
{
|
||||
// HACK! Graf, please let us change this in a cleaner way
|
||||
if ( menuDelegate.GetClass() == 'DoomMenuDelegate' )
|
||||
{
|
||||
menuDelegate.Destroy();
|
||||
menuDelegate = new("SWWMMenuDelegate");
|
||||
}
|
||||
// Hey Graf how about you let us replace the class used for the
|
||||
// "Read This!" menu in mapinfo/gameinfo or something so I
|
||||
// don't have to do this hack here?
|
||||
Menu cur = Menu.GetCurrentMenu();
|
||||
if ( cur is 'ReadThisMenu' )
|
||||
{
|
||||
cur.Close();
|
||||
Menu.SetMenu('SWWMHelpMenu');
|
||||
}
|
||||
// Fancy crash effect
|
||||
if ( (gamestate == GS_LEVEL) || (gamestate == GS_TITLELEVEL) )
|
||||
{
|
||||
wasinmap = true;
|
||||
timer = 0;
|
||||
}
|
||||
else if ( (gamestate == GS_FULLCONSOLE) && ((wasinmap && !players[consoleplayer].viewheight) || (timer > 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load diff
572
zscript/swwm_thinkers_hud.zsc
Normal file
572
zscript/swwm_thinkers_hud.zsc
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
573
zscript/swwm_thinkers_player.zsc
Normal file
573
zscript/swwm_thinkers_player.zsc
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
// player-specific thinkers
|
||||
|
||||
|
||||
// Stats
|
||||
Class WeaponUsage
|
||||
{
|
||||
Class<Weapon> w;
|
||||
int kills;
|
||||
}
|
||||
|
||||
Class MonsterKill
|
||||
{
|
||||
Class<Actor> 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<WeaponUsage> wstats;
|
||||
Array<MonsterKill> mstats;
|
||||
Array<LevelStat> lstats;
|
||||
Array<Class<Weapon> > alreadygot;
|
||||
int favweapon;
|
||||
// these two are used for mission updates
|
||||
Array<int> clustervisit;
|
||||
Array<bool> secretdone;
|
||||
// [Strife] preserve previous mission logs
|
||||
String oldlogtext;
|
||||
Array<String> questbacklog;
|
||||
// hackaround for stuff getting lost
|
||||
Array<Class<SWWMCollectible> > ownedcollectibles;
|
||||
// for pistol start info (to avoid it within hubs)
|
||||
int lastcluster;
|
||||
|
||||
bool GotWeapon( Class<Weapon> which )
|
||||
{
|
||||
for ( int i=0; i<alreadygot.Size(); i++ )
|
||||
{
|
||||
if ( alreadygot[i] == which ) return true;
|
||||
}
|
||||
alreadygot.Push(which);
|
||||
return false;
|
||||
}
|
||||
|
||||
private Class<Weapon> WeaponFromInflictor( Actor inflictor, Name damagetype )
|
||||
{
|
||||
Class<Weapon> 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<mstats.Size(); i++ )
|
||||
{
|
||||
if ( mstats[i].m != victim.GetClass() ) continue;
|
||||
found = true;
|
||||
mstats[i].kills++;
|
||||
break;
|
||||
}
|
||||
if ( !found )
|
||||
{
|
||||
let ms = new("MonsterKill");
|
||||
ms.m = victim.GetClass();
|
||||
ms.kills = 1;
|
||||
mstats.Push(ms);
|
||||
}
|
||||
}
|
||||
Class<Weapon> which = WeaponFromInflictor(inflictor,damagetype);
|
||||
if ( !which ) return;
|
||||
for ( int i=0; i<wstats.Size(); i++ )
|
||||
{
|
||||
if ( wstats[i].w != which ) continue;
|
||||
wstats[i].kills++;
|
||||
if ( (favweapon == -1) || (wstats[favweapon].kills < wstats[i].kills) ) favweapon = i;
|
||||
return;
|
||||
}
|
||||
let ws = new("WeaponUsage");
|
||||
ws.w = which;
|
||||
ws.kills = 1;
|
||||
wstats.Push(ws);
|
||||
if ( (favweapon == -1) || (wstats[favweapon].kills < ws.kills) )
|
||||
favweapon = wstats.Size()-1;
|
||||
}
|
||||
|
||||
static clearscope SWWMStats Find( PlayerInfo p )
|
||||
{
|
||||
let ti = ThinkerIterator.Create("SWWMStats",STAT_STATIC);
|
||||
SWWMStats t;
|
||||
while ( t = SWWMStats(ti.Next()) )
|
||||
{
|
||||
if ( t.myplayer != p ) continue;
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Scoring
|
||||
Class SWWMCredits : Thinker
|
||||
{
|
||||
PlayerInfo myplayer;
|
||||
int credits, hcredits;
|
||||
|
||||
static void Give( PlayerInfo p, int amount, int hamount = 0 )
|
||||
{
|
||||
let c = Find(p);
|
||||
if ( !c ) return;
|
||||
if ( c.credits+amount < c.credits ) c.credits = int.max;
|
||||
else c.credits += amount;
|
||||
while ( c.credits > 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<Inventory> what;
|
||||
}
|
||||
|
||||
Class SWWMTradeHistory : Thinker
|
||||
{
|
||||
PlayerInfo myplayer;
|
||||
Array<SWWMTrade> ent;
|
||||
|
||||
static void RegisterSend( PlayerInfo p, PlayerInfo other, Class<Inventory> 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<Inventory> 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<SWWMLore> 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.Size(); i++ )
|
||||
{
|
||||
let c = (Class<SWWMCollectible>)(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<ent.Size(); i++ )
|
||||
{
|
||||
if ( ent[i].tag != "$"..tag ) continue;
|
||||
return true;
|
||||
}
|
||||
SWWMLore e = new("SWWMLore");
|
||||
e.tag = "$"..tag;
|
||||
if ( StringTable.Localize(e.tag) == "" )
|
||||
{
|
||||
Console.Printf("Entry \"%s\" has an empty tag.",ref);
|
||||
return true;
|
||||
}
|
||||
String ttab = StringTable.Localize(tab,false);
|
||||
if ( ttab ~== "People" ) e.tab = LORE_PEOPLE;
|
||||
else if ( ttab ~== "Lore" ) e.tab = LORE_LORE;
|
||||
else if ( ttab ~== "Item" ) e.tab = LORE_ITEM;
|
||||
else
|
||||
{
|
||||
Console.Printf("Entry \"%s\" has an incorrect tab setting of \"%s\".",ref,ttab);
|
||||
return true;
|
||||
}
|
||||
e.text = "$"..text;
|
||||
if ( StringTable.Localize(e.text) == "" )
|
||||
{
|
||||
Console.Printf("Entry \"%s\" has empty text.",ref);
|
||||
return true;
|
||||
}
|
||||
e.assoc = "$"..assoc;
|
||||
e.read = false;
|
||||
// "new lore" message
|
||||
if ( (level.maptime > 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<String> rel;
|
||||
rel.Clear();
|
||||
String assocstr = StringTable.Localize(ent[idx].assoc);
|
||||
assocstr.Split(rel,";",0);
|
||||
for ( int i=0; i<rel.Size(); i++ )
|
||||
{
|
||||
if ( (rel[i] != "") && !DirectAdd(rel[i]) )
|
||||
Console.Printf("Related entry \"%s\" not found, please update LANGUAGE.txt",rel[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearscope int FindEntry( String tag )
|
||||
{
|
||||
for ( int i=0; i<ent.Size(); i++ )
|
||||
{
|
||||
if ( ent[i].tag ~== tag )
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static clearscope SWWMLoreLibrary Find( PlayerInfo p )
|
||||
{
|
||||
let ti = ThinkerIterator.Create("SWWMLoreLibrary",STAT_STATIC);
|
||||
SWWMLoreLibrary ll;
|
||||
while ( ll = SWWMLoreLibrary(ti.Next()) )
|
||||
{
|
||||
if ( ll.myplayer != p ) continue;
|
||||
return ll;
|
||||
}
|
||||
return Null;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,9 +11,6 @@ enum EDoExplosionFlags
|
|||
DE_HOWL = 64, // 25% chance for hit enemies to howl
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
Class SWWMUtility
|
||||
{
|
||||
// thanks zscript
|
||||
|
|
@ -967,6 +964,157 @@ Class SWWMUtility
|
|||
return false;
|
||||
}
|
||||
|
||||
static play 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<l.backsector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
let ff = l.backsector.Get3DFloor(i);
|
||||
fal = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
||||
fah = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
||||
fbl = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
||||
fbh = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
||||
// skip if higher, we'll go with the lowest 3d floor (may not be right, but whatever)
|
||||
if ( floorfound && (fah.z > 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<l.frontsector.Get3DFloorCount(); i++ )
|
||||
{
|
||||
let ff = l.frontsector.Get3DFloor(i);
|
||||
fal = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
||||
fah = (l.v1.p,ff.model.floorplane.ZAtPoint(l.v1.p));
|
||||
fbl = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
||||
fbh = (l.v2.p,ff.model.ceilingplane.ZAtPoint(l.v2.p));
|
||||
// skip if higher, we'll go with the lowest 3d floor (may not be right, but whatever)
|
||||
if ( floorfound && (fah.z > 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 )
|
||||
{
|
||||
390
zscript/weapons/swwm_baseweapon.zsc
Normal file
390
zscript/weapons/swwm_baseweapon.zsc
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
// Base class for all SWWM Weapons
|
||||
Class SWWMWeapon : Weapon abstract
|
||||
{
|
||||
Mixin SWWMOverlapPickupSound;
|
||||
|
||||
bool wasused;
|
||||
private int SWeaponFlags;
|
||||
|
||||
Class<Ammo> 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<ngun; i++ )
|
||||
{
|
||||
let d = user.DropInventory(sw);
|
||||
if ( !d || (ngun <= 1) ) continue;
|
||||
// adjust angle for multi-drops
|
||||
d.angle = user.angle+ang;
|
||||
d.vel.xy = RotateVector((5,0),d.angle);
|
||||
d.vel.z = 1;
|
||||
d.vel += user.vel;
|
||||
ang += 30;
|
||||
}
|
||||
// don't autoswitch just yet (hacky)
|
||||
if ( swapto )
|
||||
{
|
||||
user.player.ReadyWeapon = null;
|
||||
user.player.PendingWeapon = WP_NOCHANGE;
|
||||
}
|
||||
}
|
||||
Touch(user);
|
||||
// we got picked up
|
||||
if ( bDestroyed || Owner || !bSPECIAL )
|
||||
{
|
||||
// autoswitch to us if we got swapped
|
||||
if ( swapto ) user.A_SelectWeapon(GetClass());
|
||||
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<cf.clines.Size(); i++ )
|
||||
cf.clines[i].Activate(user,cf.csides[i],SPAC_Cross);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// subtracts given ammo from price, drops excess
|
||||
virtual bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon )
|
||||
{
|
||||
// save time, always return false if we don't use ammo
|
||||
if ( !ownedWeapon.Ammo1 && !ownedWeapon.Ammo2 ) return false;
|
||||
bool gotstuff = false;
|
||||
int oldamount1 = 0, oldamount2 = 0;
|
||||
if ( ownedWeapon.Ammo1 ) oldamount1 = ownedWeapon.Ammo1.Amount;
|
||||
if ( ownedWeapon.Ammo2 ) oldamount2 = ownedWeapon.Ammo2.Amount;
|
||||
if ( AmmoGive1 > 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<Ammo> 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;
|
||||
}
|
||||
}
|
||||
291
zscript/weapons/swwm_baseweapon_fx.zsc
Normal file
291
zscript/weapons/swwm_baseweapon_fx.zsc
Normal file
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Junk](-.8,.8),FRandom[Junk](-.8,.8),FRandom[Junk](-.8,.8))).unit()*FRandom[Junk](0.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos+x*2);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Junk](128,192));
|
||||
}
|
||||
numpt = int(Random[Junk](3,8)*scale.x*4);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Junk](-1,1),FRandom[Junk](-1,1),FRandom[Junk](-1,1)).unit()*FRandom[Junk](2,8);
|
||||
let s = Spawn("SWWMSpark",pos+x*2);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = int(Random[Junk](2,5)*scale.x*4);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Junk](-1,1),FRandom[Junk](-1,1),FRandom[Junk](-1,1)).unit()*FRandom[Junk](2,8);
|
||||
let s = Spawn("SWWMChip",pos+x*2);
|
||||
s.vel = pvel;
|
||||
}
|
||||
if ( !Random[Junk](0,3) ) A_StartSound("bullet/ricochet",CHAN_VOICE,attenuation:2.5);
|
||||
else A_StartSound("bullet/hit",CHAN_VOICE,attenuation:3.0);
|
||||
Spawn("InvisibleSplasher",pos);
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
TNT1 A 1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class SWWMWeaponLight : DynamicLight
|
||||
{
|
||||
int cnt;
|
||||
Default
|
||||
{
|
||||
DynamicLight.Type "Point";
|
||||
args 255,224,64,150;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
Super.Tick();
|
||||
if ( !target )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if ( target.player )
|
||||
{
|
||||
Vector3 x, y, z, origin;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
|
||||
origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*12);
|
||||
SetOrigin(origin,true);
|
||||
}
|
||||
else SetOrigin(target.pos,true);
|
||||
if ( cnt++ > 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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
|
||||
}
|
||||
numpt = Random[Ponch](4,12);
|
||||
if ( bAMBUSH ) numpt /= 3;
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Ponch](4,8);
|
||||
if ( bAMBUSH ) numpt /= 3;
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class BigPunchSplash : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
DamageType 'Melee';
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
+NOGRAVITY;
|
||||
+NOCLIP;
|
||||
+DONTSPLASH;
|
||||
+NOTELEPORT;
|
||||
+NODAMAGETHRUST;
|
||||
+FORCERADIUSDMG;
|
||||
+NOINTERACTION;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
SWWMUtility.DoExplosion(self,special1,40000,120,60,ignoreme:target);
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class BigPunchImpact : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
+NOGRAVITY;
|
||||
+NOCLIP;
|
||||
+NOTELEPORT;
|
||||
+NOINTERACTION;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_QuakeEx(8,8,8,18,0,600,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.9);
|
||||
A_StartSound("pusher/althit",CHAN_VOICE,CHANF_DEFAULT,bAMBUSH?.6:1.);
|
||||
A_SprayDecal("BigWallCrack",-20);
|
||||
int numpt = Random[Ponch](9,16);
|
||||
if ( bAMBUSH ) numpt /= 3;
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
|
||||
}
|
||||
numpt = Random[Ponch](9,15);
|
||||
if ( bAMBUSH ) numpt /= 3;
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Ponch](9,16);
|
||||
if ( bAMBUSH ) numpt /= 3;
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
440
zscript/weapons/swwm_baseweapon_melee.zsc
Normal file
440
zscript/weapons/swwm_baseweapon_melee.zsc
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
// melee/parrying stuff
|
||||
|
||||
Class ParriedBuff : Inventory
|
||||
{
|
||||
Vector3 oldvel;
|
||||
|
||||
Default
|
||||
{
|
||||
+INVENTORY.UNDROPPABLE;
|
||||
+INVENTORY.UNTOSSABLE;
|
||||
Inventory.Amount 1;
|
||||
Inventory.MaxAmount 1;
|
||||
}
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( Owner.bBLASTED ) oldvel = Owner.vel;
|
||||
if ( !Owner.bMISSILE && !Owner.bSKULLFLY )
|
||||
{
|
||||
// lost soul is no longer attacking
|
||||
// remove blasted flag just in case
|
||||
Owner.bBLASTED = false;
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if ( special1 <= 0 ) return;
|
||||
// smoke trail
|
||||
Actor s;
|
||||
if ( special1&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,.6);
|
||||
s.scale *= 1.2;
|
||||
s.alpha *= .3;
|
||||
}
|
||||
if ( special1 > 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<UseList> 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<ut.uses.Size(); i++ )
|
||||
{
|
||||
if ( ut.uses[i].hitactor )
|
||||
{
|
||||
// punching is not greeting/patting (that'd be weird)
|
||||
if ( (ut.uses[i].hitactor == self) || (ut.uses[i].hitactor is 'Demolitionist')
|
||||
|| (ut.uses[i].hitactor is 'HeadpatTracker')
|
||||
|| (ut.uses[i].hitactor is 'FroggyChair') ) continue;
|
||||
if ( ut.uses[i].hitactor.Used(self) ) break;
|
||||
}
|
||||
else if ( ut.uses[i].hitline && UseLineTracer.TangibleLine(ut.uses[i]) )
|
||||
{
|
||||
int locknum = SWWMUtility.GetLineLock(ut.uses[i].hitline);
|
||||
if ( !locknum || CheckKeys(locknum,false,true) )
|
||||
ut.uses[i].hitline.RemoteActivate(self,ut.uses[i].hitside,SPAC_Use,ut.uses[i].pos);
|
||||
if ( !(ut.uses[i].hitline.activation&SPAC_UseThrough) ) break;
|
||||
}
|
||||
}
|
||||
invoker.wallponch = false;
|
||||
// check for shootables
|
||||
SWWMBulletTrail.DoTrail(self,origin,dir,DEFMELEERANGE*rangemul,0);
|
||||
let raging = RagekitPower(FindInventory("RagekitPower"));
|
||||
int maxang = raging?18:12;
|
||||
for ( int i=0; i<maxang; i++ )
|
||||
{
|
||||
if ( TryMelee(angle+i*(45./16),dmg,hitsound,rangemul) || TryMelee(angle-i*(45./16),dmg,hitsound,rangemul) )
|
||||
return;
|
||||
}
|
||||
// check for walls instead
|
||||
FTranslatedLineTarget t;
|
||||
double slope = AimLineAttack(angle,DEFMELEERANGE*rangemul,t,0.,ALF_CHECK3D);
|
||||
FLineTraceData d;
|
||||
LineTrace(angle,DEFMELEERANGE*rangemul,slope,TRF_THRUACTORS,player.viewheight,data:d);
|
||||
if ( d.HitType == TRACE_HitNone ) return;
|
||||
Vector3 HitNormal = -d.HitDir;
|
||||
if ( d.HitType == TRACE_HitFloor )
|
||||
{
|
||||
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.top.Normal;
|
||||
else HitNormal = d.HitSector.floorplane.Normal;
|
||||
}
|
||||
else if ( d.HitType == TRACE_HitCeiling )
|
||||
{
|
||||
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.bottom.Normal;
|
||||
else HitNormal = d.HitSector.ceilingplane.Normal;
|
||||
}
|
||||
else if ( d.HitType == TRACE_HitWall )
|
||||
{
|
||||
HitNormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
||||
if ( !d.LineSide ) HitNormal *= -1;
|
||||
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
|
||||
}
|
||||
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation+HitNormal*4);
|
||||
p.angle = atan2(HitNormal.y,HitNormal.x);
|
||||
p.pitch = asin(-HitNormal.z);
|
||||
if ( d.HitType == TRACE_HitFloor ) p.CheckSplash(40);
|
||||
if ( raging )
|
||||
{
|
||||
let ps = Spawn("BigPunchSplash",d.HitLocation+HitNormal*4);
|
||||
ps.target = self;
|
||||
ps.special1 = dmg;
|
||||
}
|
||||
int quakin = raging?4:1;
|
||||
A_QuakeEx(quakin,quakin,quakin,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12*quakin);
|
||||
A_StartSound(raging?"pusher/althit":(hitsound!="")?hitsound:"demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
|
||||
A_AlertMonsters(swwm_uncapalert?0:100);
|
||||
if ( raging ) raging.DoHitFX();
|
||||
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,raging?(dmg*8):dmg,self,d.HitDir,d.HitLocation.z);
|
||||
}
|
||||
}
|
||||
138
zscript/weapons/swwm_baseweapon_precisechair.zsc
Normal file
138
zscript/weapons/swwm_baseweapon_precisechair.zsc
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// precise crosshair
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
extend Class SWWMWeapon
|
||||
{
|
||||
ui SWWMCrosshairTracer ctr;
|
||||
ui Vector3 cpos;
|
||||
ui Color ccol;
|
||||
ui Vector2 lagvpos;
|
||||
transient ui uint prevms;
|
||||
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
// force custom crosshair
|
||||
if ( swwm_precisecrosshair ) crosshair = 99;
|
||||
else crosshair = 0;
|
||||
}
|
||||
|
||||
// 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.);
|
||||
}
|
||||
}
|
||||
890
zscript/weapons/swwm_blazeit.zsc
Normal file
890
zscript/weapons/swwm_blazeit.zsc
Normal file
|
|
@ -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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"};
|
||||
static const Class<Actor> 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<Ammo> 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<Ammo> 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<Ammo> kind )
|
||||
{
|
||||
static const Class<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> types[] = {"HellblazerMissiles","HellblazerCrackshots","HellblazerRavagers","HellblazerWarheads"};
|
||||
static const Class<Actor> 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<Ammo> 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<Ammo> 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<Ammo> kind )
|
||||
{
|
||||
static const Class<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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<Ammo> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<int> 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<Ammo> 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<Ammo> 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<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
|
||||
// there's probably a better way to do this but I'm lazy
|
||||
Array<Int> 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<queue.Size(); i++ )
|
||||
cbt.AppendFormat("%d,",queue[i]);
|
||||
MenuSound("menu/democlose");
|
||||
EventHandler.SendNetworkEvent(cbt,consoleplayer);
|
||||
Close();
|
||||
return true;
|
||||
case MKEY_UP:
|
||||
if ( queue.Size() <= 0 )
|
||||
{
|
||||
MenuSound("menu/noinvuse");
|
||||
return true;
|
||||
}
|
||||
PopAmmo();
|
||||
MenuSound("menu/demoscroll");
|
||||
return true;
|
||||
case MKEY_DOWN:
|
||||
if ( IsDone() )
|
||||
{
|
||||
MenuSound("menu/noinvuse");
|
||||
return true;
|
||||
}
|
||||
PushAmmo();
|
||||
return true;
|
||||
case MKEY_RIGHT:
|
||||
MenuSound("menu/demotab");
|
||||
sel0++;
|
||||
if ( sel0 > 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<Ammo> 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<l.Count(); i++ )
|
||||
{
|
||||
sw = l.StringWidth(i);
|
||||
if ( sw > 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<l.Count(); i++ )
|
||||
{
|
||||
Screen.DrawText(fnt,Font.CR_WHITE,x,y,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||||
y += 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<int> 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<boundsmax.z; z+=step.z )
|
||||
for ( y=boundsmin.y; y<boundsmax.y; y+=step.y )
|
||||
for ( x=boundsmin.x; x<boundsmax.x; x+=step.x )
|
||||
{
|
||||
Vector3 spot = (x,y,z);
|
||||
if ( level.PointInSector(spot.xy) != hitsector ) continue;
|
||||
spot += (FRandom[Wallbuster](-step.x,step.x),FRandom[Wallbuster](-step.y,step.y),FRandom[Wallbuster](-step.z,step.z));
|
||||
if ( !level.IsPointInLevel(spot) ) continue;
|
||||
if ( (initial || !(busttics%3)) && !Random[Wallbuster](0,2) )
|
||||
{
|
||||
Vector3 pvel = (bustdir+(FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.))).unit()*FRandom[Wallbuster](-2.,8.);
|
||||
let s = Actor.Spawn("SWWMSmoke",spot);
|
||||
s.vel = pvel;
|
||||
s.scale *= 2.5;
|
||||
s.special1 = Random[Wallbuster](3,8);
|
||||
s.SetShade(Color(1,1,1)*Random[Wallbuster](40,120));
|
||||
}
|
||||
if ( (!initial && (busttics%2)) || (busttics > 4) ) continue;
|
||||
int numpt = Random[Wallbuster](0,3);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (bustdir+(FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.))).unit()*FRandom[Wallbuster](9.,24.);
|
||||
let s = Actor.Spawn("SWWMSpark",spot);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Wallbuster](1,2);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (bustdir+(FRandom[Wallbuster](-.6,.6),FRandom[Wallbuster](-.6,.6),FRandom[Wallbuster](-.6,.6))).unit()*FRandom[Wallbuster](2.,16.);
|
||||
let s = Actor.Spawn("SWWMChip",spot);
|
||||
s.vel = pvel;
|
||||
s.scale *= FRandom[Wallbuster](1.5,3.);
|
||||
s.A_SetTranslation('Rubble');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsIOSWall( Line l )
|
||||
{
|
||||
TextureID facetex[9];
|
||||
facetex[0] = TexMan.CheckForTexture("ZZZFACE1",TexMan.Type_Wall);
|
||||
facetex[1] = TexMan.CheckForTexture("ZZZFACE2",TexMan.Type_Wall);
|
||||
facetex[2] = TexMan.CheckForTexture("ZZZFACE3",TexMan.Type_Wall);
|
||||
facetex[3] = TexMan.CheckForTexture("ZZZFACE4",TexMan.Type_Wall);
|
||||
facetex[4] = TexMan.CheckForTexture("ZZZFACE5",TexMan.Type_Wall);
|
||||
facetex[5] = TexMan.CheckForTexture("DBRAIN1",TexMan.Type_Wall);
|
||||
facetex[6] = TexMan.CheckForTexture("DBRAIN2",TexMan.Type_Wall);
|
||||
facetex[7] = TexMan.CheckForTexture("DBRAIN3",TexMan.Type_Wall);
|
||||
facetex[8] = TexMan.CheckForTexture("DBRAIN4",TexMan.Type_Wall);
|
||||
for ( int i=0; i<9; i++ )
|
||||
{
|
||||
for ( int j=0; j<3; j++ )
|
||||
{
|
||||
if ( l.sidedef[0].GetTexture(j) == facetex[i] ) return true;
|
||||
if ( l.sidedef[1] && l.sidedef[1].GetTexture(j) == facetex[i] ) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ProjectileBust( Actor a, int accdamage, Vector3 x )
|
||||
{
|
||||
LineTracer faketracer = new("LineTracer");
|
||||
F3DFloor ff;
|
||||
Vector3 HitNormal;
|
||||
if ( a.BlockingFloor )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<a.BlockingFloor.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(a.BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(a.BlockingFloor.Get3DFloor(i).top.ZAtPoint(a.pos.xy) ~== a.floorz) ) continue;
|
||||
ff = a.BlockingFloor.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff )
|
||||
{
|
||||
faketracer.Results.ffloor = ff;
|
||||
HitNormal = -ff.top.Normal;
|
||||
}
|
||||
else HitNormal = a.BlockingFloor.floorplane.Normal;
|
||||
faketracer.Results.HitType = TRACE_HitFloor;
|
||||
faketracer.Results.HitSector = a.BlockingFloor;
|
||||
}
|
||||
else if ( a.BlockingCeiling )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<a.BlockingCeiling.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(a.BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(a.BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(a.pos.xy) ~== a.ceilingz) ) continue;
|
||||
ff = a.BlockingCeiling.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff )
|
||||
{
|
||||
faketracer.Results.ffloor = ff;
|
||||
HitNormal = -ff.bottom.Normal;
|
||||
}
|
||||
else HitNormal = a.BlockingCeiling.ceilingplane.Normal;
|
||||
faketracer.Results.HitType = TRACE_HitCeiling;
|
||||
faketracer.Results.HitSector = a.BlockingCeiling;
|
||||
}
|
||||
else if ( a.BlockingLine )
|
||||
{
|
||||
HitNormal = (-a.BlockingLine.delta.y,a.BlockingLine.delta.x,0).unit();
|
||||
int wside = SWWMUtility.PointOnLineSide(a.pos.xy,a.BlockingLine);
|
||||
if ( !wside ) HitNormal *= -1;
|
||||
faketracer.Results.HitType = TRACE_HitWall;
|
||||
faketracer.Results.HitLine = a.BlockingLine;
|
||||
faketracer.Results.Side = wside;
|
||||
faketracer.Results.Tier = TIER_Middle;
|
||||
// guess the tier hit
|
||||
if ( a.BlockingLine.sidedef[1] )
|
||||
{
|
||||
double ceil = a.BlockingLine.sidedef[!wside].sector.ceilingplane.ZAtPoint(a.pos.xy);
|
||||
double flor = a.BlockingLine.sidedef[!wside].sector.floorplane.ZAtPoint(a.pos.xy);
|
||||
if ( a.pos.z >= 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<hs.lines.Size(); i++ )
|
||||
{
|
||||
if( IsIOSWall(hs.lines[i]) ) return false;
|
||||
}
|
||||
hp = 1;
|
||||
}
|
||||
else if ( d.HitType == TRACE_HitFloor )
|
||||
{
|
||||
// no busting the goat
|
||||
for ( int i=0; i<hs.lines.Size(); i++ )
|
||||
{
|
||||
if( IsIOSWall(hs.lines[i]) ) return false;
|
||||
}
|
||||
hp = 0;
|
||||
}
|
||||
else return false; // this isn't a valid hit, needs to be world geometry
|
||||
// Check if it's a door
|
||||
if ( !swwm_cbtall && !SWWMUtility.IsDoorSector(hs,hp) ) return false;
|
||||
let ti = ThinkerIterator.Create("BusterWall",STAT_USER);
|
||||
BusterWall iter, bust = null;
|
||||
while ( iter = BusterWall(ti.Next()) )
|
||||
{
|
||||
if ( (iter.hitsector != hs) || (iter.hitplane != hp) ) continue;
|
||||
bust = iter;
|
||||
break;
|
||||
}
|
||||
bool mnew = false;
|
||||
if ( !bust )
|
||||
{
|
||||
bust = new("BusterWall");
|
||||
bust.ChangeStatNum(STAT_USER);
|
||||
bust.hitsector = hs;
|
||||
bust.accdamage = 0;
|
||||
bust.hitplane = hp;
|
||||
bust.bustdir = x;
|
||||
mnew = true;
|
||||
}
|
||||
bust.delay = max(bust.delay,5+min(20,accdamage>>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<hs.lines.Size(); i++ )
|
||||
{
|
||||
Line l = hs.lines[i];
|
||||
if ( l.v1.p.x < a.x ) a.x = l.v1.p.x;
|
||||
if ( l.v2.p.x < a.x ) a.x = l.v2.p.x;
|
||||
if ( l.v1.p.y < a.y ) a.y = l.v1.p.y;
|
||||
if ( l.v2.p.y < a.y ) a.y = l.v2.p.y;
|
||||
if ( l.v1.p.x > 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<hs.Lines.Size(); i++ )
|
||||
{
|
||||
Line l = hs.Lines[i];
|
||||
int locknum = SWWMUtility.GetLineLock(l);
|
||||
if ( locknum && (!instigator || !instigator.CheckKeys(locknum,false,true)) ) continue;
|
||||
if ( !l.sidedef[1] ) continue;
|
||||
int away = 0;
|
||||
if ( l.sidedef[0].sector == hs ) away = 1;
|
||||
// temporarily set filler texture so switches don't play a sound
|
||||
TextureID oldtex[3][2];
|
||||
for ( int j=0; j<3; j++ ) for ( int k=0; k<2; k++ )
|
||||
{
|
||||
oldtex[j][k] = l.sidedef[k].GetTexture(j);
|
||||
l.sidedef[k].SetTexture(j,rubble);
|
||||
}
|
||||
l.Activate(instigator,away,SPAC_Use);
|
||||
l.Activate(instigator,away,SPAC_Impact);
|
||||
for ( int j=0; j<3; j++ ) for ( int k=0; k<2; k++ )
|
||||
l.sidedef[k].SetTexture(j,oldtex[j][k]);
|
||||
// clear any use/impact specials
|
||||
if ( l.Activation&(SPAC_Use|SPAC_Impact) )
|
||||
l.Activation &= ~(SPAC_Use|SPAC_Impact);
|
||||
}
|
||||
// stop all movers
|
||||
let ti2 = ThinkerIterator.Create("SectorEffect",STAT_SECTOREFFECT);
|
||||
SectorEffect se;
|
||||
while ( se = SectorEffect(ti2.Next()) )
|
||||
{
|
||||
if ( se.GetSector() != hs ) continue;
|
||||
se.Destroy();
|
||||
}
|
||||
bust.step = (clamp((b.x-a.x)/4.,2.,32.),clamp((b.y-a.y)/4.,2.,32.),clamp(partheight/4.,2.,32.));
|
||||
// stop all sound sequences
|
||||
for ( int i=1; i<=4; i++ ) hs.StopSoundSequence(i);
|
||||
// quakin'
|
||||
let q = Actor.Spawn("BustedQuake",(hs.centerspot.x,hs.centerspot.y,thisheight));
|
||||
q.special1 = clamp(int(girthitude**.15),1,9);
|
||||
if ( hp )
|
||||
{
|
||||
// blow up that ceiling
|
||||
hs.MoveCeiling(abs(partheight),bust.cutheight,0,1,false);
|
||||
bust.boundsmin = (a.x,a.y,thisheight)+(1,1,1);
|
||||
bust.boundsmax = (b.x,b.y,bust.cutheight)-(1,1,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// blow up that floor
|
||||
hs.MoveFloor(abs(partheight),abs(bust.cutheight),0,-1,false,true);
|
||||
bust.boundsmin = (a.x,a.y,bust.cutheight)+(1,1,1);
|
||||
bust.boundsmax = (b.x,b.y,thisheight)-(1,1,1);
|
||||
}
|
||||
bust.SpawnDebris(true);
|
||||
hs.SetTexture(hp,rubble);
|
||||
hs.SetXScale(hp,1.);
|
||||
hs.SetYScale(hp,1.);
|
||||
hs.SetAngle(hp,0.);
|
||||
for ( int i=0; i<hs.Lines.Size(); i++ )
|
||||
{
|
||||
Line l = hs.Lines[i];
|
||||
if ( !l.sidedef[1] )
|
||||
{
|
||||
if ( hp && !(l.flags&Line.ML_DONTPEGBOTTOM) )
|
||||
l.sidedef[0].AddTextureYOffset(1,-partheight); // shift down
|
||||
else if ( !hp && (l.flags&Line.ML_DONTPEGBOTTOM) )
|
||||
l.sidedef[0].AddTextureYOffset(1,partheight); // shift up
|
||||
continue;
|
||||
}
|
||||
int away = 0;
|
||||
if ( l.sidedef[0].sector == hs ) away = 1;
|
||||
for ( int j=0; j<2; j++ )
|
||||
{
|
||||
if ( l.sidedef[j].GetTexture(hp?0:2).IsValid() )
|
||||
{
|
||||
if ( j == away )
|
||||
{
|
||||
if ( hp && !(l.flags&Line.ML_DONTPEGTOP) )
|
||||
l.sidedef[j].AddTextureYOffset(0,-partheight); // shift down
|
||||
else if ( !hp && !(l.flags&Line.ML_DONTPEGBOTTOM) )
|
||||
l.sidedef[j].AddTextureYOffset(2,partheight); // shift up
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
l.sidedef[j].SetTexture(hp?0:2,rubble);
|
||||
l.sidedef[j].SetTextureXScale(hp?0:2,1.);
|
||||
l.sidedef[j].SetTextureYScale(hp?0:2,1.);
|
||||
}
|
||||
}
|
||||
}
|
||||
// damnums
|
||||
Vector3 bcenter = (bust.boundsmin+bust.boundsmax)*.5;
|
||||
if ( swwm_accdamage )
|
||||
SWWMScoreObj.Spawn(-bust.accdamage,level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
|
||||
else for ( int i=0; i<bust.acchits.Size(); i++ )
|
||||
SWWMScoreObj.Spawn(-bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Class Wallbuster : SWWMWeapon
|
||||
{
|
||||
Class<Ammo> loaded[25];
|
||||
456
zscript/weapons/swwm_cbt_fx.zsc
Normal file
456
zscript/weapons/swwm_cbt_fx.zsc
Normal file
|
|
@ -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<int> 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<boundsmax.z; z+=step.z )
|
||||
for ( y=boundsmin.y; y<boundsmax.y; y+=step.y )
|
||||
for ( x=boundsmin.x; x<boundsmax.x; x+=step.x )
|
||||
{
|
||||
Vector3 spot = (x,y,z);
|
||||
if ( level.PointInSector(spot.xy) != hitsector ) continue;
|
||||
spot += (FRandom[Wallbuster](-step.x,step.x),FRandom[Wallbuster](-step.y,step.y),FRandom[Wallbuster](-step.z,step.z));
|
||||
if ( !level.IsPointInLevel(spot) ) continue;
|
||||
if ( (initial || !(busttics%3)) && !Random[Wallbuster](0,2) )
|
||||
{
|
||||
Vector3 pvel = (bustdir+(FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.))).unit()*FRandom[Wallbuster](-2.,8.);
|
||||
let s = Actor.Spawn("SWWMSmoke",spot);
|
||||
s.vel = pvel;
|
||||
s.scale *= 2.5;
|
||||
s.special1 = Random[Wallbuster](3,8);
|
||||
s.SetShade(Color(1,1,1)*Random[Wallbuster](40,120));
|
||||
}
|
||||
if ( (!initial && (busttics%2)) || (busttics > 4) ) continue;
|
||||
int numpt = Random[Wallbuster](0,3);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (bustdir+(FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.))).unit()*FRandom[Wallbuster](9.,24.);
|
||||
let s = Actor.Spawn("SWWMSpark",spot);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Wallbuster](1,2);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (bustdir+(FRandom[Wallbuster](-.6,.6),FRandom[Wallbuster](-.6,.6),FRandom[Wallbuster](-.6,.6))).unit()*FRandom[Wallbuster](2.,16.);
|
||||
let s = Actor.Spawn("SWWMChip",spot);
|
||||
s.vel = pvel;
|
||||
s.scale *= FRandom[Wallbuster](1.5,3.);
|
||||
s.A_SetTranslation('Rubble');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsIOSWall( Line l )
|
||||
{
|
||||
TextureID facetex[9];
|
||||
facetex[0] = TexMan.CheckForTexture("ZZZFACE1",TexMan.Type_Wall);
|
||||
facetex[1] = TexMan.CheckForTexture("ZZZFACE2",TexMan.Type_Wall);
|
||||
facetex[2] = TexMan.CheckForTexture("ZZZFACE3",TexMan.Type_Wall);
|
||||
facetex[3] = TexMan.CheckForTexture("ZZZFACE4",TexMan.Type_Wall);
|
||||
facetex[4] = TexMan.CheckForTexture("ZZZFACE5",TexMan.Type_Wall);
|
||||
facetex[5] = TexMan.CheckForTexture("DBRAIN1",TexMan.Type_Wall);
|
||||
facetex[6] = TexMan.CheckForTexture("DBRAIN2",TexMan.Type_Wall);
|
||||
facetex[7] = TexMan.CheckForTexture("DBRAIN3",TexMan.Type_Wall);
|
||||
facetex[8] = TexMan.CheckForTexture("DBRAIN4",TexMan.Type_Wall);
|
||||
for ( int i=0; i<9; i++ )
|
||||
{
|
||||
for ( int j=0; j<3; j++ )
|
||||
{
|
||||
if ( l.sidedef[0].GetTexture(j) == facetex[i] ) return true;
|
||||
if ( l.sidedef[1] && l.sidedef[1].GetTexture(j) == facetex[i] ) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ProjectileBust( Actor a, int accdamage, Vector3 x )
|
||||
{
|
||||
LineTracer faketracer = new("LineTracer");
|
||||
F3DFloor ff;
|
||||
Vector3 HitNormal;
|
||||
if ( a.BlockingFloor )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<a.BlockingFloor.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(a.BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(a.BlockingFloor.Get3DFloor(i).top.ZAtPoint(a.pos.xy) ~== a.floorz) ) continue;
|
||||
ff = a.BlockingFloor.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff )
|
||||
{
|
||||
faketracer.Results.ffloor = ff;
|
||||
HitNormal = -ff.top.Normal;
|
||||
}
|
||||
else HitNormal = a.BlockingFloor.floorplane.Normal;
|
||||
faketracer.Results.HitType = TRACE_HitFloor;
|
||||
faketracer.Results.HitSector = a.BlockingFloor;
|
||||
}
|
||||
else if ( a.BlockingCeiling )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<a.BlockingCeiling.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(a.BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(a.BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(a.pos.xy) ~== a.ceilingz) ) continue;
|
||||
ff = a.BlockingCeiling.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff )
|
||||
{
|
||||
faketracer.Results.ffloor = ff;
|
||||
HitNormal = -ff.bottom.Normal;
|
||||
}
|
||||
else HitNormal = a.BlockingCeiling.ceilingplane.Normal;
|
||||
faketracer.Results.HitType = TRACE_HitCeiling;
|
||||
faketracer.Results.HitSector = a.BlockingCeiling;
|
||||
}
|
||||
else if ( a.BlockingLine )
|
||||
{
|
||||
HitNormal = (-a.BlockingLine.delta.y,a.BlockingLine.delta.x,0).unit();
|
||||
int wside = SWWMUtility.PointOnLineSide(a.pos.xy,a.BlockingLine);
|
||||
if ( !wside ) HitNormal *= -1;
|
||||
faketracer.Results.HitType = TRACE_HitWall;
|
||||
faketracer.Results.HitLine = a.BlockingLine;
|
||||
faketracer.Results.Side = wside;
|
||||
faketracer.Results.Tier = TIER_Middle;
|
||||
// guess the tier hit
|
||||
if ( a.BlockingLine.sidedef[1] )
|
||||
{
|
||||
double ceil = a.BlockingLine.sidedef[!wside].sector.ceilingplane.ZAtPoint(a.pos.xy);
|
||||
double flor = a.BlockingLine.sidedef[!wside].sector.floorplane.ZAtPoint(a.pos.xy);
|
||||
if ( a.pos.z >= 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<hs.lines.Size(); i++ )
|
||||
{
|
||||
if( IsIOSWall(hs.lines[i]) ) return false;
|
||||
}
|
||||
hp = 1;
|
||||
}
|
||||
else if ( d.HitType == TRACE_HitFloor )
|
||||
{
|
||||
// no busting the goat
|
||||
for ( int i=0; i<hs.lines.Size(); i++ )
|
||||
{
|
||||
if( IsIOSWall(hs.lines[i]) ) return false;
|
||||
}
|
||||
hp = 0;
|
||||
}
|
||||
else return false; // this isn't a valid hit, needs to be world geometry
|
||||
// Check if it's a door
|
||||
if ( !swwm_cbtall && !SWWMUtility.IsDoorSector(hs,hp) ) return false;
|
||||
let ti = ThinkerIterator.Create("BusterWall",STAT_USER);
|
||||
BusterWall iter, bust = null;
|
||||
while ( iter = BusterWall(ti.Next()) )
|
||||
{
|
||||
if ( (iter.hitsector != hs) || (iter.hitplane != hp) ) continue;
|
||||
bust = iter;
|
||||
break;
|
||||
}
|
||||
bool mnew = false;
|
||||
if ( !bust )
|
||||
{
|
||||
bust = new("BusterWall");
|
||||
bust.ChangeStatNum(STAT_USER);
|
||||
bust.hitsector = hs;
|
||||
bust.accdamage = 0;
|
||||
bust.hitplane = hp;
|
||||
bust.bustdir = x;
|
||||
mnew = true;
|
||||
}
|
||||
bust.delay = max(bust.delay,5+min(20,accdamage>>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<hs.lines.Size(); i++ )
|
||||
{
|
||||
Line l = hs.lines[i];
|
||||
if ( l.v1.p.x < a.x ) a.x = l.v1.p.x;
|
||||
if ( l.v2.p.x < a.x ) a.x = l.v2.p.x;
|
||||
if ( l.v1.p.y < a.y ) a.y = l.v1.p.y;
|
||||
if ( l.v2.p.y < a.y ) a.y = l.v2.p.y;
|
||||
if ( l.v1.p.x > 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<hs.Lines.Size(); i++ )
|
||||
{
|
||||
Line l = hs.Lines[i];
|
||||
int locknum = SWWMUtility.GetLineLock(l);
|
||||
if ( locknum && (!instigator || !instigator.CheckKeys(locknum,false,true)) ) continue;
|
||||
if ( !l.sidedef[1] ) continue;
|
||||
int away = 0;
|
||||
if ( l.sidedef[0].sector == hs ) away = 1;
|
||||
// temporarily set filler texture so switches don't play a sound
|
||||
TextureID oldtex[3][2];
|
||||
for ( int j=0; j<3; j++ ) for ( int k=0; k<2; k++ )
|
||||
{
|
||||
oldtex[j][k] = l.sidedef[k].GetTexture(j);
|
||||
l.sidedef[k].SetTexture(j,rubble);
|
||||
}
|
||||
l.Activate(instigator,away,SPAC_Use);
|
||||
l.Activate(instigator,away,SPAC_Impact);
|
||||
for ( int j=0; j<3; j++ ) for ( int k=0; k<2; k++ )
|
||||
l.sidedef[k].SetTexture(j,oldtex[j][k]);
|
||||
// clear any use/impact specials
|
||||
if ( l.Activation&(SPAC_Use|SPAC_Impact) )
|
||||
l.Activation &= ~(SPAC_Use|SPAC_Impact);
|
||||
}
|
||||
// stop all movers
|
||||
let ti2 = ThinkerIterator.Create("SectorEffect",STAT_SECTOREFFECT);
|
||||
SectorEffect se;
|
||||
while ( se = SectorEffect(ti2.Next()) )
|
||||
{
|
||||
if ( se.GetSector() != hs ) continue;
|
||||
se.Destroy();
|
||||
}
|
||||
bust.step = (clamp((b.x-a.x)/4.,2.,32.),clamp((b.y-a.y)/4.,2.,32.),clamp(partheight/4.,2.,32.));
|
||||
// stop all sound sequences
|
||||
for ( int i=1; i<=4; i++ ) hs.StopSoundSequence(i);
|
||||
// quakin'
|
||||
let q = Actor.Spawn("BustedQuake",(hs.centerspot.x,hs.centerspot.y,thisheight));
|
||||
q.special1 = clamp(int(girthitude**.15),1,9);
|
||||
if ( hp )
|
||||
{
|
||||
// blow up that ceiling
|
||||
hs.MoveCeiling(abs(partheight),bust.cutheight,0,1,false);
|
||||
bust.boundsmin = (a.x,a.y,thisheight)+(1,1,1);
|
||||
bust.boundsmax = (b.x,b.y,bust.cutheight)-(1,1,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// blow up that floor
|
||||
hs.MoveFloor(abs(partheight),abs(bust.cutheight),0,-1,false,true);
|
||||
bust.boundsmin = (a.x,a.y,bust.cutheight)+(1,1,1);
|
||||
bust.boundsmax = (b.x,b.y,thisheight)-(1,1,1);
|
||||
}
|
||||
bust.SpawnDebris(true);
|
||||
hs.SetTexture(hp,rubble);
|
||||
hs.SetXScale(hp,1.);
|
||||
hs.SetYScale(hp,1.);
|
||||
hs.SetAngle(hp,0.);
|
||||
for ( int i=0; i<hs.Lines.Size(); i++ )
|
||||
{
|
||||
Line l = hs.Lines[i];
|
||||
if ( !l.sidedef[1] )
|
||||
{
|
||||
if ( hp && !(l.flags&Line.ML_DONTPEGBOTTOM) )
|
||||
l.sidedef[0].AddTextureYOffset(1,-partheight); // shift down
|
||||
else if ( !hp && (l.flags&Line.ML_DONTPEGBOTTOM) )
|
||||
l.sidedef[0].AddTextureYOffset(1,partheight); // shift up
|
||||
continue;
|
||||
}
|
||||
int away = 0;
|
||||
if ( l.sidedef[0].sector == hs ) away = 1;
|
||||
for ( int j=0; j<2; j++ )
|
||||
{
|
||||
if ( l.sidedef[j].GetTexture(hp?0:2).IsValid() )
|
||||
{
|
||||
if ( j == away )
|
||||
{
|
||||
if ( hp && !(l.flags&Line.ML_DONTPEGTOP) )
|
||||
l.sidedef[j].AddTextureYOffset(0,-partheight); // shift down
|
||||
else if ( !hp && !(l.flags&Line.ML_DONTPEGBOTTOM) )
|
||||
l.sidedef[j].AddTextureYOffset(2,partheight); // shift up
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
l.sidedef[j].SetTexture(hp?0:2,rubble);
|
||||
l.sidedef[j].SetTextureXScale(hp?0:2,1.);
|
||||
l.sidedef[j].SetTextureYScale(hp?0:2,1.);
|
||||
}
|
||||
}
|
||||
}
|
||||
// damnums
|
||||
Vector3 bcenter = (bust.boundsmin+bust.boundsmax)*.5;
|
||||
if ( swwm_accdamage )
|
||||
SWWMScoreObj.Spawn(-bust.accdamage,level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
|
||||
else for ( int i=0; i<bust.acchits.Size(); i++ )
|
||||
SWWMScoreObj.Spawn(-bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
368
zscript/weapons/swwm_cbt_ui.zsc
Normal file
368
zscript/weapons/swwm_cbt_ui.zsc
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
// when you need a whole-ass menu to reload a weapon
|
||||
|
||||
Class WallbusterReloadMenu : GenericMenu
|
||||
{
|
||||
transient Font TewiFont, MPlusFont, MiniwiFont, k6x8Font;
|
||||
TextureID MainWindow, AmmoIcon[4];
|
||||
int sel0;
|
||||
Array<int> 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<Ammo> 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<Ammo> 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<Ammo> types[] = {"RedShell","GreenShell","BlueShell","PurpleShell"};
|
||||
// there's probably a better way to do this but I'm lazy
|
||||
Array<Int> 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<queue.Size(); i++ )
|
||||
cbt.AppendFormat("%d,",queue[i]);
|
||||
MenuSound("menu/democlose");
|
||||
EventHandler.SendNetworkEvent(cbt,consoleplayer);
|
||||
Close();
|
||||
return true;
|
||||
case MKEY_UP:
|
||||
if ( queue.Size() <= 0 )
|
||||
{
|
||||
MenuSound("menu/noinvuse");
|
||||
return true;
|
||||
}
|
||||
PopAmmo();
|
||||
MenuSound("menu/demoscroll");
|
||||
return true;
|
||||
case MKEY_DOWN:
|
||||
if ( IsDone() )
|
||||
{
|
||||
MenuSound("menu/noinvuse");
|
||||
return true;
|
||||
}
|
||||
PushAmmo();
|
||||
return true;
|
||||
case MKEY_RIGHT:
|
||||
MenuSound("menu/demotab");
|
||||
sel0++;
|
||||
if ( sel0 > 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<Ammo> 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<l.Count(); i++ )
|
||||
{
|
||||
sw = l.StringWidth(i);
|
||||
if ( sw > 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<l.Count(); i++ )
|
||||
{
|
||||
Screen.DrawText(fnt,Font.CR_WHITE,x,y,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
||||
y += 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
505
zscript/weapons/swwm_danmaku.zsc
Normal file
505
zscript/weapons/swwm_danmaku.zsc
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
// Mr. BIG SHOT Industries "Eviscerator" High Load Flak Cannon (from SWWM series)
|
||||
// Slot 5, replaces Chaingun, Dragon Claw, Hammer of Retribution
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
544
zscript/weapons/swwm_deathlydeathcannon.zsc
Normal file
544
zscript/weapons/swwm_deathlydeathcannon.zsc
Normal file
|
|
@ -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<ns; i++ )
|
||||
{
|
||||
Sector s = level.Sectors[i];
|
||||
for ( Actor a=s.thinglist; a; a=a.snext )
|
||||
a.LastHeard = self;
|
||||
}
|
||||
}
|
||||
else A_AlertMonsters(); // full range alert
|
||||
A_QuakeEx(9,9,9,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:2.5);
|
||||
A_ZoomFactor(.7,ZOOM_INSTANT);
|
||||
A_ZoomFactor(1.);
|
||||
A_PlayerFire();
|
||||
SWWMHandler.DoFlash(self,Color(120,255,255,255),30);
|
||||
A_Overlay(PSP_WEAPON+1,"FireSmoke");
|
||||
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.);
|
||||
invoker.chargestate = CS_POSTFIRE;
|
||||
invoker.clipcount = 0;
|
||||
Vector3 x, y, z, origin;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*15+y*4-z);
|
||||
Actor s;
|
||||
if ( invoker.inverted ) s = Spawn("YnykronAltShot",origin);
|
||||
else s = Spawn("YnykronShot",origin);
|
||||
s.target = self;
|
||||
s.angle = angle;
|
||||
s.pitch = BulletSlope();
|
||||
invoker.specialf1 = 1.;
|
||||
A_Overlay(PSP_WEAPON+3,"FireBlast");
|
||||
}
|
||||
|
||||
action void A_Backblast()
|
||||
{
|
||||
Vector3 x, y, z, origin;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),-x*15+y*4-z);
|
||||
int numpt = Random[Ynykron](10,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("SWWMSmoke",origin);
|
||||
s.scale *= 3.;
|
||||
s.alpha *= .4*invoker.specialf1;
|
||||
s.special1 = Random[Ynykron](2,8);
|
||||
s.vel += x*FRandom[Ynykron](-40.,-4.)*invoker.specialf1+y*FRandom[Ynykron](-1.,1.)+z*FRandom[Ynykron](-1.,1.);
|
||||
}
|
||||
invoker.specialf1 -= .2;
|
||||
}
|
||||
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !Owner || !Owner.player ) return;
|
||||
if ( chargestate == CS_IDLE ) chargelevel = 0.;
|
||||
else if ( chargestate == CS_CHARGING )
|
||||
{
|
||||
chargelevel = min(chargelevel*0.997+.2,40.);
|
||||
if ( chargelevel >= 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ns; i++ )
|
||||
{
|
||||
Sector s = level.Sectors[i];
|
||||
for ( Actor a=s.thinglist; a; a=a.snext )
|
||||
a.LastHeard = self;
|
||||
}
|
||||
}
|
||||
else A_AlertMonsters(); // full range alert
|
||||
A_QuakeEx(9,9,9,4,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:2.5);
|
||||
A_ZoomFactor(.7,ZOOM_INSTANT);
|
||||
A_ZoomFactor(1.);
|
||||
A_PlayerFire();
|
||||
SWWMHandler.DoFlash(self,Color(120,255,255,255),30);
|
||||
A_Overlay(PSP_WEAPON+1,"FireSmoke");
|
||||
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.);
|
||||
invoker.chargestate = CS_POSTFIRE;
|
||||
invoker.clipcount = 0;
|
||||
Vector3 x, y, z, origin;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*15+y*4-z);
|
||||
Actor s;
|
||||
if ( invoker.inverted ) s = Spawn("YnykronAltShot",origin);
|
||||
else s = Spawn("YnykronShot",origin);
|
||||
s.target = self;
|
||||
s.angle = angle;
|
||||
s.pitch = BulletSlope();
|
||||
invoker.specialf1 = 1.;
|
||||
A_Overlay(PSP_WEAPON+3,"FireBlast");
|
||||
}
|
||||
|
||||
action void A_Backblast()
|
||||
{
|
||||
Vector3 x, y, z, origin;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
|
||||
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),-x*15+y*4-z);
|
||||
int numpt = Random[Ynykron](10,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("SWWMSmoke",origin);
|
||||
s.scale *= 3.;
|
||||
s.alpha *= .4*invoker.specialf1;
|
||||
s.special1 = Random[Ynykron](2,8);
|
||||
s.vel += x*FRandom[Ynykron](-40.,-4.)*invoker.specialf1+y*FRandom[Ynykron](-1.,1.)+z*FRandom[Ynykron](-1.,1.);
|
||||
}
|
||||
invoker.specialf1 -= .2;
|
||||
}
|
||||
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !Owner || !Owner.player ) return;
|
||||
if ( chargestate == CS_IDLE ) chargelevel = 0.;
|
||||
else if ( chargestate == CS_CHARGING )
|
||||
{
|
||||
chargelevel = min(chargelevel*0.997+.2,40.);
|
||||
if ( chargelevel >= 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.1,.8);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel+vel.unit()*3.;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](240,255));
|
||||
s.special1 = Random[ExploS](1,2);
|
||||
s.scale *= 1.8;
|
||||
s.alpha *= .2;
|
||||
}
|
||||
bAMBUSH = true;
|
||||
SWWMUtility.DoExplosion(self,GetMissileDamage(0,0),0,80,ignoreme:target);
|
||||
bAMBUSH = false;
|
||||
tcnt++;
|
||||
if ( tcnt < 2 ) return;
|
||||
tcnt = 0;
|
||||
Spawn("AirRingLight",pos);
|
||||
}
|
||||
void A_Splode()
|
||||
{
|
||||
A_AlertMonsters(swwm_uncapalert?0:8000);
|
||||
if ( target && SWWMUtility.SphereIntersect(target,pos,120) )
|
||||
{
|
||||
// push away
|
||||
Vector3 dir = level.Vec3Diff(pos,target.Vec3Offset(0,0,target.height/2));
|
||||
double dist = dir.length();
|
||||
dir /= dist;
|
||||
double mm = 8000000./max(20.,dist);
|
||||
if ( (target.pos.z > 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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](240,255));
|
||||
s.special1 = Random[ExploS](1,2);
|
||||
s.scale *= 2.4;
|
||||
s.alpha *= .4;
|
||||
}
|
||||
numpt = Random[ExploS](8,12);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[ExploS](6,16);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,100,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
TNT1 A -1;
|
||||
Stop;
|
||||
Death:
|
||||
TNT1 A 1 A_Splode();
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class AirBulletLight : PaletteLight
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "ImpactWav";
|
||||
ReactionTime 5;
|
||||
Args 0,0,0,150;
|
||||
}
|
||||
}
|
||||
|
||||
Class AirRingLight : PaletteLight
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "ImpactWav,1";
|
||||
ReactionTime 30;
|
||||
Args 0,0,0,120;
|
||||
}
|
||||
}
|
||||
|
||||
Class AirBulletRing : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
RenderStyle "Add";
|
||||
+NOGRAVITY;
|
||||
+NOBLOCKMAP;
|
||||
+DONTSPLASH;
|
||||
+ROLLSPRITE;
|
||||
+ROLLCENTER;
|
||||
+FORCEXYBILLBOARD;
|
||||
+NOINTERACTION;
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( isFrozen() ) return;
|
||||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||||
if ( tics > 0 ) tics--;
|
||||
while ( !tics )
|
||||
{
|
||||
if ( !SetState(CurState.NextState) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XRG1 ABCDEFGHIJKLMNOPQRSTUVWX 2;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class DeepTracer : LineTracer
|
||||
{
|
||||
Actor ignoreme;
|
||||
174
zscript/weapons/swwm_deepdarkimpact_fx.zsc
Normal file
174
zscript/weapons/swwm_deepdarkimpact_fx.zsc
Normal file
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.1,.8);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel+vel.unit()*3.;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](240,255));
|
||||
s.special1 = Random[ExploS](1,2);
|
||||
s.scale *= 1.8;
|
||||
s.alpha *= .2;
|
||||
}
|
||||
bAMBUSH = true;
|
||||
SWWMUtility.DoExplosion(self,GetMissileDamage(0,0),0,80,ignoreme:target);
|
||||
bAMBUSH = false;
|
||||
tcnt++;
|
||||
if ( tcnt < 2 ) return;
|
||||
tcnt = 0;
|
||||
Spawn("AirRingLight",pos);
|
||||
}
|
||||
void A_Splode()
|
||||
{
|
||||
A_AlertMonsters(swwm_uncapalert?0:8000);
|
||||
if ( target && SWWMUtility.SphereIntersect(target,pos,120) )
|
||||
{
|
||||
// push away
|
||||
Vector3 dir = level.Vec3Diff(pos,target.Vec3Offset(0,0,target.height/2));
|
||||
double dist = dir.length();
|
||||
dir /= dist;
|
||||
double mm = 8000000./max(20.,dist);
|
||||
if ( (target.pos.z > 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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](240,255));
|
||||
s.special1 = Random[ExploS](1,2);
|
||||
s.scale *= 2.4;
|
||||
s.alpha *= .4;
|
||||
}
|
||||
numpt = Random[ExploS](8,12);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[ExploS](6,16);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,100,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
TNT1 A -1;
|
||||
Stop;
|
||||
Death:
|
||||
TNT1 A 1 A_Splode();
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class AirBulletLight : PaletteLight
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "ImpactWav";
|
||||
ReactionTime 5;
|
||||
Args 0,0,0,150;
|
||||
}
|
||||
}
|
||||
|
||||
Class AirRingLight : PaletteLight
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "ImpactWav,1";
|
||||
ReactionTime 30;
|
||||
Args 0,0,0,120;
|
||||
}
|
||||
}
|
||||
|
||||
Class AirBulletRing : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
RenderStyle "Add";
|
||||
+NOGRAVITY;
|
||||
+NOBLOCKMAP;
|
||||
+DONTSPLASH;
|
||||
+ROLLSPRITE;
|
||||
+ROLLCENTER;
|
||||
+FORCEXYBILLBOARD;
|
||||
+NOINTERACTION;
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( isFrozen() ) return;
|
||||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||||
if ( tics > 0 ) tics--;
|
||||
while ( !tics )
|
||||
{
|
||||
if ( !SetState(CurState.NextState) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XRG1 ABCDEFGHIJKLMNOPQRSTUVWX 2;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8))).unit()*FRandom[Pusher](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Pusher](128,192));
|
||||
}
|
||||
numpt = Random[Pusher](0,2);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Pusher](1,3);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class BigPusherImpact : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
+NOGRAVITY;
|
||||
+NOCLIP;
|
||||
+NOTELEPORT;
|
||||
+NOINTERACTION;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_QuakeEx(5,5,5,15,0,300,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.8);
|
||||
A_StartSound("pusher/althit",CHAN_VOICE);
|
||||
A_SprayDecal("ImpactMark",-20);
|
||||
int numpt = Random[Pusher](8,16);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8))).unit()*FRandom[Pusher](.8,2.6);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Pusher](128,192));
|
||||
}
|
||||
numpt = Random[Pusher](4,10);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,12);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Pusher](8,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,16);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class PusherProjectile : Actor
|
||||
{
|
||||
Vector3 oldvel;
|
||||
double oldangle, oldpitch;
|
||||
Actor lasthit;
|
||||
int hittics;
|
||||
|
||||
Default
|
||||
{
|
||||
Obituary "$O_PUSHER";
|
||||
Speed 50;
|
||||
Radius 10;
|
||||
Height 10;
|
||||
DamageFunction clamp(int(3*vel.length()),0,150);
|
||||
DamageType 'Tenderize';
|
||||
BounceType "Hexen";
|
||||
BounceFactor 1.0;
|
||||
WallBounceFactor 1.0;
|
||||
Gravity 0.3;
|
||||
PROJECTILE;
|
||||
+USEBOUNCESTATE;
|
||||
+CANBOUNCEWATER;
|
||||
+INTERPOLATEANGLES;
|
||||
+DONTBOUNCEONSHOOTABLES;
|
||||
-BOUNCEAUTOOFF;
|
||||
+NODAMAGETHRUST;
|
||||
-BOUNCEONUNRIPPABLES;
|
||||
-ALLOWBOUNCEONACTORS;
|
||||
+RIPPER;
|
||||
-NOGRAVITY;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_StartSound("pusher/fly",CHAN_BODY,CHANF_LOOP,1.,2.);
|
||||
A_StartSound("pusher/altfire",CHAN_VOICE);
|
||||
oldvel = vel;
|
||||
oldangle = angle;
|
||||
oldpitch = pitch;
|
||||
}
|
||||
void A_Reorient()
|
||||
{
|
||||
oldvel = vel;
|
||||
oldangle = angle;
|
||||
oldpitch = pitch;
|
||||
if ( vel.length() <= 0. ) return;
|
||||
Vector3 dir = vel.unit();
|
||||
angle += clamp(deltaangle(angle,atan2(dir.y,dir.x)),-2,2);
|
||||
pitch += clamp(deltaangle(pitch,asin(-dir.z)),-2,2);
|
||||
// cancel if we hit a wall dead-on
|
||||
FLineTraceData d;
|
||||
LineTrace(oldangle,30,oldpitch,TRF_THRUACTORS|TRF_NOSKY,5,data:d);
|
||||
if ( d.HitType != TRACE_HitNone )
|
||||
{
|
||||
angle = oldangle;
|
||||
pitch = oldpitch;
|
||||
ClearBounce();
|
||||
special1 = 1;
|
||||
ExplodeMissile(BlockingLine,BlockingMobj);
|
||||
}
|
||||
}
|
||||
private bool HitSkyLine( Line l, int hitside )
|
||||
{
|
||||
if ( !l ) return false;
|
||||
// if it's onesided, check if it's a Line_Horizon
|
||||
if ( !l.sidedef[1] ) return (l.special == Line_Horizon);
|
||||
Sector outs, ins;
|
||||
if ( hitside )
|
||||
{
|
||||
ins = l.backsector;
|
||||
outs = l.frontsector;
|
||||
}
|
||||
else
|
||||
{
|
||||
ins = l.frontsector;
|
||||
outs = l.backsector;
|
||||
}
|
||||
// return true if we're in a sector with sky and we hit the upper part of the line
|
||||
if ( ins.GetTexture(1) != skyflatnum ) return false;
|
||||
return (outs.ceilingplane.ZAtPoint(pos.xy) <= pos.z);
|
||||
}
|
||||
void A_HandleBounce()
|
||||
{
|
||||
Vector3 HitNormal = -vel.unit();
|
||||
F3DFloor ff;
|
||||
int lineside = 1;
|
||||
if ( BlockingFloor )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingFloor.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
||||
ff = BlockingFloor.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.top.Normal;
|
||||
else HitNormal = BlockingFloor.floorplane.Normal;
|
||||
}
|
||||
else if ( BlockingCeiling )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
||||
ff = BlockingCeiling.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.bottom.Normal;
|
||||
else HitNormal = BlockingCeiling.ceilingplane.Normal;
|
||||
}
|
||||
else if ( BlockingLine )
|
||||
{
|
||||
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
|
||||
{
|
||||
lineside = 0;
|
||||
HitNormal *= -1;
|
||||
}
|
||||
}
|
||||
else if ( BlockingMobj )
|
||||
{
|
||||
Vector3 diff = level.Vec3Diff(pos,BlockingMobj.pos);
|
||||
if ( (pos.x+radius) <= (BlockingMobj.pos.x-BlockingMobj.radius) )
|
||||
HitNormal = (-1,0,0);
|
||||
else if ( (pos.x-radius) >= (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;
|
||||
371
zscript/weapons/swwm_jackhammer_fx.zsc
Normal file
371
zscript/weapons/swwm_jackhammer_fx.zsc
Normal file
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8))).unit()*FRandom[Pusher](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Pusher](128,192));
|
||||
}
|
||||
numpt = Random[Pusher](0,2);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Pusher](1,3);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class BigPusherImpact : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Radius 0.1;
|
||||
Height 0;
|
||||
+NOGRAVITY;
|
||||
+NOCLIP;
|
||||
+NOTELEPORT;
|
||||
+NOINTERACTION;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_QuakeEx(5,5,5,15,0,300,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.8);
|
||||
A_StartSound("pusher/althit",CHAN_VOICE);
|
||||
A_SprayDecal("ImpactMark",-20);
|
||||
int numpt = Random[Pusher](8,16);
|
||||
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8))).unit()*FRandom[Pusher](.8,2.6);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Pusher](128,192));
|
||||
}
|
||||
numpt = Random[Pusher](4,10);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,12);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Pusher](8,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,16);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class PusherProjectile : Actor
|
||||
{
|
||||
Vector3 oldvel;
|
||||
double oldangle, oldpitch;
|
||||
Actor lasthit;
|
||||
int hittics;
|
||||
|
||||
Default
|
||||
{
|
||||
Obituary "$O_PUSHER";
|
||||
Speed 50;
|
||||
Radius 10;
|
||||
Height 10;
|
||||
DamageFunction clamp(int(3*vel.length()),0,150);
|
||||
DamageType 'Tenderize';
|
||||
BounceType "Hexen";
|
||||
BounceFactor 1.0;
|
||||
WallBounceFactor 1.0;
|
||||
Gravity 0.3;
|
||||
PROJECTILE;
|
||||
+USEBOUNCESTATE;
|
||||
+CANBOUNCEWATER;
|
||||
+INTERPOLATEANGLES;
|
||||
+DONTBOUNCEONSHOOTABLES;
|
||||
-BOUNCEAUTOOFF;
|
||||
+NODAMAGETHRUST;
|
||||
-BOUNCEONUNRIPPABLES;
|
||||
-ALLOWBOUNCEONACTORS;
|
||||
+RIPPER;
|
||||
-NOGRAVITY;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_StartSound("pusher/fly",CHAN_BODY,CHANF_LOOP,1.,2.);
|
||||
A_StartSound("pusher/altfire",CHAN_VOICE);
|
||||
oldvel = vel;
|
||||
oldangle = angle;
|
||||
oldpitch = pitch;
|
||||
}
|
||||
void A_Reorient()
|
||||
{
|
||||
oldvel = vel;
|
||||
oldangle = angle;
|
||||
oldpitch = pitch;
|
||||
if ( vel.length() <= 0. ) return;
|
||||
Vector3 dir = vel.unit();
|
||||
angle += clamp(deltaangle(angle,atan2(dir.y,dir.x)),-2,2);
|
||||
pitch += clamp(deltaangle(pitch,asin(-dir.z)),-2,2);
|
||||
// cancel if we hit a wall dead-on
|
||||
FLineTraceData d;
|
||||
LineTrace(oldangle,30,oldpitch,TRF_THRUACTORS|TRF_NOSKY,5,data:d);
|
||||
if ( d.HitType != TRACE_HitNone )
|
||||
{
|
||||
angle = oldangle;
|
||||
pitch = oldpitch;
|
||||
ClearBounce();
|
||||
special1 = 1;
|
||||
ExplodeMissile(BlockingLine,BlockingMobj);
|
||||
}
|
||||
}
|
||||
private bool HitSkyLine( Line l, int hitside )
|
||||
{
|
||||
if ( !l ) return false;
|
||||
// if it's onesided, check if it's a Line_Horizon
|
||||
if ( !l.sidedef[1] ) return (l.special == Line_Horizon);
|
||||
Sector outs, ins;
|
||||
if ( hitside )
|
||||
{
|
||||
ins = l.backsector;
|
||||
outs = l.frontsector;
|
||||
}
|
||||
else
|
||||
{
|
||||
ins = l.frontsector;
|
||||
outs = l.backsector;
|
||||
}
|
||||
// return true if we're in a sector with sky and we hit the upper part of the line
|
||||
if ( ins.GetTexture(1) != skyflatnum ) return false;
|
||||
return (outs.ceilingplane.ZAtPoint(pos.xy) <= pos.z);
|
||||
}
|
||||
void A_HandleBounce()
|
||||
{
|
||||
Vector3 HitNormal = -vel.unit();
|
||||
F3DFloor ff;
|
||||
int lineside = 1;
|
||||
if ( BlockingFloor )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingFloor.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
||||
ff = BlockingFloor.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.top.Normal;
|
||||
else HitNormal = BlockingFloor.floorplane.Normal;
|
||||
}
|
||||
else if ( BlockingCeiling )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
||||
ff = BlockingCeiling.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.bottom.Normal;
|
||||
else HitNormal = BlockingCeiling.ceilingplane.Normal;
|
||||
}
|
||||
else if ( BlockingLine )
|
||||
{
|
||||
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
|
||||
{
|
||||
lineside = 0;
|
||||
HitNormal *= -1;
|
||||
}
|
||||
}
|
||||
else if ( BlockingMobj )
|
||||
{
|
||||
Vector3 diff = level.Vec3Diff(pos,BlockingMobj.pos);
|
||||
if ( (pos.x+radius) <= (BlockingMobj.pos.x-BlockingMobj.radius) )
|
||||
HitNormal = (-1,0,0);
|
||||
else if ( (pos.x-radius) >= (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;
|
||||
}
|
||||
}
|
||||
1184
zscript/weapons/swwm_shot.zsc
Normal file
1184
zscript/weapons/swwm_shot.zsc
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
552
zscript/weapons/swwm_sparkyboi.zsc
Normal file
552
zscript/weapons/swwm_sparkyboi.zsc
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,6);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
|
||||
s.special1 = Random[ExploS](1,4);
|
||||
s.scale *= 2.8;
|
||||
s.alpha *= .4;
|
||||
}
|
||||
numpt = Random[ExploS](10,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[ExploS](20,30);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = int(Random[ExploS](3,5)*(1.+.4*special1));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("ExplodiumMagArm",pos);
|
||||
s.target = target;
|
||||
}
|
||||
Spawn("ExploLight2",pos);
|
||||
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,20+15*special1,cvel);
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A 1
|
||||
{
|
||||
pitch += pitchvel;
|
||||
angle += anglevel;
|
||||
if ( vel.length() > 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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
|
||||
s.special1 = Random[ExploS](1,2);
|
||||
s.scale *= 2.4;
|
||||
s.alpha *= .4;
|
||||
}
|
||||
numpt = Random[ExploS](8,12);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[ExploS](6,16);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Spawn("ExploLight",pos);
|
||||
}
|
||||
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 ExplodiumGun : SWWMWeapon
|
||||
{
|
||||
int clipcount;
|
||||
296
zscript/weapons/swwm_splode_fx.zsc
Normal file
296
zscript/weapons/swwm_splode_fx.zsc
Normal file
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,6);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
|
||||
s.special1 = Random[ExploS](1,4);
|
||||
s.scale *= 2.8;
|
||||
s.alpha *= .4;
|
||||
}
|
||||
numpt = Random[ExploS](10,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[ExploS](20,30);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = int(Random[ExploS](3,5)*(1.+.4*special1));
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("ExplodiumMagArm",pos);
|
||||
s.target = target;
|
||||
}
|
||||
Spawn("ExploLight2",pos);
|
||||
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,20+15*special1,cvel);
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A 1
|
||||
{
|
||||
pitch += pitchvel;
|
||||
angle += anglevel;
|
||||
if ( vel.length() > 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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
|
||||
s.special1 = Random[ExploS](1,2);
|
||||
s.scale *= 2.4;
|
||||
s.alpha *= .4;
|
||||
}
|
||||
numpt = Random[ExploS](8,12);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[ExploS](6,16);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Spawn("ExploLight",pos);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
570
zscript/weapons/swwm_tastytreat.zsc
Normal file
570
zscript/weapons/swwm_tastytreat.zsc
Normal file
|
|
@ -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<min(ma.Amount,invoker.clipcount-dropamt); i++ ) ma.PrintPickupMessage(true,ma.PickupMessage());
|
||||
if ( min(ma.Amount,invoker.clipcount-dropamt) > 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<min(ma.Amount,invoker.clipcount-dropamt); i++ ) ma.PrintPickupMessage(true,ma.PickupMessage());
|
||||
if ( min(ma.Amount,invoker.clipcount-dropamt) > 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4))).unit()*FRandom[Silverbullet](.4,2.);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.scale *= 1.8;
|
||||
s.special1 = Random[Silverbullet](1,3);
|
||||
s.SetShade(Color(1,1,1)*Random[Silverbullet](96,192));
|
||||
}
|
||||
numpt = Random[Silverbullet](6,9);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1)).unit()*FRandom[Silverbullet](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.scale *= 1.3;
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Silverbullet](10,15);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6))).unit()*FRandom[Silverbullet](6,20);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.scale *= 1.6;
|
||||
s.vel = pvel;
|
||||
}
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( isFrozen() ) return;
|
||||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||||
if ( tics > 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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4))).unit()*FRandom[Silverbullet](.4,2.);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.scale *= 1.8;
|
||||
s.special1 = Random[Silverbullet](1,4);
|
||||
s.SetShade(Color(1,1,1)*Random[Silverbullet](96,192));
|
||||
}
|
||||
numpt = Random[Silverbullet](6,9);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1)).unit()*FRandom[Silverbullet](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.scale *= 1.3;
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Silverbullet](10,15);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6))).unit()*FRandom[Silverbullet](6,20);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.scale *= 1.6;
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Silverbullet](16,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("FatChodeExtraArm",pos);
|
||||
s.target = target;
|
||||
}
|
||||
numpt = Random[Silverbullet](8,10);
|
||||
Vector3 y, z, dir;
|
||||
double a, s;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(realpitch,realangle,0);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let e = Spawn("FatChodeExplosionArm",pos);
|
||||
e.target = target;
|
||||
a = FRandom[Silverbullet](0,360);
|
||||
s = FRandom[Silverbullet](0,.2);
|
||||
dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
||||
e.angle = atan2(dir.y,dir.x);
|
||||
e.pitch = asin(-dir.z);
|
||||
}
|
||||
Spawn("ExploLight3",pos);
|
||||
Spawn("FatChodeRing",pos);
|
||||
}
|
||||
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 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;
|
||||
402
zscript/weapons/swwm_thiccboolet_fx.zsc
Normal file
402
zscript/weapons/swwm_thiccboolet_fx.zsc
Normal file
|
|
@ -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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4))).unit()*FRandom[Silverbullet](.4,2.);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.scale *= 1.8;
|
||||
s.special1 = Random[Silverbullet](1,3);
|
||||
s.SetShade(Color(1,1,1)*Random[Silverbullet](96,192));
|
||||
}
|
||||
numpt = Random[Silverbullet](6,9);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1)).unit()*FRandom[Silverbullet](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.scale *= 1.3;
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Silverbullet](10,15);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6))).unit()*FRandom[Silverbullet](6,20);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.scale *= 1.6;
|
||||
s.vel = pvel;
|
||||
}
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
if ( isFrozen() ) return;
|
||||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||||
if ( tics > 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<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4),FRandom[Silverbullet](-.4,.4))).unit()*FRandom[Silverbullet](.4,2.);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.scale *= 1.8;
|
||||
s.special1 = Random[Silverbullet](1,4);
|
||||
s.SetShade(Color(1,1,1)*Random[Silverbullet](96,192));
|
||||
}
|
||||
numpt = Random[Silverbullet](6,9);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1),FRandom[Silverbullet](-1,1)).unit()*FRandom[Silverbullet](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.scale *= 1.3;
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Silverbullet](10,15);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+(FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6),FRandom[Silverbullet](-.6,.6))).unit()*FRandom[Silverbullet](6,20);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.scale *= 1.6;
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Silverbullet](16,20);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let s = Spawn("FatChodeExtraArm",pos);
|
||||
s.target = target;
|
||||
}
|
||||
numpt = Random[Silverbullet](8,10);
|
||||
Vector3 y, z, dir;
|
||||
double a, s;
|
||||
[x, y, z] = swwm_CoordUtil.GetAxes(realpitch,realangle,0);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
let e = Spawn("FatChodeExplosionArm",pos);
|
||||
e.target = target;
|
||||
a = FRandom[Silverbullet](0,360);
|
||||
s = FRandom[Silverbullet](0,.2);
|
||||
dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
|
||||
e.angle = atan2(dir.y,dir.x);
|
||||
e.pitch = asin(-dir.z);
|
||||
}
|
||||
Spawn("ExploLight3",pos);
|
||||
Spawn("FatChodeRing",pos);
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue