//---------------------------------------------------------- void fx_runner_link( gentity_t *ent ) { vec3_t dir; if ( ent->target ) { // try to use the target to override the orientation gentity_t *target = NULL; target = G_Find( target, FOFS(targetname), ent->target ); if ( !target ) { // Bah, no good, dump a warning, but continue on and use the UP vector Com_Printf( "fx_runner_link: target specified but not found: %s\n", ent->target ); Com_Printf( " -assuming UP orientation.\n" ); } else { // Our target is valid so let's override the default UP vector VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); vectoangles( dir, ent->s.angles ); } } // don't really do anything with this right now other than do a check to warn the designers if the target2 is bogus if ( ent->target2 ) { gentity_t *target = NULL; target = G_Find( target, FOFS(targetname), ent->target2 ); if ( !target ) { // Target2 is bogus, but we can still continue Com_Printf( "fx_runner_link: target2 was specified but is not valid: %s\n", ent->target2 ); } } G_SetAngles( ent, ent->s.angles ); if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) // STARTOFF || ONESHOT { // We won't even consider thinking until we are used ent->nextthink = -1; } else { if ( VALIDSTRING( ent->soundSet ) == true ) { ent->s.loopSound = CAS_GetBModelSound( ent->soundSet, BMS_MID ); if ( ent->s.loopSound < 0 ) { ent->s.loopSound = 0; } } // Let's get to work right now! ent->e_ThinkFunc = thinkF_fx_runner_think; ent->nextthink = level.time + 200; // wait a small bit, then start working } // make us useable if we can be targeted if ( ent->targetname ) { ent->e_UseFunc = useF_fx_runner_use; } }
qboolean NPC_GetMoveDirection( vec3_t out, float *distance ) { vec3_t angles; //Clear the struct memset( &frameNavInfo, 0, sizeof( frameNavInfo ) ); //Get our movement, if any if ( NPC_GetMoveInformation( frameNavInfo.direction, &frameNavInfo.distance ) == qfalse ) return qfalse; //Setup the return value *distance = frameNavInfo.distance; //For starters VectorCopy( frameNavInfo.direction, frameNavInfo.pathDirection ); //If on a ladder, move appropriately if ( NPC->watertype & CONTENTS_LADDER ) { NPC_LadderMove( frameNavInfo.direction ); return qtrue; } //Attempt a straight move to goal if ( NPC_ClearPathToGoal( frameNavInfo.direction, NPCInfo->goalEntity ) == qfalse ) { //See if we're just stuck if ( NAV_MoveToGoal( NPC, frameNavInfo ) == WAYPOINT_NONE ) { //Can't reach goal, just face vectoangles( frameNavInfo.direction, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qfalse; } frameNavInfo.flags |= NIF_MACRO_NAV; } //Avoid any collisions on the way if ( NAV_AvoidCollision( NPC, NPCInfo->goalEntity, frameNavInfo ) == qfalse ) { //FIXME: Emit a warning, this is a worst case scenario //FIXME: if we have a clear path to our goal (exluding bodies), but then this // check (against bodies only) fails, shouldn't we fall back // to macro navigation? Like so: if ( !(frameNavInfo.flags&NIF_MACRO_NAV) ) {//we had a clear path to goal and didn't try macro nav, but can't avoid collision so try macro nav here //See if we're just stuck if ( NAV_MoveToGoal( NPC, frameNavInfo ) == WAYPOINT_NONE ) { //Can't reach goal, just face vectoangles( frameNavInfo.direction, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qfalse; } frameNavInfo.flags |= NIF_MACRO_NAV; } } //Setup the return values VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qtrue; }
/* ===================== CG_AddParticleToScene ===================== */ void CG_AddParticleToScene (cparticle_t *p, vec3_t org, float alpha) { vec3_t point; polyVert_t verts[4]; float width; float height; float time, time2; float ratio; float invratio; vec3_t color; polyVert_t TRIverts[3]; vec3_t rright2, rup2; if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) {// create a front facing polygon if (p->type != P_WEATHER_FLURRY) { if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { if (org[2] > p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground p->org[2] = ( p->start + crandom () * 4 ); if (p->type == P_BUBBLE_TURBULENT) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } } } else { if (org[2] < p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground while (p->org[2] < p->end) { p->org[2] += (p->start - p->end); } if (p->type == P_WEATHER_TURBULENT) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } } } // Rafael snow pvs check if (!p->link) return; p->alpha = 1; } // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp if (Distance( cg.snap->ps.origin, org ) > 1024) { return; } // done. if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255 * p->alpha; VectorMA (org, -p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255 * p->alpha; } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy( point, TRIverts[0].xyz ); TRIverts[0].st[0] = 1; TRIverts[0].st[1] = 0; TRIverts[0].modulate[0] = 255; TRIverts[0].modulate[1] = 255; TRIverts[0].modulate[2] = 255; TRIverts[0].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, TRIverts[1].xyz); TRIverts[1].st[0] = 0; TRIverts[1].st[1] = 0; TRIverts[1].modulate[0] = 255; TRIverts[1].modulate[1] = 255; TRIverts[1].modulate[2] = 255; TRIverts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, TRIverts[2].xyz); TRIverts[2].st[0] = 0; TRIverts[2].st[1] = 1; TRIverts[2].modulate[0] = 255; TRIverts[2].modulate[1] = 255; TRIverts[2].modulate[2] = 255; TRIverts[2].modulate[3] = 255 * p->alpha; } } else if (p->type == P_SPRITE) { vec3_t rr, ru; vec3_t rotate_ang; VectorSet (color, 1.0, 1.0, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, pvup, point); VectorMA (point, -width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, pvup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, pvup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) {// create a front rotating facing polygon if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { return; } if (p->color == BLOODRED) VectorSet (color, 0.22f, 0.0f, 0.0f); else if (p->color == GREY75) { float len; float greyit; float val; len = Distance (cg.snap->ps.origin, org); if (!len) len = 1; val = 4096/len; greyit = 0.25 * val; if (greyit > 0.5) greyit = 0.5; VectorSet (color, greyit, greyit, greyit); } else VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (cg.time > p->startfade) { invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); if (p->color == EMISIVEFADE) { float fval; fval = (invratio * invratio); if (fval < 0) fval = 0; VectorSet (color, fval , fval , fval ); } invratio *= p->alpha; } else invratio = 1 * p->alpha; if (invratio > 1) invratio = 1; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->type != P_SMOKE_IMPACT) { vec3_t temp; vectoangles (rforward, temp); p->accumroll += p->roll; temp[ROLL] += p->accumroll * 0.1; AngleVectors ( temp, NULL, rright2, rup2); } else { VectorCopy (rright, rright2); VectorCopy (rup, rup2); } if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, p->width, pvright, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255 * invratio; } else if (p->type == P_BLEED) { vec3_t rr, ru; vec3_t rotate_ang; float alpha; alpha = p->alpha; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } else { VectorCopy (pvup, ru); VectorCopy (pvright, rr); } VectorMA (org, -p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 111; verts[0].modulate[1] = 19; verts[0].modulate[2] = 9; verts[0].modulate[3] = 255 * alpha; VectorMA (org, -p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 111; verts[1].modulate[1] = 19; verts[1].modulate[2] = 9; verts[1].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 111; verts[2].modulate[1] = 19; verts[2].modulate[2] = 9; verts[2].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 111; verts[3].modulate[1] = 19; verts[3].modulate[2] = 9; verts[3].modulate[3] = 255 * alpha; } else if (p->type == P_FLAT_SCALEUP) { float width, height; float sinR, cosR; if (p->color == BLOODRED) VectorSet (color, 1, 1, 1); else VectorSet (color, 0.5, 0.5, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (width > p->endwidth) width = p->endwidth; if (height > p->endheight) height = p->endheight; sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= sinR; verts[0].xyz[1] -= cosR; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= cosR; verts[1].xyz[1] += sinR; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += sinR; verts[2].xyz[1] += cosR; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += cosR; verts[3].xyz[1] -= sinR; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255; } else if (p->type == P_FLAT) { VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= p->height; verts[0].xyz[1] -= p->width; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= p->height; verts[1].xyz[1] += p->width; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += p->height; verts[2].xyz[1] += p->width; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += p->height; verts[3].xyz[1] -= p->width; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // Ridah else if (p->type == P_ANIM) { vec3_t rr, ru; vec3_t rotate_ang; int i, j; time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (ratio >= 1.0f) { ratio = 0.9999f; } width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); // if we are "inside" this sprite, don't draw if (Distance( cg.snap->ps.origin, org ) < width/1.5) { return; } i = p->shaderAnim; j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); p->pshader = shaderAnims[i][j]; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, pvup, point); VectorMA (point, -width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, pvup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, pvup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // done. if (!p->pshader) { // (SA) temp commented out for DM // CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); return; } if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); else trap_R_AddPolyToScene( p->pshader, 4, verts ); }
void NPC_BSSniper_Patrol( void ) {//FIXME: pick up on bodies of dead buddies? NPCS.NPC->count = 0; if ( NPCS.NPCInfo->confusionTime < level.time ) { //Look for any enemies if ( NPCS.NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES ) { if ( NPC_CheckPlayerTeamStealth() ) { //NPCInfo->behaviorState = BS_HUNT_AND_KILL;//Should be auto now //NPC_AngerSound(); NPC_UpdateAngles( qtrue, qtrue ); return; } } if ( !(NPCS.NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) { //Is there danger nearby int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS ); if ( NPC_CheckForDanger( alertEvent ) ) { NPC_UpdateAngles( qtrue, qtrue ); return; } else {//check for other alert events //There is an event to look at if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCS.NPCInfo->lastAlertID ) { NPCS.NPCInfo->lastAlertID = level.alertEvents[alertEvent].ID; if ( level.alertEvents[alertEvent].level == AEL_DISCOVERED ) { if ( level.alertEvents[alertEvent].owner && level.alertEvents[alertEvent].owner->client && level.alertEvents[alertEvent].owner->health >= 0 && level.alertEvents[alertEvent].owner->client->playerTeam == NPCS.NPC->client->enemyTeam ) {//an enemy G_SetEnemy( NPCS.NPC, level.alertEvents[alertEvent].owner ); //NPCInfo->enemyLastSeenTime = level.time; TIMER_Set( NPCS.NPC, "attackDelay", Q_irand( (6-NPCS.NPCInfo->stats.aim)*100, (6-NPCS.NPCInfo->stats.aim)*500 ) ); } } else {//FIXME: get more suspicious over time? //Save the position for movement (if necessary) //FIXME: sound? VectorCopy( level.alertEvents[alertEvent].position, NPCS.NPCInfo->investigateGoal ); NPCS.NPCInfo->investigateDebounceTime = level.time + Q_irand( 500, 1000 ); if ( level.alertEvents[alertEvent].level == AEL_SUSPICIOUS ) {//suspicious looks longer NPCS.NPCInfo->investigateDebounceTime += Q_irand( 500, 2500 ); } } } } if ( NPCS.NPCInfo->investigateDebounceTime > level.time ) {//FIXME: walk over to it, maybe? Not if not chase enemies flag //NOTE: stops walking or doing anything else below vec3_t dir, angles; float o_yaw, o_pitch; VectorSubtract( NPCS.NPCInfo->investigateGoal, NPCS.NPC->client->renderInfo.eyePoint, dir ); vectoangles( dir, angles ); o_yaw = NPCS.NPCInfo->desiredYaw; o_pitch = NPCS.NPCInfo->desiredPitch; NPCS.NPCInfo->desiredYaw = angles[YAW]; NPCS.NPCInfo->desiredPitch = angles[PITCH]; NPC_UpdateAngles( qtrue, qtrue ); NPCS.NPCInfo->desiredYaw = o_yaw; NPCS.NPCInfo->desiredPitch = o_pitch; return; } } } //If we have somewhere to go, then do that if ( UpdateGoal() ) { NPCS.ucmd.buttons |= BUTTON_WALKING; NPC_MoveToGoal( qtrue ); } NPC_UpdateAngles( qtrue, qtrue ); }
/* * Toss, bounce, and fly movement. When onground, do nothing. */ void SV_Physics_Toss(edict_t *ent) { trace_t trace; vec3_t move; float backoff; edict_t *slave; qboolean wasinwater; qboolean isinwater; vec3_t old_origin; if (!ent) { return; } /* regular thinking */ SV_RunThink(ent); /* if not a team captain, so movement will be handled elsewhere */ if (ent->flags & FL_TEAMSLAVE) { return; } if (ent->velocity[2] > 0) { ent->groundentity = NULL; } /* check for the groundentity going away */ if (ent->groundentity) { if (!ent->groundentity->inuse) { ent->groundentity = NULL; } } /* if onground, return without moving */ if (ent->groundentity) { return; } VectorCopy(ent->s.origin, old_origin); SV_CheckVelocity(ent); /* add gravity */ if ((ent->movetype != MOVETYPE_FLY) && (ent->movetype != MOVETYPE_FLYMISSILE) && (ent->movetype != MOVETYPE_WALLBOUNCE)) { SV_AddGravity(ent); } /* move angles */ VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); /* move origin */ VectorScale(ent->velocity, FRAMETIME, move); trace = SV_PushEntity(ent, move); if (!ent->inuse) { return; } if (trace.fraction < 1) { if (ent->movetype == MOVETYPE_WALLBOUNCE) { backoff = 2.0; } else if (ent->movetype == MOVETYPE_BOUNCE) { backoff = 1.5; } else { backoff = 1; } ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff); if (ent->movetype == MOVETYPE_WALLBOUNCE) { vectoangles(ent->velocity, ent->s.angles); } /* stop if on ground */ if ((trace.plane.normal[2] > 0.7) && (ent->movetype != MOVETYPE_WALLBOUNCE)) { if ((ent->velocity[2] < 60) || (ent->movetype != MOVETYPE_BOUNCE)) { ent->groundentity = trace.ent; ent->groundentity_linkcount = trace.ent->linkcount; VectorCopy(vec3_origin, ent->velocity); VectorCopy(vec3_origin, ent->avelocity); } } } /* check for water transition */ wasinwater = (ent->watertype & MASK_WATER); ent->watertype = gi.pointcontents(ent->s.origin); isinwater = ent->watertype & MASK_WATER; if (isinwater) { ent->waterlevel = 1; } else { ent->waterlevel = 0; } if (!wasinwater && isinwater) { gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); } else if (wasinwater && !isinwater) { gi.positioned_sound(ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); } /* move teamslaves */ for (slave = ent->teamchain; slave; slave = slave->teamchain) { VectorCopy(ent->s.origin, slave->s.origin); gi.linkentity(slave); } }
void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; i<dist; i++) { VectorMA (point, crittersize, forward, point); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; p->endtime = cg.time + 350 + (crandom() * 100); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; p->endheight = LARGESIZE; p->endwidth = LARGESIZE; p->type = P_SMOKE; VectorCopy( origin, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -1; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } }
void TurretClientRun(centity_t *ent) { if (!ent->ghoul2) { weaponInfo_t *weaponInfo; trap->G2API_InitGhoul2Model(&ent->ghoul2, CG_ConfigString( CS_MODELS+ent->currentState.modelindex ), 0, 0, 0, 0, 0); if (!ent->ghoul2) { //bad return; } ent->torsoBolt = trap->G2API_AddBolt( ent->ghoul2, 0, "*flash02" ); trap->G2API_SetBoneAngles( ent->ghoul2, 0, "bone_hinge", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 100, cg.time ); trap->G2API_SetBoneAngles( ent->ghoul2, 0, "bone_gback", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 100, cg.time ); trap->G2API_SetBoneAngles( ent->ghoul2, 0, "bone_barrel", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 100, cg.time ); trap->G2API_SetBoneAnim( ent->ghoul2, 0, "model_root", 0, 11, BONE_ANIM_OVERRIDE_FREEZE, 0.8f, cg.time, 0, 0 ); ent->turAngles[ROLL] = 0; ent->turAngles[PITCH] = 90; ent->turAngles[YAW] = 0; weaponInfo = &cg_weapons[WP_TURRET]; if ( !weaponInfo->registered ) { CG_RegisterWeapon(WP_TURRET); } } if (ent->currentState.fireflag == 2) { //I'm about to blow if (ent->turAngles) { trap->G2API_SetBoneAngles( ent->ghoul2, 0, "bone_hinge", ent->turAngles, BONE_ANGLES_REPLACE, NEGATIVE_Y, NEGATIVE_Z, NEGATIVE_X, NULL, 100, cg.time ); } return; } else if (ent->currentState.fireflag && ent->bolt4 != ent->currentState.fireflag) { vec3_t muzzleOrg, muzzleDir; mdxaBone_t boltMatrix; trap->G2API_GetBoltMatrix(ent->ghoul2, 0, ent->torsoBolt, &boltMatrix, /*ent->lerpAngles*/vec3_origin, ent->lerpOrigin, cg.time, cgs.gameModels, ent->modelScale); BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, muzzleOrg); BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_X, muzzleDir); trap->FX_PlayEffectID(cgs.effects.mTurretMuzzleFlash, muzzleOrg, muzzleDir, -1, -1, qfalse); ent->bolt4 = ent->currentState.fireflag; } else if (!ent->currentState.fireflag) { ent->bolt4 = 0; } if (ent->currentState.bolt2 != ENTITYNUM_NONE) { //turn toward the enemy centity_t *enemy = &cg_entities[ent->currentState.bolt2]; if (enemy) { vec3_t enAng; vec3_t enPos; VectorCopy(enemy->currentState.pos.trBase, enPos); VectorSubtract(enPos, ent->lerpOrigin, enAng); VectorNormalize(enAng); vectoangles(enAng, enAng); enAng[ROLL] = 0; enAng[PITCH] += 90; CreepToPosition(enAng, ent->turAngles); } } else { vec3_t idleAng; float turnAmount; if (ent->turAngles[YAW] > 360) { ent->turAngles[YAW] -= 361; } if (!ent->dustTrailTime) { ent->dustTrailTime = cg.time; } turnAmount = (cg.time-ent->dustTrailTime)*0.03; if (turnAmount > 360) { turnAmount = 360; } idleAng[PITCH] = 90; idleAng[ROLL] = 0; idleAng[YAW] = ent->turAngles[YAW] + turnAmount; ent->dustTrailTime = cg.time; CreepToPosition(idleAng, ent->turAngles); } if (cg.time < ent->frame_minus1_refreshed) { ent->frame_minus1_refreshed = cg.time; return; } ent->frame_minus1_refreshed = cg.time; trap->G2API_SetBoneAngles( ent->ghoul2, 0, "bone_hinge", ent->turAngles, BONE_ANGLES_REPLACE, NEGATIVE_Y, NEGATIVE_Z, NEGATIVE_X, NULL, 100, cg.time ); }
/////////////////////////////////////////////////////////////////////// // Make the change in angles a little more gradual, not so snappy // Subtle, but noticeable. // // Modified from the original id ChangeYaw code... /////////////////////////////////////////////////////////////////////// void ACEMV_ChangeBotAngle (edict_t *ent) { float ideal_yaw; float ideal_pitch; float current_yaw; float current_pitch; float move; float speed; vec3_t ideal_angle; // Normalize the move angle first VectorNormalize(ent->move_vector); current_yaw = anglemod(ent->s.angles[YAW]); current_pitch = anglemod(ent->s.angles[PITCH]); vectoangles (ent->move_vector, ideal_angle); ideal_yaw = anglemod(ideal_angle[YAW]); ideal_pitch = anglemod(ideal_angle[PITCH]); // Yaw if (current_yaw != ideal_yaw) { move = ideal_yaw - current_yaw; speed = ent->yaw_speed; if (ideal_yaw > current_yaw) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->s.angles[YAW] = anglemod (current_yaw + move); } // Pitch if (current_pitch != ideal_pitch) { move = ideal_pitch - current_pitch; speed = ent->yaw_speed; if (ideal_pitch > current_pitch) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->s.angles[PITCH] = anglemod (current_pitch + move); } }
static void WP_FireConcussionAlt( gentity_t *ent ) {//a rail-gun-like beam int damage = weaponData[WP_CONCUSSION].altDamage, skip, traces = DISRUPTOR_ALT_TRACES; //int velocity = weaponData[WP_CONCUSSION].altVelocity; 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; if (ent->s.number >= MAX_CLIENTS) { vec3_t angles; vectoangles(forwardVec, angles); angles[PITCH] += ( Q_flrand(-1.0f, 1.0f) * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f angles[YAW] += ( Q_flrand(-1.0f, 1.0f) * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f AngleVectors(angles, forwardVec, vrightVec, up); } //Shove us backwards for half a second VectorMA( ent->client->ps.velocity, -200, forwardVec, ent->client->ps.velocity ); ent->client->ps.groundEntityNum = ENTITYNUM_NONE; if ( (ent->client->ps.pm_flags&PMF_DUCKED) ) {//hunkered down ent->client->ps.pm_time = 100; } else { ent->client->ps.pm_time = 250; } ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK|PMF_TIME_NOFRICTION; //FIXME: only if on ground? So no "rocket jump"? Or: (see next FIXME) //FIXME: instead, set a forced ucmd backmove instead of this sliding 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 = CONC_ALT_NPC_DAMAGE_EASY; break; case 1: damage = CONC_ALT_NPC_DAMAGE_MEDIUM; break; case 2: default: damage = CONC_ALT_NPC_DAMAGE_HARD; break; } damage *= weaponData[WP_CONCUSSION].npcAltDmgMult; } VectorCopy( muzzle, start ); WP_TraceSetStart( ent, start, vec3_origin, vec3_origin ); 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; // } //Make it a little easier to hit guys at long range vec3_t shot_mins, shot_maxs; VectorSet( shot_mins, -1, -1, -1 ); VectorSet( shot_maxs, 1, 1, 1 ); 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 ); gi.trace( &tr, start, shot_mins, shot_maxs, 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! Concussion 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_CONC_ALT_SHOT ); //tent->svFlags |= SVF_BROADCAST; //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_MANDA || traceEnt->client->NPC_class == CLASS_COMMANDO||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( "concussion/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_CONC_ALT ); qboolean noKnockBack = (qboolean)((traceEnt->flags&FL_NO_KNOCKBACK) != 0);//will be set if they die, I want to know if it was on *before* they died 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, MOD_CONC_ALT, hitLoc ); break; } G_Damage( traceEnt, ent, ent, forwardVec, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT, hitLoc ); //do knockback and knockdown manually if ( traceEnt->client ) {//only if we hit a client vec3_t pushDir; VectorCopy( forwardVec, pushDir ); if ( pushDir[2] < 0.2f ) { pushDir[2] = 0.2f; }//hmm, re-normalize? nah... //if ( traceEnt->NPC || Q_irand(0,g_spskill->integer+1) ) { if ( !noKnockBack ) {//knock-backable G_Throw( traceEnt, pushDir, 200 ); if ( traceEnt->client->NPC_class == CLASS_ROCKETTROOPER ) { traceEnt->client->ps.pm_time = Q_irand( 1500, 3000 ); } } if ( traceEnt->health > 0 ) {//alive if ( G_HasKnockdownAnims( traceEnt ) ) {//knock-downable G_Knockdown( traceEnt, ent, pushDir, 400, qtrue ); } } } } 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_CONC_ALT_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 beam all the way to the end tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT ); tent->svFlags |= SVF_BROADCAST; 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: creates *way* too many effects, make it one effect somehow? G_PlayEffect( G_EffectIndex( "concussion/alt_ring" ), spot, forwardVec ); } //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 ); G_PlayEffect( G_EffectIndex( "concussion/altmuzzle_flash" ), muzzle, forwardVec ); }
//G_Printf("%i: Bounced Bolt @ Level %i\n", player->s.number, otherDefLevel); void OJP_HandleBoltBlock(gentity_t *bolt, gentity_t *player, trace_t *trace) { //handles all the behavior needed to saber block a blaster bolt. //I created this function to unite the duplicated code in G_MissileImpact vec3_t fwd; gentity_t *saberBoltEffect; int otherDefLevel=ReflectionLevel(player); int randMax = 0; gentity_t *prevOwner = &g_entities[bolt->r.ownerNum]; //previous owner of the bolt. Used for awarding experience to attacker. qboolean manualDeflect = ((player->client->pers.cmd.buttons & BUTTON_ATTACK) ? qtrue : qfalse); //create the bolt saber block effect saberBoltEffect = G_TempEntity( bolt->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(bolt->r.currentOrigin, saberBoltEffect->s.origin); VectorCopy(trace->plane.normal, saberBoltEffect->s.angles); saberBoltEffect->s.eventParm = 0; saberBoltEffect->s.weapon = 0;//saberNum saberBoltEffect->s.legsAnim = 0;//bladeNum if(manualDeflect) { if(otherDefLevel == FORCE_LEVEL_3) { //manual reflection, bounce to the crosshair, roughly int rand = Q_irand(1,100); rand = ( rand <= 50 ? player->client->shotsBlocked+1 : 9001 ); } else if(otherDefLevel == FORCE_LEVEL_2) { //natural reflection, bounce back to the attacker. int rand = Q_irand(1,100); if(rand <= 25) { randMax=player->client->shotsBlocked+1; } else randMax=9001; } else { //just deflect the attack int rand = Q_irand(1,100); if(rand <= 15) { randMax=player->client->shotsBlocked+1; } else randMax=9001; } } else { //determine reflection level. if(otherDefLevel == FORCE_LEVEL_3) { //manual reflection, bounce to the crosshair, roughly randMax=2; } else if(otherDefLevel == FORCE_LEVEL_2) { //natural reflection, bounce back to the attacker. randMax=5; } else { //just deflect the attack randMax=7; } } //G_Printf("%i: Bounced Bolt @ Level %i\n", player->s.number, otherDefLevel); AngleVectors(player->client->ps.viewangles, fwd, NULL, NULL); player->client->shotsBlocked++; if(player->client->shotsBlocked >= randMax && player->client->ps.fd.saberAnimLevel != SS_MEDIUM && player->client->ps.fd.saberAnimLevel != SS_TAVION && player->client->ps.fd.saberAnimLevel != SS_FAST) { gentity_t *owner = &g_entities[player->r.ownerNum]; float distance = VectorDistance(owner->r.currentOrigin, prevOwner->r.currentOrigin); player->client->shotsBlocked=0; if(distance < 80.0f) { //attempted upclose exception G_DeflectMissile(player, bolt, fwd); } else { vec3_t bounce_dir, angs; float speed; int i=0; //gentity_t *owner = ent; //int isowner = 0; if(!manualDeflect) { for(i=0; i<3; i++) { fwd[i]=1.5f; } } //add some slop factor to the manual reflections. if(player->client->pers.cmd.forwardmove >= 0) { float slopFactor = (MISHAP_MAXINACCURACY-6) * (FORCE_LEVEL_3 - player->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE])/FORCE_LEVEL_3; //[MoreRandom] slopFactor += Q_irand(1,5); vectoangles( fwd, angs ); //angs[PITCH] += flrand(-slopFactor, slopFactor); //angs[YAW] += flrand(-slopFactor, slopFactor); angs[PITCH] += flrand(1, slopFactor); angs[YAW] += flrand(1, slopFactor); AngleVectors( angs, fwd, NULL, NULL ); } else { vectoangles( fwd, angs ); angs[PITCH]+=flrand(1,3); angs[YAW]+=flrand(1,3); AngleVectors( angs, fwd, NULL, NULL ); } //G_Printf("%i: %i: Level 3 Reflect\n", level.time, player->s.number); //save the original speed speed = VectorNormalize( bolt->s.pos.trDelta ); VectorCopy(fwd, bounce_dir); VectorScale( bounce_dir, speed, bolt->s.pos.trDelta ); bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( bolt->r.currentOrigin, bolt->s.pos.trBase ); if ( bolt->s.weapon != WP_SABER && bolt->s.weapon != G2_MODEL_PART ) { //you are mine, now! bolt->r.ownerNum = player->s.number; } if ( bolt->s.weapon == WP_ROCKET_LAUNCHER ) { //stop homing bolt->think = 0; bolt->nextthink = 0; } } } else G_DeflectMissile(player, bolt, fwd); /* if (otherDefLevel <= FORCE_LEVEL_1) {//only randomly deflect away the bolt G_DeflectMissile(player, bolt, fwd); } else if (otherDefLevel == FORCE_LEVEL_2) {//bounce the bolt back to sender //G_Printf("%i: %i: Level 2 Reflect\n", level.time, player->s.number); G_DeflectMissile(player, bolt, fwd); } else if(otherDefLevel == FORCE_LEVEL_3 && distance < 80.0f &&(BG_SaberInAttack( player->client->ps.saberMove ) || PM_SaberInStart( player->client->ps.saberMove))) {//manual reflection, bounce to the crosshair, roughly G_DeflectMissile(player, bolt, fwd);; } else if(player->client->ps.fd.saberAnimLevel == SS_FAST || player->client->ps.fd.saberAnimLevel == SS_MEDIUM || player->client->ps.fd.saberAnimLevel == SS_TAVION) { G_DeflectMissile(player, bolt, fwd); } else {//FORCE_LEVEL_3, reflect the bolt to whereever the player is aiming. gentity_t *owner = &g_entities[player->r.ownerNum]; float distance = VectorDistance(owner->r.currentOrigin, prevOwner->r.currentOrigin); if(distance < 80.0f) {//attempted upclose exception G_DeflectMissile(player, bolt, fwd); } else { vec3_t bounce_dir, angs; float speed; //gentity_t *owner = ent; //int isowner = 0; //add some slop factor to the manual reflections. if(player->client->pers.cmd.forwardmove >= 0) { float slopFactor = (MISHAP_MAXINACCURACY-6) * (FORCE_LEVEL_3 - player->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE])/FORCE_LEVEL_3; //[MoreRandom] float distance = VectorDistance(player->r.currentOrigin,prevOwner->r.currentOrigin); slopFactor += Q_irand(1,5); vectoangles( fwd, angs ); //angs[PITCH] += flrand(-slopFactor, slopFactor); //angs[YAW] += flrand(-slopFactor, slopFactor); angs[PITCH] += flrand(1, slopFactor); angs[YAW] += flrand(1, slopFactor); AngleVectors( angs, fwd, NULL, NULL ); } else { vectoangles( fwd, angs ); angs[PITCH]+=flrand(1,3); angs[YAW]+=flrand(1,3); AngleVectors( angs, fwd, NULL, NULL ); } //G_Printf("%i: %i: Level 3 Reflect\n", level.time, player->s.number); //save the original speed speed = VectorNormalize( bolt->s.pos.trDelta ); VectorCopy(fwd, bounce_dir); VectorScale( bounce_dir, speed, bolt->s.pos.trDelta ); bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( bolt->r.currentOrigin, bolt->s.pos.trBase ); if ( bolt->s.weapon != WP_SABER && bolt->s.weapon != G2_MODEL_PART ) {//you are mine, now! bolt->r.ownerNum = player->s.number; } if ( bolt->s.weapon == WP_ROCKET_LAUNCHER ) {//stop homing bolt->think = 0; bolt->nextthink = 0; } } } */ //For jedi AI player->client->ps.saberEventFlags |= SEF_DEFLECTED; //deduce DP cost //[ExpSys] bolt->activator = prevOwner; if(otherDefLevel == FORCE_LEVEL_3 && player->client->pers.cmd.forwardmove < 0) { int amount = OJP_SaberBlockCost(player, bolt, trace->endpos); amount/=100*40; G_DodgeDrain(player, prevOwner, amount); } else G_DodgeDrain(player, prevOwner, OJP_SaberBlockCost(player, bolt, trace->endpos)); //[ExpSys] //[SaberLockSys] player->client->ps.saberLockFrame = 0; //break out of saberlocks. //[/SaberLockSys] //debounce time between blocks. player->client->ps.saberBlockTime = level.time + (600 - (player->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]*200)); }
static void WP_FireConcussion( gentity_t *ent ) {//a fast rocket-like projectile vec3_t start; int damage = weaponData[WP_CONCUSSION].damage; float vel = weaponData[WP_CONCUSSION].velocity; if (ent->s.number >= MAX_CLIENTS) { vec3_t angles; vectoangles(forwardVec, angles); angles[PITCH] += ( Q_flrand(-1.0f, 1.0f) * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f angles[YAW] += ( Q_flrand(-1.0f, 1.0f) * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f AngleVectors(angles, forwardVec, vrightVec, up); } //hold us still for a bit ent->client->ps.pm_time = 300; ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; //add viewkick if ( ent->s.number < MAX_CLIENTS//player only && !cg.renderingThirdPerson )//gives an advantage to being in 3rd person, but would look silly otherwise {//kick the view back cg.kick_angles[PITCH] = Q_flrand( -10, -15 ); cg.kick_time = level.time; } VectorCopy( muzzle, start ); WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall gentity_t *missile = CreateMissile( start, forwardVec, vel, 10000, ent, qfalse ); missile->classname = "conc_proj"; missile->s.weapon = WP_CONCUSSION; missile->mass = 10; // Do the damages if ( ent->s.number != 0 ) { if ( g_spskill->integer == 0 ) { damage = CONC_NPC_DAMAGE_EASY; } else if ( g_spskill->integer == 1 ) { damage = CONC_NPC_DAMAGE_NORMAL; } else { damage = CONC_NPC_DAMAGE_HARD; } damage *= weaponData[WP_BLASTER].npcDmgMult; } // Make it easier to hit things VectorSet( missile->maxs, ROCKET_SIZE, ROCKET_SIZE, ROCKET_SIZE ); VectorScale( missile->maxs, -1, missile->mins ); missile->damage = damage; missile->dflags = DAMAGE_EXTRA_KNOCKBACK; missile->methodOfDeath = MOD_CONC; missile->splashMethodOfDeath = MOD_CONC; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; missile->splashDamage = weaponData[WP_CONCUSSION].splashDamage; missile->splashRadius = weaponData[WP_CONCUSSION].splashRadius; // we don't want it to ever bounce missile->bounceCount = 0; }
void drop_charge (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "detpack"; bolt->nextthink = level.time + FRAMETIME; bolt->think = G_RunObject; bolt->s.eType = ET_GENERAL; bolt->s.g2radius = 100; bolt->s.modelGhoul2 = 1; bolt->s.modelindex = G_ModelIndex("models/weapons2/detpack/det_pack_proj.glm"); bolt->parent = self; bolt->r.ownerNum = self->s.number; bolt->damage = Detpack_Damage; bolt->splashDamage = Detpack_Damage; bolt->splashRadius = 200; bolt->methodOfDeath = MOD_DET_PACK_SPLASH; bolt->splashMethodOfDeath = MOD_DET_PACK_SPLASH; bolt->clipmask = MASK_SHOT; bolt->s.solid = 2; bolt->r.contents = MASK_SHOT; bolt->touch = charge_stick; bolt->physicsObject = qtrue; bolt->s.genericenemyindex = self->s.number+MAX_GENTITIES; //rww - so client prediction knows we own this and won't hit it VectorSet( bolt->r.mins, -2, -2, -2 ); VectorSet( bolt->r.maxs, 2, 2, 2 ); bolt->health = 1; bolt->takedamage = qtrue; bolt->pain = DetPackPain; bolt->die = DetPackDie; bolt->s.weapon = WP_DET_PACK; bolt->setTime = level.time; G_SetOrigin(bolt, start); bolt->s.pos.trType = TR_GRAVITY; VectorCopy( start, bolt->s.pos.trBase ); VectorScale(dir, 300, bolt->s.pos.trDelta ); bolt->s.pos.trTime = level.time; bolt->s.apos.trType = TR_GRAVITY; bolt->s.apos.trTime = level.time; bolt->s.apos.trBase[YAW] = rand()%360; bolt->s.apos.trBase[PITCH] = rand()%360; bolt->s.apos.trBase[ROLL] = rand()%360; if (rand()%10 < 5) { bolt->s.apos.trBase[YAW] = -bolt->s.apos.trBase[YAW]; } vectoangles(dir, bolt->s.angles); VectorCopy(bolt->s.angles, bolt->s.apos.trBase); VectorSet(bolt->s.apos.trDelta, 300, 0, 0 ); bolt->s.apos.trTime = level.time; trap_LinkEntity(bolt); //[CoOp] //make some sight/sound events AddSoundEvent( NULL, bolt->r.currentOrigin, 128, AEL_MINOR, qtrue, qfalse ); AddSightEvent( NULL, bolt->r.currentOrigin, 128, AEL_SUSPICIOUS, 10 ); //[/CoOp] }
void charge_stick (gentity_t *self, gentity_t *other, trace_t *trace) { gentity_t *tent; if ( other && (other->flags&FL_BBRUSH) && other->s.pos.trType == TR_STATIONARY && other->s.apos.trType == TR_STATIONARY ) {//a perfectly still breakable brush, let us attach directly to it! self->target_ent = other;//remember them when we blow up } else if ( other && other->s.number < ENTITYNUM_WORLD && other->s.eType == ET_MOVER && trace->plane.normal[2] > 0 ) {//stick to it? self->s.groundEntityNum = other->s.number; } else if (other && other->s.number < ENTITYNUM_WORLD && (other->client || !other->s.weapon)) { //hit another entity that is not stickable, "bounce" off //self->target_ent = other; vec3_t vNor, tN; VectorCopy(trace->plane.normal, vNor); VectorNormalize(vNor); VectorNPos(self->s.pos.trDelta, tN); self->s.pos.trDelta[0] += vNor[0]*(tN[0]*(((float)Q_irand(1, 10))*0.1)); self->s.pos.trDelta[1] += vNor[1]*(tN[1]*(((float)Q_irand(1, 10))*0.1)); self->s.pos.trDelta[2] += vNor[1]*(tN[2]*(((float)Q_irand(1, 10))*0.1)); vectoangles(vNor, self->s.angles); vectoangles(vNor, self->s.apos.trBase); self->touch = charge_stick; return; } else if (other && other->s.number < ENTITYNUM_WORLD) { //hit an entity that we just want to explode on (probably another projectile or something) vec3_t v; self->touch = 0; self->think = 0; self->nextthink = 0; self->takedamage = qfalse; VectorClear(self->s.apos.trDelta); self->s.apos.trType = TR_STATIONARY; G_RadiusDamage( self->r.currentOrigin, self->parent, self->splashDamage, self->splashRadius, self, self, MOD_DET_PACK_SPLASH ); VectorCopy(trace->plane.normal, v); VectorCopy(v, self->pos2); self->count = -1; G_PlayEffect(EFFECT_EXPLOSION_DETPACK, self->r.currentOrigin, v); self->think = G_FreeEntity; self->nextthink = level.time; return; } //JAC Bugfix //if we get here I guess we hit hte world so we can stick to it //Raz: This fix requires a bit of explaining.. // When you suicide, all of the detpacks you have placed (either on a wall, or still falling in the air) will // have their ent->think() set to DetPackBlow and ent->nextthink will be between 100 <-> 300 // If your detpacks land on a surface (i.e. charge_stick gets called) within that 100<->300 ms then ent->think() // will be overwritten (set to DetpackBlow) and ent->nextthink will be 30000 // The end result is your detpacks won't explode, but will be stuck to the wall for 30 seconds without // being able to detonate them (or shoot them) // The fix Sil came up with is to check the think() function in charge_stick, and only overwrite it // if they haven't been primed to detonate if ( self->think == G_RunObject ) { self->touch = 0; self->think = DetPackBlow; //[OpenRP - Detpacks last forever] self->nextthink = level.time + Q3_INFINITE; //make them last forever //[/OpenRP - Detpacks last forever] } VectorClear(self->s.apos.trDelta); self->s.apos.trType = TR_STATIONARY; self->s.pos.trType = TR_STATIONARY; VectorCopy( self->r.currentOrigin, self->s.origin ); VectorCopy( self->r.currentOrigin, self->s.pos.trBase ); VectorClear( self->s.pos.trDelta ); VectorClear( self->s.apos.trDelta ); VectorNormalize(trace->plane.normal); vectoangles(trace->plane.normal, self->s.angles); VectorCopy(self->s.angles, self->r.currentAngles ); VectorCopy(self->s.angles, self->s.apos.trBase); VectorCopy(trace->plane.normal, self->pos2); self->count = -1; G_Sound(self, CHAN_WEAPON, G_SoundIndex("sound/weapons/detpack/stick.wav")); tent = G_TempEntity( self->r.currentOrigin, EV_MISSILE_MISS ); tent->s.weapon = 0; tent->parent = self; tent->r.ownerNum = self->s.number; //so that the owner can blow it up with projectiles self->r.svFlags |= SVF_OWNERNOTSHARED; }
qboolean NPC_GetMoveDirectionAltRoute( vec3_t out, float *distance, qboolean tryStraight ) { vec3_t angles; NPCInfo->aiFlags &= ~NPCAI_BLOCKED; //Clear the struct memset( &frameNavInfo, 0, sizeof( frameNavInfo ) ); //Get our movement, if any if ( NPC_GetMoveInformation( frameNavInfo.direction, &frameNavInfo.distance ) == qfalse ) return qfalse; //Setup the return value *distance = frameNavInfo.distance; //For starters VectorCopy( frameNavInfo.direction, frameNavInfo.pathDirection ); //If on a ladder, move appropriately if ( NPC->watertype & CONTENTS_LADDER ) { NPC_LadderMove( frameNavInfo.direction ); return qtrue; } //Attempt a straight move to goal if ( !tryStraight || NPC_ClearPathToGoal( frameNavInfo.direction, NPCInfo->goalEntity ) == qfalse ) {//blocked //Can't get straight to goal, use macro nav if ( NAVNEW_MoveToGoal( NPC, frameNavInfo ) == WAYPOINT_NONE ) { //Can't reach goal, just face vectoangles( frameNavInfo.direction, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qfalse; } //else we are on our way frameNavInfo.flags |= NIF_MACRO_NAV; } else {//we have no architectural problems, see if there are ents inthe way and try to go around them //not blocked if ( d_altRoutes->integer ) {//try macro nav navInfo_t tempInfo; memcpy( &tempInfo, &frameNavInfo, sizeof( tempInfo ) ); if ( NAVNEW_AvoidCollision( NPC, NPCInfo->goalEntity, tempInfo, qtrue, 5 ) == qfalse ) {//revert to macro nav //Can't get straight to goal, dump tempInfo and use macro nav if ( NAVNEW_MoveToGoal( NPC, frameNavInfo ) == WAYPOINT_NONE ) { //Can't reach goal, just face vectoangles( frameNavInfo.direction, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qfalse; } //else we are on our way frameNavInfo.flags |= NIF_MACRO_NAV; } else {//otherwise, either clear or can avoid memcpy( &frameNavInfo, &tempInfo, sizeof( frameNavInfo ) ); } } else {//OR: just give up if ( NAVNEW_AvoidCollision( NPC, NPCInfo->goalEntity, frameNavInfo, qtrue, 30 ) == qfalse ) {//give up return qfalse; } } } //Setup the return values VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qtrue; }
/* =============== G_DamageFeedback Called just before a snapshot is sent to the given player. Totals up all damage and generates both the player_state_t damage values to that client for pain blends and kicks, and global pain sound events for all clients. =============== */ void P_DamageFeedback( gentity_t *player ) { gclient_t *client; float count; vec3_t angles; client = player->client; if ( client->ps.pm_type == PM_DEAD ) { return; } // total points of damage shot at the player this frame count = client->damage_blood + client->damage_armor; if ( count == 0 ) { return; // didn't take any damage } if ( count > 255 ) { count = 255; } // send the information to the client // world damage (falling, slime, etc) uses a special code // to make the blend blob centered instead of positional if ( client->damage_fromWorld ) { client->ps.damagePitch = 255; client->ps.damageYaw = 255; client->damage_fromWorld = qfalse; } else { vectoangles( client->damage_from, angles ); client->ps.damagePitch = angles[PITCH]/360.0 * 256; client->ps.damageYaw = angles[YAW]/360.0 * 256; } // play an apropriate pain sound if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) { // don't do more than two pain sounds a second if ( level.time - client->ps.painTime < 500 ) { return; } P_SetTwitchInfo(client); player->pain_debounce_time = level.time + 700; G_AddEvent( player, EV_PAIN, player->health ); client->ps.damageEvent++; if (client->damage_armor && !client->damage_blood) { client->ps.damageType = 1; //pure shields } else if (client->damage_armor) { client->ps.damageType = 2; //shields and health } else { client->ps.damageType = 0; //pure health } } client->ps.damageCount = count; // // clear totals // client->damage_blood = 0; client->damage_armor = 0; client->damage_knockback = 0; }
qboolean NPC_MoveToGoal( qboolean tryStraight ) { #if AI_TIMERS int startTime = GetTime(0); #endif// AI_TIMERS //If taking full body pain, don't move if ( PM_InKnockDown( &NPC->client->ps ) || ( ( NPC->s.legsAnim >= BOTH_PAIN1 ) && ( NPC->s.legsAnim <= BOTH_PAIN19 ) ) ) { return qtrue; } if( NPC->s.eFlags & EF_LOCKED_TO_WEAPON ) {//If in an emplaced gun, never try to navigate! return qtrue; } float distance; vec3_t dir; //FIXME: if can't get to goal & goal is a target (enemy), try to find a waypoint that has line of sight to target, at least? //Get our movement direction #if 1 if ( NPC_GetMoveDirectionAltRoute( dir, &distance, tryStraight ) == qfalse ) #else if ( NPC_GetMoveDirection( dir, &distance ) == qfalse ) #endif return qfalse; NPCInfo->distToGoal = distance; //Convert the move to angles vectoangles( dir, NPCInfo->lastPathAngles ); //FIXME: still getting ping-ponging in certain cases... !!! Nav/avoidance error? WTF???!!! //If in combat move, then move directly towards our goal if ( NPC_CheckCombatMove() ) {//keep current facing G_UcmdMoveForDir( NPC, &ucmd, dir ); } else {//face our goal //FIXME: strafe instead of turn if change in dir is small and temporary NPCInfo->desiredPitch = 0.0f; NPCInfo->desiredYaw = AngleNormalize360( NPCInfo->lastPathAngles[YAW] ); //Pitch towards the goal and also update if flying or swimming if ( NPCInfo->stats.moveType == MT_FLYSWIM ) { NPCInfo->desiredPitch = AngleNormalize360( NPCInfo->lastPathAngles[PITCH] ); if ( dir[2] ) { float scale = (dir[2] * distance); if ( scale > 64 ) { scale = 64; } else if ( scale < -64 ) { scale = -64; } NPC->client->ps.velocity[2] = scale; //NPC->client->ps.velocity[2] = (dir[2] > 0) ? 64 : -64; } } //Set any final info ucmd.forwardmove = 127; } #if AI_TIMERS navTime += GetTime( startTime ); #endif// AI_TIMERS return qtrue; }
/* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. If "g_synchronousClients 1" is set, this will be called exactly once for each server frame, which makes for smooth demo recording. ============== */ void ClientThink_real( gentity_t *ent ) { gclient_t *client; pmove_t pm; int oldEventSequence; int msec; int i; usercmd_t *ucmd; client = ent->client; // don't think if the client is not yet connected (and thus not yet spawned in) if (client->pers.connected != CON_CONNECTED) { return; } // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; // sanity check the command time to prevent speedup cheating if ( ucmd->serverTime > level.time + 200 ) { ucmd->serverTime = level.time + 200; // G_Printf("serverTime <<<<<\n" ); } if ( ucmd->serverTime < level.time - 1000 ) { ucmd->serverTime = level.time - 1000; // G_Printf("serverTime >>>>>\n" ); } msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) { return; } if ( msec > 200 ) { msec = 200; } if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); } if ( pmove_fixed.integer || client->pers.pmoveFixed ) { ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; //if (ucmd->serverTime - client->ps.commandTime <= 0) // return; } // // check for exiting intermission // if ( level.intermissiontime ) { ClientIntermissionThink( client ); return; } // spectators don't do much if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { return; } SpectatorThink( ent, ucmd ); return; } if (ent && ent->client && (ent->client->ps.eFlags & EF_INVULNERABLE)) { if (ent->client->invulnerableTimer <= level.time) { ent->client->ps.eFlags &= ~EF_INVULNERABLE; } } // check for inactivity timer, but never drop the local client of a non-dedicated server if ( !ClientInactivityTimer( client ) ) { return; } // clear the rewards if time if ( level.time > client->rewardTime ) { client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); } if ( client->noclip ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.eFlags & EF_DISINTEGRATION ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) { client->ps.pm_type = PM_DEAD; } else { if (client->ps.forceGripChangeMovetype) { client->ps.pm_type = client->ps.forceGripChangeMovetype; } else { client->ps.pm_type = PM_NORMAL; } } client->ps.gravity = g_gravity.value; // set speed client->ps.speed = g_speed.value; client->ps.basespeed = g_speed.value; if (ent->client->ps.duelInProgress) { gentity_t *duelAgainst = &g_entities[ent->client->ps.duelIndex]; //Keep the time updated, so once this duel ends this player can't engage in a duel for another //10 seconds. This will give other people a chance to engage in duels in case this player wants //to engage again right after he's done fighting and someone else is waiting. ent->client->ps.fd.privateDuelTime = level.time + 10000; if (ent->client->ps.duelTime < level.time) { //Bring out the sabers if (ent->client->ps.weapon == WP_SABER && ent->client->ps.saberHolstered && ent->client->ps.duelTime) { if (!saberOffSound || !saberOnSound) { saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav"); saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav"); } ent->client->ps.saberHolstered = qfalse; G_Sound(ent, CHAN_AUTO, saberOnSound); G_AddEvent(ent, EV_PRIVATE_DUEL, 2); ent->client->ps.duelTime = 0; } if (duelAgainst && duelAgainst->client && duelAgainst->inuse && duelAgainst->client->ps.weapon == WP_SABER && duelAgainst->client->ps.saberHolstered && duelAgainst->client->ps.duelTime) { if (!saberOffSound || !saberOnSound) { saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav"); saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav"); } duelAgainst->client->ps.saberHolstered = qfalse; G_Sound(duelAgainst, CHAN_AUTO, saberOnSound); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 2); duelAgainst->client->ps.duelTime = 0; } } else { client->ps.speed = 0; client->ps.basespeed = 0; ucmd->forwardmove = 0; ucmd->rightmove = 0; ucmd->upmove = 0; } if (!duelAgainst || !duelAgainst->client || !duelAgainst->inuse || duelAgainst->client->ps.duelIndex != ent->s.number) { ent->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); } else if (duelAgainst->health < 1 || duelAgainst->client->ps.stats[STAT_HEALTH] < 1) { ent->client->ps.duelInProgress = 0; duelAgainst->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0); //Winner gets full health.. providing he's still alive if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0) { if (ent->health < ent->client->ps.stats[STAT_MAX_HEALTH]) { ent->client->ps.stats[STAT_HEALTH] = ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } if (g_spawnInvulnerability.integer) { ent->client->ps.eFlags |= EF_INVULNERABLE; ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer; } } /* trap_SendServerCommand( ent-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) ); trap_SendServerCommand( duelAgainst-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) ); */ //Private duel announcements are now made globally because we only want one duel at a time. if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0) { trap_SendServerCommand( -1, va("cp \"%s %s %s!\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER"), duelAgainst->client->pers.netname) ); } else { //it was a draw, because we both managed to die in the same frame trap_SendServerCommand( -1, va("cp \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELTIE")) ); } } else { vec3_t vSub; float subLen = 0; VectorSubtract(ent->client->ps.origin, duelAgainst->client->ps.origin, vSub); subLen = VectorLength(vSub); if (subLen >= 1024) { ent->client->ps.duelInProgress = 0; duelAgainst->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELSTOP")) ); } } } /* if ( client->ps.powerups[PW_HASTE] ) { client->ps.speed *= 1.3; } */ if (client->ps.usingATST && ent->health > 0) { //we have special shot clip boxes as an ATST ent->r.contents |= CONTENTS_NOSHOT; ATST_ManageDamageBoxes(ent); } else { ent->r.contents &= ~CONTENTS_NOSHOT; client->damageBoxHandle_Head = 0; client->damageBoxHandle_RLeg = 0; client->damageBoxHandle_LLeg = 0; } //rww - moved this stuff into the pmove code so that it's predicted properly //BG_AdjustClientSpeed(&client->ps, &client->pers.cmd, level.time); // set up for pmove oldEventSequence = client->ps.eventSequence; memset (&pm, 0, sizeof(pm)); if ( ent->flags & FL_FORCE_GESTURE ) { ent->flags &= ~FL_FORCE_GESTURE; ent->client->pers.cmd.buttons |= BUTTON_GESTURE; } if (ent->client && ent->client->ps.fallingToDeath && (level.time - FALL_FADE_TIME) > ent->client->ps.fallingToDeath) { //die! player_die(ent, ent, ent, 100000, MOD_FALLING); respawn(ent); ent->client->ps.fallingToDeath = 0; G_MuteSound(ent->s.number, CHAN_VOICE); //stop screaming, because you are dead! } if (ent->client->ps.otherKillerTime > level.time && ent->client->ps.groundEntityNum != ENTITYNUM_NONE && ent->client->ps.otherKillerDebounceTime < level.time) { ent->client->ps.otherKillerTime = 0; ent->client->ps.otherKiller = ENTITYNUM_NONE; } else if (ent->client->ps.otherKillerTime > level.time && ent->client->ps.groundEntityNum == ENTITYNUM_NONE) { if (ent->client->ps.otherKillerDebounceTime < (level.time + 100)) { ent->client->ps.otherKillerDebounceTime = level.time + 100; } } // WP_ForcePowersUpdate( ent, msec, ucmd); //update any active force powers // WP_SaberPositionUpdate(ent, ucmd); //check the server-side saber point, do apprioriate server-side actions (effects are cs-only) if ((ent->client->pers.cmd.buttons & BUTTON_USE) && ent->client->ps.useDelay < level.time) { TryUse(ent); ent->client->ps.useDelay = level.time + 100; } pm.ps = &client->ps; pm.cmd = *ucmd; if ( pm.ps->pm_type == PM_DEAD ) { pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else if ( ent->r.svFlags & SVF_BOT ) { pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP; } else { pm.tracemask = MASK_PLAYERSOLID; } pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.debugLevel = g_debugMove.integer; pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0; pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; pm.pmove_msec = pmove_msec.integer; pm.animations = bgGlobalAnimations;//NULL; pm.gametype = g_gametype.integer; VectorCopy( client->ps.origin, client->oldOrigin ); if (level.intermissionQueued != 0 && g_singlePlayer.integer) { if ( level.time - level.intermissionQueued >= 1000 ) { pm.cmd.buttons = 0; pm.cmd.forwardmove = 0; pm.cmd.rightmove = 0; pm.cmd.upmove = 0; if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) { trap_SendConsoleCommand( EXEC_APPEND, "centerview\n"); } ent->client->ps.pm_type = PM_SPINTERMISSION; } } for ( i = 0 ; i < MAX_CLIENTS ; i++ ) { if (g_entities[i].inuse && g_entities[i].client) { pm.bgClients[i] = &g_entities[i].client->ps; } } if (ent->client->ps.saberLockTime > level.time) { gentity_t *blockOpp = &g_entities[ent->client->ps.saberLockEnemy]; if (blockOpp && blockOpp->inuse && blockOpp->client) { vec3_t lockDir, lockAng; //VectorClear( ent->client->ps.velocity ); VectorSubtract( blockOpp->r.currentOrigin, ent->r.currentOrigin, lockDir ); //lockAng[YAW] = vectoyaw( defDir ); vectoangles(lockDir, lockAng); SetClientViewAngle( ent, lockAng ); } if ( ( ent->client->buttons & BUTTON_ATTACK ) && ! ( ent->client->oldbuttons & BUTTON_ATTACK ) ) { ent->client->ps.saberLockHits++; } if (ent->client->ps.saberLockHits > 2) { if (!ent->client->ps.saberLockAdvance) { ent->client->ps.saberLockHits -= 3; } ent->client->ps.saberLockAdvance = qtrue; } } else { ent->client->ps.saberLockFrame = 0; } Pmove (&pm); switch(pm.cmd.generic_cmd) { case 0: break; case GENCMD_SABERSWITCH: Cmd_ToggleSaber_f(ent); break; case GENCMD_ENGAGE_DUEL: Cmd_EngageDuel_f(ent); break; case GENCMD_FORCE_HEAL: ForceHeal(ent); break; case GENCMD_FORCE_SPEED: ForceSpeed(ent, 0); break; case GENCMD_FORCE_THROW: ForceThrow(ent, qfalse); break; case GENCMD_FORCE_PULL: ForceThrow(ent, qtrue); break; case GENCMD_FORCE_DISTRACT: ForceTelepathy(ent); break; case GENCMD_FORCE_RAGE: ForceRage(ent); break; case GENCMD_FORCE_PROTECT: ForceProtect(ent); break; case GENCMD_FORCE_ABSORB: ForceAbsorb(ent); break; case GENCMD_FORCE_HEALOTHER: ForceTeamHeal(ent); break; case GENCMD_FORCE_FORCEPOWEROTHER: ForceTeamForceReplenish(ent); break; case GENCMD_FORCE_SEEING: ForceSeeing(ent); break; case GENCMD_USE_SEEKER: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SEEKER)) && G_ItemUsable(&ent->client->ps, HI_SEEKER) ) { ItemUse_Seeker(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_SEEKER, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SEEKER); } break; case GENCMD_USE_FIELD: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SHIELD)) && G_ItemUsable(&ent->client->ps, HI_SHIELD) ) { ItemUse_Shield(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_SHIELD, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SHIELD); } break; case GENCMD_USE_BACTA: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC)) && G_ItemUsable(&ent->client->ps, HI_MEDPAC) ) { ItemUse_MedPack(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_MEDPAC, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_MEDPAC); } break; case GENCMD_USE_ELECTROBINOCULARS: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) { ItemUse_Binoculars(ent); if (ent->client->ps.zoomMode == 0) { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); } else { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); } } break; case GENCMD_ZOOM: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) { ItemUse_Binoculars(ent); if (ent->client->ps.zoomMode == 0) { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); } else { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); } } break; case GENCMD_USE_SENTRY: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SENTRY_GUN)) && G_ItemUsable(&ent->client->ps, HI_SENTRY_GUN) ) { ItemUse_Sentry(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_SENTRY_GUN, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SENTRY_GUN); } break; case GENCMD_SABERATTACKCYCLE: Cmd_SaberAttackCycle_f(ent); break; default: break; } // save results of pmove if ( ent->client->ps.eventSequence != oldEventSequence ) { ent->eventTime = level.time; } if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); } else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); } SendPendingPredictableEvents( &ent->client->ps ); if ( !( ent->client->ps.eFlags & EF_FIRING ) ) { client->fireHeld = qfalse; // for grapple } // use the snapped origin for linking so it matches client predicted versions VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); VectorCopy (pm.mins, ent->r.mins); VectorCopy (pm.maxs, ent->r.maxs); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; // execute client events ClientEvents( ent, oldEventSequence ); if ( pm.useEvent ) { //TODO: Use // TryUse( ent ); } // link entity now, after any personal teleporters have been used trap_LinkEntity (ent); if ( !ent->client->noclip ) { G_TouchTriggers( ent ); } // NOTE: now copy the exact origin over otherwise clients can be snapped into solid VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); //test for solid areas in the AAS file // BotTestAAS(ent->r.currentOrigin); // touch other objects ClientImpacts( ent, &pm ); // save results of triggers and client events if (ent->client->ps.eventSequence != oldEventSequence) { ent->eventTime = level.time; } // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; // Did we kick someone in our pmove sequence? if (client->ps.forceKickFlip) { gentity_t *faceKicked = &g_entities[client->ps.forceKickFlip-1]; if (faceKicked && faceKicked->client && (!OnSameTeam(ent, faceKicked) || g_friendlyFire.integer) && (!faceKicked->client->ps.duelInProgress || faceKicked->client->ps.duelIndex == ent->s.number) && (!ent->client->ps.duelInProgress || ent->client->ps.duelIndex == faceKicked->s.number)) { if ( faceKicked && faceKicked->client && faceKicked->health && faceKicked->takedamage ) {//push them away and do pain vec3_t oppDir; int strength = (int)VectorNormalize2( client->ps.velocity, oppDir ); strength *= 0.05; VectorScale( oppDir, -1, oppDir ); G_Damage( faceKicked, ent, ent, oppDir, client->ps.origin, strength, DAMAGE_NO_ARMOR, MOD_MELEE ); if (faceKicked->health > 0 && faceKicked->client->ps.stats[STAT_HEALTH] > 0 && faceKicked->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN) { if (Q_irand(1, 10) <= 3) { //only actually knock over sometimes, but always do velocity hit faceKicked->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; faceKicked->client->ps.forceHandExtendTime = level.time + 1100; faceKicked->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim } faceKicked->client->ps.otherKiller = ent->s.number; faceKicked->client->ps.otherKillerTime = level.time + 5000; faceKicked->client->ps.otherKillerDebounceTime = level.time + 100; faceKicked->client->ps.velocity[0] = oppDir[0]*(strength*40); faceKicked->client->ps.velocity[1] = oppDir[1]*(strength*40); faceKicked->client->ps.velocity[2] = 200; } G_Sound( faceKicked, CHAN_AUTO, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) ); } } client->ps.forceKickFlip = 0; } // check for respawning if ( client->ps.stats[STAT_HEALTH] <= 0 ) { // wait for the attack button to be pressed if ( level.time > client->respawnTime ) { // forcerespawn is to prevent users from waiting out powerups if ( g_forcerespawn.integer > 0 && ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) { respawn( ent ); return; } // pressing attack or use is the normal respawn method if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) { respawn( ent ); } } return; } // perform once-a-second actions ClientTimerActions( ent, msec ); G_UpdateClientBroadcasts ( ent ); }
//--------------------------------------------------------- void FireWeapon( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { float alert = 256; // track shots taken for accuracy tracking. ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++; // set aiming directions if ( ent->s.weapon == WP_DISRUPTOR && alt_fire ) { if ( ent->NPC ) { //snipers must use the angles they actually did their shot trace with AngleVectors( ent->lastAngles, wpFwd, wpVright, wpUp ); } } else if ( ent->s.weapon == WP_ATST_SIDE || ent->s.weapon == WP_ATST_MAIN ) { vec3_t delta1, enemy_org1, muzzle1; vec3_t angleToEnemy1; VectorCopy( ent->client->renderInfo.muzzlePoint, muzzle1 ); if ( !ent->s.number ) {//player driving an AT-ST //SIGH... because we can't anticipate alt-fire, must calc muzzle here and now mdxaBone_t boltMatrix; int bolt; if ( ent->client->ps.weapon == WP_ATST_MAIN ) {//FIXME: alt_fire should fire both barrels, but slower? if ( ent->alt_fire ) { bolt = ent->handRBolt; } else { bolt = ent->handLBolt; } } else {// ATST SIDE weapons if ( ent->alt_fire ) { if ( gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], "head_light_blaster_cann" ) ) {//don't have it! return; } bolt = ent->genericBolt2; } else { if ( gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], "head_concussion_charger" ) ) {//don't have it! return; } bolt = ent->genericBolt1; } } vec3_t yawOnlyAngles = {0, ent->currentAngles[YAW], 0}; if ( ent->currentAngles[YAW] != ent->client->ps.legsYaw ) { yawOnlyAngles[YAW] = ent->client->ps.legsYaw; } gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, bolt, &boltMatrix, yawOnlyAngles, ent->currentOrigin, (cg.time?cg.time:level.time), NULL, ent->s.modelScale ); // work the matrix axis stuff into the original axis and origins used. gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent->client->renderInfo.muzzlePoint ); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, ent->client->renderInfo.muzzleDir ); ent->client->renderInfo.mPCalcTime = level.time; AngleVectors( ent->client->ps.viewangles, wpFwd, wpVright, wpUp ); //CalcMuzzlePoint( ent, wpFwd, vright, wpUp, wpMuzzle, 0 ); } else if ( !ent->enemy ) {//an NPC with no enemy to auto-aim at VectorCopy( ent->client->renderInfo.muzzleDir, wpFwd ); } else {//NPC, auto-aim at enemy CalcEntitySpot( ent->enemy, SPOT_HEAD, enemy_org1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, wpFwd, wpVright, wpUp); } } else if ( ent->s.weapon == WP_BOT_LASER && ent->enemy ) { vec3_t delta1, enemy_org1, muzzle1; vec3_t angleToEnemy1; CalcEntitySpot( ent->enemy, SPOT_HEAD, enemy_org1 ); CalcEntitySpot( ent, SPOT_WEAPON, muzzle1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, wpFwd, wpVright, wpUp); } else { AngleVectors( ent->client->ps.viewangles, wpFwd, wpVright, wpUp ); } ent->alt_fire = alt_fire; CalcMuzzlePoint ( ent, wpFwd, wpVright, wpUp, wpMuzzle , 0); // fire the specific weapon switch( ent->s.weapon ) { // Player weapons //----------------- case WP_SABER: return; break; case WP_BRYAR_PISTOL: WP_FireBryarPistol( ent, alt_fire ); break; case WP_BLASTER: WP_FireBlaster( ent, alt_fire ); break; case WP_DISRUPTOR: alert = 50; // if you want it to alert enemies, remove this WP_FireDisruptor( ent, alt_fire ); break; case WP_BOWCASTER: WP_FireBowcaster( ent, alt_fire ); break; case WP_REPEATER: WP_FireRepeater( ent, alt_fire ); break; case WP_DEMP2: WP_FireDEMP2( ent, alt_fire ); break; case WP_FLECHETTE: WP_FireFlechette( ent, alt_fire ); break; case WP_ROCKET_LAUNCHER: WP_FireRocket( ent, alt_fire ); break; case WP_THERMAL: WP_FireThermalDetonator( ent, alt_fire ); break; case WP_TRIP_MINE: alert = 0; // if you want it to alert enemies, remove this WP_PlaceLaserTrap( ent, alt_fire ); break; case WP_DET_PACK: alert = 0; // if you want it to alert enemies, remove this WP_FireDetPack( ent, alt_fire ); break; case WP_BOT_LASER: WP_BotLaser( ent ); break; case WP_EMPLACED_GUN: // doesn't care about whether it's alt-fire or not. We can do an alt-fire if needed WP_EmplacedFire( ent ); break; case WP_MELEE: alert = 0; // if you want it to alert enemies, remove this WP_Melee( ent ); break; case WP_ATST_MAIN: WP_ATSTMainFire( ent ); break; case WP_ATST_SIDE: // TEMP if ( alt_fire ) { // WP_FireRocket( ent, qfalse ); WP_ATSTSideAltFire(ent); } else { if ( ent->s.number == 0 && ent->client->ps.vehicleModel ) { WP_ATSTMainFire( ent ); } else { WP_ATSTSideFire(ent); } } break; case WP_TIE_FIGHTER: // TEMP WP_EmplacedFire( ent ); break; case WP_RAPID_FIRE_CONC: // TEMP if ( alt_fire ) { WP_FireRepeater( ent, alt_fire ); } else { WP_EmplacedFire( ent ); } break; case WP_STUN_BATON: WP_FireStunBaton( ent, alt_fire ); break; case WP_BLASTER_PISTOL: // enemy version WP_FireBryarPistol( ent, qfalse ); // never an alt-fire? break; // case WP_TRICORDER: // WP_TricorderScan( ent, alt_fire ); // break; default: return; break; } if ( !ent->s.number ) { if ( ent->s.weapon == WP_FLECHETTE || (ent->s.weapon == WP_BOWCASTER && !alt_fire) ) {//these can fire multiple shots, count them individually within the firing functions } else if ( W_AccuracyLoggableWeapon( ent->s.weapon, alt_fire, MOD_UNKNOWN ) ) { ent->client->sess.missionStats.shotsFired++; } } // We should probably just use this as a default behavior, in special cases, just set alert to false. if ( ent->s.number == 0 && alert > 0 ) { AddSoundEvent( ent, wpMuzzle, alert, AEL_DISCOVERED ); AddSightEvent( ent, wpMuzzle, alert*2, AEL_DISCOVERED, 20 ); } }
void soldier_fire (edict_t *self, int flash_number) { vec3_t start; vec3_t forward, right, up; vec3_t aim; vec3_t dir; vec3_t end; float r, u; int flash_index; if (self->s.skinnum < 2) flash_index = blaster_flash[flash_number]; else if (self->s.skinnum < 4) flash_index = shotgun_flash[flash_number]; else flash_index = machinegun_flash[flash_number]; AngleVectors (self->s.angles, forward, right, NULL); G_ProjectSource (self->s.origin, monster_flash_offset[flash_index], forward, right, start); if (flash_number == 5 || flash_number == 6) { VectorCopy (forward, aim); } else { VectorCopy (self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract (end, start, aim); vectoangles (aim, dir); AngleVectors (dir, forward, right, up); r = crandom()*1000; u = crandom()*500; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); VectorSubtract (end, start, aim); VectorNormalize (aim); } if (self->s.skinnum <= 1) { monster_fire_blaster (self, start, aim, 5, 600, flash_index, EF_BLASTER); } else if (self->s.skinnum <= 3) { monster_fire_shotgun (self, start, aim, 2, 1, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_index); } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) self->monsterinfo.pausetime = level.time + (3 + rand() % 8) * FRAMETIME; monster_fire_bullet (self, start, aim, 2, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_index); if (level.time >= self->monsterinfo.pausetime) self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; else self->monsterinfo.aiflags |= AI_HOLD_FRAME; } }
/* =============== CG_AddParticles =============== */ void CG_AddParticles (void) { cparticle_t *p, *next; float alpha; float time, time2; vec3_t org; int color; cparticle_t *active, *tail; int type; vec3_t rotate_ang; if (!initparticles) CG_ClearParticles (); VectorCopy( cg.refdef.viewaxis[0], pvforward ); VectorCopy( cg.refdef.viewaxis[1], pvright ); VectorCopy( cg.refdef.viewaxis[2], pvup ); vectoangles( cg.refdef.viewaxis[0], rotate_ang ); roll += ((cg.time - oldtime) * 0.1) ; rotate_ang[ROLL] += (roll*0.9); AngleVectors ( rotate_ang, rforward, rright, rup); oldtime = cg.time; active = NULL; tail = NULL; for (p=active_particles ; p ; p=next) { next = p->next; time = (cg.time - p->time)*0.001; alpha = p->alpha + time*p->alphavel; if (alpha <= 0) { // faded out p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_WEATHER_FLURRY) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_FLAT_SCALEUP_FADE) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) { // temporary sprite CG_AddParticleToScene (p, p->org, alpha); p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } p->next = NULL; if (!tail) active = tail = p; else { tail->next = p; tail = p; } if (alpha > 1.0) alpha = 1; color = p->color; time2 = time*time; org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; type = p->type; CG_AddParticleToScene (p, org, alpha); } active_particles = active; }
//---------------------------------------------------------------- static void turretG2_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org, ang; gentity_t *bolt; if ( (trap->PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... if ( ent->random ) { vectoangles( dir, ang ); ang[PITCH] += flrand( -ent->random, ent->random ); ang[YAW] += flrand( -ent->random, ent->random ); AngleVectors( ang, dir, NULL, NULL ); } vectoangles(dir, ang); if ( (ent->spawnflags&SPF_TURRETG2_TURBO) ) { //muzzle flash G_PlayEffectID( ent->genericValue13, org, ang ); ent->s.weapon = WP_BLASTER; ent->s.weaponVariation = 0; WP_FireGenericMissile( ent, 0, start, dir ); if ( ent->alt_fire ) { TurboLaser_SetBoneAnim( ent, 2, 3 ); } else { TurboLaser_SetBoneAnim( ent, 0, 1 ); } } else { G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), org, ang ); bolt = G_Spawn(); bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_BLASTER; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; bolt->dflags = (DAMAGE_NO_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS); // Don't push them around, or else we are constantly re-aiming bolt->splashDamage = ent->splashDamage; bolt->splashRadius = ent->splashDamage; bolt->methodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->splashMethodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //bolt->trigger_formation = qfalse; // don't draw tail on first frame VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); } }
void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; VectorNegate (dir, dir); length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; i<dist; i++) { VectorMA (point, crittersize, forward, point); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 5.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; // RF, stay around for long enough to expand and dissipate naturally if (length) p->endtime = cg.time + 4500 + (crandom() * 3500); else p->endtime = cg.time + 750 + (crandom() * 500); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; // RF, expand while falling p->endheight = LARGESIZE*3.0; p->endwidth = LARGESIZE*3.0; if (!length) { p->width *= 0.2f; p->height *= 0.2f; p->endheight = NORMALSIZE; p->endwidth = NORMALSIZE; } p->type = P_SMOKE; VectorCopy( point, p->org ); p->vel[0] = crandom()*6; p->vel[1] = crandom()*6; p->vel[2] = random()*20; // RF, add some gravity/randomness p->accel[0] = crandom()*3; p->accel[1] = crandom()*3; p->accel[2] = -PARTICLE_GRAVITY*0.4; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } }
//----------------------------------------------------- static void turretG2_aim( gentity_t *self ) //----------------------------------------------------- { vec3_t enemyDir, org, org2; vec3_t desiredAngles, setAngle; float diffYaw = 0.0f, diffPitch = 0.0f; float maxYawSpeed = (self->spawnflags&SPF_TURRETG2_TURBO)?30.0f:14.0f; float maxPitchSpeed = (self->spawnflags&SPF_TURRETG2_TURBO)?15.0f:3.0f; // move our gun base yaw to where we should be at this time.... BG_EvaluateTrajectory( &self->s.apos, level.time, self->r.currentAngles ); self->r.currentAngles[YAW] = AngleNormalize360( self->r.currentAngles[YAW] ); self->speed = AngleNormalize360( self->speed ); if ( self->enemy ) { mdxaBone_t boltMatrix; // ...then we'll calculate what new aim adjustments we should attempt to make this frame // Aim at enemy if ( self->enemy->client ) { VectorCopy( self->enemy->client->renderInfo.eyePoint, org ); } else { VectorCopy( self->enemy->r.currentOrigin, org ); } if ( self->spawnflags & 2 ) { org[2] -= 15; } else { org[2] -= 5; } if ( (self->spawnflags&SPF_TURRETG2_LEAD_ENEMY) ) {//we want to lead them a bit vec3_t diff, velocity; float dist; VectorSubtract( org, self->s.origin, diff ); dist = VectorNormalize( diff ); if ( self->enemy->client ) { VectorCopy( self->enemy->client->ps.velocity, velocity ); } else { VectorCopy( self->enemy->s.pos.trDelta, velocity ); } VectorMA( org, (dist/self->mass), velocity, org ); } // Getting the "eye" here trap->G2API_GetBoltMatrix( self->ghoul2, 0, (self->alt_fire?self->genericValue12:self->genericValue11), &boltMatrix, self->r.currentAngles, self->s.origin, level.time, NULL, self->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, org2 ); VectorSubtract( org, org2, enemyDir ); vectoangles( enemyDir, desiredAngles ); diffYaw = AngleSubtract( self->r.currentAngles[YAW], desiredAngles[YAW] ); diffPitch = AngleSubtract( self->speed, desiredAngles[PITCH] ); } else { // no enemy, so make us slowly sweep back and forth as if searching for a new one // diffYaw = sin( level.time * 0.0001f + self->count ) * 5.0f; // don't do this for now since it can make it go into walls. } if ( diffYaw ) { // cap max speed.... if ( fabs(diffYaw) > maxYawSpeed ) { diffYaw = ( diffYaw >= 0 ? maxYawSpeed : -maxYawSpeed ); } // ...then set up our desired yaw VectorSet( setAngle, 0.0f, diffYaw, 0.0f ); VectorCopy( self->r.currentAngles, self->s.apos.trBase ); VectorScale( setAngle,- 5, self->s.apos.trDelta ); self->s.apos.trTime = level.time; self->s.apos.trType = TR_LINEAR; } if ( diffPitch ) { if ( fabs(diffPitch) > maxPitchSpeed ) { // cap max speed self->speed += (diffPitch > 0.0f) ? -maxPitchSpeed : maxPitchSpeed; } else { // small enough, so just add half the diff so we smooth out the stopping self->speed -= ( diffPitch );//desiredAngles[PITCH]; } // Note that this is NOT interpolated, so it will be less smooth...On the other hand, it does use Ghoul2 to blend, so it may smooth it out a bit? if ( (self->spawnflags&SPF_TURRETG2_TURBO) ) { if ( self->spawnflags & 2 ) { VectorSet( desiredAngles, 0.0f, 0.0f, -self->speed ); } else { VectorSet( desiredAngles, 0.0f, 0.0f, self->speed ); } G2Tur_SetBoneAngles(self, "pitch", desiredAngles); } else { if ( self->spawnflags & 2 ) { VectorSet( desiredAngles, self->speed, 0.0f, 0.0f ); } else { VectorSet( desiredAngles, -self->speed, 0.0f, 0.0f ); } G2Tur_SetBoneAngles(self, "Bone_body", desiredAngles); } /* trap->G2API_SetBoneAngles( self->ghoul2, 0, "Bone_body", desiredAngles, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 100, level.time ); */ } if ( diffYaw || diffPitch ) {//FIXME: turbolaser sounds if ( (self->spawnflags&SPF_TURRETG2_TURBO) ) { self->s.loopSound = G_SoundIndex( "sound/vehicles/weapons/turbolaser/turn.wav" ); } else { self->s.loopSound = G_SoundIndex( "sound/chars/turret/move.wav" ); } } else { self->s.loopSound = 0; } }
void soldier_fire (edict_t *self, int flash_number) { vec3_t start; vec3_t forward, right, up; vec3_t aim; vec3_t dir; vec3_t end; float r, u; int flash_index; if ((self->s.skinnum % 6) < 2) flash_index = blaster_flash[flash_number]; else if ((self->s.skinnum % 6) < 4) flash_index = shotgun_flash[flash_number]; else flash_index = machinegun_flash[flash_number]; AngleVectors (self->s.angles, forward, right, NULL); G_ProjectSource (self->s.origin, monster_flash_offset[flash_index], forward, right, start); if (flash_number == 5 || flash_number == 6) { VectorCopy (forward, aim); } else { VectorCopy (self->enemy->s.origin, end); end[2] += self->enemy->viewheight; // Lazarus fog reduction of accuracy if(self->monsterinfo.visibility < FOG_CANSEEGOOD) { end[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); } VectorSubtract (end, start, aim); // Lazarus: Accuracy is skill level dependent if(skill->value < 3) { vectoangles (aim, dir); AngleVectors (dir, forward, right, up); r = crandom()*(1000 - 333*skill->value); u = crandom()*(500 - 167*skill->value); VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); VectorSubtract (end, start, aim); } VectorNormalize (aim); } if ((self->s.skinnum % 6) <= 1) { // Lazarus: make bolt speed skill level dependent monster_fire_blaster (self, start, aim, 5, 600 + 100*skill->value, flash_index, EF_BLASTER, BLASTER_ORANGE); } else if ((self->s.skinnum % 6) <= 3) { monster_fire_shotgun (self, start, aim, 2, 1, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_index); } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) self->monsterinfo.pausetime = level.time + (3 + rand() % 8) * FRAMETIME; monster_fire_bullet (self, start, aim, 2, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_index); if (level.time >= self->monsterinfo.pausetime) self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; else self->monsterinfo.aiflags |= AI_HOLD_FRAME; } }
//----------------------------------------------------- qboolean VEH_TurretAim( Vehicle_t *pVeh, gentity_t *parent, gentity_t *turretEnemy, turretStats_t *turretStats, vehWeaponInfo_t *vehWeapon, int turretNum, int curMuzzle, vec3_t desiredAngles ) //----------------------------------------------------- { vec3_t curAngles, addAngles, newAngles, yawAngles, pitchAngles; float aimCorrect = qfalse; WP_CalcVehMuzzle( parent, curMuzzle ); //get the current absolute angles of the turret right now vectoangles( pVeh->m_vMuzzleDir[curMuzzle], curAngles ); //subtract out the vehicle's angles to get the relative alignment AnglesSubtract( curAngles, pVeh->m_vOrientation, curAngles ); if ( turretEnemy ) { aimCorrect = qtrue; // ...then we'll calculate what new aim adjustments we should attempt to make this frame // Aim at enemy VEH_TurretAnglesToEnemy( pVeh, curMuzzle, vehWeapon->fSpeed, turretEnemy, turretStats->bAILead, desiredAngles ); } //subtract out the vehicle's angles to get the relative desired alignment AnglesSubtract( desiredAngles, pVeh->m_vOrientation, desiredAngles ); //Now clamp the desired relative angles //clamp yaw desiredAngles[YAW] = AngleNormalize180( desiredAngles[YAW] ); if ( pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft && desiredAngles[YAW] > pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft ) { aimCorrect = qfalse; desiredAngles[YAW] = pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft; } if ( pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight && desiredAngles[YAW] < pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight ) { aimCorrect = qfalse; desiredAngles[YAW] = pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight; } //clamp pitch desiredAngles[PITCH] = AngleNormalize180( desiredAngles[PITCH] ); if ( pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown && desiredAngles[PITCH] > pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown ) { aimCorrect = qfalse; desiredAngles[PITCH] = pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown; } if ( pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp && desiredAngles[PITCH] < pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp ) { aimCorrect = qfalse; desiredAngles[PITCH] = pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp; } //Now get the offset we want from our current relative angles AnglesSubtract( desiredAngles, curAngles, addAngles ); //Now cap the addAngles for our fTurnSpeed if ( addAngles[PITCH] > turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[PITCH] = turretStats->fTurnSpeed; } else if ( addAngles[PITCH] < -turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[PITCH] = -turretStats->fTurnSpeed; } if ( addAngles[YAW] > turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[YAW] = turretStats->fTurnSpeed; } else if ( addAngles[YAW] < -turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[YAW] = -turretStats->fTurnSpeed; } //Now add the additional angles back in to our current relative angles //FIXME: add some AI aim error randomness...? newAngles[PITCH] = AngleNormalize180( curAngles[PITCH]+addAngles[PITCH] ); newAngles[YAW] = AngleNormalize180( curAngles[YAW]+addAngles[YAW] ); //Now set the bone angles to the new angles //set yaw if ( turretStats->yawBone ) { VectorClear( yawAngles ); yawAngles[turretStats->yawAxis] = newAngles[YAW]; NPC_SetBoneAngles( parent, turretStats->yawBone, yawAngles ); } //set pitch if ( turretStats->pitchBone ) { VectorClear( pitchAngles ); pitchAngles[turretStats->pitchAxis] = newAngles[PITCH]; NPC_SetBoneAngles( parent, turretStats->pitchBone, pitchAngles ); } //force muzzle to recalc next check pVeh->m_iMuzzleTime[curMuzzle] = 0; return aimCorrect; }