quake2-action/acesrc/acebot_items.c

1360 lines
34 KiB
C

///////////////////////////////////////////////////////////////////////
//
// ACE - Quake II Bot Base Code
//
// Version 1.0
//
// Original file is Copyright(c), Steve Yeager 1998, All Rights Reserved
//
//
// All other files are Copyright(c) Id Software, Inc.
////////////////////////////////////////////////////////////////////////
/*
* $Header: /LicenseToKill/src/acesrc/acebot_items.c 15 22/10/99 6:21 Riever $
*
* $Log: /LicenseToKill/src/acesrc/acebot_items.c $
*
* 15 22/10/99 6:21 Riever
* Modified item weightings
*
* 14 26/09/99 7:36 Riever
* HC reloading problem fixed - also changed sniper rifle reload code
* again.
*
* 13 21/09/99 14:08 Riever
* AMMO recognition code implemented - takes into account maximum allowed
* of each ammo type
*
* 12 21/09/99 12:49 Riever
* Removed a debug print that got left behind
*
* 11 21/09/99 7:30 Riever
* Found empty gun reload problem on special weapons - fix is now being
* tested (works on MP5)
*
* 10 20/09/99 21:30 Riever
* Persuaded bots to reload until last magazine. Need to review logic over
* full / empty weapons to fix the last niggle!
*
* 9 18/09/99 19:22 Riever
* Special handling added for NODE_DOOR to enable them to be successfully
* re-listed.
*
* 8 18/09/99 15:06 Riever
* Removed code that stopped door nodes being put in item list on reloads
*
* 7 18/09/99 9:50 Riever
* Corrected errors in MP5 code
*
* 6 18/09/99 8:43 Riever
* Integrated NODE_DOOR with item table system
*
* 5 18/09/99 8:06 Riever
* NODE_DOOR is now created correctly
*
* 4 14/09/99 21:53 Riever
* Used safe_bprintf method for all source files
*
* 3 14/09/99 14:07 Riever
* Added dual pistols to item collection list
*
* 2 13/09/99 19:52 Riever
* Added headers
*
*/
///////////////////////////////////////////////////////////////////////
//
// acebot_items.c - This file contains all of the
// item handling routines for the
// ACE bot, including fact table support
//
///////////////////////////////////////////////////////////////////////
#include "../g_local.h"
#include "acebot.h"
int num_players = 0;
int num_items = 0;
item_table_t item_table[MAX_EDICTS];
edict_t *players[MAX_CLIENTS]; // pointers to all players in the game
///////////////////////////////////////////////////////////////////////
// Add the player to our list
///////////////////////////////////////////////////////////////////////
void ACEIT_PlayerAdded(edict_t *ent)
{
players[num_players++] = ent;
}
///////////////////////////////////////////////////////////////////////
// Remove player from list
///////////////////////////////////////////////////////////////////////
void ACEIT_PlayerRemoved(edict_t *ent)
{
int i;
int pos;
// watch for 0 players
if(num_players == 0)
return;
// special cas for only one player
if(num_players == 1)
{
num_players = 0;
return;
}
// Find the player
for(i=0;i<num_players;i++)
if(ent == players[i])
pos = i;
// decrement
for(i=pos;i<num_players-1;i++)
players[i] = players[i+1];
num_players--;
}
///////////////////////////////////////////////////////////////////////
// Can we get there?
///////////////////////////////////////////////////////////////////////
qboolean ACEIT_IsReachable(edict_t *self, vec3_t goal)
{
trace_t trace;
vec3_t v;
VectorCopy(self->mins,v);
v[2] += 18; // Stepsize
//AQ2 trace = gi.trace (self->s.origin, v, self->maxs, goal, self, MASK_OPAQUE);
trace = gi.trace (self->s.origin, v, self->maxs, goal, self, MASK_SOLID|MASK_OPAQUE);
// Yes we can see it
if (trace.fraction == 1.0)
return true;
else
return false;
}
///////////////////////////////////////////////////////////////////////
// Visiblilty check
///////////////////////////////////////////////////////////////////////
qboolean ACEIT_IsVisible(edict_t *self, vec3_t goal)
{
trace_t trace;
//AQ2 trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, goal, self, MASK_OPAQUE);
trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, goal, self, MASK_SOLID|MASK_OPAQUE);
// Yes we can see it
if (trace.fraction == 1.0)
return true;
else
return false;
}
///////////////////////////////////////////////////////////////////////
// Weapon changing support
///////////////////////////////////////////////////////////////////////
//AQ2 CHANGE
// Completely rewritten for AQ2!
//
qboolean ACEIT_ChangeWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
return false;
// Do we have ammo for it?
if (item->ammo)
{
ammo_item = FindItem(item->ammo);
ammo_index = ITEM_INDEX(ammo_item);
if (!ent->client->pers.inventory[ammo_index] )//&& !g_select_empty->value)
clips = false;
else
clips = true;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
//Do we have one in the chamber?
if( ent->client->weaponstate == WEAPON_END_MAG)
{
loaded = false;
}
// No ammo - forget it!
if(!loaded && !clips)
return false;
// If it's not loaded - use a new clip
else if( !loaded )
Cmd_New_Reload_f ( ent );
return true;
}
// Change to this weapon
ent->client->newweapon = item;
ChangeWeapon( ent );
return true;
}
//===============================
// Handling for MK23 (debugging)
//===============================
qboolean ACEIT_ChangeMK23SpecialWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
{
// safe_bprintf(PRINT_HIGH,"Not got MP23\n");
return false;
}
// Do we have ammo for it?
if (item->ammo)
{
ammo_item = FindItem(item->ammo);
ammo_index = ITEM_INDEX(ammo_item);
if (ent->client->pers.inventory[ammo_index] < 1)
clips = false;
else
clips = true;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
//Do we have one in the chamber?
if( ent->client->weaponstate == WEAPON_END_MAG)
{
loaded = false;
}
// No ammo - forget it!
if(!loaded && !clips)
{
// safe_bprintf(PRINT_HIGH,"Not got MK23 Ammo\n");
return false;
}
// If it's not loaded - use a new clip
else if( !loaded )
{
// safe_bprintf(PRINT_HIGH,"Need to reload MK23\n");
Cmd_New_Reload_f ( ent );
}
return true;
}
// Not current weapon, check ammo
if( (ent->client->mk23_rds < 1) && !clips )
{
// safe_bprintf(PRINT_HIGH,"No change - No MK23 Ammo\n");
return false;
}
// Change to this weapon
// safe_bprintf(PRINT_HIGH,"Changing to MK23\n");
ent->client->newweapon = item;
ChangeWeapon ( ent );
return true;
}
//===============================
// Handling for HC (debugging)
//===============================
qboolean ACEIT_ChangeHCSpecialWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
{
// safe_bprintf(PRINT_HIGH,"Not got HC\n");
return false;
}
// Do we have ammo for it?
if (item->ammo)
{
ammo_item = FindItem(item->ammo);
ammo_index = ITEM_INDEX(ammo_item);
if (ent->client->pers.inventory[ammo_index] < 2)
clips = false;
else
clips = true;
}
// Check ammo
if( (ent->client->cannon_rds < 2) )
{
// safe_bprintf(PRINT_HIGH,"No change - No HC Ammo\n");
loaded = false;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
/* //Do we have one in the chamber?
if( ent->client->weaponstate == WEAPON_END_MAG)
{
loaded = false;
}*/
// No ammo - forget it!
if(!loaded && !clips)
{
// safe_bprintf(PRINT_HIGH,"Not got HC Ammo\n");
DropSpecialWeapon ( ent );
return false;
}
// If it's not loaded - use a new clip
else if( !loaded )
{
// safe_bprintf(PRINT_HIGH,"Need to reload HC\n");
Cmd_New_Reload_f ( ent );
}
return true;
}
// Not current weapon, check ammo
if( !loaded && !clips )
{
// safe_bprintf(PRINT_HIGH,"No change - No HC Ammo\n");
return false;
}
// Change to this weapon
// safe_bprintf(PRINT_HIGH,"Changing to HandCannon\n");
ent->client->newweapon = item;
ChangeWeapon ( ent );
return true;
}
//===============================
// Handling for Sniper Rifle (debugging)
//===============================
qboolean ACEIT_ChangeSniperSpecialWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
{
// safe_bprintf(PRINT_HIGH,"Not got Sniper Rifle\n");
return false;
}
// Do we have ammo for it?
if (item->ammo)
{
ammo_item = FindItem(item->ammo);
ammo_index = ITEM_INDEX(ammo_item);
if (ent->client->pers.inventory[ammo_index] < 1)
clips = false;
else
clips = true;
}
// Check ammo
if( (ent->client->sniper_rds < 1) )
{
// safe_bprintf(PRINT_HIGH,"No change - No Sniper Ammo\n");
loaded = false;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
/* //Do we have one in the chamber?
if( ent->client->weaponstate == WEAPON_END_MAG)
{
loaded = false;
}*/
// No ammo - forget it!
if(!loaded && !clips)
{
// safe_bprintf(PRINT_HIGH,"Not got Sniper Ammo\n");
DropSpecialWeapon ( ent );
return false;
}
// If it's not loaded - use a new clip
else if( !loaded )
{
// safe_bprintf(PRINT_HIGH,"Need to reload Sniper Rifle\n");
Cmd_New_Reload_f ( ent );
}
return true;
}
// No ammo
if( !loaded && !clips )
{
// safe_bprintf(PRINT_HIGH,"No change - No Sniper Ammo\n");
return false;
}
// Change to this weapon
// safe_bprintf(PRINT_HIGH,"Changing to Sniper Rifle\n");
ent->client->newweapon = item;
ChangeWeapon ( ent );
return true;
}
//===============================
// Handling for M3 (debugging)
//===============================
qboolean ACEIT_ChangeM3SpecialWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
{
// safe_bprintf(PRINT_HIGH,"Not got M3\n");
return false;
}
// Do we have ammo for it?
if (item->ammo)
{
ammo_item = FindItem(item->ammo);
ammo_index = ITEM_INDEX(ammo_item);
if (ent->client->pers.inventory[ammo_index] < 1)
clips = false;
else
clips = true;
}
// Check ammo
if( (ent->client->shot_rds < 1) )
{
// safe_bprintf(PRINT_HIGH,"No change - No M3 Ammo\n");
loaded = false;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
/* //Do we have one in the chamber?
if( ent->client->weaponstate == WEAPON_END_MAG)
{
loaded = false;
}*/
// No ammo - forget it!
if(!loaded && !clips)
{
// safe_bprintf(PRINT_HIGH,"Not got M3 Ammo\n");
DropSpecialWeapon ( ent );
return false;
}
// If it's not loaded - use a new clip
else if( !loaded )
{
// safe_bprintf(PRINT_HIGH,"Need to reload M3\n");
Cmd_New_Reload_f ( ent );
}
return true;
}
// No ammo
if( !loaded && !clips )
{
// safe_bprintf(PRINT_HIGH,"No change - No M3 Ammo\n");
return false;
}
// Change to this weapon
// safe_bprintf(PRINT_HIGH,"Changing to M3\n");
ent->client->newweapon = item;
ChangeWeapon ( ent );
return true;
}
//===============================
// Handling for M4 (debugging)
//===============================
qboolean ACEIT_ChangeM4SpecialWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
{
// safe_bprintf(PRINT_HIGH,"Not got MP4\n");
return false;
}
// Do we have ammo for it?
if (item->ammo)
{
ammo_item = FindItem(item->ammo);
ammo_index = ITEM_INDEX(ammo_item);
if (ent->client->pers.inventory[ammo_index] < 1)//&& !g_select_empty->value)
clips = false;
else
clips = true;
}
// Check ammo
if( (ent->client->m4_rds < 1) )
{
// safe_bprintf(PRINT_HIGH,"No change - No M4 Ammo\n");
loaded = false;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
/* //Do we have one in the chamber?
if( ent->client->weaponstate == WEAPON_END_MAG)
{
loaded = false;
}*/
// No ammo - forget it!
if(!loaded && !clips)
{
// safe_bprintf(PRINT_HIGH,"Not got M4 Ammo\n");
DropSpecialWeapon ( ent );
return false;
}
// If it's not loaded - use a new clip
else if( !loaded )
{
// safe_bprintf(PRINT_HIGH,"Need to reload M4\n");
Cmd_New_Reload_f ( ent );
}
return true;
}
// No ammo
if( !loaded && !clips )
{
// safe_bprintf(PRINT_HIGH,"No change - No M4 Ammo\n");
return false;
}
// Change to this weapon
// safe_bprintf(PRINT_HIGH,"Changing to M4\n");
ent->client->newweapon = item;
ChangeWeapon ( ent );
return true;
}
//===============================
// Handling for MP5 (debugging)
//===============================
qboolean ACEIT_ChangeMP5SpecialWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
{
// safe_bprintf(PRINT_HIGH,"Not got MP5\n");
return false;
}
// Do we have ammo for it?
// if (item->ammo)
// {
// ammo_item = FindItem(item->ammo);
ammo_item = FindItem("Machinegun Magazine");
ammo_index = ITEM_INDEX(ammo_item);
if (ent->client->pers.inventory[ammo_index] < 1)
{
// safe_bprintf(PRINT_HIGH,"Out of MP5 Clips\n");
clips = false;
}
else
clips = true;
// }
// check ammo
if( (ent->client->mp5_rds < 1) )
{
// safe_bprintf(PRINT_HIGH,"No MP5 Ammo\n");
loaded = false;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
// No ammo - forget it!
if(!loaded && !clips)
{
// safe_bprintf(PRINT_HIGH,"Stopping MP5 use - no ammo\n");
DropSpecialWeapon ( ent );
return false;
}
// If it's not loaded - use a new clip
else if( !loaded && clips)
{
// safe_bprintf(PRINT_HIGH,"Need to reload MP5\n");
Cmd_New_Reload_f ( ent );
}
return true;
}
// Not current weapon, check ammo
if( !loaded && !clips )
{
// safe_bprintf(PRINT_HIGH,"No change - No MP5 Ammo\n");
return false;
}
// Change to this weapon
// safe_bprintf(PRINT_HIGH,"Changing to MP5\n");
ent->client->newweapon = item;
ChangeWeapon ( ent );
return true;
}
//===============================
// Handling for DUAL PISTOLS (debugging)
//===============================
qboolean ACEIT_ChangeDualSpecialWeapon (edict_t *ent, gitem_t *item)
{
int ammo_index;
gitem_t *ammo_item;
qboolean loaded = true;
qboolean clips = true;
// Has not picked up weapon yet
if(!ent->client->pers.inventory[ITEM_INDEX(item)])
{
// safe_bprintf(PRINT_HIGH,"Not got Dual Pistols\n");
return false;
}
// Do we have ammo for it?
if (item->ammo)
{
ammo_item = FindItem(item->ammo);
ammo_index = ITEM_INDEX(ammo_item);
if (ent->client->pers.inventory[ammo_index] < 2)//&& !g_select_empty->value)
clips = false;
else
clips = true;
}
// Not current weapon, check ammo
if( (ent->client->dual_rds < 2) )
{
// safe_bprintf(PRINT_HIGH,"No change - No Dual Ammo\n");
loaded = false;
}
// see if we're already using it
if (item == ent->client->pers.weapon)
{
/* //Do we have one in the chamber?
if( ent->client->weaponstate == WEAPON_END_MAG)
{
loaded = false;
}*/
// No ammo - forget it!
if(!loaded && !clips)
{
// safe_bprintf(PRINT_HIGH,"Not got Dual Ammo\n");
DropSpecialWeapon ( ent );
return false;
}
// If it's not loaded - use a new clip
else if( !loaded )
{
// safe_bprintf(PRINT_HIGH,"Need to reload Dual\n");
Cmd_New_Reload_f ( ent );
}
return true;
}
// Not current weapon, check ammo
if( !loaded && !clips )
{
// safe_bprintf(PRINT_HIGH,"No change - No Dual Ammo\n");
return false;
}
// Change to this weapon
// safe_bprintf(PRINT_HIGH,"Changing to Dual\n");
ent->client->newweapon = item;
ChangeWeapon ( ent );
return true;
}
//AQ2 END
extern gitem_armor_t jacketarmor_info;
extern gitem_armor_t combatarmor_info;
extern gitem_armor_t bodyarmor_info;
///////////////////////////////////////////////////////////////////////
// Check if we can use the armor
///////////////////////////////////////////////////////////////////////
qboolean ACEIT_CanUseArmor (gitem_t *item, edict_t *other)
{
int old_armor_index;
gitem_armor_t *oldinfo;
gitem_armor_t *newinfo;
int newcount;
float salvage;
int salvagecount;
// get info on new armor
newinfo = (gitem_armor_t *)item->info;
old_armor_index = ArmorIndex (other);
// handle armor shards specially
if (item->tag == ARMOR_SHARD)
return true;
// get info on old armor
if (old_armor_index == ITEM_INDEX(FindItem("Jacket Armor")))
oldinfo = &jacketarmor_info;
else if (old_armor_index == ITEM_INDEX(FindItem("Combat Armor")))
oldinfo = &combatarmor_info;
else // (old_armor_index == body_armor_index)
oldinfo = &bodyarmor_info;
if (newinfo->normal_protection <= oldinfo->normal_protection)
{
// calc new armor values
salvage = newinfo->normal_protection / oldinfo->normal_protection;
salvagecount = salvage * newinfo->base_count;
newcount = other->client->pers.inventory[old_armor_index] + salvagecount;
if (newcount > oldinfo->max_count)
newcount = oldinfo->max_count;
// if we're already maxed out then we don't need the new armor
if (other->client->pers.inventory[old_armor_index] >= newcount)
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////
// Determines the NEED for an item
//
// This function can be modified to support new items to pick up
// Any other logic that needs to be added for custom decision making
// can be added here. For now it is very simple.
///////////////////////////////////////////////////////////////////////
float ACEIT_ItemNeed(edict_t *self, int item)
{
gitem_t *haveband;
int band;
// Make sure item is at least close to being valid
if(item < 0 || item > 100)
return 0.0;
//AQ2 ADD
// find out if they have a bandolier
haveband = FindItem(BAND_NAME);
if ( self->client->pers.inventory[ITEM_INDEX(haveband)] )
band = 1;
else
band = 0;
//AQ2 END
switch(item)
{
// Health
case ITEMLIST_HEALTH_SMALL:
case ITEMLIST_HEALTH_MEDIUM:
case ITEMLIST_HEALTH_LARGE:
case ITEMLIST_HEALTH_MEGA:
if(self->health < 100)
return 1.0 - (float)self->health/100.0f; // worse off, higher priority
else
return 0.0;
case ITEMLIST_AMMOPACK:
case ITEMLIST_QUADDAMAGE:
case ITEMLIST_INVULNERABILITY:
case ITEMLIST_SILENCER:
// case ITEMLIST_REBREATHER
// case ITEMLIST_ENVIRONMENTSUIT
case ITEMLIST_ADRENALINE:
case ITEMLIST_BANDOLIER:
return 0.5;
/*
// Weapons
case ITEMLIST_ROCKETLAUNCHER:
case ITEMLIST_RAILGUN:
case ITEMLIST_MACHINEGUN:
case ITEMLIST_CHAINGUN:
case ITEMLIST_SHOTGUN:
case ITEMLIST_SUPERSHOTGUN:
case ITEMLIST_BFG10K:
case ITEMLIST_GRENADELAUNCHER:
case ITEMLIST_HYPERBLASTER:
if(!self->client->pers.inventory[item])
return 0.7;
else
return 0.0;*/
// Ammo
case ITEMLIST_SLUGS:
if(self->client->pers.inventory[ITEMLIST_SLUGS] < self->client->pers.max_slugs)
return 0.3;
else
return 0.0;
case ITEMLIST_BULLETS:
if(self->client->pers.inventory[ITEMLIST_BULLETS] < self->client->pers.max_bullets)
return 0.3;
else
return 0.0;
case ITEMLIST_SHELLS:
if(self->client->pers.inventory[ITEMLIST_SHELLS] < self->client->pers.max_shells)
return 0.3;
else
return 0.0;
case ITEMLIST_CELLS:
if(self->client->pers.inventory[ITEMLIST_CELLS] < self->client->pers.max_cells)
return 0.3;
else
return 0.0;
case ITEMLIST_ROCKETS:
if(self->client->pers.inventory[ITEMLIST_ROCKETS] < self->client->pers.max_rockets)
return 0.3;
else
return 0.0;
case ITEMLIST_GRENADES:
if(self->client->pers.inventory[ITEMLIST_GRENADES] < self->client->pers.max_grenades)
return 0.3;
else
return 0.0;
case ITEMLIST_BODYARMOR:
if(ACEIT_CanUseArmor (FindItem("Body Armor"), self))
return 0.6;
else
return 0.0;
case ITEMLIST_COMBATARMOR:
if(ACEIT_CanUseArmor (FindItem("Combat Armor"), self))
return 0.6;
else
return 0.0;
case ITEMLIST_JACKETARMOR:
if(ACEIT_CanUseArmor (FindItem("Jacket Armor"), self))
return 0.6;
else
return 0.0;
case ITEMLIST_POWERSCREEN:
case ITEMLIST_POWERSHIELD:
return 0.5;
/* case ITEMLIST_FLAG1:
// If I am on team one, I want team two's flag
if(!self->client->pers.inventory[item] && self->client->resp.ctf_team == CTF_TEAM2)
return 10.0;
else
return 0.0;
case ITEMLIST_FLAG2:
if(!self->client->pers.inventory[item] && self->client->resp.ctf_team == CTF_TEAM1)
return 10.0;
else
return 0.0;
case ITEMLIST_RESISTANCETECH:
case ITEMLIST_STRENGTHTECH:
case ITEMLIST_HASTETECH:
case ITEMLIST_REGENERATIONTECH:
// Check for other tech
if(!self->client->pers.inventory[ITEMLIST_RESISTANCETECH] &&
!self->client->pers.inventory[ITEMLIST_STRENGTHTECH] &&
!self->client->pers.inventory[ITEMLIST_HASTETECH] &&
!self->client->pers.inventory[ITEMLIST_REGENERATIONTECH])
return 0.4;
else
return 0.0;*/
//AQ2 ADD
case ITEMLIST_MP5:
case ITEMLIST_M4:
case ITEMLIST_M3:
case ITEMLIST_HC:
case ITEMLIST_SNIPER:
case ITEMLIST_DUAL:
if(
( self->client->unique_weapon_total < unique_weapons->value + band )
&& (!self->client->pers.inventory[item])
)
return 0.7;
else
return 0.0;
case ITEMLIST_AMMO_CLIP:
if(self->client->pers.inventory[ITEM_INDEX(FindItem(AMMO_CLIP_NAME))]
< self->client->pers.max_bullets)
return 0.3;
else
return 0.0;
case ITEMLIST_AMMO_M4:
if(self->client->pers.inventory[ITEM_INDEX(FindItem(AMMO_M4_NAME))]
< self->client->pers.max_cells)
return 0.3;
else
return 0.0;
case ITEMLIST_AMMO_MAG:
if(self->client->pers.inventory[ITEM_INDEX(FindItem(AMMO_MAG_NAME))]
< self->client->pers.max_rockets)
return 0.3;
else
return 0.0;
case ITEMLIST_AMMO_SNIPER:
if(self->client->pers.inventory[ITEM_INDEX(FindItem(AMMO_SNIPER_NAME))]
< self->client->pers.max_slugs)
return 0.3;
else
return 0.0;
case ITEMLIST_AMMO_M3:
if(self->client->pers.inventory[ITEM_INDEX(FindItem(AMMO_M3_NAME))]
< self->client->pers.max_shells)
return 0.3;
else
return 0.0;
//AQ2 END
default:
return 0.0;
}
}
///////////////////////////////////////////////////////////////////////
// Convert a classname to its index value
//
// I prefer to use integers/defines for simplicity sake. This routine
// can lead to some slowdowns I guess, but makes the rest of the code
// easier to deal with.
///////////////////////////////////////////////////////////////////////
int ACEIT_ClassnameToIndex(char *classname)
{
if(strcmp(classname,"weapon_Mk23")==0)
return ITEMLIST_MK23;
if(strcmp(classname,"weapon_MP5")==0)
return ITEMLIST_MP5;
if(strcmp(classname,"weapon_M4")==0)
return ITEMLIST_M4;
if(strcmp(classname,"weapon_M3")==0)
return ITEMLIST_M3;
if(strcmp(classname,"weapon_HC")==0)
return ITEMLIST_HC;
if(strcmp(classname,"weapon_Sniper")==0)
return ITEMLIST_SNIPER;
if(strcmp(classname,"weapon_Dual")==0)
return ITEMLIST_DUAL;
if(strcmp(classname,"weapon_Knife")==0)
return ITEMLIST_KNIFE;
if(strcmp(classname,"weapon_Grenade")==0)
return ITEMLIST_GRENADE;
if(strcmp(classname,"item_quiet")==0)
return ITEMLIST_SIL;
if(strcmp(classname,"item_slippers")==0)
return ITEMLIST_SLIP;
if(strcmp(classname,"item_band")==0)
return ITEMLIST_BAND;
if(strcmp(classname,"item_vest")==0)
return ITEMLIST_KEV;
if(strcmp(classname,"item_lasersight")==0)
return ITEMLIST_LASER;
if(strcmp(classname,"ammo_clip")==0)
return ITEMLIST_AMMO_CLIP;
if(strcmp(classname,"ammo_m4")==0)
return ITEMLIST_AMMO_M4;
if(strcmp(classname,"ammo_mag")==0)
return ITEMLIST_AMMO_MAG;
if(strcmp(classname,"ammo_sniper")==0)
return ITEMLIST_AMMO_SNIPER;
if(strcmp(classname,"ammo_m3")==0)
return ITEMLIST_AMMO_M3;
// Normal quake stuff:
if(strcmp(classname,"item_armor_body")==0)
return ITEMLIST_BODYARMOR;
if(strcmp(classname,"item_armor_combat")==0)
return ITEMLIST_COMBATARMOR;
if(strcmp(classname,"item_armor_jacket")==0)
return ITEMLIST_JACKETARMOR;
if(strcmp(classname,"item_armor_shard")==0)
return ITEMLIST_ARMORSHARD;
if(strcmp(classname,"item_power_screen")==0)
return ITEMLIST_POWERSCREEN;
if(strcmp(classname,"item_power_shield")==0)
return ITEMLIST_POWERSHIELD;
if(strcmp(classname,"weapon_grapple")==0)
return ITEMLIST_GRAPPLE;
if(strcmp(classname,"weapon_blaster")==0)
return ITEMLIST_BLASTER;
if(strcmp(classname,"weapon_shotgun")==0)
return ITEMLIST_SHOTGUN;
if(strcmp(classname,"weapon_supershotgun")==0)
return ITEMLIST_SUPERSHOTGUN;
if(strcmp(classname,"weapon_machinegun")==0)
return ITEMLIST_MACHINEGUN;
if(strcmp(classname,"weapon_chaingun")==0)
return ITEMLIST_CHAINGUN;
if(strcmp(classname,"weapon_chaingun")==0)
return ITEMLIST_CHAINGUN;
if(strcmp(classname,"ammo_grenades")==0)
return ITEMLIST_GRENADES;
if(strcmp(classname,"weapon_grenadelauncher")==0)
return ITEMLIST_GRENADELAUNCHER;
if(strcmp(classname,"weapon_rocketlauncher")==0)
return ITEMLIST_ROCKETLAUNCHER;
if(strcmp(classname,"weapon_hyperblaster")==0)
return ITEMLIST_HYPERBLASTER;
if(strcmp(classname,"weapon_railgun")==0)
return ITEMLIST_RAILGUN;
if(strcmp(classname,"weapon_bfg10k")==0)
return ITEMLIST_BFG10K;
if(strcmp(classname,"ammo_shells")==0)
return ITEMLIST_SHELLS;
if(strcmp(classname,"ammo_bullets")==0)
return ITEMLIST_BULLETS;
if(strcmp(classname,"ammo_cells")==0)
return ITEMLIST_CELLS;
if(strcmp(classname,"ammo_rockets")==0)
return ITEMLIST_ROCKETS;
if(strcmp(classname,"ammo_slugs")==0)
return ITEMLIST_SLUGS;
if(strcmp(classname,"item_quad")==0)
return ITEMLIST_QUADDAMAGE;
if(strcmp(classname,"item_invunerability")==0)
return ITEMLIST_INVULNERABILITY;
if(strcmp(classname,"item_silencer")==0)
return ITEMLIST_SILENCER;
if(strcmp(classname,"item_rebreather")==0)
return ITEMLIST_REBREATHER;
if(strcmp(classname,"item_enviornmentsuit")==0)
return ITEMLIST_ENVIRONMENTSUIT;
if(strcmp(classname,"item_ancienthead")==0)
return ITEMLIST_ANCIENTHEAD;
if(strcmp(classname,"item_adrenaline")==0)
return ITEMLIST_ADRENALINE;
if(strcmp(classname,"item_bandolier")==0)
return ITEMLIST_BANDOLIER;
if(strcmp(classname,"item_pack")==0)
return ITEMLIST_AMMOPACK;
if(strcmp(classname,"item_datacd")==0)
return ITEMLIST_DATACD;
if(strcmp(classname,"item_powercube")==0)
return ITEMLIST_POWERCUBE;
if(strcmp(classname,"item_pyramidkey")==0)
return ITEMLIST_PYRAMIDKEY;
if(strcmp(classname,"item_dataspinner")==0)
return ITEMLIST_DATASPINNER;
if(strcmp(classname,"item_securitypass")==0)
return ITEMLIST_SECURITYPASS;
if(strcmp(classname,"item_bluekey")==0)
return ITEMLIST_BLUEKEY;
if(strcmp(classname,"item_redkey")==0)
return ITEMLIST_REDKEY;
if(strcmp(classname,"item_commandershead")==0)
return ITEMLIST_COMMANDERSHEAD;
if(strcmp(classname,"item_airstrikemarker")==0)
return ITEMLIST_AIRSTRIKEMARKER;
if(strcmp(classname,"item_health")==0) // ??
return ITEMLIST_HEALTH;
if(strcmp(classname,"item_flag_team1")==0)
return ITEMLIST_FLAG1;
if(strcmp(classname,"item_flag_team2")==0)
return ITEMLIST_FLAG2;
if(strcmp(classname,"item_tech1")==0)
return ITEMLIST_RESISTANCETECH;
if(strcmp(classname,"item_tech2")==0)
return ITEMLIST_STRENGTHTECH;
if(strcmp(classname,"item_tech3")==0)
return ITEMLIST_HASTETECH;
if(strcmp(classname,"item_tech4")==0)
return ITEMLIST_REGENERATIONTECH;
if(strcmp(classname,"item_health_small")==0)
return ITEMLIST_HEALTH_SMALL;
if(strcmp(classname,"item_health_medium")==0)
return ITEMLIST_HEALTH_MEDIUM;
if(strcmp(classname,"item_health_large")==0)
return ITEMLIST_HEALTH_LARGE;
if(strcmp(classname,"item_health_mega")==0)
return ITEMLIST_HEALTH_MEGA;
return INVALID;
}
///////////////////////////////////////////////////////////////////////
// Only called once per level, when saved will not be called again
//
// Downside of the routine is that items can not move about. If the level
// has been saved before and reloaded, it could cause a problem if there
// are items that spawn at random locations.
//
#define DEBUG // uncomment to write out items to a file.
///////////////////////////////////////////////////////////////////////
void ACEIT_BuildItemNodeTable (qboolean rebuild)
{
edict_t *items;
int i,item_index;
vec3_t v,v1,v2;
#ifdef DEBUG
FILE *pOut; // for testing
if((pOut = fopen("items.txt","wt"))==NULL)
return;
#endif
num_items = 0;
// Add game items
for(items = g_edicts; items < &g_edicts[globals.num_edicts]; items++)
{
// filter out crap
if(items->solid == SOLID_NOT)
continue;
if(!items->classname)
continue;
/////////////////////////////////////////////////////////////////
// Items
/////////////////////////////////////////////////////////////////
item_index = ACEIT_ClassnameToIndex(items->classname);
////////////////////////////////////////////////////////////////
// SPECIAL NAV NODE DROPPING CODE
////////////////////////////////////////////////////////////////
// Special node dropping for platforms
if(strcmp(items->classname,"func_plat")==0)
{
if(!rebuild)
ACEND_AddNode(items,NODE_PLATFORM);
item_index = 99; // to allow to pass the item index test
}
// Special node dropping for teleporters
if(strcmp(items->classname,"misc_teleporter_dest")==0 || strcmp(items->classname,"misc_teleporter")==0)
{
if(!rebuild)
ACEND_AddNode(items,NODE_TELEPORTER);
item_index = 99;
}
// Special node dropping for doors - RiEvEr
if(strcmp(items->classname,"func_door_rotating")==0 )
{
item_index = 99;
if(!rebuild)
{
// add a pointer to the item entity
item_table[num_items].ent = items;
item_table[num_items].item = item_index;
// create the node
item_table[num_items].node = ACEND_AddNode(items,NODE_DOOR);
#ifdef DEBUG
fprintf(pOut,"item: %s node: %d pos: %f %f %f\n",items->classname,
item_table[num_items].node,
items->s.origin[0],items->s.origin[1],items->s.origin[2]);
#endif
num_items++;
continue;
}
}
#ifdef DEBUG
if(item_index == INVALID)
fprintf(pOut,"Rejected item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
// else
// fprintf(pOut,"item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
#endif
if(item_index == INVALID)
continue;
// add a pointer to the item entity
item_table[num_items].ent = items;
item_table[num_items].item = item_index;
// If new, add nodes for items
if(!rebuild)
{
item_table[num_items].node = ACEND_AddNode(items,NODE_ITEM);
#ifdef DEBUG
fprintf(pOut,"item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
#endif
num_items++;
}
else // Now if rebuilding, just relink ent structures
{
// Find stored location
for(i=0;i<numnodes;i++)
{
if(nodes[i].type == NODE_ITEM ||
nodes[i].type == NODE_PLATFORM ||
nodes[i].type == NODE_DOOR|| // RiEvEr
nodes[i].type == NODE_TELEPORTER) // valid types
{
VectorCopy(items->s.origin,v);
// Add 16 to item type nodes
if(nodes[i].type == NODE_ITEM)
v[2] += 16;
// Add 32 to teleporter
if(nodes[i].type == NODE_TELEPORTER)
v[2] += 32;
// Door handling
if( nodes[i].type == NODE_DOOR)
{
vec3_t position;
// Find mid point of door max and min and put the node there
VectorCopy(items->s.origin, position);
// find center of door
position[0] = position[0] + items->mins[0] + ((items->maxs[0] - items->mins[0]) /2);
position[1] = position[1] + items->mins[1] + ((items->maxs[1] - items->mins[1]) /2);
position[2] -= 16; // lower it a little
// Set location
VectorCopy(position, v);
}
if(nodes[i].type == NODE_PLATFORM)
{
VectorCopy(items->maxs,v1);
VectorCopy(items->mins,v2);
// To get the center
v[0] = (v1[0] - v2[0]) / 2 + v2[0];
v[1] = (v1[1] - v2[1]) / 2 + v2[1];
v[2] = items->mins[2]+64;
}
if(v[0] == nodes[i].origin[0] &&
v[1] == nodes[i].origin[1] &&
v[2] == nodes[i].origin[2])
{
// found a match now link to facts
item_table[num_items].node = i;
#ifdef DEBUG
fprintf(pOut,"Relink item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
#endif
num_items++;
}
}
}
}
}
#ifdef DEBUG
fclose(pOut);
#endif
}