void CASW_Prediction::CheckMarineError( int nSlot, int commands_acknowledged ) { C_ASW_Player *player; Vector origin; Vector delta; float len; static int pos = 0; // Not in the game yet if ( !engine->IsInGame() ) return; // Not running prediction if ( !cl_predict->GetInt() ) return; player = C_ASW_Player::GetLocalASWPlayer( nSlot ); if ( !player ) return; C_ASW_Marine* pMarine = player->GetMarine(); if (!pMarine) return; // Not predictable yet (flush entity packet?) if ( !pMarine->IsIntermediateDataAllocated() ) return; origin = pMarine->GetNetworkOrigin(); const void *slot = pMarine->GetPredictedFrame( commands_acknowledged - 1 ); if ( !slot ) return; if ( !m_bMarineOriginTypedescriptionSearched ) { m_bMarineOriginTypedescriptionSearched = true; const typedescription_t *td = CPredictionCopy::FindFlatFieldByName( "m_vecNetworkOrigin", pMarine->GetPredDescMap() ); if ( td ) { m_MarineOriginTypeDescription.AddToTail( td ); } } if ( !m_MarineOriginTypeDescription.Count() ) return; Vector predicted_origin; memcpy( (Vector *)&predicted_origin, (Vector *)( (byte *)slot + m_MarineOriginTypeDescription[ 0 ]->flatOffset[ TD_OFFSET_PACKED ] ), sizeof( Vector ) ); // Compare what the server returned with what we had predicted it to be VectorSubtract ( predicted_origin, origin, delta ); len = VectorLength( delta ); if (len > MAX_PREDICTION_ERROR ) { // A teleport or something, clear out error len = 0; } else { if ( len > MIN_PREDICTION_EPSILON ) { pMarine->NotePredictionError( delta ); if ( cl_showerror.GetInt() >= 1 ) { con_nprint_t np; np.fixed_width_font = true; np.color[0] = 1.0f; np.color[1] = 0.95f; np.color[2] = 0.7f; np.index = 20 + ( ++pos % 20 ); np.time_to_live = 2.0f; engine->Con_NXPrintf( &np, "marine pred error %6.3f units (%6.3f %6.3f %6.3f)", len, delta.x, delta.y, delta.z ); } } } }
/* ======================== func_explosive_objective ======================== */ void func_explosive_objective_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { vec3_t origin; vec3_t chunkorigin; vec3_t size; int count; int mass; int enemy; int otherteam; //gi.dprintf(DEVELOPER_MSG_GAME, "self: %s\ninflictor: %s\n attacker: %s\n", // self->classname, inflictor->classname, attacker->classname); if (!attacker->client || !attacker->client->resp.mos) return; // bmodel origins are (0 0 0), we need to adjust that here VectorScale (self->size, 0.5, size); VectorAdd (self->absmin, size, origin); VectorCopy (origin, self->s.origin); self->takedamage = DAMAGE_NO; if (self->dmg) T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE); VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity); VectorNormalize (self->velocity); VectorScale (self->velocity, 150, self->velocity); // start chunks towards the center VectorScale (size, 0.5, size); mass = self->mass; if (!mass) mass = 75; // big chunks if (mass >= 100) { count = mass / 100; if (count > 8) count = 8; while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin); } } // small chunks count = mass / 25; if (count > 16) count = 16; while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin); } G_UseTargets (self, attacker); // hack for 2 team games if (self->obj_owner != 99) { team_list[self->obj_owner]->score -= self->obj_loss; enemy = (self->obj_owner) ? 0 : 1; } else enemy = 99; if (self->obj_owner != attacker->client->resp.team_on->index) team_list[attacker->client->resp.team_on->index]->score += self->obj_gain; else if (self->obj_owner == attacker->client->resp.team_on->index && enemy != 99) team_list[enemy]->score += self->obj_gain; if (dedicated->value) safe_cprintf(NULL, PRINT_HIGH, "%s destroyed by %s [%s]\n", self->obj_name, attacker->client->pers.netname, team_list[attacker->client->resp.team_on->index]->teamname); centerprintall("%s destroyed by:\n\n%s\n%s", self->obj_name, attacker->client->pers.netname, team_list[attacker->client->resp.team_on->index]->teamname); otherteam = (self->obj_owner+1)%2; if ((!team_list[otherteam]->kills_and_points && team_list[otherteam]->score < team_list[otherteam]->need_points) || (team_list[otherteam]->kills_and_points && team_list[otherteam]->kills < team_list[otherteam]->need_kills)) gi.sound(self, CHAN_NO_PHS_ADD, gi.soundindex(va("%s/objectives/touch_cap.wav", team_list[otherteam]->teamid)), 1, 0, 0); // gi.dprintf(DEVELOPER_MSG_GAME, "pts:%i ndpts:%i kills:%i ndkills:%i\n",team_list[(self->obj_owner+1)%2]->score,team_list[(self->obj_owner+1)%2]->need_points, //team_list[(self->obj_owner+1)%2]->kills,team_list[(self->obj_owner+1)%2]->need_kills); if (self->deathtarget) { self->target = self->deathtarget; if (self->target) G_UseTargets (self, attacker); } if (self->dmg) BecomeExplosion1 (self); else G_FreeEdict (self); }
static void OldBuildST( triangle_t *ptri, int numtri ){ int i, j; int width, height, iwidth, iheight, swidth; float basex, basey; float s_scale, t_scale; float scale; vec3_t mins, maxs; float *pbasevert; vec3_t vtemp1, vtemp2, normal; // // find bounds of all the verts on the base frame // ClearBounds( mins, maxs ); for ( i = 0 ; i < numtri ; i++ ) for ( j = 0 ; j < 3 ; j++ ) AddPointToBounds( ptri[i].verts[j], mins, maxs ); for ( i = 0 ; i < 3 ; i++ ) { mins[i] = floor( mins[i] ); maxs[i] = ceil( maxs[i] ); } width = maxs[0] - mins[0]; height = maxs[2] - mins[2]; if ( !g_fixedwidth ) { // old style scale = 8; if ( width * scale >= 150 ) { scale = 150.0 / width; } if ( height * scale >= 190 ) { scale = 190.0 / height; } s_scale = t_scale = scale; iwidth = ceil( width * s_scale ); iheight = ceil( height * t_scale ); iwidth += 4; iheight += 4; } else { // new style iwidth = g_fixedwidth / 2; iheight = g_fixedheight; s_scale = (float)( iwidth - 4 ) / width; t_scale = (float)( iheight - 4 ) / height; } // // determine which side of each triangle to map the texture to // for ( i = 0 ; i < numtri ; i++ ) { VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 ); VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 ); CrossProduct( vtemp1, vtemp2, normal ); if ( normal[1] > 0 ) { basex = iwidth + 2; } else { basex = 2; } basey = 2; for ( j = 0 ; j < 3 ; j++ ) { pbasevert = ptri[i].verts[j]; triangle_st[i][j][0] = Q_rint( ( pbasevert[0] - mins[0] ) * s_scale + basex ); triangle_st[i][j][1] = Q_rint( ( maxs[2] - pbasevert[2] ) * t_scale + basey ); } } // make the width a multiple of 4; some hardware requires this, and it ensures // dword alignment for each scan swidth = iwidth * 2; model.skinwidth = ( swidth + 3 ) & ~3; model.skinheight = iheight; }
int Pickup_Powerup( gentity_t *ent, gentity_t *other ) { int quantity; int i; gclient_t *client; if ( !other->client->ps.powerups[ent->item->giTag] ) { // round timing to seconds to make multiple powerup timers // count in sync other->client->ps.powerups[ent->item->giTag] = level.time - ( level.time % 1000 ); } if ( ent->count ) { quantity = ent->count; } else { quantity = ent->item->quantity; } other->client->ps.powerups[ent->item->giTag] += quantity * 1000; // give any nearby players a "denied" anti-reward for ( i = 0 ; i < level.maxclients ; i++ ) { vec3_t delta; float len; vec3_t forward; trace_t tr; client = &level.clients[i]; if ( client == other->client ) { continue; } if ( client->pers.connected == CON_DISCONNECTED ) { continue; } if ( client->ps.stats[STAT_HEALTH] <= 0 ) { continue; } // if same team in team game, no sound // cannot use OnSameTeam as it expects to g_entities, not clients if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam ) { continue; } // if too far away, no sound VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta ); len = VectorNormalize( delta ); if ( len > 192 ) { continue; } // if not facing, no sound AngleVectors( client->ps.viewangles, forward, NULL, NULL ); if ( DotProduct( delta, forward ) < 0.4 ) { continue; } // if not line of sight, no sound trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID ); if ( tr.fraction != 1.0 ) { continue; } // anti-reward client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD; } return RESPAWN_POWERUP; }
/* ================= fire_hit Used for all impact (hit/punch/slash) attacks ================= */ qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick) { trace_t tr; vec3_t forward, right, up; vec3_t v; vec3_t point; float range; vec3_t dir; //see if enemy is in range VectorSubtract (self->enemy->s.origin, self->s.origin, dir); range = VectorLength(dir); if (range > aim[0]) return false; if (aim[1] > self->mins[0] && aim[1] < self->maxs[0]) { // the hit is straight on so back the range up to the edge of their bbox range -= self->enemy->maxs[0]; } else { // this is a side hit so adjust the "right" value out to the edge of their bbox if (aim[1] < 0) aim[1] = self->enemy->mins[0]; else aim[1] = self->enemy->maxs[0]; } VectorMA (self->s.origin, range, dir, point); tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT); if (tr.fraction < 1) { if (!tr.ent->takedamage) return false; // if it will hit any client/monster then hit the one we wanted to hit if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) tr.ent = self->enemy; } AngleVectors(self->s.angles, forward, right, up); VectorMA (self->s.origin, range, forward, point); VectorMA (point, aim[1], right, point); VectorMA (point, aim[2], up, point); VectorSubtract (point, self->enemy->s.origin, dir); // do the damage T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT); if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) return false; // do our special form of knockback here VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v); VectorSubtract (v, point, v); VectorNormalize (v); VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity); if (self->enemy->velocity[2] > 0) self->enemy->groundentity = NULL; return true; }
void heat_think (edict_t *self) { edict_t *target = NULL; edict_t *aquire = NULL; vec3_t vec; int len; int oldlen = 0; if (!self) { return; } VectorClear(vec); /* aquire new target */ while ((target = findradius(target, self->s.origin, 1024)) != NULL) { if (self->owner == target) { continue; } if (!(target->svflags & SVF_MONSTER)) { continue; } if (!target->client) { continue; } if (target->health <= 0) { continue; } if (!visible(self, target)) { continue; } if (!infront(self, target)) { continue; } VectorSubtract(self->s.origin, target->s.origin, vec); len = VectorLength(vec); if ((aquire == NULL) || (len < oldlen)) { aquire = target; self->target_ent = aquire; oldlen = len; } } if (aquire != NULL) { VectorSubtract(aquire->s.origin, self->s.origin, vec); vectoangles(vec, self->s.angles); VectorNormalize(vec); VectorCopy(vec, self->movedir); VectorScale(vec, 500, self->velocity); } self->nextthink = level.time + 0.1; }
/* ================= fire_grenade ================= */ void Grenade_Explode (edict_t *ent) { vec3_t origin; int mod; if (!ent) { return; } if (ent->owner->client) { PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); } if (ent->enemy) { float points; vec3_t v; vec3_t dir; VectorAdd(ent->enemy->mins, ent->enemy->maxs, v); VectorMA(ent->enemy->s.origin, 0.5, v, v); VectorSubtract(ent->s.origin, v, v); points = ent->dmg - 0.5 * VectorLength(v); VectorSubtract(ent->enemy->s.origin, ent->s.origin, dir); if (ent->spawnflags & 1) { mod = MOD_HANDGRENADE; } else { mod = MOD_GRENADE; } T_Damage(ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } if (ent->spawnflags & 2) { mod = MOD_HELD_GRENADE; } else if (ent->spawnflags & 1) { mod = MOD_HG_SPLASH; } else { mod = MOD_G_SPLASH; } T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); VectorMA(ent->s.origin, -0.02, ent->velocity, origin); gi.WriteByte(svc_temp_entity); if (ent->waterlevel) { if (ent->groundentity) { gi.WriteByte(TE_GRENADE_EXPLOSION_WATER); } else { gi.WriteByte(TE_ROCKET_EXPLOSION_WATER); } } else { if (ent->groundentity) { gi.WriteByte(TE_GRENADE_EXPLOSION); } else { gi.WriteByte(TE_ROCKET_EXPLOSION); } } gi.WritePosition(origin); gi.multicast(ent->s.origin, MULTICAST_PHS); G_FreeEdict(ent); }
static qboolean InitFlyby (player_state_t * self, player_state_t * player, int checkvis) { float f, max; vec3_t vec, vec2; vec3_t forward, right, up; VectorCopy (player->viewangles, vec); vec[0] = 0; AngleVectors (vec, forward, right, up); // for (i = 0; i < 3; i++) // forward[i] *= 3; max = 1000; VectorAdd (forward, up, vec2); VectorAdd (vec2, right, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorAdd (forward, up, vec2); VectorSubtract (vec2, right, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorAdd (forward, right, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorSubtract (forward, right, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorAdd (forward, up, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorSubtract (forward, up, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorAdd (up, right, vec2); VectorSubtract (vec2, forward, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorSubtract (up, right, vec2); VectorSubtract (vec2, forward, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } // invert VectorSubtract (vec3_origin, forward, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorCopy (forward, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } // invert VectorSubtract (vec3_origin, right, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } VectorCopy (right, vec2); if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { max = f; VectorCopy (vec2, vec); } // ack, can't find him if (max >= 1000) { // Cam_Unlock(); return false; } locked = true; VectorCopy (vec, desired_position); return true; }
// ZOID // // Take over the user controls and track a player. // We find a nice position to watch the player and move there void Cam_Track (usercmd_t *cmd) { player_state_t *player, *self; frame_t *frame; vec3_t vec; float len; if (!cl.spectator) return; if (cl_hightrack->int_val && !locked) Cam_CheckHighTarget (); if (!autocam || cls.state != ca_active) return; if (locked && (!cl.players[spec_track].name[0] || cl.players[spec_track].spectator)) { locked = false; if (cl_hightrack->int_val) Cam_CheckHighTarget (); else Cam_Unlock (); return; } frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; player = frame->playerstate + spec_track; self = frame->playerstate + cl.playernum; if (!locked || !Cam_IsVisible (player, desired_position)) { if (!locked || realtime - cam_lastviewtime > 0.1) { if (!InitFlyby (self, player, true)) InitFlyby (self, player, false); cam_lastviewtime = realtime; } } else cam_lastviewtime = realtime; // couldn't track for some reason if (!locked || !autocam) return; if (cl_chasecam->int_val) { cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; VectorCopy (player->viewangles, cl.viewangles); VectorCopy (player->origin, desired_position); if (memcmp (&desired_position, &self->origin, sizeof (desired_position)) != 0) { MSG_WriteByte (&cls.netchan.message, clc_tmove); MSG_WriteCoord (&cls.netchan.message, desired_position[0]); MSG_WriteCoord (&cls.netchan.message, desired_position[1]); MSG_WriteCoord (&cls.netchan.message, desired_position[2]); // move there locally immediately VectorCopy (desired_position, self->origin); } self->weaponframe = player->weaponframe; } else { // Ok, move to our desired position and set our angles to view // the player VectorSubtract (desired_position, self->origin, vec); len = vlen (vec); cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; if (len > 16) { // close enough? MSG_WriteByte (&cls.netchan.message, clc_tmove); MSG_WriteCoord (&cls.netchan.message, desired_position[0]); MSG_WriteCoord (&cls.netchan.message, desired_position[1]); MSG_WriteCoord (&cls.netchan.message, desired_position[2]); } // move there locally immediately VectorCopy (desired_position, self->origin); VectorSubtract (player->origin, desired_position, vec); vectoangles (vec, cl.viewangles); cl.viewangles[0] = -cl.viewangles[0]; } }
/* ================= BaseWindingForPlane ================= */ winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist) { int i, x; vec_t max, v; vec3_t org, vright, vup; winding_t *w; // find the major axis max = -MAX_MAP_BOUNDS; x = -1; for (i=0 ; i<3; i++) { v = fabs(normal[i]); if (v > max) { x = i; max = v; } } if (x==-1) Com_Error (ERR_DROP, "BaseWindingForPlane: no axis found"); VectorCopy (vec3_origin, vup); switch (x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } v = DotProduct (vup, normal); VectorMA (vup, -v, normal, vup); VectorNormalize2(vup, vup); VectorScale (normal, dist, org); CrossProduct (vup, normal, vright); VectorScale (vup, MAX_MAP_BOUNDS, vup); VectorScale (vright, MAX_MAP_BOUNDS, vright); // project a really big axis aligned box onto the plane w = AllocWinding (4); VectorSubtract (org, vright, w->p[0]); VectorAdd (w->p[0], vup, w->p[0]); VectorAdd (org, vright, w->p[1]); VectorAdd (w->p[1], vup, w->p[1]); VectorAdd (org, vright, w->p[2]); VectorSubtract (w->p[2], vup, w->p[2]); VectorSubtract (org, vright, w->p[3]); VectorSubtract (w->p[3], vup, w->p[3]); w->numpoints = 4; return w; }
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) { int i, j, k; float *p, *copy; vec3_t dir; float d; int numHullPoints, numNew; vec3_t hullPoints[MAX_HULL_POINTS]; vec3_t newHullPoints[MAX_HULL_POINTS]; vec3_t hullDirs[MAX_HULL_POINTS]; qboolean hullSide[MAX_HULL_POINTS]; qboolean outside; if ( !*hull ) { *hull = CopyWinding( w ); return; } numHullPoints = (*hull)->numpoints; Com_Memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) ); for ( i = 0 ; i < w->numpoints ; i++ ) { p = w->p[i]; // calculate hull side vectors for ( j = 0 ; j < numHullPoints ; j++ ) { k = ( j + 1 ) % numHullPoints; VectorSubtract( hullPoints[k], hullPoints[j], dir ); VectorNormalize2( dir, dir ); CrossProduct( normal, dir, hullDirs[j] ); } outside = qfalse; for ( j = 0 ; j < numHullPoints ; j++ ) { VectorSubtract( p, hullPoints[j], dir ); d = DotProduct( dir, hullDirs[j] ); if ( d >= ON_EPSILON ) { outside = qtrue; } if ( d >= -ON_EPSILON ) { hullSide[j] = qtrue; } else { hullSide[j] = qfalse; } } // if the point is effectively inside, do nothing if ( !outside ) { continue; } // find the back side to front side transition for ( j = 0 ; j < numHullPoints ; j++ ) { if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) { break; } } if ( j == numHullPoints ) { continue; } // insert the point here VectorCopy( p, newHullPoints[0] ); numNew = 1; // copy over all points that aren't double fronts j = (j+1)%numHullPoints; for ( k = 0 ; k < numHullPoints ; k++ ) { if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) { continue; } copy = hullPoints[ (j+k+1) % numHullPoints ]; VectorCopy( copy, newHullPoints[numNew] ); numNew++; } numHullPoints = numNew; Com_Memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) ); } FreeWinding( *hull ); w = AllocWinding( numHullPoints ); w->numpoints = numHullPoints; *hull = w; Com_Memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) ); }
/* ** RB_DrawSun */ void RB_DrawSun( void ) { float size; float dist; vec3_t origin, vec1, vec2; vec3_t temp; if ( !backEnd.skyRenderedThisView ) { return; } if ( !r_drawSun->integer ) { return; } qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) size = dist * 0.4; VectorScale( tr.sunDirection, dist, origin ); PerpendicularVector( vec1, tr.sunDirection ); CrossProduct( tr.sunDirection, vec1, vec2 ); VectorScale( vec1, size, vec1 ); VectorScale( vec2, size, vec2 ); // farthest depth range qglDepthRange( 1.0, 1.0 ); // FIXME: use quad stamp RB_BeginSurface( tr.sunShader, tess.fogNum ); VectorCopy( origin, temp ); VectorSubtract( temp, vec1, temp ); VectorSubtract( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 0; tess.texCoords[tess.numVertexes][0][1] = 0; tess.vertexColors[tess.numVertexes][0] = 255; tess.vertexColors[tess.numVertexes][1] = 255; tess.vertexColors[tess.numVertexes][2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorAdd( temp, vec1, temp ); VectorSubtract( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 0; tess.texCoords[tess.numVertexes][0][1] = 1; tess.vertexColors[tess.numVertexes][0] = 255; tess.vertexColors[tess.numVertexes][1] = 255; tess.vertexColors[tess.numVertexes][2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorAdd( temp, vec1, temp ); VectorAdd( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 1; tess.texCoords[tess.numVertexes][0][1] = 1; tess.vertexColors[tess.numVertexes][0] = 255; tess.vertexColors[tess.numVertexes][1] = 255; tess.vertexColors[tess.numVertexes][2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorSubtract( temp, vec1, temp ); VectorAdd( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 1; tess.texCoords[tess.numVertexes][0][1] = 0; tess.vertexColors[tess.numVertexes][0] = 255; tess.vertexColors[tess.numVertexes][1] = 255; tess.vertexColors[tess.numVertexes][2] = 255; tess.numVertexes++; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 1; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 3; RB_EndSurface(); // back to normal depth range qglDepthRange( 0.0, 1.0 ); }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; qboolean isGiant = qfalse; bgEntity_t *pEnt; qboolean skipStep = qfalse; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( BG_InReboundHold( pm->ps->legsAnim ) ) { gravity = qfalse; } if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } pEnt = pm_entSelf; if (pm->ps->clientNum >= MAX_CLIENTS) { if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0) { return; } } VectorCopy(start_o, down); down[2] -= STEPSIZE; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); VectorSet(up, 0, 0, 1); // never step up when you still have up velocity if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) { return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); if (pm->ps->clientNum >= MAX_CLIENTS) { // apply ground friction, even if on ladder if (pEnt && pEnt->s.NPC_class == CLASS_ATST || (pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->type == VH_WALKER) ) { //AT-STs can step high up[2] += 66.0f; isGiant = qtrue; } else if ( pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) { //also can step up high up[2] += 64.0f; isGiant = qtrue; } else { up[2] += STEPSIZE; } } else { up[2] += STEPSIZE; } // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); if ( pm->stepSlideFix ) { if ( pm->ps->clientNum < MAX_CLIENTS && trace.plane.normal[2] < MIN_WALK_NORMAL ) { //normal players cannot step up slopes that are too steep to walk on! vec3_t stepVec; //okay, the step up ends on a slope that it too steep to step up onto, //BUT: //If the step looks like this: // (B)\__ // \_____(A) //Then it might still be okay, so we figure out the slope of the entire move //from (A) to (B) and if that slope is walk-upabble, then it's okay VectorSubtract( trace.endpos, down_o, stepVec ); VectorNormalize( stepVec ); if ( stepVec[2] > (1.0f-MIN_WALK_NORMAL) ) { skipStep = qtrue; } } } if ( !trace.allsolid && !skipStep ) //normal players cannot step up slopes that are too steep to walk on! { if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) { //Rancor don't step on clients if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } /* else if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_ATST && OnSameTeam( pEnt, traceEnt) ) {//NPC AT-ST's don't step up on allies VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } */ else { VectorCopy (trace.endpos, pm->ps->origin); if ( pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } } else { if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } } if ( !pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
void PM_VehicleImpact(bgEntity_t *pEnt, trace_t *trace) { // See if the vehicle has crashed into the ground. Vehicle_t *pSelfVeh = pEnt->m_pVehicle; float magnitude = VectorLength( pm->ps->velocity ) * pSelfVeh->m_pVehicleInfo->mass / 50.0f; qboolean forceSurfDestruction = qfalse; #ifdef QAGAME gentity_t *hitEnt = trace!=NULL?&g_entities[trace->entityNum]:NULL; if (!hitEnt || //nothing to hit (pSelfVeh && pSelfVeh->m_pPilot &&//I'm a piloted vehicle hitEnt && hitEnt->s.eType == ET_MISSILE && hitEnt->inuse &&//that hit a missile hitEnt->r.ownerNum == pSelfVeh->m_pPilot->s.number)//and the missile is owned by my pilot ) { //don't hit it return; } if ( pSelfVeh//I have a vehicle struct && pSelfVeh->m_iRemovedSurfaces )//vehicle has bits removed { //spiralling to our deaths, explode on any solid impact if ( hitEnt->s.NPC_class == CLASS_VEHICLE ) { //hit another vehicle, explode! //Give credit to whoever got me into this death spiral state //[Asteroids] G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_COLLISION ); /* gentity_t *parent = (gentity_t *)pSelfVeh->m_pParentEntity; gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } //FIXME: damage hitEnt, some, too? Our explosion should hurt them some, but... G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT */ //[/Asteroids] return; } else if ( !VectorCompare( trace->plane.normal, vec3_origin ) && (trace->entityNum == ENTITYNUM_WORLD || hitEnt->r.bmodel ) ) { //have a valid hit plane and we hit a solid brush vec3_t moveDir; float impactDot; VectorCopy( pm->ps->velocity, moveDir ); VectorNormalize( moveDir ); impactDot = DotProduct( moveDir, trace->plane.normal ); if ( impactDot <= -0.7f )//hit rather head-on and hard { // Just DIE now //Give credit to whoever got me into this death spiral state //[Asteroids] G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING ); /* gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT */ //[/Asteroids] return; } } } if ( trace->entityNum < ENTITYNUM_WORLD && hitEnt->s.eType == ET_MOVER && hitEnt->s.apos.trType != TR_STATIONARY//rotating && (hitEnt->spawnflags&16) //IMPACT && Q_stricmp( "func_rotating", hitEnt->classname ) == 0 ) { //hit a func_rotating that is supposed to destroy anything it touches! //guarantee the hit will happen, thereby taking off a piece of the ship forceSurfDestruction = qtrue; } else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #endif /* if ( (pSelfVeh->m_ulFlags&VEH_GEARSOPEN) && trace->plane.normal[2] > 0.7f && fabs(pSelfVeh->m_vOrientation[PITCH]) < 0.2f && fabs(pSelfVeh->m_vOrientation[ROLL]) < 0.2f )*/ { //we're landing, we're cool //FIXME: some sort of landing "thump", not the impactFX /* if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { vec3_t up = {0,0,1}; #ifdef QAGAME G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up ); #else trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up, -1, -1 ); #endif } */ //this was annoying me -rww //FIXME: this shouldn't even be getting called when the vehicle is at rest! #ifdef QAGAME if (hitEnt && (hitEnt->s.eType == ET_PLAYER || hitEnt->s.eType == ET_NPC) && pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //always smack players } else #endif { return; } } if ( pSelfVeh && (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) && //this is kind of weird on tauntauns and atst's.. (magnitude >= 100||forceSurfDestruction) ) { if ( pEnt->m_pVehicle->m_iHitDebounce < pm->cmd.serverTime || forceSurfDestruction ) { //a bit of a hack, may conflict with getting shot, but... //FIXME: impact sound and effect should be gotten from g_vehicleInfo...? //FIXME: should pass in trace.endpos and trace.plane.normal vec3_t vehUp; //[Asteroids] #ifdef QAGAME qboolean noDamage = qfalse; #else //#ifndef QAGAME //[/Asteroids] bgEntity_t *hitEnt; #endif if ( trace && !pSelfVeh->m_iRemovedSurfaces && !forceSurfDestruction ) { qboolean turnFromImpact = qfalse, turnHitEnt = qfalse; float l = pm->ps->speed*0.5f; vec3_t bounceDir; #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( (trace->entityNum == ENTITYNUM_WORLD || hitEnt->s.solid == SOLID_BMODEL)//bounce off any brush && !VectorCompare(trace->plane.normal, vec3_origin) )//have a valid plane to bounce off of { //bounce off in the opposite direction of the impact if (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER) { pm->ps->speed *= pml.frametime; VectorCopy(trace->plane.normal, bounceDir); } else if ( trace->plane.normal[2] >= MIN_LANDING_SLOPE//flat enough to land on && pSelfVeh->m_LandTrace.fraction < 1.0f //ground present && pm->ps->speed <= MIN_LANDING_SPEED ) { //could land here, don't bounce off, in fact, return altogether! return; } else { if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { turnFromImpact = qtrue; } VectorCopy(trace->plane.normal, bounceDir); } } else if ( pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER ) { //check for impact with another fighter #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle && hitEnt->m_pVehicle->m_pVehicleInfo && hitEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER ) { //two vehicles hit each other, turn away from the impact turnFromImpact = qtrue; turnHitEnt = qtrue; #ifndef QAGAME VectorSubtract( pm->ps->origin, hitEnt->s.origin, bounceDir ); #else VectorSubtract( pm->ps->origin, hitEnt->r.currentOrigin, bounceDir ); #endif VectorNormalize( bounceDir ); } } if ( turnFromImpact ) { //bounce off impact surf and turn away vec3_t pushDir= {0}, turnAwayAngles, turnDelta; float turnStrength, pitchTurnStrength, yawTurnStrength; vec3_t moveDir; float bounceDot, turnDivider; //bounce if ( !turnHitEnt ) { //hit wall VectorScale(bounceDir, (pm->ps->speed*0.25f/pSelfVeh->m_pVehicleInfo->mass), pushDir); } else { //hit another fighter #ifndef QAGAME VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, bounceDir ); #else if ( hitEnt->client ) { VectorScale( bounceDir, (pm->ps->speed+hitEnt->client->ps.speed)*0.5f, pushDir ); } else { VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, pushDir ); } #endif VectorScale(pushDir, (l/pSelfVeh->m_pVehicleInfo->mass), pushDir); VectorScale(pushDir, 0.1f, pushDir); } VectorNormalize2( pm->ps->velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(pm->ps->velocity, pushDir, pm->ps->velocity); //turn turnDivider = (pSelfVeh->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) { //don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } turnStrength = (magnitude/2000.0f); if ( turnStrength < 0.1f ) { turnStrength = 0.1f; } else if ( turnStrength > 2.0f ) { turnStrength = 2.0f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, pSelfVeh->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) { //shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //pSelfVeh->m_vOrientation[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); pSelfVeh->m_vFullAngleVelocity[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) { //shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //pSelfVeh->m_vOrientation[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); pSelfVeh->m_vFullAngleVelocity[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } /* PM_SetPMViewAngle(pm->ps, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd); if ( pm_entVeh ) {//I'm a vehicle, so pm_entVeh is actually my pilot bgEntity_t *pilot = pm_entVeh; if ( !BG_UnrestrainedPitchRoll( pilot->playerState, pSelfVeh ) ) { //set the rider's viewangles to the vehicle's viewangles PM_SetPMViewAngle(pilot->playerState, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd); } } */ #ifdef QAGAME//server-side, turn the guy we hit away from us, too if ( turnHitEnt//make the other guy turn and get pushed && hitEnt->client //must be a valid client && !FighterIsLanded( hitEnt->m_pVehicle, &hitEnt->client->ps )//but not if landed && !(hitEnt->spawnflags&2) )//and not if suspended { l = hitEnt->client->ps.speed; //now bounce *them* away and turn them //flip the bounceDir VectorScale( bounceDir, -1, bounceDir ); //do bounce VectorScale( bounceDir, (pm->ps->speed+l)*0.5f, pushDir ); VectorScale(pushDir, (l*0.5f/hitEnt->m_pVehicle->m_pVehicleInfo->mass), pushDir); VectorNormalize2( hitEnt->client->ps.velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(hitEnt->client->ps.velocity, pushDir, hitEnt->client->ps.velocity); //turn turnDivider = (hitEnt->m_pVehicle->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) { //don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, hitEnt->m_pVehicle->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) { //shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //hitEnt->m_pVehicle->m_vOrientation[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); hitEnt->m_pVehicle->m_vFullAngleVelocity[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) { //shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } hitEnt->m_pVehicle->m_vFullAngleVelocity[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } } #endif } } #ifdef QAGAME if (!hitEnt) { return; } AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { //G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp ); //tempent use bad! G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pSelfVeh->m_pVehicleInfo->iImpactFX); } pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; magnitude /= pSelfVeh->m_pVehicleInfo->toughness * 50.0f; if (hitEnt && (hitEnt->s.eType != ET_TERRAIN || !(hitEnt->spawnflags & 1) || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)) { //don't damage the vehicle from terrain that doesn't want to damage vehicles //[Asteroids] gentity_t *killer = NULL; //[/Asteroids] if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //increase the damage... float mult = (pSelfVeh->m_vOrientation[PITCH]*0.1f); if (mult < 1.0f) { mult = 1.0f; } if (hitEnt->inuse && hitEnt->takedamage) { //if the other guy takes damage, don't hurt us a lot for ramming him //unless it's a vehicle, then we get 1.5 times damage if (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle) { mult = 1.5f; } else { mult = 0.5f; } } magnitude *= mult; } pSelfVeh->m_iLastImpactDmg = magnitude; //FIXME: what about proper death credit to the guy who shot you down? //FIXME: actually damage part of the ship that impacted? if ( hitEnt->s.eType == ET_MISSILE )//missile { //FIX: NEVER do or take impact damage from a missile... noDamage = qtrue; if ( (hitEnt->s.eFlags&EF_JETPACK_ACTIVE)//vehicle missile && ((gentity_t *)hitEnt)->r.ownerNum < MAX_CLIENTS )//valid client owner { //I ran into a missile and died because of the impact, give credit to the missile's owner (PROBLEM: might this ever accidently give improper credit to client 0?) /* if ( ((gentity_t *)hitEnt)->r.ownerNum == pEnt->s.number ) {//hit our own missile? Don't damage ourselves or it... (so we don't kill ourselves!) if it hits *us*, then fine, but not here noDamage = qtrue; } */ killer = &g_entities[((gentity_t *)hitEnt)->r.ownerNum]; } } if ( !noDamage ) { G_Damage( (gentity_t *)pEnt, ((gentity_t*)hitEnt), killer!=NULL?killer:((gentity_t *)hitEnt), NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING) );//FIXME: MOD_IMPACT } //G_Damage( (gentity_t *)pEnt, NULL, NULL, NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT //[/Asteroids] if (pSelfVeh->m_pVehicleInfo->surfDestruction) { G_FlyVehicleSurfaceDestruction((gentity_t *)pEnt, trace, magnitude, forceSurfDestruction ); } pSelfVeh->m_ulFlags |= VEH_CRASHING; } if (hitEnt && hitEnt->inuse && hitEnt->takedamage) { //damage this guy because we hit him float pmult = 1.0f; int finalD; gentity_t *attackEnt; if ( (hitEnt->s.eType == ET_PLAYER && hitEnt->s.number < MAX_CLIENTS) || (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class != CLASS_VEHICLE) ) { //probably a humanoid, or something if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //player die good.. if me fighter pmult = 2000.0f; } else { pmult = 40.0f; } if (hitEnt->client && BG_IsKnockDownable(&hitEnt->client->ps) && G_CanBeEnemy((gentity_t *)pEnt, hitEnt)) { //smash! if (hitEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN) { hitEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; hitEnt->client->ps.forceHandExtendTime = pm->cmd.serverTime + 1100; hitEnt->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim } hitEnt->client->ps.otherKiller = pEnt->s.number; hitEnt->client->ps.otherKillerTime = pm->cmd.serverTime + 5000; hitEnt->client->ps.otherKillerDebounceTime = pm->cmd.serverTime + 100; //[Asteroids] hitEnt->client->otherKillerMOD = MOD_COLLISION; hitEnt->client->otherKillerVehWeapon = 0; hitEnt->client->otherKillerWeaponType = WP_NONE; //[/Asteroids] //add my velocity into his to force him along in the correct direction from impact VectorAdd(hitEnt->client->ps.velocity, pm->ps->velocity, hitEnt->client->ps.velocity); //upward thrust hitEnt->client->ps.velocity[2] += 200.0f; } } if (pSelfVeh->m_pPilot) { attackEnt = (gentity_t *)pSelfVeh->m_pPilot; } else { attackEnt = (gentity_t *)pEnt; } finalD = magnitude*pmult; if (finalD < 1) { finalD = 1; } //[/Asteroids] if ( !noDamage ) { G_Damage( hitEnt, attackEnt, attackEnt, NULL, pm->ps->origin, finalD, 0, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING/*MOD_MELEE*/) );//FIXME: MOD_IMPACT } //[Asteroids] } #else //this is gonna result in "double effects" for the client doing the prediction. //it doesn't look bad though. could just use predicted events, but I'm too lazy. hitEnt = PM_BGEntForNum(trace->entityNum); if (!hitEnt || hitEnt->s.owner != pEnt->s.number) { //don't hit your own missiles! AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp, -1, -1 ); pSelfVeh->m_ulFlags |= VEH_CRASHING; } #endif } } }
void UI_DoSFXSaber( vector3 *blade_muz, vector3 *blade_tip, vector3 *trail_tip, vector3 *trail_muz, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight, qboolean doTrail, int cnum, int bnum ) { vector3 mid, blade_dir, end_dir, trail_dir, base_dir; float radiusmult, effectradius, coreradius, effectalpha = 1.0f, AngleScale = 1.0f; float blade_len, trail_len, base_len; vector3 rgb = { 1, 1, 1 }; int i; qhandle_t glow = 0; refEntity_t saber, sbak; VectorSubtract( blade_tip, blade_muz, &blade_dir ); VectorSubtract( trail_tip, trail_muz, &trail_dir ); blade_len = lengthMax;//VectorLength(blade_dir); trail_len = VectorLength( &trail_dir ); VectorNormalize( &blade_dir ); VectorNormalize( &trail_dir ); if ( lengthMax < 1.0f ) { return; } VectorSubtract( trail_tip, blade_tip, &end_dir ); VectorSubtract( trail_muz, blade_muz, &base_dir ); base_len = VectorLength( &base_dir ); VectorNormalize( &end_dir ); VectorNormalize( &base_dir ); switch ( color ) { case SABER_RED: glow = redSaberGlowShader; break; case SABER_ORANGE: glow = orangeSaberGlowShader; break; case SABER_YELLOW: glow = yellowSaberGlowShader; break; case SABER_GREEN: glow = greenSaberGlowShader; break; case SABER_PURPLE: glow = purpleSaberGlowShader; break; // case SABER_WHITE: case SABER_RGB: case SABER_FLAME1: case SABER_ELEC1: case SABER_FLAME2: case SABER_ELEC2: glow = rgbSaberGlowShader; break; case SABER_BLACK: glow = blackSaberGlowShader; break; default: glow = blueSaberGlowShader; break; } VectorMA( blade_muz, blade_len * 0.5f, &blade_dir, &mid ); memset( &saber, 0, sizeof(refEntity_t) ); if ( blade_len < lengthMax ) { radiusmult = 0.5f + ((blade_len / lengthMax) / 2); } else { radiusmult = 1.0f; } effectradius = ((radius * 1.6f * 1.0f) + crandom() * 0.1f)*radiusmult; coreradius = ((radius * 0.4f * 1.0f) + crandom() * 0.1f)*radiusmult; UI_RGBForSaberColor( color, &rgb, bnum ); for ( i = 0; i<3; i++ ) rgb.data[i] *= 255; { saber.renderfx = rfx; if ( blade_len - ((effectradius*1.0f) / 2) > 0 ) { saber.radius = effectradius*AngleScale; saber.saberLength = (blade_len - (saber.radius / 2)); VectorCopy( blade_muz, &saber.origin ); VectorCopy( &blade_dir, &saber.axis[0] ); saber.reType = RT_SABER_GLOW; saber.customShader = glow; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff * 1.0f; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i] * effectalpha; saber.shaderRGBA[3] = 255 * effectalpha; } SE_R_AddRefEntityToScene( &saber, cnum ); } // Do the hot core VectorMA( blade_muz, blade_len, &blade_dir, &saber.origin ); VectorMA( blade_muz, -1, &blade_dir, &saber.oldorigin ); saber.customShader = sfxSaberBladeShader; saber.reType = RT_LINE; saber.radius = coreradius; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i]; } sbak = saber; SE_R_AddRefEntityToScene( &saber, cnum ); if ( color >= SABER_RGB /*|| color == SABER_WHITE*/ ) {// Add the saber surface that provides color. sbak.customShader = sfxSaberBlade2Shader; sbak.reType = RT_LINE; sbak.shaderTexCoord.x = sbak.shaderTexCoord.y = 1.0f; sbak.shaderRGBA[0] = sbak.shaderRGBA[1] = sbak.shaderRGBA[2] = sbak.shaderRGBA[3] = 0xff; sbak.radius = coreradius; SE_R_AddRefEntityToScene( &sbak, cnum ); } } { saber.renderfx = rfx; if ( trail_len - ((effectradius*AngleScale) / 2) > 0 ) { saber.radius = effectradius*AngleScale; saber.saberLength = (trail_len - (saber.radius / 2)); VectorCopy( trail_muz, &saber.origin ); VectorCopy( &trail_dir, &saber.axis[0] ); saber.reType = RT_SABER_GLOW; saber.customShader = glow; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff * effectalpha; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i] * effectalpha; saber.shaderRGBA[3] = 255 * effectalpha; } SE_R_AddRefEntityToScene( &saber, cnum ); } // Do the hot core VectorMA( trail_muz, trail_len, &trail_dir, &saber.origin ); VectorMA( trail_muz, -1, &trail_dir, &saber.oldorigin ); saber.customShader = sfxSaberBladeShader; saber.reType = RT_LINE; saber.radius = coreradius; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i]; saber.shaderRGBA[3] = 255; } sbak = saber; SE_R_AddRefEntityToScene( &saber, cnum ); if ( color >= SABER_RGB /*|| color == SABER_WHITE*/ ) {// Add the saber surface that provides color. sbak.customShader = sfxSaberBlade2Shader; sbak.reType = RT_LINE; sbak.shaderTexCoord.x = sbak.shaderTexCoord.y = 1.0f; sbak.shaderRGBA[0] = sbak.shaderRGBA[1] = sbak.shaderRGBA[2] = sbak.shaderRGBA[3] = 0xff; sbak.radius = coreradius; SE_R_AddRefEntityToScene( &sbak, cnum ); } } VectorMA( blade_muz, blade_len - 0.5f, &blade_dir, blade_tip ); VectorMA( trail_muz, trail_len - 0.5f, &trail_dir, trail_tip ); if ( base_len > 2 ) { saber.renderfx = rfx; if ( base_len - (effectradius*AngleScale) > 0 ) { saber.radius = effectradius*AngleScale; saber.saberLength = (base_len - (effectradius*AngleScale)); VectorMA( blade_muz, ((effectradius*AngleScale) / 2), &base_dir, &saber.origin ); VectorCopy( &base_dir, &saber.axis[0] ); saber.reType = RT_SABER_GLOW; saber.customShader = glow; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff * effectalpha; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i] * effectalpha; saber.shaderRGBA[3] = 255 * effectalpha; } SE_R_AddRefEntityToScene( &saber, cnum ); } // Do the hot core VectorMA( blade_muz, base_len, &base_dir, &saber.origin ); VectorMA( blade_muz, -0.1f, &base_dir, &saber.oldorigin ); saber.customShader = sfxSaberBladeShader; saber.reType = RT_LINE; saber.radius = coreradius; saber.saberLength = base_len; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; if ( color < SABER_RGB /*&& color != SABER_WHITE*/ ) saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; else { for ( i = 0; i < 3; i++ ) saber.shaderRGBA[i] = rgb.data[i]; saber.shaderRGBA[3] = 255; } sbak = saber; SE_R_AddRefEntityToScene( &saber, cnum ); if ( color >= SABER_RGB /*|| color == SABER_WHITE*/ ) {// Add the saber surface that provides color. sbak.customShader = sfxSaberBlade2Shader; saber.reType = RT_LINE; saber.shaderTexCoord.x = saber.shaderTexCoord.y = 1.0f; saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; saber.radius = coreradius; saber.saberLength = base_len; SE_R_AddRefEntityToScene( &sbak, cnum ); } } }
void Cam_FinishMove (usercmd_t *cmd) { int i; player_info_t *s; int end; if (cls.state != ca_active) return; if (!cl.spectator) // only in spectator mode return; #if 0 if (autocam && locked) { frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; player = frame->playerstate + spec_track; self = frame->playerstate + cl.playernum; VectorSubtract (player->origin, self->origin, vec); if (cam_forceview) { cam_forceview = false; vectoangles (vec, cam_viewangles); cam_viewangles[0] = -cam_viewangles[0]; } else { vectoangles (vec, vec2); vec2[PITCH] = -vec2[PITCH]; cam_viewangles[PITCH] = adjustang (cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch->value); cam_viewangles[YAW] = adjustang (cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw->value); } VectorCopy (cam_viewangles, cl.viewangles); } #endif if (cmd->buttons & BUTTON_ATTACK) { if (!(oldbuttons & BUTTON_ATTACK)) { oldbuttons |= BUTTON_ATTACK; autocam++; if (autocam > CAM_TRACK) { Cam_Unlock (); VectorCopy (cl.viewangles, cmd->angles); return; } } else return; } else { oldbuttons &= ~BUTTON_ATTACK; if (!autocam) return; } if (autocam && cl_hightrack->int_val) { Cam_CheckHighTarget (); return; } if (locked) { if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons & BUTTON_JUMP)) return; // don't pogo stick if (!(cmd->buttons & BUTTON_JUMP)) { oldbuttons &= ~BUTTON_JUMP; return; } oldbuttons |= BUTTON_JUMP; // don't jump again until released } // Con_Printf("Selecting track target...\n"); if (locked && autocam) end = (spec_track + 1) % MAX_CLIENTS; else end = spec_track; i = end; do { s = &cl.players[i]; if (s->name[0] && !s->spectator) { Cam_Lock (i); return; } i = (i + 1) % MAX_CLIENTS; } while (i != end); // stay on same guy? i = spec_track; s = &cl.players[i]; if (s->name[0] && !s->spectator) { Cam_Lock (i); return; } Con_Printf ("No target found ...\n"); autocam = locked = false; }
void UI_SaberDrawBlade( itemDef_t *item, char *saberName, int saberModel, saberType_t saberType, vector3 *origin, vector3 *angles, int bladeNum ) { vector3 org_, end, axis_[3] = { { 0.0f } }; // shut the compiler up mdxaBone_t boltMatrix; effectTrailArgStruct_t fx; saber_colors_t bladeColor; float bladeLength, bladeRadius; char bladeColorString[MAX_QPATH]; int snum; const char *tagName; int bolt; qboolean tagHack = qfalse; int styleToUse = atoi( UI_Cvar_VariableString( "cg_saberBladeStyle" ) ); if ( (item->flags&ITF_ISSABER) && saberModel < 2 ) { snum = 0; trap->Cvar_VariableStringBuffer( "ui_saber_color", bladeColorString, sizeof(bladeColorString) ); } else//if ( item->flags&ITF_ISSABER2 ) - presumed { snum = 1; trap->Cvar_VariableStringBuffer( "ui_saber2_color", bladeColorString, sizeof(bladeColorString) ); } if ( !trap->G2API_HasGhoul2ModelOnIndex( &(item->ghoul2), saberModel ) ) {//invalid index! return; } bladeColor = TranslateSaberColor( bladeColorString ); bladeLength = UI_SaberBladeLengthForSaber( saberName, bladeNum ); bladeRadius = UI_SaberBladeRadiusForSaber( saberName, bladeNum ); tagName = va( "*blade%d", bladeNum + 1 ); bolt = trap->G2API_AddBolt( item->ghoul2, saberModel, tagName ); if ( bolt == -1 ) { tagHack = qtrue; //hmm, just fall back to the most basic tag (this will also make it work with pre-JKA saber models bolt = trap->G2API_AddBolt( item->ghoul2, saberModel, "*flash" ); if ( bolt == -1 ) {//no tag_flash either?!! bolt = 0; } } // angles.pitch = curYaw; // angles.roll = 0; trap->G2API_GetBoltMatrix( item->ghoul2, saberModel, bolt, &boltMatrix, angles, origin, uiInfo.uiDC.realTime, NULL, &vec3_origin );//NULL was cgs.model_draw // work the matrix axis stuff into the original axis and origins used. BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, &org_ ); BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, &axis_[0] );//front (was NEGATIVE_Y, but the md3->glm exporter screws up this tag somethin' awful) // ...changed this back to NEGATIVE_Y BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, &axis_[1] );//right ... and changed this to NEGATIVE_X BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_Z, &axis_[2] );//up VectorMA( &org_, bladeLength, &axis_[0], &end ); VectorAdd( &end, &axis_[0], &end ); if ( tagHack ) { switch ( saberType ) { default: case SABER_SINGLE: VectorMA( &org_, 1.0f, &axis_[0], &org_ ); break; case SABER_DAGGER: case SABER_LANCE: break; case SABER_STAFF: if ( bladeNum == 0 ) VectorMA( &org_, 12 * 1.0f, &axis_[0], &org_ ); if ( bladeNum == 1 ) { VectorScale( &axis_[0], -1, &axis_[0] ); VectorMA( &org_, 12 * 1.0f, &axis_[0], &org_ ); } break; case SABER_BROAD: if ( bladeNum == 0 ) VectorMA( &org_, -1 * 1.0f, &axis_[1], &org_ ); else if ( bladeNum == 1 ) VectorMA( &org_, 1 * 1.0f, &axis_[1], &org_ ); break; case SABER_PRONG: if ( bladeNum == 0 ) VectorMA( &org_, -3 * 1.0f, &axis_[1], &org_ ); else if ( bladeNum == 1 ) VectorMA( &org_, 3 * 1.0f, &axis_[1], &org_ ); break; case SABER_ARC: VectorSubtract( &axis_[1], &axis_[2], &axis_[1] ); VectorNormalize( &axis_[1] ); switch ( bladeNum ) { case 0: VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], 0.75f, &axis_[0] ); VectorScale( &axis_[1], 0.25f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; case 1: VectorScale( &axis_[0], 0.25f, &axis_[0] ); VectorScale( &axis_[1], 0.75f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; case 2: VectorMA( &org_, -8 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], -0.25f, &axis_[0] ); VectorScale( &axis_[1], 0.75f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; case 3: VectorMA( &org_, -16 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], -0.75f, &axis_[0] ); VectorScale( &axis_[1], 0.25f, &axis_[1] ); VectorAdd( &axis_[0], &axis_[1], &axis_[0] ); break; default: break; } break; case SABER_SAI: if ( bladeNum == 1 ) VectorMA( &org_, -3 * 1.0f, &axis_[1], &org_ ); else if ( bladeNum == 2 ) VectorMA( &org_, 3 * 1.0f, &axis_[1], &org_ ); break; case SABER_CLAW: switch ( bladeNum ) { case 0: VectorMA( &org_, 2 * 1.0f, &axis_[0], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[2], &org_ ); break; case 1: VectorMA( &org_, 2 * 1.0f, &axis_[0], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[2], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[1], &org_ ); break; case 2: VectorMA( &org_, 2 * 1.0f, &axis_[0], &org_ ); VectorMA( &org_, 2 * 1.0f, &axis_[2], &org_ ); VectorMA( &org_, -2 * 1.0f, &axis_[1], &org_ ); break; default: break; } break; case SABER_STAR: switch ( bladeNum ) { case 0: VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 1: VectorScale( &axis_[0], 0.33f, &axis_[0] ); VectorScale( &axis_[2], 0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 2: VectorScale( &axis_[0], -0.33f, &axis_[0] ); VectorScale( &axis_[2], 0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 3: VectorScale( &axis_[0], -1, &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 4: VectorScale( &axis_[0], -0.33f, &axis_[0] ); VectorScale( &axis_[2], -0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; case 5: VectorScale( &axis_[0], 0.33f, &axis_[0] ); VectorScale( &axis_[2], -0.67f, &axis_[2] ); VectorAdd( &axis_[0], &axis_[2], &axis_[0] ); VectorMA( &org_, 8 * 1.0f, &axis_[0], &org_ ); break; default: break; } break; case SABER_TRIDENT: switch ( bladeNum ) { case 0: VectorMA( &org_, 24 * 1.0f, &axis_[0], &org_ ); break; case 1: VectorMA( &org_, -6 * 1.0f, &axis_[1], &org_ ); VectorMA( &org_, 24 * 1.0f, &axis_[0], &org_ ); break; case 2: VectorMA( &org_, 6 * 1.0f, &axis_[1], &org_ ); VectorMA( &org_, 24 * 1.0f, &axis_[0], &org_ ); break; case 3: VectorMA( &org_, -32 * 1.0f, &axis_[0], &org_ ); VectorScale( &axis_[0], -1, &axis_[0] ); break; default: break; } break; case SABER_SITH_SWORD: //no blade break; } } VectorCopy( &org_, &fx.mVerts[0].origin ); VectorMA( &end, 3.0f, &axis_[0], &fx.mVerts[1].origin ); VectorCopy( &end, &fx.mVerts[2].origin ); VectorMA( &org_, 3.0f, &axis_[0], &fx.mVerts[3].origin ); //Raz: Temporarily switch to basejka sabers for flame and electric users if ( bladeColor == SABER_FLAME1 || bladeColor == SABER_ELEC1 || bladeColor == SABER_FLAME2 || bladeColor == SABER_ELEC2 || bladeColor == SABER_BLACK ) styleToUse = 0; // Pass in the renderfx flags attached to the saber weapon model...this is done so that saber glows // will get rendered properly in a mirror...not sure if this is necessary?? //CG_DoSaber( org_, axis_[0], saberLen, client->saber[saberNum].blade[bladeNum].lengthMax, client->saber[saberNum].blade[bladeNum].radius, // scolor, renderfx, (qboolean)(saberNum==0&&bladeNum==0) ); switch ( styleToUse ) { case 0: UI_DoSaber( &org_, &axis_[0], bladeLength, bladeLength, bladeRadius, bladeColor, 0, qfalse, 0, snum ); break; /* case 1: UI_DoEp1Saber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; case 2: UI_DoEp2Saber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; case 3: UI_DoEp3Saber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; */ default: case 1: UI_DoSFXSaber( &fx.mVerts[0].origin, &fx.mVerts[1].origin, &fx.mVerts[2].origin, &fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, qfalse, qfalse, 0, snum ); break; /* case 5: UI_DoOTSaber( fx.mVerts[0].origin, fx.mVerts[1].origin, fx.mVerts[2].origin, fx.mVerts[3].origin, bladeLength, bladeRadius, bladeColor, 0, false, false, 0, snum ); break; */ } }
fixedWinding_t *TryMergeWinding( fixedWinding_t *f1, fixedWinding_t *f2, vec3_t planenormal ){ vec_t *p1, *p2, *p3, *p4, *back; fixedWinding_t *newf; int i, j, k, l; vec3_t normal, delta; vec_t dot; qboolean keep1, keep2; // // find a common edge // p1 = p2 = NULL; // stop compiler warning j = 0; // for ( i = 0; i < f1->numpoints; i++ ) { p1 = f1->points[i]; p2 = f1->points[( i + 1 ) % f1->numpoints]; for ( j = 0; j < f2->numpoints; j++ ) { p3 = f2->points[j]; p4 = f2->points[( j + 1 ) % f2->numpoints]; for ( k = 0; k < 3; k++ ) { if ( fabs( p1[k] - p4[k] ) > 0.1 ) { //EQUAL_EPSILON) //ME break; } if ( fabs( p2[k] - p3[k] ) > 0.1 ) { //EQUAL_EPSILON) //ME break; } } //end for if ( k == 3 ) { break; } } //end for if ( j < f2->numpoints ) { break; } } //end for if ( i == f1->numpoints ) { return NULL; // no matching edges } // // check slope of connected lines // if the slopes are colinear, the point can be removed // back = f1->points[( i + f1->numpoints - 1 ) % f1->numpoints]; VectorSubtract( p1, back, delta ); CrossProduct( planenormal, delta, normal ); VectorNormalize( normal, normal ); back = f2->points[( j + 2 ) % f2->numpoints]; VectorSubtract( back, p1, delta ); dot = DotProduct( delta, normal ); if ( dot > CONTINUOUS_EPSILON ) { return NULL; // not a convex polygon } keep1 = (qboolean)( dot < -CONTINUOUS_EPSILON ); back = f1->points[( i + 2 ) % f1->numpoints]; VectorSubtract( back, p2, delta ); CrossProduct( planenormal, delta, normal ); VectorNormalize( normal, normal ); back = f2->points[( j + f2->numpoints - 1 ) % f2->numpoints]; VectorSubtract( back, p2, delta ); dot = DotProduct( delta, normal ); if ( dot > CONTINUOUS_EPSILON ) { return NULL; // not a convex polygon } keep2 = (qboolean)( dot < -CONTINUOUS_EPSILON ); // // build the new polygon // newf = NewFixedWinding( f1->numpoints + f2->numpoints ); // copy first polygon for ( k = ( i + 1 ) % f1->numpoints ; k != i ; k = ( k + 1 ) % f1->numpoints ) { if ( k == ( i + 1 ) % f1->numpoints && !keep2 ) { continue; } VectorCopy( f1->points[k], newf->points[newf->numpoints] ); newf->numpoints++; } // copy second polygon for ( l = ( j + 1 ) % f2->numpoints ; l != j ; l = ( l + 1 ) % f2->numpoints ) { if ( l == ( j + 1 ) % f2->numpoints && !keep1 ) { continue; } VectorCopy( f2->points[l], newf->points[newf->numpoints] ); newf->numpoints++; } return newf; }
// RAFAEL void Trap_Think (edict_t *ent) { edict_t *target = NULL; edict_t *best = NULL; vec3_t vec; int len, i; int oldlen = 8000; vec3_t forward, right, up; if (!ent) { return; } if (ent->timestamp < level.time) { BecomeExplosion1(ent); return; } ent->nextthink = level.time + 0.1; if (!ent->groundentity) { return; } /* ok lets do the blood effect */ if (ent->s.frame > 4) { if (ent->s.frame == 5) { if (ent->wait == 64) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/trapdown.wav"), 1, ATTN_IDLE, 0); } ent->wait -= 2; ent->delay += level.time; for (i = 0; i < 3; i++) { best = G_Spawn(); if (strcmp(ent->enemy->classname, "monster_gekk") == 0) { best->s.modelindex = gi.modelindex("models/objects/gekkgib/torso/tris.md2"); best->s.effects |= TE_GREENBLOOD; } else if (ent->mass > 200) { best->s.modelindex = gi.modelindex("models/objects/gibs/chest/tris.md2"); best->s.effects |= TE_BLOOD; } else { best->s.modelindex = gi.modelindex("models/objects/gibs/sm_meat/tris.md2"); best->s.effects |= TE_BLOOD; } AngleVectors(ent->s.angles, forward, right, up); RotatePointAroundVector(vec, up, right, ((360.0 / 3) * i) + ent->delay); VectorMA(vec, ent->wait / 2, vec, vec); VectorAdd(vec, ent->s.origin, vec); VectorAdd(vec, forward, best->s.origin); best->s.origin[2] = ent->s.origin[2] + ent->wait; VectorCopy(ent->s.angles, best->s.angles); best->solid = SOLID_NOT; best->s.effects |= EF_GIB; best->takedamage = DAMAGE_YES; best->movetype = MOVETYPE_TOSS; best->svflags |= SVF_MONSTER; best->deadflag = DEAD_DEAD; VectorClear(best->mins); VectorClear(best->maxs); best->watertype = gi.pointcontents(best->s.origin); if (best->watertype & MASK_WATER) { best->waterlevel = 1; } best->nextthink = level.time + 0.1; best->think = G_FreeEdict; gi.linkentity(best); } if (ent->wait < 19) { ent->s.frame++; } return; } ent->s.frame++; if (ent->s.frame == 8) { ent->nextthink = level.time + 1.0; ent->think = G_FreeEdict; best = G_Spawn(); SP_item_foodcube(best); VectorCopy(ent->s.origin, best->s.origin); best->s.origin[2] += 16; best->velocity[2] = 400; best->count = ent->mass; gi.linkentity(best); return; } return; } ent->s.effects &= ~EF_TRAP; if (ent->s.frame >= 4) { ent->s.effects |= EF_TRAP; VectorClear(ent->mins); VectorClear(ent->maxs); } if (ent->s.frame < 4) { ent->s.frame++; } while ((target = findradius(target, ent->s.origin, 256)) != NULL) { if (target == ent) { continue; } if (!(target->svflags & SVF_MONSTER) && !target->client) { continue; } if (target->health <= 0) { continue; } if (!visible(ent, target)) { continue; } if (!best) { best = target; continue; } VectorSubtract(ent->s.origin, target->s.origin, vec); len = VectorLength(vec); if (len < oldlen) { oldlen = len; best = target; } } /* pull the enemy in */ if (best) { vec3_t forward; if (best->groundentity) { best->s.origin[2] += 1; best->groundentity = NULL; } VectorSubtract(ent->s.origin, best->s.origin, vec); len = VectorLength(vec); if (best->client) { VectorNormalize(vec); VectorMA(best->velocity, 250, vec, best->velocity); } else { best->ideal_yaw = vectoyaw(vec); M_ChangeYaw(best); AngleVectors(best->s.angles, forward, NULL, NULL); VectorScale(forward, 256, best->velocity); } gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/trapsuck.wav"), 1, ATTN_IDLE, 0); if (len < 32) { if (best->mass < 400) { T_Damage(best, ent, ent->owner, vec3_origin, best->s.origin, vec3_origin, 100000, 1, 0, MOD_TRAP); ent->enemy = best; ent->wait = 64; VectorCopy(ent->s.origin, ent->s.old_origin); ent->timestamp = level.time + 30; if (deathmatch->value) { ent->mass = best->mass / 4; } else { ent->mass = best->mass / 10; } /* ok spawn the food cube */ ent->s.frame = 5; } else { BecomeExplosion1(ent); return; } } } }
/* ============ LoadPortals ============ */ void LoadPortals( char *name ){ int i, j, hint; vportal_t *p; leaf_t *l; char magic[80]; FILE *f; int numpoints; fixedWinding_t *w; int leafnums[2]; visPlane_t plane; if ( !strcmp( name,"-" ) ) { f = stdin; } else { f = fopen( name, "r" ); if ( !f ) { Error( "LoadPortals: couldn't read %s\n",name ); } } if ( fscanf( f,"%79s\n%i\n%i\n%i\n",magic, &portalclusters, &numportals, &numfaces ) != 4 ) { Error( "LoadPortals: failed to read header" ); } if ( strcmp( magic,PORTALFILE ) ) { Error( "LoadPortals: not a portal file" ); } Sys_Printf( "%6i portalclusters\n", portalclusters ); Sys_Printf( "%6i numportals\n", numportals ); Sys_Printf( "%6i numfaces\n", numfaces ); // these counts should take advantage of 64 bit systems automatically leafbytes = ( ( portalclusters + 63 ) & ~63 ) >> 3; leaflongs = leafbytes / sizeof( long ); portalbytes = ( ( numportals * 2 + 63 ) & ~63 ) >> 3; portallongs = portalbytes / sizeof( long ); // each file portal is split into two memory portals portals = safe_malloc( 2 * numportals * sizeof( vportal_t ) ); memset( portals, 0, 2 * numportals * sizeof( vportal_t ) ); leafs = safe_malloc( portalclusters * sizeof( leaf_t ) ); memset( leafs, 0, portalclusters * sizeof( leaf_t ) ); for ( i = 0; i < portalclusters; i++ ) leafs[i].merged = -1; numBSPVisBytes = VIS_HEADER_SIZE + portalclusters * leafbytes; if ( numBSPVisBytes > MAX_MAP_VISIBILITY ) { Error( "MAX_MAP_VISIBILITY exceeded" ); } ( (int *)bspVisBytes )[0] = portalclusters; ( (int *)bspVisBytes )[1] = leafbytes; for ( i = 0, p = portals ; i < numportals ; i++ ) { if ( fscanf( f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1] ) != 3 ) { Error( "LoadPortals: reading portal %i", i ); } if ( numpoints > MAX_POINTS_ON_WINDING ) { Error( "LoadPortals: portal %i has too many points", i ); } if ( (unsigned)leafnums[0] > portalclusters || (unsigned)leafnums[1] > portalclusters ) { Error( "LoadPortals: reading portal %i", i ); } if ( fscanf( f, "%i ", &hint ) != 1 ) { Error( "LoadPortals: reading hint state" ); } w = p->winding = NewFixedWinding( numpoints ); w->numpoints = numpoints; for ( j = 0 ; j < numpoints ; j++ ) { double v[3]; int k; // scanf into double, then assign to vec_t // so we don't care what size vec_t is if ( fscanf( f, "(%lf %lf %lf ) " , &v[0], &v[1], &v[2] ) != 3 ) { Error( "LoadPortals: reading portal %i", i ); } for ( k = 0 ; k < 3 ; k++ ) w->points[j][k] = v[k]; } fscanf( f, "\n" ); // calc plane PlaneFromWinding( w, &plane ); // create forward portal l = &leafs[leafnums[0]]; if ( l->numportals == MAX_PORTALS_ON_LEAF ) { Error( "Leaf with too many portals" ); } l->portals[l->numportals] = p; l->numportals++; p->num = i + 1; p->hint = hint; p->winding = w; VectorSubtract( vec3_origin, plane.normal, p->plane.normal ); p->plane.dist = -plane.dist; p->leaf = leafnums[1]; SetPortalSphere( p ); p++; // create backwards portal l = &leafs[leafnums[1]]; if ( l->numportals == MAX_PORTALS_ON_LEAF ) { Error( "Leaf with too many portals" ); } l->portals[l->numportals] = p; l->numportals++; p->num = i + 1; p->hint = hint; p->winding = NewFixedWinding( w->numpoints ); p->winding->numpoints = w->numpoints; for ( j = 0 ; j < w->numpoints ; j++ ) { VectorCopy( w->points[w->numpoints - 1 - j], p->winding->points[j] ); } p->plane = plane; p->leaf = leafnums[0]; SetPortalSphere( p ); p++; } faces = safe_malloc( 2 * numfaces * sizeof( vportal_t ) ); memset( faces, 0, 2 * numfaces * sizeof( vportal_t ) ); faceleafs = safe_malloc( portalclusters * sizeof( leaf_t ) ); memset( faceleafs, 0, portalclusters * sizeof( leaf_t ) ); for ( i = 0, p = faces; i < numfaces; i++ ) { if ( fscanf( f, "%i %i ", &numpoints, &leafnums[0] ) != 2 ) { Error( "LoadPortals: reading portal %i", i ); } w = p->winding = NewFixedWinding( numpoints ); w->numpoints = numpoints; for ( j = 0 ; j < numpoints ; j++ ) { double v[3]; int k; // scanf into double, then assign to vec_t // so we don't care what size vec_t is if ( fscanf( f, "(%lf %lf %lf ) " , &v[0], &v[1], &v[2] ) != 3 ) { Error( "LoadPortals: reading portal %i", i ); } for ( k = 0 ; k < 3 ; k++ ) w->points[j][k] = v[k]; } fscanf( f, "\n" ); // calc plane PlaneFromWinding( w, &plane ); l = &faceleafs[leafnums[0]]; l->merged = -1; if ( l->numportals == MAX_PORTALS_ON_LEAF ) { Error( "Leaf with too many faces" ); } l->portals[l->numportals] = p; l->numportals++; p->num = i + 1; p->winding = w; // normal pointing out of the leaf VectorSubtract( vec3_origin, plane.normal, p->plane.normal ); p->plane.dist = -plane.dist; p->leaf = -1; SetPortalSphere( p ); p++; } fclose( f ); }
/* ================= fire_bfg ================= */ void bfg_explode (edict_t *self) { edict_t *ent; float points; vec3_t v; float dist; if (!self) { return; } if (self->s.frame == 0) { /* the BFG effect */ ent = NULL; while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) { if (!ent->takedamage) { continue; } if (ent == self->owner) { continue; } if (!CanDamage(ent, self)) { continue; } if (!CanDamage(ent, self->owner)) { continue; } VectorAdd(ent->mins, ent->maxs, v); VectorMA(ent->s.origin, 0.5, v, v); VectorSubtract(self->s.origin, v, v); dist = VectorLength(v); points = self->radius_dmg * (1.0 - sqrt(dist / self->dmg_radius)); if (ent == self->owner) { points = points * 0.5; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BFG_EXPLOSION); gi.WritePosition(ent->s.origin); gi.multicast(ent->s.origin, MULTICAST_PHS); T_Damage(ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); } } self->nextthink = level.time + FRAMETIME; self->s.frame++; if (self->s.frame == 5) { self->think = G_FreeEdict; } }
/* ================ R_EmitEdge ================ */ void R_EmitEdge(mvertex_t *pv0, mvertex_t *pv1){ edge_t *edge, *pcheck; int u_check; float u, u_step; vec3_t local, transformed; float *world; int v, v2, ceilv0; float scale, lzi0, u0, v0; int side; if(r_lastvertvalid){ u0 = r_u1; v0 = r_v1; lzi0 = r_lzi1; ceilv0 = r_ceilv1; } else { world = &pv0->position[0]; // transform and project VectorSubtract(world, modelorg, local); TransformVector(local, transformed); if(transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; lzi0 = 1.0 / transformed[2]; // FIXME: build x/yscale into transform? scale = xscale * lzi0; u0 =(xcenter + scale * transformed[0]); if(u0 < r_refdef.fvrectx_adj) u0 = r_refdef.fvrectx_adj; if(u0 > r_refdef.fvrectright_adj) u0 = r_refdef.fvrectright_adj; scale = yscale * lzi0; v0 =(ycenter - scale * transformed[1]); if(v0 < r_refdef.fvrecty_adj) v0 = r_refdef.fvrecty_adj; if(v0 > r_refdef.fvrectbottom_adj) v0 = r_refdef.fvrectbottom_adj; ceilv0 =(int) ceil(v0); } world = &pv1->position[0]; // transform and project VectorSubtract(world, modelorg, local); TransformVector(local, transformed); if(transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; r_lzi1 = 1.0 / transformed[2]; scale = xscale * r_lzi1; r_u1 =(xcenter + scale * transformed[0]); if(r_u1 < r_refdef.fvrectx_adj) r_u1 = r_refdef.fvrectx_adj; if(r_u1 > r_refdef.fvrectright_adj) r_u1 = r_refdef.fvrectright_adj; scale = yscale * r_lzi1; r_v1 =(ycenter - scale * transformed[1]); if(r_v1 < r_refdef.fvrecty_adj) r_v1 = r_refdef.fvrecty_adj; if(r_v1 > r_refdef.fvrectbottom_adj) r_v1 = r_refdef.fvrectbottom_adj; if(r_lzi1 > lzi0) lzi0 = r_lzi1; if(lzi0 > r_nearzi) // for mipmap finding r_nearzi = lzi0; // for right edges, all we want is the effect on 1/z if(r_nearzionly) return; r_emitted = 1; r_ceilv1 =(int) ceil(r_v1); // create the edge if(ceilv0 == r_ceilv1){ // we cache unclipped horizontal edges as fully clipped if(cacheoffset != 0x7FFFFFFF){ cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK); } return; // horizontal edge } side = ceilv0 > r_ceilv1; edge = edge_p++; edge->owner = r_pedge; edge->nearzi = lzi0; if(side == 0){ // trailing edge(go from p1 to p2) v = ceilv0; v2 = r_ceilv1 - 1; edge->surfs[0] = surface_p - surfaces; edge->surfs[1] = 0; u_step =((r_u1 - u0) /(r_v1 - v0)); u = u0 +((float)v - v0) * u_step; } else { // leading edge(go from p2 to p1) v2 = ceilv0 - 1; v = r_ceilv1; edge->surfs[0] = 0; edge->surfs[1] = surface_p - surfaces; u_step =((u0 - r_u1) /(v0 - r_v1)); u = r_u1 +((float)v - r_v1) * u_step; } edge->u_step = u_step * 0x100000; edge->u = u * 0x100000 + 0xFFFFF; // we need to do this to avoid stepping off the edges if a very nearly // horizontal edge is less than epsilon above a scan, and numeric error causes // it to incorrectly extend to the scan, and the extension of the line goes off // the edge of the screen // FIXME: is this actually needed? if(edge->u < r_refdef.vrect_x_adj_shift20) edge->u = r_refdef.vrect_x_adj_shift20; if(edge->u > r_refdef.vrectright_adj_shift20) edge->u = r_refdef.vrectright_adj_shift20; // // sort the edge in normally // u_check = edge->u; if(edge->surfs[0]) u_check++; // sort trailers after leaders if(!newedges[v] || newedges[v]->u >= u_check){ edge->next = newedges[v]; newedges[v] = edge; } else { pcheck = newedges[v]; while(pcheck->next && pcheck->next->u < u_check) pcheck = pcheck->next; edge->next = pcheck->next; pcheck->next = edge; } edge->nextremove = removeedges[v2]; removeedges[v2] = edge; }
/* ================= fire_lead This is an internal support routine used for bullet/pellet based weapons. ================= */ static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) { trace_t tr; vec3_t dir; vec3_t forward, right, up; vec3_t end; float r; float u; vec3_t water_start; qboolean water = false; int content_mask = MASK_SHOT | MASK_WATER; tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread; u = crandom()*vspread; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); if (gi.pointcontents (start) & MASK_WATER) { water = true; VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } tr = gi.trace (start, NULL, NULL, end, self, content_mask); // see if we hit water if (tr.contents & MASK_WATER) { int color; water = true; VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { if (tr.contents & CONTENTS_WATER) { if (strcmp(tr.surface->name, "*brwater") == 0) color = SPLASH_BROWN_WATER; else color = SPLASH_BLUE_WATER; } else if (tr.contents & CONTENTS_SLIME) color = SPLASH_SLIME; else if (tr.contents & CONTENTS_LAVA) color = SPLASH_LAVA; else color = SPLASH_UNKNOWN; if (color != SPLASH_UNKNOWN) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (8); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (color); gi.multicast (tr.endpos, MULTICAST_PVS); } // change bullet's course when it enters water VectorSubtract (end, start, dir); vectoangles (dir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread*2; u = crandom()*vspread*2; VectorMA (water_start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_impact); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } // if went through water, determine where the end and make a bubble trail if (water) { vec3_t pos; VectorSubtract (tr.endpos, water_start, dir); VectorNormalize (dir); VectorMA (tr.endpos, -2, dir, pos); if (gi.pointcontents (pos) & MASK_WATER) VectorCopy (pos, tr.endpos); else tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); VectorAdd (water_start, tr.endpos, pos); VectorScale (pos, 0.5, pos); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (water_start); gi.WritePosition (tr.endpos); gi.multicast (pos, MULTICAST_PVS); } }
//--------------------------------------------------------- void WP_DisruptorAltFire( gentity_t *ent ) //--------------------------------------------------------- { int damage = weaponData[WP_DISRUPTOR].altDamage, skip, traces = DISRUPTOR_ALT_TRACES; qboolean render_impact = qtrue; vec3_t start, end; vec3_t muzzle2, spot, dir; trace_t tr; gentity_t *traceEnt, *tent; float dist, shotDist, shotRange = 8192; qboolean hitDodged = qfalse, fullCharge = qfalse; VectorCopy( muzzle, muzzle2 ); // making a backup copy // The trace start will originate at the eye so we can ensure that it hits the crosshair. if ( ent->NPC ) { switch ( g_spskill->integer ) { case 0: damage = DISRUPTOR_NPC_ALT_DAMAGE_EASY; break; case 1: damage = DISRUPTOR_NPC_ALT_DAMAGE_MEDIUM; break; case 2: default: damage = DISRUPTOR_NPC_ALT_DAMAGE_HARD; break; } VectorCopy( muzzle, start ); fullCharge = qtrue; } else { VectorCopy( ent->client->renderInfo.eyePoint, start ); AngleVectors( ent->client->renderInfo.eyeAngles, forwardVec, NULL, NULL ); // don't let NPC's do charging int count = ( level.time - ent->client->ps.weaponChargeTime - 50 ) / DISRUPTOR_CHARGE_UNIT; if ( count < 1 ) { count = 1; } else if ( count >= 10 ) { count = 10; fullCharge = qtrue; } // more powerful charges go through more things if ( count < 3 ) { traces = 1; } else if ( count < 6 ) { traces = 2; } //else do full traces damage = damage * count + weaponData[WP_DISRUPTOR].damage * 0.5f; // give a boost to low charge shots } skip = ent->s.number; // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) // { // // in overcharge mode, so doing double damage // damage *= 2; // } for ( int i = 0; i < traces; i++ ) { VectorMA( start, shotRange, forwardVec, end ); //NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0" //alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter gi.trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } if ( tr.entityNum == ent->s.number ) { // should never happen, but basically we don't want to consider a hit to ourselves? // Get ready for an attempt to trace through another person VectorCopy( tr.endpos, muzzle2 ); VectorCopy( tr.endpos, start ); skip = tr.entityNum; #ifdef _DEBUG gi.Printf( "BAD! Disruptor gun shot somehow traced back and hit the owner!\n" ); #endif continue; } // always render a shot beam, doing this the old way because I don't much feel like overriding the effect. //NOTE: let's just draw one beam, at the end //tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT ); //tent->svFlags |= SVF_BROADCAST; //tent->alt_fire = fullCharge; // mark us so we can alter the effect //VectorCopy( muzzle2, tent->s.origin2 ); if ( tr.fraction >= 1.0f ) { // draw the beam but don't do anything else break; } traceEnt = &g_entities[tr.entityNum]; if ( traceEnt //&& traceEnt->NPC && ( traceEnt->s.weapon == WP_SABER || (traceEnt->client && (traceEnt->client->NPC_class == CLASS_BOBAFETT||traceEnt->client->NPC_class == CLASS_REBORN) ) ) ) {//FIXME: need a more reliable way to know we hit a jedi? hitDodged = Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE ); //acts like we didn't even hit him } if ( !hitDodged ) { if ( render_impact ) { if (( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage ) || !Q_stricmp( traceEnt->classname, "misc_model_breakable" ) || traceEnt->s.eType == ET_MOVER ) { // Create a simple impact type mark that doesn't last long in the world G_PlayEffect( G_EffectIndex( "disruptor/alt_hit" ), tr.endpos, tr.plane.normal ); if ( traceEnt->client && LogAccuracyHit( traceEnt, ent )) {//NOTE: hitting multiple ents can still get you over 100% accuracy ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } int hitLoc = G_GetHitLocFromTrace( &tr, MOD_DISRUPTOR ); if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH ) {//hehe G_Damage( traceEnt, ent, ent, forwardVec, tr.endpos, 10, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, fullCharge ? MOD_SNIPER : MOD_DISRUPTOR, hitLoc ); break; } G_Damage( traceEnt, ent, ent, forwardVec, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, fullCharge ? MOD_SNIPER : MOD_DISRUPTOR, hitLoc ); if ( traceEnt->s.eType == ET_MOVER ) {//stop the traces on any mover break; } } else { // we only make this mark on things that can't break or move tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS ); tent->svFlags |= SVF_BROADCAST; VectorCopy( tr.plane.normal, tent->pos1 ); break; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool? } } else // not rendering impact, must be a skybox or other similar thing? { break; // don't try anymore traces } } // Get ready for an attempt to trace through another person VectorCopy( tr.endpos, muzzle2 ); VectorCopy( tr.endpos, start ); skip = tr.entityNum; hitDodged = qfalse; } //just draw one solid beam all the way to the end... tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT ); tent->svFlags |= SVF_BROADCAST; tent->alt_fire = fullCharge; // mark us so we can alter the effect VectorCopy( muzzle, tent->s.origin2 ); // now go along the trail and make sight events VectorSubtract( tr.endpos, muzzle, dir ); shotDist = VectorNormalize( dir ); //FIXME: if shoot *really* close to someone, the alert could be way out of their FOV for ( dist = 0; dist < shotDist; dist += 64 ) { //FIXME: on a really long shot, this could make a LOT of alerts in one frame... VectorMA( muzzle, dist, dir, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); } //FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention? VectorMA( start, shotDist-4, forwardVec, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); }
void bfg_think (edict_t *self) { edict_t *ent; edict_t *ignore; vec3_t point; vec3_t dir; vec3_t start; vec3_t end; int dmg; trace_t tr; if (deathmatch->value) dmg = 5; else dmg = 10; ent = NULL; while ((ent = findradius(ent, self->s.origin, 256)) != NULL) { if (ent == self) continue; if (ent == self->owner) continue; if (!ent->takedamage) continue; if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) continue; VectorMA (ent->absmin, 0.5, ent->size, point); VectorSubtract (point, self->s.origin, dir); VectorNormalize (dir); ignore = self; VectorCopy (self->s.origin, start); VectorMA (start, 2048, dir, end); while(1) { tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); if (!tr.ent) break; // hurt it if we can v this is an immune to laser flag if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); // if we hit something that's not a monster or player we're done if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_LASER_SPARKS); gi.WriteByte (4); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (self->s.skinnum); gi.multicast (tr.endpos, MULTICAST_PVS); break; } ignore = tr.ent; VectorCopy (tr.endpos, start); } gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BFG_LASER); gi.WritePosition (self->s.origin); gi.WritePosition (tr.endpos); gi.multicast (self->s.origin, MULTICAST_PHS); } self->nextthink = level.time + FRAMETIME; }
void CG_MiscModelExplosion( vec3_t mins, vec3_t maxs, int size, material_t chunkType ) { int ct = 13; float r; vec3_t org, mid, dir; char *effect = NULL, *effect2 = NULL; VectorAdd( mins, maxs, mid ); VectorScale( mid, 0.5f, mid ); switch( chunkType ) { case MAT_GLASS: effect = "chunks/glassbreak"; ct = 5; break; case MAT_GLASS_METAL: effect = "chunks/glassbreak"; effect2 = "chunks/metalexplode"; ct = 5; break; case MAT_ELECTRICAL: case MAT_ELEC_METAL: effect = "chunks/sparkexplode"; ct = 5; break; case MAT_METAL: case MAT_METAL2: case MAT_METAL3: case MAT_CRATE1: case MAT_CRATE2: effect = "chunks/metalexplode"; ct = 2; break; case MAT_GRATE1: effect = "chunks/grateexplode"; ct = 8; break; case MAT_ROPE: ct = 20; effect = "chunks/ropebreak"; break; case MAT_WHITE_METAL: //not sure what this crap is really supposed to be.. case MAT_DRK_STONE: case MAT_LT_STONE: case MAT_GREY_STONE: switch( size ) { case 2: effect = "chunks/rockbreaklg"; break; case 1: default: effect = "chunks/rockbreakmed"; break; } } if ( !effect ) { return; } ct += 7 * size; // FIXME: real precache .. VERify that these need to be here...don't think they would because the effects should be registered in g_breakable theFxScheduler.RegisterEffect( effect ); if ( effect2 ) { // FIXME: real precache theFxScheduler.RegisterEffect( effect2 ); } // spawn chunk roughly in the bbox of the thing.. for ( int i = 0; i < ct; i++ ) { for( int j = 0; j < 3; j++ ) { r = random() * 0.8f + 0.1f; org[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] ); } // shoot effect away from center VectorSubtract( org, mid, dir ); VectorNormalize( dir ); if ( effect2 && ( rand() & 1 )) { theFxScheduler.PlayEffect( effect2, org, dir ); } else { theFxScheduler.PlayEffect( effect, org, dir ); } } }
/* =============== GrabFrame =============== */ static void GrabFrame( char *frame ){ triangle_t *ptri; int i, j; trivert_t *ptrivert; int num_tris; char file1[1024]; frame_t *fr; vertexnormals_t vnorms[MAX_VERTS]; int index_xyz; char *framefile; // the frame 'run1' will be looked for as either // run.1 or run1.tri, so the new alias sequence save // feature an be used framefile = FindFrameFile( frame ); sprintf( file1, "%s/%s", cdarchive, framefile ); ExpandPathAndArchive( file1 ); sprintf( file1, "%s/%s",cddir, framefile ); printf( "grabbing %s ", file1 ); if ( model.num_frames >= MAX_FRAMES ) { Error( "model.num_frames >= MAX_FRAMES" ); } fr = &g_frames[model.num_frames]; model.num_frames++; strcpy( fr->name, frame ); // // load the frame // if ( do3ds ) { Load3DSTriangleList( file1, &ptri, &num_tris, NULL, NULL ); } else{ LoadTriangleList( file1, &ptri, &num_tris, NULL, NULL ); } if ( num_tris != model.num_tris ) { Error( "%s: number of triangles doesn't match base frame\n", file1 ); } // // allocate storage for the frame's vertices // ptrivert = fr->v; for ( i = 0 ; i < model.num_xyz ; i++ ) { vnorms[i].numnormals = 0; VectorClear( vnorms[i].normalsum ); } ClearBounds( fr->mins, fr->maxs ); // // store the frame's vertices in the same order as the base. This assumes the // triangles and vertices in this frame are in exactly the same order as in the // base // for ( i = 0 ; i < num_tris ; i++ ) { vec3_t vtemp1, vtemp2, normal; float ftemp; VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 ); VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 ); CrossProduct( vtemp1, vtemp2, normal ); VectorNormalize( normal, normal ); // rotate the normal so the model faces down the positive x axis ftemp = normal[0]; normal[0] = -normal[1]; normal[1] = ftemp; for ( j = 0 ; j < 3 ; j++ ) { index_xyz = triangles[i].index_xyz[j]; // rotate the vertices so the model faces down the positive x axis // also adjust the vertices to the desired origin ptrivert[index_xyz].v[0] = ( ( -ptri[i].verts[j][1] ) * scale_up ) + adjust[0]; ptrivert[index_xyz].v[1] = ( ptri[i].verts[j][0] * scale_up ) + adjust[1]; ptrivert[index_xyz].v[2] = ( ptri[i].verts[j][2] * scale_up ) + adjust[2]; AddPointToBounds( ptrivert[index_xyz].v, fr->mins, fr->maxs ); VectorAdd( vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum ); vnorms[index_xyz].numnormals++; } } // // calculate the vertex normals, match them to the template list, and store the // index of the best match // for ( i = 0 ; i < model.num_xyz ; i++ ) { int j; vec3_t v; float maxdot; int maxdotindex; int c; c = vnorms[i].numnormals; if ( !c ) { Error( "Vertex with no triangles attached" ); } VectorScale( vnorms[i].normalsum, 1.0 / c, v ); VectorNormalize( v, v ); maxdot = -999999.0; maxdotindex = -1; for ( j = 0 ; j < NUMVERTEXNORMALS ; j++ ) { float dot; dot = DotProduct( v, avertexnormals[j] ); if ( dot > maxdot ) { maxdot = dot; maxdotindex = j; } } ptrivert[i].lightnormalindex = maxdotindex; } free( ptri ); }
void CG_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins, const vec3_t maxs, float speed, int numChunks, material_t chunkType, int customChunk, float baseScale, int customSound = 0 ) { localEntity_t *le; refEntity_t *re; vec3_t dir; int i, j, k; int chunkModel = 0; leBounceSound_t bounce = LEBS_NONE; float r, speedMod = 1.0f; qboolean chunk = qfalse; if ( chunkType == MAT_NONE ) { // Well, we should do nothing return; } if ( customSound ) { if ( cgs.sound_precache[customSound] ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.sound_precache[customSound] ); } } // Set up our chunk sound info...breaking sounds are done here so they are done once on breaking..some return instantly because the chunks are done with effects instead of models switch( chunkType ) { case MAT_GLASS: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound ); } return; break; case MAT_GRATE1: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.grateSound ); } return; break; case MAT_ELECTRICAL:// (sparks) if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgi_S_RegisterSound (va("sound/ambience/spark%d.wav", Q_irand(1, 6))) ); } return; break; case MAT_DRK_STONE: case MAT_LT_STONE: case MAT_GREY_STONE: case MAT_WHITE_METAL: // not quite sure what this stuff is supposed to be...it's for Stu if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.rockBreakSound ); bounce = LEBS_ROCK; } speedMod = 0.5f; // rock blows up less break; case MAT_GLASS_METAL: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound ); // FIXME: should probably have a custom sound bounce = LEBS_METAL; } break; case MAT_CRATE1: case MAT_CRATE2: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.crateBreakSound[Q_irand(0,1)] ); } break; case MAT_METAL: case MAT_METAL2: case MAT_METAL3: case MAT_ELEC_METAL:// FIXME: maybe have its own sound? if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.chunkSound ); bounce = LEBS_METAL; } speedMod = 0.8f; // metal blows up a bit more break; case MAT_ROPE: /* if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgi_S_RegisterSound( "" )); FIXME: needs a sound } */ return; break; } if ( baseScale <= 0.0f ) { baseScale = 1.0f; } // Chunks for( i = 0; i < numChunks; i++ ) { if ( customChunk > 0 ) { // Try to use a custom chunk. if ( cgs.model_draw[customChunk] ) { chunk = qtrue; chunkModel = cgs.model_draw[customChunk]; } } if ( !chunk ) { // No custom chunk. Pick a random chunk type at run-time so we don't get the same chunks switch( chunkType ) { case MAT_METAL2: //bluegrey chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)]; break; case MAT_GREY_STONE://gray chunkModel = cgs.media.chunkModels[CHUNK_ROCK1][Q_irand(0, 3)]; break; case MAT_LT_STONE: //tan chunkModel = cgs.media.chunkModels[CHUNK_ROCK2][Q_irand(0, 3)]; break; case MAT_DRK_STONE://brown chunkModel = cgs.media.chunkModels[CHUNK_ROCK3][Q_irand(0, 3)]; break; case MAT_WHITE_METAL: chunkModel = cgs.media.chunkModels[CHUNK_WHITE_METAL][Q_irand(0, 3)]; break; case MAT_CRATE1://yellow multi-colored crate chunks chunkModel = cgs.media.chunkModels[CHUNK_CRATE1][Q_irand(0, 3)]; break; case MAT_CRATE2://red multi-colored crate chunks chunkModel = cgs.media.chunkModels[CHUNK_CRATE2][Q_irand(0, 3)]; break; case MAT_ELEC_METAL: case MAT_GLASS_METAL: case MAT_METAL://grey chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)]; break; case MAT_METAL3: if ( rand() & 1 ) { chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)]; } else { chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)]; } break; } } // It wouldn't look good to throw a bunch of RGB axis models...so make sure we have something to work with. if ( chunkModel ) { le = CG_AllocLocalEntity(); re = &le->refEntity; re->hModel = chunkModel; le->leType = LE_FRAGMENT; le->endTime = cg.time + 1300 + random() * 900; // spawn chunk roughly in the bbox of the thing...bias towards center in case thing blowing up doesn't complete fill its bbox. for( j = 0; j < 3; j++ ) { r = random() * 0.8f + 0.1f; re->origin[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] ); } VectorCopy( re->origin, le->pos.trBase ); // Move out from center of thing, otherwise you can end up things moving across the brush in an undesirable direction. Visually looks wrong VectorSubtract( re->origin, origin, dir ); VectorNormalize( dir ); VectorScale( dir, Q_flrand( speed * 0.5f, speed * 1.25f ) * speedMod, le->pos.trDelta ); // Angular Velocity VectorSet( le->angles.trBase, random() * 360, random() * 360, random() * 360 ); le->angles.trDelta[0] = crandom(); le->angles.trDelta[1] = crandom(); le->angles.trDelta[2] = 0; // don't do roll VectorScale( le->angles.trDelta, random() * 600.0f + 200.0f, le->angles.trDelta ); le->pos.trType = TR_GRAVITY; le->angles.trType = TR_LINEAR; le->pos.trTime = le->angles.trTime = cg.time; le->bounceFactor = 0.2f + random() * 0.2f; le->leFlags |= LEF_TUMBLE; le->ownerGentNum = owner; le->leBounceSoundType = bounce; // Make sure that we have the desired start size set le->radius = Q_flrand( baseScale * 0.75f, baseScale * 1.25f ); re->nonNormalizedAxes = qtrue; AxisCopy( axisDefault, re->axis ); // could do an angles to axis, but this is cheaper and works ok for( k = 0; k < 3; k++ ) { VectorScale( re->axis[k], le->radius, re->axis[k] ); } } } }
/* =========== SelectRandomFurthestSpawnPoint Chooses a player start, deathmatch start, etc ============ */ gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles, qboolean isbot ) { gentity_t *spot; vec3_t delta; float dist; float list_dist[MAX_SPAWN_POINTS]; gentity_t *list_spot[MAX_SPAWN_POINTS]; int numSpots, rnd, i, j; numSpots = 0; spot = NULL; while((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { if(SpotWouldTelefrag(spot)) continue; if(((spot->flags & FL_NO_BOTS) && isbot) || ((spot->flags & FL_NO_HUMANS) && !isbot)) { // spot is not for this human/bot player continue; } VectorSubtract( spot->s.origin, avoidPoint, delta ); dist = VectorLength( delta ); for (i = 0; i < numSpots; i++) { if(dist > list_dist[i]) { if (numSpots >= MAX_SPAWN_POINTS) numSpots = MAX_SPAWN_POINTS - 1; for(j = numSpots; j > i; j--) { list_dist[j] = list_dist[j-1]; list_spot[j] = list_spot[j-1]; } list_dist[i] = dist; list_spot[i] = spot; numSpots++; break; } } if(i >= numSpots && numSpots < MAX_SPAWN_POINTS) { list_dist[numSpots] = dist; list_spot[numSpots] = spot; numSpots++; } } if(!numSpots) { spot = G_Find(NULL, FOFS(classname), "info_player_deathmatch"); if (!spot) G_Error( "Couldn't find a spawn point" ); VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } // select a random spot from the spawn points furthest away rnd = random() * (numSpots / 2); VectorCopy (list_spot[rnd]->s.origin, origin); origin[2] += 9; VectorCopy (list_spot[rnd]->s.angles, angles); return list_spot[rnd]; }
/* ================= R_TraceLine ================= */ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_t end, ref_entity_t *test, int umask ) { ref_model_t *model; r_fragmentframecount++; // for multi-check avoidance // fill in a default trace Mem_Set( tr, 0, sizeof( trace_t )); trace_surface = NULL; trace_umask = umask; trace_fraction = 1; VectorCopy( end, trace_impact ); Mem_Set( &trace_plane, 0, sizeof( trace_plane )); ClearBounds( trace_absmins, trace_absmaxs ); AddPointToBounds( start, trace_absmins, trace_absmaxs ); AddPointToBounds( end, trace_absmins, trace_absmaxs ); model = test->model; if( model ) { if( model->type == mod_world || model->type == mod_brush ) { mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata; vec3_t temp, start_l, end_l, axis[3]; bool rotated = !Matrix3x3_Compare( test->axis, matrix3x3_identity ); // transform VectorSubtract( start, test->origin, start_l ); VectorSubtract( end, test->origin, end_l ); if( rotated ) { VectorCopy( start_l, temp ); Matrix3x3_Transform( test->axis, temp, start_l ); VectorCopy( end_l, temp ); Matrix3x3_Transform( test->axis, temp, end_l ); } VectorCopy( start_l, trace_start ); VectorCopy( end_l, trace_end ); // world uses a recursive approach using BSP tree, submodels // just walk the list of surfaces linearly if( test->model->type == mod_world ) R_RecursiveHullCheck( bmodel->nodes, start_l, end_l ); else if( BoundsIntersect( model->mins, model->maxs, trace_absmins, trace_absmaxs ) ) R_TraceAgainstBmodel( bmodel ); // transform back if( rotated && trace_fraction != 1 ) { Matrix3x3_Transpose( axis, test->axis ); VectorCopy( tr->vecPlaneNormal, temp ); Matrix3x3_Transform( axis, temp, trace_plane.normal ); } } } // calculate the impact plane, if any if( trace_fraction < 1.0f ) { VectorNormalize( trace_plane.normal ); trace_plane.dist = DotProduct( trace_plane.normal, trace_impact ); CategorizePlane( &trace_plane ); tr->flPlaneDist = trace_plane.dist; VectorCopy( trace_plane.normal, tr->vecPlaneNormal ); tr->iContents = trace_surface->contents; tr->pHit = (edict_t *)test; } tr->flFraction = trace_fraction; VectorCopy( trace_impact, tr->vecEndPos ); return trace_surface; }