mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
773 lines
21 KiB
C
773 lines
21 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1998 Steve Yeager
|
|
|
|
This file is part of ACE Bot source code.
|
|
|
|
ACE Bot source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
ACE Bot source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with ACE Bot source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ACE - Quake II Bot Base Code
|
|
//
|
|
// Version 1.0
|
|
//
|
|
// This file is Copyright(c), Steve Yeager 1998, All Rights Reserved
|
|
//
|
|
//
|
|
// All other files are Copyright(c) Id Software, Inc.
|
|
//
|
|
// Please see liscense.txt in the source directory for the copyright
|
|
// information regarding those files belonging to Id Software, Inc.
|
|
//
|
|
// Should you decide to release a modified version of ACE, you MUST
|
|
// include the following text (minus the BEGIN and END lines) in the
|
|
// documentation for your modification.
|
|
//
|
|
// --- BEGIN ---
|
|
//
|
|
// The ACE Bot is a product of Steve Yeager, and is available from
|
|
// the ACE Bot homepage, at http://www.axionfx.com/ace.
|
|
//
|
|
// This program is a modification of the ACE Bot, and is therefore
|
|
// in NO WAY supported by Steve Yeager.
|
|
//
|
|
// --- END ---
|
|
//
|
|
// I, Steve Yeager, hold no responsibility for any harm caused by the
|
|
// use of this source code, especially to small children and animals.
|
|
// It is provided as-is with no implied warranty or support.
|
|
//
|
|
// I also wish to thank and acknowledge the great work of others
|
|
// that has helped me to develop this code.
|
|
//
|
|
// John Cricket - For ideas and swapping code.
|
|
// Ryan Feltrin - For ideas and swapping code.
|
|
// SABIN - For showing how to do true client based movement.
|
|
// BotEpidemic - For keeping us up to date.
|
|
// Telefragged.com - For giving ACE a home.
|
|
// Microsoft - For giving us such a wonderful crash free OS.
|
|
// id - Need I say more.
|
|
//
|
|
// And to all the other testers, pathers, and players and people
|
|
// who I can't remember who the heck they were, but helped out.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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_NODES]; // was 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
|
|
|
|
trace = gi.trace (self->s.origin, v, self->maxs, goal, self, 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;
|
|
|
|
trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, goal, self, MASK_OPAQUE);
|
|
|
|
// Yes we can see it
|
|
if (trace.fraction == 1.0)
|
|
return true;
|
|
else
|
|
return false;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Weapon changing support
|
|
///////////////////////////////////////////////////////////////////////
|
|
qboolean ACEIT_ChangeWeapon (edict_t *ent, gitem_t *item)
|
|
{
|
|
int ammo_index;
|
|
gitem_t *ammo_item;
|
|
|
|
// see if we're already using it
|
|
if (item == ent->client->pers.weapon)
|
|
return 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)
|
|
return false;
|
|
}
|
|
|
|
// Change to this weapon
|
|
ent->client->newweapon = item;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
|
|
// Make sure item is at least close to being valid
|
|
if(item < 0 || item > 100)
|
|
return 0.0;
|
|
|
|
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:
|
|
case ITEMLIST_FLASHLIGHT: // Knightmare added
|
|
case ITEMLIST_STASIS: // Knightmare added
|
|
case ITEMLIST_JETPACK: // Knightmare added
|
|
return 0.6;
|
|
|
|
// 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.4;
|
|
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 1.5;
|
|
else
|
|
return 0.0;
|
|
|
|
// Knightmare added
|
|
case ITEMLIST_HOMINGROCKETS:
|
|
if(self->client->pers.inventory[ITEMLIST_HOMINGROCKETS] < self->client->pers.max_homing_rockets)
|
|
return 1.5;
|
|
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;
|
|
|
|
// Knightmare added
|
|
case ITEMLIST_FUEL:
|
|
if(self->client->pers.inventory[ITEMLIST_FUEL] < self->client->pers.max_fuel)
|
|
return 0.3;
|
|
else
|
|
return 0.0;
|
|
|
|
// Knightmare added
|
|
case ITEMLIST_AMMOGENPACK:
|
|
return 0.5;
|
|
|
|
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 or three, I want team two's flag
|
|
if(!self->client->pers.inventory[item]
|
|
&& self->client->resp.ctf_team == CTF_TEAM2 || self->client->resp.ctf_team == CTF_TEAM3)
|
|
return 10.0;
|
|
else
|
|
return 0.0;
|
|
|
|
case ITEMLIST_FLAG2:
|
|
if(!self->client->pers.inventory[item]
|
|
&& self->client->resp.ctf_team == CTF_TEAM1 || self->client->resp.ctf_team == CTF_TEAM3)
|
|
return 10.0;
|
|
else
|
|
return 0.0;
|
|
|
|
case ITEMLIST_FLAG3:
|
|
if(!self->client->pers.inventory[item]
|
|
&& self->client->resp.ctf_team == CTF_TEAM1 || self->client->resp.ctf_team == CTF_TEAM2)
|
|
return 10.0;
|
|
else
|
|
return 0.0;
|
|
|
|
|
|
case ITEMLIST_RESISTANCETECH:
|
|
case ITEMLIST_STRENGTHTECH:
|
|
case ITEMLIST_HASTETECH:
|
|
case ITEMLIST_REGENERATIONTECH:
|
|
case ITEMLIST_VAMPIRETECH: // Knightmare added
|
|
case ITEMLIST_AMMOGENTECH: // Knightamre added
|
|
// 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_VAMPIRETECH] &&
|
|
!self->client->pers.inventory[ITEMLIST_AMMOGENTECH] &&
|
|
!self->client->pers.inventory[ITEMLIST_REGENERATIONTECH])
|
|
return 0.4;
|
|
else
|
|
return 0.0;
|
|
|
|
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,"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;
|
|
|
|
// Knightmare added
|
|
if(strcmp(classname,"item_armor_shard_flat")==0)
|
|
return ITEMLIST_ARMORSHARD_FLAT;
|
|
|
|
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,"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,"weapon_hml")==0)
|
|
// return ITEMLIST_HML;
|
|
|
|
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;
|
|
|
|
// Knightmare added
|
|
if(strcmp(classname,"ammo_homing_missiles")==0)
|
|
return ITEMLIST_HOMINGROCKETS;
|
|
|
|
if(strcmp(classname,"ammo_slugs")==0)
|
|
return ITEMLIST_SLUGS;
|
|
|
|
// Knightmare added
|
|
if(strcmp(classname,"ammo_fuel")==0)
|
|
return ITEMLIST_FUEL;
|
|
|
|
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;
|
|
|
|
// Knightmare added
|
|
if(strcmp(classname,"item_flashlight")==0)
|
|
return ITEMLIST_FLASHLIGHT;
|
|
|
|
if(strcmp(classname,"item_jetpack")==0)
|
|
return ITEMLIST_JETPACK;
|
|
|
|
if(strcmp(classname,"item_freeze")==0)
|
|
return ITEMLIST_STASIS;
|
|
//end Knightmare
|
|
|
|
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_health_small")==0)
|
|
return ITEMLIST_HEALTH_SMALL;
|
|
|
|
if(strcmp(classname,"item_health")==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;
|
|
|
|
if(strcmp(classname,"item_flag_team1")==0)
|
|
return ITEMLIST_FLAG1;
|
|
|
|
if(strcmp(classname,"item_flag_team2")==0)
|
|
return ITEMLIST_FLAG2;
|
|
|
|
// Knightmare added
|
|
if(strcmp(classname,"item_flag_team3")==0)
|
|
return ITEMLIST_FLAG3;
|
|
|
|
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;
|
|
|
|
// Knightmare added
|
|
if(strcmp(classname,"item_tech5")==0)
|
|
return ITEMLIST_VAMPIRETECH;
|
|
|
|
if(strcmp(classname,"item_tech6")==0)
|
|
return ITEMLIST_AMMOGENTECH;
|
|
|
|
if(strcmp(classname,"item_ammogen_pack")==0)
|
|
return ITEMLIST_AMMOGENPACK;
|
|
// end Knightmare
|
|
|
|
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;
|
|
int j;
|
|
|
|
#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++)
|
|
items = g_edicts+1; // skip the worldspawn
|
|
for (j = 1; j < globals.num_edicts; j++, items++)
|
|
{
|
|
// filter out crap
|
|
if (!items->inuse) // Knightmare added
|
|
continue;
|
|
if (!items->classname)
|
|
continue;
|
|
if (items->solid == SOLID_NOT)
|
|
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;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
// Add a new node at the item's location.
|
|
item_table[num_items].node = ACEND_AddNode(items,NODE_ITEM);
|
|
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_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;
|
|
|
|
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
|
|
|
|
}
|