swwmgz_m/zscript/swwm_common.zsc

397 lines
9.6 KiB
Text

// common code goes here
// extra sound channels for the mod
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
};
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
// fixup for 4.5 compat, will be removed once a new version lands
// SKILLP_SpawnMulti is not defined in zscript, so it has to be declared here
// (somehow this hackaround doesn't break anything in devbuilds)
const SKILLP_SpawnMulti = SKILLP_PlayerRespawn+1;
// Future planning, will be filled out with AI stuff and whatnot someday
Class SWWMMonster : Actor abstract
{
// 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));
}
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 ( Health > 0 )
{
DamageMobj(user,user,Health,'Untie',DMG_FORCED|DMG_THRUSTLESS);
return true;
}
return false;
}
Default
{
Tag "$FN_KEEN";
Health 100;
Radius 10;
Height 54;
Mass int.max;
PainChance 256;
+SOLID;
+SPAWNCEILING;
+NOGRAVITY;
+SHOOTABLE;
+NOICEDEATH;
+DONTFALL;
+NOBLOOD;
+DONTTHRUST;
}
States
{
Spawn:
KEE2 A -1;
Stop;
Death:
KEE2 A 6 A_StartSound("newkeen/hit");
KEE2 B 6 A_DropKeen();
KEE2 C 6;
KEE2 DE 6;
KEE2 F 30;
KEE2 F -1 A_KeenDie();
Stop;
Pain:
KEE2 G 4 A_StartSound("newkeen/hit");
KEE2 G 8;
Goto Spawn;
}
}
Class SWWMDroppedKeen : Actor
{
Default
{
Radius 10;
Height 32;
Gravity .5;
+NOBLOCKMAP;
}
States
{
Spawn:
KEE3 A 0 NoDelay
{
A_StartSound("newkeen/fall",volume:.5);
A_StartSound("newkeen/die",CHAN_VOICE);
}
KEE3 A 1 A_JumpIf(pos.z<=floorz,1);
Wait;
KEE3 B 1
{
A_StartSound("newkeen/bounce");
vel.z = 4;
}
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
Wait;
KEE3 B 1
{
A_StartSound("newkeen/bounce",volume:.8);
vel.z = 2;
}
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
Wait;
KEE3 B 12 A_StartSound("newkeen/bounce",volume:.6);
TNT1 A 1 { Spawn("TeleportFog",pos,ALLOW_REPLACE); }
Stop;
}
}
Class SWWMBossBrainExpl : Actor
{
void A_Ignite()
{
A_QuakeEx(3,3,3,20,0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollintensity:2.);
A_StartSound("explodium/hit",CHAN_VOICE,CHANF_DEFAULT,.4,.5);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](8,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](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](5,10);
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](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](2,24);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = int(Random[ExploS](-1,2)+special1);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("SWWMBossBrainExplArm",pos);
s.target = target;
}
}
Default
{
RenderStyle "Add";
Scale 2.5;
Radius .1;
Height 0.;
+NOGRAVITY;
+NOBLOCKMAP;
+NOINTERACTION;
+DONTSPLASH;
+NOTELEPORT;
+FORCEXYBILLBOARD;
}
States
{
Spawn:
TNT1 A 10;
TNT1 A 0 A_Ignite();
XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
Stop;
}
}
Class SWWMBossBrainExplArm : Actor
{
Default
{
PROJECTILE;
+THRUACTORS;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
-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
{
A_CountDown();
Spawn("ExplodiumMagTrail",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,3);
s.scale *= 2.4;
s.alpha *= 0.1+.4*(ReactionTime/15.);
}
Wait;
}
}
Class SWWMBossBrainPain : Actor
{
Default
{
RenderStyle "Add";
Radius .1;
Height 0.;
}
States
{
Spawn:
MBRN B 1 Bright A_FadeOut(.05);
Wait;
}
}
Class SWWMBossBrain : BossBrain
{
bool eyeless;
int smallcooldown;
override void PostBeginPlay()
{
Super.PostBeginPlay();
let ti = ThinkerIterator.Create("BossEye");
if ( ti.Next() ) eyeless = false;
else eyeless = true;
if ( !eyeless )
{
// proper boss
bCOUNTKILL = true;
level.total_monsters++;
}
}
void SpawnBrainExpl( bool death = false )
{
if ( death )
{
// big explosions throughout
for ( int x=-350; x<=350; x+=10 )
{
let s = Spawn("SWWMBossBrainExpl",Vec2OffsetZ(x,-280,Random[BrainExplode](120,500)));
s.tics = Random[BrainExplode](5,120);
s.special1 = Random[BrainExplode](0,3);
}
return;
}
if ( level.maptime < smallcooldown ) return;
smallcooldown = level.maptime+10;
// small explosion on brain hole
for ( int x=-40; x<=40; x+=20 )
{
let s = Spawn("SWWMBossBrainExpl",Vec2OffsetZ(x,-280,Random[BrainExplode](380,420)));
s.tics = Random[BrainExplode](1,10);
s.scale *= .5;
}
}
// kill every single monster in the map, burn away spawn cubes, remove eyes
// just let players have their 100% kills in peace
void EverythingDies()
{
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( a is 'BossEye' ) a.SetStateLabel("Null");
else if ( a is 'SpawnShot' )
{
a.Spawn("SpawnFire",a.pos,ALLOW_REPLACE);
a.SetStateLabel("Null");
}
else if ( (a.Health > 0) && (a.bBossSpawned || a.bCOUNTKILL) )
a.DamageMobj(self,self,a.Health,'EndMii',DMG_FORCED|DMG_THRUSTLESS);
}
}
Default
{
Tag "$FN_BOSSBRAIN";
Radius 20;
Height 40;
+NOBLOOD;
}
States
{
Spawn:
MBRN A -1;
Stop;
Pain:
MBRN A 10
{
A_StartSound("brain/pain",CHAN_VOICE,attenuation:ATTN_NONE);
A_QuakeEx(3,3,3,15,0,65535,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.3);
if ( !eyeless ) SpawnBrainExpl(false);
Spawn("SWWMBossBrainPain",pos);
}
Goto Spawn;
Death:
MBRN A 120
{
A_StartSound("brain/death",CHAN_VOICE,attenuation:ATTN_NONE);
A_QuakeEx(9,9,9,120,0,65535,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
if ( !eyeless )
{
SpawnBrainExpl(true);
EverythingDies();
}
Spawn("SWWMBossBrainPain",pos);
}
MBRN A -1 A_BrainDie();
Stop;
}
}
Class SWWMNothing : Actor
{
States
{
Spawn:
TNT1 A 1;
Stop;
}
}