/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; gentity_t *tent; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int eventSequence; char userinfo[MAX_INFO_STRING]; index = ent - g_entities; client = ent->client; VectorClear(spawn_origin); // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer >= GT_CTF ) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } else { // the first spawn should be at a good looking spot if ( !client->pers.initialSpawn && client->pers.localClient ) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint(spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } } client->pers.teamState.state = TEAM_ACTIVE; // always clear the kamikaze flag ent->s.eFlags &= ~EF_KAMIKAZE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { persistant[i] = client->ps.persistant[i]; } eventSequence = client->ps.eventSequence; Com_Memset (client, 0, sizeof(*client)); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; client->lastkilled_client = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { client->ps.persistant[i] = persistant[i]; } client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); // set max health client->pers.maxHealth = 100; /*atoi( Info_ValueForKey( userinfo, "handicap" ) ); if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; }*/ // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy (playerMins, ent->r.mins); VectorCopy (playerMaxs, ent->r.maxs); client->ps.clientNum = index; client->ps.stats[STAT_WEAPONS] = ( 1 << WP_MACHINEGUN ); if ( g_gametype.integer == GT_TEAM ) { client->ps.ammo[WP_MACHINEGUN] = 50; } else { client->ps.ammo[WP_MACHINEGUN] = 50; } client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); client->ps.ammo[WP_GAUNTLET] = -1; client->ps.ammo[WP_GRAPPLING_HOOK] = -1; // health will count down towards max_health if (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_FT) { ent->health = client->ps.stats[STAT_HEALTH] = 2 * client->ps.stats[STAT_MAX_HEALTH]; } else { ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; } G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if (!level.intermissiontime) { if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { G_KillBox(ent); // force the base weapon up client->ps.weapon = WP_MACHINEGUN; client->ps.weaponstate = WEAPON_READY; // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); // select the highest weapon number available, after any spawn given items have fired client->ps.weapon = 1; for (i = WP_NUM_WEAPONS - 1 ; i > 0 ; i--) { if (client->ps.stats[STAT_WEAPONS] & (1 << i)) { client->ps.weapon = i; break; } } // positively link the client, even if the command times are weird VectorCopy(ent->client->ps.origin, ent->r.currentOrigin); tent = G_TempEntity(ent->client->ps.origin, EV_PLAYER_TELEPORT_IN); tent->s.clientNum = ent->s.clientNum; trap_LinkEntity (ent); } } else { // move players to intermission MoveClientToIntermission(ent); } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; client->sess.spectatorState = SPECTATOR_NOT; ClientThink( ent-g_entities ); // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); }
void jp_portal_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { //Remove the touch function for a while to avoid flooding self->genericValue1 = level.time; self->touch = NULL; if ( self->s.health != -1 ) { vector3 tpPos; vector3 sourceVelocity, destVelocity; vector3 sourceNormal, destNormal; //Fetch normals VectorCopy( &self->s.boneAngles1, &sourceNormal ); VectorCopy( &g_entities[self->s.health].s.boneAngles1, &destNormal ); //Set position to teleport to VectorCopy( &g_entities[self->s.health].s.origin, &tpPos ); VectorMA( &tpPos, 32.0f, &destNormal, &tpPos ); //BEGIN position if ( other->client ) { qboolean isNPC = (other->s.eType == ET_NPC); uint32_t eFlags = other->client->ps.eFlags ^ EF_TELEPORT_BIT; VectorCopy( &tpPos, &other->client->ps.origin ); other->client->ps.origin.z += 1.0f; SetClientViewAngle( other, &other->client->ps.viewangles ); other->client->ps.eFlags ^= EF_TELEPORT_BIT; BG_PlayerStateToEntityState( &other->client->ps, &other->s, qtrue ); if ( isNPC ) other->s.eType = ET_NPC; other->s.eFlags = eFlags; VectorCopy( &other->client->ps.origin, &other->r.currentOrigin ); } else { VectorCopy( &tpPos, &other->s.pos.trBase ); } //END position //BEGIN velocity if ( other->client ) {//Players and NPCs float reflectFactor; vector3 exitVector; vector3 velNormal; float length; vector3 debugLine; VectorNormalize2( &other->client->ps.velocity, &velNormal ); reflectFactor = 2 * DotProduct( &sourceNormal, &velNormal ); VectorCopy( &other->client->ps.velocity, &sourceVelocity ); length = VectorLength( &sourceVelocity ); exitVector.x = velNormal.x - reflectFactor * sourceNormal.x; exitVector.y = velNormal.y - reflectFactor * sourceNormal.y; exitVector.z = velNormal.z - reflectFactor * sourceNormal.z; VectorMA( &self->s.origin, -128.0f, &velNormal, &debugLine ); G_TestLine( &debugLine, &self->s.origin, 0xF0, 7500 ); MatrixMultiply3f( self->portal_matrix, &exitVector, &destVelocity ); VectorMA( &g_entities[self->s.health].s.origin, 128.0f, &destVelocity, &debugLine ); G_TestLine( &g_entities[self->s.health].s.origin, &debugLine, 0x0F, 7500 ); VectorScale( &destVelocity, length, &destVelocity ); } else {//Projectiles byteAlias_t ba; float velocity; ba.i = other->genericValue1; // stored during missile creation velocity = ba.f; // VectorScale( destNormal, velocity, other->s.pos.trDelta ); VectorMA( &other->s.pos.trBase, velocity, &destNormal, &other->s.pos.trDelta ); other->s.pos.trTime = level.time; other->nextthink = level.time + other->genericValue2; } //VectorSet( destVelocity, destNormal.x*speed, destNormal.y*speed, destNormal.z*speed ); if ( other->client ) {//Players and NPCs VectorCopy( &destVelocity, &other->client->ps.velocity ); other->client->ps.pm_flags |= PMF_DUCKED; other->s.solid = 0; } else {//Projectiles VectorCopy( &destVelocity, &other->s.pos.trDelta ); VectorCopy( &tpPos, &other->r.currentOrigin ); } //END velocity g_entities[self->s.health].genericValue1 = level.time; g_entities[self->s.health].touch = NULL; } return; }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles ) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[ MAX_PERSISTANT ]; gentity_t *spawnPoint = NULL; int flags; int savedPing; int teamLocal; int eventSequence; char userinfo[ MAX_INFO_STRING ]; vec3_t up = { 0.0f, 0.0f, 1.0f }; int maxAmmo, maxClips; weapon_t weapon; index = ent - g_entities; client = ent->client; teamLocal = client->pers.teamSelection; // only start client if chosen a class and joined a team if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE ) client->sess.spectatorState = SPECTATOR_FREE; else if( client->pers.classSelection == PCL_NONE ) client->sess.spectatorState = SPECTATOR_LOCKED; if( origin != NULL ) VectorCopy( origin, spawn_origin ); if( angles != NULL ) VectorCopy( angles, spawn_angles ); // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if( client->sess.spectatorState != SPECTATOR_NOT ) { if( teamLocal == TEAM_NONE ) spawnPoint = SelectSpectatorSpawnPoint( spawn_origin, spawn_angles ); else if( teamLocal == TEAM_ALIENS ) spawnPoint = SelectAlienLockSpawnPoint( spawn_origin, spawn_angles ); else if( teamLocal == TEAM_HUMANS ) spawnPoint = SelectHumanLockSpawnPoint( spawn_origin, spawn_angles ); } else { if( spawn == NULL ) { G_Error( "ClientSpawn: spawn is NULL\n" ); return; } spawnPoint = spawn; if( ent != spawn ) { //start spawn animation on spawnPoint G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue ); if( spawnPoint->buildableTeam == TEAM_ALIENS ) spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME; else if( spawnPoint->buildableTeam == TEAM_HUMANS ) spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME; } } // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & ( EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED ); flags ^= EF_TELEPORT_BIT; G_UnlaggedClear( ent ); // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; for( i = 0; i < MAX_PERSISTANT; i++ ) persistant[ i ] = client->ps.persistant[ i ]; eventSequence = client->ps.eventSequence; memset( client, 0, sizeof( *client ) ); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; client->lastkilled_client = -1; for( i = 0; i < MAX_PERSISTANT; i++ ) client->ps.persistant[ i ] = persistant[ i ]; client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[ PERS_SPAWN_COUNT ]++; client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof( userinfo ) ); client->ps.eFlags = flags; //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection ); ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[ index ]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; // calculate each client's acceleration ent->evaluateAcceleration = qtrue; client->ps.stats[ STAT_WEAPONS ] = 0; client->ps.stats[ STAT_WEAPONS2 ] = 0; client->ps.stats[ STAT_SLOTS ] = 0; client->ps.eFlags = flags; client->ps.clientNum = index; BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL ); if( client->sess.spectatorState == SPECTATOR_NOT ) client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = BG_Class( ent->client->pers.classSelection )->health; else client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = 100; // clear entity values if( ent->client->pers.classSelection == PCL_HUMAN ) { BG_AddWeaponToInventory( WP_BLASTER, client->ps.stats ); BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats ); weapon = client->pers.humanItemSelection; } else if( client->sess.spectatorState == SPECTATOR_NOT ) weapon = BG_Class( ent->client->pers.classSelection )->startWeapon; else weapon = WP_NONE; maxAmmo = BG_Weapon( weapon )->maxAmmo; maxClips = BG_Weapon( weapon )->maxClips; BG_AddWeaponToInventory( weapon, client->ps.stats ); client->ps.ammo = maxAmmo; client->ps.clips = maxClips; ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection; ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection; ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; ent->client->ps.stats[ STAT_STATE ] = 0; VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f ); // health will count down towards max_health ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25; //if evolving scale health if( ent == spawn ) { ent->health *= ent->client->pers.evolveHealthFraction; client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction; } //clear the credits array for( i = 0; i < MAX_CLIENTS; i++ ) ent->credits[ i ] = 0; client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); #define UP_VEL 150.0f #define F_VEL 50.0f //give aliens some spawn velocity if( client->sess.spectatorState == SPECTATOR_NOT && client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { if( ent == spawn ) { //evolution particle system G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) ); } else { spawn_angles[ YAW ] += 180.0f; AngleNormalize360( spawn_angles[ YAW ] ); if( spawnPoint->s.origin2[ 2 ] > 0.0f ) { vec3_t forward, dir; AngleVectors( spawn_angles, forward, NULL, NULL ); VectorScale( forward, F_VEL, forward ); VectorAdd( spawnPoint->s.origin2, forward, dir ); VectorNormalize( dir ); VectorScale( dir, UP_VEL, client->ps.velocity ); } G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 ); } } else if( client->sess.spectatorState == SPECTATOR_NOT && client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { spawn_angles[ YAW ] += 180.0f; AngleNormalize360( spawn_angles[ YAW ] ); } // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if( !( client->sess.spectatorState != SPECTATOR_NOT ) ) { trap_LinkEntity( ent ); // force the base weapon up client->ps.weapon = WP_NONE; client->ps.weaponstate = WEAPON_READY; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->lastKillTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if( level.intermissiontime ) MoveClientToIntermission( ent ); else { // fire the targets of the spawn point if( !spawn ) G_UseTargets( spawnPoint, ent ); // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = 1; for( i = WP_NUM_WEAPONS - 1; i > 0 ; i-- ) { if( BG_InventoryContainsWeapon( i, client->ps.stats ) ) { client->ps.weapon = i; break; } } } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if( client->sess.spectatorState == SPECTATOR_NOT ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } // must do this here so the number of active clients is calculated CalculateRanks( ); // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); }
qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, qboolean anglesOnly ) { vec3_t newAngles; float animLength, spinStart, spinEnd, spinAmt, spinLength; animNumber_t spinAnim; if ( ent->client->ps.legsAnim == BOTH_JUMPFLIPSTABDOWN ) { spinAnim = BOTH_JUMPFLIPSTABDOWN; spinStart = 300.0f;//700.0f; spinEnd = 1400.0f; spinAmt = 180.0f; } else if ( ent->client->ps.legsAnim == BOTH_JUMPFLIPSLASHDOWN1 ) { spinAnim = BOTH_JUMPFLIPSLASHDOWN1; spinStart = 300.0f;//700.0f;//1500.0f; spinEnd = 1400.0f;//2300.0f; spinAmt = 180.0f; } else { if ( !anglesOnly ) { if ( !ent->s.number ) { cg.overrides.active &= ~CG_OVERRIDE_3RD_PERSON_VOF; cg.overrides.thirdPersonVertOffset = 0; } } return qfalse; } animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, spinAnim ); float elapsedTime = (float)(animLength-ent->client->ps.legsAnimTimer); //face me if ( elapsedTime >= spinStart && elapsedTime <= spinEnd ) { spinLength = spinEnd - spinStart; VectorCopy( ent->client->ps.viewangles, newAngles ); newAngles[YAW] = ent->angle + (spinAmt * (elapsedTime-spinStart) / spinLength); if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD ) { //don't clamp angles when looking through a viewEntity SetClientViewAngle( ent, newAngles ); } ucmd->angles[PITCH] = ANGLE2SHORT( ent->client->ps.viewangles[PITCH] ) - ent->client->ps.delta_angles[PITCH]; ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW]; if ( anglesOnly ) { return qtrue; } } else if ( anglesOnly ) { return qfalse; } //push me if ( ent->client->ps.legsAnimTimer > 300 )//&& ent->client->ps.groundEntityNum == ENTITYNUM_NONE ) { //haven't landed or reached end of anim yet if ( ent->s.number || !player_locked ) { vec3_t pushDir, pushAngles = {0,ent->angle,0}; AngleVectors( pushAngles, pushDir, NULL, NULL ); if ( DotProduct( ent->client->ps.velocity, pushDir ) < 100 ) { VectorMA( ent->client->ps.velocity, 10, pushDir, ent->client->ps.velocity ); } } } //do a dip in the view if ( !ent->s.number ) { float viewDip = 0; if ( elapsedTime < animLength/2.0f ) { //starting anim viewDip = (elapsedTime/animLength)*-120.0f; } else { //ending anim viewDip = ((animLength-elapsedTime)/animLength)*-120.0f; } cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_VOF; cg.overrides.thirdPersonVertOffset = cg_thirdPersonVertOffset.value+viewDip; //pm->ps->viewheight = standheight + viewDip; } return qtrue; }
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, (EG2_Collision)0, 0); 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; }
void SandCreature_Attack( qboolean miss ) { //FIXME: make it able to grab a thermal detonator, take it down, // then have it explode inside them, killing them // (or, do damage, making them stick half out of the ground and // screech for a bit, giving you a chance to run for it!) //FIXME: effect and sound //FIXME: shootable during this anim? if ( !NPC->enemy->client ) { NPC_SetAnim( NPC, SETANIM_LEGS, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); } else { NPC_SetAnim( NPC, SETANIM_LEGS, Q_irand( BOTH_ATTACK1, BOTH_ATTACK2 ), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); } //don't do anything else while in this anim TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer ); float playerDist = Distance( player->currentOrigin, NPC->currentOrigin ); if ( playerDist < 256 ) { //FIXME: tone this down CGCam_Shake( 0.75f*playerDist/128.0f, NPC->client->ps.legsAnimTimer ); } if ( miss ) {//purposely missed him, chance of knocking him down //FIXME: if, during the attack anim, I do end up catching him close to my mouth, then snatch him anyway... if ( NPC->enemy && NPC->enemy->client ) { vec3_t dir2Enemy; VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, dir2Enemy ); if ( dir2Enemy[2] < 30 ) { dir2Enemy[2] = 30; } if ( g_spskill->integer > 0 ) { float enemyDist = VectorNormalize( dir2Enemy ); //FIXME: tone this down, smaller radius if ( enemyDist < 200 && NPC->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) { float throwStr = ((200-enemyDist)*0.4f)+20; if ( throwStr > 45 ) { throwStr = 45; } G_Throw( NPC->enemy, dir2Enemy, throwStr ); if ( g_spskill->integer > 1 ) {//knock them down, too if ( NPC->enemy->health > 0 && Q_flrand( 50, 150 ) > enemyDist ) {//knock them down G_Knockdown( NPC->enemy, NPC, dir2Enemy, 300, qtrue ); if ( NPC->enemy->s.number < MAX_CLIENTS ) {//make the player look up at me vec3_t vAng; vectoangles( dir2Enemy, vAng ); VectorSet( vAng, AngleNormalize180(vAng[PITCH])*-1, NPC->enemy->client->ps.viewangles[YAW], 0 ); SetClientViewAngle( NPC->enemy, vAng ); } } } } } } } else { NPC->enemy->activator = NPC; // kind of dumb, but when we are locked to the Rancor, we are owned by it. NPC->activator = NPC->enemy;//remember him //this guy isn't going anywhere anymore NPC->enemy->contents = 0; NPC->enemy->clipmask = 0; if ( NPC->activator->client ) { NPC->activator->client->ps.SaberDeactivate(); NPC->activator->client->ps.eFlags |= EF_HELD_BY_SAND_CREATURE; if ( NPC->activator->health > 0 && NPC->activator->client ) { G_AddEvent( NPC->activator, Q_irand(EV_DEATH1, EV_DEATH3), 0 ); NPC_SetAnim( NPC->activator, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); NPC_SetAnim( NPC->activator, SETANIM_TORSO, BOTH_FALLDEATH1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TossClientItems( NPC ); if ( NPC->activator->NPC ) {//no more thinking for you NPC->activator->NPC->nextBStateThink = Q3_INFINITE; } } /* if ( !NPC->activator->s.number ) { cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_CDP|CG_OVERRIDE_3RD_PERSON_RNG); cg.overrides.thirdPersonCameraDamp = 0; cg.overrides.thirdPersonRange = 120; } */ } else { NPC->activator->s.eFlags |= EF_HELD_BY_SAND_CREATURE; } } }
//---------------------------------------------------------- void emplaced_gun_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { vec3_t fwd1, fwd2; if ( self->health <= 0 ) { // can't use a dead gun. return; } if ( self->svFlags & SVF_INACTIVE ) { return; // can't use inactive gun } if ( !activator->client ) { return; // only a client can use it. } if ( self->activator ) { // someone is already in the gun. return; } // We'll just let the designers duke this one out....I mean, as to whether they even want to limit such a thing. if ( self->spawnflags & EMPLACED_FACING ) { // Let's get some direction vectors for the users AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL ); // Get the guns direction vector AngleVectors( self->pos1, fwd2, NULL, NULL ); float dot = DotProduct( fwd1, fwd2 ); // Must be reasonably facing the way the gun points ( 90 degrees or so ), otherwise we don't allow to use it. if ( dot < 0.0f ) { return; } } // don't allow using it again for half a second if ( self->delay + 500 < level.time ) { int oldWeapon = activator->s.weapon; if ( oldWeapon == WP_SABER ) { self->alt_fire = activator->client->ps.saberActive; } // swap the users weapon with the emplaced gun and add the ammo the gun has to the player activator->client->ps.weapon = self->s.weapon; Add_Ammo( activator, WP_EMPLACED_GUN, self->count ); activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN ); // Allow us to point from one to the other activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; if ( activator->weaponModel >= 0 ) { // rip that gun out of their hands.... gi.G2API_RemoveGhoul2Model( activator->ghoul2, activator->weaponModel ); activator->weaponModel = -1; } extern void ChangeWeapon( gentity_t *ent, int newWeapon ); if ( activator->NPC ) { if ( activator->weaponModel >= 0 ) { // rip that gun out of their hands.... gi.G2API_RemoveGhoul2Model( activator->ghoul2, activator->weaponModel ); activator->weaponModel = -1; // Doesn't work? // activator->maxs[2] += 35; // make it so you can potentially shoot their head // activator->s.radius += 10; // increase ghoul radius so we can collide with the enemy more accurately // gi.linkentity( activator ); } ChangeWeapon( activator, WP_EMPLACED_GUN ); } else if ( activator->s.number == 0 ) { // we don't want for it to draw the weapon select stuff cg.weaponSelect = WP_EMPLACED_GUN; CG_CenterPrint( "@INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 ); } // Since we move the activator inside of the gun, we reserve a solid spot where they were standing in order to be able to get back out without being in solid if ( self->nextTrain ) {//you never know G_FreeEntity( self->nextTrain ); } self->nextTrain = G_Spawn(); //self->nextTrain->classname = "emp_placeholder"; self->nextTrain->contents = CONTENTS_MONSTERCLIP|CONTENTS_PLAYERCLIP;//hmm... playerclip too now that we're doing it for NPCs? G_SetOrigin( self->nextTrain, activator->client->ps.origin ); VectorCopy( activator->mins, self->nextTrain->mins ); VectorCopy( activator->maxs, self->nextTrain->maxs ); gi.linkentity( self->nextTrain ); //need to inflate the activator's mins/maxs since the gunsit anim puts them outside of their bbox VectorSet( activator->mins, -24, -24, -24 ); VectorSet( activator->maxs, 24, 24, 40 ); // Move the activator into the center of the gun. For NPC's the only way the can get out of the gun is to die. VectorCopy( self->s.origin, activator->client->ps.origin ); activator->client->ps.origin[2] += 30; // move them up so they aren't standing in the floor gi.linkentity( activator ); // the gun will track which weapon we used to have self->s.weapon = oldWeapon; // Lock the player activator->client->ps.eFlags |= EF_LOCKED_TO_WEAPON; activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; self->delay = level.time; // can't disconnect from the thing for half a second // Let the gun be considered an enemy self->svFlags |= SVF_NONNPC_ENEMY; self->noDamageTeam = activator->client->playerTeam; // FIXME: don't do this, we'll try and actually put the player in this beast // move the player to the center of the gun // activator->contents = 0; // VectorCopy( self->currentOrigin, activator->client->ps.origin ); SetClientViewAngle( activator, self->pos1 ); //FIXME: should really wait a bit after spawn and get this just once? self->waypoint = NAV_FindClosestWaypointForEnt( self, WAYPOINT_NONE ); #ifdef _DEBUG if ( self->waypoint == -1 ) { gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) ); } #endif G_Sound( self, G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" )); } }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int savedEvents[MAX_PS_EVENTS]; int eventSequence; char userinfo[MAX_INFO_STRING]; index = ent - g_entities; client = ent->client; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer >= GT_CTF ) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else { do { // the first spawn should be at a good looking spot if ( !client->pers.initialSpawn && client->pers.localClient ) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles ); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, spawn_origin, spawn_angles); } // Tim needs to prevent bots from spawning at the initial point // on q3dm0... if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } // just to be symetric, we have a nohumans option... if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } break; } while ( 1 ); } client->pers.teamState.state = TEAM_ACTIVE; // always clear the kamikaze flag // ent->s.eFlags &= ~EF_KAMIKAZE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { persistant[i] = client->ps.persistant[i]; } // also save the predictable events otherwise we might get double or dropped events for (i = 0; i < MAX_PS_EVENTS; i++) { savedEvents[i] = client->ps.events[i]; } eventSequence = client->ps.eventSequence; memset (client, 0, sizeof(*client)); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; client->lastkilled_client = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { client->ps.persistant[i] = persistant[i]; } // CORKSCREW this caused a bug... dunno why really :/ // for (i = 0; i < MAX_PS_EVENTS; i++) { // client->ps.events[i] = savedEvents[i]; // } // client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); // set max health client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) ); if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy (playerMins, ent->r.mins); VectorCopy (playerMaxs, ent->r.maxs); client->ps.clientNum = index; client->ps.stats[STAT_WEAPONS] = ( 1 << WP_RAILGUN ); client->ps.ammo[WP_RAILGUN] = 999; client->ps.weapon = WP_RAILGUN; // health will count down towards max_health ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { } else { G_KillBox( ent ); trap_LinkEntity (ent); // force the base weapon up client->ps.weapon = WP_RAILGUN; client->ps.weaponstate = WEAPON_READY; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if ( level.intermissiontime ) { MoveClientToIntermission( ent ); } else { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); }
/* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. If "g_synchronousClients 1" is set, this will be called exactly once for each server frame, which makes for smooth demo recording. ============== */ void ClientThink_real( gentity_t *ent ) { gclient_t *client; pmove_t pm; int oldEventSequence; int msec; int i; usercmd_t *ucmd; client = ent->client; // don't think if the client is not yet connected (and thus not yet spawned in) if (client->pers.connected != CON_CONNECTED) { return; } // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; // sanity check the command time to prevent speedup cheating if ( ucmd->serverTime > level.time + 200 ) { ucmd->serverTime = level.time + 200; // G_Printf("serverTime <<<<<\n" ); } if ( ucmd->serverTime < level.time - 1000 ) { ucmd->serverTime = level.time - 1000; // G_Printf("serverTime >>>>>\n" ); } msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) { return; } if ( msec > 200 ) { msec = 200; } if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); } if ( pmove_fixed.integer || client->pers.pmoveFixed ) { ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; //if (ucmd->serverTime - client->ps.commandTime <= 0) // return; } // // check for exiting intermission // if ( level.intermissiontime ) { ClientIntermissionThink( client ); return; } // spectators don't do much if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { return; } SpectatorThink( ent, ucmd ); return; } if (ent && ent->client && (ent->client->ps.eFlags & EF_INVULNERABLE)) { if (ent->client->invulnerableTimer <= level.time) { ent->client->ps.eFlags &= ~EF_INVULNERABLE; } } // check for inactivity timer, but never drop the local client of a non-dedicated server if ( !ClientInactivityTimer( client ) ) { return; } // clear the rewards if time if ( level.time > client->rewardTime ) { client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); } if ( client->noclip ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.eFlags & EF_DISINTEGRATION ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) { client->ps.pm_type = PM_DEAD; } else { if (client->ps.forceGripChangeMovetype) { client->ps.pm_type = client->ps.forceGripChangeMovetype; } else { client->ps.pm_type = PM_NORMAL; } } client->ps.gravity = g_gravity.value; // set speed client->ps.speed = g_speed.value; client->ps.basespeed = g_speed.value; if (ent->client->ps.duelInProgress) { gentity_t *duelAgainst = &g_entities[ent->client->ps.duelIndex]; //Keep the time updated, so once this duel ends this player can't engage in a duel for another //10 seconds. This will give other people a chance to engage in duels in case this player wants //to engage again right after he's done fighting and someone else is waiting. ent->client->ps.fd.privateDuelTime = level.time + 10000; if (ent->client->ps.duelTime < level.time) { //Bring out the sabers if (ent->client->ps.weapon == WP_SABER && ent->client->ps.saberHolstered && ent->client->ps.duelTime) { if (!saberOffSound || !saberOnSound) { saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav"); saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav"); } ent->client->ps.saberHolstered = qfalse; G_Sound(ent, CHAN_AUTO, saberOnSound); G_AddEvent(ent, EV_PRIVATE_DUEL, 2); ent->client->ps.duelTime = 0; } if (duelAgainst && duelAgainst->client && duelAgainst->inuse && duelAgainst->client->ps.weapon == WP_SABER && duelAgainst->client->ps.saberHolstered && duelAgainst->client->ps.duelTime) { if (!saberOffSound || !saberOnSound) { saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav"); saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav"); } duelAgainst->client->ps.saberHolstered = qfalse; G_Sound(duelAgainst, CHAN_AUTO, saberOnSound); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 2); duelAgainst->client->ps.duelTime = 0; } } else { client->ps.speed = 0; client->ps.basespeed = 0; ucmd->forwardmove = 0; ucmd->rightmove = 0; ucmd->upmove = 0; } if (!duelAgainst || !duelAgainst->client || !duelAgainst->inuse || duelAgainst->client->ps.duelIndex != ent->s.number) { ent->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); } else if (duelAgainst->health < 1 || duelAgainst->client->ps.stats[STAT_HEALTH] < 1) { ent->client->ps.duelInProgress = 0; duelAgainst->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0); //Winner gets full health.. providing he's still alive if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0) { if (ent->health < ent->client->ps.stats[STAT_MAX_HEALTH]) { ent->client->ps.stats[STAT_HEALTH] = ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } if (g_spawnInvulnerability.integer) { ent->client->ps.eFlags |= EF_INVULNERABLE; ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer; } } /* trap_SendServerCommand( ent-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) ); trap_SendServerCommand( duelAgainst-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) ); */ //Private duel announcements are now made globally because we only want one duel at a time. if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0) { trap_SendServerCommand( -1, va("cp \"%s %s %s!\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER"), duelAgainst->client->pers.netname) ); } else { //it was a draw, because we both managed to die in the same frame trap_SendServerCommand( -1, va("cp \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELTIE")) ); } } else { vec3_t vSub; float subLen = 0; VectorSubtract(ent->client->ps.origin, duelAgainst->client->ps.origin, vSub); subLen = VectorLength(vSub); if (subLen >= 1024) { ent->client->ps.duelInProgress = 0; duelAgainst->client->ps.duelInProgress = 0; G_AddEvent(ent, EV_PRIVATE_DUEL, 0); G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELSTOP")) ); } } } /* if ( client->ps.powerups[PW_HASTE] ) { client->ps.speed *= 1.3; } */ if (client->ps.usingATST && ent->health > 0) { //we have special shot clip boxes as an ATST ent->r.contents |= CONTENTS_NOSHOT; ATST_ManageDamageBoxes(ent); } else { ent->r.contents &= ~CONTENTS_NOSHOT; client->damageBoxHandle_Head = 0; client->damageBoxHandle_RLeg = 0; client->damageBoxHandle_LLeg = 0; } //rww - moved this stuff into the pmove code so that it's predicted properly //BG_AdjustClientSpeed(&client->ps, &client->pers.cmd, level.time); // set up for pmove oldEventSequence = client->ps.eventSequence; memset (&pm, 0, sizeof(pm)); if ( ent->flags & FL_FORCE_GESTURE ) { ent->flags &= ~FL_FORCE_GESTURE; ent->client->pers.cmd.buttons |= BUTTON_GESTURE; } if (ent->client && ent->client->ps.fallingToDeath && (level.time - FALL_FADE_TIME) > ent->client->ps.fallingToDeath) { //die! player_die(ent, ent, ent, 100000, MOD_FALLING); respawn(ent); ent->client->ps.fallingToDeath = 0; G_MuteSound(ent->s.number, CHAN_VOICE); //stop screaming, because you are dead! } if (ent->client->ps.otherKillerTime > level.time && ent->client->ps.groundEntityNum != ENTITYNUM_NONE && ent->client->ps.otherKillerDebounceTime < level.time) { ent->client->ps.otherKillerTime = 0; ent->client->ps.otherKiller = ENTITYNUM_NONE; } else if (ent->client->ps.otherKillerTime > level.time && ent->client->ps.groundEntityNum == ENTITYNUM_NONE) { if (ent->client->ps.otherKillerDebounceTime < (level.time + 100)) { ent->client->ps.otherKillerDebounceTime = level.time + 100; } } // WP_ForcePowersUpdate( ent, msec, ucmd); //update any active force powers // WP_SaberPositionUpdate(ent, ucmd); //check the server-side saber point, do apprioriate server-side actions (effects are cs-only) if ((ent->client->pers.cmd.buttons & BUTTON_USE) && ent->client->ps.useDelay < level.time) { TryUse(ent); ent->client->ps.useDelay = level.time + 100; } pm.ps = &client->ps; pm.cmd = *ucmd; if ( pm.ps->pm_type == PM_DEAD ) { pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else if ( ent->r.svFlags & SVF_BOT ) { pm.tracemask = MASK_PLAYERSOLID | CONTENTS_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 ); }
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) { gentity_t *tent; // use temp events at source and destination to prevent the effect // from getting dropped by a second player event #ifndef SMOKINGUNS if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) { #else if ( player->client->sess.sessionTeam < TEAM_SPECTATOR ) { #endif tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = player->s.clientNum; tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = player->s.clientNum; } // unlink to make sure it can't possibly interfere with G_KillBox trap_UnlinkEntity (player); VectorCopy ( origin, player->client->ps.origin ); player->client->ps.origin[2] += 1; // spit the player out AngleVectors( angles, player->client->ps.velocity, NULL, NULL ); VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity ); player->client->ps.pm_time = 160; // hold time player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; // toggle the teleport bit so the client knows to not lerp player->client->ps.eFlags ^= EF_TELEPORT_BIT; #ifdef SMOKINGUNS //unlagged - backward reconciliation #3 // we don't want players being backward-reconciled back through teleporters G_ResetHistory( player ); //unlagged - backward reconciliation #3 #endif // set angles SetClientViewAngle( player, angles ); // kill anything at the destination #ifndef SMOKINGUNS if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) { #else if ( player->client->sess.sessionTeam < TEAM_SPECTATOR ) { #endif G_KillBox (player); #ifdef SMOKINGUNS // Tequila comment: G_KillBox will set dontTelefrag as needed if (player->client->dontTelefrag) { #ifdef DEBUG_TELEFRAG_CASE G_Printf(S_COLOR_MAGENTA "TeleportPlayer: Telefrag case delayed at respawn for %s...\n",player->client->pers.netname); #endif trap_SendServerCommand( player->s.clientNum, va("print \"Go away %s\n\"",player->client->pers.netname) ); // So we will link the player entity later with normal content player->r.contents = 0; } #endif } // save results of pmove BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue ); // use the precise origin for linking VectorCopy( player->client->ps.origin, player->r.currentOrigin ); #ifndef SMOKINGUNS if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) { #else if ( player->client->sess.sessionTeam < TEAM_SPECTATOR ) { #endif trap_LinkEntity (player); } } /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) Point teleporters at these. Now that we don't have teleport destination pads, this is just an info_notnull */ void SP_misc_teleporter_dest( gentity_t *ent ) { } #ifdef SMOKINGUNS // Imported from WoP OpenSource project //=========================================================== /*QUAKED misc_externalmodel (1 0 0) (-16 -16 -16) (16 16 16) "model" arbitrary .md3 file to display "wait" time in seconds before the animation begins */ #define ANIMATION_THINKTIME 50 static void Think_AnimationExternalmodel( gentity_t *ent ) { if(ent->animationEnd>ent->animationStart) { ent->s.frame = (int)((float)level.time*0.001f*ent->animationFPS)%(ent->animationEnd-ent->animationStart); ent->s.frame += ent->animationStart; ent->nextthink = level.time + ANIMATION_THINKTIME; } } void SP_misc_externalmodel( gentity_t *ent ) { ent->s.modelindex = G_ModelIndex( ent->model ); // VectorSet (ent->mins, -16, -16, -16); // VectorSet (ent->maxs, 16, 16, 16); trap_LinkEntity (ent); G_SetOrigin( ent, ent->s.origin ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); if(ent->animationEnd>ent->animationStart && ent->animationFPS>0.0f) { ent->think = Think_AnimationExternalmodel; ent->nextthink = level.time + ANIMATION_THINKTIME; // Tequila: Support for new entity features if (ent->wait>0.0f) ent->nextthink += (int)(ent->wait*1000); } } #endif //=========================================================== /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16) "model" arbitrary .md3 file to display */ void SP_misc_model( gentity_t *ent ) { #if 0 ent->s.modelindex = G_ModelIndex( ent->model ); VectorSet (ent->mins, -16, -16, -16); VectorSet (ent->maxs, 16, 16, 16); trap_LinkEntity (ent); G_SetOrigin( ent, ent->s.origin ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); #else G_FreeEntity( ent ); #endif } //=========================================================== void locateCamera( gentity_t *ent ) { vec3_t dir; gentity_t *target; gentity_t *owner; owner = G_PickTarget( ent->target ); if ( !owner ) { G_Printf( "Couldn't find target for misc_portal_surface\n" ); G_FreeEntity( ent ); return; } ent->r.ownerNum = owner->s.number; // frame holds the rotate speed if ( owner->spawnflags & 1 ) { ent->s.frame = 25; } else if ( owner->spawnflags & 2 ) { ent->s.frame = 75; } #ifndef SMOKINGUNS // swing camera ? if ( owner->spawnflags & 4 ) { // set to 0 for no rotation at all ent->s.powerups = 0; } else { ent->s.powerups = 1; } #else // set to 0 for no rotation at all ent->s.powerups = 1; #endif // clientNum holds the rotate offset ent->s.clientNum = owner->s.clientNum; VectorCopy( owner->s.origin, ent->s.origin2 ); // see if the portal_camera has a target target = G_PickTarget( owner->target ); if ( target ) { VectorSubtract( target->s.origin, owner->s.origin, dir ); VectorNormalize( dir ); } else { G_SetMovedir( owner->s.angles, dir ); } ent->s.eventParm = DirToByte( dir ); }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int eventSequence; // char userinfo[MAX_INFO_STRING]; forcedata_t savedForce; void *ghoul2save; int saveSaberNum = ENTITYNUM_NONE; int wDisable = 0; index = ent - g_entities; client = ent->client; if (client->ps.fd.forceDoInit) { //force a reread of force powers WP_InitForcePowers( ent ); client->ps.fd.forceDoInit = 0; } // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_SAGA) { spawnPoint = SelectSagaSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else { do { // the first spawn should be at a good looking spot if ( !client->pers.initialSpawn && client->pers.localClient ) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles ); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, spawn_origin, spawn_angles); } // Tim needs to prevent bots from spawning at the initial point // on q3dm0... if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } // just to be symetric, we have a nohumans option... if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } break; } while ( 1 ); } client->pers.teamState.state = TEAM_ACTIVE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { persistant[i] = client->ps.persistant[i]; } eventSequence = client->ps.eventSequence; savedForce = client->ps.fd; ghoul2save = client->ghoul2; saveSaberNum = client->ps.saberEntityNum; memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset? //rww - Don't wipe the ghoul2 instance or the animation data client->ghoul2 = ghoul2save; //or the saber ent num client->ps.saberEntityNum = saveSaberNum; client->ps.fd = savedForce; client->ps.duelIndex = ENTITYNUM_NONE; client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; client->lastkilled_client = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { client->ps.persistant[i] = persistant[i]; } client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; // trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); // set max health client->pers.maxHealth = 100;//atoi( Info_ValueForKey( userinfo, "handicap" ) ); if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy (playerMins, ent->r.mins); VectorCopy (playerMaxs, ent->r.maxs); client->ps.clientNum = index; //give default weapons client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE ); if (g_gametype.integer == GT_HOLOCRON) { //always get free saber level 1 in holocron client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //these are precached in g_items, ClearRegisteredItems() } else { if (client->ps.fd.forcePowerLevel[FP_SABERATTACK]) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //these are precached in g_items, ClearRegisteredItems() } else { //if you don't have saber attack rank then you don't get a saber client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON); } } if (g_gametype.integer == GT_TOURNAMENT) { wDisable = g_duelWeaponDisable.integer; } else { wDisable = g_weaponDisable.integer; } if (!wDisable || !(wDisable & (1 << WP_BRYAR_PISTOL))) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL ); } else if (g_gametype.integer == GT_JEDIMASTER) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL ); } if (g_gametype.integer == GT_JEDIMASTER) { client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER); client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON); } if (client->ps.stats[STAT_WEAPONS] & (1 << WP_BRYAR_PISTOL)) { client->ps.weapon = WP_BRYAR_PISTOL; } else if (client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER)) { client->ps.weapon = WP_SABER; } else { client->ps.weapon = WP_STUN_BATON; } /* client->ps.stats[STAT_HOLDABLE_ITEMS] |= ( 1 << HI_BINOCULARS ); client->ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_BINOCULARS, IT_HOLDABLE); */ client->ps.stats[STAT_HOLDABLE_ITEMS] = 0; client->ps.stats[STAT_HOLDABLE_ITEM] = 0; if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { client->ps.stats[STAT_WEAPONS] = 0; client->ps.stats[STAT_HOLDABLE_ITEMS] = 0; client->ps.stats[STAT_HOLDABLE_ITEM] = 0; } client->ps.ammo[AMMO_BLASTER] = 100; //ammoData[AMMO_BLASTER].max; //100 seems fair. // client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max; // client->ps.ammo[AMMO_FORCE] = ammoData[AMMO_FORCE].max; // client->ps.ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max; // client->ps.ammo[AMMO_ROCKETS] = ammoData[AMMO_ROCKETS].max; /* client->ps.stats[STAT_WEAPONS] = ( 1 << WP_BRYAR_PISTOL); if ( g_gametype.integer == GT_TEAM ) { client->ps.ammo[WP_BRYAR_PISTOL] = 50; } else { client->ps.ammo[WP_BRYAR_PISTOL] = 100; } */ client->ps.rocketLockIndex = MAX_CLIENTS; client->ps.rocketLockTime = 0; //rww - Set here to initialize the circling seeker drone to off. //A quick note about this so I don't forget how it works again: //ps.genericEnemyIndex is kept in sync between the server and client. //When it gets set then an entitystate value of the same name gets //set along with an entitystate flag in the shared bg code. Which //is why a value needs to be both on the player state and entity state. //(it doesn't seem to just carry over the entitystate value automatically //because entity state value is derived from player state data or some //such) client->ps.genericEnemyIndex = -1; client->ps.isJediMaster = qfalse; client->ps.fallingToDeath = 0; //Do per-spawn force power initialization WP_SpawnInitForcePowers( ent ); // health will count down towards max_health ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];// * 1.25; Boot - no extra 25 hp on start. // Start with a small amount of armor as well. client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH] * 0.25; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { } else { G_KillBox( ent ); trap_LinkEntity (ent); // force the base weapon up client->ps.weapon = WP_BRYAR_PISTOL; client->ps.weaponstate = FIRST_WEAPON; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = WeaponReadyAnim[client->ps.weapon]; client->ps.legsAnim = WeaponReadyAnim[client->ps.weapon]; if ( level.intermissiontime ) { MoveClientToIntermission( ent ); } else { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = 1; for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) { if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) { client->ps.weapon = i; break; } } } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } if (g_spawnInvulnerability.integer) { ent->client->ps.eFlags |= EF_INVULNERABLE; ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer; } // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); }
void JKG_GrappleUpdate( gentity_t *self ) { gentity_t *grappler = &g_entities[self->client->grappleIndex]; if (!grappler->inuse || !grappler->client || grappler->client->grappleIndex != self->s.number || !BG_InGrappleMove(grappler->client->ps.torsoAnim) || !BG_InGrappleMove(grappler->client->ps.legsAnim) || !BG_InGrappleMove(self->client->ps.torsoAnim) || !BG_InGrappleMove(self->client->ps.legsAnim) || !self->client->grappleState || !grappler->client->grappleState || grappler->health < 1 || self->health < 1 || !G_PrettyCloseIGuess(self->client->ps.origin[2], grappler->client->ps.origin[2], 4.0f)) { self->client->grappleState = 0; if ((BG_InGrappleMove(self->client->ps.torsoAnim) && self->client->ps.torsoTimer > 100) || (BG_InGrappleMove(self->client->ps.legsAnim) && self->client->ps.legsTimer > 100)) { //if they're pretty far from finishing the anim then shove them into another anim G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0); if (self->client->ps.torsoAnim == BOTH_KYLE_MISS) { //providing the anim set succeeded.. self->client->ps.weaponTime = self->client->ps.torsoTimer; } } } else { vec3_t grapAng; VectorSubtract(grappler->client->ps.origin, self->client->ps.origin, grapAng); if (VectorLength(grapAng) > 64.0f) { //too far away, break it off if ((BG_InGrappleMove(self->client->ps.torsoAnim) && self->client->ps.torsoTimer > 100) || (BG_InGrappleMove(self->client->ps.legsAnim) && self->client->ps.legsTimer > 100)) { self->client->grappleState = 0; G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0); if (self->client->ps.torsoAnim == BOTH_KYLE_MISS) { //providing the anim set succeeded.. self->client->ps.weaponTime = self->client->ps.torsoTimer; } } } else { vectoangles(grapAng, grapAng); SetClientViewAngle(self, grapAng); if (self->client->grappleState >= 20) { //grapplee //try to position myself at the correct distance from my grappler float idealDist; vec3_t gFwd, idealSpot; trace_t trace; if (grappler->client->ps.torsoAnim == BOTH_KYLE_PA_1) { //grab punch idealDist = 46.0f; } else { //knee-throw idealDist = 34.0f; } AngleVectors(grappler->client->ps.viewangles, gFwd, 0, 0); VectorMA(grappler->client->ps.origin, idealDist, gFwd, idealSpot); trap_Trace(&trace, self->client->ps.origin, self->r.mins, self->r.maxs, idealSpot, self->s.number, self->clipmask); if (!trace.startsolid && !trace.allsolid && trace.fraction == 1.0f) { //go there G_SetOrigin(self, idealSpot); VectorCopy(idealSpot, self->client->ps.origin); } } else if (self->client->grappleState >= 1) { //grappler if (grappler->client->ps.weapon == WP_SABER) { //make sure their saber is shut off if (!grappler->client->ps.saberHolstered) { grappler->client->ps.saberHolstered = 2; if (grappler->client->saber[0].soundOff) { G_Sound(grappler, CHAN_AUTO, grappler->client->saber[0].soundOff); } if (grappler->client->saber[1].soundOff && grappler->client->saber[1].model[0]) { G_Sound(grappler, CHAN_AUTO, grappler->client->saber[1].soundOff); } } } //check for smashy events if (self->client->ps.torsoAnim == BOTH_KYLE_PA_1) { //grab punch if (self->client->grappleState == 1) { //smack if (self->client->ps.torsoTimer < 3400) { int grapplerAnim = grappler->client->ps.torsoAnim; int grapplerTime = grappler->client->ps.torsoTimer; G_Damage(grappler, self, self, NULL, self->client->ps.origin, 10, 0, MOD_MELEE); //G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) ); //it might try to put them into a pain anim or something, so override it back again if (grappler->health > 0) { grappler->client->ps.torsoAnim = grapplerAnim; grappler->client->ps.torsoTimer = grapplerTime; grappler->client->ps.legsAnim = grapplerAnim; grappler->client->ps.legsTimer = grapplerTime; grappler->client->ps.weaponTime = grapplerTime; } self->client->grappleState++; } } else if (self->client->grappleState == 2) { //smack! if (self->client->ps.torsoTimer < 2550) { int grapplerAnim = grappler->client->ps.torsoAnim; int grapplerTime = grappler->client->ps.torsoTimer; G_Damage(grappler, self, self, NULL, self->client->ps.origin, 10, 0, MOD_MELEE); //G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) ); //it might try to put them into a pain anim or something, so override it back again if (grappler->health > 0) { grappler->client->ps.torsoAnim = grapplerAnim; grappler->client->ps.torsoTimer = grapplerTime; grappler->client->ps.legsAnim = grapplerAnim; grappler->client->ps.legsTimer = grapplerTime; grappler->client->ps.weaponTime = grapplerTime; } self->client->grappleState++; } } else { //SMACK! if (self->client->ps.torsoTimer < 1300) { vec3_t tossDir; G_Damage(grappler, self, self, NULL, self->client->ps.origin, 30, 0, MOD_MELEE); //G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) ); self->client->grappleState = 0; VectorSubtract(grappler->client->ps.origin, self->client->ps.origin, tossDir); VectorNormalize(tossDir); VectorScale(tossDir, 500.0f, tossDir); tossDir[2] = 200.0f; VectorAdd(grappler->client->ps.velocity, tossDir, grappler->client->ps.velocity); if (grappler->health > 0) { //if still alive knock them down grappler->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; grappler->client->ps.forceHandExtendTime = level.time + 1300; } } } } else if (self->client->ps.torsoAnim == BOTH_KYLE_PA_2) { //knee throw if (self->client->grappleState == 1) { //knee to the face if (self->client->ps.torsoTimer < 3200) { int grapplerAnim = grappler->client->ps.torsoAnim; int grapplerTime = grappler->client->ps.torsoTimer; G_Damage(grappler, self, self, NULL, self->client->ps.origin, 20, 0, MOD_MELEE); //G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) ); //it might try to put them into a pain anim or something, so override it back again if (grappler->health > 0) { grappler->client->ps.torsoAnim = grapplerAnim; grappler->client->ps.torsoTimer = grapplerTime; grappler->client->ps.legsAnim = grapplerAnim; grappler->client->ps.legsTimer = grapplerTime; grappler->client->ps.weaponTime = grapplerTime; } self->client->grappleState++; } } else if (self->client->grappleState == 2) { //smashed on the ground if (self->client->ps.torsoTimer < 2000) { //G_Damage(grappler, self, self, NULL, self->client->ps.origin, 10, 0, MOD_MELEE); //don't do damage on this one, it would look very freaky if they died G_EntitySound( grappler, CHAN_VOICE, G_SoundIndex("*pain100.wav") ); //G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) ); self->client->grappleState++; } } else { //and another smash if (self->client->ps.torsoTimer < 1000) { G_Damage(grappler, self, self, NULL, self->client->ps.origin, 30, 0, MOD_MELEE); //G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) ); //it might try to put them into a pain anim or something, so override it back again if (grappler->health > 0) { grappler->client->ps.torsoTimer = 1000; //G_SetAnim(grappler, &grappler->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0); grappler->client->grappleState = 0; } else { //override death anim grappler->client->ps.torsoAnim = BOTH_DEADFLOP1; grappler->client->ps.legsAnim = BOTH_DEADFLOP1; } self->client->grappleState = 0; } } } else { //? } } } } }
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles, qboolean spit ) { gentity_t *tent; // use temp events at source and destination to prevent the effect // from getting dropped by a second player event /*freeze if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) { freeze*/ if ( (g_gametype.integer == GT_FREEZE && !is_spectator( player->client )) || (g_gametype.integer != GT_FREEZE && player->client->sess.sessionTeam != TEAM_SPECTATOR) ) { //freeze tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = player->s.clientNum; tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = player->s.clientNum; } // unlink to make sure it can't possibly interfere with G_KillBox //player->relink = 0; trap_UnlinkEntity (player); VectorCopy ( origin, player->client->ps.origin ); player->client->ps.origin[2] += 1; // spit the player out if (spit == qtrue) { AngleVectors( angles, player->client->ps.velocity, NULL, NULL ); VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity ); player->client->ps.pm_time = 160; // hold time player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } // toggle the teleport bit so the client knows to not lerp player->client->ps.eFlags ^= EF_TELEPORT_BIT; //unlagged - backward reconciliation #3 // we don't want players being backward-reconciled back through teleporters G_ResetHistory( player ); //unlagged - backward reconciliation #3 // set angles if (player->client->pers.fixedTeleporterAngles == qfalse) SetClientViewAngle( player, angles ); // kill anything at the destination /*freeze if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) { freeze*/ if ( (g_gametype.integer == GT_FREEZE && !is_spectator( player->client )) || (g_gametype.integer != GT_FREEZE && player->client->sess.sessionTeam != TEAM_SPECTATOR) ) { //freeze G_KillBox (player); } // save results of pmove BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue ); // use the precise origin for linking VectorCopy( player->client->ps.origin, player->r.currentOrigin ); /*freeze if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) { freeze*/ if ( (g_gametype.integer == GT_FREEZE && !is_spectator( player->client )) || (g_gametype.integer != GT_FREEZE && player->client->sess.sessionTeam != TEAM_SPECTATOR) ) { //freeze trap_LinkEntity (player); } }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; int flags; int savedPing; int savedTeam; qboolean update = qfalse; save_position_t *pos = NULL; index = ent - g_entities; client = ent->client; G_UpdateSpawnCounts(); client->pers.lastSpawnTime = level.time; if (client->sess.sessionTeam != TEAM_AXIS && client->sess.sessionTeam != TEAM_ALLIES) { spawnPoint = SelectSpectatorSpawnPoint(spawn_origin, spawn_angles); } else { spawnPoint = SelectPlayerSpawnPoint(client->sess.sessionTeam, spawn_origin, spawn_angles, client->sess.spawnObjectiveIndex); } client->pers.teamState.state = TEAM_ACTIVE; // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & EF_TELEPORT_BIT; flags ^= EF_TELEPORT_BIT; flags |= (client->ps.eFlags & EF_VOTED); // clear everything but the persistant data ent->s.eFlags &= ~EF_MOUNTEDTANK; // Nico, notify timerun_stop notify_timerun_stop(ent, 0); ent->client->sess.timerunActive = qfalse; saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; savedTeam = client->ps.teamNum; for (i = 0 ; i < MAX_PERSISTANT ; i++) { persistant[i] = client->ps.persistant[i]; } memset(client, 0, sizeof (*client)); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; client->ps.teamNum = savedTeam; for (i = 0 ; i < MAX_PERSISTANT ; i++) { client->ps.persistant[i] = persistant[i]; } // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->ps.persistant[PERS_HWEAPON_USE] = 0; client->airOutTime = level.time + 12000; // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; // DHM - Nerve :: Init to -1 on first spawn; ent->props_frame_state = -1; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy(playerMins, ent->r.mins); VectorCopy(playerMaxs, ent->r.maxs); // Ridah, setup the bounding boxes and viewheights for prediction VectorCopy(ent->r.mins, client->ps.mins); VectorCopy(ent->r.maxs, client->ps.maxs); client->ps.crouchViewHeight = CROUCH_VIEWHEIGHT; client->ps.standViewHeight = DEFAULT_VIEWHEIGHT; client->ps.deadViewHeight = DEAD_VIEWHEIGHT; client->ps.crouchMaxZ = client->ps.maxs[2] - (client->ps.standViewHeight - client->ps.crouchViewHeight); client->ps.runSpeedScale = 0.8; client->ps.sprintSpeedScale = 1.1; client->ps.crouchSpeedScale = 0.25; client->ps.weaponstate = WEAPON_READY; // Rafael client->ps.friction = 1.0; // done. // TTimo // retrieve from the persistant storage (we use this in pmoveExt_t beause we need it in bg_*) client->pmext.bAutoReload = client->pers.bAutoReloadAux; // done client->ps.clientNum = index; trap_GetUsercmd(client - level.clients, &ent->client->pers.cmd); // NERVE - SMF - moved this up here if (client->sess.playerType != client->sess.latchPlayerType) { update = qtrue; } client->sess.playerType = client->sess.latchPlayerType; if (client->sess.playerWeapon != client->sess.latchPlayerWeapon) { client->sess.playerWeapon = client->sess.latchPlayerWeapon; update = qtrue; } client->sess.playerWeapon2 = client->sess.latchPlayerWeapon2; if (update) { ClientUserinfoChanged(index); } G_UpdateCharacter(client); SetWolfSpawnWeapons(client); client->pers.maxHealth = 125; client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->pers.cmd.weapon = ent->client->ps.weapon; // dhm - end ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH]; G_SetOrigin(ent, spawn_origin); VectorCopy(spawn_origin, client->ps.origin); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; SetClientViewAngle(ent, spawn_angles); if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { trap_LinkEntity(ent); } client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; client->latched_wbuttons = 0; //----(SA) added // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink(ent - g_entities); // positively link the client, even if the command times are weird if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); VectorCopy(ent->client->ps.origin, ent->r.currentOrigin); trap_LinkEntity(ent); } // run the presend to set anything else ClientEndFrame(ent); // set idle animation on weapon ent->client->ps.weapAnim = ((ent->client->ps.weapAnim & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | PM_IdleAnimForWeapon(ent->client->ps.weapon); // clear entity state values BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); // show_bug.cgi?id=569 G_ResetMarkers(ent); // RF, start the scripting system if (client->sess.sessionTeam != TEAM_SPECTATOR) { // RF, call entity scripting event G_Script_ScriptEvent(ent, "playerstart", ""); } // Nico, autoload position if (ent->client->pers.autoLoad && !ent->client->sess.lastDieWasASelfkill && (ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES)) { if (ent->client->sess.sessionTeam == TEAM_ALLIES) { pos = ent->client->sess.alliesSaves; } else { pos = ent->client->sess.axisSaves; } if (pos->valid) { VectorCopy(pos->origin, ent->client->ps.origin); // Nico, load angles if cg_loadViewAngles = 1 if (ent->client->pers.loadViewAngles) { SetClientViewAngle(ent, pos->vangles); } // Nico, load saved weapon if cg_loadWeapon = 1 if (ent->client->pers.loadWeapon) { ent->client->ps.weapon = pos->weapon; } VectorClear(ent->client->ps.velocity); if (ent->client->ps.stats[STAT_HEALTH] < 100 && ent->client->ps.stats[STAT_HEALTH] > 0) { ent->health = 100; } } } }
/** Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; gentity_t *tent; int flags; int eventSequence; char userinfo[MAX_INFO_STRING]; index = ent - g_entities; client = ent->client; // follow other players when eliminated if (!level.warmupTime && g_gametype.integer == GT_ELIMINATION && client->sess.sessionTeam != TEAM_SPECTATOR) { if (level.roundStarted) { client->sess.spectatorState = SPECTATOR_FREE; client->eliminated = qtrue; return; } else { client->sess.spectatorState = SPECTATOR_NOT; client->eliminated = qfalse; } } VectorClear(spawn_origin); // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if (client->sess.sessionTeam == TEAM_SPECTATOR) { SelectSpectatorSpawnPoint(spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_DEFRAG) { spawnPoint = SelectDefragSpawnPoint(spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_CTF) { spawnPoint = SelectCTFSpawnPoint(client->sess.sessionTeam, spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } else if (client->pers.lastKiller) { spawnPoint = SelectSpawnPoint(client->pers.lastKiller->ps.origin, spawn_origin, spawn_angles, ent->r.svFlags & SVF_BOT); } else { spawnPoint = SelectSpawnPoint(client->ps.origin, spawn_origin, spawn_angles, ent->r.svFlags & SVF_BOT); } if (!spawnPoint) { G_Error("Cannot find a spawn point.\n"); return; } // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT); flags ^= EF_TELEPORT_BIT; // unlagged - backward reconciliation #3 // we don't want players being backward-reconciled to the place they died G_ResetHistory( ent ); // and this is as good a time as any to clear the saved state ent->client->saved.leveltime = 0; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; eventSequence = client->ps.eventSequence; Com_Memcpy(persistant, client->ps.persistant, sizeof persistant); Com_Memset(client, 0, sizeof(*client)); client->pers = saved; client->sess = savedSess; client->ps.eventSequence = eventSequence; Com_Memcpy(client->ps.persistant, persistant, sizeof client->ps.persistant); // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo(index, userinfo, sizeof(userinfo)); // set max health client->pers.maxHealth = atoi(Info_ValueForKey(userinfo, "handicap")); if (client->pers.maxHealth < 1 || client->pers.maxHealth > 100) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags &= ~FL_NO_KNOCKBACK; ent->flags &= ~FL_FORCE_GESTURE; VectorCopy(playerMins, ent->r.mins); VectorCopy(playerMaxs, ent->r.maxs); client->ps.clientNum = index; ClientGiveWeapons(client); if (g_gametype.integer == GT_ELIMINATION) { client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] * 2; client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH] * 1.5; } else { client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; } ent->health = client->ps.stats[STAT_HEALTH]; G_SetOrigin(ent, spawn_origin); VectorCopy(spawn_origin, client->ps.origin); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd(client - level.clients, &ent->client->pers.cmd); SetClientViewAngle(ent, spawn_angles); // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if (level.intermissiontime) { // move players to intermission MoveClientToIntermission(ent); } else if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { G_KillBox(ent); // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); // positively link the client, even if the command times are weird VectorCopy(ent->client->ps.origin, ent->r.currentOrigin); tent = G_TempEntity(ent->client->ps.origin, EV_PLAYER_TELEPORT_IN); tent->s.clientNum = ent->s.clientNum; trap_LinkEntity (ent); } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.totalTime - 100; client->pers.cmd.serverTime = level.totalTime; ClientThink(ent-g_entities); // run the presend to set anything else, follow spectators wait // until all clients have been reconnected after map_restart if (ent->client->sess.spectatorState != SPECTATOR_FOLLOW) { ClientEndFrame(ent); } // clear entity state values BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); }
//---------------------------------------------------------- void emplaced_gun_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { vec3_t fwd1, fwd2; if ( self->health <= 0 ) { // can't use a dead gun. return; } if ( self->svFlags & SVF_INACTIVE ) { return; // can't use inactive gun } if ( !activator->client ) { return; // only a client can use it. } if ( self->activator ) { // someone is already in the gun. return; } if ( other && other->client && G_IsRidingVehicle( other ) ) {//can't use eweb when on a vehicle return; } if ( activator && activator->client && G_IsRidingVehicle( activator ) ) {//can't use eweb when on a vehicle return; } // We'll just let the designers duke this one out....I mean, as to whether they even want to limit such a thing. if ( self->spawnflags & EMPLACED_FACING ) { // Let's get some direction vectors for the users AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL ); // Get the guns direction vector AngleVectors( self->pos1, fwd2, NULL, NULL ); float dot = DotProduct( fwd1, fwd2 ); // Must be reasonably facing the way the gun points ( 90 degrees or so ), otherwise we don't allow to use it. if ( dot < 0.0f ) { return; } } // don't allow using it again for half a second if ( self->delay + 500 < level.time ) { int oldWeapon = activator->s.weapon; if ( oldWeapon == WP_SABER ) { self->alt_fire = activator->client->ps.SaberActive(); } // swap the users weapon with the emplaced gun and add the ammo the gun has to the player activator->client->ps.weapon = self->s.weapon; Add_Ammo( activator, WP_EMPLACED_GUN, self->count ); activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN ); // Allow us to point from one to the other activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; G_RemoveWeaponModels( activator ); extern void ChangeWeapon( gentity_t *ent, int newWeapon ); if ( activator->NPC ) { ChangeWeapon( activator, WP_EMPLACED_GUN ); } else if ( activator->s.number == 0 ) { // we don't want for it to draw the weapon select stuff cg.weaponSelect = WP_EMPLACED_GUN; CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 ); } // Since we move the activator inside of the gun, we reserve a solid spot where they were standing in order to be able to get back out without being in solid if ( self->nextTrain ) {//you never know G_FreeEntity( self->nextTrain ); } self->nextTrain = G_Spawn(); //self->nextTrain->classname = "emp_placeholder"; self->nextTrain->contents = CONTENTS_MONSTERCLIP|CONTENTS_PLAYERCLIP;//hmm... playerclip too now that we're doing it for NPCs? G_SetOrigin( self->nextTrain, activator->client->ps.origin ); VectorCopy( activator->mins, self->nextTrain->mins ); VectorCopy( activator->maxs, self->nextTrain->maxs ); gi.linkentity( self->nextTrain ); //need to inflate the activator's mins/maxs since the gunsit anim puts them outside of their bbox VectorSet( activator->mins, -24, -24, -24 ); VectorSet( activator->maxs, 24, 24, 40 ); // Move the activator into the center of the gun. For NPC's the only way the can get out of the gun is to die. VectorCopy( self->s.origin, activator->client->ps.origin ); activator->client->ps.origin[2] += 30; // move them up so they aren't standing in the floor gi.linkentity( activator ); // the gun will track which weapon we used to have self->s.weapon = oldWeapon; // Lock the player activator->client->ps.eFlags |= EF_LOCKED_TO_WEAPON; activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; self->delay = level.time; // can't disconnect from the thing for half a second // Let the gun be considered an enemy //Ugh, so much AI code seems to assume enemies are clients, maybe this shouldn't be on, but it's too late in the game to change it now without knowing what side-effects this will have self->svFlags |= SVF_NONNPC_ENEMY; self->noDamageTeam = activator->client->playerTeam; // FIXME: don't do this, we'll try and actually put the player in this beast // move the player to the center of the gun // activator->contents = 0; // VectorCopy( self->currentOrigin, activator->client->ps.origin ); SetClientViewAngle( activator, self->pos1 ); //FIXME: should really wait a bit after spawn and get this just once? self->waypoint = NAV::GetNearestNode(self); #ifdef _DEBUG if ( self->waypoint == -1 ) { gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) ); } #endif G_Sound( self, G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" )); #ifdef _IMMERSION G_Force( self, G_ForceIndex( "fffx/weapons/emplaced/emplaced_mount", FF_CHANNEL_TOUCH ) ); #endif // _IMMERSION if ( !(self->spawnflags&EMPLACED_PLAYERUSE) || activator->s.number == 0 ) {//player-only usescript or any usescript // Run use script G_ActivateBehavior( self, BSET_USE ); } } }