void CM_AddFacetBevels( facet_t *facet ) { int i, j, k, l; int axis, dir, order, flipped; float plane[4], d, newplane[4]; winding_t *w, *w2; vec3_t mins, maxs, vec, vec2; #ifndef ADDBEVELS return; #endif Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); w = BaseWindingForPlane( plane, plane[3] ); for ( j = 0 ; j < facet->numBorders && w ; j++ ) { if ( facet->borderPlanes[j] == facet->surfacePlane ) { continue; } Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane ); if ( !facet->borderInward[j] ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } ChopWindingInPlace( &w, plane, plane[3], 0.1f ); } if ( !w ) { return; } WindingBounds( w, mins, maxs ); // add the axial planes order = 0; for ( axis = 0 ; axis < 3 ; axis++ ) { for ( dir = -1 ; dir <= 1 ; dir += 2, order++ ) { VectorClear( plane ); plane[axis] = dir; if ( dir == 1 ) { plane[3] = maxs[axis]; } else { plane[3] = -mins[axis]; } //if it's the surface plane if ( CM_PlaneEqual( &planes[facet->surfacePlane], plane, &flipped ) ) { continue; } // see if the plane is allready present for ( i = 0 ; i < facet->numBorders ; i++ ) { if ( CM_PlaneEqual( &planes[facet->borderPlanes[i]], plane, &flipped ) ) { break; } } if ( i == facet->numBorders ) { if ( facet->numBorders > 4 + 6 + 16 ) { Com_Printf( "ERROR: too many bevels\n" ); } facet->borderPlanes[facet->numBorders] = CM_FindPlane2( plane, &flipped ); facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = flipped; facet->numBorders++; } } } // // add the edge bevels // // test the non-axial plane edges for ( j = 0 ; j < w->numpoints ; j++ ) { k = ( j + 1 ) % w->numpoints; VectorSubtract( w->p[j], w->p[k], vec ); //if it's a degenerate edge if ( VectorNormalize( vec ) < 0.5 ) { continue; } CM_SnapVector( vec ); for ( k = 0; k < 3 ; k++ ) if ( vec[k] == -1 || vec[k] == 1 ) { break; } // axial if ( k < 3 ) { continue; // only test non-axial edges } // try the six possible slanted axials from this edge for ( axis = 0 ; axis < 3 ; axis++ ) { for ( dir = -1 ; dir <= 1 ; dir += 2 ) { // construct a plane VectorClear( vec2 ); vec2[axis] = dir; CrossProduct( vec, vec2, plane ); if ( VectorNormalize( plane ) < 0.5 ) { continue; } plane[3] = DotProduct( w->p[j], plane ); // if all the points of the facet winding are // behind this plane, it is a proper edge bevel for ( l = 0 ; l < w->numpoints ; l++ ) { d = DotProduct( w->p[l], plane ) - plane[3]; if ( d > 0.1 ) { break; // point in front } } if ( l < w->numpoints ) { continue; } //if it's the surface plane if ( CM_PlaneEqual( &planes[facet->surfacePlane], plane, &flipped ) ) { continue; } // see if the plane is allready present for ( i = 0 ; i < facet->numBorders ; i++ ) { if ( CM_PlaneEqual( &planes[facet->borderPlanes[i]], plane, &flipped ) ) { break; } } if ( i == facet->numBorders ) { if ( facet->numBorders > 4 + 6 + 16 ) { Com_Printf( "ERROR: too many bevels\n" ); } facet->borderPlanes[facet->numBorders] = CM_FindPlane2( plane, &flipped ); for ( k = 0 ; k < facet->numBorders ; k++ ) { if ( facet->borderPlanes[facet->numBorders] == facet->borderPlanes[k] ) { Com_Printf( "WARNING: bevel plane already used\n" ); } } facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = flipped; // w2 = CopyWinding( w ); Vector4Copy( planes[facet->borderPlanes[facet->numBorders]].plane, newplane ); if ( !facet->borderInward[facet->numBorders] ) { VectorNegate( newplane, newplane ); newplane[3] = -newplane[3]; } //end if ChopWindingInPlace( &w2, newplane, newplane[3], 0.1f ); if ( !w2 ) { // TTimo - can't stand this, useless and noisy //Com_DPrintf("WARNING: CM_AddFacetBevels... invalid bevel\n"); continue; } else { FreeWinding( w2 ); } // facet->numBorders++; //already got a bevel // break; } } } } FreeWinding( w ); #ifndef BSPC //add opposite plane facet->borderPlanes[facet->numBorders] = facet->surfacePlane; facet->borderNoAdjust[facet->numBorders] = 0; facet->borderInward[facet->numBorders] = qtrue; facet->numBorders++; #endif //BSPC }
/* ============== ClientEndFrame Called at the end of each server frame for each connected client A fast client will have multiple ClientThink for each ClientEdFrame, while a slow client may have multiple ClientEndFrame between ClientThink. ============== */ void ClientEndFrame( gentity_t *ent ) { int i; clientPersistant_t *pers; if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { SpectatorClientEndFrame( ent ); return; } pers = &ent->client->pers; // turn off any expired powerups for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ent->client->ps.powerups[ i ] < level.time ) { ent->client->ps.powerups[ i ] = 0; } } // save network bandwidth #if 0 if ( !g_synchronousClients->integer && (ent->client->ps.pm_type == PM_NORMAL || ent->client->ps.pm_type == PM_FLOAT) ) { // FIXME: this must change eventually for non-sync demo recording VectorClear( ent->client->ps.viewangles ); } #endif // // If the end of unit layout is displayed, don't give // the player any normal movement attributes // if ( level.intermissiontime ) { return; } // burn from lava, etc P_WorldEffects (ent); // apply all the damage taken this frame P_DamageFeedback (ent); // add the EF_CONNECTION flag if we haven't gotten commands recently if ( level.time - ent->client->lastCmdTime > 1000 ) { ent->s.eFlags |= EF_CONNECTION; } else { ent->s.eFlags &= ~EF_CONNECTION; } ent->client->ps.stats[STAT_HEALTH] = ent->health; // FIXME: get rid of ent->health... G_SetClientSound (ent); // set the latest infor if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); } else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); } SendPendingPredictableEvents( &ent->client->ps ); // set the bit for the reachability area the client is currently in // i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin ); // ent->client->areabits[i >> 3] |= 1 << (i & 7); }
void bfg_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { if (!self || !other) { G_FreeEdict(self); return; } if (other == self->owner) { return; } if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict(self); return; } if (self->owner && self->owner->client) { PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); } /* core explosion - prevents firing it into the wall/floor */ if (other->takedamage) { if (plane) { T_Damage(other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); } else { T_Damage(other, self, self->owner, self->velocity, self->s.origin, vec3_origin, 200, 0, 0, MOD_BFG_BLAST); } } T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); gi.sound(self, CHAN_VOICE, gi.soundindex( "weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); self->solid = SOLID_NOT; self->touch = NULL; VectorMA(self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); VectorClear(self->velocity); self->s.modelindex = gi.modelindex("sprites/s_bfg3.sp2"); self->s.frame = 0; self->s.sound = 0; self->s.effects &= ~EF_ANIM_ALLFAST; self->think = bfg_explode; self->nextthink = level.time + FRAMETIME; self->enemy = other; gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BFG_BIGEXPLOSION); gi.WritePosition(self->s.origin); gi.multicast(self->s.origin, MULTICAST_PVS); }
qboolean PM_AdjustAngleForWallRun( gentity_t *ent, usercmd_t *ucmd, qboolean doMove ) { if (( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT || ent->client->ps.legsAnim == BOTH_WALL_RUN_LEFT ) && ent->client->ps.legsAnimTimer > 500 ) {//wall-running and not at end of anim //stick to wall, if there is one vec3_t rt, traceTo, mins = {ent->mins[0],ent->mins[1],0}, maxs = {ent->maxs[0],ent->maxs[1],24}, fwdAngles = {0, ent->client->ps.viewangles[YAW], 0}; trace_t trace; float dist, yawAdjust; AngleVectors( fwdAngles, NULL, rt, NULL ); if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) { dist = 128; yawAdjust = -90; } else { dist = -128; yawAdjust = 90; } VectorMA( ent->currentOrigin, dist, rt, traceTo ); gi.trace( &trace, ent->currentOrigin, mins, maxs, traceTo, ent->s.number, ent->clipmask ); if ( trace.fraction < 1.0f && trace.plane.normal[2] == 0.0f ) {//still a vertical wall there //FIXME: don't pull around 90 turns //FIXME: simulate stepping up steps here, somehow? if ( ent->s.number || !player_locked ) { if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) { ucmd->rightmove = 127; } else { ucmd->rightmove = -127; } } if ( ucmd->upmove < 0 ) { ucmd->upmove = 0; } if ( ent->NPC ) {//invalid now VectorClear( ent->client->ps.moveDir ); } //make me face perpendicular to the wall ent->client->ps.viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust; if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD ) {//don't clamp angles when looking through a viewEntity SetClientViewAngle( ent, ent->client->ps.viewangles ); } ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW]; if ( ent->s.number || !player_locked ) { if ( doMove ) { //push me forward vec3_t fwd; float zVel = ent->client->ps.velocity[2]; if ( zVel > forceJumpStrength[FORCE_LEVEL_2]/2.0f ) { zVel = forceJumpStrength[FORCE_LEVEL_2]/2.0f; } if ( ent->client->ps.legsAnimTimer > 500 ) {//not at end of anim yet fwdAngles[YAW] = ent->client->ps.viewangles[YAW]; AngleVectors( fwdAngles, fwd, NULL, NULL ); //FIXME: or MA? float speed = 175; if ( ucmd->forwardmove < 0 ) {//slower speed = 100; } else if ( ucmd->forwardmove > 0 ) { speed = 250;//running speed } VectorScale( fwd, speed, ent->client->ps.velocity ); } ent->client->ps.velocity[2] = zVel;//preserve z velocity VectorMA( ent->client->ps.velocity, -128, trace.plane.normal, ent->client->ps.velocity ); //pull me toward the wall, too //VectorMA( ent->client->ps.velocity, dist, rt, ent->client->ps.velocity ); } } ucmd->forwardmove = 0; return qtrue; } else if ( doMove ) {//stop it if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) { NPC_SetAnim( ent, SETANIM_BOTH, BOTH_WALL_RUN_RIGHT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } else if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_LEFT ) { NPC_SetAnim( ent, SETANIM_BOTH, BOTH_WALL_RUN_LEFT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } } } return qfalse; }
/* * CopyToBodyQue */ static edict_t *CopyToBodyQue( edict_t *ent, edict_t *attacker, int damage ) { edict_t *body; int contents; if( GS_RaceGametype() ) return NULL; contents = G_PointContents( ent->s.origin ); if( contents & CONTENTS_NODROP ) return NULL; G_Client_UnlinkBodies( ent ); // grab a body que and cycle to the next one body = &game.edicts[gs.maxclients + level.body_que + 1]; level.body_que = ( level.body_que + 1 ) % BODY_QUEUE_SIZE; // send an effect on the removed body if( body->s.modelindex && body->s.type == ET_CORPSE ) ThrowSmallPileOfGibs( body, 10 ); GClip_UnlinkEntity( body ); memset( body, 0, sizeof( edict_t ) ); //clean up garbage //init body edict G_InitEdict( body ); body->classname = "body"; body->health = ent->health; body->mass = ent->mass; body->r.owner = ent->r.owner; body->s.type = ent->s.type; body->s.team = ent->s.team; body->s.effects = 0; body->r.svflags = SVF_CORPSE; body->r.svflags &= ~SVF_NOCLIENT; body->activator = ent; if( g_deadbody_followkiller->integer ) body->enemy = attacker; //use flat yaw body->s.angles[PITCH] = 0; body->s.angles[ROLL] = 0; body->s.angles[YAW] = ent->s.angles[YAW]; body->s.modelindex2 = 0; // <- is bodyOwner when in ET_CORPSE, but not in ET_GENERIC or ET_PLAYER body->s.weapon = 0; //copy player position and box size VectorCopy( ent->s.old_origin, body->s.old_origin ); VectorCopy( ent->s.origin, body->s.origin ); VectorCopy( ent->s.origin, body->olds.origin ); VectorCopy( ent->r.mins, body->r.mins ); VectorCopy( ent->r.maxs, body->r.maxs ); VectorCopy( ent->r.absmin, body->r.absmin ); VectorCopy( ent->r.absmax, body->r.absmax ); VectorCopy( ent->r.size, body->r.size ); VectorCopy( ent->velocity, body->velocity ); body->r.maxs[2] = body->r.mins[2] + 8; body->r.solid = SOLID_YES; body->takedamage = DAMAGE_YES; body->r.clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->movetype = MOVETYPE_TOSS; body->die = body_die; body->think = body_think; // body self destruction countdown if( ent->health < GIB_HEALTH || meansOfDeath == MOD_ELECTROBOLT_S /* electrobolt always gibs */ ) { ThrowSmallPileOfGibs( body, damage ); // reset gib impulse VectorClear( body->velocity ); ThrowClientHead( body, damage ); // sets ET_GIB body->s.frame = 0; body->nextThink = level.time + 3000 + random() * 3000; body->deadflag = DEAD_DEAD; } else if( ent->s.type == ET_PLAYER ) { // copy the model body->s.type = ET_CORPSE; body->s.modelindex = ent->s.modelindex; body->s.bodyOwner = ent->s.number; // bodyOwner is the same as modelindex2 body->s.skinnum = ent->s.skinnum; body->s.teleported = true; // launch the death animation on the body { static int i; i = ( i+1 )%3; G_AddEvent( body, EV_DIE, i, true ); switch( i ) { default: case 0: body->s.frame = ( ( BOTH_DEAD1&0x3F )|( BOTH_DEAD1&0x3F )<<6|( 0 &0xF )<<12 ); break; case 1: body->s.frame = ( ( BOTH_DEAD2&0x3F )|( BOTH_DEAD2&0x3F )<<6|( 0 &0xF )<<12 ); break; case 2: body->s.frame = ( ( BOTH_DEAD3&0x3F )|( BOTH_DEAD3&0x3F )<<6|( 0 &0xF )<<12 ); break; } } body->think = body_ready; body->takedamage = DAMAGE_NO; body->r.solid = SOLID_NOT; body->nextThink = level.time + 500; // make damageable in 0.5 seconds } else // wasn't a player, just copy it's model { VectorClear( body->velocity ); body->s.modelindex = ent->s.modelindex; body->s.frame = ent->s.frame; body->nextThink = level.time + 5000 + random()*10000; } GClip_LinkEntity( body ); return body; }
/* ================= ClientEndServerFrame Called for each player at the end of the server frame and right after spawning ================= */ void ClientEndServerFrame (edict_t *ent) { float bobtime; int i; current_player = ent; current_client = ent->client; // // If the origin or velocity have changed since ClientThink(), // update the pmove values. This will happen when the client // is pushed by a bmodel or kicked by an explosion. // // If it wasn't updated here, the view position would lag a frame // behind the body position when pushed -- "sinking into plats" // for (i=0 ; i<3 ; i++) { current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0; current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0; } // // If the end of unit layout is displayed, don't give // the player any normal movement attributes // if (level.intermissiontime) { // FIXME: add view drifting here? current_client->ps.blend[3] = 0; current_client->ps.fov = 90; G_SetStats (ent); return; } AngleVectors (ent->client->v_angle, forward, right, up); // burn from lava, etc P_WorldEffects (); // // set model angles from view angles so other things in // the world can tell which direction you are looking // if (ent->client->v_angle[PITCH] > 180) ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3; else ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3; ent->s.angles[YAW] = ent->client->v_angle[YAW]; ent->s.angles[ROLL] = 0; ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4; // // calculate speed and cycle to be used for // all cyclic walking effects // xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]); if (xyspeed < 5) { bobmove = 0; current_client->bobtime = 0; // start at beginning of cycle again } else if (ent->groundentity) { // so bobbing only cycles when on ground if (xyspeed > 210) bobmove = 0.25; else if (xyspeed > 100) bobmove = 0.125; else bobmove = 0.0625; } bobtime = (current_client->bobtime += bobmove); if (current_client->ps.pmove.pm_flags & PMF_DUCKED) bobtime *= 4; bobcycle = (int)bobtime; bobfracsin = fabs(sin(bobtime*M_PI)); // detect hitting the floor P_FallingDamage (ent); // apply all the damage taken this frame P_DamageFeedback (ent); // determine the view offsets SV_CalcViewOffset (ent); // determine the gun offsets SV_CalcGunOffset (ent); // determine the full screen color blend // must be after viewoffset, so eye contents can be // accurately determined // FIXME: with client prediction, the contents // should be determined by the client SV_CalcBlend (ent); // chase cam stuff if (ent->client->resp.spectator) G_SetSpectatorStats(ent); else G_SetStats (ent); G_CheckChaseStats(ent); G_SetClientEvent (ent); G_SetClientEffects (ent); G_SetClientSound (ent); G_SetClientFrame (ent); VectorCopy (ent->velocity, ent->client->oldvelocity); VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles); // clear weapon kicks VectorClear (ent->client->kick_origin); VectorClear (ent->client->kick_angles); // if the scoreboard is up, update it if (ent->client->showscores && !(level.framenum & 31) ) { DeathmatchScoreboardMessage (ent, ent->enemy); gi.unicast (ent, false); } }
/* ============== SV_CalcGunOffset ============== */ void SV_CalcGunOffset (edict_t *ent) { int i; float delta; //ROGUE static gitem_t *heatbeam; if (!heatbeam) heatbeam = FindItemByClassname ("weapon_plasmabeam"); //ROGUE - heatbeam shouldn't bob so the beam looks right if (ent->client->pers.weapon != heatbeam) { // ROGUE // gun angles from bobbing ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005; ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01; if (bobcycle & 1) { ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL]; ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW]; } ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005; // gun angles from delta movement for (i=0 ; i<3 ; i++) { delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i]; if (delta > 180) delta -= 360; if (delta < -180) delta += 360; if (delta > 45) delta = 45; if (delta < -45) delta = -45; if (i == YAW) ent->client->ps.gunangles[ROLL] += 0.1*delta; ent->client->ps.gunangles[i] += 0.2 * delta; } } // ROGUE else { for (i=0; i<3; i++) ent->client->ps.gunangles[i] = 0; } //ROGUE // gun height VectorClear (ent->client->ps.gunoffset); // ent->ps->gunorigin[2] += bob; // gun_x / gun_y / gun_z are development tools for (i=0 ; i<3 ; i++) { ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value); ent->client->ps.gunoffset[i] += right[i]*gun_x->value; ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value); } }
/* =========== PutClientInServer Called when a player connects to a server or respawns in a deathmatch. ============ */ void PutClientInServer (edict_t *ent) { vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; client_persistant_t saved; client_respawn_t resp; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client SelectSpawnPoint (ent, spawn_origin, spawn_angles); index = ent-g_edicts-1; client = ent->client; // deathmatch wipes most client data every spawn if (deathmatch->value) { char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); InitClientPersistant (client); ClientUserinfoChanged (ent, userinfo); } else if (coop->value) { // int n; char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); // this is kind of ugly, but it's how we want to handle keys in coop // for (n = 0; n < game.num_items; n++) // { // if (itemlist[n].flags & IT_KEY) // resp.coop_respawn.inventory[n] = client->pers.inventory[n]; // } resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged; resp.coop_respawn.helpchanged = client->pers.helpchanged; client->pers = resp.coop_respawn; ClientUserinfoChanged (ent, userinfo); if (resp.score > client->pers.score) client->pers.score = resp.score; } else { memset (&resp, 0, sizeof(resp)); } // clear everything but the persistant data saved = client->pers; memset (client, 0, sizeof(*client)); client->pers = saved; if (client->pers.health <= 0) InitClientPersistant(client); client->resp = resp; // copy some data from the client to the entity FetchClientEntData (ent); // clear entity values ent->groundentity = NULL; ent->client = &game.clients[index]; ent->takedamage = DAMAGE_AIM; ent->movetype = MOVETYPE_WALK; ent->viewheight = 22; ent->inuse = true; ent->classname = "player"; ent->mass = 200; ent->solid = SOLID_BBOX; ent->deadflag = DEAD_NO; ent->air_finished = level.time + 12; ent->clipmask = MASK_PLAYERSOLID; ent->model = "players/male/tris.md2"; ent->pain = player_pain; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags &= ~FL_NO_KNOCKBACK; ent->svflags &= ~SVF_DEADMONSTER; VectorCopy (mins, ent->mins); VectorCopy (maxs, ent->maxs); VectorClear (ent->velocity); // clear playerstate values memset (&ent->client->ps, 0, sizeof(client->ps)); client->ps.pmove.origin[0] = spawn_origin[0]*8; client->ps.pmove.origin[1] = spawn_origin[1]*8; client->ps.pmove.origin[2] = spawn_origin[2]*8; if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { client->ps.fov = 90; } else { client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov")); if (client->ps.fov < 1) client->ps.fov = 90; else if (client->ps.fov > 160) client->ps.fov = 160; } client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); // clear entity state values ent->s.effects = 0; ent->s.modelindex = 255; // will use the skin specified model ent->s.modelindex2 = 255; // custom gun model // sknum is player num and weapon number // weapon number will be added in changeweapon ent->s.skinnum = ent - g_edicts - 1; ent->s.frame = 0; VectorCopy (spawn_origin, ent->s.origin); ent->s.origin[2] += 1; // make sure off ground VectorCopy (ent->s.origin, ent->s.old_origin); // set the delta angle for (i=0 ; i<3 ; i++) { client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); } ent->s.angles[PITCH] = 0; ent->s.angles[YAW] = spawn_angles[YAW]; ent->s.angles[ROLL] = 0; VectorCopy (ent->s.angles, client->ps.viewangles); VectorCopy (ent->s.angles, client->v_angle); // spawn a spectator if (client->pers.spectator) { client->chase_target = NULL; client->resp.spectator = true; ent->movetype = MOVETYPE_NOCLIP; ent->solid = SOLID_NOT; ent->svflags |= SVF_NOCLIENT; ent->client->ps.gunindex = 0; gi.linkentity (ent); return; } else client->resp.spectator = false; if (!KillBox (ent)) { // could't spawn in? } gi.linkentity (ent); // force the current weapon up client->newweapon = client->pers.weapon; ChangeWeapon (ent); }
/* ================== player_die ================== */ void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { int n; VectorClear (self->avelocity); self->takedamage = DAMAGE_YES; self->movetype = MOVETYPE_TOSS; self->s.modelindex2 = 0; // remove linked weapon model self->s.angles[0] = 0; self->s.angles[2] = 0; self->s.sound = 0; self->client->weapon_sound = 0; self->maxs[2] = -8; // self->solid = SOLID_NOT; self->svflags |= SVF_DEADMONSTER; if (!self->deadflag) { self->client->respawn_time = level.time + 1.0; LookAtKiller (self, inflictor, attacker); self->client->ps.pmove.pm_type = PM_DEAD; ClientObituary (self, inflictor, attacker); TossClientWeapon (self); if (deathmatch->value) Cmd_Help_f (self); // show scores // clear inventory // this is kind of ugly, but it's how we want to handle keys in coop for (n = 0; n < game.num_items; n++) { if (coop->value && itemlist[n].flags & IT_KEY) self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n]; self->client->pers.inventory[n] = 0; } } // remove powerups self->client->quad_framenum = 0; self->client->invincible_framenum = 0; self->client->breather_framenum = 0; self->client->enviro_framenum = 0; self->flags &= ~FL_POWER_ARMOR; if (self->health < -40) { // gib gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n= 0; n < 4; n++) ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ThrowClientHead (self, damage); self->takedamage = DAMAGE_NO; } else { // normal death if (!self->deadflag) { static int i; i = (i+1)%3; // start a death animation self->client->anim_priority = ANIM_DEATH; if (self->client->ps.pmove.pm_flags & PMF_DUCKED) { self->s.frame = FRAME_crdeath1-1; self->client->anim_end = FRAME_crdeath5; } else switch (i) { case 0: self->s.frame = FRAME_death101-1; self->client->anim_end = FRAME_death106; break; case 1: self->s.frame = FRAME_death201-1; self->client->anim_end = FRAME_death206; break; case 2: self->s.frame = FRAME_death301-1; self->client->anim_end = FRAME_death308; break; } gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0); } } self->deadflag = DEAD_DEAD; gi.linkentity (self); }
void CL_Heatbeam (vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; float len; int j; cparticle_t *p; vec3_t forward, right, up; int i; float d, c, s; vec3_t dir; float ltime; float step = 32.0, rstep; float start_pt; float rot; VectorCopy (start, move); VectorSubtract (end, start, vec); len = VectorNormalize (vec); // MakeNormalVectors (vec, right, up); VectorCopy (cl.v_forward, forward); VectorCopy (cl.v_right, right); VectorCopy (cl.v_up, up); VectorMA (move, -0.5, right, move); VectorMA (move, -0.5, up, move); for (i=0; i<8; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; VectorClear (p->accel); d = crand()*M_PI; c = cos(d)*30; s = sin(d)*30; p->alpha = 1.0; p->alphavel = -5.0 / (1+frand()); p->color = 223 - (rand()&7); for (j=0 ; j<3 ; j++) { p->org[j] = move[j]; } VectorScale (vec, 450, p->vel); VectorMA (p->vel, c, right, p->vel); VectorMA (p->vel, s, up, p->vel); } /* ltime = (float) cl.time/1000.0; start_pt = fmod(ltime*16.0,step); VectorMA (move, start_pt, vec, move); VectorScale (vec, step, vec); // Com_Printf ("%f\n", ltime); rstep = M_PI/12.0; for (i=start_pt ; i<len ; i+=step) { if (i>step*5) // don't bother after the 5th ring break; for (rot = 0; rot < M_PI*2; rot += rstep) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; VectorClear (p->accel); // rot+= fmod(ltime, 12.0)*M_PI; // c = cos(rot)/2.0; // s = sin(rot)/2.0; c = cos(rot)/1.5; s = sin(rot)/1.5; // trim it so it looks like it's starting at the origin if (i < 10) { VectorScale (right, c*(i/10.0), dir); VectorMA (dir, s*(i/10.0), up, dir); } else { VectorScale (right, c, dir); VectorMA (dir, s, up, dir); } p->alpha = 0.5; // p->alphavel = -1.0 / (1+frand()*0.2); p->alphavel = -1000.0; // p->color = 0x74 + (rand()&7); p->color = 223 - (rand()&7); for (j=0 ; j<3 ; j++) { p->org[j] = move[j] + dir[j]*3; // p->vel[j] = dir[j]*6; p->vel[j] = 0; } } VectorAdd (move, vec, move); } */ }
/* =============== R_RocketTrail =============== */ void R_RocketTrail (vec3_t start, vec3_t end, int type) { vec3_t vec, dist; float len, size, lifetime; int j; particle_t *p; static int tracercount; VectorSubtract (end, start, vec); len = VectorNormalize (vec); dist[0] = vec[0]; dist[1] = vec[1]; dist[2] = vec[2]; size = 1; lifetime = 2; switch (type) { case 9: // Spit break; case 8: // Ice size *= 5 * 3; dist[0] *= 5 * 3; dist[1] *= 5 * 3; dist[2] *= 5 * 3; break; case rt_acidball: // Ice size = 5; lifetime = .8; break; default: size = 3; dist[0] *= 3; dist[1] *= 3; dist[2] *= 3; break; } while (len > 0) { len -= size; p = AllocParticle(); if (!p) return; VectorClear (p->vel); p->die = cl.time + lifetime; switch (type) { case rt_rocket_trail: // rocket trail p->ramp = rand() & 3; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_smoke: // smoke smoke p->ramp = (rand() & 3) + 2; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_blood: // blood p->type = pt_slowgrav; p->color = 134 + (rand() & 7); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_tracer:; case rt_tracer2:; // tracer p->die = cl.time + 0.5; p->type = pt_static; if (type == 3) p->color = 130 + (rand() & 6); // 243 + (rand() & 3); else p->color = 230 + ((tracercount & 4) << 1); tracercount++; VectorCopy (start, p->org); if (tracercount & 1) { p->vel[0] = 30 * vec[1]; p->vel[1] = 30 * -vec[0]; } else { p->vel[0] = 30 * -vec[1]; p->vel[1] = 30 * vec[0]; } break; case rt_slight_blood: // slight blood p->type = pt_slowgrav; p->color = 134 + (rand() & 7); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); len -= size; break; case rt_bloodshot: // bloodshot trail p->type = pt_darken; p->color = 136 + (rand() & 5); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); len -= size; break; case rt_voor_trail: // voor trail p->color = 9*16 + 8 + (rand() & 3); p->type = pt_static; p->die = cl.time + 0.3; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 15) - 8); break; case rt_fireball: // Fireball p->ramp = rand() & 3; p->color = ramp4[(int)(p->ramp)]; p->type = pt_fireball; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 200) - 100; p->vel[1] = (rand() % 200) - 100; p->vel[2] = (rand() % 200) - 100; break; case rt_acidball: // Acid ball p->ramp = rand() & 3; p->color = ramp10[(int)(p->ramp)]; p->type = pt_acidball; p->die = cl.time + 0.5; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 40) - 20; p->vel[1] = (rand() % 40) - 20; p->vel[2] = (rand() % 40) - 20; break; case rt_ice: // Ice p->ramp = rand() & 3; p->color = ramp5[(int)(p->ramp)]; p->type = pt_ice; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 16) - 8; p->vel[1] = (rand() % 16) - 8; p->vel[2] = (rand() % 20) - 40; break; case rt_spit: // Spit p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spit; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); break; case rt_spell: // Spell p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spell; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); p->vel[0] = vec[0] * -10; p->vel[1] = vec[1] * -10; p->vel[2] = vec[2] * -10; break; case rt_vorpal: // vorpal missile p->type = pt_vorpal; p->color = 44 + (rand() & 3) + 256; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() & 15) - 8); break; case rt_setstaff: // set staff p->type = pt_setstaff; p->color = ramp9[0]; p->ramp = rand() & 3; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 6) - 3); p->org[2] = start[2] + ((rand() % 10) - 5); p->vel[0] = (rand() & 7) - 4; p->vel[1] = (rand() & 7) - 4; break; case rt_magicmissile: // magic missile p->type = pt_magicmissile; p->color = 148 + (rand() & 11); p->ramp = rand() & 3; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() % 48) - 24); p->vel[2] = -((rand() & 15) + 8); break; case rt_boneshard: // bone shard p->type = pt_boneshard; p->color = 368 + (rand() & 16); for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() % 48) - 24); p->vel[2] = -((rand() & 15) + 8); break; case rt_scarab: // scarab staff p->type = pt_scarab; p->color = 250 + (rand() & 3); for (j = 0; j < 3; j++) p->org[j] = start[j] + (rand() & 7); p->vel[2] = -(rand() & 7); break; } VectorAdd (start, dist, start); } }
//void CL_Heatbeam (vec3_t start, vec3_t end) void CL_Heatbeam (vec3_t start, vec3_t forward) { vec3_t move; vec3_t vec; float len; int j; cparticle_t *p; vec3_t right, up; int i; float c, s; vec3_t dir; float ltime; float step = 32.0, rstep; float start_pt; float rot; float variance; vec3_t end; VectorMA (start, 4096, forward, end); VectorCopy (start, move); VectorSubtract (end, start, vec); len = VectorNormalize (vec); // FIXME - pmm - these might end up using old values? // MakeNormalVectors (vec, right, up); VectorCopy (cl.v_right, right); VectorCopy (cl.v_up, up); if (vidref_val == VIDREF_GL) { // GL mode VectorMA (move, -0.5, right, move); VectorMA (move, -0.5, up, move); } // otherwise assume SOFT ltime = (float) cl.time/1000.0; start_pt = fmod(ltime*96.0,step); VectorMA (move, start_pt, vec, move); VectorScale (vec, step, vec); // Com_Printf ("%f\n", ltime); rstep = M_PI/10.0; for (i=start_pt ; i<len ; i+=step) { if (i>step*5) // don't bother after the 5th ring break; for (rot = 0; rot < M_PI*2; rot += rstep) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; VectorClear (p->accel); // rot+= fmod(ltime, 12.0)*M_PI; // c = cos(rot)/2.0; // s = sin(rot)/2.0; // variance = 0.4 + ((float)rand()/(float)RAND_MAX) *0.2; variance = 0.5; c = cos(rot)*variance; s = sin(rot)*variance; // trim it so it looks like it's starting at the origin if (i < 10) { VectorScale (right, c*(i/10.0), dir); VectorMA (dir, s*(i/10.0), up, dir); } else { VectorScale (right, c, dir); VectorMA (dir, s, up, dir); } p->alpha = 0.5; // p->alphavel = -1.0 / (1+frand()*0.2); p->alphavel = -1000.0; // p->color = 0x74 + (rand()&7); p->color = 223 - (rand()&7); for (j=0 ; j<3 ; j++) { p->org[j] = move[j] + dir[j]*3; // p->vel[j] = dir[j]*6; p->vel[j] = 0; } } VectorAdd (move, vec, move); } }
void CL_Heatbeam (vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; float len; int j,k; cparticle_t *p; vec3_t right, up; int i; float d, c, s; vec3_t dir; float ltime; float step = 5.0; VectorCopy (start, move); VectorSubtract (end, start, vec); len = VectorNormalize (vec); // MakeNormalVectors (vec, right, up); VectorCopy (cl.v_right, right); VectorCopy (cl.v_up, up); VectorMA (move, -1, right, move); VectorMA (move, -1, up, move); VectorScale (vec, step, vec); ltime = (float) cl.time/1000.0; // for (i=0 ; i<len ; i++) for (i=0 ; i<len ; i+=step) { d = i * 0.1 - fmod(ltime,16.0)*M_PI; c = cos(d)/1.75; s = sin(d)/1.75; #ifdef DOUBLE_SCREW for (k=-1; k<2; k+=2) { #else k=1; #endif if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; VectorClear (p->accel); p->alpha = 0.5; // p->alphavel = -1.0 / (1+frand()*0.2); // only last one frame! p->alphavel = INSTANT_PARTICLE; // p->color = 0x74 + (rand()&7); // p->color = 223 - (rand()&7); p->color = 223; // p->color = 240; // trim it so it looks like it's starting at the origin if (i < 10) { VectorScale (right, c*(i/10.0)*k, dir); VectorMA (dir, s*(i/10.0)*k, up, dir); } else { VectorScale (right, c*k, dir); VectorMA (dir, s*k, up, dir); } for (j=0 ; j<3 ; j++) { p->org[j] = move[j] + dir[j]*3; // p->vel[j] = dir[j]*6; p->vel[j] = 0; } #ifdef DOUBLE_SCREW } #endif VectorAdd (move, vec, move); } }
//=========================================================================== // recursive subdivision of the line by the BSP tree. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_trace_t AAS_TraceClientBBox( vec3_t start, vec3_t end, int presencetype, int passent ) { int side, nodenum, tmpplanenum; float front, back, frac; vec3_t cur_start, cur_end, cur_mid, v1, v2; aas_tracestack_t tracestack[127]; aas_tracestack_t *tstack_p; aas_node_t *aasnode; aas_plane_t *plane; aas_trace_t trace; //clear the trace structure memset( &trace, 0, sizeof( aas_trace_t ) ); if ( !( *aasworld ).loaded ) { return trace; } tstack_p = tracestack; //we start with the whole line on the stack VectorCopy( start, tstack_p->start ); VectorCopy( end, tstack_p->end ); tstack_p->planenum = 0; //start with node 1 because node zero is a dummy for a solid leaf tstack_p->nodenum = 1; //starting at the root of the tree tstack_p++; while ( 1 ) { //pop up the stack tstack_p--; //if the trace stack is empty (ended up with a piece of the //line to be traced in an area) if ( tstack_p < tracestack ) { tstack_p++; //nothing was hit trace.startsolid = qfalse; trace.fraction = 1.0; //endpos is the end of the line VectorCopy( end, trace.endpos ); //nothing hit trace.ent = 0; trace.area = 0; trace.planenum = 0; return trace; } //end if //number of the current node to test the line against nodenum = tstack_p->nodenum; //if it is an area if ( nodenum < 0 ) { #ifdef AAS_SAMPLE_DEBUG if ( -nodenum > ( *aasworld ).numareasettings ) { botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n" ); return trace; } //end if #endif //AAS_SAMPLE_DEBUG //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); //if can't enter the area because it hasn't got the right presence type if ( !( ( *aasworld ).areasettings[-nodenum].presencetype & presencetype ) ) { //if the start point is still the initial start point //NOTE: no need for epsilons because the points will be //exactly the same when they're both the start point if ( tstack_p->start[0] == start[0] && tstack_p->start[1] == start[1] && tstack_p->start[2] == start[2] ) { trace.startsolid = qtrue; trace.fraction = 0.0; VectorClear(v1); } //end if else { trace.startsolid = qfalse; VectorSubtract( end, start, v1 ); VectorSubtract( tstack_p->start, start, v2 ); trace.fraction = VectorLength( v2 ) / VectorNormalize( v1 ); VectorMA( tstack_p->start, -0.125, v1, tstack_p->start ); } //end else VectorCopy( tstack_p->start, trace.endpos ); trace.ent = 0; trace.area = -nodenum; // VectorSubtract(end, start, v1); trace.planenum = tstack_p->planenum; //always take the plane with normal facing towards the trace start plane = &( *aasworld ).planes[trace.planenum]; if ( DotProduct( v1, plane->normal ) > 0 ) { trace.planenum ^= 1; } return trace; } //end if else { if ( passent >= 0 ) { if ( AAS_AreaEntityCollision( -nodenum, tstack_p->start, tstack_p->end, presencetype, passent, &trace ) ) { if ( !trace.startsolid ) { VectorSubtract( end, start, v1 ); VectorSubtract( trace.endpos, start, v2 ); trace.fraction = VectorLength( v2 ) / VectorLength( v1 ); } //end if return trace; } //end if } //end if } //end else trace.lastarea = -nodenum; continue; } //end if //if it is a solid leaf if ( !nodenum ) { //if the start point is still the initial start point //NOTE: no need for epsilons because the points will be //exactly the same when they're both the start point if ( tstack_p->start[0] == start[0] && tstack_p->start[1] == start[1] && tstack_p->start[2] == start[2] ) { trace.startsolid = qtrue; trace.fraction = 0.0; VectorClear(v1); } //end if else { trace.startsolid = qfalse; VectorSubtract( end, start, v1 ); VectorSubtract( tstack_p->start, start, v2 ); trace.fraction = VectorLength( v2 ) / VectorNormalize( v1 ); VectorMA( tstack_p->start, -0.125, v1, tstack_p->start ); } //end else VectorCopy( tstack_p->start, trace.endpos ); trace.ent = 0; trace.area = 0; //hit solid leaf // VectorSubtract(end, start, v1); trace.planenum = tstack_p->planenum; //always take the plane with normal facing towards the trace start plane = &( *aasworld ).planes[trace.planenum]; if ( DotProduct( v1, plane->normal ) > 0 ) { trace.planenum ^= 1; } return trace; } //end if #ifdef AAS_SAMPLE_DEBUG if ( nodenum > ( *aasworld ).numnodes ) { botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n" ); return trace; } //end if #endif //AAS_SAMPLE_DEBUG //the node to test against aasnode = &( *aasworld ).nodes[nodenum]; //start point of current line to test against node VectorCopy( tstack_p->start, cur_start ); //end point of the current line to test against node VectorCopy( tstack_p->end, cur_end ); //the current node plane plane = &( *aasworld ).planes[aasnode->planenum]; switch ( plane->type ) {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!! //check for axial planes case PLANE_X: { front = cur_start[0] - plane->dist; back = cur_end[0] - plane->dist; break; } //end case case PLANE_Y: { front = cur_start[1] - plane->dist; back = cur_end[1] - plane->dist; break; } //end case case PLANE_Z: { front = cur_start[2] - plane->dist; back = cur_end[2] - plane->dist; break; } //end case*/ default: //gee it's not an axial plane { front = DotProduct( cur_start, plane->normal ) - plane->dist; back = DotProduct( cur_end, plane->normal ) - plane->dist; break; } //end default } //end switch //calculate the hitpoint with the node (split point of the line) //put the crosspoint TRACEPLANE_EPSILON pixels on the near side if ( front < 0 ) { frac = ( front + TRACEPLANE_EPSILON ) / ( front - back ); } else { frac = ( front - TRACEPLANE_EPSILON ) / ( front - back );} //if the whole to be traced line is totally at the front of this node //only go down the tree with the front child if ( ( front >= -ON_EPSILON && back >= -ON_EPSILON ) ) { //keep the current start and end point on the stack //and go down the tree with the front child tstack_p->nodenum = aasnode->children[0]; tstack_p++; if ( tstack_p >= &tracestack[127] ) { botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" ); return trace; } //end if } //end if //if the whole to be traced line is totally at the back of this node //only go down the tree with the back child else if ( ( front < ON_EPSILON && back < ON_EPSILON ) ) { //keep the current start and end point on the stack //and go down the tree with the back child tstack_p->nodenum = aasnode->children[1]; tstack_p++; if ( tstack_p >= &tracestack[127] ) { botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" ); return trace; } //end if } //end if //go down the tree both at the front and back of the node else { tmpplanenum = tstack_p->planenum; // if ( frac < 0 ) { frac = 0.001; //0 } else if ( frac > 1 ) { frac = 0.999; //1 } //frac = front / (front-back); // cur_mid[0] = cur_start[0] + ( cur_end[0] - cur_start[0] ) * frac; cur_mid[1] = cur_start[1] + ( cur_end[1] - cur_start[1] ) * frac; cur_mid[2] = cur_start[2] + ( cur_end[2] - cur_start[2] ) * frac; // AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); //side the front part of the line is on side = front < 0; //first put the end part of the line on the stack (back side) VectorCopy( cur_mid, tstack_p->start ); //not necesary to store because still on stack //VectorCopy(cur_end, tstack_p->end); tstack_p->planenum = aasnode->planenum; tstack_p->nodenum = aasnode->children[!side]; tstack_p++; if ( tstack_p >= &tracestack[127] ) { botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" ); return trace; } //end if //now put the part near the start of the line on the stack so we will //continue with thats part first. This way we'll find the first //hit of the bbox VectorCopy( cur_start, tstack_p->start ); VectorCopy( cur_mid, tstack_p->end ); tstack_p->planenum = tmpplanenum; tstack_p->nodenum = aasnode->children[side]; tstack_p++; if ( tstack_p >= &tracestack[127] ) { botimport.Print( PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n" ); return trace; } //end if } //end else } //end while // return trace; } //end of the function AAS_TraceClientBBox
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowReachability(aas_reachability_t *reach) { vec3_t dir, cmdmove, velocity; float speed, zvel; aas_clientmove_t move; int contentmask = BOTMASK_SOLID; // ZTM: FIXME: Get contentmask from Game VM! AAS_ShowAreaPolygons(reach->areanum, 5, qtrue); //AAS_ShowArea(reach->areanum, qtrue); AAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); // if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP || (reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) { AAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //set the velocity VectorScale(dir, speed, velocity); //set the command movement VectorClear(cmdmove); cmdmove[2] = aassettings.phys_jumpvel; // AAS_PredictPlayerMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 3, 30, 0.1f, SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue, contentmask); // if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) { AAS_JumpReachRunStart(reach, dir, contentmask); AAS_DrawCross(dir, 4, LINECOLOR_BLUE); } //end if } //end if else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) { zvel = AAS_RocketJumpZVelocity(reach->start, contentmask); AAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //get command movement VectorScale(dir, speed, cmdmove); VectorSet(velocity, 0, 0, zvel); // AAS_PredictPlayerMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue, contentmask); } //end else if else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) { VectorSet(cmdmove, 0, 0, 0); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //set the velocity //NOTE: the edgenum is the horizontal velocity VectorScale(dir, reach->edgenum, velocity); //NOTE: the facenum is the Z velocity velocity[2] = reach->facenum; // AAS_PredictPlayerMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue, contentmask); } //end else if } //end of the function AAS_ShowReachability
/* ============= DeformText Change a polygon into a bunch of text polygons ============= */ void DeformText(const char *text) { int i; vec3_t origin, width, height; int len; int ch; byte color[4]; float bottom, top; vec3_t mid; int row, col; float frow, fcol, size; height[0] = 0; height[1] = 0; height[2] = -1; CrossProduct(tess.normal[0].v, height, width); // find the midpoint of the box VectorClear(mid); bottom = 999999; top = -999999; for (i = 0 ; i < 4 ; i++) { VectorAdd(tess.xyz[i].v, mid, mid); if (tess.xyz[i].v[2] < bottom) { bottom = tess.xyz[i].v[2]; } if (tess.xyz[i].v[2] > top) { top = tess.xyz[i].v[2]; } } VectorScale(mid, 0.25f, origin); // determine the individual character size height[0] = 0; height[1] = 0; height[2] = (top - bottom) * 0.5f; VectorScale(width, height[2] * -0.75f, width); // determine the starting position len = strlen(text); VectorMA(origin, (len - 1), width, origin); // clear the shader indexes tess.numIndexes = 0; tess.numVertexes = 0; color[0] = color[1] = color[2] = color[3] = 255; // draw each character for (i = 0 ; i < len ; i++) { ch = text[i]; ch &= 255; if (ch != ' ') { row = ch >> 4; col = ch & 15; frow = row * 0.0625f; fcol = col * 0.0625f; size = 0.0625f; RB_AddQuadStampExt(origin, width, height, color, fcol, frow, fcol + size, frow + size); } VectorMA(origin, -2, width, origin); }
void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) { edict_t *rocket; edict_t *rocket2; rocket = G_Spawn(); VectorCopy (start, rocket->s.origin); VectorCopy (dir, rocket->movedir); vectoangles (dir, rocket->s.angles); VectorScale (dir, speed, rocket->velocity); rocket->movetype = MOVETYPE_FLYMISSILE; rocket->clipmask = MASK_SHOT; rocket->solid = SOLID_BBOX; rocket->s.effects |= EF_ROCKET; VectorClear (rocket->mins); VectorClear (rocket->maxs); rocket->s.modelindex = gi.modelindex("models/objects/bomb/tris.md2"); rocket->owner = self; rocket->touch = rocket_touch; rocket->nextthink = level.time + 8000/speed; rocket->think = G_FreeEdict; rocket->dmg = damage; rocket->radius_dmg = radius_damage; rocket->dmg_radius = damage_radius; rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); rocket->classname = "rocket"; if (self->client) check_dodge (self, rocket->s.origin, dir, speed); gi.linkentity (rocket); /* // rocket 2 rocket2 = G_Spawn(); VectorCopy (start, rocket2->s.origin); VectorCopy (dir, rocket2->movedir); vectoangles (dir, rocket2->s.angles); VectorScale (dir, speed - 100, rocket2->velocity); rocket2->movetype = MOVETYPE_FLYMISSILE; rocket2->clipmask = MASK_SHOT; rocket2->solid = SOLID_BBOX; rocket2->s.effects |= EF_ROCKET; VectorClear (rocket2->mins); VectorClear (rocket2->maxs); rocket2->s.modelindex = gi.modelindex("models/objects/bomb/tris.md2"); rocket2->owner = self; rocket2->touch = rocket_touch; rocket2->nextthink = level.time + 8000/speed; rocket2->think = G_FreeEdict; rocket2->dmg = damage; rocket2->radius_dmg = radius_damage; rocket2->dmg_radius = damage_radius; rocket2->s.sound = gi.soundindex ("weapons/rockfly.wav"); rocket2->classname = "rocket"; if (self->client) check_dodge (self, rocket2->s.origin, dir, speed); gi.linkentity (rocket2); */ }
//---------------------------------------------------------- void SP_emplaced_eweb( gentity_t *ent ) { char name[] = "models/map_objects/hoth/eweb_model.glm"; ent->svFlags |= SVF_PLAYER_USABLE; ent->contents = CONTENTS_BODY; if ( ent->spawnflags & EMPLACED_INACTIVE ) { ent->svFlags |= SVF_INACTIVE; } VectorSet( ent->mins, -12, -12, -24 ); VectorSet( ent->maxs, 12, 12, 24 ); ent->takedamage = qtrue; if ( ( ent->spawnflags & EWEB_INVULNERABLE )) { ent->flags |= FL_GODMODE; } ent->s.radius = 80; ent->spawnflags |= 4; // deadsolid //ent->e_ThinkFunc = thinkF_NULL; ent->e_PainFunc = painF_eweb_pain; ent->e_DieFunc = dieF_eweb_die; G_EffectIndex( "emplaced/explode" ); G_EffectIndex( "emplaced/dead_smoke" ); G_SoundIndex( "sound/weapons/eweb/eweb_aim.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_dismount.mp3" ); //G_SoundIndex( "sound/weapons/eweb/eweb_empty.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_fire.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_hitplayer.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_hitsurface.wav" ); //G_SoundIndex( "sound/weapons/eweb/eweb_load.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_mount.mp3" ); // Set up our defaults and override with custom amounts as necessary G_SpawnInt( "count", "999", &ent->count ); G_SpawnInt( "health", "250", &ent->health ); G_SpawnInt( "splashDamage", "40", &ent->splashDamage ); G_SpawnInt( "splashRadius", "100", &ent->splashRadius ); G_SpawnFloat( "delay", "200", &ent->random ); // NOTE: spawning into a different field!! G_SpawnFloat( "wait", "800", &ent->wait ); ent->max_health = ent->health; ent->dflags |= DAMAGE_CUSTOM_HUD; // dumb, but we draw a custom hud ent->s.modelindex = G_ModelIndex( name ); ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, name, ent->s.modelindex, NULL_HANDLE, NULL_HANDLE, 0, 0 ); // Activate our tags and bones ent->handLBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*cannonflash" ); //muzzle bolt ent->headBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "cannon_Xrot" ); //for placing the owner relative to rotation ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue ); ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cannon_Yrot", qtrue ); ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cannon_Xrot", qtrue ); gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0); gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0); //gi.G2API_SetBoneAngles( &ent->ghoul2[0], "cannon_Yrot", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL); //set the constraints for this guy as an emplaced weapon, and his constraint angles //ent->s.origin2[0] = 60.0f; //60 degrees in either direction RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN )); ent->s.weapon = WP_EMPLACED_GUN; G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); VectorCopy( ent->s.angles, ent->lastAngles ); // store base angles for later VectorClear( ent->pos1 ); ent->e_UseFunc = useF_eweb_use; ent->bounceCount = 1;//to distinguish it from the emplaced gun gi.linkentity (ent); }
/* =============== SV_CalcViewOffset Auto pitching on slopes? fall from 128: 400 = 160000 fall from 256: 580 = 336400 fall from 384: 720 = 518400 fall from 512: 800 = 640000 fall from 640: 960 = damage = deltavelocity*deltavelocity * 0.0001 =============== */ void SV_CalcViewOffset (edict_t *ent) { float *angles; float bob; float ratio; float delta; vec3_t v; //=================================== // base angles angles = ent->client->ps.kick_angles; // if dead, fix the angle and don't add any kick if (ent->deadflag) { VectorClear (angles); if(ent->flags & FL_SAM_RAIMI) { ent->client->ps.viewangles[ROLL] = 0; ent->client->ps.viewangles[PITCH] = 0; } else { ent->client->ps.viewangles[ROLL] = 40; ent->client->ps.viewangles[PITCH] = -15; } ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; } else { // add angles based on weapon kick VectorCopy (ent->client->kick_angles, angles); // add angles based on damage kick ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME; if (ratio < 0) { ratio = 0; ent->client->v_dmg_pitch = 0; ent->client->v_dmg_roll = 0; } angles[PITCH] += ratio * ent->client->v_dmg_pitch; angles[ROLL] += ratio * ent->client->v_dmg_roll; // add pitch based on fall kick ratio = (ent->client->fall_time - level.time) / FALL_TIME; if (ratio < 0) ratio = 0; angles[PITCH] += ratio * ent->client->fall_value; // add angles based on velocity delta = DotProduct (ent->velocity, forward); angles[PITCH] += delta*run_pitch->value; delta = DotProduct (ent->velocity, right); angles[ROLL] += delta*run_roll->value; // add angles based on bob delta = bobfracsin * bob_pitch->value * xyspeed; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) delta *= 6; // crouching angles[PITCH] += delta; delta = bobfracsin * bob_roll->value * xyspeed; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) delta *= 6; // crouching if (bobcycle & 1) delta = -delta; angles[ROLL] += delta; } //=================================== // base origin VectorClear (v); // add view height v[2] += ent->viewheight; // add fall height ratio = (ent->client->fall_time - level.time) / FALL_TIME; if (ratio < 0) ratio = 0; v[2] -= ratio * ent->client->fall_value * 0.4; // add bob height bob = bobfracsin * xyspeed * bob_up->value; if (bob > 6) bob = 6; //gi.DebugGraph (bob *2, 255); v[2] += bob; // add kick offset VectorAdd (v, ent->client->kick_origin, v); // absolutely bound offsets // so the view can never be outside the player box if (v[0] < -14) v[0] = -14; else if (v[0] > 14) v[0] = 14; if (v[1] < -14) v[1] = -14; else if (v[1] > 14) v[1] = 14; if (v[2] < -22) v[2] = -22; else if (v[2] > 30) v[2] = 30; VectorCopy (v, ent->client->ps.viewoffset); }
/* ============== RE_BuildSkeleton ============== */ int RE_BuildSkeleton(refSkeleton_t * skel, qhandle_t hAnim, int startFrame, int endFrame, float frac, qboolean clearOrigin) { skelAnimation_t *skelAnim; skelAnim = R_GetAnimationByHandle(hAnim); if(skelAnim->type == AT_MD5 && skelAnim->md5) { int i; md5Animation_t *anim; md5Channel_t *channel; md5Frame_t *newFrame, *oldFrame; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; int componentsApplied; anim = skelAnim->md5; // Validate the frames so there is no chance of a crash. // This will write directly into the entity structure, so // when the surfaces are rendered, they don't need to be // range checked again. /* if((startFrame >= anim->numFrames) || (startFrame < 0) || (endFrame >= anim->numFrames) || (endFrame < 0)) { ri.Printf(PRINT_DEVELOPER, "RE_BuildSkeleton: no such frame %d to %d for '%s'\n", startFrame, endFrame, anim->name); //startFrame = 0; //endFrame = 0; } */ Q_clamp(startFrame, 0, anim->numFrames - 1); Q_clamp(endFrame, 0, anim->numFrames - 1); // compute frame pointers oldFrame = &anim->frames[startFrame]; newFrame = &anim->frames[endFrame]; // calculate a bounding box in the current coordinate system for(i = 0; i < 3; i++) { skel->bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; skel->bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; } for(i = 0, channel = anim->channels; i < anim->numChannels; i++, channel++) { // set baseframe values VectorCopy(channel->baseOrigin, newOrigin); VectorCopy(channel->baseOrigin, oldOrigin); QuatCopy(channel->baseQuat, newQuat); QuatCopy(channel->baseQuat, oldQuat); componentsApplied = 0; // update tranlation bits if(channel->componentsBits & COMPONENT_BIT_TX) { oldOrigin[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_TY) { oldOrigin[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_TZ) { oldOrigin[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; newOrigin[2] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } // update quaternion rotation bits if(channel->componentsBits & COMPONENT_BIT_QX) { ((vec_t *) oldQuat)[0] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[0] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_QY) { ((vec_t *) oldQuat)[1] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[1] = newFrame->components[channel->componentsOffset + componentsApplied]; componentsApplied++; } if(channel->componentsBits & COMPONENT_BIT_QZ) { ((vec_t *) oldQuat)[2] = oldFrame->components[channel->componentsOffset + componentsApplied]; ((vec_t *) newQuat)[2] = newFrame->components[channel->componentsOffset + componentsApplied]; } QuatCalcW(oldQuat); QuatNormalize(oldQuat); QuatCalcW(newQuat); QuatNormalize(newQuat); #if 1 VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); QuatSlerp(oldQuat, newQuat, frac, lerpedQuat); #else VectorCopy(newOrigin, lerpedOrigin); QuatCopy(newQuat, lerpedQuat); #endif // copy lerped information to the bone + extra data skel->bones[i].parentIndex = channel->parentIndex; if(channel->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } QuatCopy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, channel->name, sizeof(skel->bones[i].name)); #endif } skel->numBones = anim->numChannels; skel->type = SK_RELATIVE; return qtrue; } else if(skelAnim->type == AT_PSA && skelAnim->psa) { int i; psaAnimation_t *anim; axAnimationKey_t *newKey, *oldKey; axReferenceBone_t *refBone; vec3_t newOrigin, oldOrigin, lerpedOrigin; quat_t newQuat, oldQuat, lerpedQuat; refSkeleton_t skeleton; anim = skelAnim->psa; Q_clamp(startFrame, 0, anim->info.numRawFrames - 1); Q_clamp(endFrame, 0, anim->info.numRawFrames - 1); ClearBounds(skel->bounds[0], skel->bounds[1]); skel->numBones = anim->info.numBones; for(i = 0, refBone = anim->bones; i < anim->info.numBones; i++, refBone++) { oldKey = &anim->keys[startFrame * anim->info.numBones + i]; newKey = &anim->keys[endFrame * anim->info.numBones + i]; VectorCopy(newKey->position, newOrigin); VectorCopy(oldKey->position, oldOrigin); QuatCopy(newKey->quat, newQuat); QuatCopy(oldKey->quat, oldQuat); //QuatCalcW(oldQuat); //QuatNormalize(oldQuat); //QuatCalcW(newQuat); //QuatNormalize(newQuat); VectorLerp(oldOrigin, newOrigin, frac, lerpedOrigin); QuatSlerp(oldQuat, newQuat, frac, lerpedQuat); // copy lerped information to the bone + extra data skel->bones[i].parentIndex = refBone->parentIndex; if(refBone->parentIndex < 0 && clearOrigin) { VectorClear(skel->bones[i].origin); QuatClear(skel->bones[i].rotation); // move bounding box back VectorSubtract(skel->bounds[0], lerpedOrigin, skel->bounds[0]); VectorSubtract(skel->bounds[1], lerpedOrigin, skel->bounds[1]); } else { VectorCopy(lerpedOrigin, skel->bones[i].origin); } QuatCopy(lerpedQuat, skel->bones[i].rotation); #if defined(REFBONE_NAMES) Q_strncpyz(skel->bones[i].name, refBone->name, sizeof(skel->bones[i].name)); #endif // calculate absolute values for the bounding box approximation VectorCopy(skel->bones[i].origin, skeleton.bones[i].origin); QuatCopy(skel->bones[i].rotation, skeleton.bones[i].rotation); if(refBone->parentIndex >= 0) { vec3_t rotated; quat_t quat; refBone_t *parent; refBone_t *bone; bone = &skeleton.bones[i]; parent = &skeleton.bones[refBone->parentIndex]; QuatTransformVector(parent->rotation, bone->origin, rotated); VectorAdd(parent->origin, rotated, bone->origin); QuatMultiply1(parent->rotation, bone->rotation, quat); QuatCopy(quat, bone->rotation); AddPointToBounds(bone->origin, skel->bounds[0], skel->bounds[1]); } } skel->numBones = anim->info.numBones; skel->type = SK_RELATIVE; return qtrue; } //ri.Printf(PRINT_WARNING, "RE_BuildSkeleton: bad animation '%s' with handle %i\n", anim->name, hAnim); // FIXME: clear existing bones and bounds? return qfalse; }
/* =============== P_DamageFeedback Handles color blends and view kicks =============== */ void P_DamageFeedback (edict_t *player) { gclient_t *client; float side; float realcount, count, kick; vec3_t v; int r, l; static vec3_t power_color = {0.0, 1.0, 0.0}; static vec3_t acolor = {1.0, 1.0, 1.0}; static vec3_t bcolor = {1.0, 0.0, 0.0}; client = player->client; // flash the backgrounds behind the status numbers client->ps.stats[STAT_FLASHES] = 0; if (client->damage_blood) client->ps.stats[STAT_FLASHES] |= 1; if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) client->ps.stats[STAT_FLASHES] |= 2; // total points of damage shot at the player this frame count = (client->damage_blood + client->damage_armor + client->damage_parmor); if (count == 0) return; // didn't take any damage // start a pain animation if still in the player model if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255) { static int i; client->anim_priority = ANIM_PAIN; if (client->ps.pmove.pm_flags & PMF_DUCKED) { player->s.frame = FRAME_crpain1-1; client->anim_end = FRAME_crpain4; } else { i = (i+1)%3; switch (i) { case 0: player->s.frame = FRAME_pain101-1; client->anim_end = FRAME_pain104; break; case 1: player->s.frame = FRAME_pain201-1; client->anim_end = FRAME_pain204; break; case 2: player->s.frame = FRAME_pain301-1; client->anim_end = FRAME_pain304; break; } } } realcount = count; if (count < 10) count = 10; // always make a visible effect // play an apropriate pain sound if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) { r = 1 + (rand()&1); player->pain_debounce_time = level.time + 0.7; if (player->health < 25) l = 25; else if (player->health < 50) l = 50; else if (player->health < 75) l = 75; else l = 100; gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0); } // the total alpha of the blend is always proportional to count if (client->damage_alpha < 0) client->damage_alpha = 0; client->damage_alpha += count*0.01; if (client->damage_alpha < 0.2) client->damage_alpha = 0.2; if (client->damage_alpha > 0.6) client->damage_alpha = 0.6; // don't go too saturated // the color of the blend will vary based on how much was absorbed // by different armors VectorClear (v); if (client->damage_parmor) VectorMA (v, (float)client->damage_parmor/realcount, power_color, v); if (client->damage_armor) VectorMA (v, (float)client->damage_armor/realcount, acolor, v); if (client->damage_blood) VectorMA (v, (float)client->damage_blood/realcount, bcolor, v); VectorCopy (v, client->damage_blend); // // calculate view angle kicks // kick = abs(client->damage_knockback); if (kick && player->health > 0) // kick of 0 means no view adjust at all { kick = kick * 100 / player->health; if (kick < count*0.5) kick = count*0.5; if (kick > 50) kick = 50; VectorSubtract (client->damage_from, player->s.origin, v); VectorNormalize (v); side = DotProduct (v, right); client->v_dmg_roll = kick*side*0.3; side = -DotProduct (v, forward); client->v_dmg_pitch = kick*side*0.3; client->v_dmg_time = level.time + DAMAGE_TIME; } // // clear totals // client->damage_blood = 0; client->damage_armor = 0; client->damage_parmor = 0; client->damage_knockback = 0; }
/* =============== CL_DiminishingTrail =============== */ void CL_DiminishingTrail(vec3_t start, vec3_t end, centity_t *old, int flags){ vec3_t move; vec3_t vec; float len; int j; cparticle_t *p; float dec; float orgscale; float velscale; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); dec = 0.5; VectorScale(vec, dec, vec); if(old->trailcount > 900){ orgscale = 4; velscale = 15; } else if(old->trailcount > 800){ orgscale = 2; velscale = 10; } else { orgscale = 1; velscale = 5; } while(len > 0){ len -= dec; if(!free_particles) return; // drop less particles as it flies if((rand()&1023) < old->trailcount){ p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = cl.time; if(flags & EF_GIB){ p->alpha = 1.0; p->alphavel = -1.0 /(1 + frand() * 0.4); p->color = 0xe8 +(rand() & 7); for(j = 0; j < 3; j++){ p->org[j] = move[j] + crand() * orgscale; p->vel[j] = crand() * velscale; p->accel[j] = 0; } p->vel[2] -= PARTICLE_GRAVITY; } else if(flags & EF_GREENGIB){ p->alpha = 1.0; p->alphavel = -1.0 /(1 + frand() * 0.4); p->color = 0xdb +(rand() & 7); for(j = 0; j < 3; j++){ p->org[j] = move[j] + crand() * orgscale; p->vel[j] = crand() * velscale; p->accel[j] = 0; } p->vel[2] -= PARTICLE_GRAVITY; } else { p->alpha = 1.0; p->alphavel = -1.0 /(1 + frand() * 0.2); p->color = 4 +(rand() & 7); for(j = 0; j < 3; j++){ p->org[j] = move[j] + crand() * orgscale; p->vel[j] = crand() * velscale; } p->accel[2] = 20; } } old->trailcount -= 5; if(old->trailcount < 100) old->trailcount = 100; VectorAdd(move, vec, move); } }
//========================================== // BOT_DMclass_CombatMovement // // NOTE: Very simple for now, just a basic move about avoidance. // Change this routine for more advanced attack movement. //========================================== void BOT_DMclass_CombatMovement( edict_t *self, usercmd_t *ucmd ) { float c; float dist; bool rocket = false; vec3_t away_from_rocket = { 0, 0, 0 }; if( !self->enemy || self->ai->rush_item ) { BOT_DMclass_Move( self, ucmd ); return; } if( self->ai->pers.skillLevel >= 0.25f ) rocket = BOT_DMclass_FindRocket( self, away_from_rocket ); dist = DistanceFast( self->s.origin, self->enemy->s.origin ); c = random(); if( level.time > self->ai->combatmovepush_timeout ) { bool canMOVELEFT, canMOVERIGHT, canMOVEFRONT, canMOVEBACK; canMOVELEFT = AI_CanMove( self, BOT_MOVE_LEFT ); canMOVERIGHT = AI_CanMove( self, BOT_MOVE_RIGHT ); canMOVEFRONT = AI_CanMove( self, BOT_MOVE_FORWARD ); canMOVEBACK = AI_CanMove( self, BOT_MOVE_BACK ); self->ai->combatmovepush_timeout = level.time + AI_COMBATMOVE_TIMEOUT; VectorClear( self->ai->combatmovepushes ); if( rocket ) { //VectorScale(away_from_rocket,1,self->ai->combatmovepushes); if( away_from_rocket[0] ) { if( ( away_from_rocket[0] < 0 ) && canMOVEBACK ) self->ai->combatmovepushes[0] = -1; else if( ( away_from_rocket[0] > 0 ) && canMOVEFRONT ) self->ai->combatmovepushes[0] = 1; } if( away_from_rocket[1] ) { if( ( away_from_rocket[1] < 0 ) && canMOVELEFT ) self->ai->combatmovepushes[1] = -1; else if( ( away_from_rocket[1] > 0 ) && canMOVERIGHT ) self->ai->combatmovepushes[1] = 1; } ucmd->buttons |= BUTTON_SPECIAL; } else if( dist < 150 ) // range = AIWEAP_MELEE_RANGE; { if( self->s.weapon == WEAP_GUNBLADE ) // go into him! { ucmd->buttons &= ~BUTTON_ATTACK; // remove pressing fire if( canMOVEFRONT ) // move to your enemy self->ai->combatmovepushes[0] = 1; else if( c <= 0.5 && canMOVELEFT ) self->ai->combatmovepushes[1] = -1; else if( canMOVERIGHT ) self->ai->combatmovepushes[1] = 1; } else { //priorize sides if( canMOVELEFT || canMOVERIGHT ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } if( c < 0.3 && canMOVEBACK ) self->ai->combatmovepushes[0] = -1; } } else if( dist < 500 ) //AIWEAP_SHORT_RANGE limit is Grenade Laucher range { if( canMOVELEFT || canMOVERIGHT ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } if( c < 0.3 && canMOVEFRONT ) { self->ai->combatmovepushes[0] = 1; } } else if( dist < 900 ) { if( canMOVELEFT || canMOVERIGHT ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } } else //range = AIWEAP_LONG_RANGE; { if( c < 0.75 && ( canMOVELEFT || canMOVERIGHT ) ) { if( canMOVELEFT && canMOVERIGHT ) { self->ai->combatmovepushes[1] = c < 0.5 ? -1 : 1; } else if( canMOVELEFT ) { self->ai->combatmovepushes[1] = -1; } else { self->ai->combatmovepushes[1] = 1; } } } } if( !rocket && ( self->health < 25 || ( dist >= 500 && c < 0.2 ) || ( dist >= 1000 && c < 0.5 ) ) ) { BOT_DMclass_Move( self, ucmd ); } if( !self->ai->camp_item ) { ucmd->forwardmove = self->ai->combatmovepushes[0]; } ucmd->sidemove = self->ai->combatmovepushes[1]; ucmd->upmove = self->ai->combatmovepushes[2]; }
/* =============== CL_RailTrail =============== */ void CL_RailTrail(vec3_t start, vec3_t end){ vec3_t move; vec3_t vec; float len; int j; cparticle_t *p; float dec; vec3_t right, up; int i; float d, c, s; vec3_t dir; byte clr = 0x74; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); MakeNormalVectors(vec, right, up); for(i = 0; i < len; i++){ if(!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; VectorClear(p->accel); d = i * 0.1; c = cos(d); s = sin(d); VectorScale(right, c, dir); VectorMA(dir, s, up, dir); p->alpha = 1.0; p->alphavel = -1.0 /(1 + frand() * 0.2); p->color = clr +(rand() & 7); for(j = 0; j < 3; j++){ p->org[j] = move[j] + dir[j] * 3; p->vel[j] = dir[j] * 6; } VectorAdd(move, vec, move); } dec = 0.75; VectorScale(vec, dec, vec); VectorCopy(start, move); while(len > 0){ len -= dec; if(!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; VectorClear(p->accel); p->alpha = 1.0; p->alphavel = -1.0 /(0.6 + frand() * 0.2); p->color = 0x0 +(rand() & 15); for(j = 0; j < 3; j++){ p->org[j] = move[j] + crand() * 3; p->vel[j] = crand() * 3; p->accel[j] = 0; } VectorAdd(move, vec, move); } }
/* * G_ClientRespawn */ void G_ClientRespawn( edict_t *self, bool ghost ) { int i; edict_t *spawnpoint; vec3_t hull_mins, hull_maxs; vec3_t spawn_origin, spawn_angles; gclient_t *client; int old_team; G_SpawnQueue_RemoveClient( self ); self->r.svflags &= ~SVF_NOCLIENT; //if invalid be spectator if( self->r.client->team < 0 || self->r.client->team >= GS_MAX_TEAMS ) self->r.client->team = TEAM_SPECTATOR; // force ghost always to true when in spectator team if( self->r.client->team == TEAM_SPECTATOR ) ghost = true; old_team = self->s.team; if( self->r.client->teamstate.is_coach ) ghost = true; GClip_UnlinkEntity( self ); client = self->r.client; memset( &client->resp, 0, sizeof( client->resp ) ); memset( &client->ps, 0, sizeof( client->ps ) ); client->resp.timeStamp = level.time; client->resp.gunbladeChargeTimeStamp = level.time; client->ps.playerNum = PLAYERNUM( self ); // clear entity values memset( &self->snap, 0, sizeof( self->snap ) ); memset( &self->s, 0, sizeof( self->s ) ); memset( &self->olds, 0, sizeof( self->olds ) ); memset( &self->invpak, 0, sizeof( self->invpak ) ); self->s.number = self->olds.number = ENTNUM( self ); // relink client struct self->r.client = &game.clients[PLAYERNUM( self )]; // update team self->s.team = client->team; self->deadflag = DEAD_NO; self->s.type = ET_PLAYER; self->groundentity = NULL; self->takedamage = DAMAGE_AIM; self->think = player_think; self->pain = player_pain; self->die = player_die; self->viewheight = playerbox_stand_viewheight; self->r.inuse = true; self->mass = PLAYER_MASS; self->air_finished = level.time + ( 12 * 1000 ); self->r.clipmask = MASK_PLAYERSOLID; self->waterlevel = 0; self->watertype = 0; self->flags &= ~FL_NO_KNOCKBACK; self->r.svflags &= ~SVF_CORPSE; self->enemy = NULL; self->r.owner = NULL; self->max_health = 100; self->health = self->max_health; if( AI_GetType( self->ai ) == AI_ISBOT ) { self->think = NULL; self->classname = "bot"; } else if( self->r.svflags & SVF_FAKECLIENT ) self->classname = "fakeclient"; else self->classname = "player"; VectorCopy( playerbox_stand_mins, self->r.mins ); VectorCopy( playerbox_stand_maxs, self->r.maxs ); VectorClear( self->velocity ); VectorClear( self->avelocity ); VectorCopy( self->r.mins, hull_mins ); VectorCopy( self->r.maxs, hull_maxs ); trap_CM_RoundUpToHullSize( hull_mins, hull_maxs, NULL ); if( self->r.maxs[2] > hull_maxs[2] ) self->viewheight -= (self->r.maxs[2] - hull_maxs[2]); client->ps.POVnum = ENTNUM( self ); // set movement info client->ps.pmove.stats[PM_STAT_MAXSPEED] = (short)DEFAULT_PLAYERSPEED; client->ps.pmove.stats[PM_STAT_JUMPSPEED] = (short)DEFAULT_JUMPSPEED; client->ps.pmove.stats[PM_STAT_DASHSPEED] = (short)DEFAULT_DASHSPEED; if( ghost ) { self->r.solid = SOLID_NOT; self->movetype = MOVETYPE_NOCLIP; if( self->s.team == TEAM_SPECTATOR ) self->r.svflags |= SVF_NOCLIENT; } else { self->r.client->resp.takeStun = true; self->r.solid = SOLID_YES; self->movetype = MOVETYPE_PLAYER; client->ps.pmove.stats[PM_STAT_FEATURES] = static_cast<unsigned short>(PMFEAT_DEFAULT); if( !g_allow_bunny->integer ) client->ps.pmove.stats[PM_STAT_FEATURES] &= ~( PMFEAT_AIRCONTROL|PMFEAT_FWDBUNNY ); } ClientUserinfoChanged( self, client->userinfo ); if( old_team != self->s.team ) G_Teams_UpdateMembersList(); SelectSpawnPoint( self, &spawnpoint, spawn_origin, spawn_angles ); VectorCopy( spawn_origin, client->ps.pmove.origin ); VectorCopy( spawn_origin, self->s.origin ); VectorCopy( self->s.origin, self->s.old_origin ); // set angles self->s.angles[PITCH] = 0; self->s.angles[YAW] = anglemod( spawn_angles[YAW] ); self->s.angles[ROLL] = 0; VectorCopy( self->s.angles, client->ps.viewangles ); // set the delta angle for( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; // don't put spectators in the game if( !ghost ) { if( KillBox( self ) ) { } } self->s.attenuation = ATTN_NORM; self->s.teleported = true; // hold in place briefly client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; client->ps.pmove.pm_time = 14; client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] = CLIENT_RESPAWN_FREEZE_DELAY; client->ps.pmove.stats[PM_STAT_NOAUTOATTACK] = 1000; // set race stats to invisible client->ps.stats[STAT_TIME_SELF] = STAT_NOTSET; client->ps.stats[STAT_TIME_BEST] = STAT_NOTSET; client->ps.stats[STAT_TIME_RECORD] = STAT_NOTSET; client->ps.stats[STAT_TIME_ALPHA] = STAT_NOTSET; client->ps.stats[STAT_TIME_BETA] = STAT_NOTSET; BOT_Respawn( self ); self->r.client->level.respawnCount++; G_UseTargets( spawnpoint, self ); GClip_LinkEntity( self ); // let the gametypes perform their changes if( game.asEngine != NULL ) GT_asCallPlayerRespawn( self, old_team, self->s.team ); else G_Gametype_GENERIC_ClientRespawn( self, old_team, self->s.team ); }
// RAFAEL void CL_TrapParticles(entity_t *ent){ vec3_t move; vec3_t vec; vec3_t start, end; float len; int j; cparticle_t *p; int dec; ent->origin[2] -= 14; VectorCopy(ent->origin, start); VectorCopy(ent->origin, end); end[2] += 64; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); // FIXME: this is a really silly way to have a loop while(len > 0){ len -= dec; if(!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = cl.time; p->alpha = 1.0; p->alphavel = -1.0 /(0.3 + frand() * 0.2); p->color = 0xe0; for(j = 0; j < 3; j++){ p->org[j] = move[j] + crand(); p->vel[j] = crand() * 15; p->accel[j] = 0; } p->accel[2] = PARTICLE_GRAVITY; VectorAdd(move, vec, move); } { int i, j, k; cparticle_t *p; float vel; vec3_t dir; vec3_t org; ent->origin[2] += 14; VectorCopy(ent->origin, org); for(i = -2; i <= 2; i += 4) for(j = -2; j <= 2; j += 4) for(k = -2; k <= 4; k += 4){ if(!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; p->color = 0xe0 +(rand() & 3); p->alpha = 1.0; p->alphavel = -1.0 /(0.3 +(rand() & 7) * 0.02); p->org[0] = org[0] + i +((rand() & 23) * crand()); p->org[1] = org[1] + j +((rand() & 23) * crand()); p->org[2] = org[2] + k +((rand() & 23) * crand()); dir[0] = j * 8; dir[1] = i * 8; dir[2] = k * 8; VectorNormalize(dir); vel = 50 +(rand() & 63); VectorScale(dir, vel, p->vel); p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; } } }
/* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. If "g_synchronousClients 1" is set, this will be called exactly once for each server frame, which makes for smooth demo recording. ============== */ void ClientThink_real( gentity_t *ent ) { gclient_t *client; pmove_t pm; int oldEventSequence; int msec; int i; usercmd_t *ucmd; client = ent->client; // don't think if the client is not yet connected (and thus not yet spawned in) if (client->pers.connected != CON_CONNECTED) { return; } // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; // sanity check the command time to prevent speedup cheating if ( ucmd->serverTime > level.time + 200 ) { ucmd->serverTime = level.time + 200; // G_Printf("serverTime <<<<<\n" ); } if ( ucmd->serverTime < level.time - 1000 ) { ucmd->serverTime = level.time - 1000; // G_Printf("serverTime >>>>>\n" ); } msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) { return; } if ( msec > 200 ) { msec = 200; } if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); } if ( pmove_fixed.integer || client->pers.pmoveFixed ) { ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; //if (ucmd->serverTime - client->ps.commandTime <= 0) // return; } // // check for exiting intermission // if ( level.intermissiontime ) { ClientIntermissionThink( client ); return; } // spectators don't do much if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { return; } SpectatorThink( ent, ucmd ); return; } if (ent && ent->client && (ent->client->ps.eFlags & EF_INVULNERABLE)) { if (ent->client->invulnerableTimer <= level.time) { ent->client->ps.eFlags &= ~EF_INVULNERABLE; } } // check for inactivity timer, but never drop the local client of a non-dedicated server if ( !ClientInactivityTimer( client ) ) { return; } // clear the rewards if time if ( level.time > client->rewardTime ) { client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); } if ( client->noclip ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.eFlags & EF_DISINTEGRATION ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) { client->ps.pm_type = PM_DEAD; } else { if (client->ps.forceGripChangeMovetype) { client->ps.pm_type = client->ps.forceGripChangeMovetype; } else { client->ps.pm_type = PM_NORMAL; } } client->ps.gravity = g_gravity.value; // set speed client->ps.speed = g_speed.value; client->ps.basespeed = g_speed.value; if (ent->client->ps.duelInProgress) { gentity_t *duelAgainst = &g_entities[ent->client->ps.duelIndex]; //Keep the time updated, so once this duel ends this player can't engage in a duel for another //10 seconds. This will give other people a chance to engage in duels in case this player wants //to engage again right after he's done fighting and someone else is waiting. ent->client->ps.fd.privateDuelTime = level.time + 10000; if (ent->client->ps.duelTime < level.time) { //Bring out the sabers if (ent->client->ps.weapon == WP_SABER && ent->client->ps.saberHolstered && ent->client->ps.duelTime) { if (!saberOffSound || !saberOnSound) { saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav"); saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav"); } ent->client->ps.saberHolstered = qfalse; G_Sound(ent, CHAN_AUTO, saberOnSound); G_AddEvent(ent, EV_PRIVATE_DUEL, 2); ent->client->ps.duelTime = 0; } if (duelAgainst && duelAgainst->client && duelAgainst->inuse && duelAgainst->client->ps.weapon == WP_SABER && duelAgainst->client->ps.saberHolstered && duelAgainst->client->ps.duelTime) { if (!saberOffSound || !saberOnSound) { saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav"); saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav"); } duelAgainst->client->ps.saberHolstered = qfalse; G_Sound(duelAgainst, CHAN_AUTO, saberOnSound); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 2); duelAgainst->client->ps.duelTime = 0; } } else { client->ps.speed = 0; client->ps.basespeed = 0; ucmd->forwardmove = 0; ucmd->rightmove = 0; ucmd->upmove = 0; } if (!duelAgainst || !duelAgainst->client || !duelAgainst->inuse || duelAgainst->client->ps.duelIndex != ent->s.number) { ent->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); } else if (duelAgainst->health < 1 || duelAgainst->client->ps.stats[STAT_HEALTH] < 1) { ent->client->ps.duelInProgress = 0; duelAgainst->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0); //Winner gets full health.. providing he's still alive if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0) { if (ent->health < ent->client->ps.stats[STAT_MAX_HEALTH]) { ent->client->ps.stats[STAT_HEALTH] = ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } if (g_spawnInvulnerability.integer) { ent->client->ps.eFlags |= EF_INVULNERABLE; ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer; } } /* trap_SendServerCommand( ent-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) ); trap_SendServerCommand( duelAgainst-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) ); */ //Private duel announcements are now made globally because we only want one duel at a time. if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0) { trap_SendServerCommand( -1, va("cp \"%s %s %s!\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER"), duelAgainst->client->pers.netname) ); } else { //it was a draw, because we both managed to die in the same frame trap_SendServerCommand( -1, va("cp \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELTIE")) ); } } else { vec3_t vSub; float subLen = 0; VectorSubtract(ent->client->ps.origin, duelAgainst->client->ps.origin, vSub); subLen = VectorLength(vSub); if (subLen >= 1024) { ent->client->ps.duelInProgress = 0; duelAgainst->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELSTOP")) ); } } } /* if ( client->ps.powerups[PW_HASTE] ) { client->ps.speed *= 1.3; } */ if (client->ps.usingATST && ent->health > 0) { //we have special shot clip boxes as an ATST ent->r.contents |= CONTENTS_NOSHOT; ATST_ManageDamageBoxes(ent); } else { ent->r.contents &= ~CONTENTS_NOSHOT; client->damageBoxHandle_Head = 0; client->damageBoxHandle_RLeg = 0; client->damageBoxHandle_LLeg = 0; } //rww - moved this stuff into the pmove code so that it's predicted properly //BG_AdjustClientSpeed(&client->ps, &client->pers.cmd, level.time); // set up for pmove oldEventSequence = client->ps.eventSequence; memset (&pm, 0, sizeof(pm)); if ( ent->flags & FL_FORCE_GESTURE ) { ent->flags &= ~FL_FORCE_GESTURE; ent->client->pers.cmd.buttons |= BUTTON_GESTURE; } if (ent->client && ent->client->ps.fallingToDeath && (level.time - FALL_FADE_TIME) > ent->client->ps.fallingToDeath) { //die! player_die(ent, ent, ent, 100000, MOD_FALLING); respawn(ent); ent->client->ps.fallingToDeath = 0; G_MuteSound(ent->s.number, CHAN_VOICE); //stop screaming, because you are dead! } if (ent->client->ps.otherKillerTime > level.time && ent->client->ps.groundEntityNum != ENTITYNUM_NONE && ent->client->ps.otherKillerDebounceTime < level.time) { ent->client->ps.otherKillerTime = 0; ent->client->ps.otherKiller = ENTITYNUM_NONE; } else if (ent->client->ps.otherKillerTime > level.time && ent->client->ps.groundEntityNum == ENTITYNUM_NONE) { if (ent->client->ps.otherKillerDebounceTime < (level.time + 100)) { ent->client->ps.otherKillerDebounceTime = level.time + 100; } } // WP_ForcePowersUpdate( ent, msec, ucmd); //update any active force powers // WP_SaberPositionUpdate(ent, ucmd); //check the server-side saber point, do apprioriate server-side actions (effects are cs-only) if ((ent->client->pers.cmd.buttons & BUTTON_USE) && ent->client->ps.useDelay < level.time) { TryUse(ent); ent->client->ps.useDelay = level.time + 100; } pm.ps = &client->ps; pm.cmd = *ucmd; if ( pm.ps->pm_type == PM_DEAD ) { pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else if ( ent->r.svFlags & SVF_BOT ) { pm.tracemask = MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP; } else { pm.tracemask = MASK_PLAYERSOLID; } pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.debugLevel = g_debugMove.integer; pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0; pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; pm.pmove_msec = pmove_msec.integer; pm.animations = bgGlobalAnimations;//NULL; pm.gametype = g_gametype.integer; VectorCopy( client->ps.origin, client->oldOrigin ); if (level.intermissionQueued != 0 && g_singlePlayer.integer) { if ( level.time - level.intermissionQueued >= 1000 ) { pm.cmd.buttons = 0; pm.cmd.forwardmove = 0; pm.cmd.rightmove = 0; pm.cmd.upmove = 0; if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) { trap_SendConsoleCommand( EXEC_APPEND, "centerview\n"); } ent->client->ps.pm_type = PM_SPINTERMISSION; } } for ( i = 0 ; i < MAX_CLIENTS ; i++ ) { if (g_entities[i].inuse && g_entities[i].client) { pm.bgClients[i] = &g_entities[i].client->ps; } } if (ent->client->ps.saberLockTime > level.time) { gentity_t *blockOpp = &g_entities[ent->client->ps.saberLockEnemy]; if (blockOpp && blockOpp->inuse && blockOpp->client) { vec3_t lockDir, lockAng; //VectorClear( ent->client->ps.velocity ); VectorSubtract( blockOpp->r.currentOrigin, ent->r.currentOrigin, lockDir ); //lockAng[YAW] = vectoyaw( defDir ); vectoangles(lockDir, lockAng); SetClientViewAngle( ent, lockAng ); } if ( ( ent->client->buttons & BUTTON_ATTACK ) && ! ( ent->client->oldbuttons & BUTTON_ATTACK ) ) { ent->client->ps.saberLockHits++; } if (ent->client->ps.saberLockHits > 2) { if (!ent->client->ps.saberLockAdvance) { ent->client->ps.saberLockHits -= 3; } ent->client->ps.saberLockAdvance = qtrue; } } else { ent->client->ps.saberLockFrame = 0; //check for taunt if ( (pm.cmd.generic_cmd == GENCMD_ENGAGE_DUEL) && (g_gametype.integer == GT_TOURNAMENT) ) {//already in a duel, make it a taunt command pm.cmd.buttons |= BUTTON_GESTURE; } } Pmove (&pm); if (pm.checkDuelLoss) { if (pm.checkDuelLoss > 0 && pm.checkDuelLoss <= MAX_CLIENTS) { gentity_t *clientLost = &g_entities[pm.checkDuelLoss-1]; if (clientLost && clientLost->inuse && clientLost->client && Q_irand(0, 40) > clientLost->health) { vec3_t attDir; VectorSubtract(ent->client->ps.origin, clientLost->client->ps.origin, attDir); VectorNormalize(attDir); VectorClear(clientLost->client->ps.velocity); clientLost->client->ps.forceHandExtend = HANDEXTEND_NONE; clientLost->client->ps.forceHandExtendTime = 0; gGAvoidDismember = 1; G_Damage(clientLost, ent, ent, attDir, clientLost->client->ps.origin, 9999, DAMAGE_NO_PROTECTION, MOD_SABER); if (clientLost->health < 1) { gGAvoidDismember = 2; G_CheckForDismemberment(clientLost, clientLost->client->ps.origin, 999, (clientLost->client->ps.legsAnim&~ANIM_TOGGLEBIT)); } gGAvoidDismember = 0; } } pm.checkDuelLoss = 0; } switch(pm.cmd.generic_cmd) { case 0: break; case GENCMD_SABERSWITCH: Cmd_ToggleSaber_f(ent); break; case GENCMD_ENGAGE_DUEL: if ( g_gametype.integer == GT_TOURNAMENT ) {//already in a duel, made it a taunt command } else { Cmd_EngageDuel_f(ent); } break; case GENCMD_FORCE_HEAL: ForceHeal(ent); break; case GENCMD_FORCE_SPEED: ForceSpeed(ent, 0); break; case GENCMD_FORCE_THROW: ForceThrow(ent, qfalse); break; case GENCMD_FORCE_PULL: ForceThrow(ent, qtrue); break; case GENCMD_FORCE_DISTRACT: ForceTelepathy(ent); break; case GENCMD_FORCE_RAGE: ForceRage(ent); break; case GENCMD_FORCE_PROTECT: ForceProtect(ent); break; case GENCMD_FORCE_ABSORB: ForceAbsorb(ent); break; case GENCMD_FORCE_HEALOTHER: ForceTeamHeal(ent); break; case GENCMD_FORCE_FORCEPOWEROTHER: ForceTeamForceReplenish(ent); break; case GENCMD_FORCE_SEEING: ForceSeeing(ent); break; case GENCMD_USE_SEEKER: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SEEKER)) && G_ItemUsable(&ent->client->ps, HI_SEEKER) ) { ItemUse_Seeker(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_SEEKER, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SEEKER); } break; case GENCMD_USE_FIELD: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SHIELD)) && G_ItemUsable(&ent->client->ps, HI_SHIELD) ) { ItemUse_Shield(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_SHIELD, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SHIELD); } break; case GENCMD_USE_BACTA: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC)) && G_ItemUsable(&ent->client->ps, HI_MEDPAC) ) { ItemUse_MedPack(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_MEDPAC, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_MEDPAC); } break; case GENCMD_USE_ELECTROBINOCULARS: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) { ItemUse_Binoculars(ent); if (ent->client->ps.zoomMode == 0) { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); } else { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); } } break; case GENCMD_ZOOM: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) { ItemUse_Binoculars(ent); if (ent->client->ps.zoomMode == 0) { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); } else { G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); } } break; case GENCMD_USE_SENTRY: if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SENTRY_GUN)) && G_ItemUsable(&ent->client->ps, HI_SENTRY_GUN) ) { ItemUse_Sentry(ent); G_AddEvent(ent, EV_USE_ITEM0+HI_SENTRY_GUN, 0); ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SENTRY_GUN); } break; case GENCMD_SABERATTACKCYCLE: Cmd_SaberAttackCycle_f(ent); break; default: break; } // save results of pmove if ( ent->client->ps.eventSequence != oldEventSequence ) { ent->eventTime = level.time; } if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); } else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); } SendPendingPredictableEvents( &ent->client->ps ); if ( !( ent->client->ps.eFlags & EF_FIRING ) ) { client->fireHeld = qfalse; // for grapple } // use the snapped origin for linking so it matches client predicted versions VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); VectorCopy (pm.mins, ent->r.mins); VectorCopy (pm.maxs, ent->r.maxs); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; // execute client events ClientEvents( ent, oldEventSequence ); if ( pm.useEvent ) { //TODO: Use // TryUse( ent ); } // link entity now, after any personal teleporters have been used trap_LinkEntity (ent); if ( !ent->client->noclip ) { G_TouchTriggers( ent ); } // NOTE: now copy the exact origin over otherwise clients can be snapped into solid VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); //test for solid areas in the AAS file // BotTestAAS(ent->r.currentOrigin); // touch other objects ClientImpacts( ent, &pm ); // save results of triggers and client events if (ent->client->ps.eventSequence != oldEventSequence) { ent->eventTime = level.time; } // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; // Did we kick someone in our pmove sequence? if (client->ps.forceKickFlip) { gentity_t *faceKicked = &g_entities[client->ps.forceKickFlip-1]; if (faceKicked && faceKicked->client && (!OnSameTeam(ent, faceKicked) || g_friendlyFire.integer) && (!faceKicked->client->ps.duelInProgress || faceKicked->client->ps.duelIndex == ent->s.number) && (!ent->client->ps.duelInProgress || ent->client->ps.duelIndex == faceKicked->s.number)) { if ( faceKicked && faceKicked->client && faceKicked->health && faceKicked->takedamage ) {//push them away and do pain vec3_t oppDir; int strength = (int)VectorNormalize2( client->ps.velocity, oppDir ); strength *= 0.05; VectorScale( oppDir, -1, oppDir ); G_Damage( faceKicked, ent, ent, oppDir, client->ps.origin, strength, DAMAGE_NO_ARMOR, MOD_MELEE ); if ( faceKicked->client->ps.weapon != WP_SABER || faceKicked->client->ps.fd.saberAnimLevel < FORCE_LEVEL_3 || (!BG_SaberInAttack(faceKicked->client->ps.saberMove) && !PM_SaberInStart(faceKicked->client->ps.saberMove) && !PM_SaberInReturn(faceKicked->client->ps.saberMove) && !PM_SaberInTransition(faceKicked->client->ps.saberMove)) ) { if (faceKicked->health > 0 && faceKicked->client->ps.stats[STAT_HEALTH] > 0 && faceKicked->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN) { if (Q_irand(1, 10) <= 3) { //only actually knock over sometimes, but always do velocity hit faceKicked->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; faceKicked->client->ps.forceHandExtendTime = level.time + 1100; faceKicked->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim } faceKicked->client->ps.otherKiller = ent->s.number; faceKicked->client->ps.otherKillerTime = level.time + 5000; faceKicked->client->ps.otherKillerDebounceTime = level.time + 100; faceKicked->client->ps.velocity[0] = oppDir[0]*(strength*40); faceKicked->client->ps.velocity[1] = oppDir[1]*(strength*40); faceKicked->client->ps.velocity[2] = 200; } } G_Sound( faceKicked, CHAN_AUTO, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) ); } } client->ps.forceKickFlip = 0; } // check for respawning if ( client->ps.stats[STAT_HEALTH] <= 0 ) { // wait for the attack button to be pressed if ( level.time > client->respawnTime && !gDoSlowMoDuel ) { // forcerespawn is to prevent users from waiting out powerups if ( g_forcerespawn.integer > 0 && ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) { respawn( ent ); return; } // pressing attack or use is the normal respawn method if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) { respawn( ent ); } } else if (gDoSlowMoDuel) { client->respawnTime = level.time + 1000; } return; } // perform once-a-second actions ClientTimerActions( ent, msec ); G_UpdateClientBroadcasts ( ent ); }
/* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; vec3_t forward, impactpoint, bouncedir; int eFlags; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } if ( other->takedamage ) { if ( ent->s.weapon != WP_PROX_LAUNCHER ) { if ( other->client && other->client->invulnerabilityTime > level.time ) { // VectorCopy( ent->s.pos.trDelta, forward ); VectorNormalize( forward ); if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { VectorCopy( bouncedir, trace->plane.normal ); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile( ent, trace ); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } // impact damage if (other->takedamage) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; g_entities[ent->r.ownerNum].client->accuracy[ent->s.weapon][1]++; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } } if( ent->s.weapon == WP_PROX_LAUNCHER ) { if( ent->s.pos.trType != TR_GRAVITY ) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if( other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; vectoangles( trace->plane.normal, ent->s.angles ); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->r.mins, -4, -4, -4); VectorSet(ent->r.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } if (!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->client ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; ent->enemy = other; v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->enemy = NULL; } SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin( ent, v ); G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); return; } // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->client ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; g_entities[ent->r.ownerNum].client->accuracy[ent->s.weapon][1]++; } } } trap_LinkEntity( ent ); }
void fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) { edict_t *bolt; trace_t tr; if (!self) { return; } VectorNormalize(dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; /* yes, I know it looks weird that projectiles are deadmonsters what this means is that when prediction is used against the object (blaster/hyperblaster shots), the player won't be solid clipped against the object. Right now trying to run into a firing hyperblaster is very jerky since you are predicted 'against' the shots. */ VectorCopy(start, bolt->s.origin); VectorCopy(start, bolt->s.old_origin); vectoangles(dir, bolt->s.angles); VectorScale(dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_BBOX; bolt->s.effects |= effect; bolt->s.renderfx |= RF_NOSHADOW; VectorClear(bolt->mins); VectorClear(bolt->maxs); bolt->s.modelindex = gi.modelindex("models/objects/laser/tris.md2"); bolt->s.sound = gi.soundindex("misc/lasfly.wav"); bolt->owner = self; bolt->touch = blaster_touch; bolt->nextthink = level.time + 2; bolt->think = G_FreeEdict; bolt->dmg = damage; bolt->classname = "bolt"; if (hyper) { bolt->spawnflags = 1; } gi.linkentity(bolt); if (self->client) { check_dodge(self, bolt->s.origin, dir, speed); } tr = gi.trace(self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA(bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch(bolt, tr.ent, NULL, NULL); } }
void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { edict_t *dest; int i; vec3_t nodeo; if (!other->client) return; dest = G_Find (NULL, FOFS(targetname), self->target); if (!dest) { gi.dprintf(DEVELOPER_MSG_GAME, "Couldn't find destination\n"); return; } if (!Bot_FindNode(self, 120, TELEPORT_NODE) && dntg->value) { //start node VectorCopy (other->s.origin, nodeo); if (!(other->client->ps.pmove.pm_flags & PMF_DUCKED)) { nodeo[2] += 5; Bot_PlaceNode(nodeo, TELEPORT_NODE, 0); } else Bot_PlaceNode(nodeo, TELEPORT_NODE, 1); Bot_CalcNode(other, numnodes); //dest node VectorCopy (dest->s.origin, nodeo); nodeo[2] += 20; Bot_PlaceNode(nodeo, NORMAL_NODE, 0); Bot_CalcNode(other, numnodes); //connection nodes[numnodes-1].dist[numnodes] = 1; nprintf(PRINT_HIGH, "Teleporter nodes placed and connected!\n"); } // unlink to make sure it can't possibly interfere with KillBox gi.unlinkentity (other); VectorCopy (dest->s.origin, other->s.origin); VectorCopy (dest->s.origin, other->s.old_origin); other->s.origin[2] += 10; // clear the velocity and hold them in place briefly VectorClear (other->velocity); other->client->ps.pmove.pm_time = 160>>3; // hold time other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; // draw the teleport splash at source and on the player self->owner->s.event = EV_PLAYER_TELEPORT; other->s.event = EV_PLAYER_TELEPORT; // set angles for (i=0 ; i<3 ; i++) other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]); VectorClear (other->s.angles); VectorClear (other->client->ps.viewangles); VectorClear (other->client->v_angle); // kill anything at the destination KillBox (other); gi.linkentity (other); }