/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the included (GNU.txt) GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #define NUMVERTEXNORMALS 162 float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; void R_Rockettrail_Callback(struct cvar_s *var, char *oldvalue) { int i; model_t *mod; extern model_t mod_known[]; extern int mod_numknown; if (cls.state == ca_disconnected) return; // don't bother parsing while disconnected for (i=0 , mod=mod_known ; iflags & EF_ROCKET) P_DefaultTrail(mod); } } void R_Grenadetrail_Callback(struct cvar_s *var, char *oldvalue) { int i; model_t *mod; extern model_t mod_known[]; extern int mod_numknown; if (cls.state == ca_disconnected) return; // don't bother parsing while disconnected for (i=0 , mod=mod_known ; iflags & EF_GRENADE) P_DefaultTrail(mod); } } particleengine_t pe_null; particleengine_t pe_classic; particleengine_t pe_darkplaces; particleengine_t pe_script; particleengine_t *particlesystem[] = { &pe_script, &pe_darkplaces, &pe_classic, &pe_null, NULL, }; void R_ParticleSystem_Callback(struct cvar_s *var, char *oldvalue) { int i; if (pe) pe->ShutdownParticles(); pe = NULL; for (i = 0; particlesystem[i]; i++) { if ( (particlesystem[i]->name1 && !stricmp(var->string, particlesystem[i]->name1)) || (particlesystem[i]->name2 && !stricmp(var->string, particlesystem[i]->name2))) { pe = particlesystem[i]; break; } if (!pe) if (particlesystem[i]->name1) pe = particlesystem[i]; } if (!pe) Sys_Error("No particle system available. Please recompile."); pe->InitParticles(); pe->ClearParticles(); CL_RegisterParticles(); } cvar_t r_rockettrail = SCVARFC("r_rockettrail", "1", CVAR_SEMICHEAT, R_Rockettrail_Callback); cvar_t r_grenadetrail = SCVARFC("r_grenadetrail", "1", CVAR_SEMICHEAT, R_Grenadetrail_Callback); #ifdef MINIMAL //minimal builds get a different default. cvar_t r_particlesystem = SCVARFC("r_particlesystem", "classic", CVAR_SEMICHEAT, R_ParticleSystem_Callback); #else cvar_t r_particlesystem = SCVARFC("r_particlesystem", "script", CVAR_SEMICHEAT, R_ParticleSystem_Callback); #endif cvar_t r_particlesdesc = SCVARF("r_particlesdesc", "spikeset tsshaft", CVAR_SEMICHEAT); extern cvar_t r_bouncysparks; extern cvar_t r_part_rain; extern cvar_t r_bloodstains; extern cvar_t gl_part_flame; cvar_t r_part_rain_quantity = SCVAR("r_part_rain_quantity", "1"); cvar_t r_particle_tracelimit = SCVAR("r_particle_tracelimit", "250"); cvar_t r_part_sparks = SCVAR("r_part_sparks", "1"); cvar_t r_part_sparks_trifan = SCVAR("r_part_sparks_trifan", "1"); cvar_t r_part_sparks_textured = SCVAR("r_part_sparks_textured", "1"); cvar_t r_part_beams = SCVAR("r_part_beams", "1"); cvar_t r_part_contentswitch = SCVAR("r_part_contentswitch", "1"); particleengine_t *pe; void P_InitParticleSystem(void) { char *particlecvargroupname = "Particle effects"; Cvar_Register(&r_particlesystem, "Particles"); //particles Cvar_Register(&r_particlesdesc, particlecvargroupname); Cvar_Register(&r_bouncysparks, particlecvargroupname); Cvar_Register(&r_part_rain, particlecvargroupname); Cvar_Register(&r_part_rain_quantity, particlecvargroupname); Cvar_Register(&r_particle_tracelimit, particlecvargroupname); Cvar_Register(&r_part_sparks, particlecvargroupname); Cvar_Register(&r_part_sparks_trifan, particlecvargroupname); Cvar_Register(&r_part_sparks_textured, particlecvargroupname); Cvar_Register(&r_part_beams, particlecvargroupname); Cvar_Register(&r_part_contentswitch, particlecvargroupname); Cvar_Register (&gl_part_flame, particlecvargroupname); } #ifdef Q2BSPS qboolean Q2TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal) { vec3_t nul = {0,0,0}; trace_t trace = CM_BoxTrace(pmove.physents[0].model, start, end, nul, nul, MASK_SOLID); if (trace.fraction < 1) { VectorCopy (trace.plane.normal, normal); VectorCopy (trace.endpos, impact); return true; } return false; } #endif qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal) { trace_t trace; float len, bestlen; int i; vec3_t delta, ts, te; physent_t *pe; qboolean clipped=false; memset (&trace, 0, sizeof(trace)); VectorSubtract(end, start, delta); bestlen = Length(delta); VectorCopy (end, impact); for (i=0 ; i< pmove.numphysent ; i++) { pe = &pmove.physents[i]; if (pe->nonsolid) continue; if (pe->model) { VectorSubtract(start, pe->origin, ts); VectorSubtract(end, pe->origin, te); pe->model->funcs.Trace(pe->model, 0, 0, ts, te, vec3_origin, vec3_origin, &trace); if (trace.fraction<1) { VectorSubtract(trace.endpos, ts, delta); len = Length(delta); if (len < bestlen) { bestlen = len; VectorCopy (trace.plane.normal, normal); VectorAdd (pe->origin, trace.endpos, impact); } clipped=true; } if (trace.startsolid) { VectorNormalize(delta); normal[0] = -delta[0]; normal[1] = -delta[1]; normal[2] = -delta[2]; VectorCopy (start, impact); return true; } } } if (clipped) { return true; } else { return false; } } //handy utility... void P_EmitEffect (vec3_t pos, int type, trailstate_t **tsk) { if (cl.paused) return; pe->RunParticleEffectState(pos, NULL, ((host_frametime>0.1)?0.1:host_frametime), type, tsk); } // P_SelectableTrail: given default/opposite effects, model pointer, and a user selection cvar // changes model to the appropriate trail effect and default trail index void P_SelectableTrail(model_t *model, cvar_t *selection, int mdleffect, int mdlcidx, int oppeffect, int oppcidx) { int select = (int)(selection->value); switch (select) { case 0: // check for string, otherwise no trail if (selection->string[0] == '0') { model->particletrail = P_INVALID; break; } else { int effect = P_FindParticleType(selection->string); if (effect >= 0) { model->particletrail = effect; model->traildefaultindex = mdlcidx; break; } } // fall through to default (so semicheat will work properly) case 1: // default model effect default: model->particletrail = mdleffect; model->traildefaultindex = mdlcidx; break; case 2: // opposite effect model->particletrail = oppeffect; model->traildefaultindex= oppcidx; break; case 3: // alt rocket effect model->particletrail = P_ParticleTypeForName("TR_ALTROCKET"); model->traildefaultindex = 107; break; case 4: // gib model->particletrail = P_ParticleTypeForName("TR_BLOOD"); model->traildefaultindex = 70; break; case 5: // zombie gib model->particletrail = P_ParticleTypeForName("TR_SLIGHTBLOOD"); model->traildefaultindex = 70; break; case 6: // Scrag tracer model->particletrail = P_ParticleTypeForName("TR_WIZSPIKE"); model->traildefaultindex = 60; break; case 7: // Knight tracer model->particletrail = P_ParticleTypeForName("TR_KNIGHTSPIKE"); model->traildefaultindex = 238; break; case 8: // Vore tracer model->particletrail = P_ParticleTypeForName("TR_VORESPIKE"); model->traildefaultindex = 154; break; case 9: // rail trail model->particletrail = P_ParticleTypeForName("TR_RAILTRAIL"); model->traildefaultindex = 15; break; } } //figure out which particle trail to use for the given model, filling in its values as required. void P_DefaultTrail (model_t *model) { // TODO: EF_BRIGHTFIELD should probably be handled in here somewhere // TODO: make trail default color into RGB values instead of indexes if (model->engineflags & MDLF_NODEFAULTTRAIL) return; if (model->flags & EF_ROCKET) P_SelectableTrail(model, &r_rockettrail, P_ParticleTypeForName("TR_ROCKET"), 109, P_ParticleTypeForName("TR_GRENADE"), 6); else if (model->flags & EF_GRENADE) P_SelectableTrail(model, &r_grenadetrail, P_ParticleTypeForName("TR_GRENADE"), 6, P_ParticleTypeForName("TR_ROCKET"), 109); else if (model->flags & EF_GIB) { model->particletrail = P_ParticleTypeForName("TR_BLOOD"); model->traildefaultindex = 70; } else if (model->flags & EF_TRACER) { model->particletrail = P_ParticleTypeForName("TR_WIZSPIKE"); model->traildefaultindex = 60; } else if (model->flags & EF_ZOMGIB) { model->particletrail = P_ParticleTypeForName("TR_SLIGHTBLOOD"); model->traildefaultindex = 70; } else if (model->flags & EF_TRACER2) { model->particletrail = P_ParticleTypeForName("TR_KNIGHTSPIKE"); model->traildefaultindex = 238; } else if (model->flags & EF_TRACER3) { model->particletrail = P_ParticleTypeForName("TR_VORESPIKE"); model->traildefaultindex = 154; } else if (model->flags & EFH2_BLOODSHOT) //these are the hexen2 ones. { model->particletrail = P_ParticleTypeForName("t_bloodshot"); model->traildefaultindex = 136; } else if (model->flags & EFH2_FIREBALL) { model->particletrail = P_ParticleTypeForName("t_fireball"); model->traildefaultindex = 424; } else if (model->flags & EFH2_ACIDBALL) { model->particletrail = P_ParticleTypeForName("t_acidball"); model->traildefaultindex = 440; } else if (model->flags & EFH2_ICE) { model->particletrail = P_ParticleTypeForName("t_ice"); model->traildefaultindex = 408; } else if (model->flags & EFH2_SPIT) { model->particletrail = P_ParticleTypeForName("t_spit"); model->traildefaultindex = 260; } else if (model->flags & EFH2_SPELL) { model->particletrail = P_ParticleTypeForName("t_spell"); model->traildefaultindex = 260; } else if (model->flags & EFH2_VORP_MISSILE) { model->particletrail = P_ParticleTypeForName("t_vorpmissile"); model->traildefaultindex = 302; } else if (model->flags & EFH2_SET_STAFF) { model->particletrail = P_ParticleTypeForName("t_setstaff"); model->traildefaultindex = 424; } else if (model->flags & EFH2_MAGICMISSILE) { model->particletrail = P_ParticleTypeForName("t_magicmissile"); model->traildefaultindex = 149; } else if (model->flags & EFH2_BONESHARD) { model->particletrail = P_ParticleTypeForName("t_boneshard"); model->traildefaultindex = 384; } else if (model->flags & EFH2_SCARAB) { model->particletrail = P_ParticleTypeForName("t_scarab"); model->traildefaultindex = 254; } else model->particletrail = P_INVALID; }