SSQC Side:
Firstly, in "defs.qc", at the bottom:
Code: Select all
// QRV : Particle Effect Emitters ( mapfx )
.float emitter_active;
.string emitter_fx;
// QRV : CSQC Stuff
float ENT_PARTICLE_EMITTER = 4;
.float enttype;
Now, I'm using DarkPlaces release win32 build from 28th, June, 2011, and my SSQC uses the dpextensions.qc from that release.
From the reading of Urre's code on CSQC Rockets, I had to add the following to get this working. I actually added mine to the bottom of dpextensions.qc,
but this is a bad idea. Really, you should make a new file, "dpextensions2.qc" or something, and add it after "dpextensions.qc" in the "progs.src".
"dpextensions2.qc":
Code: Select all
// Entity Sending
.float(entity viewer) SendEntity;
.float Version;
float MSG_ENTITY = 5;
Now, the following block of code wants to go in a new file, for the sake of this "tutorial" lets call that file mapfx_particles.qc, save it with your other SSQC files.
It should go underneath "misc.qc" in the "progs.src".
mapfx_particles.qc:
Code: Select all
//=============================================================================================
//
// QRV : Map Effects : Particles
//
//
// Handles particle emitters, should also be linked to CSQC cuz its "better"
//=============================================================================================
//.float emitter_active
//.string emitter_fx
// QRV : Map Effects : Particle Emitter : SendToCSQC
float() mapfx_particle_emitter_sendtocsqc =
{
WriteByte( MSG_ENTITY, ENT_PARTICLE_EMITTER );
// Emitter origin
WriteCoord( MSG_ENTITY, self.origin_x );
WriteCoord( MSG_ENTITY, self.origin_y );
WriteCoord( MSG_ENTITY, self.origin_z );
// Emitter velocity ( TODO: This could be avoided, at the cost of an extra byte sent, or an extra ENT_* thingy )
WriteCoord( MSG_ENTITY, self.velocity_x );
WriteCoord( MSG_ENTITY, self.velocity_y );
WriteCoord( MSG_ENTITY, self.velocity_z );
// Emitter Re-Emit time
WriteCoord( MSG_ENTITY, self.wait );
// Emitter Particles to emit per emit burst
WriteByte( MSG_ENTITY, self.delay );
// Emitter active flag
WriteByte( MSG_ENTITY, self.emitter_active );
// Emitter ParticleEffectNum
WriteString( MSG_ENTITY, self.emitter_fx );
// o.O
return TRUE;
};
// QRV : Map Effects : Particle Emitter : Use
void() mapfx_particle_emitter_use =
{
// Toggle on/off for the emitter, this re-sends the whole emitter info,
// which can be avoided, but I need to do some more testing of how ssqc <-> ccsqc works
// to make sure my idea doesnt balls up.
if ( self.emitter_active == 1 )
{
self.emitter_active = 0;
self.nextthink = time + 0.01;
return;
}
// Default to turn on
self.emitter_active = 1;
self.nextthink = time + 0.01;
};
// QRV : Map Effects : Particle Emitter : Think
void() mapfx_particle_emitter_think =
{
// This is used to delay sends to csqc, i cant remember why i did it like this
// but it made sense at the time, can probably be changed/removed
// csqc thingy
self.SendEntity = mapfx_particle_emitter_sendtocsqc;
self.Version = self.Version + 1;
self.nextthink = -1;
};
// QRV : Map Effects : Particle Emitter
//
// .message = EffectInfo.txt effect name
// .origin = Position of emitter
// .velocity = Particle emit velocity
// .wait = Particle emit speed.
// .delay = Amount of particles to emit per emit burst
// .targetname = For turning emitter on/off
//
// spawnflags & 4 = start turned off, wait for trigger to turn on
//
// QRV : Map Effects : Particle Emitter : Spawn
// this isnt used by the map editor object, but is for spawning via code
// This is hardcoded to always be active, but making it dynamically triggerable wouldnt be hard
void( vector t_org, vector t_vel, float t_wait, float t_delay, string particle_fx_name ) mapfx_particle_emitter_spawn =
{
local entity pfx;
// QRV : Clientside particle emitters? o.O
pfx = spawn();
pfx.enttype = ENT_PARTICLE_EMITTER;
pfx.origin = t_org;
pfx.velocity = t_vel;
pfx.emitter_active = 1;
pfx.emitter_fx = particle_fx_name;
pfx.wait = t_wait;
pfx.delay = t_delay;
pfx.light_lev = 1;
pfx.pflags = PFLAGS_FULLDYNAMIC;
pfx.think = mapfx_particle_emitter_think;
pfx.nextthink = time + 0.05 + random() * 0.2; // This staggers the send to csqc some, maybe change it
};
// QRV : Map Effects : Particle Emitter
//
// .message = EffectInfo.txt effect name
// .origin = Position of emitter
// .velocity = Particle emit velocity
// .wait = Particle emit speed.
// .delay = Amount of particles to emit per emit burst
// .targetname = For turning emitter on/off
//
// spawnflags & 4 = start turned off, wait for trigger to turn on
//
// QRV : Map Effects : Particle Emitter
void() mapfx_particle_emitter =
{
// Sanity - We must have the name of the EffectInfo, everything else we can make defaults for
if ( ! self.message )
{
dprint("mapfx_particle_emitter: No 'message' field.\n");
remove(self);
}
// Setup some stuff
self.emitter_active = 1;
self.emitter_fx = self.message;
// Without this, for whatever reason, CSQC wont get the entity sent to it ( from dp anyway )
// Urre had this problem in some code of his, and might know a solution now.
self.light_lev = 1;
self.pflags = PFLAGS_FULLDYNAMIC;
if ( self.targetname )
self.use = mapfx_particle_emitter_use;
self.think = mapfx_particle_emitter_think;
self.nextthink = time + 0.05;
// Do we start turned off?
if ( self.spawnflags & 4 )
{
// More sanity
if ( ! self.targetname )
{
dprint("mapfx_particle_emitter: No 'targetname' field for triggerable emitter.\n");
remove(self);
}
self.emitter_active = 0;
// if we start turned off, we dont need to tell csqc till we turn on
self.nextthink = -1;
}
// Ok, we could check more stuff from the map editor, but meh
};
// This is from another file in my project, but its an example of how i do my "flaming walltorches" ( my maps are q3bsp based also, but this shouldnt matter )
//=========================================
// mapfx_walltorch
//=========================================
void() mapfx_walltorch =
{
// Pre-cache model
precache_model ("progs/flame.mdl");
// Set the model
setmodel (self, "progs/flame.mdl");
// Sound effect
FireAmbient ();
// Now, we make a second entity :O
// this wont be static, as its a particle flame emitter... :/ ( CSQC at some point? )
mapfx_particle_emitter_spawn (
self.origin,
'0 0 0',
1,
1,
"MAPFX_FLAME_WALLTORCH"
);
// Make static on this model
makestatic (self);
};
Ok, couple of things you should note here, the "mapfx_particle_emitter" is the Map Object, ie, this is the name for use in your map editors, the keys/flags are explained above the function. As I keep saying, this isnt a good tutoral as it assumes you know some things.
Also, "mapfx_walltorch" is another map object, no properties for it, it just display the wall torch model with a csqc particle emitter thingy above it.
Oh, for the map editors, i use box ( -8 -8 -8 ) to ( 8 8 8 ) for the map entity.
At a first writing, this is it for the SSQC side, I aint tested it, but it should complie if you got it all working.
Now on to the CSQC Side of things, again, I'm assuming you've got a CSQC codebase of some sort, and are able to compile it.
I'm not giving a tutorial on how to get the CSQC codebase up and running, because I'm not entirely sure I've done my correct, or if I'm even using
the most "recent" of whatever. Theres a wiki page on doing a basic CSQC hud somewhere on google, my codebase came from that.