/* =============== trigger_ammo_touch =============== */ void trigger_ammo_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { int ammo, clips, maxClips, maxAmmo; if( !other->client ) return; if( other->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS ) return; if( self->timestamp > level.time ) return; if( other->client->ps.weaponstate != WEAPON_READY ) return; if( BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 2 ) return; if( !BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 4 ) return; if( self->spawnflags & 1 ) self->timestamp = level.time + 1000; else self->timestamp = level.time + FRAMETIME; BG_FindAmmoForWeapon( other->client->ps.weapon, &maxAmmo, &maxClips ); BG_UnpackAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups, &ammo, &clips ); if( ( ammo + self->damage ) > maxAmmo ) { if( clips < maxClips ) { clips++; ammo = 1; } else ammo = maxAmmo; } else ammo += self->damage; BG_PackAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups, ammo, clips ); }
/* =============== CG_HumanText =============== */ static void CG_HumanText( char *text, playerState_t *ps ) { char *name; int ammo, clips; upgrade_t upgrade = UP_NONE; if( cg.weaponSelect <= 32 ) name = cg_weapons[ cg.weaponSelect ].humanName; else if( cg.weaponSelect > 32 ) { name = cg_upgrades[ cg.weaponSelect - 32 ].humanName; upgrade = cg.weaponSelect - 32; } BG_UnpackAmmoArray( ps->weapon, &ps->ammo, ps->powerups, &ammo, &clips ); if( !ammo && !clips && !BG_FindInfinteAmmoForWeapon( ps->weapon ) ) { //no ammo switch( ps->weapon ) { case WP_MACHINEGUN: case WP_CHAINGUN: case WP_SHOTGUN: case WP_LAUNCHER: case WP_ROCKET_LAUNCHER: Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Find an Armoury and press %s for more ammo\n", CG_KeyNameForCommand( "buy ammo" ) ) ); break; case WP_LAS_GUN: case WP_MASS_DRIVER: Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Find a Reactor or Repeater and press %s for more ammo\n", CG_KeyNameForCommand( "buy ammo" ) ) ); break; default: break; } } else { switch( ps->weapon ) { case WP_LAUNCHER: Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to fire the %s\n", CG_KeyNameForCommand( "+attack" ), BG_FindHumanNameForWeapon( ps->weapon ) ) ); Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to fire incendiary grenade\n", CG_KeyNameForCommand( "+button5" ) ) ); break; case WP_PISTOL: case WP_MACHINEGUN: case WP_SHOTGUN: case WP_LAS_GUN: case WP_CHAINGUN: case WP_MASS_DRIVER: case WP_ROCKET_LAUNCHER: Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to fire the %s\n", CG_KeyNameForCommand( "+attack" ), BG_FindHumanNameForWeapon( ps->weapon ) ) ); Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Hold %s to zoom\n", CG_KeyNameForCommand( "+button5" ) ) ); break; case WP_HBUILD: case WP_HBUILD2: CG_HumanCkitText( text, ps ); break; default: break; } } Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s and ", CG_KeyNameForCommand( "weapprev" ) ) ); Q_strcat( text, MAX_TUTORIAL_TEXT, va( "%s to select an upgrade\n", CG_KeyNameForCommand( "weapnext" ) ) ); if( upgrade == UP_NONE || ( upgrade > UP_NONE && BG_FindUsableForUpgrade( upgrade ) ) ) { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to use the %s\n", CG_KeyNameForCommand( "+button2" ), name ) ); } if( ps->stats[ STAT_HEALTH ] <= 35 && BG_InventoryContainsUpgrade( UP_MEDKIT, ps->stats ) ) { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to use your %s\n", CG_KeyNameForCommand( "itemact medkit" ), BG_FindHumanNameForUpgrade( UP_MEDKIT ) ) ); } Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to use a structure\n", CG_KeyNameForCommand( "+button7" ) ) ); }
/* ================== ClientTimerActions Actions that happen once a second ================== */ void ClientTimerActions( gentity_t *ent, int msec ) { gclient_t *client; usercmd_t *ucmd; int aForward, aRight; ucmd = &ent->client->pers.cmd; aForward = abs( ucmd->forwardmove ); aRight = abs( ucmd->rightmove ); client = ent->client; client->time100 += msec; client->time1000 += msec; client->time10000 += msec; while ( client->time100 >= 100 ) { client->time100 -= 100; //if not trying to run then not trying to sprint if( aForward <= 64 ) client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST; if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) ) client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST; if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && ucmd->upmove >= 0 ) { //subtract stamina if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) ) client->ps.stats[ STAT_STAMINA ] -= STAMINA_LARMOUR_TAKE; else client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE; if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA ) client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA; } if( ( aForward <= 64 && aForward > 5 ) || ( aRight <= 64 && aRight > 5 ) ) { //restore stamina client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE; if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA ) client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; } else if( aForward <= 5 && aRight <= 5 ) { //restore stamina faster client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE; if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA ) client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; } //client is charging up for a pounce if( client->ps.weapon == WP_ALEVEL3 || client->ps.weapon == WP_ALEVEL3_UPG ) { int pounceSpeed = 0; if( client->ps.weapon == WP_ALEVEL3 ) pounceSpeed = LEVEL3_POUNCE_SPEED; else if( client->ps.weapon == WP_ALEVEL3_UPG ) pounceSpeed = LEVEL3_POUNCE_UPG_SPEED; if( client->ps.stats[ STAT_MISC ] < pounceSpeed && ucmd->buttons & BUTTON_ATTACK2 ) client->ps.stats[ STAT_MISC ] += ( 100.0f / (float)LEVEL3_POUNCE_CHARGE_TIME ) * pounceSpeed; if( !( ucmd->buttons & BUTTON_ATTACK2 ) ) { if( client->ps.stats[ STAT_MISC ] > 0 ) { client->allowedToPounce = qtrue; client->pouncePayload = client->ps.stats[ STAT_MISC ]; } client->ps.stats[ STAT_MISC ] = 0; } if( client->ps.stats[ STAT_MISC ] > pounceSpeed ) client->ps.stats[ STAT_MISC ] = pounceSpeed; } //client is charging up for a... charge if( client->ps.weapon == WP_ALEVEL4 ) { if( client->ps.stats[ STAT_MISC ] < LEVEL4_CHARGE_TIME && ucmd->buttons & BUTTON_ATTACK2 && !client->charging ) { client->charging = qfalse; //should already be off, just making sure client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING; if( ucmd->forwardmove > 0 ) { //trigger charge sound...is quite annoying //if( client->ps.stats[ STAT_MISC ] <= 0 ) // G_AddEvent( ent, EV_LEV4_CHARGE_PREPARE, 0 ); client->ps.stats[ STAT_MISC ] += (int)( 100 * (float)LEVEL4_CHARGE_CHARGE_RATIO ); if( client->ps.stats[ STAT_MISC ] > LEVEL4_CHARGE_TIME ) client->ps.stats[ STAT_MISC ] = LEVEL4_CHARGE_TIME; } else client->ps.stats[ STAT_MISC ] = 0; } if( !( ucmd->buttons & BUTTON_ATTACK2 ) || client->charging || client->ps.stats[ STAT_MISC ] == LEVEL4_CHARGE_TIME ) { if( client->ps.stats[ STAT_MISC ] > LEVEL4_MIN_CHARGE_TIME ) { client->ps.stats[ STAT_MISC ] -= 100; if( client->charging == qfalse ) G_AddEvent( ent, EV_LEV4_CHARGE_START, 0 ); client->charging = qtrue; client->ps.stats[ STAT_STATE ] |= SS_CHARGING; //if the charger has stopped moving take a chunk of charge away if( VectorLength( client->ps.velocity ) < 64.0f || aRight ) client->ps.stats[ STAT_MISC ] = client->ps.stats[ STAT_MISC ] / 2; //can't charge backwards if( ucmd->forwardmove < 0 ) client->ps.stats[ STAT_MISC ] = 0; } else client->ps.stats[ STAT_MISC ] = 0; if( client->ps.stats[ STAT_MISC ] <= 0 ) { client->ps.stats[ STAT_MISC ] = 0; client->charging = qfalse; client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING; } } } //client is charging up an lcannon if( client->ps.weapon == WP_LUCIFER_CANNON ) { int ammo; BG_UnpackAmmoArray( WP_LUCIFER_CANNON, client->ps.ammo, client->ps.powerups, &ammo, NULL ); if( client->ps.stats[ STAT_MISC ] < LCANNON_TOTAL_CHARGE && ucmd->buttons & BUTTON_ATTACK ) client->ps.stats[ STAT_MISC ] += ( 100.0f / LCANNON_CHARGE_TIME ) * LCANNON_TOTAL_CHARGE; if( client->ps.stats[ STAT_MISC ] > LCANNON_TOTAL_CHARGE ) client->ps.stats[ STAT_MISC ] = LCANNON_TOTAL_CHARGE; if( client->ps.stats[ STAT_MISC ] > ( ammo * LCANNON_TOTAL_CHARGE ) / 10 ) client->ps.stats[ STAT_MISC ] = ammo * LCANNON_TOTAL_CHARGE / 10; } switch( client->ps.weapon ) { case WP_ABUILD: case WP_ABUILD2: case WP_HBUILD: case WP_HBUILD2: //set validity bit on buildable if( ( client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE ) { int dist = BG_FindBuildDistForClass( ent->client->ps.stats[ STAT_PCLASS ] ); vec3_t dummy; if( G_itemFits( ent, (buildable_t)(client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT), dist, dummy ) == IBE_NONE ) client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT; else client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT; } //update build timer if( client->ps.stats[ STAT_MISC ] > 0 ) client->ps.stats[ STAT_MISC ] -= 100; if( client->ps.stats[ STAT_MISC ] < 0 ) client->ps.stats[ STAT_MISC ] = 0; break; default: break; } if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ) { int remainingStartupTime = MEDKIT_STARTUP_TIME - ( level.time - client->lastMedKitTime ); if( remainingStartupTime < 0 ) { if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] && ent->client->medKitHealthToRestore && ent->client->ps.pm_type != PM_DEAD ) { ent->client->medKitHealthToRestore--; ent->health++; } else ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; } else { if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] && ent->client->medKitHealthToRestore && ent->client->ps.pm_type != PM_DEAD ) { //partial increase if( level.time > client->medKitIncrementTime ) { ent->client->medKitHealthToRestore--; ent->health++; client->medKitIncrementTime = level.time + ( remainingStartupTime / MEDKIT_STARTUP_SPEED ); } } else ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; } } } while( client->time1000 >= 1000 ) { client->time1000 -= 1000; //client is poison clouded if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED ) G_Damage( ent, client->lastPoisonCloudedClient, client->lastPoisonCloudedClient, NULL, NULL, LEVEL1_PCLOUD_DMG, 0, MOD_LEVEL1_PCLOUD ); //client is poisoned if( client->ps.stats[ STAT_STATE ] & SS_POISONED ) { int i; int seconds = ( ( level.time - client->lastPoisonTime ) / 1000 ) + 1; int damage = ALIEN_POISON_DMG, damage2 = 0; for( i = 0; i < seconds; i++ ) { if( i == seconds - 1 ) damage2 = damage; damage *= ALIEN_POISON_DIVIDER; } damage = damage2 - damage; G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL, NULL, damage, 0, MOD_POISON ); } //replenish alien health if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { int entityList[ MAX_GENTITIES ]; vec3_t range = { LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE }; vec3_t mins, maxs; int i, num; gentity_t *boostEntity; float modifier = 1.0f; VectorAdd( client->ps.origin, range, maxs ); VectorSubtract( client->ps.origin, range, mins ); num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( i = 0; i < num; i++ ) { boostEntity = &g_entities[ entityList[ i ] ]; if( boostEntity->client && boostEntity->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && boostEntity->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL4 ) { modifier = LEVEL4_REGEN_MOD; break; } else if( boostEntity->s.eType == ET_BUILDABLE && boostEntity->s.modelindex == BA_A_BOOSTER && boostEntity->spawned ) { modifier = BOOSTER_REGEN_MOD; break; } } if( ent->health > 0 && ent->health < client->ps.stats[ STAT_MAX_HEALTH ] && ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time ) ent->health += BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier; if( ent->health > client->ps.stats[ STAT_MAX_HEALTH ] ) ent->health = client->ps.stats[ STAT_MAX_HEALTH ]; } } while( client->time10000 >= 10000 ) { client->time10000 -= 10000; if( client->ps.weapon == WP_ALEVEL3_UPG ) { int ammo, maxAmmo; BG_FindAmmoForWeapon( WP_ALEVEL3_UPG, &maxAmmo, NULL ); BG_UnpackAmmoArray( WP_ALEVEL3_UPG, client->ps.ammo, client->ps.powerups, &ammo, NULL ); if( ammo < maxAmmo ) { ammo++; BG_PackAmmoArray( WP_ALEVEL3_UPG, client->ps.ammo, client->ps.powerups, ammo, 0 ); } } } }