A gameplay modification for GZDoom https://github.com/ZDoom/gzdoom
Made for Freedoom but should work with any Doom IWAD.
More discussion can be found on the forums: https://forum.zdoom.org/viewtopic.php?f=43&t=12973
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1054 lines
24 KiB
1054 lines
24 KiB
// ------------------------------------------------------------
|
|
// Misc. effects
|
|
// ------------------------------------------------------------
|
|
|
|
//channel constants
|
|
enum HDSoundChannels{
|
|
CHAN_WEAPONBODY=8, //for weapon sounds that are not the gun firing
|
|
CHAN_POCKETS=9, //for pocket sounds in reloading, etc.
|
|
CHAN_ARCZAP=69420, //electrical zapping arc noises
|
|
CHAN_DISTANT=4047, //distant gunfire sounds
|
|
}
|
|
|
|
|
|
//debris actor: simplified physics, just bounce until dead and lie still, +noblockmap
|
|
//basically we just need to account for conveyors and platforms
|
|
class HDDebris:HDActor{
|
|
bool stopped;
|
|
double grav;
|
|
double wdth;
|
|
default{
|
|
+noblockmap -solid -shootable +dontgib +forcexybillboard +notrigger +cannotpush
|
|
height 2;radius 2;
|
|
bouncesound "misc/casing2";bouncefactor 0.4;maxstepheight 2;
|
|
+rollsprite;+rollcenter;
|
|
}
|
|
override void postbeginplay(){
|
|
if(max(abs(pos.x),abs(pos.y),abs(pos.z))>=32768){destroy();return;}
|
|
super.postbeginplay();
|
|
stopped=false;
|
|
grav=getgravity();
|
|
if(bwallsprite)grav*=frandom(0.4,0.9);
|
|
wdth=radius*1.8;
|
|
}
|
|
override void Tick(){
|
|
if(isfrozen())return;
|
|
if(bmovewithsector){
|
|
actor.tick();
|
|
if(bnointeraction)return;
|
|
if(vel.xy==(0,0)&&floorz>=pos.z){
|
|
setz(floorz);
|
|
bnointeraction=true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
double velxylength=vel.xy.length();
|
|
int fracamount=int(max(1,velxylength/radius));
|
|
vector3 frac=vel/fracamount;
|
|
bool keeptrymove=true;
|
|
for(int i=0;i<fracamount;i++){
|
|
addz(frac.z,true);
|
|
if(keeptrymove&&!trymove(pos.xy+frac.xy,true,true)){
|
|
A_StartSound(bouncesound);
|
|
if(blockingmobj){
|
|
vel*=-bouncefactor;
|
|
}else if(blockingline){
|
|
vel*=bouncefactor;
|
|
vel.xy=rotatevector(vel.xy,frandom(80,280));
|
|
}
|
|
keeptrymove=false;
|
|
}
|
|
}
|
|
checkportaltransition();
|
|
|
|
bool onfloor=floorz>=pos.z;
|
|
|
|
//bounce off floor or ceiling
|
|
if(
|
|
onfloor
|
|
||ceilingz<=pos.z //most debris actors are negligible height
|
|
){
|
|
if(onfloor)setz(floorz);
|
|
A_StartSound(bouncesound);
|
|
if(velxylength)vel.xy=rotatevector(vel.xy,frandom(-10,10)*(vel.z?1./vel.z:0.01))*bouncefactor;
|
|
else vel.xy=(frandom(-0.01,0.01),frandom(-0.01,0.01));
|
|
vel.z=onfloor?abs(vel.z*bouncefactor):-abs(vel.z*bouncefactor);
|
|
}
|
|
|
|
//apply gravity
|
|
if(onfloor){
|
|
if(velxylength<0.01){
|
|
if(findstate("death"))setstatelabel("death");
|
|
else{destroy();return;}
|
|
brelativetofloor=true;
|
|
bmovewithsector=true;
|
|
if(vel.x||vel.y)vel.xy+=vel.xy.unit()*abs(vel.z);
|
|
}else vel.xy*=0.99;
|
|
}else vel.z-=grav;
|
|
|
|
NextTic();
|
|
}
|
|
}
|
|
|
|
|
|
//the wallchunk!
|
|
class WallChunk:HDDebris{
|
|
default{
|
|
+noteleport
|
|
scale 0.16;bouncesound "none";
|
|
}
|
|
int flip;
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
scale.x*=randompick(-1,1)*frandom(0.6,1.3);
|
|
scale.y*=frandom(0.6,1.3);
|
|
bwallsprite=randompick(0,0,0,1); //+wallsprite crashes software
|
|
roll=random(0,3)*90;
|
|
flip=random(1,4);
|
|
if(!random(0,9))A_StartSound("misc/wallchunks");
|
|
frame=random(0,3);
|
|
bouncefactor=frandom(0.1,0.3);
|
|
}
|
|
void A_Dust(){
|
|
A_SetScale(-scale.x,scale.y);
|
|
A_SetTics(flip);
|
|
angle=angle+45*flip;
|
|
}
|
|
states{
|
|
spawn:
|
|
DUST # 1 nodelay A_Dust();
|
|
wait;
|
|
---- BCD 0;
|
|
death:
|
|
---- A 1 A_SetTics(random(10,20)<<3);
|
|
stop;
|
|
}
|
|
}
|
|
class WallChunker:HDActor{
|
|
default{
|
|
height 8;radius 12;
|
|
}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
if(
|
|
ceilingz-pos.z<height
|
|
&&pos.z-floorz<2
|
|
&&checkmove(pos.xy,PCM_NOACTORS)
|
|
){
|
|
destroy();
|
|
return;
|
|
}
|
|
|
|
if(ceilingz-pos.z<12&&pos.z-floorz>12)chunkdir=-2;
|
|
else chunkdir=5;
|
|
}
|
|
double chunkdir;
|
|
states{
|
|
spawn:
|
|
TNT1 AAAAAAAAAAAAAAAAAAAAAA 0 A_SpawnItemEx("HugeWallChunk",0,0,4,frandom(6,12),0,frandom(-3,12)*chunkdir,frandom(0,360),SXF_NOCHECKPOSITION);
|
|
TNT1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 0 A_SpawnItemEx("BigWallChunk",0,0,4,frandom(7,18),0,frandom(-3,14)*chunkdir,frandom(0,360),SXF_NOCHECKPOSITION);
|
|
TNT1 AA 0 A_SpawnItemEx ("HDSmoke",-1,0,1,frandom(-2,2),0,0,frandom(0,360),SXF_NOCHECKPOSITION);
|
|
stop;
|
|
}
|
|
}
|
|
|
|
//the other chunk!
|
|
class HDSmokeChunk:HDDebris{
|
|
default{
|
|
scale 0.2;
|
|
damagetype "hot";
|
|
obituary "%o was smoked and roasted.";
|
|
bouncefactor 0.2;bouncesound "";
|
|
}
|
|
states{
|
|
spawn:
|
|
DUST A 0 nodelay{
|
|
frame=random(0,3);
|
|
scale*=randompick(-1,1);
|
|
if(!random(0,4))brockettrail=true;
|
|
if(!random(0,4))bgrenadetrail=true;
|
|
}
|
|
spawn2:
|
|
---- A random(3,7){
|
|
accuracy++;
|
|
if(accuracy>=18)setstatelabel("death");
|
|
A_StartSound("misc/firecrkl",CHAN_BODY,
|
|
volume:0.2,pitch:frandom(0.8,1.2)
|
|
);
|
|
}
|
|
loop;
|
|
DUST ABCD 0;
|
|
death:
|
|
PUFF C 0{
|
|
bnogravity=true;
|
|
vel.z+=0.3;
|
|
A_SetScale(randompick(-1,1)*frandom(0.4,0.6),frandom(0.4,0.6));
|
|
A_SetRenderStyle(0.6,Style_Add);
|
|
}
|
|
PUFF CCCCCDDDD 1{
|
|
scale*=1.1;
|
|
A_FadeOut(0.05);
|
|
}
|
|
stop;
|
|
}
|
|
}
|
|
|
|
|
|
//puffs for smoke, bulletpuffs, flames, etc.
|
|
class HDPuff:HDActor{
|
|
double decel;double fade;double grow;int fadeafter;double minalpha;double startvelz;double grav;
|
|
property decel:decel;
|
|
property fade:fade;
|
|
property grow:grow;
|
|
property fadeafter:fadeafter;
|
|
property minalpha:minalpha;
|
|
property startvelz:startvelz;
|
|
default{
|
|
+puffgetsowner +hittracer
|
|
+noblockmap -solid +cannotpush
|
|
+nointeraction
|
|
+rollsprite +rollcenter +forcexybillboard
|
|
height 0;radius 0;renderstyle "translucent";gravity 0.1;
|
|
|
|
hdpuff.decel 0.9;
|
|
hdpuff.fade 0.98;
|
|
hdpuff.fadeafter 10;
|
|
hdpuff.grow 0.14;
|
|
hdpuff.minalpha 0.1;
|
|
hdpuff.startvelz 2.;
|
|
}
|
|
override void postbeginplay(){
|
|
HDActor.postbeginplay();
|
|
if(max(abs(pos.x),abs(pos.y),abs(pos.z))>=32768){destroy();return;}
|
|
roll=random(0,3)*90;
|
|
scale.x*=randompick(-1,1);
|
|
grow*=scale.x;
|
|
vel.z+=startvelz;
|
|
grav=getgravity();
|
|
}
|
|
override void Tick(){
|
|
if(isfrozen())return;
|
|
|
|
alpha*=fade;
|
|
if(alpha<minalpha){
|
|
destroy();
|
|
return;
|
|
}
|
|
scale.x+=grow;scale.y=scale.x;
|
|
vel*=decel;
|
|
vel.z-=grav;
|
|
if(
|
|
(vel.x||vel.y)
|
|
&&!trymove(pos.xy+vel.xy,true)
|
|
)vel.xy=(0,0);
|
|
if(vel.z){
|
|
if(
|
|
(vel.z>0 && pos.z+8>ceilingz)||
|
|
(vel.z<0 && pos.z<floorz)
|
|
)vel.z=0;
|
|
addz(vel.z);
|
|
}
|
|
if(pos.z>ceilingz)setz(ceilingz-8);
|
|
else if(pos.z<floorz)setz(floorz);
|
|
|
|
NextTic();
|
|
}
|
|
}
|
|
class HDBulletPuff:HDPuff{
|
|
int scarechance;
|
|
property scarechance:scarechance;
|
|
default{
|
|
stamina 5;missiletype "WallChunk";alpha 0.8;
|
|
|
|
hdpuff.decel 0.7;
|
|
hdpuff.fadeafter 0;
|
|
hdpuff.fade 0.9;
|
|
hdpuff.grow 0.1;
|
|
hdpuff.minalpha 0.1;
|
|
hdpuff.startvelz 4;
|
|
gravity 0.1;
|
|
|
|
hdbulletpuff.scarechance 5;
|
|
}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
if(max(abs(pos.x),abs(pos.y),abs(pos.z))>=32768){destroy();return;}
|
|
|
|
if(target&&(
|
|
!!target.player
|
|
||botbot(target)
|
|
))scarechance>>=2;
|
|
if(!random(0,scarechance))hdmobai.frighten(self,256);
|
|
|
|
int stm=stamina;
|
|
double vol=min(1.,0.1*stm);
|
|
A_StartSound("misc/bullethit",CHAN_BODY,CHANF_OVERLAP,vol);
|
|
A_ChangeVelocity(-0.4*cos(pitch),0,frandom(0.1,0.4)*-sin(pitch),CVF_RELATIVE);
|
|
trymove(pos.xy+vel.xy,false);
|
|
scale*=frandom(0.9,1.1);
|
|
let gdfmt=getdefaultbytype((class<actor>)(missilename));
|
|
for(int i=0;i<stamina;i++){
|
|
A_SpawnParticle("gray",
|
|
SPF_RELATIVE,70,frandom(4,20)*gdfmt.scale.x,0,
|
|
frandom(-3,3),frandom(-3,3),frandom(0,4),
|
|
frandom(-0.1,.8)*stm,frandom(-0.3,0.3)*stm,vel.z+frandom(0.1,0.3)*stm,
|
|
frandom(-0.1,0.1),frandom(-0.1,0.1),-HDCONST_GRAVITY
|
|
);
|
|
}
|
|
}
|
|
void A_CheckSmokeSprite(){
|
|
if(abs(scale.y)>3.){
|
|
setstatelabel("toobig");
|
|
scale*=0.3;
|
|
grow*=0.3;
|
|
}
|
|
}
|
|
states{
|
|
spawn:
|
|
TNT1 A 0 nodelay A_CheckSmokeSprite();
|
|
PUFF CD 8;wait;
|
|
toobig:
|
|
RSMK AB 8;wait;
|
|
}
|
|
}
|
|
class BulletPuffBig:HDBulletPuff{
|
|
default{
|
|
stamina 5;scale 0.6;
|
|
hdbulletpuff.scarechance 5;
|
|
}
|
|
}
|
|
class BulletPuffMedium:HDBulletPuff{
|
|
default{
|
|
stamina 4;scale 0.5;
|
|
hdbulletpuff.scarechance 10;
|
|
}
|
|
}
|
|
class BulletPuffSmall:HDBulletPuff{
|
|
default{
|
|
stamina 3;scale 0.4;missiletype "TinyWallChunk";
|
|
hdbulletpuff.scarechance 20;
|
|
}
|
|
}
|
|
class FragPuff:HDBulletPuff{
|
|
default{
|
|
stamina 1;scale 0.5;
|
|
hdbulletpuff.scarechance 40;
|
|
}
|
|
}
|
|
class PenePuff:HDBulletPuff{
|
|
default{
|
|
stamina 4;scale 0.6;
|
|
hdbulletpuff.scarechance 4;
|
|
}
|
|
states{
|
|
spawn:
|
|
TNT1 A 0 nodelay A_CheckSmokeSprite();
|
|
PUFF ABCD 2;wait;
|
|
toobig:
|
|
RSMK ABCD 2; wait;
|
|
}
|
|
}
|
|
class HDSmoke:HDPuff{
|
|
default{
|
|
scale 1;gravity 0.05;alpha 0.7;
|
|
hdpuff.fadeafter 3;
|
|
hdpuff.decel 0.96;
|
|
hdpuff.fade 0.96;
|
|
hdpuff.grow 0.02;
|
|
hdpuff.minalpha 0.005;
|
|
}
|
|
override void postbeginplay(){
|
|
actor smm;
|
|
int bcc=0;
|
|
thinkeriterator bexpm=ThinkerIterator.create(getclassname());
|
|
actor osm;
|
|
while(smm=actor(bexpm.next())){
|
|
if(
|
|
abs(smm.pos.x-pos.x)<128
|
|
&&abs(smm.pos.y-pos.y)<128
|
|
){
|
|
if(!bcc)osm=smm;
|
|
bcc++;
|
|
}
|
|
if(
|
|
bcc>20
|
|
&&!!osm
|
|
){
|
|
osm.destroy();
|
|
break;
|
|
}
|
|
}
|
|
super.postbeginplay();
|
|
}
|
|
states{
|
|
spawn:
|
|
RSMK A random(3,5);RSMK A 0 A_SetScale(scale.y*2);
|
|
---- BCD -1{frame=random(1,3);}wait;
|
|
}
|
|
}
|
|
class HDGunSmoke:HDSmoke{
|
|
default{
|
|
scale 0.3;renderstyle "add";alpha 0.4;
|
|
hdpuff.decel 0.97;
|
|
hdpuff.fade 0.8;
|
|
hdpuff.grow 0.06;
|
|
hdpuff.minalpha 0.01;
|
|
hdpuff.startvelz 0;
|
|
}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
a_changevelocity(cos(pitch)*4,0,-sin(pitch)*4,CVF_RELATIVE);
|
|
vel+=(frandom(-0.1,0.1),frandom(-0.1,0.1),frandom(0.4,0.9));
|
|
}
|
|
}
|
|
class HDGunSmokeStill:HDGunSmoke{
|
|
override void postbeginplay(){
|
|
HDSmoke.postbeginplay();
|
|
}
|
|
}
|
|
class HDFlameRed:HDPuff{
|
|
default{
|
|
renderstyle "add";
|
|
alpha 0.6;scale 0.3;gravity 0.05;
|
|
|
|
hdpuff.fadeafter 3;
|
|
hdpuff.grow -0.01;
|
|
hdpuff.fade 0.8;
|
|
hdpuff.decel 0.8;
|
|
hdpuff.startvelz 4;
|
|
}
|
|
states{
|
|
spawn:
|
|
BAL1 A 0 nodelay A_SpawnItemEx("HDRedFireLight",flags:SXF_SETTARGET);
|
|
BAL1 ABCDE 1;
|
|
TNT1 A 0 A_CheckProximity("death","HDFlameRed",64,4,CPXF_LESSOREQUAL);
|
|
stop;
|
|
death:
|
|
TNT1 A 0{
|
|
grow=0.01;
|
|
fade=0.9;
|
|
decel=0.9;
|
|
vel.z+=2;
|
|
minalpha=0.1;
|
|
addz(-vel.z);
|
|
A_SetTranslucent(0.6,0);
|
|
scale=(1.2,1.2);gravity=0.1;
|
|
}
|
|
RSMK CD -1{frame=random(0,3);}
|
|
wait;
|
|
}
|
|
}
|
|
class HDRedFireLight:PointLight{
|
|
default{+dynamiclight.additive}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
args[0]=60;
|
|
args[1]=40;
|
|
args[2]=10;
|
|
args[3]=64;
|
|
args[4]=0;
|
|
}
|
|
override void tick(){
|
|
if(!target||args[3]<1){destroy();return;}
|
|
args[3]=int(frandom(0.8,1.09)*args[3]);
|
|
setorigin(target.pos,true);
|
|
}
|
|
}
|
|
class HDFlameRedBig:HDActor{
|
|
default{
|
|
+nointeraction
|
|
+rollsprite +rollcenter +spriteangle +bright
|
|
translation 1;
|
|
spriteangle 90;
|
|
renderstyle "add";
|
|
}
|
|
void A_FlameFade(){
|
|
A_FadeOut(frandom(-0.002,0.02));
|
|
addz(frandom(-0.1,0.1));
|
|
scale*=frandom(0.98,1.01);
|
|
scale.x*=randompick(1,1,-1);
|
|
if(target)setorigin(target.pos+(
|
|
(frandom(-target.radius,target.radius),frandom(-target.radius,target.radius))*0.6,
|
|
frandom(0,target.height*0.6))
|
|
,true);
|
|
}
|
|
void A_SmokeFade(){
|
|
actor sss=spawn("HDSmoke",(pos.x,pos.y,pos.z+36*scale.y),ALLOW_REPLACE);
|
|
sss.scale=scale;
|
|
}
|
|
states{
|
|
spawn:
|
|
FIR7 ABABABABABAB 1 A_FlameFade();
|
|
FIR7 A 0 A_SmokeFade();
|
|
stop;
|
|
}
|
|
}
|
|
class HDSmokeSmall:HDFlameRed{
|
|
override void postbeginplay(){
|
|
hdactor.postbeginplay();
|
|
setstatelabel("death");
|
|
}
|
|
}
|
|
|
|
|
|
class HDExplosion:IdleDummy{
|
|
default{
|
|
+forcexybillboard +bright
|
|
alpha 0.9;renderstyle "add";
|
|
deathsound "world/explode";
|
|
}
|
|
states{
|
|
spawn:
|
|
death:
|
|
MISL B 0 nodelay{
|
|
if(max(abs(pos.x),abs(pos.y),abs(pos.z))>=32768){destroy();return;}
|
|
vel.z+=2;
|
|
A_StartSound(deathsound,CHAN_BODY);
|
|
let xxx=spawn("HDExplosionLight",pos);
|
|
xxx.target=self;
|
|
}
|
|
MISL BB 0 A_SpawnItemEx("ParticleWhiteSmall", 0,0,0, vel.x+random(-2,2),vel.y+random(-2,2),vel.z,0,SXF_ABSOLUTEMOMENTUM|SXF_NOCHECKPOSITION|SXF_TRANSFERPOINTERS);
|
|
MISL BBBB 0 A_SpawnItemEx("HDSmoke", 0,0,0, vel.x+frandom(-2,2),vel.y+frandom(-2,2),vel.z,0,SXF_ABSOLUTEMOMENTUM|SXF_NOCHECKPOSITION|SXF_TRANSFERPOINTERS);
|
|
MISL B 0 A_Jump(256,"fade");
|
|
fade:
|
|
MISL B 1 A_FadeOut(0.1);
|
|
MISL C 1 A_FadeOut(0.2);
|
|
MISL DD 1 A_FadeOut(0.2);
|
|
TNT1 A 20;
|
|
stop;
|
|
}
|
|
}
|
|
|
|
class HDExplosionLight:PointLight{
|
|
default{
|
|
stamina 128;
|
|
}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
args[0]=240;
|
|
args[1]=200;
|
|
args[2]=60;
|
|
args[3]=stamina;
|
|
args[4]=0;
|
|
}
|
|
override void tick(){
|
|
args[3]=int(frandom(0.3,0.4)*args[3]);
|
|
if(args[3]<1)destroy();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//transfer sprite frame fader
|
|
//deathheight = amount to fade every 4 tics
|
|
class HDCopyTrail:IdleDummy{
|
|
default{
|
|
+noclip +rollsprite +rollcenter +nointeraction
|
|
deathheight 0.6;
|
|
renderstyle "add";
|
|
}
|
|
states{spawn:#### A -1;stop;}
|
|
override void Tick(){
|
|
clearinterpolation();
|
|
if(isfrozen())return;
|
|
scale.x+=frandom(-0.01,0.01);scale.y=scale.x;
|
|
accuracy++;
|
|
if(accuracy>=4){
|
|
accuracy=0;
|
|
alpha*=deathheight;
|
|
vel*=deathheight;
|
|
if(alpha<0.04){destroy();return;}
|
|
}
|
|
setorigin(pos+vel,true);
|
|
//don't even bother with nexttic, it's just one frame!
|
|
}
|
|
}
|
|
extend class HDActor{
|
|
actor A_Trail(double spread=0.6){
|
|
vector3 v=(frandom(-1,1),frandom(-1,1),frandom(-1,1));
|
|
bool gbg;actor aaa;
|
|
[gbg,aaa]=A_SpawnItemEx("HDCopyTrail",
|
|
0,0,0,vel.x+v.x,vel.y+v.y,vel.z+v.z,0,
|
|
SXF_TRANSFERALPHA|SXF_TRANSFERRENDERSTYLE|SXF_TRANSFERSCALE|
|
|
SXF_TRANSFERPITCH|SXF_TRANSFERSPRITEFRAME|SXF_TRANSFERROLL|
|
|
SXF_ABSOLUTEVELOCITY|SXF_TRANSFERTRANSLATION|SXF_NOCHECKPOSITION|
|
|
SXF_TRANSFERSTENCILCOL|SXF_TRANSFERPOINTERS
|
|
);
|
|
return aaa;
|
|
}
|
|
}
|
|
class HDFader:HDCopyTrail{
|
|
default{+rollsprite +rollcenter +noblockmap +nointeraction deathheight 0.1; renderstyle "translucent";}
|
|
override void Tick(){
|
|
clearinterpolation();
|
|
if(isfrozen()||level.time&(1|2))return;
|
|
setorigin(pos+vel,true);
|
|
setz(clamp(pos.z,floorz,ceilingz));
|
|
a_fadeout(deathheight);
|
|
}
|
|
}
|
|
|
|
|
|
//thinker used to generate distant sound
|
|
//DistantNoise.Make(self,"world/rocketfar");
|
|
class DistantNoise:Thinker{
|
|
sound distantsound;
|
|
int distances[MAXPLAYERS];
|
|
int ticker;
|
|
double volume,pitch;
|
|
static void Make(
|
|
actor source,
|
|
sound distantsound,
|
|
double volume=1.,
|
|
double pitch=0
|
|
){
|
|
DistantNoise dnt=new("DistantNoise");
|
|
dnt.ticker=0;
|
|
dnt.distantsound=distantsound;
|
|
dnt.volume=clamp(0.,volume,5.);
|
|
dnt.pitch=pitch;
|
|
for(int i=0;i<MAXPLAYERS;i++){
|
|
if(
|
|
playeringame[i]
|
|
&&!!players[i].mo
|
|
){
|
|
dnt.distances[i]=int(players[i].mo.distance3d(source)/HDCONST_SPEEDOFSOUND);
|
|
}else dnt.distances[i]=-1;
|
|
}
|
|
}
|
|
override void Tick(){
|
|
if(level.isfrozen())return;
|
|
int playersleft=0;
|
|
for(int i=0;i<MAXPLAYERS;i++){
|
|
if(distances[i]<0)continue;
|
|
if(
|
|
!!players[i].mo
|
|
){
|
|
playersleft++;
|
|
if(distances[i]==ticker){
|
|
distances[i]=-1;
|
|
//for volumes greater than 1, play the sound on top of itself until spent
|
|
double thisvol=volume;
|
|
while(thisvol>0){
|
|
players[i].mo.A_StartSound(
|
|
distantsound,CHAN_DISTANT,
|
|
CHANF_OVERLAP|CHANF_LOCAL,
|
|
min(1.,thisvol), //if we ever stop needing this clamp, delete the loop
|
|
pitch:pitch
|
|
);
|
|
thisvol-=1.;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(playersleft)ticker++;
|
|
else destroy();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//Quake effect affecting each player differently depending on distance
|
|
//DistantQuaker.Quake(self,8,40,4096,10,256,512,256);
|
|
class DistantQuaker:IdleDummy{
|
|
int intensity;
|
|
double frequency;
|
|
bool wave;
|
|
//Quake effect affecting each player differently depending on distance
|
|
//DistantQuaker.Quake(self,8,40,4096,10,256,512,256);
|
|
static void Quake(
|
|
actor caller,
|
|
int intensity=3,
|
|
int duration=35,
|
|
double quakeradius=1024,
|
|
int frequency=10,
|
|
double speed=HDCONST_SPEEDOFSOUND,
|
|
double minwaveradius=HDCONST_MINDISTANTSOUND,
|
|
double dropoffrate=HDCONST_MINDISTANTSOUND
|
|
){
|
|
if(
|
|
caller.ceilingpic==skyflatnum
|
|
||caller.ceilingz-caller.floorz>HDCONST_MINDISTANTSOUND
|
|
){
|
|
intensity=clamp(intensity-1,1,9);
|
|
duration=int(0.9*duration);
|
|
}
|
|
double dist;
|
|
for(int i=0;i<MAXPLAYERS;i++){
|
|
if(playeringame[i] && players[i].mo){
|
|
dist=players[i].mo.distance3d(caller);
|
|
if(dist<=quakeradius){
|
|
let it=DistantQuaker(caller.spawn("DistantQuaker",players[i].mo.pos,ALLOW_REPLACE));
|
|
if(it){
|
|
if(dist<=dropoffrate)it.intensity=intensity;
|
|
else it.intensity=int(clamp(intensity-floor(dist/dropoffrate),1,9));
|
|
if(dist>minwaveradius)it.wave=true;else it.wave=false;
|
|
if(it.intensity<3)it.deathsound="null";
|
|
else it.deathsound="world/quake";
|
|
it.stamina=int(dist/speed);
|
|
it.mass=duration;
|
|
it.frequency=frequency;
|
|
it.target=players[i].mo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
states{
|
|
spawn:
|
|
TNT1 A 1 nodelay A_SetTics(stamina);
|
|
TNT1 A 0{
|
|
if(max(abs(pos.x),abs(pos.y),abs(pos.z))>32000)return;
|
|
if(wave){
|
|
A_StartSound("weapons/subfwoosh",CHAN_AUTO,volume:0.1*intensity);
|
|
if(
|
|
target
|
|
&&target.pos.z-target.floorz<256
|
|
)A_QuakeEx(
|
|
0,0,intensity,mass,0,16,deathsound,
|
|
QF_SCALEDOWN|QF_WAVE,0,0,frequency,0,int(mass*0.62)
|
|
);
|
|
}else{
|
|
A_QuakeEx(
|
|
intensity*2,intensity*2,intensity*2,mass,0,16,deathsound,
|
|
QF_SCALEDOWN,highpoint:int(mass*0.62)
|
|
);
|
|
}
|
|
}
|
|
TNT1 A 1{
|
|
if(target && mass>0){
|
|
mass--;
|
|
setxyz(target.pos);
|
|
}else{
|
|
destroy();
|
|
return;
|
|
}
|
|
}wait;
|
|
}
|
|
}
|
|
|
|
|
|
//SO MUCH BLOOD
|
|
class BloodSplatSilent:HDPuff{
|
|
default{
|
|
scale 0.4;
|
|
alpha 0.8;gravity 0.3;
|
|
|
|
hdpuff.startvelz 1.6;
|
|
hdpuff.fadeafter 0;
|
|
hdpuff.decel 0.86;
|
|
hdpuff.fade 0.9;
|
|
hdpuff.grow 0.01;
|
|
hdpuff.minalpha 0.01;
|
|
}
|
|
states{
|
|
spawn:
|
|
BLUD ABC 4{
|
|
if(floorz>=pos.z){
|
|
bflatsprite=true;bmovewithsector=true;bnointeraction=true;
|
|
setz(floorz);vel=(0,0,0);
|
|
fade=0.98;
|
|
}
|
|
}wait;
|
|
}
|
|
}
|
|
class BloodSplat:BloodSplatSilent replaces Blood{
|
|
default{
|
|
seesound "misc/bulletflesh";
|
|
}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
if(!bambush)A_StartSound(seesound,CHAN_BODY,CHANF_OVERLAP,0.2);
|
|
}
|
|
}
|
|
class BloodSplattest:BloodSplat replaces BloodSplatter{}
|
|
class NotQuiteBloodSplat:BloodSplat{
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
A_StartSound("misc/bulletflesh",CHAN_BODY,CHANF_OVERLAP,0.02);
|
|
actor p=spawn("PenePuff",pos,ALLOW_REPLACE);
|
|
p.target=target;p.master=master;p.vel=vel*0.3;
|
|
scale*=frandom(0.2,0.5);
|
|
}
|
|
}
|
|
class ShieldNotBlood:NotQuiteBloodSplat{
|
|
override void postbeginplay(){
|
|
bloodsplat.postbeginplay();
|
|
if(
|
|
hdmobbase(target)
|
|
&&hdmobbase(target).bbloodlesswhileshielded
|
|
&&target.countinv("HDMagicShield")>50
|
|
){
|
|
A_SetTranslucent(1,1);
|
|
grav=-0.6;
|
|
scale*=0.4;
|
|
setstatelabel("spawnshield");
|
|
bnointeraction=true;
|
|
return;
|
|
}
|
|
A_StartSound("misc/bulletflesh",CHAN_AUTO,volume:0.02);
|
|
actor p=spawn("PenePuff",pos,ALLOW_REPLACE);
|
|
p.target=target;p.master=master;p.vel=vel*0.3;
|
|
scale*=frandom(0.2,0.5);
|
|
}
|
|
states{
|
|
spawnshield:
|
|
TFOG ABCDEFGHIJ 3 bright A_FadeOut(0.05);
|
|
stop;
|
|
}
|
|
}
|
|
class ShieldNeverBlood:IdleDummy{
|
|
default{
|
|
+forcexybillboard +rollsprite +rollcenter
|
|
renderstyle "add";
|
|
}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
scale*=frandom(0.2,0.5);
|
|
roll=frandom(0,360);
|
|
}
|
|
states{
|
|
spawn:
|
|
TFOG ABCDEFGHIJ 3 bright A_FadeOut(0.08);
|
|
stop;
|
|
}
|
|
}
|
|
class BloodTrail:HDPuff{
|
|
default{
|
|
alpha 0.7;scale 0.2;gravity 0.02;
|
|
}
|
|
states{
|
|
spawn:
|
|
BLOD A -1;
|
|
}
|
|
}
|
|
class MegaBloodSplatter:IdleDummy{
|
|
override void postbeginplay(){
|
|
actor.postbeginplay();
|
|
if(!A_CheckSight("null")){
|
|
for(int i=0;i<20;i++){
|
|
actor b=spawn("BloodSplatSilent",self.pos,ALLOW_REPLACE);
|
|
b.vel=self.vel+(random(-4,4),random(-4,4),random(-1,7));
|
|
b.translation=self.translation;
|
|
}
|
|
}
|
|
destroy();
|
|
}
|
|
}
|
|
class HDBloodTrailFloor:IdleDummy{
|
|
default{
|
|
+flatsprite +movewithsector
|
|
height 1;radius 1;alpha 0.6;
|
|
}
|
|
override void postbeginplay(){
|
|
super.postbeginplay();
|
|
frame=random(0,3);
|
|
scale*=frandom(0.6,1.2);
|
|
setz(floorz);
|
|
}
|
|
states{
|
|
spawn:
|
|
BLUD # 100 nodelay A_FadeOut(0.05);
|
|
wait;
|
|
BLUD ABCD 0;
|
|
stop;
|
|
}
|
|
}
|
|
|
|
|
|
//Ominous shards of green or blue energy
|
|
class FragShard:IdleDummy{
|
|
default{
|
|
renderstyle "add";+forcexybillboard;scale 0.3;alpha 0;
|
|
}
|
|
override void tick(){
|
|
if(isfrozen())return;
|
|
trymove(self.pos.xy+vel.xy,true);
|
|
if(alpha<1)alpha+=0.05;
|
|
addz(vel.z,true);
|
|
|
|
NextTic();
|
|
}
|
|
states{
|
|
spawn:
|
|
BFE2 D 20 bright nodelay{
|
|
if(stamina>0) A_SetTics(stamina);
|
|
}stop;
|
|
}
|
|
}
|
|
extend class HDActor{
|
|
//A_ShardSuck(self.pos+(0,0,32),20);
|
|
virtual void A_ShardSuck(vector3 aop,int range=4,bool forcegreen=false){
|
|
actor a=spawn("FragShard",aop,ALLOW_REPLACE);
|
|
a.setorigin(aop+(random(-range,range)*6,random(-range,range)*6,random(-range,range)*6),false);
|
|
a.vel=(aop-a.pos)*0.05;
|
|
a.stamina=20;
|
|
if(forcegreen)a.A_SetTranslation("AllGreen");
|
|
}
|
|
}
|
|
|
|
//Teleport fog
|
|
class TeleFog:IdleDummy replaces TeleportFog{
|
|
default{
|
|
renderstyle "add";alpha 0.6;
|
|
}
|
|
override void postbeginplay(){
|
|
actor.postbeginplay();
|
|
scale.x*=randompick(-1,1);
|
|
A_StartSound("misc/teleport");
|
|
}
|
|
states{
|
|
spawn:
|
|
TFOG AA 2 nodelay bright light("TLS1") A_FadeIn(0.2);
|
|
TFOG BBCCCDDEEFGHII 2 bright light("TLS1"){
|
|
A_ShardSuck(pos+(0,0,frandom(24,48)),forcegreen:true);
|
|
}
|
|
TFOG JJJJ random(2,3) bright light("TLS1"){
|
|
alpha-=0.2;
|
|
A_ShardSuck(pos+(0,0,frandom(24,48)),forcegreen:true);
|
|
}stop;
|
|
nope:
|
|
TNT1 A 20 light("TLS1");
|
|
stop;
|
|
}
|
|
}
|
|
|
|
//Electro shit
|
|
const HDCONST_ZAPARCDEFAULTDEV=1.4;
|
|
extend class HDActor{
|
|
static void ParticleZigZag(
|
|
actor caller,
|
|
vector3 orig,
|
|
vector3 dest,
|
|
int segments=12,
|
|
bool relpos=false, //if true, treats o/d inputs as relative to caller
|
|
vector3 pvel=(0,0,0),
|
|
double dev=HDCONST_ZAPARCDEFAULTDEV //amount of deviation in each node
|
|
){
|
|
if(orig==dest)return;
|
|
|
|
if(!relpos){
|
|
dest-=caller.pos;
|
|
orig-=caller.pos;
|
|
}
|
|
|
|
array<double> arcx;
|
|
array<double> arcy;
|
|
array<double> arcz;
|
|
arcx.clear();
|
|
arcy.clear();
|
|
arcz.clear();
|
|
|
|
vector3 frac=(dest-orig)/segments;
|
|
dev*=max(abs(frac.x),abs(frac.y),abs(frac.z));
|
|
int lastpoint=segments-1;
|
|
for(int i=0;i<segments;i++){
|
|
if(i==lastpoint){
|
|
arcx.push(dest.x);
|
|
arcy.push(dest.y);
|
|
arcz.push(dest.z);
|
|
}else{
|
|
arcx.push(frandom(-dev,dev)+orig.x+frac.x*i);
|
|
arcy.push(frandom(-dev,dev)+orig.y+frac.y*i);
|
|
arcz.push(frandom(-dev,dev)+orig.z+frac.z*i);
|
|
}
|
|
}
|
|
|
|
int arx=arcx.size()-1;
|
|
for(int i=0;i<arx;i++){
|
|
int ii=i+1;
|
|
vector3 firstpoint=(arcx[i],arcy[i],arcz[i]);
|
|
vector3 lastpoint=(arcx[ii],arcy[ii],arcz[ii]);
|
|
vector3 pointfrac=lastpoint-firstpoint;
|
|
int pointdist=int(pointfrac.length())>>1;
|
|
pointfrac=pointfrac.unit()*2.;
|
|
for(int j=0;j<pointdist;j++){
|
|
caller.A_SpawnParticle(
|
|
"azure",SPF_FULLBRIGHT,20,frandom(2,4),0,
|
|
firstpoint.x,
|
|
firstpoint.y,
|
|
firstpoint.z,
|
|
pvel.x+frandom(-0.1,0.1),pvel.y+frandom(-0.1,0.1),pvel.z+frandom(-0.1,0.1)
|
|
);
|
|
firstpoint+=pointfrac;
|
|
}
|
|
}
|
|
}
|
|
static void ZapArc(
|
|
actor a1,
|
|
actor a2=null,
|
|
int flags=0,
|
|
double radius=0,
|
|
double height=0,
|
|
vector3 pvel=(0,0,0),
|
|
double dev=HDCONST_ZAPARCDEFAULTDEV
|
|
){
|
|
if(!a1)a1=a2;
|
|
vector3 a1pos,a2pos;
|
|
if(
|
|
!a2
|
|
||a1==a2
|
|
){
|
|
if(!a2)a2=a1;
|
|
if(!a1)return;
|
|
if(radius<=0)radius=a1.radius*1.2;
|
|
if(height<=0)height=a1.height*1.1;
|
|
double flr=a1.pos.z>a1.floorz?a1.height*-0.1:0;
|
|
a1pos=(radius*(frandom(-1,1),frandom(-1,1)),frandom(flr,height));
|
|
a2pos=(radius*(frandom(-1,1),frandom(-1,1)),frandom(flr,height));
|
|
}else{
|
|
a1pos=(a1.pos.xy,a1.pos.z+a1.height*0.6);
|
|
a2pos=(a2.pos.xy,a2.pos.z+a2.height*0.6);
|
|
if(flags&ARC2_RANDOMSOURCE){
|
|
double radius=a1.radius*0.6;
|
|
a1pos.xy+=(frandom(-radius,radius),frandom(-radius,radius));
|
|
a1pos.z+=a1.height*frandom(-0.3,0.2);
|
|
}
|
|
if(flags&ARC2_RANDOMDEST){
|
|
double radius=a2.radius*0.6;
|
|
a2pos.xy+=(frandom(-radius,radius),frandom(-radius,radius));
|
|
a2pos.z+=a2.height*frandom(-0.3,0.2);
|
|
}
|
|
}
|
|
ParticleZigZag(a1,a1pos,a2pos,relpos:(a1==a2),pvel:pvel,dev:dev);
|
|
if(!(flags&ARC2_SILENT)){
|
|
a1.A_StartSound("misc/zap",CHAN_ARCZAP,CHANF_OVERLAP);
|
|
a2.A_StartSound("misc/zap2",CHAN_ARCZAP,CHANF_OVERLAP);
|
|
a2.A_StartSound("misc/zap3",CHAN_ARCZAP,CHANF_OVERLAP);
|
|
}
|
|
}
|
|
enum ArcActorsFlags{
|
|
ARC2_RANDOMSOURCE=1,
|
|
ARC2_RANDOMDEST=2,
|
|
ARC2_SILENT=4,
|
|
}
|
|
|
|
//sit around and zap nearby shit at random
|
|
static void ArcZap(
|
|
actor caller,
|
|
double rad=0,
|
|
int maxdamage=8,
|
|
bool indiscriminate=false
|
|
){
|
|
array<actor> zappables;zappables.clear();
|
|
if(!rad)rad=frandom(32,128);
|
|
blockthingsiterator it=blockthingsiterator.create(caller,rad);
|
|
while(it.next()){
|
|
actor itt=it.thing;
|
|
if(
|
|
itt.bshootable
|
|
&&(
|
|
indiscriminate
|
|
||caller.ishostile(itt)
|
|
)
|
|
&&caller.distance3dsquared(itt)<=(rad*rad)
|
|
&&caller.checksight(itt)
|
|
)zappables.push(itt);
|
|
}
|
|
actor itt=caller;
|
|
if(zappables.size())itt=zappables[random(0,zappables.size()-1)];
|
|
ZapArc(caller,itt,ARC2_RANDOMSOURCE,rad,rad*0.3,dev:0.8);
|
|
if(itt)itt.damagemobj(caller,caller,random(1,maxdamage),"electrical");
|
|
}
|
|
}
|
|
|