/* * GS_CheckAmmoInWeapon */ qboolean GS_CheckAmmoInWeapon( player_state_t *playerState, int checkweapon ) { firedef_t *firedef = GS_FiredefForPlayerState( playerState, checkweapon ); if( checkweapon != WEAP_NONE && !playerState->inventory[checkweapon] ) return qfalse; if( !firedef->usage_count || firedef->ammo_id == AMMO_NONE ) return qtrue; return ( playerState->inventory[firedef->ammo_id] >= firedef->usage_count ) ? qtrue : qfalse; }
/* * G_SetClientEffects */ static void G_SetClientEffects( edict_t *ent ) { gclient_t *client = ent->r.client; if( G_IsDead( ent ) || GS_MatchState() >= MATCH_STATE_POSTMATCH ) return; if( client->ps.inventory[POWERUP_QUAD] > 0 ) { ent->s.effects |= EF_QUAD; if( client->ps.inventory[POWERUP_QUAD] < 6 ) ent->s.effects |= EF_EXPIRING_QUAD; } if( client->ps.inventory[POWERUP_SHELL] > 0 ) { ent->s.effects |= EF_SHELL; if( client->ps.inventory[POWERUP_SHELL] < 6 ) ent->s.effects |= EF_EXPIRING_SHELL; } if( client->ps.inventory[POWERUP_REGEN] > 0 ) { ent->s.effects |= EF_REGEN; if( client->ps.inventory[POWERUP_REGEN] < 6 ) ent->s.effects |= EF_EXPIRING_REGEN; } if( ent->s.weapon ) { firedef_t *firedef = GS_FiredefForPlayerState( &client->ps, ent->s.weapon ); if( firedef && firedef->fire_mode == FIRE_MODE_STRONG ) ent->s.effects |= EF_STRONG_WEAPON; } if( client->ps.pmove.stats[PM_STAT_STUN] ) ent->s.effects |= EF_PLAYER_STUNNED; else ent->s.effects &= ~EF_PLAYER_STUNNED; // show cheaters!!! if( ent->flags & FL_GODMODE ) ent->s.effects |= EF_GODMODE; // add chatting icon effect if( ent->r.client->resp.snap.buttons & BUTTON_BUSYICON ) ent->s.effects |= EF_BUSYICON; }
//========================================== // BOT_DMclass_FireWeapon // Fire if needed //========================================== static bool BOT_DMclass_FireWeapon( edict_t *self, usercmd_t *ucmd ) { #define WFAC_GENERIC_PROJECTILE 300.0 #define WFAC_GENERIC_INSTANT 150.0 float firedelay; vec3_t target; int weapon, i; float wfac; vec3_t fire_origin; trace_t trace; bool continuous_fire = false; firedef_t *firedef = GS_FiredefForPlayerState( &self->r.client->ps, self->r.client->ps.stats[STAT_WEAPON] ); if( !self->enemy ) return false; weapon = self->s.weapon; if( weapon < 0 || weapon >= WEAP_TOTAL ) weapon = 0; if( !firedef ) return false; // Aim to center of the box for( i = 0; i < 3; i++ ) target[i] = self->enemy->s.origin[i] + ( 0.5f * ( self->enemy->r.maxs[i] + self->enemy->r.mins[i] ) ); fire_origin[0] = self->s.origin[0]; fire_origin[1] = self->s.origin[1]; fire_origin[2] = self->s.origin[2] + self->viewheight; if( self->s.weapon == WEAP_LASERGUN || self->s.weapon == WEAP_PLASMAGUN ) continuous_fire = true; if( !continuous_fire && !BOT_DMclass_CheckShot( self, target ) ) return false; // find out our weapon AIM style if( AIWeapons[weapon].aimType == AI_AIMSTYLE_PREDICTION_EXPLOSIVE ) { // in the lowest skill level, don't predict projectiles if( self->ai->pers.skillLevel >= 0.33f ) BOT_DMclass_PredictProjectileShot( self, fire_origin, firedef->speed, target, self->enemy->velocity ); wfac = WFAC_GENERIC_PROJECTILE * 1.3; // aim to the feet when enemy isn't higher if( fire_origin[2] > ( target[2] + ( self->enemy->r.mins[2] * 0.8 ) ) ) { vec3_t checktarget; VectorSet( checktarget, self->enemy->s.origin[0], self->enemy->s.origin[1], self->enemy->s.origin[2] + self->enemy->r.mins[2] + 4 ); G_Trace( &trace, fire_origin, vec3_origin, vec3_origin, checktarget, self, MASK_SHOT ); if( trace.fraction == 1.0f || ( trace.ent > 0 && game.edicts[trace.ent].takedamage ) ) VectorCopy( checktarget, target ); } else if( !AI_IsStep( self->enemy ) ) wfac *= 2.5; // more imprecise for air rockets } else if( AIWeapons[weapon].aimType == AI_AIMSTYLE_PREDICTION ) { if( self->s.weapon == WEAP_PLASMAGUN ) wfac = WFAC_GENERIC_PROJECTILE * 0.5; else wfac = WFAC_GENERIC_PROJECTILE; // in the lowest skill level, don't predict projectiles if( self->ai->pers.skillLevel >= 0.33f ) BOT_DMclass_PredictProjectileShot( self, fire_origin, firedef->speed, target, self->enemy->velocity ); } else if( AIWeapons[weapon].aimType == AI_AIMSTYLE_DROP ) { //jalToDo wfac = WFAC_GENERIC_PROJECTILE; // in the lowest skill level, don't predict projectiles if( self->ai->pers.skillLevel >= 0.33f ) BOT_DMclass_PredictProjectileShot( self, fire_origin, firedef->speed, target, self->enemy->velocity ); } else // AI_AIMSTYLE_INSTANTHIT { if( self->s.weapon == WEAP_ELECTROBOLT ) wfac = WFAC_GENERIC_INSTANT; else if( self->s.weapon == WEAP_LASERGUN ) wfac = WFAC_GENERIC_INSTANT * 1.5; else wfac = WFAC_GENERIC_INSTANT; } wfac = 25 + wfac * ( 1.0f - self->ai->pers.skillLevel ); // look to target VectorSubtract( target, fire_origin, self->ai->move_vector ); if( self->r.client->ps.weaponState == WEAPON_STATE_READY || self->r.client->ps.weaponState == WEAPON_STATE_REFIRE || self->r.client->ps.weaponState == WEAPON_STATE_REFIRESTRONG ) { // in continuous fire weapons don't add delays if( self->s.weapon == WEAP_LASERGUN || self->s.weapon == WEAP_PLASMAGUN ) firedelay = 1.0f; else firedelay = ( 1.0f - self->ai->pers.skillLevel ) - ( random()-0.25f ); if( firedelay > 0.0f ) { if( G_InFront( self, self->enemy ) ) { ucmd->buttons |= BUTTON_ATTACK; // could fire, but wants to? } // mess up angles only in the attacking frames if( self->r.client->ps.weaponState == WEAPON_STATE_READY || self->r.client->ps.weaponState == WEAPON_STATE_REFIRE || self->r.client->ps.weaponState == WEAPON_STATE_REFIRESTRONG ) { if( (self->s.weapon == WEAP_LASERGUN) || (self->s.weapon == WEAP_PLASMAGUN) ) { target[0] += sinf( (float)level.time/100.0) * wfac; target[1] += cosf( (float)level.time/100.0) * wfac; } else { target[0] += ( random()-0.5f ) * wfac; target[1] += ( random()-0.5f ) * wfac; } } } } //update angles VectorSubtract( target, fire_origin, self->ai->move_vector ); AI_ChangeAngle( self ); if( nav.debugMode && bot_showcombat->integer ) G_PrintChasersf( self, "%s: attacking %s\n", self->ai->pers.netname, self->enemy->r.client ? self->enemy->r.client->netname : self->classname ); return true; }
/* * GS_ThinkPlayerWeapon */ int GS_ThinkPlayerWeapon( player_state_t *playerState, int buttons, int msecs, int timeDelta ) { firedef_t *firedef; qboolean refire = qfalse; assert( playerState->stats[STAT_PENDING_WEAPON] >= 0 && playerState->stats[STAT_PENDING_WEAPON] < WEAP_TOTAL ); if( GS_MatchPaused() ) return playerState->stats[STAT_WEAPON]; if( playerState->pmove.pm_type != PM_NORMAL ) { playerState->weaponState = WEAPON_STATE_READY; playerState->stats[STAT_PENDING_WEAPON] = playerState->stats[STAT_WEAPON] = WEAP_NONE; playerState->stats[STAT_WEAPON_TIME] = 0; return playerState->stats[STAT_WEAPON]; } if( playerState->pmove.stats[PM_STAT_NOUSERCONTROL] > 0 ) buttons = 0; if( !msecs ) goto done; if( playerState->stats[STAT_WEAPON_TIME] > 0 ) playerState->stats[STAT_WEAPON_TIME] -= msecs; else playerState->stats[STAT_WEAPON_TIME] = 0; firedef = GS_FiredefForPlayerState( playerState, playerState->stats[STAT_WEAPON] ); // during cool-down time it can shoot again or go into reload time if( playerState->weaponState == WEAPON_STATE_REFIRE || playerState->weaponState == WEAPON_STATE_REFIRESTRONG ) { int last_firemode; if( playerState->stats[STAT_WEAPON_TIME] > 0 ) goto done; last_firemode = ( playerState->weaponState == WEAPON_STATE_REFIRESTRONG ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK; if( last_firemode == firedef->fire_mode ) refire = qtrue; playerState->weaponState = WEAPON_STATE_READY; } // nothing can be done during reload time if( playerState->weaponState == WEAPON_STATE_RELOADING ) { if( playerState->stats[STAT_WEAPON_TIME] > 0 ) goto done; playerState->weaponState = WEAPON_STATE_READY; } if( playerState->weaponState == WEAPON_STATE_NOAMMOCLICK ) { if( playerState->stats[STAT_WEAPON_TIME] > 0 ) goto done; if( playerState->stats[STAT_WEAPON] != playerState->stats[STAT_PENDING_WEAPON] ) playerState->weaponState = WEAPON_STATE_READY; } // there is a weapon to be changed if( playerState->stats[STAT_WEAPON] != playerState->stats[STAT_PENDING_WEAPON] ) { if( ( playerState->weaponState == WEAPON_STATE_READY ) || ( playerState->weaponState == WEAPON_STATE_DROPPING ) || ( playerState->weaponState == WEAPON_STATE_ACTIVATING ) ) { if( playerState->weaponState != WEAPON_STATE_DROPPING ) { playerState->weaponState = WEAPON_STATE_DROPPING; playerState->stats[STAT_WEAPON_TIME] += firedef->weapondown_time; if( firedef->weapondown_time ) module_PredictedEvent( playerState->POVnum, EV_WEAPONDROP, 0 ); } } } // do the change if( playerState->weaponState == WEAPON_STATE_DROPPING ) { if( playerState->stats[STAT_WEAPON_TIME] > 0 ) goto done; playerState->stats[STAT_WEAPON] = playerState->stats[STAT_PENDING_WEAPON]; // update the firedef firedef = GS_FiredefForPlayerState( playerState, playerState->stats[STAT_WEAPON] ); playerState->weaponState = WEAPON_STATE_ACTIVATING; playerState->stats[STAT_WEAPON_TIME] += firedef->weaponup_time; module_PredictedEvent( playerState->POVnum, EV_WEAPONACTIVATE, playerState->stats[STAT_WEAPON] ); } if( playerState->weaponState == WEAPON_STATE_ACTIVATING ) { if( playerState->stats[STAT_WEAPON_TIME] > 0 ) goto done; playerState->weaponState = WEAPON_STATE_READY; } if( ( playerState->weaponState == WEAPON_STATE_READY ) || ( playerState->weaponState == WEAPON_STATE_NOAMMOCLICK ) ) { if( playerState->stats[STAT_WEAPON_TIME] > 0 ) goto done; if( !GS_ShootingDisabled() ) { if( buttons & BUTTON_ATTACK ) { if( GS_CheckAmmoInWeapon( playerState, playerState->stats[STAT_WEAPON] ) ) { playerState->weaponState = WEAPON_STATE_FIRING; } else { // player has no ammo nor clips if( playerState->weaponState == WEAPON_STATE_NOAMMOCLICK ) { playerState->weaponState = WEAPON_STATE_RELOADING; playerState->stats[STAT_WEAPON_TIME] += NOAMMOCLICK_AUTOSWITCH; if( playerState->stats[STAT_PENDING_WEAPON] == playerState->stats[STAT_WEAPON] ) playerState->stats[STAT_PENDING_WEAPON] = GS_SelectBestWeapon( playerState ); } else { playerState->weaponState = WEAPON_STATE_NOAMMOCLICK; playerState->stats[STAT_WEAPON_TIME] += NOAMMOCLICK_PENALTY; module_PredictedEvent( playerState->POVnum, EV_NOAMMOCLICK, 0 ); goto done; } } } // gunblade auto attack is special else if( playerState->stats[STAT_WEAPON] == WEAP_GUNBLADE && playerState->pmove.stats[PM_STAT_NOUSERCONTROL] <= 0 && playerState->pmove.stats[PM_STAT_NOAUTOATTACK] <= 0 && GS_CheckBladeAutoAttack( playerState, timeDelta ) ) { firedef = &GS_GetWeaponDef( WEAP_GUNBLADE )->firedef_weak; playerState->weaponState = WEAPON_STATE_FIRING; } } } if( playerState->weaponState == WEAPON_STATE_FIRING ) { int parm = playerState->stats[STAT_WEAPON]; if( firedef->fire_mode == FIRE_MODE_STRONG ) parm |= EV_INVERSE; playerState->stats[STAT_WEAPON_TIME] += firedef->reload_time; if( firedef->fire_mode == FIRE_MODE_STRONG ) playerState->weaponState = WEAPON_STATE_REFIRESTRONG; else playerState->weaponState = WEAPON_STATE_REFIRE; if( refire && firedef->smooth_refire ) module_PredictedEvent( playerState->POVnum, EV_SMOOTHREFIREWEAPON, parm ); else module_PredictedEvent( playerState->POVnum, EV_FIREWEAPON, parm ); // waste ammo if( !GS_InfiniteAmmo() ) { if( firedef->ammo_id != AMMO_NONE && firedef->usage_count ) playerState->inventory[firedef->ammo_id] -= firedef->usage_count; } } done: return playerState->stats[STAT_WEAPON]; }