Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

464 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Weapon data file parsing, shared by game & client dlls.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include <KeyValues.h>
  9. #include <tier0/mem.h>
  10. #include "filesystem.h"
  11. #include "utldict.h"
  12. #include "ammodef.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. // The sound categories found in the weapon classname.txt files
  16. // This needs to match the WeaponSound_t enum in weapon_parse.h
  17. #if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
  18. const char *pWeaponSoundCategories[ NUM_SHOOT_SOUND_TYPES ] =
  19. {
  20. "empty",
  21. "single_shot",
  22. "single_shot_npc",
  23. "double_shot",
  24. "double_shot_npc",
  25. "burst",
  26. "reload",
  27. "reload_npc",
  28. "melee_miss",
  29. "melee_hit",
  30. "melee_hit_world",
  31. "special1",
  32. "special2",
  33. "special3",
  34. "taunt",
  35. "deploy"
  36. };
  37. #else
  38. extern const char *pWeaponSoundCategories[ NUM_SHOOT_SOUND_TYPES ];
  39. #endif
  40. int GetWeaponSoundFromString( const char *pszString )
  41. {
  42. for ( int i = EMPTY; i < NUM_SHOOT_SOUND_TYPES; i++ )
  43. {
  44. if ( !Q_stricmp(pszString,pWeaponSoundCategories[i]) )
  45. return (WeaponSound_t)i;
  46. }
  47. return -1;
  48. }
  49. // Item flags that we parse out of the file.
  50. typedef struct
  51. {
  52. const char *m_pFlagName;
  53. int m_iFlagValue;
  54. } itemFlags_t;
  55. #if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
  56. itemFlags_t g_ItemFlags[8] =
  57. {
  58. { "ITEM_FLAG_SELECTONEMPTY", ITEM_FLAG_SELECTONEMPTY },
  59. { "ITEM_FLAG_NOAUTORELOAD", ITEM_FLAG_NOAUTORELOAD },
  60. { "ITEM_FLAG_NOAUTOSWITCHEMPTY", ITEM_FLAG_NOAUTOSWITCHEMPTY },
  61. { "ITEM_FLAG_LIMITINWORLD", ITEM_FLAG_LIMITINWORLD },
  62. { "ITEM_FLAG_EXHAUSTIBLE", ITEM_FLAG_EXHAUSTIBLE },
  63. { "ITEM_FLAG_DOHITLOCATIONDMG", ITEM_FLAG_DOHITLOCATIONDMG },
  64. { "ITEM_FLAG_NOAMMOPICKUPS", ITEM_FLAG_NOAMMOPICKUPS },
  65. { "ITEM_FLAG_NOITEMPICKUP", ITEM_FLAG_NOITEMPICKUP }
  66. };
  67. #else
  68. extern itemFlags_t g_ItemFlags[8];
  69. #endif
  70. static CUtlDict< FileWeaponInfo_t*, unsigned short > m_WeaponInfoDatabase;
  71. #ifdef _DEBUG
  72. // used to track whether or not two weapons have been mistakenly assigned the wrong slot
  73. bool g_bUsedWeaponSlots[MAX_WEAPON_SLOTS][MAX_WEAPON_POSITIONS] = { { false } };
  74. #endif
  75. //-----------------------------------------------------------------------------
  76. // Purpose:
  77. // Input : *name -
  78. // Output : FileWeaponInfo_t
  79. //-----------------------------------------------------------------------------
  80. static WEAPON_FILE_INFO_HANDLE FindWeaponInfoSlot( const char *name )
  81. {
  82. // Complain about duplicately defined metaclass names...
  83. unsigned short lookup = m_WeaponInfoDatabase.Find( name );
  84. if ( lookup != m_WeaponInfoDatabase.InvalidIndex() )
  85. {
  86. return lookup;
  87. }
  88. FileWeaponInfo_t *insert = CreateWeaponInfo();
  89. lookup = m_WeaponInfoDatabase.Insert( name, insert );
  90. Assert( lookup != m_WeaponInfoDatabase.InvalidIndex() );
  91. return lookup;
  92. }
  93. // Find a weapon slot, assuming the weapon's data has already been loaded.
  94. WEAPON_FILE_INFO_HANDLE LookupWeaponInfoSlot( const char *name )
  95. {
  96. return m_WeaponInfoDatabase.Find( name );
  97. }
  98. // FIXME, handle differently?
  99. static FileWeaponInfo_t gNullWeaponInfo;
  100. //-----------------------------------------------------------------------------
  101. // Purpose:
  102. // Input : handle -
  103. // Output : FileWeaponInfo_t
  104. //-----------------------------------------------------------------------------
  105. FileWeaponInfo_t *GetFileWeaponInfoFromHandle( WEAPON_FILE_INFO_HANDLE handle )
  106. {
  107. if ( handle < 0 || handle >= m_WeaponInfoDatabase.Count() )
  108. {
  109. return &gNullWeaponInfo;
  110. }
  111. if ( handle == m_WeaponInfoDatabase.InvalidIndex() )
  112. {
  113. return &gNullWeaponInfo;
  114. }
  115. return m_WeaponInfoDatabase[ handle ];
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. // Output : WEAPON_FILE_INFO_HANDLE
  120. //-----------------------------------------------------------------------------
  121. WEAPON_FILE_INFO_HANDLE GetInvalidWeaponInfoHandle( void )
  122. {
  123. return (WEAPON_FILE_INFO_HANDLE)m_WeaponInfoDatabase.InvalidIndex();
  124. }
  125. #if 0
  126. void ResetFileWeaponInfoDatabase( void )
  127. {
  128. int c = m_WeaponInfoDatabase.Count();
  129. for ( int i = 0; i < c; ++i )
  130. {
  131. delete m_WeaponInfoDatabase[ i ];
  132. }
  133. m_WeaponInfoDatabase.RemoveAll();
  134. #ifdef _DEBUG
  135. memset(g_bUsedWeaponSlots, 0, sizeof(g_bUsedWeaponSlots));
  136. #endif
  137. }
  138. #endif
  139. void PrecacheFileWeaponInfoDatabase( IFileSystem *pFilesystem, const unsigned char *pICEKey )
  140. {
  141. if ( m_WeaponInfoDatabase.Count() )
  142. return;
  143. KeyValues *manifest = new KeyValues( "weaponscripts" );
  144. if ( manifest->LoadFromFile( pFilesystem, "scripts/weapon_manifest.txt", "GAME" ) )
  145. {
  146. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
  147. {
  148. if ( !Q_stricmp( sub->GetName(), "file" ) )
  149. {
  150. char fileBase[512];
  151. Q_FileBase( sub->GetString(), fileBase, sizeof(fileBase) );
  152. WEAPON_FILE_INFO_HANDLE tmp;
  153. #ifdef CLIENT_DLL
  154. if ( ReadWeaponDataFromFileForSlot( pFilesystem, fileBase, &tmp, pICEKey ) )
  155. {
  156. gWR.LoadWeaponSprites( tmp );
  157. }
  158. #else
  159. ReadWeaponDataFromFileForSlot( pFilesystem, fileBase, &tmp, pICEKey );
  160. #endif
  161. }
  162. else
  163. {
  164. Error( "Expecting 'file', got %s\n", sub->GetName() );
  165. }
  166. }
  167. }
  168. manifest->deleteThis();
  169. }
  170. KeyValues* ReadEncryptedKVFile( IFileSystem *pFilesystem, const char *szFilenameWithoutExtension, const unsigned char *pICEKey, bool bForceReadEncryptedFile /*= false*/ )
  171. {
  172. Assert( strchr( szFilenameWithoutExtension, '.' ) == NULL );
  173. char szFullName[512];
  174. const char *pSearchPath = "MOD";
  175. if ( pICEKey == NULL )
  176. {
  177. pSearchPath = "GAME";
  178. }
  179. // Open the weapon data file, and abort if we can't
  180. KeyValues *pKV = new KeyValues( "WeaponDatafile" );
  181. Q_snprintf(szFullName,sizeof(szFullName), "%s.txt", szFilenameWithoutExtension);
  182. if ( bForceReadEncryptedFile || !pKV->LoadFromFile( pFilesystem, szFullName, pSearchPath ) ) // try to load the normal .txt file first
  183. {
  184. #ifndef _XBOX
  185. if ( pICEKey )
  186. {
  187. Q_snprintf(szFullName,sizeof(szFullName), "%s.ctx", szFilenameWithoutExtension); // fall back to the .ctx file
  188. FileHandle_t f = pFilesystem->Open( szFullName, "rb", pSearchPath );
  189. if (!f)
  190. {
  191. pKV->deleteThis();
  192. return NULL;
  193. }
  194. // load file into a null-terminated buffer
  195. int fileSize = pFilesystem->Size(f);
  196. char *buffer = (char*)MemAllocScratch(fileSize + 1);
  197. Assert(buffer);
  198. pFilesystem->Read(buffer, fileSize, f); // read into local buffer
  199. buffer[fileSize] = 0; // null terminate file as EOF
  200. pFilesystem->Close( f ); // close file after reading
  201. UTIL_DecodeICE( (unsigned char*)buffer, fileSize, pICEKey );
  202. bool retOK = pKV->LoadFromBuffer( szFullName, buffer, pFilesystem );
  203. MemFreeScratch();
  204. if ( !retOK )
  205. {
  206. pKV->deleteThis();
  207. return NULL;
  208. }
  209. }
  210. else
  211. {
  212. pKV->deleteThis();
  213. return NULL;
  214. }
  215. #else
  216. pKV->deleteThis();
  217. return NULL;
  218. #endif
  219. }
  220. return pKV;
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose: Read data on weapon from script file
  224. // Output: true - if data2 successfully read
  225. // false - if data load fails
  226. //-----------------------------------------------------------------------------
  227. bool ReadWeaponDataFromFileForSlot( IFileSystem* pFilesystem, const char *szWeaponName, WEAPON_FILE_INFO_HANDLE *phandle, const unsigned char *pICEKey )
  228. {
  229. if ( !phandle )
  230. {
  231. Assert( 0 );
  232. return false;
  233. }
  234. *phandle = FindWeaponInfoSlot( szWeaponName );
  235. FileWeaponInfo_t *pFileInfo = GetFileWeaponInfoFromHandle( *phandle );
  236. Assert( pFileInfo );
  237. if ( pFileInfo->bParsedScript )
  238. return true;
  239. char sz[128];
  240. Q_snprintf( sz, sizeof( sz ), "scripts/%s", szWeaponName );
  241. KeyValues *pKV = ReadEncryptedKVFile( pFilesystem, sz, pICEKey,
  242. #if defined( DOD_DLL )
  243. true // Only read .ctx files!
  244. #else
  245. false
  246. #endif
  247. );
  248. if ( !pKV )
  249. return false;
  250. pFileInfo->Parse( pKV, szWeaponName );
  251. pKV->deleteThis();
  252. return true;
  253. }
  254. //-----------------------------------------------------------------------------
  255. // FileWeaponInfo_t implementation.
  256. //-----------------------------------------------------------------------------
  257. FileWeaponInfo_t::FileWeaponInfo_t()
  258. {
  259. bParsedScript = false;
  260. bLoadedHudElements = false;
  261. szClassName[0] = 0;
  262. szPrintName[0] = 0;
  263. szViewModel[0] = 0;
  264. szWorldModel[0] = 0;
  265. szAnimationPrefix[0] = 0;
  266. iSlot = 0;
  267. iPosition = 0;
  268. iMaxClip1 = 0;
  269. iMaxClip2 = 0;
  270. iDefaultClip1 = 0;
  271. iDefaultClip2 = 0;
  272. iWeight = 0;
  273. iRumbleEffect = -1;
  274. bAutoSwitchTo = false;
  275. bAutoSwitchFrom = false;
  276. iFlags = 0;
  277. szAmmo1[0] = 0;
  278. szAmmo2[0] = 0;
  279. memset( aShootSounds, 0, sizeof( aShootSounds ) );
  280. iAmmoType = 0;
  281. iAmmo2Type = 0;
  282. m_bMeleeWeapon = false;
  283. iSpriteCount = 0;
  284. iconActive = 0;
  285. iconInactive = 0;
  286. iconAmmo = 0;
  287. iconAmmo2 = 0;
  288. iconCrosshair = 0;
  289. iconAutoaim = 0;
  290. iconZoomedCrosshair = 0;
  291. iconZoomedAutoaim = 0;
  292. bShowUsageHint = false;
  293. m_bAllowFlipping = true;
  294. m_bBuiltRightHanded = true;
  295. }
  296. #ifdef CLIENT_DLL
  297. extern ConVar hud_fastswitch;
  298. #endif
  299. void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
  300. {
  301. // Okay, we tried at least once to look this up...
  302. bParsedScript = true;
  303. // Classname
  304. Q_strncpy( szClassName, szWeaponName, MAX_WEAPON_STRING );
  305. // Printable name
  306. Q_strncpy( szPrintName, pKeyValuesData->GetString( "printname", WEAPON_PRINTNAME_MISSING ), MAX_WEAPON_STRING );
  307. // View model & world model
  308. Q_strncpy( szViewModel, pKeyValuesData->GetString( "viewmodel" ), MAX_WEAPON_STRING );
  309. Q_strncpy( szWorldModel, pKeyValuesData->GetString( "playermodel" ), MAX_WEAPON_STRING );
  310. Q_strncpy( szAnimationPrefix, pKeyValuesData->GetString( "anim_prefix" ), MAX_WEAPON_PREFIX );
  311. iSlot = pKeyValuesData->GetInt( "bucket", 0 );
  312. iPosition = pKeyValuesData->GetInt( "bucket_position", 0 );
  313. // Use the console (X360) buckets if hud_fastswitch is set to 2.
  314. #ifdef CLIENT_DLL
  315. if ( hud_fastswitch.GetInt() == 2 )
  316. #else
  317. if ( IsX360() )
  318. #endif
  319. {
  320. iSlot = pKeyValuesData->GetInt( "bucket_360", iSlot );
  321. iPosition = pKeyValuesData->GetInt( "bucket_position_360", iPosition );
  322. }
  323. iMaxClip1 = pKeyValuesData->GetInt( "clip_size", WEAPON_NOCLIP ); // Max primary clips gun can hold (assume they don't use clips by default)
  324. iMaxClip2 = pKeyValuesData->GetInt( "clip2_size", WEAPON_NOCLIP ); // Max secondary clips gun can hold (assume they don't use clips by default)
  325. iDefaultClip1 = pKeyValuesData->GetInt( "default_clip", iMaxClip1 ); // amount of primary ammo placed in the primary clip when it's picked up
  326. iDefaultClip2 = pKeyValuesData->GetInt( "default_clip2", iMaxClip2 ); // amount of secondary ammo placed in the secondary clip when it's picked up
  327. iWeight = pKeyValuesData->GetInt( "weight", 0 );
  328. iRumbleEffect = pKeyValuesData->GetInt( "rumble", -1 );
  329. // LAME old way to specify item flags.
  330. // Weapon scripts should use the flag names.
  331. iFlags = pKeyValuesData->GetInt( "item_flags", ITEM_FLAG_LIMITINWORLD );
  332. for ( int i=0; i < ARRAYSIZE( g_ItemFlags ); i++ )
  333. {
  334. int iVal = pKeyValuesData->GetInt( g_ItemFlags[i].m_pFlagName, -1 );
  335. if ( iVal == 0 )
  336. {
  337. iFlags &= ~g_ItemFlags[i].m_iFlagValue;
  338. }
  339. else if ( iVal == 1 )
  340. {
  341. iFlags |= g_ItemFlags[i].m_iFlagValue;
  342. }
  343. }
  344. bShowUsageHint = ( pKeyValuesData->GetInt( "showusagehint", 0 ) != 0 ) ? true : false;
  345. bAutoSwitchTo = ( pKeyValuesData->GetInt( "autoswitchto", 1 ) != 0 ) ? true : false;
  346. bAutoSwitchFrom = ( pKeyValuesData->GetInt( "autoswitchfrom", 1 ) != 0 ) ? true : false;
  347. m_bBuiltRightHanded = ( pKeyValuesData->GetInt( "BuiltRightHanded", 1 ) != 0 ) ? true : false;
  348. m_bAllowFlipping = ( pKeyValuesData->GetInt( "AllowFlipping", 1 ) != 0 ) ? true : false;
  349. m_bMeleeWeapon = ( pKeyValuesData->GetInt( "MeleeWeapon", 0 ) != 0 ) ? true : false;
  350. #if defined(_DEBUG) && defined(HL2_CLIENT_DLL)
  351. // make sure two weapons aren't in the same slot & position
  352. if ( iSlot >= MAX_WEAPON_SLOTS ||
  353. iPosition >= MAX_WEAPON_POSITIONS )
  354. {
  355. Warning( "Invalid weapon slot or position [slot %d/%d max], pos[%d/%d max]\n",
  356. iSlot, MAX_WEAPON_SLOTS - 1, iPosition, MAX_WEAPON_POSITIONS - 1 );
  357. }
  358. else
  359. {
  360. if (g_bUsedWeaponSlots[iSlot][iPosition])
  361. {
  362. Warning( "Duplicately assigned weapon slots in selection hud: %s (%d, %d)\n", szPrintName, iSlot, iPosition );
  363. }
  364. g_bUsedWeaponSlots[iSlot][iPosition] = true;
  365. }
  366. #endif
  367. // Primary ammo used
  368. const char *pAmmo = pKeyValuesData->GetString( "primary_ammo", "None" );
  369. if ( strcmp("None", pAmmo) == 0 )
  370. Q_strncpy( szAmmo1, "", sizeof( szAmmo1 ) );
  371. else
  372. Q_strncpy( szAmmo1, pAmmo, sizeof( szAmmo1 ) );
  373. iAmmoType = GetAmmoDef()->Index( szAmmo1 );
  374. // Secondary ammo used
  375. pAmmo = pKeyValuesData->GetString( "secondary_ammo", "None" );
  376. if ( strcmp("None", pAmmo) == 0)
  377. Q_strncpy( szAmmo2, "", sizeof( szAmmo2 ) );
  378. else
  379. Q_strncpy( szAmmo2, pAmmo, sizeof( szAmmo2 ) );
  380. iAmmo2Type = GetAmmoDef()->Index( szAmmo2 );
  381. // Now read the weapon sounds
  382. memset( aShootSounds, 0, sizeof( aShootSounds ) );
  383. KeyValues *pSoundData = pKeyValuesData->FindKey( "SoundData" );
  384. if ( pSoundData )
  385. {
  386. for ( int i = EMPTY; i < NUM_SHOOT_SOUND_TYPES; i++ )
  387. {
  388. const char *soundname = pSoundData->GetString( pWeaponSoundCategories[i] );
  389. if ( soundname && soundname[0] )
  390. {
  391. Q_strncpy( aShootSounds[i], soundname, MAX_WEAPON_STRING );
  392. }
  393. }
  394. }
  395. }