Major code refactoring. SWWMHandler could still use some more, though.

This commit is contained in:
Mari the Deer 2021-02-27 23:59:40 +01:00
commit c5abe83831
94 changed files with 17859 additions and 17678 deletions

View file

@ -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-";

View file

@ -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"

View file

@ -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"

View file

@ -0,0 +1 @@
// Blackfire Igniter projectiles and effects

View file

@ -0,0 +1 @@
// Plasma Blast projectiles and effects

View file

@ -0,0 +1 @@
// Itamex Hammer projectiles and effects

View file

@ -0,0 +1 @@
// Sheen HMG projectiles and effects

View file

@ -0,0 +1 @@
// Grand Lance projectiles and effects

View file

@ -0,0 +1 @@
// Quadravol projectiles and effects

View file

@ -0,0 +1 @@
// Puntzer Beta projectiles and effects

View file

@ -0,0 +1 @@
// Puntzer Gamma projectiles and effects

View file

@ -0,0 +1 @@
// Ray-Khom projectiles and effects

View file

@ -0,0 +1 @@
// EMP Rail Carbine projectiles and effects

View 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;
}
}
}

View 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());
}
}
}

View 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;
}
}
}
}
}
}
}
}

View 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);
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}
}

View 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;
}
}*/
}

View 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;
}
}

File diff suppressed because it is too large Load diff

View 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));
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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 {}

View file

@ -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
{

View file

@ -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
{

View 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

File diff suppressed because it is too large Load diff

636
zscript/swwm_gesture.zsc Normal file
View 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
View 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
View 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
View 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;
}
}

View 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
}
}

View 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

View 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;
}
}

View 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;
}
}

View file

@ -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 )
{

View 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;
}
}

View 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();
}
}

View 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);
}
}

View 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.);
}
}

View 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;
}
}

View file

@ -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;
}
}

View file

@ -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];

View 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;
}
}

View 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;
}
}
}

View 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;
}
}

View file

@ -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;
}
}

View 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;
}
}

View file

@ -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;
}
}

View file

@ -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;

View 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;
}
}

View file

@ -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;

View 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;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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;
}
}

View file

@ -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;
}
}

View file

@ -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;

View 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;
}
}

View 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;
}
}

View file

@ -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;
}
}

View file

@ -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;

View 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;
}
}