sof2-sdk/code/game/bg_weapons.c
2002-07-15 00:00:00 +00:00

1265 lines
33 KiB
C

// Copyright (C) 2001-2002 Raven Software
//
// bg_weapons.c - weapon data loading
#include "q_shared.h"
#include "bg_public.h"
#include "bg_local.h"
#include "g_local.h"
// names as they appear in the SOF2.wpn and inview files
char *bg_weaponNames[WP_NUM_WEAPONS] =
{
"No Weapon", // WP_NONE,
"Knife", // WP_KNIFE,
"M1911A1", // WP_M1911A1_PISTOL,
"US SOCOM", // WP_US_SOCOM_PISTOL,
"M590", // WP_M590_SHOTGUN,
"Micro Uzi", // WP_MICRO_UZI_SUBMACHINEGUN,
"M3A1", // WP_M3A1_SUBMACHINEGUN,
"MP5", // WP_MP5
"USAS-12", // WP_USAS_12_SHOTGUN,
"M4", // WP_M4_ASSAULT_RIFLE,
"AK74", // WP_AK74_ASSAULT_RIFLE,
"MSG90A1", // WP_MSG90A1_SNIPER_RIFLE,
"M60", // WP_M60_MACHINEGUN,
"MM1", // WP_MM1_GRENADE_LAUNCHER,
"RPG7", // WP_RPG7_LAUNCHER,
"M84", // WP_M84_GRENADE,
"SMOHG92", // WP_SMOHG92_GRENADE,
"ANM14", // WP_ANM14_GRENADE,
"M15", // WP_M15_GRENADE,
};
weaponData_t weaponData[WP_NUM_WEAPONS];
char *ammoNames[AMMO_MAX] =
{
"Knife", // AMMO_KNIFE,
"0.45 ACP", // AMMO_045,
"5.56mm", // AMMO_556,
"9mm", // AMMO_9 ,
"12 gauge", // AMMO_12 ,
"7.62mm", // AMMO_762,
"40mm grenade", // AMMO_40,
"RPG7", // AMMO_RPG7
"M15", // AMMO_M15,
"M84", // AMMO_M84,
"SMOHG92", // AMMO_SMOHG92,
"ANM14", // AMMO_ANM14,
"7.62mm belt", // AMMO_762_BELT,
"9mm|mp5", // AMMO_9_MP5
};
ammoData_t ammoData[AMMO_MAX];
static const char* BG_GetRealAmmoName ( ammo_t ammoNum )
{
static char name[64] = "";
char* or;
or = strchr ( ammoNames[ammoNum], '|' );
if ( or )
{
Q_strncpyz ( name, ammoNames[ammoNum], or - ammoNames[ammoNum] + 1);
}
else
{
strcpy ( name, ammoNames[ammoNum] );
}
return name;
}
static qboolean BG_ParseAmmoStats(ammo_t ammoNum, void *group)
{
char tmpStr[256];
ammoData_t *ammo; // ammo
ammo = &ammoData[ammoNum];
memset(ammo, 0, sizeof(ammoData_t));
ammo->name = (char*)trap_VM_LocalStringAlloc ( BG_GetRealAmmoName ( ammoNum ) );
Q_strlwr ( ammo->name );
// Get the scale of the gore for this bullet
trap_GPG_FindPairValue(group, "mp_goreScale||goreScale", "1", tmpStr );
ammo->goreScale = atof ( tmpStr );
// Max ammo will be filled in by the weapon parsing
return qtrue;
}
qboolean BG_InitAmmoStats(void)
{
void *GP2, *topGroup;
int i;
ammoData[AMMO_NONE].goreScale = 0.0f;
ammoData[AMMO_NONE].name = "none";
GP2 = trap_GP_ParseFile("ext_data/sof2.ammo", qtrue, qfalse);
if (!GP2)
{
return qfalse;
}
topGroup = trap_GP_GetBaseParseGroup(GP2);
for ( i = 0; i < AMMO_MAX; i ++ )
{
void* topSubs;
const char* realName;
realName = BG_GetRealAmmoName ( i );
topSubs = trap_GPG_GetSubGroups(topGroup);
while(topSubs)
{
char name[256];
trap_GPG_GetName(topSubs, name);
if (Q_stricmp(name, "ammo") != 0)
{
continue;
}
trap_GPG_FindPairValue(topSubs, "name", "", name);
if ( !Q_stricmp ( name, realName ) )
{
BG_ParseAmmoStats(i, topSubs );
break;
}
topSubs = trap_GPG_GetNext(topSubs);
}
if ( !topSubs )
{
Com_Printf("BG_InitAmmoStats: Unknown ammo: %s\n", BG_GetRealAmmoName ( i ) );
}
}
trap_GP_Delete(&GP2);
return qtrue;
}
static qboolean BG_ParseAttackStats ( int weaponNum, attackData_t* attack, void *attacksub, qboolean pickupsDisabled )
{
void* sub;
char tmpStr[256];
int i;
// No group is success. This is to allow NULL to be passed
if ( NULL == attacksub )
{
return qtrue;
}
// Assign a melee attribute if there is one
trap_GPG_FindPairValue(attacksub, "mp_melee||melee", "none", tmpStr );
if ( Q_stricmp ( tmpStr, "none" ) )
{
Q_strlwr ( tmpStr );
attack->melee = trap_VM_LocalStringAlloc ( tmpStr );
}
trap_GPG_FindPairValue(attacksub, "name", "NONE", attack->name);
trap_GPG_FindPairValue(attacksub, "hudIcon", "NONE", attack->icon);
if ( pickupsDisabled )
{
trap_GPG_FindPairValue(attacksub, "mp_ammoType_outfitting", "", tmpStr);
if ( !tmpStr[0] )
{
trap_GPG_FindPairValue(attacksub, "mp_ammoType||ammoType", "none", tmpStr);
}
}
else
{
trap_GPG_FindPairValue(attacksub, "mp_ammoType||ammoType", "none", tmpStr);
}
attack->ammoIndex = AMMO_NONE;
for (i = 0; i < AMMO_MAX; i++)
{
if (0 == Q_stricmp(tmpStr, ammoNames[i]))
{
attack->ammoIndex = i;
break;
}
}
#ifdef _DEBUG
if (AMMO_MAX == i)
{
Com_Printf("BG_ParseWeaponStats: Unknown ammo: %s\n", tmpStr);
}
#endif
// Parse the weapon animations
trap_GPG_FindPairValue( attacksub, "mp_animFire", "TORSO_ATTACK_PISTOL", tmpStr );
attack->animFire = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue( attacksub, "mp_animFireZoomed", "", tmpStr );
attack->animFireZoomed = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue(attacksub, "mp_range||range", "8192", tmpStr);
attack->rV.range = atoi(tmpStr);
trap_GPG_FindPairValue(attacksub, "mp_radius||radius", "0", tmpStr);
attack->splashRadius = atoi(tmpStr);
trap_GPG_FindPairValue(attacksub, "mp_fireDelay||fireDelay", "0", tmpStr);
attack->fireDelay = atoi(tmpStr);
trap_GPG_FindPairValue(attacksub, "mp_clipSize||clipSize", "0", tmpStr);
attack->clipSize = atoi(tmpStr);
trap_GPG_FindPairValue(attacksub, "mp_fireAmount||fireAmount", "1", tmpStr);
attack->fireAmount = atoi(tmpStr);
trap_GPG_FindPairValue(attacksub, "mp_fireFromClip||fireFromClip", "1", tmpStr);
attack->fireFromClip = atoi(tmpStr);
trap_GPG_FindPairValue(attacksub, "mp_damage||damage", "0", tmpStr);
attack->damage = atoi(tmpStr);
trap_GPG_FindPairValue(attacksub, "mp_inaccuracy||inaccuracy", "0", tmpStr);
attack->inaccuracy = (int)(atof(tmpStr)*1000.0f);
trap_GPG_FindPairValue(attacksub, "mp_maxInaccuracy||maxInaccuracy", "0", tmpStr);
attack->maxInaccuracy = (int)(atof(tmpStr)*1000.0f);
trap_GPG_FindPairValue(attacksub, "mp_gore||gore", "YES", tmpStr);
attack->gore = (Q_stricmp ( tmpStr, "YES" )?qfalse:qtrue);
trap_GPG_FindPairValue(attacksub,"mp_extraClips", "0", tmpStr );
attack->extraClips = atoi ( tmpStr );
// max ammo is the combination of all guns that share the ammo
ammoData[attack->ammoIndex].max += attack->clipSize * attack->extraClips;
trap_GPG_FindPairValue(attacksub,"mp_kickAngles||kickAngles", "0 0 0 0 0 0", tmpStr);
sscanf( tmpStr, "%f %f %f %f %f %f",
&attack->minKickAngles[0],
&attack->maxKickAngles[0],
&attack->minKickAngles[1],
&attack->maxKickAngles[1],
&attack->minKickAngles[2],
&attack->maxKickAngles[2] );
if (0 == attack->inaccuracy)
{
trap_GPG_FindPairValue(attacksub, "mp_spread||spread", "0", tmpStr);
attack->inaccuracy = atof(tmpStr);
}
trap_GPG_FindPairValue(attacksub, "mp_pellets||pellets", "1", tmpStr);
attack->pellets = atof(tmpStr);
attack->mod = (meansOfDeath_t)weaponNum;
trap_GPG_FindPairValue(attacksub, "mp_lockFlashToBarrel||lockFlashToBarrel", "true", tmpStr);
if (0 == Q_stricmp(tmpStr, "false"))
{
attack->weaponFlags |= UNLOCK_MUZZLEFLASH;
}
// load effects, sounds
trap_GPG_FindPairValue(attacksub, "muzzleFlash", "", attack->muzzleEffect);
trap_GPG_FindPairValue(attacksub, "3rdPersonMuzzleFlash", "", attack->muzzleEffectInWorld);
trap_GPG_FindPairValue(attacksub, "EjectBone", "", attack->ejectBone);
trap_GPG_FindPairValue(attacksub, "ShellCasingEject", "", attack->shellEject);
trap_GPG_FindPairValue(attacksub, "TracerEffect", "", attack->tracerEffect);
// Some alt attacks have special bones they need their muzzle flashes attached to
trap_GPG_FindPairValue ( attacksub, "mp_muzzleFlashBone", "", attack->muzzleEffectBone );
sub = trap_GPG_FindSubGroup(attacksub, "fireModes");
if (sub)
{
int i;
for ( i = 0; i < 5; i ++ )
{
trap_GPG_FindPairValue ( sub, va("mp_mode%i||mode%i", i, i ), "", tmpStr );
if ( !tmpStr[0] )
{
continue;
}
if (0 == Q_stricmp("single", tmpStr))
attack->weaponFlags |= (1<<WP_FIREMODE_SINGLE);
else if (0 == Q_stricmp("auto", tmpStr))
attack->weaponFlags |= (1<<WP_FIREMODE_AUTO);
else if (0 == Q_stricmp("burst", tmpStr))
attack->weaponFlags |= (1<<WP_FIREMODE_BURST);
else
attack->weaponFlags |= (1<<WP_FIREMODE_SINGLE);
}
}
else
{
attack->weaponFlags |= (1<<WP_FIREMODE_SINGLE);
}
sub = trap_GPG_FindSubGroup(attacksub, "projectile");
if (sub)
{
attack->weaponFlags |= PROJECTILE_FIRE;
trap_GPG_FindPairValue(sub, "gravity", "1", tmpStr);
if (0 < atof(tmpStr))
attack->weaponFlags |= PROJECTILE_GRAVITY;
trap_GPG_FindPairValue(sub, "detonation", "0", tmpStr);
if (0 == Q_stricmp(tmpStr,"timer"))
attack->weaponFlags |= PROJECTILE_TIMED;
trap_GPG_FindPairValue(sub, "mp_bounce||bounce", "0", tmpStr );
attack->bounceScale = atof ( tmpStr );
switch ( weaponNum )
{
case WP_ANM14_GRENADE:
// incediary grenade
attack->weaponFlags |= PROJECTILE_DAMAGE_AREA;
break;
case WP_KNIFE:
if ( attack->weaponFlags & PROJECTILE_GRAVITY )
{
attack->weaponFlags &= ~PROJECTILE_GRAVITY;
attack->weaponFlags |= PROJECTILE_LIGHTGRAVITY;
}
break;
}
trap_GPG_FindPairValue(sub, "mp_speed||speed", "0", tmpStr);
attack->rV.velocity = atoi(tmpStr);
trap_GPG_FindPairValue(sub, "mp_timer||timer", "10", tmpStr);
attack->projectileLifetime = (int)(atof(tmpStr) * 1000);
// 'trail' effect
trap_GPG_FindPairValue(sub, "mp_effect||effect", "", attack->tracerEffect);
trap_GPG_FindPairValue(sub, "model", "", attack->missileG2Model);
trap_GPG_FindPairValue(sub, "mp_explosionEffect||explosionEffect", "", attack->explosionEffect);
trap_GPG_FindPairValue(sub, "mp_explosionSound||explosionSound", "", attack->explosionSound);
}
return qtrue;
}
static qboolean BG_ParseWeaponStats(weapon_t weaponNum, void *group, qboolean pickupsDisabled )
{
char tmpStr[256];
weaponData_t *weapon;
weapon = &weaponData[weaponNum];
memset(weapon, 0, sizeof(weaponData_t));
weapon->classname = bg_weaponNames[weaponNum];
trap_GPG_FindPairValue(group, "category", "0", tmpStr);
weapon->category = atoi(tmpStr);
trap_GPG_FindPairValue(group, "safe", "false", tmpStr);
weapon->safe = !Q_stricmp(tmpStr, "true");
trap_GPG_FindPairValue(group, "model", "", weapon->worldModel);
trap_GPG_FindPairValue(group, "menuImage", "", weapon->menuImage);
// Grab the animations
trap_GPG_FindPairValue( group, "mp_animRaise", "TORSO_RAISE", tmpStr );
weapon->animRaise = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue( group, "mp_animDrop", "TORSO_DROP", tmpStr );
weapon->animDrop = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue( group, "mp_animIdle", "TORSO_IDLE_PISTOL", tmpStr );
weapon->animIdle = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue( group, "mp_animIdleZoomed", "", tmpStr );
weapon->animIdleZoomed = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue( group, "mp_animReload", "", tmpStr );
weapon->animReload = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue( group, "mp_animReloadStart", "", tmpStr );
weapon->animReloadStart = GetIDForString ( bg_animTable, tmpStr );
trap_GPG_FindPairValue( group, "mp_animReloadEnd", "", tmpStr );
weapon->animReloadEnd = GetIDForString ( bg_animTable, tmpStr );
// primary attack
BG_ParseAttackStats ( weaponNum, &weapon->attack[ATTACK_NORMAL], trap_GPG_FindSubGroup(group, "attack"), pickupsDisabled );
// alternate attack
BG_ParseAttackStats ( weaponNum, &weapon->attack[ATTACK_ALTERNATE], trap_GPG_FindSubGroup(group, "altattack"), pickupsDisabled );
return qtrue;
}
qboolean BG_InitWeaponStats( qboolean pickupsDisabled )
{
void *GP2, *topGroup, *topSubs;
char name[256];
int i;
GP2 = trap_GP_ParseFile("ext_data/sof2.wpn", qtrue, qfalse);
if (!GP2)
{
return qfalse;
}
topGroup = trap_GP_GetBaseParseGroup(GP2);
topSubs = trap_GPG_GetSubGroups(topGroup);
while(topSubs)
{
trap_GPG_GetName(topSubs, name);
if (Q_stricmp(name, "weapon") == 0)
{
trap_GPG_FindPairValue(topSubs, "name", "", name);
for(i=0;i<WP_NUM_WEAPONS;i++)
{
if (Q_stricmp(bg_weaponNames[i], name) == 0)
{
BG_ParseWeaponStats(i, topSubs, pickupsDisabled );
break;
}
}
#ifdef _DEBUG
if (i == WP_NUM_WEAPONS)
{
Com_Printf("BG_InitWeaponStats: Unknown weapon: %s\n", name);
}
#endif
}
topSubs = trap_GPG_GetNext(topSubs);
}
trap_GP_Delete(&GP2);
return qtrue;
}
////////////////////////////////////////////////////////////////////////////////////
TWeaponParseInfo weaponParseInfo[WP_NUM_WEAPONS];
char weaponLeftHand[MAX_QPATH];
char weaponRightHand[MAX_QPATH];
static char *BG_BuildSideSurfaceList(void *group, char *pattern, char *sideSurfaces[])
{
void *value;
char *output, *data;
char fieldName[256], fieldValue[256];
int length;
int i;
output = trap_VM_LocalAlloc(0);
length = strlen(pattern);
i=0;
value = trap_GPG_GetPairs(group);
while((value)&&(i<MAX_SIDE_SURFACES))
{
trap_GPV_GetName(value, fieldName);
if (Q_stricmpn(fieldName, pattern, length) == 0)
{
trap_GPV_GetTopValue(value, fieldValue);
data = trap_VM_LocalAllocUnaligned(strlen(fieldValue)+1);
strcpy(data, fieldValue);
sideSurfaces[i]=data;
i++;
}
value = trap_GPV_GetNext(value);
}
data = trap_VM_LocalAllocUnaligned(1);
*data = 0;
return output;
}
static char *BG_BuildList(void *group, char *pattern)
{
void *value;
char *output, *data;
char fieldName[256], fieldValue[256];
int length;
output = trap_VM_LocalAlloc(0);
length = strlen(pattern);
value = trap_GPG_GetPairs(group);
while(value)
{
trap_GPV_GetName(value, fieldName);
if (Q_stricmpn(fieldName, pattern, length) == 0)
{
trap_GPV_GetTopValue(value, fieldValue);
data = trap_VM_LocalAllocUnaligned(strlen(fieldValue)+1);
strcpy(data, fieldValue);
}
value = trap_GPV_GetNext(value);
}
data = trap_VM_LocalAllocUnaligned(1);
*data = 0;
return output;
}
#define MAX_WEAPON_FILES 10
static void *weaponFrames[MAX_WEAPON_FILES];
static void *frameGroup[MAX_WEAPON_FILES];
static int numWeaponFiles = 0;
static int numInitialFiles = 0;
static qboolean BG_OpenWeaponFrames(const char *name)
{
weaponFrames[numWeaponFiles] = trap_GP_ParseFile((char *)name, qtrue, qfalse);
if (!weaponFrames)
{
return qfalse;
}
frameGroup[numWeaponFiles] = trap_GP_GetBaseParseGroup(weaponFrames[numWeaponFiles]);
numWeaponFiles++;
return qtrue;
}
static TNoteTrack *BG_FindNoteTracks(void *group)
{
void *sub;
char name[256];
TNoteTrack *head, *last, *current, *insert;
head = last = insert = 0;
sub = trap_GPG_GetSubGroups(group);
while(sub)
{
trap_GPG_GetName(sub, name);
if (Q_stricmp(name, "notetrack") == 0)
{
current = (TNoteTrack *)trap_VM_LocalAlloc(sizeof(*current));
memset(current, 0, sizeof(*current));
// last character is automatically 0 cuz of the memset
trap_GPG_FindPairValue(sub, "note", "", current->mNote);
trap_GPG_FindPairValue(sub, "frame", "-1", name);
current->mFrame = atoi(name);
last=insert=head;
while(insert)
{
if(current->mFrame<insert->mFrame)
{
break;
}
last=insert;
insert=insert->mNext;
}
if(insert==head)
{
head=current;
}
else
{
last->mNext=current;
}
current->mNext=insert;
}
sub = trap_GPG_GetNext(sub);
}
return head;
}
static void BG_FindWeaponFrames(TAnimInfoWeapon *animInfo, int choice)
{
void *group;
int i;
if (!numWeaponFiles || !animInfo->mAnim[choice])
{
animInfo->mNumFrames[choice] = -1;
return;
}
for(i=0;i<numWeaponFiles;i++)
{
char temp[256];
group = trap_GPG_GetSubGroups ( frameGroup[i] );
while ( group )
{
char* name;
// Get the name and break it down to just the filename without
// and extension
trap_GPG_GetName ( group, temp );
name = COM_SkipPath ( temp );
COM_StripExtension ( name, temp );
if ( Q_stricmp ( temp, animInfo->mAnim[choice] ) == 0 )
{
break;
}
group = trap_GPG_GetNext ( group );
}
if (group)
{
trap_GPG_FindPairValue(group, "startframe", "0", temp);
animInfo->mStartFrame[choice] = atoi(temp);
trap_GPG_FindPairValue(group, "duration", "0", temp);
animInfo->mNumFrames[choice] = atoi(temp);
trap_GPG_FindPairValue(group, "fps", "0", temp);
animInfo->mFPS[choice] = atoi(temp);
animInfo->mNoteTracks[choice] = BG_FindNoteTracks(group);
return;
}
}
animInfo->mNumFrames[choice] = -1;
}
static void BG_CloseWeaponFrames(int upTo)
{
int i;
for(i=upTo; i < numWeaponFiles; i++)
{
if (weaponFrames[i])
{
trap_GP_Delete(&weaponFrames[i]);
}
}
numWeaponFiles = upTo;
}
static qboolean BG_ParseAnimGroup(weapon_t weapon, void *animGroup)
{
void *sub;
char name[256];
TAnimWeapon *anim;
TAnimInfoWeapon *info;
char value[256];
int i;
char temp[256];
anim = (TAnimWeapon *)trap_VM_LocalAlloc(sizeof(*anim));
memset(anim, 0, sizeof(*anim));
anim->mNext = weaponParseInfo[weapon].mAnimList;
weaponParseInfo[weapon].mAnimList = anim;
trap_GPG_FindPairValue(animGroup, "name", "", anim->mName);
trap_GPG_FindPairValue(animGroup, "mp_muzzle||muzzle", "", anim->mMuzzle);
sub = trap_GPG_GetSubGroups(animGroup);
while(sub)
{
trap_GPG_GetName(sub, name);
if (Q_stricmp(name, "info") == 0)
{
info = (TAnimInfoWeapon *)trap_VM_LocalAlloc(sizeof(*info));
memset(info, 0, sizeof(*info));
info->mNext = anim->mInfos;
anim->mInfos = info;
info->mNumChoices = 0;
trap_GPG_FindPairValue(sub, "name", "", info->mName);
trap_GPG_FindPairValue(sub, "type", "", info->mType);
// Cache for later
if ( !Q_stricmp ( info->mType, "weaponmodel" ) )
{
anim->mWeaponModelInfo = info;
}
// We first look for a multiplayer specific speed. If we don't
// find a valid speed, use the single player speed instead.
trap_GPG_FindPairValue(sub, "mp_speed||speed", "0", temp);
info->mSpeed = atof(temp);
if(!info->mSpeed)
{
trap_GPG_FindPairValue(sub, "mp_speed||speed", "1", temp);
info->mSpeed = atof(temp);
}
trap_GPG_FindPairValue(sub, "lodbias", "0", temp);
info->mLODBias = atoi(temp);
for(i=0;i<=MAX_WEAPON_ANIM_CHOICES;i++)
{
if (i == 0)
{
strcpy(temp, "anim||animNoLerp");
}
else
{
Com_sprintf(temp, sizeof(temp), "anim%d||animNoLerp%d", i, i);
}
trap_GPG_FindPairValue(sub, temp, "", value);
if (value[0] && info->mNumChoices < MAX_WEAPON_ANIM_CHOICES)
{
info->mAnim[info->mNumChoices] = (char *)trap_VM_LocalAlloc(strlen(value)+1);
strcpy(info->mAnim[info->mNumChoices], value);
if (i == 0)
{
strcpy(temp, "transition");
}
else
{
Com_sprintf(temp, sizeof(temp), "transition%d", i);
}
trap_GPG_FindPairValue(sub, temp, "", value);
if (value[0])
{
info->mTransition[info->mNumChoices] = (char *)trap_VM_LocalAlloc(strlen(value)+1);
strcpy(info->mTransition[info->mNumChoices], value);
}
if (i == 0)
{
strcpy(temp, "end");
}
else
{
Com_sprintf(temp, sizeof(temp), "end%d", i);
}
trap_GPG_FindPairValue(sub, temp, "", value);
if (value[0])
{
info->mEnd[info->mNumChoices] = (char *)trap_VM_LocalAlloc(strlen(value)+1);
strcpy(info->mEnd[info->mNumChoices], value);
}
info->mNumChoices++;
}
}
}
sub = trap_GPG_GetNext(sub);
}
return qtrue;
}
static TBoltonWeapon *BG_ParseBolton(void *boltonGroup)
{
TBoltonWeapon *bolton;
void *sub;
char temp[256];
bolton = (TBoltonWeapon *)trap_VM_LocalAlloc(sizeof(*bolton));
memset(bolton, 0, sizeof(*bolton));
trap_GPG_FindPairValue(boltonGroup, "name", "", bolton->mName);
trap_GPG_FindPairValue(boltonGroup, "model", "", bolton->mModel);
trap_GPG_FindPairValue(boltonGroup, "parent", "", bolton->mParent);
trap_GPG_FindPairValue(boltonGroup, "bolttobone", "", bolton->mBoltToBone);
trap_GPG_FindPairValue(boltonGroup, "frames", "", temp);
BG_OpenWeaponFrames(temp);
sub = trap_GPG_FindSubGroup(boltonGroup, "rightside");
if (sub)
{
BG_BuildSideSurfaceList(sub, "surface", bolton->mRightSide);
}
sub = trap_GPG_FindSubGroup(boltonGroup, "joint");
if (sub)
{
trap_GPG_FindPairValue(sub, "bone", "", bolton->mJointBone);
trap_GPG_FindPairValue(sub, "parentBone", "", bolton->mJointParentBone);
trap_GPG_FindPairValue(sub, "fwd", "", bolton->mJointForward);
trap_GPG_FindPairValue(sub, "right", "", bolton->mJointRight);
trap_GPG_FindPairValue(sub, "up", "", bolton->mJointUp);
}
return bolton;
}
static qboolean BG_ParseWeaponGroup(TWeaponModel *weapon, void *weaponGroup)
{
void *sub, *hand;
char name[256];
TOptionalWeapon *option;
char temp[256];
trap_GPG_FindPairValue(weaponGroup, "name", "", weapon->mName);
trap_GPG_FindPairValue(weaponGroup, "model", "", weapon->mModel);
trap_GPG_FindPairValue(weaponGroup, "frames", "", temp);
BG_OpenWeaponFrames(temp);
sub = trap_GPG_GetSubGroups(weaponGroup);
while(sub)
{
trap_GPG_GetName(sub, name);
if (Q_stricmp(name, "buffer") == 0)
{
trap_GPG_FindPairValue(sub, "name", "", weapon->mBufferName);
trap_GPG_FindPairValue(sub, "model", "", weapon->mBufferModel);
trap_GPG_FindPairValue(sub, "bolttobone", "", weapon->mBufferBoltToBone);
trap_GPG_FindPairValue(sub, "mp_muzzle||muzzle", "", weapon->mBufferMuzzle);
trap_GPG_FindPairValue(sub, "mp_altmuzzle", "", weapon->mBufferAltMuzzle);
}
else if (Q_stricmp(name, "hands") == 0)
{
hand = trap_GPG_FindSubGroup(sub, "left");
if (hand)
{
trap_GPG_FindPairValue(hand, "bolttobone", "", weapon->mLeftHandsBoltToBone);
}
hand = trap_GPG_FindSubGroup(sub, "right");
if (hand)
{
trap_GPG_FindPairValue(hand, "bolttobone", "", weapon->mRightHandsBoltToBone);
}
}
else if (Q_stricmp(name, "bolton") == 0)
{
weapon->mBolton = BG_ParseBolton(sub);
}
else if (Q_stricmp(name, "rightside") == 0)
{
BG_BuildSideSurfaceList(sub, "surface", weapon->mRightSideSurfaces);
}
else if (Q_stricmp(name, "leftside") == 0)
{
BG_BuildSideSurfaceList(sub, "surface", weapon->mLeftSideSurfaces);
}
else if (Q_stricmp(name, "front") == 0)
{
BG_BuildSideSurfaceList(sub, "surface", weapon->mFrontSurfaces);
}
else if (Q_stricmp(name, "optionalpart") == 0)
{
option = (TOptionalWeapon *)trap_VM_LocalAlloc(sizeof(*option));
memset(option, 0, sizeof(*option));
trap_GPG_FindPairValue(sub, "name", "", option->mName);
trap_GPG_FindPairValue(sub, "muzzle", "", option->mMuzzle);
BG_BuildSideSurfaceList(sub, "surface", option->mSurfaces);
option->mNext=weapon->mOptionalList;
weapon->mOptionalList=option;
/*
if(weapon->mOptionalList)
{
option->mNext=weapon->mOptionalList;
weapon->mOptionalList=option;
}
else
{
weapon->mOptionalList=option;
} */
}
sub = trap_GPG_GetNext(sub);
}
return qtrue;
}
static qboolean BG_ParseWeapon(weapon_t weapon, void *group)
{
void *sub, *soundName, *surfaceCallbackName;
void *soundValue, *surfaceCallbackValue;
char onOffVal[256];
char name[256];
int i, j;
TAnimWeapon *anims;
TAnimInfoWeapon *infos;
char temp[256];
memset(&weaponParseInfo[weapon], 0, sizeof(TWeaponParseInfo));
weaponParseInfo[weapon].mName = bg_weaponNames[weapon];
trap_GPG_FindPairValue(group, "foreshorten", "0.0", temp);
weaponParseInfo[weapon].mForeshorten = atof(temp);
sub = trap_GPG_GetSubGroups(group);
while(sub)
{
trap_GPG_GetName(sub, name);
if (Q_stricmp(name, "viewoffset") == 0)
{
trap_GPG_FindPairValue(sub, "forward", "0.0", temp);
weaponParseInfo[weapon].mViewOffset[0] = atof(temp);
trap_GPG_FindPairValue(sub, "right", "0.0", temp);
weaponParseInfo[weapon].mViewOffset[1] = atof(temp);
trap_GPG_FindPairValue(sub, "up", "0.0", temp);
weaponParseInfo[weapon].mViewOffset[2] = atof(temp);
}
else if (Q_stricmp(name, "sounds") == 0)
{
soundName = trap_GPG_GetSubGroups(sub);
for(i=0;soundName && (i < MAX_WEAPON_SOUNDS);i++)
{
trap_GPG_GetName(soundName, weaponParseInfo[weapon].mSoundNames[i]);
soundValue = trap_GPG_GetPairs(soundName);
for(j=0;soundValue && (j<MAX_WEAPON_SOUND_SLOTS);j++)
{
trap_GPV_GetTopValue(soundValue, weaponParseInfo[weapon].mSounds[i][j]);
soundValue = trap_GPV_GetNext(soundValue);
}
soundName = trap_GPG_GetNext(soundName);
}
}
else if (Q_stricmp(name, "surfaces") == 0)
{
surfaceCallbackName = trap_GPG_GetSubGroups(sub);
for(i=0;surfaceCallbackName && (i < MAX_SURFACE_CALLBACKS);i++)
{
trap_GPG_GetName(surfaceCallbackName, weaponParseInfo[weapon].mSurfaceCallbacks[i].mName);
surfaceCallbackValue = trap_GPG_GetPairs(surfaceCallbackName);
for(j=0;surfaceCallbackValue && (j<MAX_CALLBACK_SURFACES);j++)
{
trap_GPG_GetName(surfaceCallbackValue, weaponParseInfo[weapon].mSurfaceCallbacks[i].mOnOffSurfaces[j].mName);
trap_GPV_GetTopValue(surfaceCallbackValue, onOffVal);
assert(onOffVal);
weaponParseInfo[weapon].mSurfaceCallbacks[i].mOnOffSurfaces[j].mStatus=(!Q_stricmp(onOffVal,"on"))?1:0;
surfaceCallbackValue=trap_GPV_GetNext(surfaceCallbackValue);
}
surfaceCallbackName=trap_GPG_GetNext(surfaceCallbackName);
}
}
else if (Q_stricmp(name, "weaponmodel") == 0)
{
BG_ParseWeaponGroup(&weaponParseInfo[weapon].mWeaponModel, sub);
}
else if (Q_stricmp(name, "anim") == 0)
{
BG_ParseAnimGroup(weapon, sub);
}
sub = trap_GPG_GetNext(sub);
}
anims = weaponParseInfo[weapon].mAnimList;
while(anims)
{
infos = anims->mInfos;
while(infos)
{
for(i=0;i<infos->mNumChoices;i++)
{
BG_FindWeaponFrames(infos, i);
}
infos = infos->mNext;
}
anims = anims->mNext;
}
BG_CloseWeaponFrames(numInitialFiles);
return qtrue;
}
qboolean BG_ParseInviewFile( qboolean pickupsDisabled )
{
void *GP2, *topGroup, *topSubs, *group;
char name[256], temp[256];
int i;
GP2 = trap_GP_ParseFile("inview/sof2.inview", qtrue, qfalse);
if (!GP2)
{
return qfalse;
}
weaponLeftHand[0] = 0;
weaponRightHand[0] = 0;
topGroup = trap_GP_GetBaseParseGroup(GP2);
topSubs = trap_GPG_GetSubGroups(topGroup);
while(topSubs)
{
trap_GPG_GetName(topSubs, name);
if (Q_stricmp(name, "hands") == 0)
{
group = trap_GPG_FindSubGroup(topSubs, "left");
if (group)
{
trap_GPG_FindPairValue(group, "model", "", weaponLeftHand);
trap_GPG_FindPairValue(group, "frames", "", temp);
if (BG_OpenWeaponFrames(temp))
{
numInitialFiles++;
}
}
group = trap_GPG_FindSubGroup(topSubs, "right");
if (group)
{
trap_GPG_FindPairValue(group, "model", "", weaponRightHand);
trap_GPG_FindPairValue(group, "frames", "", temp);
if (BG_OpenWeaponFrames(temp))
{
numInitialFiles++;
}
}
}
else if (Q_stricmp(name, "hud") == 0)
{
}
else if (Q_stricmp(name, "weapon") == 0)
{
trap_GPG_FindPairValue(topSubs, "name", "", name);
for(i=0;i<WP_NUM_WEAPONS;i++)
{
if (Q_stricmp(bg_weaponNames[i], name) == 0)
{
BG_ParseWeapon(i, topSubs);
break;
}
}
#ifdef _DEBUG
if (i == WP_NUM_WEAPONS)
{
Com_Printf("BG_InitWeapons: Unknown weapon: %s\n", name);
}
#endif
}
topSubs = trap_GPG_GetNext(topSubs);
}
BG_CloseWeaponFrames(0);
trap_GP_Delete(&GP2);
BG_InitAmmoStats();
return BG_InitWeaponStats( pickupsDisabled );
}
TAnimWeapon *BG_GetInviewAnim(int weaponIdx,const char *animKey,int *animIndex)
{
TAnimWeapon *animWeapon;
(*animIndex)=0;
animWeapon=weaponParseInfo[weaponIdx].mAnimList;
while((animWeapon!=0)&&(Q_stricmp(animWeapon->mName,animKey)))
{
animWeapon=animWeapon->mNext;
(*animIndex)++;
}
if(!animWeapon)
{
return(0);
}
return(animWeapon);
}
TAnimWeapon *BG_GetInviewAnimFromIndex(int weaponIdx,int animIndex)
{
TAnimWeapon *animWeapon;
int i=0;
animWeapon=weaponParseInfo[weaponIdx].mAnimList;
while((animWeapon!=0)&&(i!=animIndex))
{
animWeapon=animWeapon->mNext;
i++;
}
if(!animWeapon)
{
return(0);
}
return(animWeapon);
}
TAnimInfoWeapon *BG_GetInviewModelAnim(int weaponIdx,const char *modelKey,const char *animKey)
{
TAnimWeapon *animWeapon;
TAnimInfoWeapon *animInfoWeapon;
animWeapon=weaponParseInfo[weaponIdx].mAnimList;
while((animWeapon!=0)&&(Q_stricmp(animWeapon->mName,animKey)))
{
animWeapon=animWeapon->mNext;
}
if(!animWeapon)
{
return(0);
}
animInfoWeapon=animWeapon->mInfos;
while((animInfoWeapon!=0)&&(Q_stricmp(animInfoWeapon->mType,modelKey)))
{
animInfoWeapon=animInfoWeapon->mNext;
}
if(!animInfoWeapon)
{
return(0);
}
return(animInfoWeapon);
}
/*
===============
BG_WeaponHasAlternateAmmo
Returns qtrue if the given weapon has ammo for its alternate attack
===============
*/
qboolean BG_WeaponHasAlternateAmmo ( int weapon )
{
// No valid ammo index means no alternate ammo
if ( weaponData[weapon].attack[ATTACK_ALTERNATE].ammoIndex == AMMO_NONE )
{
return qfalse;
}
// If the alternate attack doesnt deplete ammo then it doesnt use it
if ( !weaponData[weapon].attack[ATTACK_ALTERNATE].fireAmount )
{
return qfalse;
}
// If the alternate ammo is the same as the primary ammo then
// the primary is good enough
if ( weaponData[weapon].attack[ATTACK_ALTERNATE].ammoIndex ==
weaponData[weapon].attack[ATTACK_NORMAL].ammoIndex )
{
return qfalse;
}
// Yup, alternates have ammo
return qtrue;
}
/*
===============
BG_FindFireMode
Finds the firemode for the given weapon using the given default
===============
*/
int BG_FindFireMode ( weapon_t weapon, attackType_t attack, int firemode )
{
int i;
if ( !weapon )
{
return WP_FIREMODE_NONE;
}
for ( i=0; i <= WP_FIREMODE_SINGLE; i++ )
{
if( firemode >= WP_FIREMODE_MAX )
{
firemode = WP_FIREMODE_NONE + 1;
}
if( weaponData[weapon].attack[ATTACK_NORMAL].weaponFlags&(1<<firemode))
{
break;
}
else
{
firemode++;
}
}
assert ( firemode < WP_FIREMODE_MAX );
return firemode;
}
/*
===============
BG_CalculateBulletEndpoint
Calculates the end point of a bullet based on the given inaccuracy and range
===============
*/
void BG_CalculateBulletEndpoint ( vec3_t muzzlePoint, vec3_t fireAngs, float inaccuracy, float range, vec3_t end, int *seed )
{
float fGaussianX = 0;
float fGaussianY = 0;
vec3_t dir;
vec3_t fwd;
vec3_t up;
vec3_t right;
AngleVectors ( fireAngs, fwd, right, up );
// No inaccuracy so just extend it forward by the range
if ( inaccuracy <= 0.0f )
{
VectorMA (muzzlePoint, range, fwd, end);
return;
}
// Gaussian spread should keep it a bit less random looking
while ( 1 )
{
float fGaussian;
float f1;
float f2;
f1 = (float)(Q_rand ( seed ) % 15000) / 15000.0f;
f2 = (float)(Q_rand ( seed ) % 15000) / 15000.0f;
fGaussianX = (f1-0.5f) + (f2-0.5f);
f1 = (float)(Q_rand ( seed ) % 15000) / 15000.0f;
f2 = (float)(Q_rand ( seed ) % 15000) / 15000.0f;
fGaussianY = (f1-0.5f) + (f2-0.5f);
fGaussian = fGaussianX * fGaussianX + fGaussianY * fGaussianY;
if ( fGaussian < 1 )
{
break;
}
}
VectorMA ( fwd, 0.05f * inaccuracy * fGaussianX, right, dir );
VectorMA ( dir, 0.05f * inaccuracy * fGaussianY, up, dir );
VectorMA (muzzlePoint, range, dir, end);
}
/*
===============
BG_GetMaxAmmo
Returns the max ammo a client can hold for the given ammo index
===============
*/
int BG_GetMaxAmmo ( const playerState_t* ps, int ammoIndex )
{
int ammo;
weapon_t weapon;
if ( ammoIndex == AMMO_NONE )
{
return 0;
}
for ( ammo = 0, weapon = WP_KNIFE; weapon < WP_NUM_WEAPONS; weapon ++ )
{
if ( !(ps->stats[STAT_WEAPONS] & (1<<weapon)) )
{
if ( weapon != ps->stats[STAT_OUTFIT_GRENADE] )
{
continue;
}
}
if ( weaponData[weapon].attack[ATTACK_NORMAL].ammoIndex == ammoIndex )
{
ammo += (weaponData[weapon].attack[ATTACK_NORMAL].extraClips + 1) * weaponData[weapon].attack[ATTACK_NORMAL].clipSize;
ammo -= ps->clip[ATTACK_NORMAL][weapon];
}
if ( BG_WeaponHasAlternateAmmo ( weapon ) )
{
if ( weaponData[weapon].attack[ATTACK_ALTERNATE].ammoIndex == ammoIndex )
{
ammo += (weaponData[weapon].attack[ATTACK_ALTERNATE].extraClips + 1) * weaponData[weapon].attack[ATTACK_ALTERNATE].clipSize;
ammo -= ps->clip[ATTACK_ALTERNATE][weapon];
}
}
}
return ammo;
}