Announcement

Collapse
No announcement yet.

Weather.qc

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Weather.qc

    Triggerable Weather class ver 1

    HOW:

    1) Copy and paste the below QC in a text document and save it as WEATHER.QC
    2) Use a NON weather-modified Quake source (any source) and add DPEXTENSIONS.QC after DEFS.QC in PROGS.SRC
    3) add WEATHER.QC anywhere you like after DPEXTENSIONS in PROGS.SRC
    4) compile and enjoy

    WEATHER.QC
    Code:
    /*__________>  TRIGGERABLE WEATHER  ver 1.0 <__________*/
    /*      modified from Lord Havoc's rain and snow       */
    /*             includes Sevens sound mods              */
    /*__________>   Gypsy - BREAK QUAKE MOD     <__________*/
    
    float SPAWN_RAIN = 1;
    float SPAWN_SNOW = 2;
    
    // ideas to do:
    // float SPAWN_ADD_WIND = 4;
    // float SPAWN_ACID_RAIN = 8;
    // float SPAWN_FIRE_RAIN = 16;
    
    float CLEAR = 0;
    float FORECAST = 1;
    
    .float weather;
    .float weather_cached;
    
    /*===============================================
                         Sounds 
    ===============================================*/
    
    void() precache_weather
    {
    	precache_sound ("ambience/rain.wav");
    	precache_sound ("ambience/thunderrain.wav");
    };
    
    void() regenton = 
    {
    	if (random()> 0.94)		
    		sound (self, CHAN_VOICE, "ambience/rain.wav", 1, ATTN_NORM);   
    	else
    		return;
    };
    
    void() blitzton =  
    {
    	if (random()> 0.997)		
    		sound (self, CHAN_WEAPON, "ambience/thunderrain.wav", 1, ATTN_NORM);
    	else
    		return;
    };
    
    /*===============================================
                         Thinks
    ===============================================*/
    
    void() snow_think =
    {
    	self.nextthink = time + 0.1;
    	te_particlesnow(self.absmin, self.absmax, self.dest, self.count, self.cnt);
    };
    
    void() rain_think =
    {
    	if(!self.weather_cached)
    	{
    		self.weather_cached = 1;
    		precache_weather();
    	}
    	
    	self.nextthink = time + 0.1;
    	te_particlerain(self.absmin, self.absmax, self.dest, self.count, self.cnt);
    
    	blitzton();
    	regenton();
    };
    
    /*===============================================
                        Activator
    ===============================================*/
    
    void() weather_use =
    {		
    	if(!self.weather || self.weather == CLEAR)
    	{
    		self.weather = FORECAST;
    		if (self.spawnflags & SPAWN_RAIN)
    		{
    			sound (self, CHAN_WEAPON, "ambience/thunderrain.wav", 1, ATTN_NORM);
    			self.think = rain_think;
    		}
    		else if (self.spawnflags & SPAWN_SNOW)
    		{	
    			self.think = snow_think;
    		}
    		self.nextthink = time + 0.1;
    		return;
    	}
    
    	self.weather = CLEAR;
    	self.think = SUB_Null;
    };
    
    /*===============================================
                     Mother Nature
    ===============================================*/
    
    void() func_weather =
    {
    	
    	self.dest = self.velocity;
    	self.velocity = '0 0 0';
    	if (!self.dest & SPAWN_RAIN)
    		self.dest = '0 0 -700';
    	else if (!self.dest & SPAWN_SNOW)
    		self.dest = '0 0 -100';
    	
    	self.angles = '0 0 0';
    	self.movetype = MOVETYPE_NONE;
    	self.solid = SOLID_NOT;
    	
    	setmodel(self, self.model);
    	setorigin(self, self.origin);
    	setsize(self, self.mins, self.maxs);
    	
    	self.model = "";
    	
    	if (!self.cnt)
    		self.cnt = 12;
    	if (!self.count)
    		self.count = (self.absmax_x - self.absmin_x)*(self.absmax_y - self.absmin_y)/8192;
    	if (self.count < 1)
    	{
    		remove(self);
    		return;
    	}
    	// convert from per second to per 0.1 sec,
    	self.count = ceil(self.count * 0.1);
    	
    	if(!self.targetname)
    		weather_use();
    	else
    		self.use = weather_use;
    };

    Here is the Entity definition. Just copy and paste it at the end of your .FGD

    Code:
    //
    // func_weather mod - Gypsy
    //
    
    @SolidClass base(Targetname) = func_weather: "func_weather"
    [
    	spawnflags(Flags) = 
    	[
    		1 : "Rain" : 1
    		2 : "Snow" : 0
    	]
    	velocity(string): "velocity" : "0 0 -700"
    	count(integer): "count" : 4
    	cnt(integer): "cnt" : 2
    ]
    HOW DOES THIS WORK:

    Make a block primitive the size of your weather area, convert it to an entitity and choose func_weather as it's classname. If you want rain, set the rain flag. If you want snow set the snow flag. I have no idea what will happen if you set both. If you want the weather to be set off by a trigger simply give it a name and put its name in some entity's target field. If the weather has no name it becomes automatic and constant.

    POSSIBLE USES:

    1) create multiple rain blocks with separate triggers, as the triggers are set off more rain/snow will fall as more weather is activated
    2) simply turn the rain/snow on and then turn it off at a later event
    3) use multiple triggers and weather blocks to change from snow to rain or vise versa

    ASIDES:

    1) all .ent files will work the exact same with this class
    2) sounds only get cached if rain is ever used
    3) one thunderclap happens as rain is initiated
    4) you have to have Sevens rain and thunder sounds (or sounds named the same)
    5) If you are using a weather-modded quake you will have to delete ALL weather functions at the end of MISC.QC and you can delete the precaches in WORLD.QC before you compile WEATHER.QC into your quake
    6) This class only depends on DPEXTENSIONS.QC.
    Last edited by MadGypsy; 01-31-2012, 12:00 PM.
    http://www.nextgenquake.com

  • #2
    I added a wind modifier. If anyone would like to help me make the wind better it would be appreciated. I used Sevens "random() >" system to determine if wind should happen. If wind does happen, the angle of the rain changes along with the rain count, then it all starts undoing itself when it reaches it's peak.

    Code:
    /*__________>  TRIGGERABLE WEATHER  ver 1.05 <__________*/
    /*       modified from Lord Havoc's rain and snow       */
    /*              includes Sevens sound mods              */
    /*__________>    Gypsy - BREAK QUAKE MOD     <__________*/
    
    float SPAWN_RAIN = 1;
    float SPAWN_SNOW = 2;
    
    // ideas to do:
    // float SPAWN_ACID_RAIN = 4;
    // float SPAWN_FIRE_RAIN = 8;
    
    float MODIFY_NONE = 0;
    float MODIFY_DYNAMIC_WIND = 1;
    
    float CLEAR = 0;
    float FORECAST = 1;
    
    float STATE_NO_WIND = 0;
    float STATE_WIND = 1;
    
    .float weather;
    .float modifiers;
    
    /*===============================================
                         Sounds 
    ===============================================*/
    
    void() regenton = 
    {
    	if (random()> 0.94)
    	{
    		precache_sound ("ambience/rain.wav");		
    		sound (self, CHAN_VOICE, "ambience/rain.wav", 1, ATTN_NORM); 
    	}		
    	else
    		return;
    };
    
    void() blitzton =  
    {
    	if (random()> 0.997)
    	{
    		precache_sound ("ambience/thunderrain.wav");	
    		sound (self, CHAN_WEAPON, "ambience/thunderrain.wav", 1, ATTN_NORM);
    	}
    	else
    		return;
    };
    
    /*===============================================
                         Modifiers
    ===============================================*/
    
    entity(entity ent) add_dynamic_wind =
    {
    	local vector v;
    	local float multiplier; 
    	
    	// grab original count
    	ent.count = ceil( ( ( ent.absmax_x - ent.absmin_x ) * ( ent.absmax_y - ent.absmin_y ) / 8192 ) * .1 );
    	
    	if (ent.state == STATE_NO_WIND)
    	{
    		if (random()> 0.997 && ent.dest_x < 1)	
    		{
    			ent.state = STATE_WIND;
    			precache_sound ("ambience/wind1.wav");
    			sound (ent, CHAN_BODY, "ambience/wind1.wav", 1, ATTN_NORM);
    		}
    		else if(ent.dest_x > 0)
    		{
    			v_x = ent.dest_x - 2;
    			v_y = floor( v_x * .3 );
    		}
    	}
    	
    	if (ent.state == STATE_WIND)
    	{
    		if(ent.dest_x < 200)
    		{
    			v_x = ent.dest_x + 2;
    			v_y = ceil( v_x * .3 );
    		}
    		else
    		{
    			ent.state = STATE_NO_WIND;
    		}
    	}
    	
    	multiplier = 1 + ( v_y * .01 );
    	ent.count = ceil( ent.count * multiplier );
    	
    	v_z = ent.dest_z;
    	
    	ent.dest = v;
    	
    	return ent; 
    };
    
    /*===============================================
                         Thinks
    ===============================================*/
    
    void() snow_think =
    {
    	if(self.modifiers == MODIFY_DYNAMIC_WIND)
    		self = add_dynamic_wind(self);
    
    	self.nextthink = time + 0.1;
    	te_particlesnow(self.absmin, self.absmax, self.dest, self.count, self.cnt);
    };
    
    void() rain_think =
    {
    	if(self.modifiers == MODIFY_DYNAMIC_WIND)
    		self = add_dynamic_wind(self);
    		
    	self.nextthink = time + 0.1;
    	te_particlerain(self.absmin, self.absmax, self.dest, self.count, self.cnt);
    	
    	blitzton();
    	regenton();
    };
    
    /*===============================================
                        Activator
    ===============================================*/
    
    void() weather_use =
    {		
    	if(!self.weather || self.weather == CLEAR)
    	{
    		self.weather = FORECAST;
    		if (self.spawnflags & SPAWN_RAIN)
    		{
    			precache_sound ("ambience/thunderrain.wav");
    			sound (self, CHAN_WEAPON, "ambience/thunderrain.wav", 1, ATTN_NORM);
    			self.think = rain_think;
    		}
    		else if (self.spawnflags & SPAWN_SNOW)
    		{	
    			self.think = snow_think;
    		}
    		self.nextthink = time + 0.1;
    		return;
    	}
    
    	self.weather = CLEAR;
    	self.think = SUB_Null;
    };
    
    /*===============================================
                     Mother Nature
    ===============================================*/
    
    void() func_weather =
    {
    	
    	self.dest = self.velocity;
    	self.velocity = '0 0 0';
    	if (!self.dest & SPAWN_RAIN)
    		self.dest = '0 0 -700';
    	else if (!self.dest & SPAWN_SNOW)
    		self.dest = '0 0 -100';
    	
    	self.angles = '0 0 0';
    	self.movetype = MOVETYPE_NONE;
    	self.solid = SOLID_NOT;
    	
    	setmodel(self, self.model);
    	setorigin(self, self.origin);
    	setsize(self, self.mins, self.maxs);
    	
    	self.model = "";
    	
    	if (!self.cnt)
    		self.cnt = 12;
    		
    	if (!self.count)
    		self.count = ( self.absmax_x - self.absmin_x ) * ( self.absmax_y - self.absmin_y ) / 8192;
    	if (self.count < 1)
    	{
    		remove(self);
    		return;
    	}
    	// convert to 1/10th of a sec,
    	self.count = ceil(self.count * 0.1);
    	
    	self.state = STATE_NO_WIND;
    	
    	if(!self.modifiers)
    		self.modifiers = MODIFY_NONE;
    	
    	if(!self.targetname)
    		weather_use();
    	else
    		self.use = weather_use;
    };
    The .fgd code has become this now

    Code:
    @SolidClass base(Targetname) = func_weather: "func_weather"
    [
    	spawnflags(Flags) = 
    	[
    		1 : "Rain" : 1
    		2 : "Snow" : 0
    	]
    	modifiers(choices) : "modifiers" : 0 =
    	[
    		0 : "none"
    		1 : "add wind"
    	]
    	velocity(string): "velocity" : "0 0 -700"
    	count(integer): "count" : 4
    	cnt(integer): "cnt" : 2
    ]
    Last edited by MadGypsy; 02-01-2012, 09:00 AM.
    http://www.nextgenquake.com

    Comment


    • #3
      Im working on some code like this for my mod. Seven and I also collaborated on it a while back...some of this may be identical.

      I struggled a bit with the sounds for the rain bleeding through to parts of the map (e1m1 for example) where the rain sounds ought not be heard, Making code to work on all maps is a challenge.

      Comment


      • #4
        That's why I don't even bother manipulating actual quake. For one there are already so many people that are way better than me working on that and for 2 I like to build maps. I know that the couple of little quake mods I have "released" to this site kinda suck but I have 2 hopes

        1) somebody will take my "half-ass" mod and make it better
        2) my posts here can sort of serve as a history on how I grow as a quake "developer"

        I have only been messing with QuakeC for about 1 week and without taking a single tutorial I have figured out a whole freakin bunch. When I was trying to get the x velocity from dest, I fell back on the kick_gibs mod. At first I was confused and was trying self.dest.velocity_x. Of course that gave me compile errors and just on a whim I tried dest_x AND IT WORKED! (lol). I've been OOP programming in other languages for a while and I have never heard of underscore syntax. Anyway, that's my little nooby example of figuring things out that aren't necessarily obvious (even to a programmer).

        I'm pretty sure that I'm going to tear the entire Quake core apart and reprogram it from the bottom up. It's my little simple mods like the one above that are giving me the scope and focus of the code.I would prefer (for my game) that aside from globals, everything is a plugin - weapons, monsters, every possible thing. There are so many dependencies in Quake it is unreal. Of course, when and if I ever finish it won't be quake anymore - which is exactly my goal.

        Y'all remake quake, I'm gonna break quake and in that we will have all possibilities covered (lol),
        Michael
        http://www.nextgenquake.com

        Comment


        • #5
          Hello MadGypsy, Hello Cobalt,

          First of all, this is an interesting thread.

          I am working on a extension of it at the moment too.
          It will find its release in small mod compilation V3.72.

          During creation of custom map-specific particle effects, I suddenly felt the necessity of ambience sound support.
          I could not use the rain+thunder sound code from the weather for it, because of the random code usage.
          So I remembered my discussions with Cobalt on this matter.
          Knowing that Cobalt struggles hard to find a sound spawn function, that does not *bleed* through walls.
          So I recalled this dpextension:
          DP_SV_POINTSOUND
          void(vector origin, string sample, float volume, float attenuation) pointsound = #483;
          It is exactly what I needed for the ambience support.
          I am just finishing a map and added some steam particle effects on tubes/wholes.
          But that smoke/steam alone does not give the full *feeling*.
          So it needs an additional *shhh* sound.
          And DP_SV_POINTSOUND does the job fantastic.

          Now I just polishing the QC code so that everybody can add ambience sound via editing .ent files. Without the necessity to modify QC anymore.
          Something like the "custom particle effect support", but for sound.

          You can adjust the coordinate, volume, loop frequence and sound filename via the .ent file.


          This is a full working beta:
          Code:
          void() callsound =
          {
          	pointsound (self.origin, self.model, self.alpha, ATTN_NORM);
          //	pointsound (vector origin, string sample, float volume, float attenuation);
          };
          
          
          void() sound_think =
          {
          	precache_sound (self.model);
          	self.nextthink = time + self.count;
          	callsound ();
          };
          
          
          void() func_sound =
          {
          	self.think = sound_think;
          	self.nextthink = time + 0.01;
          };

          This is a .ent file declaration sample that spawns the sound:
          Code:
          {
          "classname" "info_notnull"
          "origin" "416 -40 180"	// represents coordinate
          "nextthink" "0.1"
          "alpha" "0.5"		// represents volume (1= max,  0= min)
          "count" "2.3"		// represents time loop (in seconds)
          "think" "sound_think"
          "model" "sound/vengeance/hknightmag.wav"
          }

          Maybe this is useful for someone.
          The further you move away from the sound spawning point, the more quiet it gets.
          Meshes seem to work like real walls it seems. They make it even more quiet.
          Which is a very welcome effect.

          Best wishes,
          Seven

          Comment


          • #6
            That's awesome Seven. I'm curious as to why you write the func but then skip it with an info_notnull. For instance why not just do it like this (not tested just a quick rewrite)

            Code:
            void() sound_think =
            {
            	precache_sound (self.model);
            	self.nextthink = time + self.count;
            	pointsound (self.origin, self.model, self.alpha, ATTN_NORM);
            };
            
            
            void() func_sound =
            {
            
                    if(!self.model || !self.origin) //check if the entity is even valid
                    {
                        remove(self);
                        return;                     //terminate the whole process
                    }
                    else
                    {
                        if(!self.alpha)
                            self.alpha = 0.5;
                        if(!self.count)
                            self.count = 2;
            
            	    self.think = sound_think;
            	    self.nextthink = time + 0.01;
                   }
            };
            and then in the ent
            Code:
            {
            "classname" "func_sound"
            "origin" "416 -40 180"	// represents coordinate
            "alpha" "0.5"		// represents volume (1= max,  0= min)
            "count" "2.3"		// represents time loop (in seconds)
            "model" "sound/vengeance/hknightmag.wav"
            }
            I'm not saying there is anything wrong with the way you do it. I am merely expressing that the way I just wrote it

            a) makes for a smaller .ent definition
            b) makes func_sound handle some error possibilities or conversely sets some vars to a commonly used default, potentially making the .ent definition even smaller

            BTW, the fgd for the above would be

            Code:
            @PointClass = func_sound: "func_sound"
            [
                alpha(integer) : 0.5
                count(integer) : 2
                model(string) : ""
            ]
            Or if you really wanted to make the def bad ass you could do something like this

            Code:
            @PointClass = func_sound: "func_sound"
            [
                alpha(integer) : 0.5
                count(integer) : 2
                model(choices) : "model" : 0 =
                [
                    0 : "record"
                    1 : "the address"
                    2 : "of every"
                    3 : "possible"
                    4 : "sound"
                ]
            ]
            Gypsy

            P.S.> Man it would be fantastic if quake C had switch/case statements. Like if you could do something like this
            Code:
            void() some_func
            {
                switch (self.spawnflag)
                {
                    case "AMBUSH":
                        //do stuff
                        break;
                    case "CRUCIFIED":
                        //do stuff
                        break;
                    case "SNEAKY":
                        //do stuf
                        break;
                    default:
                        bprint("not a spawn flag");
                        break; 
                }
            
            };
            Last edited by MadGypsy; 02-14-2012, 10:09 AM.
            http://www.nextgenquake.com

            Comment


            • #7
              You can use FTEQCC which supports switch/case. Kleshik uses them quite often. such as this:

              Code:
              string GetAmmoHUDImageFromAmmoType(float nAmmoType)
              { // Return Ammo HUD Image from Ammo Type
              	local string strReturn;
              	switch(nAmmoType)
              	{
              		case AMMOTYPE_SHELLS:
              			strReturn = HUDIMAGE_AMMO_SHELLS;
              			break;
              		case AMMOTYPE_NAILS:
              			strReturn = HUDIMAGE_AMMO_NAILS;
              			break;
              		case AMMOTYPE_ROCKETS:
              			strReturn = HUDIMAGE_AMMO_ROCKETS;
              			break;
              		case AMMOTYPE_CELLS:
              			strReturn = HUDIMAGE_AMMO_CELLS;
              			break;
              		default:
              			strReturn = "";
              			break;
              	}
              	
              	return strReturn;
              }
              Clan Brotherhood of the Axe

              Comment


              • #8
                Oh snap man, THANK YOU!, It supports arrays too! Now, if we could just get some JSON/associative array & for each loops support we'd have QuakePHP and I'd be bad ass at it.



                I know there are do/while loops but do/while loops always run at least once (or at least they do in every language I program in) and do/while loops are easy to infinite loop. I like loops that only "do" if there is definitely something to do, not "do" once regardless, before it hits the while. Maybe quakeC only calls the do after evaluating the while, I need to check the specs.

                Anyway thanks for the heads up Monster
                http://www.nextgenquake.com

                Comment

                Working...
                X