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.

345 lines
8.4 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "decals.h"
  8. #include "igamesystem.h"
  9. #include "utlsymbol.h"
  10. #include "utldict.h"
  11. #include "KeyValues.h"
  12. #include "filesystem.h"
  13. #ifdef CLIENT_DLL
  14. #include "iefx.h"
  15. #endif
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. #define DECAL_LIST_FILE "scripts/decals_subrect.txt"
  19. //#define DECAL_LIST_FILE "scripts/decals.txt"
  20. #define TRANSLATION_DATA_SECTION "TranslationData"
  21. //-----------------------------------------------------------------------------
  22. // Purpose:
  23. //-----------------------------------------------------------------------------
  24. class CDecalEmitterSystem : public IDecalEmitterSystem, public CAutoGameSystem
  25. {
  26. public:
  27. CDecalEmitterSystem( char const *name ) : CAutoGameSystem( name )
  28. {
  29. }
  30. virtual bool Init();
  31. virtual void Shutdown();
  32. virtual void LevelInitPreEntity();
  33. // Public interface
  34. virtual int GetDecalIndexForName( char const *decalname );
  35. virtual const char *GetDecalNameForIndex( int nIndex );
  36. virtual char const *TranslateDecalForGameMaterial( char const *decalName, unsigned char gamematerial );
  37. private:
  38. char const *ImpactDecalForGameMaterial( int gamematerial );
  39. void LoadDecalsFromScript( char const *filename );
  40. void Clear();
  41. struct DecalListEntry
  42. {
  43. DecalListEntry()
  44. {
  45. name = UTL_INVAL_SYMBOL;
  46. precache_index = -1;
  47. weight = 1.0f;
  48. }
  49. CUtlSymbol name;
  50. int precache_index;
  51. float weight;
  52. };
  53. struct DecalEntry
  54. {
  55. DecalEntry()
  56. {
  57. }
  58. DecalEntry( const DecalEntry& src )
  59. {
  60. int c = src.indices.Count();
  61. for ( int i = 0; i < c; i++ )
  62. {
  63. indices.AddToTail( src.indices[ i ] );
  64. }
  65. }
  66. DecalEntry& operator = ( const DecalEntry& src )
  67. {
  68. if ( this == &src )
  69. return *this;
  70. int c = src.indices.Count();
  71. for ( int i = 0; i < c; i++ )
  72. {
  73. indices.AddToTail( src.indices[ i ] );
  74. }
  75. return *this;
  76. }
  77. CUtlVector< int > indices;
  78. };
  79. CUtlVector< DecalListEntry > m_AllDecals;
  80. CUtlDict< DecalEntry, int > m_Decals;
  81. CUtlSymbolTable m_DecalFileNames;
  82. CUtlDict< int, int > m_GameMaterialTranslation;
  83. };
  84. static CDecalEmitterSystem g_DecalSystem( "CDecalEmitterSystem" );
  85. IDecalEmitterSystem *decalsystem = &g_DecalSystem;
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. // Input : *decalname -
  89. // Output : int
  90. //-----------------------------------------------------------------------------
  91. int CDecalEmitterSystem::GetDecalIndexForName( char const *decalname )
  92. {
  93. if ( !decalname || !decalname[ 0 ] )
  94. return -1;
  95. int idx = m_Decals.Find( decalname );
  96. if ( idx == m_Decals.InvalidIndex() )
  97. return -1;
  98. DecalEntry *e = &m_Decals[ idx ];
  99. Assert( e );
  100. int count = e->indices.Count();
  101. if ( count <= 0 )
  102. return -1;
  103. float totalweight = 0.0f;
  104. int slot = 0;
  105. for ( int i = 0; i < count; i++ )
  106. {
  107. idx = e->indices[ i ];
  108. DecalListEntry *item = &m_AllDecals[ idx ];
  109. Assert( item );
  110. if ( !totalweight )
  111. {
  112. slot = idx;
  113. }
  114. // Always assume very first slot will match
  115. totalweight += item->weight;
  116. if ( !totalweight || random->RandomFloat(0,totalweight) < item->weight )
  117. {
  118. slot = idx;
  119. }
  120. }
  121. return m_AllDecals[ slot ].precache_index;
  122. }
  123. const char *CDecalEmitterSystem::GetDecalNameForIndex( int nIndex )
  124. {
  125. for ( int nDecal = 0; nDecal < m_AllDecals.Count(); ++nDecal )
  126. {
  127. if ( m_AllDecals[ nDecal ].precache_index == nIndex )
  128. {
  129. return m_DecalFileNames.String( m_AllDecals[ nDecal ].name );
  130. }
  131. }
  132. return "";
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose:
  136. // Output : Returns true on success, false on failure.
  137. //-----------------------------------------------------------------------------
  138. bool CDecalEmitterSystem::Init()
  139. {
  140. LoadDecalsFromScript( DECAL_LIST_FILE );
  141. return true;
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. //-----------------------------------------------------------------------------
  146. void CDecalEmitterSystem::LevelInitPreEntity()
  147. {
  148. // Precache all entries
  149. int c = m_AllDecals.Count();
  150. for ( int i = 0 ; i < c; i++ )
  151. {
  152. DecalListEntry& e = m_AllDecals[ i ];
  153. #if defined( CLIENT_DLL )
  154. e.precache_index = effects->Draw_DecalIndexFromName( (char *)m_DecalFileNames.String( e.name ) );
  155. #else
  156. e.precache_index = engine->PrecacheDecal( m_DecalFileNames.String( e.name ) );
  157. #endif
  158. }
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose:
  162. // Input : *filename -
  163. //-----------------------------------------------------------------------------
  164. void CDecalEmitterSystem::LoadDecalsFromScript( char const *filename )
  165. {
  166. KeyValues *kv = new KeyValues( filename );
  167. Assert( kv );
  168. if ( kv )
  169. {
  170. KeyValues *translation = NULL;
  171. #ifndef _XBOX
  172. if ( kv->LoadFromFile( filesystem, filename ) )
  173. #else
  174. if ( kv->LoadFromFile( filesystem, filename, "GAME" ) )
  175. #endif
  176. {
  177. KeyValues *p = kv;
  178. while ( p )
  179. {
  180. if ( p->GetFirstSubKey() )
  181. {
  182. char const *keyname = p->GetName();
  183. if ( !Q_stricmp( keyname, TRANSLATION_DATA_SECTION ) )
  184. {
  185. translation = p;
  186. }
  187. else
  188. {
  189. DecalEntry entry;
  190. for ( KeyValues *sub = p->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  191. {
  192. MEM_ALLOC_CREDIT();
  193. DecalListEntry decal;
  194. decal.precache_index = -1;
  195. decal.name = m_DecalFileNames.AddString( sub->GetName() );
  196. decal.weight = sub->GetFloat();
  197. // Add to global list
  198. int idx = m_AllDecals.AddToTail( decal );
  199. // Add index only to local list
  200. entry.indices.AddToTail( idx );
  201. }
  202. // Add entry to main dictionary
  203. m_Decals.Insert( keyname, entry );
  204. }
  205. }
  206. p = p->GetNextKey();
  207. }
  208. }
  209. else
  210. {
  211. Msg( "CDecalEmitterSystem::LoadDecalsFromScript: Unable to load '%s'\n", filename );
  212. }
  213. if ( !translation )
  214. {
  215. Msg( "CDecalEmitterSystem::LoadDecalsFromScript: Script '%s' missing section '%s'\n",
  216. filename,
  217. TRANSLATION_DATA_SECTION );
  218. }
  219. else
  220. {
  221. // Now parse game material to entry translation table
  222. for ( KeyValues *sub = translation->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  223. {
  224. // Don't add NULL string to list
  225. if ( !Q_stricmp( sub->GetString(), "" ) )
  226. continue;
  227. int idx = m_Decals.Find( sub->GetString() );
  228. if ( idx != m_Decals.InvalidIndex() )
  229. {
  230. m_GameMaterialTranslation.Insert( sub->GetName(), idx );
  231. }
  232. else
  233. {
  234. Msg( "CDecalEmitterSystem::LoadDecalsFromScript: Translation for game material type '%s' references unknown decal '%s'\n",
  235. sub->GetName(), sub->GetString() );
  236. }
  237. }
  238. }
  239. kv->deleteThis();
  240. }
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose:
  244. // Input : gamematerial -
  245. // Output : char const
  246. //-----------------------------------------------------------------------------
  247. char const *CDecalEmitterSystem::ImpactDecalForGameMaterial( int gamematerial )
  248. {
  249. char gm[ 2 ];
  250. gm[0] = (char)gamematerial;
  251. gm[1] = 0;
  252. int idx = m_GameMaterialTranslation.Find( gm );
  253. if ( idx == m_GameMaterialTranslation.InvalidIndex() )
  254. return NULL;
  255. return m_Decals.GetElementName( m_GameMaterialTranslation.Element(idx) );
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose:
  259. //-----------------------------------------------------------------------------
  260. void CDecalEmitterSystem::Shutdown()
  261. {
  262. Clear();
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. //-----------------------------------------------------------------------------
  267. void CDecalEmitterSystem::Clear()
  268. {
  269. m_DecalFileNames.RemoveAll();
  270. m_Decals.Purge();
  271. m_AllDecals.Purge();
  272. m_GameMaterialTranslation.Purge();
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose:
  276. // Input : *decalName -
  277. // gamematerial -
  278. // Output : char const
  279. //-----------------------------------------------------------------------------
  280. char const *CDecalEmitterSystem::TranslateDecalForGameMaterial( char const *decalName, unsigned char gamematerial )
  281. {
  282. if ( gamematerial == CHAR_TEX_CONCRETE )
  283. return decalName;
  284. if ( !Q_stricmp( decalName, "Impact.Concrete" ) )
  285. {
  286. if ( gamematerial == '-' )
  287. return "";
  288. char const *d = ImpactDecalForGameMaterial( gamematerial );
  289. if ( d )
  290. {
  291. return d;
  292. }
  293. }
  294. return decalName;
  295. }