summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--depend24
-rw-r--r--src/cgame/cg_attachment.c344
-rw-r--r--src/cgame/cg_buildable.c40
-rw-r--r--src/cgame/cg_consolecmds.c4
-rw-r--r--src/cgame/cg_ents.c103
-rw-r--r--src/cgame/cg_event.c44
-rw-r--r--src/cgame/cg_local.h318
-rw-r--r--src/cgame/cg_main.c11
-rw-r--r--src/cgame/cg_particles.c500
-rw-r--r--src/cgame/cg_players.c7
-rw-r--r--src/cgame/cg_servercmds.c14
-rw-r--r--src/cgame/cg_trails.c1746
-rw-r--r--src/cgame/cg_view.c6
-rw-r--r--src/cgame/cg_weapons.c87
-rw-r--r--src/game/bg_misc.c38
-rw-r--r--src/game/bg_public.h3
-rw-r--r--src/game/g_buildable.c83
-rw-r--r--src/game/g_cmds.c1
-rw-r--r--src/game/g_main.c18
-rw-r--r--src/game/g_weapon.c6
-rw-r--r--src/game/q_math.c11
-rw-r--r--src/game/tremulous.h2
-rw-r--r--src/ui/ui_main.c41
24 files changed, 2185 insertions, 1267 deletions
diff --git a/Makefile b/Makefile
index e9d3cd3b..9a74bf50 100644
--- a/Makefile
+++ b/Makefile
@@ -65,6 +65,7 @@ CGOBJ = \
$(CGDIRNAME)/cg_weapons.o \
$(CGDIRNAME)/cg_mem.o \
$(CGDIRNAME)/cg_scanner.o \
+ $(CGDIRNAME)/cg_attachment.o \
$(CGDIRNAME)/cg_trails.o \
$(CGDIRNAME)/cg_particles.o \
$(CGDIRNAME)/cg_ptr.o \
diff --git a/depend b/depend
index 92cb5d62..230aaa93 100644
--- a/depend
+++ b/depend
@@ -237,6 +237,10 @@ releasei386-glibc/cgame/cg_animmapobj.o: src/cgame/cg_animmapobj.c src/cgame/cg_
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
+releasei386-glibc/cgame/cg_attachment.o: src/cgame/cg_attachment.c src/cgame/cg_local.h \
+ src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
+ src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
+ src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
releasei386-glibc/cgame/cg_buildable.o: src/cgame/cg_buildable.c src/cgame/cg_local.h \
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
@@ -281,10 +285,6 @@ releasei386-glibc/cgame/cg_mem.o: src/cgame/cg_mem.c src/cgame/cg_local.h src/ga
src/game/surfaceflags.h src/cgame/tr_types.h src/game/bg_public.h \
src/game/tremulous.h src/cgame/cg_public.h src/ui/ui_shared.h \
src/ui/keycodes.h ui/menudef.h
-releasei386-glibc/cgame/cg_mp3decoder.o: src/cgame/cg_mp3decoder.c src/cgame/cg_mp3decoder.h \
- src/cgame/cg_local.h src/game/q_shared.h src/game/surfaceflags.h \
- src/cgame/tr_types.h src/game/bg_public.h src/game/tremulous.h \
- src/cgame/cg_public.h src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
releasei386-glibc/cgame/cg_particles.o: src/cgame/cg_particles.c src/cgame/cg_local.h \
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
@@ -337,6 +337,10 @@ debugi386-glibc/cgame/cg_animmapobj.o: src/cgame/cg_animmapobj.c src/cgame/cg_lo
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
+debugi386-glibc/cgame/cg_attachment.o: src/cgame/cg_attachment.c src/cgame/cg_local.h \
+ src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
+ src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
+ src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
debugi386-glibc/cgame/cg_buildable.o: src/cgame/cg_buildable.c src/cgame/cg_local.h \
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
@@ -381,10 +385,6 @@ debugi386-glibc/cgame/cg_mem.o: src/cgame/cg_mem.c src/cgame/cg_local.h src/game
src/game/surfaceflags.h src/cgame/tr_types.h src/game/bg_public.h \
src/game/tremulous.h src/cgame/cg_public.h src/ui/ui_shared.h \
src/ui/keycodes.h ui/menudef.h
-debugi386-glibc/cgame/cg_mp3decoder.o: src/cgame/cg_mp3decoder.c src/cgame/cg_mp3decoder.h \
- src/cgame/cg_local.h src/game/q_shared.h src/game/surfaceflags.h \
- src/cgame/tr_types.h src/game/bg_public.h src/game/tremulous.h \
- src/cgame/cg_public.h src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
debugi386-glibc/cgame/cg_particles.o: src/cgame/cg_particles.c src/cgame/cg_local.h \
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
@@ -437,6 +437,10 @@ qvm/cgame/cg_animmapobj.asm: src/cgame/cg_animmapobj.c src/cgame/cg_local.h \
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
+qvm/cgame/cg_attachment.asm: src/cgame/cg_attachment.c src/cgame/cg_local.h \
+ src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
+ src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
+ src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
qvm/cgame/cg_buildable.asm: src/cgame/cg_buildable.c src/cgame/cg_local.h \
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
@@ -481,10 +485,6 @@ qvm/cgame/cg_mem.asm: src/cgame/cg_mem.c src/cgame/cg_local.h src/game/q_shared.
src/game/surfaceflags.h src/cgame/tr_types.h src/game/bg_public.h \
src/game/tremulous.h src/cgame/cg_public.h src/ui/ui_shared.h \
src/ui/keycodes.h ui/menudef.h
-qvm/cgame/cg_mp3decoder.asm: src/cgame/cg_mp3decoder.c src/cgame/cg_mp3decoder.h \
- src/cgame/cg_local.h src/game/q_shared.h src/game/surfaceflags.h \
- src/cgame/tr_types.h src/game/bg_public.h src/game/tremulous.h \
- src/cgame/cg_public.h src/ui/ui_shared.h src/ui/keycodes.h ui/menudef.h
qvm/cgame/cg_particles.asm: src/cgame/cg_particles.c src/cgame/cg_local.h \
src/game/q_shared.h src/game/surfaceflags.h src/cgame/tr_types.h \
src/game/bg_public.h src/game/tremulous.h src/cgame/cg_public.h \
diff --git a/src/cgame/cg_attachment.c b/src/cgame/cg_attachment.c
new file mode 100644
index 00000000..d8f20d90
--- /dev/null
+++ b/src/cgame/cg_attachment.c
@@ -0,0 +1,344 @@
+// cg_attachment.c -- an abstract attachment system
+
+/*
+ * Portions Copyright (C) 2000-2001 Tim Angus
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the OSML - Open Source Modification License v1.0 as
+ * described in the file COPYING which is distributed with this source
+ * code.
+ *
+ * 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.
+ */
+
+#include "cg_local.h"
+
+/*
+===============
+CG_AttachmentPoint
+
+Return the attachment point
+===============
+*/
+qboolean CG_AttachmentPoint( attachment_t *a, vec3_t v )
+{
+ centity_t *cent;
+
+ if( !a )
+ return qfalse;
+
+ switch( a->type )
+ {
+ case AT_STATIC:
+ if( !a->staticValid )
+ return qfalse;
+
+ VectorCopy( a->origin, v );
+ break;
+
+ case AT_TAG:
+ if( !a->tagValid )
+ return qfalse;
+
+ AxisCopy( axisDefault, a->re.axis );
+ CG_PositionRotatedEntityOnTag( &a->re, &a->parent,
+ a->model, a->tagName );
+ VectorCopy( a->re.origin, v );
+ break;
+
+ case AT_CENT:
+ if( !a->centValid )
+ return qfalse;
+
+ if( a->centNum == cg.predictedPlayerState.clientNum )
+ {
+ // this is smoother if it's the local client
+ VectorCopy( cg.predictedPlayerState.origin, v );
+ }
+ else
+ {
+ cent = &cg_entities[ a->centNum ];
+ VectorCopy( cent->lerpOrigin, v );
+ }
+ break;
+
+ case AT_PARTICLE:
+ if( !a->particleValid )
+ return qfalse;
+
+ if( !a->particle->valid )
+ {
+ a->particleValid = qfalse;
+ return qfalse;
+ }
+ else
+ VectorCopy( a->particle->origin, v );
+ break;
+
+ default:
+ CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
+ break;
+ }
+
+ if( a->hasOffset )
+ VectorAdd( v, a->offset, v );
+
+ return qtrue;
+}
+
+/*
+===============
+CG_AttachmentDir
+
+Return the attachment direction
+===============
+*/
+qboolean CG_AttachmentDir( attachment_t *a, vec3_t v )
+{
+ vec3_t forward;
+ centity_t *cent;
+
+ if( !a )
+ return qfalse;
+
+ switch( a->type )
+ {
+ case AT_STATIC:
+ //FIXME: hmmmmmmm
+ return qfalse;
+ break;
+
+ case AT_TAG:
+ if( !a->tagValid )
+ return qfalse;
+
+ VectorCopy( a->re.axis[ 0 ], v );
+ break;
+
+ case AT_CENT:
+ if( !a->centValid )
+ return qfalse;
+
+ cent = &cg_entities[ a->centNum ];
+ AngleVectors( cent->lerpAngles, forward, NULL, NULL );
+ VectorCopy( forward, v );
+ break;
+
+ case AT_PARTICLE:
+ if( !a->particleValid )
+ return qfalse;
+
+ if( !a->particle->valid )
+ {
+ a->particleValid = qfalse;
+ return qfalse;
+ }
+ else
+ VectorCopy( a->particle->velocity, v );
+ break;
+
+ default:
+ CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
+ break;
+ }
+
+ VectorNormalize( v );
+ return qtrue;
+}
+
+/*
+===============
+CG_AttachmentVelocity
+
+If the attachment can have velocity, return it
+===============
+*/
+qboolean CG_AttachmentVelocity( attachment_t *a, vec3_t v )
+{
+ if( !a )
+ return qfalse;
+
+ if( a->particleValid && a->particle->valid )
+ {
+ VectorCopy( a->particle->velocity, v );
+ return qtrue;
+ }
+ else if( a->centValid )
+ {
+ centity_t *cent = &cg_entities[ a->centNum ];
+
+ VectorCopy( cent->currentState.pos.trDelta, v );
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+===============
+CG_AttachmentCentNum
+
+If the attachment has a centNum, return it
+===============
+*/
+int CG_AttachmentCentNum( attachment_t *a )
+{
+ if( !a || !a->centValid )
+ return -1;
+
+ return a->centNum;
+}
+
+/*
+===============
+CG_Attached
+
+If the attachment is valid, return qtrue
+===============
+*/
+qboolean CG_Attached( attachment_t *a )
+{
+ if( !a )
+ return qfalse;
+
+ return a->attached;
+}
+
+/*
+===============
+CG_AttachToPoint
+
+Attach to a point in space
+===============
+*/
+void CG_AttachToPoint( attachment_t *a )
+{
+ if( !a || !a->staticValid )
+ return;
+
+ a->type = AT_STATIC;
+ a->attached = qtrue;
+}
+
+/*
+===============
+CG_AttachToCent
+
+Attach to a centity_t
+===============
+*/
+void CG_AttachToCent( attachment_t *a )
+{
+ if( !a || !a->centValid )
+ return;
+
+ a->type = AT_CENT;
+ a->attached = qtrue;
+}
+
+/*
+===============
+CG_AttachToTag
+
+Attach to a model tag
+===============
+*/
+void CG_AttachToTag( attachment_t *a )
+{
+ if( !a || !a->tagValid )
+ return;
+
+ a->type = AT_TAG;
+ a->attached = qtrue;
+}
+
+/*
+===============
+CG_AttachToParticle
+
+Attach to a particle
+===============
+*/
+void CG_AttachToParticle( attachment_t *a )
+{
+ if( !a || !a->particleValid )
+ return;
+
+ a->type = AT_PARTICLE;
+ a->attached = qtrue;
+}
+
+/*
+===============
+CG_SetAttachmentPoint
+===============
+*/
+void CG_SetAttachmentPoint( attachment_t *a, vec3_t v )
+{
+ if( !a )
+ return;
+
+ VectorCopy( v, a->origin );
+ a->staticValid = qtrue;
+}
+
+/*
+===============
+CG_SetAttachmentCent
+===============
+*/
+void CG_SetAttachmentCent( attachment_t *a, centity_t *cent )
+{
+ if( !a || !cent )
+ return;
+
+ a->centNum = cent->currentState.number;
+ a->centValid = qtrue;
+}
+
+/*
+===============
+CG_SetAttachmentTag
+===============
+*/
+void CG_SetAttachmentTag( attachment_t *a, refEntity_t parent,
+ qhandle_t model, char *tagName )
+{
+ if( !a )
+ return;
+
+ a->parent = parent;
+ a->model = model;
+ strncpy( a->tagName, tagName, MAX_STRING_CHARS );
+ a->tagValid = qtrue;
+}
+
+/*
+===============
+CG_SetAttachmentParticle
+===============
+*/
+void CG_SetAttachmentParticle( attachment_t *a, particle_t *p )
+{
+ if( !a )
+ return;
+
+ a->particle = p;
+ a->particleValid = qtrue;
+}
+
+/*
+===============
+CG_SetAttachmentOffset
+===============
+*/
+void CG_SetAttachmentOffset( attachment_t *a, vec3_t v )
+{
+ if( !a )
+ return;
+
+ VectorCopy( v, a->offset );
+ a->hasOffset = qtrue;
+}
diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c
index 38259dc6..1daaf464 100644
--- a/src/cgame/cg_buildable.c
+++ b/src/cgame/cg_buildable.c
@@ -108,9 +108,13 @@ void CG_AlienBuildableExplosion( vec3_t origin, vec3_t dir )
//particle system
ps = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDestroyedPS );
- CG_SetParticleSystemOrigin( ps, origin );
- CG_SetParticleSystemNormal( ps, dir );
- CG_AttachParticleSystemToOrigin( ps );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentPoint( &ps->attachment, origin );
+ CG_SetParticleSystemNormal( ps, dir );
+ CG_AttachToPoint( &ps->attachment );
+ }
}
@@ -205,9 +209,13 @@ void CG_HumanBuildableExplosion( vec3_t origin, vec3_t dir )
//particle system
ps = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDestroyedPS );
- CG_SetParticleSystemOrigin( ps, origin );
- CG_SetParticleSystemNormal( ps, dir );
- CG_AttachParticleSystemToOrigin( ps );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentPoint( &ps->attachment, origin );
+ CG_SetParticleSystemNormal( ps, dir );
+ CG_AttachToPoint( &ps->attachment );
+ }
}
@@ -500,6 +508,8 @@ void CG_InitBuildables( )
cg.buildablesFraction = (float)i / (float)( BA_NUM_BUILDABLES - 1 );
trap_UpdateScreen( );
}
+
+ cgs.media.teslaZapTS = CG_RegisterTrailSystem( "models/buildables/tesla/zap" );
}
/*
@@ -823,8 +833,12 @@ static void CG_BuildableParticleEffects( centity_t *cent )
if( healthFrac < 0.33f && !CG_IsParticleSystemValid( &cent->buildablePS ) )
{
cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDamagedPS );
- CG_SetParticleSystemCent( cent->buildablePS, cent );
- CG_AttachParticleSystemToCent( cent->buildablePS );
+
+ if( CG_IsParticleSystemValid( &cent->buildablePS ) )
+ {
+ CG_SetAttachmentCent( &cent->buildablePS->attachment, cent );
+ CG_AttachToCent( &cent->buildablePS->attachment );
+ }
}
else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( &cent->buildablePS ) )
CG_DestroyParticleSystem( &cent->buildablePS );
@@ -834,9 +848,13 @@ static void CG_BuildableParticleEffects( centity_t *cent )
if( healthFrac < 0.33f && !CG_IsParticleSystemValid( &cent->buildablePS ) )
{
cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDamagedPS );
- CG_SetParticleSystemCent( cent->buildablePS, cent );
- CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 );
- CG_AttachParticleSystemToCent( cent->buildablePS );
+
+ if( CG_IsParticleSystemValid( &cent->buildablePS ) )
+ {
+ CG_SetAttachmentCent( &cent->buildablePS->attachment, cent );
+ CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 );
+ CG_AttachToCent( &cent->buildablePS->attachment );
+ }
}
else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( &cent->buildablePS ) )
CG_DestroyParticleSystem( &cent->buildablePS );
diff --git a/src/cgame/cg_consolecmds.c b/src/cgame/cg_consolecmds.c
index bf15145c..c875fa48 100644
--- a/src/cgame/cg_consolecmds.c
+++ b/src/cgame/cg_consolecmds.c
@@ -171,6 +171,10 @@ static consoleCommand_t commands[ ] =
{ "tell_target", CG_TellTarget_f },
{ "tell_attacker", CG_TellAttacker_f },
{ "tcmd", CG_TargetCommand_f },
+ { "testPS", CG_TestPS_f },
+ { "destroyTestPS", CG_DestroyTestPS_f },
+ { "testTS", CG_TestTS_f },
+ { "destroyTestTS", CG_DestroyTestTS_f },
};
diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c
index c4adf46b..8c047e27 100644
--- a/src/cgame/cg_ents.c
+++ b/src/cgame/cg_ents.c
@@ -240,6 +240,8 @@ static void CG_EntityEffects( centity_t *cent )
trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
}
+ if( cg.time > cent->muzzleTSDeathTime && CG_IsTrailSystemValid( &cent->muzzleTS ) )
+ CG_DestroyTrailSystem( &cent->muzzleTS );
}
@@ -320,6 +322,7 @@ static void CG_LaunchMissile( centity_t *cent )
entityState_t *es;
const weaponInfo_t *wi;
particleSystem_t *ps;
+ trailSystem_t *ts;
weapon_t weapon;
weaponMode_t weaponMode;
@@ -335,8 +338,23 @@ static void CG_LaunchMissile( centity_t *cent )
if( wi->wim[ weaponMode ].missileParticleSystem )
{
ps = CG_SpawnNewParticleSystem( wi->wim[ weaponMode ].missileParticleSystem );
- CG_SetParticleSystemCent( ps, cent );
- CG_AttachParticleSystemToCent( ps );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ CG_AttachToCent( &ps->attachment );
+ }
+ }
+
+ if( wi->wim[ weaponMode ].missileTrailSystem )
+ {
+ ts = CG_SpawnNewTrailSystem( wi->wim[ weaponMode ].missileTrailSystem );
+
+ if( CG_IsTrailSystemValid( &ts ) )
+ {
+ CG_SetAttachmentCent( &ts->frontAttachment, cent );
+ CG_AttachToCent( &ts->frontAttachment );
+ }
}
}
@@ -763,54 +781,51 @@ CG_Lev2ZapChain
*/
static void CG_Lev2ZapChain( centity_t *cent )
{
- int i = 0;
+ int i;
entityState_t *es;
- vec3_t start, end;
centity_t *source, *target;
es = &cent->currentState;
- if( es->time > 0 )
+ for( i = 0; i <= 2; i++ )
{
- source = &cg_entities[ es->powerups ];
- target = &cg_entities[ es->time ];
-
- if( es->powerups == cg.predictedPlayerState.clientNum )
- VectorCopy( cg.predictedPlayerState.origin, start );
- else
- VectorCopy( source->currentState.pos.trBase, start );
-
- VectorCopy( target->currentState.pos.trBase, end );
+ switch( i )
+ {
+ case 0:
+ if( es->time <= 0 )
+ continue;
- CG_DynamicLightningBolt( cgs.media.lightningShader, start, end,
- 1+((cg.time%((i+2)*(i+3)))+i)%2, 7 + (float)(i%3)*5 + 6.0*random(),
- qtrue, 1.0, 0, i*i*3 );
- }
+ source = &cg_entities[ es->powerups ];
+ target = &cg_entities[ es->time ];
+ break;
- if( es->time2 > 0 )
- {
- source = &cg_entities[ es->time ];
- target = &cg_entities[ es->time2 ];
+ case 1:
+ if( es->time2 <= 0 )
+ continue;
- VectorCopy( source->currentState.pos.trBase, start );
- VectorCopy( target->currentState.pos.trBase, end );
+ source = &cg_entities[ es->time ];
+ target = &cg_entities[ es->time2 ];
+ break;
- CG_DynamicLightningBolt( cgs.media.lightningShader, start, end,
- 1+((cg.time%((i+2)*(i+3)))+i)%2, 7 + (float)(i%3)*5 + 6.0*random(),
- qtrue, 1.0, 0, i*i*3 );
- }
+ case 2:
+ if( es->constantLight <= 0 )
+ continue;
- if( es->constantLight > 0 )
- {
- source = &cg_entities[ es->time2 ];
- target = &cg_entities[ es->constantLight ];
+ source = &cg_entities[ es->time2 ];
+ target = &cg_entities[ es->constantLight ];
+ break;
+ }
- VectorCopy( source->currentState.pos.trBase, start );
- VectorCopy( target->currentState.pos.trBase, end );
+ if( !CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
+ cent->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
- CG_DynamicLightningBolt( cgs.media.lightningShader, start, end,
- 1+((cg.time%((i+2)*(i+3)))+i)%2, 7 + (float)(i%3)*5 + 6.0*random(),
- qtrue, 1.0, 0, i*i*3 );
+ if( CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
+ {
+ CG_SetAttachmentCent( &cent->level2ZapTS[ i ]->frontAttachment, source );
+ CG_SetAttachmentCent( &cent->level2ZapTS[ i ]->backAttachment, target );
+ CG_AttachToCent( &cent->level2ZapTS[ i ]->frontAttachment );
+ CG_AttachToCent( &cent->level2ZapTS[ i ]->backAttachment );
+ }
}
}
@@ -983,8 +998,22 @@ CG_CEntityPVSLeave
*/
static void CG_CEntityPVSLeave( centity_t *cent )
{
+ int i;
+ entityState_t *es = &cent->currentState;
+
if( cg_debugPVS.integer )
CG_Printf( "Entity %d left PVS\n", cent->currentState.number );
+
+ switch( es->eType )
+ {
+ case ET_LEV2_ZAP_CHAIN:
+ for( i = 0; i <= 2; i++ )
+ {
+ if( CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
+ CG_DestroyTrailSystem( &cent->level2ZapTS[ i ] );
+ }
+ break;
+ }
}
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index 4d176096..9273922f 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -740,7 +740,29 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
case EV_TESLATRAIL:
DEBUGNAME( "EV_TESLATRAIL" );
cent->currentState.weapon = WP_TESLAGEN;
- CG_TeslaTrail( es->origin2, es->pos.trBase, es->generic1, es->clientNum );
+ {
+ centity_t *source = &cg_entities[ es->generic1 ];
+ centity_t *target = &cg_entities[ es->clientNum ];
+ vec3_t sourceOffset = { 0.0f, 0.0f, 28.0f };
+ vec3_t targetOffset = { 0.0f, 0.0f, -2.0f };
+
+ if( !CG_IsTrailSystemValid( &source->muzzleTS ) )
+ {
+ source->muzzleTS = CG_SpawnNewTrailSystem( cgs.media.teslaZapTS );
+
+ if( CG_IsTrailSystemValid( &source->muzzleTS ) )
+ {
+ CG_SetAttachmentCent( &source->muzzleTS->frontAttachment, source );
+ CG_SetAttachmentCent( &source->muzzleTS->backAttachment, target );
+ CG_AttachToCent( &source->muzzleTS->frontAttachment );
+ CG_AttachToCent( &source->muzzleTS->backAttachment );
+ CG_SetAttachmentOffset( &source->muzzleTS->frontAttachment, sourceOffset );
+ CG_SetAttachmentOffset( &source->muzzleTS->backAttachment, targetOffset );
+
+ source->muzzleTSDeathTime = cg.time + cg_teslaTrailTime.integer;
+ }
+ }
+ }
break;
case EV_BULLET_HIT_WALL:
@@ -880,8 +902,12 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienEvolveSound );
{
particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienEvolvePS );
- CG_SetParticleSystemCent( ps, cent );
- CG_AttachParticleSystemToCent( ps );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ CG_AttachToCent( &ps->attachment );
+ }
}
if( es->number == cg.clientNum )
@@ -902,10 +928,14 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
DEBUGNAME( "EV_ALIEN_ACIDTUBE" );
{
particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienAcidTubePS );
- CG_SetParticleSystemCent( ps, cent );
- ByteToDir( es->eventParm, dir );
- CG_SetParticleSystemNormal( ps, dir );
- CG_AttachParticleSystemToCent( ps );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ ByteToDir( es->eventParm, dir );
+ CG_SetParticleSystemNormal( ps, dir );
+ CG_AttachToCent( &ps->attachment );
+ }
}
break;
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index d6b07714..069dd604 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -71,19 +71,6 @@
#define NUM_CROSSHAIRS 10
-//TA: ripped from wolf source
-// Ridah, trails
-#define STYPE_STRETCH 0
-#define STYPE_REPEAT 1
-
-#define TJFL_FADEIN (1<<0)
-#define TJFL_CROSSOVER (1<<1)
-#define TJFL_NOCULL (1<<2)
-#define TJFL_FIXDISTORT (1<<3)
-#define TJFL_SPARKHEADFLARE (1<<4)
-#define TJFL_NOPOLYMERGE (1<<5)
-// done.
-
#define TEAM_OVERLAY_MAXNAME_WIDTH 12
#define TEAM_OVERLAY_MAXLOCATION_WIDTH 16
@@ -124,6 +111,51 @@ typedef enum
JPS_ASCENDING
} jetPackState_t;
+//======================================================================
+
+//attachment system
+typedef enum
+{
+ AT_STATIC,
+ AT_TAG,
+ AT_CENT,
+ AT_PARTICLE
+} attachmentType_t;
+
+//forward declaration for particle_t
+struct particle_s;
+
+typedef struct attachment_s
+{
+ attachmentType_t type;
+ qboolean attached;
+
+ qboolean staticValid;
+ qboolean tagValid;
+ qboolean centValid;
+ qboolean particleValid;
+
+ qboolean hasOffset;
+ vec3_t offset;
+
+ //AT_STATIC
+ vec3_t origin;
+
+ //AT_TAG
+ refEntity_t re; //FIXME: should be pointers?
+ refEntity_t parent; //
+ qhandle_t model;
+ char tagName[ MAX_STRING_CHARS ];
+
+ //AT_CENT
+ int centNum;
+
+ //AT_PARTICLE
+ struct particle_s *particle;
+} attachment_t;
+
+//======================================================================
+
//particle system stuff
#define MAX_SHADER_FRAMES 32
#define MAX_EJECTORS_PER_SYSTEM 4
@@ -269,51 +301,18 @@ RUN TIME STRUCTURES
===============
*/
-typedef enum
-{
- PSA_STATIC,
- PSA_TAG,
- PSA_CENT_ORIGIN,
- PSA_PARTICLE
-} psAttachmentType_t;
-
-
-typedef struct psAttachment_s
-{
- qboolean staticValid;
- qboolean tagValid;
- qboolean centValid;
- qboolean normalValid;
- qboolean particleValid;
-
- //PMT_STATIC
- vec3_t origin;
-
- //PMT_TAG
- refEntity_t re; //FIXME: should be pointers?
- refEntity_t parent; //
- qhandle_t model;
- char tagName[ MAX_STRING_CHARS ];
-
- //PMT_CENT_ANGLES
- int centNum;
-
- //PMT_NORMAL
- vec3_t normal;
-} psAttachment_t;
-
-
typedef struct particleSystem_s
{
baseParticleSystem_t *class;
- psAttachmentType_t attachType;
- psAttachment_t attachment;
- qboolean attached; //is the particle system attached to anything
+ attachment_t attachment;
qboolean valid;
qboolean lazyRemove; //mark this system for later removal
+ //for PMT_NORMAL
+ qboolean normalValid;
+ vec3_t normal;
} particleSystem_t;
@@ -357,14 +356,133 @@ typedef struct particle_s
pLerpValues_t rotation;
qboolean valid;
+ int frameWhenInvalidated;
int sortKey;
-
- particleSystem_t *childSystem;
} particle_t;
+//======================================================================
-//=================================================
+//trail system stuff
+#define MAX_BEAMS_PER_SYSTEM 4
+
+#define MAX_BASETRAIL_SYSTEMS 64
+#define MAX_BASETRAIL_BEAMS MAX_BASETRAIL_SYSTEMS*MAX_BEAMS_PER_SYSTEM
+
+#define MAX_TRAIL_SYSTEMS 32
+#define MAX_TRAIL_BEAMS MAX_TRAIL_SYSTEMS*MAX_BEAMS_PER_SYSTEM
+#define MAX_TRAIL_BEAM_NODES 128
+
+#define MAX_TRAIL_BEAM_JITTERS 4
+
+typedef enum
+{
+ TBTT_STRETCH,
+ TBTT_REPEAT
+} trailBeamTextureType_t;
+
+typedef struct baseTrailJitter_s
+{
+ float magnitude;
+ int period;
+} baseTrailJitter_t;
+
+//beam template
+typedef struct baseTrailBeam_s
+{
+ int numSegments;
+ float frontWidth;
+ float backWidth;
+ float frontAlpha;
+ float backAlpha;
+ byte frontColor[ 3 ];
+ byte backColor[ 3 ];
+
+ // the time it takes for a segment to vanish (single attached only)
+ int segmentTime;
+
+ // the time it takes for a beam to fade out (double attached only)
+ int fadeOutTime;
+
+ char shaderName[ MAX_QPATH ];
+ qhandle_t shader;
+
+ trailBeamTextureType_t textureType;
+
+ //TBTT_STRETCH
+ float frontTextureCoord;
+ float backTextureCoord;
+
+ //TBTT_REPEAT
+ float repeatLength;
+ qboolean clampToBack;
+
+ qboolean realLight;
+
+ int numJitters;
+ baseTrailJitter_t jitters[ MAX_TRAIL_BEAM_JITTERS ];
+ qboolean jitterAttachments;
+} baseTrailBeam_t;
+
+
+//trail system template
+typedef struct baseTrailSystem_s
+{
+ char name[ MAX_QPATH ];
+ baseTrailBeam_t *beams[ MAX_BEAMS_PER_SYSTEM ];
+ int numBeams;
+
+ qboolean registered; //whether or not the assets for this trail have been loaded
+} baseTrailSystem_t;
+
+typedef struct trailSystem_s
+{
+ baseTrailSystem_t *class;
+
+ attachment_t frontAttachment;
+ attachment_t backAttachment;
+
+ int destroyTime;
+ qboolean valid;
+} trailSystem_t;
+
+typedef struct trailBeamNode_s
+{
+ vec3_t refPosition;
+ vec3_t position;
+
+ int timeLeft;
+
+ float textureCoord;
+ float halfWidth;
+ byte alpha;
+ byte color[ 3 ];
+
+ float jitters[ MAX_TRAIL_BEAM_JITTERS ];
+ float jitter;
+
+ struct trailBeamNode_s *prev;
+ struct trailBeamNode_s *next;
+
+ qboolean used;
+} trailBeamNode_t;
+
+typedef struct trailBeam_s
+{
+ baseTrailBeam_t *class;
+ trailSystem_t *parent;
+
+ trailBeamNode_t nodePool[ MAX_TRAIL_BEAM_NODES ];
+ trailBeamNode_t *nodes;
+
+ int lastEvalTime;
+
+ qboolean valid;
+
+ int nextJitterTimes[ MAX_TRAIL_BEAM_JITTERS ];
+} trailBeam_t;
+
+//======================================================================
// player entities need to track more information
// than any other type of entity.
@@ -435,8 +553,6 @@ typedef struct lightFlareStatus_s
//=================================================
-#define MAX_CENTITY_PARTICLE_SYSTEMS 8
-
// centity_t have a direct corespondence with gentity_t in the game, but
// only the entityState_t is directly communicated to the cgame
typedef struct centity_s
@@ -495,6 +611,11 @@ typedef struct centity_s
particleSystem_t *entityPS;
qboolean entityPSMissing;
+ trailSystem_t *level2ZapTS[ 3 ];
+
+ trailSystem_t *muzzleTS; //used for the tesla and reactor
+ int muzzleTSDeathTime;
+
qboolean valid;
qboolean oldValid;
} centity_t;
@@ -521,7 +642,6 @@ typedef enum
{
LE_MARK,
LE_EXPLOSION,
- LE_LIGHTNING_BOLT, //wolf trail
LE_SPRITE_EXPLOSION,
LE_FRAGMENT,
LE_MOVE_SCALE_FADE,
@@ -700,6 +820,7 @@ typedef struct weaponInfoMode_s
qhandle_t missileSprite;
int missileSpriteSize;
qhandle_t missileParticleSystem;
+ qhandle_t missileTrailSystem;
qboolean missileRotates;
qboolean missileAnimates;
int missileAnimStartFrame;
@@ -1086,12 +1207,10 @@ typedef struct
qhandle_t gibSpark1;
qhandle_t gibSpark2;
- qhandle_t smoke2;
-
qhandle_t machinegunBrassModel;
qhandle_t shotgunBrassModel;
- qhandle_t lightningShader;
+ qhandle_t level2ZapTS;
qhandle_t friendShader;
@@ -1148,7 +1267,6 @@ typedef struct
qhandle_t bulletFlashModel;
qhandle_t ringFlashModel;
qhandle_t dishFlashModel;
- qhandle_t lightningExplosionModel;
// weapon effect shaders
qhandle_t bloodExplosionShader;
@@ -1216,9 +1334,6 @@ typedef struct
qhandle_t selectCursor;
qhandle_t sizeCursor;
- //TA: for wolf trail effects
- qhandle_t sparkFlareShader;
-
//light armour
qhandle_t larmourHeadSkin;
qhandle_t larmourLegsSkin;
@@ -1243,6 +1358,7 @@ typedef struct
qhandle_t humanBuildableDestroyedPS;
qhandle_t alienBuildableDamagedPS;
qhandle_t alienBuildableDestroyedPS;
+ qhandle_t teslaZapTS;
sfxHandle_t lCannonWarningSound;
@@ -1463,6 +1579,7 @@ extern vmCvar_t cg_depthSortParticles;
extern vmCvar_t cg_consoleLatency;
extern vmCvar_t cg_lightFlare;
extern vmCvar_t cg_debugParticles;
+extern vmCvar_t cg_debugTrails;
extern vmCvar_t cg_debugPVS;
extern vmCvar_t cg_disableWarningDialogs;
extern vmCvar_t cg_disableScannerPlane;
@@ -1659,7 +1776,6 @@ void CG_MissileHitPlayer( weapon_t weapon, weaponMode_t weaponMode, vec3_
void CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum );
void CG_ShotgunFire( entityState_t *es );
-void CG_TeslaTrail( vec3_t start, vec3_t end, int srcENum, int destENum );
void CG_AddViewWeapon (playerState_t *ps);
void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent );
void CG_DrawItemSelect( rectDef_t *rect, vec4_t color );
@@ -1707,7 +1823,6 @@ localEntity_t *CG_SmokePuff( const vec3_t p,
void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing );
void CG_SpawnEffect( vec3_t org );
void CG_GibPlayer( vec3_t playerOrigin );
-void CG_BigExplode( vec3_t playerOrigin );
void CG_Bleed( vec3_t origin, int entityNum );
@@ -1715,30 +1830,6 @@ localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir,
qhandle_t hModel, qhandle_t shader, int msec,
qboolean isSprite );
-//TA: wolf tesla effect
-void CG_DynamicLightningBolt( qhandle_t shader, vec3_t start, vec3_t pend,
- int numBolts, float maxWidth, qboolean fade,
- float startAlpha, int recursion, int randseed );
-
-// Ridah, trails
-//
-// cg_trails.c
-//
-int CG_AddTrailJunc( int headJuncIndex, qhandle_t shader, int spawnTime,
- int sType, vec3_t pos, int trailLife, float alphaStart,
- float alphaEnd, float startWidth, float endWidth, int flags,
- vec3_t colorStart, vec3_t colorEnd, float sRatio, float animSpeed );
-int CG_AddSparkJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife,
- float alphaStart, float alphaEnd, float startWidth, float endWidth );
-int CG_AddSmokeJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife,
- float alpha, float startWidth, float endWidth );
-int CG_AddFireJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife,
- float alpha, float startWidth, float endWidth );
-void CG_AddTrails( void );
-void CG_ClearTrails( void );
-// done.
-
-
//
// cg_snapshot.c
//
@@ -1774,6 +1865,28 @@ void CG_Free( void *ptr );
void CG_DefragmentMemory( void );
//
+// cg_attachment.c
+//
+qboolean CG_AttachmentPoint( attachment_t *a, vec3_t v );
+qboolean CG_AttachmentDir( attachment_t *a, vec3_t v );
+qboolean CG_AttachmentVelocity( attachment_t *a, vec3_t v );
+int CG_AttachmentCentNum( attachment_t *a );
+
+qboolean CG_Attached( attachment_t *a );
+
+void CG_AttachToPoint( attachment_t *a );
+void CG_AttachToCent( attachment_t *a );
+void CG_AttachToTag( attachment_t *a );
+void CG_AttachToParticle( attachment_t *a );
+void CG_SetAttachmentPoint( attachment_t *a, vec3_t v );
+void CG_SetAttachmentCent( attachment_t *a, centity_t *cent );
+void CG_SetAttachmentTag( attachment_t *a, refEntity_t parent,
+ qhandle_t model, char *tagName );
+void CG_SetAttachmentParticle( attachment_t *a, particle_t *p );
+
+void CG_SetAttachmentOffset( attachment_t *a, vec3_t v );
+
+//
// cg_particles.c
//
void CG_LoadParticleSystems( void );
@@ -1785,20 +1898,31 @@ void CG_DestroyParticleSystem( particleSystem_t **ps );
qboolean CG_IsParticleSystemInfinite( particleSystem_t *ps );
qboolean CG_IsParticleSystemValid( particleSystem_t **ps );
-void CG_SetParticleSystemCent( particleSystem_t *ps, centity_t *cent );
-void CG_AttachParticleSystemToCent( particleSystem_t *ps );
-void CG_SetParticleSystemTag( particleSystem_t *ps, refEntity_t parent, qhandle_t model, char *tagName );
-void CG_AttachParticleSystemToTag( particleSystem_t *ps );
-void CG_SetParticleSystemOrigin( particleSystem_t *ps, vec3_t origin );
-void CG_AttachParticleSystemToOrigin( particleSystem_t *ps );
void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal );
-void CG_AttachParticleSystemToParticle( particleSystem_t *ps );
-void CG_SetParticleSystemParentParticle( particleSystem_t *ps, particle_t *p );
void CG_AddParticles( void );
void CG_ParticleSystemEntity( centity_t *cent );
+void CG_TestPS_f( void );
+void CG_DestroyTestPS_f( void );
+
+//
+// cg_trails.c
+//
+void CG_LoadTrailSystems( void );
+qhandle_t CG_RegisterTrailSystem( char *name );
+
+trailSystem_t *CG_SpawnNewTrailSystem( qhandle_t psHandle );
+void CG_DestroyTrailSystem( trailSystem_t **ts );
+
+qboolean CG_IsTrailSystemValid( trailSystem_t **ts );
+
+void CG_AddTrails( void );
+
+void CG_TestTS_f( void );
+void CG_DestroyTestTS_f( void );
+
//
// cg_ptr.c
//
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index a5db719c..663ace40 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -199,6 +199,7 @@ vmCvar_t cg_depthSortParticles;
vmCvar_t cg_consoleLatency;
vmCvar_t cg_lightFlare;
vmCvar_t cg_debugParticles;
+vmCvar_t cg_debugTrails;
vmCvar_t cg_debugPVS;
vmCvar_t cg_disableWarningDialogs;
vmCvar_t cg_disableScannerPlane;
@@ -304,6 +305,7 @@ static cvarTable_t cvarTable[ ] =
{ &cg_consoleLatency, "cg_consoleLatency", "3000", CVAR_ARCHIVE },
{ &cg_lightFlare, "cg_lightFlare", "3", CVAR_ARCHIVE },
{ &cg_debugParticles, "cg_debugParticles", "0", CVAR_CHEAT },
+ { &cg_debugTrails, "cg_debugTrails", "0", CVAR_CHEAT },
{ &cg_debugPVS, "cg_debugPVS", "0", CVAR_CHEAT },
{ &cg_disableWarningDialogs, "cg_disableWarningDialogs", "0", CVAR_ARCHIVE },
{ &cg_disableScannerPlane, "cg_disableScannerPlane", "0", CVAR_ARCHIVE },
@@ -794,8 +796,8 @@ static void CG_RegisterGraphics( void )
cgs.media.upgradeClassIconShader = trap_R_RegisterShader( "icons/icona_upgrade.tga" );
- cgs.media.machinegunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/m_shell.md3" );
- cgs.media.shotgunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" );
+ cgs.media.machinegunBrassModel = trap_R_RegisterModel( "models/weapons/shells/rifle_shell.md3" );
+ cgs.media.shotgunBrassModel = trap_R_RegisterModel( "models/weapons/shells/shotgun_shell.md3" );
cgs.media.gibAbdomen = trap_R_RegisterModel( "models/gibs/abdomen.md3" );
cgs.media.gibArm = trap_R_RegisterModel( "models/gibs/arm.md3" );
@@ -825,8 +827,6 @@ static void CG_RegisterGraphics( void )
cgs.media.alienGib3 = trap_R_RegisterModel( "models/fx/alien_gibs/a_gib3.md3" );
cgs.media.alienGib4 = trap_R_RegisterModel( "models/fx/alien_gibs/a_gib4.md3" );
- cgs.media.smoke2 = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" );
-
cgs.media.balloonShader = trap_R_RegisterShader( "sprites/balloon3" );
cgs.media.bloodExplosionShader = trap_R_RegisterShader( "bloodExplosion" );
@@ -1803,6 +1803,9 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
CG_LoadParticleSystems( );
CG_UpdateMediaFraction( 0.05f );
+ CG_LoadTrailSystems( );
+ CG_UpdateMediaFraction( 0.05f );
+
CG_RegisterSounds( );
CG_UpdateMediaFraction( 0.60f );
diff --git a/src/cgame/cg_particles.c b/src/cgame/cg_particles.c
index b6a52d90..a6ef99e5 100644
--- a/src/cgame/cg_particles.c
+++ b/src/cgame/cg_particles.c
@@ -25,7 +25,8 @@ static int numBaseParticles = 0;
static particleSystem_t particleSystems[ MAX_PARTICLE_SYSTEMS ];
static particleEjector_t particleEjectors[ MAX_PARTICLE_EJECTORS ];
static particle_t particles[ MAX_PARTICLES ];
-static particle_t sortParticles[ MAX_PARTICLES ];
+static particle_t *sortedParticles[ MAX_PARTICLES ];
+static particle_t *radixBuffer[ MAX_PARTICLES ];
/*
===============
@@ -85,7 +86,7 @@ CG_DestroyParticle
Destroy an individual particle
===============
*/
-static void CG_DestroyParticle( particle_t *p )
+static void CG_DestroyParticle( particle_t *p, vec3_t impactNormal )
{
//this particle has an onDeath particle system attached
if( p->class->onDeathSystemName[ 0 ] != '\0' )
@@ -96,13 +97,19 @@ static void CG_DestroyParticle( particle_t *p )
if( CG_IsParticleSystemValid( &ps ) )
{
- CG_SetParticleSystemOrigin( ps, p->origin );
- CG_SetParticleSystemNormal( ps, p->velocity );
- CG_AttachParticleSystemToOrigin( ps );
+ if( impactNormal )
+ CG_SetParticleSystemNormal( ps, impactNormal );
+
+ CG_SetAttachmentPoint( &ps->attachment, p->origin );
+ CG_AttachToPoint( &ps->attachment );
}
}
p->valid = qfalse;
+
+ //this gives other systems a couple of
+ //frames to realise the particle is gone
+ p->frameWhenInvalidated = cg.clientFrame;
}
/*
@@ -118,14 +125,14 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
particle_t *p = NULL;
particleEjector_t *pe = parent;
particleSystem_t *ps = parent->parent;
- vec3_t forward;
- centity_t *cent = &cg_entities[ ps->attachment.centNum ];
+ vec3_t attachmentPoint, attachmentVelocity;
for( i = 0; i < MAX_PARTICLES; i++ )
{
p = &particles[ i ];
- if( !p->valid )
+ //FIXME: the + 1 may be unnecessary
+ if( !p->valid && cg.clientFrame > p->frameWhenInvalidated + 1 )
{
memset( p, 0, sizeof( particle_t ) );
@@ -148,59 +155,10 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
p->rotation.initial = CG_RandomiseValue( bp->rotation.initial, bp->rotation.initialRandFrac );
p->rotation.final = CG_RandomiseValue( bp->rotation.final, bp->rotation.finalRandFrac );
- switch( ps->attachType )
- {
- case PSA_STATIC:
- if( !ps->attachment.staticValid )
- return NULL;
-
- VectorCopy( ps->attachment.origin, p->origin );
- break;
-
- case PSA_TAG:
- if( !ps->attachment.tagValid )
- return NULL;
-
- AxisCopy( axisDefault, ps->attachment.re.axis );
- CG_PositionRotatedEntityOnTag( &ps->attachment.re, &ps->attachment.parent,
- ps->attachment.model, ps->attachment.tagName );
- VectorCopy( ps->attachment.re.origin, p->origin );
- break;
-
- case PSA_CENT_ORIGIN:
- if( !ps->attachment.centValid )
- return NULL;
-
- VectorCopy( cent->lerpOrigin, p->origin );
- break;
-
- case PSA_PARTICLE:
- if( !ps->attachment.particleValid )
- return NULL;
-
- //find a particle which has ps as a child
- for( j = 0; j < MAX_PARTICLES; j++ )
- {
- particle_t *parentParticle = &particles[ j ];
-
- if( parentParticle->valid && parentParticle->childSystem == ps )
- {
- VectorCopy( parentParticle->origin, p->origin );
- break;
- }
- }
-
- if( j == MAX_PARTICLES )
- {
- //didn't find the parent, so it's probably died already
-
- //prevent further (expensive) attempts at particle creation
- ps->attachment.particleValid = qfalse;
- return NULL;
- }
- break;
- }
+ if( !CG_AttachmentPoint( &ps->attachment, attachmentPoint ) )
+ return NULL;
+ VectorCopy( attachmentPoint, p->origin );
VectorAdd( p->origin, bp->displacement, p->origin );
for( j = 0; j <= 2; j++ )
@@ -209,52 +167,32 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
switch( bp->velMoveType )
{
case PMT_STATIC:
-
if( bp->velMoveValues.dirType == PMD_POINT )
VectorSubtract( bp->velMoveValues.point, p->origin, p->velocity );
else if( bp->velMoveValues.dirType == PMD_LINEAR )
VectorCopy( bp->velMoveValues.dir, p->velocity );
-
break;
case PMT_TAG:
-
- if( !ps->attachment.tagValid )
- return NULL;
-
- if( bp->velMoveValues.dirType == PMD_POINT )
- VectorSubtract( ps->attachment.re.origin, p->origin, p->velocity );
- else if( bp->velMoveValues.dirType == PMD_LINEAR )
- VectorCopy( ps->attachment.re.axis[ 0 ], p->velocity );
-
- break;
-
case PMT_CENT_ANGLES:
-
- if( !ps->attachment.centValid )
- return NULL;
-
if( bp->velMoveValues.dirType == PMD_POINT )
- VectorSubtract( cent->lerpOrigin, p->origin, p->velocity );
+ VectorSubtract( attachmentPoint, p->origin, p->velocity );
else if( bp->velMoveValues.dirType == PMD_LINEAR )
{
- AngleVectors( cent->lerpAngles, forward, NULL, NULL );
- VectorCopy( forward, p->velocity );
+ if( !CG_AttachmentDir( &ps->attachment, p->velocity ) )
+ return NULL;
}
-
break;
case PMT_NORMAL:
-
- if( !ps->attachment.normalValid )
+ if( !ps->normalValid )
return NULL;
- VectorCopy( ps->attachment.normal, p->velocity );
+ VectorCopy( ps->normal, p->velocity );
//normal displacement
VectorNormalize( p->velocity );
VectorMA( p->origin, bp->normalDisplacement, p->velocity, p->origin );
-
break;
}
@@ -264,11 +202,11 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
CG_RandomiseValue( bp->velMoveValues.mag, bp->velMoveValues.magRandFrac ),
p->velocity );
- if( ps->attachment.centValid )
+ if( CG_AttachmentVelocity( &ps->attachment, attachmentVelocity ) )
{
VectorMA( p->velocity,
- CG_RandomiseValue( bp->velMoveValues.parentVelFrac, bp->velMoveValues.parentVelFracRandFrac ),
- cent->currentState.pos.trDelta, p->velocity );
+ CG_RandomiseValue( bp->velMoveValues.parentVelFrac,
+ bp->velMoveValues.parentVelFracRandFrac ), attachmentVelocity, p->velocity );
}
p->lastEvalTime = cg.time;
@@ -278,15 +216,12 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
//this particle has a child particle system attached
if( bp->childSystemName[ 0 ] != '\0' )
{
- particleSystem_t *ps;
-
- ps = CG_SpawnNewParticleSystem( bp->childSystemHandle );
+ particleSystem_t *ps = CG_SpawnNewParticleSystem( bp->childSystemHandle );
if( CG_IsParticleSystemValid( &ps ) )
{
- CG_SetParticleSystemParentParticle( ps, p );
- CG_SetParticleSystemNormal( ps, p->velocity );
- CG_AttachParticleSystemToParticle( ps );
+ CG_SetAttachmentParticle( &ps->attachment, p );
+ CG_AttachToParticle( &ps->attachment );
}
}
@@ -324,7 +259,7 @@ static void CG_SpawnNewParticles( void )
if( pe->valid )
{
//a non attached particle system can't make particles
- if( !ps->attached )
+ if( !CG_Attached( &ps->attachment ) )
continue;
bpe = particleEjectors[ i ].class;
@@ -528,60 +463,13 @@ qhandle_t CG_RegisterParticleSystem( char *name )
}
}
- CG_Printf( S_COLOR_RED "ERROR: failed to load particle system %s\n", name );
+ CG_Printf( S_COLOR_RED "ERROR: failed to register particle system %s\n", name );
return 0;
}
/*
===============
-atof_neg
-
-atof with an allowance for negative values
-===============
-*/
-static float atof_neg( char *token, qboolean allowNegative )
-{
- float value;
-
- value = atof( token );
-
- if( !allowNegative && value < 0.0f )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: negative value %f is now allowed here, "
- "replaced with 1.0f\n", value );
- value = 1.0f;
- }
-
- return value;
-}
-
-/*
-===============
-atoi_neg
-
-atoi with an allowance for negative values
-===============
-*/
-static int atoi_neg( char *token, qboolean allowNegative )
-{
- int value;
-
- value = atoi( token );
-
- if( !allowNegative && value < 0 )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: negative value %d is now allowed here, "
- "replaced with 1\n", value );
- value = 1;
- }
-
- return value;
-}
-
-
-/*
-===============
CG_ParseValueAndVariance
Parse a value and its random variance
@@ -1275,7 +1163,8 @@ static qboolean CG_ParseParticleSystem( baseParticleSystem_t *bps, char **text_p
}
else if( numBaseParticleEjectors == MAX_BASEPARTICLE_EJECTORS )
{
- CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n", MAX_BASEPARTICLE_EJECTORS );
+ CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n",
+ MAX_BASEPARTICLE_EJECTORS );
return qfalse;
}
else
@@ -1347,11 +1236,8 @@ static qboolean CG_ParseParticleFile( const char *fileName )
{
token = COM_Parse( &text_p );
- if( !token )
- break;
-
if( !Q_stricmp( token, "" ) )
- return qfalse;
+ break;
if( !Q_stricmp( token, "{" ) )
{
@@ -1380,7 +1266,8 @@ static qboolean CG_ParseParticleFile( const char *fileName )
if( numBaseParticleSystems == MAX_BASEPARTICLE_SYSTEMS )
{
- CG_Printf( S_COLOR_RED "ERROR: maximum number of particle systems (%d) reached\n", MAX_BASEPARTICLE_EJECTORS );
+ CG_Printf( S_COLOR_RED "ERROR: maximum number of particle systems (%d) reached\n",
+ MAX_BASEPARTICLE_SYSTEMS );
return qfalse;
}
else
@@ -1518,167 +1405,9 @@ void CG_LoadParticleSystems( void )
}
}
-
-/*
-===============
-CG_AttachParticleSystemToCent
-
-Attach a particle system to a centity_t
-===============
-*/
-void CG_AttachParticleSystemToCent( particleSystem_t *ps )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachType = PSA_CENT_ORIGIN;
- ps->attached = qtrue;
-}
-
-/*
-===============
-CG_SetParticleSystemCent
-
-Set a particle system attachment means
-===============
-*/
-void CG_SetParticleSystemCent( particleSystem_t *ps, centity_t *cent )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachment.centValid = qtrue;
- ps->attachment.centNum = cent->currentState.number;
-}
-
-/*
-===============
-CG_AttachParticleSystemToTag
-
-Attach a particle system to a model tag
-===============
-*/
-void CG_AttachParticleSystemToTag( particleSystem_t *ps )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachType = PSA_TAG;
- ps->attached = qtrue;
-}
-
-/*
-===============
-CG_SetParticleSystemToTag
-
-Set a particle system attachment means
-===============
-*/
-void CG_SetParticleSystemTag( particleSystem_t *ps, refEntity_t parent,
- qhandle_t model, char *tagName )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachment.tagValid = qtrue;
- ps->attachment.parent = parent;
- ps->attachment.model = model;
- strncpy( ps->attachment.tagName, tagName, MAX_STRING_CHARS );
-}
-
-/*
-===============
-CG_AttachParticleSystemToOrigin
-
-Attach a particle system to a point in space
-===============
-*/
-void CG_AttachParticleSystemToOrigin( particleSystem_t *ps )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachType = PSA_STATIC;
- ps->attached = qtrue;
-}
-
-/*
-===============
-CG_SetParticleSystemOrigin
-
-Set a particle system attachment means
-===============
-*/
-void CG_SetParticleSystemOrigin( particleSystem_t *ps, vec3_t origin )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachment.staticValid = qtrue;
- VectorCopy( origin, ps->attachment.origin );
-}
-
-/*
-===============
-CG_AttachParticleSystemToParticle
-
-Attach a particle system to a particle
-===============
-*/
-void CG_AttachParticleSystemToParticle( particleSystem_t *ps )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachType = PSA_PARTICLE;
- ps->attached = qtrue;
-}
-
-/*
-===============
-CG_SetParticleSystemParentParticle
-
-Set a particle system attachment means
-===============
-*/
-void CG_SetParticleSystemParentParticle( particleSystem_t *ps, particle_t *p )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->attachment.particleValid = qtrue;
- p->childSystem = ps;
-}
-
/*
===============
CG_SetParticleSystemNormal
-
-Set a particle system attachment means
===============
*/
void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal )
@@ -1689,9 +1418,9 @@ void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal )
return;
}
- ps->attachment.normalValid = qtrue;
- VectorCopy( normal, ps->attachment.normal );
- VectorNormalize( ps->attachment.normal );
+ ps->normalValid = qtrue;
+ VectorCopy( normal, ps->normal );
+ VectorNormalize( ps->normal );
}
@@ -1807,6 +1536,7 @@ static void CG_GarbageCollectParticleSystems( void )
int i, j, count;
particleSystem_t *ps;
particleEjector_t *pe;
+ int centNum;
for( i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
{
@@ -1830,9 +1560,10 @@ static void CG_GarbageCollectParticleSystems( void )
//check systems where the parent cent has left the PVS
//( local player entity is always valid )
- if( ps->attachment.centValid && ps->attachment.centNum != cg.snap->ps.clientNum )
+ if( ( centNum = CG_AttachmentCentNum( &ps->attachment ) ) >= 0 &&
+ centNum != cg.snap->ps.clientNum )
{
- if( !cg_entities[ ps->attachment.centNum ].valid )
+ if( !cg_entities[ centNum ].valid )
ps->lazyRemove = qtrue;
}
@@ -1874,16 +1605,14 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
{
particleSystem_t *ps = p->parent->parent;
baseParticle_t *bp = p->class;
- vec3_t acceleration, forward, newOrigin;
+ vec3_t acceleration, newOrigin;
vec3_t mins, maxs;
float deltaTime, bounce, radius, dot;
trace_t trace;
- centity_t *cent;
switch( bp->accMoveType )
{
case PMT_STATIC:
-
if( bp->accMoveValues.dirType == PMD_POINT )
VectorSubtract( bp->accMoveValues.point, p->origin, acceleration );
else if( bp->accMoveValues.dirType == PMD_LINEAR )
@@ -1892,40 +1621,28 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
break;
case PMT_TAG:
-
- if( !ps->attachment.tagValid )
- return;
-
- if( bp->accMoveValues.dirType == PMD_POINT )
- VectorSubtract( ps->attachment.re.origin, p->origin, acceleration );
- else if( bp->accMoveValues.dirType == PMD_LINEAR )
- VectorCopy( ps->attachment.re.axis[ 0 ], acceleration );
-
- break;
-
case PMT_CENT_ANGLES:
+ if( bp->accMoveValues.dirType == PMD_POINT )
+ {
+ vec3_t point;
- if( !ps->attachment.centValid )
- return;
-
- cent = &cg_entities[ ps->attachment.centNum ];
+ if( !CG_AttachmentPoint( &ps->attachment, point ) )
+ return;
- if( bp->accMoveValues.dirType == PMD_POINT )
- VectorSubtract( cent->lerpOrigin, p->origin, acceleration );
+ VectorSubtract( point, p->origin, acceleration );
+ }
else if( bp->accMoveValues.dirType == PMD_LINEAR )
{
- AngleVectors( cent->lerpAngles, forward, NULL, NULL );
- VectorCopy( forward, acceleration );
+ if( !CG_AttachmentDir( &ps->attachment, acceleration ) )
+ return;
}
-
break;
case PMT_NORMAL:
-
- if( !ps->attachment.normalValid )
+ if( !ps->normalValid )
return;
- VectorCopy( ps->attachment.normal, acceleration );
+ VectorCopy( ps->normal, acceleration );
break;
}
@@ -1974,10 +1691,8 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
VectorMA( p->origin, deltaTime, p->velocity, newOrigin );
p->lastEvalTime = cg.time;
- if( !ps->attachment.centValid )
- CG_Trace( &trace, p->origin, mins, maxs, newOrigin, -1, CONTENTS_SOLID );
- else
- CG_Trace( &trace, p->origin, mins, maxs, newOrigin, ps->attachment.centNum, CONTENTS_SOLID );
+ CG_Trace( &trace, p->origin, mins, maxs, newOrigin,
+ CG_AttachmentCentNum( &ps->attachment ), CONTENTS_SOLID );
//not hit anything or not a collider
if( trace.fraction == 1.0f || bounce == 0.0f )
@@ -1988,9 +1703,14 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
//remove particles that get into a CONTENTS_NODROP brush
if( ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) ||
- ( bp->cullOnStartSolid && trace.startsolid ) || bp->bounceCull )
+ ( bp->cullOnStartSolid && trace.startsolid ) )
{
- CG_DestroyParticle( p );
+ CG_DestroyParticle( p, NULL );
+ return;
+ }
+ else if( bp->bounceCull )
+ {
+ CG_DestroyParticle( p, trace.plane.normal );
return;
}
@@ -2011,7 +1731,7 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
CG_Radix
===============
*/
-static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest )
+static void CG_Radix( int bits, int size, particle_t **source, particle_t **dest )
{
int count[ 256 ];
int index[ 256 ];
@@ -2020,7 +1740,7 @@ static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest )
memset( count, 0, sizeof( count ) );
for( i = 0; i < size; i++ )
- count[ GETKEY( source[ i ].sortKey, bits ) ]++;
+ count[ GETKEY( source[ i ]->sortKey, bits ) ]++;
index[ 0 ] = 0;
@@ -2028,7 +1748,7 @@ static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest )
index[ i ] = index[ i - 1 ] + count[ i - 1 ];
for( i = 0; i < size; i++ )
- dest[ index[ GETKEY( source[ i ].sortKey, bits ) ]++ ] = source[ i ];
+ dest[ index[ GETKEY( source[ i ]->sortKey, bits ) ]++ ] = source[ i ];
}
/*
@@ -2038,7 +1758,7 @@ CG_RadixSort
Radix sort with 4 byte size buckets
===============
*/
-static void CG_RadixSort( particle_t *source, particle_t *temp, int size )
+static void CG_RadixSort( particle_t **source, particle_t **temp, int size )
{
CG_Radix( 0, size, source, temp );
CG_Radix( 8, size, temp, source );
@@ -2059,19 +1779,22 @@ static void CG_CompactAndSortParticles( void )
int numParticles;
vec3_t delta;
+ for( i = 0; i < MAX_PARTICLES; i++ )
+ sortedParticles[ i ] = &particles[ i ];
+
for( i = MAX_PARTICLES - 1; i >= 0; i-- )
{
- if( particles[ i ].valid )
+ if( sortedParticles[ i ]->valid )
{
- while( particles[ j ].valid )
+ //find the first hole
+ while( sortedParticles[ j ]->valid )
j++;
//no more holes
if( j >= i )
break;
- particles[ j ] = particles[ i ];
- memset( &particles[ i ], 0, sizeof( particles[ 0 ] ) );
+ sortedParticles[ j ] = sortedParticles[ i ];
}
}
@@ -2080,18 +1803,19 @@ static void CG_CompactAndSortParticles( void )
//set sort keys
for( i = 0; i < numParticles; i++ )
{
- VectorSubtract( particles[ i ].origin, cg.refdef.vieworg, delta );
- particles[ i ].sortKey = (int)DotProduct( delta, delta );
+ VectorSubtract( sortedParticles[ i ]->origin, cg.refdef.vieworg, delta );
+ sortedParticles[ i ]->sortKey = (int)DotProduct( delta, delta );
}
- CG_RadixSort( particles, sortParticles, numParticles );
+ CG_RadixSort( sortedParticles, radixBuffer, numParticles );
+ //FIXME: wtf?
//reverse order of particles array
for( i = 0; i < numParticles; i++ )
- sortParticles[ i ] = particles[ numParticles - i - 1 ];
+ radixBuffer[ i ] = sortedParticles[ numParticles - i - 1 ];
for( i = 0; i < numParticles; i++ )
- particles[ i ] = sortParticles[ i ];
+ sortedParticles[ i ] = radixBuffer[ i ];
}
/*
@@ -2198,7 +1922,7 @@ void CG_AddParticles( void )
for( i = 0; i < MAX_PARTICLES; i++ )
{
- p = &particles[ i ];
+ p = sortedParticles[ i ];
if( p->valid )
{
@@ -2209,7 +1933,7 @@ void CG_AddParticles( void )
CG_RenderParticle( p );
}
else
- CG_DestroyParticle( p );
+ CG_DestroyParticle( p, NULL );
}
}
@@ -2258,11 +1982,63 @@ void CG_ParticleSystemEntity( centity_t *cent )
if( CG_IsParticleSystemValid( &cent->entityPS ) )
{
- CG_SetParticleSystemOrigin( cent->entityPS, cent->lerpOrigin );
- CG_SetParticleSystemCent( cent->entityPS, cent );
- CG_AttachParticleSystemToOrigin( cent->entityPS );
+ CG_SetAttachmentPoint( &cent->entityPS->attachment, cent->lerpOrigin );
+ CG_SetAttachmentCent( &cent->entityPS->attachment, cent );
+ CG_AttachToPoint( &cent->entityPS->attachment );
}
else
cent->entityPSMissing = qtrue;
}
}
+
+static particleSystem_t *testPS;
+static qhandle_t testPSHandle;
+
+/*
+===============
+CG_DestroyTestPS_f
+
+Destroy the test a particle system
+===============
+*/
+void CG_DestroyTestPS_f( void )
+{
+ if( CG_IsParticleSystemValid( &testPS ) )
+ CG_DestroyParticleSystem( &testPS );
+}
+
+/*
+===============
+CG_TestPS_f
+
+Test a particle system
+===============
+*/
+void CG_TestPS_f( void )
+{
+ vec3_t origin;
+ vec3_t up = { 0.0f, 0.0f, 1.0f };
+ char psName[ MAX_QPATH ];
+
+ if( trap_Argc( ) < 2 )
+ return;
+
+ Q_strncpyz( psName, CG_Argv( 1 ), MAX_QPATH );
+ testPSHandle = CG_RegisterParticleSystem( psName );
+
+ if( testPSHandle )
+ {
+ CG_DestroyTestPS_f( );
+
+ testPS = CG_SpawnNewParticleSystem( testPSHandle );
+
+ VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[ 0 ], origin );
+
+ if( CG_IsParticleSystemValid( &testPS ) )
+ {
+ CG_SetAttachmentPoint( &testPS->attachment, origin );
+ CG_SetParticleSystemNormal( testPS, up );
+ CG_AttachToPoint( &testPS->attachment );
+ }
+ }
+}
diff --git a/src/cgame/cg_players.c b/src/cgame/cg_players.c
index 18a32efc..1ef18f8b 100644
--- a/src/cgame/cg_players.c
+++ b/src/cgame/cg_players.c
@@ -1631,9 +1631,10 @@ static void CG_PlayerUpgrades( centity_t *cent, refEntity_t *torso )
if( CG_IsParticleSystemValid( &cent->jetPackPS ) )
{
- CG_SetParticleSystemTag( cent->jetPackPS, jetpack, jetpack.hModel, "tag_flash" );
- CG_SetParticleSystemCent( cent->jetPackPS, cent );
- CG_AttachParticleSystemToTag( cent->jetPackPS );
+ CG_SetAttachmentTag( &cent->jetPackPS->attachment,
+ jetpack, jetpack.hModel, "tag_flash" );
+ CG_SetAttachmentCent( &cent->jetPackPS->attachment, cent );
+ CG_AttachToTag( &cent->jetPackPS->attachment );
}
}
else if( CG_IsParticleSystemValid( &cent->jetPackPS ) )
diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c
index e2391127..a92fa37e 100644
--- a/src/cgame/cg_servercmds.c
+++ b/src/cgame/cg_servercmds.c
@@ -484,10 +484,6 @@ static void CG_MapRestart( void )
CG_InitLocalEntities( );
CG_InitMarkPolys( );
- // Ridah, trails
- CG_ClearTrails( );
- // done.
-
// make sure the "3 frags left" warnings play again
cg.fraglimitWarnings = 0;
@@ -1048,9 +1044,13 @@ static void CG_ServerCommand( void )
if( !strcmp( cmd, "poisoncloud" ) )
{
cg.poisonedTime = cg.time;
- cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS );
- CG_SetParticleSystemCent( cg.poisonCloudPS, &cg.predictedPlayerEntity );
- CG_AttachParticleSystemToCent( cg.poisonCloudPS );
+
+ if( CG_IsParticleSystemValid( &cg.poisonCloudPS ) )
+ {
+ cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS );
+ CG_SetAttachmentCent( &cg.poisonCloudPS->attachment, &cg.predictedPlayerEntity );
+ CG_AttachToCent( &cg.poisonCloudPS->attachment );
+ }
return;
}
diff --git a/src/cgame/cg_trails.c b/src/cgame/cg_trails.c
index 30347e61..e80225e4 100644
--- a/src/cgame/cg_trails.c
+++ b/src/cgame/cg_trails.c
@@ -1,816 +1,1430 @@
-// Ridah, cg_trails.c - draws a trail using multiple junction points
+// cg_trails.c -- the trail system
-#include "cg_local.h"
-
-typedef struct trailJunc_s
-{
- struct trailJunc_s *nextGlobal, *prevGlobal; // next junction in the global list it is in (free or used)
- struct trailJunc_s *nextJunc; // next junction in the trail
- struct trailJunc_s *nextHead, *prevHead; // next head junc in the world
-
- qboolean inuse, freed;
- int ownerIndex;
- qhandle_t shader;
-
- int sType;
- int flags;
- float sTex;
- vec3_t pos;
- int spawnTime, endTime;
- float alphaStart, alphaEnd;
- vec3_t colorStart, colorEnd;
- float widthStart, widthEnd;
-
- // current settings
- float alpha;
- float width;
- vec3_t color;
-
-} trailJunc_t;
-
-#define MAX_TRAILJUNCS 4096
+/*
+ * Portions Copyright (C) 2000-2001 Tim Angus
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the OSML - Open Source Modification License v1.0 as
+ * described in the file COPYING which is distributed with this source
+ * code.
+ *
+ * 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.
+ */
-trailJunc_t trailJuncs[ MAX_TRAILJUNCS ];
-trailJunc_t *freeTrails, *activeTrails;
-trailJunc_t *headTrails;
+#include "cg_local.h"
-qboolean initTrails = qfalse;
+static baseTrailSystem_t baseTrailSystems[ MAX_BASETRAIL_SYSTEMS ];
+static baseTrailBeam_t baseTrailBeams[ MAX_BASETRAIL_BEAMS ];
+static int numBaseTrailSystems = 0;
+static int numBaseTrailBeams = 0;
-int numTrailsInuse;
+static trailSystem_t trailSystems[ MAX_TRAIL_SYSTEMS ];
+static trailBeam_t trailBeams[ MAX_TRAIL_BEAMS ];
/*
===============
-CG_ClearTrails
+CG_CalculateBeamTextureCoordinates
+
+Fills in trailBeamNode_t.textureCoord
===============
*/
-void CG_ClearTrails( void )
+static void CG_CalculateBeamTextureCoordinates( trailBeam_t *tb )
{
- int i;
-
- memset( trailJuncs, 0, sizeof( trailJunc_t ) * MAX_TRAILJUNCS );
+ trailBeamNode_t *i = NULL;
+ trailSystem_t *ts;
+ baseTrailBeam_t *btb;
+ float nodeDistances[ MAX_TRAIL_BEAM_NODES ];
+ float totalDistance = 0.0f, position = 0.0f;
+ int j, numNodes = 0;
+ float TCRange, widthRange, alphaRange;
+ vec3_t colorRange;
+ float fadeAlpha = 1.0f;
+
+ if( !tb || !tb->nodes )
+ return;
- freeTrails = trailJuncs;
- activeTrails = NULL;
- headTrails = NULL;
+ ts = tb->parent;
+ btb = tb->class;
- for( i = 0; i < MAX_TRAILJUNCS; i++ )
+ if( ts->destroyTime > 0 && btb->fadeOutTime )
{
- trailJuncs[ i ].nextGlobal = &trailJuncs[ i + 1 ];
+ fadeAlpha -= ( cg.time - ts->destroyTime ) / btb->fadeOutTime;
- if( i > 0 )
- trailJuncs[ i ].prevGlobal = &trailJuncs[ i - 1 ];
- else
- trailJuncs[ i ].prevGlobal = NULL;
+ if( fadeAlpha < 0.0f )
+ fadeAlpha = 0.0f;
+ }
- trailJuncs[ i ].inuse = qfalse;
+ TCRange = tb->class->backTextureCoord -
+ tb->class->frontTextureCoord;
+ widthRange = tb->class->backWidth -
+ tb->class->frontWidth;
+ alphaRange = tb->class->backAlpha -
+ tb->class->frontAlpha;
+ VectorSubtract( tb->class->backColor,
+ tb->class->frontColor, colorRange );
+
+ for( i = tb->nodes; i && i->next; i = i->next )
+ {
+ nodeDistances[ numNodes++ ] =
+ Distance( i->position, i->next->position );
}
- trailJuncs[ MAX_TRAILJUNCS - 1 ].nextGlobal = NULL;
+ for( j = 0; j < numNodes; j++ )
+ totalDistance += nodeDistances[ j ];
- initTrails = qtrue;
- numTrailsInuse = 0;
+ for( j = 0, i = tb->nodes; i; i = i->next, j++ )
+ {
+ if( tb->class->textureType == TBTT_STRETCH )
+ {
+ i->textureCoord = tb->class->frontTextureCoord +
+ ( ( position / totalDistance ) * TCRange );
+ }
+ else if( tb->class->textureType == TBTT_REPEAT )
+ {
+ if( tb->class->clampToBack )
+ i->textureCoord = ( totalDistance - position ) /
+ tb->class->repeatLength;
+ else
+ i->textureCoord = position / tb->class->repeatLength;
+ }
+
+ i->halfWidth = ( tb->class->frontWidth +
+ ( ( position / totalDistance ) * widthRange ) ) / 2.0f;
+ i->alpha = (byte)( (float)0xFF * ( tb->class->frontAlpha +
+ ( ( position / totalDistance ) * alphaRange ) ) * fadeAlpha );
+ VectorMA( tb->class->frontColor, ( position / totalDistance ),
+ colorRange, i->color );
+
+ position += nodeDistances[ j ];
+ }
}
/*
===============
-CG_SpawnTrailJunc
+CG_LightVertex
+
+Lights a particular vertex
===============
*/
-trailJunc_t *CG_SpawnTrailJunc( trailJunc_t *headJunc )
+static void CG_LightVertex( vec3_t point, byte alpha, byte *rgba )
{
- trailJunc_t *j;
+ int i;
+ vec3_t alight, dlight, lightdir;
- if( !freeTrails )
- return NULL;
+ trap_R_LightForPoint( point, alight, dlight, lightdir );
+ for( i = 0; i <= 2; i++ )
+ rgba[ i ] = (int)alight[ i ];
- if( cg_paused.integer )
- return NULL;
+ rgba[ 3 ] = alpha;
+}
- // select the first free trail, and remove it from the list
- j = freeTrails;
- freeTrails = j->nextGlobal;
+/*
+===============
+CG_RenderBeam
- if( freeTrails )
- freeTrails->prevGlobal = NULL;
+Renders a beam
+===============
+*/
+static void CG_RenderBeam( trailBeam_t *tb )
+{
+ trailBeamNode_t *i = NULL;
+ trailBeamNode_t *prev = NULL;
+ trailBeamNode_t *next = NULL;
+ vec3_t up;
+ polyVert_t verts[ ( MAX_TRAIL_BEAM_NODES - 1 ) * 4 ];
+ int numVerts = 0;
+ baseTrailBeam_t *btb;
+
+ if( !tb || !tb->nodes )
+ return;
- j->nextGlobal = activeTrails;
+ btb = tb->class;
- if( activeTrails )
- activeTrails->prevGlobal = j;
+ CG_CalculateBeamTextureCoordinates( tb );
- activeTrails = j;
- j->prevGlobal = NULL;
- j->inuse = qtrue;
- j->freed = qfalse;
+ i = tb->nodes;
- // if this owner has a headJunc, add us to the start
- if( headJunc )
+ do
{
- // remove the headJunc from the list of heads
- if( headJunc == headTrails )
- {
- headTrails = headJunc->nextHead;
+ prev = i->prev;
+ next = i->next;
- if( headTrails )
- headTrails->prevHead = NULL;
+ if( prev && next )
+ {
+ //this node has two neighbours
+ GetPerpendicularViewVector( cg.refdef.vieworg, next->position, prev->position, up );
+ }
+ else if( !prev && next )
+ {
+ //this is the front
+ GetPerpendicularViewVector( cg.refdef.vieworg, next->position, i->position, up );
+ }
+ else if( prev && !next )
+ {
+ //this is the back
+ GetPerpendicularViewVector( cg.refdef.vieworg, i->position, prev->position, up );
}
else
+ break;
+
+ if( prev )
{
- if( headJunc->nextHead )
- headJunc->nextHead->prevHead = headJunc->prevHead;
+ VectorMA( i->position, i->halfWidth + i->jitter, up, verts[ numVerts ].xyz );
+ verts[ numVerts ].st[ 0 ] = i->textureCoord;
+ verts[ numVerts ].st[ 1 ] = 1.0f;
+
+ if( btb->realLight )
+ CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate );
+ else
+ {
+ VectorCopy( i->color, verts[ numVerts ].modulate );
+ verts[ numVerts ].modulate[ 3 ] = i->alpha;
+ }
+
+ numVerts++;
- if( headJunc->prevHead )
- headJunc->prevHead->nextHead = headJunc->nextHead;
+ VectorMA( i->position, -i->halfWidth + i->jitter, up, verts[ numVerts ].xyz );
+ verts[ numVerts ].st[ 0 ] = i->textureCoord;
+ verts[ numVerts ].st[ 1 ] = 0.0f;
+
+ if( btb->realLight )
+ CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate );
+ else
+ {
+ VectorCopy( i->color, verts[ numVerts ].modulate );
+ verts[ numVerts ].modulate[ 3 ] = i->alpha;
+ }
+
+ numVerts++;
}
- headJunc->prevHead = NULL;
- headJunc->nextHead = NULL;
- }
- // make us the headTrail
- if( headTrails )
- headTrails->prevHead = j;
+ if( next )
+ {
+ VectorMA( i->position, -i->halfWidth + i->jitter, up, verts[ numVerts ].xyz );
+ verts[ numVerts ].st[ 0 ] = i->textureCoord;
+ verts[ numVerts ].st[ 1 ] = 0.0f;
- j->nextHead = headTrails;
- j->prevHead = NULL;
- headTrails = j;
+ if( btb->realLight )
+ CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate );
+ else
+ {
+ VectorCopy( i->color, verts[ numVerts ].modulate );
+ verts[ numVerts ].modulate[ 3 ] = i->alpha;
+ }
- j->nextJunc = headJunc; // if headJunc is NULL, then we'll just be the end of the list
+ numVerts++;
- numTrailsInuse++;
+ VectorMA( i->position, i->halfWidth + i->jitter, up, verts[ numVerts ].xyz );
+ verts[ numVerts ].st[ 0 ] = i->textureCoord;
+ verts[ numVerts ].st[ 1 ] = 1.0f;
- // debugging
-// CG_Printf( "NumTrails: %i\n", numTrailsInuse );
+ if( btb->realLight )
+ CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate );
+ else
+ {
+ VectorCopy( i->color, verts[ numVerts ].modulate );
+ verts[ numVerts ].modulate[ 3 ] = i->alpha;
+ }
- return j;
-}
+ numVerts++;
+ }
+
+ i = i->next;
+ } while( i );
+ trap_R_AddPolysToScene( tb->class->shader, 4, &verts[ 0 ], numVerts / 4 );
+}
/*
===============
-CG_AddTrailJunc
-
- returns the index of the trail junction created
+CG_AllocateBeamNode
- Used for generic trails
+Allocates a trailBeamNode_t from a trailBeam_t's nodePool
===============
*/
-int CG_AddTrailJunc( int headJuncIndex, qhandle_t shader, int spawnTime, int sType, vec3_t pos,
- int trailLife, float alphaStart, float alphaEnd, float startWidth,
- float endWidth, int flags, vec3_t colorStart, vec3_t colorEnd,
- float sRatio, float animSpeed )
+static trailBeamNode_t *CG_AllocateBeamNode( trailBeam_t *tb )
{
- trailJunc_t *j, *headJunc;
+ baseTrailBeam_t *btb = tb->class;
+ int i;
+ trailBeamNode_t *tbn;
- if( headJuncIndex > 0 )
+ for( i = 0; i < MAX_TRAIL_BEAM_NODES; i++ )
{
- headJunc = &trailJuncs[ headJuncIndex - 1 ];
-
- if( !headJunc->inuse )
- headJunc = NULL;
+ tbn = &tb->nodePool[ i ];
+ if( !tbn->used )
+ {
+ tbn->timeLeft = btb->segmentTime;
+ tbn->prev = NULL;
+ tbn->next = NULL;
+ tbn->used = qtrue;
+ return tbn;
+ }
}
- else
- headJunc = NULL;
- j = CG_SpawnTrailJunc( headJunc );
+ // no space left
+ return NULL;
+}
- if( !j )
+/*
+===============
+CG_DestroyBeamNode
+
+Removes a node from a beam
+Returns the new head
+===============
+*/
+static trailBeamNode_t *CG_DestroyBeamNode( trailBeamNode_t *tbn )
+{
+ trailBeamNode_t *newHead = NULL;
+
+ if( tbn->prev )
{
-// CG_Printf("couldnt spawn trail junc\n");
- return 0;
- }
+ if( tbn->next )
+ {
+ // node is in the middle
+ tbn->prev->next = tbn->next;
+ tbn->next->prev = tbn->prev;
+ }
+ else // node is at the back
+ tbn->prev->next = NULL;
- if( alphaStart > 1.0 )
- alphaStart = 1.0;
+ // find the new head (shouldn't have changed)
+ newHead = tbn->prev;
- if( alphaStart < 0.0 )
- alphaStart = 0.0;
+ while( newHead->prev )
+ newHead = newHead->prev;
+ }
+ else if( tbn->next )
+ {
+ //node is at the front
+ tbn->next->prev = NULL;
+ newHead = tbn->next;
+ }
- if( alphaEnd > 1.0 )
- alphaEnd = 1.0;
+ tbn->prev = NULL;
+ tbn->next = NULL;
+ tbn->used = qfalse;
- if( alphaEnd < 0.0 )
- alphaEnd = 0.0;
+ return newHead;
+}
- // setup the trail junction
- j->shader = shader;
- j->sType = sType;
- VectorCopy( pos, j->pos );
- j->flags = flags;
+/*
+===============
+CG_FindLastBeamNode
- j->spawnTime = spawnTime;
- j->endTime = spawnTime + trailLife;
+Returns the last beam node in a beam
+===============
+*/
+static trailBeamNode_t *CG_FindLastBeamNode( trailBeam_t *tb )
+{
+ trailBeamNode_t *i = tb->nodes;
- VectorCopy( colorStart, j->colorStart );
- VectorCopy( colorEnd, j->colorEnd );
+ while( i && i->next )
+ i = i->next;
- j->alphaStart = alphaStart;
- j->alphaEnd = alphaEnd;
+ return i;
+}
- j->widthStart = startWidth;
- j->widthEnd = endWidth;
+/*
+===============
+CG_CountBeamNodes
+
+Returns the number of nodes in a beam
+===============
+*/
+static int CG_CountBeamNodes( trailBeam_t *tb )
+{
+ trailBeamNode_t *i = tb->nodes;
+ int numNodes = 0;
- if( sType == STYPE_REPEAT )
+ while( i )
{
- if( headJunc )
- j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / sRatio) / j->widthEnd );
- else
- {
- // FIXME: need a way to specify offset timing
- j->sTex = ( animSpeed * ( 1.0 - ( (float)( cg.time % 1000 ) / 1000.0 ) ) ) / ( sRatio );
-// j->sTex = 0;
- }
+ numNodes++;
+ i = i->next;
}
- return ( (int)( j - trailJuncs ) + 1 );
+ return numNodes;
}
/*
===============
-CG_AddSparkJunc
+CG_PrependBeamNode
- returns the index of the trail junction created
+Prepend a new beam node to the front of a beam
+Returns the new node
===============
*/
-int CG_AddSparkJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife,
- float alphaStart, float alphaEnd, float startWidth, float endWidth )
+static trailBeamNode_t *CG_PrependBeamNode( trailBeam_t *tb )
{
- trailJunc_t *j, *headJunc;
+ trailBeamNode_t *i;
- if( headJuncIndex > 0 )
+ if( tb->nodes )
{
- headJunc = &trailJuncs[ headJuncIndex - 1 ];
+ // prepend another node
+ i = CG_AllocateBeamNode( tb );
- if( !headJunc->inuse )
- headJunc = NULL;
+ if( i )
+ {
+ i->next = tb->nodes;
+ tb->nodes->prev = i;
+ tb->nodes = i;
+ }
}
- else
- headJunc = NULL;
+ else //add first node
+ {
+ i = CG_AllocateBeamNode( tb );
- j = CG_SpawnTrailJunc( headJunc );
+ if( i )
+ tb->nodes = i;
+ }
- if( !j )
- return 0;
+ return i;
+}
- // setup the trail junction
- j->shader = shader;
- j->sType = STYPE_STRETCH;
- VectorCopy( pos, j->pos );
- j->flags = TJFL_NOCULL; // don't worry about fading up close
+/*
+===============
+CG_AppendBeamNode
- j->spawnTime = cg.time;
- j->endTime = cg.time + trailLife;
+Append a new beam node to the back of a beam
+Returns the new node
+===============
+*/
+static trailBeamNode_t *CG_AppendBeamNode( trailBeam_t *tb )
+{
+ trailBeamNode_t *last, *i;
- VectorSet( j->colorStart, 1.0, 0.8 + 0.2 * alphaStart, 0.4 + 0.4 * alphaStart );
- VectorSet( j->colorEnd, 1.0, 0.8 + 0.2 * alphaEnd, 0.4 + 0.4 * alphaEnd );
-// VectorScale( j->colorStart, alphaStart, j->colorStart );
-// VectorScale( j->colorEnd, alphaEnd, j->colorEnd );
+ if( tb->nodes )
+ {
+ // append another node
+ last = CG_FindLastBeamNode( tb );
+ i = CG_AllocateBeamNode( tb );
- j->alphaStart = alphaStart*2;
- j->alphaEnd = alphaEnd*2;
-// j->alphaStart = 1.0;
-// j->alphaEnd = 1.0;
+ if( i )
+ {
+ last->next = i;
+ i->prev = last;
+ i->next = NULL;
+ }
+ }
+ else //add first node
+ {
+ i = CG_AllocateBeamNode( tb );
- j->widthStart = startWidth;
- j->widthEnd = endWidth;
+ if( i )
+ tb->nodes = i;
+ }
- return ( (int)( j - trailJuncs ) + 1 );
+ return i;
}
/*
===============
-CG_AddSmokeJunc
-
- returns the index of the trail junction created
+CG_ApplyJitters
===============
*/
-int CG_AddSmokeJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife,
- float alpha, float startWidth, float endWidth )
+static void CG_ApplyJitters( trailBeam_t *tb )
{
-#define ST_RATIO 4.0 // sprite image: width / height
- trailJunc_t *j, *headJunc;
+ trailBeamNode_t *i = NULL;
+ int j;
+ baseTrailBeam_t *btb;
+ trailSystem_t *ts;
+ trailBeamNode_t *start;
+ trailBeamNode_t *end;
+
+ if( !tb || !tb->nodes )
+ return;
- if( headJuncIndex > 0 )
+ btb = tb->class;
+ ts = tb->parent;
+
+ for( j = 0; j < btb->numJitters; j++ )
{
- headJunc = &trailJuncs[ headJuncIndex - 1 ];
+ if( tb->nextJitterTimes[ j ] <= cg.time )
+ {
+ for( i = tb->nodes; i; i = i->next )
+ i->jitters[ j ] = ( crandom( ) * btb->jitters[ j ].magnitude );
- if( !headJunc->inuse )
- headJunc = NULL;
+ tb->nextJitterTimes[ j ] = cg.time + btb->jitters[ j ].period;
+ }
}
- else
- headJunc = NULL;
- j = CG_SpawnTrailJunc( headJunc );
+ start = tb->nodes;
+ end = CG_FindLastBeamNode( tb );
- if( !j )
- return 0;
+ if( !btb->jitterAttachments )
+ {
+ if( CG_Attached( &ts->frontAttachment ) && start->next )
+ start = start->next;
- // setup the trail junction
- j->shader = shader;
- j->sType = STYPE_REPEAT;
- VectorCopy( pos, j->pos );
- j->flags = TJFL_FADEIN;
+ if( CG_Attached( &ts->backAttachment ) && end->prev )
+ end = end->prev;
+ }
- j->spawnTime = cg.time;
- j->endTime = cg.time + trailLife;
+ for( i = start; i; i = i->next )
+ {
+ i->jitter = 0.0f;
- // VectorSet(j->colorStart, 0.2, 0.2, 0.2);
- VectorSet(j->colorStart, 0.0, 0.0, 0.0);
- // VectorSet(j->colorEnd, 0.1, 0.1, 0.1);
- VectorSet(j->colorEnd, 0.0, 0.0, 0.0);
+ for( j = 0; j < btb->numJitters; j++ )
+ i->jitter += i->jitters[ j ];
- j->alphaStart = alpha;
- j->alphaEnd = 0.0;
+ //mmmm... nice
+ if( i == end )
+ break;
+ }
+}
- j->widthStart = startWidth;
- j->widthEnd = endWidth;
+/*
+===============
+CG_UpdateBeam
- if( headJunc )
- j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / ST_RATIO ) / j->widthEnd );
- else
+Updates a beam
+===============
+*/
+static void CG_UpdateBeam( trailBeam_t *tb )
+{
+ baseTrailBeam_t *btb;
+ trailSystem_t *ts;
+ trailBeamNode_t *i;
+ int deltaTime;
+ int nodesToAdd;
+ int j;
+ int numNodes;
+
+ if( !tb )
+ return;
+
+ btb = tb->class;
+ ts = tb->parent;
+
+ deltaTime = cg.time - tb->lastEvalTime;
+ tb->lastEvalTime = cg.time;
+
+ // first make sure this beam has enough nodes
+ if( ts->destroyTime <= 0 )
{
- // first junction, so this will become the "tail" very soon, make it fade out
- j->sTex = 0;
- j->alphaStart = 0.0;
- j->alphaEnd = 0.0;
+ nodesToAdd = btb->numSegments - CG_CountBeamNodes( tb ) + 1;
+
+ while( nodesToAdd-- )
+ {
+ i = CG_AppendBeamNode( tb );
+
+ if( !tb->nodes->next && CG_Attached( &ts->frontAttachment ) )
+ {
+ // this is the first node to be added
+ CG_AttachmentPoint( &ts->frontAttachment, i->refPosition );
+ }
+ else
+ VectorCopy( i->prev->refPosition, i->refPosition );
+ }
}
- return ( (int)( j - trailJuncs ) + 1 );
-}
+ numNodes = CG_CountBeamNodes( tb );
-void CG_KillTrail( trailJunc_t *t );
+ for( i = tb->nodes; i; i = i->next )
+ VectorCopy( i->refPosition, i->position );
-/*
-===========
-CG_FreeTrailJunc
-===========
-*/
-void CG_FreeTrailJunc( trailJunc_t *junc )
-{
- // kill any juncs after us, so they aren't left hanging
- if( junc->nextJunc )
- CG_KillTrail( junc );
+ if( CG_Attached( &ts->frontAttachment ) && CG_Attached( &ts->backAttachment ) )
+ {
+ // beam between two attachments
+ vec3_t dir, front, back;
+
+ if( ts->destroyTime > 0 && ( cg.time - ts->destroyTime ) >= btb->fadeOutTime )
+ {
+ tb->valid = qfalse;
+ return;
+ }
- // make it non-active
- junc->inuse = qfalse;
- junc->freed = qtrue;
+ CG_AttachmentPoint( &ts->frontAttachment, front );
+ CG_AttachmentPoint( &ts->backAttachment, back );
+ VectorSubtract( back, front, dir );
- if( junc->nextGlobal )
- junc->nextGlobal->prevGlobal = junc->prevGlobal;
+ for( j = 0, i = tb->nodes; i; i = i->next, j++ )
+ {
+ float scale = (float)j / (float)( numNodes - 1 );
- if( junc->prevGlobal )
- junc->prevGlobal->nextGlobal = junc->nextGlobal;
+ VectorMA( front, scale, dir, i->position );
+ }
+ }
+ else if( CG_Attached( &ts->frontAttachment ) )
+ {
+ // beam from one attachment
- if( junc == activeTrails )
- activeTrails = junc->nextGlobal;
+ // cull the trail tail
+ i = CG_FindLastBeamNode( tb );
- // if it's a head, remove it
- if( junc == headTrails )
- headTrails = junc->nextHead;
+ if( i && i->timeLeft >= 0 )
+ {
+ i->timeLeft -= deltaTime;
- if( junc->nextHead )
- junc->nextHead->prevHead = junc->prevHead;
+ if( i->timeLeft < 0 )
+ {
+ tb->nodes = CG_DestroyBeamNode( i );
- if( junc->prevHead )
- junc->prevHead->nextHead = junc->nextHead;
+ if( !tb->nodes )
+ {
+ tb->valid = qfalse;
+ return;
+ }
- junc->nextHead = NULL;
- junc->prevHead = NULL;
+ // if the ts has been destroyed, stop creating new nodes
+ if( ts->destroyTime <= 0 )
+ CG_PrependBeamNode( tb );
+ }
+ else if( i->timeLeft >= 0 && i->prev )
+ {
+ vec3_t dir;
+ float length;
- // stick it in the free list
- junc->prevGlobal = NULL;
- junc->nextGlobal = freeTrails;
+ VectorSubtract( i->refPosition, i->prev->refPosition, dir );
+ length = VectorNormalize( dir ) *
+ ( (float)i->timeLeft / (float)tb->class->segmentTime );
- if( freeTrails )
- freeTrails->prevGlobal = junc;
+ VectorMA( i->prev->refPosition, length, dir, i->position );
+ }
+ }
- freeTrails = junc;
+ CG_AttachmentPoint( &ts->frontAttachment, tb->nodes->refPosition );
+ VectorCopy( tb->nodes->refPosition, tb->nodes->position );
+ }
- numTrailsInuse--;
+ CG_ApplyJitters( tb );
}
/*
-===========
-CG_KillTrail
-===========
+===============
+CG_ParseTrailBeamColor
+===============
*/
-void CG_KillTrail( trailJunc_t *t )
+static qboolean CG_ParseTrailBeamColor( byte *c, char **text_p )
{
- trailJunc_t *next;
+ char *token;
+ int i;
- next = t->nextJunc;
+ for( i = 0; i <= 2; i++ )
+ {
+ token = COM_Parse( text_p );
+
+ if( !Q_stricmp( token, "" ) )
+ return qfalse;
- // kill the trail here
- t->nextJunc = NULL;
+ c[ i ] = (int)( (float)0xFF * atof_neg( token, qfalse ) );
+ }
- if( next )
- CG_FreeTrailJunc( next );
+ return qtrue;
}
/*
-==============
-CG_AddTrailToScene
+===============
+CG_ParseTrailBeam
- TODO: this can do with some major optimization
-==============
+Parse a trail beam
+===============
*/
-static vec3_t vforward, vright, vup;
+static qboolean CG_ParseTrailBeam( baseTrailBeam_t *btb, char **text_p )
+{
+ char *token;
+
+ // read optional parameters
+ while( 1 )
+ {
+ token = COM_Parse( text_p );
-//TA: staticised to please QVM
- #define MAX_TRAIL_VERTS 2048
-static polyVert_t verts[ MAX_TRAIL_VERTS ];
-static polyVert_t outVerts[ MAX_TRAIL_VERTS * 3 ];
+ if( !Q_stricmp( token, "" ) )
+ return qfalse;
-void CG_AddTrailToScene( trailJunc_t *trail, int iteration, int numJuncs )
-{
- int k, i, n, l, numOutVerts;
- polyVert_t mid;
- float mod[ 4 ];
- float sInc = 0.0f, s = 0.0f; // TTimo: init
- trailJunc_t *j, *jNext;
- vec3_t fwd, up, p, v;
-
- // clipping vars
- #define TRAIL_FADE_CLOSE_DIST 64.0
- #define TRAIL_FADE_FAR_SCALE 4.0
- vec3_t viewProj;
- float viewDist, fadeAlpha;
-
- // add spark shader at head position
- if( trail->flags & TJFL_SPARKHEADFLARE )
- {
- j = trail;
- VectorCopy( j->pos, p );
- VectorMA( p, -j->width * 2, vup, p );
- VectorMA( p, -j->width * 2, vright, p );
- VectorCopy( p, verts[ 0 ].xyz );
- verts[ 0 ].st[ 0 ] = 0;
- verts[ 0 ].st[ 1 ] = 0;
- verts[ 0 ].modulate[ 0 ] = 255;
- verts[ 0 ].modulate[ 1 ] = 255;
- verts[ 0 ].modulate[ 2 ] = 255;
- verts[ 0 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 );
-
- VectorCopy( j->pos, p );
- VectorMA( p, -j->width * 2, vup, p );
- VectorMA( p, j->width * 2, vright, p );
- VectorCopy( p, verts[ 1 ].xyz );
- verts[ 1 ].st[ 0 ] = 0;
- verts[ 1 ].st[ 1 ] = 1;
- verts[ 1 ].modulate[ 0 ] = 255;
- verts[ 1 ].modulate[ 1 ] = 255;
- verts[ 1 ].modulate[ 2 ] = 255;
- verts[ 1 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 );
-
- VectorCopy( j->pos, p );
- VectorMA( p, j->width * 2, vup, p );
- VectorMA( p, j->width * 2, vright, p );
- VectorCopy( p, verts[ 2 ].xyz );
- verts[ 2 ].st[ 0 ] = 1;
- verts[ 2 ].st[ 1 ] = 1;
- verts[ 2 ].modulate[ 0 ] = 255;
- verts[ 2 ].modulate[ 1 ] = 255;
- verts[ 2 ].modulate[ 2 ] = 255;
- verts[ 2 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 );
-
- VectorCopy( j->pos, p );
- VectorMA( p, j->width * 2, vup, p );
- VectorMA( p, -j->width * 2, vright, p );
- VectorCopy( p, verts[ 3 ].xyz );
- verts[ 3 ].st[ 0 ] = 1;
- verts[ 3 ].st[ 1 ] = 0;
- verts[ 3 ].modulate[ 0 ] = 255;
- verts[ 3 ].modulate[ 1 ] = 255;
- verts[ 3 ].modulate[ 2 ] = 255;
- verts[ 3 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 );
-
- trap_R_AddPolyToScene( cgs.media.sparkFlareShader, 4, verts );
- }
-
-// if (trail->flags & TJFL_CROSSOVER && iteration < 1) {
-// iteration = 1;
-// }
-
- if( !numJuncs )
- {
- // first count the number of juncs in the trail
- j = trail;
- numJuncs = 0;
- sInc = 0;
-
- while( j )
- {
- numJuncs++;
-
- // check for a dead next junc
- if( !j->inuse && j->nextJunc && !j->nextJunc->inuse )
- CG_KillTrail( j );
- else if( j->nextJunc && j->nextJunc->freed )
+ if( !Q_stricmp( token, "segments" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ btb->numSegments = atoi_neg( token, qfalse );
+
+ if( btb->numSegments >= MAX_TRAIL_BEAM_NODES )
{
- // not sure how this can happen, but it does, and causes infinite loops
- j->nextJunc = NULL;
+ btb->numSegments = MAX_TRAIL_BEAM_NODES - 1;
+ CG_Printf( S_COLOR_YELLOW "WARNING: too many segments in trail beam\n" );
}
+ continue;
+ }
+ else if( !Q_stricmp( token, "width" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
- if( j->nextJunc )
- sInc += VectorDistance( j->nextJunc->pos, j->pos );
+ btb->frontWidth = atof_neg( token, qfalse );
- j = j->nextJunc;
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ if( !Q_stricmp( token, "-" ) )
+ btb->backWidth = btb->frontWidth;
+ else
+ btb->backWidth = atof_neg( token, qfalse );
+ continue;
}
- }
+ else if( !Q_stricmp( token, "alpha" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
- if( numJuncs < 2 )
- return;
+ btb->frontAlpha = atof_neg( token, qfalse );
- if( trail->sType == STYPE_STRETCH )
- {
- //sInc = ((1.0 - 0.1) / (float)(numJuncs)); // hack, the end of funnel shows a bit of the start (looping)
- s = 0.05;
- //s = 0.05;
- }
- else if( trail->sType == STYPE_REPEAT )
- s = trail->sTex;
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
- // now traverse the list
- j = trail;
- jNext = j->nextJunc;
- i = 0;
- while( jNext )
- {
- // first get the directional vectors to the next junc
- VectorSubtract( jNext->pos, j->pos, fwd );
- GetPerpendicularViewVector( cg.refdef.vieworg, j->pos, jNext->pos, up );
-
- // if it's a crossover, draw it twice
- if( j->flags & TJFL_CROSSOVER )
+ if( !Q_stricmp( token, "-" ) )
+ btb->backAlpha = btb->frontAlpha;
+ else
+ btb->backAlpha = atof_neg( token, qfalse );
+ continue;
+ }
+ else if( !Q_stricmp( token, "color" ) )
{
- if( iteration > 0 )
- {
- ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj );
- VectorSubtract( cg.refdef.vieworg, viewProj, v );
- VectorNormalize( v );
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
- if( iteration == 1 )
- VectorMA( up, 0.3, v, up );
+ if( !Q_stricmp( token, "{" ) )
+ {
+ if( !CG_ParseTrailBeamColor( btb->frontColor, text_p ) )
+ break;
+
+ token = COM_Parse( text_p );
+ if( Q_stricmp( token, "}" ) )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
+ break;
+ }
+
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ if( !Q_stricmp( token, "-" ) )
+ {
+ btb->backColor[ 0 ] = btb->frontColor[ 0 ];
+ btb->backColor[ 1 ] = btb->frontColor[ 1 ];
+ btb->backColor[ 2 ] = btb->frontColor[ 2 ];
+ }
+ else if( !Q_stricmp( token, "{" ) )
+ {
+ if( !CG_ParseTrailBeamColor( btb->backColor, text_p ) )
+ break;
+
+ token = COM_Parse( text_p );
+ if( Q_stricmp( token, "}" ) )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
+ break;
+ }
+ }
else
- VectorMA( up, -0.3, v, up );
-
- VectorNormalize( up );
+ {
+ CG_Printf( S_COLOR_RED "ERROR: missing '{'\n" );
+ break;
+ }
}
+ else
+ {
+ CG_Printf( S_COLOR_RED "ERROR: missing '{'\n" );
+ break;
+ }
+
+ continue;
}
- // do fading when moving towards the projection point onto the trail segment vector
- else if( !( j->flags & TJFL_NOCULL ) && ( j->widthEnd > 4 || jNext->widthEnd > 4 ) )
+ else if( !Q_stricmp( token, "segmentTime" ) )
{
- ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj );
- viewDist = Distance( viewProj, cg.refdef.vieworg );
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ btb->segmentTime = atoi_neg( token, qfalse );
+ continue;
+ }
+ else if( !Q_stricmp( token, "fadeOutTime" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ btb->fadeOutTime = atoi_neg( token, qfalse );
+ continue;
+ }
+ else if( !Q_stricmp( token, "shader" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ Q_strncpyz( btb->shaderName, token, MAX_QPATH );
+
+ continue;
+ }
+ else if( !Q_stricmp( token, "textureType" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ if( !Q_stricmp( token, "stretch" ) )
+ {
+ btb->textureType = TBTT_STRETCH;
+
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ btb->frontTextureCoord = atof_neg( token, qfalse );
+
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
- if( viewDist < ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE ) )
+ btb->backTextureCoord = atof_neg( token, qfalse );
+ }
+ else if( !Q_stricmp( token, "repeat" ) )
{
- if( viewDist < TRAIL_FADE_CLOSE_DIST )
- fadeAlpha = 0.0;
+ btb->textureType = TBTT_REPEAT;
+
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ if( !Q_stricmp( token, "front" ) )
+ btb->clampToBack = qfalse;
+ else if( !Q_stricmp( token, "back" ) )
+ btb->clampToBack = qtrue;
else
- fadeAlpha = ( viewDist - TRAIL_FADE_CLOSE_DIST ) / ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE );
+ {
+ CG_Printf( S_COLOR_RED "ERROR: unknown textureType clamp \"%s\"\n", token );
+ break;
+ }
- if( fadeAlpha < j->alpha )
- j->alpha = fadeAlpha;
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
- if( fadeAlpha < jNext->alpha )
- jNext->alpha = fadeAlpha;
+ btb->repeatLength = atof_neg( token, qfalse );
}
+ else
+ {
+ CG_Printf( S_COLOR_RED "ERROR: unknown textureType \"%s\"\n", token );
+ break;
+ }
+
+ continue;
}
+ else if( !Q_stricmp( token, "realLight" ) )
+ {
+ btb->realLight = qtrue;
- // now output the QUAD for this segment
+ continue;
+ }
+ else if( !Q_stricmp( token, "jitter" ) )
+ {
+ if( btb->numJitters == MAX_TRAIL_BEAM_JITTERS )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: too many jitters\n", token );
+ break;
+ }
+
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
+
+ btb->jitters[ btb->numJitters ].magnitude = atof_neg( token, qfalse );
- // 1 ----
- VectorMA( j->pos, 0.5 * j->width, up, p );
- VectorCopy( p, verts[ i ].xyz );
- verts[ i ].st[ 0 ] = s;
- verts[ i ].st[ 1 ] = 1.0;
+ token = COM_Parse( text_p );
+ if( !Q_stricmp( token, "" ) )
+ break;
- for( k = 0; k < 3; k++ )
- verts[ i ].modulate[ k ] = (unsigned char)( j->color[ k ] * 255.0 );
+ btb->jitters[ btb->numJitters ].period = atoi_neg( token, qfalse );
- verts[ i ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 );
+ btb->numJitters++;
+
+ continue;
+ }
+ else if( !Q_stricmp( token, "jitterAttachments" ) )
+ {
+ btb->jitterAttachments = qtrue;
- // blend this with the previous junc
- if( j != trail )
+ continue;
+ }
+ else if( !Q_stricmp( token, "}" ) )
+ return qtrue; //reached the end of this trail beam
+ else
{
- VectorAdd( verts[ i ].xyz, verts[ i - 1 ].xyz, verts[ i ].xyz );
- VectorScale( verts[ i ].xyz, 0.5, verts[ i ].xyz );
- VectorCopy( verts[ i ].xyz, verts[ i - 1 ].xyz );
+ CG_Printf( S_COLOR_RED "ERROR: unknown token '%s' in trail beam\n", token );
+ return qfalse;
}
- else if( j->flags & TJFL_FADEIN )
- verts[ i ].modulate[ 3 ] = 0; // fade in
+ }
- i++;
+ return qfalse;
+}
- // 2 ----
- VectorMA( p, -1 * j->width, up, p );
- VectorCopy( p, verts[ i ].xyz );
- verts[ i ].st[ 0 ] = s;
- verts[ i ].st[ 1 ] = 0.0;
+/*
+===============
+CG_InitialiseTrailBeam
+===============
+*/
+static void CG_InitialiseTrailBeam( baseTrailBeam_t *btb )
+{
+ memset( btb, 0, sizeof( baseTrailBeam_t ) );
- for( k = 0; k < 3; k++ )
- verts[ i ].modulate[ k ] = (unsigned char)( j->color[ k ] * 255.0 );
+ btb->numSegments = 1;
+ btb->frontWidth = btb->backWidth = 8.0f;
+ btb->frontAlpha = btb->backAlpha = 1.0f;
+ memset( btb->frontColor, 0xFF, sizeof( btb->frontColor ) );
+ memset( btb->backColor, 0xFF, sizeof( btb->backColor ) );
- verts[ i ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 );
+ btb->segmentTime = 250;
+
+ btb->textureType = TBTT_STRETCH;
+ btb->frontTextureCoord = 0.0f;
+ btb->backTextureCoord = 1.0f;
+}
+
+/*
+===============
+CG_ParseTrailSystem
+
+Parse a trail system section
+===============
+*/
+static qboolean CG_ParseTrailSystem( baseTrailSystem_t *bts, char **text_p, const char *name )
+{
+ char *token;
+
+ // read optional parameters
+ while( 1 )
+ {
+ token = COM_Parse( text_p );
- // blend this with the previous junc
- if( j != trail )
+ if( !Q_stricmp( token, "" ) )
+ return qfalse;
+
+ if( !Q_stricmp( token, "{" ) )
{
- VectorAdd( verts[ i ].xyz, verts[ i - 3 ].xyz, verts[ i ].xyz );
- VectorScale( verts[ i ].xyz, 0.5, verts[ i ].xyz );
- VectorCopy( verts[ i ].xyz, verts[ i - 3 ].xyz );
- }
- else if( j->flags & TJFL_FADEIN )
- verts[ i ].modulate[ 3 ] = 0; // fade in
+ CG_InitialiseTrailBeam( &baseTrailBeams[ numBaseTrailBeams ] );
- i++;
+ if( !CG_ParseTrailBeam( &baseTrailBeams[ numBaseTrailBeams ], text_p ) )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: failed to parse trail beam\n" );
+ return qfalse;
+ }
+
+ if( bts->numBeams == MAX_BEAMS_PER_SYSTEM )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: trail system has > %d beams\n", MAX_BEAMS_PER_SYSTEM );
+ return qfalse;
+ }
+ else if( numBaseTrailBeams == MAX_BASETRAIL_BEAMS )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: maximum number of trail beams (%d) reached\n",
+ MAX_BASETRAIL_BEAMS );
+ return qfalse;
+ }
+ else
+ {
+ //start parsing beams again
+ bts->beams[ bts->numBeams ] = &baseTrailBeams[ numBaseTrailBeams ];
+ bts->numBeams++;
+ numBaseTrailBeams++;
+ }
+ continue;
+ }
+ else if( !Q_stricmp( token, "beam" ) ) //acceptable text
+ continue;
+ else if( !Q_stricmp( token, "}" ) )
+ {
+ if( cg_debugTrails.integer >= 1 )
+ CG_Printf( "Parsed trail system %s\n", name );
- if( trail->sType == STYPE_REPEAT )
- s = jNext->sTex;
+ return qtrue; //reached the end of this trail system
+ }
else
{
- //s += sInc;
- s += VectorDistance( j->pos, jNext->pos ) / sInc;
- if( s > 1.0 )
- s = 1.0;
+ CG_Printf( S_COLOR_RED "ERROR: unknown token '%s' in trail system %s\n", token, bts->name );
+ return qfalse;
}
+ }
- // 3 ----
- VectorMA( jNext->pos, -0.5 * jNext->width, up, p );
- VectorCopy( p, verts[ i ].xyz );
- verts[ i ].st[ 0 ] = s;
- verts[ i ].st[ 1 ] = 0.0;
+ return qfalse;
+}
- for( k = 0; k < 3; k++ )
- verts[ i ].modulate[ k ] = (unsigned char)( jNext->color[ k ] * 255.0 );
+/*
+===============
+CG_ParseTrailFile
- verts[ i ].modulate[ 3 ] = (unsigned char)( jNext->alpha * 255.0 );
- i++;
+Load the trail systems from a trail file
+===============
+*/
+static qboolean CG_ParseTrailFile( const char *fileName )
+{
+ char *text_p;
+ int i;
+ int len;
+ char *token;
+ char text[ 32000 ];
+ char tsName[ MAX_QPATH ];
+ qboolean tsNameSet = qfalse;
+ fileHandle_t f;
+
+ // load the file
+ len = trap_FS_FOpenFile( fileName, &f, FS_READ );
+ if( len <= 0 )
+ return qfalse;
+
+ if( len >= sizeof( text ) - 1 )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: trail file %s too long\n", fileName );
+ return qfalse;
+ }
- // 4 ----
- VectorMA( p, jNext->width, up, p );
- VectorCopy( p, verts[ i ].xyz );
- verts[ i ].st[ 0 ] = s;
- verts[ i ].st[ 1 ] = 1.0;
+ trap_FS_Read( text, len, f );
+ text[ len ] = 0;
+ trap_FS_FCloseFile( f );
- for( k = 0; k < 3; k++ )
- verts[ i ].modulate[ k ] = (unsigned char)( jNext->color[ k ] * 255.0 );
+ // parse the text
+ text_p = text;
- verts[ i ].modulate[ 3 ] = (unsigned char)( jNext->alpha * 255.0 );
- i++;
+ // read optional parameters
+ while( 1 )
+ {
+ token = COM_Parse( &text_p );
- if( i + 4 > MAX_TRAIL_VERTS )
+ if( !Q_stricmp( token, "" ) )
break;
- j = jNext;
- jNext = j->nextJunc;
+ if( !Q_stricmp( token, "{" ) )
+ {
+ if( tsNameSet )
+ {
+ //check for name space clashes
+ for( i = 0; i < numBaseTrailSystems; i++ )
+ {
+ if( !Q_stricmp( baseTrailSystems[ i ].name, tsName ) )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: a trail system is already named %s\n", tsName );
+ return qfalse;
+ }
+ }
+
+ Q_strncpyz( baseTrailSystems[ numBaseTrailSystems ].name, tsName, MAX_QPATH );
+
+ if( !CG_ParseTrailSystem( &baseTrailSystems[ numBaseTrailSystems ], &text_p, tsName ) )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: %s: failed to parse trail system %s\n", fileName, tsName );
+ return qfalse;
+ }
+
+ //start parsing trail systems again
+ tsNameSet = qfalse;
+
+ if( numBaseTrailSystems == MAX_BASETRAIL_SYSTEMS )
+ {
+ CG_Printf( S_COLOR_RED "ERROR: maximum number of trail systems (%d) reached\n",
+ MAX_BASETRAIL_SYSTEMS );
+ return qfalse;
+ }
+ else
+ numBaseTrailSystems++;
+
+ continue;
+ }
+ else
+ {
+ CG_Printf( S_COLOR_RED "ERROR: unamed trail system\n" );
+ return qfalse;
+ }
+ }
+
+ if( !tsNameSet )
+ {
+ Q_strncpyz( tsName, token, sizeof( tsName ) );
+ tsNameSet = qtrue;
+ }
+ else
+ {
+ CG_Printf( S_COLOR_RED "ERROR: trail system already named\n" );
+ return qfalse;
+ }
}
- if( trail->flags & TJFL_FIXDISTORT )
+ return qtrue;
+}
+
+/*
+===============
+CG_LoadTrailSystems
+
+Load trail system templates
+===============
+*/
+void CG_LoadTrailSystems( void )
+{
+ int i;
+ /*const char *s[ MAX_TRAIL_FILES ];*/
+
+ //clear out the old
+ numBaseTrailSystems = 0;
+ numBaseTrailBeams = 0;
+
+ for( i = 0; i < MAX_BASETRAIL_SYSTEMS; i++ )
{
- // build the list of outVerts, by dividing up the QUAD's into 4 Tri's each, so as to allow
- // any shaped (convex) Quad without bilinear distortion
- for( k = 0, numOutVerts = 0; k < i; k += 4 )
- {
- VectorCopy( verts[ k ].xyz, mid.xyz );
- mid.st[ 0 ] = verts[ k ].st[ 0 ];
- mid.st[ 1 ] = verts[ k ].st[ 1 ];
+ baseTrailSystem_t *bts = &baseTrailSystems[ i ];
+ memset( bts, 0, sizeof( baseTrailSystem_t ) );
+ }
- for( l = 0; l < 4; l++ )
- mod[ l ] = (float)verts[ k ].modulate[ l ];
+ for( i = 0; i < MAX_BASETRAIL_BEAMS; i++ )
+ {
+ baseTrailBeam_t *btb = &baseTrailBeams[ i ];
+ memset( btb, 0, sizeof( baseTrailBeam_t ) );
+ }
- for( n = 1; n < 4; n++ )
- {
- VectorAdd( verts[ k + n ].xyz, mid.xyz, mid.xyz );
- mid.st[ 0 ] += verts[ k + n ].st[ 0 ];
- mid.st[ 1 ] += verts[ k + n ].st[ 1 ];
+ //and bring in the new
+/* for( i = 0; i < MAX_TRAIL_FILES; i++ )
+ {
+ s[ i ] = CG_ConfigString( CS_TRAIL_FILES + i );
- for( l = 0; l < 4; l++ )
- mod[ l ] += (float)verts[ k + n ].modulate[ l ];
- }
+ if( strlen( s[ i ] ) > 0 )
+ {
+ CG_Printf( "...loading '%s'\n", s[ i ] );
+ CG_ParseTrailFile( s[ i ] );
+ }
+ else
+ break;
+ }*/
+ CG_Printf( "trail.trail: %d\n", CG_ParseTrailFile( "scripts/trail.trail" ) );
+}
+
+/*
+===============
+CG_RegisterTrailSystem
+
+Load the media that a trail system needs
+===============
+*/
+qhandle_t CG_RegisterTrailSystem( char *name )
+{
+ int i, j;
+ baseTrailSystem_t *bts;
+ baseTrailBeam_t *btb;
- VectorScale( mid.xyz, 0.25, mid.xyz );
- mid.st[ 0 ] *= 0.25;
- mid.st[ 1 ] *= 0.25;
+ for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ )
+ {
+ bts = &baseTrailSystems[ i ];
- for( l = 0; l < 4; l++ )
- mid.modulate[ l ] = (unsigned char)( mod[ l ] / 4.0 );
+ if( !Q_stricmp( bts->name, name ) )
+ {
+ //already registered
+ if( bts->registered )
+ return i + 1;
- // now output the tri's
- for( n = 0; n < 4; n++ )
+ for( j = 0; j < bts->numBeams; j++ )
{
- outVerts[ numOutVerts++ ] = verts[ k + n ];
- outVerts[ numOutVerts++ ] = mid;
+ btb = bts->beams[ j ];
- if( n < 3 )
- outVerts[ numOutVerts++ ] = verts[ k + n + 1 ];
- else
- outVerts[ numOutVerts++ ] = verts[ k ];
+ btb->shader = trap_R_RegisterShader( btb->shaderName );
}
+ if( cg_debugTrails.integer >= 1 )
+ CG_Printf( "Registered trail system %s\n", name );
+
+ bts->registered = qtrue;
+
+ //avoid returning 0
+ return i + 1;
}
+ }
- if( !( trail->flags & TJFL_NOPOLYMERGE ) )
- trap_R_AddPolysToScene( trail->shader, 3, &outVerts[ 0 ], numOutVerts / 3 );
- else
+ CG_Printf( S_COLOR_RED "ERROR: failed to register trail system %s\n", name );
+ return 0;
+}
+
+
+/*
+===============
+CG_SpawnNewTrailBeam
+
+Allocate a new trail beam
+===============
+*/
+static trailBeam_t *CG_SpawnNewTrailBeam( baseTrailBeam_t *btb,
+ trailSystem_t *parent )
+{
+ int i;
+ trailBeam_t *tb = NULL;
+ trailSystem_t *ts = parent;
+
+ for( i = 0; i < MAX_TRAIL_BEAMS; i++ )
+ {
+ tb = &trailBeams[ i ];
+
+ if( !tb->valid )
{
- int k;
+ memset( tb, 0, sizeof( trailBeam_t ) );
- for( k = 0; k < numOutVerts / 3; k++ )
- trap_R_AddPolyToScene( trail->shader, 3, &outVerts[ k * 3 ] );
+ //found a free slot
+ tb->class = btb;
+ tb->parent = ts;
+
+ tb->valid = qtrue;
+
+ if( cg_debugTrails.integer >= 1 )
+ CG_Printf( "TB %s created\n", ts->class->name );
+
+ break;
}
}
- else
+
+ return tb;
+}
+
+
+/*
+===============
+CG_SpawnNewTrailSystem
+
+Spawns a new trail system
+===============
+*/
+trailSystem_t *CG_SpawnNewTrailSystem( qhandle_t psHandle )
+{
+ int i, j;
+ trailSystem_t *ts = NULL;
+ baseTrailSystem_t *bts = &baseTrailSystems[ psHandle - 1 ];
+
+ if( !bts->registered )
{
- // send the polygons
- // FIXME: is it possible to send a GL_STRIP here? We are actually sending 2x the verts we really need to
- if( !( trail->flags & TJFL_NOPOLYMERGE ) )
- trap_R_AddPolysToScene( trail->shader, 4, &verts[ 0 ], i / 4 );
- else
+ CG_Printf( S_COLOR_RED "ERROR: a trail system has not been registered yet\n" );
+ return NULL;
+ }
+
+ for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ )
+ {
+ ts = &trailSystems[ i ];
+
+ if( !ts->valid )
{
- int k;
+ memset( ts, 0, sizeof( trailSystem_t ) );
+
+ //found a free slot
+ ts->class = bts;
+
+ ts->valid = qtrue;
+ ts->destroyTime = -1;
+
+ for( j = 0; j < bts->numBeams; j++ )
+ CG_SpawnNewTrailBeam( bts->beams[ j ], ts );
- for( k = 0; k < i / 4; k++ )
- trap_R_AddPolyToScene( trail->shader, 4, &verts[ k * 4 ] );
+ if( cg_debugTrails.integer >= 1 )
+ CG_Printf( "TS %s created\n", bts->name );
+
+ break;
}
}
- // do we need to make another pass?
- if( trail->flags & TJFL_CROSSOVER )
+ return ts;
+}
+
+/*
+===============
+CG_DestroyTrailSystem
+
+Destroy a trail system
+===============
+*/
+void CG_DestroyTrailSystem( trailSystem_t **ts )
+{
+ (*ts)->destroyTime = cg.time;
+
+ if( CG_Attached( &(*ts)->frontAttachment ) &&
+ !CG_Attached( &(*ts)->backAttachment ) )
{
- if( iteration < 2 )
- CG_AddTrailToScene( trail, iteration + 1, numJuncs );
+ vec3_t v;
+
+ // attach the trail head to a static point
+ CG_AttachmentPoint( &(*ts)->frontAttachment, v );
+ CG_SetAttachmentPoint( &(*ts)->frontAttachment, v );
+ CG_AttachToPoint( &(*ts)->frontAttachment );
+
+ (*ts)->frontAttachment.centValid = qfalse; // a bit naughty
}
+ ts = NULL;
}
/*
===============
-CG_AddTrails
+CG_IsTrailSystemValid
+
+Test a trail system for validity
===============
*/
-void CG_AddTrails( void )
+qboolean CG_IsTrailSystemValid( trailSystem_t **ts )
{
- float lifeFrac;
- trailJunc_t *j, *jNext;
+ if( *ts == NULL || ( *ts && !(*ts)->valid ) )
+ {
+ if( *ts && !(*ts)->valid )
+ *ts = NULL;
+
+ return qfalse;
+ }
- if( !initTrails )
- CG_ClearTrails( );
+ return qtrue;
+}
- //AngleVectors( cg.snap->ps.viewangles, vforward, vright, vup );
- VectorCopy( cg.refdef.viewaxis[ 0 ], vforward );
- VectorCopy( cg.refdef.viewaxis[ 1 ], vright );
- VectorCopy( cg.refdef.viewaxis[ 2 ], vup );
+/*
+===============
+CG_GarbageCollectTrailSystems
- // update the settings for each junc
- j = activeTrails;
+Destroy inactive trail systems
+===============
+*/
+static void CG_GarbageCollectTrailSystems( void )
+{
+ int i, j, count;
+ trailSystem_t *ts;
+ trailBeam_t *tb;
+ int centNum;
- while( j )
+ for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ )
{
- lifeFrac = (float)( cg.time - j->spawnTime ) / (float)( j->endTime - j->spawnTime );
+ ts = &trailSystems[ i ];
+ count = 0;
+
+ //don't bother checking already invalid systems
+ if( !ts->valid )
+ continue;
- if( lifeFrac >= 1.0 )
+ for( j = 0; j < MAX_TRAIL_BEAMS; j++ )
{
- j->inuse = qfalse; // flag it as dead
- j->width = j->widthEnd;
- j->alpha = j->alphaEnd;
+ tb = &trailBeams[ j ];
+
+ if( tb->valid && tb->parent == ts )
+ count++;
+ }
+
+ if( !count )
+ ts->valid = qfalse;
- if( j->alpha > 1.0 )
- j->alpha = 1.0;
- else if( j->alpha < 0.0 )
- j->alpha = 0.0;
+ //check systems where the parent cent has left the PVS
+ //( local player entity is always valid )
+ if( ( centNum = CG_AttachmentCentNum( &ts->frontAttachment ) ) >= 0 &&
+ centNum != cg.snap->ps.clientNum )
+ {
+ trailSystem_t *tempTS = ts;
- VectorCopy( j->colorEnd, j->color );
+ if( !cg_entities[ centNum ].valid )
+ CG_DestroyTrailSystem( &tempTS );
}
- else
+
+ if( ( centNum = CG_AttachmentCentNum( &ts->backAttachment ) ) >= 0 &&
+ centNum != cg.snap->ps.clientNum )
{
- j->width = j->widthStart + ( j->widthEnd - j->widthStart ) * lifeFrac;
- j->alpha = j->alphaStart + ( j->alphaEnd - j->alphaStart ) * lifeFrac;
+ trailSystem_t *tempTS = ts;
- if( j->alpha > 1.0 )
- j->alpha = 1.0;
- else if( j->alpha < 0.0 )
- j->alpha = 0.0;
+ if( !cg_entities[ centNum ].valid )
+ CG_DestroyTrailSystem( &tempTS );
+ }
+
+ if( cg_debugTrails.integer >= 1 && !ts->valid )
+ CG_Printf( "TS %s garbage collected\n", ts->class->name );
+ }
+}
- VectorSubtract( j->colorEnd, j->colorStart, j->color );
- VectorMA( j->colorStart, lifeFrac, j->color, j->color );
+/*
+===============
+CG_AddTrails
+
+Add trails to the scene
+===============
+*/
+void CG_AddTrails( void )
+{
+ int i;
+ trailBeam_t *tb;
+ int numTS = 0, numTB = 0;
+
+ //remove expired trail systems
+ CG_GarbageCollectTrailSystems( );
+
+ for( i = 0; i < MAX_TRAIL_BEAMS; i++ )
+ {
+ tb = &trailBeams[ i ];
+
+ if( tb->valid )
+ {
+ CG_UpdateBeam( tb );
+ CG_RenderBeam( tb );
}
+ }
+
+ if( cg_debugTrails.integer >= 2 )
+ {
+ for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ )
+ if( trailSystems[ i ].valid )
+ numTS++;
+
+ for( i = 0; i < MAX_TRAIL_BEAMS; i++ )
+ if( trailBeams[ i ].valid )
+ numTB++;
- j = j->nextGlobal;
+ CG_Printf( "TS: %d TB: %d\n", numTS, numTB );
}
+}
+
+static trailSystem_t *testTS;
+static qhandle_t testTSHandle;
+
+/*
+===============
+CG_DestroyTestTS_f
+
+Destroy the test a trail system
+===============
+*/
+void CG_DestroyTestTS_f( void )
+{
+ if( CG_IsTrailSystemValid( &testTS ) )
+ CG_DestroyTrailSystem( &testTS );
+}
+
+/*
+===============
+CG_TestTS_f
- // draw the trailHeads
- j = headTrails;
+Test a trail system
+===============
+*/
+void CG_TestTS_f( void )
+{
+ char tsName[ MAX_QPATH ];
- while( j )
+ if( trap_Argc( ) < 2 )
+ return;
+
+ Q_strncpyz( tsName, CG_Argv( 1 ), MAX_QPATH );
+ testTSHandle = CG_RegisterTrailSystem( tsName );
+
+ if( testTSHandle )
{
- jNext = j->nextHead; // in case it gets removed
+ CG_DestroyTestTS_f( );
- if( !j->inuse )
- CG_FreeTrailJunc( j );
- else
- CG_AddTrailToScene( j, 0, 0 );
+ testTS = CG_SpawnNewTrailSystem( testTSHandle );
- j = jNext;
+ if( CG_IsTrailSystemValid( &testTS ) )
+ {
+ CG_SetAttachmentCent( &testTS->frontAttachment, &cg_entities[ 0 ] );
+ CG_AttachToCent( &testTS->frontAttachment );
+ }
}
}
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index 0e98036d..59016dc4 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -1311,7 +1311,7 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
// build the render lists
if( !cg.hyperspace )
{
- CG_AddPacketEntities( ); // adter calcViewValues, so predicted player state is correct
+ CG_AddPacketEntities( ); // after calcViewValues, so predicted player state is correct
CG_AddMarks( );
CG_AddLocalEntities( );
}
@@ -1322,9 +1322,7 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
if( !cg.hyperspace )
{
CG_AddParticles( );
-
- //TA: wolf trails stuff
- CG_AddTrails( ); // this must come last, so the trails dropped this frame get drawn
+ CG_AddTrails( );
}
// add buffered sounds
diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c
index dcef9f82..935342ac 100644
--- a/src/cgame/cg_weapons.c
+++ b/src/cgame/cg_weapons.c
@@ -157,35 +157,6 @@ static void CG_ShotgunEjectBrass( centity_t *cent )
le->leMarkType = LEMT_NONE;
}
-/*
-==========================
-CG_TeslaTrail
-==========================
-*/
-void CG_TeslaTrail( vec3_t start, vec3_t end, int srcENum, int destENum )
-{
- localEntity_t *le;
- refEntity_t *re;
-
- //add a bunch of bolt segments
- le = CG_AllocLocalEntity( );
- re = &le->refEntity;
-
- le->leType = LE_LIGHTNING_BOLT;
- le->startTime = cg.time;
- le->endTime = cg.time + cg_teslaTrailTime.value;
- le->lifeRate = 1.0 / ( le->endTime - le->startTime );
- re->customShader = cgs.media.lightningShader;
-
- le->srcENum = srcENum;
- le->destENum = destENum;
- le->vOffset = 28;
- le->maxRange = BG_FindRangeForBuildable( BA_H_TESLAGEN ) * M_ROOT3;
-
- VectorCopy( start, re->origin );
- VectorCopy( end, re->oldorigin );
-}
-
/*
=================
@@ -351,6 +322,19 @@ static qboolean CG_ParseWeaponModeSection( weaponInfoMode_t *wim, char **text_p
continue;
}
+ else if( !Q_stricmp( token, "missileTrailSystem" ) )
+ {
+ token = COM_Parse( text_p );
+ if( !token )
+ break;
+
+ wim->missileTrailSystem = CG_RegisterTrailSystem( token );
+
+ if( !wim->missileTrailSystem )
+ CG_Printf( S_COLOR_RED "ERROR: missile trail system not found %s\n", token );
+
+ continue;
+ }
else if( !Q_stricmp( token, "muzzleParticleSystem" ) )
{
token = COM_Parse( text_p );
@@ -856,8 +840,7 @@ void CG_InitWeapons( void )
for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
CG_RegisterWeapon( i );
- cgs.media.lightningShader = trap_R_RegisterShader( "models/ammo/tesla/tesla_bolt");
- cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" );
+ cgs.media.level2ZapTS = CG_RegisterTrailSystem( "models/weapons/lev2zap/lightning" );
}
@@ -1102,9 +1085,9 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
cent->currentState.number != cg.predictedPlayerState.clientNum )
{
if( noGunModel )
- CG_SetParticleSystemTag( cent->muzzlePS, *parent, parent->hModel, "tag_weapon" );
+ CG_SetAttachmentTag( &cent->muzzlePS->attachment, *parent, parent->hModel, "tag_weapon" );
else
- CG_SetParticleSystemTag( cent->muzzlePS, gun, weapon->weaponModel, "tag_flash" );
+ CG_SetAttachmentTag( &cent->muzzlePS->attachment, gun, weapon->weaponModel, "tag_flash" );
}
//if the PS is infinite disable it when not firing
@@ -1148,13 +1131,17 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
{
cent->muzzlePS = CG_SpawnNewParticleSystem( weapon->wim[ weaponMode ].muzzleParticleSystem );
- if( noGunModel )
- CG_SetParticleSystemTag( cent->muzzlePS, *parent, parent->hModel, "tag_weapon" );
- else
- CG_SetParticleSystemTag( cent->muzzlePS, gun, weapon->weaponModel, "tag_flash" );
+ if( CG_IsParticleSystemValid( &cent->muzzlePS ) )
+ {
+ if( noGunModel )
+ CG_SetAttachmentTag( &cent->muzzlePS->attachment, *parent, parent->hModel, "tag_weapon" );
+ else
+ CG_SetAttachmentTag( &cent->muzzlePS->attachment, gun, weapon->weaponModel, "tag_flash" );
+
+ CG_SetAttachmentCent( &cent->muzzlePS->attachment, cent );
+ CG_AttachToTag( &cent->muzzlePS->attachment );
+ }
- CG_SetParticleSystemCent( cent->muzzlePS, cent );
- CG_AttachParticleSystemToTag( cent->muzzlePS );
cent->muzzlePsTrigger = qfalse;
}
@@ -1228,15 +1215,19 @@ void CG_AddViewWeapon( playerState_t *ps )
VectorMA( origin, -8, cg.refdef.viewaxis[ 2 ], origin );
if( cent->muzzlePS )
- CG_SetParticleSystemOrigin( cent->muzzlePS, origin );
+ CG_SetAttachmentPoint( &cent->muzzlePS->attachment, origin );
//check for particle systems
if( wi->wim[ weaponMode ].muzzleParticleSystem && cent->muzzlePsTrigger )
{
cent->muzzlePS = CG_SpawnNewParticleSystem( wi->wim[ weaponMode ].muzzleParticleSystem );
- CG_SetParticleSystemOrigin( cent->muzzlePS, origin );
- CG_SetParticleSystemCent( cent->muzzlePS, cent );
- CG_AttachParticleSystemToOrigin( cent->muzzlePS );
+
+ if( CG_IsParticleSystemValid( &cent->muzzlePS ) )
+ {
+ CG_SetAttachmentPoint( &cent->muzzlePS->attachment, origin );
+ CG_SetAttachmentCent( &cent->muzzlePS->attachment, cent );
+ CG_AttachToPoint( &cent->muzzlePS->attachment );
+ }
cent->muzzlePsTrigger = qfalse;
}
@@ -1765,9 +1756,13 @@ void CG_MissileHitWall( weapon_t weaponNum, weaponMode_t weaponMode, int clientN
if( ps )
{
particleSystem_t *partSystem = CG_SpawnNewParticleSystem( ps );
- CG_SetParticleSystemOrigin( partSystem, origin );
- CG_SetParticleSystemNormal( partSystem, dir );
- CG_AttachParticleSystemToOrigin( partSystem );
+
+ if( CG_IsParticleSystemValid( &partSystem ) )
+ {
+ CG_SetAttachmentPoint( &partSystem->attachment, origin );
+ CG_SetParticleSystemNormal( partSystem, dir );
+ CG_AttachToPoint( &partSystem->attachment );
+ }
}
//
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index 062ffdaf..10bb419b 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -5037,3 +5037,41 @@ int BG_GetValueOfHuman( playerState_t *ps )
return ceil( ALIEN_MAX_SINGLE_KILLS * portion );
}
+
+/*
+===============
+atof_neg
+
+atof with an allowance for negative values
+===============
+*/
+float atof_neg( char *token, qboolean allowNegative )
+{
+ float value;
+
+ value = atof( token );
+
+ if( !allowNegative && value < 0.0f )
+ value = 1.0f;
+
+ return value;
+}
+
+/*
+===============
+atoi_neg
+
+atoi with an allowance for negative values
+===============
+*/
+int atoi_neg( char *token, qboolean allowNegative )
+{
+ int value;
+
+ value = atoi( token );
+
+ if( !allowNegative && value < 0 )
+ value = 1;
+
+ return value;
+}
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index ea3b7eef..5cc157e4 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -1288,3 +1288,6 @@ float VectorDistance( vec3_t v1, vec3_t v2 );
float VectorMinComponent( vec3_t v );
float VectorMaxComponent( vec3_t v );
float round( float v );
+
+float atof_neg( char *token, qboolean allowNegative );
+int atoi_neg( char *token, qboolean allowNegative );
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index fddbaf13..d83e76b7 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -1258,7 +1258,6 @@ Called when an alien touches a booster
*/
void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace )
{
- int maxAmmo, maxClips;
gclient_t *client = other->client;
if( !self->spawned )
@@ -1289,52 +1288,50 @@ void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace )
//==================================================================================
+#define TRAPPER_ACCURACY 10 // lower is better
/*
================
-ADef_fireonemeny
+ATrapper_FireOnEnemy
-Used by ADef2_Think to fire at enemy
+Used by ATrapper_Think to fire at enemy
================
*/
-void ADef_FireOnEnemy( gentity_t *self, int firespeed, float range )
+void ATrapper_FireOnEnemy( gentity_t *self, int firespeed, float range )
{
- vec3_t dirToTarget;
- vec3_t halfAcceleration, thirdJerk;
- float distanceToTarget = BG_FindRangeForBuildable( self->s.modelindex );
- int i;
-
- VectorScale( self->enemy->acceleration, 1.0f / 2.0f, halfAcceleration );
- VectorScale( self->enemy->jerk, 1.0f / 3.0f, thirdJerk );
-
- //O( time ) - worst case O( time ) = ( range * 1000 ) / speed
- for( i = 0; (float)( i * LOCKBLOB_SPEED ) / 1000.0f < distanceToTarget; i++ )
+ gentity_t *enemy = self->enemy;
+ vec3_t dirToTarget;
+ vec3_t halfAcceleration, thirdJerk;
+ float distanceToTarget = BG_FindRangeForBuildable( self->s.modelindex );
+ int lowMsec = 0;
+ int highMsec = (int)( (
+ ( ( distanceToTarget * LOCKBLOB_SPEED ) +
+ ( distanceToTarget * BG_FindSpeedForClass( enemy->client->ps.stats[ STAT_PCLASS ] ) ) ) /
+ ( LOCKBLOB_SPEED * LOCKBLOB_SPEED ) ) * 1000.0f );
+
+ VectorScale( enemy->acceleration, 1.0f / 2.0f, halfAcceleration );
+ VectorScale( enemy->jerk, 1.0f / 3.0f, thirdJerk );
+
+ // highMsec and lowMsec can only move toward
+ // one another, so the loop must terminate
+ while( highMsec - lowMsec > TRAPPER_ACCURACY )
{
- float time = (float)i / 1000.0f;
-
- if( i > ( ( (float)range * 1000.0f ) / LOCKBLOB_SPEED ) )
- {
- VectorSubtract( self->enemy->s.pos.trBase, self->s.pos.trBase, dirToTarget );
- distanceToTarget = VectorLength( dirToTarget );
-
- G_LogPrintf( "ADef_FireOnEnemy failed.\n"
- " %dth iteration\n enemy location: %v\n"
- " enemy accleration: %v\n enemy jerk: %v\n"
- " base location: %v\n distanceToTarget: %f\n",
- i, self->enemy->s.pos.trBase, self->enemy->acceleration,
- self->enemy->jerk, self->s.pos.trBase, distanceToTarget );
-
- return;
- }
+ int partitionMsec = ( highMsec + lowMsec ) / 2;
+ float time = (float)partitionMsec / 1000.0f;
+ float projectileDistance = LOCKBLOB_SPEED * time;
- VectorMA( self->enemy->s.pos.trBase, time, self->enemy->s.pos.trDelta,
- dirToTarget );
+ VectorMA( enemy->s.pos.trBase, time, enemy->s.pos.trDelta, dirToTarget );
VectorMA( dirToTarget, time * time, halfAcceleration, dirToTarget );
VectorMA( dirToTarget, time * time * time, thirdJerk, dirToTarget );
VectorSubtract( dirToTarget, self->s.pos.trBase, dirToTarget );
distanceToTarget = VectorLength( dirToTarget );
- distanceToTarget -= self->enemy->r.maxs[ 0 ];
+ if( projectileDistance < distanceToTarget )
+ lowMsec = partitionMsec;
+ else if( projectileDistance > distanceToTarget )
+ highMsec = partitionMsec;
+ else if( projectileDistance == distanceToTarget )
+ break; // unlikely to happen
}
VectorNormalize( dirToTarget );
@@ -1348,12 +1345,12 @@ void ADef_FireOnEnemy( gentity_t *self, int firespeed, float range )
/*
================
-ADef_checktarget
+ATrapper_CheckTarget
-Used by ADef2_Think to check enemies for validity
+Used by ATrapper_Think to check enemies for validity
================
*/
-qboolean ADef_CheckTarget( gentity_t *self, gentity_t *target, int range )
+qboolean ATrapper_CheckTarget( gentity_t *self, gentity_t *target, int range )
{
vec3_t distance;
trace_t trace;
@@ -1393,12 +1390,12 @@ qboolean ADef_CheckTarget( gentity_t *self, gentity_t *target, int range )
/*
================
-adef_findenemy
+ATrapper_FindEnemy
-Used by DDef2_Think to locate enemy gentities
+Used by ATrapper_Think to locate enemy gentities
================
*/
-void ADef_FindEnemy( gentity_t *ent, int range )
+void ATrapper_FindEnemy( gentity_t *ent, int range )
{
gentity_t *target;
@@ -1406,7 +1403,7 @@ void ADef_FindEnemy( gentity_t *ent, int range )
for( target = g_entities; target < &g_entities[ level.num_entities ]; target++ )
{
//if target is not valid keep searching
- if( !ADef_CheckTarget( ent, target, range ) )
+ if( !ATrapper_CheckTarget( ent, target, range ) )
continue;
//we found a target
@@ -1444,8 +1441,8 @@ void ATrapper_Think( gentity_t *self )
if( self->spawned && findOvermind( self ) )
{
//if the current target is not valid find a new one
- if( !ADef_CheckTarget( self, self->enemy, range ) )
- ADef_FindEnemy( self, range );
+ if( !ATrapper_CheckTarget( self, self->enemy, range ) )
+ ATrapper_FindEnemy( self, range );
//if a new target cannot be found don't do anything
if( !self->enemy )
@@ -1453,7 +1450,7 @@ void ATrapper_Think( gentity_t *self )
//if we are pointing at our target and we can fire shoot it
if( self->count < level.time )
- ADef_FireOnEnemy( self, firespeed, range );
+ ATrapper_FireOnEnemy( self, firespeed, range );
}
}
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 606d1875..840fc3bb 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -460,6 +460,7 @@ void G_ChangeTeam( gentity_t *ent, pTeam_t newTeam )
level.bankCredits[ ent->client->ps.clientNum ] = 0;
ent->client->ps.persistant[ PERS_CREDIT ] = 0;
+ ent->client->ps.persistant[ PERS_SCORE ] = 0;
ent->client->pers.classSelection = PCL_NONE;
ClientSpawn( ent, NULL, NULL, NULL );
}
diff --git a/src/game/g_main.c b/src/game/g_main.c
index 64715b20..8ae93622 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -59,8 +59,8 @@ vmCvar_t g_synchronousClients;
vmCvar_t g_warmup;
vmCvar_t g_doWarmup;
vmCvar_t g_restarted;
-vmCvar_t g_log;
-vmCvar_t g_logSync;
+vmCvar_t g_logFile;
+vmCvar_t g_logFileSync;
vmCvar_t g_blood;
vmCvar_t g_podiumDist;
vmCvar_t g_podiumDrop;
@@ -124,8 +124,8 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue },
{ &g_doWarmup, "g_doWarmup", "0", 0, 0, qtrue },
- { &g_log, "g_log", "games.log", CVAR_ARCHIVE, 0, qfalse },
- { &g_logSync, "g_logSync", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_logFile, "g_logFile", "games.log", CVAR_ARCHIVE, 0, qfalse },
+ { &g_logFileSync, "g_logFileSync", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse },
@@ -477,15 +477,15 @@ void G_InitGame( int levelTime, int randomSeed, int restart )
level.snd_fry = G_SoundIndex( "sound/player/fry.wav" ); // FIXME standing in lava / slime
- if( g_log.string[ 0 ] )
+ if( g_logFile.string[ 0 ] )
{
- if( g_logSync.integer )
- trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND_SYNC );
+ if( g_logFileSync.integer )
+ trap_FS_FOpenFile( g_logFile.string, &level.logFile, FS_APPEND_SYNC );
else
- trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND );
+ trap_FS_FOpenFile( g_logFile.string, &level.logFile, FS_APPEND );
if( !level.logFile )
- G_Printf( "WARNING: Couldn't open logfile: %s\n", g_log.string );
+ G_Printf( "WARNING: Couldn't open logfile: %s\n", g_logFile.string );
else
{
char serverinfo[ MAX_INFO_STRING ];
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index 94a1aabe..a28da78b 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -631,12 +631,6 @@ void teslaFire( gentity_t *ent )
// move origin a bit to come closer to the drawn gun muzzle
VectorMA( tent->s.origin2, 28, up, tent->s.origin2 );
-
- // no explosion at end if SURF_NOIMPACT, but still make the trail
- if( tr.surfaceFlags & SURF_NOIMPACT )
- tent->s.eventParm = 255; // don't make the explosion at the end
- else
- tent->s.eventParm = DirToByte( tr.plane.normal );
}
diff --git a/src/game/q_math.c b/src/game/q_math.c
index 62c5a5a8..78209fd1 100644
--- a/src/game/q_math.c
+++ b/src/game/q_math.c
@@ -1378,8 +1378,6 @@ float pointToLineDistance( const vec3_t p0, const vec3_t p1, const vec3_t p2 )
return c1 / c2;
}
-//TA: wolf trail stuff
-// Ridah
/*
=================
GetPerpendicularViewVector
@@ -1417,15 +1415,6 @@ void ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vP
VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
}
-float VectorDistance(vec3_t v1, vec3_t v2)
-{
- vec3_t dir;
-
- VectorSubtract(v2, v1, dir);
- return VectorLength(dir);
-}
-// done.
-
/*
================
VectorMaxComponent
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index 085fabbf..ab9db16c 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -261,7 +261,7 @@
#define TRAPPER_CREEPSIZE 30
#define TRAPPER_RANGE 400
#define TRAPPER_REPEAT 1000
-#define LOCKBLOB_SPEED 500.0f
+#define LOCKBLOB_SPEED 650.0f
#define LOCKBLOB_DOT 0.85f // max angle = acos( LOCKBLOB_DOT )
#define OVERMIND_BP 0
diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c
index 60e801cf..85b3fdb9 100644
--- a/src/ui/ui_main.c
+++ b/src/ui/ui_main.c
@@ -115,7 +115,6 @@ static char* netnames[] = {
};
static int gamecodetoui[] = {4,2,3,0,5,1,6};
-static int uitogamecode[] = {4,6,2,3,1,5,7};
static void UI_StartServerRefresh(qboolean full);
@@ -2663,27 +2662,6 @@ static qboolean UI_Handicap_HandleKey(int flags, float *special, int key) {
return qfalse;
}
-static qboolean UI_Effects_HandleKey(int flags, float *special, int key) {
- if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
-
- if (key == K_MOUSE2) {
- uiInfo.effectsColor--;
- } else {
- uiInfo.effectsColor++;
- }
-
- if( uiInfo.effectsColor > 6 ) {
- uiInfo.effectsColor = 0;
- } else if (uiInfo.effectsColor < 0) {
- uiInfo.effectsColor = 6;
- }
-
- trap_Cvar_SetValue( "color1", uitogamecode[uiInfo.effectsColor] );
- return qtrue;
- }
- return qfalse;
-}
-
static qboolean UI_ClanName_HandleKey(int flags, float *special, int key) {
if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
int i;
@@ -2974,25 +2952,6 @@ static qboolean UI_RedBlue_HandleKey(int flags, float *special, int key) {
return qfalse;
}
-static qboolean UI_Crosshair_HandleKey(int flags, float *special, int key) {
- if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
- if (key == K_MOUSE2) {
- uiInfo.currentCrosshair--;
- } else {
- uiInfo.currentCrosshair++;
- }
-
- if (uiInfo.currentCrosshair >= NUM_CROSSHAIRS) {
- uiInfo.currentCrosshair = 0;
- } else if (uiInfo.currentCrosshair < 0) {
- uiInfo.currentCrosshair = NUM_CROSSHAIRS - 1;
- }
- trap_Cvar_Set("cg_drawCrosshair", va("%d", uiInfo.currentCrosshair));
- return qtrue;
- }
- return qfalse;
-}
-
static qboolean UI_SelectedPlayer_HandleKey(int flags, float *special, int key) {