Spreadgun buckshot and slug implemented.

Tweaks here and there to other stuff.
Refined wall jumping/climbing, also works on actors (including other players).
Refined how boosting/dashing handles falling speeds.
Added improved air control. It was very much needed.
Added "kick" sounds to wall jumps.
Add option to hear other player's voices in mp.
Fix some broken localization.
Fix invulnerable monsters bleeding from some attacks.
Fix desync when jumping on top of another player with prediction enabled.
Make moths immune to your damage, so you can stop accidentally killing them.
Make normal ammo buyable in Hexen again.
This commit is contained in:
Mari the Deer 2020-02-27 02:00:17 +01:00
commit 7a01fdc4e8
57 changed files with 979 additions and 115 deletions

View file

@ -874,7 +874,7 @@ Class FabricatorTier1 : AmmoFabricator
Inventory.PickupMessage "$T_FABRICATOR1";
Inventory.MaxAmount 30;
AmmoFabricator.Budget 2000;
AmmoFabricator.PerType 4;
AmmoFabricator.PerType 2;
Stamina 3000;
}
}
@ -887,7 +887,7 @@ Class FabricatorTier2 : AmmoFabricator
Inventory.PickupMessage "$T_FABRICATOR2";
Inventory.MaxAmount 20;
AmmoFabricator.Budget 15000;
AmmoFabricator.PerType 8;
AmmoFabricator.PerType 4;
Stamina 12000;
}
}
@ -900,7 +900,7 @@ Class FabricatorTier3 : AmmoFabricator
Inventory.PickupMessage "$T_FABRICATOR3";
Inventory.MaxAmount 10;
AmmoFabricator.Budget 100000;
AmmoFabricator.PerType 16;
AmmoFabricator.PerType 8;
Stamina 150000;
}
}

View file

@ -515,6 +515,7 @@ Class SWWMOneLiner : HUDMessageBase
override bool Tick()
{
if ( players[consoleplayer].Health <= 0 ) curtime = int.min;
curtime--;
return (curtime<-20);
}
@ -818,6 +819,7 @@ Class SWWMChip : Actor
+INTERPOLATEANGLES;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
BounceType "Doom";
BounceFactor 0.3;
Gravity 0.35;
@ -1019,14 +1021,14 @@ Class SWWMBulletTrail : LineTracer
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[0].hitpos);
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
b.A_CheckTerrain();
}
for ( int i=5; i<t.Results.Distance; i+=10 )
{
if ( !Random[Boolet](0,bubblechance) ) continue;
let b = Actor.Spawn(smoky?"SWWMSmallSmoke":"SWWMBubble",level.Vec3Offset(pos,dir*i));
b.Scale *= FRandom[Boolet](0.4,0.6);
b.Scale *= FRandom[Boolet](.4,.6);
}
t.Destroy();
}
@ -1177,7 +1179,7 @@ Class LastLine
Class SWWMHandler : EventHandler
{
transient String oneliner, onelinersnd;
transient int onelinertic, onelinerspan;
transient int onelinertic, onelinerspan, onelinerlevel;
transient int lastlock, lastcombat;
transient Array<Actor> combatactors;
transient Array<Int> combattics;
@ -1192,25 +1194,40 @@ Class SWWMHandler : EventHandler
transient CVar mutevoice;
static int AddOneliner( String type, int delay = 5 )
static int AddOneliner( String type, int level, int delay = 5 )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return 0;
CVar voicetype = CVar.GetCVar('swwm_voicetype',players[consoleplayer]);
String voicetype = CVar.GetCVar('swwm_voicetype',players[consoleplayer]).GetString();
// suppress non-rage comments when ragekit is active, only screaming allowed
if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") && (CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() < 2) ) return 0;
if ( players[consoleplayer].mo.FindInventory("RagekitPower") && (type != "ragekit") ) return 0;
int whichline;
int countem = 0, i = 1;
String testme, locme;
do
{
testme = String.Format("SWWM_SUBS_%s_%s%d",voicetype.GetString().MakeUpper(),type.MakeUpper(),i);
testme = String.Format("SWWM_SUBS_%s_%s%d",voicetype.MakeUpper(),type.MakeUpper(),i);
locme = StringTable.Localize(testme,false);
if ( testme != locme ) countem++;
i++;
}
while ( (testme != locme) && (i < 100) ); // gotta prevent infinite loops
if ( countem == 0 ) return 0;
if ( countem == 0 )
{
if ( voicetype ~== "default" ) return 0;
// retry with the default voicetype
voicetype = "default";
i = 1;
do
{
testme = String.Format("SWWM_SUBS_DEFAULT_%s%d",type.MakeUpper(),i);
locme = StringTable.Localize(testme,false);
if ( testme != locme ) countem++;
i++;
}
while ( (testme != locme) && (i < 100) ); // gotta prevent infinite loops
if ( countem == 0 ) return 0;
}
// check last line so we don't repeat
int last = 0, ent;
for ( int i=0; i<hnd.lastlines.Size(); i++ )
@ -1234,10 +1251,11 @@ Class SWWMHandler : EventHandler
lst.lineno = whichline;
hnd.lastlines.Push(lst);
}
hnd.oneliner = String.Format("$SWWM_SUBS_%s_%s%d",voicetype.GetString().MakeUpper(),type.MakeUpper(),whichline);
hnd.onelinersnd = String.Format("voice/%s/%s%d",voicetype.GetString(),type,whichline);
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)*Thinker.TICRATE);
hnd.onelinerlevel = level;
return hnd.onelinertic+hnd.onelinerspan;
}
@ -1325,8 +1343,8 @@ Class SWWMHandler : EventHandler
override void WorldLoaded( WorldEvent e )
{
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
if ( !e.IsSaveGame && !e.IsReopen && (gamestate != GS_TITLELEVEL) && (mutevoice.GetInt() < 3) )
AddOneliner("mapstart");
if ( !e.IsSaveGame && !e.IsReopen && (gamestate != GS_TITLELEVEL) )
AddOneliner("mapstart",3);
if ( !e.IsSaveGame && !e.IsReopen )
{
// for skipping over merged exit lines (sharing vertices)
@ -1467,7 +1485,11 @@ Class SWWMHandler : EventHandler
if ( onelinertic && (onelinertic < gametic) )
{
if ( players[consoleplayer].health > 0 )
players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE);
{
if ( onelinerlevel > mutevoice.GetInt() )
players[consoleplayer].mo.A_StartSound(onelinersnd,CHAN_DEMOVOICE,CHANF_DEFAULT,1.,ATTN_NONE);
SendNetworkEvent("swwmremoteliner."..onelinersnd,consoleplayer,onelinerlevel);
}
onelinertic = 0;
onelinerspan = 0;
}
@ -1540,8 +1562,8 @@ Class SWWMHandler : EventHandler
enteredcombat = true;
}
}
if ( enteredcombat && (!highesttic || (gametic > highesttic+100)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner("fightstart",10);
if ( enteredcombat && (!highesttic || (gametic > highesttic+100)) )
lastcombat = AddOneliner("fightstart",1,10);
}
override void WorldThingDied( WorldEvent e )
@ -1557,7 +1579,6 @@ Class SWWMHandler : EventHandler
override void WorldThingDamaged( WorldEvent e )
{
if ( !mutevoice ) mutevoice = CVar.GetCVar('swwm_mutevoice',players[consoleplayer]);
if ( e.Damage > 0 )
SWWMScoreObj.Spawn(-e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),Font.CR_RED);
if ( e.Thing.player )
@ -1577,8 +1598,8 @@ Class SWWMHandler : EventHandler
{
if ( (e.DamageSource.bISMONSTER || e.DamageSource.player) && (e.Thing == players[consoleplayer].mo) && (e.Thing.Health > 0) )
{
if ( (!lastcombat || (gametic > lastcombat+20)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner(e.Thing.IsFriend(e.DamageSource)?"friendhit":"gethit",15);
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner(e.Thing.IsFriend(e.DamageSource)?"friendhit":"gethit",1,15);
highesttic = gametic;
}
if ( (e.DamageSource == players[consoleplayer].mo) && (e.Thing.bISMONSTER || e.Thing.player) )
@ -1586,8 +1607,8 @@ Class SWWMHandler : EventHandler
// make sure it's not a moth, because otherwise they won't shut up about accidentally hurting them (it happens a lot)
if ( e.Thing.IsFriend(e.DamageSource) && !(e.Thing is 'LampMoth') )
{
if ( (!lastcombat || (gametic > lastcombat+20)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner("hitfriend",15);
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner("hitfriend",1,15);
highesttic = gametic;
}
}
@ -1606,8 +1627,8 @@ Class SWWMHandler : EventHandler
if ( e.DamageSource == players[consoleplayer].mo )
{
highesttic = gametic;
if ( (!lastcombat || (gametic > lastcombat+20)) && (mutevoice.GetInt() < 1) )
lastcombat = AddOneliner("scorekill",15);
if ( !lastcombat || (gametic > lastcombat+20) )
lastcombat = AddOneliner("scorekill",1,15);
}
if ( !e.Thing.default.bCountKill ) // no credits
return;
@ -1621,7 +1642,7 @@ Class SWWMHandler : EventHandler
// scoring
int score = min(2000,int(ceil(e.Thing.SpawnHealth()*.5)*10));
int ofs = 0;
if ( e.Thing.Health <= e.Thing.GetGibHealth() )
if ( ((e.Thing.Health <= e.Thing.GetGibHealth()) || (e.DamageSource.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH)) && !e.DamageSource.bNOEXTREMEDEATH && !(e.Inflictor && e.Inflictor.bNOEXTREMEDEATH) )
{
score = int(score*1.25);
if ( e.DamageSource.player == players[consoleplayer] )
@ -1640,7 +1661,7 @@ Class SWWMHandler : EventHandler
{
score += 10000;
if ( e.DamageSource.player == players[consoleplayer] )
SWWMScoreObj.Spawn(score,e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_BOSSKILL",++ofs);
SWWMScoreObj.Spawn(0,e.Thing.Vec3Offset(0,0,e.Thing.Height/2),Font.CR_FIRE,"$SWWM_BOSSKILL",++ofs);
}
SWWMCredits.Give(e.DamageSource.player,score);
if ( e.DamageSource.player == players[consoleplayer] )
@ -1702,6 +1723,7 @@ Class SWWMHandler : EventHandler
{
let l = SWWMOneLiner.Make(oneliner,onelinerspan);
StatusBar.AttachMessage(l,-3473);
SendNetworkEvent("swwmremotelinertxt."..oneliner,consoleplayer,onelinerlevel);
}
for ( int i=0; i<flashes.size(); i++ )
{
@ -1738,8 +1760,8 @@ Class SWWMHandler : EventHandler
if ( !locknum ) return;
if ( e.Thing.CheckLocalView() && !e.Thing.CheckKeys(locknum,false,true) )
{
if ( (!lastlock || (gametic > lastlock+20)) && (mutevoice.GetInt() < 2) )
lastlock = AddOneliner("locked");
if ( !lastlock || (gametic > lastlock+20) )
lastlock = AddOneliner("locked",2);
}
}
@ -2066,26 +2088,41 @@ Class SWWMHandler : EventHandler
else if ( e.Name ~== "swwmkoraxline" )
{
if ( consoleplayer != e.Args[1] ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= 3 ) return;
switch ( e.Args[0] )
{
case 0:
AddOneliner("koraxgreet",60);
AddOneliner("koraxgreet",3,60);
break;
case 1:
AddOneliner("koraxblood",150);
AddOneliner("koraxblood",3,150);
break;
case 2:
AddOneliner("koraxgame",120);
AddOneliner("koraxgame",3,120);
break;
case 3:
AddOneliner("koraxworship",80);
AddOneliner("koraxworship",3,80);
break;
case 4:
AddOneliner("koraxmasters",90);
AddOneliner("koraxmasters",3,90);
break;
}
}
else if ( e.Name.Left(16) ~== "swwmremoteliner." )
{
if ( consoleplayer == e.Args[0] ) return;
if ( !CVar.GetCVar('swwm_othervoice',players[consoleplayer]).GetBool() ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= 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 ( !CVar.GetCVar('swwm_othervoice',players[consoleplayer]).GetBool() ) return;
if ( CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() >= 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)));
}
}
// stuff for hud

View file

@ -269,9 +269,8 @@ Class SWWMCasing : Actor abstract
Death:
#### # -1
{
pitch = 0;
pitch = roll = 0;
angle = FRandom[Junk](0,360);
roll = FRandom[Junk](0,360);
}
Stop;
}
@ -470,7 +469,7 @@ Class SWWMWeapon : Weapon abstract
invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25);
if ( !d.HitActor.bNOBLOOD )
if ( !d.HitActor.bNOBLOOD && !d.HitActor.bINVULNERABLE )
{
d.HitActor.TraceBleed(dmg,invoker);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
@ -531,6 +530,14 @@ Class SWWMWeapon : Weapon abstract
{
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;
}
Default
{
Weapon.BobStyle "Alpha";

View file

@ -252,7 +252,7 @@ Class PusherProjectile : Actor
{
if ( target == lasthit ) return 0;
lasthit = target;
if ( target.bNOBLOOD ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP);
if ( target.bNOBLOOD || 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);
SWWMHandler.DoKnockback(target,vel.unit(),85000);
@ -339,7 +339,7 @@ Class PusherWeapon : SWWMWeapon
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,8500);
d.HitActor.A_QuakeEx(4,4,4,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("PusherImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);
@ -421,7 +421,7 @@ Class PusherWeapon : SWWMWeapon
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,85000);
d.HitActor.A_QuakeEx(9,9,9,15,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("BigPusherImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);

View file

@ -504,8 +504,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
if ( !type ) continue;
// skip chanceboxes, since they're not yet implemented
if ( type is 'Chancebox' ) continue;
// no fabricators outside of hexen, otherwise no ammo
if ( (gameinfo.gametype&GAME_Hexen) && (type is 'Ammo') ) continue;
// no fabricators outside of hexen
if ( !(gameinfo.gametype&GAME_Hexen) && (type is 'AmmoFabricator') ) continue;
// skip maxed items
let cur = players[consoleplayer].mo.FindInventory(type);

View file

@ -1,7 +1,7 @@
// The Demolitionist
Class Demolitionist : PlayerPawn
{
int last_jump_held;
int last_jump_held, last_boost;
Vector3 dashdir;
double dashfuel, dashboost;
int dashcooldown, boostcooldown, fuelcooldown;
@ -225,6 +225,7 @@ Class Demolitionist : PlayerPawn
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( mod == 'Jump' ) return StringTable.Localize("$O_JUMP");
if ( mod == 'Dash' ) return StringTable.Localize("$O_DASH");
if ( mod == 'GroundPound' ) return StringTable.Localize("$O_POUND");
return Super.GetObituary(victim,inflictor,mod,playerattack);
@ -275,6 +276,7 @@ Class Demolitionist : PlayerPawn
{
A_AlertMonsters(800);
dashboost *= (player.cmd.buttons&BT_JUMP)?.9:.4;
last_boost = level.maptime+1;
}
mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost);
dashfuel = max(0.,dashfuel-dashboost);
@ -531,6 +533,19 @@ Class Demolitionist : PlayerPawn
else
{
Super.MovePlayer();
// override air control because we REALLY need the extra mobility
if ( !player.onground && !bNOGRAVITY && (waterlevel < 2) )
{
double fs = TweakSpeeds(1.,0.);
fs *= max(abs(player.cmd.forwardmove/12800.),abs(player.cmd.sidemove/10240.));
if ( CanCrouch() && (player.crouchfactor != -1) ) fs *= player.crouchfactor;
Vector3 accel = (RotateVector((player.cmd.forwardmove,-player.cmd.sidemove),angle),0);
accel *= fs/960.;
double spd = vel.length();
if ( spd > 10. ) vel = (vel+accel/TICRATE).unit()*spd;
else vel = vel+accel/TICRATE;
player.jumptics = -2;
}
if ( abs(roll) > 0. ) roll += clamp(deltaangle(roll,0),-3.,3.);
}
guideangle *= .9;
@ -568,7 +583,7 @@ Class Demolitionist : PlayerPawn
player.cheats &= ~CF_REVERTPLEASE;
player.camera = player.mo;
}
vel *= 0;
vel.z += clamp(-vel.z*.4,0.,30.);
player.jumptics = -1;
SetStateLabel("Dash");
A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP);
@ -578,8 +593,53 @@ Class Demolitionist : PlayerPawn
override void CheckJump()
{
if ( InStateSequence(CurState,FindState("Dash")) ) return; // do not
bool walljump = LineTrace(angle-180,Radius*2,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,height*.2);
bool wallclimb = LineTrace(angle,Radius*2,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,height*.2);
Vector2 walldir = (cos(angle),sin(angle));
bool walljump = false, wallclimb = false;
FLineTraceData d;
Actor jumpactor = null;
for ( int i=-4; i<int(height*.8); i++ )
{
if ( !wallclimb )
{
for ( int k=0; k<30; k+=10 )
{
for ( int j=-1; j<=1; j+=2 )
{
double ang = angle+k*j;
wallclimb = LineTrace(ang,Radius+12,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,i,data:d);
if ( wallclimb )
{
jumpactor = d.HitActor;
walldir = (walldir*.7+(cos(ang),sin(ang))*.3);
break;
}
}
if ( wallclimb ) break;
}
if ( wallclimb ) break;
}
if ( wallclimb ) break;
if ( !walljump )
{
for ( int k=0; k<60; k+=10 )
{
for ( int j=-1; j<=1; j+=2 )
{
double ang = (angle-180)+k*j;
walljump = LineTrace(ang,Radius+12,0,TRF_NOSKY|TRF_THRUHITSCAN|TRF_BLOCKSELF|TRF_SOLIDACTORS,i,data:d);
if ( walljump )
{
jumpactor = d.HitActor;
walldir = (walldir*.7+(cos(ang+180),sin(ang+180))*.3);
break;
}
}
if ( walljump ) break;
}
if ( walljump ) break;
}
if ( walljump ) break;
}
if ( player.cmd.buttons&BT_JUMP )
{
if ( player.crouchoffset ) player.crouching = 1;
@ -590,6 +650,7 @@ Class Demolitionist : PlayerPawn
&& ((player.onground && (player.jumptics == 0))
|| (!player.onground && (level.maptime > last_jump_held) && (((dashfuel > 10.) && (boostcooldown <= 0)) || walljump || wallclimb))) )
{
if ( !player.onground && (level.maptime < last_boost+8) ) return;
double jumpvelz = JumpZ*35./TICRATE;
double jumpfac = 0;
for ( let p=Inv; p; p=p.Inv )
@ -603,21 +664,27 @@ Class Demolitionist : PlayerPawn
}
if ( jumpfac > 0 ) jumpvelz *= jumpfac;
double pvelz = vel.z;
if ( !player.onground )
if ( !player.onground && !(player.cheats&CF_PREDICTING) )
{
// check for wall stuff
if ( walljump )
{
vel.z = max(-abs(vel.z)*.1,vel.z);
vel.z += jumpvelz;
vel.xy = (cos(angle),sin(angle))*20*Speed;
if ( vel.z < 10. )
vel.z += jumpvelz+clamp(-pvelz*.6,0.,30.);
vel.xy += walldir*20*Speed;
}
else if ( wallclimb )
{
vel.z = max(-abs(vel.z)*.1,vel.z);
vel.z += jumpvelz;
vel.xy = (cos(angle),sin(angle))*5*Speed;
if ( vel.z < 10. )
vel.z += jumpvelz+clamp(-pvelz*.8,0.,30.);
vel.xy += walldir*5*Speed;
}
if ( jumpactor && jumpactor.bSHOOTABLE )
{
SWWMHandler.DoKnockback(jumpactor,(-walldir,0),12000);
jumpactor.DamageMobj(self,self,10,'Jump');
}
if ( walljump || wallclimb ) A_StartSound("demolitionist/kick",CHAN_FOOTSTEP,CHANF_OVERLAP);
}
bOnMobj = false;
player.jumpTics = -1;
@ -627,16 +694,19 @@ Class Demolitionist : PlayerPawn
{
dashboost = 3.;
boostcooldown = 20;
vel.z += jumpvelz+clamp(-pvelz,0.,30.);
if ( vel.z < 10. )
vel.z += jumpvelz+clamp(-pvelz*.4,0.,30.);
A_StartSound("demolitionist/jet",CHAN_JETPACK,CHANF_LOOP);
mystats.boostcount++;
}
else
{
dashboost = 0.;
vel.z += jumpvelz*1.25;
if ( vel.z < 10. )
vel.z += jumpvelz*1.25;
}
SetStateLabel("Jump");
if ( !player.onground ) last_boost = level.maptime+1;
}
last_jump_held = level.maptime+1;
}
@ -819,6 +889,7 @@ Class Demolitionist : PlayerPawn
}
void A_DemoScream()
{
A_StopSound(CHAN_DEMOVOICE);
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
A_PlayerScream();
@ -828,7 +899,6 @@ Class Demolitionist : PlayerPawn
override bool OnGiveSecret( bool printmsg, bool playsound )
{
if ( !player ) return false;
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
int score = 500;
// last secret (this is called before counting it up, so have to subtract)
if ( level.found_secrets == level.total_secrets-1 )
@ -837,7 +907,7 @@ Class Demolitionist : PlayerPawn
Console.Printf(StringTable.Localize("$SWWM_LASTSECRET"),player.GetUserName(),score);
}
else Console.Printf(StringTable.Localize("$SWWM_FINDSECRET"),player.GetUserName(),score);
if ( CheckLocalView() && (mute.GetInt() < 2) ) SWWMHandler.AddOneliner("findsecret",40);
if ( CheckLocalView() ) SWWMHandler.AddOneliner("findsecret",2,40);
SWWMCredits.Give(player,score);
return true;
}
@ -847,9 +917,8 @@ Class Demolitionist : PlayerPawn
if ( !player ) return;
// add lore if any
SWWMLoreLibrary.Add(player,item.GetClassName());
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
if ( (item is 'Weapon') && mystats && !mystats.GotWeapon(Weapon(item).GetClass()) && CheckLocalView() && (mute.GetInt() < 2) )
SWWMHandler.AddOneliner("getweapon");
if ( (item is 'Weapon') && mystats && !mystats.GotWeapon(Weapon(item).GetClass()) && CheckLocalView() )
SWWMHandler.AddOneliner("getweapon",2);
if ( (item is 'Key') && !key_reentrant )
{
// score
@ -871,15 +940,14 @@ Class Demolitionist : PlayerPawn
override bool UseInventory( Inventory item )
{
if ( !player ) return Super.UseInventory(item);
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
bool res = Super.UseInventory(item);
if ( CheckLocalView() )
{
if ( !res && !(item is 'Weapon') ) A_StartSound("menu/noinvuse",CHAN_ITEM);
if ( (item is 'PuzzleItem') && (mute.GetInt() < 2) )
if ( item is 'PuzzleItem' )
{
if ( res ) SWWMHandler.AddOneliner("puzzsucc",10);
else SWWMHandler.AddOneliner("puzzfail",20);
if ( res ) SWWMHandler.AddOneliner("puzzsucc",2,10);
else SWWMHandler.AddOneliner("puzzfail",2,20);
}
}
return res;
@ -902,9 +970,11 @@ Class Demolitionist : PlayerPawn
override bool Used( Actor user )
{
if ( !(user is 'Demolitionist') || !user.player ) return false;
CVar othermute = CVar.GetCVar('swwm_mutevoice',user.player);
if ( (user.player == players[consoleplayer]) && (othermute.GetInt() < 2) )
SWWMHandler.AddOneliner("greet");
if ( (user.player == players[consoleplayer]) && (health > 0) )
{
SWWMHandler.AddOneliner("greet",2);
return true;
}
return false;
}
States

View file

@ -576,7 +576,7 @@ Class RagekitPower : Powerup
Super.InitEffect();
if ( !Owner ) return;
if ( Owner.player == players[consoleplayer] )
lastrage = SWWMHandler.AddOneliner("ragekit",20);
lastrage = SWWMHandler.AddOneliner("ragekit",2,20);
SWWMHandler.DoFlash(Owner,Color(64,255,0,0),30);
Owner.A_QuakeEx(8,8,8,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
lasteffect = int.min;
@ -596,7 +596,7 @@ Class RagekitPower : Powerup
{
SWWMHandler.DoFlash(Owner,Color(16,255,0,0),5);
if ( (Owner.player == players[consoleplayer]) && (gametic > lastrage) && (CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() < 2) )
lastrage = SWWMHandler.AddOneliner("ragekit",5);
lastrage = SWWMHandler.AddOneliner("ragekit",2,5);
Owner.A_QuakeEx(2,2,2,Random[Rage](1,2),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.5);
}
}
@ -619,7 +619,7 @@ Class RagekitPower : Powerup
SWWMHandler.DoFlash(Owner,Color(64,255,0,0),10);
Owner.A_QuakeEx(8,8,8,Random[Rage](3,8),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
if ( (Owner.player == players[consoleplayer]) && (gametic > lastrage) )
lastrage = SWWMHandler.AddOneliner("ragekit",5);
lastrage = SWWMHandler.AddOneliner("ragekit",2,5);
Owner.A_StartSound("powerup/ragekithit",CHAN_POWERUP);
lasteffect = level.maptime;
}
@ -748,6 +748,12 @@ Class LampMoth : Actor
if ( master && master.player ) SetFriendPlayer(master.player);
else bFRIENDLY = false;
}
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
{
// no hurt moff
if ( source && IsFriend(source) ) damage = 0;
return Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
}
bool isEntranced()
{
if ( !lamp )
@ -880,7 +886,7 @@ Class LampMoth : Actor
A_FaceTarget(0,0);
lifespan -= 5;
Vector3 awaydir = level.Vec3Diff(target.Vec3Offset(0,0,target.height),pos).unit();
vel += awaydir*32.;
vel += awaydir*8.;
int dmg = target.DamageMobj(self,self,GetMissileDamage(0,0),'Melee',Random[Moth](0,8)?DMG_NO_PAIN:0);
if ( !target.bNOBLOOD )
{
@ -1003,6 +1009,7 @@ Class CompanionLamp : Actor
{
Vector3 Trail;
Array<LampMoth> moff;
Actor parent;
Default
{
@ -1046,7 +1053,7 @@ Class CompanionLamp : Actor
}
let s = Spawn("SWWMSmallSmoke",m.pos);
s.alpha *= .3;
m.master = target;
m.master = parent;
m.lamp = self;
m.trail = m.pos;
moff.Push(m);
@ -1054,7 +1061,7 @@ Class CompanionLamp : Actor
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !target || !SWWMLamp(master) )
if ( !parent || !SWWMLamp(master) )
{
Destroy();
return;
@ -1080,7 +1087,7 @@ Class CompanionLamp : Actor
override void Tick()
{
Super.Tick();
if ( !target || !SWWMLamp(master) )
if ( !parent || !SWWMLamp(master) )
{
Destroy();
return;
@ -1092,8 +1099,8 @@ Class CompanionLamp : Actor
{
for ( int j=1; j>=-1; j-=2 )
{
double ang = (target.angle-180)+i*j;
Vector3 testpos = level.Vec3Offset(target.pos,(cos(ang)*32,sin(ang)*32,target.height-8+1.5*sin(level.maptime*3.)));
double ang = (parent.angle-180)+i*j;
Vector3 testpos = level.Vec3Offset(parent.pos,(cos(ang)*32,sin(ang)*32,parent.height-8+1.5*sin(level.maptime*3.)));
if ( !level.IsPointInLevel(testpos) ) continue;
Vector3 oldpos = pos;
Vector3 oldprev = prev;
@ -1123,19 +1130,19 @@ Class CompanionLamp : Actor
// check at most for a 45 degree offset
if ( foundspot && (i > 45) ) break;
}
Vector3 diff = level.Vec3Diff(pos,target.pos);
Vector3 diff = level.Vec3Diff(pos,parent.pos);
if ( diff.length() > 400 )
{
Actor f = Spawn("SWWMItemFog",pos);
f.A_StartSound("lamp/disappear",CHAN_VOICE);
SetOrigin(trail,false);
angle = AngleTo(target);
angle = AngleTo(parent);
vel *= 0.;
f = Spawn("SWWMItemFog",pos);
f.A_StartSound("lamp/appear",CHAN_VOICE);
return;
}
angle += Clamp(deltaangle(angle,AngleTo(target)),-5.,5.);
angle += Clamp(deltaangle(angle,AngleTo(parent)),-5.,5.);
vel *= .8;
bool blocked = false;
if ( BlockingLine && BlockingLineIsBlocking() )
@ -1174,7 +1181,7 @@ Class CompanionLamp : Actor
vel += 4.*normal;
blocked = true;
}
if ( (diff.x > -16) && (diff.x < 16) && (diff.y > -16) && (diff.y < 16) && (diff.z > -16) && (diff.z < target.height+8) )
if ( (diff.x > -16) && (diff.x < 16) && (diff.y > -16) && (diff.y < 16) && (diff.z > -16) && (diff.z < parent.height+8) )
{
if ( diff.x < 0 ) vel.x -= .2;
else vel.x += .2;
@ -1277,7 +1284,7 @@ Class SWWMLamp : Inventory
if ( !thelamp )
{
thelamp = Spawn("CompanionLamp",Owner.Vec3Offset(cos(Owner.angle)*20,sin(Owner.angle)*20,24));
thelamp.target = Owner;
CompanionLamp(thelamp).parent = Owner;
thelamp.master = self;
let f = Spawn("SWWMItemFog",thelamp.pos);
f.A_StartSound("lamp/appear",CHAN_VOICE);

View file

@ -1,6 +1,303 @@
// Blackmann "Rhino Stopper" Spreadgun (from Instant Action 3, also planned for Zanaveth Ultra Suite 2)
// Slot 3, replaces Shotgun, Ethereal Crossbow, Serpent Staff
Class RedShellCasing : SWWMCasing
{
Default
{
BounceSound "spreadgun/casing";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
heat = 0;
}
}
Class GreenShellCasing : RedShellCasing {}
Class WhiteShellCasing : RedShellCasing {}
Class BlueShellCasing : RedShellCasing {}
Class BlackShellCasing : RedShellCasing {}
Class PurpleShellCasing : RedShellCasing {}
Class GoldShellCasing : RedShellCasing
{
Default
{
BounceSound "spreadgun/gcasing";
}
}
Class SWWMDamageAccumulator : Thinker
{
Actor victim, inflictor, source;
Array<Int> amounts;
int total;
Name type;
bool dontgib;
override void Tick()
{
Super.Tick();
// so many damn safeguards in this
if ( !victim )
{
Destroy();
return;
}
int gibhealth = victim.GetGibHealth();
// おまえはもう死んでいる
if ( (victim.health-total <= gibhealth) && !dontgib )
{
// safeguard for inflictors that have somehow ceased to exist, which apparently STILL CAN HAPPEN
if ( inflictor ) inflictor.bEXTREMEDEATH = true;
else type = 'Extreme';
}
// make sure accumulation isn't reentrant
// 何?
for ( int i=0; i<amounts.Size(); i++ )
{
if ( !victim ) break;
victim.DamageMobj(inflictor,source,amounts[i],type,DMG_THRUSTLESS);
}
// clean up
if ( inflictor ) inflictor.bEXTREMEDEATH = false;
Destroy();
}
static void Accumulate( Actor victim, int amount, Actor inflictor, Actor source, Name type, bool dontgib = false )
{
if ( !victim ) return;
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
match = a;
break;
}
if ( !match )
{
match = new("SWWMDamageAccumulator");
match.ChangeStatNum(STAT_USER);
match.victim = victim;
match.amounts.Clear();
}
match.amounts.Push(amount);
match.total += amount;
match.inflictor = inflictor;
match.source = source;
match.type = type;
match.dontgib = dontgib;
}
static clearscope int GetAmount( Actor victim )
{
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
return a.total;
}
return 0;
}
}
Class SpreadgunTracer : LineTracer
{
Actor ignoreme;
Array<HitListEntry> hitlist;
Array<Line> shootthroughlist;
Array<WaterHit> waterhitlist;
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
int amt = SWWMDamageAccumulator.GetAmount(Results.HitActor);
// getgibhealth isn't clearscope, fuck
int gibhealth = -int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor);
if ( Results.HitActor.GibHealth != int.min ) gibhealth = -abs(Results.HitActor.GibHealth);
// if gibbed, go through without dealing more damage
if ( Results.HitActor.health-amt <= gibhealth ) return TRACE_Skip;
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
hitlist.Push(ent);
// go right on through if dead
if ( Results.HitActor.health-amt <= 0 ) return TRACE_Skip;
// stap
return TRACE_Abort;
}
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class SpreadSlugTracer : SpreadgunTracer
{
double penetration; // please don't laugh
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
ent.hitdamage = min(Results.HitActor.health+int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor),int(penetration));
hitlist.Push(ent);
penetration = max(0,penetration-ent.hitdamage);
if ( penetration <= 0 ) return TRACE_Abort;
return TRACE_Skip;
}
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class SpreadImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("spreadgun/pellet",CHAN_VOICE,CHANF_DEFAULT,.4,4.);
A_SprayDecal("TinyBulletChip",-20);
int numpt = Random[Spreadgun](-2,2);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (-x+(FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8))).unit()*FRandom[Spreadgun](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.scale *= .6;
s.special1 = Random[Spreadgun](0,1);
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
}
numpt = Random[Spreadgun](-3,3);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Spreadgun](-2,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class SlugImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("spreadgun/slug",CHAN_VOICE,CHANF_DEFAULT,1.,2.);
A_SprayDecal("BulletChip",-20);
int numpt = Random[Spreadgun](6,12);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (-x+(FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8))).unit()*FRandom[Spreadgun](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.special1 = Random[Spreadgun](0,2);
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
}
numpt = Random[Spreadgun](4,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Spreadgun](10,15);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class Spreadgun : SWWMWeapon
{
bool fired; // shell was used
@ -9,6 +306,18 @@ Class Spreadgun : SWWMWeapon
transient ui TextureID WeaponBox, AmmoIcon[7], LoadedIcon[7];
transient ui Font TewiFont;
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( loadammo is 'RedShell' ) return StringTable.Localize("$O_SPREADGUN_RED");
if ( loadammo is 'GreenShell' ) return StringTable.Localize("$O_SPREADGUN_GREEN");
if ( loadammo is 'WhiteShell' ) return StringTable.Localize("$O_SPREADGUN_WHITE");
if ( loadammo is 'BlueShell' ) return StringTable.Localize("$O_SPREADGUN_BLUE");
if ( loadammo is 'BlackShell' ) return StringTable.Localize("$O_SPREADGUN_BLACK");
if ( loadammo is 'PurpleShell' ) return StringTable.Localize("$O_SPREADGUN_PURPLE");
if ( loadammo is 'GoldShell' ) return StringTable.Localize("$O_SPREADGUN_GOLD");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
@ -122,6 +431,7 @@ Class Spreadgun : SWWMWeapon
action void A_DropShell()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
static const Class<Actor> casetypes[] = {"RedShellCasing","GreenShellCasing","WhiteShellCasing","BlueShellCasing","BlackShellCasing","PurpleShellCasing","GoldShellCasing"};
if ( !invoker.fired )
{
for ( int i=0; i<7; i++ )
@ -131,7 +441,81 @@ Class Spreadgun : SWWMWeapon
break;
}
}
else Console.Printf("// TODO drop spent shell");
else
{
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-10*z);
let c = Spawn(casetypes[i],origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2,3));
c.vel += vel*.5;
break;
}
}
}
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class<Actor> impact = "SpreadImpact", int bc = 1, bool large = false )
{
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
t.ShootThroughList[i].Activate(self,0,SPAC_PCross);
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Spawn(large?"InvisibleSplasher":"SmolInvisibleSplasher",t.WaterHitList[i].hitpos);
b.A_CheckTerrain();
}
for ( int i=5; i<t.Results.Distance; i+=10 )
{
if ( !Random[Boolet](0,bc) ) continue;
let b = Actor.Spawn("SWWMBubble",level.Vec3Offset(origin,dir*i));
b.Scale *= FRandom[Boolet](.1,.3);
}
for ( int i=0; i<t.HitList.Size(); i++ )
{
int realdmg = dmg?dmg:t.HitList[i].HitDamage;
SWWMDamageAccumulator.Accumulate(t.HitList[i].HitActor,realdmg,invoker,self,'shot',!large);
SWWMHandler.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Spreadgun](0.4,1.2));
if ( t.HitList[i].HitActor.bNOBLOOD || t.HitList[i].HitActor.bINVULNERABLE )
{
let p = Spawn(impact,t.HitList[i].HitLocation);
p.angle = atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180;
p.pitch = asin(t.HitList[i].x.z);
}
else
{
t.HitList[i].HitActor.TraceBleed(realdmg,self);
t.HitList[i].HitActor.SpawnBlood(t.HitList[i].HitLocation,atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180,realdmg);
if ( large ) t.HitList[i].HitActor.A_StartSound("spreadgun/slugf",CHAN_BODY,CHANF_OVERLAP,1.,2.);
else t.HitList[i].HitActor.A_StartSound("spreadgun/pelletf",CHAN_BODY,CHANF_OVERLAP,.4,4.);
}
}
if ( t.Results.HitType != TRACE_HitNone )
{
Vector3 hitnormal = -t.Results.HitVector;
if ( t.Results.HitType == TRACE_HitFloor )
{
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.top.Normal;
else hitnormal = t.Results.HitSector.floorplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.bottom.Normal;
else hitnormal = t.Results.HitSector.ceilingplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitWall )
{
hitnormal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
if ( !t.Results.Side ) hitnormal *= -1;
}
let p = Spawn(impact,t.Results.HitPos+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
if ( t.Results.HitLine ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
}
}
action void A_FireShell()
@ -142,7 +526,6 @@ Class Spreadgun : SWWMWeapon
static const int louds[] = {800,1000,1100,1200,1400,600,2500};
static const int quakes[] = {3,4,2,4,3,1,6};
static const Color cols[] = {Color(40,255,192,64),Color(36,255,192,80),Color(64,255,160,32),Color(48,32,176,255),Color(72,255,128,16),Color(24,255,224,96),Color(96,255,224,16)};
Console.Printf("// TODO fire shell");
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
@ -153,6 +536,108 @@ Class Spreadgun : SWWMWeapon
A_ZoomFactor(1.+quakes[i]*.04,ZOOM_INSTANT);
A_ZoomFactor(1.);
SWWMHandler.DoFlash(self,cols[i],5);
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z);
Vector3 x2, y2, z2;
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
double a, s;
Vector3 dir;
SpreadgunTracer st;
SpreadSlugTracer sst;
switch ( i )
{
case 1:
sst = new("SpreadSlugTracer");
sst.ignoreme = self;
sst.penetration = 250.;
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.01);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
sst.hitlist.Clear();
sst.shootthroughlist.Clear();
sst.waterhitlist.Clear();
sst.Trace(origin,level.PointInSector(origin.xy),dir,8000.,0);
ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1,true);
for ( int i=0; i<3; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.alpha *= 0.5;
}
for ( int i=0; i<6; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .8;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.);
}
for ( int i=0; i<10; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .2;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
break;
case 2:
Console.Printf("\cg// TODO Dragon's Breath\c-");
break;
case 3:
Console.Printf("\cg// TODO Kinylum Saltshot\c-");
break;
case 4:
Console.Printf("\cg// TODO Napalm Rounds\c-");
break;
case 5:
Console.Printf("\cg// TODO The Ball\c-");
break;
case 6:
Console.Printf("\cg// TODO Golden Shell\c-");
break;
default:
st = new("SpreadgunTracer");
st.ignoreme = self;
for ( int j=0; j<30; j++ )
{
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.22);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
st.hitlist.Clear();
st.shootthroughlist.Clear();
st.waterhitlist.Clear();
st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,0);
ProcessTraceHit(st,origin,dir,6,7000,bc:5);
}
for ( int i=0; i<9; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.alpha *= .2;
}
for ( int i=0; i<16; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
for ( int i=0; i<20; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
break;
}
break;
}
A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP);
@ -185,7 +670,6 @@ Class Spreadgun : SWWMWeapon
Ammo a = Ammo(other.FindInventory(types[i]));
if ( !a ) continue;
nextammo = types[i];
Ammo1 = a;
return;
}
nextammo = AmmoType1;
@ -206,17 +690,21 @@ Class Spreadgun : SWWMWeapon
int ridx;
if ( rev )
{
ridx = (cur-i)%7;
if ( ridx < 0 ) ridx = 6;
ridx = cur-i;
if ( ridx < 0 ) break;
}
else ridx = (i+cur+1)%7;
if ( CountInv(types[ridx]) <= 0 ) continue;
next = ridx;
break;
}
if ( rev && (invoker.nextammo == types[next]) )
{
A_SwitchAmmoType(false); // recheck forward
return;
}
if ( invoker.nextammo != types[next] ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL);
invoker.nextammo = types[next];
invoker.Ammo1 = Ammo(FindInventory(invoker.nextammo));
A_WeaponReady(WRF_NOFIRE);
}
@ -532,25 +1020,67 @@ Class Spreadgun : SWWMWeapon
XZWK FGHI 2;
Goto ReadyFired;
FlashRed:
XZWZ A 2 Bright;
XZWZ A 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 120;
l.target = self;
}
Stop;
FlashGreen:
XZWZ B 2 Bright;
XZWZ B 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 90;
l.target = self;
}
Stop;
FlashWhite:
XZWZ C 2 Bright;
XZWZ C 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[1] = 176;
l.args[2] = 32;
l.args[3] = 160;
l.target = self;
}
Stop;
FlashBlue:
XZWZ D 2 Bright;
XZWZ D 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[0] = 96;
l.args[1] = 224;
l.args[2] = 255;
l.args[3] = 160;
l.target = self;
}
Stop;
FlashBlack:
XZWZ E 2 Bright;
XZWZ E 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[1] = 144;
l.args[2] = 16;
l.args[3] = 280;
l.target = self;
}
Stop;
FlashPurple:
XZWZ F 2 Bright;
XZWZ F 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 60;
l.target = self;
}
Stop;
FlashGold:
XZWZ G 2 Bright;
XZWZ G 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 300;
l.target = self;
}
Stop;
}
}
}

View file

@ -318,7 +318,7 @@ Class ExplodiumGun : SWWMWeapon
int dmg = 40;
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,48000);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("SWWMBulletImpact",d.HitLocation);
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;

View file

@ -825,7 +825,7 @@ Class CandyGun : SWWMWeapon
int dmg = 350;
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,72000);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Explodium',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
if ( d.HitActor.bNOBLOOD )
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
let p = Spawn("SWWMBulletImpact",d.HitLocation);
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;