/* * CG_AddLocalEntities */ void CG_AddLocalEntities( void ) { #define FADEINFRAMES 2 int f; lentity_t *le, *next, *hnode; entity_t *ent; float scale, frac, fade, time, scaleIn, fadeIn; float backlerp; vec3_t angles; time = cg.frameTime; backlerp = 1.0f - cg.lerpfrac; hnode = &cg_localents_headnode; for( le = hnode->next; le != hnode; le = next ) { next = le->next; frac = ( cg.time - le->start ) * 0.01f; f = ( int )floor( frac ); clamp_low( f, 0 ); // it's time to DIE if( f >= le->frames - 1 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); continue; } if( le->frames > 1 ) { scale = 1.0f - frac / ( le->frames - 1 ); scale = bound( 0.0f, scale, 1.0f ); fade = scale * 255.0f; // quick fade in, if time enough if( le->frames > FADEINFRAMES * 2 ) { scaleIn = frac / (float)FADEINFRAMES; clamp( scaleIn, 0.0f, 1.0f ); fadeIn = scaleIn * 255.0f; } else fadeIn = 255.0f; } else { scale = 1.0f; fade = 255.0f; fadeIn = 255.0f; } ent = &le->ent; if( le->light && scale ) CG_AddLightToScene( ent->origin, le->light * scale, le->lightcolor[0], le->lightcolor[1], le->lightcolor[2] ); if( le->type == LE_LASER ) { CG_QuickPolyBeam( ent->origin, ent->origin2, ent->radius, ent->customShader ); // wsw : jalfixme: missing the color (comes inside ent->skinnum) continue; } if( le->type == LE_DASH_SCALE ) { if( f < 1 ) ent->scale = 0.15 * frac; else { VecToAngles( &ent->axis[AXIS_RIGHT], angles ); ent->axis[1*3+1] += 0.005f *sin( DEG2RAD ( angles[YAW] ) ); //length ent->axis[1*3+0] += 0.005f *cos( DEG2RAD ( angles[YAW] ) ); //length ent->axis[0*3+1] += 0.008f *cos( DEG2RAD ( angles[YAW] ) ); //width ent->axis[0*3+0] -= 0.008f *sin( DEG2RAD ( angles[YAW] ) ); //width ent->axis[2*3+2] -= 0.052f; //height if( ent->axis[AXIS_UP+2] <= 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } if( le->type == LE_PUFF_SCALE ) { if( le->frames - f < 4 ) ent->scale = 1.0f - 1.0f * ( frac - abs( 4-le->frames ) )/4; } if( le->type == LE_PUFF_SHRINK ) { if( frac < 3 ) ent->scale = 1.0f - 0.2f * frac/4; else { ent->scale = 0.8 - 0.8*( frac-3 )/3; VectorScale( le->velocity, 0.85f, le->velocity ); } } if( le->type == LE_EXPLOSION_TRACER ) { if( cg.time - ent->rotation > 10.0f ) { ent->rotation = cg.time; if( ent->radius - 16*frac > 4 ) CG_Explosion_Puff( ent->origin, ent->radius-16*frac, le->frames - f ); } } switch( le->type ) { case LE_NO_FADE: break; case LE_RGB_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[0] = ( uint8_t )( fade * le->color[0] ); ent->shaderRGBA[1] = ( uint8_t )( fade * le->color[1] ); ent->shaderRGBA[2] = ( uint8_t )( fade * le->color[2] ); break; case LE_SCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = 1.0f + 1.0f / scale; ent->scale = min( ent->scale, 5.0f ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; case LE_INVERSESCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = scale + 0.1f; clamp( ent->scale, 0.1f, 1.0f ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; case LE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; default: break; } ent->backlerp = backlerp; if ( le->avelocity[0] || le->avelocity[1] || le->avelocity[2] ) { VectorMA( le->angles, time, le->avelocity, le->angles ); AnglesToAxis( le->angles, le->ent.axis ); } // apply rotational friction if( le->bounce ) { // FIXME? int i; const float adj = 100 * 6 * time; // magic constants here for( i = 0; i < 3; i++ ) { if( le->avelocity[i] > 0.0f ) { le->avelocity[i] -= adj; if( le->avelocity[i] < 0.0f ) { le->avelocity[i] = 0.0f; } } else if ( le->avelocity[i] < 0.0f ) { le->avelocity[i] += adj; if ( le->avelocity[i] > 0.0f ) { le->avelocity[i] = 0.0f; } } } } if( le->bounce ) { trace_t trace; vec3_t next_origin; VectorMA( ent->origin, time, le->velocity, next_origin ); CG_Trace( &trace, ent->origin, debris_mins, debris_maxs, next_origin, 0, MASK_SOLID ); // remove the particle when going out of the map if( ( trace.contents & CONTENTS_NODROP ) || ( trace.surfFlags & SURF_SKY ) ) { le->frames = 0; } else if( trace.fraction != 1.0 ) // found solid { float dot; float xyzspeed, orig_xyzspeed; float bounce; orig_xyzspeed = VectorLength( le->velocity ); // Reflect velocity dot = DotProduct( le->velocity, trace.plane.normal ); VectorMA( le->velocity, -2.0f * dot, trace.plane.normal, le->velocity ); //put new origin in the impact point, but move it out a bit along the normal VectorMA( trace.endpos, 1, trace.plane.normal, ent->origin ); // make sure we don't gain speed from bouncing off bounce = 2.0f * le->bounce * 0.01f; if( bounce < 1.5f ) bounce = 1.5f; xyzspeed = orig_xyzspeed / bounce; VectorNormalize( le->velocity ); VectorScale( le->velocity, xyzspeed, le->velocity ); //the entity has not speed enough. Stop checks if( xyzspeed * time < 1.0f ) { trace_t traceground; vec3_t ground_origin; //see if we have ground VectorCopy( ent->origin, ground_origin ); ground_origin[2] += ( debris_mins[2] - 4 ); CG_Trace( &traceground, ent->origin, debris_mins, debris_maxs, ground_origin, 0, MASK_SOLID ); if( traceground.fraction != 1.0 ) { le->bounce = 0; VectorClear( le->velocity ); VectorClear( le->accel ); VectorClear( le->avelocity ); if( le->type == LE_EXPLOSION_TRACER ) { // blx le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } } else { VectorCopy( ent->origin, ent->origin2 ); VectorCopy( next_origin, ent->origin ); } } else { VectorCopy( ent->origin, ent->origin2 ); VectorMA( ent->origin, time, le->velocity, ent->origin ); } VectorCopy( ent->origin, ent->lightingOrigin ); VectorMA( le->velocity, time, le->accel, le->velocity ); CG_AddEntityToScene( ent ); } }
// RAFAEL void Trap_Think (edict_t *ent) { edict_t *target = NULL; edict_t *best = NULL; vec3_t vec; int len, i; int oldlen = 8000; vec3_t forward, right, up; if (!ent) { return; } if (ent->timestamp < level.time) { BecomeExplosion1(ent); return; } ent->nextthink = level.time + 0.1; if (!ent->groundentity) { return; } /* ok lets do the blood effect */ if (ent->s.frame > 4) { if (ent->s.frame == 5) { if (ent->wait == 64) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/trapdown.wav"), 1, ATTN_IDLE, 0); } ent->wait -= 2; ent->delay += level.time; for (i = 0; i < 3; i++) { best = G_Spawn(); if (strcmp(ent->enemy->classname, "monster_gekk") == 0) { best->s.modelindex = gi.modelindex("models/objects/gekkgib/torso/tris.md2"); best->s.effects |= TE_GREENBLOOD; } else if (ent->mass > 200) { best->s.modelindex = gi.modelindex("models/objects/gibs/chest/tris.md2"); best->s.effects |= TE_BLOOD; } else { best->s.modelindex = gi.modelindex("models/objects/gibs/sm_meat/tris.md2"); best->s.effects |= TE_BLOOD; } AngleVectors(ent->s.angles, forward, right, up); RotatePointAroundVector(vec, up, right, ((360.0 / 3) * i) + ent->delay); VectorMA(vec, ent->wait / 2, vec, vec); VectorAdd(vec, ent->s.origin, vec); VectorAdd(vec, forward, best->s.origin); best->s.origin[2] = ent->s.origin[2] + ent->wait; VectorCopy(ent->s.angles, best->s.angles); best->solid = SOLID_NOT; best->s.effects |= EF_GIB; best->takedamage = DAMAGE_YES; best->movetype = MOVETYPE_TOSS; best->svflags |= SVF_MONSTER; best->deadflag = DEAD_DEAD; VectorClear(best->mins); VectorClear(best->maxs); best->watertype = gi.pointcontents(best->s.origin); if (best->watertype & MASK_WATER) { best->waterlevel = 1; } best->nextthink = level.time + 0.1; best->think = G_FreeEdict; gi.linkentity(best); } if (ent->wait < 19) { ent->s.frame++; } return; } ent->s.frame++; if (ent->s.frame == 8) { ent->nextthink = level.time + 1.0; ent->think = G_FreeEdict; best = G_Spawn(); SP_item_foodcube(best); VectorCopy(ent->s.origin, best->s.origin); best->s.origin[2] += 16; best->velocity[2] = 400; best->count = ent->mass; gi.linkentity(best); return; } return; } ent->s.effects &= ~EF_TRAP; if (ent->s.frame >= 4) { ent->s.effects |= EF_TRAP; VectorClear(ent->mins); VectorClear(ent->maxs); } if (ent->s.frame < 4) { ent->s.frame++; } while ((target = findradius(target, ent->s.origin, 256)) != NULL) { if (target == ent) { continue; } if (!(target->svflags & SVF_MONSTER) && !target->client) { continue; } if (target->health <= 0) { continue; } if (!visible(ent, target)) { continue; } if (!best) { best = target; continue; } VectorSubtract(ent->s.origin, target->s.origin, vec); len = VectorLength(vec); if (len < oldlen) { oldlen = len; best = target; } } /* pull the enemy in */ if (best) { vec3_t forward; if (best->groundentity) { best->s.origin[2] += 1; best->groundentity = NULL; } VectorSubtract(ent->s.origin, best->s.origin, vec); len = VectorLength(vec); if (best->client) { VectorNormalize(vec); VectorMA(best->velocity, 250, vec, best->velocity); } else { best->ideal_yaw = vectoyaw(vec); M_ChangeYaw(best); AngleVectors(best->s.angles, forward, NULL, NULL); VectorScale(forward, 256, best->velocity); } gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/trapsuck.wav"), 1, ATTN_IDLE, 0); if (len < 32) { if (best->mass < 400) { T_Damage(best, ent, ent->owner, vec3_origin, best->s.origin, vec3_origin, 100000, 1, 0, MOD_TRAP); ent->enemy = best; ent->wait = 64; VectorCopy(ent->s.origin, ent->s.old_origin); ent->timestamp = level.time + 30; if (deathmatch->value) { ent->mass = best->mass / 4; } else { ent->mass = best->mass / 10; } /* ok spawn the food cube */ ent->s.frame = 5; } else { BecomeExplosion1(ent); return; } } } }
int Pickup_Powerup( gentity_t *ent, gentity_t *other ) { int quantity; int i; gclient_t *client; if ( !other->client->ps.powerups[ent->item->giTag] ) { // round timing to seconds to make multiple powerup timers // count in sync other->client->ps.powerups[ent->item->giTag] = level.time - ( level.time % 1000 ); } if ( ent->count ) { quantity = ent->count; } else { quantity = ent->item->quantity; } other->client->ps.powerups[ent->item->giTag] += quantity * 1000; // give any nearby players a "denied" anti-reward for ( i = 0 ; i < level.maxclients ; i++ ) { vec3_t delta; float len; vec3_t forward; trace_t tr; client = &level.clients[i]; if ( client == other->client ) { continue; } if ( client->pers.connected == CON_DISCONNECTED ) { continue; } if ( client->ps.stats[STAT_HEALTH] <= 0 ) { continue; } // if same team in team game, no sound // cannot use OnSameTeam as it expects to g_entities, not clients if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam ) { continue; } // if too far away, no sound VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta ); len = VectorNormalize( delta ); if ( len > 192 ) { continue; } // if not facing, no sound AngleVectors( client->ps.viewangles, forward, NULL, NULL ); if ( DotProduct( delta, forward ) < 0.4 ) { continue; } // if not line of sight, no sound trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID ); if ( tr.fraction != 1.0 ) { continue; } // anti-reward client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD; } return RESPAWN_POWERUP; }
//----------------------------------------------------------------------------- // Purpose: This takes the current place the NPC's trying to get to, figures out // what keys to press to get the vehicle to go there, and then sends // them to the vehicle. //----------------------------------------------------------------------------- void CNPC_CraneDriver::DriveVehicle( void ) { // No targets? if ( !GetEnemy() && m_vecDesiredPosition == vec3_origin ) return; Vector vecTarget = m_vecDesiredPosition; // Track our targets if ( m_hPickupTarget ) { vecTarget = m_hPickupTarget->GetAbsOrigin(); } else if ( !m_bForcedPickup && !m_bForcedDropoff && GetEnemy() ) { vecTarget = GetEnemy()->GetAbsOrigin(); } // Move the crane over the target // Use the crane type as a targeting point Vector vecCraneTip = m_hCrane->GetCraneTipPosition(); Vector2D vecCraneTip2D( vecCraneTip.x, vecCraneTip.y ); Vector2D vecTarget2D( vecTarget.x, vecTarget.y ); Vector2D vecOrigin2D( m_hCrane->GetAbsOrigin().x, m_hCrane->GetAbsOrigin().y ); if ( g_debug_vehicledriver.GetInt() ) { NDebugOverlay::Box( vecTarget, -Vector(50,50,50), Vector(50,50,50), 0,255,0, true, 0.1 ); NDebugOverlay::Box( vecCraneTip, -Vector(2,2,5000), Vector(2,2,5), 0,255,0, true, 0.1 ); NDebugOverlay::Box( vecTarget, -Vector(2,2,5), Vector(2,2,5000), 0,255,0, true, 0.1 ); } // Store off the distance to our target m_flDistanceToTarget = (vecTarget2D - vecCraneTip2D).Length(); // First determine whether we need to extend / retract the arm float flDistToTarget = (vecOrigin2D - vecTarget2D).LengthSqr(); float flDistToCurrent = (vecOrigin2D - vecCraneTip2D).LengthSqr(); float flDelta = fabs(flDistToTarget - flDistToCurrent); // Slow down as we get closer, but do it based upon our current extension rate float flMinDelta = 50 + (50 * fabs(m_hCrane->GetExtensionRate() / CRANE_EXTENSION_RATE_MAX)); flMinDelta *= flMinDelta; if ( flDelta > flMinDelta ) { if ( flDistToCurrent > flDistToTarget ) { // Retract m_pVehicleInterface->NPC_ThrottleReverse(); } else if ( flDistToCurrent < flDistToTarget ) { // Extend m_pVehicleInterface->NPC_ThrottleForward(); } } else { m_pVehicleInterface->NPC_ThrottleCenter(); } // Then figure out if we need to rotate. Do it all in 2D space. Vector vecRight, vecForward; m_hCrane->GetVectors( &vecForward, &vecRight, NULL ); vecRight.z = 0; vecForward.z = 0; VectorNormalize( vecRight ); VectorNormalize( vecForward ); Vector vecToTarget = ( vecTarget - m_hCrane->GetAbsOrigin() ); vecToTarget.z = 0; VectorNormalize( vecToTarget ); float flDotRight = DotProduct( vecRight, vecToTarget ); float flDotForward = DotProduct( vecForward, vecToTarget ); // Start slowing if we're going to hit the point soon float flTurnInDeg = RAD2DEG( acos(flDotForward) ); float flSpeed = m_hCrane->GetMaxTurnRate() * (flTurnInDeg / 15.0); flSpeed = min( m_hCrane->GetMaxTurnRate(), flSpeed ); if ( fabs(flSpeed) < 0.05 ) { // We're approaching the target, so stop turning m_pVehicleInterface->NPC_TurnCenter(); } else { if ( flDotRight < 0 ) { // Turn right m_pVehicleInterface->NPC_TurnRight( flSpeed ); } else if ( flDotRight > 0 ) { // Turn left m_pVehicleInterface->NPC_TurnLeft( flSpeed ); } } }
void bfg_think (edict_t *self) { edict_t *ent; edict_t *ignore; vec3_t point; vec3_t dir; vec3_t start; vec3_t end; int dmg; trace_t tr; if (!self) { return; } if (deathmatch->value) { dmg = 5; } else { dmg = 10; } ent = NULL; while ((ent = findradius(ent, self->s.origin, 256)) != NULL) { if (ent == self) { continue; } if (ent == self->owner) { continue; } if (!ent->takedamage) { continue; } if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) { continue; } VectorMA(ent->absmin, 0.5, ent->size, point); VectorSubtract(point, self->s.origin, dir); VectorNormalize(dir); ignore = self; VectorCopy(self->s.origin, start); VectorMA(start, 2048, dir, end); while (1) { tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER); if (!tr.ent) { break; } /* hurt it if we can */ if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) { T_Damage(tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); } /* if we hit something that's not a monster or player we're done */ if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) { gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_LASER_SPARKS); gi.WriteByte(4); gi.WritePosition(tr.endpos); gi.WriteDir(tr.plane.normal); gi.WriteByte(self->s.skinnum); gi.multicast(tr.endpos, MULTICAST_PVS); break; } ignore = tr.ent; VectorCopy(tr.endpos, start); } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BFG_LASER); gi.WritePosition(self->s.origin); gi.WritePosition(tr.endpos); gi.multicast(self->s.origin, MULTICAST_PHS); } self->nextthink = level.time + FRAMETIME; }
// Setup a CCoreDispInfo given a mapdispinfo_t. // If pFace is non-NULL, then lightmap texture coordinates will be generated. void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp, CCoreDispInfo *pCoreDispInfo, dface_t *pFace ) { winding_t *pWinding = pMapDisp->face.originalface->winding; Assert( pWinding->numpoints == 4 ); // // set initial surface data // CCoreDispSurface *pSurf = pCoreDispInfo->GetSurface(); texinfo_t *pTexInfo = &texinfo[ pMapDisp->face.texinfo ]; // init material contents pMapDisp->contents = pMapDisp->face.contents; if (!(pMapDisp->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) ) { pMapDisp->contents |= CONTENTS_SOLID; } pSurf->SetContents( pMapDisp->contents ); // Calculate the lightmap coordinates. Vector2D lmCoords[4] = {Vector2D(0,0),Vector2D(0,1),Vector2D(1,0),Vector2D(1,1)}; Vector2D tCoords[4] = {Vector2D(0,0),Vector2D(0,1),Vector2D(1,0),Vector2D(1,1)}; if( pFace ) { Assert( pFace->numedges == 4 ); Vector pt[4]; for( int i=0; i < 4; i++ ) pt[i] = pWinding->p[i]; CalcTextureCoordsAtPoints( pTexInfo->lightmapVecsLuxelsPerWorldUnits, pFace->m_LightmapTextureMinsInLuxels, pt, 4, lmCoords ); int zeroOffset[2] = {0,0}; CalcTextureCoordsAtPoints( pTexInfo->textureVecsTexelsPerWorldUnits, zeroOffset, pt, 4, tCoords ); } // // set face point data ... // pSurf->SetPointCount( 4 ); for( int i = 0; i < 4; i++ ) { // position pSurf->SetPoint( i, pWinding->p[i] ); for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); ++j ) { pSurf->SetLuxelCoord( j, i, lmCoords[i] ); } pSurf->SetTexCoord( i, tCoords[i] ); } // // reset surface given start info // pSurf->SetPointStart( pMapDisp->startPosition ); pSurf->FindSurfPointStartIndex(); pSurf->AdjustSurfPointData(); // // adjust face lightmap data - this will be done a bit more accurately // when the common code get written, for now it works!!! (GDC, E3) // Vector points[4]; for( int ndxPt = 0; ndxPt < 4; ndxPt++ ) { points[ndxPt] = pSurf->GetPoint( ndxPt ); } Vector edgeU = points[3] - points[0]; Vector edgeV = points[1] - points[0]; bool bUMajor = ( edgeU.Length() > edgeV.Length() ); if( pFace ) { int lightmapWidth = pFace->m_LightmapTextureSizeInLuxels[0]; int lightmapHeight = pFace->m_LightmapTextureSizeInLuxels[1]; if ( ( bUMajor && ( lightmapHeight > lightmapWidth ) ) || ( !bUMajor && ( lightmapWidth > lightmapHeight ) ) ) { pFace->m_LightmapTextureSizeInLuxels[0] = lightmapHeight; pFace->m_LightmapTextureSizeInLuxels[1] = lightmapWidth; lightmapWidth = lightmapHeight; lightmapHeight = pFace->m_LightmapTextureSizeInLuxels[1]; } for ( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS + 1 ); ndxBump++ ) { pSurf->SetLuxelCoord( ndxBump, 0, Vector2D( 0.0f, 0.0f ) ); pSurf->SetLuxelCoord( ndxBump, 1, Vector2D( 0.0f, ( float )lightmapHeight ) ); pSurf->SetLuxelCoord( ndxBump, 2, Vector2D( ( float )lightmapWidth, ( float )lightmapHeight ) ); pSurf->SetLuxelCoord( ndxBump, 3, Vector2D( ( float )lightmapWidth, 0.0f ) ); } } // Setup the displacement vectors and offsets. int size = ( ( ( 1 << pMapDisp->power ) + 1 ) * ( ( 1 << pMapDisp->power ) + 1 ) ); Vector vectorDisps[2048]; float dispDists[2048]; Assert( size < sizeof(vectorDisps)/sizeof(vectorDisps[0]) ); for( int j = 0; j < size; j++ ) { Vector v; float dist; VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v ); VectorAdd( v, pMapDisp->vectorOffsets[j], v ); dist = VectorLength( v ); VectorNormalize( v ); vectorDisps[j] = v; dispDists[j] = dist; } // Use CCoreDispInfo to setup the actual vertex positions. pCoreDispInfo->InitDispInfo( pMapDisp->power, pMapDisp->minTess, pMapDisp->smoothingAngle, pMapDisp->alphaValues, vectorDisps, dispDists ); pCoreDispInfo->Create(); }
void UI_DoSFXSaber( vector3 *blade_muz, vector3 *blade_tip, vector3 *trail_tip, vector3 *trail_muz, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight, qboolean doTrail, int cnum, int bnum ) { vector3 mid, blade_dir, end_dir, trail_dir, base_dir; float radiusmult, effectradius, coreradius, effectalpha = 1.0f, AngleScale = 1.0f; float blade_len, trail_len, base_len; vector3 rgb = { 1, 1, 1 }; int i; qhandle_t glow = 0; refEntity_t saber, sbak; VectorSubtract( blade_tip, blade_muz, &blade_dir ); VectorSubtract( trail_tip, trail_muz, &trail_dir ); blade_len = lengthMax;//VectorLength(blade_dir); trail_len = VectorLength( &trail_dir ); VectorNormalize( &blade_dir ); VectorNormalize( &trail_dir ); if ( lengthMax < 1.0f ) { return; } VectorSubtract( trail_tip, blade_tip, &end_dir ); VectorSubtract( trail_muz, blade_muz, &base_dir ); base_len = VectorLength( &base_dir ); VectorNormalize( &end_dir ); VectorNormalize( &base_dir ); switch ( color ) { case SABER_RED: glow = redSaberGlowShader; break; case SABER_ORANGE: glow = orangeSaberGlowShader; break; case SABER_YELLOW: glow = yellowSaberGlowShader; break; case SABER_GREEN: glow = greenSaberGlowShader; break; case SABER_PURPLE: glow = purpleSaberGlowShader; break; // case SABER_WHITE: case SABER_RGB: case SABER_FLAME1: case SABER_ELEC1: case SABER_FLAME2: case SABER_ELEC2: glow = rgbSaberGlowShader; break; case SABER_BLACK: glow = blackSaberGlowShader; break; default: glow = blueSaberGlowShader; break; } VectorMA( blade_muz, blade_len * 0.5f, &blade_dir, &mid ); memset( &saber, 0, sizeof(refEntity_t) ); if ( blade_len < lengthMax ) { radiusmult = 0.5f + ((blade_len / lengthMax) / 2); } else { radiusmult = 1.0f; } effectradius = ((radius * 1.6f * 1.0f) + crandom() * 0.1f)*radiusmult; coreradius = ((radius * 0.4f * 1.0f) + crandom() * 0.1f)*radiusmult; UI_RGBForSaberColor( color, &rgb, bnum ); for ( i = 0; i<3; i++ ) rgb.data[i] *= 255; { saber.renderfx = rfx; if ( blade_len - ((effectradius*1.0f) / 2) > 0 ) { saber.radius = effectradius*AngleScale; saber.saberLength = (blade_len - (saber.radius / 2)); VectorCopy( blade_muz, &saber.origin ); VectorCopy( &blade_dir, &saber.axis[0] ); saber.reType = RT_SABER_GLOW; saber.customShader = glow; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff * 1.0f; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i] * effectalpha; saber.shaderRGBA[3] = 255 * effectalpha; } SE_R_AddRefEntityToScene( &saber, cnum ); } // Do the hot core VectorMA( blade_muz, blade_len, &blade_dir, &saber.origin ); VectorMA( blade_muz, -1, &blade_dir, &saber.oldorigin ); saber.customShader = sfxSaberBladeShader; saber.reType = RT_LINE; saber.radius = coreradius; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i]; } sbak = saber; SE_R_AddRefEntityToScene( &saber, cnum ); if ( color >= SABER_RGB /*|| color == SABER_WHITE*/ ) {// Add the saber surface that provides color. sbak.customShader = sfxSaberBlade2Shader; sbak.reType = RT_LINE; sbak.shaderTexCoord.x = sbak.shaderTexCoord.y = 1.0f; sbak.shaderRGBA[0] = sbak.shaderRGBA[1] = sbak.shaderRGBA[2] = sbak.shaderRGBA[3] = 0xff; sbak.radius = coreradius; SE_R_AddRefEntityToScene( &sbak, cnum ); } } { saber.renderfx = rfx; if ( trail_len - ((effectradius*AngleScale) / 2) > 0 ) { saber.radius = effectradius*AngleScale; saber.saberLength = (trail_len - (saber.radius / 2)); VectorCopy( trail_muz, &saber.origin ); VectorCopy( &trail_dir, &saber.axis[0] ); saber.reType = RT_SABER_GLOW; saber.customShader = glow; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff * effectalpha; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i] * effectalpha; saber.shaderRGBA[3] = 255 * effectalpha; } SE_R_AddRefEntityToScene( &saber, cnum ); } // Do the hot core VectorMA( trail_muz, trail_len, &trail_dir, &saber.origin ); VectorMA( trail_muz, -1, &trail_dir, &saber.oldorigin ); saber.customShader = sfxSaberBladeShader; saber.reType = RT_LINE; saber.radius = coreradius; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i]; saber.shaderRGBA[3] = 255; } sbak = saber; SE_R_AddRefEntityToScene( &saber, cnum ); if ( color >= SABER_RGB /*|| color == SABER_WHITE*/ ) {// Add the saber surface that provides color. sbak.customShader = sfxSaberBlade2Shader; sbak.reType = RT_LINE; sbak.shaderTexCoord.x = sbak.shaderTexCoord.y = 1.0f; sbak.shaderRGBA[0] = sbak.shaderRGBA[1] = sbak.shaderRGBA[2] = sbak.shaderRGBA[3] = 0xff; sbak.radius = coreradius; SE_R_AddRefEntityToScene( &sbak, cnum ); } } VectorMA( blade_muz, blade_len - 0.5f, &blade_dir, blade_tip ); VectorMA( trail_muz, trail_len - 0.5f, &trail_dir, trail_tip ); if ( base_len > 2 ) { saber.renderfx = rfx; if ( base_len - (effectradius*AngleScale) > 0 ) { saber.radius = effectradius*AngleScale; saber.saberLength = (base_len - (effectradius*AngleScale)); VectorMA( blade_muz, ((effectradius*AngleScale) / 2), &base_dir, &saber.origin ); VectorCopy( &base_dir, &saber.axis[0] ); saber.reType = RT_SABER_GLOW; saber.customShader = glow; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff * effectalpha; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i] * effectalpha; saber.shaderRGBA[3] = 255 * effectalpha; } SE_R_AddRefEntityToScene( &saber, cnum ); } // Do the hot core VectorMA( blade_muz, base_len, &base_dir, &saber.origin ); VectorMA( blade_muz, -0.1f, &base_dir, &saber.oldorigin ); saber.customShader = sfxSaberBladeShader; saber.reType = RT_LINE; saber.radius = coreradius; saber.saberLength = base_len; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i]; saber.shaderRGBA[3] = 255; } sbak = saber; SE_R_AddRefEntityToScene( &saber, cnum ); if ( color >= SABER_RGB /*|| color == SABER_WHITE*/ ) {// Add the saber surface that provides color. sbak.customShader = sfxSaberBlade2Shader; saber.reType = RT_LINE; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; saber.radius = coreradius; saber.saberLength = base_len; SE_R_AddRefEntityToScene( &sbak, cnum ); } } }
/* ================= R_TraceLine ================= */ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_t end, ref_entity_t *test, int umask ) { ref_model_t *model; r_fragmentframecount++; // for multi-check avoidance // fill in a default trace Mem_Set( tr, 0, sizeof( trace_t )); trace_surface = NULL; trace_umask = umask; trace_fraction = 1; VectorCopy( end, trace_impact ); Mem_Set( &trace_plane, 0, sizeof( trace_plane )); ClearBounds( trace_absmins, trace_absmaxs ); AddPointToBounds( start, trace_absmins, trace_absmaxs ); AddPointToBounds( end, trace_absmins, trace_absmaxs ); model = test->model; if( model ) { if( model->type == mod_world || model->type == mod_brush ) { mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata; vec3_t temp, start_l, end_l, axis[3]; bool rotated = !Matrix3x3_Compare( test->axis, matrix3x3_identity ); // transform VectorSubtract( start, test->origin, start_l ); VectorSubtract( end, test->origin, end_l ); if( rotated ) { VectorCopy( start_l, temp ); Matrix3x3_Transform( test->axis, temp, start_l ); VectorCopy( end_l, temp ); Matrix3x3_Transform( test->axis, temp, end_l ); } VectorCopy( start_l, trace_start ); VectorCopy( end_l, trace_end ); // world uses a recursive approach using BSP tree, submodels // just walk the list of surfaces linearly if( test->model->type == mod_world ) R_RecursiveHullCheck( bmodel->nodes, start_l, end_l ); else if( BoundsIntersect( model->mins, model->maxs, trace_absmins, trace_absmaxs ) ) R_TraceAgainstBmodel( bmodel ); // transform back if( rotated && trace_fraction != 1 ) { Matrix3x3_Transpose( axis, test->axis ); VectorCopy( tr->vecPlaneNormal, temp ); Matrix3x3_Transform( axis, temp, trace_plane.normal ); } } } // calculate the impact plane, if any if( trace_fraction < 1.0f ) { VectorNormalize( trace_plane.normal ); trace_plane.dist = DotProduct( trace_plane.normal, trace_impact ); CategorizePlane( &trace_plane ); tr->flPlaneDist = trace_plane.dist; VectorCopy( trace_plane.normal, tr->vecPlaneNormal ); tr->iContents = trace_surface->contents; tr->pHit = (edict_t *)test; } tr->flFraction = trace_fraction; VectorCopy( trace_impact, tr->vecEndPos ); return trace_surface; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_TEShatterSurface::PostDataUpdate( DataUpdateType_t updateType ) { RecordShatterSurface(); CSmartPtr<CSimple3DEmitter> pGlassEmitter = CSimple3DEmitter::Create( "C_TEShatterSurface 1" ); pGlassEmitter->SetSortOrigin( m_vecOrigin ); Vector vecColor; engine->ComputeLighting( m_vecOrigin, NULL, true, vecColor ); // HACK: Blend a little toward white to match the materials... VectorLerp( vecColor, Vector( 1, 1, 1 ), 0.3, vecColor ); PMaterialHandle hMaterial1; PMaterialHandle hMaterial2; if (m_nSurfaceType == SHATTERSURFACE_GLASS) { hMaterial1 = pGlassEmitter->GetPMaterial( "effects/fleck_glass1" ); hMaterial2 = pGlassEmitter->GetPMaterial( "effects/fleck_glass2" ); } else { hMaterial1 = pGlassEmitter->GetPMaterial( "effects/fleck_tile1" ); hMaterial2 = pGlassEmitter->GetPMaterial( "effects/fleck_tile2" ); } // --------------------------------------------------- // Figure out number of particles required to fill space // --------------------------------------------------- int nNumWide = m_flWidth / m_flShardSize; int nNumHigh = m_flHeight / m_flShardSize; Vector vWidthStep,vHeightStep; AngleVectors(m_vecAngles,NULL,&vWidthStep,&vHeightStep); vWidthStep *= m_flShardSize; vHeightStep *= m_flShardSize; // --------------------- // Create glass shards // ---------------------- Vector vCurPos = m_vecOrigin; vCurPos.x += 0.5*m_flShardSize; vCurPos.z += 0.5*m_flShardSize; float flMinSpeed = 9999999999; float flMaxSpeed = 0; Particle3D *pParticle = NULL; for (int width=0;width<nNumWide;width++) { for (int height=0;height<nNumHigh;height++) { if (random->RandomInt(0,1)) { pParticle = (Particle3D *) pGlassEmitter->AddParticle( sizeof(Particle3D), hMaterial1, vCurPos ); } else { pParticle = (Particle3D *) pGlassEmitter->AddParticle( sizeof(Particle3D), hMaterial2, vCurPos ); } Vector vForceVel = Vector(0,0,0); if (random->RandomInt(0, 3) != 0) { float flForceDistSqr = (vCurPos - m_vecForcePos).LengthSqr(); vForceVel = m_vecForce; if (flForceDistSqr > 0 ) { vForceVel *= ( 40.0f / flForceDistSqr ); } } if (pParticle) { pParticle->m_flLifeRemaining = random->RandomFloat(GLASS_SHARD_MIN_LIFE,GLASS_SHARD_MAX_LIFE); pParticle->m_vecVelocity = vForceVel; pParticle->m_vecVelocity += RandomVector(-25,25); pParticle->m_uchSize = m_flShardSize + random->RandomFloat(-0.5*m_flShardSize,0.5*m_flShardSize); pParticle->m_vAngles = m_vecAngles; pParticle->m_flAngSpeed = random->RandomFloat(-400,400); pParticle->m_uchFrontColor[0] = (byte)(m_uchFrontColor[0] * vecColor.x ); pParticle->m_uchFrontColor[1] = (byte)(m_uchFrontColor[1] * vecColor.y ); pParticle->m_uchFrontColor[2] = (byte)(m_uchFrontColor[2] * vecColor.z ); pParticle->m_uchBackColor[0] = (byte)(m_uchBackColor[0] * vecColor.x ); pParticle->m_uchBackColor[1] = (byte)(m_uchBackColor[1] * vecColor.y ); pParticle->m_uchBackColor[2] = (byte)(m_uchBackColor[2] * vecColor.z ); } // Keep track of min and max speed for collision detection float flForceSpeed = vForceVel.Length(); if (flForceSpeed > flMaxSpeed) { flMaxSpeed = flForceSpeed; } if (flForceSpeed < flMinSpeed) { flMinSpeed = flForceSpeed; } vCurPos += vHeightStep; } vCurPos -= nNumHigh*vHeightStep; vCurPos += vWidthStep; } // -------------------------------------------------- // Set collision parameters // -------------------------------------------------- Vector vMoveDir = m_vecForce; VectorNormalize(vMoveDir); pGlassEmitter->m_ParticleCollision.Setup( m_vecOrigin, &vMoveDir, GLASS_SHARD_NOISE, flMinSpeed, flMaxSpeed, GLASS_SHARD_GRAVITY, GLASS_SHARD_DAMPING ); }
/* ================ CM_TraceThroughVerticalCylinder get the first intersection of the ray with the cylinder the cylinder extends halfheight above and below the origin ================ */ void CM_TraceThroughVerticalCylinder( traceWork_t *tw, trace_t &trace, vec3_t origin, float radius, float halfheight, vec3_t start, vec3_t end) { float length, scale, fraction, l1, l2; float /*a, */b, c, d, sqrtd; vec3_t v1, dir, start2d, end2d, org2d, intersection; // 2d coordinates VectorSet(start2d, start[0], start[1], 0); VectorSet(end2d, end[0], end[1], 0); VectorSet(org2d, origin[0], origin[1], 0); // if between lower and upper cylinder bounds if (start[2] <= origin[2] + halfheight && start[2] >= origin[2] - halfheight) { // if inside the cylinder VectorSubtract(start2d, org2d, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { trace.fraction = 0; trace.startsolid = qtrue; VectorSubtract(end2d, org2d, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { trace.allsolid = qtrue; } return; } } // VectorSubtract(end2d, start2d, dir); length = VectorNormalize(dir); // l1 = CM_DistanceFromLineSquared(org2d, start2d, end2d, dir); VectorSubtract(end2d, org2d, v1); l2 = VectorLengthSquared(v1); // if no intersection with the cylinder and the end point is at least an epsilon away if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { return; } // // // (start[0] - origin[0] - t * dir[0]) ^ 2 + (start[1] - origin[1] - t * dir[1]) ^ 2 = radius ^ 2 // (v1[0] + t * dir[0]) ^ 2 + (v1[1] + t * dir[1]) ^ 2 = radius ^ 2; // v1[0] ^ 2 + 2 * v1[0] * t * dir[0] + (t * dir[0]) ^ 2 + // v1[1] ^ 2 + 2 * v1[1] * t * dir[1] + (t * dir[1]) ^ 2 = radius ^ 2 // t ^ 2 * (dir[0] ^ 2 + dir[1] ^ 2) + t * (2 * v1[0] * dir[0] + 2 * v1[1] * dir[1]) + // v1[0] ^ 2 + v1[1] ^ 2 - radius ^ 2 = 0 // VectorSubtract(start, origin, v1); // dir is normalized so we can use a = 1 //a = 1.0f;// * (dir[0] * dir[0] + dir[1] * dir[1]); b = 2.0f * (v1[0] * dir[0] + v1[1] * dir[1]); c = v1[0] * v1[0] + v1[1] * v1[1] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); d = b * b - 4.0f * c;// * a; if (d > 0) { sqrtd = sqrtf(d); // = (- b + sqrtd) * 0.5f;// / (2.0f * a); fraction = (- b - sqrtd) * 0.5f;// / (2.0f * a); // if (fraction < 0) { fraction = 0; } else { fraction /= length; } if ( fraction < trace.fraction ) { VectorSubtract(end, start, dir); VectorMA(start, fraction, dir, intersection); // if the intersection is between the cylinder lower and upper bound if (intersection[2] <= origin[2] + halfheight && intersection[2] >= origin[2] - halfheight) { // trace.fraction = fraction; VectorSubtract(intersection, origin, dir); dir[2] = 0; #ifdef CAPSULE_DEBUG l2 = VectorLength(dir); if (l2 <= radius) { int bah = 1; } #endif scale = 1 / (radius+RADIUS_EPSILON); VectorScale(dir, scale, dir); VectorCopy(dir, trace.plane.normal); VectorAdd( tw->modelOrigin, intersection, intersection); trace.plane.dist = DotProduct(trace.plane.normal, intersection); trace.contents = CONTENTS_BODY; } } } else if (d == 0) { //t[0] = (- b ) / 2 * a; // slide along the cylinder } // no intersection at all }
/* ================ CM_TraceThroughLeaf ================ */ void CM_TraceThroughLeaf( traceWork_t *tw, trace_t &trace, clipMap_t *local, cLeaf_t *leaf ) { int k; int brushnum; cbrush_t *b; cPatch_t *patch; // trace line against all brushes in the leaf for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) { brushnum = local->leafbrushes[leaf->firstLeafBrush+k]; b = &local->brushes[brushnum]; if ( b->checkcount == local->checkcount ) { continue; // already checked this brush in another leaf } b->checkcount = local->checkcount; if ( !(b->contents & tw->contents) ) { continue; } #ifndef BSPC if (com_terrainPhysics->integer && cmg.landScape && (b->contents & CONTENTS_TERRAIN) ) { // Invalidate the checkcount for terrain as the terrain brush has to be processed // many times. b->checkcount--; CM_TraceThroughTerrain( tw, trace, b ); } else #endif { CM_TraceThroughBrush( tw, trace, b, false ); } if ( !trace.fraction ) { return; } } // trace line against all patches in the leaf #ifdef BSPC if (1) { #else if ( !cm_noCurves->integer ) { #endif for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { patch = local->surfaces[ local->leafsurfaces[ leaf->firstLeafSurface + k ] ]; if ( !patch ) { continue; } if ( patch->checkcount == local->checkcount ) { continue; // already checked this patch in another leaf } patch->checkcount = local->checkcount; if ( !(patch->contents & tw->contents) ) { continue; } CM_TraceThroughPatch( tw, trace, patch ); if ( !trace.fraction ) { return; } } } } #define RADIUS_EPSILON 1.0f /* ================ CM_TraceThroughSphere get the first intersection of the ray with the sphere ================ */ void CM_TraceThroughSphere( traceWork_t *tw, trace_t &trace, vec3_t origin, float radius, vec3_t start, vec3_t end ) { float l1, l2, length, scale, fraction; float /*a, */b, c, d, sqrtd; vec3_t v1, dir, intersection; // if inside the sphere VectorSubtract(start, origin, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { trace.fraction = 0; trace.startsolid = qtrue; // test for allsolid VectorSubtract(end, origin, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { trace.allsolid = qtrue; } return; } // VectorSubtract(end, start, dir); length = VectorNormalize(dir); // l1 = CM_DistanceFromLineSquared(origin, start, end, dir); VectorSubtract(end, origin, v1); l2 = VectorLengthSquared(v1); // if no intersection with the sphere and the end point is at least an epsilon away if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { return; } // // | origin - (start + t * dir) | = radius // a = dir[0]^2 + dir[1]^2 + dir[2]^2; // b = 2 * (dir[0] * (start[0] - origin[0]) + dir[1] * (start[1] - origin[1]) + dir[2] * (start[2] - origin[2])); // c = (start[0] - origin[0])^2 + (start[1] - origin[1])^2 + (start[2] - origin[2])^2 - radius^2; // VectorSubtract(start, origin, v1); // dir is normalized so a = 1 //a = 1.0f;//dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]; b = 2.0f * (dir[0] * v1[0] + dir[1] * v1[1] + dir[2] * v1[2]); c = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); d = b * b - 4.0f * c;// * a; if (d > 0) { sqrtd = sqrtf(d); // = (- b + sqrtd) * 0.5f; // / (2.0f * a); fraction = (- b - sqrtd) * 0.5f; // / (2.0f * a); // if (fraction < 0) { fraction = 0; } else { fraction /= length; } if ( fraction < trace.fraction ) { trace.fraction = fraction; VectorSubtract(end, start, dir); VectorMA(start, fraction, dir, intersection); VectorSubtract(intersection, origin, dir); #ifdef CAPSULE_DEBUG l2 = VectorLength(dir); if (l2 < radius) { int bah = 1; } #endif scale = 1 / (radius+RADIUS_EPSILON); VectorScale(dir, scale, dir); VectorCopy(dir, trace.plane.normal); VectorAdd( tw->modelOrigin, intersection, intersection); trace.plane.dist = DotProduct(trace.plane.normal, intersection); trace.contents = CONTENTS_BODY; } } else if (d == 0) { //t1 = (- b ) / 2; // slide along the sphere } // no intersection at all }
//NOTE NOTE NOTE NOTE NOTE NOTE //I want to keep this function BG too, because it's fairly generic already, and it //would be nice to have proper prediction of animations. -rww // This function makes sure that the rider's in this vehicle are properly animated. void AnimateRiders( Vehicle_t *pVeh ) { animNumber_t Anim = BOTH_VS_IDLE; float fSpeedPercToMax; int iFlags = SETANIM_FLAG_NORMAL, iBlend = 300; playerState_t *pilotPS; playerState_t *parentPS; int curTime; // Boarding animation. if ( pVeh->m_iBoarding != 0 ) { // We've just started moarding, set the amount of time it will take to finish moarding. if ( pVeh->m_iBoarding < 0 ) { int iAnimLen; // Boarding from left... if ( pVeh->m_iBoarding == -1 ) { Anim = BOTH_VS_MOUNT_L; } else if ( pVeh->m_iBoarding == -2 ) { Anim = BOTH_VS_MOUNT_R; } else if ( pVeh->m_iBoarding == -3 ) { Anim = BOTH_VS_MOUNTJUMP_L; } else if ( pVeh->m_iBoarding == VEH_MOUNT_THROW_LEFT) { iBlend = 0; Anim = BOTH_VS_MOUNTTHROW_R; } else if ( pVeh->m_iBoarding == VEH_MOUNT_THROW_RIGHT) { iBlend = 0; Anim = BOTH_VS_MOUNTTHROW_L; } // Set the delay time (which happens to be the time it takes for the animation to complete). // NOTE: Here I made it so the delay is actually 40% (0.4f) of the animation time. #ifdef _JK2MP iAnimLen = BG_AnimLength( pVeh->m_pPilot->localAnimIndex, Anim ) * 0.4f; pVeh->m_iBoarding = BG_GetTime() + iAnimLen; #else iAnimLen = PM_AnimLength( pVeh->m_pPilot->client->clientInfo.animFileIndex, Anim );// * 0.4f; if (pVeh->m_iBoarding!=VEH_MOUNT_THROW_LEFT && pVeh->m_iBoarding!=VEH_MOUNT_THROW_RIGHT) { pVeh->m_iBoarding = level.time + (iAnimLen*0.4f); } else { pVeh->m_iBoarding = level.time + iAnimLen; } #endif // Set the animation, which won't be interrupted until it's completed. // TODO: But what if he's killed? Should the animation remain persistant??? iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; #ifdef _JK2MP BG_SetAnim(pVeh->m_pPilot->playerState, bgAllAnims[pVeh->m_pPilot->localAnimIndex].anims, SETANIM_BOTH, Anim, iFlags, iBlend); #else NPC_SetAnim( pVeh->m_pPilot, SETANIM_BOTH, Anim, iFlags, iBlend ); if (pVeh->m_pOldPilot) { iAnimLen = PM_AnimLength( pVeh->m_pPilot->client->clientInfo.animFileIndex, BOTH_VS_MOUNTTHROWEE); NPC_SetAnim( pVeh->m_pOldPilot, SETANIM_BOTH, BOTH_VS_MOUNTTHROWEE, iFlags, iBlend ); } #endif } #ifndef _JK2MP if (pVeh->m_pOldPilot && pVeh->m_pOldPilot->client->ps.torsoAnimTimer<=0) { if (Q_irand(0, player->count)==0) { player->count++; player->lastEnemy = pVeh->m_pOldPilot; G_StartMatrixEffect(player, MEF_LOOK_AT_ENEMY|MEF_NO_RANGEVAR|MEF_NO_VERTBOB|MEF_NO_SPIN, 1000); } gentity_t* oldPilot = pVeh->m_pOldPilot; pVeh->m_pVehicleInfo->Eject(pVeh, pVeh->m_pOldPilot, qtrue); // will set pointer to zero // Kill Him //---------- oldPilot->client->noRagTime = -1; // no ragdoll for you G_Damage(oldPilot, pVeh->m_pPilot, pVeh->m_pPilot, pVeh->m_pPilot->currentAngles, pVeh->m_pPilot->currentOrigin, 1000, 0, MOD_CRUSH); // Compute THe Throw Direction As Backwards From The Vehicle's Velocity //---------------------------------------------------------------------- vec3_t throwDir; VectorScale(pVeh->m_pParentEntity->client->ps.velocity, -1.0f, throwDir); VectorNormalize(throwDir); throwDir[2] += 0.3f; // up a little // Now Throw Him Out //------------------- G_Throw(oldPilot, throwDir, VectorLength(pVeh->m_pParentEntity->client->ps.velocity)/10.0f); NPC_SetAnim(oldPilot, SETANIM_BOTH, BOTH_DEATHBACKWARD1, SETANIM_FLAG_OVERRIDE, iBlend ); } #endif return; } #ifdef _JK2MP //fixme if (1) return; #endif #ifdef _JK2MP pilotPS = pVeh->m_pPilot->playerState; parentPS = pVeh->m_pPilot->playerState; #else pilotPS = &pVeh->m_pPilot->client->ps; parentPS = &pVeh->m_pParentEntity->client->ps; #endif #ifndef _JK2MP//SP curTime = level.time; #elif QAGAME//MP GAME curTime = level.time; #elif CGAME//MP CGAME //FIXME: pass in ucmd? Not sure if this is reliable... curTime = pm->cmd.serverTime; #endif // Percentage of maximum speed relative to current speed. fSpeedPercToMax = parentPS->speed / pVeh->m_pVehicleInfo->speedMax; // Going in reverse... #ifdef _JK2MP if ( pVeh->m_ucmd.forwardmove < 0 && !(pVeh->m_ulFlags & VEH_SLIDEBREAKING)) #else if ( fSpeedPercToMax < -0.018f && !(pVeh->m_ulFlags & VEH_SLIDEBREAKING)) #endif { Anim = BOTH_VS_REV; iBlend = 500; } else { bool HasWeapon = ((pilotPS->weapon != WP_NONE) && (pilotPS->weapon != WP_MELEE)); bool Attacking = (HasWeapon && !!(pVeh->m_ucmd.buttons&BUTTON_ATTACK)); #ifdef _JK2MP //fixme: flying tends to spaz out a lot bool Flying = false; bool Crashing = false; #else bool Flying = !!(pVeh->m_ulFlags & VEH_FLYING); bool Crashing = !!(pVeh->m_ulFlags & VEH_CRASHING); #endif bool Right = (pVeh->m_ucmd.rightmove>0); bool Left = (pVeh->m_ucmd.rightmove<0); bool Turbo = (curTime<pVeh->m_iTurboTime); EWeaponPose WeaponPose = WPOSE_NONE; // Remove Crashing Flag //---------------------- pVeh->m_ulFlags &= ~VEH_CRASHING; // Put Away Saber When It Is Not Active //-------------------------------------- #ifndef _JK2MP if (HasWeapon && (Turbo || (pilotPS->weapon==WP_SABER && !pilotPS->SaberActive()))) { if (pVeh->m_pPilot->s.number<MAX_CLIENTS) { CG_ChangeWeapon(WP_NONE); } pVeh->m_pPilot->client->ps.weapon = WP_NONE; G_RemoveWeaponModels(pVeh->m_pPilot); } #endif // Don't Interrupt Attack Anims //------------------------------ #ifdef _JK2MP if (pilotPS->weaponTime>0) { return; } #else if (pilotPS->torsoAnim>=BOTH_VS_ATL_S && pilotPS->torsoAnim<=BOTH_VS_ATF_G) { float bodyCurrent = 0.0f; int bodyEnd = 0; if (!!gi.G2API_GetBoneAnimIndex(&pVeh->m_pPilot->ghoul2[pVeh->m_pPilot->playerModel], pVeh->m_pPilot->rootBone, level.time, &bodyCurrent, NULL, &bodyEnd, NULL, NULL, NULL)) { if (bodyCurrent<=((float)(bodyEnd)-1.5f)) { return; } } } #endif // Compute The Weapon Pose //-------------------------- if (pilotPS->weapon==WP_BLASTER) { WeaponPose = WPOSE_BLASTER; } else if (pilotPS->weapon==WP_SABER) { if ( (pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VS_ATL_TO_R_S) { pVeh->m_ulFlags &= ~VEH_SABERINLEFTHAND; } if (!(pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VS_ATR_TO_L_S) { pVeh->m_ulFlags |= VEH_SABERINLEFTHAND; } WeaponPose = (pVeh->m_ulFlags&VEH_SABERINLEFTHAND)?(WPOSE_SABERLEFT):(WPOSE_SABERRIGHT); } if (Attacking && WeaponPose) {// Attack! iBlend = 100; iFlags = SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART; // Auto Aiming //=============================================== if (!Left && !Right) // Allow player strafe keys to override { #ifndef _JK2MP if (pVeh->m_pPilot->enemy) { vec3_t toEnemy; float toEnemyDistance; vec3_t actorRight; float actorRightDot; VectorSubtract(pVeh->m_pPilot->currentOrigin, pVeh->m_pPilot->enemy->currentOrigin, toEnemy); toEnemyDistance = VectorNormalize(toEnemy); AngleVectors(pVeh->m_pParentEntity->currentAngles, 0, actorRight, 0); actorRightDot = DotProduct(toEnemy, actorRight); if (fabsf(actorRightDot)>0.5f || pilotPS->weapon==WP_SABER) { Left = (actorRightDot>0.0f); Right = !Left; } else { Right = Left = false; } } else #endif if (pilotPS->weapon==WP_SABER && !Left && !Right) { Left = (WeaponPose==WPOSE_SABERLEFT); Right = !Left; } } if (Left) {// Attack Left switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VS_ATL_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_ATL_S; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_ATR_TO_L_S; break; default: assert(0); } } else if (Right) {// Attack Right switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VS_ATR_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_ATL_TO_R_S; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_ATR_S; break; default: assert(0); } } else {// Attack Ahead switch(WeaponPose) { case WPOSE_BLASTER: Anim = BOTH_VS_ATF_G; break; default: assert(0); } } } else if (Left && pVeh->m_ucmd.buttons&BUTTON_USE) {// Look To The Left Behind iBlend = 400; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; switch(WeaponPose) { case WPOSE_SABERLEFT: Anim = BOTH_VS_IDLE_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_IDLE_SR; break; default: Anim = BOTH_VS_LOOKLEFT; } } else if (Right && pVeh->m_ucmd.buttons&BUTTON_USE) {// Look To The Right Behind iBlend = 400; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; switch(WeaponPose) { case WPOSE_SABERLEFT: Anim = BOTH_VS_IDLE_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_IDLE_SR; break; default: Anim = BOTH_VS_LOOKRIGHT; } } else if (Turbo) {// Kicked In Turbo iBlend = 50; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS; Anim = BOTH_VS_TURBO; } else if (Flying) {// Off the ground in a jump iBlend = 800; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_AIR; break; case WPOSE_BLASTER: Anim = BOTH_VS_AIR_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_AIR_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_AIR_SR; break; default: assert(0); } } else if (Crashing) {// Hit the ground! iBlend = 100; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS; switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_LAND; break; case WPOSE_BLASTER: Anim = BOTH_VS_LAND_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_LAND_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_LAND_SR; break; default: assert(0); } } else {// No Special Moves iBlend = 300; iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS; if (pVeh->m_vOrientation[ROLL] <= -20) {// Lean Left switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_LEANL; break; case WPOSE_BLASTER: Anim = BOTH_VS_LEANL_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_LEANL_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_LEANL_SR; break; default: assert(0); } } else if (pVeh->m_vOrientation[ROLL] >= 20) {// Lean Right switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_LEANR; break; case WPOSE_BLASTER: Anim = BOTH_VS_LEANR_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_LEANR_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_LEANR_SR; break; default: assert(0); } } else {// No Lean switch(WeaponPose) { case WPOSE_NONE: Anim = BOTH_VS_IDLE; break; case WPOSE_BLASTER: Anim = BOTH_VS_IDLE_G; break; case WPOSE_SABERLEFT: Anim = BOTH_VS_IDLE_SL; break; case WPOSE_SABERRIGHT: Anim = BOTH_VS_IDLE_SR; break; default: assert(0); } } }// No Special Moves }// Going backwards? #ifdef _JK2MP iFlags &= ~SETANIM_FLAG_OVERRIDE; if (pVeh->m_pPilot->playerState->torsoAnim == Anim) { pVeh->m_pPilot->playerState->torsoTimer = BG_AnimLength(pVeh->m_pPilot->localAnimIndex, Anim); } if (pVeh->m_pPilot->playerState->legsAnim == Anim) { pVeh->m_pPilot->playerState->legsTimer = BG_AnimLength(pVeh->m_pPilot->localAnimIndex, Anim); } BG_SetAnim(pVeh->m_pPilot->playerState, bgAllAnims[pVeh->m_pPilot->localAnimIndex].anims, SETANIM_BOTH, Anim, iFlags|SETANIM_FLAG_HOLD, iBlend); #else NPC_SetAnim( pVeh->m_pPilot, SETANIM_BOTH, Anim, iFlags, iBlend ); #endif }
/* =============== G_UpdateZaps =============== */ void G_UpdateZaps( int msec ) { int i, j; zap_t *zap; int damage; for( i = 0; i < MAX_ZAPS; i++ ) { zap = &zaps[ i ]; if( zap->used ) { //check each target is valid for( j = 0; j < zap->numTargets; j++ ) { gentity_t *source; gentity_t *target = zap->targets[ j ]; if( j == 0 ) source = zap->creator; else source = zap->targets[ j - 1 ]; if( target->health <= 0 || !target->inuse || //early out Distance( source->s.origin, target->s.origin ) > LEVEL2_AREAZAP_RANGE ) { target = zap->targets[ j ] = G_FindNewZapTarget( source ); //couldn't find a target, so forget about the rest of the chain if( !target ) zap->numTargets = j; } } if( zap->numTargets ) { for( j = 0; j < zap->numTargets; j++ ) { gentity_t *source; gentity_t *target = zap->targets[ j ]; float r = 1.0f / zap->numTargets; float damageFraction = 2 * r - 2 * j * r * r - r * r; vec3_t forward; if( j == 0 ) source = zap->creator; else source = zap->targets[ j - 1 ]; damage = ceil( ( (float)msec / LEVEL2_AREAZAP_TIME ) * LEVEL2_AREAZAP_DMG * damageFraction ); // don't let a high msec value inflate the total damage if( damage + zap->damageUsed > LEVEL2_AREAZAP_DMG ) damage = LEVEL2_AREAZAP_DMG - zap->damageUsed; VectorSubtract( target->s.origin, source->s.origin, forward ); VectorNormalize( forward ); //do the damage if( damage ) { G_Damage( target, source, zap->creator, forward, target->s.origin, damage, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP ); if( g_modAlienRate.integer > 0 ) damage = damage * 100 / g_modAlienRate.integer; zap->damageUsed += damage; } } } G_UpdateZapEffect( zap ); zap->timeToLive -= msec; if( zap->timeToLive <= 0 || zap->numTargets == 0 || zap->creator->health <= 0 ) { zap->used = qfalse; G_FreeEntity( zap->effectChannel ); } } } }
//------------------------------------------------------------------------------ // Purpose : Update the direction and position of my spotlight // Input : // Output : //------------------------------------------------------------------------------ void CPointSpotlight::SpotlightUpdate(void) { // --------------------------------------------------- // If I don't have a spotlight attempt to create one // --------------------------------------------------- if ( !m_hSpotlight ) { if ( m_bSpotlightOn ) { // Make the spotlight SpotlightCreate(); } else { return; } } else if ( !m_bSpotlightOn ) { SpotlightDestroy(); return; } if ( !m_hSpotlightTarget ) { DevWarning( "**Attempting to update point_spotlight but target ent is NULL\n" ); SpotlightDestroy(); SpotlightCreate(); if ( !m_hSpotlightTarget ) return; } m_vSpotlightCurrentPos = SpotlightCurrentPos(); // Update spotlight target velocity Vector vTargetDir; VectorSubtract( m_vSpotlightCurrentPos, m_hSpotlightTarget->GetAbsOrigin(), vTargetDir ); float vTargetDist = vTargetDir.Length(); // If we haven't moved at all, don't recompute if ( vTargetDist < 1 ) { m_hSpotlightTarget->SetAbsVelocity( vec3_origin ); return; } Vector vecNewVelocity = vTargetDir; VectorNormalize(vecNewVelocity); vecNewVelocity *= (10 * vTargetDist); // If a large move is requested, just jump to final spot as we probably hit a discontinuity if (vecNewVelocity.Length() > 200) { VectorNormalize(vecNewVelocity); vecNewVelocity *= 200; VectorNormalize(vTargetDir); m_hSpotlightTarget->SetAbsOrigin( m_vSpotlightCurrentPos ); } m_hSpotlightTarget->SetAbsVelocity( vecNewVelocity ); m_hSpotlightTarget->m_vSpotlightOrg = GetAbsOrigin(); // Avoid sudden change in where beam fades out when cross disconinuities VectorSubtract( m_hSpotlightTarget->GetAbsOrigin(), m_hSpotlightTarget->m_vSpotlightOrg, m_hSpotlightTarget->m_vSpotlightDir ); float flBeamLength = VectorNormalize( m_hSpotlightTarget->m_vSpotlightDir ); m_flSpotlightCurLength = (0.60*m_flSpotlightCurLength) + (0.4*flBeamLength); ComputeRenderInfo(); //NDebugOverlay::Cross3D(GetAbsOrigin(),Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1); //NDebugOverlay::Cross3D(m_vSpotlightCurrentPos,Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1); //NDebugOverlay::Cross3D(m_vSpotlightTargetPos,Vector(-5,-5,-5),Vector(5,5,5),255,0,0,true,0.1); }
void CG_MiscModelExplosion( vec3_t mins, vec3_t maxs, int size, material_t chunkType ) { int ct = 13; float r; vec3_t org, mid, dir; char *effect = NULL, *effect2 = NULL; VectorAdd( mins, maxs, mid ); VectorScale( mid, 0.5f, mid ); switch( chunkType ) { case MAT_GLASS: effect = "chunks/glassbreak"; ct = 5; break; case MAT_GLASS_METAL: effect = "chunks/glassbreak"; effect2 = "chunks/metalexplode"; ct = 5; break; case MAT_ELECTRICAL: case MAT_ELEC_METAL: effect = "chunks/sparkexplode"; ct = 5; break; case MAT_METAL: case MAT_METAL2: case MAT_METAL3: case MAT_CRATE1: case MAT_CRATE2: effect = "chunks/metalexplode"; ct = 2; break; case MAT_GRATE1: effect = "chunks/grateexplode"; ct = 8; break; case MAT_ROPE: ct = 20; effect = "chunks/ropebreak"; break; case MAT_WHITE_METAL: //not sure what this crap is really supposed to be.. case MAT_DRK_STONE: case MAT_LT_STONE: case MAT_GREY_STONE: switch( size ) { case 2: effect = "chunks/rockbreaklg"; break; case 1: default: effect = "chunks/rockbreakmed"; break; } } if ( !effect ) { return; } ct += 7 * size; // FIXME: real precache .. VERify that these need to be here...don't think they would because the effects should be registered in g_breakable theFxScheduler.RegisterEffect( effect ); if ( effect2 ) { // FIXME: real precache theFxScheduler.RegisterEffect( effect2 ); } // spawn chunk roughly in the bbox of the thing.. for ( int i = 0; i < ct; i++ ) { for( int j = 0; j < 3; j++ ) { r = random() * 0.8f + 0.1f; org[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] ); } // shoot effect away from center VectorSubtract( org, mid, dir ); VectorNormalize( dir ); if ( effect2 && ( rand() & 1 )) { theFxScheduler.PlayEffect( effect2, org, dir ); } else { theFxScheduler.PlayEffect( effect, org, dir ); } } }
void FX_8472Teleport( vec3_t org, int parm ) { int i, t; vec3_t dir = {0,0,1}, start, end, vel, color; if ( parm ) { // Beaming out for ( i = 0; i < 32; i++ ) { // Pick a random direction.. VectorSet( dir, crandom(), crandom(), crandom() ); VectorNormalize( dir ); start[0] = org[0] + dir[0] * 26; start[1] = org[1] + dir[1] * 26; start[2] = org[2] + fabs(dir[2] * 10) - 18; // Now build the velocity vector vel[0] = dir[0] * 8; vel[1] = dir[1] * 8; vel[2] = dir[2] * 6; FX_AddSprite( start, vel, NULL, 24 + random() * 8, -18, 1.0, 0.0, crandom()*180,0, 1000, cgs.media.portalFlareShader, FXF_NON_LINEAR_FADE ); } cgi_S_StartSound( org, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.speciesBeamoutSound ); } else { // Beaming in VectorMA( org, 65, dir, start ); VectorMA( org, -40, dir, end ); FX_AddLine( start, end, 1.0f, 0.5f, 48.0f, 0.4f, 0.1f, 1500, cgs.media.speciesPortalShader, FXF_NON_LINEAR_FADE ); for ( i = -1; i <= 1; i++ ) { VectorCopy( org, start ); start[2] += 10.0f + i * 16.0f; for ( t = -1; t <= 1; t++ ) { // create a 3 x 3 grid of starting points for the effect VectorClear( vel ); VectorMA( vel, t * 16, cg.refdef.viewaxis[1], vel ); FX_AddSprite( start, vel, NULL, 56.0f, -10.0f, 0.3f, 0.0f, -90.0f, 0, 1800, cgs.media.portalFlareShader, FXF_NON_LINEAR_FADE ); } } cgi_S_StartSound( org, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.speciesBeaminSound ); } VectorSet( color, 1.0f, 1.0f, 0.7f ); CG_AddTempLight( org, 200, color, 1000 ); }
void CG_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins, const vec3_t maxs, float speed, int numChunks, material_t chunkType, int customChunk, float baseScale, int customSound = 0 ) { localEntity_t *le; refEntity_t *re; vec3_t dir; int i, j, k; int chunkModel = 0; leBounceSound_t bounce = LEBS_NONE; float r, speedMod = 1.0f; qboolean chunk = qfalse; if ( chunkType == MAT_NONE ) { // Well, we should do nothing return; } if ( customSound ) { if ( cgs.sound_precache[customSound] ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.sound_precache[customSound] ); } } // Set up our chunk sound info...breaking sounds are done here so they are done once on breaking..some return instantly because the chunks are done with effects instead of models switch( chunkType ) { case MAT_GLASS: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound ); } return; break; case MAT_GRATE1: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.grateSound ); } return; break; case MAT_ELECTRICAL:// (sparks) if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgi_S_RegisterSound (va("sound/ambience/spark%d.wav", Q_irand(1, 6))) ); } return; break; case MAT_DRK_STONE: case MAT_LT_STONE: case MAT_GREY_STONE: case MAT_WHITE_METAL: // not quite sure what this stuff is supposed to be...it's for Stu if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.rockBreakSound ); bounce = LEBS_ROCK; } speedMod = 0.5f; // rock blows up less break; case MAT_GLASS_METAL: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound ); // FIXME: should probably have a custom sound bounce = LEBS_METAL; } break; case MAT_CRATE1: case MAT_CRATE2: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.crateBreakSound[Q_irand(0,1)] ); } break; case MAT_METAL: case MAT_METAL2: case MAT_METAL3: case MAT_ELEC_METAL:// FIXME: maybe have its own sound? if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.chunkSound ); bounce = LEBS_METAL; } speedMod = 0.8f; // metal blows up a bit more break; case MAT_ROPE: /* if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgi_S_RegisterSound( "" )); FIXME: needs a sound } */ return; break; } if ( baseScale <= 0.0f ) { baseScale = 1.0f; } // Chunks for( i = 0; i < numChunks; i++ ) { if ( customChunk > 0 ) { // Try to use a custom chunk. if ( cgs.model_draw[customChunk] ) { chunk = qtrue; chunkModel = cgs.model_draw[customChunk]; } } if ( !chunk ) { // No custom chunk. Pick a random chunk type at run-time so we don't get the same chunks switch( chunkType ) { case MAT_METAL2: //bluegrey chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)]; break; case MAT_GREY_STONE://gray chunkModel = cgs.media.chunkModels[CHUNK_ROCK1][Q_irand(0, 3)]; break; case MAT_LT_STONE: //tan chunkModel = cgs.media.chunkModels[CHUNK_ROCK2][Q_irand(0, 3)]; break; case MAT_DRK_STONE://brown chunkModel = cgs.media.chunkModels[CHUNK_ROCK3][Q_irand(0, 3)]; break; case MAT_WHITE_METAL: chunkModel = cgs.media.chunkModels[CHUNK_WHITE_METAL][Q_irand(0, 3)]; break; case MAT_CRATE1://yellow multi-colored crate chunks chunkModel = cgs.media.chunkModels[CHUNK_CRATE1][Q_irand(0, 3)]; break; case MAT_CRATE2://red multi-colored crate chunks chunkModel = cgs.media.chunkModels[CHUNK_CRATE2][Q_irand(0, 3)]; break; case MAT_ELEC_METAL: case MAT_GLASS_METAL: case MAT_METAL://grey chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)]; break; case MAT_METAL3: if ( rand() & 1 ) { chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)]; } else { chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)]; } break; } } // It wouldn't look good to throw a bunch of RGB axis models...so make sure we have something to work with. if ( chunkModel ) { le = CG_AllocLocalEntity(); re = &le->refEntity; re->hModel = chunkModel; le->leType = LE_FRAGMENT; le->endTime = cg.time + 1300 + random() * 900; // spawn chunk roughly in the bbox of the thing...bias towards center in case thing blowing up doesn't complete fill its bbox. for( j = 0; j < 3; j++ ) { r = random() * 0.8f + 0.1f; re->origin[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] ); } VectorCopy( re->origin, le->pos.trBase ); // Move out from center of thing, otherwise you can end up things moving across the brush in an undesirable direction. Visually looks wrong VectorSubtract( re->origin, origin, dir ); VectorNormalize( dir ); VectorScale( dir, Q_flrand( speed * 0.5f, speed * 1.25f ) * speedMod, le->pos.trDelta ); // Angular Velocity VectorSet( le->angles.trBase, random() * 360, random() * 360, random() * 360 ); le->angles.trDelta[0] = crandom(); le->angles.trDelta[1] = crandom(); le->angles.trDelta[2] = 0; // don't do roll VectorScale( le->angles.trDelta, random() * 600.0f + 200.0f, le->angles.trDelta ); le->pos.trType = TR_GRAVITY; le->angles.trType = TR_LINEAR; le->pos.trTime = le->angles.trTime = cg.time; le->bounceFactor = 0.2f + random() * 0.2f; le->leFlags |= LEF_TUMBLE; le->ownerGentNum = owner; le->leBounceSoundType = bounce; // Make sure that we have the desired start size set le->radius = Q_flrand( baseScale * 0.75f, baseScale * 1.25f ); re->nonNormalizedAxes = qtrue; AxisCopy( axisDefault, re->axis ); // could do an angles to axis, but this is cheaper and works ok for( k = 0; k < 3; k++ ) { VectorScale( re->axis[k], le->radius, re->axis[k] ); } } } }
//------------------ void Cmd_Fx( gentity_t *ent ) { vec3_t dir; gentity_t *fx_ent = NULL; if ( Q_stricmp( gi.argv(1), "play" ) == 0 ) { if ( gi.argc() == 3 ) { // I guess, only allow one active at a time while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { G_FreeEntity( fx_ent ); } fx_ent = G_Spawn(); fx_ent->fxFile = gi.argv( 2 ); // Move out in front of the person spawning the effect AngleVectors( ent->currentAngles, dir, NULL, NULL ); VectorMA( ent->currentOrigin, 32, dir, fx_ent->s.origin ); extern void SP_fx_runner( gentity_t *ent ); SP_fx_runner( fx_ent ); fx_ent->delay = 2000; // adjusting delay fx_ent->classname = "cmd_fx"; // and classname return; } } else if ( Q_stricmp( gi.argv(1), "stop" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { G_FreeEntity( fx_ent ); } return; } else if ( Q_stricmp( gi.argv(1), "delay" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 3 ) { fx_ent->delay = atoi( gi.argv( 2 )); } else { gi.Printf( S_COLOR_GREEN"FX: current delay is: %i\n", fx_ent->delay ); } return; } } else if ( Q_stricmp( gi.argv(1), "random" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 3 ) { fx_ent->random = atoi( gi.argv( 2 )); } else { gi.Printf( S_COLOR_GREEN"FX: current random is: %6.2f\n", fx_ent->random ); } return; } } else if ( Q_stricmp( gi.argv(1), "origin" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 5 ) { fx_ent->s.origin[0] = atof( gi.argv( 2 )); fx_ent->s.origin[1] = atof( gi.argv( 3 )); fx_ent->s.origin[2] = atof( gi.argv( 4 )); G_SetOrigin( fx_ent, fx_ent->s.origin ); } else { gi.Printf( S_COLOR_GREEN"FX: current origin is: <%6.2f %6.2f %6.2f>\n", fx_ent->currentOrigin[0], fx_ent->currentOrigin[1], fx_ent->currentOrigin[2] ); } return; } } else if ( Q_stricmp( gi.argv(1), "dir" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 5 ) { fx_ent->s.angles[0] = atof( gi.argv( 2 )); fx_ent->s.angles[1] = atof( gi.argv( 3 )); fx_ent->s.angles[2] = atof( gi.argv( 4 )); if ( !VectorNormalize( fx_ent->s.angles )) { // must have been zero length fx_ent->s.angles[2] = 1; } } else { gi.Printf( S_COLOR_GREEN"FX: current dir is: <%6.2f %6.2f %6.2f>\n", fx_ent->s.angles[0], fx_ent->s.angles[1], fx_ent->s.angles[2] ); } return; } } gi.Printf( S_COLOR_CYAN"Fx--------------------------------------------------------\n" ); gi.Printf( S_COLOR_CYAN"commands: sample usage:\n" ); gi.Printf( S_COLOR_CYAN"----------------------------------------------------------\n" ); gi.Printf( S_COLOR_CYAN"fx play <filename> fx play sparks, fx play env/fire\n" ); gi.Printf( S_COLOR_CYAN"fx stop fx stop\n" ); gi.Printf( S_COLOR_CYAN"fx delay <#> fx delay 1000\n" ); gi.Printf( S_COLOR_CYAN"fx random <#> fx random 200\n" ); gi.Printf( S_COLOR_CYAN"fx origin <#><#><#> fx origin 10 20 30\n" ); gi.Printf( S_COLOR_CYAN"fx dir <#><#><#> fx dir 0 0 -1\n\n" ); }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void EmitInitialDispInfos( void ) { int i; mapdispinfo_t *pMapDisp; ddispinfo_t *pDisp; Vector v; // Calculate the total number of verts. int nTotalVerts = 0; int nTotalTris = 0; for ( i=0; i < nummapdispinfo; i++ ) { nTotalVerts += NUM_DISP_POWER_VERTS( mapdispinfo[i].power ); nTotalTris += NUM_DISP_POWER_TRIS( mapdispinfo[i].power ); } // Clear the output arrays.. g_dispinfo.Purge(); g_dispinfo.SetSize( nummapdispinfo ); g_DispVerts.SetSize( nTotalVerts ); g_DispTris.SetSize( nTotalTris ); int iCurVert = 0; int iCurTri = 0; for( i = 0; i < nummapdispinfo; i++ ) { pDisp = &g_dispinfo[i]; pMapDisp = &mapdispinfo[i]; CDispVert *pOutVerts = &g_DispVerts[iCurVert]; CDispTri *pOutTris = &g_DispTris[iCurTri]; // Setup the vert pointers. pDisp->m_iDispVertStart = iCurVert; pDisp->m_iDispTriStart = iCurTri; iCurVert += NUM_DISP_POWER_VERTS( pMapDisp->power ); iCurTri += NUM_DISP_POWER_TRIS( pMapDisp->power ); // // save power, minimum tesselation, and smoothing angle // pDisp->power = pMapDisp->power; // If the high bit is set - this is FLAGS! pDisp->minTess = pMapDisp->flags; pDisp->minTess |= 0x80000000; // pDisp->minTess = pMapDisp->minTess; pDisp->smoothingAngle = pMapDisp->smoothingAngle; pDisp->m_iMapFace = -2; // get surface contents pDisp->contents = pMapDisp->face.contents; pDisp->startPosition = pMapDisp->startPosition; // // add up the vectorOffsets and displacements, save alphas (per vertex) // int size = ( ( ( 1 << pDisp->power ) + 1 ) * ( ( 1 << pDisp->power ) + 1 ) ); for( int j = 0; j < size; j++ ) { VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v ); VectorAdd( v, pMapDisp->vectorOffsets[j], v ); float dist = VectorLength( v ); VectorNormalize( v ); VectorCopy( v, pOutVerts[j].m_vVector ); pOutVerts[j].m_flDist = dist; pOutVerts[j].m_flAlpha = pMapDisp->alphaValues[j]; } int nTriCount = ( (1 << (pDisp->power)) * (1 << (pDisp->power)) * 2 ); for ( int iTri = 0; iTri< nTriCount; ++iTri ) { pOutTris[iTri].m_uiTags = pMapDisp->triTags[iTri]; } //=================================================================== //=================================================================== // save the index for face data reference pMapDisp->face.dispinfo = i; } }
void PM_VehicleImpact(bgEntity_t *pEnt, trace_t *trace) { // See if the vehicle has crashed into the ground. Vehicle_t *pSelfVeh = pEnt->m_pVehicle; float magnitude = VectorLength( pm->ps->velocity ) * pSelfVeh->m_pVehicleInfo->mass / 50.0f; qboolean forceSurfDestruction = qfalse; #ifdef QAGAME gentity_t *hitEnt = trace!=NULL?&g_entities[trace->entityNum]:NULL; if (!hitEnt || //nothing to hit (pSelfVeh && pSelfVeh->m_pPilot &&//I'm a piloted vehicle hitEnt && hitEnt->s.eType == ET_MISSILE && hitEnt->inuse &&//that hit a missile hitEnt->r.ownerNum == pSelfVeh->m_pPilot->s.number)//and the missile is owned by my pilot ) { //don't hit it return; } if ( pSelfVeh//I have a vehicle struct && pSelfVeh->m_iRemovedSurfaces )//vehicle has bits removed { //spiralling to our deaths, explode on any solid impact if ( hitEnt->s.NPC_class == CLASS_VEHICLE ) { //hit another vehicle, explode! //Give credit to whoever got me into this death spiral state //[Asteroids] G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_COLLISION ); /* gentity_t *parent = (gentity_t *)pSelfVeh->m_pParentEntity; gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } //FIXME: damage hitEnt, some, too? Our explosion should hurt them some, but... G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT */ //[/Asteroids] return; } else if ( !VectorCompare( trace->plane.normal, vec3_origin ) && (trace->entityNum == ENTITYNUM_WORLD || hitEnt->r.bmodel ) ) { //have a valid hit plane and we hit a solid brush vec3_t moveDir; float impactDot; VectorCopy( pm->ps->velocity, moveDir ); VectorNormalize( moveDir ); impactDot = DotProduct( moveDir, trace->plane.normal ); if ( impactDot <= -0.7f )//hit rather head-on and hard { // Just DIE now //Give credit to whoever got me into this death spiral state //[Asteroids] G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING ); /* gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT */ //[/Asteroids] return; } } } if ( trace->entityNum < ENTITYNUM_WORLD && hitEnt->s.eType == ET_MOVER && hitEnt->s.apos.trType != TR_STATIONARY//rotating && (hitEnt->spawnflags&16) //IMPACT && Q_stricmp( "func_rotating", hitEnt->classname ) == 0 ) { //hit a func_rotating that is supposed to destroy anything it touches! //guarantee the hit will happen, thereby taking off a piece of the ship forceSurfDestruction = qtrue; } else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #endif /* if ( (pSelfVeh->m_ulFlags&VEH_GEARSOPEN) && trace->plane.normal[2] > 0.7f && fabs(pSelfVeh->m_vOrientation[PITCH]) < 0.2f && fabs(pSelfVeh->m_vOrientation[ROLL]) < 0.2f )*/ { //we're landing, we're cool //FIXME: some sort of landing "thump", not the impactFX /* if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { vec3_t up = {0,0,1}; #ifdef QAGAME G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up ); #else trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up, -1, -1 ); #endif } */ //this was annoying me -rww //FIXME: this shouldn't even be getting called when the vehicle is at rest! #ifdef QAGAME if (hitEnt && (hitEnt->s.eType == ET_PLAYER || hitEnt->s.eType == ET_NPC) && pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //always smack players } else #endif { return; } } if ( pSelfVeh && (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) && //this is kind of weird on tauntauns and atst's.. (magnitude >= 100||forceSurfDestruction) ) { if ( pEnt->m_pVehicle->m_iHitDebounce < pm->cmd.serverTime || forceSurfDestruction ) { //a bit of a hack, may conflict with getting shot, but... //FIXME: impact sound and effect should be gotten from g_vehicleInfo...? //FIXME: should pass in trace.endpos and trace.plane.normal vec3_t vehUp; //[Asteroids] #ifdef QAGAME qboolean noDamage = qfalse; #else //#ifndef QAGAME //[/Asteroids] bgEntity_t *hitEnt; #endif if ( trace && !pSelfVeh->m_iRemovedSurfaces && !forceSurfDestruction ) { qboolean turnFromImpact = qfalse, turnHitEnt = qfalse; float l = pm->ps->speed*0.5f; vec3_t bounceDir; #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( (trace->entityNum == ENTITYNUM_WORLD || hitEnt->s.solid == SOLID_BMODEL)//bounce off any brush && !VectorCompare(trace->plane.normal, vec3_origin) )//have a valid plane to bounce off of { //bounce off in the opposite direction of the impact if (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER) { pm->ps->speed *= pml.frametime; VectorCopy(trace->plane.normal, bounceDir); } else if ( trace->plane.normal[2] >= MIN_LANDING_SLOPE//flat enough to land on && pSelfVeh->m_LandTrace.fraction < 1.0f //ground present && pm->ps->speed <= MIN_LANDING_SPEED ) { //could land here, don't bounce off, in fact, return altogether! return; } else { if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { turnFromImpact = qtrue; } VectorCopy(trace->plane.normal, bounceDir); } } else if ( pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER ) { //check for impact with another fighter #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle && hitEnt->m_pVehicle->m_pVehicleInfo && hitEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER ) { //two vehicles hit each other, turn away from the impact turnFromImpact = qtrue; turnHitEnt = qtrue; #ifndef QAGAME VectorSubtract( pm->ps->origin, hitEnt->s.origin, bounceDir ); #else VectorSubtract( pm->ps->origin, hitEnt->r.currentOrigin, bounceDir ); #endif VectorNormalize( bounceDir ); } } if ( turnFromImpact ) { //bounce off impact surf and turn away vec3_t pushDir= {0}, turnAwayAngles, turnDelta; float turnStrength, pitchTurnStrength, yawTurnStrength; vec3_t moveDir; float bounceDot, turnDivider; //bounce if ( !turnHitEnt ) { //hit wall VectorScale(bounceDir, (pm->ps->speed*0.25f/pSelfVeh->m_pVehicleInfo->mass), pushDir); } else { //hit another fighter #ifndef QAGAME VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, bounceDir ); #else if ( hitEnt->client ) { VectorScale( bounceDir, (pm->ps->speed+hitEnt->client->ps.speed)*0.5f, pushDir ); } else { VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, pushDir ); } #endif VectorScale(pushDir, (l/pSelfVeh->m_pVehicleInfo->mass), pushDir); VectorScale(pushDir, 0.1f, pushDir); } VectorNormalize2( pm->ps->velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(pm->ps->velocity, pushDir, pm->ps->velocity); //turn turnDivider = (pSelfVeh->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) { //don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } turnStrength = (magnitude/2000.0f); if ( turnStrength < 0.1f ) { turnStrength = 0.1f; } else if ( turnStrength > 2.0f ) { turnStrength = 2.0f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, pSelfVeh->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) { //shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //pSelfVeh->m_vOrientation[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); pSelfVeh->m_vFullAngleVelocity[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) { //shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //pSelfVeh->m_vOrientation[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); pSelfVeh->m_vFullAngleVelocity[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } /* PM_SetPMViewAngle(pm->ps, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd); if ( pm_entVeh ) {//I'm a vehicle, so pm_entVeh is actually my pilot bgEntity_t *pilot = pm_entVeh; if ( !BG_UnrestrainedPitchRoll( pilot->playerState, pSelfVeh ) ) { //set the rider's viewangles to the vehicle's viewangles PM_SetPMViewAngle(pilot->playerState, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd); } } */ #ifdef QAGAME//server-side, turn the guy we hit away from us, too if ( turnHitEnt//make the other guy turn and get pushed && hitEnt->client //must be a valid client && !FighterIsLanded( hitEnt->m_pVehicle, &hitEnt->client->ps )//but not if landed && !(hitEnt->spawnflags&2) )//and not if suspended { l = hitEnt->client->ps.speed; //now bounce *them* away and turn them //flip the bounceDir VectorScale( bounceDir, -1, bounceDir ); //do bounce VectorScale( bounceDir, (pm->ps->speed+l)*0.5f, pushDir ); VectorScale(pushDir, (l*0.5f/hitEnt->m_pVehicle->m_pVehicleInfo->mass), pushDir); VectorNormalize2( hitEnt->client->ps.velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(hitEnt->client->ps.velocity, pushDir, hitEnt->client->ps.velocity); //turn turnDivider = (hitEnt->m_pVehicle->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) { //don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, hitEnt->m_pVehicle->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) { //shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //hitEnt->m_pVehicle->m_vOrientation[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); hitEnt->m_pVehicle->m_vFullAngleVelocity[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) { //shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } hitEnt->m_pVehicle->m_vFullAngleVelocity[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } } #endif } } #ifdef QAGAME if (!hitEnt) { return; } AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { //G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp ); //tempent use bad! G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pSelfVeh->m_pVehicleInfo->iImpactFX); } pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; magnitude /= pSelfVeh->m_pVehicleInfo->toughness * 50.0f; if (hitEnt && (hitEnt->s.eType != ET_TERRAIN || !(hitEnt->spawnflags & 1) || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)) { //don't damage the vehicle from terrain that doesn't want to damage vehicles //[Asteroids] gentity_t *killer = NULL; //[/Asteroids] if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //increase the damage... float mult = (pSelfVeh->m_vOrientation[PITCH]*0.1f); if (mult < 1.0f) { mult = 1.0f; } if (hitEnt->inuse && hitEnt->takedamage) { //if the other guy takes damage, don't hurt us a lot for ramming him //unless it's a vehicle, then we get 1.5 times damage if (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle) { mult = 1.5f; } else { mult = 0.5f; } } magnitude *= mult; } pSelfVeh->m_iLastImpactDmg = magnitude; //FIXME: what about proper death credit to the guy who shot you down? //FIXME: actually damage part of the ship that impacted? if ( hitEnt->s.eType == ET_MISSILE )//missile { //FIX: NEVER do or take impact damage from a missile... noDamage = qtrue; if ( (hitEnt->s.eFlags&EF_JETPACK_ACTIVE)//vehicle missile && ((gentity_t *)hitEnt)->r.ownerNum < MAX_CLIENTS )//valid client owner { //I ran into a missile and died because of the impact, give credit to the missile's owner (PROBLEM: might this ever accidently give improper credit to client 0?) /* if ( ((gentity_t *)hitEnt)->r.ownerNum == pEnt->s.number ) {//hit our own missile? Don't damage ourselves or it... (so we don't kill ourselves!) if it hits *us*, then fine, but not here noDamage = qtrue; } */ killer = &g_entities[((gentity_t *)hitEnt)->r.ownerNum]; } } if ( !noDamage ) { G_Damage( (gentity_t *)pEnt, ((gentity_t*)hitEnt), killer!=NULL?killer:((gentity_t *)hitEnt), NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING) );//FIXME: MOD_IMPACT } //G_Damage( (gentity_t *)pEnt, NULL, NULL, NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT //[/Asteroids] if (pSelfVeh->m_pVehicleInfo->surfDestruction) { G_FlyVehicleSurfaceDestruction((gentity_t *)pEnt, trace, magnitude, forceSurfDestruction ); } pSelfVeh->m_ulFlags |= VEH_CRASHING; } if (hitEnt && hitEnt->inuse && hitEnt->takedamage) { //damage this guy because we hit him float pmult = 1.0f; int finalD; gentity_t *attackEnt; if ( (hitEnt->s.eType == ET_PLAYER && hitEnt->s.number < MAX_CLIENTS) || (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class != CLASS_VEHICLE) ) { //probably a humanoid, or something if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //player die good.. if me fighter pmult = 2000.0f; } else { pmult = 40.0f; } if (hitEnt->client && BG_IsKnockDownable(&hitEnt->client->ps) && G_CanBeEnemy((gentity_t *)pEnt, hitEnt)) { //smash! if (hitEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN) { hitEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; hitEnt->client->ps.forceHandExtendTime = pm->cmd.serverTime + 1100; hitEnt->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim } hitEnt->client->ps.otherKiller = pEnt->s.number; hitEnt->client->ps.otherKillerTime = pm->cmd.serverTime + 5000; hitEnt->client->ps.otherKillerDebounceTime = pm->cmd.serverTime + 100; //[Asteroids] hitEnt->client->otherKillerMOD = MOD_COLLISION; hitEnt->client->otherKillerVehWeapon = 0; hitEnt->client->otherKillerWeaponType = WP_NONE; //[/Asteroids] //add my velocity into his to force him along in the correct direction from impact VectorAdd(hitEnt->client->ps.velocity, pm->ps->velocity, hitEnt->client->ps.velocity); //upward thrust hitEnt->client->ps.velocity[2] += 200.0f; } } if (pSelfVeh->m_pPilot) { attackEnt = (gentity_t *)pSelfVeh->m_pPilot; } else { attackEnt = (gentity_t *)pEnt; } finalD = magnitude*pmult; if (finalD < 1) { finalD = 1; } //[/Asteroids] if ( !noDamage ) { G_Damage( hitEnt, attackEnt, attackEnt, NULL, pm->ps->origin, finalD, 0, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING/*MOD_MELEE*/) );//FIXME: MOD_IMPACT } //[Asteroids] } #else //this is gonna result in "double effects" for the client doing the prediction. //it doesn't look bad though. could just use predicted events, but I'm too lazy. hitEnt = PM_BGEntForNum(trace->entityNum); if (!hitEnt || hitEnt->s.owner != pEnt->s.number) { //don't hit your own missiles! AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp, -1, -1 ); pSelfVeh->m_ulFlags |= VEH_CRASHING; } #endif } } }
void UI_SaberDrawBlade( itemDef_t *item, char *saberName, int saberModel, saberType_t saberType, vector3 *origin, vector3 *angles, int bladeNum ) { vector3 org_, end, axis_[3] = { { 0.0f } }; // shut the compiler up mdxaBone_t boltMatrix; effectTrailArgStruct_t fx; saber_colors_t bladeColor; float bladeLength, bladeRadius; char bladeColorString[MAX_QPATH]; int snum; const char *tagName; int bolt; qboolean tagHack = qfalse; int styleToUse = atoi( UI_Cvar_VariableString( "cg_saberBladeStyle" ) ); if ( (item->flags&ITF_ISSABER) && saberModel < 2 ) { snum = 0; trap->Cvar_VariableStringBuffer( "ui_saber_color", bladeColorString, sizeof(bladeColorString) ); } else//if ( item->flags&ITF_ISSABER2 ) - presumed { snum = 1; trap->Cvar_VariableStringBuffer( "ui_saber2_color", bladeColorString, sizeof(bladeColorString) ); } if ( !trap->G2API_HasGhoul2ModelOnIndex( &(item->ghoul2), saberModel ) ) {//invalid index! return; } bladeColor = TranslateSaberColor( bladeColorString ); bladeLength = UI_SaberBladeLengthForSaber( saberName, bladeNum ); bladeRadius = UI_SaberBladeRadiusForSaber( saberName, bladeNum ); tagName = va( "*blade%d", bladeNum + 1 ); bolt = trap->G2API_AddBolt( item->ghoul2, saberModel, tagName ); if ( bolt == -1 ) { tagHack = qtrue; //hmm, just fall back to the most basic tag (this will also make it work with pre-JKA saber models bolt = trap->G2API_AddBolt( item->ghoul2, saberModel, "*flash" ); if ( bolt == -1 ) {//no tag_flash either?!! bolt = 0; } } // angles.pitch = curYaw; // angles.roll = 0; trap->G2API_GetBoltMatrix( item->ghoul2, saberModel, bolt, &boltMatrix, angles, origin, uiInfo.uiDC.realTime, NULL, &vec3_origin );//NULL was cgs.model_draw // work the matrix axis stuff into the original axis and origins used. BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, &org_ ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, &axis_[0] );//front (was NEGATIVE_Y, but the md3->glm exporter screws up this tag somethin' awful) // ...changed this back to NEGATIVE_Y BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, &axis_[1] );//right ... and changed this to NEGATIVE_X BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_Z, &axis_[2] );//up VectorMA( &org_, bladeLength, &axis_[0], &end ); VectorAdd( &end, &axis_[0], &end ); if ( tagHack ) { switch ( saberType ) { default: case SABER_SINGLE: VectorMA( &org_, 1.0f, &axis_[0], &org_ ); break; case SABER_DAGGER: case SABER_LANCE: break; case SABER_STAFF: if ( bladeNum == 0 ) VectorMA( &org_, 12 * 1.0f, &axis_[0], &org_ ); if ( bladeNum == 1 ) { VectorScale( &axis_[0], -1, &axis_[0] ); VectorMA( &org_, 12 * 1.0f, &axis_[0], &org_ ); } break; case SABER_BROAD: if ( bladeNum == 0 ) VectorMA( &org_, -1 * 1.0f, &axis_[1], &org_ ); else if ( bladeNum == 1 ) VectorMA( &org_, 1 * 1.0f, &axis_[1], &org_ ); break; case SABER_PRONG: if ( bladeNum == 0 ) VectorMA( &org_, -3 * 1.0f, &axis_[1], &org_ ); else if ( bladeNum == 1 ) VectorMA( &org_, 3 * 1.0f, &axis_[1], &org_ ); break; case SABER_ARC: VectorSubtract( &axis_[1], &axis_[2], &axis_[1] ); VectorNormalize( &axis_[1] ); switch ( bladeNum ) { case 0: VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], 0.75f, &axis_[0] ); VectorScale( &axis_[1], 0.25f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; case 1: VectorScale( &axis_[0], 0.25f, &axis_[0] ); VectorScale( &axis_[1], 0.75f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; case 2: VectorMA( &org_, -8 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], -0.25f, &axis_[0] ); VectorScale( &axis_[1], 0.75f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; case 3: VectorMA( &org_, -16 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], -0.75f, &axis_[0] ); VectorScale( &axis_[1], 0.25f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; default: break; } break; case SABER_SAI: if ( bladeNum == 1 ) VectorMA( &org_, -3 * 1.0f, &axis_[1], &org_ ); else if ( bladeNum == 2 ) VectorMA( &org_, 3 * 1.0f, &axis_[1], &org_ ); break; case SABER_CLAW: switch ( bladeNum ) { case 0: VectorMA( &org_, 2 * 1.0f, &axis_[0], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[2], &org_ ); break; case 1: VectorMA( &org_, 2 * 1.0f, &axis_[0], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[2], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[1], &org_ ); break; case 2: VectorMA( &org_, 2 * 1.0f, &axis_[0], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[2], &org_ ); VectorMA( &org_, -2 * 1.0f, &axis_[1], &org_ ); break; default: break; } break; case SABER_STAR: switch ( bladeNum ) { case 0: VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 1: VectorScale( &axis_[0], 0.33f, &axis_[0] ); VectorScale( &axis_[2], 0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 2: VectorScale( &axis_[0], -0.33f, &axis_[0] ); VectorScale( &axis_[2], 0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 3: VectorScale( &axis_[0], -1, &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 4: VectorScale( &axis_[0], -0.33f, &axis_[0] ); VectorScale( &axis_[2], -0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 5: VectorScale( &axis_[0], 0.33f, &axis_[0] ); VectorScale( &axis_[2], -0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; default: break; } break; case SABER_TRIDENT: switch ( bladeNum ) { case 0: VectorMA( &org_, 24 * 1.0f, &axis_[0], &org_ ); break; case 1: VectorMA( &org_, -6 * 1.0f, &axis_[1], &org_ ); VectorMA( &org_, 24 * 1.0f, &axis_[0], &org_ ); break; case 2: VectorMA( &org_, 6 * 1.0f, &axis_[1], &org_ ); VectorMA( &org_, 24 * 1.0f, &axis_[0], &org_ ); break; case 3: VectorMA( &org_, -32 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], -1, &axis_[0] ); break; default: break; } break; case SABER_SITH_SWORD: //no blade break; } } VectorCopy( &org_, &fx.mVerts[0].origin ); VectorMA( &end, 3.0f, &axis_[0], &fx.mVerts[1].origin ); VectorCopy( &end, &fx.mVerts[2].origin ); VectorMA( &org_, 3.0f, &axis_[0], &fx.mVerts[3].origin ); //Raz: Temporarily switch to basejka sabers for flame and electric users if ( bladeColor == SABER_FLAME1 || bladeColor == SABER_ELEC1 || bladeColor == SABER_FLAME2 || bladeColor == SABER_ELEC2 || bladeColor == SABER_BLACK ) styleToUse = 0; // Pass in the renderfx flags attached to the saber weapon model...this is done so that saber glows // will get rendered properly in a mirror...not sure if this is necessary?? //CG_DoSaber( org_, axis_[0], saberLen, client->saber[saberNum].blade[bladeNum].lengthMax, client->saber[saberNum].blade[bladeNum].radius, // scolor, renderfx, (qboolean)(saberNum==0&&bladeNum==0) ); switch ( styleToUse ) { case 0: UI_DoSaber( &org_, &axis_[0], bladeLength, bladeLength, bladeRadius, bladeColor, 0, qfalse, 0, snum ); break; /* case 1: UI_DoEp1Saber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; case 2: UI_DoEp2Saber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; case 3: UI_DoEp3Saber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; */ default: case 1: UI_DoSFXSaber( &fx.mVerts[0].origin, &fx.mVerts[1].origin, &fx.mVerts[2].origin, &fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, qfalse, qfalse, 0, snum ); break; /* case 5: UI_DoOTSaber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; */ } }
qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t normal, planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; //qboolean damageSelf = qtrue; numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); if ( gravity ) { VectorCopy( pm->ps->velocity, endVelocity ); endVelocity[2] -= pm->ps->gravity * pml.frametime+3; //[JetpackChange1.3] if(pm->ps->pm_type == PM_JETPACK && pm->cmd.upmove <= 0 && !(pm->ps->pm_flags & PMF_DUCKED)) endVelocity[2] -= endVelocity[2]/100*20; //[/JetpackChange1.3] pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) ) { // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } time_left = pml.frametime; // never turn against the ground plane if ( pml.groundPlane ) { numplanes = 1; VectorCopy( pml.groundTrace.plane.normal, planes[0] ); if ( !PM_GroundSlideOkay( planes[0][2] ) ) { planes[0][2] = 0; VectorNormalize( planes[0] ); } } else { numplanes = 0; } // never turn against original velocity VectorNormalize2( pm->ps->velocity, planes[numplanes] ); numplanes++; for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) { // calculate position we are trying to move to VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { // entity is completely trapped in another solid pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pm->ps->origin); } if (trace.fraction == 1) { break; // moved the entire distance } // save entity for contact PM_AddTouchEnt( trace.entityNum ); if (pm->ps->clientNum >= MAX_CLIENTS) { bgEntity_t *pEnt = pm_entSelf; if (pEnt && pEnt->s.eType == ET_NPC && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle) { //do vehicle impact stuff then PM_VehicleImpact(pEnt, &trace); } } #ifdef QAGAME else { if ( PM_ClientImpact( &trace ) ) { continue; } } #endif time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } VectorCopy( trace.plane.normal, normal ); if ( !PM_GroundSlideOkay( normal[2] ) ) { //wall-running //never push up off a sloped wall normal[2] = 0; VectorNormalize( normal ); } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) ) { //no sliding if stuck to wall! for ( i = 0 ; i < numplanes ; i++ ) { if ( VectorCompare( normal, planes[i] ) ) {//DotProduct( normal, planes[i] ) > 0.99 ) { VectorAdd( normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } } VectorCopy (normal, planes[numplanes]); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for ( i = 0 ; i < numplanes ; i++ ) { into = DotProduct( pm->ps->velocity, planes[i] ); if ( into >= 0.1 ) { continue; // move doesn't interact with the plane } // see how hard we are hitting things if ( -into > pml.impactSpeed ) { pml.impactSpeed = -into; } // slide along the plane PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); // slide along the plane PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); // see if there is a second plane that the new move enters for ( j = 0 ; j < numplanes ; j++ ) { if ( j == i ) { continue; } if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a triple plane interaction VectorClear( pm->ps->velocity ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } if ( gravity ) { VectorCopy( endVelocity, pm->ps->velocity ); } // don't change velocity if in a timer (FIXME: is this correct?) if ( pm->ps->pm_time ) { VectorCopy( primal_velocity, pm->ps->velocity ); } return (qboolean)( bumpcount != 0 ); }
/* ============== RB_DrawSun (SA) FIXME: sun should render behind clouds, so passing dark areas cover it up ============== */ void RB_DrawSun( void ) { float size; float dist; vec3_t origin, vec1, vec2; vec3_t temp; byte color[4]; if ( !tr.sunShader ) { return; } if ( !backEnd.skyRenderedThisView ) { return; } if ( !r_drawSun->integer ) { return; } qglPushMatrix(); qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); qglTranslatef( backEnd.viewParms.orientation.origin[0], backEnd.viewParms.orientation.origin[1], backEnd.viewParms.orientation.origin[2] ); dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) // (SA) shrunk the size of the sun size = dist * 0.2; VectorScale( tr.sunDirection, dist, origin ); PerpendicularVector( vec1, tr.sunDirection ); CrossProduct( tr.sunDirection, vec1, vec2 ); VectorScale( vec1, size, vec1 ); VectorScale( vec2, size, vec2 ); // farthest depth range qglDepthRange( 1.0, 1.0 ); color[0] = color[1] = color[2] = color[3] = 255; // (SA) simpler sun drawing RB_BeginSurface( tr.sunShader, tess.fogNum ); RB_AddQuadStamp( origin, vec1, vec2, color ); /* VectorCopy( origin, temp ); VectorSubtract( temp, vec1, temp ); VectorSubtract( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 0; tess.texCoords0[tess.numVertexes].v[1] = 0; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorAdd( temp, vec1, temp ); VectorSubtract( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 0; tess.texCoords0[tess.numVertexes].v[1] = 1; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorAdd( temp, vec1, temp ); VectorAdd( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 1; tess.texCoords0[tess.numVertexes].v[1] = 1; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorSubtract( temp, vec1, temp ); VectorAdd( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 1; tess.texCoords0[tess.numVertexes].v[1] = 0; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 1; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 3; */ RB_EndSurface(); if ( r_drawSun->integer > 1 ) { // draw flare effect // (SA) FYI: This is cheezy and was only a test so far. // If we decide to use the flare business I will /definatly/ improve all this // get a point a little closer dist = dist * 0.7; VectorScale( tr.sunDirection, dist, origin ); // and make the flare a little smaller VectorScale( vec1, 0.5f, vec1 ); VectorScale( vec2, 0.5f, vec2 ); // add the vectors to give an 'off angle' result VectorAdd( tr.sunDirection, backEnd.viewParms.orientation.axis[0], temp ); VectorNormalize( temp ); // amplify the result origin[0] += temp[0] * 500.0; origin[1] += temp[1] * 500.0; origin[2] += temp[2] * 500.0; // (SA) FIXME: todo: flare effect should render last (on top of everything else) and only when sun is in view (sun moving out of camera past degree n should start to cause flare dimming until view angle to sun is off by angle n + x. // draw the flare RB_BeginSurface( tr.sunflareShader[0], tess.fogNum ); RB_AddQuadStamp( origin, vec1, vec2, color ); RB_EndSurface(); } // back to normal depth range qglDepthRange( 0.0, 1.0 ); qglPopMatrix(); }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; qboolean isGiant = qfalse; bgEntity_t *pEnt; qboolean skipStep = qfalse; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( BG_InReboundHold( pm->ps->legsAnim ) ) { gravity = qfalse; } if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } pEnt = pm_entSelf; if (pm->ps->clientNum >= MAX_CLIENTS) { if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0) { return; } } VectorCopy(start_o, down); down[2] -= STEPSIZE; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); VectorSet(up, 0, 0, 1); // never step up when you still have up velocity if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) { return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); if (pm->ps->clientNum >= MAX_CLIENTS) { // apply ground friction, even if on ladder if (pEnt && pEnt->s.NPC_class == CLASS_ATST || (pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->type == VH_WALKER) ) { //AT-STs can step high up[2] += 66.0f; isGiant = qtrue; } else if ( pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) { //also can step up high up[2] += 64.0f; isGiant = qtrue; } else { up[2] += STEPSIZE; } } else { up[2] += STEPSIZE; } // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); if ( pm->stepSlideFix ) { if ( pm->ps->clientNum < MAX_CLIENTS && trace.plane.normal[2] < MIN_WALK_NORMAL ) { //normal players cannot step up slopes that are too steep to walk on! vec3_t stepVec; //okay, the step up ends on a slope that it too steep to step up onto, //BUT: //If the step looks like this: // (B)\__ // \_____(A) //Then it might still be okay, so we figure out the slope of the entire move //from (A) to (B) and if that slope is walk-upabble, then it's okay VectorSubtract( trace.endpos, down_o, stepVec ); VectorNormalize( stepVec ); if ( stepVec[2] > (1.0f-MIN_WALK_NORMAL) ) { skipStep = qtrue; } } } if ( !trace.allsolid && !skipStep ) //normal players cannot step up slopes that are too steep to walk on! { if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) { //Rancor don't step on clients if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } /* else if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_ATST && OnSameTeam( pEnt, traceEnt) ) {//NPC AT-ST's don't step up on allies VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } */ else { VectorCopy (trace.endpos, pm->ps->origin); if ( pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } } else { if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } } if ( !pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
void heat_think (edict_t *self) { edict_t *target = NULL; edict_t *aquire = NULL; vec3_t vec; int len; int oldlen = 0; if (!self) { return; } VectorClear(vec); /* aquire new target */ while ((target = findradius(target, self->s.origin, 1024)) != NULL) { if (self->owner == target) { continue; } if (!(target->svflags & SVF_MONSTER)) { continue; } if (!target->client) { continue; } if (target->health <= 0) { continue; } if (!visible(self, target)) { continue; } if (!infront(self, target)) { continue; } VectorSubtract(self->s.origin, target->s.origin, vec); len = VectorLength(vec); if ((aquire == NULL) || (len < oldlen)) { aquire = target; self->target_ent = aquire; oldlen = len; } } if (aquire != NULL) { VectorSubtract(aquire->s.origin, self->s.origin, vec); vectoangles(vec, self->s.angles); VectorNormalize(vec); VectorCopy(vec, self->movedir); VectorScale(vec, 500, self->velocity); } self->nextthink = level.time + 0.1; }
fixedWinding_t *TryMergeWinding( fixedWinding_t *f1, fixedWinding_t *f2, vec3_t planenormal ){ vec_t *p1, *p2, *p3, *p4, *back; fixedWinding_t *newf; int i, j, k, l; vec3_t normal, delta; vec_t dot; qboolean keep1, keep2; // // find a common edge // p1 = p2 = NULL; // stop compiler warning j = 0; // for ( i = 0; i < f1->numpoints; i++ ) { p1 = f1->points[i]; p2 = f1->points[( i + 1 ) % f1->numpoints]; for ( j = 0; j < f2->numpoints; j++ ) { p3 = f2->points[j]; p4 = f2->points[( j + 1 ) % f2->numpoints]; for ( k = 0; k < 3; k++ ) { if ( fabs( p1[k] - p4[k] ) > 0.1 ) { //EQUAL_EPSILON) //ME break; } if ( fabs( p2[k] - p3[k] ) > 0.1 ) { //EQUAL_EPSILON) //ME break; } } //end for if ( k == 3 ) { break; } } //end for if ( j < f2->numpoints ) { break; } } //end for if ( i == f1->numpoints ) { return NULL; // no matching edges } // // check slope of connected lines // if the slopes are colinear, the point can be removed // back = f1->points[( i + f1->numpoints - 1 ) % f1->numpoints]; VectorSubtract( p1, back, delta ); CrossProduct( planenormal, delta, normal ); VectorNormalize( normal, normal ); back = f2->points[( j + 2 ) % f2->numpoints]; VectorSubtract( back, p1, delta ); dot = DotProduct( delta, normal ); if ( dot > CONTINUOUS_EPSILON ) { return NULL; // not a convex polygon } keep1 = (qboolean)( dot < -CONTINUOUS_EPSILON ); back = f1->points[( i + 2 ) % f1->numpoints]; VectorSubtract( back, p2, delta ); CrossProduct( planenormal, delta, normal ); VectorNormalize( normal, normal ); back = f2->points[( j + f2->numpoints - 1 ) % f2->numpoints]; VectorSubtract( back, p2, delta ); dot = DotProduct( delta, normal ); if ( dot > CONTINUOUS_EPSILON ) { return NULL; // not a convex polygon } keep2 = (qboolean)( dot < -CONTINUOUS_EPSILON ); // // build the new polygon // newf = NewFixedWinding( f1->numpoints + f2->numpoints ); // copy first polygon for ( k = ( i + 1 ) % f1->numpoints ; k != i ; k = ( k + 1 ) % f1->numpoints ) { if ( k == ( i + 1 ) % f1->numpoints && !keep2 ) { continue; } VectorCopy( f1->points[k], newf->points[newf->numpoints] ); newf->numpoints++; } // copy second polygon for ( l = ( j + 1 ) % f2->numpoints ; l != j ; l = ( l + 1 ) % f2->numpoints ) { if ( l == ( j + 1 ) % f2->numpoints && !keep1 ) { continue; } VectorCopy( f2->points[l], newf->points[newf->numpoints] ); newf->numpoints++; } return newf; }
/* ================= fire_hit Used for all impact (hit/punch/slash) attacks ================= */ qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick) { trace_t tr; vec3_t forward, right, up; vec3_t v; vec3_t point; float range; vec3_t dir; if (!self) { return false; } /* see if enemy is in range */ VectorSubtract(self->enemy->s.origin, self->s.origin, dir); range = VectorLength(dir); if (range > aim[0]) { return false; } if ((aim[1] > self->mins[0]) && (aim[1] < self->maxs[0])) { /* the hit is straight on so back the range up to the edge of their bbox */ range -= self->enemy->maxs[0]; } else { /* this is a side hit so adjust the "right" value out to the edge of their bbox */ if (aim[1] < 0) { aim[1] = self->enemy->mins[0]; } else { aim[1] = self->enemy->maxs[0]; } } VectorMA(self->s.origin, range, dir, point); tr = gi.trace(self->s.origin, NULL, NULL, point, self, MASK_SHOT); if (tr.fraction < 1) { if (!tr.ent->takedamage) { return false; } /* if it will hit any client/monster then hit the one we wanted to hit */ if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) { tr.ent = self->enemy; } } AngleVectors(self->s.angles, forward, right, up); VectorMA(self->s.origin, range, forward, point); VectorMA(point, aim[1], right, point); VectorMA(point, aim[2], up, point); VectorSubtract(point, self->enemy->s.origin, dir); /* do the damage */ T_Damage(tr.ent, self, self, dir, point, vec3_origin, damage, kick / 2, DAMAGE_NO_KNOCKBACK, MOD_HIT); if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) { return false; } /* do our special form of knockback here */ VectorMA(self->enemy->absmin, 0.5, self->enemy->size, v); VectorSubtract(v, point, v); VectorNormalize(v); VectorMA(self->enemy->velocity, kick, v, self->enemy->velocity); if (self->enemy->velocity[2] > 0) { self->enemy->groundentity = NULL; } return true; }
//--------------------------------------------------------- void WP_DisruptorAltFire( gentity_t *ent ) //--------------------------------------------------------- { int damage = weaponData[WP_DISRUPTOR].altDamage, skip, traces = DISRUPTOR_ALT_TRACES; qboolean render_impact = qtrue; vec3_t start, end; vec3_t muzzle2, spot, dir; trace_t tr; gentity_t *traceEnt, *tent; float dist, shotDist, shotRange = 8192; qboolean hitDodged = qfalse, fullCharge = qfalse; VectorCopy( muzzle, muzzle2 ); // making a backup copy // The trace start will originate at the eye so we can ensure that it hits the crosshair. if ( ent->NPC ) { switch ( g_spskill->integer ) { case 0: damage = DISRUPTOR_NPC_ALT_DAMAGE_EASY; break; case 1: damage = DISRUPTOR_NPC_ALT_DAMAGE_MEDIUM; break; case 2: default: damage = DISRUPTOR_NPC_ALT_DAMAGE_HARD; break; } VectorCopy( muzzle, start ); fullCharge = qtrue; } else { VectorCopy( ent->client->renderInfo.eyePoint, start ); AngleVectors( ent->client->renderInfo.eyeAngles, forwardVec, NULL, NULL ); // don't let NPC's do charging int count = ( level.time - ent->client->ps.weaponChargeTime - 50 ) / DISRUPTOR_CHARGE_UNIT; if ( count < 1 ) { count = 1; } else if ( count >= 10 ) { count = 10; fullCharge = qtrue; } // more powerful charges go through more things if ( count < 3 ) { traces = 1; } else if ( count < 6 ) { traces = 2; } //else do full traces damage = damage * count + weaponData[WP_DISRUPTOR].damage * 0.5f; // give a boost to low charge shots } skip = ent->s.number; // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) // { // // in overcharge mode, so doing double damage // damage *= 2; // } for ( int i = 0; i < traces; i++ ) { VectorMA( start, shotRange, forwardVec, end ); //NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0" //alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter gi.trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } if ( tr.entityNum == ent->s.number ) { // should never happen, but basically we don't want to consider a hit to ourselves? // Get ready for an attempt to trace through another person VectorCopy( tr.endpos, muzzle2 ); VectorCopy( tr.endpos, start ); skip = tr.entityNum; #ifdef _DEBUG gi.Printf( "BAD! Disruptor gun shot somehow traced back and hit the owner!\n" ); #endif continue; } // always render a shot beam, doing this the old way because I don't much feel like overriding the effect. //NOTE: let's just draw one beam, at the end //tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT ); //tent->svFlags |= SVF_BROADCAST; //tent->alt_fire = fullCharge; // mark us so we can alter the effect //VectorCopy( muzzle2, tent->s.origin2 ); if ( tr.fraction >= 1.0f ) { // draw the beam but don't do anything else break; } traceEnt = &g_entities[tr.entityNum]; if ( traceEnt //&& traceEnt->NPC && ( traceEnt->s.weapon == WP_SABER || (traceEnt->client && (traceEnt->client->NPC_class == CLASS_BOBAFETT||traceEnt->client->NPC_class == CLASS_REBORN) ) ) ) {//FIXME: need a more reliable way to know we hit a jedi? hitDodged = Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE ); //acts like we didn't even hit him } if ( !hitDodged ) { if ( render_impact ) { if (( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage ) || !Q_stricmp( traceEnt->classname, "misc_model_breakable" ) || traceEnt->s.eType == ET_MOVER ) { // Create a simple impact type mark that doesn't last long in the world G_PlayEffect( G_EffectIndex( "disruptor/alt_hit" ), tr.endpos, tr.plane.normal ); if ( traceEnt->client && LogAccuracyHit( traceEnt, ent )) {//NOTE: hitting multiple ents can still get you over 100% accuracy ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } int hitLoc = G_GetHitLocFromTrace( &tr, MOD_DISRUPTOR ); if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH ) {//hehe G_Damage( traceEnt, ent, ent, forwardVec, tr.endpos, 10, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, fullCharge ? MOD_SNIPER : MOD_DISRUPTOR, hitLoc ); break; } G_Damage( traceEnt, ent, ent, forwardVec, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, fullCharge ? MOD_SNIPER : MOD_DISRUPTOR, hitLoc ); if ( traceEnt->s.eType == ET_MOVER ) {//stop the traces on any mover break; } } else { // we only make this mark on things that can't break or move tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS ); tent->svFlags |= SVF_BROADCAST; VectorCopy( tr.plane.normal, tent->pos1 ); break; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool? } } else // not rendering impact, must be a skybox or other similar thing? { break; // don't try anymore traces } } // Get ready for an attempt to trace through another person VectorCopy( tr.endpos, muzzle2 ); VectorCopy( tr.endpos, start ); skip = tr.entityNum; hitDodged = qfalse; } //just draw one solid beam all the way to the end... tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT ); tent->svFlags |= SVF_BROADCAST; tent->alt_fire = fullCharge; // mark us so we can alter the effect VectorCopy( muzzle, tent->s.origin2 ); // now go along the trail and make sight events VectorSubtract( tr.endpos, muzzle, dir ); shotDist = VectorNormalize( dir ); //FIXME: if shoot *really* close to someone, the alert could be way out of their FOV for ( dist = 0; dist < shotDist; dist += 64 ) { //FIXME: on a really long shot, this could make a LOT of alerts in one frame... VectorMA( muzzle, dist, dir, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); } //FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention? VectorMA( start, shotDist-4, forwardVec, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); }
/* ================= fire_lead This is an internal support routine used for bullet/pellet based weapons. ================= */ static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) { trace_t tr; vec3_t dir; vec3_t forward, right, up; vec3_t end; float r; float u; vec3_t water_start; qboolean water = false; int content_mask = MASK_SHOT | MASK_WATER; tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread; u = crandom()*vspread; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); if (gi.pointcontents (start) & MASK_WATER) { water = true; VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } tr = gi.trace (start, NULL, NULL, end, self, content_mask); // see if we hit water if (tr.contents & MASK_WATER) { int color; water = true; VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { if (tr.contents & CONTENTS_WATER) { if (strcmp(tr.surface->name, "*brwater") == 0) color = SPLASH_BROWN_WATER; else color = SPLASH_BLUE_WATER; } else if (tr.contents & CONTENTS_SLIME) color = SPLASH_SLIME; else if (tr.contents & CONTENTS_LAVA) color = SPLASH_LAVA; else color = SPLASH_UNKNOWN; if (color != SPLASH_UNKNOWN) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (8); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (color); gi.multicast (tr.endpos, MULTICAST_PVS); } // change bullet's course when it enters water VectorSubtract (end, start, dir); vectoangles (dir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread*2; u = crandom()*vspread*2; VectorMA (water_start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_impact); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } // if went through water, determine where the end and make a bubble trail if (water) { vec3_t pos; VectorSubtract (tr.endpos, water_start, dir); VectorNormalize (dir); VectorMA (tr.endpos, -2, dir, pos); if (gi.pointcontents (pos) & MASK_WATER) VectorCopy (pos, tr.endpos); else tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); VectorAdd (water_start, tr.endpos, pos); VectorScale (pos, 0.5, pos); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (water_start); gi.WritePosition (tr.endpos); gi.multicast (pos, MULTICAST_PVS); } }
void CG_SmallPileOfGibs( const vec3_t origin, int damage, const vec3_t initialVelocity, int team ) { lentity_t *le; int i, j, count; vec3_t angles, velocity; int time; if( !cg_gibs->integer ) return; time = 50; count = 14 + cg_gibs->integer; // 15 models minimum clamp( count, 15, 128 ); for( i = 0; i < count; i++ ) { vec4_t color; // coloring switch ( rand( ) % 3 ) { case 0: // orange Vector4Set( color, 1, 0.5, 0, 1 ); break; case 1: // purple Vector4Set( color, 1, 0, 1, 1 ); break; case 2: default: if( ( team == TEAM_ALPHA ) || ( team == TEAM_BETA ) ) { // team CG_TeamColor( team, color ); for( j = 0; j < 3; j++ ) { color[j] = bound( 60.0f / 255.0f, color[j], 1.0f ); } } else { // grey Vector4Set( color, 60.0f / 255.0f, 60.0f / 255.0f, 60.0f / 255.0f, 1.0f ); } break; } le = CG_AllocModel( LE_ALPHA_FADE, origin, vec3_origin, time + time * random( ), color[0], color[1], color[2], color[3], 0, 0, 0, 0, CG_MediaModel( cgs.media.modIlluminatiGibs ), NULL ); // random rotation and scale variations VectorSet( angles, crandom() * 360, crandom() * 360, crandom() * 360 ); AnglesToAxis( angles, le->ent.axis ); le->ent.scale = 0.8f - ( random() * 0.25 ); le->ent.renderfx = RF_FULLBRIGHT|RF_NOSHADOW; velocity[0] = crandom() * 0.5; velocity[1] = crandom() * 0.5; velocity[2] = 0.5 + random() * 0.5; // always have upwards VectorNormalize( velocity ); VectorScale( velocity, min( damage * 10, 300 ), velocity ); velocity[0] += crandom() * bound( 0, damage, 150 ); velocity[1] += crandom() * bound( 0, damage, 150 ); velocity[2] += random() * bound( 0, damage, 250 ); VectorAdd( initialVelocity, velocity, le->velocity ); le->avelocity[0] = random() * 1200; le->avelocity[1] = random() * 1200; le->avelocity[2] = random() * 1200; //friction and gravity VectorSet( le->accel, -0.2f, -0.2f, -900 ); le->bounce = 75; } }