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.

3219 lines
106 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "game_item_schema.h"
  9. #include "schemainitutils.h"
  10. #include "tf_shareddefs.h"
  11. #include "tf_item_tools.h"
  12. #include "in_buttons.h"
  13. #include "econ_holidays.h"
  14. #ifndef GC_DLL
  15. #include "econ_item_system.h"
  16. #include "tf_quest_restriction.h"
  17. #include "engine/IEngineSound.h"
  18. extern ISoundEmitterSystemBase *soundemitterbase;
  19. #endif // !GC_DLL
  20. #ifdef CLIENT_DLL
  21. #include "materialsystem/itexturecompositor.h"
  22. #endif
  23. extern const char *s_pszMatchGroups[];
  24. // For a particular set of KeyValues, ensure that all of the one-level-deep subkeys are a subset of the values in testKeys.
  25. // This ensures that there are no typos in the keynames.
  26. static bool ValidateKeysAreSubset( KeyValues* kv, const CUtlVector<const char *>& testKeys, CUtlVector<CUtlString> *pVecErrors )
  27. {
  28. int numTestEntries = testKeys.Count();
  29. Assert(numTestEntries >= 0);
  30. if (numTestEntries == 0)
  31. return true;
  32. // This currently is inefficient, it's O(len(_keyvalues) * len(_testKeys)). It could easily be made faster for large N, but for small lengths
  33. // cache dominates. It also has the nice property that it will show up on the profiler if it's a problem.
  34. for ( KeyValues *pKey = kv->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey() )
  35. {
  36. bool matchAny = false;
  37. const char* testVal = pKey->GetName();
  38. for ( auto it = testKeys.begin(); it != testKeys.end(); ++it ) {
  39. if (0 == V_stricmp((*it), testVal)) {
  40. matchAny = true;
  41. break;
  42. }
  43. }
  44. if (!matchAny)
  45. {
  46. if (pVecErrors)
  47. {
  48. CUtlString choices(CFmtStr("Unexpected key '%s', expected one of: ", testVal));
  49. int numTestEntriesLessOne = numTestEntries - 1;
  50. for (int i = 0; i < numTestEntriesLessOne; ++i)
  51. {
  52. choices.Append(CFmtStr("\"%s\", ", testKeys[i]));
  53. }
  54. choices.Append(CFmtStr("\"%s\".", testKeys[numTestEntriesLessOne]));
  55. pVecErrors->AddToTail(choices);
  56. }
  57. return false;
  58. }
  59. }
  60. return true;
  61. }
  62. bool SchemaMMGroup_t::IsCategoryValid() const
  63. {
  64. FOR_EACH_VEC( m_vecModes, i )
  65. {
  66. if ( m_vecModes[i]->PassesRestrictions() )
  67. return true;
  68. }
  69. return false;
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose: Returns true if the vector contains a set of items that matches the inputs for this recipe
  73. // Note it will fail if the vector contains extra items that aren't needed.
  74. //
  75. //-----------------------------------------------------------------------------
  76. bool CTFCraftingRecipeDefinition::ItemListMatchesInputs( CUtlVector<CEconItem*> *vecCraftingItems, KeyValues *out_pkvCraftParams, bool bIgnoreSlop, CUtlVector<uint64> *vecChosenItems ) const
  77. {
  78. CUtlVector<CEconItem*> vecTmp;
  79. vecTmp = *vecCraftingItems;
  80. int hack_iForcedClass = -1,
  81. hack_iForcedSlot = LOADOUT_POSITION_INVALID;
  82. const CEconItemSetDefinition *hack_pForcedItemSetDef = NULL;
  83. int *iForcedClass = NULL,
  84. *iForcedSlot = NULL;
  85. const CEconItemSetDefinition **ppForcedItemSetDef = NULL;
  86. if ( out_pkvCraftParams )
  87. {
  88. iForcedClass = &hack_iForcedClass;
  89. iForcedSlot = &hack_iForcedSlot;
  90. ppForcedItemSetDef = &hack_pForcedItemSetDef;
  91. }
  92. // If we require all items to be used by the same class, find the matching classes for all items
  93. CBitVec<LOADOUT_COUNT> iCUForAllItems;
  94. if ( m_bRequiresAllSameClass )
  95. {
  96. iCUForAllItems.SetAll();
  97. for ( int iVC = 0; iVC < vecCraftingItems->Count(); iVC++ )
  98. {
  99. const CTFItemDefinition *pDef = GetItemSchema()->GetTFItemDefinition( vecCraftingItems->Element(iVC)->GetDefinitionIndex() );
  100. const CBitVec<LOADOUT_COUNT> *pDefCU = pDef->GetClassUsability();
  101. if ( pDefCU )
  102. {
  103. iCUForAllItems.And( *pDefCU, &iCUForAllItems );
  104. }
  105. }
  106. // If we didn't find a single class that all items can be used by, we're done
  107. if ( iCUForAllItems.IsAllClear() )
  108. return false;
  109. }
  110. CBitVec<LOADOUT_COUNT> bvSlotCoverage;
  111. bvSlotCoverage.SetAll();
  112. for ( int i = 0; i < m_InputItemsCriteria.Count(); i++ )
  113. {
  114. int32 iDefFound = -1;
  115. // Find the required count of each item
  116. for ( int iItem = 0; iItem < vecTmp.Count(); iItem++ )
  117. {
  118. CTFItemDefinition *pItemDef = GetItemSchema()->GetTFItemDefinition( vecTmp[iItem]->GetDefinitionIndex() );
  119. if ( m_InputItemsCriteria[i].BEvaluate( pItemDef ) )
  120. {
  121. if ( m_bRequiresAllSameSlot )
  122. {
  123. CBitVec<LOADOUT_COUNT> bvSlots;
  124. pItemDef->FilloutSlotUsage( &bvSlots );
  125. bvSlotCoverage.And( bvSlots, &bvSlotCoverage );
  126. // If we have no slots that are used by all our weapons, we're done
  127. if ( bvSlotCoverage.IsAllClear() )
  128. return false;
  129. }
  130. if ( iForcedClass && m_iCacheClassUsageForOutputFromItem == i )
  131. {
  132. // If the item def has a class_token_id key, we use that. Otherwise, we find a class that uses it.
  133. const char *pszToken = pItemDef->GetClassToken();
  134. if ( pszToken && pszToken[0] )
  135. {
  136. *iForcedClass = StringFieldToInt( pszToken, GetItemSchema()->GetClassUsabilityStrings() );
  137. }
  138. else if ( *iForcedClass == -1 )
  139. {
  140. const CBitVec<LOADOUT_COUNT> *pCU;
  141. if ( m_bRequiresAllSameClass )
  142. {
  143. // We need to find the first class that can use all the items
  144. pCU = &iCUForAllItems;
  145. }
  146. else
  147. {
  148. pCU = pItemDef->GetClassUsability();
  149. }
  150. // Find the first class
  151. if ( pCU )
  152. {
  153. for ( int iCU = 0; iCU < LOADOUT_COUNT; iCU++ )
  154. {
  155. if ( pCU->IsBitSet(iCU) )
  156. {
  157. *iForcedClass = iCU;
  158. break;
  159. }
  160. }
  161. }
  162. // If we need to be the same class, but couldn't find a common class across the items, we're done.
  163. if ( m_bRequiresAllSameClass && *iForcedClass == -1 )
  164. return false;
  165. }
  166. }
  167. if ( ppForcedItemSetDef && m_iCacheSetForOutputFromItem == i )
  168. {
  169. // If they've passed in a set item, remember it's set index
  170. // Abort if they somehow have an item here that doesn't have a set index
  171. const CEconItemSetDefinition *pItemSetDef = pItemDef->GetItemSetDefinition();
  172. if ( !pItemSetDef )
  173. return false;
  174. *ppForcedItemSetDef = pItemSetDef;
  175. }
  176. if ( iForcedSlot && m_iCacheSlotUsageForOutputFromItem == i )
  177. {
  178. // If the item def has a slot_token_id key, we use that. Otherwise, we find the first class that uses it.
  179. const char *pszToken = pItemDef->GetSlotToken();
  180. if ( pszToken && pszToken[0] )
  181. {
  182. *iForcedSlot = StringFieldToInt( pszToken, GetItemSchema()->GetLoadoutStrings( EQUIP_TYPE_CLASS ) );
  183. }
  184. else if ( *iForcedSlot == LOADOUT_POSITION_INVALID )
  185. {
  186. // If we have a forced class, we find the slot that class uses. Otherwise, we find the first slot used.
  187. if ( iForcedClass )
  188. {
  189. *iForcedSlot = pItemDef->GetLoadoutSlot( *iForcedClass );
  190. }
  191. else
  192. {
  193. *iForcedSlot = pItemDef->GetLoadoutSlot( 0 );
  194. }
  195. }
  196. }
  197. // Matched. Remove the item and continue
  198. iDefFound = pItemDef->GetDefinitionIndex();
  199. if ( vecChosenItems )
  200. {
  201. vecChosenItems->AddToTail( vecTmp[iItem]->GetItemID() );
  202. }
  203. vecTmp.Remove(iItem);
  204. break;
  205. }
  206. }
  207. if ( iDefFound == -1 )
  208. return false;
  209. // If we want dupes of the above item, look for them
  210. int iDupes = (int)m_InputItemDupeCounts[i];
  211. if ( iDupes > 1 )
  212. {
  213. iDupes--;
  214. for ( int iItem = vecTmp.Count()-1; iItem >= 0; iItem-- )
  215. {
  216. if ( (int)vecTmp[iItem]->GetDefinitionIndex() == iDefFound )
  217. {
  218. vecTmp.Remove(iItem);
  219. iDupes--;
  220. }
  221. }
  222. if ( iDupes != 0 )
  223. return false;
  224. }
  225. }
  226. if ( out_pkvCraftParams )
  227. {
  228. out_pkvCraftParams->SetInt( "forced_class", hack_iForcedClass );
  229. out_pkvCraftParams->SetInt( "forced_slot", hack_iForcedSlot );
  230. if ( hack_pForcedItemSetDef )
  231. {
  232. out_pkvCraftParams->SetString( "forced_set_def_name", hack_pForcedItemSetDef->m_pszName );
  233. }
  234. }
  235. // We've only matched if there aren't any leftover items, or we're ignoring slop
  236. return ( vecTmp.Count() == 0 || bIgnoreSlop );
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. //-----------------------------------------------------------------------------
  241. bool CTFCraftingRecipeDefinition::CanMatchAgainstBackpack( CUtlVector<CEconItem*> *vecAllItems, CUtlVector<CEconItem*> vecItemsByClass[ LOADOUT_COUNT ], CUtlVector<CEconItem*> vecItemsBySlot[ CLASS_LOADOUT_POSITION_COUNT ], CUtlVector<uint64> *vecChosenItems ) const
  242. {
  243. // If we require all the same class, examine the class lists individually
  244. if ( m_bRequiresAllSameClass )
  245. {
  246. for (int iClass = 0; iClass < CLASS_LOADOUT_POSITION_COUNT; iClass++ )
  247. {
  248. if ( !vecItemsByClass[iClass].Count() )
  249. continue;
  250. if ( CheckSubItemListAgainstBackpack( &vecItemsByClass[iClass], vecChosenItems ) )
  251. return true;
  252. }
  253. return false;
  254. }
  255. // If we require all the same slot, examine the slot lists individually
  256. if ( m_bRequiresAllSameSlot )
  257. {
  258. for (int iSlot = 0; iSlot < CLASS_LOADOUT_POSITION_COUNT; iSlot++ )
  259. {
  260. if ( !vecItemsBySlot[iSlot].Count() )
  261. continue;
  262. if ( CheckSubItemListAgainstBackpack( &vecItemsBySlot[iSlot], vecChosenItems ) )
  263. return true;
  264. }
  265. return false;
  266. }
  267. return CheckSubItemListAgainstBackpack( vecAllItems, vecChosenItems );
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. //-----------------------------------------------------------------------------
  272. bool CTFCraftingRecipeDefinition::CheckSubItemListAgainstBackpack( CUtlVector<CEconItem*> *vecCraftingItems, CUtlVector<uint64> *vecChosenItems ) const
  273. {
  274. CUtlVector<CEconItem*> vecTmp;
  275. vecTmp = *vecCraftingItems;
  276. CBitVec<LOADOUT_COUNT> bvSlotCoverage;
  277. bvSlotCoverage.SetAll();
  278. int iForcedClass = 0;
  279. int iForcedSlot = 0;
  280. for ( int i = 0; i < m_InputItemsCriteria.Count(); i++ )
  281. {
  282. int32 iDefFound = -1;
  283. // Find the required count of each item
  284. for ( int iItem = 0; iItem < vecTmp.Count(); iItem++ )
  285. {
  286. CTFItemDefinition *pItemDef = GetItemSchema()->GetTFItemDefinition( vecTmp[iItem]->GetDefinitionIndex() );
  287. if ( m_InputItemsCriteria[i].BEvaluate( pItemDef ) )
  288. {
  289. if ( m_iCacheClassUsageForOutputFromItem == i )
  290. {
  291. // If the item def has a class_token_id key, we use that. Otherwise, we find a class that uses it.
  292. const char *pszToken = pItemDef->GetClassToken();
  293. if ( pszToken && pszToken[0] )
  294. {
  295. iForcedClass = StringFieldToInt( pszToken, GetItemSchema()->GetClassUsabilityStrings() );
  296. }
  297. else if ( iForcedClass == -1 )
  298. {
  299. const CBitVec<LOADOUT_COUNT> *pCU;
  300. if ( m_bRequiresAllSameClass )
  301. {
  302. // We need to find the first class that can use all the items
  303. pCU = pItemDef->GetClassUsability();//&iCUForAllItems;
  304. }
  305. else
  306. {
  307. pCU = pItemDef->GetClassUsability();
  308. }
  309. // Find the first class
  310. if ( pCU )
  311. {
  312. for ( int iCU = 0; iCU < LOADOUT_COUNT; iCU++ )
  313. {
  314. if ( pCU->IsBitSet(iCU) )
  315. {
  316. iForcedClass = iCU;
  317. break;
  318. }
  319. }
  320. }
  321. // If we need to be the same class, but couldn't find a common class across the items, we're done.
  322. if ( m_bRequiresAllSameClass && iForcedClass == -1 )
  323. return false;
  324. }
  325. }
  326. if ( iForcedSlot && m_iCacheSlotUsageForOutputFromItem == i )
  327. {
  328. // If the item def has a slot_token_id key, we use that. Otherwise, we find the first class that uses it.
  329. const char *pszToken = pItemDef->GetSlotToken();
  330. if ( pszToken && pszToken[0] )
  331. {
  332. iForcedSlot = StringFieldToInt( pszToken, GetItemSchema()->GetLoadoutStrings( EQUIP_TYPE_CLASS ) );
  333. }
  334. else if ( iForcedSlot == LOADOUT_POSITION_INVALID )
  335. {
  336. // If we have a forced class, we find the slot that class uses. Otherwise, we find the first slot used.
  337. if ( iForcedClass )
  338. {
  339. iForcedSlot = pItemDef->GetLoadoutSlot( iForcedClass );
  340. }
  341. else
  342. {
  343. iForcedSlot = pItemDef->GetLoadoutSlot( 0 );
  344. }
  345. }
  346. }
  347. // Found a match.
  348. iDefFound = pItemDef->GetDefinitionIndex();
  349. bool bValidMatch = true;
  350. // If we want dupes of the above item, look for them before settling on this item.
  351. int iDupes = (int)m_InputItemDupeCounts[i];
  352. if ( iDupes > 1 )
  353. {
  354. CUtlVector<int> vecDupeItems;
  355. // We've already found one of the items.
  356. iDupes--;
  357. for ( int iDupeItem = vecTmp.Count()-1; iDupeItem >= 0 && iDupes > 0; iDupeItem-- )
  358. {
  359. // Ignore the item we first found
  360. if ( iDupeItem == iItem )
  361. continue;
  362. if ( (int)vecTmp[iDupeItem]->GetDefinitionIndex() == iDefFound )
  363. {
  364. vecDupeItems.AddToTail( iDupeItem );
  365. iDupes--;
  366. }
  367. }
  368. bValidMatch = (iDupes == 0);
  369. if ( bValidMatch )
  370. {
  371. // We found all the dupes we wanted, so remove them all
  372. FOR_EACH_VEC( vecDupeItems, iDupeItem )
  373. {
  374. if ( vecChosenItems )
  375. {
  376. vecChosenItems->AddToTail( vecTmp[ vecDupeItems[iDupeItem] ]->GetItemID() );
  377. }
  378. vecTmp.Remove( vecDupeItems[iDupeItem] );
  379. }
  380. }
  381. }
  382. if ( bValidMatch )
  383. {
  384. if ( vecChosenItems )
  385. {
  386. vecChosenItems->AddToTail( vecTmp[iItem]->GetItemID() );
  387. }
  388. vecTmp.Remove(iItem);
  389. break;
  390. }
  391. }
  392. }
  393. if ( iDefFound == -1 )
  394. return false;
  395. }
  396. return true;
  397. }
  398. //-----------------------------------------------------------------------------
  399. // Purpose:
  400. //-----------------------------------------------------------------------------
  401. static void InitPerClassStringArray( KeyValues *pPerClassData, const char *(&outputArray)[LOADOUT_COUNT] )
  402. {
  403. if ( pPerClassData )
  404. {
  405. const char* pszBaseName = pPerClassData->GetString( "basename", NULL );
  406. for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; i++ )
  407. {
  408. if ( outputArray[i] && *outputArray[i] )
  409. {
  410. delete outputArray[i];
  411. outputArray[i] = NULL;
  412. }
  413. char* pszOut = NULL;
  414. CUtlString strClassString( pPerClassData->GetString( GetItemSchema()->GetClassUsabilityStrings()[i], NULL ) );
  415. // If there's a class specific string defined, use that
  416. if ( !strClassString.IsEmpty() )
  417. {
  418. size_t nLength = strClassString.Length() + 1;
  419. pszOut = new char[ nLength ];
  420. V_strncpy( pszOut, strClassString, nLength );
  421. }
  422. else if ( pszBaseName )
  423. {
  424. // If we have a basename specified, use that to construct our class-specific string
  425. // ( ex. models/badge_%s.mdl turns into models/badge_scout.mdl, etc. )
  426. // So this is fun. ClassUsabilityStrings refers to the "Demoman", but the vast majority of his models are whatever_demo.mdl
  427. // The RIGHT fix would be to either:
  428. // 1) change all the model and content files to whatever_demoman.mdl
  429. // 2) fixup the schema so every reference to "demoman" is changed to "demo" and update GetClassUsabilityStrings
  430. // and fix everything that breaks
  431. // But we're not doing that right now. If this class is the TF_CLASS_DEMOMAN, just force "demo"
  432. CFmtStr fmtStr;
  433. if ( i == TF_CLASS_DEMOMAN )
  434. {
  435. fmtStr.sprintf( pszBaseName, "demo", "demo", "demo" );
  436. }
  437. else
  438. {
  439. fmtStr.sprintf( pszBaseName, GetItemSchema()->GetClassUsabilityStrings()[i], GetItemSchema()->GetClassUsabilityStrings()[i], GetItemSchema()->GetClassUsabilityStrings()[i] );
  440. }
  441. int nLength = fmtStr.Length() + 1;
  442. pszOut = new char[ nLength ];
  443. V_strncpy( pszOut, fmtStr, nLength );
  444. }
  445. outputArray[i] = pszOut;
  446. if ( outputArray[0] == NULL )
  447. {
  448. outputArray[0] = outputArray[i];
  449. }
  450. }
  451. }
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose:
  455. //-----------------------------------------------------------------------------
  456. static bool InitPerClassStringVectorArray( KeyValues *pPerClassData, CUtlVector< const char * > (&outputArray)[LOADOUT_COUNT], CUtlVector<CUtlString>* pVecErrors )
  457. {
  458. if ( pPerClassData )
  459. {
  460. if ( !ValidateKeysAreSubset( pPerClassData, GetItemSchema()->GetClassUsabilityStrings(), pVecErrors ) )
  461. {
  462. return false;
  463. }
  464. for ( int i = 1; i < LOADOUT_COUNT; i++ )
  465. {
  466. KeyValues *pClassKey = pPerClassData->FindKey( GetItemSchema()->GetClassUsabilityStrings()[i] );
  467. if ( pClassKey )
  468. {
  469. // check single line case
  470. const char *pszValue = pClassKey->GetString();
  471. if ( pszValue && *pszValue )
  472. {
  473. outputArray[i].AddToTail( pszValue );
  474. }
  475. // check multi line case
  476. FOR_EACH_SUBKEY( pClassKey, pValueKey )
  477. {
  478. pszValue = pValueKey->GetString();
  479. if ( pszValue && *pszValue )
  480. {
  481. outputArray[i].AddToTail( pszValue );
  482. }
  483. }
  484. }
  485. }
  486. }
  487. return true;
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose:
  491. //-----------------------------------------------------------------------------
  492. CRandomChanceString::CRandomChanceString()
  493. {
  494. m_unTotalChance = 0;
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose:
  498. //-----------------------------------------------------------------------------
  499. void CRandomChanceString::AddString( const char *pszString, int nChance )
  500. {
  501. Assert( nChance > 0 );
  502. std::pair< const char *, int > toAdd( pszString, nChance );
  503. m_vecChoices.AddToTail( toAdd );
  504. m_unTotalChance += nChance;
  505. }
  506. //-----------------------------------------------------------------------------
  507. // Purpose:
  508. //-----------------------------------------------------------------------------
  509. const char *CRandomChanceString::GetRandomString() const
  510. {
  511. int nRandomRoll = RandomInt( 1, m_unTotalChance );
  512. int nStartWindow = 0;
  513. FOR_EACH_VEC( m_vecChoices, i )
  514. {
  515. int nEndWindow = nStartWindow + m_vecChoices[i].second;
  516. if ( nRandomRoll > nStartWindow && nRandomRoll <= nEndWindow )
  517. {
  518. return m_vecChoices[i].first;
  519. }
  520. nStartWindow = nEndWindow;
  521. }
  522. return NULL;
  523. }
  524. //-----------------------------------------------------------------------------
  525. // Purpose:
  526. //-----------------------------------------------------------------------------
  527. static bool ParseRandomChanceStringFromKV( KeyValues *pClassKey, CRandomChanceString *pOut )
  528. {
  529. Assert( pClassKey );
  530. Assert( pOut );
  531. // check single line case
  532. const char *pszName = pClassKey->GetString();
  533. if ( pszName && *pszName )
  534. {
  535. // there's only one choice
  536. pOut->AddString( pszName, 1 );
  537. }
  538. else
  539. {
  540. // check multi line case
  541. FOR_EACH_SUBKEY( pClassKey, pValueKey )
  542. {
  543. const char *pszChoice = pValueKey->GetName();
  544. int nChance = pValueKey->GetInt();
  545. if ( pszChoice && *pszChoice && nChance > 0 )
  546. {
  547. pOut->AddString( pszChoice, nChance );
  548. }
  549. }
  550. }
  551. return true;
  552. }
  553. //-----------------------------------------------------------------------------
  554. // Purpose:
  555. //-----------------------------------------------------------------------------
  556. static bool InitPerClassRandomChanceStringArray( KeyValues *pPerClassData, CRandomChanceString (&outputArray)[LOADOUT_COUNT], CUtlVector<CUtlString>* pVecErrors )
  557. {
  558. if ( pPerClassData )
  559. {
  560. if ( !ValidateKeysAreSubset( pPerClassData, GetItemSchema()->GetClassUsabilityStrings(), pVecErrors ) )
  561. {
  562. return false;
  563. }
  564. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  565. {
  566. KeyValues *pClassKey = pPerClassData->FindKey( GetItemSchema()->GetClassUsabilityStrings()[i] );
  567. if ( pClassKey )
  568. {
  569. ParseRandomChanceStringFromKV( pClassKey, &outputArray[i] );
  570. }
  571. }
  572. }
  573. return true;
  574. }
  575. CTFTauntInfo::CTFTauntInfo()
  576. {
  577. for ( int i=0; i<LOADOUT_COUNT; ++i )
  578. {
  579. m_pszProp[i] = NULL;
  580. m_pszPropIntroScene[i] = NULL;
  581. m_pszPropOutroScene[i] = NULL;
  582. }
  583. m_pszParticleAttachment = NULL;
  584. m_flTauntSeparationForwardDistance = 0;
  585. m_flTauntSeparationRightDistance = 0;
  586. m_flMinTauntTime = 2.f;
  587. m_bIsPartnerTaunt = false;
  588. m_bStopTauntIfMoved = false;
  589. m_nFOV = 0;
  590. m_flCameraDist = 0;
  591. m_flCameraDistUp = -15;
  592. }
  593. //-----------------------------------------------------------------------------
  594. // Purpose:
  595. //-----------------------------------------------------------------------------
  596. bool CTFTauntInfo::InitTauntInputRemap( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  597. {
  598. static const char *s_pszAllowedTauntInputButtonNames[] =
  599. {
  600. "IN_ATTACK",
  601. "IN_ATTACK2",
  602. "IN_FORWARD",
  603. "IN_BACK"
  604. };
  605. static int s_iAllowedTauntInputButtons[] =
  606. {
  607. IN_ATTACK,
  608. IN_ATTACK2,
  609. IN_FORWARD,
  610. IN_BACK
  611. };
  612. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszAllowedTauntInputButtonNames ) == ARRAYSIZE( s_iAllowedTauntInputButtons ) );
  613. FOR_EACH_SUBKEY( pKV, pButtonKey )
  614. {
  615. const char *pszButtonName = pButtonKey->GetName();
  616. int iButton = 0;
  617. for ( int i=0; i<ARRAYSIZE( s_pszAllowedTauntInputButtonNames ); i++ )
  618. {
  619. if ( !V_strcmp( pszButtonName, s_pszAllowedTauntInputButtonNames[i] ) )
  620. {
  621. iButton = s_iAllowedTauntInputButtons[i];
  622. break;
  623. }
  624. }
  625. if ( iButton == 0 )
  626. {
  627. AssertMsg( 0, "Taunt input button [%s] is not valid.\n", pszButtonName );
  628. return false;
  629. }
  630. KeyValues *pPressedKey = pButtonKey->FindKey( "pressed" );
  631. KeyValues *pReleasedKey = pButtonKey->FindKey( "released" );
  632. if ( pPressedKey || pReleasedKey )
  633. {
  634. int iNew = m_vecTauntInputRemap.AddToTail();
  635. m_vecTauntInputRemap[iNew].m_iButton = iButton;
  636. if ( !InitPerClassStringVectorArray( pPressedKey, m_vecTauntInputRemap[iNew].m_vecButtonPressedScenes, pVecErrors ) )
  637. return false;
  638. if ( !InitPerClassStringVectorArray( pReleasedKey, m_vecTauntInputRemap[iNew].m_vecButtonReleasedScenes, pVecErrors ) )
  639. return false;
  640. }
  641. }
  642. return true;
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Purpose:
  646. //-----------------------------------------------------------------------------
  647. bool CTFTauntInfo::BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  648. {
  649. FOR_EACH_SUBKEY( pKV, pSubKey )
  650. {
  651. const char *pszKeyName = pSubKey->GetName();
  652. if ( !V_strcmp( pszKeyName, "custom_taunt_scene_per_class" ) )
  653. {
  654. if ( !InitPerClassStringVectorArray( pSubKey, m_vecIntroScenes, pVecErrors ) )
  655. return false;
  656. }
  657. else if ( !V_strcmp( pszKeyName, "custom_taunt_outro_scene_per_class" ) )
  658. {
  659. if ( !InitPerClassStringVectorArray( pSubKey, m_vecOutroScenes, pVecErrors ) )
  660. return false;
  661. }
  662. else if ( !V_strcmp( pszKeyName, "custom_partner_taunt_per_class" ) )
  663. {
  664. if ( !InitPerClassStringVectorArray( pSubKey, m_vecPartnerTauntInitiatorScenes, pVecErrors ) )
  665. return false;
  666. if ( !InitPerClassStringVectorArray( pSubKey, m_vecPartnerTauntReceiverScenes, pVecErrors ) )
  667. return false;
  668. }
  669. else if ( !V_strcmp( pszKeyName, "custom_partner_taunt_initiator_per_class" ) )
  670. {
  671. if ( !InitPerClassStringVectorArray( pSubKey, m_vecPartnerTauntInitiatorScenes, pVecErrors ) )
  672. return false;
  673. }
  674. else if ( !V_strcmp( pszKeyName, "custom_partner_taunt_receiver_per_class" ) )
  675. {
  676. if ( !InitPerClassStringVectorArray( pSubKey, m_vecPartnerTauntReceiverScenes, pVecErrors ) )
  677. return false;
  678. }
  679. else if ( !V_strcmp( pszKeyName, "custom_taunt_input_remap" ) )
  680. {
  681. if ( !InitTauntInputRemap( pSubKey, pVecErrors ) )
  682. {
  683. return false;
  684. }
  685. }
  686. else if ( !V_strcmp( pszKeyName, "custom_taunt_prop_per_class" ) )
  687. {
  688. InitPerClassStringArray( pSubKey, m_pszProp );
  689. }
  690. else if ( !V_strcmp( pszKeyName, "custom_taunt_prop_scene_per_class" ) )
  691. {
  692. InitPerClassStringArray( pSubKey, m_pszPropIntroScene );
  693. }
  694. else if ( !V_strcmp( pszKeyName, "custom_taunt_prop_outro_scene_per_class" ) )
  695. {
  696. InitPerClassStringArray( pSubKey, m_pszPropOutroScene );
  697. }
  698. else if ( !V_strcmp( pszKeyName, "taunt_separation_forward_distance" ) )
  699. {
  700. m_flTauntSeparationForwardDistance = pSubKey->GetFloat();
  701. }
  702. else if ( !V_strcmp( pszKeyName, "taunt_separation_right_distance" ) )
  703. {
  704. m_flTauntSeparationRightDistance = pSubKey->GetFloat();
  705. }
  706. else if ( !V_strcmp( pszKeyName, "min_taunt_time" ) )
  707. {
  708. m_flMinTauntTime = pSubKey->GetFloat();
  709. }
  710. else if ( !V_strcmp( pszKeyName, "is_partner_taunt" ) )
  711. {
  712. m_bIsPartnerTaunt = pSubKey->GetBool();
  713. }
  714. else if ( !V_strcmp( pszKeyName, "stop_taunt_if_moved" ) )
  715. {
  716. m_bStopTauntIfMoved = pSubKey->GetBool();
  717. }
  718. else if ( !V_strcmp( pszKeyName, "fov" ) )
  719. {
  720. m_nFOV = pSubKey->GetInt();
  721. }
  722. else if ( !V_strcmp( pszKeyName, "camera_dist" ) )
  723. {
  724. m_flCameraDist = pSubKey->GetFloat();
  725. }
  726. else if ( !V_strcmp( pszKeyName, "camera_dist_up" ) )
  727. {
  728. m_flCameraDistUp = pSubKey->GetFloat();
  729. }
  730. else if ( !V_strcmp( pszKeyName, "particle_attachment" ) )
  731. {
  732. m_pszParticleAttachment = pSubKey->GetString();
  733. }
  734. else
  735. {
  736. AssertMsg( 0, "'%s' key is invalid", pszKeyName );
  737. return false;
  738. }
  739. }
  740. return true;
  741. }
  742. CQuestThemeDefinition::CQuestThemeDefinition()
  743. : m_pRawKVs( NULL )
  744. , m_pszName( NULL )
  745. , m_pszNotificationRes( NULL )
  746. , m_pszQuestItemRes( NULL )
  747. , m_pszRewardString( NULL )
  748. , m_pszDiscardString( NULL )
  749. , m_pszInGameTrackerRes( NULL )
  750. , m_eUnackPos( UNACK_ITEM_QUEST_OUTPUT )
  751. {
  752. memset( m_vecGiveStrings, NULL, sizeof( m_vecGiveStrings ) );
  753. memset( m_vecCompleteStrings, NULL, sizeof( m_vecCompleteStrings ) );
  754. memset( m_vecFullyCompleteStrings, NULL, sizeof( m_vecFullyCompleteStrings ) );
  755. }
  756. CQuestThemeDefinition::~CQuestThemeDefinition()
  757. {
  758. if ( m_pRawKVs )
  759. {
  760. m_pRawKVs->deleteThis();
  761. m_pRawKVs = NULL;
  762. }
  763. }
  764. bool CQuestThemeDefinition::BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  765. {
  766. if ( m_pRawKVs )
  767. {
  768. m_pRawKVs->deleteThis();
  769. m_pRawKVs = NULL;
  770. }
  771. m_pRawKVs = new KeyValues( pKV->GetName() );
  772. MergeDefinitionPrefab( m_pRawKVs, pKV );
  773. m_pszName = m_pRawKVs->GetName();
  774. m_pszNotificationRes = m_pRawKVs->GetString( "notification_res", NULL );
  775. m_pszQuestItemRes = m_pRawKVs->GetString( "quest_item_res", NULL );
  776. m_pszInGameTrackerRes = m_pRawKVs->GetString( "in_game_res", NULL );
  777. m_eUnackPos = (unacknowledged_item_inventory_positions_t)m_pRawKVs->GetInt( "unack_position", UNACK_ITEM_QUEST_OUTPUT );
  778. KeyValues *pKVSounds = m_pRawKVs->FindKey( "sounds" );
  779. if ( pKVSounds )
  780. {
  781. // "I have a mission for you"
  782. KeyValues *pKVGiveSounds = pKVSounds->FindKey( "give_quest" );
  783. if ( pKVGiveSounds )
  784. {
  785. InitPerClassRandomChanceStringArray( pKVGiveSounds, m_vecGiveStrings, pVecErrors );
  786. }
  787. // "You completed a quest"
  788. KeyValues *pKVCompleteSounds = pKVSounds->FindKey( "complete_quest" );
  789. if ( pKVCompleteSounds )
  790. {
  791. InitPerClassRandomChanceStringArray( pKVCompleteSounds, m_vecCompleteStrings, pVecErrors );
  792. }
  793. // "You completed a quest"
  794. KeyValues *pKVFullyCompleteSounds = pKVSounds->FindKey( "fully_complete_quest" );
  795. if ( pKVFullyCompleteSounds )
  796. {
  797. InitPerClassRandomChanceStringArray( pKVFullyCompleteSounds, m_vecFullyCompleteStrings, pVecErrors );
  798. }
  799. m_pszRewardString = pKVSounds->GetString( "give_reward", NULL );
  800. m_pszDiscardString = pKVSounds->GetString( "discard_quest", NULL );
  801. m_pszOnRevealText = pKVSounds->GetString( "reveal_sound", NULL );
  802. }
  803. SCHEMA_INIT_CHECK( m_pszName != NULL, "No name given for quest theme!" );
  804. SCHEMA_INIT_CHECK( m_pszNotificationRes != NULL, "No notification res file specified for theme '%s'", m_pszName );
  805. SCHEMA_INIT_CHECK( m_pszQuestItemRes != NULL, "No quest item res file specified for theme '%s'", m_pszName );
  806. SCHEMA_INIT_CHECK ( m_pszInGameTrackerRes != NULL, "No in game tracker res file specified for theme '%s'", m_pszName );
  807. return SCHEMA_INIT_SUCCESS();
  808. }
  809. //-----------------------------------------------------------------------------
  810. // Purpose:
  811. //-----------------------------------------------------------------------------
  812. CQuestDefinition::CQuestDefinition( void )
  813. {}
  814. //-----------------------------------------------------------------------------
  815. // Purpose:
  816. //-----------------------------------------------------------------------------
  817. bool CQuestDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
  818. {
  819. KeyValues* pKVOjectives = pKVItem->FindKey( "objectives" );
  820. if ( pKVOjectives )
  821. {
  822. FOR_EACH_TRUE_SUBKEY( pKVOjectives, pKVObj )
  823. {
  824. const CQuestObjectiveDefinition* pObjective = NULL;
  825. SCHEMA_INIT_SUBSTEP( GEconItemSchema().AddQuestObjective( &pObjective, pKVObj, pVecErrors ) );
  826. SCHEMA_INIT_CHECK( pObjective != NULL, "Could not create quest objective" );
  827. m_vecObjectiveDefinitions.AddToTail( (CTFQuestObjectiveDefinition*)pObjective );
  828. }
  829. }
  830. m_nNumObjectivesToRoll = (uint16)pKVItem->GetInt( "objectives_to_roll", 0 );
  831. SCHEMA_INIT_CHECK( m_nNumObjectivesToRoll >= 0, "Num objectives to roll is < 0!" );
  832. SCHEMA_INIT_CHECK( m_nNumObjectivesToRoll <= m_vecObjectiveDefinitions.Count(), "Num objectives to roll is greater than the number of objectives" );
  833. m_pszRewardLootlistName = pKVItem->GetString( "reward", NULL );
  834. SCHEMA_INIT_CHECK( m_pszRewardLootlistName != NULL, "No reward specified for quest!" );
  835. m_nMaxStandardPoints = pKVItem->GetInt( "max_standard_points" );
  836. m_nMaxBonusPoints = pKVItem->GetInt( "max_bonus_points" );
  837. m_pszQuestThemeName = pKVItem->GetString( "theme", NULL );
  838. SCHEMA_INIT_CHECK( m_pszQuestThemeName != NULL, "Invalid quest theme \"%s\"", m_pszQuestThemeName );
  839. m_pszCorrespondingOperationName = pKVItem->GetString( "operation", NULL );
  840. SCHEMA_INIT_CHECK( m_pszCorrespondingOperationName != NULL, "Quest missing \"operation\"!" );
  841. KeyValues* pKVSDescriptions = pKVItem->FindKey( "descriptions" );
  842. if ( pKVSDescriptions )
  843. {
  844. FOR_EACH_TRUE_SUBKEY( pKVSDescriptions, pKVDesc )
  845. {
  846. const char* pszDescToken = pKVDesc->GetString( "token", NULL );
  847. SCHEMA_INIT_CHECK( pszDescToken != NULL, "Description token not set!" );
  848. m_vecQuestDescriptions.AddToTail( pszDescToken );
  849. }
  850. }
  851. KeyValues* pKVNamesBlock = pKVItem->FindKey( "names" );
  852. if ( pKVNamesBlock )
  853. {
  854. FOR_EACH_TRUE_SUBKEY( pKVNamesBlock, pKVName )
  855. {
  856. const char* pszNameToken = pKVName->GetString( "token", NULL );
  857. SCHEMA_INIT_CHECK( pszNameToken != NULL, "Name token not set!" );
  858. m_vecQuestNames.AddToTail( pszNameToken );
  859. }
  860. }
  861. SCHEMA_INIT_CHECK( m_nMaxStandardPoints > 0, "Max standard points is <= 0!" );
  862. SCHEMA_INIT_CHECK( m_nMaxBonusPoints >= 0, "Max bonus points is < 0!" );
  863. m_pszQuickplayMapName = pKVItem->GetString( "quickplay_map" );
  864. m_strMatchmakingGroupName = pKVItem->GetString( "mm_group" );
  865. m_strMatchmakingCategoryName = pKVItem->GetString( "mm_category" );
  866. m_strMatchmakingMapName = pKVItem->GetString( "mm_map" );
  867. // loaner items for this quest
  868. m_vecRequiredItemSets.Purge();
  869. KeyValues* pKVRequiredItemsBlock = pKVItem->FindKey( "required_items" );
  870. if ( pKVRequiredItemsBlock )
  871. {
  872. FOR_EACH_TRUE_SUBKEY( pKVRequiredItemsBlock, pRequiredItem )
  873. {
  874. int iNewLoaner = m_vecRequiredItemSets.AddToTail();
  875. m_vecRequiredItemSets[ iNewLoaner ].BInitFromKV( pRequiredItem );
  876. SCHEMA_INIT_SUBSTEP( m_vecRequiredItemSets[ iNewLoaner ].BPostInit( pVecErrors ) );
  877. }
  878. }
  879. return SCHEMA_INIT_SUCCESS();
  880. }
  881. void CQuestDefinition::GetRolledObjectivesForItem( QuestObjectiveDefVec_t& vecRolledObjectives, const CEconItem* pItem ) const
  882. {
  883. // See if we need to roll some optional objectives, or if we just have all of them
  884. if ( m_nNumObjectivesToRoll > 0 )
  885. {
  886. QuestObjectiveDefVec_t vecAdvancedObjectives;
  887. QuestObjectiveDefVec_t vecOptionalObjectives;
  888. FOR_EACH_VEC( m_vecObjectiveDefinitions, i )
  889. {
  890. if ( m_vecObjectiveDefinitions[ i ]->IsAdvanced() )
  891. {
  892. vecAdvancedObjectives.AddToTail( m_vecObjectiveDefinitions[ i ] );
  893. }
  894. else if ( m_vecObjectiveDefinitions[ i ]->IsOptional() )
  895. {
  896. vecOptionalObjectives.AddToTail( m_vecObjectiveDefinitions[ i ] );
  897. }
  898. else
  899. {
  900. vecRolledObjectives.AddToTail( m_vecObjectiveDefinitions[ i ] );
  901. }
  902. }
  903. // Figure out how many to remove
  904. uint16 nNumToAdd = m_nNumObjectivesToRoll;
  905. CUniformRandomStream randomStream;
  906. // Don't use the global RNG for the shuffling
  907. // Seed with the original ID
  908. randomStream.SetSeed( pItem->GetOriginalID() );
  909. // You always get 1 advanced objective
  910. if ( vecAdvancedObjectives.Count() )
  911. {
  912. int nRandomIndex = randomStream.RandomInt( 0, vecAdvancedObjectives.Count() - 1 );
  913. vecRolledObjectives.AddToTail( vecAdvancedObjectives[ nRandomIndex ] );
  914. vecAdvancedObjectives.Remove( nRandomIndex );
  915. --nNumToAdd;
  916. }
  917. QuestObjectiveDefVec_t vecPossibleRolls;
  918. vecPossibleRolls.AddVectorToTail( vecAdvancedObjectives );
  919. vecPossibleRolls.AddVectorToTail( vecOptionalObjectives );
  920. // Roll from all rest of the optional objectives until we've got enough
  921. while( nNumToAdd && vecPossibleRolls.Count() )
  922. {
  923. int nRandomIndex = randomStream.RandomInt( 0, vecPossibleRolls.Count() - 1 );
  924. vecRolledObjectives.AddToTail( vecPossibleRolls[ nRandomIndex ] );
  925. vecPossibleRolls.Remove( nRandomIndex );
  926. --nNumToAdd;
  927. }
  928. }
  929. else
  930. {
  931. vecRolledObjectives.AddVectorToTail( m_vecObjectiveDefinitions );
  932. }
  933. }
  934. const char *CQuestDefinition::GetRolledDescriptionForItem( const CEconItem* pItem ) const
  935. {
  936. if ( m_vecQuestDescriptions.Count() )
  937. {
  938. // Don't use the global RNG for the shuffling
  939. CUniformRandomStream randomStream;
  940. randomStream.SetSeed( pItem->GetOriginalID() );
  941. return m_vecQuestDescriptions[ randomStream.RandomInt( 0, m_vecQuestDescriptions.Count() - 1 ) ];
  942. }
  943. Assert( 0 );
  944. return NULL;
  945. }
  946. const char *CQuestDefinition::GetRolledNameForItem( const CEconItem* pItem ) const
  947. {
  948. if ( m_vecQuestNames.Count() )
  949. {
  950. // Don't use the global RNG for the shuffling
  951. CUniformRandomStream randomStream;
  952. randomStream.SetSeed( pItem->GetOriginalID() );
  953. return m_vecQuestNames[ randomStream.RandomInt( 0, m_vecQuestNames.Count() - 1 ) ];
  954. }
  955. Assert( 0 );
  956. return NULL;
  957. }
  958. const CQuestThemeDefinition *CQuestDefinition::GetQuestTheme() const
  959. {
  960. return GetItemSchema()->GetQuestThemeByName( m_pszQuestThemeName );
  961. }
  962. //-----------------------------------------------------------------------------
  963. // Purpose:
  964. //-----------------------------------------------------------------------------
  965. void CTFItemDefinition::InternalInitialize()
  966. {
  967. m_eEquipType = EQUIP_TYPE_INVALID;
  968. m_iDefaultLoadoutSlot = LOADOUT_POSITION_INVALID;
  969. m_iAnimationSlot = -1;
  970. m_vbClassUsability.ClearAll();
  971. for ( int i = 0; i < ARRAYSIZE( m_iLoadoutSlots ); i++ )
  972. {
  973. m_iLoadoutSlots[i] = LOADOUT_POSITION_INVALID;
  974. m_pszPlayerDisplayModel[i] = NULL;
  975. m_pszPlayerDisplayModelAlt[i] = NULL;
  976. }
  977. m_pTauntData = NULL;
  978. m_pQuestData = NULL;
  979. #ifndef GC_DLL
  980. m_pszAdText = NULL;
  981. m_pszAdResFile = NULL;
  982. #endif // GC_DLL
  983. #ifdef CLIENT_DLL
  984. m_bHasDetailedIcon = false;
  985. #endif // CLIENT_DLL
  986. }
  987. #include "filesystem.h"
  988. //-----------------------------------------------------------------------------
  989. // Purpose:
  990. //-----------------------------------------------------------------------------
  991. bool CTFItemDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
  992. {
  993. CEconItemDefinition::BInitFromKV( pKVItem, pVecErrors );
  994. // Our superclass should initialize our raw definition, including any prefab work.
  995. KeyValues *pKVInitValues = GetRawDefinition();
  996. Assert( pKVInitValues );
  997. // Reset default properties.
  998. InternalInitialize();
  999. CUtlDict< EEquipType_t > dictEquipType;
  1000. dictEquipType.Insert( "account", EQUIP_TYPE_ACCOUNT );
  1001. dictEquipType.Insert( "class", EQUIP_TYPE_CLASS );
  1002. // Default to class equip type
  1003. const char *pszEquipType = pKVInitValues->GetString( "equip_type", "class" );
  1004. auto idx = dictEquipType.Find( pszEquipType );
  1005. if ( idx != dictEquipType.InvalidIndex() )
  1006. {
  1007. m_eEquipType = dictEquipType[ idx ];
  1008. }
  1009. SCHEMA_INIT_CHECK( m_eEquipType != EQUIP_TYPE_INVALID,
  1010. "Item definition %i \"%s\" used uknown equip type: %s!", GetDefinitionIndex(), GetItemBaseName(), pszEquipType );
  1011. // Get the default loadout slot
  1012. const char *pszLoadoutSlot = pKVInitValues->GetString("item_slot", "");
  1013. if ( *pszLoadoutSlot )
  1014. {
  1015. if ( !V_strcmp( pszLoadoutSlot, "head" ) )
  1016. {
  1017. pszLoadoutSlot = "misc";
  1018. }
  1019. m_iDefaultLoadoutSlot = StringFieldToInt( pszLoadoutSlot, GetItemSchema()->GetLoadoutStrings( m_eEquipType ), true );
  1020. SCHEMA_INIT_CHECK(
  1021. m_iDefaultLoadoutSlot >= 0,
  1022. "Item definition %i \"%s\" used unknown loadout slot: %s!", GetDefinitionIndex(), GetItemBaseName(), pszLoadoutSlot );
  1023. }
  1024. // Class usability--use our copy of kv item
  1025. KeyValues *pClasses = pKVInitValues->FindKey( "used_by_classes" );
  1026. if ( pClasses )
  1027. {
  1028. KeyValues *pKVClass = pClasses->GetFirstSubKey();
  1029. while ( pKVClass )
  1030. {
  1031. int iClass = StringFieldToInt( pKVClass->GetName(), GetItemSchema()->GetClassUsabilityStrings() );
  1032. if ( iClass > -1 )
  1033. {
  1034. m_vbClassUsability.Set(iClass);
  1035. m_iLoadoutSlots[iClass] = m_iDefaultLoadoutSlot;
  1036. // If the value is "1", the class uses this item in the default loadout slot.
  1037. const char *pszValue = pKVClass->GetString();
  1038. if ( pszValue[0] != '1' )
  1039. {
  1040. int iSlot = StringFieldToInt( pszValue, GetItemSchema()->GetLoadoutStrings( EQUIP_TYPE_CLASS ) );
  1041. Assert( iSlot != -1 );
  1042. if ( iSlot != -1 )
  1043. {
  1044. m_iLoadoutSlots[iClass] = iSlot;
  1045. }
  1046. }
  1047. }
  1048. pKVClass = pKVClass->GetNextKey();
  1049. }
  1050. // add "all_class" if applicable
  1051. if ( CanBeUsedByAllClasses() )
  1052. {
  1053. KeyValues *pKVAllClassKey = new KeyValues( "all_class", "all_class", "1" );
  1054. pClasses->AddSubKey( pKVAllClassKey );
  1055. }
  1056. }
  1057. // Verify that no items are set up to be equipped in a wearable slot for some classes and a
  1058. // non-wearable slot other times. "Is this in a wearable slot?" is used to determine whether
  1059. // or not content can be allowed to stream, so we don't allow an item to overlap.
  1060. bool bHasAnyWearableSlots = false,
  1061. bHasAnyNonwearableSlots = false;
  1062. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  1063. {
  1064. if ( m_iLoadoutSlots[i] != LOADOUT_POSITION_INVALID )
  1065. {
  1066. const bool bThisIsWearableSlot = IsWearableSlot( m_iLoadoutSlots[i] );
  1067. (bThisIsWearableSlot ? bHasAnyWearableSlots : bHasAnyNonwearableSlots) = true;
  1068. }
  1069. }
  1070. SCHEMA_INIT_CHECK(
  1071. !(bHasAnyWearableSlots && bHasAnyNonwearableSlots),
  1072. "Item definition %i \"%s\" used in both wearable and not wearable slots!", GetDefinitionIndex(), GetItemBaseName() );
  1073. // "anim_slot"
  1074. const char *pszAnimSlot = pKVInitValues->GetString("anim_slot");
  1075. if ( pszAnimSlot && pszAnimSlot[0] )
  1076. {
  1077. if ( Q_stricmp(pszAnimSlot, "FORCE_NOT_USED") == 0 )
  1078. {
  1079. m_iAnimationSlot = -2;
  1080. }
  1081. else
  1082. {
  1083. m_iAnimationSlot = StringFieldToInt( pszAnimSlot, GetItemSchema()->GetWeaponTypeSubstrings() );
  1084. }
  1085. }
  1086. InitPerClassStringArray( pKVInitValues->FindKey( "model_player_per_class" ), m_pszPlayerDisplayModel );
  1087. InitPerClassStringArray( pKVInitValues->FindKey( "model_player_per_class_alt" ), m_pszPlayerDisplayModelAlt );
  1088. #if defined DEBUG && defined CLIENT_DLL
  1089. for ( int i = 0; i < m_vbClassUsability.GetNumBits(); i++ )
  1090. {
  1091. if ( m_vbClassUsability[i] && m_pszPlayerDisplayModel[ i ] )
  1092. {
  1093. // SCHEMA_INIT_CHECK( g_pFullFileSystem->FileExists( m_pszPlayerDisplayModel[ i ] ), "Missing model %s specified in model_player_per_class in item %s", m_pszPlayerDisplayModel[ i ], GetItemBaseName() );
  1094. }
  1095. }
  1096. #endif
  1097. KeyValues *pTauntKV = pKVInitValues->FindKey( "taunt" );
  1098. if ( pTauntKV )
  1099. {
  1100. Assert( !m_pTauntData );
  1101. m_pTauntData = new CTFTauntInfo();
  1102. SCHEMA_INIT_CHECK(
  1103. m_pTauntData->BInitFromKV( pTauntKV, pVecErrors ),
  1104. "Item definition %i \"%s\" failed to initialize taunt data!", GetDefinitionIndex(), GetItemBaseName()
  1105. );
  1106. }
  1107. // Init quest data if we have any
  1108. KeyValues *pQuestKV = pKVInitValues->FindKey( "quest" );
  1109. if ( pQuestKV )
  1110. {
  1111. Assert( !m_pQuestData );
  1112. m_pQuestData = new CQuestDefinition();
  1113. SCHEMA_INIT_CHECK( m_pQuestData->BInitFromKV( pQuestKV, pVecErrors ), "Item def %i \"%s\" failed to initialize quest data!", GetDefinitionIndex(), GetItemBaseName() );
  1114. }
  1115. // Stomp duplicate properties.
  1116. if ( !m_pszPlayerDisplayModel[0] )
  1117. {
  1118. m_pszPlayerDisplayModel[0] = GetBasePlayerDisplayModel();
  1119. }
  1120. // Auto-generated tags based on slot/class.
  1121. m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( CFmtStr( "auto__slot_%s", pszLoadoutSlot ).Get() ) );
  1122. for ( int i = 0; i < m_vbClassUsability.GetNumBits(); i++ )
  1123. {
  1124. if ( m_vbClassUsability[i] )
  1125. {
  1126. m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( CFmtStr( "auto__class_%s", GetItemSchema()->GetClassUsabilityStrings()[i] ).Get() ) );
  1127. }
  1128. }
  1129. #ifndef GC_DLL
  1130. m_pszAdText = pKVInitValues->GetString( "ad_text", NULL );
  1131. m_pszAdResFile = pKVInitValues->GetString( "ad_res_file", "Resource/UI/econ/ItemAdDefault.res" );
  1132. #endif
  1133. const char * pszPaintKit = pKVInitValues->GetString( "item_paintkit", NULL );
  1134. if ( pszPaintKit )
  1135. {
  1136. int iPaintIndex = GetItemSchema()->GetItemPaintKits().Find( pszPaintKit );
  1137. SCHEMA_INIT_CHECK(
  1138. GetItemSchema()->GetItemPaintKits().IsValidIndex( iPaintIndex ),
  1139. "Item paintkit [%s] in definition %i \"%s\" does not exist", pszPaintKit, GetDefinitionIndex(), GetItemBaseName()
  1140. );
  1141. SetItemPaintKitDefinition( GetItemSchema()->GetItemPaintKits()[iPaintIndex] );
  1142. }
  1143. #ifdef CLIENT_DLL
  1144. m_bHasDetailedIcon = pKVInitValues->GetBool( "has_detailed_icon" );
  1145. #endif // CLIENT_DLL
  1146. return SCHEMA_INIT_SUCCESS();
  1147. }
  1148. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  1149. //-----------------------------------------------------------------------------
  1150. // Purpose:
  1151. //-----------------------------------------------------------------------------
  1152. bool CTFItemDefinition::BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CUtlVector<CUtlString>* pVecErrors )
  1153. {
  1154. if ( !CEconItemDefinition::BInitFromTestItemKVs( iNewDefIndex, pKVItem, pVecErrors ) )
  1155. return false;
  1156. // Use the tester's class usage choices, even when testing existing items
  1157. m_vbClassUsability.ClearAll();
  1158. int iClassUsage = pKVItem->GetInt( "class_usage", 0 );
  1159. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  1160. {
  1161. if ( iClassUsage & (1 << i) || (iClassUsage & 1) )
  1162. {
  1163. m_vbClassUsability.Set(i);
  1164. m_iLoadoutSlots[i] = m_iDefaultLoadoutSlot;
  1165. }
  1166. }
  1167. // Initialize player display model.
  1168. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  1169. {
  1170. m_pszPlayerDisplayModel[i] = NULL;
  1171. m_pszPlayerDisplayModelAlt[i] = NULL;
  1172. }
  1173. InitPerClassStringArray( pKVItem->FindKey( "model_player_per_class" ), m_pszPlayerDisplayModel );
  1174. InitPerClassStringArray( pKVItem->FindKey( "model_player_per_class_alt" ), m_pszPlayerDisplayModelAlt );
  1175. KeyValues *pTauntKV = pKVItem->FindKey( "taunt" );
  1176. if ( pTauntKV )
  1177. {
  1178. Assert( !m_pTauntData );
  1179. m_pTauntData = new CTFTauntInfo();
  1180. if ( !m_pTauntData->BInitFromKV( pTauntKV, pVecErrors ) )
  1181. return false;
  1182. }
  1183. // Stomp duplicate properties.
  1184. if ( !m_pszPlayerDisplayModel[0] )
  1185. {
  1186. m_pszPlayerDisplayModel[0] = GetBasePlayerDisplayModel();
  1187. }
  1188. return true;
  1189. }
  1190. //-----------------------------------------------------------------------------
  1191. // Purpose:
  1192. //-----------------------------------------------------------------------------
  1193. void CTFItemDefinition::CopyPolymorphic( const CEconItemDefinition *pSourceDef )
  1194. {
  1195. Assert( dynamic_cast<const CTFItemDefinition *>( pSourceDef ) != NULL );
  1196. *this = *(const CTFItemDefinition *)pSourceDef;
  1197. }
  1198. #endif // defined(CLIENT_DLL) || defined(GAME_DLL)
  1199. //-----------------------------------------------------------------------------
  1200. // Purpose:
  1201. //-----------------------------------------------------------------------------
  1202. void CTFStyleInfo::BInitFromKV( KeyValues *pKVStyle, CUtlVector<CUtlString> *pVecErrors )
  1203. {
  1204. Assert( pKVStyle );
  1205. m_iSkins[ TF_TEAM_RED ] = pKVStyle->GetInt( "skin_red", 0 );
  1206. m_iSkins[ TF_TEAM_BLUE ] = pKVStyle->GetInt( "skin_blu", 0 );
  1207. m_iViewmodelSkins[ TF_TEAM_RED ] = pKVStyle->GetInt( "v_skin_red", -1 );
  1208. m_iViewmodelSkins[ TF_TEAM_BLUE ] = pKVStyle->GetInt( "v_skin_blu", -1 );
  1209. const char *pszPlayerModel = pKVStyle->GetString( "model_player", NULL );
  1210. if ( pszPlayerModel )
  1211. {
  1212. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  1213. {
  1214. m_pszPlayerDisplayModel[0][i] = pszPlayerModel;
  1215. }
  1216. }
  1217. else
  1218. {
  1219. InitPerClassStringArray( pKVStyle->FindKey( "model_player_per_class" ), m_pszPlayerDisplayModel[0] );
  1220. InitPerClassStringArray( pKVStyle->FindKey( "model_player_per_class_red" ), m_pszPlayerDisplayModel[0] );
  1221. InitPerClassStringArray( pKVStyle->FindKey( "model_player_per_class_blue" ), m_pszPlayerDisplayModel[1] );
  1222. }
  1223. CEconStyleInfo::BInitFromKV( pKVStyle, pVecErrors );
  1224. }
  1225. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  1226. //-----------------------------------------------------------------------------
  1227. // Purpose:
  1228. //-----------------------------------------------------------------------------
  1229. void CTFItemDefinition::GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const
  1230. {
  1231. Assert( out_pVecModelStrings );
  1232. // Is this definition supposed to use dynamic-loaded content or precache it?
  1233. if ( !bDynamicLoad || !IsContentStreamable() )
  1234. {
  1235. // Parent class base meshes, if relevant.
  1236. CEconItemDefinition::GeneratePrecacheModelStrings( bDynamicLoad, out_pVecModelStrings );
  1237. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  1238. {
  1239. // Per-class models.
  1240. const char *pszModel = GetPlayerDisplayModel(i);
  1241. if ( pszModel && pszModel[0] )
  1242. {
  1243. out_pVecModelStrings->AddToTail( pszModel );
  1244. }
  1245. // Per-class alt-models
  1246. const char *pszModelAlt = GetPlayerDisplayModelAlt(i);
  1247. if ( pszModelAlt && pszModelAlt[0] )
  1248. {
  1249. out_pVecModelStrings->AddToTail( pszModelAlt );
  1250. }
  1251. // Per-class custom taunt prop
  1252. if ( GetTauntData() )
  1253. {
  1254. const char *pszCustomTauntProp = GetTauntData()->GetProp(i);
  1255. if ( pszCustomTauntProp && pszCustomTauntProp[0] )
  1256. {
  1257. out_pVecModelStrings->AddToTail( pszCustomTauntProp );
  1258. }
  1259. }
  1260. }
  1261. const char *pszModel = GetWorldDisplayModel();
  1262. if ( pszModel && pszModel[0] )
  1263. {
  1264. out_pVecModelStrings->AddToTail( pszModel );
  1265. }
  1266. }
  1267. }
  1268. //-----------------------------------------------------------------------------
  1269. // Purpose:
  1270. //-----------------------------------------------------------------------------
  1271. void CTFStyleInfo::GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const
  1272. {
  1273. Assert( out_pVecModelStrings );
  1274. for ( int i = 0; i < ARRAYSIZE( m_pszPlayerDisplayModel ); i++ )
  1275. {
  1276. for ( int j = 0; j < ARRAYSIZE( m_pszPlayerDisplayModel[i] ); j++ )
  1277. {
  1278. const char* pszModelName = m_pszPlayerDisplayModel[i][j];
  1279. if ( pszModelName && *pszModelName )
  1280. {
  1281. out_pVecModelStrings->AddToTail( pszModelName );
  1282. }
  1283. }
  1284. }
  1285. CEconStyleInfo::GeneratePrecacheModelStringsForStyle( out_pVecModelStrings );
  1286. }
  1287. #endif // defined(CLIENT_DLL) || defined(GAME_DLL)
  1288. //-----------------------------------------------------------------------------
  1289. // Purpose:
  1290. //-----------------------------------------------------------------------------
  1291. const char *CTFStyleInfo::GetPlayerDisplayModel( int iClass, int iTeam ) const
  1292. {
  1293. Assert( iClass >= 0 );
  1294. Assert( iClass < ARRAYSIZE( m_pszPlayerDisplayModel[0] ) );
  1295. const char *pszBlueModel = m_pszPlayerDisplayModel[1][iClass];
  1296. if ( iTeam == TF_TEAM_BLUE && pszBlueModel && *pszBlueModel )
  1297. {
  1298. return pszBlueModel;
  1299. }
  1300. // always return red team as default
  1301. return m_pszPlayerDisplayModel[0][iClass];
  1302. }
  1303. //-----------------------------------------------------------------------------
  1304. // Purpose: Return the load-out slot that this item must be placed into
  1305. //-----------------------------------------------------------------------------
  1306. int CTFItemDefinition::GetLoadoutSlot( int iLoadoutClass ) const
  1307. {
  1308. if ( iLoadoutClass == GEconItemSchema().GetAccountIndex() )
  1309. {
  1310. return GetAccountLoadoutSlot();
  1311. }
  1312. if ( iLoadoutClass <= 0 || iLoadoutClass >= LOADOUT_COUNT )
  1313. return m_iDefaultLoadoutSlot;
  1314. return m_iLoadoutSlots[iLoadoutClass];
  1315. }
  1316. #ifndef GC_DLL
  1317. //-----------------------------------------------------------------------------
  1318. // Purpose: Returns true if this item is in a wearable slot, or is acting as a wearable
  1319. //-----------------------------------------------------------------------------
  1320. bool CTFItemDefinition::IsAWearable() const
  1321. {
  1322. if ( IsWearableSlot( GetDefaultLoadoutSlot() ) && !IsActingAsAWeapon() )
  1323. return true;
  1324. if ( IsActingAsAWearable() )
  1325. return true;
  1326. return false;
  1327. }
  1328. //-----------------------------------------------------------------------------
  1329. // Purpose: Returns true if the content for this item view should be streamed. If false,
  1330. // it should be preloaded.
  1331. //-----------------------------------------------------------------------------
  1332. ConVar item_enable_content_streaming( "item_enable_content_streaming", "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED );
  1333. bool CTFItemDefinition::IsContentStreamable() const
  1334. {
  1335. #if defined( WITH_STREAMABLE_WEAPONS )
  1336. extern ConVar tf_loadondemand_default;
  1337. // If we support streamable weapons and loadondemand_default is true, then we do not want to restrict demand loading
  1338. // to wearables only, so skip that check.
  1339. if (!tf_loadondemand_default.GetBool())
  1340. #endif
  1341. {
  1342. if (!IsAWearable())
  1343. return false;
  1344. }
  1345. return item_enable_content_streaming.GetBool()
  1346. && CEconItemDefinition::IsContentStreamable();
  1347. }
  1348. #endif // !GC_DLL
  1349. //-----------------------------------------------------------------------------
  1350. // Purpose:
  1351. //-----------------------------------------------------------------------------
  1352. void CTFItemDefinition::FilloutSlotUsage( CBitVec<LOADOUT_COUNT> *pBV ) const
  1353. {
  1354. pBV->ClearAll();
  1355. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  1356. {
  1357. if ( m_iLoadoutSlots[i] == LOADOUT_POSITION_INVALID )
  1358. continue;
  1359. pBV->Set( m_iLoadoutSlots[i] );
  1360. }
  1361. }
  1362. //-----------------------------------------------------------------------------
  1363. // Purpose:
  1364. //-----------------------------------------------------------------------------
  1365. bool CTFItemDefinition::CanBeUsedByAllClasses( void ) const
  1366. {
  1367. // Right now, Civilian isn't a real class, so we only have 9 classes in this check
  1368. for ( int iClass = 1; iClass < (LOADOUT_COUNT-1); iClass++ )
  1369. {
  1370. if ( !CanBeUsedByClass(iClass) )
  1371. return false;
  1372. }
  1373. return true;
  1374. }
  1375. //-----------------------------------------------------------------------------
  1376. // Purpose:
  1377. //-----------------------------------------------------------------------------
  1378. bool CTFItemDefinition::CanBePlacedInSlot( int nSlot ) const
  1379. {
  1380. for ( int i = 0; i < LOADOUT_COUNT; i++ )
  1381. {
  1382. if ( m_iLoadoutSlots[i] == nSlot )
  1383. return true;
  1384. }
  1385. return false;
  1386. }
  1387. //-----------------------------------------------------------------------------
  1388. KeyValues *CTFItemDefinition::GetPaintKitWearDefinition( int nWear ) const
  1389. {
  1390. CEconItemPaintKitDefinition *pPaintKit = GetCustomPainkKitDefinition();
  1391. if ( pPaintKit )
  1392. {
  1393. return pPaintKit->GetPaintKitWearKV( nWear );
  1394. }
  1395. return NULL;
  1396. }
  1397. //-----------------------------------------------------------------------------
  1398. const char *CTFItemDefinition::GetPaintKitName() const
  1399. {
  1400. CEconItemPaintKitDefinition *pPaintKit = GetCustomPainkKitDefinition();
  1401. if ( pPaintKit )
  1402. {
  1403. return pPaintKit->GetName( );
  1404. }
  1405. return NULL;
  1406. }
  1407. //-----------------------------------------------------------------------------
  1408. // Purpose:
  1409. //-----------------------------------------------------------------------------
  1410. bool CTFRequiredQuestItemsSet::BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  1411. {
  1412. KeyValues* pKVQualifyingItems = pKV->FindKey( "qualifying_items" );
  1413. if ( pKVQualifyingItems )
  1414. {
  1415. FOR_EACH_TRUE_SUBKEY( pKVQualifyingItems, pItem )
  1416. {
  1417. m_vecQualifyingItemDefs.AddToTail( pItem->GetInt( "defindex", INVALID_ITEM_DEF_INDEX ) );
  1418. }
  1419. }
  1420. m_LoanerItemDef = pKV->GetInt( "loaner_defindex", INVALID_ITEM_DEF_INDEX );
  1421. return SCHEMA_INIT_SUCCESS();
  1422. }
  1423. //-----------------------------------------------------------------------------
  1424. // Purpose: Make sure all the defindexes point to actual item defs
  1425. //-----------------------------------------------------------------------------
  1426. bool CTFRequiredQuestItemsSet::BPostInit( CUtlVector<CUtlString> *pVecErrors )
  1427. {
  1428. // Verify all of the item defindex
  1429. FOR_EACH_VEC( m_vecQualifyingItemDefs, i )
  1430. {
  1431. const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( m_vecQualifyingItemDefs[ i ] );
  1432. SCHEMA_INIT_CHECK( pItemDef != NULL, "No item definition for defindex %d!", m_vecQualifyingItemDefs[ i ] );
  1433. }
  1434. const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( m_LoanerItemDef );
  1435. SCHEMA_INIT_CHECK( pItemDef != NULL, "No item definition for defindex %d!", m_LoanerItemDef );
  1436. return SCHEMA_INIT_SUCCESS();
  1437. }
  1438. //-----------------------------------------------------------------------------
  1439. // Purpose: Given a vector of item defs, check if it contains ANY of our qualifying items
  1440. //-----------------------------------------------------------------------------
  1441. bool CTFRequiredQuestItemsSet::OwnsRequiredItems( const CUtlVector< item_definition_index_t >& vecOwnedItemDefs ) const
  1442. {
  1443. FOR_EACH_VEC( vecOwnedItemDefs, i )
  1444. {
  1445. FOR_EACH_VEC( m_vecQualifyingItemDefs, j )
  1446. {
  1447. if ( vecOwnedItemDefs[ i ] == m_vecQualifyingItemDefs[ j ] )
  1448. return true;
  1449. }
  1450. }
  1451. return false;
  1452. }
  1453. //-----------------------------------------------------------------------------
  1454. // Purpose:
  1455. //-----------------------------------------------------------------------------
  1456. CTFQuestObjectiveConditionsDefinition::CTFQuestObjectiveConditionsDefinition( void )
  1457. : m_nDefIndex( INVALID_QUEST_OBJECTIVE_CONDITIONS_INDEX )
  1458. #ifndef GC_DLL
  1459. , m_pConditionsKey( NULL )
  1460. #endif
  1461. {}
  1462. //-----------------------------------------------------------------------------
  1463. // Purpose:
  1464. //-----------------------------------------------------------------------------
  1465. CTFQuestObjectiveConditionsDefinition::~CTFQuestObjectiveConditionsDefinition( void )
  1466. {}
  1467. //-----------------------------------------------------------------------------
  1468. // Purpose:
  1469. //-----------------------------------------------------------------------------
  1470. bool CTFQuestObjectiveConditionsDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
  1471. {
  1472. m_nDefIndex = atoi( pKVItem->GetName() );
  1473. SCHEMA_INIT_CHECK( m_nDefIndex != INVALID_QUEST_OBJECTIVE_CONDITIONS_INDEX, "Invalid quest objective conditions def index!" );
  1474. m_vecRequiredItemSets.Purge();
  1475. KeyValues* pKVRequiredItemsBlock = pKVItem->FindKey( "required_items" );
  1476. if ( pKVRequiredItemsBlock )
  1477. {
  1478. FOR_EACH_TRUE_SUBKEY( pKVRequiredItemsBlock, pRequiredItem )
  1479. {
  1480. m_vecRequiredItemSets[ m_vecRequiredItemSets.AddToTail() ].BInitFromKV( pRequiredItem );
  1481. }
  1482. }
  1483. #ifndef GC_DLL
  1484. m_pConditionsKey = pKVItem->FindKey( "condition_logic" );
  1485. SCHEMA_INIT_CHECK( m_pConditionsKey != NULL, "Missing conditions block for condition def %d!", m_nDefIndex );
  1486. // Conditions don't get created until needed on the server, so let's create them right now
  1487. // as a test to make sure they're valid and fail early rather than later.
  1488. CTFQuestCondition *pTempConditions = NULL;
  1489. const char *pszType = m_pConditionsKey->GetString( "type" );
  1490. pTempConditions = CreateEvaluatorByName( pszType, NULL );
  1491. SCHEMA_INIT_CHECK( pTempConditions != NULL, "Failed to create evaluators" );
  1492. if ( !pTempConditions->BInitFromKV( m_pConditionsKey, pVecErrors ) )
  1493. {
  1494. delete pTempConditions;
  1495. SCHEMA_INIT_CHECK( false, "Failed to init conditions" );
  1496. }
  1497. if ( pTempConditions && pKVItem->GetBool( "spew" ) )
  1498. {
  1499. pTempConditions->PrintDebugText();
  1500. DevMsg( "\n" );
  1501. }
  1502. // clean up after test parsing quest conditions
  1503. delete pTempConditions;
  1504. #endif
  1505. return SCHEMA_INIT_SUCCESS();
  1506. }
  1507. //-----------------------------------------------------------------------------
  1508. // Purpose:
  1509. //-----------------------------------------------------------------------------
  1510. bool CTFQuestObjectiveConditionsDefinition::BPostInit( CUtlVector<CUtlString> *pVecErrors )
  1511. {
  1512. // Verify all of the item defindex
  1513. FOR_EACH_VEC( m_vecRequiredItemSets, i )
  1514. {
  1515. SCHEMA_INIT_SUBSTEP( m_vecRequiredItemSets[i].BPostInit( pVecErrors ) );
  1516. }
  1517. return SCHEMA_INIT_SUCCESS();
  1518. }
  1519. //-----------------------------------------------------------------------------
  1520. // Purpose:
  1521. //-----------------------------------------------------------------------------
  1522. CTFQuestObjectiveDefinition::CTFQuestObjectiveDefinition( void )
  1523. {
  1524. }
  1525. //-----------------------------------------------------------------------------
  1526. // Purpose:
  1527. //-----------------------------------------------------------------------------
  1528. CTFQuestObjectiveDefinition::~CTFQuestObjectiveDefinition()
  1529. {
  1530. }
  1531. //-----------------------------------------------------------------------------
  1532. // Purpose: Init our restrictions
  1533. //-----------------------------------------------------------------------------
  1534. bool CTFQuestObjectiveDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  1535. {
  1536. if ( !CQuestObjectiveDefinition::BInitFromKV( pKVItem, pVecErrors ) )
  1537. return false;
  1538. m_nConditionDefIndex = pKVItem->GetInt( "conditions_def_index", INVALID_QUEST_OBJECTIVE_CONDITIONS_INDEX );
  1539. SCHEMA_INIT_CHECK( GetItemSchema()->GetQuestObjectiveConditionByDefIndex( m_nConditionDefIndex ) != NULL, "Could not find quest objective conditions for defindex %d!", m_nConditionDefIndex );
  1540. return SCHEMA_INIT_SUCCESS();
  1541. }
  1542. const CTFQuestObjectiveConditionsDefinition* CTFQuestObjectiveDefinition::GetConditions() const
  1543. {
  1544. return GetItemSchema()->GetQuestObjectiveConditionByDefIndex( m_nConditionDefIndex );
  1545. }
  1546. #ifndef GC_DLL
  1547. KeyValues *CTFQuestObjectiveDefinition::GetConditionsKeyValues() const
  1548. {
  1549. const CTFQuestObjectiveConditionsDefinition* pDef = GetItemSchema()->GetQuestObjectiveConditionByDefIndex( m_nConditionDefIndex );
  1550. if ( pDef )
  1551. {
  1552. return pDef->GetKeyValues();
  1553. }
  1554. return NULL;
  1555. }
  1556. #endif
  1557. //-----------------------------------------------------------------------------
  1558. // Purpose:
  1559. //-----------------------------------------------------------------------------
  1560. // Used to convert strings to ints for class usability
  1561. struct PlayerClassInfo_t
  1562. {
  1563. const char *m_pchName;
  1564. const char *m_pchLocalizationKey;
  1565. };
  1566. static PlayerClassInfo_t gs_PlayerClassData[] =
  1567. {
  1568. { "Undefined", "#TF_Class_Name_Undefined" },
  1569. { "Scout", "#TF_Class_Name_Scout" },
  1570. { "Sniper", "#TF_Class_Name_Sniper" },
  1571. { "Soldier", "#TF_Class_Name_Soldier" },
  1572. { "Demoman", "#TF_Class_Name_Demoman" },
  1573. { "Medic", "#TF_Class_Name_Medic" },
  1574. { "Heavy", "#TF_Class_Name_HWGuy" },
  1575. { "Pyro", "#TF_Class_Name_Pyro" },
  1576. { "Spy", "#TF_Class_Name_Spy" },
  1577. { "Engineer", "#TF_Class_Name_Engineer" },
  1578. { "Invalid", "" } // lots of code loops over these classes based on LOADOUT_COUNT, which is wrong, but this allows them to do it safely
  1579. };
  1580. bool BIsPlayerClassValid( int iClass )
  1581. {
  1582. return iClass >= 0 && iClass < ARRAYSIZE( gs_PlayerClassData );
  1583. }
  1584. const char *GetPlayerClassName( int iClass )
  1585. {
  1586. if ( !BIsPlayerClassValid( iClass ) )
  1587. return NULL;
  1588. return gs_PlayerClassData[ iClass ].m_pchName;
  1589. }
  1590. const char *GetPlayerClassLocalizationKey( int iClass )
  1591. {
  1592. if ( !BIsPlayerClassValid( iClass ) )
  1593. return NULL;
  1594. return gs_PlayerClassData[ iClass ].m_pchLocalizationKey;
  1595. }
  1596. itemid_t GetAssociatedQuestItemID( const IEconItemInterface *pEconItem )
  1597. {
  1598. static CSchemaAttributeDefHandle pLoanerIDLowAttrib( "quest loaner id low" );
  1599. static CSchemaAttributeDefHandle pLoanerIDHiAttrib( "quest loaner id hi" );
  1600. if ( !pLoanerIDLowAttrib || !pLoanerIDHiAttrib )
  1601. return INVALID_ITEM_ID;
  1602. itemid_t questItemID = INVALID_ITEM_ID;
  1603. uint32 nLow, nHi;
  1604. if ( pEconItem->FindAttribute( pLoanerIDLowAttrib, &nLow ) && pEconItem->FindAttribute( pLoanerIDHiAttrib, &nHi ) )
  1605. {
  1606. // Reconstruct the itemID
  1607. itemid_t nIDLow = 0x00000000FFFFFFFF & (itemid_t)nLow;
  1608. itemid_t nIDHi = 0xFFFFFFFF00000000 & (itemid_t)nHi << 32;
  1609. questItemID = nIDLow | nIDHi;
  1610. }
  1611. return questItemID;
  1612. }
  1613. // Loadout positions
  1614. const char *g_szLoadoutStrings[] =
  1615. {
  1616. // Weapons & Equipment
  1617. "primary", // LOADOUT_POSITION_PRIMARY = 0,
  1618. "secondary", // LOADOUT_POSITION_SECONDARY,
  1619. "melee", // LOADOUT_POSITION_MELEE,
  1620. "utility", // LOADOUT_POSITION_UTILITY,
  1621. "building", // LOADOUT_POSITION_BUILDING,
  1622. "pda", // LOADOUT_POSITION_PDA,
  1623. "pda2", // LOADOUT_POSITION_PDA2,
  1624. // Wearables
  1625. "head", // LOADOUT_POSITION_HEAD,
  1626. "misc", // LOADOUT_POSITION_MISC,
  1627. "action", // LOADOUT_POSITION_ACTION,
  1628. "", // LOADOUT_POSITION_MISC2
  1629. "taunt", // LOADOUT_POSITION_TAUNT
  1630. "", // LOADOUT_POSITION_TAUNT2
  1631. "", // LOADOUT_POSITION_TAUNT3
  1632. "", // LOADOUT_POSITION_TAUNT4
  1633. "", // LOADOUT_POSITION_TAUNT5
  1634. "", // LOADOUT_POSITION_TAUNT6
  1635. "", // LOADOUT_POSITION_TAUNT7
  1636. "", // LOADOUT_POSITION_TAUNT8
  1637. #ifdef STAGING_ONLY
  1638. "dispenser", // LOADOUT_POSITION_PDA_ADDON1
  1639. "teleporter", // LOADOUT_POSITION_PDA_ADDON2
  1640. "pda3", // LOADOUT_POSITION_PDA3,
  1641. //"", // LOADOUT_POSITION_MISC3
  1642. //"", // LOADOUT_POSITION_MISC4
  1643. //"", // LOADOUT_POSITION_MISC5
  1644. //"", // LOADOUT_POSITION_MISC6
  1645. //"", // LOADOUT_POSITION_MISC7
  1646. //"", // LOADOUT_POSITION_MISC8
  1647. //"", // LOADOUT_POSITION_MISC9
  1648. //"", // LOADOUT_POSITION_MISC10
  1649. "", // LOADOUT_POSITION_BUILDING2,
  1650. #endif // STAGING_ONLY
  1651. };
  1652. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szLoadoutStrings ) <= CLASS_LOADOUT_POSITION_COUNT ); // we don't support mapping directly to slots like "misc2", "taunt2-8", etc.
  1653. // Loadout positions used to display loadout slots to players (localized)
  1654. const char *g_szLoadoutStringsForDisplay[] =
  1655. {
  1656. // Weapons & Equipment
  1657. "#LoadoutSlot_Primary", // LOADOUT_POSITION_PRIMARY = 0,
  1658. "#LoadoutSlot_Secondary", // LOADOUT_POSITION_SECONDARY,
  1659. "#LoadoutSlot_Melee", // LOADOUT_POSITION_MELEE,
  1660. "#LoadoutSlot_Utility", // LOADOUT_POSITION_UTILITY,
  1661. "#LoadoutSlot_Building", // LOADOUT_POSITION_BUILDING,
  1662. "#LoadoutSlot_pda", // LOADOUT_POSITION_PDA,
  1663. "#LoadoutSlot_pda2", // LOADOUT_POSITION_PDA2
  1664. // Wearables
  1665. "#LoadoutSlot_Misc", // LOADOUT_POSITION_HEAD
  1666. "#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC
  1667. "#LoadoutSlot_Action", // LOADOUT_POSITION_ACTION
  1668. "#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC2
  1669. "#LoadoutSlot_Taunt", // LOADOUT_POSITION_TAUNT,
  1670. "#LoadoutSlot_Taunt2", // LOADOUT_POSITION_TAUNT2,
  1671. "#LoadoutSlot_Taunt3", // LOADOUT_POSITION_TAUNT3,
  1672. "#LoadoutSlot_Taunt4", // LOADOUT_POSITION_TAUNT4,
  1673. "#LoadoutSlot_Taunt5", // LOADOUT_POSITION_TAUNT5,
  1674. "#LoadoutSlot_Taunt6", // LOADOUT_POSITION_TAUNT6,
  1675. "#LoadoutSlot_Taunt7", // LOADOUT_POSITION_TAUNT7,
  1676. "#LoadoutSlot_Taunt8", // LOADOUT_POSITION_TAUNT8,
  1677. #ifdef STAGING_ONLY
  1678. "#LoadoutSlot_pda_addon1", // LOADOUT_POSITION_PDA_ADDON1,
  1679. "#LoadoutSlot_pda_addon2", // LOADOUT_POSITION_PDA_ADDON2,
  1680. "#LoadoutSlot_pda3", // LOADOUT_POSITION_PDA3
  1681. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC3
  1682. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC4
  1683. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC5
  1684. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC6
  1685. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC7
  1686. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC8
  1687. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC9
  1688. //"#LoadoutSlot_Misc", // LOADOUT_POSITION_MISC10
  1689. "#LoadoutSlot_Building", // LOADOUT_POSITION_BUILDING2,
  1690. #endif // STAGING_ONLY
  1691. };
  1692. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szLoadoutStringsForDisplay ) == CLASS_LOADOUT_POSITION_COUNT );
  1693. // Loadout positions
  1694. const char *g_szAccountLoadoutStrings[] =
  1695. {
  1696. "quest",
  1697. ""
  1698. ""
  1699. };
  1700. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szAccountLoadoutStrings ) <= ACCOUNT_LOADOUT_POSITION_COUNT ); // we don't support mapping directly to slots like "misc2", "taunt2-8", etc.
  1701. // Loadout positions used to display loadout slots to players (localized)
  1702. const char *g_szAccountLoadoutStringsForDisplay[] =
  1703. {
  1704. "#LoadoutSlot_Account1",
  1705. "#LoadoutSlot_Account2",
  1706. "#LoadoutSlot_Account3",
  1707. };
  1708. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szAccountLoadoutStringsForDisplay ) == ACCOUNT_LOADOUT_POSITION_COUNT );
  1709. // Weapon types
  1710. const char *g_szWeaponTypeSubstrings[] =
  1711. {
  1712. // Weapons & Equipment
  1713. "PRIMARY",
  1714. "SECONDARY",
  1715. "MELEE",
  1716. "GRENADE",
  1717. "BUILDING",
  1718. "PDA",
  1719. "ITEM1",
  1720. "ITEM2",
  1721. "HEAD",
  1722. "MISC",
  1723. "MELEE_ALLCLASS",
  1724. "SECONDARY2",
  1725. "PRIMARY2"
  1726. };
  1727. COMPILE_TIME_ASSERT( ARRAYSIZE( g_szWeaponTypeSubstrings ) == TF_WPN_TYPE_COUNT );
  1728. CTFItemSchema::CTFItemSchema()
  1729. : m_mapQuestObjectiveConditions( DefLessFunc( ObjectiveConditionDefIndex_t ) )
  1730. , m_mapQuestThemes( CaselessStringLessThan )
  1731. , m_mapWars( DefLessFunc( WarDefinitionMap_t::KeyType_t ) )
  1732. , m_mapGameCategories( DefLessFunc( GameCategoryMap_t::KeyType_t ) )
  1733. , m_mapMMGroups( DefLessFunc( MMGroupMap_t::KeyType_t ) )
  1734. {
  1735. // Runs at global constructor time, please don't put anything in here, especially asserts
  1736. }
  1737. //-----------------------------------------------------------------------------
  1738. // Purpose:
  1739. //-----------------------------------------------------------------------------
  1740. void CTFItemSchema::Reset()
  1741. {
  1742. m_vecMvMMaps.Purge();
  1743. m_vecMvMMissions.Purge();
  1744. m_vecMvMTours.Purge();
  1745. m_mapGameCategories.PurgeAndDeleteElements();
  1746. #ifndef GC_DLL
  1747. m_mapQuestThemes.PurgeAndDeleteElements();
  1748. #endif
  1749. CEconItemSchema::Reset();
  1750. }
  1751. //-----------------------------------------------------------------------------
  1752. // Purpose:
  1753. //-----------------------------------------------------------------------------
  1754. void CTFItemSchema::InitializeStringTable( const char **ppStringTable, unsigned int unStringCount, CUtlVector<const char *> *out_pvecStringTable )
  1755. {
  1756. Assert( ppStringTable != NULL );
  1757. Assert( out_pvecStringTable != NULL );
  1758. Assert( out_pvecStringTable->Size() == 0 );
  1759. for ( unsigned int i = 0; i < unStringCount; i++ )
  1760. {
  1761. Assert( ppStringTable[i] != NULL );
  1762. out_pvecStringTable->AddToTail( ppStringTable[i] );
  1763. }
  1764. }
  1765. //-----------------------------------------------------------------------------
  1766. // Purpose: Parses game specific items_master data.
  1767. //-----------------------------------------------------------------------------
  1768. bool CTFItemSchema::BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors )
  1769. {
  1770. // First time through, prepare string tables. Must happen before calling parent BInitSchema.
  1771. if ( m_vecClassUsabilityStrings.Size() == 0 )
  1772. {
  1773. // Special case, since player class data is an array of structs, not an array of strings
  1774. for ( unsigned int i = 0; i < ARRAYSIZE( gs_PlayerClassData ); i++ )
  1775. {
  1776. m_vecClassUsabilityStrings.AddToTail( gs_PlayerClassData[i].m_pchName );
  1777. }
  1778. Assert( m_vecClassUsabilityStrings.Size() == LOADOUT_COUNT );
  1779. InitializeStringTable( &g_szLoadoutStrings[0], ARRAYSIZE(g_szLoadoutStrings), &m_vecClassLoadoutStrings );
  1780. Assert( m_vecClassLoadoutStrings.Size() <= CLASS_LOADOUT_POSITION_COUNT );
  1781. InitializeStringTable( &g_szLoadoutStringsForDisplay[0], ARRAYSIZE(g_szLoadoutStringsForDisplay), &m_vecClassLoadoutStringsForDisplay );
  1782. Assert( m_vecClassLoadoutStringsForDisplay.Size() == CLASS_LOADOUT_POSITION_COUNT );
  1783. InitializeStringTable( &g_szAccountLoadoutStrings[0], ARRAYSIZE(g_szAccountLoadoutStrings), &m_vecAccountLoadoutStrings );
  1784. Assert( m_vecAccountLoadoutStrings.Size() <= ACCOUNT_LOADOUT_POSITION_COUNT );
  1785. InitializeStringTable( &g_szAccountLoadoutStringsForDisplay[0], ARRAYSIZE(g_szAccountLoadoutStringsForDisplay), &m_vecAccountLoadoutStringsForDisplay );
  1786. Assert( m_vecAccountLoadoutStringsForDisplay.Size() == ACCOUNT_LOADOUT_POSITION_COUNT );
  1787. InitializeStringTable( &g_szWeaponTypeSubstrings[0], ARRAYSIZE(g_szWeaponTypeSubstrings), &m_vecWeaponTypeSubstrings );
  1788. Assert( m_vecWeaponTypeSubstrings.Size() == TF_WPN_TYPE_COUNT );
  1789. }
  1790. // This needs to happen BEFORE we get the quest objectives since they're going to reference these.
  1791. KeyValues *pKVQuestObjectiveConditions = pKVRawDefinition->FindKey( "quest_objective_conditions" );
  1792. SCHEMA_INIT_SUBSTEP( BInitQuestObjectiveConditions( pKVQuestObjectiveConditions, pVecErrors ) );
  1793. SCHEMA_INIT_SUBSTEP( CEconItemSchema::BInitSchema( pKVRawDefinition, pVecErrors ) );
  1794. KeyValues *pKVMvmMaps = pKVRawDefinition->FindKey( "mvm_maps" );
  1795. SCHEMA_INIT_SUBSTEP( BInitMvmMissions( pKVMvmMaps, pVecErrors ) );
  1796. KeyValues *pKVMvmTours = pKVRawDefinition->FindKey( "mvm_tours" );
  1797. SCHEMA_INIT_SUBSTEP( BInitMvmTours( pKVMvmTours, pVecErrors ) );
  1798. KeyValues *pKVMMCats = pKVRawDefinition->FindKey( "matchmaking_categories" );
  1799. SCHEMA_INIT_SUBSTEP( BInitMMCategories( pKVMMCats, pVecErrors ) );
  1800. KeyValues *pKVMasterMaps = pKVRawDefinition->FindKey( "master_maps_list" );
  1801. SCHEMA_INIT_SUBSTEP( BInitMaps( pKVMasterMaps, pVecErrors ) );
  1802. KeyValues *pKVMaps = pKVRawDefinition->FindKey( "maps" );
  1803. SCHEMA_INIT_SUBSTEP( BInitGameModes( pKVMaps, pVecErrors ) );
  1804. SCHEMA_INIT_SUBSTEP( BPostInitMaps( pVecErrors ) );
  1805. KeyValues *pKVQuestThemes = pKVRawDefinition->FindKey( "quest_themes" );
  1806. SCHEMA_INIT_SUBSTEP( BInitQuestThemes( pKVQuestThemes, pVecErrors ) );
  1807. KeyValues* pKVWarDefs = pKVRawDefinition->FindKey( "war_definitions" );
  1808. SCHEMA_INIT_SUBSTEP( BInitWarDefs( pKVWarDefs, pVecErrors ) );
  1809. #ifdef GAME_DLL
  1810. IGameEvent * event = gameeventmanager->CreateEvent( "schema_updated" );
  1811. if ( event )
  1812. {
  1813. gameeventmanager->FireEvent( event, true );
  1814. }
  1815. #elif defined( CLIENT_DLL )
  1816. IGameEvent * event = gameeventmanager->CreateEvent( "schema_updated" );
  1817. if ( event )
  1818. {
  1819. gameeventmanager->FireEventClientSide( event );
  1820. }
  1821. #endif
  1822. return SCHEMA_INIT_SUCCESS();
  1823. }
  1824. const char CTFItemSchema::k_rchOverrideItemLevelDescStringAttribName[] = "override item level desc string";
  1825. const char CTFItemSchema::k_rchMvMTicketItemDefName[] = "Tour of Duty Ticket";
  1826. const char CTFItemSchema::k_rchMvMSquadSurplusVoucherItemDefName[] = "MvM Squad Surplus Voucher";
  1827. const char CTFItemSchema::k_rchMvMPowerupBottleItemDefName[] = "Power Up Canteen (MvM)";
  1828. const char CTFItemSchema::k_rchMvMChallengeCompletedMaskAttribName[] = "mvm completed challenges bitmask";
  1829. const char CTFItemSchema::k_rchLadderPassItemDefName[] = "Competitive Matchmaking Official";
  1830. //-----------------------------------------------------------------------------
  1831. // Purpose:
  1832. //-----------------------------------------------------------------------------
  1833. const char *s_pszGameModes[eNumGameCategories] =
  1834. {
  1835. "payload", // kGameCategory_Escort
  1836. "ctf", // kGameCategory_CTF
  1837. "attack_defense", // kGameCategory_AttackDefense
  1838. "koth", // kGameCategory_Koth
  1839. "capture_point", // kGameCategory_CP
  1840. "payload_race", // kGameCategory_EscortRace
  1841. "event_mix", // kGameCategory_EventMix
  1842. "special_delivery", // kGameCategory_SD
  1843. "", // kGameCategory_Quickplay
  1844. "event_24_7", // kGameCategory_Event247,
  1845. "arena", // kGameCategory_Arena
  1846. "robot_destruction", // kGameCategory_RobotDestruction
  1847. "powerup", // kGameCategory_Powerup
  1848. "featured", // kGameCategory_Featured
  1849. "passtime", // kGameCategory_Passtime
  1850. "community_update", // kGameCategory_Community_Update
  1851. "misc", // kGameCategory_Misc
  1852. "competitive_6v6", // kGameCategory_Competitive_6v6
  1853. "other", // kGameCategory_Other
  1854. "halloween", // kGameCategory_Halloween
  1855. };
  1856. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszGameModes ) == eNumGameCategories );
  1857. static const char *s_pszQuickplayMatchTypes[] =
  1858. {
  1859. "advanced_only", // kQuickplay_AdvancedUsersOnly (default)
  1860. "all_users", // kQuickplay_AllUsers
  1861. "disabled", // kQuickplay_Disabled
  1862. };
  1863. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszQuickplayMatchTypes ) == kQuickplayTypeCount );
  1864. static const char *s_pszMvMBadgeContractPointsAttributes[] =
  1865. {
  1866. NULL, // we don't support normal for contract
  1867. "mvm contract points intermediate",
  1868. "mvm contract points advanced",
  1869. "mvm contract points expert",
  1870. NULL, // should we do haunted?
  1871. };
  1872. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszMvMBadgeContractPointsAttributes ) == k_EMvMChallengeDifficultyLastValid );
  1873. const char *CTFItemSchema::GetMvMBadgeContractPointsAttributeName( EMvMChallengeDifficulty difficulty )
  1874. {
  1875. if ( difficulty != k_EMvMChallengeDifficulty_Invalid )
  1876. return s_pszMvMBadgeContractPointsAttributes[ difficulty - 1 ];
  1877. return NULL;
  1878. }
  1879. static const char *s_pszMvMBadgeContractLevelAttributes[] =
  1880. {
  1881. NULL, // we don't support normal for contract
  1882. "mvm contract level intermediate",
  1883. "mvm contract level advanced",
  1884. "mvm contract level expert",
  1885. NULL, // should we do haunted?
  1886. };
  1887. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszMvMBadgeContractLevelAttributes ) == k_EMvMChallengeDifficultyLastValid );
  1888. const char *CTFItemSchema::GetMvMBadgeContractLevelAttributeName( EMvMChallengeDifficulty difficulty )
  1889. {
  1890. if ( difficulty != k_EMvMChallengeDifficulty_Invalid )
  1891. return s_pszMvMBadgeContractLevelAttributes[ difficulty - 1 ];
  1892. return NULL;
  1893. }
  1894. const char* s_pszMMTypes[kMatchmakingTypeCount] =
  1895. {
  1896. "special_events",
  1897. "core",
  1898. "alternative",
  1899. "competitive_6v6",
  1900. };
  1901. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszMMTypes ) == kMatchmakingTypeCount );
  1902. bool CTFItemSchema::BInitMMCategories( KeyValues *pKVCategories, CUtlVector<CUtlString> *pVecErrors )
  1903. {
  1904. m_mapMMGroups.PurgeAndDeleteElements();
  1905. FOR_EACH_TRUE_SUBKEY( pKVCategories, pKVCategory )
  1906. {
  1907. int nCatType = StringFieldToInt( pKVCategory->GetName(), s_pszMMTypes, ARRAYSIZE( s_pszMMTypes ) );
  1908. SCHEMA_INIT_CHECK( nCatType != -1, "BInitMMCategories: unknown mm category type '%s'", pKVCategory->GetName() );
  1909. EMatchmakingGroupType eType = (EMatchmakingGroupType)nCatType;
  1910. SchemaMMGroup_t *pCat = m_mapMMGroups[ m_mapMMGroups.Insert( eType, new SchemaMMGroup_t() ) ];
  1911. pCat->m_pszName = pKVCategory->GetName();
  1912. pCat->m_eMMGroup = eType;
  1913. pCat->m_pszLocalizedName = pKVCategory->GetString( "localized_name" );
  1914. pCat->m_nMaxExcludes = pKVCategory->GetInt( "max_excludes", -1 );
  1915. SCHEMA_INIT_CHECK( pCat->m_nMaxExcludes != -1, "BInitMMCategories: missing 'max_excludes' for mm category type '%s'", pKVCategory->GetName() );
  1916. KeyValues* pKVValidMatchGroups = pKVCategory->FindKey( "valid_match_groups" );
  1917. if ( pKVValidMatchGroups )
  1918. {
  1919. FOR_EACH_SUBKEY( pKVValidMatchGroups, pKVGroup )
  1920. {
  1921. EMatchGroup eGroup = (EMatchGroup)StringFieldToInt( pKVGroup->GetName(), s_pszMatchGroups, (int)k_nMatchGroup_Count, false );
  1922. pCat->m_bitsValidMMGroups.Set( eGroup, 1 );
  1923. }
  1924. }
  1925. }
  1926. return SCHEMA_INIT_SUCCESS();
  1927. }
  1928. bool SchemaGameCategory_t::PassesRestrictions() const
  1929. {
  1930. if ( m_vecRestrictions.Count() <= 0 )
  1931. return true;
  1932. FOR_EACH_VEC( m_vecRestrictions, i )
  1933. {
  1934. switch ( m_vecRestrictions[i].m_eType )
  1935. {
  1936. case kMatchmakingGameModeRestrictionType_Holiday:
  1937. #ifndef GC_DLL
  1938. if ( UTIL_IsHolidayActive( m_vecRestrictions[i].m_nValue ) )
  1939. #else
  1940. if ( EconHolidays_IsHolidayActive( m_vecRestrictions[i].m_nValue, CRTime::RTime32TimeCur() ) )
  1941. #endif
  1942. return true;
  1943. break;
  1944. case kMatchmakingGameModeRestrictionType_Operation:
  1945. if ( GetItemSchema() )
  1946. {
  1947. FOR_EACH_MAP_FAST( GetItemSchema()->GetOperationDefinitions(), iOperation )
  1948. {
  1949. CEconOperationDefinition *pOperation = GetItemSchema()->GetOperationDefinitions()[iOperation];
  1950. if ( pOperation && pOperation->IsActive() )
  1951. {
  1952. if ( Q_stricmp( pOperation->GetName(), m_vecRestrictions[i].m_strValue ) == 0 )
  1953. return true;
  1954. }
  1955. }
  1956. }
  1957. break;
  1958. default:
  1959. break;
  1960. }
  1961. }
  1962. return false;
  1963. }
  1964. //-----------------------------------------------------------------------------
  1965. // Purpose:
  1966. //-----------------------------------------------------------------------------
  1967. bool CTFItemSchema::BInitGameModes( KeyValues *pKVMaps, CUtlVector<CUtlString> *pVecErrors )
  1968. {
  1969. m_mapGameCategories.PurgeAndDeleteElements();
  1970. if ( NULL == pKVMaps )
  1971. return true;
  1972. FOR_EACH_TRUE_SUBKEY( pKVMaps, pKVGameMode )
  1973. {
  1974. int iGameType = StringFieldToInt( pKVGameMode->GetName(), s_pszGameModes, ARRAYSIZE( s_pszGameModes ) );
  1975. SCHEMA_INIT_CHECK( iGameType != -1,
  1976. "BInitMaps(): unknown game type '%s'", pKVGameMode->GetName() );
  1977. EGameCategory eGameType = (EGameCategory)iGameType;
  1978. SchemaGameCategory_t* pGameMode = m_mapGameCategories[ m_mapGameCategories.Insert( eGameType, new SchemaGameCategory_t() ) ];
  1979. pGameMode->m_eGameCategory = eGameType;
  1980. pGameMode->m_pszLocalizedName = pKVGameMode->GetString( "localized_name", NULL );
  1981. pGameMode->m_pszLocalizedDesc = pKVGameMode->GetString( "localized_desc", NULL );
  1982. pGameMode->m_pszListImage = pKVGameMode->GetString( "list_image", NULL );
  1983. pGameMode->m_pszName = pKVGameMode->GetName();
  1984. pGameMode->m_pszMMType = pKVGameMode->GetString( "mm_type" );
  1985. int nMMType = StringFieldToInt( pKVGameMode->GetString( "mm_type" ), s_pszMMTypes, ARRAYSIZE( s_pszMMTypes ) );
  1986. if ( nMMType != (int)kMatchmakingType_None )
  1987. {
  1988. EMatchmakingGroupType eMMType = (EMatchmakingGroupType)nMMType;
  1989. auto idx = m_mapMMGroups.Find( eMMType );
  1990. SCHEMA_INIT_CHECK( idx != m_mapMMGroups.InvalidIndex(), "mm_type '%s' does not have a matchmaking_categories entry", pKVGameMode->GetString( "mm_type" ) );
  1991. SCHEMA_INIT_CHECK( pGameMode->m_pszLocalizedName != NULL, "game mode '%s' missing localized name!", pKVGameMode->GetName() );
  1992. SCHEMA_INIT_CHECK( pGameMode->m_pszLocalizedDesc != NULL, "game mode '%s' missing localized desc!", pKVGameMode->GetName() );
  1993. SCHEMA_INIT_CHECK( pGameMode->m_pszListImage != NULL, "game mode '%s' missing list image!", pKVGameMode->GetName() );
  1994. pGameMode->m_pMMGroup = m_mapMMGroups[ idx ];
  1995. m_mapMMGroups[ idx ]->m_vecModes.AddToTail( pGameMode );
  1996. KeyValues *pKVRestrictions = pKVGameMode->FindKey( "restrictions" );
  1997. if ( pKVRestrictions )
  1998. {
  1999. FOR_EACH_SUBKEY( pKVRestrictions, pKVRestriction )
  2000. {
  2001. EMatchmakingGameModeRestrictionType eType = kMatchmakingGameModeRestrictionType_None;
  2002. int nValue = -1;
  2003. const char *pszValue = NULL;
  2004. const char *pszType = pKVRestriction->GetName();
  2005. if ( Q_stricmp( pszType, "holiday" ) == 0 )
  2006. {
  2007. eType = kMatchmakingGameModeRestrictionType_Holiday;
  2008. #ifndef GC_DLL
  2009. nValue = UTIL_GetHolidayForString( pKVRestriction->GetString() );
  2010. #else
  2011. nValue = EconHolidays_GetHolidayForString( pKVRestriction->GetString() );
  2012. #endif
  2013. }
  2014. else if ( Q_stricmp( pszType, "operation" ) == 0 )
  2015. {
  2016. eType = kMatchmakingGameModeRestrictionType_Operation;
  2017. pszValue = pKVRestriction->GetString();
  2018. }
  2019. if ( eType != kMatchmakingGameModeRestrictionType_None )
  2020. {
  2021. int iIndex = pGameMode->m_vecRestrictions.AddToTail();
  2022. pGameMode->m_vecRestrictions[iIndex].m_eType = eType;
  2023. pGameMode->m_vecRestrictions[iIndex].m_nValue = nValue;
  2024. pGameMode->m_vecRestrictions[iIndex].m_strValue = pszValue;
  2025. }
  2026. }
  2027. }
  2028. }
  2029. KeyValues *pKVMapList = pKVGameMode->FindKey( "maplist" );
  2030. if ( pKVMapList )
  2031. {
  2032. FOR_EACH_TRUE_SUBKEY( pKVMapList, pKVMap )
  2033. {
  2034. MapDef_t* pMap = const_cast< MapDef_t* >( GetMasterMapDefByName( pKVMap->GetString( "name" ) ) );
  2035. SCHEMA_INIT_CHECK ( pMap != NULL, "Map %s listed in game mode %s doesn't exist!", pKVMap->GetString( "name" ), pGameMode->m_pszName );
  2036. pMap->m_vecAssociatedGameCategories.AddToTail( eGameType );
  2037. pGameMode->AddMap( pMap, pKVMap->GetBool( "enabled" ) );
  2038. }
  2039. }
  2040. SCHEMA_INIT_CHECK( pGameMode->m_vecEnabledMaps.Count(), "BInitMaps(): ERROR!! No valid maps for game type %s (at least one must be \"enabled\" in _maps.txt).", pKVGameMode->GetName() );
  2041. }
  2042. return SCHEMA_INIT_SUCCESS();
  2043. }
  2044. //-----------------------------------------------------------------------------
  2045. // Purpose:
  2046. //-----------------------------------------------------------------------------
  2047. bool CTFItemSchema::BInitMaps( KeyValues *pKVMaps, CUtlVector<CUtlString> *pVecErrors )
  2048. {
  2049. m_vecMasterListOfMaps.PurgeAndDeleteElements();
  2050. FOR_EACH_TRUE_SUBKEY( pKVMaps, pKVMap )
  2051. {
  2052. const char* pszMapStampname = pKVMap->GetString( "maptoken", "" );
  2053. MapDef_t* pMap = new MapDef_t( pszMapStampname );
  2054. pMap->pszMapName = pKVMap->GetString( "name", NULL );
  2055. SCHEMA_INIT_CHECK( pMap->pszMapName != NULL,
  2056. "BInitMaps(): missing map name for master map entry %s.", pKVMap->GetName() );
  2057. pMap->m_nDefIndex = V_atoi( pKVMap->GetName() );
  2058. pMap->pszMapNameLocKey = pKVMap->GetString( "localizedname", NULL );
  2059. SCHEMA_INIT_CHECK( (pMap->mapStampDef == NULL) == (pszMapStampname[0] == '\0'),
  2060. "BInitGameModes(): unable to find map stamp definition '%s' for map '%s'.", pszMapStampname, pMap->pszMapName );
  2061. pMap->pszMapNameLocKey = pKVMap->GetString( "localizedname", NULL );
  2062. pMap->pszAuthorsLocKey = pKVMap->GetString( "authors", NULL );
  2063. pMap->pszStrangePrefixLocKey = pKVMap->GetString( "strangeprefixtoken", NULL );
  2064. pMap->m_nStatsIdentifier = pKVMap->GetInt( "statsidentifier", -1 );
  2065. // initialize from optional "tags" block
  2066. KeyValues *pKVTags = pKVMap->FindKey( "tags" );
  2067. if ( pKVTags )
  2068. {
  2069. FOR_EACH_SUBKEY( pKVTags, pKVTag )
  2070. {
  2071. pMap->vecTags.AddToTail( GetHandleForTag( pKVTag->GetName() ) );
  2072. }
  2073. }
  2074. // Init rolling match tags
  2075. pKVTags = pKVMap->FindKey( "rolling_match_tags" );
  2076. if ( pKVTags )
  2077. {
  2078. FOR_EACH_SUBKEY( pKVTags, pKVTag )
  2079. {
  2080. pMap->m_vecRollingMatchTags.AddToTail( GetHandleForTag( pKVTag->GetName() ) );
  2081. }
  2082. }
  2083. // Init rolling match targets
  2084. pKVTags = pKVMap->FindKey( "rolling_match_target_tags" );
  2085. if ( pKVTags )
  2086. {
  2087. FOR_EACH_SUBKEY( pKVTags, pKVTag )
  2088. {
  2089. pMap->m_vecRollingMatchTargets.AddToTail( { GetHandleForTag( pKVTag->GetName() ), pKVTag->GetFloat( "weight", 1.f ) } );
  2090. }
  2091. }
  2092. m_vecMasterListOfMaps.AddToTail( pMap );
  2093. }
  2094. return true;
  2095. }
  2096. bool CTFItemSchema::BPostInitMaps( CUtlVector<CUtlString> *pVecErrors )
  2097. {
  2098. //
  2099. // Go through each map and check if any of the other maps have a matching tag. If it does,
  2100. // add it as a map that we could roll for a rolling match "next map" vote. We're doing this
  2101. // now so we don't compute it every time choose maps to vote on
  2102. //
  2103. FOR_EACH_VEC( m_vecMasterListOfMaps, i )
  2104. {
  2105. MapDef_t* pMapOuter = m_vecMasterListOfMaps[ i ];
  2106. bool bRequiredToHaveRollingMatchTags = false;
  2107. // Some maps dont need to have rolling match tags (ex. MvM, the "invalid map", maps we choose because reasons )
  2108. FOR_EACH_VEC( pMapOuter->m_vecAssociatedGameCategories, j )
  2109. {
  2110. const SchemaGameCategory_t* pCategory = GetItemSchema()->GetGameCategory( pMapOuter->m_vecAssociatedGameCategories[j] );
  2111. if ( !pCategory )
  2112. {
  2113. continue;
  2114. }
  2115. const SchemaMMGroup_t* pMMGroup = pCategory->m_pMMGroup;
  2116. if ( !pMMGroup )
  2117. {
  2118. continue;
  2119. }
  2120. if ( pCategory->m_vecEnabledMaps.Find( pMapOuter ) == pCategory->m_vecEnabledMaps.InvalidIndex() )
  2121. {
  2122. continue;
  2123. }
  2124. if ( pMMGroup->m_bitsValidMMGroups.IsBitSet( k_nMatchGroup_Casual_12v12 ) )
  2125. {
  2126. bRequiredToHaveRollingMatchTags = true;
  2127. break;
  2128. }
  2129. }
  2130. if ( !bRequiredToHaveRollingMatchTags )
  2131. continue;
  2132. //
  2133. // For each map, figure out which maps it can vote into. Use the highest weight if it's
  2134. // in multiple groups.
  2135. //
  2136. FOR_EACH_VEC( pMapOuter->m_vecRollingMatchTargets, j )
  2137. {
  2138. // Figure out how many maps match this tag so we can get the weighting right
  2139. const MapDef_t::WeightedNextMapTargets_t& tag = pMapOuter->m_vecRollingMatchTargets[ j ];
  2140. CUtlVector< MapDef_t* > vecMatchedMaps;
  2141. FOR_EACH_VEC( m_vecMasterListOfMaps, k )
  2142. {
  2143. MapDef_t *pCandidate = m_vecMasterListOfMaps[ k ];
  2144. // Don't have ourselves as a potential target
  2145. if ( m_vecMasterListOfMaps[ k ]->m_nDefIndex == pMapOuter->m_nDefIndex )
  2146. continue;
  2147. bool bEnabled = false;
  2148. FOR_EACH_VEC( pCandidate->m_vecAssociatedGameCategories, idxCandidateCategory )
  2149. {
  2150. const SchemaGameCategory_t* pCategory = GetItemSchema()->GetGameCategory( pCandidate->m_vecAssociatedGameCategories[idxCandidateCategory] );
  2151. if ( pCategory && pCategory->m_vecEnabledMaps.Find( pCandidate ) != pCategory->m_vecEnabledMaps.InvalidIndex() )
  2152. {
  2153. bEnabled = true;
  2154. break;
  2155. }
  2156. }
  2157. if ( !bEnabled )
  2158. {
  2159. continue;
  2160. }
  2161. if ( m_vecMasterListOfMaps[ k ]->BHasRollingMatchTag( tag.m_tag ) )
  2162. {
  2163. vecMatchedMaps.AddToTail( m_vecMasterListOfMaps[ k ] );
  2164. }
  2165. }
  2166. // Add each map to THIS map's list
  2167. float flTargetIndividualWeight = tag.m_flWeight / (float)vecMatchedMaps.Count();
  2168. FOR_EACH_VEC( vecMatchedMaps, k )
  2169. {
  2170. pMapOuter->AddMapAsTargetWithWeight( { vecMatchedMaps[ k ]->m_nDefIndex, flTargetIndividualWeight } );
  2171. }
  2172. //
  2173. // Normalize the weights so we can do scaling later on
  2174. //
  2175. float flMaxWeight = 0.f;
  2176. FOR_EACH_VEC( pMapOuter->m_vecRollingMatchMaps, k )
  2177. {
  2178. flMaxWeight = Max( pMapOuter->m_vecRollingMatchMaps[ k ].m_flWeight, flMaxWeight );
  2179. }
  2180. // Normalize
  2181. FOR_EACH_VEC( pMapOuter->m_vecRollingMatchMaps, k )
  2182. {
  2183. pMapOuter->m_vecRollingMatchMaps[ k ].m_flWeight = pMapOuter->m_vecRollingMatchMaps[ k ].m_flWeight / flMaxWeight;
  2184. }
  2185. }
  2186. // We only have to roll one-less than the amount needed because the current map is always selected
  2187. SCHEMA_INIT_CHECK( pMapOuter->m_vecRollingMatchMaps.Count() >= NEXT_MAP_VOTE_OPTIONS - 1, "Not enough maps with matching tags for map %s",
  2188. pMapOuter->pszMapName );
  2189. }
  2190. return true;
  2191. }
  2192. //-----------------------------------------------------------------------------
  2193. // Purpose: Inits data for quest themes
  2194. //-----------------------------------------------------------------------------
  2195. bool CTFItemSchema::BInitQuestThemes( KeyValues *pKVThemes, CUtlVector<CUtlString> *pVecErrors )
  2196. {
  2197. if ( NULL != pKVThemes )
  2198. {
  2199. FOR_EACH_TRUE_SUBKEY( pKVThemes, pKVTheme )
  2200. {
  2201. CQuestThemeDefinition *pTheme = new CQuestThemeDefinition();
  2202. SCHEMA_INIT_SUBSTEP( pTheme->BInitFromKV( pKVTheme, pVecErrors ) );
  2203. m_mapQuestThemes.Insert( pKVTheme->GetName(), pTheme );
  2204. }
  2205. }
  2206. return SCHEMA_INIT_SUCCESS();
  2207. }
  2208. //-----------------------------------------------------------------------------
  2209. // Purpose: Inits data for quest objective conditions
  2210. //-----------------------------------------------------------------------------
  2211. bool CTFItemSchema::BInitQuestObjectiveConditions( KeyValues *pKVConditionsBlock, CUtlVector<CUtlString> *pVecErrors )
  2212. {
  2213. m_mapQuestObjectiveConditions.PurgeAndDeleteElements();
  2214. SCHEMA_INIT_CHECK( pKVConditionsBlock != NULL, "No quest objective conditions block found!" );
  2215. FOR_EACH_TRUE_SUBKEY( pKVConditionsBlock, pKVCondition )
  2216. {
  2217. CTFQuestObjectiveConditionsDefinition *pNewCondition = new CTFQuestObjectiveConditionsDefinition();
  2218. SCHEMA_INIT_SUBSTEP( pNewCondition->BInitFromKV( pKVCondition, pVecErrors ) );
  2219. m_mapQuestObjectiveConditions.Insert( pNewCondition->GetDefIndex(), pNewCondition );
  2220. }
  2221. return SCHEMA_INIT_SUCCESS();
  2222. }
  2223. //-----------------------------------------------------------------------------
  2224. // Purpose: Post init for all quest objective conditions
  2225. //-----------------------------------------------------------------------------
  2226. bool CTFItemSchema::BObjectiveConditionsPostInit( CUtlVector<CUtlString> *pVecErrors )
  2227. {
  2228. FOR_EACH_MAP_FAST( m_mapQuestObjectiveConditions, i )
  2229. {
  2230. SCHEMA_INIT_SUBSTEP( m_mapQuestObjectiveConditions[ i ]->BPostInit( pVecErrors ) );
  2231. }
  2232. return SCHEMA_INIT_SUCCESS();
  2233. }
  2234. //-----------------------------------------------------------------------------
  2235. // Purpose: Init all war definitions
  2236. //-----------------------------------------------------------------------------
  2237. bool CTFItemSchema::BInitWarDefs( KeyValues *pKVWarDefs, CUtlVector<CUtlString> *pVecErrors )
  2238. {
  2239. m_mapWars.PurgeAndDeleteElements();
  2240. FOR_EACH_TRUE_SUBKEY( pKVWarDefs, pKVWar )
  2241. {
  2242. CWarDefinition* pNewWarDef = new CWarDefinition();
  2243. SCHEMA_INIT_SUBSTEP( pNewWarDef->BInitFromKV( pKVWar, pVecErrors ) );
  2244. m_mapWars.Insert( pNewWarDef->GetDefIndex(), pNewWarDef );
  2245. }
  2246. return SCHEMA_INIT_SUCCESS();
  2247. }
  2248. //-----------------------------------------------------------------------------
  2249. // Purpose: Inits data for MVM maps / missions
  2250. //-----------------------------------------------------------------------------
  2251. bool CTFItemSchema::BInitMvmMissions( KeyValues *pKVMvmMaps, CUtlVector<CUtlString> *pVecErrors )
  2252. {
  2253. m_vecMvMMaps.RemoveAll();
  2254. m_vecMvMMissions.RemoveAll();
  2255. if ( NULL == pKVMvmMaps )
  2256. return true;
  2257. // initialize the rewards sections
  2258. bool bResult = true;
  2259. FOR_EACH_TRUE_SUBKEY( pKVMvmMaps, pKVMap )
  2260. {
  2261. int nMapIndex = m_vecMvMMaps.AddToTail();
  2262. MvMMap_t &map = m_vecMvMMaps[nMapIndex];
  2263. map.m_sMap = pKVMap->GetName();
  2264. int nMapNameLen = strlen( map.m_sMap.Get() );
  2265. map.m_sDisplayName = pKVMap->GetString( "display_name" );
  2266. // Locate missions
  2267. KeyValues *pKVMissions = pKVMap->FindKey( "missions" );
  2268. if ( pKVMissions )
  2269. {
  2270. // Parse mission subkeys
  2271. FOR_EACH_TRUE_SUBKEY( pKVMissions, pKVMission )
  2272. {
  2273. int nMissionIndex = m_vecMvMMissions.AddToTail(); // global mission index (not map-specific)
  2274. MvMMission_t &mission = m_vecMvMMissions[nMissionIndex];
  2275. map.m_vecMissions.AddToTail( nMissionIndex ); // index from map -> global mission list
  2276. mission.m_sPop = pKVMission->GetName();
  2277. mission.m_iDisplayMapIndex = nMapIndex;
  2278. mission.m_sDisplayName = pKVMission->GetString( "display_name" );
  2279. mission.m_sMode = pKVMission->GetString( "mode" );
  2280. mission.m_sMapNameActual = pKVMission->GetString( "map_file_override", map.m_sMap.Get() );
  2281. const char *pszDiff = pKVMission->GetString( "difficulty", "" );
  2282. mission.m_eDifficulty = GetMvMChallengeDifficultyByInternalName( pszDiff );
  2283. mission.m_unMannUpPoints = pKVMission->GetInt( "mannup_points" );
  2284. if ( mission.m_eDifficulty == k_EMvMChallengeDifficulty_Invalid )
  2285. {
  2286. pVecErrors->AddToTail( CUtlString( CFmtStr(
  2287. "MvM mission '%s' on map %s has missing or invalid 'difficulty' value", mission.m_sPop.Get(), map.m_sMap.Get() ) ) );
  2288. bResult = false;
  2289. continue;
  2290. }
  2291. // Pop filenames are required to obey a naming convention.
  2292. if ( ( Q_stricmp( mission.m_sPop.Get(), map.m_sMap.Get() ) != 0 )
  2293. && ( Q_strnicmp( mission.m_sPop.Get(), map.m_sMap.Get(), nMapNameLen ) != 0
  2294. || mission.m_sPop.Get()[nMapNameLen] != '_' ) )
  2295. {
  2296. pVecErrors->AddToTail( CUtlString( CFmtStr(
  2297. "MvM mission '%s' on map %s does not obey map prefix naming convention", mission.m_sPop.Get(), map.m_sMap.Get() ) ) );
  2298. bResult = false;
  2299. continue;
  2300. }
  2301. }
  2302. }
  2303. SCHEMA_INIT_CHECK( map.m_vecMissions.Count() > 0,
  2304. "MvM map %s doesn't have any associated missions", map.m_sMap.Get() );
  2305. }
  2306. return bResult;
  2307. }
  2308. //-----------------------------------------------------------------------------
  2309. // Purpose: Inits data for MVM tours (sets of missions)
  2310. //-----------------------------------------------------------------------------
  2311. bool CTFItemSchema::BInitMvmTours( KeyValues *pKVMvmTours, CUtlVector<CUtlString> *pVecErrors )
  2312. {
  2313. m_vecMvMTours.RemoveAll();
  2314. if ( NULL == pKVMvmTours )
  2315. return true;
  2316. // initialize the rewards sections
  2317. bool bResult = true;
  2318. FOR_EACH_TRUE_SUBKEY( pKVMvmTours, pKVTour )
  2319. {
  2320. MvMTour_t tour;
  2321. tour.m_sTourInternalName = pKVTour->GetName();
  2322. SCHEMA_INIT_CHECK( FindMvmMissionByName( tour.m_sTourInternalName.Get() ) < 0,
  2323. "Duplicate MvM tour \"%s\"", tour.m_sTourInternalName.Get() );
  2324. tour.m_sTourNameLocalizationToken = pKVTour->GetString( "tour_name" );
  2325. SCHEMA_INIT_CHECK( tour.m_sTourNameLocalizationToken.Get() && tour.m_sTourNameLocalizationToken.Get()[0] == '#',
  2326. "MvM tour \"%s\" didn't specify valid localization token for 'tour_name'", tour.m_sTourInternalName.Get() );
  2327. const char *pszBadgeItemDefName = pKVTour->GetString( "badge_item_def" );
  2328. tour.m_pBadgeItemDef = pszBadgeItemDefName ? GetItemSchema()->GetItemDefinitionByName( pszBadgeItemDefName ) : NULL;
  2329. SCHEMA_INIT_CHECK( (pszBadgeItemDefName == NULL) == (tour.m_pBadgeItemDef == NULL),
  2330. "MvM tour \"%s\" specified invalid badge definition name '%s'", tour.m_sTourInternalName.Get(), pszBadgeItemDefName );
  2331. const char *pszDiff = pKVTour->GetString( "difficulty", "" );
  2332. tour.m_eDifficulty = GetMvMChallengeDifficultyByInternalName( pszDiff );
  2333. SCHEMA_INIT_CHECK( tour.m_eDifficulty != k_EMvMChallengeDifficulty_Invalid,
  2334. "MvM tour \"%s\" specified invalid difficulty '%s'", tour.m_sTourInternalName.Get(), pszDiff );
  2335. tour.m_bIsNew = pKVTour->GetBool( "is_new", false );
  2336. tour.m_sLootImageName = pKVTour->GetString( "loot_image" );
  2337. SCHEMA_INIT_CHECK( tour.m_sTourNameLocalizationToken.Get() && tour.m_sTourNameLocalizationToken.Get()[0] == '#',
  2338. "MvM tour \"%s\" didn't specify valid localization token for 'tour_name'", tour.m_sTourInternalName.Get() );
  2339. #ifdef GC
  2340. const char *pszMissionCompleteLootListName = pKVTour->GetString( "mission_complete_loot_list" );
  2341. tour.m_pMissionCompleteLootList = pszMissionCompleteLootListName ? GetItemSchema()->GetLootListByName( pszMissionCompleteLootListName ) : NULL;
  2342. SCHEMA_INIT_CHECK( (pszMissionCompleteLootListName == NULL) == (tour.m_pMissionCompleteLootList == NULL),
  2343. "MvM tour \"%s\" specified invalid mission completion loot list '%s'", tour.m_sTourInternalName.Get(), pszMissionCompleteLootListName );
  2344. const char *pszTourCompleteLootListName = pKVTour->GetString( "tour_complete_loot_list" );
  2345. tour.m_pTourCompleteLootList = pszTourCompleteLootListName ? GetItemSchema()->GetLootListByName( pszTourCompleteLootListName ) : NULL;
  2346. SCHEMA_INIT_CHECK( (pszTourCompleteLootListName == NULL) == (tour.m_pMissionCompleteLootList == NULL),
  2347. "MvM tour \"%s\" specified invalid tour completion loot list '%s'", tour.m_sTourInternalName.Get(), pszTourCompleteLootListName );
  2348. #endif
  2349. // Locate missions
  2350. tour.m_nAllChallengesBits = 0;
  2351. KeyValues *pKVMissions = pKVTour->FindKey( "missions" );
  2352. if ( pKVMissions )
  2353. {
  2354. bool bContainsNonBitMissions = false; // does this contain any missions with bit set to -1 (practice)?
  2355. bool bContainsBitMissions = false; // does this contain any missions with bit set to anything besides -1 (non-practice)?
  2356. // Parse mission values
  2357. FOR_EACH_VALUE( pKVMissions, pKVMission )
  2358. {
  2359. const char *pszMissionName = pKVMission->GetName();
  2360. int iMissionBit = pKVMission->GetInt();
  2361. // Bounds check our bits. -1 is valid because it means "don't track".
  2362. SCHEMA_INIT_CHECK( iMissionBit >= -1 && iMissionBit <= 31,
  2363. "MvM tour \"%s\" mission \"%s\" specifies invalid tour completion bit %i", tour.m_sTourInternalName.Get(), pszMissionName, iMissionBit );
  2364. // Find our mission information to link to.
  2365. int iMissionIndex = FindMvmMissionByName( pszMissionName );
  2366. SCHEMA_INIT_CHECK( m_vecMvMMissions.IsValidIndex( iMissionIndex ),
  2367. "MvM tour \"%s\" unable to locate mission \"%s\"", tour.m_sTourInternalName.Get(), pszMissionName );
  2368. // Make sure the same tour doesn't contain both badge-adjusting missions and non-adjusting
  2369. // missions.
  2370. bContainsNonBitMissions |= (iMissionBit == -1);
  2371. bContainsBitMissions |= (iMissionBit != -1);
  2372. SCHEMA_INIT_CHECK( !bContainsNonBitMissions || !bContainsBitMissions,
  2373. "MvM tour \"%s\" mission \"%s\" contains both practice (-1 bit) and non-practice missions", tour.m_sTourInternalName.Get(), pszMissionName );
  2374. // Make sure we haven't already used this bit for this tour.
  2375. if ( iMissionBit >= 0 )
  2376. {
  2377. uint32 unMask = 1U << (unsigned int)iMissionBit;
  2378. SCHEMA_INIT_CHECK( (tour.m_nAllChallengesBits & unMask) == 0,
  2379. "MvM tour \"%s\" mission \"%s\" re-uses bit %i", tour.m_sTourInternalName.Get(), pszMissionName, iMissionBit );
  2380. tour.m_nAllChallengesBits |= unMask;
  2381. }
  2382. // Success for this mission.
  2383. MvMTourMission_t mission;
  2384. mission.m_iMissionIndex = iMissionIndex;
  2385. mission.m_iBadgeSlot = iMissionBit;
  2386. tour.m_vecMissions.AddToTail( mission );
  2387. }
  2388. }
  2389. SCHEMA_INIT_CHECK( tour.m_vecMissions.Count() > 0,
  2390. "MvM tour \"%s\" has no missions specified", tour.m_sTourInternalName.Get() );
  2391. m_vecMvMTours.AddToTail( tour );
  2392. }
  2393. return bResult;
  2394. }
  2395. //-----------------------------------------------------------------------------
  2396. const CQuestThemeDefinition *CTFItemSchema::GetQuestThemeByName( const char *pszDefName ) const
  2397. {
  2398. Assert( pszDefName );
  2399. if ( pszDefName )
  2400. {
  2401. FOR_EACH_MAP_FAST( m_mapQuestThemes, i )
  2402. {
  2403. if ( !Q_stricmp( m_mapQuestThemes[ i ]->GetName(), pszDefName ) )
  2404. {
  2405. return m_mapQuestThemes[ i ];
  2406. }
  2407. }
  2408. }
  2409. return NULL;
  2410. }
  2411. //-----------------------------------------------------------------------------
  2412. const CTFQuestObjectiveConditionsDefinition* CTFItemSchema::GetQuestObjectiveConditionByDefIndex( ObjectiveConditionDefIndex_t nDefIndex )
  2413. {
  2414. const CTFQuestObjectiveConditionsDefinition* pDef = NULL;
  2415. auto idx = m_mapQuestObjectiveConditions.Find( nDefIndex );
  2416. if ( idx != m_mapQuestObjectiveConditions.InvalidIndex() )
  2417. {
  2418. pDef = m_mapQuestObjectiveConditions[ idx ];
  2419. }
  2420. return pDef;
  2421. }
  2422. //-----------------------------------------------------------------------------
  2423. const CWarDefinition *CTFItemSchema::GetWarDefinitionByIndex( war_definition_index_t nDefIndex ) const
  2424. {
  2425. auto idx = m_mapWars.Find( nDefIndex );
  2426. if ( idx != m_mapWars.InvalidIndex() )
  2427. {
  2428. return m_mapWars[ idx ];
  2429. }
  2430. return NULL;
  2431. }
  2432. //-----------------------------------------------------------------------------
  2433. const CWarDefinition *CTFItemSchema::GetWarDefinitionByName( const char* pszDefName ) const
  2434. {
  2435. FOR_EACH_MAP_FAST( m_mapWars, i )
  2436. {
  2437. if ( !V_stricmp( pszDefName, m_mapWars[i]->GetDefName() ) )
  2438. {
  2439. return m_mapWars[i];
  2440. }
  2441. }
  2442. return NULL;
  2443. }
  2444. //-----------------------------------------------------------------------------
  2445. const char *CTFItemSchema::GetMvmMissionName( int iChallengeIndex ) const
  2446. {
  2447. if ( iChallengeIndex == k_iMvmMissionIndex_Any )
  2448. return "(any)";
  2449. if ( m_vecMvMMissions.IsValidIndex( iChallengeIndex ) )
  2450. return m_vecMvMMissions[iChallengeIndex].m_sPop.Get();
  2451. Assert( iChallengeIndex == k_iMvmMissionIndex_NotInSchema );
  2452. return "(invalid)";
  2453. }
  2454. //-----------------------------------------------------------------------------
  2455. int CTFItemSchema::FindMvmMissionByName( const char *pszMissionName ) const
  2456. {
  2457. if ( pszMissionName == NULL || *pszMissionName == '\0' )
  2458. return k_iMvmMissionIndex_Any;
  2459. FOR_EACH_VEC( m_vecMvMMissions, i )
  2460. {
  2461. if ( !V_stricmp( m_vecMvMMissions[i].m_sPop.Get(), pszMissionName ) )
  2462. return i;
  2463. }
  2464. return k_iMvmMissionIndex_NotInSchema;
  2465. }
  2466. //-----------------------------------------------------------------------------
  2467. int CTFItemSchema::FindMvmTourByName( const char *pszTourName ) const
  2468. {
  2469. if ( pszTourName == NULL || *pszTourName == '\0' )
  2470. return k_iMvmTourIndex_Empty;
  2471. FOR_EACH_VEC( m_vecMvMTours, i )
  2472. {
  2473. if ( !V_stricmp( m_vecMvMTours[i].m_sTourInternalName.Get(), pszTourName ) )
  2474. return i;
  2475. }
  2476. return k_iMvmTourIndex_NotInSchema;
  2477. }
  2478. //-----------------------------------------------------------------------------
  2479. int CTFItemSchema::FindMvmMissionInTour( int idxTour, int idxMissionInSchema ) const
  2480. {
  2481. if ( idxTour < 0 || idxTour >= m_vecMvMTours.Count() )
  2482. return -1;
  2483. const MvMTour_t &tour = m_vecMvMTours[idxTour];
  2484. FOR_EACH_VEC( tour.m_vecMissions, i )
  2485. {
  2486. if ( tour.m_vecMissions[i].m_iMissionIndex == idxMissionInSchema )
  2487. return i;
  2488. }
  2489. // Not found
  2490. return -1;
  2491. }
  2492. //-----------------------------------------------------------------------------
  2493. int CTFItemSchema::GetMvmMissionBadgeSlotForTour( int idxTour, int idxMissionInSchema ) const
  2494. {
  2495. int idxMissionInTour = FindMvmMissionInTour( idxTour, idxMissionInSchema );
  2496. if ( idxMissionInTour < 0 )
  2497. return -1;
  2498. int iBadgeSlot = m_vecMvMTours[idxTour].m_vecMissions[ idxMissionInTour ].m_iBadgeSlot;
  2499. Assert( iBadgeSlot >= 0 );
  2500. return iBadgeSlot;
  2501. }
  2502. //-----------------------------------------------------------------------------
  2503. const MapDef_t *CTFItemSchema::GetMasterMapDefByName( const char *pszSearchName ) const
  2504. {
  2505. FOR_EACH_VEC( m_vecMasterListOfMaps, i )
  2506. {
  2507. if ( !V_stricmp( m_vecMasterListOfMaps[i]->pszMapName, pszSearchName ) )
  2508. return m_vecMasterListOfMaps[i];
  2509. }
  2510. return NULL;
  2511. }
  2512. //-----------------------------------------------------------------------------
  2513. const MapDef_t *CTFItemSchema::GetMasterMapDefByIndex( MapDefIndex_t unIndex ) const
  2514. {
  2515. FOR_EACH_VEC( m_vecMasterListOfMaps, i )
  2516. {
  2517. if ( m_vecMasterListOfMaps[i]->m_nDefIndex == unIndex )
  2518. return m_vecMasterListOfMaps[i];
  2519. }
  2520. return NULL;
  2521. }
  2522. const SchemaGameCategory_t* CTFItemSchema::GetGameCategory( EGameCategory eType ) const
  2523. {
  2524. auto idx = m_mapGameCategories.Find( eType );
  2525. if ( idx != m_mapGameCategories.InvalidIndex() )
  2526. {
  2527. return m_mapGameCategories[ idx ];
  2528. }
  2529. return NULL;
  2530. }
  2531. const SchemaMMGroup_t* CTFItemSchema::GetMMGroup( EMatchmakingGroupType eCat ) const
  2532. {
  2533. auto idx = m_mapMMGroups.Find( eCat );
  2534. if ( idx != m_mapMMGroups.InvalidIndex() )
  2535. {
  2536. return m_mapMMGroups[ idx ];
  2537. }
  2538. return NULL;
  2539. }
  2540. #ifdef TF_CLIENT_DLL
  2541. //-----------------------------------------------------------------------------
  2542. // Purpose: Returns the number of actual "real" items referenced by the item definition
  2543. // (i.e. items that would take up space in the backpack). Overriding this here
  2544. // to account for map stamps and any other TF-specific item types.
  2545. //-----------------------------------------------------------------------------
  2546. int CTFItemSchema::CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef )
  2547. {
  2548. AssertMsg( pItemDef, "NULL item definition! This should not happen!" );
  2549. if ( !pItemDef )
  2550. return 0;
  2551. if ( pItemDef->GetItemClass() && !Q_strcmp( pItemDef->GetItemClass(), "map_token" ) )
  2552. return 0;
  2553. return CEconItemSchema::CalculateNumberOfConcreteItems( pItemDef );
  2554. }
  2555. #endif // TF_CLIENT_DLL
  2556. RTime32 CTFItemSchema::GetCustomExpirationDate( const char *pszExpirationDate ) const
  2557. {
  2558. if ( !V_stricmp( pszExpirationDate, "end_of_halloween" ) )
  2559. return EconHolidays_TerribleHack_GetHalloweenEndData();
  2560. return CEconItemSchema::GetCustomExpirationDate( pszExpirationDate );
  2561. }
  2562. EMvMChallengeDifficulty GetMvMChallengeDifficultyByInternalName( const char *pszEnglishID )
  2563. {
  2564. if ( !Q_stricmp( pszEnglishID, "normal" ) )
  2565. return k_EMvMChallengeDifficulty_Normal;
  2566. if ( !Q_stricmp( pszEnglishID, "intermediate" ) )
  2567. return k_EMvMChallengeDifficulty_Intermediate;
  2568. if ( !Q_stricmp( pszEnglishID, "advanced" ) )
  2569. return k_EMvMChallengeDifficulty_Advanced;
  2570. if ( !Q_stricmp( pszEnglishID, "expert" ) )
  2571. return k_EMvMChallengeDifficulty_Expert;
  2572. if ( !Q_stricmp( pszEnglishID, "haunted" ) )
  2573. return k_EMvMChallengeDifficulty_Haunted;
  2574. return k_EMvMChallengeDifficulty_Invalid;
  2575. }
  2576. const char *GetMvMChallengeDifficultyLocName( EMvMChallengeDifficulty eDifficulty )
  2577. {
  2578. switch ( eDifficulty )
  2579. {
  2580. default:
  2581. AssertMsg( false, "Bogus challenge difficulty" );
  2582. case k_EMvMChallengeDifficulty_Normal:
  2583. return "#TF_MvM_Normal";
  2584. case k_EMvMChallengeDifficulty_Intermediate:
  2585. return "#TF_MvM_Intermediate";
  2586. case k_EMvMChallengeDifficulty_Advanced:
  2587. return "#TF_MvM_Advanced";
  2588. case k_EMvMChallengeDifficulty_Expert:
  2589. return "#TF_MvM_Expert";
  2590. case k_EMvMChallengeDifficulty_Haunted:
  2591. return "#TF_MvM_Haunted";
  2592. }
  2593. }
  2594. //-----------------------------------------------------------------------------
  2595. // Purpose:
  2596. //-----------------------------------------------------------------------------
  2597. bool CTFItemSchema::BCanStrangeFilterApplyToStrangeSlotInItem( uint32 /*strange_event_restriction_t*/ unRestrictionType, uint32 unRestrictionValue, const IEconItemInterface *pItem, int iStrangeSlot, uint32 *out_pOptionalScoreType ) const
  2598. {
  2599. uint32 unStrangeScoreType;
  2600. if ( !CEconItemSchema::BCanStrangeFilterApplyToStrangeSlotInItem( unRestrictionType, unRestrictionValue, pItem, iStrangeSlot, &unStrangeScoreType ) )
  2601. return false;
  2602. // do not apply to operation type trackers. Makes no sense and is not intended
  2603. if ( unStrangeScoreType == kKillEaterEvent_CosmeticOperationContractsCompleted
  2604. || unStrangeScoreType == kKillEaterEvent_CosmeticOperationKills
  2605. || unStrangeScoreType == kKillEaterEvent_CosmeticOperationContractsPoints
  2606. || unStrangeScoreType == kKillEaterEvent_CosmeticOperationBonusPoints
  2607. ) {
  2608. return false;
  2609. }
  2610. // Items that can't be restored cannot have filters applied
  2611. static CSchemaAttributeDefHandle pAttrDef_CannotRestore( "cannot restore" );
  2612. if ( pAttrDef_CannotRestore && pItem->FindAttribute( pAttrDef_CannotRestore ) )
  2613. return false;
  2614. // Operation Passes cannot have filters
  2615. static CSchemaAttributeDefHandle pAttrDef_IsOperationPass( "is_operation_pass" );
  2616. if ( pAttrDef_IsOperationPass && pItem->FindAttribute( pAttrDef_IsOperationPass ) )
  2617. return false;
  2618. // Strange filters can never apply to gifts-given-out. It doesn't make any sense in any event, but
  2619. // we also only use this for the Spirit of Giving, and didn't really intend for that to be customizable
  2620. // like this.
  2621. if ( unStrangeScoreType == kKillEaterEvent_GiftsGiven )
  2622. return false;
  2623. if ( unRestrictionType == kStrangeEventRestriction_Map )
  2624. {
  2625. const MapDef_t *pSchemaMap = GetItemSchema()->GetMasterMapDefByIndex( unRestrictionValue );
  2626. if ( !pSchemaMap )
  2627. return false;
  2628. if ( unStrangeScoreType == kKillEaterEvent_UnderwaterKill )
  2629. {
  2630. // Don't allow this filter to apply to underwater kills if it's set to filter to a level where
  2631. // no-one can go underwater.
  2632. if ( !pSchemaMap->vecTags.HasElement( GetItemSchema()->GetHandleForTag( "map_has_deep_water" ) ) )
  2633. return false;
  2634. }
  2635. else if ( unStrangeScoreType == kKillEaterEvent_DefenderKill )
  2636. {
  2637. // All TF game modes besides arena have some sort of objective that will count for defender
  2638. // kills -- a flag, a capture point, or a cart.
  2639. const SchemaGameCategory_t* pArenaCategory = GetGameCategory( kGameCategory_Arena );
  2640. FOR_EACH_VEC( pArenaCategory->m_vecEnabledMaps, i )
  2641. {
  2642. if ( pSchemaMap == pArenaCategory->m_vecEnabledMaps[ i ] )
  2643. {
  2644. return false;
  2645. }
  2646. }
  2647. }
  2648. }
  2649. else if (unRestrictionType == kStrangeEventRestriction_Competitive)
  2650. {
  2651. // Get the Current Competitive Season? Assuming its just season 1 right now
  2652. // Just put this in the Schema somewhere I assume
  2653. //return true;
  2654. }
  2655. if ( out_pOptionalScoreType )
  2656. {
  2657. *out_pOptionalScoreType = unStrangeScoreType;
  2658. }
  2659. return true;
  2660. }
  2661. //-----------------------------------------------------------------------------
  2662. // Purpose:
  2663. //-----------------------------------------------------------------------------
  2664. IEconTool *CTFItemSchema::CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
  2665. {
  2666. if ( pszToolType )
  2667. {
  2668. if ( !V_stricmp( pszToolType, "tf_spellbook_page" ) )
  2669. {
  2670. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  2671. if ( pszUsageRestriction ) return NULL;
  2672. return new CEconTool_TFSpellbookPage( pszToolType, unCapabilities );
  2673. }
  2674. if ( !V_stricmp( pszToolType, "tf_event_enable" ) )
  2675. {
  2676. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  2677. if ( pszUsageRestriction ) return NULL;
  2678. if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
  2679. if ( pUsageKV ) return NULL;
  2680. return new CEconTool_TFEventEnableHalloween( pszToolType, pszUseString );
  2681. }
  2682. }
  2683. return CEconItemSchema::CreateEconToolImpl( pszToolType, pszUseString, pszUsageRestriction, unCapabilities, pUsageKV );
  2684. }
  2685. #if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
  2686. int PaintkitAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  2687. {
  2688. char *commandName = "r_texcomp_debug";
  2689. int numMatches = 0;
  2690. partial += Q_strlen( commandName );
  2691. while ( partial[ 0 ] != 0 && V_isspace( partial[ 0 ] ) )
  2692. ++partial;
  2693. int partialLen = Q_strlen( partial );
  2694. // Don't autocomplete until there are at least 3 characters to guess on.
  2695. if ( partialLen < 3 )
  2696. return 0;
  2697. Assert( GetItemSchema() );
  2698. const CEconItemSchema::ItemPaintKitMap_t& paintKits = GetItemSchema()->GetItemPaintKits();
  2699. FOR_EACH_MAP_FAST( paintKits, i )
  2700. {
  2701. const char* pThisKeyName = paintKits.Key( i );
  2702. if ( Q_stristr( pThisKeyName, partial ) != NULL )
  2703. Q_snprintf( commands[ numMatches++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", commandName, pThisKeyName );
  2704. if ( numMatches == COMMAND_COMPLETION_MAXITEMS )
  2705. break;
  2706. }
  2707. return numMatches;
  2708. }
  2709. CON_COMMAND_F_COMPLETION( r_texcomp_debug, "Usage: r_texcomp_debug <paintkit_name> [wear level=1]", 0, PaintkitAutocomplete )
  2710. {
  2711. if ( args.ArgC() < 2 )
  2712. {
  2713. Msg( "usage: r_texcomp_debug <paintkit_name> [wear level=1]\n" );
  2714. return;
  2715. }
  2716. int wearlevel = ( args.ArgC() >= 3 )
  2717. ? atoi( args[ 2 ] )
  2718. : 1;
  2719. Assert( GetItemSchema() );
  2720. const CEconItemSchema::ItemPaintKitMap_t& paintKits = GetItemSchema()->GetItemPaintKits();
  2721. int ndx = paintKits.Find( args[ 1 ] );
  2722. if ( ndx == paintKits.InvalidIndex() )
  2723. {
  2724. Msg( "Couldn't find paintkit named %s\n", args[ 1 ] );
  2725. return;
  2726. }
  2727. KeyValues* pKV = paintKits[ ndx ]->GetPaintKitWearKV( wearlevel );
  2728. if ( pKV == NULL )
  2729. {
  2730. Msg( "Couldn't find wear level %d for painkit %s\n", wearlevel, args[ 1 ] );
  2731. return;
  2732. }
  2733. ITextureCompositor* pWeaponSkinBaseCompositor = materials->NewTextureCompositor( 1, 1, args[ 1 ], TF_TEAM_RED, 0, pKV, TEX_COMPOSITE_CREATE_FLAGS_LOG_NODES_ONLY );
  2734. Assert( pWeaponSkinBaseCompositor == NULL ); pWeaponSkinBaseCompositor;
  2735. }
  2736. #endif // STAGING_ONLY && CLIENT_DLL