game-source/paroxysm/quake/specfx.qc
Bill Currie 7f3ef60d60 compiles with qfcc (this was probably a pointless exercise due to Deek's
work, but it's nice having a compilable base paroxysm).
2002-02-21 20:36:23 +00:00

590 lines
15 KiB
C++

/* Frank Condello 09.28.98 - made for the Paroxysm MacQuake Mod.
EMAIL: pox@planetquake.com
WEB: http://www.planetquake.com/paroxysm
=========================================================================
Custom Paroxysm visual and audio effects
These are simple little ambient effects that are customizable within Quiver.
Feel free to use this code in anyway.
=========================================================================
Start
=========================================================================
Spark code (called 'e_spark' to seperate from plasma-gun's spark)
The .float spark_freq controls the delay between sparks in seconds
note: spark_freq is randomized slightly to give a better effect.
=========================================================================
*/
.float spark_freq;
void() spawn_espark;
void() e_spark =
{
precache_sound ("misc/spark1.wav");
precache_sound ("misc/spark2.wav");
precache_sound ("misc/spark3.wav");
precache_sound ("misc/spark4.wav");
if (self.spark_freq < 0)
self.spark_freq = 0; // default frequency to 0
// not triggered so spark it up
if (!self.targetname)
{
self.nextthink = time + random();
self.think = spawn_espark;
}
// wait for trigger
self.use = spawn_espark;
};
void() espark_touch =
{
self.solid = SOLID_NOT;
if (random() < 0.1) //remove some sparks on touch
remove(self);
};
void() spawn_espark =
{
local entity espark;
local float r;
r = rint(random() * 3); //play a random spark sound
if (r == 1)
sound (self, CHAN_VOICE, "misc/spark1.wav", 1, ATTN_STATIC);
else if (r == 2)
sound (self, CHAN_VOICE, "misc/spark2.wav", 1, ATTN_STATIC);
else if (r == 0)
sound (self, CHAN_VOICE, "misc/spark3.wav", 1, ATTN_STATIC);
else
sound (self, CHAN_VOICE, "misc/spark4.wav", 1, ATTN_STATIC);
espark = spawn ();
setorigin (espark, self.origin);
espark.movetype = MOVETYPE_BOUNCE;
espark.solid = SOLID_TRIGGER;
espark.classname = "spark";
//set the spark velocity - currently sparks emit 360ΒΌ around the origin in the horizontal axis
espark.velocity_x = (random() * 200) - 25;
espark.velocity_y = (random() * 200) - 25;
espark.velocity_z = 0;
espark.touch = espark_touch;
espark.nextthink = time + random()*3; //random lifespan for sparks that aren't removed on touch
espark.think = SUB_Remove;
setmodel (espark, "progs/spark.spr");
setsize (espark, '0 0 0', '0 0 0');
//user defined frequency is randomized give a more organic feel
self.nextthink = time + (random() + self.spark_freq);
self.think = spawn_espark;// spawn next spark
};
/*
=========================
end Spark code
=========================
=============================================
Start Drip code; a_drip is the spawn function.
drip_freq controls the delay between drips in
seconds.
drip_snd controls the type of sound produced.
1 = no sound - use this to silence drips in a rain group
2 = random drip sound on touch
=============================================
*/
.float drip_snd;
.float drip_freq;
void() go_drip;
// define the splash animation
void() s_splash1 = [0, s_splash2] {};
void() s_splash2 = [1, s_splash3] {};
void() s_splash3 = [2, s_splash4] {};
void() s_splash4 = [3, s_splash5] {};
void() s_splash5 = [4, s_splash6] {};
void() s_splash6 = [5, SUB_Remove] {};
void() a_drip =
{
precache_model ("progs/drip.spr");
precache_model ("progs/splash.spr");
precache_sound ("misc/drip1.wav");
precache_sound ("misc/drip2.wav");
precache_sound ("misc/drip3.wav");
if (self.drip_freq <= 0) //default frequency to 3 seconds
self.drip_freq = 3;
if (!self.drip_snd)
self.drip_snd = 1; //default to no sound
// not triggered so go drip
if (!self.targetname)
{
self.nextthink = time + random();
self.think = go_drip;
}
// wait for trigger
self.use = go_drip;
};
void() drip_touch =
{
local float r;
r = rint(random() * 2);
if (self.drip_snd == 2) // play a random drip sound
{
if (r == 1)
sound (self, CHAN_VOICE, "misc/drip1.wav", 1, ATTN_IDLE);
else if (r == 2)
sound (self, CHAN_VOICE, "misc/drip2.wav", 1, ATTN_IDLE);
else if (r == 0)
sound (self, CHAN_VOICE, "misc/drip3.wav", 1, ATTN_IDLE);
}
self.solid = SOLID_NOT;
setmodel (self, "progs/splash.spr");
//added this particle effect cause GL quake tends to blur the slpash.spr out of existance.
particle (self.origin, '0 0 0.25', 0, 5);
s_splash1();
};
void() go_drip =
{
local entity drip;
drip = spawn ();
drip.solid = SOLID_TRIGGER;
drip.movetype = MOVETYPE_TOSS;
drip.drip_snd = self.drip_snd;
//let gravity control the drip
drip.velocity_x = 0;
drip.velocity_y = 0;
drip.velocity_z = 0;
drip.classname = "drip";
setmodel (drip, "progs/drip.spr");
setsize (drip, '-1 -1 -1', '1 1 1');
setorigin (drip, self.origin);
drip.nextthink = time + 5;
drip.think = SUB_Remove;
drip.touch = drip_touch;
drip.classname = "drip"; //added for completeness and possible future use of 'acid' drips
self.nextthink = time + (random() + self.drip_freq);
self.think = go_drip;
};
/*
=========================
end new Drip code
=========================
*/
/*
=================================================================================================
Custom Sound Entity (cust_sound)
The lack of a Mac sound editor that produces looping .wav files in a format that Quake understands
prompted the creation of this function. Then I added some options to make it worth while.
snd_attn - sets the sound's attenuation (defaults to ATTN_NORM (1))
snd_volume - sets the sounds overall volume (typically values of 0.5 to 1) default is 1
strt_onoff - 0 = initially on, 1 = initially off
snd_rep - number of times to play the sound (-2 is infinite loop)
snd_loop - delay in seconds before playing sound again (use sound length for looping sounds)
snd_rand - random seed, this number is multiplied by random() and added to loop (default 0 is no random)
the_snd - directory path to the sound - example: "misc/mysound.wav" NOTE: "sound/" is NOT needed
targetname - sounds can be triggered by using a targetname.
TOGGLE_SND - spawn flag, determines if sounds can be toggled on and off.
BUGS
1. Quake won't start playing these sounds on the first frame - (Unless you precache every possible sound
at world_spawn)
HACKY FIX - If you need a sound to play right away;
Position a trigger in such a way that it is touched when the player enters the map - NOT touching the
player initially (doesn't work on frame 1 remember?). Try making a trigger 4 units tall and place
info_player_start just above it. Not pretty, but it does the trick - Don't forget to set
strt_onoff to 1 (initially off) for this to work
2. When Quake no longer 'hears' or more arcurately - 'sees' the sound, it kills it until it comes into view
again. This causes the sound to stop playing and remsume at the NEXT loop point when it is in view, so for
sounds that must be looped constantly, I would reccomend using the ambient_sound function (see below)
=================================================================================================
*/
//NOTE: some of these .floats are used in ambient_sound as well (see below)
float TOGGLE_SND = 1;
.float snd_attn;
.float snd_volume;
.float strt_onoff;
.float snd_rep;
.float snd_rand;
.float snd_loop;
.float snd_strt;
.string the_snd;
void() sound_think;
void() play_sound;
void() sound_wait =
{
// hang around until next use
self.nextthink = time + 60*10;
self.think = sound_wait;
};
void() stop_sound =
{
// if sound is set to toggle, silence it and set use to play
if (self.spawnflags & TOGGLE_SND)
{
sound (self, CHAN_VOICE, "misc/null.wav", 0, self.snd_attn);
self.use = play_sound;
sound_wait();
}
// sound doesn't toggle so kill it
else
{
sound (self, CHAN_VOICE, "misc/null.wav", 0, self.snd_attn);
remove (self);
}
};
void() play_sound =
{
//infinite repeat
if (self.snd_rep == -2)
{
sound (self, CHAN_VOICE, self.the_snd, self.snd_volume, self.snd_attn);
sound_think();
}
// sound is done
else if (self.snd_rep == 0)
remove (self);
// play the sound and reduce repititions by 1
if (self.snd_rep >= 1)
{
sound (self, CHAN_VOICE, self.the_snd, self.snd_volume, self.snd_attn);
self.snd_rep = self.snd_rep - 1;
sound_think();
}
};
void() sound_think =
{
// if sound is toggled, set next use to stop
if (self.spawnflags & TOGGLE_SND)
self.use = stop_sound;
// determine user-defined loop time then play the sound
self.nextthink = time + (random()*self.snd_rand) + self.snd_loop;
self.think = play_sound;
};
void() cust_sound =
{
precache_sound (self.the_snd);
precache_sound ("misc/null.wav");
//default attenuation to NORM if not specified
if(!self.snd_attn)
self.snd_attn = 2;
self.snd_attn = self.snd_attn - 1;// needed so the default to NORM works (since 0 = NONE)
//default volume to one if not specified
if(self.snd_volume <= 0)
self.snd_volume = 1;
// start sound if not triggered
if (!self.targetname)
play_sound();
// start sound if initially on, set use to stop_sound
if (self.strt_onoff == 0)
{
self.use = stop_sound;
play_sound();
}
// start sound when triggered
if (self.strt_onoff == 1)
{
self.use = play_sound;
}
};
//Made this so custom ambient sounds could be called with a single function
//ONLY WORKS WITH LOOPED .WAV FILES
//Added a targetname attribute so ambient sounds can be triggered.
//NOTE: These sounds can not be stopped once started
//To my knowledge you can't make these with existing Mac sound editors
//(you can make looping wavs, just not ones Quake will loop)
//CoolEdit 1.5 (for Windows) is the only editor that can create these looping wavs (to my knowledge)
void() ambientsound_go =
{
ambientsound (self.origin, self.the_snd, self.snd_volume, self.snd_attn);
};
void() ambient_sound =
{
precache_sound (self.the_snd);
//default volume to one if not specified
if(self.snd_volume <= 0)
self.snd_volume = 1;
/*
POX v1.11 - don't change this (since NONE is valid)
//default attenuation to NORM if not specified
if(!self.snd_attn)
self.snd_attn = 2;
self.snd_attn = self.snd_attn - 1;// needed so the default to NORM works (since 0 = NONE)
*/
//start right away if not triggered
if(!self.targetname)
{
//Needs to start on the second frame since that's when entities not called in world_spawn are created
self.nextthink = time + 0.1;
self.think = ambientsound_go;
}
//wait for trigger
else
self.use = ambientsound_go;
};
/*
colour_light is a small hack to try and simulate coloured lighting.
It does this by calling the built-in v_cshift function
This can be used for 'atmospheric' effects like outdoor haze and darkness.
This function is overriden by Quake when under liquids and by the background flash when an item is picked up
The GL Quake command 'gl_polyblend 0' also negates this entity.
colourvalue - this is the only parameter, must be in this format;
v_cshift R G B I\n
where R is Red (0 - 255), G is Green (0 - 255), B is Rlue (0 - 255), and I is Intensity (0 - 255)
This effect works poorly in small increments in GL Quake, no colour will be noticeable until about an
intesity of about 5 (depending on the colour) and some colour/intensity combinations result in ugly
banding. It's best to test colour levels in the console before applying them in your map.
These entities are activated by touch (the colour shift only occurs when the player is touching the trigger field)
But can also be enabled/disabled by using a targetname.
The string must be exactly as shown (including the v_cshift and the carraige return
at the end) I could have used multiple stuffcmd statements to make it more user friendly but.. eh.
*/
void() colourlight_wait;
.string colourvalue;
float CLSTART_OFF = 1;
void() colourlight_off =
{
if(other.classname != "player")
return;
//turn it off and reset
stuffcmd (other, "v_cshift 0 0 0 0\n");
self.use = colourlight_wait;
self.touch = SUB_Null;
};
void() colourlight_toggle =
{
//called after light is triggered a second time
self.touch = colourlight_off;
};
void() colourlight_use =
{
if(other.classname != "player")
return;
//POX v1.2 - better clearing of v_cshift (in PostThink)
if(other.cshift_finished > time)
return;
stuffcmd (other, self.colourvalue);
//POX v1.2 - better clearing of v_cshift (in PostThink)
other.cshift_off = FALSE;
other.cshift_finished = time + 0.1; //check every frame
//if targetted, alow it to be shut down
if(self.targetname)
self.use = colourlight_toggle;
};
void() colourlight_wait =
{
//activated by a trigger so wait for touch
self.touch = colourlight_use;
};
void() colour_light =
{
InitTrigger ();
//Can be made active by use of a targetname
//Must be triggered by touch
if((!self.targetname) || !(self.spawnflags & CLSTART_OFF))
self.touch = colourlight_use;
else
{
self.use = colourlight_wait;
self.touch = SUB_Null;
}
};
/*
particle_stream
Changed for Paroxysm v1.11's new regen stations
Creates a regen stream-pulse thingy model with out a grate and no touch
This entity should be used in conjunction with a regen_station trigger
clr = skin# (0 = blue, 1 = yellow, 2 = red)
NOTE: Origin is the BOTTOM of the stream (so use the middle of the bounding box in a map editor)
*/
.float clr;
void() regen_ambientsound;
void() particle_stream =
{
precache_sound("ambience/regen1.wav");
precache_model ("progs/stream.mdl");
self.angles = '0 0 0';
self.solid = SOLID_NOT;
self.movetype = MOVETYPE_NONE;
setmodel(self, "progs/stream.mdl");
//POX v1.2 - just in case
if(self.clr > 2 || self.clr < 0)
self.clr = 0;
self.skin = self.clr;
//POX v1.2 Fixed sound orign (makestatic messed it up?)
regen_ambientsound ();
makestatic (self);
};
/*
POX v1.2
misc_explobox
BSP based explo_box'es
Try to use rectangular objects, since entites use bounding box collision detection
*/
void() bsp_explode =
{
self.takedamage = DAMAGE_NO;
self.trigger_field.classname = "explo_box";
// did say self.owner
T_RadiusDamage (self.trigger_field, self.trigger_field, self.dmg, world);
sound (self.trigger_field, CHAN_VOICE, "weapons/r_exp3.wav", 1, ATTN_NORM);
particle (self.trigger_field.origin, '0 0 0', 75, 255);
remove (self);
remove (self.trigger_field);
};
void() misc_explobsp =
{
local entity spot;
self.solid = SOLID_BBOX;
self.movetype = MOVETYPE_NONE;
setmodel (self, self.model);
setsize( self, self.mins, self.maxs );
precache_sound ("weapons/r_exp3.wav");
if (!self.health)
self.health = 20;
if (!self.dmg)
self.dmg = 160;
self.th_die = bsp_explode;
self.takedamage = DAMAGE_AIM;
self.nobleed = TRUE;
//POX 1.2 - HACK!
//put a null entity at the center of the model to hold the explosion position
spot = spawn();
setmodel (spot, string_null);
spot.origin_x = self.absmin_x + (self.size_x * 0.5);
spot.origin_y = self.absmin_y + (self.size_y * 0.5);
spot.origin_z = self.absmin_z + (self.size_z * 0.5);
setsize (spot, '0 0 0', '0 0 0');
self.trigger_field = spot;
};