/* 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; };