/* * Pickup_Weapon */ qboolean Pickup_Weapon( edict_t *ent, edict_t *other ) { int ammo_tag; other->r.client->ps.inventory[ent->item->tag]++; // never allow the player to carry more than 2 copies of the same weapon if( other->r.client->ps.inventory[ent->item->tag] > ent->item->inventory_max ) other->r.client->ps.inventory[ent->item->tag] = ent->item->inventory_max; if( !( ent->spawnflags & DROPPED_ITEM ) ) { // give them some ammo with it ammo_tag = ent->item->weakammo_tag; if( ammo_tag ) Add_Ammo( other->r.client, GS_FindItemByTag( ammo_tag ), GS_FindItemByTag( ammo_tag )->quantity, qtrue ); } else { //it's a dropped weapon ammo_tag = ent->item->weakammo_tag; if( ent->count && ammo_tag ) Add_Ammo( other->r.client, GS_FindItemByTag( ammo_tag ), ent->count, qtrue ); } return qtrue; }
/* * Pickup_Weapon */ bool Pickup_Weapon( edict_t *other, const gsitem_t *item, int flags, int ammo_count ) { int ammo_tag; gs_weapon_definition_t *weapondef; weapondef = GS_GetWeaponDef( item->tag ); other->r.client->ps.inventory[item->tag]++; // never allow the player to carry more than 2 copies of the same weapon if( other->r.client->ps.inventory[item->tag] > item->inventory_max ) other->r.client->ps.inventory[item->tag] = item->inventory_max; if( !(flags & DROPPED_ITEM) ) { // give them some ammo with it ammo_tag = item->ammo_tag; if( ammo_tag ) Add_Ammo( other->r.client, GS_FindItemByTag( ammo_tag ), weapondef->firedef.weapon_pickup, true ); } else { // it's a dropped weapon ammo_tag = item->ammo_tag; if( ammo_count && ammo_tag ) Add_Ammo( other->r.client, GS_FindItemByTag( ammo_tag ), ammo_count, true ); } return true; }
int GS_Armor_TagForCount( float armorcount ) { int count = ARMOR_TO_INT( armorcount ); if( count > GS_FindItemByTag( ARMOR_YA )->inventory_max ) return ARMOR_RA; if( count > GS_FindItemByTag( ARMOR_GA )->inventory_max ) return ARMOR_YA; if( count ) return ARMOR_GA; return ARMOR_NONE; }
int GS_Armor_MaxCountForTag( int tag ) { gsitem_t *item = GS_FindItemByTag( tag ); if( item ) return item->inventory_max; return 255; }
int GS_Armor_PickupCountForTag( int tag ) { gsitem_t *item = GS_FindItemByTag( tag ); if( item ) return item->quantity; return 0; }
BotItemsSelector::ItemAndGoalWeights BotItemsSelector::ComputeAmmoWeights( const gsitem_t *item ) const { if( Inventory()[item->tag] < item->inventory_max ) { float quantityFactor = 1.0f - Inventory()[item->tag] / (float)item->inventory_max; if( quantityFactor > 0 ) { quantityFactor = 1.0f / Q_RSqrt( quantityFactor ); } for( int weapon = WEAP_GUNBLADE; weapon < WEAP_TOTAL; weapon++ ) { // TODO: Preache const gsitem_t *weaponItem = GS_FindItemByTag( weapon ); if( weaponItem->ammo_tag == item->tag ) { if( Inventory()[weaponItem->tag] ) { switch( weaponItem->tag ) { case WEAP_ELECTROBOLT: return ItemAndGoalWeights( quantityFactor, quantityFactor ); case WEAP_LASERGUN: return ItemAndGoalWeights( quantityFactor * 1.1f, quantityFactor ); case WEAP_PLASMAGUN: return ItemAndGoalWeights( quantityFactor * 1.1f, quantityFactor ); case WEAP_ROCKETLAUNCHER: return ItemAndGoalWeights( quantityFactor, quantityFactor ); default: return ItemAndGoalWeights( 0.5f * quantityFactor, quantityFactor ); } } return ItemAndGoalWeights( quantityFactor * 0.33f, quantityFactor * 0.5f ); } } } return ItemAndGoalWeights( 0.0, 0.0f ); }
static void WeaponString( edict_t *who, int weapon, char *buf, int buflen, const char *current_color ) { int strong_ammo, weak_ammo; gs_weapon_definition_t *weapondef = GS_GetWeaponDef( weapon ); Q_snprintfz( buf, buflen, "%s%s%s", ( GS_FindItemByTag( weapon )->color ? GS_FindItemByTag( weapon )->color : "" ), GS_FindItemByTag( weapon )->shortname, current_color ); strong_ammo = who->r.client->ps.inventory[weapondef->firedef.ammo_id]; weak_ammo = who->r.client->ps.inventory[weapondef->firedef_weak.ammo_id]; if( weapon == WEAP_GUNBLADE ) Q_strncatz( buf, va( ":%i", strong_ammo ), buflen ); else if( strong_ammo > 0 ) Q_strncatz( buf, va( ":%i/%i", strong_ammo, weak_ammo ), buflen ); else Q_strncatz( buf, va( ":%i", weak_ammo ), buflen ); }
/* * GS_InitWeapons */ void GS_InitWeapons( void ) { int i; gsitem_t *item; gs_weapon_definition_t *weapondef; for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { item = GS_FindItemByTag( i ); weapondef = GS_GetWeaponDef( i ); assert( item && weapondef ); // hack : use the firedef pickup counts on items if( item->weakammo_tag && GS_FindItemByTag( item->weakammo_tag ) ) { GS_FindItemByTag( item->weakammo_tag )->quantity = weapondef->firedef_weak.ammo_pickup; GS_FindItemByTag( item->weakammo_tag )->inventory_max = weapondef->firedef_weak.ammo_max; } if( item->ammo_tag && GS_FindItemByTag( item->ammo_tag ) ) { GS_FindItemByTag( item->ammo_tag )->quantity = weapondef->firedef.ammo_pickup; GS_FindItemByTag( item->ammo_tag )->inventory_max = weapondef->firedef.ammo_max; } } }
/* * G_StatsMessage * * Generates stats message for the entity * The returned string must be freed by the caller using G_Free * Note: This string must never contain " characters */ char *G_StatsMessage( edict_t *ent ) { gclient_t *client; gsitem_t *item; int i, shot_weak, hit_weak, shot_strong, hit_strong, shot_total, hit_total; static char entry[MAX_TOKEN_CHARS]; assert( ent && ent->r.client ); client = ent->r.client; // message header Q_snprintfz( entry, sizeof( entry ), "%d", PLAYERNUM( ent ) ); for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { item = GS_FindItemByTag( i ); assert( item ); hit_weak = hit_strong = 0; shot_weak = shot_strong = 0; if( item->weakammo_tag != AMMO_NONE ) { hit_weak = client->level.stats.accuracy_hits[item->weakammo_tag - AMMO_GUNBLADE]; shot_weak = client->level.stats.accuracy_shots[item->weakammo_tag - AMMO_GUNBLADE]; } if( item->ammo_tag != AMMO_NONE ) { hit_strong = client->level.stats.accuracy_hits[item->ammo_tag - AMMO_GUNBLADE]; shot_strong = client->level.stats.accuracy_shots[item->ammo_tag - AMMO_GUNBLADE]; } hit_total = hit_weak + hit_strong; shot_total = shot_weak + shot_strong; Q_strncatz( entry, va( " %d", shot_total ), sizeof( entry ) ); if( shot_total < 1 ) continue; Q_strncatz( entry, va( " %d", hit_total ), sizeof( entry ) ); // strong Q_strncatz( entry, va( " %d", shot_strong ), sizeof( entry ) ); if( shot_strong != shot_total ) Q_strncatz( entry, va( " %d", hit_strong ), sizeof( entry ) ); } Q_strncatz( entry, va( " %d %d", client->level.stats.total_damage_given, client->level.stats.total_damage_received ), sizeof( entry ) ); Q_strncatz( entry, va( " %d %d", client->level.stats.health_taken, client->level.stats.armor_taken ), sizeof( entry ) ); // add enclosing quote Q_strncatz( entry, "\"", sizeof( entry ) ); return entry; }
static void Say_Team_Armor( edict_t *who, char *buf, int buflen, const char *current_color ) { if( GS_Armor_TagForCount( who->r.client->resp.armor ) != ARMOR_NONE ) { Q_snprintfz( buf, buflen, "%s%i%s", GS_FindItemByTag( GS_Armor_TagForCount( who->r.client->resp.armor ) )->color, ARMOR_TO_INT( who->r.client->resp.armor ), current_color ); } else { Q_snprintfz( buf, buflen, "%s0%s", S_COLOR_GREEN, current_color ); } }
/* * SetItemNames * * Called by worldspawn */ void G_PrecacheItems( void ) { int i; gsitem_t *item; // precache item names and weapondefs for( i = 1; ( item = GS_FindItemByTag( i ) ) != NULL; i++ ) { trap_ConfigString( CS_ITEMS + i, item->name ); if( item->type & IT_WEAPON && GS_GetWeaponDef( item->tag ) ) { G_PrecacheWeapondef( i, &GS_GetWeaponDef( item->tag )->firedef ); G_PrecacheWeapondef( i, &GS_GetWeaponDef( item->tag )->firedef_weak ); } } // precache items if( GS_Instagib() ) { item = GS_FindItemByTag( WEAP_INSTAGUN ); PrecacheItem( item ); } else { for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { item = GS_FindItemByTag( i ); PrecacheItem( item ); } } // Vic: precache ammo pack if it's droppable item = GS_FindItemByClassname( "item_ammopack" ); if( item && G_Gametype_CanDropItem( item, qtrue ) ) { PrecacheItem( item ); } }
void G_Gametype_GENERIC_PlayerKilled( edict_t *targ, edict_t *attacker, edict_t *inflictor ) { if( !attacker || GS_MatchState() != MATCH_STATE_PLAYTIME || ( targ->r.svflags & SVF_CORPSE ) ) return; if( !attacker->r.client || attacker == targ || attacker == world ) teamlist[targ->s.team].stats.score--; else { if( GS_InvidualGameType() ) teamlist[attacker->s.team].stats.score = attacker->r.client->level.stats.score; if( GS_IsTeamDamage( &targ->s, &attacker->s ) ) teamlist[attacker->s.team].stats.score--; else teamlist[attacker->s.team].stats.score++; } // drop items if( targ->r.client && !( G_PointContents( targ->s.origin ) & CONTENTS_NODROP ) ) { // drop the weapon if ( targ->r.client->ps.stats[STAT_WEAPON] > WEAP_GUNBLADE ) { gsitem_t *weaponItem = GS_FindItemByTag( targ->r.client->ps.stats[STAT_WEAPON] ); if( weaponItem ) { edict_t *drop = Drop_Item( targ, weaponItem ); if( drop ) { drop->count = targ->r.client->ps.inventory[ weaponItem->weakammo_tag ]; targ->r.client->ps.inventory[ weaponItem->weakammo_tag ] = 0; } } } // drop ammo pack (won't drop anything if player doesn't have any strong ammo) Drop_Item( targ, GS_FindItemByTag( AMMO_PACK ) ); } }
BotItemsSelector::ItemAndGoalWeights BotItemsSelector::ComputeWeaponWeights( const gsitem_t *item, bool onlyGotGB ) const { if( Inventory()[item->tag] ) { // TODO: Precache const gsitem_t *ammo = GS_FindItemByTag( item->ammo_tag ); if( Inventory()[ammo->tag] >= ammo->inventory_max ) { return ItemAndGoalWeights( 0, 0 ); } float ammoQuantityFactor = 1.0f - Inventory()[ammo->tag] / (float)ammo->inventory_max; if( ammoQuantityFactor > 0 ) { ammoQuantityFactor = 1.0f / Q_RSqrt( ammoQuantityFactor ); } switch( item->tag ) { case WEAP_ELECTROBOLT: return ItemAndGoalWeights( ammoQuantityFactor, 0.5f * ammoQuantityFactor ); case WEAP_LASERGUN: return ItemAndGoalWeights( ammoQuantityFactor * 1.1f, 0.6f * ammoQuantityFactor ); case WEAP_PLASMAGUN: return ItemAndGoalWeights( ammoQuantityFactor * 1.1f, 0.6f * ammoQuantityFactor ); case WEAP_ROCKETLAUNCHER: return ItemAndGoalWeights( ammoQuantityFactor, 0.5f * ammoQuantityFactor ); default: return ItemAndGoalWeights( 0.75f * ammoQuantityFactor, 0.75f * ammoQuantityFactor ); } } // We may consider plasmagun in a bot's hand as a top tier weapon too const int topTierWeapons[4] = { WEAP_ELECTROBOLT, WEAP_LASERGUN, WEAP_ROCKETLAUNCHER, WEAP_PLASMAGUN }; // TODO: Precompute float topTierWeaponGreed = 0.0f; for( int i = 0; i < 4; ++i ) { if( !Inventory()[topTierWeapons[i]] ) { topTierWeaponGreed += 1.0f; } } for( int i = 0; i < 4; ++i ) { if( topTierWeapons[i] == item->tag ) { float weight = ( onlyGotGB ? 2.0f : 0.9f ) + ( topTierWeaponGreed - 1.0f ) / 3.0f; return ItemAndGoalWeights( weight, weight ); } } return onlyGotGB ? ItemAndGoalWeights( 1.5f, 2.0f ) : ItemAndGoalWeights( 0.75f, 0.75f ); }
static qboolean Pickup_AmmoPack( edict_t *ent, edict_t *other ) { gsitem_t *item; int i; if( !other->r.client ) return qfalse; for( i = AMMO_GUNBLADE; i < AMMO_TOTAL; i++ ) { item = GS_FindItemByTag( i ); if( item ) Add_Ammo( other->r.client, item, ent->invpak[i], qtrue ); } return qtrue; }
static bool Pickup_AmmoPack( edict_t *other, const int *invpack ) { gsitem_t *item; int i; if( !other->r.client ) return false; if( !invpack ) return false; for( i = AMMO_GUNBLADE + 1; i < AMMO_TOTAL; i++ ) { item = GS_FindItemByTag( i ); if( item ) Add_Ammo( other->r.client, item, invpack[i], true ); } return true; }
/* * G_TickOutPowerUps */ static void G_TickOutPowerUps( void ) { edict_t *ent; gsitem_t *item; int i; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( ent->r.inuse && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) { for( i = POWERUP_QUAD; i < POWERUP_TOTAL; i++ ) { item = GS_FindItemByTag( i ); if( item && item->quantity && ent->r.client->ps.inventory[item->tag] > 0 ) { ent->r.client->ps.inventory[item->tag]--; } } } } // also tick out dropped powerups for( ent = game.edicts + gs.maxclients + BODY_QUEUE_SIZE; ENTNUM( ent ) < game.numentities; ent++ ) { if( !ent->r.inuse || !ent->item ) continue; if( !( ent->item->type & IT_POWERUP ) ) continue; if( ent->spawnflags & DROPPED_ITEM ) { ent->count--; if( ent->count <= 0 ) { G_FreeEdict( ent ); continue; } } } }
/* * CG_RegisterWeaponModel */ struct weaponinfo_s *CG_RegisterWeaponModel( char *cgs_name, int weaponTag ) { char filename[MAX_QPATH]; weaponinfo_t *weaponinfo; Q_strncpyz( filename, cgs_name, sizeof( filename ) ); COM_StripExtension( filename ); weaponinfo = CG_FindWeaponModelSpot( filename ); if( weaponinfo->inuse == true ) return weaponinfo; weaponinfo->inuse = CG_WeaponModelUpdateRegistration( weaponinfo, filename ); if( !weaponinfo->inuse ) { if( cg_debugWeaponModels->integer ) CG_Printf( "%sWEAPmodel: Failed:%s%s\n", S_COLOR_YELLOW, filename, S_COLOR_WHITE ); return NULL; } // find the item for this weapon and try to assign the outline color if( weaponTag ) { gsitem_t *item = GS_FindItemByTag( weaponTag ); if( item ) { if( item->color && strlen( item->color ) > 1 ) { byte_vec4_t colorByte; Vector4Scale( color_table[ColorIndex( item->color[1] )], 255, colorByte ); CG_SetOutlineColor( weaponinfo->outlineColor, colorByte ); } } } return weaponinfo; }
/* * Drop_Weapon */ void Drop_Weapon( edict_t *ent, const gsitem_t *item ) { int otherweapon; edict_t *drop; int ammodrop = 0; if( item->tag < 1 || item->tag >= WEAP_TOTAL ) { G_PrintMsg( ent, "Can't drop unknown weapon\n" ); return; } // find out the amount of ammo to drop if( ent->r.client->ps.inventory[item->tag] > 1 && ent->r.client->ps.inventory[item->ammo_tag] > 5 ) { ammodrop = ent->r.client->ps.inventory[item->ammo_tag] / 2; } else // drop all { ammodrop = ent->r.client->ps.inventory[item->ammo_tag]; } drop = Drop_Item( ent, item ); if( drop ) { ent->r.client->ps.inventory[item->ammo_tag] -= ammodrop; drop->count = ammodrop; drop->spawnflags |= DROPPED_PLAYER_ITEM; ent->r.client->ps.inventory[item->tag]--; if( !ent->r.client->ps.inventory[item->tag] ) { otherweapon = GS_SelectBestWeapon( &ent->r.client->ps ); Use_Weapon( ent, GS_FindItemByTag( otherweapon ) ); } } }
/* * SCB_DrawPlayerStats */ static int SCB_DrawPlayerStats( int x, int y, struct qfontface_s *font ) { int xoffset, yoffset, lines; int i, j, num_weapons, weap, xpos, width, done; gsitem_t *it; char string[MAX_STRING_CHARS]; vec4_t color = { 0.5, 0.5, 0.5, 0.5f }; // don't display stats if( !cg_scoreboardStats->integer ) return 0; // total number of weapon num_weapons = WEAP_TOTAL-WEAP_GUNBLADE; width = ( SCB_TINYFIELD_PIXELWIDTH + 2 * SCB_SMALLFIELD_PIXELWIDTH ) * 2 + SCB_SMALLFIELD_PIXELWIDTH; xpos = -width / 2; // Center the box xoffset = xpos; yoffset = trap_SCR_FontHeight( font ); // Room for header, it's actually written later if we have at least one stat yoffset += trap_SCR_FontHeight( font ); lines = 0; for( i = 0; i < num_weapons; ) { xoffset = xpos; // two weapons per line for( j = 0, done = 0; done < 2 && i + j < num_weapons; j++ ) { weap = WEAP_GUNBLADE + i + j; if( scb_player_stats[2*( i+j )] == -1 && scb_player_stats[2*( i+j )+1] == -1 ) continue; it = GS_FindItemByTag( weap ); // short name Q_snprintfz( string, sizeof( string ), "%s%2s", it->color, it->shortname ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, string, SCB_TINYFIELD_PIXELWIDTH, font, colorWhite ); Q_snprintfz( string, sizeof( string ), "%2d%c", scb_player_stats[2*( i+j )+1], '%' ); trap_SCR_DrawStringWidth( x + xoffset + 2 * SCB_TINYFIELD_PIXELWIDTH, y + yoffset, ALIGN_CENTER_TOP, string, 2*SCB_SMALLFIELD_PIXELWIDTH, font, colorWhite ); // separator xoffset = 0; done++; } // next line if( done > 0 ) { lines++; yoffset += trap_SCR_FontHeight( font ); } i += j; } if( lines ) { // if we drew anything, draw header and box too xoffset = xpos; yoffset = trap_SCR_FontHeight( font ); // header trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, CG_TranslateString( "Weapon stats" ), width, font, colorMdGrey ); yoffset += trap_SCR_FontHeight( font ); // box trap_R_DrawStretchPic( x + xoffset - SCB_TINYFIELD_PIXELWIDTH/2, y + yoffset, width + SCB_TINYFIELD_PIXELWIDTH, lines * trap_SCR_FontHeight( font ), 0, 0, 1, 1, color, cgs.shaderWhite ); return ( trap_SCR_FontHeight( font ) * ( 2+lines ) ); } return 0; }
/* * Cmd_Give_f * * Give items to a client */ static void Cmd_Give_f( edict_t *ent ) { char *name; gsitem_t *it; int i; bool give_all; if( !sv_cheats->integer ) { G_PrintMsg( ent, "Cheats are not enabled on this server.\n" ); return; } name = trap_Cmd_Args(); if( !Q_stricmp( name, "all" ) ) give_all = true; else give_all = false; if( give_all || !Q_stricmp( trap_Cmd_Argv( 1 ), "health" ) ) { if( trap_Cmd_Argc() == 3 ) ent->health = atoi( trap_Cmd_Argv( 2 ) ); else ent->health = ent->max_health; if( !give_all ) return; } if( give_all || !Q_stricmp( name, "weapons" ) ) { for( i = 0; i < GS_MAX_ITEM_TAGS; i++ ) { it = GS_FindItemByTag( i ); if( !it ) continue; if( !( it->flags & ITFLAG_PICKABLE ) ) continue; if( !( it->type & IT_WEAPON ) ) continue; ent->r.client->ps.inventory[i] += 1; } if( !give_all ) return; } if( give_all || !Q_stricmp( name, "ammo" ) ) { for( i = 0; i < GS_MAX_ITEM_TAGS; i++ ) { it = GS_FindItemByTag( i ); if( !it ) continue; if( !( it->flags & ITFLAG_PICKABLE ) ) continue; if( !( it->type & IT_AMMO ) ) continue; Add_Ammo( ent->r.client, it, 1000, true ); } if( !give_all ) return; } if( give_all || !Q_stricmp( name, "armor" ) ) { ent->r.client->resp.armor = GS_Armor_MaxCountForTag( ARMOR_RA ); if( !give_all ) return; } if( give_all ) { for( i = 0; i < GS_MAX_ITEM_TAGS; i++ ) { it = GS_FindItemByTag( i ); if( !it ) continue; if( !( it->flags & ITFLAG_PICKABLE ) ) continue; if( it->type & ( IT_ARMOR|IT_WEAPON|IT_AMMO ) ) continue; ent->r.client->ps.inventory[i] = 1; } return; } it = GS_FindItemByName( name ); if( !it ) { name = trap_Cmd_Argv( 1 ); it = GS_FindItemByName( name ); if( !it ) { G_PrintMsg( ent, "unknown item\n" ); return; } } if( !( it->flags & ITFLAG_PICKABLE ) ) { G_PrintMsg( ent, "non-pickup (givable) item\n" ); return; } if( it->type & IT_AMMO ) { if( trap_Cmd_Argc() == 3 ) ent->r.client->ps.inventory[it->tag] = atoi( trap_Cmd_Argv( 2 ) ); else ent->r.client->ps.inventory[it->tag] += it->quantity; } else { if( it->tag && ( it->tag > 0 ) && ( it->tag < GS_MAX_ITEM_TAGS ) ) { if( GS_FindItemByTag( it->tag ) != NULL ) ent->r.client->ps.inventory[it->tag]++; } else G_PrintMsg( ent, "non-pickup (givable) item\n" ); } }
//========================================== // BOT_DMclass_UpdateStatus // update ai.status values based on bot state, // so ai can decide based on these settings //========================================== static void BOT_DMclass_UpdateStatus( edict_t *self ) { float LowNeedFactor = 0.5; gclient_t *client; int i; qboolean onlyGotGB = qtrue; edict_t *ent; ai_handle_t *ai; client = self->r.client; ai = &self->ai; for( i = 0; i < nav.num_goalEnts; i++ ) { ent = nav.goalEnts[i].ent; // item timing disabled by now if( ent->r.solid == SOLID_NOT ) { ai->status.entityWeights[i] = 0; continue; } if( ent->r.client ) { ai->status.entityWeights[i] = BOT_DMclass_PlayerWeight( self, ent ) * self->ai.pers.cha.offensiveness; continue; } if( ent->item ) { if( ent->r.solid == SOLID_NOT ) { ai->status.entityWeights[i] = 0; continue; } if( ent->item->type & IT_WEAPON ) { if( client->ps.inventory[ent->item->tag] ) { ai->status.entityWeights[i] *= LowNeedFactor; onlyGotGB = qfalse; } } else if( ent->item->type & IT_AMMO ) { if( client->ps.inventory[ent->item->tag] >= ent->item->inventory_max ) { ai->status.entityWeights[i] = 0.0; } else { // find weapon item for this ammo gsitem_t *weaponItem; int weapon; for( weapon = WEAP_GUNBLADE; weapon < WEAP_TOTAL; weapon++ ) { weaponItem = GS_FindItemByTag( weapon ); if( weaponItem->ammo_tag == ent->item->tag ) { if( !client->ps.inventory[weaponItem->tag] ) self->ai.status.entityWeights[i] *= LowNeedFactor; } } } } else if( ent->item->type & IT_ARMOR ) { if ( self->r.client->resp.armor < ent->item->inventory_max || !ent->item->inventory_max ) { if( ent->item->inventory_max ) { if( ( (float)self->r.client->resp.armor / (float)ent->item->inventory_max ) > 0.75 ) ai->status.entityWeights[i] = self->ai.pers.inventoryWeights[ent->item->tag] * LowNeedFactor; } else ai->status.entityWeights[i] = self->ai.pers.inventoryWeights[ent->item->tag]; } else { ai->status.entityWeights[i] = 0; } } else if( ent->item->type & IT_HEALTH ) { if( ent->item->tag == HEALTH_MEGA || ent->item->tag == HEALTH_ULTRA ) ai->status.entityWeights[i] = self->ai.pers.inventoryWeights[ent->item->tag]; else { if( self->health == self->max_health ) ai->status.entityWeights[i] = 0; else { float health_func; health_func = self->health / self->max_health; health_func *= health_func; ai->status.entityWeights[i] = self->ai.pers.inventoryWeights[ent->item->tag] + ( 1.1f - health_func ); } } } else if( ent->item->type & IT_POWERUP ) { ai->status.entityWeights[i] = self->ai.pers.inventoryWeights[ent->item->tag]; } } } if( onlyGotGB ) { for( i = 0; i < nav.num_goalEnts; i++ ) { ent = nav.goalEnts[i].ent; if( ent->item && ent->item->type & IT_WEAPON ) self->ai.status.entityWeights[i] *= 2.0f; } } }
/* * CG_SC_PrintPlayerStats */ static void CG_SC_PrintPlayerStats( const char *s, void ( *pp ) ) { int playerNum; int i, shot_weak, hit_weak, shot_strong, hit_strong, hit_total, shot_total; int total_damage_given, total_damage_received, health_taken, armor_taken; gsitem_t *item; void ( *print )( const char *format, ... ) = pp; playerNum = CG_ParseValue( &s ); if( playerNum < 0 || playerNum >= gs.maxclients ) return; // print stats to console/file print( "Stats for %s" S_COLOR_WHITE ":\r\n\r\n", cgs.clientInfo[playerNum].name ); print( " Weapon Weak Strong\r\n" ); print( " hit/shot percent hit/shot percent hit/shot percent\r\n" ); for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { item = GS_FindItemByTag( i ); assert( item ); shot_total = CG_ParseValue( &s ); if( shot_total < 1 ) // only continue with registered shots continue; hit_total = CG_ParseValue( &s ); shot_strong = CG_ParseValue( &s ); hit_strong = (shot_strong != shot_total ? CG_ParseValue( &s ) : hit_total); shot_weak = shot_total - shot_strong; hit_weak = hit_total - hit_strong; // name print( "%s%2s" S_COLOR_WHITE ": ", item->color, item->shortname ); #define STATS_PERCENT(hit,total) ((total) == 0 ? 0 : ((hit) == (total) ? 100 : (float)(hit) * 100.0f / (float)(total))) // total print( S_COLOR_GREEN "%3i" S_COLOR_WHITE "/" S_COLOR_CYAN "%3i " S_COLOR_YELLOW "%2.1f", hit_total, shot_total, STATS_PERCENT( hit_total, shot_total ) ); // weak print( " " S_COLOR_GREEN "%3i" S_COLOR_WHITE "/" S_COLOR_CYAN "%3i " S_COLOR_YELLOW "%2.1f", hit_weak, shot_weak, STATS_PERCENT( hit_weak, shot_weak ) ); // strong print( " " S_COLOR_GREEN "%3i" S_COLOR_WHITE "/" S_COLOR_CYAN "%3i " S_COLOR_YELLOW "%2.1f", hit_strong, shot_strong, STATS_PERCENT( hit_strong, shot_strong ) ); print( "\r\n" ); } print( "\r\n" ); total_damage_given = CG_ParseValue( &s ); total_damage_received = CG_ParseValue( &s ); print( S_COLOR_YELLOW "Damage given/received: " S_COLOR_WHITE "%i/%i " S_COLOR_YELLOW "ratio: %s%3.2f\r\n", total_damage_given, total_damage_received, ( total_damage_given > total_damage_received ? S_COLOR_GREEN : S_COLOR_RED ), STATS_PERCENT( total_damage_given, total_damage_given + total_damage_received ) ); health_taken = CG_ParseValue( &s ); armor_taken = CG_ParseValue( &s ); print( S_COLOR_YELLOW "Health/Armor taken : " S_COLOR_CYAN "%i" S_COLOR_WHITE "/" S_COLOR_CYAN "%i\r\n", health_taken, armor_taken ); #undef STATS_PERCENT }
/* * G_PlayerStatsMessage * generic one to add the stats of the current player into the scoreboard message at cgame */ static const char *G_PlayerStatsMessage( edict_t *ent ) { gsitem_t *it; int i; int weakhit, weakshot; int hit, shot; edict_t *target; gclient_t *client; static char entry[MAX_TOKEN_CHARS]; // when chasing generate from target target = ent; client = ent->r.client; if( client->resp.chase.active && game.edicts[client->resp.chase.target].r.client ) { target = &game.edicts[client->resp.chase.target]; client = target->r.client; } // message header entry[0] = '\0'; Q_snprintfz( entry, sizeof( entry ), "plstats 0 \"" ); Q_strncatz( entry, va( " %d", PLAYERNUM( target ) ), sizeof( entry ) ); // weapon loop for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { it = GS_FindItemByTag( i ); assert( it ); weakhit = hit = 0; weakshot = shot = 0; if( it->weakammo_tag != AMMO_NONE ) { weakhit = client->level.stats.accuracy_hits[it->weakammo_tag-AMMO_GUNBLADE]; weakshot = client->level.stats.accuracy_shots[it->weakammo_tag-AMMO_GUNBLADE]; } if( it->ammo_tag != AMMO_NONE ) { hit = client->level.stats.accuracy_hits[it->ammo_tag-AMMO_GUNBLADE]; shot = client->level.stats.accuracy_shots[it->ammo_tag-AMMO_GUNBLADE]; } // both in one Q_strncatz( entry, va( " %d", weakshot+shot ), sizeof( entry ) ); if( weakshot+shot > 0 ) { Q_strncatz( entry, va( " %d", weakhit+hit ), sizeof( entry ) ); if( i == WEAP_LASERGUN || i == WEAP_ELECTROBOLT ) { // strong Q_strncatz( entry, va( " %d", shot ), sizeof( entry ) ); if( shot != (weakshot+shot) ) Q_strncatz( entry, va( " %d", hit ), sizeof( entry ) ); } } } // add enclosing quote Q_strncatz( entry, "\"", sizeof( entry ) ); return entry; }
/* * GS_Cmd_UseItem */ gsitem_t *GS_Cmd_UseItem( player_state_t *playerState, const char *string, int typeMask ) { gsitem_t *item = NULL; assert( playerState ); if( playerState->pmove.pm_type >= PM_SPECTATOR ) return NULL; if( !string || !string[0] ) return NULL; if( Q_isdigit( string ) ) { int tag = atoi( string ); item = GS_FindItemByTag( tag ); } else item = GS_FindItemByName( string ); if( !item ) return NULL; if( typeMask && !( item->type & typeMask ) ) return NULL; // we don't have this item in the inventory if( !playerState->inventory[item->tag] ) { if( gs.module == GS_MODULE_CGAME && !( item->type & IT_WEAPON ) ) module_Printf( "Item %s is not in inventory\n", item->name ); return NULL; } // see if we can use it if( !(item->flags & ITFLAG_USABLE) ) return NULL; if( item->type & IT_WEAPON ) { if( !( playerState->pmove.stats[PM_STAT_FEATURES] & PMFEAT_WEAPONSWITCH ) ) return NULL; if( item->tag == playerState->stats[STAT_PENDING_WEAPON] ) // it's already being loaded return NULL; // check for need of any kind of ammo/fuel/whatever if( item->ammo_tag != AMMO_NONE && item->weakammo_tag != AMMO_NONE ) { gs_weapon_definition_t *weapondef = GS_GetWeaponDef( item->tag ); if( weapondef ) { // do we have any of these ammos ? if( playerState->inventory[item->weakammo_tag] >= weapondef->firedef_weak.usage_count ) return item; if( playerState->inventory[item->ammo_tag] >= weapondef->firedef.usage_count ) return item; } return NULL; } return item; // one of the weapon modes doesn't require ammo to be fired } if( item->type & IT_AMMO ) return item; if( item->type & IT_HEALTH ) return item; if( item->type & IT_POWERUP ) return item; return NULL; }
edict_t *Drop_Item( edict_t *ent, gsitem_t *item ) { edict_t *dropped; vec3_t forward, right; vec3_t offset; if( !G_Gametype_CanDropItem( item, qfalse ) ) return NULL; dropped = G_Spawn(); dropped->classname = item->classname; dropped->item = item; dropped->spawnflags = DROPPED_ITEM; VectorCopy( item_box_mins, dropped->r.mins ); VectorCopy( item_box_maxs, dropped->r.maxs ); dropped->r.solid = SOLID_TRIGGER; dropped->movetype = MOVETYPE_TOSS; dropped->touch = drop_temp_touch; dropped->stop = AI_AddGoalEntity; dropped->r.owner = ent; dropped->r.svflags &= ~SVF_NOCLIENT; dropped->s.team = ent->s.team; dropped->s.type = ET_ITEM; dropped->s.itemNum = item->tag; dropped->s.effects = 0; // default effects are applied client side dropped->s.modelindex = trap_ModelIndex( dropped->item->world_model[0] ); dropped->s.modelindex2 = trap_ModelIndex( dropped->item->world_model[1] ); dropped->attenuation = 1; if( ent->r.client ) { trace_t trace; AngleVectors( ent->r.client->ps.viewangles, forward, right, NULL ); VectorSet( offset, 24, 0, -16 ); G_ProjectSource( ent->s.origin, offset, forward, right, dropped->s.origin ); G_Trace( &trace, ent->s.origin, dropped->r.mins, dropped->r.maxs, dropped->s.origin, ent, CONTENTS_SOLID ); VectorCopy( trace.endpos, dropped->s.origin ); dropped->spawnflags |= DROPPED_PLAYER_ITEM; // ugly hack for dropping backpacks if( item->tag == AMMO_PACK_WEAK || item->tag == AMMO_PACK_STRONG || item->tag == AMMO_PACK ) { int w; qboolean anything = qfalse; for( w = WEAP_GUNBLADE + 1; w < WEAP_TOTAL; w++ ) { if( item->tag == AMMO_PACK_WEAK || item->tag == AMMO_PACK ) { int weakTag = GS_FindItemByTag( w )->weakammo_tag; if( ent->r.client->ps.inventory[weakTag] > 0 ) { dropped->invpak[weakTag] = ent->r.client->ps.inventory[weakTag]; ent->r.client->ps.inventory[weakTag] = 0; anything = qtrue; } } if( item->tag == AMMO_PACK_STRONG || item->tag == AMMO_PACK ) { int strongTag = GS_FindItemByTag( w )->ammo_tag; if( ent->r.client->ps.inventory[strongTag] ) { dropped->invpak[strongTag] = ent->r.client->ps.inventory[strongTag]; ent->r.client->ps.inventory[strongTag] = 0; anything = qtrue; } } } if( !anything ) // if nothing was added to the pack, don't bother spawning it { G_FreeEdict( dropped ); return NULL; } } // power-ups are special if( ( item->type & IT_POWERUP ) && item->quantity ) { if( ent->r.client->ps.inventory[item->tag] ) { dropped->count = ent->r.client->ps.inventory[item->tag]; ent->r.client->ps.inventory[item->tag] = 0; } else { dropped->count = item->quantity; } } } else { AngleVectors( ent->s.angles, forward, right, NULL ); VectorCopy( ent->s.origin, dropped->s.origin ); // ugly hack for dropping backpacks if( item->tag == AMMO_PACK_WEAK || item->tag == AMMO_PACK_STRONG || item->tag == AMMO_PACK ) { int w; for( w = WEAP_GUNBLADE + 1; w < WEAP_TOTAL; w++ ) { if( item->tag == AMMO_PACK_WEAK || item->tag == AMMO_PACK ) { gsitem_t *ammo = GS_FindItemByTag( GS_FindItemByTag( w )->weakammo_tag ); if( ammo ) dropped->invpak[ammo->tag] = ammo->quantity; } if( item->tag == AMMO_PACK_STRONG || item->tag == AMMO_PACK ) { gsitem_t *ammo = GS_FindItemByTag( GS_FindItemByTag( w )->ammo_tag ); if( ammo ) dropped->invpak[ammo->tag] = ammo->quantity; } } } // power-ups are special if( ( item->type & IT_POWERUP ) && item->quantity ) { dropped->count = item->quantity; } } VectorScale( forward, 100, dropped->velocity ); dropped->velocity[2] = 300; dropped->think = drop_make_touchable; dropped->nextThink = level.time + 1000; ent->r.client->teamstate.last_drop_item = item; VectorCopy( dropped->s.origin, ent->r.client->teamstate.last_drop_location ); GClip_LinkEntity( dropped ); return dropped; }
//========================================== // BOT_DMclass_InitPersistant // Persistant after respawns. //========================================== void BOT_DMclass_InitPersistant( edict_t *self ) { gsitem_t *item; int i, w; self->classname = "dmbot"; if( self->r.client->netname ) self->ai->pers.netname = self->r.client->netname; else self->ai->pers.netname = "dmBot"; //set 'class' functions self->ai->pers.RunFrame = BOT_DMclass_RunFrame; self->ai->pers.UpdateStatus = BOT_DMclass_UpdateStatus; self->ai->pers.blockedTimeout = BOT_DMClass_BlockedTimeout; //available moveTypes for this class self->ai->pers.moveTypesMask = ( LINK_MOVE|LINK_STAIRS|LINK_FALL|LINK_WATER|LINK_WATERJUMP|LINK_JUMPPAD|LINK_PLATFORM|LINK_TELEPORT|LINK_LADDER|LINK_JUMP|LINK_CROUCH ); //Persistant Inventory Weights (0 = can not pick) memset( self->ai->pers.inventoryWeights, 0, sizeof( self->ai->pers.inventoryWeights ) ); // weapons self->ai->pers.inventoryWeights[WEAP_GUNBLADE] = 0.0f; self->ai->pers.inventoryWeights[WEAP_MACHINEGUN] = 0.75f; self->ai->pers.inventoryWeights[WEAP_RIOTGUN] = 0.75f; self->ai->pers.inventoryWeights[WEAP_GRENADELAUNCHER] = 0.7f; self->ai->pers.inventoryWeights[WEAP_ROCKETLAUNCHER] = 0.8f; self->ai->pers.inventoryWeights[WEAP_PLASMAGUN] = 0.75f; self->ai->pers.inventoryWeights[WEAP_ELECTROBOLT] = 0.8f; self->ai->pers.inventoryWeights[WEAP_LASERGUN] = 0.8f; // ammo self->ai->pers.inventoryWeights[AMMO_WEAK_GUNBLADE] = 0.0f; self->ai->pers.inventoryWeights[AMMO_BULLETS] = 0.7f; self->ai->pers.inventoryWeights[AMMO_SHELLS] = 0.7f; self->ai->pers.inventoryWeights[AMMO_GRENADES] = 0.7f; self->ai->pers.inventoryWeights[AMMO_ROCKETS] = 0.7f; self->ai->pers.inventoryWeights[AMMO_PLASMA] = 0.7f; self->ai->pers.inventoryWeights[AMMO_BOLTS] = 0.7f; self->ai->pers.inventoryWeights[AMMO_LASERS] = 0.7f; // armor self->ai->pers.inventoryWeights[ARMOR_RA] = self->ai->pers.cha.armor_grabber * 2.0f; self->ai->pers.inventoryWeights[ARMOR_YA] = self->ai->pers.cha.armor_grabber * 1.0f; self->ai->pers.inventoryWeights[ARMOR_GA] = self->ai->pers.cha.armor_grabber * 0.75f; self->ai->pers.inventoryWeights[ARMOR_SHARD] = self->ai->pers.cha.armor_grabber * 0.5f; // health self->ai->pers.inventoryWeights[HEALTH_MEGA] = /*self->ai->pers.cha.health_grabber **/ 2.0f; self->ai->pers.inventoryWeights[HEALTH_ULTRA] = /*self->ai->pers.cha.health_grabber **/ 2.0f; self->ai->pers.inventoryWeights[HEALTH_LARGE] = /*self->ai->pers.cha.health_grabber **/ 1.0f; self->ai->pers.inventoryWeights[HEALTH_MEDIUM] = /*self->ai->pers.cha.health_grabber **/ 0.9f; self->ai->pers.inventoryWeights[HEALTH_SMALL] = /*self->ai->pers.cha.health_grabber **/ 0.4f; // backpack self->ai->pers.inventoryWeights[AMMO_PACK] = 0.4f; self->ai->pers.inventoryWeights[POWERUP_QUAD] = self->ai->pers.cha.offensiveness * 2.0f; self->ai->pers.inventoryWeights[POWERUP_SHELL] = self->ai->pers.cha.offensiveness * 2.0f; // scale the inventoryWeights by the character weapon affinities // FIXME: rewrite this loop! for( i = 1; i < MAX_ITEMS; i++ ) { if( ( item = GS_FindItemByTag( i ) ) == NULL ) continue; if( item->type & IT_WEAPON ) { self->ai->pers.inventoryWeights[i] *= self->ai->pers.cha.weapon_affinity[ i - ( WEAP_GUNBLADE - 1 ) ]; } else if( item->type & IT_AMMO ) { // find weapon for ammo for( w = WEAP_GUNBLADE; w < WEAP_TOTAL; w++ ) { if( GS_FindItemByTag( w )->ammo_tag == item->tag || GS_FindItemByTag( w )->weakammo_tag == item->tag ) { self->ai->pers.inventoryWeights[i] *= self->ai->pers.cha.weapon_affinity[ w - ( WEAP_GUNBLADE - 1 ) ]; break; } } } } }
//========================================== // BOT_DMclass_UpdateStatus // update ai.status values based on bot state, // so ai can decide based on these settings //========================================== static void BOT_DMclass_UpdateStatus( edict_t *self ) { float LowNeedFactor = 0.5; gclient_t *client; int i; bool onlyGotGB = true; edict_t *ent; ai_handle_t *ai; nav_ents_t *goalEnt; client = self->r.client; ai = self->ai; FOREACH_GOALENT( goalEnt ) { i = goalEnt->id; ent = goalEnt->ent; // item timing disabled by now if( ent->r.solid == SOLID_NOT ) { ai->status.entityWeights[i] = 0; continue; } if( ent->r.client ) { ai->status.entityWeights[i] = BOT_DMclass_PlayerWeight( self, ent ) * self->ai->pers.cha.offensiveness; continue; } if( ent->item ) { if( ent->r.solid == SOLID_NOT ) { ai->status.entityWeights[i] = 0; continue; } if( ent->item->type & IT_WEAPON ) { if( client->ps.inventory[ent->item->tag] ) { if( client->ps.inventory[ent->item->ammo_tag] ) { // find ammo item for this weapon gsitem_t *ammoItem = GS_FindItemByTag( ent->item->ammo_tag ); if( ammoItem->inventory_max ) { ai->status.entityWeights[i] *= (0.5 + 0.5 * (1.0 - (float)client->ps.inventory[ent->item->ammo_tag] / ammoItem->inventory_max)); } ai->status.entityWeights[i] *= LowNeedFactor; } else { // we need some ammo ai->status.entityWeights[i] *= LowNeedFactor; } onlyGotGB = false; } } else if( ent->item->type & IT_AMMO ) { if( client->ps.inventory[ent->item->tag] >= ent->item->inventory_max ) { ai->status.entityWeights[i] = 0.0; } else { #if 0 // find weapon item for this ammo gsitem_t *weaponItem; int weapon; for( weapon = WEAP_GUNBLADE; weapon < WEAP_TOTAL; weapon++ ) { weaponItem = GS_FindItemByTag( weapon ); if( weaponItem->ammo_tag == ent->item->tag ) { if( !client->ps.inventory[weaponItem->tag] ) self->ai->status.entityWeights[i] *= LowNeedFactor; } } #endif } } else if( ent->item->type & IT_ARMOR ) { if ( self->r.client->resp.armor < ent->item->inventory_max || !ent->item->inventory_max ) { if( ent->item->inventory_max ) { if( ( (float)self->r.client->resp.armor / (float)ent->item->inventory_max ) > 0.75 ) ai->status.entityWeights[i] = self->ai->pers.inventoryWeights[ent->item->tag] * LowNeedFactor; } else ai->status.entityWeights[i] = self->ai->pers.inventoryWeights[ent->item->tag]; } else { ai->status.entityWeights[i] = 0; } } else if( ent->item->type & IT_HEALTH ) { if( ent->item->tag == HEALTH_MEGA || ent->item->tag == HEALTH_ULTRA || ent->item->tag == HEALTH_SMALL ) ai->status.entityWeights[i] = self->ai->pers.inventoryWeights[ent->item->tag]; else { if( self->health >= self->max_health ) ai->status.entityWeights[i] = 0; else { float health_func; health_func = self->health / self->max_health; health_func *= health_func; ai->status.entityWeights[i] = self->ai->pers.inventoryWeights[ent->item->tag] + ( 1.1f - health_func ); } } } else if( ent->item->type & IT_POWERUP ) { ai->status.entityWeights[i] = self->ai->pers.inventoryWeights[ent->item->tag]; } } } if( onlyGotGB ) { FOREACH_GOALENT( goalEnt ) { i = goalEnt->id; ent = goalEnt->ent; if( ent->item && ent->item->type & IT_WEAPON ) self->ai->status.entityWeights[i] *= 2.0f; } } }
/* * ClientThink */ void ClientThink( edict_t *ent, usercmd_t *ucmd, int timeDelta ) { gclient_t *client; int i, j; static pmove_t pm; int delta, count; client = ent->r.client; client->ps.POVnum = ENTNUM( ent ); client->ps.playerNum = PLAYERNUM( ent ); // anti-lag if( ent->r.svflags & SVF_FAKECLIENT ) { client->timeDelta = 0; } else { int nudge; int fixedNudge = ( game.snapFrameTime ) * 0.5; // fixme: find where this nudge comes from. // add smoothing to timeDelta between the last few ucmds and a small fine-tuning nudge. nudge = fixedNudge + g_antilag_timenudge->integer; timeDelta += nudge; clamp( timeDelta, -g_antilag_maxtimedelta->integer, 0 ); // smooth using last valid deltas i = client->timeDeltasHead - 6; if( i < 0 ) i = 0; for( count = 0, delta = 0; i < client->timeDeltasHead; i++ ) { if( client->timeDeltas[i & G_MAX_TIME_DELTAS_MASK] < 0 ) { delta += client->timeDeltas[i & G_MAX_TIME_DELTAS_MASK]; count++; } } if( !count ) client->timeDelta = timeDelta; else { delta /= count; client->timeDelta = ( delta + timeDelta ) * 0.5; } client->timeDeltas[client->timeDeltasHead & G_MAX_TIME_DELTAS_MASK] = timeDelta; client->timeDeltasHead++; #ifdef UCMDTIMENUDGE client->timeDelta += client->pers.ucmdTimeNudge; #endif } clamp( client->timeDelta, -g_antilag_maxtimedelta->integer, 0 ); // update activity if he touched any controls if( ucmd->forwardmove != 0 || ucmd->sidemove != 0 || ucmd->upmove != 0 || ( ucmd->buttons & ~BUTTON_BUSYICON ) != 0 || client->ucmd.angles[PITCH] != ucmd->angles[PITCH] || client->ucmd.angles[YAW] != ucmd->angles[YAW] ) G_Client_UpdateActivity( client ); client->ucmd = *ucmd; // can exit intermission after two seconds, not counting postmatch if( GS_MatchState() == MATCH_STATE_WAITEXIT && ( ucmd->buttons & BUTTON_ATTACK ) && game.serverTime > GS_MatchStartTime() + 2000 ) level.exitNow = true; // (is this really needed?:only if not cared enough about ps in the rest of the code) // refresh player state position from the entity VectorCopy( ent->s.origin, client->ps.pmove.origin ); VectorCopy( ent->velocity, client->ps.pmove.velocity ); VectorCopy( ent->s.angles, client->ps.viewangles ); client->ps.pmove.gravity = level.gravity; if( GS_MatchState() >= MATCH_STATE_POSTMATCH || GS_MatchPaused() || ( ent->movetype != MOVETYPE_PLAYER && ent->movetype != MOVETYPE_NOCLIP ) ) client->ps.pmove.pm_type = PM_FREEZE; else if( ent->s.type == ET_GIB ) client->ps.pmove.pm_type = PM_GIB; else if( ent->movetype == MOVETYPE_NOCLIP || client->isTV ) client->ps.pmove.pm_type = PM_SPECTATOR; else client->ps.pmove.pm_type = PM_NORMAL; // set up for pmove memset( &pm, 0, sizeof( pmove_t ) ); pm.playerState = &client->ps; if( !client->isTV ) pm.cmd = *ucmd; if( memcmp( &client->old_pmove, &client->ps.pmove, sizeof( pmove_state_t ) ) ) pm.snapinitial = true; // perform a pmove Pmove( &pm ); // save results of pmove client->old_pmove = client->ps.pmove; // update the entity with the new position VectorCopy( client->ps.pmove.origin, ent->s.origin ); VectorCopy( client->ps.pmove.velocity, ent->velocity ); VectorCopy( client->ps.viewangles, ent->s.angles ); ent->viewheight = client->ps.viewheight; VectorCopy( pm.mins, ent->r.mins ); VectorCopy( pm.maxs, ent->r.maxs ); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; if( pm.groundentity == -1 ) { ent->groundentity = NULL; } else { G_AwardResetPlayerComboStats( ent ); ent->groundentity = &game.edicts[pm.groundentity]; ent->groundentity_linkcount = ent->groundentity->linkcount; } GClip_LinkEntity( ent ); GS_AddLaserbeamPoint( &ent->r.client->resp.trail, &ent->r.client->ps, ucmd->serverTimeStamp ); // Regeneration if( ent->r.client->ps.inventory[POWERUP_REGEN] > 0 && ent->health < 200) { ent->health += ( game.frametime * 0.001f ) * 10.0f; // Regen expires if health reaches 200 if ( ent->health >= 199.0f ) ent->r.client->ps.inventory[POWERUP_REGEN]--; } // fire touch functions if( ent->movetype != MOVETYPE_NOCLIP ) { edict_t *other; // touch other objects for( i = 0; i < pm.numtouch; i++ ) { other = &game.edicts[pm.touchents[i]]; for( j = 0; j < i; j++ ) { if( &game.edicts[pm.touchents[j]] == other ) break; } if( j != i ) continue; // duplicated // player can't touch projectiles, only projectiles can touch the player G_CallTouch( other, ent, NULL, 0 ); } } ent->s.weapon = GS_ThinkPlayerWeapon( &client->ps, ucmd->buttons, ucmd->msec, client->timeDelta ); if( G_IsDead( ent ) ) { if( ent->deathTimeStamp + g_respawn_delay_min->integer <= level.time ) client->resp.snap.buttons |= ucmd->buttons; } else if( client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] <= 0 ) client->resp.snap.buttons |= ucmd->buttons; // trigger the instashield if( GS_Instagib() && g_instashield->integer ) { if( client->ps.pmove.pm_type == PM_NORMAL && pm.cmd.upmove < 0 && client->resp.instashieldCharge == INSTA_SHIELD_MAX && client->ps.inventory[POWERUP_SHELL] == 0 ) { client->ps.inventory[POWERUP_SHELL] = client->resp.instashieldCharge; G_Sound( ent, CHAN_AUTO, trap_SoundIndex( GS_FindItemByTag( POWERUP_SHELL )->pickup_sound ), ATTN_NORM ); } } // if( client->ps.pmove.pm_type == PM_NORMAL ) client->level.stats.had_playtime = true; // generating plrkeys (optimized for net communication) ClientMakePlrkeys( client, ucmd ); }
//========================================== // BOT_DMclass_ChooseWeapon // Choose weapon based on range & weights //========================================== static float BOT_DMclass_ChooseWeapon( edict_t *self ) { float dist; int i; float best_weight = 0.0; gsitem_t *weaponItem; int curweapon, weapon_range = 0, best_weapon = WEAP_NONE; curweapon = self->r.client->ps.stats[STAT_PENDING_WEAPON]; // if no enemy, then what are we doing here? if( !self->enemy ) { weapon_range = AIWEAP_MEDIUM_RANGE; if( curweapon == WEAP_GUNBLADE || curweapon == WEAP_NONE ) self->ai->changeweapon_timeout = level.time; } else // Base weapon selection on distance: { dist = DistanceFast( self->s.origin, self->enemy->s.origin ); if( dist < 150 ) weapon_range = AIWEAP_MELEE_RANGE; else if( dist < 500 ) // Medium range limit is Grenade launcher range weapon_range = AIWEAP_SHORT_RANGE; else if( dist < 900 ) weapon_range = AIWEAP_MEDIUM_RANGE; else weapon_range = AIWEAP_LONG_RANGE; } if( self->ai->changeweapon_timeout > level.time ) return AIWeapons[curweapon].RangeWeight[weapon_range]; for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { float rangeWeight; if( ( weaponItem = GS_FindItemByTag( i ) ) == NULL ) continue; if( !GS_CheckAmmoInWeapon( &self->r.client->ps, i ) ) continue; rangeWeight = AIWeapons[i].RangeWeight[weapon_range] * self->ai->pers.cha.weapon_affinity[i - ( WEAP_GUNBLADE - 1 )]; // weigh up if having strong ammo if( self->r.client->ps.inventory[weaponItem->ammo_tag] ) rangeWeight *= 1.25; // add a small random factor (less random the more skill) rangeWeight += brandom( -( 1.0 - self->ai->pers.skillLevel ), 1.0 - self->ai->pers.skillLevel ); // compare range weights if( rangeWeight > best_weight ) { best_weight = rangeWeight; best_weapon = i; } } // do the change (same weapon, or null best_weapon is covered at ChangeWeapon) if( best_weapon != WEAP_NONE ) BOT_DMClass_ChangeWeapon( self, best_weapon ); return AIWeapons[curweapon].RangeWeight[weapon_range]; // return current }
/* * PrecacheItem * * Precaches all data needed for a given item. * This will be called for each item spawned in a level, * and for each item in each client's inventory. */ void PrecacheItem( gsitem_t *it ) { int i; const char *s, *start; char data[MAX_QPATH]; int len; gsitem_t *ammo; if( !it ) return; if( it->pickup_sound ) trap_SoundIndex( it->pickup_sound ); for( i = 0; i < MAX_ITEM_MODELS; i++ ) { if( it->world_model[i] ) trap_ModelIndex( it->world_model[i] ); } if( it->icon ) trap_ImageIndex( it->icon ); // parse everything for its ammo if( it->ammo_tag ) { ammo = GS_FindItemByTag( it->ammo_tag ); if( ammo != it ) PrecacheItem( ammo ); } // parse the space separated precache string for other items for( i = 0; i < 3; i++ ) { if( i == 0 ) s = it->precache_models; else if( i == 1 ) s = it->precache_sounds; else s = it->precache_images; if( !s || !s[0] ) continue; while( *s ) { start = s; while( *s && *s != ' ' ) s++; len = s-start; if( len >= MAX_QPATH || len < 5 ) G_Error( "PrecacheItem: %s has bad precache string", it->classname ); memcpy( data, start, len ); data[len] = 0; if( *s ) s++; if( i == 0 ) trap_ModelIndex( data ); else if( i == 1 ) trap_SoundIndex( data ); else trap_ImageIndex( data ); } } }