diff --git a/README.md b/README.md index 590d5a1..aa5016b 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,8 @@ You'll notice some differences and things that the original Bid For Power didn't - 3d particles for bubbles, antigrav rock and rock debris, that can be enabled with `cg_3dparticles` cvar. For 3d rock particles, you need 3 models inside models/effects/ directory: `pebble1.md3`, `pebble2.md3` and `pebble3.md3`; otherwise, it will appear the standard ones. That option is included in BFP OPTIONS menu in VIEW & FX, also with a new explosion type option in EXPLOSIONS: Ultra Hardcore - piercing weapons, like Razor disk, emit spark particles when hitting a player - beam struggle emits spark particles in the middle of both beams (in original BFP, emits in the zeroed origin) -- new particles: charge smoke, which appears when charging ki near the ground -- new cool missile trails (that replaces original Q3 missile smoke trails), includes rainbow option (it can be disabled by setting `cg_oldRocketTrail 1`). These trails are quite similar to the Rocket pod weapon from [Bazooka Q3 (BQ3)](https://planetquake.gamespy.com/View10ca.html?view=Articles.Detail&id=693) mod +- new charge smoke particle, which appears when charging ki near the ground +- new cool missile trails (it can be disabled by setting `cg_oldRocketTrail 1` to see Q3 missile smoke trails back). These trails are quite similar to the Rocket pod weapon from [Bazooka Q3 (BQ3)](https://planetquake.gamespy.com/View10ca.html?view=Articles.Detail&id=693) mod #### PMOVE: - balanced player pmove physics: @@ -135,6 +135,13 @@ You'll notice some differences and things that the original Bid For Power didn't - some Q3e patches are applied, like events and function stuff... - ... +### NEW FEATURES (BFPR): + +- `missileArch [0/1]`: the projectile, with missile or rdmissile attack type, moves like an arch across the crosshair +- `missileDlightRainbow [0/1]` to see rainbow dynamic rainbow light effect, and `missileTrailRainbow [0/1]` to see rainbow trail effect, in the skin config +- new confetti particle, use `explosionConfetti ` in the skin config for the explosion + + # How to build ### Introduction diff --git a/source/cgame/cg_effects.c b/source/cgame/cg_effects.c index 8ca214d..e237cd0 100644 --- a/source/cgame/cg_effects.c +++ b/source/cgame/cg_effects.c @@ -872,8 +872,8 @@ void CG_ExplosionEffect( vec3_t origin, vec3_t dir, bfpAttackSkinConfig_t *skinA skinAtkCfg->missileSoundPath, skinAtkCfg->missileSound, skinAtkCfg->chargeSoundPath, skinAtkCfg->chargeSound ); Com_Printf( " ^6flashSound^7=^3'%s'^7 (sfx^7=^3%d^7) ^6firingSound^7=^3'%s'^7 (sfx^7=^3%d^7)\n", skinAtkCfg->flashSoundPath, skinAtkCfg->flashSound, skinAtkCfg->firingSoundPath, skinAtkCfg->firingSound ); - Com_Printf( " ^6missileDlight^7=^3%d^7, ^6color^7=(^3%f^7,^3%f^7,^3%f^7)\n", - skinAtkCfg->missileDlight, skinAtkCfg->missileDlightColor[0], skinAtkCfg->missileDlightColor[1], skinAtkCfg->missileDlightColor[2] ); + Com_Printf( " ^6missileDlight^7=^3%d^7, ^6color^7=(^3%f^7,^3%f^7,^3%f^7)^7, ^6rainbow^7=^3%d^7\n", + skinAtkCfg->missileDlight, skinAtkCfg->missileDlightColor[0], skinAtkCfg->missileDlightColor[1], skinAtkCfg->missileDlightColor[2], skinAtkCfg->missileDlightRainbow ); Com_Printf( " ^6missileTrailFunc^7=^3%d^7, ^6trailTime^7=^3%d^7, ^6trailRadius^7=^3%d\n", skinAtkCfg->missileTrailFunc, skinAtkCfg->missileTrailTime, skinAtkCfg->missileTrailRadius ); Com_Printf( " ^6beamShader^7=^3'%s'^7 (shader^7=^3%d^7) ^6spiralBeamShader^7=^3'%s'^7 (shader^7=^3%d^7)\n", @@ -957,6 +957,9 @@ void CG_ExplosionEffect( vec3_t origin, vec3_t dir, bfpAttackSkinConfig_t *skinA leRing = CG_SpawnExplosionModel( origin, dir, LE_EXPLOSION_RING, cgs.media.ringFlashModel, cgs.media.railExplosionShader, 500 ); } CG_SmokeExplosion( origin, dir, skinAtkCfg ); // BFP - Explosion smoke + if ( skinAtkCfg->explosionConfetti > 0 ) { // BFPR - Confetti particles + CG_ParticleConfetti( cgs.media.whiteShader, origin, dir, skinAtkCfg->explosionConfetti, 5, 700.0f ); + } { const float MAX_SCALE = 25.0f, MAX_SCALEFACTOR = 6.0f; // limits to prevent too large scaling @@ -995,6 +998,8 @@ void CG_ExplosionEffect( vec3_t origin, vec3_t dir, bfpAttackSkinConfig_t *skinA leSphere->lightColor[0] = missileDlightColor[0]; leSphere->lightColor[1] = missileDlightColor[1]; leSphere->lightColor[2] = missileDlightColor[2]; + // BFPR - Missile dynamic rainbow light color + leSphere->lightRainbow = skinAtkCfg->missileDlightRainbow; } if ( cg_bigExplosions.integer > 0 ) { // BFP - Big explosions diff --git a/source/cgame/cg_ents.c b/source/cgame/cg_ents.c index f0dbc18..b067843 100644 --- a/source/cgame/cg_ents.c +++ b/source/cgame/cg_ents.c @@ -433,8 +433,15 @@ static void CG_Missile( centity_t *cent ) { */ // add dynamic light if ( skinAtkCfg->missileDlight ) { - trap_R_AddLightToScene(cent->lerpOrigin, skinAtkCfg->missileDlight, - skinAtkCfg->missileDlightColor[0], skinAtkCfg->missileDlightColor[1], skinAtkCfg->missileDlightColor[2] ); + if ( skinAtkCfg->missileDlightRainbow ) { // BFPR - Missile dynamic rainbow light color + float rf, gf, bf; + float elapsed = ( cg.time + cent->currentState.pos.trTime + cent->currentState.number * 97 ) * 0.0006f; + CG_HSVtoRGB( elapsed, 1.0f, 1.0f, &rf, &gf, &bf ); + trap_R_AddLightToScene( cent->lerpOrigin, skinAtkCfg->missileDlight, rf, gf, bf ); + } else { + trap_R_AddLightToScene( cent->lerpOrigin, skinAtkCfg->missileDlight, + skinAtkCfg->missileDlightColor[0], skinAtkCfg->missileDlightColor[1], skinAtkCfg->missileDlightColor[2] ); + } } // add missile sound diff --git a/source/cgame/cg_local.h b/source/cgame/cg_local.h index 6e5970f..18b4d35 100644 --- a/source/cgame/cg_local.h +++ b/source/cgame/cg_local.h @@ -271,6 +271,7 @@ typedef struct localEntity_s { float light; vec3_t lightColor; + qboolean lightRainbow; // BFPR - Cycle lightColor through rainbow hues (missileDlightRainbow), overrides lightColor leMarkType_t leMarkType; // mark to leave on fragment impact leBounceSoundType_t leBounceSoundType; @@ -341,11 +342,13 @@ typedef struct { // dynamic light int missileDlight; // missile dynamic light vec3_t missileDlightColor; // missile dynamic light color + qboolean missileDlightRainbow; // BFPR: missile dynamic light cycles through rainbow hues, overrides missileDlightColor // trail int missileTrailFunc; // missile trail function: "beam" / "rocket" / "spiralbeam" / "none" int missileTrailTime; // missile trail time (rocket trail only) int missileTrailRadius; // missile trail radius (rocket trail only) + qboolean missileTrailRainbow; // BFPR: missile trail cycles through rainbow hues, ignoring trail's fixed color // beam char beamShaderName[MAX_QPATH]; // beam shader @@ -385,6 +388,7 @@ typedef struct { qboolean explosionShell; // show explosion shell model int explosionRocks; // number of rocks int explosionSparks; // number of sparks + int explosionConfetti; // BFPR - number of confetti/leaf-like pieces int explosionSmoke; // number of smokes int explosionSmokeRadius; // explosion smoke radius int explosionSmokeLife; // explosion smoke duration @@ -1489,6 +1493,7 @@ void CG_KiTrail( int entityNum, vec3_t origin, qboolean remove, qhandle_t hShade void CG_BeamTrail( int entityNum, vec3_t origin, vec3_t muzzleOrigin, qhandle_t hShader ); void CG_CorkscrewTrail( int entityNum, vec3_t origin, vec3_t muzzleOrigin, qhandle_t beamShader, qhandle_t corkscrewShader ); void CG_MissileTrail( int entityNum, vec3_t origin, float radius, qhandle_t hShader, vec3_t color, qboolean rainbow ); +void CG_HSVtoRGB( float h, float s, float v, float *r, float *g, float *b ); // BFPR - Missile trails rainbow void CG_DrawMissileTrails( void ); // @@ -1747,3 +1752,5 @@ void CG_ParticleSparks (qhandle_t pshader, vec3_t origin, vec3_t vel); void CG_ParticleChargeSmoke (centity_t *cent, qhandle_t pshader, vec3_t origin, float size, float radialVel, float baseRadius); // BFP - Beam struggle spark particle void CG_ParticleBeamStruggleSpark (qhandle_t pshader, vec3_t origin, vec3_t vel); +// BFPR - Confetti particle +void CG_ParticleConfetti (qhandle_t pshader, vec3_t origin, vec3_t dir, int num, float size, float speed); diff --git a/source/cgame/cg_localents.c b/source/cgame/cg_localents.c index 3c18b72..0183b8f 100644 --- a/source/cgame/cg_localents.c +++ b/source/cgame/cg_localents.c @@ -514,6 +514,7 @@ CG_AddExplosion */ static void CG_AddExplosion( localEntity_t *ex ) { refEntity_t *ent; + vec3_t rainbowColor = { 0, 0, 0 }; // BFPR - For missileDlightRainbow ent = &ex->refEntity; @@ -543,12 +544,29 @@ static void CG_AddExplosion( localEntity_t *ex ) { VectorScale( ent->axis[2], scale, ent->axis[2] ); } + // BFPR - missileDlightRainbow + if ( ex->lightRainbow ) { + float posOffset = ex->refEntity.origin[0] + ex->refEntity.origin[1] + ex->refEntity.origin[2]; + float elapsed = ( cg.time + ex->startTime + posOffset ) * 0.0006f; + CG_HSVtoRGB( elapsed, 1.0f, 1.0f, &rainbowColor[0], &rainbowColor[1], &rainbowColor[2] ); + } + + // BFPR - missileDlightRainbow, tint the explosion sphere shader with the rainbow color too, + // so the model itself cycles in sync with its dynamic light + if ( ex->leType == LE_EXPLOSION_SPHERE && ex->lightRainbow ) { + ent->shaderRGBA[0] = rainbowColor[0] * 0xff; + ent->shaderRGBA[1] = rainbowColor[1] * 0xff; + ent->shaderRGBA[2] = rainbowColor[2] * 0xff; + ent->shaderRGBA[3] = 0xff; + } + // add the entity trap_R_AddRefEntityToScene(ent); // add the dlight if ( cg_lightExplosions.integer > 0 ) { // BFP - Dynamic explosion lights float light; + vec3_t lightColor; light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime ); if ( light < 0.5 ) { @@ -557,7 +575,15 @@ static void CG_AddExplosion( localEntity_t *ex ) { light = 1.0 - ( light - 0.5 ) * 2; } light = ex->light * light; - trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2] ); + + // BFPR - missileDlightRainbow + if ( ex->lightRainbow ) { + VectorCopy( rainbowColor, lightColor ); + } else { + VectorCopy( ex->lightColor, lightColor ); + } + + trap_R_AddLightToScene( ent->origin, light, lightColor[0], lightColor[1], lightColor[2] ); } } diff --git a/source/cgame/cg_particles.c b/source/cgame/cg_particles.c index f9d6281..d16411f 100644 --- a/source/cgame/cg_particles.c +++ b/source/cgame/cg_particles.c @@ -41,6 +41,7 @@ typedef struct particle_s vec3_t vel; vec3_t accel; int color; + vec3_t confettiColor; // BFPR - For confetti particle float alpha; float alphavel; int type; @@ -84,6 +85,7 @@ typedef enum P_SPARK, // BFP - Beam struggle spark P_ROCK_DEBRIS, // BFP - Bouncing rock fragment (explosion impact on ground) P_WATER_SPLASH, // BFP - Water entry splash bubble (upward arc, stops at surface) + P_CONFETTI, // BFPR - Falling confetti/leaf-like square that flutters down and flattens on landing P_SPRITE } particle_type_t; @@ -279,6 +281,159 @@ static void AddSparkParticle( cparticle_t *p, vec3_t org, float alpha ) trap_R_AddPolyToScene( p->pshader, 4, verts ); } +/* +=================== +AddConfettiParticle +=================== +*/ +static void AddConfettiParticle( cparticle_t *p, vec3_t org, float alpha ) // BFPR - Confetti particle +{ + polyVert_t verts[4]; + polyVert_t backVerts[4]; + vec3_t right, up; + float width, height; + + // BFP - All confetti physics (initial soft bounce, landing/flattening + // against the slope, leaf-like flight) lives here because this is the + // function that actually runs every frame for P_CONFETTI (see the switch + // in CG_AddParticleToScene) -- the equivalent block inside + // AddGenericParticle is never reached for this type. + if ( !p->custom ) + { + trace_t trace; + vec3_t confettiMins = {0, 0, -2}; + qboolean settled; + float dt = (float)( timenonscaled - p->time ) * 0.001f; // seconds since last update + if ( dt < 0.0f ) { + dt = 0.0f; + } else if ( dt > 0.1f ) { + dt = 0.1f; // clamp against huge frame gaps (loading, hitches) + } + + settled = ( VectorLength( p->vel ) < 100 ); + + // air drag on the burst velocity + if ( !settled ) + { + float dragFactor = 1.0f - ( 2.2f * dt ); + if ( dragFactor < 0.0f ) { + dragFactor = 0.0f; + } + VectorScale( p->vel, dragFactor, p->vel ); + settled = ( VectorLength( p->vel ) < 100 ); + } + + // same trace-every-frame pattern as P_ROCK_DEBRIS + CG_Trace( &trace, p->org, confettiMins, confettiMins, org, -1, CONTENTS_SOLID ); + if ( trace.fraction < 1.0f ) + { + VectorCopy( trace.endpos, p->org ); + VectorCopy( trace.endpos, org ); + + if ( settled || trace.plane.normal[2] >= 0.7f ) + { + VectorCopy( trace.plane.normal, p->accel ); + p->custom = 1; + VectorClear( p->vel ); + VectorClear( p->accel ); + } + } + else + { + // No collision this frame -- just advance to the physical position + VectorCopy( org, p->org ); + + if ( settled ) + { + // light, floaty gravity for the slow leaf-like fall + p->accel[2] = -500; + p->vel[2] = -130.0f; + } + } + + if ( settled ) + { + p->accumroll += 3; + p->rollBounceCount += 2; + } + else + { + // still carrying the explosion's burst speed: fast, erratic + // tumble, like a piece of paper being thrown + p->accumroll += 12; + p->rollBounceCount += 9; + } + + if ( p->accumroll >= 360 ) { + p->accumroll -= 360; + } + if ( p->accumroll < 0 ) { + p->accumroll += 360; + } + if ( p->rollBounceCount >= 360 ) { + p->rollBounceCount -= 360; + } + if ( p->rollBounceCount < 0 ) { + p->rollBounceCount += 360; + } + + p->time = timenonscaled; + } + + if ( p->custom ) + { + vec3_t normal, tangent; + VectorCopy( p->accel, normal ); + if ( VectorLength( normal ) < 0.5f ) { + VectorSet( normal, 0, 0, 1 ); // safety fallback + } + PerpendicularVector( tangent, normal ); + RotatePointAroundVector( right, normal, tangent, (float)p->accumroll ); + CrossProduct( normal, right, up ); + width = p->width; + height = p->height; + } + else + { + vec3_t angles; + vectoangles( rforward, angles ); + angles[ROLL] += (float)p->accumroll; + angles[PITCH] += (float)p->rollBounceCount; + AngleVectors( angles, NULL, right, up ); + width = p->width; + height = p->height; + } + + VectorMA( org, -height, up, verts[0].xyz ); + VectorMA( verts[0].xyz, -width, right, verts[0].xyz ); + Vector2Set( verts[0].st, 0, 0 ); + Byte4Set( verts[0].modulate, p->confettiColor[0]*255, p->confettiColor[1]*255, p->confettiColor[2]*255, alpha*255 ); + + VectorMA( org, -height, up, verts[1].xyz ); + VectorMA( verts[1].xyz, width, right, verts[1].xyz ); + Vector2Set( verts[1].st, 1, 0 ); + Byte4Set( verts[1].modulate, p->confettiColor[0]*255, p->confettiColor[1]*255, p->confettiColor[2]*255, alpha*255 ); + + VectorMA( org, height, up, verts[2].xyz ); + VectorMA( verts[2].xyz, width, right, verts[2].xyz ); + Vector2Set( verts[2].st, 1, 1 ); + Byte4Set( verts[2].modulate, p->confettiColor[0]*255, p->confettiColor[1]*255, p->confettiColor[2]*255, alpha*255 ); + + VectorMA( org, height, up, verts[3].xyz ); + VectorMA( verts[3].xyz, -width, right, verts[3].xyz ); + Vector2Set( verts[3].st, 0, 1 ); + Byte4Set( verts[3].modulate, p->confettiColor[0]*255, p->confettiColor[1]*255, p->confettiColor[2]*255, alpha*255 ); + + trap_R_AddPolyToScene( p->pshader, 4, verts ); + + // draw the back face too, since whiteShader has normal backface culling + backVerts[0] = verts[0]; + backVerts[1] = verts[3]; + backVerts[2] = verts[2]; + backVerts[3] = verts[1]; + trap_R_AddPolyToScene( p->pshader, 4, backVerts ); +} + /* =================== AddGenericParticle @@ -651,6 +806,9 @@ void CG_AddParticleToScene (cparticle_t *p, vec3_t org, float alpha) case P_SPARK: AddSparkParticle(p, org, alpha); break; + case P_CONFETTI: // BFPR - Confetti particle + AddConfettiParticle(p, org, alpha); + break; default: AddGenericParticle(p, org, alpha); break; @@ -717,6 +875,7 @@ void CG_AddParticles (void) || p->type == P_AURA // BFP - Add P_AURA to remove particles || p->type == P_ROCK_DEBRIS // BFP - Add P_ROCK_DEBRIS to remove particles || p->type == P_WATER_SPLASH // BFP - Add P_WATER_SPLASH to remove particles + || p->type == P_CONFETTI // BFPR - Add P_CONFETTI to remove particles || p->type == P_SPARK) // BFP - Add P_SPARK to remove particles { if (timenonscaled > p->endtime) @@ -1444,3 +1603,81 @@ void CG_ParticleBeamStruggleSpark (qhandle_t pshader, vec3_t origin, vec3_t vel) p->pshader = pshader; p->height = p->width = (rand() % 4) + 2; } + +// BFPR - Confetti particle +void CG_ParticleConfetti (qhandle_t pshader, vec3_t origin, vec3_t dir, int num, float size, float speed) { + int i; + vec3_t right, up, forward; + vec3_t vel, spawnOrg; + float sz, rSpread, uSpread; + + if ( num <= 0 ) { + return; + } + + // Build axes for the spread + VectorCopy( dir, forward ); + PerpendicularVector( right, forward ); + CrossProduct( forward, right, up ); + VectorNormalize( right ); + VectorNormalize( up ); + + for ( i = 0; i < num; i++ ) { + cparticle_t *p; + + if ( !free_particles ) return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->time = timenonscaled; + p->endtime = timenonscaled + 8000 + (rand() % 2000); // 8-10 seconds lifetime + p->startfade = p->endtime - 500; // starts fading out near the end + + VectorSet( p->confettiColor, random(), random(), random() ); + p->alpha = 1.0f; + p->alphavel = 0.0f; // fade is handled manually + + p->pmodel = 0; + p->type = P_CONFETTI; + p->pshader = pshader; + + // initial position with a bit of spread + VectorCopy( origin, spawnOrg ); + spawnOrg[0] += (rand() % 20) - 10; + spawnOrg[1] += (rand() % 20) - 10; + spawnOrg[2] += (rand() % 20) - 10; + VectorCopy( spawnOrg, p->org ); + + // initial explosive burst velocity + rSpread = (rand() % 1800) - 1000; + uSpread = (rand() % 4500) - 1000; + VectorScale( dir, speed, vel ); + VectorMA( vel, rSpread, right, vel ); + VectorMA( vel, uSpread, up, vel ); + VectorCopy( vel, p->vel ); + + p->accel[0] = 0; + p->accel[1] = 0; + p->accel[2] = -300.0f; // gravity + + // random size + sz = size * (0.5f + random()); + p->width = sz; + p->height = sz; + p->endwidth = sz; + p->endheight = sz; + + // random initial rotation + p->rollBounceCount = rand() % 360; + p->accumroll = 360; + + // phase/frequency for the leaf-like sideways sway + p->start = crandom() * (float)M_PI * 2.0f; + p->end = 1.5f + (crandom() * 1.5f); + + p->stopped = qfalse; + p->custom = 0; // 0 = still airborne, 1 = landed and flattened + } +} diff --git a/source/cgame/cg_skinconfig.c b/source/cgame/cg_skinconfig.c index ef2b15e..c28d778 100644 --- a/source/cgame/cg_skinconfig.c +++ b/source/cgame/cg_skinconfig.c @@ -312,6 +312,14 @@ static void CG_ParseSkinConfigBuffer( char *buf, bfpSkinConfig_t *config ) { } else { CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); } + } else if ( !Q_stricmpn( ptr, "missileDlightRainbow", 20 ) && ( ptr[20] == ' ' || ptr[20] == '\t' ) ) { // BFPR - missileDlightRainbow + ptr += 20; + if ( CG_SkinConfig_ReadAttackIndex( &ptr, &attackIdx ) ) { + CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); + config->attacks[attackIdx].missileDlightRainbow = atoi( value ); + } else { + CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); + } } else if ( !Q_stricmpn( ptr, "missileTrailFunc", 16 ) && ( ptr[16] == ' ' || ptr[16] == '\t' ) ) { ptr += 16; if ( CG_SkinConfig_ReadAttackIndex( &ptr, &attackIdx ) ) { @@ -336,6 +344,14 @@ static void CG_ParseSkinConfigBuffer( char *buf, bfpSkinConfig_t *config ) { } else { CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); } + } else if ( !Q_stricmpn( ptr, "missileTrailRainbow", 19 ) && ( ptr[19] == ' ' || ptr[19] == '\t' ) ) { // BFPR - missileTrailRainbow + ptr += 19; + if ( CG_SkinConfig_ReadAttackIndex( &ptr, &attackIdx ) ) { + CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); + config->attacks[attackIdx].missileTrailRainbow = atoi( value ); + } else { + CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); + } } else if ( !Q_stricmpn( ptr, "spiralBeamShader", 16 ) && ( ptr[16] == ' ' || ptr[16] == '\t' ) ) { ptr += 16; if ( CG_SkinConfig_ReadAttackIndex( &ptr, &attackIdx ) ) { @@ -575,6 +591,14 @@ static void CG_ParseSkinConfigBuffer( char *buf, bfpSkinConfig_t *config ) { } else { CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); } + } else if ( !Q_stricmpn( ptr, "explosionConfetti", 17 ) && ( ptr[17] == ' ' || ptr[17] == '\t' ) ) { // BFPR - explosionConfetti + ptr += 17; + if ( CG_SkinConfig_ReadAttackIndex( &ptr, &attackIdx ) ) { + CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); + config->attacks[attackIdx].explosionConfetti = atoi( value ); + } else { + CG_SkinConfig_ReadToken( &ptr, value, sizeof( value ) ); + } } else if ( !Q_stricmpn( ptr, "explosionSmokeRadius", 20 ) && ( ptr[20] == ' ' || ptr[20] == '\t' ) ) { ptr += 20; if ( CG_SkinConfig_ReadAttackIndex( &ptr, &attackIdx ) ) { @@ -769,6 +793,8 @@ void CG_SetDefaultSkinConfig( bfpSkinConfig_t *config ) { a->missileTrailFunc = MISSILE_TRAIL_FUNC_ROCKET; a->missileDlight = 200; a->missileDlightColor[0] = 1.0f; a->missileDlightColor[1] = 0.75f; a->missileDlightColor[2] = 0.0f; + a->missileDlightRainbow = 0; + a->missileTrailRainbow = 0; a->missileModelRotation = 0.2f; a->explosionRing = 1; a->explosionShell = 1; @@ -932,10 +958,10 @@ void CG_LoadSkinConfig( clientInfo_t *ci ) { a->missileSoundPath, a->missileSound, a->chargeSoundPath, a->chargeSound ); Com_Printf( " ^6flashSound^7=^3'%s'^7 (sfx^7=^3%d^7) ^6firingSound^7=^3'%s'^7 (sfx^7=^3%d^7)\n", a->flashSoundPath, a->flashSound, a->firingSoundPath, a->firingSound ); - Com_Printf( " ^6missileDlight^7=^3%d^7, ^6color^7=(^3%f^7,^3%f^7,^3%f^7)\n", - a->missileDlight, a->missileDlightColor[0], a->missileDlightColor[1], a->missileDlightColor[2] ); - Com_Printf( " ^6missileTrailFunc^7=^3%d^7, ^6trailTime^7=^3%d^7, ^6trailRadius^7=^3%d\n", - a->missileTrailFunc, a->missileTrailTime, a->missileTrailRadius ); + Com_Printf( " ^6missileDlight^7=^3%d^7, ^6color^7=(^3%f^7,^3%f^7,^3%f^7)^7, ^6rainbow^7=^3%d^7\n", + a->missileDlight, a->missileDlightColor[0], a->missileDlightColor[1], a->missileDlightColor[2], a->missileDlightRainbow ); + Com_Printf( " ^6missileTrailFunc^7=^3%d^7, ^6trailTime^7=^3%d^7, ^6trailRadius^7=^3%d^7, ^6trailRainbow^7=^3%d^7\n", + a->missileTrailFunc, a->missileTrailTime, a->missileTrailRadius, a->missileTrailRainbow ); Com_Printf( " ^6beamShader^7=^3'%s'^7 (shader^7=^3%d^7) ^6spiralBeamShader^7=^3'%s'^7 (shader^7=^3%d^7)\n", a->beamShaderName, a->beamShader, a->spiralBeamShaderName, a->spiralBeamShader ); Com_Printf( " ^6flashModel^7=^3'%s'^7 (model^7=^3%d^7) ^6flashShader^7=^3'%s'^7 (shader^7=^3%d^7)\n", @@ -950,8 +976,8 @@ void CG_LoadSkinConfig( clientInfo_t *ci ) { a->missileRadius, a->missileRadiusChargeMult, a->missileScaleFactor, a->missileScaleFactorChargeMult ); Com_Printf( " ^6explosionModel^7=^3'%s'^7 (model^7=^3%d^7) ^6explosionShader^7=^3'%s'^7 (shader^7=^3%d^7)\n", a->explosionModelName, a->explosionModel, a->explosionShaderName, a->explosionShader ); - Com_Printf( " ^6explosionRing^7=^3%d^7, ^6explosionShell^7=^3%d^7, ^6explosionRocks^7=^3%d^7, ^6explosionSparks^7=^3%d\n", - a->explosionRing, a->explosionShell, a->explosionRocks, a->explosionSparks ); + Com_Printf( " ^6explosionRing^7=^3%d^7, ^6explosionShell^7=^3%d^7, ^6explosionRocks^7=^3%d^7, ^6explosionSparks^7=^3%d^7, ^6explosionConfetti^7=^3%d\n", + a->explosionRing, a->explosionShell, a->explosionRocks, a->explosionSparks, a->explosionConfetti ); Com_Printf( " ^6explosionSmoke^7=^3%d^7, ^6smokeRadius^7=^3%d^7, ^6smokeLife^7=^3%d^7, ^6smokeSpeed^7=^3%d\n", a->explosionSmoke, a->explosionSmokeRadius, a->explosionSmokeLife, a->explosionSmokeSpeed ); Com_Printf( " ^6explosionScaleFactor^7=^3%f^7, ^6explosionScaleFactorChargeMult^7=^3%f\n", diff --git a/source/cgame/cg_trails.c b/source/cgame/cg_trails.c index 5080205..9d68dee 100644 --- a/source/cgame/cg_trails.c +++ b/source/cgame/cg_trails.c @@ -448,15 +448,16 @@ void CG_CorkscrewTrail( int entityNum, vec3_t origin, vec3_t muzzleOrigin, qhand } +// BFPR - Functions for a rainbow effect /* ===================== -HSVtoRGB +CG_HSVtoRGB ===================== */ static float ModFloat( float a, float b ) { return ( b <= 0.0f ) ? 0 : ( a - (int)( a / b ) * b ); } -static void HSVtoRGB( float h, float s, float v, float *r, float *g, float *b ) { +void CG_HSVtoRGB( float h, float s, float v, float *r, float *g, float *b ) { int i; float f, p, q, t; h = ModFloat( h, 1.0f ); @@ -591,7 +592,7 @@ void CG_DrawMissileTrails( void ) { float elapsed = ( cg.time - trail->rainbowStartTime ) * 0.0006f; float hue = (float)age / SEGMENT_LIFESPAN_MSEC; hue = ModFloat( elapsed, 1.0f ); - HSVtoRGB( hue, 1.0f, 1.0f, &rf, &gf, &bf ); + CG_HSVtoRGB( hue, 1.0f, 1.0f, &rf, &gf, &bf ); r = (byte)(rf * alphaByte); g = (byte)(gf * alphaByte); b = (byte)(bf * alphaByte); diff --git a/source/cgame/cg_weapons.c b/source/cgame/cg_weapons.c index 0e2d84f..3e82f22 100644 --- a/source/cgame/cg_weapons.c +++ b/source/cgame/cg_weapons.c @@ -466,7 +466,7 @@ void CG_RocketTrail( centity_t *ent, bfpAttackSkinConfig_t *skinAtkCfg ) { // BFP - Missile trail if ( cg_oldRocketTrail.integer <= 0 ) { - CG_MissileTrail( ent->currentState.number, origin, skinAtkCfg->missileTrailRadius, cgs.media.railCoreShader, color, qfalse ); + CG_MissileTrail( ent->currentState.number, origin, skinAtkCfg->missileTrailRadius, cgs.media.railCoreShader, color, skinAtkCfg->missileTrailRainbow ); } if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { @@ -956,7 +956,13 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent // add lightning bolt CG_LightningBolt( nonPredictedCent, nonPredictedCent->pe.muzzleOrigin, skinAtkCfg ); - if ( skinAtkCfg->missileDlightColor[0] > 0 + // BFPR - Missile dynamic rainbow light color + if ( skinAtkCfg->missileDlightRainbow ) { + float rf, gf, bf; + float elapsed = ( cg.time + cent->muzzleFlashTime + cent->currentState.number * 97 ) * 0.0006f; + CG_HSVtoRGB( elapsed, 1.0f, 1.0f, &rf, &gf, &bf ); + trap_R_AddLightToScene( parent->origin, 300 + (rand()&31), rf, gf, bf ); + } else if ( skinAtkCfg->missileDlightColor[0] > 0 || skinAtkCfg->missileDlightColor[1] > 0 || skinAtkCfg->missileDlightColor[1] > 0 ) { trap_R_AddLightToScene( parent->origin, 300 + (rand()&31), skinAtkCfg->missileDlightColor[0], diff --git a/source/game/bg_misc.c b/source/game/bg_misc.c index 1a5b8b2..751a911 100644 --- a/source/game/bg_misc.c +++ b/source/game/bg_misc.c @@ -769,6 +769,28 @@ void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t resu } } + +/* +================ +BG_LerpQuadraticSpline + +Interpolates a quadratic Bézier curve between 3 points +to generate a smooth, arch-shaped trajectory +================ +*/ +void BG_LerpQuadraticSpline( const vec3_t cp1, const vec3_t cp2, const vec3_t cp3, float t, vec3_t lerpBase ) { // BFPR - Interpolates a quadratic Bézier curve between 3 points + float quadB1 = t * t; + float quadB2 = 2 * t * ( 1 - t ); + float quadB3 = ( 1 - t ) * ( 1 - t ); + + // cp1: start point + // cp2: mid point + // cp3: end point + lerpBase[0] = cp1[0]*quadB1 + cp2[0]*quadB2 + cp3[0]*quadB3; + lerpBase[1] = cp1[1]*quadB1 + cp2[1]*quadB2 + cp3[1]*quadB3; + lerpBase[2] = cp1[2]*quadB1 + cp2[2]*quadB2 + cp3[2]*quadB3; +} + const char *eventnames[EV_MAX] = { // BFP - Events are declared in bg_events.h file #define EVENT_STRINGS diff --git a/source/game/bg_public.h b/source/game/bg_public.h index efafb6f..4c41de8 100644 --- a/source/game/bg_public.h +++ b/source/game/bg_public.h @@ -492,6 +492,7 @@ typedef struct { int missileGravity; // missile gravity float missileAcceleration; // missile acceleration int missileDuration; // missile lifetime duration + qboolean missileArch; // BFPR - Missile arch int multishot; // multishot, number of shots for a weapon qboolean bounces; // projectile can bounce @@ -809,6 +810,9 @@ typedef enum { void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ); void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ); +// BFPR - Interpolates a quadratic Bézier curve between 3 points +void BG_LerpQuadraticSpline( const vec3_t cp1, const vec3_t cp2, const vec3_t cp3, float t, vec3_t lerpBase ); + void BG_AddPredictableEventToPlayerstate( entity_event_t newEvent, int eventParm, playerState_t *ps, int entityNum ); void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ); diff --git a/source/game/bg_weaponconfig.c b/source/game/bg_weaponconfig.c index 0a8845e..ad7dc79 100644 --- a/source/game/bg_weaponconfig.c +++ b/source/game/bg_weaponconfig.c @@ -113,6 +113,7 @@ bfpWeapon_t *BG_SetDefaultBFPWeapon( void ) { wpCfg.movementPenalty = 0; wpCfg.missileGravity = 0; wpCfg.missileAcceleration = 0; + wpCfg.missileArch = 0; wpCfg.multishot = 0; wpCfg.bounces = qfalse; wpCfg.noZBounce = qfalse; @@ -177,6 +178,7 @@ bfpWeapon_t *BG_SetMonsterDefaultBFPWeapon( void ) { wpCfg.movementPenalty = 0; wpCfg.missileGravity = 0; wpCfg.missileAcceleration = 0; + wpCfg.missileArch = 0; wpCfg.multishot = 0; wpCfg.bounces = qfalse; wpCfg.noZBounce = qfalse; @@ -559,6 +561,13 @@ static void BG_ParseBFPWeaponConfigFile( char *buf ) { if ( cur ) { cur->missileAcceleration = (float)atof( value ); } + } else if ( !Q_stricmpn( ptr, "missileArch", 11 ) && ( ptr[11] == ' ' || ptr[11] == '\t' ) ) { // BFPR - Missile arch + char value[32]; + ptr += 11; + BG_ReadConfigToken( &ptr, value, sizeof( value ) ); + if ( cur ) { + cur->missileArch = atoi( value ); + } } else if ( !Q_stricmpn( ptr, "missileDuration", 15 ) && ( ptr[15] == ' ' || ptr[15] == '\t' ) ) { char value[32]; ptr += 15; @@ -794,8 +803,8 @@ void BG_LoadBFPWeaponConfig( void ) { Com_Printf( " ^2coneOfFireX: ^3%d ^2coneOfFireY: ^3%d\n", d->coneOfFireX, d->coneOfFireY ); Com_Printf( " ^2piercing: ^3%d ^2reflective: ^3%d ^2priority: ^3%d ^2blinding: ^3%d\n", d->piercing, d->reflective, d->priority, d->blinding ); - Com_Printf( " ^2missileGravity: ^3%d ^2missileAcceleration: ^3%f ^2missileDuration: ^3%d\n", - d->missileGravity, d->missileAcceleration, d->missileDuration ); + Com_Printf( " ^2missileGravity: ^3%d ^2missileAcceleration: ^3%f ^2missileDuration: ^3%d ^2missileArch: ^3%d\n", + d->missileGravity, d->missileAcceleration, d->missileDuration, d->missileArch ); Com_Printf( " ^2multishot: ^3%d ^2bounces: ^3%d ^2bounceFriction: ^3%f ^2noZBounce: ^3%d\n", d->multishot, d->bounces, d->bounceFriction, d->noZBounce ); Com_Printf( " ^2extraKnockback: ^3%d ^2railTrail: ^3%d ^2movementPenalty: ^3%d ^2explosionSpawn: ^3%d\n", @@ -1192,8 +1201,8 @@ void BG_LoadBFPAttacksetsConfig( void ) { Com_Printf( "\n ^2coneOfFireX: ^3%d ^2coneOfFireY: ^3%d", wpCfg->coneOfFireX, wpCfg->coneOfFireY ); Com_Printf( "\n ^2piercing: ^3%d ^2reflective: ^3%d ^2priority: ^3%d ^2blinding: ^3%d", wpCfg->piercing, wpCfg->reflective, wpCfg->priority, wpCfg->blinding ); - Com_Printf( "\n ^2missileGravity: ^3%d ^2missileAcceleration: ^3%f ^2missileDuration: ^3%d", - wpCfg->missileGravity, wpCfg->missileAcceleration, wpCfg->missileDuration ); + Com_Printf( "\n ^2missileGravity: ^3%d ^2missileAcceleration: ^3%f ^2missileDuration: ^3%d ^2missileArch: ^3%d", + wpCfg->missileGravity, wpCfg->missileAcceleration, wpCfg->missileDuration, wpCfg->missileArch ); Com_Printf( "\n ^2multishot: ^3%d ^2bounces: ^3%d ^2bounceFriction: ^3%f ^2noZBounce: ^3%d", wpCfg->multishot, wpCfg->bounces, wpCfg->bounceFriction, wpCfg->noZBounce ); Com_Printf( "\n ^2extraKnockback: ^3%d ^2railTrail: ^3%d ^2movementPenalty: ^3%d ^2explosionSpawn: ^3%d", diff --git a/source/game/g_cmds.c b/source/game/g_cmds.c index e0ad8cf..4d02aee 100644 --- a/source/game/g_cmds.c +++ b/source/game/g_cmds.c @@ -1747,7 +1747,7 @@ void Cmd_Stats_f( gentity_t *ent ) { Cmd_BFP_Fly_f ===================== */ -void Cmd_BFP_Fly_f( gentity_t* ent ) { // BFP - Flight +static void Cmd_BFP_Fly_f( gentity_t* ent ) { // BFP - Flight // BFP - No flight if ( g_noFlight.integer > 0 ) { @@ -1763,71 +1763,24 @@ void Cmd_BFP_Fly_f( gentity_t* ent ) { // BFP - Flight } } -/* -===================== -Cmd_BFP_SetKiCharge_f -===================== -*/ -void Cmd_BFP_SetKiCharge_f( gentity_t* ent ) { // BFP - Set Ki charge - // BFP - NOTE: Charging only when ki use has been toggled, - // but it's a charging pose animation only, - // what were they (original BFP devs) thinking? - - // Nah, that implementation doesn't make sense, - // if you want silly and useless stuff like that, play it in original BFP - if ( ent->client && ent->client->ps.pm_type != PM_DEAD - && ( ent->client->ps.eFlags & EF_AURA ) ) { - ent->client->ps.torsoTimer = ent->client->ps.legsTimer = 0; - ent->client->ps.legsAnim = LEGS_CHARGE; - ent->client->ps.torsoAnim = TORSO_CHARGE; - } -} - -/* -===================== -Cmd_BFP_SetKiUse_f -===================== -*/ -void Cmd_BFP_SetKiUse_f( gentity_t* ent ) { // BFP - Set Ki use - // BFP - NOTE: Originally, an unfinished command... -#if 0 - if ( ent->client->ps.pm_type != PM_DEAD ) { - ent->client->ps.eFlags |= EF_KI_BOOST; - } -#endif -} - /* ===================== Cmd_BFP_KiUseToggle_f ===================== */ -void Cmd_BFP_KiUseToggle_f( gentity_t* ent ) { // BFP - Ki use toggle +static void Cmd_BFP_KiUseToggle_f( gentity_t* ent ) { // BFP - Ki use toggle if ( ent->client && ent->client->ps.pm_type != PM_DEAD ) { ent->client->ps.eFlags ^= EF_KI_BOOST; } } -/* -===================== -Cmd_BFP_SetKiIdle_f -===================== -*/ -void Cmd_BFP_SetKiIdle_f( gentity_t* ent ) { // BFP - Set Ki idle - - // BFP - NOTE: originally... Ki idling means disabling the ki? Sounds like it isn't operating... - if ( ent->client && ent->client->ps.pm_type != PM_DEAD ) { - ent->client->ps.eFlags &= ~EF_KI_BOOST; - } -} - /* ===================== Cmd_BFP_SelectCharacter_f ===================== */ -void Cmd_BFP_SelectCharacter_f( gentity_t* ent ) { // BFP - Select character +static void Cmd_BFP_SelectCharacter_f( gentity_t* ent ) { // BFP - Select character char characterselected[MAX_TOKEN_CHARS]; // BFP - NOTE: That command was left without finishing the implementation to change the character of this way @@ -1847,7 +1800,7 @@ void Cmd_BFP_SelectCharacter_f( gentity_t* ent ) { // BFP - Select character Cmd_BFP_Block_f ================= */ -void Cmd_BFP_Block_f( gentity_t* ent ) { // BFP - Block +static void Cmd_BFP_Block_f( gentity_t* ent ) { // BFP - Block if ( ent->client && ent->client->ps.pm_type != PM_DEAD ) { ent->client->ps.pm_flags |= PMF_BLOCK; @@ -1855,14 +1808,6 @@ void Cmd_BFP_Block_f( gentity_t* ent ) { // BFP - Block } } -void Cmd_BFP_StartMelee_f( gentity_t* ent ) { // BFP - Start melee - // BFP - NOTE: That command was left without finishing the implementation to start melee -} - -void Cmd_BFP_StopMelee_f( gentity_t* ent ) { // BFP - Stop melee - // BFP - NOTE: That command was left without finishing the implementation to stop melee -} - /* @@ -1973,22 +1918,12 @@ void ClientCommand( int clientNum ) { Cmd_Stats_f( ent ); else if (Q_stricmp (cmd, "fly") == 0) // BFP - Flight Cmd_BFP_Fly_f( ent ); - else if (Q_stricmp (cmd, "set_ki_charge") == 0) // BFP - Set Ki charge - Cmd_BFP_SetKiCharge_f( ent ); - else if (Q_stricmp (cmd, "set_ki_use") == 0) // BFP - Set Ki use - Cmd_BFP_SetKiUse_f( ent ); else if (Q_stricmp (cmd, "kiusetoggle") == 0) // BFP - Ki use toggle Cmd_BFP_KiUseToggle_f( ent ); - else if (Q_stricmp (cmd, "set_ki_idle") == 0) // BFP - Set Ki idle - Cmd_BFP_SetKiIdle_f( ent ); else if (Q_stricmp (cmd, "selectcharacter") == 0) // BFP - Select character Cmd_BFP_SelectCharacter_f( ent ); else if (Q_stricmp (cmd, "block") == 0) // BFP - Block Cmd_BFP_Block_f( ent ); - else if (Q_stricmp (cmd, "start_melee") == 0) // BFP - Start melee - Cmd_BFP_StartMelee_f( ent ); - else if (Q_stricmp (cmd, "stop_melee") == 0) // BFP - Stop melee - Cmd_BFP_StopMelee_f( ent ); else trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) ); } diff --git a/source/game/g_local.h b/source/game/g_local.h index 9449dc7..fe7b690 100644 --- a/source/game/g_local.h +++ b/source/game/g_local.h @@ -204,6 +204,13 @@ struct gentity_s { float homing; // BFP - Homing float homingRange; // BFP - Homing range + + // BFPR - Missile arch + vec3_t archStartPoint; // missile arch: spawn origin + vec3_t archControlPoint; // missile arch: control point derived from launch offset (Bézier P1) + vec3_t archTargetPoint; // missile arch: crosshair trace endpoint, locked at spawn (Bézier P2) + int archStartTime; // missile arch: level.time at spawn + int archDuration; // missile arch: total flight time in ms, distance/speed based }; #define SPAWN_HEIGHT 0.0f @@ -756,7 +763,6 @@ void ClientCommand( int clientNum ); void ClientThink( int clientNum ); void ClientEndFrame( gentity_t *ent ); void G_RunClient( gentity_t *ent ); -void Cmd_BFP_Fly_f( gentity_t *ent ); // BFP - Flight // // g_team.c diff --git a/source/game/g_missile.c b/source/game/g_missile.c index 592d1b9..d49ea76 100644 --- a/source/game/g_missile.c +++ b/source/game/g_missile.c @@ -816,6 +816,38 @@ static void G_MissileAcceleration( gentity_t *ent ) { // BFP - Missile accelerat } } +/* +================ +G_MissileArch +================ +*/ +static void G_MissileArch( gentity_t *ent ) { // BFPR - Missile arch + if ( ent && ent->bfpWeapon && ent->bfpWeapon->missileArch ) { + vec3_t curPos, nextPos, dir; + float t0, t1, dt, speed; + int nextTime; + + dt = FRAMETIME * 0.001f; // seconds per server frame, same units as TR_LINEAR + t0 = (float)( level.time - ent->archStartTime ) / (float) ent->archDuration; + nextTime = level.time + FRAMETIME; + t1 = (float)( nextTime - ent->archStartTime ) / (float) ent->archDuration; + + // evaluate the quadratic Bézier at "now" + BG_LerpQuadraticSpline( ent->archTargetPoint, ent->archControlPoint, ent->archStartPoint, t0, curPos ); + BG_LerpQuadraticSpline( ent->archTargetPoint, ent->archControlPoint, ent->archStartPoint, t1, nextPos ); + + // derive an instantaneous velocity from the curve's local slope + VectorSubtract( nextPos, curPos, dir ); + speed = VectorLength( dir ); + if ( speed > 0.001f ) { + VectorScale( dir, 1.0f / dt, ent->s.pos.trDelta ); // units/sec + } + + VectorCopy( curPos, ent->r.currentOrigin ); + VectorCopy( curPos, ent->s.pos.trBase ); + ent->s.pos.trTime = level.time; + } +} /* ===================== @@ -1168,6 +1200,9 @@ void G_RunMissile( gentity_t *ent ) { // BFP - Reflective G_Reflective( ent, qfalse, ent->r.currentOrigin ); + // BFPR - Missile arch + G_MissileArch( ent ); + // BFP - Missile gravity G_MissileGravity( ent, &tr ); diff --git a/source/game/g_weapon.c b/source/game/g_weapon.c index 3c8d6ed..fd00b7f 100644 --- a/source/game/g_weapon.c +++ b/source/game/g_weapon.c @@ -385,6 +385,55 @@ gentity_t *G_BFPFireProjectileWeapon( gentity_t *self, vec3_t start, vec3_t dir, ApplyConeOfFire( wpCfg->coneOfFireX, wpCfg->coneOfFireY ); } + // BFPR - Missile arch + if ( wpCfg->missileArch ) { + trace_t archTr; + vec3_t archEnd, archRight, archUp, launchDir; + float length, lateral, vertical, speed = wpCfg->missileSpeed; + + // where the crosshair trace actually stops + VectorMA( start, 10240, dir, archEnd ); + trap_Trace( &archTr, start, NULL, NULL, archEnd, self->s.number, MASK_SHOT ); + if ( archTr.fraction < 1.0f ) { + float pushDist = 4096; + float availableDist = Distance( start, archTr.endpos ); + + if ( pushDist > availableDist * 0.5f ) { + pushDist = availableDist * 0.5f; // never push farther than the real half distance + } + VectorMA( archTr.endpos, pushDist, dir, proj->archTargetPoint ); + } else { + VectorCopy( archTr.endpos, proj->archTargetPoint ); + } + + VectorCopy( start, proj->archStartPoint ); + + // random launch from the center, changes the muzzle position a bit + lateral = crandom() * 0.15f; + vertical = crandom() * 0.15f; + + VectorCopy( dir, launchDir ); + AngleVectors( self->client->ps.viewangles, NULL, archRight, archUp ); + VectorMA( launchDir, lateral, archRight, launchDir ); + VectorMA( launchDir, vertical, archUp, launchDir ); + VectorNormalize( launchDir ); + + length = Distance( proj->archStartPoint, proj->archTargetPoint ); + VectorMA( proj->archStartPoint, length * 0.5f, launchDir, proj->archControlPoint ); + + proj->archStartTime = level.time; + if ( speed <= 0 ) { + speed = 1; + } + proj->archDuration = (int)( 1000.0f * length / speed ); + if ( proj->archDuration < 1 ) { + proj->archDuration = 1; + } + + VectorMA( start, ( lateral / 0.15 ) * 8.0f, archRight, start ); + VectorMA( start, ( vertical / 0.15 ) * 8.0f, archUp, start ); + } + if ( wpCfg->chargeAttack || wpCfg->chargeAutoFire ) { G_ChargeDamageScaling( proj, r ); } diff --git a/source/q3_ui/ui_players.c b/source/q3_ui/ui_players.c index f5b40b4..e4a842a 100644 --- a/source/q3_ui/ui_players.c +++ b/source/q3_ui/ui_players.c @@ -963,35 +963,8 @@ void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_ } // torso animation - // BFP - Ki attack handling animations - if ( torsoAnim == TORSO_STAND ) { // || torsoAnim == TORSO_STAND2 ) { // BFP doesn't use this animation - switch ( weaponNum ) { - case WP_ATTACK_0: torsoAnim = TORSO_ATTACK0_PREPARE; break; - case WP_ATTACK_1: torsoAnim = TORSO_ATTACK1_PREPARE; break; - case WP_ATTACK_2: torsoAnim = TORSO_ATTACK2_PREPARE; break; - case WP_ATTACK_3: torsoAnim = TORSO_ATTACK3_PREPARE; break; - case WP_ATTACK_4: torsoAnim = TORSO_ATTACK4_PREPARE; break; - default: torsoAnim = TORSO_STAND; break; - } - } - - // BFP - Ki attack handling animations - if ( torsoAnim == TORSO_STAND ) { - switch ( weaponNum ) { - case WP_ATTACK_0: torsoAnim = TORSO_ATTACK0_PREPARE; break; - case WP_ATTACK_1: torsoAnim = TORSO_ATTACK1_PREPARE; break; - case WP_ATTACK_2: torsoAnim = TORSO_ATTACK2_PREPARE; break; - case WP_ATTACK_3: torsoAnim = TORSO_ATTACK3_PREPARE; break; - case WP_ATTACK_4: torsoAnim = TORSO_ATTACK4_PREPARE; break; - default: torsoAnim = TORSO_STAND; break; - } - pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH; - //FIXME play firing sound here - } - currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; -#if 1 if ( weaponNum != pi->currentWeapon ) { // || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) { // BFP doesn't use these animations pi->pendingTorsoAnim = torsoAnim; } @@ -1002,5 +975,4 @@ void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_ pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); } -#endif }