- New fun options implemented (omnibusting, unlimited fuel, party time) - Biospark Carbine gets a requested nerf - Candygun combo fire has been buffed (watch out for that splash damage) - All powerup effects are additive (stacc 'em) - Automap hud respects gzdoom's cvars for toggling certain elements - Automap hud shows stats and times in gold when 100% / under par - Weapons and ammo are no longer affected by skill amount modifiers, for balance (and to avoid any weird glitches) - Sorting improvements for menu (weapons by slot, ammo by weapons, other items by value, etc.) - Grilled Cheese Sandwich now saves you from lethal falls properly - Blown kisses instakill nazis - Added non-violent Keen replacement (based on "Less mean-spirited Keen replacement" by SiFi270) - Added gib deaths for hell nobles, pinkies, cacos, revs and viles (sprites by Amuscaria and Ryan Cordell) - Blown kisses can activate use switches - Gestures can be chained by pressing a gesture button while another is playing - Fixed Grilled Cheese Sandwich not avoiding telefrags properly (now also works with voodoo dolls) - More precise weapon kill tracking (fixes some ragekit quirks) - Merge both DLC weaponsets, removing redundant weapons (see FuturePlans.md) - Discarded some collectables for the next updates, to save time - Preparation work for collectables update, including some (partial) lore files - Remove ammo fabricators from store, makes no sense to have them when you can just buy ammo directly - Cosmetic Boss Brain sprite replacements, just for fun - 10 more intermission tips, because yes - Added option to reduce distance at which enemy healthbars are picked - Various minor bugfixes and adjustments (and also some tiny typo fixes) - Ragekit now heals over time and with each hit (so it's more rewarding to go wild) - PNG optimization pass (again lol) - Fix crouched gestures having no facial animation
1274 lines
29 KiB
Text
1274 lines
29 KiB
Text
// common code goes here
|
|
enum ESWWMGZChannels
|
|
{
|
|
CHAN_YOUDONEFUCKEDUP = 63200, // exception handler
|
|
CHAN_DEMOVOICE = 63201, // demolitionist voices
|
|
CHAN_FOOTSTEP = 63202, // footstep sounds and others
|
|
CHAN_WEAPONEXTRA = 63203, // additional weapon sounds (usually loops)
|
|
CHAN_POWERUP = 63204, // powerup sounds
|
|
CHAN_POWERUPEXTRA = 63205, // additional powerup sounds
|
|
CHAN_JETPACK = 63206, // jetpack sound
|
|
CHAN_ITEMEXTRA = 63207, // additional item sounds
|
|
CHAN_WEAPONEXTRA2 = 63208, // additional weapon sound slot
|
|
CHAN_WEAPONEXTRA3 = 63209, // additional weapon sound slot (again)
|
|
CHAN_DAMAGE = 63210, // used for impact/hit sounds
|
|
CHAN_AMBEXTRA = 63211 // player ambience when submerged
|
|
};
|
|
|
|
// Future planning, will be filled out with AI stuff and whatnot someday
|
|
Class SWWMMonster : Actor
|
|
{
|
|
// integrated fun tags
|
|
virtual clearscope String GetFunTag( String defstr = "" )
|
|
{
|
|
return GetTag(defstr);
|
|
}
|
|
|
|
// the function that should be overriden in subclasses
|
|
virtual int HandleLocationalDamage( Actor inflictor, Actor source, int damage, Name mod, Vector3 HitLocation, int flags = 0, double angle = 0 )
|
|
{
|
|
return damage;
|
|
}
|
|
|
|
// locational damage support, akin to UE1, but hitlocation will be treated as a relative offset, to make things easier
|
|
// this one should be called directly by everything in this mod, when possible
|
|
int LocationalDamageMobj( Actor inflictor, Actor source, int damage, Name mod, Vector3 HitLocation, int flags = 0, double angle = 0 )
|
|
{
|
|
damage = HandleLocationalDamage(inflictor,source,damage,mod,HitLocation,flags,angle);
|
|
return Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
|
}
|
|
|
|
// "estimated" locational damage for the vanilla DamageMobj
|
|
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
|
{
|
|
Vector3 guesspos = (0,0,Height/2.);
|
|
// use inflictor if available, as it may be a projectile or hitscan puff
|
|
// if damage comes from an item, use owner
|
|
// all of this could be done better (or implemented as an engine feature), but whatever
|
|
Actor whomst = inflictor?inflictor:source;
|
|
if ( whomst is 'Inventory' ) whomst = Inventory(whomst).Owner;
|
|
if ( whomst )
|
|
{
|
|
if ( whomst.bMISSILE || (flags&DMG_INFLICTOR_IS_PUFF) )
|
|
guesspos = level.Vec3Diff(pos,whomst.pos);
|
|
else guesspos = level.Vec3Diff(pos,whomst.Vec3Offset(0,0,whomst.Height/2));
|
|
guesspos.x = clamp(guesspos.x,-radius,radius);
|
|
guesspos.y = clamp(guesspos.y,-radius,radius);
|
|
guesspos.z = clamp(guesspos.z,0,height);
|
|
}
|
|
return LocationalDamageMobj(inflictor,source,damage,mod,guesspos,flags,angle);
|
|
}
|
|
}
|
|
|
|
// Less mean-spirited Keen
|
|
Class SWWMHangingKeen : Actor
|
|
{
|
|
action void A_DropKeen()
|
|
{
|
|
Spawn("SWWMDroppedKeen",Vec3Offset(0,0,8));
|
|
}
|
|
Default
|
|
{
|
|
Tag "$FN_KEEN";
|
|
Health 100;
|
|
Radius 10;
|
|
Height 54;
|
|
Mass int.max;
|
|
PainChance 256;
|
|
+SOLID;
|
|
+SPAWNCEILING;
|
|
+NOGRAVITY;
|
|
+SHOOTABLE;
|
|
+NOICEDEATH;
|
|
+DONTFALL;
|
|
+NOBLOOD;
|
|
+DONTTHRUST;
|
|
PainSound "keen/pain";
|
|
DeathSound "keen/death";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
KEE2 A -1;
|
|
Stop;
|
|
Death:
|
|
KEE2 A 6;
|
|
KEE2 B 6 A_DropKeen();
|
|
KEE2 C 6 A_Scream();
|
|
KEE2 DE 6;
|
|
KEE2 F 30;
|
|
KEE2 F -1 A_KeenDie();
|
|
Stop;
|
|
Pain:
|
|
KEE2 G 4;
|
|
KEE2 G 8 A_Pain();
|
|
Goto Spawn;
|
|
}
|
|
}
|
|
Class SWWMDroppedKeen : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 10;
|
|
Height 32;
|
|
Gravity .5;
|
|
+NOBLOCKMAP;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
KEE3 A 1 A_JumpIf(pos.z<=floorz,1);
|
|
Wait;
|
|
KEE3 B 1 { vel.z = 4; }
|
|
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
|
|
Wait;
|
|
KEE3 B 1 { vel.z = 2; }
|
|
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
|
|
Wait;
|
|
KEE3 B 12;
|
|
TNT1 A 1 { Spawn("TeleportFog",pos,ALLOW_REPLACE); }
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop
|
|
Class PaletteLight : DynamicLight
|
|
{
|
|
Color pal[256];
|
|
bool IsLooping;
|
|
|
|
Default
|
|
{
|
|
Tag "Explosion";
|
|
DynamicLight.Type "Point";
|
|
Args 0,0,0,80;
|
|
ReactionTime 15;
|
|
}
|
|
private void UpdateLight()
|
|
{
|
|
int index = clamp(255-((255*ReactionTime)/abs(default.ReactionTime)),0,255);
|
|
args[LIGHT_RED] = pal[index].r;
|
|
args[LIGHT_GREEN] = pal[index].g;
|
|
args[LIGHT_BLUE] = pal[index].b;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",GetTag()));
|
|
String paldat = Wads.ReadLump(lump);
|
|
for ( int i=0; i<256; i++ )
|
|
{
|
|
pal[i].r = paldat.ByteAt(i*3);
|
|
pal[i].g = paldat.ByteAt(i*3+1);
|
|
pal[i].b = paldat.ByteAt(i*3+2);
|
|
}
|
|
if ( ReactionTime < 0 )
|
|
{
|
|
ReactionTime = -ReactionTime;
|
|
IsLooping = true;
|
|
}
|
|
UpdateLight();
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
ReactionTime--;
|
|
if ( ReactionTime < 0 )
|
|
{
|
|
if ( !IsLooping )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
else ReactionTime = abs(default.ReactionTime);
|
|
}
|
|
if ( target ) SetOrigin(target.pos,true);
|
|
UpdateLight();
|
|
}
|
|
}
|
|
|
|
// Generic smoke, lightweight tick
|
|
Class SWWMSmoke : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Shaded";
|
|
StencilColor "FFFFFF";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+NOINTERACTION;
|
|
Scale .3;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](.5,1.5);
|
|
alpha *= FRandom[Puff](.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](.2,.8);
|
|
roll = Frandom[Puff](0,360);
|
|
scale.x *= RandomPick[Puff](-1,1);
|
|
scale.y *= RandomPick[Puff](-1,1);
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( isFrozen() ) return;
|
|
vel *= .96;
|
|
vel.z += .01;
|
|
FCheckPosition tm;
|
|
// movement subdivision (these damn things are tiny)
|
|
int steps = 8;
|
|
while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) )
|
|
steps++;
|
|
bool domove = (vel!=(0,0,0));
|
|
if ( domove )
|
|
{
|
|
Vector3 steppy = vel/steps;
|
|
int changexy = steppy.X||steppy.Y;
|
|
bool readjust = false;
|
|
for ( int i=0; i<steps; i++ )
|
|
{
|
|
if ( changexy && !TryMove(Vec2Offset(steppy.x,steppy.y),1,false,tm) )
|
|
{
|
|
// hit wall, slide along it
|
|
if ( BlockingLine )
|
|
{
|
|
Vector3 normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
|
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
|
|
normal *= -1;
|
|
vel = vel-FRandom[Puff](1.,1.2)*normal*(vel dot normal);
|
|
}
|
|
readjust = true;
|
|
}
|
|
AddZ(steppy.z);
|
|
UpdateWaterLevel();
|
|
if ( pos.z <= floorz )
|
|
{
|
|
// landed on floor, slide along it
|
|
SetZ(floorz);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(FloorSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(FloorSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = FloorSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.ceilingplane.Normal;
|
|
else normal = FloorSector.floorplane.Normal;
|
|
vel = vel-FRandom[Puff](1.,1.2)*normal*(vel dot normal);
|
|
readjust = true;
|
|
}
|
|
if ( pos.z+height > ceilingz )
|
|
{
|
|
// hit the ceiling, slide along it
|
|
SetZ(ceilingz-height);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<CeilingSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CeilingSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(CeilingSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CeilingSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.floorplane.Normal;
|
|
else normal = CeilingSector.ceilingplane.Normal;
|
|
vel = vel-FRandom[Puff](1.,1.2)*normal*(vel dot normal);
|
|
readjust = true;
|
|
}
|
|
CheckPortalTransition();
|
|
if ( readjust ) break; // movement changed, can't keep stepping
|
|
}
|
|
}
|
|
if ( (waterlevel > 0) && !bAMBUSH )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.scale *= abs(scale.x);
|
|
b.vel = vel;
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// strictly non-interacting smoke, much lighter tick, used for heavier effects
|
|
Class SWWMHalfSmoke : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Shaded";
|
|
StencilColor "FFFFFF";
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+NOTELEPORT;
|
|
+NOINTERACTION;
|
|
Scale 0.3;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.5,1.5);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
|
|
roll = Frandom[Puff](0,360);
|
|
scale.x *= RandomPick[Puff](-1,1);
|
|
scale.y *= RandomPick[Puff](-1,1);
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( isFrozen() ) return;
|
|
vel *= 0.96;
|
|
vel.z += 0.01;
|
|
SetOrigin(level.Vec3Offset(pos,vel),true);
|
|
UpdateWaterLevel();
|
|
if ( (waterlevel > 0) && !bAMBUSH )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.scale *= abs(scale.x);
|
|
b.vel = vel;
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMSmallSmoke : SWWMHalfSmoke
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.1,0.3);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
|
|
roll = Frandom[Puff](0,360);
|
|
scale.x *= RandomPick[Puff](-1,1);
|
|
scale.y *= RandomPick[Puff](-1,1);
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
QSM6 ABCDEFGHIJKLMNOPQR 1 A_SetTics(1+special1);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// visual smoke, very strictly non-interacting, updates even when game is frozen
|
|
Class SWWMViewSmoke : SWWMHalfSmoke
|
|
{
|
|
Vector3 ofs, vvel;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.1,0.3);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vvel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( !target || !target.player )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
|
|
Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
|
|
SetOrigin(origin,true);
|
|
UpdateWaterLevel();
|
|
bInvisible = (players[consoleplayer].camera != target);
|
|
ofs += vvel;
|
|
vvel *= 0.96;
|
|
vvel.z += 0.01;
|
|
if ( (waterlevel > 0) && !bAMBUSH ) Destroy();
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Class SWWMBubble : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+NOTELEPORT;
|
|
+THRUACTORS;
|
|
+NOINTERACTION;
|
|
Scale 0.5;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
|
|
if ( waterlevel <= 0 ) Destroy();
|
|
SetState(ResolveState("Spawn")+Random[Puff](0,19));
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos;
|
|
if ( isFrozen() ) return;
|
|
vel *= 0.96;
|
|
vel.z += 0.05;
|
|
FCheckPosition tm;
|
|
// movement subdivision (these damn things are tiny)
|
|
int steps = 8;
|
|
while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) )
|
|
steps++;
|
|
bool domove = (vel!=(0,0,0));
|
|
if ( domove )
|
|
{
|
|
Vector3 steppy = vel/steps;
|
|
int changexy = steppy.X||steppy.Y;
|
|
bool readjust = false;
|
|
for ( int i=0; i<steps; i++ )
|
|
{
|
|
if ( changexy && !TryMove(Vec2Offset(steppy.x,steppy.y),1,false,tm) )
|
|
{
|
|
// hit wall, slide along it
|
|
if ( BlockingLine )
|
|
{
|
|
Vector3 normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();;
|
|
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
|
|
normal *= -1;
|
|
vel = vel-FRandom[Puff](1.,1.2)*normal*(vel dot normal);
|
|
}
|
|
readjust = true;
|
|
}
|
|
AddZ(steppy.z);
|
|
UpdateWaterLevel();
|
|
if ( pos.z <= floorz )
|
|
{
|
|
// landed on floor, slide along it
|
|
SetZ(floorz);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(FloorSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(FloorSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = FloorSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.ceilingplane.Normal;
|
|
else normal = FloorSector.floorplane.Normal;
|
|
vel = vel-FRandom[Puff](1.,1.2)*normal*(vel dot normal);
|
|
readjust = true;
|
|
}
|
|
if ( pos.z+height > ceilingz )
|
|
{
|
|
// hit the ceiling, slide along it
|
|
SetZ(ceilingz-height);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<CeilingSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CeilingSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(CeilingSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CeilingSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.floorplane.Normal;
|
|
else normal = CeilingSector.ceilingplane.Normal;
|
|
vel = vel-FRandom[Puff](1.,1.2)*normal*(vel dot normal);
|
|
readjust = true;
|
|
}
|
|
CheckPortalTransition();
|
|
if ( readjust ) break; // movement changed, can't keep stepping
|
|
}
|
|
}
|
|
UpdateWaterLevel();
|
|
if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy();
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XBUB ABCDEFGHIJKLMNOPQRST 1;
|
|
Loop;
|
|
}
|
|
}
|
|
|
|
Class SWWMSpark : Actor
|
|
{
|
|
bool dead;
|
|
Sector tracksector;
|
|
int trackplane;
|
|
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+FORCEXYBILLBOARD;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
Gravity 0.2;
|
|
Scale 0.05;
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos;
|
|
if ( isFrozen() ) return;
|
|
if ( dead )
|
|
{
|
|
// do nothing but follow floor movement
|
|
if ( tracksector )
|
|
{
|
|
double trackz;
|
|
if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy);
|
|
else trackz = tracksector.floorplane.ZAtPoint(pos.xy);
|
|
if ( trackz != pos.z )
|
|
{
|
|
SetZ(trackz);
|
|
UpdateWaterLevel(false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vel.z -= GetGravity();
|
|
FCheckPosition tm;
|
|
// movement subdivision (these damn things are tiny)
|
|
int steps = 8;
|
|
while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) )
|
|
steps++;
|
|
bool domove = (vel!=(0,0,0));
|
|
if ( domove )
|
|
{
|
|
Vector3 steppy = vel/steps;
|
|
int changexy = steppy.X||steppy.Y;
|
|
bool readjust = false;
|
|
for ( int i=0; i<steps; i++ )
|
|
{
|
|
if ( changexy && !TryMove(Vec2Offset(steppy.x,steppy.y),1,false,tm) )
|
|
{
|
|
// hit wall, bounce on it
|
|
if ( BlockingLine )
|
|
{
|
|
Vector3 normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();;
|
|
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
|
|
normal *= -1;
|
|
vel = .4*(vel-2.*normal*(vel dot normal));
|
|
}
|
|
readjust = true;
|
|
}
|
|
AddZ(steppy.z);
|
|
UpdateWaterLevel();
|
|
if ( pos.z <= floorz )
|
|
{
|
|
// landed on floor, bounce on it
|
|
SetZ(floorz);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(FloorSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(FloorSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = FloorSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.ceilingplane.Normal;
|
|
else normal = FloorSector.floorplane.Normal;
|
|
vel = .4*(vel-2.*normal*(vel dot normal));
|
|
readjust = true;
|
|
}
|
|
if ( pos.z+height > ceilingz )
|
|
{
|
|
// hit the ceiling, bounce on it
|
|
SetZ(ceilingz-height);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<CeilingSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CeilingSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(CeilingSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CeilingSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.floorplane.Normal;
|
|
else normal = CeilingSector.ceilingplane.Normal;
|
|
vel = .4*(vel-2.*normal*(vel dot normal));
|
|
readjust = true;
|
|
}
|
|
CheckPortalTransition();
|
|
if ( readjust ) break; // movement changed, can't keep stepping
|
|
}
|
|
}
|
|
if ( (max(abs(vel.x),max(abs(vel.y),abs(vel.z))) < 1.) && (pos.z <= floorz) )
|
|
{
|
|
SetZ(floorz);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(FloorSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(FloorSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = FloorSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
if ( ff )
|
|
{
|
|
tracksector = ff.model;
|
|
trackplane = 1;
|
|
}
|
|
else
|
|
{
|
|
tracksector = FloorSector;
|
|
trackplane = 0;
|
|
}
|
|
vel = (0,0,0);
|
|
dead = true;
|
|
SetStateLabel("Death");
|
|
return;
|
|
}
|
|
}
|
|
UpdateWaterLevel();
|
|
if ( waterlevel > 0 )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.vel = vel;
|
|
b.scale *= 0.3;
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF A 1 Bright A_FadeOut(0.01);
|
|
Wait;
|
|
Death:
|
|
BLPF A 1 Bright A_FadeOut(0.05);
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class SWWMChip : Actor
|
|
{
|
|
SWWMChip prevchip, nextchip;
|
|
bool killme;
|
|
double anglevel, pitchvel, rollvel;
|
|
bool dead;
|
|
Sector tracksector;
|
|
int trackplane;
|
|
|
|
Default
|
|
{
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+INTERPOLATEANGLES;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+FORCEXYBILLBOARD;
|
|
+NOINTERACTION;
|
|
Gravity 0.35;
|
|
Scale 0.2;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
frame = Random[Junk](0,7);
|
|
scale *= Frandom[Junk](0.8,1.2);
|
|
SWWMHandler.QueueChip(self);
|
|
}
|
|
override void OnDestroy()
|
|
{
|
|
SWWMHandler.DeQueueChip(self);
|
|
Super.OnDestroy();
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( isFrozen() ) return;
|
|
if ( dead )
|
|
{
|
|
// do nothing but follow floor movement
|
|
if ( tracksector )
|
|
{
|
|
double trackz;
|
|
if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy);
|
|
else trackz = tracksector.floorplane.ZAtPoint(pos.xy);
|
|
if ( trackz != pos.z )
|
|
{
|
|
SetZ(trackz);
|
|
UpdateWaterLevel(false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vel.z -= GetGravity();
|
|
FCheckPosition tm;
|
|
// movement subdivision (these damn things are tiny)
|
|
int steps = 8;
|
|
while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) )
|
|
steps++;
|
|
bool domove = (vel!=(0,0,0));
|
|
if ( domove )
|
|
{
|
|
Vector3 steppy = vel/steps;
|
|
int changexy = steppy.X||steppy.Y;
|
|
bool readjust = false;
|
|
for ( int i=0; i<steps; i++ )
|
|
{
|
|
if ( changexy && !TryMove(Vec2Offset(steppy.x,steppy.y),1,false,tm) )
|
|
{
|
|
// hit wall, bounce on it
|
|
if ( BlockingLine )
|
|
{
|
|
Vector3 normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();;
|
|
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
|
|
normal *= -1;
|
|
vel = .3*(vel-2.*normal*(vel dot normal));
|
|
}
|
|
readjust = true;
|
|
}
|
|
AddZ(steppy.z);
|
|
UpdateWaterLevel();
|
|
if ( pos.z <= floorz )
|
|
{
|
|
// landed on floor, bounce on it
|
|
SetZ(floorz);
|
|
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;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.ceilingplane.Normal;
|
|
else normal = FloorSector.floorplane.Normal;
|
|
vel = .3*(vel-2.*normal*(vel dot normal));
|
|
readjust = true;
|
|
}
|
|
if ( pos.z+height > ceilingz )
|
|
{
|
|
// hit the ceiling, bounce on it
|
|
SetZ(ceilingz-height);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<CeilingSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CeilingSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CeilingSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
Vector3 normal;
|
|
if ( ff ) normal = -ff.model.floorplane.Normal;
|
|
else normal = CeilingSector.ceilingplane.Normal;
|
|
vel = .3*(vel-2.*normal*(vel dot normal));
|
|
readjust = true;
|
|
}
|
|
CheckPortalTransition();
|
|
if ( readjust )
|
|
{
|
|
SetStateLabel("Bounce");
|
|
break; // movement changed, can't keep stepping
|
|
}
|
|
}
|
|
}
|
|
if ( (max(abs(vel.x),max(abs(vel.y),abs(vel.z))) < 1.) && (pos.z <= floorz) )
|
|
{
|
|
SetZ(floorz);
|
|
F3DFloor ff;
|
|
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(FloorSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
|
if ( !(FloorSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = FloorSector.Get3DFloor(i);
|
|
break;
|
|
}
|
|
if ( ff )
|
|
{
|
|
tracksector = ff.model;
|
|
trackplane = 1;
|
|
}
|
|
else
|
|
{
|
|
tracksector = FloorSector;
|
|
trackplane = 0;
|
|
}
|
|
vel = (0,0,0);
|
|
pitch = 0;
|
|
roll = 0;
|
|
dead = true;
|
|
SetStateLabel("Death");
|
|
return;
|
|
}
|
|
}
|
|
if ( killme ) A_FadeOut(.01);
|
|
if ( waterlevel > 0 )
|
|
{
|
|
vel *= .98;
|
|
rollvel *= .98;
|
|
}
|
|
if ( !CheckNoDelay() || (tics == -1) ) return;
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 # 1
|
|
{
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
roll += rollvel;
|
|
}
|
|
Loop;
|
|
Bounce:
|
|
XZW1 # 0
|
|
{
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
}
|
|
Goto Spawn;
|
|
Death:
|
|
XZW2 # -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMNothing : Actor
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class PoofLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "Yellow";
|
|
ReactionTime 5;
|
|
Args 0,0,0,60;
|
|
}
|
|
}
|
|
Class PoofLight2 : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "Yellow";
|
|
ReactionTime 20;
|
|
Args 0,0,0,90;
|
|
}
|
|
}
|
|
|
|
Class SWWMItemFog : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+NOINTERACTION;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF A 2 Bright NoDelay
|
|
{
|
|
// offset up
|
|
SetOrigin(Vec3Offset(0,0,16),false);
|
|
roll = FRandom[ExploS](0,360);
|
|
scale *= FRandom[ExploS](0.9,1.1);
|
|
scale.x *= RandomPick[ExploS](-1,1);
|
|
scale.y *= RandomPick[ExploS](-1,1);
|
|
int numpt = Random[ExploS](8,12);
|
|
if ( bAMBUSH ) numpt *= 2;
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8);
|
|
let s = Spawn(bAMBUSH?"SWWMSmoke":"SWWMSmallSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(3,2,1)*Random[ExploS](64,85));
|
|
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
|
|
s.scale *= 3.;
|
|
s.alpha *= bAMBUSH?.4:.2;
|
|
}
|
|
Spawn(bAMBUSH?"PoofLight2":"PoofLight",pos);
|
|
}
|
|
BLPF A 1 Bright A_FadeOut(.3);
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class TeleLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "ImpactWav";
|
|
ReactionTime 10;
|
|
Args 0,0,0,150;
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportSparkle : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 0.3;
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+FORCEXYBILLBOARD;
|
|
+NOINTERACTION;
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( isFrozen() ) return;
|
|
A_SetScale(scale.x*specialf1);
|
|
A_FadeOut(specialf2);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF C -1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportFog : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
+FORCEXYBILLBOARD;
|
|
Radius .1;
|
|
Height 0.;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SetOrigin(Vec3Offset(0,0,28),false);
|
|
A_StartSound("misc/teleport",CHAN_VOICE);
|
|
Spawn("TeleLight",pos);
|
|
if ( swwm_simplefog ) SetStateLabel("Simple");
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1
|
|
{
|
|
int numpt = int(Random[ExploS](6,12)*alpha);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8)*alpha;
|
|
let s = Spawn("SWWMSmallSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,2,3)*int(Random[ExploS](64,85)*alpha));
|
|
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
|
|
s.scale *= 3.*alpha;
|
|
s.alpha *= alpha;
|
|
}
|
|
numpt = int(Random[ExploS](4,8));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
double ang = FRandom[ExploS](0,360);
|
|
double pt = FRandom[ExploS](-90,90);
|
|
double dist = (FRandom[ExploS](5,10)+60*(1.-alpha));
|
|
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
|
|
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
|
|
Vector3 spos = level.Vec3Offset(pos,ofs);
|
|
let s = Spawn("SWWMTeleportSparkle",spos);
|
|
s.scale *= FRandom[ExploS](.8,1.2);
|
|
s.specialf1 = FRandom[ExploS](.93,.97);
|
|
s.specialf2 = FRandom[ExploS](.02,.04);
|
|
s.roll = FRandom[ExploS](0,360);
|
|
}
|
|
A_FadeOut(.07);
|
|
}
|
|
Wait;
|
|
Simple:
|
|
SPEX ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Bullet trails from DT
|
|
Class WaterHit
|
|
{
|
|
Sector sect;
|
|
Vector3 hitpos;
|
|
}
|
|
|
|
Class InvisibleSplasher : Actor
|
|
{
|
|
Default
|
|
{
|
|
Mass 100;
|
|
VSpeed -2;
|
|
Radius 2;
|
|
Radius 2;
|
|
+NOBLOCKMAP; // needed to prevent infinite loops with some 3D floor water (yes, you read that right)
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 2;
|
|
Stop;
|
|
}
|
|
}
|
|
Class SmolInvisibleSplasher : InvisibleSplasher
|
|
{
|
|
Default
|
|
{
|
|
Mass 10;
|
|
}
|
|
}
|
|
|
|
Class SWWMBulletTrail : LineTracer
|
|
{
|
|
Array<WaterHit> WaterHitList;
|
|
Array<Line> ShootThroughList;
|
|
Actor ignoreme;
|
|
|
|
static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, int dist, int bubblechance, bool smoky = false )
|
|
{
|
|
let t = new("SWWMBulletTrail");
|
|
t.ignoreme = target;
|
|
t.WaterHitList.Clear();
|
|
t.ShootThroughList.Clear();
|
|
t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0);
|
|
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
|
{
|
|
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
|
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
|
|
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
|
|
}
|
|
for ( int i=0; i<t.WaterHitList.Size(); i++ )
|
|
{
|
|
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](.4,.6);
|
|
}
|
|
t.Destroy();
|
|
}
|
|
|
|
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 ) return TRACE_Stop;
|
|
return TRACE_Skip;
|
|
}
|
|
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
|
|
{
|
|
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
|
|
return TRACE_Stop;
|
|
ShootThroughList.Push(Results.HitLine);
|
|
return TRACE_Skip;
|
|
}
|
|
return TRACE_Stop;
|
|
}
|
|
}
|
|
|
|
// Elastic recoil from DT (unused)
|
|
Enum ESwingMode
|
|
{
|
|
SWING_Straight, // constant increment
|
|
SWING_Spring, // bounces back after a delay
|
|
};
|
|
|
|
Class Swinger : Thinker
|
|
{
|
|
Actor target;
|
|
Vector2 dir;
|
|
double inc, rmul;
|
|
int steps, mode, delay;
|
|
double str, tstr;
|
|
int cnt, cstate;
|
|
|
|
Enum ESwingerState
|
|
{
|
|
STATE_Initial,
|
|
STATE_Wait,
|
|
STATE_Return,
|
|
};
|
|
|
|
override void Tick()
|
|
{
|
|
if ( !target ) cstate = -1;
|
|
switch ( cstate )
|
|
{
|
|
case STATE_Initial:
|
|
target.A_SetAngle(target.angle+dir.x*str,SPF_INTERPOLATE);
|
|
target.A_SetPitch(target.pitch+dir.y*str,SPF_INTERPOLATE);
|
|
str += inc;
|
|
if ( ++cnt >= steps )
|
|
{
|
|
cnt = 0;
|
|
str = tstr/steps;
|
|
cstate = (mode==SWING_Straight)?(-1):(delay>0)?STATE_Wait:STATE_Return;
|
|
}
|
|
else tstr += str;
|
|
break;
|
|
case STATE_Wait:
|
|
if ( ++cnt >= delay )
|
|
{
|
|
cnt = 0;
|
|
cstate = STATE_Return;
|
|
}
|
|
break;
|
|
case STATE_Return:
|
|
target.A_SetAngle(target.angle-dir.x*(str/rmul),SPF_INTERPOLATE);
|
|
target.A_SetPitch(target.pitch-dir.y*(str/rmul),SPF_INTERPOLATE);
|
|
if ( ++cnt >= steps*rmul )
|
|
{
|
|
cnt = 0;
|
|
cstate = -1;
|
|
}
|
|
break;
|
|
default:
|
|
Destroy();
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void DoSwing( Actor target, Vector2 dir, double initial, double inc, int steps, int mode = 0, int delay = 0, double rmul = 1.0 )
|
|
{
|
|
let s = new("Swinger");
|
|
s.ChangeStatNum(Thinker.STAT_USER);
|
|
s.target = target;
|
|
s.dir = dir;
|
|
s.inc = inc;
|
|
s.rmul = rmul;
|
|
s.steps = steps;
|
|
s.mode = mode;
|
|
s.delay = delay;
|
|
s.cnt = 0;
|
|
s.cstate = 0;
|
|
s.str = initial;
|
|
s.tstr = initial;
|
|
}
|
|
}
|