Exemple #1
0
float G_GetNonLocDamageMod( class_t pcl )
{
	int            regionNum;
	damageRegion_t *region;

	for ( regionNum = 0; regionNum < g_numDamageRegions[ pcl ]; regionNum++ )
	{
		region = &g_damageRegions[ pcl ][ regionNum ];

		if ( !region->nonlocational )
		{
			continue;
		}

		if ( g_debugDamage.integer > 1 )
		{
			Com_Printf( "GetNonLocDamageModifier( pcl = %s ): "
			            S_COLOR_GREEN "FOUND:" S_COLOR_WHITE " %.2f\n",
			            BG_Class( pcl )->name, region->modifier );
		}

		return region->modifier;
	}

	if ( g_debugDamage.integer > 1 )
	{
		Com_Printf( "GetNonLocDamageModifier( pcl = %s ): "
		            S_COLOR_YELLOW "NOT FOUND:" S_COLOR_WHITE " %.2f.\n",
		            BG_Class( pcl )->name, 1.0f );
	}

	return 1.0f;
}
// FIXME: use nav handle instead of classes
void G_BotNavInit()
{
	int i;

	Log::Notice( "==== Bot Navigation Initialization ==== \n" );

	for ( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
	{
		classModelConfig_t *model;
		botClass_t bot;
		bot.polyFlagsInclude = POLYFLAGS_WALK;
		bot.polyFlagsExclude = POLYFLAGS_DISABLED;

		model = BG_ClassModelConfig( i );
		if ( model->navMeshClass )
		{
			if ( BG_ClassModelConfig( model->navMeshClass )->navMeshClass )
			{
				Log::Warn( "class '%s': navmesh reference target class '%s' must have its own navmesh",
				            BG_Class( i )->name, BG_Class( model->navMeshClass )->name );
				return;
			}

			continue;
		}

		Q_strncpyz( bot.name, BG_Class( i )->name, sizeof( bot.name ) );

		if ( !trap_BotSetupNav( &bot, &model->navHandle ) )
		{
			return;
		}
	}
	navMeshLoaded = true;
}
Exemple #3
0
/*
=================
CG_GetColorCharForHealth
=================
*/
char CG_GetColorCharForHealth( int clientnum )
{
	char health_char = '2';
	int  healthPercent;
	int  maxHealth;
	int  curWeaponClass = cgs.clientinfo[ clientnum ].curWeaponClass;

	if ( cgs.clientinfo[ clientnum ].team == TEAM_ALIENS )
	{
		maxHealth = BG_Class( curWeaponClass )->health;
	}
	else
	{
		maxHealth = BG_Class( PCL_HUMAN )->health;
	}

	healthPercent = ( int )( 100.0f * ( float ) cgs.clientinfo[ clientnum ].health /
	                         ( float ) maxHealth );

	if ( healthPercent < 33 )
	{
		health_char = '1';
	}
	else if ( healthPercent < 67 )
	{
		health_char = '3';
	}

	return health_char;
}
Exemple #4
0
void G_WeightAttack( gentity_t *self, gentity_t *victim )
{
	float  weightDPS;
	int    attackerMass, victimMass, weightDamage;

	// weigth damage is only dealt between clients
	if ( !self->client || !victim->client )
	{
		return;
	}

	// don't do friendly fire
	if ( G_OnSameTeam( self, victim ) )
	{
		return;
	}

	// ignore invincible targets
	if ( !victim->takedamage )
	{
		return;
	}

	// attacker must be above victim
	if ( self->client->ps.origin[ 2 ] + self->r.mins[ 2 ] <
	     victim->s.origin[ 2 ] + victim->r.maxs[ 2 ] )
	{
		return;
	}

	// victim must be on the ground
	if ( victim->client->ps.groundEntityNum == ENTITYNUM_NONE )
	{
		return;
	}

	// check timer
	if ( victim->client->nextCrushTime > level.time )
	{
		return;
	}

	attackerMass = BG_Class( self->client->pers.classSelection )->mass;
	victimMass = BG_Class( victim->client->pers.classSelection )->mass;
	weightDPS = WEIGHTDMG_DMG_MODIFIER * MAX( attackerMass - victimMass, 0 );

	if ( weightDPS > WEIGHTDMG_DPS_THRESHOLD )
	{
		weightDamage = ( int )( weightDPS * ( WEIGHTDMG_REPEAT / 1000.0f ) );

		if ( weightDamage > 0 )
		{
			G_Damage( victim, self, self, NULL, victim->s.origin, weightDamage,
					  DAMAGE_NO_LOCDAMAGE, ModWeight( self ) );
		}
	}

	victim->client->nextCrushTime = level.time + WEIGHTDMG_REPEAT;
}
void G_WeightAttack( gentity_t *self, gentity_t *victim )
{
	float  weightDPS, weightDamage;
	int    attackerMass, victimMass;

	// weigth damage is only dealt between clients
	if ( !self->client || !victim->client )
	{
		return;
	}

	// don't do friendly fire
	if ( G_OnSameTeam( self, victim ) )
	{
		return;
	}

	// attacker must be above victim
	if ( self->client->ps.origin[ 2 ] + self->r.mins[ 2 ] <
	     victim->s.origin[ 2 ] + victim->r.maxs[ 2 ] )
	{
		return;
	}

	// victim must be on the ground
	if ( victim->client->ps.groundEntityNum == ENTITYNUM_NONE )
	{
		return;
	}

	// check timer
	if ( victim->client->nextCrushTime > level.time )
	{
		return;
	}

	attackerMass = BG_Class( self->client->pers.classSelection )->mass;
	victimMass = BG_Class( victim->client->pers.classSelection )->mass;
	weightDPS = WEIGHTDMG_DMG_MODIFIER * std::max( attackerMass - victimMass, 0 );

	if ( weightDPS > WEIGHTDMG_DPS_THRESHOLD )
	{
		weightDamage = weightDPS * ( WEIGHTDMG_REPEAT / 1000.0f );

		victim->entity->Damage(weightDamage, self, Vec3::Load(victim->s.origin), Util::nullopt,
		                       DAMAGE_NO_LOCDAMAGE, ModWeight(self));
	}

	victim->client->nextCrushTime = level.time + WEIGHTDMG_REPEAT;
}
void BotMoveToGoal( gentity_t *self )
{
	int    staminaJumpCost;
	vec3_t dir;
	VectorCopy( self->botMind->nav.dir, dir );

	if ( dir[ 2 ] < 0 )
	{
		dir[ 2 ] = 0;
		VectorNormalize( dir );
	}

	BotAvoidObstacles( self, dir );
	BotSeek( self, dir );

	staminaJumpCost = BG_Class( self->client->ps.stats[ STAT_CLASS ] )->staminaJumpCost;

	//dont sprint or dodge if we dont have enough stamina and are about to slow
	if ( self->client->pers.team == TEAM_HUMANS
	     && self->client->ps.stats[ STAT_STAMINA ] < staminaJumpCost )
	{
		usercmd_t *botCmdBuffer = &self->botMind->cmdBuffer;

		usercmdReleaseButton( botCmdBuffer->buttons, BUTTON_SPRINT );
		usercmdReleaseButton( botCmdBuffer->buttons, BUTTON_DODGE );

		// walk to regain stamina
		BotWalk( self, true );
	}
}
bool BotSprint( gentity_t *self, bool enable )
{
	usercmd_t *botCmdBuffer = &self->botMind->cmdBuffer;
	int       staminaJumpCost;

	if ( !enable )
	{
		usercmdReleaseButton( botCmdBuffer->buttons, BUTTON_SPRINT );
		return false;
	}

	staminaJumpCost = BG_Class( self->client->ps.stats[ STAT_CLASS ] )->staminaJumpCost;

	if ( self->client->pers.team == TEAM_HUMANS
	     && self->client->ps.stats[ STAT_STAMINA ] > staminaJumpCost
	     && self->botMind->botSkill.level >= 5 )
	{
		usercmdPressButton( botCmdBuffer->buttons, BUTTON_SPRINT );
		BotWalk( self, false );
		return true;
	}
	else
	{
		usercmdReleaseButton( botCmdBuffer->buttons, BUTTON_SPRINT );
		return false;
	}
}
Exemple #8
0
void G_ImpactAttack( gentity_t *self, gentity_t *victim )
{
	float  impactVelocity, impactEnergy;
	vec3_t knockbackDir;
	int    attackerMass, impactDamage;

	// self must be a client
	if ( !self->client )
	{
		return;
	}

	// ignore invincible targets
	if ( !victim->takedamage )
	{
		return;
	}

	// don't do friendly fire
	if ( G_OnSameTeam( self, victim ) )
	{
		return;
	}

	// attacker must be above victim
	if ( self->client->ps.origin[ 2 ] + self->r.mins[ 2 ] <
	     victim->s.origin[ 2 ] + victim->r.maxs[ 2 ] )
	{
		return;
	}

	// allow the granger airlifting ritual
	if ( victim->client && victim->client->ps.stats[ STAT_STATE2 ] & SS2_JETPACK_ACTIVE &&
	     ( self->client->pers.classSelection == PCL_ALIEN_BUILDER0 ||
	       self->client->pers.classSelection == PCL_ALIEN_BUILDER0_UPG ) )
	{
		return;
	}

	// calculate impact damage
	attackerMass = BG_Class( self->client->pers.classSelection )->mass;
	impactVelocity = fabs( self->client->pmext.fallImpactVelocity[ 2 ] ) * IMPACTDMG_QU_TO_METER; // in m/s
	impactEnergy = attackerMass * impactVelocity * impactVelocity; // in J
	impactDamage = ( int )( impactEnergy * IMPACTDMG_JOULE_TO_DAMAGE );

	// deal impact damage to both clients and structures, use a threshold for friendly fire
	if ( impactDamage > 0 )
	{
		// calculate knockback direction
		VectorSubtract( victim->s.origin, self->client->ps.origin, knockbackDir );
		VectorNormalize( knockbackDir );

		G_Damage( victim, self, self, knockbackDir, victim->s.origin, impactDamage,
		          DAMAGE_NO_LOCDAMAGE, ModWeight( self ) );
	}
}
bool BotShouldJump( gentity_t *self, gentity_t *blocker, const vec3_t dir )
{
	vec3_t playerMins;
	vec3_t playerMaxs;
	float jumpMagnitude;
	trace_t trace;
	const int TRACE_LENGTH = BOT_OBSTACLE_AVOID_RANGE;
	vec3_t end;

	//blocker is not on our team, so ignore
	if ( BotGetEntityTeam( self ) != BotGetEntityTeam( blocker ) )
	{
		return false;
	}

	//already normalized

	BG_ClassBoundingBox( ( class_t ) self->client->ps.stats[STAT_CLASS], playerMins, playerMaxs, nullptr, nullptr, nullptr );

	playerMins[2] += STEPSIZE;
	playerMaxs[2] += STEPSIZE;

	//Log::Debug(vtos(self->movedir));
	VectorMA( self->s.origin, TRACE_LENGTH, dir, end );

	//make sure we are moving into a block
	trap_Trace( &trace, self->s.origin, playerMins, playerMaxs, end, self->s.number, MASK_SHOT, 0 );
	if ( trace.fraction >= 1.0f || blocker != &g_entities[trace.entityNum] )
	{
		return false;
	}

	jumpMagnitude = BG_Class( ( class_t )self->client->ps.stats[STAT_CLASS] )->jumpMagnitude;

	//find the actual height of our jump
	jumpMagnitude = Square( jumpMagnitude ) / ( self->client->ps.gravity * 2 );

	//prepare for trace
	playerMins[2] += jumpMagnitude;
	playerMaxs[2] += jumpMagnitude;

	//check if jumping will clear us of entity
	trap_Trace( &trace, self->s.origin, playerMins, playerMaxs, end, self->s.number, MASK_SHOT, 0 );

	//if we can jump over it, then jump
	//note that we also test for a blocking barricade because barricades will collapse to let us through
	if ( blocker->s.modelindex == BA_A_BARRICADE || trace.fraction == 1.0f )
	{
		return true;
	}
	else
	{
		return false;
	}
}
void G_ImpactAttack( gentity_t *self, gentity_t *victim )
{
	float  impactVelocity, impactEnergy, impactDamage;
	vec3_t knockbackDir;
	int    attackerMass;

	// self must be a client
	if ( !self->client )
	{
		return;
	}

	// don't do friendly fire
	if ( G_OnSameTeam( self, victim ) )
	{
		return;
	}

	// attacker must be above victim
	if ( self->client->ps.origin[ 2 ] + self->r.mins[ 2 ] <
	     victim->s.origin[ 2 ] + victim->r.maxs[ 2 ] )
	{
		return;
	}

	// allow the granger airlifting ritual
	if ( victim->client && victim->client->ps.stats[ STAT_STATE2 ] & SS2_JETPACK_ACTIVE &&
	     ( self->client->pers.classSelection == PCL_ALIEN_BUILDER0 ||
	       self->client->pers.classSelection == PCL_ALIEN_BUILDER0_UPG ) )
	{
		return;
	}

	// calculate impact damage
	impactVelocity = fabs( self->client->pmext.fallImpactVelocity[ 2 ] ) * QU_TO_METER; // in m/s

	if (!impactVelocity) return;

	attackerMass = BG_Class( self->client->pers.classSelection )->mass;
	impactEnergy = attackerMass * impactVelocity * impactVelocity; // in J
	impactDamage = impactEnergy * IMPACTDMG_JOULE_TO_DAMAGE;

	// calculate knockback direction
	VectorSubtract( victim->s.origin, self->client->ps.origin, knockbackDir );
	VectorNormalize( knockbackDir );

	victim->entity->Damage((float)impactDamage, self, Vec3::Load(victim->s.origin),
						   Vec3::Load(knockbackDir), DAMAGE_NO_LOCDAMAGE, ModWeight(self));
}
Exemple #11
0
/*
==============
CG_CalculateWeaponPosition
==============
*/
static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles )
{
  float         scale;
  int           delta;
  float         fracsin;
  float         bob;
  weaponInfo_t  *weapon;

  weapon = &cg_weapons[ cg.predictedPlayerState.weapon ];

  VectorCopy( cg.refdef.vieworg, origin );
  VectorCopy( cg.refdefViewAngles, angles );

  // on odd legs, invert some angles
  if( cg.bobcycle & 1 )
    scale = -cg.xyspeed;
  else
    scale = cg.xyspeed;

  // gun angles from bobbing
  // bob amount is class dependant
  bob = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob;

  if( bob != 0 )
  {
    angles[ ROLL ] += scale * cg.bobfracsin * 0.005;
    angles[ YAW ] += scale * cg.bobfracsin * 0.01;
    angles[ PITCH ] += cg.xyspeed * cg.bobfracsin * 0.005;
  }

  // drop the weapon when landing
  if( !weapon->noDrift )
  {
    delta = cg.time - cg.landTime;
    if( delta < LAND_DEFLECT_TIME )
      origin[ 2 ] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME;
    else if( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME )
      origin[ 2 ] += cg.landChange*0.25 *
        ( LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta ) / LAND_RETURN_TIME;

    // idle drift
    scale = cg.xyspeed + 40;
    fracsin = sin( cg.time * 0.001 );
    angles[ ROLL ] += scale * fracsin * 0.01;
    angles[ YAW ] += scale * fracsin * 0.01;
    angles[ PITCH ] += scale * fracsin * 0.01;
  }
}
// TODO: Consider location as well as direction when both given.
void KnockbackComponent::HandleDamage(float amount, gentity_t* source, Util::optional<Vec3> location,
                                      Util::optional<Vec3> direction, int flags, meansOfDeath_t meansOfDeath) {
	if (!(flags & DAMAGE_KNOCKBACK)) return;
	if (amount <= 0.0f) return;

	if (!direction) {
		knockbackLogger.Warn("Received damage message with knockback flag set but no direction.");
		return;
	}

	if (Math::Length(direction.value()) == 0.0f) {
		knockbackLogger.Warn("Attempt to do knockback with null vector direction.");
		return;
	}

	// TODO: Remove dependency on client.
	gclient_t *client = entity.oldEnt->client;
	assert(client);

	// Check for immunity.
	if (client->noclip) return;
	if (client->sess.spectatorState != SPECTATOR_NOT) return;

	float mass = (float)BG_Class(client->ps.stats[ STAT_CLASS ])->mass;

	if (mass <= 0.0f) {
		knockbackLogger.Warn("Attempt to do knockback against target with no mass, assuming normal mass.");
		mass = KNOCKBACK_NORMAL_MASS;
	}

	float massMod  = Math::Clamp(KNOCKBACK_NORMAL_MASS / mass, KNOCKBACK_MIN_MASSMOD, KNOCKBACK_MAX_MASSMOD);
	float strength = amount * DAMAGE_TO_KNOCKBACK * massMod;

	// Change client velocity.
	Vec3 clientVelocity = Vec3::Load(client->ps.velocity);
	clientVelocity += Math::Normalize(direction.value()) * strength;
	clientVelocity.Store(client->ps.velocity);

	// Set pmove timer so that the client can't cancel out the movement immediately.
	if (!client->ps.pm_time) {
		client->ps.pm_time = KNOCKBACK_PMOVE_TIME;
		client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	}

	knockbackLogger.Debug("Knockback: client: %i, strength: %.1f (massMod: %.1f).",
	                      entity.oldEnt->s.number, strength, massMod);
}
bool BotJump( gentity_t *self )
{
	int staminaJumpCost;

	if ( self->client->pers.team == TEAM_HUMANS )
	{
		staminaJumpCost = BG_Class( self->client->ps.stats[ STAT_CLASS ] )->staminaJumpCost;

		if ( self->client->ps.stats[STAT_STAMINA] < staminaJumpCost )
		{
			return false;
		}
	}

	self->botMind->cmdBuffer.upmove = 127;
	return true;
}
Exemple #14
0
/**
 * Return common rank
 * @param self
 * @param target
 * @return 
 */
int BotTargetRank( gentity_t *self, gentity_t *target ) {
	float distance;
	float rank = 0;
	float damage;
	float damage_pct;
	distance = botGetDistanceBetweenPlayer(self, target);
	rank += 3000 / distance;
	//--- Add some rand chance (not so high)
	rank += G_Rand_Range(0, 10);
	//If we are attacking this target, increase the chance to stick to it (unless we haven't hit it within 5 secs):
	if(self->bot->Enemy == target) {
		if(!botHitTarget( self, 5000 )) {
			rank -= 30;
		} else {
			rank += 30;
		}
	}
	//If its attacking you
	if(self->client->lasthurt_client == target->s.number) {
		rank += 10;
	}
	if(target->client) {
		damage = self->credits[ target->client->ps.clientNum ];
		//How much it has damaged you
		damage_pct = (damage / (float)BG_Class( self->client->ps.stats[ STAT_CLASS ] )->health) * 100;
		if(damage_pct > 50) {
			rank += 20;
		} else if(damage_pct > 25) {
			rank += 10;
		}
	}
	//If target health is critical, increase its chances
	if(target->health < 50) { //First Sound warning
		rank += 25;
	}
	if(target->health < 25) { //Second Sound warning
		rank += 50;
	}
	//The enemies or my friends are my enemies!
	if(self->bot->Friend) {
		if(self->bot->Friend->bot && self->bot->Friend->bot->Enemy == target) {
			rank += 30;
		}
	}
	return rank;
}
Exemple #15
0
static void CG_CompleteClass( void )
{
	int i = 0;

	if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_ALIENS )
	{
		for ( i = PCL_ALIEN_BUILDER0; i < PCL_HUMAN; i++ )
		{
			trap_CompleteCallback( BG_Class( i )->name );
		}
	}
	else if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_HUMANS )
	{
		trap_CompleteCallback( BG_Weapon( WP_HBUILD )->name );
		trap_CompleteCallback( BG_Weapon( WP_MACHINEGUN )->name );
	}
}
static void CG_CompleteClass()
{
	int i = 0;

	if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_ALIENS )
	{
		// TODO: Add iterator for alien/human classes
		for ( i = PCL_ALIEN_BUILDER0; i < PCL_HUMAN_NAKED; i++ )
		{
			trap_CompleteCallback( BG_Class( i )->name );
		}
	}
	else if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_HUMANS )
	{
		trap_CompleteCallback( BG_Weapon( WP_HBUILD )->name );
		trap_CompleteCallback( BG_Weapon( WP_MACHINEGUN )->name );
	}
}
Exemple #17
0
/*
===============
G_CrushAttack

Should only be called if there was an impact between a tyrant and another player
===============
*/
void G_CrushAttack( gentity_t *ent, gentity_t *victim )
{
	vec3_t dir;
	float  jump;
	int    damage;

	if ( !victim->takedamage ||
	     ent->client->ps.origin[ 2 ] + ent->r.mins[ 2 ] <
	     victim->s.origin[ 2 ] + victim->r.maxs[ 2 ] ||
	     ( victim->client &&
	       victim->client->ps.groundEntityNum == ENTITYNUM_NONE ) )
	{
		return;
	}

	// Deal velocity based damage to target
	jump = BG_Class( ent->client->ps.stats[ STAT_CLASS ] )->jumpMagnitude;
	damage = ( ent->client->pmext.fallVelocity + jump ) *
	         -LEVEL4_CRUSH_DAMAGE_PER_V;

	if ( damage < 0 )
	{
		damage = 0;
	}

	// Players also get damaged periodically
	if ( victim->client &&
	     ent->client->lastCrushTime + LEVEL4_CRUSH_REPEAT < level.time )
	{
		ent->client->lastCrushTime = level.time;
		damage += LEVEL4_CRUSH_DAMAGE;
	}

	if ( damage < 1 )
	{
		return;
	}

	// Crush the victim over a period of time
	VectorSubtract( victim->s.origin, ent->client->ps.origin, dir );
	VectorNormalize( dir );
	G_Damage( victim, ent, ent, dir, victim->s.origin, damage,
	          DAMAGE_NO_LOCDAMAGE, MOD_LEVEL4_CRUSH );
}
Exemple #18
0
// this causes a compiler bug on mac MrC compiler
static void CG_StepOffset( void )
{
  float         steptime;
  int           timeDelta;
  vec3_t        normal;
  playerState_t *ps = &cg.predictedPlayerState;

  BG_GetClientNormal( ps, normal );

  steptime = BG_Class( ps->stats[ STAT_CLASS ] )->steptime;

  // smooth out stair climbing
  timeDelta = cg.time - cg.stepTime;
  if( timeDelta < steptime )
  {
    float stepChange = cg.stepChange
      * (steptime - timeDelta) / steptime;

    VectorMA( cg.refdef.vieworg, -stepChange, normal, cg.refdef.vieworg );
  }
}
/*
===============
CG_OffsetFirstPersonView

===============
*/
void CG_OffsetFirstPersonView( void )
{
	float         *origin;
	float         *angles;
	float         bob;
	float         ratio;
	float         delta;
	float         speed;
	float         f;
	vec3_t        predictedVelocity;
	int           timeDelta;
	float         bob2;
	vec3_t        normal, baseOrigin;
	playerState_t *ps = &cg.predictedPlayerState;

	BG_GetClientNormal( ps, normal );

	if ( cg.snap->ps.pm_type == PM_INTERMISSION )
	{
		return;
	}

	origin = cg.refdef.vieworg;
	angles = cg.refdefViewAngles;

	VectorCopy( origin, baseOrigin );

	// if dead, fix the angle and don't add any kick
	if ( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 )
	{
		angles[ ROLL ] = 40;
		angles[ PITCH ] = -15;
		angles[ YAW ] = cg.snap->ps.stats[ STAT_VIEWLOCK ];
		origin[ 2 ] += cg.predictedPlayerState.viewheight;
		return;
	}

	// add angles based on damage kick
	if ( cg.damageTime )
	{
		ratio = cg.time - cg.damageTime;

		if ( ratio < DAMAGE_DEFLECT_TIME )
		{
			ratio /= DAMAGE_DEFLECT_TIME;
			angles[ PITCH ] += ratio * cg.v_dmg_pitch;
			angles[ ROLL ] += ratio * cg.v_dmg_roll;
		}
		else
		{
			ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;

			if ( ratio > 0 )
			{
				angles[ PITCH ] += ratio * cg.v_dmg_pitch;
				angles[ ROLL ] += ratio * cg.v_dmg_roll;
			}
		}
	}

	// add pitch based on fall kick
#if 0
	ratio = ( cg.time - cg.landTime ) / FALL_TIME;

	if ( ratio < 0 )
	{
		ratio = 0;
	}

	angles[ PITCH ] += ratio * cg.fall_value;
#endif

	// add angles based on velocity
	VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );

	delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 0 ] );
	angles[ PITCH ] += delta * cg_runpitch.value;

	delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 1 ] );
	angles[ ROLL ] -= delta * cg_runroll.value;

	// add angles based on bob
	// bob amount is class-dependent

	if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
	{
		bob2 = 0.0f;
	}
	else
	{
		bob2 = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob;
	}

#define LEVEL4_FEEDBACK 10.0f

	//give a charging player some feedback
	if ( ps->weapon == WP_ALEVEL4 )
	{
		if ( ps->stats[ STAT_MISC ] > 0 )
		{
			float fraction = ( float ) ps->stats[ STAT_MISC ] /
			                 LEVEL4_TRAMPLE_CHARGE_MAX;

			if ( fraction > 1.0f )
			{
				fraction = 1.0f;
			}

			bob2 *= ( 1.0f + fraction * LEVEL4_FEEDBACK );
		}
	}

	if ( bob2 != 0.0f )
	{
		// make sure the bob is visible even at low speeds
		speed = cg.xyspeed > 200 ? cg.xyspeed : 200;

		delta = cg.bobfracsin * ( bob2 ) * speed;

		if ( cg.predictedPlayerState.pm_flags & PMF_DUCKED )
		{
			delta *= 3; // crouching
		}

		angles[ PITCH ] += delta;
		delta = cg.bobfracsin * ( bob2 ) * speed;

		if ( cg.predictedPlayerState.pm_flags & PMF_DUCKED )
		{
			delta *= 3; // crouching accentuates roll
		}

		if ( cg.bobcycle & 1 )
		{
			delta = -delta;
		}

		angles[ ROLL ] += delta;
	}

#define LEVEL3_FEEDBACK 20.0f

	//provide some feedback for pouncing
	if ( ( cg.predictedPlayerState.weapon == WP_ALEVEL3 ||
	       cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG ) &&
	     cg.predictedPlayerState.stats[ STAT_MISC ] > 0 )
	{
		float  fraction1, fraction2;
		vec3_t forward;

		AngleVectors( angles, forward, NULL, NULL );
		VectorNormalize( forward );

		fraction1 = ( float ) cg.predictedPlayerState.stats[ STAT_MISC ] /
		            LEVEL3_POUNCE_TIME_UPG;

		if ( fraction1 > 1.0f )
		{
			fraction1 = 1.0f;
		}

		fraction2 = -sin( fraction1 * M_PI / 2 );

		VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin );
	}

#define STRUGGLE_DIST 5.0f
#define STRUGGLE_TIME 250

	//allow the player to struggle a little whilst grabbed
	if ( cg.predictedPlayerState.pm_type == PM_GRABBED )
	{
		vec3_t    forward, right, up;
		usercmd_t cmd;
		int       cmdNum;
		float     fFraction, rFraction, uFraction;

		cmdNum = trap_GetCurrentCmdNumber();
		trap_GetUserCmd( cmdNum, &cmd );

		AngleVectors( angles, forward, right, up );

		fFraction = ( float )( cg.time - cg.forwardMoveTime ) / STRUGGLE_TIME;
		rFraction = ( float )( cg.time - cg.rightMoveTime ) / STRUGGLE_TIME;
		uFraction = ( float )( cg.time - cg.upMoveTime ) / STRUGGLE_TIME;

		if ( fFraction > 1.0f )
		{
			fFraction = 1.0f;
		}

		if ( rFraction > 1.0f )
		{
			rFraction = 1.0f;
		}

		if ( uFraction > 1.0f )
		{
			uFraction = 1.0f;
		}

		if ( cmd.forwardmove > 0 )
		{
			VectorMA( origin, STRUGGLE_DIST * fFraction, forward, origin );
		}
		else if ( cmd.forwardmove < 0 )
		{
			VectorMA( origin, -STRUGGLE_DIST * fFraction, forward, origin );
		}
		else
		{
			cg.forwardMoveTime = cg.time;
		}

		if ( cmd.rightmove > 0 )
		{
			VectorMA( origin, STRUGGLE_DIST * rFraction, right, origin );
		}
		else if ( cmd.rightmove < 0 )
		{
			VectorMA( origin, -STRUGGLE_DIST * rFraction, right, origin );
		}
		else
		{
			cg.rightMoveTime = cg.time;
		}

		if ( cmd.upmove > 0 )
		{
			VectorMA( origin, STRUGGLE_DIST * uFraction, up, origin );
		}
		else if ( cmd.upmove < 0 )
		{
			VectorMA( origin, -STRUGGLE_DIST * uFraction, up, origin );
		}
		else
		{
			cg.upMoveTime = cg.time;
		}
	}

	// this *feels* more realisitic for humans <- this comment feels very descriptive
	if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_HUMANS &&
	     cg.predictedPlayerState.pm_type == PM_NORMAL )
	{
		angles[ PITCH ] += cg.bobfracsin * bob2 * 0.5;
	}

	// add view height
	VectorMA( origin, ps->viewheight, normal, origin );

	// smooth out duck height changes
	timeDelta = cg.time - cg.duckTime;

	if ( timeDelta < DUCK_TIME )
	{
		cg.refdef.vieworg[ 2 ] -= cg.duckChange
		                          * ( DUCK_TIME - timeDelta ) / DUCK_TIME;
	}

	// add bob height
	bob = cg.bobfracsin * cg.xyspeed * bob2;

	if ( bob > 6 )
	{
		bob = 6;
	}

	VectorMA( origin, bob, normal, origin );

	// add fall height
	delta = cg.time - cg.landTime;

	if ( delta < LAND_DEFLECT_TIME )
	{
		f = delta / LAND_DEFLECT_TIME;
		cg.refdef.vieworg[ 2 ] += cg.landChange * f;
	}
	else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME )
	{
		delta -= LAND_DEFLECT_TIME;
		f = 1.0 - ( delta / LAND_RETURN_TIME );
		cg.refdef.vieworg[ 2 ] += cg.landChange * f;
	}

	// add step offset
	CG_StepOffset();
}
Exemple #20
0
static int CG_CalcFov( void )
{
  float     y;
  float     phase;
  float     v;
  int       contents;
  float     fov_x, fov_y;
  float     zoomFov;
  float     f;
  int       inwater;
  int       attribFov;
  usercmd_t cmd;
  usercmd_t oldcmd;
  int       cmdNum;

  cmdNum = trap_GetCurrentCmdNumber( );
  trap_GetUserCmd( cmdNum, &cmd );
  trap_GetUserCmd( cmdNum - 1, &oldcmd );

  // switch follow modes if necessary: cycle between free -> follow -> third-person follow
  if( cmd.buttons & BUTTON_USE_HOLDABLE && !( oldcmd.buttons & BUTTON_USE_HOLDABLE ) )
  {
    if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) 
    {
      if( !cg.chaseFollow )
        cg.chaseFollow = qtrue;
      else
      {
        cg.chaseFollow = qfalse;
        trap_SendClientCommand( "follow\n" );
      }
    }
    else if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
      trap_SendClientCommand( "follow\n" );
  }

  if( cg.predictedPlayerState.pm_type == PM_INTERMISSION ||
      ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) || 
      ( cg.renderingThirdPerson ) )
  {
    // if in intermission or third person, use a fixed value
    fov_y = BASE_FOV_Y;
  }
  else
  {
    // don't lock the fov globally - we need to be able to change it
    attribFov = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fov * 0.75f;
    fov_y = attribFov;

    if ( fov_y < 1.0f )
      fov_y = 1.0f;
    else if ( fov_y > MAX_FOV_Y )
      fov_y = MAX_FOV_Y;

    if( cg.spawnTime > ( cg.time - FOVWARPTIME ) &&
        BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_FOVWARPS ) )
    {
      float fraction = (float)( cg.time - cg.spawnTime ) / FOVWARPTIME;

      fov_y = MAX_FOV_WARP_Y - ( ( MAX_FOV_WARP_Y - fov_y ) * fraction );
    }

    // account for zooms
    zoomFov = BG_Weapon( cg.predictedPlayerState.weapon )->zoomFov * 0.75f;
    if ( zoomFov < 1.0f )
      zoomFov = 1.0f;
    else if ( zoomFov > attribFov )
      zoomFov = attribFov;

    // only do all the zoom stuff if the client CAN zoom
    // FIXME: zoom control is currently hard coded to BUTTON_ATTACK2
    if( BG_Weapon( cg.predictedPlayerState.weapon )->canZoom )
    {
      if ( cg.zoomed )
      {
        f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;

        if ( f > 1.0f )
          fov_y = zoomFov;
        else
          fov_y = fov_y + f * ( zoomFov - fov_y );

        // BUTTON_ATTACK2 isn't held so unzoom next time
        if( !( cmd.buttons & BUTTON_ATTACK2 ) )
        {
          cg.zoomed   = qfalse;
          cg.zoomTime = MIN( cg.time, 
              cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
        }
      }
      else
      {
        f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;

        if ( f <= 1.0f )
          fov_y = zoomFov + f * ( fov_y - zoomFov );

        // BUTTON_ATTACK2 is held so zoom next time
        if( cmd.buttons & BUTTON_ATTACK2 )
        {
          cg.zoomed   = qtrue;
          cg.zoomTime = MIN( cg.time, 
              cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
        }
      }
    }
  }

  y = cg.refdef.height / tan( 0.5f * DEG2RAD( fov_y ) );
  fov_x = atan2( cg.refdef.width, y );
  fov_x = 2.0f * RAD2DEG( fov_x );

  // warp if underwater
  contents = CG_PointContents( cg.refdef.vieworg, -1 );

  if( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
  {
    phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2.0f;
    v = WAVE_AMPLITUDE * sin( phase );
    fov_x += v;
    fov_y -= v;
    inwater = qtrue;
  }
  else
    inwater = qfalse;

  if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) &&
      ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) &&
      cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 &&
      !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
  {
    float scale = 1.0f - (float)( cg.time - cg.poisonedTime ) /
                  BG_PlayerPoisonCloudTime( &cg.predictedPlayerState );
      
    phase = ( cg.time - cg.poisonedTime ) / 1000.0f * PCLOUD_ZOOM_FREQUENCY * M_PI * 2.0f;
    v = PCLOUD_ZOOM_AMPLITUDE * sin( phase ) * scale;
    fov_x += v;
    fov_y += v;
  }


  // set it
  cg.refdef.fov_x = fov_x;
  cg.refdef.fov_y = fov_y;

  if( !cg.zoomed )
    cg.zoomSensitivity = 1.0f;
  else
    cg.zoomSensitivity = cg.refdef.fov_y / 75.0f;

  return inwater;
}
Exemple #21
0
/*
===============
CG_OffsetFirstPersonView

===============
*/
void CG_OffsetFirstPersonView( void )
{
  float         *origin;
  float         *angles;
  float         bob;
  float         ratio;
  float         delta;
  float         speed;
  float         f;
  vec3_t        predictedVelocity;
  int           timeDelta;
  float         bob2;
  vec3_t        normal, baseOrigin;
  playerState_t *ps = &cg.predictedPlayerState;

  BG_GetClientNormal( ps, normal );

  if( cg.snap->ps.pm_type == PM_INTERMISSION )
    return;

  origin = cg.refdef.vieworg;
  angles = cg.refdefViewAngles;

  VectorCopy( origin, baseOrigin );

  // if dead, fix the angle and don't add any kick
  if( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 )
  {
    angles[ ROLL ] = 40;
    angles[ PITCH ] = -15;
    angles[ YAW ] = cg.snap->ps.stats[ STAT_VIEWLOCK ];
    origin[ 2 ] += cg.predictedPlayerState.viewheight;
    return;
  }
  // camera shake effect
  else if( cg.snap->ps.stats[ STAT_SHAKE ] > 0 )
  {
    float fac;

    fac = (float) cg.snap->ps.stats[ STAT_SHAKE ] *
          cg_cameraShakeMagnitude.value * 0.15f;

    angles[ 0 ] += crandom() * fac;
    angles[ 1 ] += crandom() * fac;
    angles[ 2 ] += crandom() * fac;
  }

  // add angles based on damage kick
  if( cg.damageTime )
  {
    ratio = cg.time - cg.damageTime;
    if( ratio < DAMAGE_DEFLECT_TIME )
    {
      ratio /= DAMAGE_DEFLECT_TIME;
      angles[ PITCH ] += ratio * cg.v_dmg_pitch;
      angles[ ROLL ] += ratio * cg.v_dmg_roll;
    }
    else
    {
      ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
      if( ratio > 0 )
      {
        angles[ PITCH ] += ratio * cg.v_dmg_pitch;
        angles[ ROLL ] += ratio * cg.v_dmg_roll;
      }
    }
  }

  // add pitch based on fall kick
#if 0
  ratio = ( cg.time - cg.landTime) / FALL_TIME;
  if (ratio < 0)
    ratio = 0;
  angles[PITCH] += ratio * cg.fall_value;
#endif

  // add angles based on velocity
  VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );

  delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 0 ] );
  angles[ PITCH ] += delta * cg_runpitch.value;

  delta = DotProduct( predictedVelocity, cg.refdef.viewaxis[ 1 ] );
  angles[ ROLL ] -= delta * cg_runroll.value;

  // add angles based on bob
  // bob amount is class dependant

  if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
    bob2 = 0.0f;
  else
    bob2 = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob;


#define LEVEL4_FEEDBACK  10.0f

  //give a charging player some feedback
  if( ps->weapon == WP_ALEVEL4 )
  {
    if( ps->stats[ STAT_MISC ] > 0 )
    {
      float fraction = (float)ps->stats[ STAT_MISC ] /
                       LEVEL4_TRAMPLE_CHARGE_MAX;

      if( fraction > 1.0f )
        fraction = 1.0f;

      bob2 *= ( 1.0f + fraction * LEVEL4_FEEDBACK );
    }
  }

  if( bob2 != 0.0f )
  {
    // make sure the bob is visible even at low speeds
    speed = cg.xyspeed > 200 ? cg.xyspeed : 200;

    delta = cg.bobfracsin * ( bob2 ) * speed;
    if( cg.predictedPlayerState.pm_flags & PMF_DUCKED )
      delta *= 3;   // crouching

    angles[ PITCH ] += delta;
    delta = cg.bobfracsin * ( bob2 ) * speed;
    if( cg.predictedPlayerState.pm_flags & PMF_DUCKED )
      delta *= 3;   // crouching accentuates roll

    if( cg.bobcycle & 1 )
      delta = -delta;

    angles[ ROLL ] += delta;
  }

#define LEVEL3_FEEDBACK  20.0f

  //provide some feedback for pouncing
  if( ( cg.predictedPlayerState.weapon == WP_ALEVEL3 ||
        cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG ) &&
      cg.predictedPlayerState.stats[ STAT_MISC ] > 0 )
  {
    float fraction1, fraction2;
    vec3_t forward;

    AngleVectors( angles, forward, NULL, NULL );
    VectorNormalize( forward );

    fraction1 = (float)cg.predictedPlayerState.stats[ STAT_MISC ] /
                LEVEL3_POUNCE_TIME_UPG;
    if( fraction1 > 1.0f )
      fraction1 = 1.0f;

    fraction2 = -sin( fraction1 * M_PI / 2 );

    VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin );
  }

#define STRUGGLE_DIST 5.0f
#define STRUGGLE_TIME 250

  //allow the player to struggle a little whilst grabbed
  if( cg.predictedPlayerState.pm_type == PM_GRABBED )
  {
    vec3_t    forward, right, up;
    usercmd_t cmd;
    int       cmdNum;
    float     fFraction, rFraction, uFraction;
    float     fFraction2, rFraction2, uFraction2;

    cmdNum = trap_GetCurrentCmdNumber();
    trap_GetUserCmd( cmdNum, &cmd );

    AngleVectors( angles, forward, right, up );

    fFraction = (float)( cg.time - cg.forwardMoveTime ) / STRUGGLE_TIME;
    rFraction = (float)( cg.time - cg.rightMoveTime ) / STRUGGLE_TIME;
    uFraction = (float)( cg.time - cg.upMoveTime ) / STRUGGLE_TIME;

    if( fFraction > 1.0f )
      fFraction = 1.0f;
    if( rFraction > 1.0f )
      rFraction = 1.0f;
    if( uFraction > 1.0f )
      uFraction = 1.0f;

    fFraction2 = -sin( fFraction * M_PI / 2 );
    rFraction2 = -sin( rFraction * M_PI / 2 );
    uFraction2 = -sin( uFraction * M_PI / 2 );

    if( cmd.forwardmove > 0 )
      VectorMA( origin, STRUGGLE_DIST * fFraction, forward, origin );
    else if( cmd.forwardmove < 0 )
      VectorMA( origin, -STRUGGLE_DIST * fFraction, forward, origin );
    else
      cg.forwardMoveTime = cg.time;

    if( cmd.rightmove > 0 )
      VectorMA( origin, STRUGGLE_DIST * rFraction, right, origin );
    else if( cmd.rightmove < 0 )
      VectorMA( origin, -STRUGGLE_DIST * rFraction, right, origin );
    else
      cg.rightMoveTime = cg.time;

    if( cmd.upmove > 0 )
      VectorMA( origin, STRUGGLE_DIST * uFraction, up, origin );
    else if( cmd.upmove < 0 )
      VectorMA( origin, -STRUGGLE_DIST * uFraction, up, origin );
    else
      cg.upMoveTime = cg.time;
  }

  if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) &&
      ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) &&
      !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
  {
    float scale, fraction, pitchFraction;
    
    scale = 1.0f - (float)( cg.time - cg.poisonedTime ) /
            BG_PlayerPoisonCloudTime( &cg.predictedPlayerState );
    if( scale < 0.0f )
      scale = 0.0f;

    fraction = sin( ( cg.time - cg.poisonedTime ) / 500.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) *
               scale;
    pitchFraction = sin( ( cg.time - cg.poisonedTime ) / 200.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) *
                    scale;

    angles[ ROLL ] += fraction * PCLOUD_ROLL_AMPLITUDE;
    angles[ YAW ] += fraction * PCLOUD_ROLL_AMPLITUDE;
    angles[ PITCH ] += pitchFraction * PCLOUD_ROLL_AMPLITUDE / 2.0f;
  }

  // this *feels* more realisitic for humans
  if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS &&
      ( cg.predictedPlayerState.pm_type == PM_NORMAL ||
        cg.predictedPlayerState.pm_type == PM_JETPACK ) )
  {
    angles[PITCH] += cg.bobfracsin * bob2 * 0.5;

    // heavy breathing effects //FIXME: sound
    if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < STAMINA_BREATHING_LEVEL )
    {
      float deltaBreath = ( cg.predictedPlayerState.stats[ STAT_STAMINA ] -
                            STAMINA_BREATHING_LEVEL ) / -250.0;
      float deltaAngle = cos( (float)cg.time/150.0 ) * deltaBreath;

      deltaAngle += ( deltaAngle < 0 ? -deltaAngle : deltaAngle ) * 0.5;

      angles[ PITCH ] -= deltaAngle;
    }
  }

//===================================

  // add view height
  VectorMA( origin, ps->viewheight, normal, origin );

  // smooth out duck height changes
  timeDelta = cg.time - cg.duckTime;
  if( timeDelta < DUCK_TIME)
  {
    cg.refdef.vieworg[ 2 ] -= cg.duckChange
      * ( DUCK_TIME - timeDelta ) / DUCK_TIME;
  }

  // add bob height
  bob = cg.bobfracsin * cg.xyspeed * bob2;

  if( bob > 6 )
    bob = 6;

  VectorMA( origin, bob, normal, origin );

  // add fall height
  delta = cg.time - cg.landTime;

  if( delta < LAND_DEFLECT_TIME )
  {
    f = delta / LAND_DEFLECT_TIME;
    cg.refdef.vieworg[ 2 ] += cg.landChange * f;
  }
  else if( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME )
  {
    delta -= LAND_DEFLECT_TIME;
    f = 1.0 - ( delta / LAND_RETURN_TIME );
    cg.refdef.vieworg[ 2 ] += cg.landChange * f;
  }

  // add step offset
  CG_StepOffset( );
}
Exemple #22
0
/*
==================
G_RewardAttackers

Function to distribute rewards to entities that killed this one.
==================
*/
void G_RewardAttackers( gentity_t *self )
{
	float     value, reward;
	int       playerNum, enemyDamage, maxHealth, damageShare;
	gentity_t *player;
	team_t    ownTeam, playerTeam;
	confidence_reason_t    reason;
	confidence_qualifier_t qualifier;

	// Only reward killing players and buildables
	if ( self->client )
	{
		ownTeam = self->client->pers.teamSelection;
		maxHealth = self->client->ps.stats[ STAT_MAX_HEALTH ];
		value = ( float )BG_GetValueOfPlayer( &self->client->ps );
	}
	else if ( self->s.eType == ET_BUILDABLE )
	{
		ownTeam = self->buildableTeam;
		maxHealth = BG_Buildable( self->s.modelindex )->health;
		value = ( float )BG_Buildable( self->s.modelindex )->value;

		// Give partial credits for buildables in construction
		if ( !self->spawned )
		{
			value *= ( float )( level.time - self->creationTime ) / BG_Buildable( self->s.modelindex )->buildTime;
		}
	}
	else
	{
		return;
	}

	enemyDamage = 0;

	// Sum up damage dealt by enemies
	for ( playerNum = 0; playerNum < level.maxclients; playerNum++ )
	{
		player = &g_entities[ playerNum ];
		playerTeam = player->client->pers.teamSelection;

		// Player must be on the other team
		if ( playerTeam == ownTeam || playerTeam <= TEAM_NONE || playerTeam >= NUM_TEAMS )
		{
			continue;
		}

		enemyDamage += self->credits[ playerNum ];
	}

	if ( enemyDamage <= 0 )
	{
		return;
	}

	// Give individual rewards
	for ( playerNum = 0; playerNum < level.maxclients; playerNum++ )
	{
		player = &g_entities[ playerNum ];
		playerTeam = player->client->pers.teamSelection;
		damageShare = self->credits[ playerNum ];

		// Clear reward array
		self->credits[ playerNum ] = 0;

		// Player must be on the other team
		if ( playerTeam == ownTeam || playerTeam <= TEAM_NONE || playerTeam >= NUM_TEAMS )
		{
			continue;
		}

		// Player must have dealt damage
		if ( damageShare <= 0 )
		{
			continue;
		}

		reward = value * ( damageShare / ( float )maxHealth );

		if ( reward <= 0.0f )
		{
			continue;
		}

		if ( self->s.eType == ET_BUILDABLE )
		{
			G_AddConfidenceToScore( player, reward );

			switch ( self->s.modelindex )
			{
				case BA_A_OVERMIND:
				case BA_H_REACTOR:
					reason = CONF_REAS_DESTR_CRUCIAL;
					break;

				case BA_A_ACIDTUBE:
				case BA_A_TRAPPER:
				case BA_A_HIVE:
				case BA_H_MGTURRET:
				case BA_H_TESLAGEN:
					reason = CONF_REAS_DESTR_AGGRESSIVE;
					break;

				default:
					reason = CONF_REAS_DESTR_SUPPORT;
			}

			qualifier = CONF_QUAL_NONE;

			G_AddConfidence( playerTeam, CONFIDENCE_DESTRUCTION, reason, qualifier, reward, player );
		}
		else
		{
			G_AddCreditsToScore( player, ( int )reward );
			G_AddCreditToClient( player->client, ( short )reward, qtrue );

			// Give confidence for killing non-naked players outside the friendly base
			switch ( self->client->ps.stats[ STAT_CLASS ] )
			{
				case PCL_ALIEN_LEVEL0:
				case PCL_ALIEN_BUILDER0:
				case PCL_ALIEN_BUILDER0_UPG:
					break;

				case PCL_HUMAN:
					// Treat a human just wearing light armor as naked
					if ( ( int )value <= BG_Class( PCL_HUMAN )->value +
					                     ( BG_Upgrade( UP_LIGHTARMOUR )->price / 2 ) )
					{
						break;
					}

				default:
					if ( G_InsideBase( player, qtrue ) || G_InsideBase( self, qfalse ) )
					{
						break;
					}

					qualifier = CONF_QUAL_OUTSIDE_OWN_BASE;

					G_AddConfidence( playerTeam, CONFIDENCE_KILLING, CONF_REAS_KILLING,
					                 qualifier, reward * CONFIDENCE_PER_CREDIT, player );
			}
		}
	}
}
Exemple #23
0
/*
==============
CG_EntityEvent

An entity has an event value
also called by CG_CheckPlayerstateEvents
==============
*/
void CG_EntityEvent( centity_t *cent, vec3_t position )
{
    entityState_t *es;
    int           event;
    vec3_t        dir;
    const char    *s;
    int           clientNum;
    clientInfo_t  *ci;
    int           steptime;

    if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
    {
        steptime = 200;
    }
    else
    {
        steptime = BG_Class( cg.snap->ps.stats[ STAT_CLASS ] )->steptime;
    }

    es = &cent->currentState;
    event = es->event & ~EV_EVENT_BITS;

    if ( cg_debugEvents.integer )
    {
        CG_Printf( "ent:%3i  event:%3i %s\n", es->number, event,
                   BG_EventName( event ) );
    }

    if ( !event )
    {
        return;
    }

    clientNum = es->clientNum;

    if ( clientNum < 0 || clientNum >= MAX_CLIENTS )
    {
        clientNum = 0;
    }

    ci = &cgs.clientinfo[ clientNum ];

    switch ( event )
    {
    case EV_FOOTSTEP:
        if ( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
        {
            if ( ci->footsteps == FOOTSTEP_CUSTOM )
            {
                trap_S_StartSound( NULL, es->number, CHAN_BODY,
                                   ci->customFootsteps[ rand() & 3 ] );
            }
            else
            {
                trap_S_StartSound( NULL, es->number, CHAN_BODY,
                                   cgs.media.footsteps[ ci->footsteps ][ rand() & 3 ] );
            }
        }

        break;

    case EV_FOOTSTEP_METAL:
        if ( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
        {
            if ( ci->footsteps == FOOTSTEP_CUSTOM )
            {
                trap_S_StartSound( NULL, es->number, CHAN_BODY,
                                   ci->customMetalFootsteps[ rand() & 3 ] );
            }
            else
            {
                trap_S_StartSound( NULL, es->number, CHAN_BODY,
                                   cgs.media.footsteps[ FOOTSTEP_METAL ][ rand() & 3 ] );
            }
        }

        break;

    case EV_FOOTSTEP_SQUELCH:
        if ( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
        {
            trap_S_StartSound( NULL, es->number, CHAN_BODY,
                               cgs.media.footsteps[ FOOTSTEP_FLESH ][ rand() & 3 ] );
        }

        break;

    case EV_FOOTSPLASH:
        if ( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
        {
            trap_S_StartSound( NULL, es->number, CHAN_BODY,
                               cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand() & 3 ] );
        }

        break;

    case EV_FOOTWADE:
        if ( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
        {
            trap_S_StartSound( NULL, es->number, CHAN_BODY,
                               cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand() & 3 ] );
        }

        break;

    case EV_SWIM:
        if ( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
        {
            trap_S_StartSound( NULL, es->number, CHAN_BODY,
                               cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand() & 3 ] );
        }

        break;

    case EV_FALL_SHORT:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound );

        if ( clientNum == cg.predictedPlayerState.clientNum )
        {
            // smooth landing z changes
            cg.landChange = -8;
            cg.landTime = cg.time;
        }

        break;

    case EV_FALL_MEDIUM:
        // use a general pain sound
        trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) );

        if ( clientNum == cg.predictedPlayerState.clientNum )
        {
            // smooth landing z changes
            cg.landChange = -16;
            cg.landTime = cg.time;
        }

        break;

    case EV_FALL_FAR:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
        cent->pe.painTime = cg.time; // don't play a pain sound right after this

        if ( clientNum == cg.predictedPlayerState.clientNum )
        {
            // smooth landing z changes
            cg.landChange = -24;
            cg.landTime = cg.time;
        }

        break;

    case EV_FALLING:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*falling1.wav" ) );
        break;

    case EV_STEP_4:
    case EV_STEP_8:
    case EV_STEP_12:
    case EV_STEP_16: // smooth out step up transitions
    case EV_STEPDN_4:
    case EV_STEPDN_8:
    case EV_STEPDN_12:
    case EV_STEPDN_16: // smooth out step down transitions
    {
        float oldStep;
        int   delta;
        int   step;

        if ( clientNum != cg.predictedPlayerState.clientNum )
        {
            break;
        }

        // if we are interpolating, we don't need to smooth steps
        if ( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ||
                cg_nopredict.integer || cg_synchronousClients.integer )
        {
            break;
        }

        // check for stepping up before a previous step is completed
        delta = cg.time - cg.stepTime;

        if ( delta < steptime )
        {
            oldStep = cg.stepChange * ( steptime - delta ) / steptime;
        }
        else
        {
            oldStep = 0;
        }

        // add this amount
        if ( event >= EV_STEPDN_4 )
        {
            step = 4 * ( event - EV_STEPDN_4 + 1 );
            cg.stepChange = oldStep - step;
        }
        else
        {
            step = 4 * ( event - EV_STEP_4 + 1 );
            cg.stepChange = oldStep + step;
        }

        if ( cg.stepChange > MAX_STEP_CHANGE )
        {
            cg.stepChange = MAX_STEP_CHANGE;
        }
        else if ( cg.stepChange < -MAX_STEP_CHANGE )
        {
            cg.stepChange = -MAX_STEP_CHANGE;
        }

        cg.stepTime = cg.time;
        break;
    }

    case EV_JUMP:
        trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );

        if ( BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
        {
            vec3_t surfNormal, refNormal = { 0.0f, 0.0f, 1.0f };
            vec3_t rotAxis;

            if ( clientNum != cg.predictedPlayerState.clientNum )
            {
                break;
            }

            //set surfNormal
            VectorCopy( cg.predictedPlayerState.grapplePoint, surfNormal );

            //if we are moving from one surface to another smooth the transition
            if ( !VectorCompare( surfNormal, cg.lastNormal ) && surfNormal[ 2 ] != 1.0f )
            {
                CrossProduct( refNormal, surfNormal, rotAxis );
                VectorNormalize( rotAxis );

                //add the op
                CG_addSmoothOp( rotAxis, 15.0f, 1.0f );
            }

            //copy the current normal to the lastNormal
            VectorCopy( surfNormal, cg.lastNormal );
        }

        break;

    case EV_LEV1_GRAB:
        trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL1Grab );
        break;

    case EV_LEV4_TRAMPLE_PREPARE:
        trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargePrepare );
        break;

    case EV_LEV4_TRAMPLE_START:
        //FIXME: stop cgs.media.alienL4ChargePrepare playing here
        trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargeStart );
        break;

    case EV_TAUNT:
        if ( !cg_noTaunt.integer )
        {
            trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
        }

        break;

    case EV_WATER_TOUCH:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrInSound );
        break;

    case EV_WATER_LEAVE:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound );
        break;

    case EV_WATER_UNDER:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound );
        break;

    case EV_WATER_CLEAR:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) );
        break;

    case EV_JETPACK_ENABLE:
        // TODO: Trigger jetpack enable animation
        break;

    case EV_JETPACK_DISABLE:
        // TODO: Trigger jetpack disable animation
        break;

    case EV_JETPACK_START:
        // TODO: Start jetpack gfx/sfx
        break;

    case EV_JETPACK_STOP:
        // TODO: Stop jetpack gfx/sfx
        break;

    case EV_NOAMMO:
        trap_S_StartSound( NULL, es->number, CHAN_WEAPON, cgs.media.weaponEmptyClick );
        break;

    case EV_CHANGE_WEAPON:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.selectSound );
        break;

    case EV_FIRE_WEAPON:
        CG_HandleFireWeapon( cent, WPM_PRIMARY );
        break;

    case EV_FIRE_WEAPON2:
        CG_HandleFireWeapon( cent, WPM_SECONDARY );
        break;

    case EV_FIRE_WEAPON3:
        CG_HandleFireWeapon( cent, WPM_TERTIARY );
        break;

    case EV_WEAPON_RELOAD:
        if ( cg_weapons[ es->eventParm ].wim[ WPM_PRIMARY ].reloadSound )
        {
            trap_S_StartSound( NULL, es->number, CHAN_WEAPON, cg_weapons[ es->eventParm ].wim[ WPM_PRIMARY ].reloadSound );
        }
        break;

    case EV_PLAYER_TELEPORT_IN:
        //deprecated
        break;

    case EV_PLAYER_TELEPORT_OUT:
        CG_PlayerDisconnect( position );
        break;

    case EV_BUILD_CONSTRUCT:
        break;

    case EV_BUILD_DESTROY:
        break;

    case EV_AMMO_REFILL:
    case EV_CLIPS_REFILL:
    case EV_FUEL_REFILL:
        // TODO: Add different sounds for EV_AMMO_REFILL, EV_CLIPS_REFILL, EV_FUEL_REFILL
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.repeaterUseSound );
        break;

    case EV_GRENADE_BOUNCE:
        if ( rand() & 1 )
        {
            trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound1 );
        }
        else
        {
            trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound2 );
        }
        break;

    case EV_WEAPON_HIT_ENTITY:
        CG_HandleWeaponHitEntity( es, position );
        break;

    case EV_WEAPON_HIT_ENVIRONMENT:
        CG_HandleWeaponHitWall( es, position );
        break;

    case EV_MISSILE_HIT_ENTITY:
        CG_HandleMissileHitEntity( es, position );
        break;

    // currently there is no support for metal sounds
    case EV_MISSILE_HIT_ENVIRONMENT:
    case EV_MISSILE_HIT_METAL:
        CG_HandleMissileHitWall( es, position );
        break;

    case EV_SHOTGUN:
        CG_HandleFireShotgun( es );
        break;

    case EV_HUMAN_BUILDABLE_DYING:
        CG_HumanBuildableDying( (buildable_t) es->modelindex, position );
        break;

    case EV_HUMAN_BUILDABLE_EXPLOSION:
        ByteToDir( es->eventParm, dir );
        CG_HumanBuildableExplosion( (buildable_t) es->modelindex, position, dir );
        break;

    case EV_ALIEN_BUILDABLE_EXPLOSION:
        ByteToDir( es->eventParm, dir );
        CG_AlienBuildableExplosion( position, dir );
        break;

    case EV_TESLATRAIL:
        cent->currentState.weapon = WP_TESLAGEN;
        {
            centity_t *source = &cg_entities[ es->generic1 ];
            centity_t *target = &cg_entities[ es->clientNum ];
            vec3_t    sourceOffset = { 0.0f, 0.0f, 28.0f };

            if ( !CG_IsTrailSystemValid( &source->muzzleTS ) )
            {
                source->muzzleTS = CG_SpawnNewTrailSystem( cgs.media.teslaZapTS );

                if ( CG_IsTrailSystemValid( &source->muzzleTS ) )
                {
                    CG_SetAttachmentCent( &source->muzzleTS->frontAttachment, source );
                    CG_SetAttachmentCent( &source->muzzleTS->backAttachment, target );
                    CG_AttachToCent( &source->muzzleTS->frontAttachment );
                    CG_AttachToCent( &source->muzzleTS->backAttachment );
                    CG_SetAttachmentOffset( &source->muzzleTS->frontAttachment, sourceOffset );

                    source->muzzleTSDeathTime = cg.time + cg_teslaTrailTime.integer;
                }
            }
        }
        break;

    case EV_GENERAL_SOUND:
        if ( cgs.gameSounds[ es->eventParm ] )
        {
            trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] );
        }
        else
        {
            s = CG_ConfigString( CS_SOUNDS + es->eventParm );
            trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) );
        }

        break;

    case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes
        if ( cgs.gameSounds[ es->eventParm ] )
        {
            trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] );
        }
        else
        {
            s = CG_ConfigString( CS_SOUNDS + es->eventParm );
            trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) );
        }

        break;

    case EV_PAIN:
        // local player sounds are triggered in CG_CheckLocalSounds,
        // so ignore events on the player
        if ( cent->currentState.number != cg.snap->ps.clientNum )
        {
            CG_PainEvent( cent, es->eventParm );
        }

        break;

    case EV_DEATH1:
    case EV_DEATH2:
    case EV_DEATH3:
        trap_S_StartSound( NULL, es->number, CHAN_VOICE,
                           CG_CustomSound( es->number, va( "*death%i.wav", event - EV_DEATH1 + 1 ) ) );
        break;

    case EV_OBITUARY:
        CG_Obituary( es );
        break;

    case EV_GIB_PLAYER:
        // no gibbing
        break;

    case EV_STOPLOOPINGSOUND:
        trap_S_StopLoopingSound( es->number );
        es->loopSound = 0;
        break;

    case EV_DEBUG_LINE:
        CG_Beam( cent );
        break;

    case EV_BUILD_DELAY:
        if ( clientNum == cg.predictedPlayerState.clientNum )
        {
            trap_S_StartLocalSound( cgs.media.buildableRepairedSound, CHAN_LOCAL_SOUND );
            cg.lastBuildAttempt = cg.time;
        }

        break;

    case EV_BUILD_REPAIR:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairSound );
        break;

    case EV_BUILD_REPAIRED:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairedSound );
        break;

    case EV_OVERMIND_ATTACK_1:
    case EV_OVERMIND_ATTACK_2:
        if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_ALIENS )
        {
            trap_S_StartLocalSound( cgs.media.alienOvermindAttack, CHAN_ANNOUNCER );
            CG_CenterPrint( va( "^%c%s", "31"[ event - EV_OVERMIND_ATTACK_1 ], _( "The Overmind is under attack!" ) ), 200, GIANTCHAR_WIDTH * 4 );
        }

        break;

    case EV_OVERMIND_DYING:
        if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_ALIENS )
        {
            trap_S_StartLocalSound( cgs.media.alienOvermindDying, CHAN_ANNOUNCER );
            CG_CenterPrint( _( "^1The Overmind is dying!" ), 200, GIANTCHAR_WIDTH * 4 );
        }

        break;

    case EV_REACTOR_ATTACK_1:
    case EV_REACTOR_ATTACK_2:
        if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_HUMANS )
        {
            CG_CenterPrint( va( "^%c%s", "31"[ event - EV_REACTOR_ATTACK_1 ], _( "The reactor is under attack!" ) ), 200, GIANTCHAR_WIDTH * 4 );
        }

        break;

    case EV_REACTOR_DYING:
        if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_HUMANS )
        {
            CG_CenterPrint( _( "^1The reactor is about to explode!" ), 200, GIANTCHAR_WIDTH * 4 );
        }

        break;

    case EV_WARN_ATTACK:
        // if eventParm is non-zero, this is for humans and there's a nearby reactor or repeater, otherwise it's for aliens
        if ( es->eventParm >= MAX_CLIENTS && es->eventParm < MAX_GENTITIES )
        {
            const char *location;
            qboolean    base = cg_entities[ es->eventParm ].currentState.modelindex == BA_H_REACTOR;
            centity_t  *locent = CG_GetLocation( cg_entities[ es->eventParm ].currentState.origin );

            CG_CenterPrint( base ? _( "Our base is under attack!" ) : _( "A forward base is under attack!" ), 200, GIANTCHAR_WIDTH * 4 );

            if ( locent )
            {
                location = CG_ConfigString( CS_LOCATIONS + locent->currentState.generic1 );
            }
            else
            {
                location = CG_ConfigString( CS_LOCATIONS );
            }

            if ( location && *location )
            {
                Com_Printf( _( "%s Under attack – %s\n" ), base ? "[reactor]" : "[repeater]", location );
            }
            else
            {
                Com_Printf( _( "%s Under attack\n" ), base ? "[reactor]" : "[repeater]" );
            }
        }
        else // this is for aliens, and the overmind is in range
        {
            CG_CenterPrint( _( "Our base is under attack!" ), 200, GIANTCHAR_WIDTH * 4 );
        }

        break;

    case EV_MGTURRET_SPINUP:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.turretSpinupSound );
        break;

    case EV_OVERMIND_SPAWNS:
        if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_ALIENS )
        {
            trap_S_StartLocalSound( cgs.media.alienOvermindSpawns, CHAN_ANNOUNCER );
            CG_CenterPrint( "The Overmind needs spawns!", 200, GIANTCHAR_WIDTH * 4 );
        }

        break;

    case EV_ALIEN_EVOLVE:
        trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienEvolveSound );
        {
            particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienEvolvePS );

            if ( CG_IsParticleSystemValid( &ps ) )
            {
                CG_SetAttachmentCent( &ps->attachment, cent );
                CG_AttachToCent( &ps->attachment );
            }
        }

        if ( es->number == cg.clientNum )
        {
            CG_ResetPainBlend();
            cg.spawnTime = cg.time;
        }

        break;

    case EV_ALIEN_EVOLVE_FAILED:
        if ( clientNum == cg.predictedPlayerState.clientNum )
        {
            //FIXME: change to "negative" sound
            trap_S_StartLocalSound( cgs.media.buildableRepairedSound, CHAN_LOCAL_SOUND );
            cg.lastEvolveAttempt = cg.time;
        }

        break;

    case EV_ALIEN_ACIDTUBE:
    {
        particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienAcidTubePS );

        if ( CG_IsParticleSystemValid( &ps ) )
        {
            CG_SetAttachmentCent( &ps->attachment, cent );
            ByteToDir( es->eventParm, dir );
            CG_SetParticleSystemNormal( ps, dir );
            CG_AttachToCent( &ps->attachment );
        }
    }
    break;

    case EV_MEDKIT_USED:
        trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.medkitUseSound );
        break;

    case EV_PLAYER_RESPAWN:
        if ( es->number == cg.clientNum )
        {
            cg.spawnTime = cg.time;
        }

        break;

    case EV_LEV2_ZAP:
        CG_Level2Zap( es );
        break;

    case EV_HIT:
        cg.hitTime = cg.time;
        break;

    case EV_MOMENTUM:
        CG_Momentum( es );
        break;

    default:
        CG_Error( "Unknown event: %i", event );
    }
}
Exemple #24
0
/*
===========
ClientUserInfoChanged

Called from ClientConnect when the player first connects and
directly by the server system when the player updates a userinfo variable.

The game can override any of the settings and call trap_SetUserinfo
if desired.
============
*/
char *ClientUserinfoChanged( int clientNum, qboolean forceName )
{
    gentity_t *ent;
    char      *s;
    char      model[ MAX_QPATH ];
    char      buffer[ MAX_QPATH ];
    char      filename[ MAX_QPATH ];
    char      oldname[ MAX_NAME_LENGTH ];
    char      newname[ MAX_NAME_LENGTH ];
    char      err[ MAX_STRING_CHARS ];
    qboolean  revertName = qfalse;
    gclient_t *client;
    char      userinfo[ MAX_INFO_STRING ];

    ent = g_entities + clientNum;
    client = ent->client;

    trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );

    // check for malformed or illegal info strings
    if( !Info_Validate(userinfo) )
    {
        trap_SendServerCommand( ent - g_entities,
                                "disconnect \"illegal or malformed userinfo\n\"" );
        trap_DropClient( ent - g_entities,
                         "dropped: illegal or malformed userinfo");
        return "Illegal or malformed userinfo";
    }
    // If their userinfo overflowed, tremded is in the process of disconnecting them.
    // If we send our own disconnect, it won't work, so just return to prevent crashes later
    //  in this function. This check must come after the Info_Validate call.
    else if( !userinfo[ 0 ] )
        return "Empty (overflowed) userinfo";

    // stickyspec toggle
    s = Info_ValueForKey( userinfo, "cg_stickySpec" );
    client->pers.stickySpec = atoi( s ) != 0;

    // set name
    Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
    s = Info_ValueForKey( userinfo, "name" );
    G_ClientCleanName( s, newname, sizeof( newname ) );

    if( strcmp( oldname, newname ) )
    {
        if( !forceName && client->pers.namelog->nameChangeTime &&
                level.time - client->pers.namelog->nameChangeTime <=
                g_minNameChangePeriod.value * 1000 )
        {
            trap_SendServerCommand( ent - g_entities, va(
                                        "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"",
                                        g_minNameChangePeriod.integer ) );
            revertName = qtrue;
        }
        else if( !forceName && g_maxNameChanges.integer > 0 &&
                 client->pers.namelog->nameChanges >= g_maxNameChanges.integer  )
        {
            trap_SendServerCommand( ent - g_entities, va(
                                        "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
                                        g_maxNameChanges.integer ) );
            revertName = qtrue;
        }
        else if( !forceName && client->pers.namelog->muted )
        {
            trap_SendServerCommand( ent - g_entities,
                                    "print \"You cannot change your name while you are muted\n\"" );
            revertName = qtrue;
        }
        else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
        {
            trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) );
            revertName = qtrue;
        }

        if( revertName )
        {
            Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer",
                        sizeof( client->pers.netname ) );
            Info_SetValueForKey( userinfo, "name", oldname );
            trap_SetUserinfo( clientNum, userinfo );
        }
        else
        {
            G_CensorString( client->pers.netname, newname,
                            sizeof( client->pers.netname ), ent );
            if( !forceName && client->pers.connected == CON_CONNECTED )
            {
                client->pers.namelog->nameChangeTime = level.time;
                client->pers.namelog->nameChanges++;
            }
            if( *oldname )
            {
                G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n",
                             clientNum, client->pers.ip.str, client->pers.guid,
                             oldname, client->pers.netname,
                             DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
            }
        }
        G_namelog_update_name( client );
    }

    if( client->pers.classSelection == PCL_NONE )
    {
        //This looks hacky and frankly it is. The clientInfo string needs to hold different
        //model details to that of the spawning class or the info change will not be
        //registered and an axis appears instead of the player model. There is zero chance
        //the player can spawn with the battlesuit, hence this choice.
        Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName,
                     BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName );
    }
    else
    {
        Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassConfig( client->pers.classSelection )->modelName,
                     BG_ClassConfig( client->pers.classSelection )->skinName );

        //model segmentation
        Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg",
                     BG_ClassConfig( client->pers.classSelection )->modelName );

        if( G_NonSegModel( filename ) )
            client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL;
        else
            client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL;
    }
    Q_strncpyz( model, buffer, sizeof( model ) );

    // wallwalk follow
    s = Info_ValueForKey( userinfo, "cg_wwFollow" );

    if( atoi( s ) )
        client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW;
    else
        client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW;

    // wallwalk toggle
    s = Info_ValueForKey( userinfo, "cg_wwToggle" );

    if( atoi( s ) )
        client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE;
    else
        client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE;

    // always sprint
    s = Info_ValueForKey( userinfo, "cg_sprintToggle" );

    if( atoi( s ) )
        client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE;
    else
        client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE;

    // fly speed
    s = Info_ValueForKey( userinfo, "cg_flySpeed" );

    if( *s )
        client->pers.flySpeed = atoi( s );
    else
        client->pers.flySpeed = BG_Class( PCL_NONE )->speed;

    // disable blueprint errors
    s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" );

    if( atoi( s ) )
        client->pers.disableBlueprintErrors = qtrue;
    else
        client->pers.disableBlueprintErrors = qfalse;

    // teamInfo
    s = Info_ValueForKey( userinfo, "teamoverlay" );

    if( atoi( s ) != 0 )
    {
        // teamoverlay was enabled so we need an update
        if( client->pers.teamInfo == 0 )
            client->pers.teamInfo = 1;
    }
    else
        client->pers.teamInfo = 0;

    s = Info_ValueForKey( userinfo, "cg_unlagged" );
    if( !s[0] || atoi( s ) != 0 )
        client->pers.useUnlagged = qtrue;
    else
        client->pers.useUnlagged = qfalse;

    Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ),
                sizeof( client->pers.voice ) );

    // send over a subset of the userinfo keys so other clients can
    // print scoreboards, display models, and play custom sounds

    Com_sprintf( userinfo, sizeof( userinfo ),
                 "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s",
                 client->pers.netname, client->pers.teamSelection, model,
                 Com_ClientListString( &client->sess.ignoreList ),
                 client->pers.voice );

    trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );

    /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/

    return NULL;
}
Exemple #25
0
/*
===========
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;

    //if client is dead and following teammate, stop following before spawning
    if( client->sess.spectatorClient != -1 )
    {
        client->sess.spectatorClient = -1;
        client->sess.spectatorState = SPECTATOR_FREE;
    }

    // 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 client is dead and following teammate, stop following before spawning
    if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
        G_StopFollowing( ent );

    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 = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
        else if( teamLocal == TEAM_ALIENS )
            spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
        else if( teamLocal == TEAM_HUMANS )
            spawnPoint = G_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_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_MISC ] = 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->ps.stats[ STAT_MAX_HEALTH ] =
            BG_Class( ent->client->pers.classSelection )->health;
    else
        client->ps.stats[ STAT_MAX_HEALTH ] = 100;

    // clear entity values
    if( ent->client->pers.classSelection == PCL_HUMAN )
    {
        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;
    client->ps.stats[ STAT_WEAPON ] = weapon;
    client->ps.ammo = maxAmmo;
    client->ps.clips = maxClips;

    // We just spawned, not changing weapons
    client->ps.persistant[ PERS_NEWWEAPON ] = 0;

    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 ] = STAMINA_MAX;

    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 );
    G_SetClientViewAngle( ent, spawn_angles );

    if( client->sess.spectatorState == SPECTATOR_NOT )
    {
        trap_LinkEntity( ent );

        // force the base weapon up
        if( client->pers.teamSelection == TEAM_HUMANS )
            G_ForceWeaponChange( ent, weapon );

        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;
    ent->nextRegenTime = 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 );

        client->ps.weapon = client->ps.stats[ STAT_WEAPON ];
    }

    // 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 );

    client->pers.infoChangeTime = level.time;
}
Exemple #26
0
void G_KnockbackByDir( gentity_t *target, const vec3_t direction, float strength,
                              qboolean ignoreMass )
{
	vec3_t dir, vel;
	int    mass;
	float  massMod;
	const classAttributes_t *ca;

	// sanity check parameters
	if ( !target || !target->client || VectorLength( direction ) == 0.0f || strength == 0 )
	{
		return;
	}

	// check target flags
	if ( target->flags & FL_NO_KNOCKBACK )
	{
		return;
	}

	ca = BG_Class( target->client->ps.stats[ STAT_CLASS ] );

	// normalize direction
	VectorCopy( direction, dir );
	VectorNormalize( dir );

	// adjust strength according to client mass
	if ( !ignoreMass )
	{
		if ( ca->mass <= 0 )
		{
			mass = KNOCKBACK_NORMAL_MASS;
		}
		else
		{
			mass = ca->mass;
		}

		massMod = ( float )KNOCKBACK_NORMAL_MASS / ( float )mass;

		if ( massMod < 0.5f )
		{
			massMod = 0.5f;
		}
		else if ( massMod > 2.0f )
		{
			massMod = 2.0f;
		}
	}
	else
	{
		// for debug print
		massMod = 1.0f;
	}

	strength *= massMod;

	// adjust client velocity
	VectorScale( dir, strength, vel );
	VectorAdd( target->client->ps.velocity, vel, target->client->ps.velocity );

	// set pmove timer so that the client can't cancel out the movement immediately
	if ( !target->client->ps.pm_time )
	{
		target->client->ps.pm_time = KNOCKBACK_PMOVE_TIME;
		target->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	}

	// print debug info
	if ( g_debugKnockback.integer )
	{
		G_Printf( "%i: Knockback: client: %i, strength: %.1f (massMod: %.1f)\n",
		          level.time, target->s.number, strength, massMod );
	}
}
Exemple #27
0
float G_GetPointDamageMod( gentity_t *target, class_t pcl, float angle, float height )
{
	int            regionNum;
	damageRegion_t *region;
	qboolean       crouching;

	if ( !target || !target->client )
	{
		return 1.0f;
	}

	crouching = ( target->client->ps.pm_flags & PMF_DUCKED );

	for ( regionNum = 0; regionNum < g_numDamageRegions[ pcl ]; regionNum++ )
	{
		region = &g_damageRegions[ pcl ][ regionNum ];

		// ignore nonlocational
		if ( region->nonlocational )
		{
			continue;
		}

		// crouch state must match
		if ( region->crouch != crouching )
		{
			continue;
		}

		// height must be within range
		if ( height < region->minHeight || height > region->maxHeight )
		{
			continue;
		}

		// angle must be within range
		if ( ( region->minAngle <= region->maxAngle && ( angle < region->minAngle || angle > region->maxAngle ) ) ||
		     ( region->minAngle >  region->maxAngle && ( angle > region->maxAngle && angle < region->minAngle ) ) )
		{
			continue;
		}

		if ( g_debugDamage.integer > 1 )
		{
			G_Printf( "GetPointDamageModifier( pcl = %s, angle = %.2f, height = %.2f ): "
			          S_COLOR_GREEN "FOUND:" S_COLOR_WHITE " %.2f (%s)\n",
			          BG_Class( pcl )->name, angle, height, region->modifier, region->name );
		}

		return region->modifier;
	}

	if ( g_debugDamage.integer > 1 )
	{
		G_Printf( "GetPointDamageModifier( pcl = %s, angle = %.2f, height = %.2f ): "
		          S_COLOR_YELLOW "NOT FOUND:" S_COLOR_WHITE " %.2f\n",
		          BG_Class( pcl )->name, angle, height, 1.0f );
	}

	return 1.0f;
}
void G_UpdateUnlockables()
{
	int              itemNum = 0, unlockableNum, unlockThreshold;
	float            momentum;
	unlockable_t     *unlockable;
	int              unlockableType = 0;
	team_t           team;

	for ( unlockableNum = 0; unlockableNum < NUM_UNLOCKABLES; unlockableNum++ )
	{
		unlockable = &unlockables[ unlockableNum ];

		// also iterate over item types, itemNum is a per-type counter
		while ( unlockableType < UNLT_NUM_UNLOCKABLETYPES - 1 &&
		        unlockableNum == unlockablesTypeOffset[ unlockableType + 1 ] )
		{
			unlockableType++;
			itemNum = 0;
		}

		switch ( unlockableType )
		{
			case UNLT_WEAPON:
				team            = BG_Weapon( itemNum )->team;
				unlockThreshold = BG_Weapon( itemNum )->unlockThreshold;
				break;

			case UNLT_UPGRADE:
				team            = TEAM_HUMANS;
				unlockThreshold = BG_Upgrade( itemNum )->unlockThreshold;
				break;

			case UNLT_BUILDABLE:
				team            = BG_Buildable( itemNum )->team;
				unlockThreshold = BG_Buildable( itemNum )->unlockThreshold;
				break;

			case UNLT_CLASS:
				team            = TEAM_ALIENS;
				unlockThreshold = BG_Class( itemNum )->unlockThreshold;
				break;

			default:
				Com_Error( ERR_FATAL, "G_UpdateUnlockables: Unknown unlockable type" );
		}

		unlockThreshold = MAX( unlockThreshold, 0 );
		momentum = level.team[ team ].momentum;

		unlockable->type            = unlockableType;
		unlockable->num             = itemNum;
		unlockable->team            = team;
		unlockable->statusKnown     = true;
		unlockable->unlockThreshold = unlockThreshold;
		unlockable->lockThreshold   = UnlockToLockThreshold( unlockThreshold );

		// calculate the item's locking state
		unlockable->unlocked = (
		    !unlockThreshold || momentum >= unlockThreshold ||
		    ( unlockable->unlocked && momentum >= unlockable->lockThreshold )
		);

		itemNum++;

		/*Com_Printf( "G_UpdateUnlockables: Team %s, Type %s, Item %s, Momentum %d, Threshold %d, "
		            "Unlocked %d, Synchronize %d\n",
		            BG_TeamName( team ), UnlockableTypeName( unlockable ), UnlockableName( unlockable ),
		            momentum, unlockThreshold, unlockable->unlocked, unlockable->synchronize );*/
	}

	// GAME knows about all teams
	unlockablesDataAvailable = true;
	unlockablesTeamKnowledge = TEAM_ALL;

	// generate masks for network transmission
	UpdateUnlockablesMask();
}
static int CG_CalcFov( void )
{
	float     y;
	float     phase;
	float     v;
	int       contents;
	float     fov_x, fov_y;
	float     zoomFov;
	float     f;
	int       inwater;
	int       attribFov;
	usercmd_t cmd;
	usercmd_t oldcmd;
	int       cmdNum;

	cmdNum = trap_GetCurrentCmdNumber();
	trap_GetUserCmd( cmdNum, &cmd );
	trap_GetUserCmd( cmdNum - 1, &oldcmd );

	// switch follow modes if necessary: cycle between free -> follow -> third-person follow
	if ( usercmdButtonPressed( cmd.buttons, BUTTON_USE_HOLDABLE ) && !usercmdButtonPressed( oldcmd.buttons, BUTTON_USE_HOLDABLE ) )
	{
		if ( cg.snap->ps.pm_flags & PMF_FOLLOW )
		{
			if ( !cg.chaseFollow )
			{
				cg.chaseFollow = qtrue;
			}
			else
			{
				cg.chaseFollow = qfalse;
				trap_SendClientCommand( "follow\n" );
			}
		}
		else if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
		{
			trap_SendClientCommand( "follow\n" );
		}
	}

	if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ||
	     ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) ||
	     ( cg.renderingThirdPerson ) )
	{
		// if in intermission or third person, use a fixed value
		fov_y = BASE_FOV_Y;
	}
	else
	{
		// don't lock the fov globally - we need to be able to change it
		if ( ( attribFov = trap_Cvar_VariableIntegerValue( BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fovCvar ) ) )
		{
			if ( attribFov < 80 )
			{
				attribFov = 80;
			}
			else if ( attribFov >= 140 )
			{
				attribFov = 140;
			}
		}
		else
		{
			attribFov = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fov;
		}
		attribFov *= 0.75;
		fov_y = attribFov;

		if ( fov_y < 1.0f )
		{
			fov_y = 1.0f;
		}
		else if ( fov_y > MAX_FOV_Y )
		{
			fov_y = MAX_FOV_Y;
		}

		if ( cg.spawnTime > ( cg.time - FOVWARPTIME ) &&
		     BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_FOVWARPS ) )
		{
			float fraction = ( float )( cg.time - cg.spawnTime ) / FOVWARPTIME;

			fov_y = MAX_FOV_WARP_Y - ( ( MAX_FOV_WARP_Y - fov_y ) * fraction );
		}

		// account for zooms
		zoomFov = BG_Weapon( cg.predictedPlayerState.weapon )->zoomFov * 0.75f;

		if ( zoomFov < 1.0f )
		{
			zoomFov = 1.0f;
		}
		else if ( zoomFov > attribFov )
		{
			zoomFov = attribFov;
		}

		// only do all the zoom stuff if the client CAN zoom
		// FIXME: zoom control is currently hard coded to WBUTTON_ATTACK2
		if ( BG_Weapon( cg.predictedPlayerState.weapon )->canZoom )
		{
			if ( cg.zoomed )
			{
				f = ( cg.time - cg.zoomTime ) / ( float ) ZOOM_TIME;

				if ( f > 1.0f )
				{
					fov_y = zoomFov;
				}
				else
				{
					fov_y = fov_y + f * ( zoomFov - fov_y );
				}

				// WBUTTON_ATTACK2 isn't held so unzoom next time
				if ( !usercmdButtonPressed( cmd.buttons, BUTTON_ATTACK2 ) || cg.snap->ps.weaponstate == WEAPON_RELOADING )
				{
					cg.zoomed = qfalse;
					cg.zoomTime = MIN( cg.time,
					                   cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
				}
			}
			else
			{
				f = ( cg.time - cg.zoomTime ) / ( float ) ZOOM_TIME;

				if ( f < 1.0f )
				{
					fov_y = zoomFov + f * ( fov_y - zoomFov );
				}

				// WBUTTON_ATTACK2 is held so zoom next time
				if ( usercmdButtonPressed( cmd.buttons, BUTTON_ATTACK2 ) && cg.snap->ps.weaponstate != WEAPON_RELOADING )
				{
					cg.zoomed = qtrue;
					cg.zoomTime = MIN( cg.time,
					                   cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
				}
			}
		}
		else if ( cg.zoomed )
		{
			cg.zoomed = qfalse;
		}
	}

	y = cg.refdef.height / tan( 0.5f * DEG2RAD( fov_y ) );
	fov_x = atan2( cg.refdef.width, y );
	fov_x = 2.0f * RAD2DEG( fov_x );

	// warp if underwater
	contents = CG_PointContents( cg.refdef.vieworg, -1 );

	if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
	{
		phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2.0f;
		v = WAVE_AMPLITUDE * sin( phase );
		fov_x += v;
		fov_y -= v;
		inwater = qtrue;
	}
	else
	{
		inwater = qfalse;
	}

	// set it
	cg.refdef.fov_x = fov_x;
	cg.refdef.fov_y = fov_y;

	if ( !cg.zoomed )
	{
		cg.zoomSensitivity = 1.0f;
	}
	else
	{
		cg.zoomSensitivity = cg.refdef.fov_y / 75.0f;
	}

	return inwater;
}
Exemple #30
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn and evolve
Initializes all non-persistent parts of playerState
============
*/
void ClientSpawn( gentity_t *ent, gentity_t *spawn, const vec3_t origin, const vec3_t angles )
{
	int                index;
	vec3_t             spawn_origin, spawn_angles;
	gclient_t          *client;
	int                i;
	clientPersistant_t saved;
	clientSession_t    savedSess;
	bool           savedNoclip, savedCliprcontents;
	int                persistant[ MAX_PERSISTANT ];
	gentity_t          *spawnPoint = nullptr;
	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;

	ClientSpawnCBSE(ent, ent == spawn);

	index = ent - g_entities;
	client = ent->client;

	teamLocal = client->pers.team;

	//if client is dead and following teammate, stop following before spawning
	if ( client->sess.spectatorClient != -1 )
	{
		client->sess.spectatorClient = -1;
		client->sess.spectatorState = SPECTATOR_FREE;
	}

	// 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 client is dead and following teammate, stop following before spawning
	if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
	{
		G_StopFollowing( ent );
	}

	if ( origin != nullptr )
	{
		VectorCopy( origin, spawn_origin );
	}

	if ( angles != nullptr )
	{
		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 = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
		}
		else if ( teamLocal == TEAM_ALIENS )
		{
			spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
		}
		else if ( teamLocal == TEAM_HUMANS )
		{
			spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
		}
	}
	else
	{
		if ( spawn == nullptr )
		{
			Com_Error(errorParm_t::ERR_DROP,  "ClientSpawn: spawn is NULL" );
		}

		spawnPoint = spawn;

		if ( spawnPoint->s.eType == entityType_t::ET_BUILDABLE )
		{
			G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, true );

			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_TELEPORT_BIT;
	G_UnlaggedClear( ent );

	// clear everything but the persistent data

	saved = client->pers;
	savedSess = client->sess;
	savedPing = client->ps.ping;
	savedNoclip = client->noclip;
	savedCliprcontents = client->cliprcontents;

	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->noclip = savedNoclip;
	client->cliprcontents = savedCliprcontents;

	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;

	//Log::Notice( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection );

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->client = &level.clients[ index ];
	ent->classname = S_PLAYER_CLASSNAME;
	if ( client->noclip )
	{
		client->cliprcontents = CONTENTS_BODY;
	}
	else
	{
		ent->r.contents = CONTENTS_BODY;
	}
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = G_PlayerDie;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags &= FL_GODMODE | FL_NOTARGET;

	// calculate each client's acceleration
	ent->evaluateAcceleration = true;

	client->ps.stats[ STAT_MISC ] = 0;

	client->ps.eFlags = flags;
	client->ps.clientNum = index;

	BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, nullptr, nullptr, nullptr );

	// clear entity values
	if ( ent->client->pers.classSelection == PCL_HUMAN_NAKED )
	{
		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;
	client->ps.stats[ STAT_WEAPON ] = weapon;
	client->ps.ammo = maxAmmo;
	client->ps.clips = maxClips;

	// We just spawned, not changing weapons
	client->ps.persistant[ PERS_NEWWEAPON ] = 0;

	client->ps.persistant[ PERS_TEAM ] = client->pers.team;

	// TODO: Check whether stats can be cleared at once instead of per field
	client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
	client->ps.stats[ STAT_FUEL ]    = JETPACK_FUEL_MAX;
	client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection;
	client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
	client->ps.stats[ STAT_PREDICTION ] = 0;
	client->ps.stats[ STAT_STATE ] = 0;

	VectorSet( client->ps.grapplePoint, 0.0f, 0.0f, 1.0f );

	//clear the credits array
	// TODO: Handle in HealthComponent or ClientComponent.
	for ( i = 0; i < MAX_CLIENTS; i++ )
	{
		ent->credits[ i ].value = 0.0f;
		ent->credits[ i ].time = 0;
		ent->credits[ i ].team = TEAM_NONE;
	}

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, client->ps.origin );

	//give aliens some spawn velocity
	if ( client->sess.spectatorState == SPECTATOR_NOT &&
	     client->pers.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, nullptr, nullptr );
				VectorAdd( spawnPoint->s.origin2, forward, dir );
				VectorNormalize( dir );
				VectorScale( dir, BG_Class( ent->client->pers.classSelection )->jumpMagnitude,
				             client->ps.velocity );
			}

			G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
		}
	}
	else if ( client->sess.spectatorState == SPECTATOR_NOT &&
	          client->pers.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 );
	G_SetClientViewAngle( ent, spawn_angles );

	if ( client->sess.spectatorState == SPECTATOR_NOT )
	{
		trap_LinkEntity( ent );

		// force the base weapon up
		if ( client->pers.team == TEAM_HUMANS )
		{
			G_ForceWeaponChange( ent, weapon );
		}

		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;
	ent->nextRegenTime = level.time;

	client->inactivityTime = level.time + g_inactivity.integer * 1000;
	usercmdClearButtons( client->latched_buttons );

	// 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 && spawnPoint )
		{
			G_EventFireEntity( spawnPoint, ent, ON_SPAWN );
		}

		// 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, true );
		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, true );

	client->pers.infoChangeTime = level.time;

	// (re)tag the client for its team
	Beacon::DeleteTags( ent );
	Beacon::Tag( ent, (team_t)ent->client->ps.persistant[ PERS_TEAM ], true );
}