Advertisement
Guest User

Untitled

a guest
Sep 29th, 2010
196
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 109.46 KB | None | 0 0
  1. #include common_scripts\utility;
  2. #using_animtree( "destructibles" );
  3.  
  4. // Car alarm constants
  5. MAX_SIMULTANEOUS_CAR_ALARMS = 2;
  6. CAR_ALARM_ALIAS = "car_alarm";
  7. CAR_ALARM_OFF_ALIAS = "car_alarm_off";
  8. NO_CAR_ALARM_MAX_ELAPSED_TIME = 120;
  9. CAR_ALARM_TIMEOUT = 25;
  10. DESTROYED_ATTACHMENT_SUFFIX = "_destroy";
  11.  
  12. SP_DAMAGE_BIAS = 0.5;
  13. SP_EXPLOSIVE_DAMAGE_BIAS = 9.0;
  14.  
  15. MP_DAMAGE_BIAS = 1.0;
  16. MP_EXPLOSIVE_DAMAGE_BIAS = 13.0;
  17.  
  18. SP_SHOTGUN_BIAS = 8.0;
  19. MP_SHOTGUN_BIAS = 4.0;
  20.  
  21. init()
  22. {
  23. /#
  24. SetDevDvarIfUninitialized( "debug_destructibles", "0" );
  25. SetDevDvarIfUninitialized( "destructibles_enable_physics", "1" );
  26. SetDevDvarIfUninitialized( "destructibles_show_radiusdamage", "0" );
  27. #/
  28.  
  29. level.destructibleSpawnedEntsLimit = 50;
  30. level.destructibleSpawnedEnts = [];
  31. level.currentCarAlarms = 0;
  32. level.commonStartTime = GetTime();
  33.  
  34. /#
  35. level.created_destructibles = [];
  36. #/
  37.  
  38. if ( !isdefined( level.func ) )
  39. {
  40. // this array will be filled with code commands that SP or MP may use but doesn't exist in the other.
  41. level.func = [];
  42. }
  43.  
  44. destructibles_enabled = true;
  45. /#
  46. destructibles_enabled = ( GetDvarInt( "destructibles_enabled", 1 ) == 1 );
  47. #/
  48.  
  49. if ( destructibles_enabled )
  50. find_destructibles();
  51.  
  52. deletables = GetEntArray( "delete_on_load", "targetname" );
  53. foreach ( ent in deletables )
  54. ent Delete();
  55.  
  56. /#
  57. SetDevDvarIfUninitialized( "scr_destructible_warning", "1" );
  58. if ( GetDvarInt( "scr_destructible_warning", 1 ) == 1 )
  59. thread warn_about_old_destructible();
  60. #/
  61.  
  62. init_destroyed_count();
  63. init_destructible_frame_queue();
  64. }
  65.  
  66. warn_about_old_destructible()
  67. {
  68. wait 1;
  69.  
  70. // Find all old prefabs and print warning/errors about them so they get updated
  71. destructibles = GetEntArray( "destructible", "targetname" );
  72. if ( destructibles.size != 0 )
  73. {
  74. PrintLn( "This map contains old destructible vehicle prefabs which no longer work properly. Please update them to use the new vehicle prefabs in map_source\\prefabs\\destructible\\. See console for a list of vehicles that you need to updated." );
  75.  
  76. PrintLn( "^1###################################################^0" );
  77. PrintLn( "^1###################################################^0" );
  78. PrintLn( "^1###################################################^0" );
  79.  
  80. foreach ( vehicle in destructibles )
  81. PrintLn( "^1Destructible vehicle at ( " + vehicle.origin + " ) uses an old prefab. Update it to use a prefab located in prefabs\destructible\^0" );
  82.  
  83. PrintLn( "^1###################################################^0" );
  84. PrintLn( "^1###################################################^0" );
  85. PrintLn( "^1###################################################^0" );
  86.  
  87. AssertMsg( "This map contains old destructible vehicle prefabs which no longer work properly. Please update them to use the new vehicle prefabs in map_source\\prefabs\\destructible\\. See console for a list of vehicles that you need to updated." );
  88. }
  89. }
  90.  
  91. find_destructibles()
  92. {
  93. //---------------------------------------------------------------------
  94. // Find all destructibles by their targetnames and run the setup
  95. //---------------------------------------------------------------------
  96.  
  97. // array_thread( GetEntArray( "destructible_vehicle", "targetname" ), ::setup_destructibles );
  98.  
  99. //assuring orders -nate
  100. vehicles = GetEntArray( "destructible_vehicle", "targetname" );
  101. foreach ( vehicle in vehicles )
  102. vehicle setup_destructibles();
  103.  
  104. destructible_toy = GetEntArray( "destructible_toy", "targetname" );
  105. foreach ( toy in destructible_toy )
  106. toy setup_destructibles();
  107.  
  108. /#
  109. total = 0;
  110. if ( GetDvarInt( "destructibles_locate" ) > 0 )
  111. {
  112. // Print out the destructibles we created and where they are all located
  113. PrintLn( "##################" );
  114. PrintLn( "DESTRUCTIBLE LIST:" );
  115. PrintLn( "##################" );
  116. PrintLn( "" );
  117.  
  118. keys = GetArrayKeys( level.created_destructibles );
  119. foreach ( key in keys )
  120. {
  121. PrintLn( key + ": " + level.created_destructibles[ key ].size );
  122. total += level.created_destructibles[ key ].size;
  123. }
  124. PrintLn( "" );
  125. PrintLn( "Total: " + total );
  126. PrintLn( "" );
  127. PrintLn( "Locations:" );
  128.  
  129. foreach ( key in keys )
  130. {
  131. foreach ( destructible in level.created_destructibles[ key ] )
  132. {
  133. PrintLn( key + ": " + destructible.origin );
  134. //destructible thread maps\_debug::drawOrgForever();
  135. }
  136. }
  137.  
  138. PrintLn( "" );
  139. PrintLn( "##################" );
  140. PrintLn( "##################" );
  141. PrintLn( "##################" );
  142.  
  143. level.created_destructibles = undefined;
  144. }
  145. #/
  146.  
  147. }
  148.  
  149. setup_destructibles( cached )
  150. {
  151. if ( !isdefined( cached ) )
  152. cached = false;
  153.  
  154. //---------------------------------------------------------------------
  155. // Figure out what destructible information this entity should use
  156. //---------------------------------------------------------------------
  157. destuctableInfo = undefined;
  158. AssertEx( IsDefined( self.destructible_type ), "Destructible object with targetname 'destructible' does not have a 'destructible_type' key / value" );
  159.  
  160. self.modeldummyon = false;// - nate added for vehicle dummy stuff. This is so I can turn a destructible into a dummy and throw it around on jeepride.
  161. self add_damage_owner_recorder(); // Mackey added to track who is damaging the car
  162.  
  163. self.destuctableInfo = common_scripts\_destructible_types::makeType( self.destructible_type );
  164. //println( "### DESTRUCTIBLE ### assigned infotype index: " + self.destuctableInfo );
  165. if ( self.destuctableInfo < 0 )
  166. return;
  167.  
  168. /#
  169. // Store what destructibles we create and where they are located so we can get a list in the console
  170. if ( !isdefined( level.created_destructibles[ self.destructible_type ] ) )
  171. level.created_destructibles[ self.destructible_type ] = [];
  172. nextIndex = level.created_destructibles[ self.destructible_type ].size;
  173. level.created_destructibles[ self.destructible_type ][ nextIndex ] = self;
  174. #/
  175.  
  176. if ( !cached )
  177. precache_destructibles();
  178.  
  179. add_destructible_fx();
  180.  
  181. //---------------------------------------------------------------------
  182. // Attach all parts to the entity
  183. //---------------------------------------------------------------------
  184. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts ) )
  185. {
  186. self.destructible_parts = [];
  187. for ( i = 0; i < level.destructible_type[ self.destuctableInfo ].parts.size; i++ )
  188. {
  189. // create the struct where the info for each entity will be held
  190. self.destructible_parts[ i ] = SpawnStruct();
  191.  
  192. // set it's current state to 0 since it has never taken damage yet and will be on it's first state
  193. self.destructible_parts[ i ].v[ "currentState" ] = 0;
  194.  
  195. // if it has a health value then store it's value
  196. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "health" ] ) )
  197. self.destructible_parts[ i ].v[ "health" ] = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "health" ];
  198.  
  199. // find random attachements such as random advertisements on taxi cabs and attach them now
  200. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "random_dynamic_attachment_1" ] ) )
  201. {
  202. randAttachmentIndex = RandomInt( level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "random_dynamic_attachment_1" ].size );
  203. attachTag = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "random_dynamic_attachment_tag" ][ randAttachmentIndex ];
  204. attach_model_1 = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "random_dynamic_attachment_1" ][ randAttachmentIndex ];
  205. attach_model_2 = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "random_dynamic_attachment_2" ][ randAttachmentIndex ];
  206. clipToRemove = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "clipToRemove" ][ randAttachmentIndex ];
  207. self thread do_random_dynamic_attachment( attachTag, attach_model_1, attach_model_2, clipToRemove );
  208. }
  209.  
  210. // continue if it's the base model since its not an attached part
  211. if ( i == 0 )
  212. continue;
  213.  
  214. // attach the part now
  215. modelName = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "modelName" ];
  216. tagName = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "tagName" ];
  217.  
  218. stateIndex = 1;
  219. while ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ stateIndex ] ) )
  220. {
  221. stateTagName = level.destructible_type[ self.destuctableInfo ].parts[ i ][ stateIndex ].v[ "tagName" ];
  222. stateModelName = level.destructible_type[ self.destuctableInfo ].parts[ i ][ stateIndex ].v[ "modelName" ];
  223. if ( IsDefined( stateTagName ) && stateTagName != tagName )
  224. {
  225. self hideapart( stateTagName );
  226. if ( self.modeldummyon )
  227. self.modeldummy hideapart( stateTagName );
  228. }
  229. stateIndex++;
  230. }
  231. }
  232. }
  233.  
  234. // some destructibles have collision that needs to change due to the large change in the destructible when it blows pu
  235. if ( IsDefined( self.target ) )
  236. thread destructible_handles_collision_brushes();
  237.  
  238. //---------------------------------------------------------------------
  239. // Make this entity take damage and wait for events
  240. //---------------------------------------------------------------------
  241. if ( self.code_classname != "script_vehicle" )
  242. self SetCanDamage( true );
  243. if ( isSP() )
  244. self thread connectTraverses();
  245. self thread destructible_think();
  246. }
  247.  
  248. destructible_create( type, tagName, health, validAttackers, validDamageZone, validDamageCause )
  249. {
  250. //---------------------------------------------------------------------
  251. // Creates a new information structure for a destructible object
  252. //---------------------------------------------------------------------
  253. Assert( IsDefined( type ) );
  254.  
  255. if ( !isdefined( level.destructible_type ) )
  256. level.destructible_type = [];
  257.  
  258. destructibleIndex = level.destructible_type.size;
  259.  
  260.  
  261. destructibleIndex = level.destructible_type.size;
  262. level.destructible_type[ destructibleIndex ] = SpawnStruct();
  263. level.destructible_type[ destructibleIndex ].v[ "type" ] = type;
  264.  
  265. level.destructible_type[ destructibleIndex ].parts = [];
  266. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ] = SpawnStruct();
  267. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "modelName" ] = self.model;
  268. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "tagName" ] = tagName;
  269. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "health" ] = health;
  270. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "validAttackers" ] = validAttackers;
  271. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "validDamageZone" ] = validDamageZone;
  272. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "validDamageCause" ] = validDamageCause;
  273. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "godModeAllowed" ] = true;
  274. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "rotateTo" ] = self.angles;
  275. level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "vehicle_exclude_anim" ] = false;
  276. }
  277.  
  278. destructible_part( tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, alsoDamageParent, physicsOnExplosion, grenadeImpactDeath, receiveDamageFromParent )
  279. {
  280. //---------------------------------------------------------------------
  281. // Adds a part to the last created destructible information structure
  282. //---------------------------------------------------------------------
  283. destructibleIndex = ( level.destructible_type.size - 1 );
  284. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  285. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts.size ) );
  286.  
  287. partIndex = level.destructible_type[ destructibleIndex ].parts.size;
  288. Assert( partIndex > 0 );
  289.  
  290. stateIndex = 0;
  291.  
  292. destructible_info( partIndex, stateIndex, tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, alsoDamageParent, physicsOnExplosion, grenadeImpactDeath, undefined, receiveDamageFromParent );
  293. }
  294.  
  295. destructible_state( tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, grenadeImpactDeath, splashRotation )
  296. {
  297. //---------------------------------------------------------------------
  298. // Adds a new part that is a state of the last created part
  299. // When the previous part reaches zero health this part will show up
  300. // and the previous part will be removed
  301. //---------------------------------------------------------------------
  302.  
  303. destructibleIndex = ( level.destructible_type.size - 1 );
  304. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  305. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size );
  306.  
  307. if ( !isdefined( tagName ) && partIndex == 0 )
  308. tagName = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ 0 ].v[ "tagName" ];
  309.  
  310. destructible_info( partIndex, stateIndex, tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, undefined, undefined, grenadeImpactDeath, splashRotation );
  311. }
  312.  
  313. destructible_fx( tagName, fxName, useTagAngles, damageType, groupNum, fxCost )
  314. {
  315. //assert( IsDefined( tagName ) );
  316. Assert( IsDefined( fxName ) );
  317.  
  318. if ( !isdefined( useTagAngles ) )
  319. useTagAngles = true;
  320.  
  321. if ( !isdefined( groupNum ) )
  322. groupNum = 0;
  323.  
  324. if ( !isdefined( fxCost ) )
  325. fxCost = 0;
  326.  
  327. destructibleIndex = ( level.destructible_type.size - 1 );
  328. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  329. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  330.  
  331. Assert( IsDefined( level.destructible_type ) );
  332. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  333. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  334. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  335. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  336.  
  337. fx_size = 0;
  338. if ( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ] ) )
  339. if ( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ][ groupNum ] ) )
  340. fx_size = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ][ groupNum ].size;
  341.  
  342. if ( IsDefined( damageType ) )
  343. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_valid_damagetype" ][ groupNum ][ fx_size ] = damageType;
  344.  
  345. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ][ groupNum ][ fx_size ] = fxName;
  346. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_tag" ][ groupNum ][ fx_size ] = tagName;
  347. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_useTagAngles" ][ groupNum ][ fx_size ] = useTagAngles;
  348. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_cost" ][ groupNum ][ fx_size ] = fxCost;
  349. }
  350.  
  351. destructible_loopfx( tagName, fxName, loopRate, fxCost )
  352. {
  353. Assert( IsDefined( tagName ) );
  354. Assert( IsDefined( fxName ) );
  355. Assert( IsDefined( loopRate ) );
  356. Assert( loopRate > 0 );
  357.  
  358. if ( !isdefined( fxCost ) )
  359. fxCost = 0;
  360.  
  361. destructibleIndex = ( level.destructible_type.size - 1 );
  362. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  363. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  364.  
  365. Assert( IsDefined( level.destructible_type ) );
  366. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  367. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  368. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  369. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  370.  
  371. fx_size = 0;
  372. if ( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_filename" ] ) )
  373. fx_size = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_filename" ].size;
  374.  
  375. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_filename" ][ fx_size ] = fxName;
  376. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_tag" ][ fx_size ] = tagName;
  377. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_rate" ][ fx_size ] = loopRate;
  378. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_cost" ][ fx_size ] = fxCost;
  379. }
  380.  
  381. destructible_healthdrain( amount, interval, badplaceRadius, badplaceTeam )
  382. {
  383. Assert( IsDefined( amount ) );
  384.  
  385. destructibleIndex = ( level.destructible_type.size - 1 );
  386. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  387. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  388.  
  389. Assert( IsDefined( level.destructible_type ) );
  390. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  391. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  392. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  393. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  394.  
  395. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "healthdrain_amount" ] = amount;
  396. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "healthdrain_interval" ] = interval;
  397. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "badplace_radius" ] = badplaceRadius;
  398. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "badplace_team" ] = badplaceTeam;
  399. }
  400.  
  401. destructible_sound( soundAlias, soundCause, groupNum )
  402. {
  403. Assert( IsDefined( soundAlias ) );
  404.  
  405. destructibleIndex = ( level.destructible_type.size - 1 );
  406. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  407. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  408.  
  409. Assert( IsDefined( level.destructible_type ) );
  410. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  411. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  412. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  413. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  414.  
  415. if ( !isdefined( groupNum ) )
  416. groupNum = 0;
  417.  
  418. if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ] ) )
  419. {
  420. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ] = [];
  421. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "soundCause" ] = [];
  422. }
  423.  
  424. if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ] ) )
  425. {
  426. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ] = [];
  427. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "soundCause" ][ groupNum ] = [];
  428. }
  429.  
  430. index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ].size;
  431. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ][ index ] = soundAlias;
  432. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "soundCause" ][ groupNum ][ index ] = soundCause;
  433. }
  434.  
  435.  
  436. destructible_loopsound( soundAlias, loopsoundCause )
  437. {
  438. Assert( IsDefined( soundAlias ) );
  439.  
  440. destructibleIndex = ( level.destructible_type.size - 1 );
  441. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  442. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  443.  
  444. Assert( IsDefined( level.destructible_type ) );
  445. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  446. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  447. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  448. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  449.  
  450. if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ] ) )
  451. {
  452. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ] = [];
  453. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsoundCause" ] = [];
  454. }
  455.  
  456. index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ].size;
  457. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ][ index ] = soundAlias;
  458. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsoundCause" ][ index ] = loopsoundCause;
  459. }
  460.  
  461. destructible_anim( animName, animTree, animType, vehicle_exclude, groupNum, mpAnim, maxStartDelay, animRateMin, animRateMax )
  462. {
  463. if ( !isdefined( vehicle_exclude ) )
  464. vehicle_exclude = false;
  465.  
  466. Assert( IsDefined( anim ) );
  467. Assert( IsDefined( animName ) );
  468. Assert( IsDefined( animtree ) );
  469.  
  470. if ( !isdefined( groupNum ) )
  471. groupNum = 0;
  472.  
  473. array = [];
  474. array[ "anim" ] = animName;
  475. array[ "animTree" ] = animtree;
  476. array[ "animType" ] = animType;
  477. array[ "vehicle_exclude_anim" ] = vehicle_exclude;
  478. array[ "groupNum" ] = groupNum;
  479. array[ "mpAnim" ] = mpAnim;
  480. array[ "maxStartDelay" ] = maxStartDelay;
  481. array[ "animRateMin" ] = animRateMin;
  482. array[ "animRateMax" ] = animRateMax;
  483. add_array_to_destructible( "animation", array );
  484. }
  485.  
  486. destructible_spotlight( tag )
  487. {
  488. AssertEx( IsDefined( tag ), "Tag wasn't defined for destructible_spotlight" );
  489. array = [];
  490. array[ "spotlight_tag" ] = tag;
  491. array[ "spotlight_fx" ] = "spotlight_fx";
  492. array[ "spotlight_brightness" ] = 0.85;
  493. array[ "randomly_flip" ] = true;
  494.  
  495. dvars = [];
  496. dvars[ "r_spotlightendradius" ] = 1200;
  497. dvars[ "r_spotlightstartradius" ] = 50;
  498. array[ "dvars" ] = dvars;
  499.  
  500. add_keypairs_to_destructible( array );
  501. }
  502.  
  503. add_key_to_destructible( key, val )
  504. {
  505. AssertEx( IsDefined( key ), "Key wasn't defined!" );
  506. AssertEx( IsDefined( val ), "Val wasn't defined!" );
  507.  
  508. array = [];
  509. array[ key ] = val;
  510. add_keypairs_to_destructible( array );
  511. }
  512.  
  513. /*
  514. =============
  515. ///ScriptDocBegin
  516. "Name: add_keypairs_to_destructible( <array> )"
  517. "Summary: Goes through the array and adds each key/val to .v."
  518. "Module: Destructibles"
  519. "MandatoryArg: <array>: Array of keypairs."
  520. "Example: add_keypairs_to_destructible( array );"
  521. "SPMP: singleplayer"
  522. ///ScriptDocEnd
  523. =============
  524. */
  525. add_keypairs_to_destructible( array )
  526. {
  527. // add a single flat array to the destructible, overwriting any existing identical keys.
  528. destructibleIndex = level.destructible_type.size - 1;
  529. partIndex = level.destructible_type[ destructibleIndex ].parts.size - 1;
  530. stateIndex = level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1;
  531.  
  532. Assert( IsDefined( level.destructible_type ) );
  533. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  534. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  535. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  536. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  537.  
  538. foreach ( key, val in array )
  539. {
  540. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ key ] = val;
  541. }
  542. }
  543.  
  544. /*
  545. =============
  546. ///ScriptDocBegin
  547. "Name: add_array_to_destructible( <array_name> , <array> )"
  548. "Summary: Goes through the array and adds each key/val to .v."
  549. "Module: Destructibles"
  550. "MandatoryArg: <array_name>: Array of keypairs."
  551. "MandatoryArg: <array>: Array of keypairs."
  552. "Example: add_array_to_destructible( array );"
  553. "SPMP: singleplayer"
  554. ///ScriptDocEnd
  555. =============
  556. */
  557. add_array_to_destructible( array_name, array )
  558. {
  559. // add an array under a key name, so you can have multiple arrays under a given key name
  560. destructibleIndex = level.destructible_type.size - 1;
  561. partIndex = level.destructible_type[ destructibleIndex ].parts.size - 1;
  562. stateIndex = level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1;
  563.  
  564. Assert( IsDefined( level.destructible_type ) );
  565. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  566. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  567. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  568. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  569.  
  570. v = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v;
  571.  
  572. if ( !isdefined( v[ array_name ] ) )
  573. {
  574. v[ array_name ] = [];
  575. }
  576.  
  577. v[ array_name ][ v[ array_name ].size ] = array;
  578.  
  579. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v = v;
  580. }
  581.  
  582. destructible_car_alarm()
  583. {
  584. destructibleIndex = ( level.destructible_type.size - 1 );
  585. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  586. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  587.  
  588. Assert( IsDefined( level.destructible_type ) );
  589. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  590. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  591. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  592. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  593.  
  594. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "triggerCarAlarm" ] = true;
  595. }
  596.  
  597. destructible_lights_out( range )
  598. {
  599. if ( !isdefined( range ) )
  600. range = 256;
  601.  
  602. destructibleIndex = ( level.destructible_type.size - 1 );
  603. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  604. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  605.  
  606. Assert( IsDefined( level.destructible_type ) );
  607. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  608. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  609. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  610. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  611.  
  612. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "break_nearby_lights" ] = range;
  613. }
  614.  
  615. random_dynamic_attachment( tagName, attachment_1, attachment_2, clipToRemove )
  616. {
  617. // made so I can put random advertisements on the destructible taxi cabs without making lots of destructible types for each version
  618.  
  619. Assert( IsDefined( tagName ) );
  620. Assert( IsDefined( attachment_1 ) );
  621.  
  622. if ( !isdefined( attachment_2 ) )
  623. attachment_2 = "";
  624.  
  625. destructibleIndex = ( level.destructible_type.size - 1 );
  626. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  627. //stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  628. stateIndex = 0;
  629.  
  630. Assert( IsDefined( level.destructible_type ) );
  631. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  632. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  633. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  634. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  635.  
  636. if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ] ) )
  637. {
  638. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ] = [];
  639. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_2" ] = [];
  640. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_tag" ] = [];
  641. }
  642.  
  643. index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ].size;
  644. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ][ index ] = attachment_1;
  645. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_2" ][ index ] = attachment_2;
  646. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_tag" ][ index ] = tagName;
  647. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "clipToRemove" ][ index ] = clipToRemove;
  648. }
  649.  
  650. destructible_physics( physTagName, physVelocity )
  651. {
  652. destructibleIndex = ( level.destructible_type.size - 1 );
  653. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  654. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  655.  
  656. Assert( IsDefined( level.destructible_type ) );
  657. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  658. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  659. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  660. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  661.  
  662. if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ] ) )
  663. {
  664. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ] = [];
  665. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_tagName" ] = [];
  666. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_velocity" ] = [];
  667. }
  668.  
  669. index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ].size;
  670. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ][ index ] = true;
  671. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_tagName" ][ index ] = physTagName;
  672. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_velocity" ][ index ] = physVelocity;
  673. }
  674.  
  675. destructible_splash_damage_scaler( damage_multiplier )
  676. {
  677. destructibleIndex = ( level.destructible_type.size - 1 );
  678. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  679. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  680.  
  681. Assert( IsDefined( level.destructible_type ) );
  682. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  683. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  684. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  685. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  686.  
  687. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "splash_damage_scaler" ] = damage_multiplier;
  688. }
  689.  
  690. destructible_explode( force_min, force_max, rangeSP, rangeMP, mindamage, maxdamage, continueDamage, originOffset, earthQuakeScale, earthQuakeRadius )
  691. {
  692. destructibleIndex = ( level.destructible_type.size - 1 );
  693. partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 );
  694. stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 );
  695.  
  696. Assert( IsDefined( level.destructible_type ) );
  697. Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) );
  698. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) );
  699. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) );
  700. Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) );
  701.  
  702. if ( isSP() )
  703. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_range" ] = rangeSP;
  704. else
  705. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_range" ] = rangeMP;
  706.  
  707. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode" ] = true;
  708. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_force_min" ] = force_min;
  709. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_force_max" ] = force_max;
  710. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_mindamage" ] = mindamage;
  711. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_maxdamage" ] = maxdamage;
  712. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "continueDamage" ] = continueDamage;
  713. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "originOffset" ] = originOffset;
  714. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "earthQuakeScale" ] = earthQuakeScale;
  715. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "earthQuakeRadius" ] = earthQuakeRadius;
  716. }
  717.  
  718. destructible_info( partIndex, stateIndex, tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, alsoDamageParent, physicsOnExplosion, grenadeImpactDeath, splashRotation, receiveDamageFromParent )
  719. {
  720. Assert( IsDefined( partIndex ) );
  721. Assert( IsDefined( stateIndex ) );
  722. Assert( IsDefined( level.destructible_type ) );
  723. Assert( level.destructible_type.size > 0 );
  724.  
  725. if ( IsDefined( modelName ) )
  726. modelName = ToLower( modelName );
  727.  
  728. destructibleIndex = ( level.destructible_type.size - 1 );
  729.  
  730. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] = SpawnStruct();
  731. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "modelName" ] = modelName;
  732. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "tagName" ] = tagName;
  733. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "health" ] = health;
  734. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "validAttackers" ] = validAttackers;
  735. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "validDamageZone" ] = validDamageZone;
  736. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "validDamageCause" ] = validDamageCause;
  737. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "alsoDamageParent" ] = alsoDamageParent;
  738. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physicsOnExplosion" ] = physicsOnExplosion;
  739. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "grenadeImpactDeath" ] = grenadeImpactDeath;
  740. // sanity check please. I set this here so that I don't have to do isdefined on every part evertime it gets hit
  741. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "godModeAllowed" ] = false;
  742. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "splashRotation" ] = splashRotation;
  743. level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "receiveDamageFromParent" ] = receiveDamageFromParent;
  744. }
  745.  
  746. precache_destructibles()
  747. {
  748. // I needed this to be seperate for vehicle scripts.
  749. //---------------------------------------------------------------------
  750. // Precache referenced models and load referenced effects
  751. //---------------------------------------------------------------------
  752.  
  753. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts ) )
  754. return;
  755.  
  756. //if ( !isdefined( level.precachedModels ) )
  757. // level.precachedModels = [];
  758.  
  759. for ( i = 0; i < level.destructible_type[ self.destuctableInfo ].parts.size; i++ )
  760. {
  761. for ( j = 0; j < level.destructible_type[ self.destuctableInfo ].parts[ i ].size; j++ )
  762. {
  763. if ( level.destructible_type[ self.destuctableInfo ].parts[ i ].size <= j )
  764. continue;
  765.  
  766. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "modelName" ] ) )
  767. {
  768. //model = level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "modelName" ];
  769. //if ( !isdefined( level.precachedModels[ model ] ) )
  770. //{
  771. // level.precachedModels[ model ] = true;
  772. // println( "precachemodel( " + model + " )" );
  773. //}
  774. PreCacheModel( level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "modelName" ] );
  775. }
  776.  
  777. // in MP we have to precache animations that will be used
  778. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "animation" ] ) )
  779. {
  780. animGroups = level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "animation" ];
  781. foreach ( group in animGroups )
  782. {
  783. if ( IsDefined( group[ "mpAnim" ] ) )
  784. noself_func( "precacheMpAnim", group[ "mpAnim" ] );
  785. }
  786. }
  787.  
  788. // find random attachements such as random advertisements on taxi cabs and precache them now
  789. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "random_dynamic_attachment_1" ] ) )
  790. {
  791. foreach ( model in level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "random_dynamic_attachment_1" ] )
  792. {
  793. if ( IsDefined( model ) && model != "" )
  794. {
  795. PreCacheModel( model );
  796. PreCacheModel( model + DESTROYED_ATTACHMENT_SUFFIX );
  797.  
  798. //if ( !isdefined( level.precachedModels[ model ] ) )
  799. //{
  800. // level.precachedModels[ model ] = true;
  801. // println( "precachemodel( " + model + " )" );
  802. //}
  803.  
  804. //if ( !isdefined( level.precachedModels[ model + DESTROYED_ATTACHMENT_SUFFIX ] ) )
  805. //{
  806. // level.precachedModels[ model + DESTROYED_ATTACHMENT_SUFFIX ] = true;
  807. // println( "precachemodel( " + model + DESTROYED_ATTACHMENT_SUFFIX + " )" );
  808. //}
  809. }
  810. }
  811. foreach ( model in level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "random_dynamic_attachment_2" ] )
  812. {
  813. if ( IsDefined( model ) && model != "" )
  814. {
  815. PreCacheModel( model );
  816. PreCacheModel( model + DESTROYED_ATTACHMENT_SUFFIX );
  817.  
  818. //if ( !isdefined( level.precachedModels[ model ] ) )
  819. //{
  820. // level.precachedModels[ model ] = true;
  821. // println( "precachemodel( " + model + " )" );
  822. //}
  823.  
  824. //if ( !isdefined( level.precachedModels[ model + DESTROYED_ATTACHMENT_SUFFIX ] ) )
  825. //{
  826. // level.precachedModels[ model + DESTROYED_ATTACHMENT_SUFFIX ] = true;
  827. // println( "precachemodel( " + model + DESTROYED_ATTACHMENT_SUFFIX + " )" );
  828. //}
  829. }
  830. }
  831. }
  832. }
  833. }
  834. }
  835.  
  836. add_destructible_fx()
  837. {
  838. // I needed this to be seperate for vehicle scripts.
  839. //---------------------------------------------------------------------
  840. // Precache referenced models and load referenced effects
  841. //---------------------------------------------------------------------
  842.  
  843. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts ) )
  844. return;
  845.  
  846. //if ( !isdefined( level.precachedFX ) )
  847. // level.precachedFX = [];
  848.  
  849. for ( i = 0; i < level.destructible_type[ self.destuctableInfo ].parts.size; i++ )
  850. {
  851. for ( j = 0; j < level.destructible_type[ self.destuctableInfo ].parts[ i ].size; j++ )
  852. {
  853. if ( level.destructible_type[ self.destuctableInfo ].parts[ i ].size <= j )
  854. continue;
  855.  
  856. part = level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ];
  857.  
  858. if ( IsDefined( part.v[ "fx_filename" ] ) )
  859. {
  860. for ( g = 0; g < part.v[ "fx_filename" ].size; g++ )
  861. {
  862. // for multiple checks on fx when doing conditional fx playing
  863. fx_filenames = part.v[ "fx_filename" ][ g ];
  864. if ( IsDefined( fx_filenames ) )
  865. {
  866. // has we already set this up?
  867. if ( IsDefined( part.v[ "fx" ] ) && IsDefined( part.v[ "fx" ][ g ] ) && part.v[ "fx" ][ g ].size == fx_filenames.size )
  868. continue;
  869.  
  870. foreach ( idx, fx_filename in fx_filenames )
  871. {
  872. level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "fx" ][ g ][ idx ] = _loadfx( fx_filename );
  873.  
  874. //if ( !isdefined( level.precachedFX[ fx_filename ] ) )
  875. //{
  876. // level.precachedFX[ fx_filename ] = true;
  877. // println( "loadfx( " + fx_filename + " )" );
  878. //}
  879. }
  880. }
  881. }
  882. }
  883.  
  884. loopfx_filenames = level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "loopfx_filename" ];
  885. if ( IsDefined( loopfx_filenames ) )
  886. {
  887. // has we already set this up?
  888. if ( IsDefined( part.v[ "loopfx" ] ) && part.v[ "loopfx" ].size == loopfx_filenames.size )
  889. continue;
  890.  
  891. foreach ( idx, loopfx_filename in loopfx_filenames )
  892. {
  893. level.destructible_type[ self.destuctableInfo ].parts[ i ][ j ].v[ "loopfx" ][ idx ] = _loadfx( loopfx_filename );
  894.  
  895. //if ( !isdefined( level.precachedFX[ loopfx_filename ] ) )
  896. //{
  897. // level.precachedFX[ loopfx_filename ] = true;
  898. // println( "loadfx( " + loopfx_filename + " )" );
  899. //}
  900. }
  901. }
  902. }
  903. }
  904.  
  905. }
  906.  
  907.  
  908. canDamageDestructible( testDestructible )
  909. {
  910. foreach ( destructible in self.destructibles )
  911. {
  912. if ( destructible == testDestructible )
  913. return true;
  914. }
  915. return false;
  916. }
  917.  
  918. destructible_think()
  919. {
  920. //---------------------------------------------------------------------
  921. // Force it to run update part one time first so we can have parts with
  922. // 0 health that will start on level load instead of waiting for damage
  923. //---------------------------------------------------------------------
  924. damage = 0;
  925. modelName = self.model;
  926. tagName = undefined;
  927. point = self.origin;
  928. direction_vec = undefined;
  929. attacker = undefined;
  930. damageType = undefined;
  931. self destructible_update_part( damage, modelName, tagName, point, direction_vec, attacker, damageType );
  932.  
  933. //---------------------------------------------------------------------
  934. // Wait until this entity takes damage
  935. //---------------------------------------------------------------------
  936. self endon( "stop_taking_damage" );
  937. for ( ;; )
  938. {
  939. // set these to undefined to clear them for each loop to save variables
  940. damage = undefined;
  941. attacker = undefined;
  942. direction_vec = undefined;
  943. point = undefined;
  944. type = undefined;
  945. modelName = undefined;
  946. tagName = undefined;
  947. partName = undefined;
  948. dflags = undefined;
  949.  
  950. self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, dflags );
  951. prof_begin( "_destructible" );
  952.  
  953. if ( !isdefined( damage ) )
  954. continue;
  955. if ( IsDefined( attacker ) && IsDefined( attacker.type ) && attacker.type == "soft_landing" && !attacker canDamageDestructible( self ) )
  956. continue;
  957.  
  958. if ( isSP() )
  959. damage *= SP_DAMAGE_BIAS;
  960. else
  961. damage *= MP_DAMAGE_BIAS;
  962.  
  963. if ( damage <= 0 )
  964. continue;
  965.  
  966. if ( IsDefined( attacker ) && IsPlayer( attacker ) )
  967. self.damageOwner = attacker;
  968.  
  969. type = getDamageType( type );
  970. Assert( IsDefined( type ) );
  971.  
  972. // shotguns only do one notify so we need to amp up the damage
  973. if ( is_shotgun_damage( attacker, type ) )
  974. {
  975. if ( isSP() )
  976. damage *= SP_SHOTGUN_BIAS;
  977. else
  978. damage *= MP_SHOTGUN_BIAS;
  979. }
  980.  
  981. /#
  982. if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 )
  983. {
  984. Print3d( point, ".", ( 1, 1, 1 ), 1.0, 0.5, 100 );
  985. if ( IsDefined( damage ) )
  986. IPrintLn( "damage amount: " + damage );
  987. if ( IsDefined( modelName ) )
  988. IPrintLn( "hit model: " + modelName );
  989. if ( IsDefined( tagName ) )
  990. IPrintLn( "hit model tag: " + tagName );
  991. else
  992. IPrintLn( "hit model tag: " );
  993. }
  994. #/
  995.  
  996. // override for when base model is damaged. We dont want to pass in empty strings
  997. if ( !isdefined( modelName ) || ( modelName == "" ) )
  998. {
  999. Assert( IsDefined( self.model ) );
  1000. modelName = self.model;
  1001. }
  1002. if ( IsDefined( tagName ) && tagName == "" )
  1003. {
  1004. if ( IsDefined( partName ) && partName != "" && partName != "tag_body" && partName != "body_animate_jnt" )
  1005. tagName = partName;
  1006. else
  1007. tagName = undefined;
  1008.  
  1009. baseModelTag = level.destructible_type[ self.destuctableInfo ].parts[ 0 ][ 0 ].v[ "tagName" ];
  1010. if ( IsDefined( baseModelTag ) && IsDefined( partName ) && ( baseModelTag == partName ) )
  1011. tagName = undefined;
  1012. }
  1013.  
  1014. prof_end( "_destructible" );
  1015.  
  1016. // special handling for splash and projectile damage
  1017. if ( type == "splash" )
  1018. {
  1019. /#
  1020. if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 )
  1021. IPrintLn( "type = splash" );
  1022. #/
  1023.  
  1024. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ 0 ][ 0 ].v[ "splash_damage_scaler" ] ) )
  1025. damage *= level.destructible_type[ self.destuctableInfo ].parts[ 0 ][ 0 ].v[ "splash_damage_scaler" ];
  1026. else
  1027. {
  1028. if ( isSP() )
  1029. damage *= SP_EXPLOSIVE_DAMAGE_BIAS;
  1030. else
  1031. damage *= MP_EXPLOSIVE_DAMAGE_BIAS;
  1032. }
  1033.  
  1034. self destructible_splash_damage( Int( damage ), point, direction_vec, attacker, type );
  1035. continue;
  1036. }
  1037.  
  1038. self thread destructible_update_part( Int( damage ), modelName, tagName, point, direction_vec, attacker, type );
  1039. }
  1040. }
  1041.  
  1042. is_shotgun_damage( attacker, type )
  1043. {
  1044. if ( type != "bullet" )
  1045. return false;
  1046.  
  1047. if ( !isdefined( attacker ) )
  1048. return false;
  1049.  
  1050. currentWeapon = undefined;
  1051. if ( IsPlayer( attacker ) )
  1052. {
  1053. currentweapon = attacker getCurrentWeapon();
  1054. }
  1055.  
  1056. if ( !isdefined( currentweapon ) )
  1057. return false;
  1058.  
  1059. class = weaponClass( currentweapon );
  1060. if ( isdefined( class ) && class == "spread" )
  1061. return true;
  1062.  
  1063. return false;
  1064. }
  1065.  
  1066. getPartAndStateIndex( modelName, tagName )
  1067. {
  1068. Assert( IsDefined( modelName ) );
  1069.  
  1070. info = SpawnStruct();
  1071. info.v = [];
  1072.  
  1073. partIndex = -1;
  1074. stateIndex = -1;
  1075. Assert( IsDefined( self.model ) );
  1076. if ( ( ToLower( modelName ) == ToLower( self.model ) ) && ( !isdefined( tagName ) ) )
  1077. {
  1078. modelName = self.model;
  1079. tagName = undefined;
  1080. partIndex = 0;
  1081. stateIndex = 0;
  1082. }
  1083.  
  1084. for ( i = 0; i < level.destructible_type[ self.destuctableInfo ].parts.size; i++ )
  1085. {
  1086. stateIndex = self.destructible_parts[ i ].v[ "currentState" ];
  1087.  
  1088. if ( level.destructible_type[ self.destuctableInfo ].parts[ i ].size <= stateIndex )
  1089. continue;
  1090.  
  1091. if ( !isdefined( tagName ) )
  1092. continue;
  1093.  
  1094. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ stateIndex ].v[ "tagName" ] ) )
  1095. {
  1096. partTagName = level.destructible_type[ self.destuctableInfo ].parts[ i ][ stateIndex ].v[ "tagName" ];
  1097. if ( partTagName == tagName )
  1098. {
  1099. partIndex = i;
  1100. break;
  1101. }
  1102. }
  1103. }
  1104. Assert( stateIndex >= 0 );
  1105. Assert( IsDefined( partIndex ) );
  1106.  
  1107. info.v[ "stateIndex" ] = stateindex;
  1108. info.v[ "partIndex" ] = partindex;
  1109.  
  1110. return info;
  1111. }
  1112.  
  1113. destructible_update_part( damage, modelName, tagName, point, direction_vec, attacker, damageType, partInfo )
  1114. {
  1115. //---------------------------------------------------------------------
  1116. // Find what part this is, or is a child of. If the base model was
  1117. // the entity that was damaged the part index will be -1
  1118. //---------------------------------------------------------------------
  1119. if ( !isdefined( self.destructible_parts ) )
  1120. return;
  1121. if ( self.destructible_parts.size == 0 )
  1122. return;
  1123.  
  1124. prof_begin( "_destructible" );
  1125.  
  1126. info = getPartAndStateIndex( modelName, tagName );
  1127. stateIndex = info.v[ "stateIndex" ];
  1128. partIndex = info.v[ "partIndex" ];
  1129.  
  1130. prof_end( "_destructible" );
  1131.  
  1132. if ( partIndex < 0 )
  1133. return;
  1134.  
  1135. //---------------------------------------------------------------------
  1136. // Deduct the damage amount from the part's health
  1137. // If the part runs out of health go to the next state
  1138. //---------------------------------------------------------------------
  1139. state_before = stateIndex;
  1140. updateHealthValue = false;
  1141. delayModelSwap = false;
  1142. prof_begin( "_destructible" );
  1143. for ( ;; )
  1144. {
  1145. stateIndex = self.destructible_parts[ partIndex ].v[ "currentState" ];
  1146.  
  1147. // there isn't another state to go to when damaged
  1148. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ] ) )
  1149. break;
  1150.  
  1151. // see if the model is also supposed to damage the parent
  1152. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ 0 ].v[ "alsoDamageParent" ] ) )
  1153. {
  1154. if ( getDamageType( damageType ) != "splash" )
  1155. {
  1156. ratio = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ 0 ].v[ "alsoDamageParent" ];
  1157. parentDamage = Int( damage * ratio );
  1158. self thread notifyDamageAfterFrame( parentDamage, attacker, direction_vec, point, damageType, "", "" );
  1159. }
  1160. }
  1161.  
  1162. // loop through all parts to see which ones also need to get this damage applied to them ( based on their "receiveDamageFromParent" value )
  1163. if ( getDamageType( damageType ) != "splash" )
  1164. {
  1165. foreach ( part in level.destructible_type[ self.destuctableInfo ].parts )
  1166. {
  1167. if ( !isdefined( part[ 0 ].v[ "receiveDamageFromParent" ] ) )
  1168. continue;
  1169.  
  1170. if ( !isdefined( part[ 0 ].v[ "tagName" ] ) )
  1171. continue;
  1172.  
  1173. ratio = part[ 0 ].v[ "receiveDamageFromParent" ];
  1174. Assert( ratio > 0 );
  1175.  
  1176. childDamage = Int( damage * ratio );
  1177. childTagName = part[ 0 ].v[ "tagName" ];
  1178. self thread notifyDamageAfterFrame( childDamage, attacker, direction_vec, point, damageType, "", childTagName );
  1179. }
  1180. }
  1181.  
  1182. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "health" ] ) )
  1183. break;
  1184. if ( !isdefined( self.destructible_parts[ partIndex ].v[ "health" ] ) )
  1185. break;
  1186.  
  1187. if ( updateHealthValue )
  1188. self.destructible_parts[ partIndex ].v[ "health" ] = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "health" ];
  1189. updateHealthValue = false;
  1190.  
  1191. /#
  1192. if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 )
  1193. {
  1194. IPrintLn( "stateindex: " + stateIndex );
  1195. IPrintLn( "damage: " + damage );
  1196. IPrintLn( "health( before ): " + self.destructible_parts[ partIndex ].v[ "health" ] );
  1197. }
  1198. #/
  1199.  
  1200. // Handle grenades hitting glass parts. Grenades should make the glass completely break instead of just doing 1 damage and shattering the glass
  1201. if ( ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "grenadeImpactDeath" ] ) ) && ( damageType == "impact" ) )
  1202. damage = 100000000;
  1203.  
  1204. // apply the damage to the part if the attacker was a valid attacker
  1205. savedHealth = self.destructible_parts[ partIndex ].v[ "health" ];
  1206. validAttacker = self isAttackerValid( partIndex, stateIndex, attacker );
  1207. if ( validAttacker )
  1208. {
  1209. validDamageCause = self isValidDamageCause( partIndex, stateIndex, damageType );
  1210. if ( validDamageCause )
  1211. {
  1212. if ( IsDefined( attacker ) )
  1213. {
  1214. if ( IsPlayer( attacker ) )
  1215. {
  1216. self.player_damage += damage;
  1217. }
  1218. else
  1219. {
  1220. if ( attacker != self )
  1221. self.non_player_damage += damage;
  1222. }
  1223. }
  1224.  
  1225. // Chad - ask Brent why we think melee is worth 100000 damage
  1226. if ( IsDefined( damageType ) )
  1227. {
  1228. if ( damageType == "melee" || damageType == "impact" )
  1229. damage = 100000;
  1230. }
  1231.  
  1232. self.destructible_parts[ partIndex ].v[ "health" ] -= damage;
  1233. }
  1234. }
  1235.  
  1236. /#
  1237. if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 )
  1238. IPrintLn( "health( after ): " + self.destructible_parts[ partIndex ].v[ "health" ] );
  1239. #/
  1240.  
  1241. // if the part still has health left then we're done
  1242. if ( self.destructible_parts[ partIndex ].v[ "health" ] > 0 )
  1243. {
  1244. prof_end( "_destructible" );
  1245. return;
  1246. }
  1247.  
  1248. // cap on the number of destructibles killed in a single frame
  1249. if ( IsDefined( partInfo ) )
  1250. {
  1251. partInfo.v[ "fxcost" ] = get_part_FX_cost_for_action_state( partIndex, self.destructible_parts[ partIndex ].v[ "currentState" ] );
  1252.  
  1253. add_destructible_to_frame_queue( self, partInfo, damage );
  1254.  
  1255. self.waiting_for_queue = true;
  1256. self waittill( "queue_processed", success );
  1257. self.waiting_for_queue = undefined;
  1258.  
  1259. if ( !success )// can we be destroyed this frame?
  1260. {
  1261. self.destructible_parts[ partIndex ].v[ "health" ] = savedHealth;
  1262. return;
  1263. }
  1264. }
  1265.  
  1266. // if the part ran out of health then carry over to the next part
  1267. damage = Int( abs( self.destructible_parts[ partIndex ].v[ "health" ] ) );
  1268.  
  1269. // Brent asks - why is this condition here? It'll never trigger given that abs() does the following:
  1270. // "fabs returns the absolute value of x. Absolute value is a number's distance from zero on the number line. The absolute value of -4 is 4; the absolute value of 4 is 4."
  1271. // It should probably be removed
  1272. if ( damage < 0 )
  1273. {
  1274. prof_end( "_destructible" );
  1275. return;
  1276. }
  1277.  
  1278. self.destructible_parts[ partIndex ].v[ "currentState" ]++;
  1279. stateIndex = self.destructible_parts[ partIndex ].v[ "currentState" ];
  1280. actionStateIndex = ( stateIndex - 1 );
  1281.  
  1282. // use these rather than re-getting them all the time. This insures that we do
  1283. // not overwrite their values too.
  1284. action_v = undefined;
  1285. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ] ) )
  1286. action_v = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v;
  1287.  
  1288. state_v = undefined;
  1289. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ] ) )
  1290. state_v = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v;
  1291.  
  1292. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ] ) )
  1293. {
  1294. prof_end( "_destructible" );
  1295. return;
  1296. }
  1297.  
  1298. //---------------------------------------------------------------------
  1299. // A state change is required so detach the old model or replace it if
  1300. // it's the base model that took the damage.
  1301. // Then attach the model ( if specified ) used for the new state
  1302. // Only do this if there is another state to go to, some parts might have
  1303. // fx or anims, or sounds but no next model to go to
  1304. //---------------------------------------------------------------------
  1305.  
  1306. // if the part is meant to explode on this state set a flag. Actual explosion will be done down below
  1307. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "explode" ] ) )
  1308. self.exploding = true;
  1309.  
  1310. // stop all previously looped sounds
  1311. if ( IsDefined( self.loopingSoundStopNotifies ) && IsDefined( self.loopingSoundStopNotifies[ toString( partIndex ) ] ) )
  1312. {
  1313. for ( i = 0; i < self.loopingSoundStopNotifies[ toString( partIndex ) ].size; i++ )
  1314. {
  1315. self notify( self.loopingSoundStopNotifies[ toString( partIndex ) ][ i ] );
  1316. if ( isSP() && self.modeldummyon )
  1317. self.modeldummy notify( self.loopingSoundStopNotifies[ toString( partIndex ) ][ i ] );
  1318. }
  1319. self.loopingSoundStopNotifies[ toString( partIndex ) ] = undefined;
  1320. }
  1321.  
  1322. // setup our destructible light if we want one and can find one
  1323. if ( IsDefined( action_v[ "break_nearby_lights" ] ) )
  1324. {
  1325. self destructible_get_my_breakable_light( action_v[ "break_nearby_lights" ] );
  1326. }
  1327.  
  1328. // swap the model
  1329. // this doesn't work when threaded off to another function
  1330. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ] ) )
  1331. {
  1332. if ( partIndex == 0 ) // base model damaged
  1333. {
  1334. newModel = state_v[ "modelName" ];
  1335. if ( IsDefined( newModel ) && newModel != self.model )
  1336. {
  1337. self SetModel( newModel );
  1338. if ( isSP() && self.modeldummyon )
  1339. self.modeldummy SetModel( newModel );
  1340. destructible_splash_rotatation( state_v );
  1341. }
  1342. }
  1343. else // part was damaged, not the base model
  1344. {
  1345. // handle a part getting damaged here - must be detached and reattached
  1346. self hideapart( tagName );
  1347. if ( isSP() && self.modeldummyon )
  1348. self.modeldummy hideapart( tagName );
  1349.  
  1350. tagName = state_v[ "tagName" ];
  1351. if ( IsDefined( tagName ) )
  1352. {
  1353. self showapart( tagName );
  1354. if ( isSP() && self.modeldummyon )
  1355. self.modeldummy showapart( tagName );
  1356. }
  1357. }
  1358. }
  1359.  
  1360. eModel = get_dummy();
  1361.  
  1362. // If its exploding clear all previous animations on the destructible. The only animation that will play after this is an explosion animation
  1363. if ( IsDefined( self.exploding ) )
  1364. self clear_anims( eModel );
  1365.  
  1366. // if the part has an anim then play it now
  1367. groupNumber = destructible_animation_think( action_v, eModel, damageType, partIndex );
  1368.  
  1369. // if the part has fx then play it now
  1370. groupNumber = destructible_fx_think( action_v, eModel, damageType, partIndex, groupNumber );
  1371.  
  1372. // if the part has a soundalias then play it now
  1373. groupNumber = destructible_sound_think( action_v, eModel, damageType, groupNumber );
  1374.  
  1375. // if the part has a looping fx then play it now
  1376. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopfx" ] ) )
  1377. {
  1378. loopfx_size = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_filename" ].size;
  1379.  
  1380. if ( loopfx_size > 0 )
  1381. self notify( "FX_State_Change" + partIndex );
  1382.  
  1383. for ( idx = 0; idx < loopfx_size; idx++ )
  1384. {
  1385. Assert( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_tag" ][ idx ] ) );
  1386. loopfx = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopfx" ][ idx ];
  1387. loopfx_tag = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_tag" ][ idx ];
  1388. loopRate = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_rate" ][ idx ];
  1389. self thread loopfx_onTag( loopfx, loopfx_tag, loopRate, partIndex );
  1390. }
  1391. }
  1392.  
  1393. // if the part has a looping soundalias then start looping it now
  1394. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopsound" ] ) )
  1395. {
  1396. for ( i = 0; i < level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopsound" ].size; i++ )
  1397. {
  1398. validSoundCause = self isValidSoundCause( "loopsoundCause", action_v, i, damageType );
  1399. if ( validSoundCause )
  1400. {
  1401. loopsoundAlias = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "loopsound" ][ i ];
  1402. loopsoundTagName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "tagName" ];
  1403. self thread play_loop_sound_on_destructible( loopsoundAlias, loopsoundTagName );
  1404.  
  1405. if ( !isdefined( self.loopingSoundStopNotifies ) )
  1406. self.loopingSoundStopNotifies = [];
  1407. if ( !isdefined( self.loopingSoundStopNotifies[ toString( partIndex ) ] ) )
  1408. self.loopingSoundStopNotifies[ toString( partIndex ) ] = [];
  1409. size = self.loopingSoundStopNotifies[ toString( partIndex ) ].size;
  1410. self.loopingSoundStopNotifies[ toString( partIndex ) ][ size ] = "stop sound" + loopsoundAlias;
  1411. }
  1412. }
  1413. }
  1414.  
  1415. // if the part is supposed to trigger a car alarm
  1416. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "triggerCarAlarm" ] ) )
  1417. {
  1418. self thread do_car_alarm();
  1419. }
  1420.  
  1421. // if the part is supposed to trigger a car alarm
  1422. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "break_nearby_lights" ] ) )
  1423. {
  1424. self thread break_nearest_light();
  1425. }
  1426.  
  1427. // if the part should drain health then start the drain
  1428. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "healthdrain_amount" ] ) )
  1429. {
  1430. self notify( "Health_Drain_State_Change" + partIndex );
  1431. healthdrain_amount = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "healthdrain_amount" ];
  1432. healthdrain_interval = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "healthdrain_interval" ];
  1433. healthdrain_modelName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "modelName" ];
  1434. healthdrain_tagName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "tagName" ];
  1435. badplaceRadius = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "badplace_radius" ];
  1436. badplaceTeam = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "badplace_team" ];
  1437. if ( healthdrain_amount > 0 )
  1438. {
  1439. Assert( ( IsDefined( healthdrain_interval ) ) && ( healthdrain_interval > 0 ) );
  1440. self thread health_drain( healthdrain_amount, healthdrain_interval, partIndex, healthdrain_modelName, healthdrain_tagName, badplaceRadius, badplaceTeam );
  1441. }
  1442. }
  1443.  
  1444. // if the part is meant to explode on this state then do it now. Causes all attached models to become physics with the specified force
  1445. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "explode" ] ) )
  1446. {
  1447. delayModelSwap = true;
  1448. force_min = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "explode_force_min" ];
  1449. force_max = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "explode_force_max" ];
  1450. range = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "explode_range" ];
  1451. mindamage = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "explode_mindamage" ];
  1452. maxdamage = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "explode_maxdamage" ];
  1453. continueDamage = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "continueDamage" ];
  1454. originOffset = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "originOffset" ];
  1455. earthQuakeScale = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "earthQuakeScale" ];
  1456. earthQuakeRadius = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "earthQuakeRadius" ];
  1457.  
  1458. if ( IsDefined( attacker ) && attacker != self )
  1459. {
  1460. // Achievement Hook
  1461. self.attacker = attacker;
  1462.  
  1463. // Only add .damage_type to script_vehicles that happen to be destructibles (ie UAZ)
  1464. // This hook provides info so the vehicle can do _player_stat::register_kill()
  1465. if ( self.code_classname == "script_vehicle" )
  1466. {
  1467. self.damage_type = damageType;
  1468. }
  1469. }
  1470.  
  1471. self thread explode( partIndex, force_min, force_max, range, mindamage, maxdamage, continueDamage, originOffset, earthQuakeScale, earthQuakeRadius, attacker );
  1472. }
  1473.  
  1474. // if the part should do physics here then initiate the physics and velocity
  1475. physTagOrigin = undefined;
  1476. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "physics" ] ) )
  1477. {
  1478. for ( i = 0; i < level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "physics" ].size; i++ )
  1479. {
  1480. physTagOrigin = undefined;
  1481. physTagName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "physics_tagName" ][ i ];
  1482. physVelocity = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v[ "physics_velocity" ][ i ];
  1483.  
  1484. initial_velocity = undefined;
  1485. if ( IsDefined( physVelocity ) )
  1486. {
  1487. physTagAngles = undefined;
  1488. if ( IsDefined( physTagName ) )
  1489. physTagAngles = self GetTagAngles( physTagName );
  1490. else if ( IsDefined( tagName ) )
  1491. physTagAngles = self GetTagAngles( tagName );
  1492. Assert( IsDefined( physTagAngles ) );
  1493.  
  1494. physTagOrigin = undefined;
  1495. if ( IsDefined( physTagName ) )
  1496. physTagOrigin = self GetTagOrigin( physTagName );
  1497. else if ( IsDefined( tagName ) )
  1498. physTagOrigin = self GetTagOrigin( tagName );
  1499. Assert( IsDefined( physTagOrigin ) );
  1500.  
  1501. phys_x = physVelocity[ 0 ] - 5 + RandomFloat( 10 );
  1502. phys_y = physVelocity[ 1 ] - 5 + RandomFloat( 10 );
  1503. phys_z = physVelocity[ 2 ] - 5 + RandomFloat( 10 );
  1504.  
  1505. forward = AnglesToForward( physTagAngles ) * phys_x * RandomFloatRange( 80, 110 );
  1506. right = AnglesToRight( physTagAngles ) * phys_y * RandomFloatRange( 80, 110 );
  1507. up = AnglesToUp( physTagAngles ) * phys_z * RandomFloatRange( 80, 110 );
  1508.  
  1509. initial_velocity = forward + right + up;
  1510.  
  1511. /#
  1512. if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 )
  1513. {
  1514. thread draw_line_for_time( physTagOrigin, physTagOrigin + initial_velocity, 1, 1, 1, 5.0 );
  1515. }
  1516. #/
  1517. }
  1518. else
  1519. {
  1520. initial_velocity = point;
  1521. impactDir = ( 0, 0, 0 );
  1522. if ( IsDefined( attacker ) )
  1523. {
  1524. impactDir = attacker.origin;
  1525. initial_velocity = VectorNormalize( point - impactDir );
  1526. initial_velocity = vector_multiply( initial_velocity, 200 );
  1527. }
  1528. }
  1529. Assert( IsDefined( initial_velocity ) );
  1530.  
  1531. if ( IsDefined( physTagName ) )
  1532. {
  1533. // Do physics on another part, and continue this thread since the current part is still unaffected by the physics
  1534.  
  1535. // get the partIndex that cooresponds to what the tagname is
  1536. physPartIndex = undefined;
  1537. for ( j = 0; j < level.destructible_type[ self.destuctableInfo ].parts.size; j++ )
  1538. {
  1539. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts[ j ][ 0 ].v[ "tagName" ] ) )
  1540. continue;
  1541.  
  1542. if ( level.destructible_type[ self.destuctableInfo ].parts[ j ][ 0 ].v[ "tagName" ] != physTagName )
  1543. continue;
  1544.  
  1545. physPartIndex = j;
  1546. break;
  1547. }
  1548.  
  1549. if ( IsDefined( physTagOrigin ) )
  1550. self thread physics_launch( physPartIndex, 0, physTagOrigin, initial_velocity );
  1551. else
  1552. self thread physics_launch( physPartIndex, 0, point, initial_velocity );
  1553. }
  1554. else
  1555. {
  1556. // Do physics on this part, therefore ending this thread
  1557.  
  1558. if ( IsDefined( physTagOrigin ) )
  1559. self thread physics_launch( partIndex, actionStateIndex, physTagOrigin, initial_velocity );
  1560. else
  1561. self thread physics_launch( partIndex, actionStateIndex, point, initial_velocity );
  1562.  
  1563. prof_end( "_destructible" );
  1564. return;
  1565. }
  1566. }
  1567. }
  1568.  
  1569. updateHealthValue = true;
  1570. }
  1571. prof_end( "_destructible" );
  1572. }
  1573.  
  1574. destructible_splash_rotatation( v )
  1575. {
  1576. // rotate model due to splash damage direction, optional
  1577. model_rotation = v[ "splashRotation" ];
  1578. model_rotate_to = v[ "rotateTo" ];
  1579.  
  1580. if ( !isdefined( model_rotate_to ) )
  1581. return;
  1582. if ( !isdefined( model_rotation ) )
  1583. return;
  1584. if ( !model_rotation )
  1585. return;
  1586. self.angles = ( self.angles[ 0 ], model_rotate_to[ 1 ], self.angles[ 2 ] );
  1587. }
  1588.  
  1589. // parameter damageType can be single damage or multiple damages separated by spaces
  1590. damage_not( damageType )
  1591. {
  1592. toks = StrTok( damageType, " " );
  1593. damages_tok = StrTok( "splash melee bullet splash impact unknown", " " );
  1594. new_string = "";
  1595.  
  1596. foreach ( idx, tok in toks )
  1597. damages_tok = array_remove( damages_tok, tok );
  1598.  
  1599. foreach ( damages in damages_tok )
  1600. new_string += damages + " ";
  1601.  
  1602. return new_string;
  1603. }
  1604.  
  1605. destructible_splash_damage( damage, point, direction_vec, attacker, damageType )
  1606. {
  1607. if ( damage <= 0 )
  1608. return;
  1609.  
  1610. if ( IsDefined( self.exploded ) )
  1611. return;
  1612.  
  1613. //------------------------------------------------------------------------
  1614. // Fill an array of all possible parts that might have been splash damaged
  1615. //------------------------------------------------------------------------
  1616.  
  1617. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts ) )
  1618. return;
  1619.  
  1620. damagedParts = self getAllActiveParts( direction_vec );
  1621.  
  1622. if ( damagedParts.size <= 0 )
  1623. return;
  1624.  
  1625. damagedParts = self setDistanceOnParts( damagedParts, point );
  1626.  
  1627. closestPartDist = getLowestPartDistance( damagedParts );
  1628. Assert( IsDefined( closestPartDist ) );
  1629.  
  1630. //--------------------------------------------------------------------------
  1631. // Damage each part depending on how close it was to the splash damage point
  1632. //--------------------------------------------------------------------------
  1633.  
  1634. prof_begin( "_destructible" );
  1635.  
  1636. foreach ( part in damagedParts )
  1637. {
  1638. distanceMod = ( part.v[ "distance" ] * 1.4 );
  1639. damageAmount = ( damage - ( distanceMod - closestPartDist ) );
  1640.  
  1641. if ( damageAmount <= 0 )
  1642. continue;
  1643.  
  1644. if ( IsDefined( self.exploded ) )
  1645. continue;
  1646.  
  1647. /#
  1648. if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 )
  1649. {
  1650. if ( IsDefined( part.v[ "tagName" ] ) )
  1651. Print3d( self GetTagOrigin( part.v[ "tagName" ] ), damageAmount, ( 1, 1, 1 ), 1.0, 0.5, 200 );
  1652. }
  1653. #/
  1654.  
  1655. self thread destructible_update_part( damageAmount, part.v[ "modelName" ], part.v[ "tagName" ], point, direction_vec, attacker, damageType, part );
  1656. }
  1657.  
  1658. prof_end( "_destructible" );
  1659. }
  1660.  
  1661. getAllActiveParts( direction_vec )
  1662. {
  1663. activeParts = [];
  1664.  
  1665. Assert( IsDefined( self.destuctableInfo ) );
  1666. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts ) )
  1667. return activeParts;
  1668.  
  1669. prof_begin( "_destructible" );
  1670.  
  1671. for ( i = 0; i < level.destructible_type[ self.destuctableInfo ].parts.size; i++ )
  1672. {
  1673. partIndex = i;
  1674. currentState = self.destructible_parts[ partIndex ].v[ "currentState" ];
  1675.  
  1676. // Splash damage rotation, rotation angle only calculated for state that has this option enabled
  1677. for ( j = 0; j < level.destructible_type[ self.destuctableInfo ].parts[ partIndex ].size; j++ )
  1678. {
  1679. splash_rotation = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ j ].v[ "splashRotation" ];
  1680. if ( IsDefined( splash_rotation ) && splash_rotation )
  1681. {
  1682. rotate_to_angle = VectorToAngles( direction_vec );
  1683. rotate_to_angle_y = rotate_to_angle[ 1 ] - 90;
  1684. level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ j ].v[ "rotateTo" ] = ( 0, rotate_to_angle_y, 0 );
  1685. }
  1686. }
  1687.  
  1688. if ( !isdefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ currentState ] ) )
  1689. continue;
  1690.  
  1691. tagName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ currentState ].v[ "tagName" ];
  1692. if ( !isdefined( tagName ) )
  1693. tagName = "";
  1694.  
  1695. if ( tagName == "" )
  1696. continue;
  1697.  
  1698. modelName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ currentState ].v[ "modelName" ];
  1699. if ( !isdefined( modelName ) )
  1700. modelName = "";
  1701.  
  1702. activePartIndex = activeParts.size;
  1703. activeParts[ activePartIndex ] = SpawnStruct();
  1704. activeParts[ activePartIndex ].v[ "modelName" ] = modelName;
  1705. activeParts[ activePartIndex ].v[ "tagName" ] = tagName;
  1706. }
  1707.  
  1708. prof_end( "_destructible" );
  1709.  
  1710. return activeParts;
  1711. }
  1712.  
  1713. setDistanceOnParts( partList, point )
  1714. {
  1715. prof_begin( "_destructible" );
  1716.  
  1717. for ( i = 0; i < partList.size; i++ )
  1718. {
  1719. d = Distance( point, self GetTagOrigin( partList[ i ].v[ "tagName" ] ) );
  1720. partList[ i ].v[ "distance" ] = d;
  1721. }
  1722.  
  1723. prof_end( "_destructible" );
  1724.  
  1725. return partList;
  1726. }
  1727.  
  1728. getLowestPartDistance( partList )
  1729. {
  1730. closestDist = undefined;
  1731.  
  1732. prof_begin( "_destructible" );
  1733.  
  1734. foreach ( part in partList )
  1735. {
  1736. Assert( IsDefined( part.v[ "distance" ] ) );
  1737. d = part.v[ "distance" ];
  1738.  
  1739. if ( !isdefined( closestDist ) )
  1740. closestDist = d;
  1741.  
  1742. if ( d < closestDist )
  1743. closestDist = d;
  1744. }
  1745.  
  1746. prof_end( "_destructible" );
  1747.  
  1748. return closestDist;
  1749. }
  1750.  
  1751.  
  1752. isValidSoundCause( soundCauseVar, action_v, soundIndex, damageType, groupNum )
  1753. {
  1754. if ( isdefined( groupNum ) )
  1755. soundCause = action_v[ soundCauseVar ][ groupNum ][ soundIndex ];
  1756. else
  1757. soundCause = action_v[ soundCauseVar ][ soundIndex ];
  1758.  
  1759. if ( !isdefined( soundCause ) )
  1760. return true;
  1761.  
  1762. if ( soundCause == damageType )
  1763. return true;
  1764.  
  1765. return false;
  1766. }
  1767.  
  1768. isAttackerValid( partIndex, stateIndex, attacker )
  1769. {
  1770. // return true if the vehicle is being force exploded
  1771. if ( IsDefined( self.forceExploding ) )
  1772. return true;
  1773.  
  1774. // return false if the vehicle is trying to explode but it's not allowed to
  1775. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "explode" ] ) )
  1776. {
  1777. if ( IsDefined( self.dontAllowExplode ) )
  1778. return false;
  1779. }
  1780.  
  1781. if ( !isdefined( attacker ) )
  1782. return true;
  1783.  
  1784. if ( attacker == self )
  1785. return true;
  1786.  
  1787. sType = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "validAttackers" ];
  1788. if ( !isdefined( sType ) )
  1789. return true;
  1790.  
  1791. if ( sType == "no_player" )
  1792. {
  1793. if ( !isplayer( attacker ) )
  1794. return true;
  1795. if ( !isdefined( attacker.damageIsFromPlayer ) )
  1796. return true;
  1797. if ( attacker.damageIsFromPlayer == false )
  1798. return true;
  1799. }
  1800. else
  1801. if ( sType == "player_only" )
  1802. {
  1803. if ( IsPlayer( attacker ) )
  1804. return true;
  1805. if ( IsDefined( attacker.damageIsFromPlayer ) && attacker.damageIsFromPlayer )
  1806. return true;
  1807. }
  1808. else
  1809. if ( sType == "no_ai" && IsDefined( level.isAIfunc ) )
  1810. {
  1811. if ( ![[ level.isAIfunc ]]( attacker ) )
  1812. return true;
  1813. }
  1814. else
  1815. if ( sType == "ai_only" && IsDefined( level.isAIfunc ) )
  1816. {
  1817. if ( [[ level.isAIfunc ]]( attacker ) )
  1818. return true;
  1819. }
  1820. else
  1821. {
  1822. AssertMsg( "Invalid attacker rules on destructible vehicle. Valid types are: ai_only, no_ai, player_only, no_player" );
  1823. }
  1824.  
  1825. return false;
  1826. }
  1827.  
  1828. isValidDamageCause( partIndex, stateIndex, damageType )
  1829. {
  1830. if ( !isdefined( damageType ) )
  1831. return true;
  1832.  
  1833. godModeAllowed = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "godModeAllowed" ];
  1834. if ( godModeAllowed && ( ( IsDefined( self.godmode ) && self.godmode ) || ( IsDefined( self.script_bulletshield ) && self.script_bulletshield ) && damageType == "bullet" ) )
  1835. return false;
  1836.  
  1837. validType = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "validDamageCause" ];
  1838. if ( !isdefined( validType ) )
  1839. return true;
  1840.  
  1841. if ( ( validType == "splash" ) && damageType != "splash" )
  1842. return false;
  1843.  
  1844. if ( ( validType == "no_melee" ) && damageType == "melee" || damageType == "impact" )
  1845. return false;
  1846.  
  1847. return true;
  1848. }
  1849.  
  1850. getDamageType( type )
  1851. {
  1852. //returns a simple damage type: melee, bullet, splash, or unknown
  1853.  
  1854. if ( !isdefined( type ) )
  1855. return "unknown";
  1856.  
  1857. type = ToLower( type );
  1858. switch( type )
  1859. {
  1860. case "mod_melee":
  1861. case "mod_crush":
  1862. case "melee":
  1863. return "melee";
  1864. case "mod_pistol_bullet":
  1865. case "mod_rifle_bullet":
  1866. case "bullet":
  1867. return "bullet";
  1868. case "mod_grenade":
  1869. case "mod_grenade_splash":
  1870. case "mod_projectile":
  1871. case "mod_projectile_splash":
  1872. case "mod_explosive":
  1873. case "splash":
  1874. return "splash";
  1875. case "mod_impact":
  1876. return "impact";
  1877. case "unknown":
  1878. return "unknown";
  1879. default:
  1880. return "unknown";
  1881. }
  1882. }
  1883.  
  1884. damage_mirror( parent, modelName, tagName )
  1885. {
  1886. self notify( "stop_damage_mirror" );
  1887. self endon( "stop_damage_mirror" );
  1888. parent endon( "stop_taking_damage" );
  1889.  
  1890. self SetCanDamage( true );
  1891. for ( ;; )
  1892. {
  1893. self waittill( "damage", damage, attacker, direction_vec, point, type );
  1894. parent notify( "damage", damage, attacker, direction_vec, point, type, modelName, tagName );
  1895. damage = undefined;
  1896. attacker = undefined;
  1897. direction_vec = undefined;
  1898. point = undefined;
  1899. type = undefined;
  1900. }
  1901. }
  1902.  
  1903. add_damage_owner_recorder()
  1904. {
  1905. // Mackey added to track who is damaging the car
  1906. self.player_damage = 0;
  1907. self.non_player_damage = 0;
  1908.  
  1909. self.car_damage_owner_recorder = true;
  1910. }
  1911.  
  1912. loopfx_onTag( loopfx, loopfx_tag, loopRate, partIndex )
  1913. {
  1914. self endon( "FX_State_Change" + partIndex );
  1915. self endon( "delete_destructible" );
  1916. level endon( "putout_fires" );
  1917.  
  1918. while( isdefined( self ) )
  1919. {
  1920. eModel = get_dummy();
  1921. PlayFXOnTag( loopfx, eModel, loopfx_tag );
  1922. wait loopRate;
  1923. }
  1924. }
  1925.  
  1926. health_drain( amount, interval, partIndex, modelName, tagName, badplaceRadius, badplaceTeam )
  1927. {
  1928. self endon( "Health_Drain_State_Change" + partIndex );
  1929. level endon( "putout_fires" );
  1930. self endon( "destroyed" );
  1931.  
  1932. if( IsDefined( badplaceRadius ) && IsDefined( level.destructible_badplace_radius_multiplier ) )
  1933. {
  1934. badplaceRadius *= level.destructible_badplace_radius_multiplier;
  1935. }
  1936.  
  1937. if( IsDefined( amount ) &&IsDefined( level.destructible_health_drain_amount_multiplier ) )
  1938. {
  1939. amount *= level.destructible_health_drain_amount_multiplier;
  1940. }
  1941.  
  1942. wait interval;
  1943.  
  1944. self.healthDrain = true;
  1945.  
  1946. uniqueName = undefined;
  1947.  
  1948. // disable the badplace radius call if level.disable_destructible_bad_places is true
  1949. if ( IsDefined( level.disable_destructible_bad_places ) && level.disable_destructible_bad_places )
  1950. badplaceRadius = undefined;
  1951.  
  1952. if ( IsDefined( badplaceRadius ) && IsDefined( badplaceTeam ) && isSP() )
  1953. {
  1954. uniqueName = "" + GetTime();
  1955. if ( !isdefined( self.disableBadPlace ) )
  1956. {
  1957. if ( IsDefined( self.script_radius ) )
  1958. {
  1959. // overwrite the badplace radius from the map
  1960. badplaceRadius = self.script_radius;
  1961. }
  1962. Assert( IsDefined( level.badplace_cylinder_func ) );
  1963. if ( badplaceTeam == "both" )
  1964. call [[ level.badplace_cylinder_func ]]( uniqueName, 0, self.origin, badplaceRadius, 128, "allies", "bad_guys" );
  1965. else
  1966. call [[ level.badplace_cylinder_func ]]( uniqueName, 0, self.origin, badplaceRadius, 128, badplaceTeam );
  1967. self thread badplace_remove( uniqueName );
  1968. }
  1969. }
  1970.  
  1971. while ( isdefined( self ) && self.destructible_parts[ partIndex ].v[ "health" ] > 0 )
  1972. {
  1973. /#
  1974. if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 )
  1975. {
  1976. IPrintLn( "health before damage: " + self.destructible_parts[ partIndex ].v[ "health" ] );
  1977. IPrintLn( "doing " + amount + " damage" );
  1978. }
  1979. #/
  1980. self notify( "damage", amount, self, ( 0, 0, 0 ), ( 0, 0, 0 ), "MOD_UNKNOWN", modelName, tagName );
  1981. wait interval;
  1982. }
  1983.  
  1984. self notify( "remove_badplace" );
  1985. }
  1986.  
  1987. badplace_remove( uniqueName )
  1988. {
  1989. self waittill_any( "destroyed", "remove_badplace" );
  1990.  
  1991. Assert( IsDefined( uniqueName ) );
  1992. Assert( IsDefined( level.badplace_delete_func ) );
  1993. call [[ level.badplace_delete_func ]]( uniqueName );
  1994. }
  1995.  
  1996. physics_launch( partIndex, stateIndex, point, initial_velocity )
  1997. {
  1998. modelName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "modelName" ];
  1999. tagName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ stateIndex ].v[ "tagName" ];
  2000.  
  2001. self hideapart( tagName );
  2002.  
  2003. /#
  2004. if ( GetDvarInt( "destructibles_enable_physics", 1 ) == 0 )
  2005. return;
  2006. #/
  2007.  
  2008. // If we've reached the max number of spawned physics models for destructible vehicles then delete one before creating another
  2009. if ( level.destructibleSpawnedEnts.size >= level.destructibleSpawnedEntsLimit )
  2010. physics_object_remove( level.destructibleSpawnedEnts[ 0 ] );
  2011.  
  2012. // Spawn a model to use for physics using the modelname and position of the part
  2013. physicsObject = Spawn( "script_model", self GetTagOrigin( tagName ) );
  2014. physicsObject.angles = self GetTagAngles( tagName );
  2015. physicsObject SetModel( modelName );
  2016.  
  2017. // Keep track of the new part so it can be removed later if we reach the max
  2018. level.destructibleSpawnedEnts[ level.destructibleSpawnedEnts.size ] = physicsObject;
  2019.  
  2020. // Do physics on the model
  2021. physicsObject PhysicsLaunchClient( point, initial_velocity );
  2022. }
  2023.  
  2024. physics_object_remove( ent )
  2025. {
  2026. newArray = [];
  2027. for ( i = 0; i < level.destructibleSpawnedEnts.size; i++ )
  2028. {
  2029. if ( level.destructibleSpawnedEnts[ i ] == ent )
  2030. continue;
  2031. newArray[ newArray.size ] = level.destructibleSpawnedEnts[ i ];
  2032. }
  2033. level.destructibleSpawnedEnts = newArray;
  2034.  
  2035. if ( isdefined( ent ) )
  2036. ent Delete();
  2037. }
  2038.  
  2039. explode( partIndex, force_min, force_max, range, mindamage, maxdamage, continueDamage, originOffset, earthQuakeScale, earthQuakeRadius, attacker )
  2040. {
  2041. Assert( IsDefined( force_min ) );
  2042. Assert( IsDefined( force_max ) );
  2043.  
  2044. if( IsDefined( range ) && IsDefined( level.destructible_explosion_radius_multiplier ) )
  2045. {
  2046. range *= level.destructible_explosion_radius_multiplier;
  2047. }
  2048.  
  2049. if ( !isdefined( originOffset ) )
  2050. originOffset = 80;
  2051.  
  2052. if ( !isdefined( continueDamage ) || ( IsDefined( continueDamage ) && !continueDamage ) )
  2053. {
  2054. if ( IsDefined( self.exploded ) )
  2055. return;
  2056. self.exploded = true;
  2057. }
  2058.  
  2059. self notify( "exploded", attacker );
  2060. level notify( "destructible_exploded" );
  2061. if ( self.code_classname == "script_vehicle" )
  2062. self notify( "death", attacker, self.damage_type );
  2063.  
  2064. // check if there is a disconnect paths brush to disconnect any traverses
  2065. if ( isSP() )
  2066. self thread disconnectTraverses();
  2067.  
  2068. wait 0.05;
  2069.  
  2070. currentState = self.destructible_parts[ partIndex ].v[ "currentState" ];
  2071. Assert( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ] ) );
  2072. tagName = undefined;
  2073. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ currentState ] ) )
  2074. tagName = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ currentState ].v[ "tagName" ];
  2075.  
  2076. if ( IsDefined( tagName ) )
  2077. explosionOrigin = self GetTagOrigin( tagName );
  2078. else
  2079. explosionOrigin = self.origin;
  2080.  
  2081. self notify( "damage", maxdamage, self, ( 0, 0, 0 ), explosionOrigin, "MOD_EXPLOSIVE", "", "" );
  2082.  
  2083. self notify( "stop_car_alarm" );
  2084.  
  2085. waittillframeend;
  2086.  
  2087. prof_begin( "_destructible" );
  2088.  
  2089. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts ) )
  2090. {
  2091. for ( i = ( level.destructible_type[ self.destuctableInfo ].parts.size - 1 ); i >= 0; i-- )
  2092. {
  2093. if ( i == partIndex )
  2094. continue;
  2095.  
  2096. stateIndex = self.destructible_parts[ i ].v[ "currentState" ];
  2097. if ( stateIndex >= level.destructible_type[ self.destuctableInfo ].parts[ i ].size )
  2098. stateIndex = level.destructible_type[ self.destuctableInfo ].parts[ i ].size - 1;
  2099. modelName = level.destructible_type[ self.destuctableInfo ].parts[ i ][ stateIndex ].v[ "modelName" ];
  2100. tagName = level.destructible_type[ self.destuctableInfo ].parts[ i ][ stateIndex ].v[ "tagName" ];
  2101.  
  2102. if ( !isdefined( modelName ) )
  2103. continue;
  2104. if ( !isdefined( tagName ) )
  2105. continue;
  2106.  
  2107. // dont do physics on parts that are supposed to be removed on explosion
  2108. if ( IsDefined( level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "physicsOnExplosion" ] ) )
  2109. {
  2110. if ( level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "physicsOnExplosion" ] > 0 )
  2111. {
  2112. velocityScaler = level.destructible_type[ self.destuctableInfo ].parts[ i ][ 0 ].v[ "physicsOnExplosion" ];
  2113.  
  2114. point = self GetTagOrigin( tagName );
  2115. initial_velocity = VectorNormalize( point - explosionOrigin );
  2116. initial_velocity = vector_multiply( initial_velocity, RandomFloatRange( force_min, force_max ) * velocityScaler );
  2117.  
  2118. self thread physics_launch( i, stateIndex, point, initial_velocity );
  2119. continue;
  2120. }
  2121. }
  2122. //self.destructible_parts[ i ] Hide();
  2123. }
  2124. }
  2125.  
  2126. prof_end( "_destructible" );
  2127.  
  2128. stopTakingDamage = ( !isdefined( continueDamage ) || ( IsDefined( continueDamage ) && !continueDamage ) );
  2129. if ( stopTakingDamage )
  2130. self notify( "stop_taking_damage" );
  2131.  
  2132. wait 0.05;
  2133.  
  2134. damageLocation = explosionOrigin + ( 0, 0, originOffset );
  2135.  
  2136. isVehicle = ( GetSubStr( level.destructible_type[ self.destuctableInfo ].v[ "type" ], 0, 7 ) == "vehicle" );
  2137.  
  2138. if ( isVehicle )
  2139. {
  2140. anim.lastCarExplosionTime = GetTime();
  2141. anim.lastCarExplosionDamageLocation = damageLocation;
  2142. anim.lastCarExplosionLocation = explosionOrigin;
  2143. anim.lastCarExplosionRange = range;
  2144. }
  2145.  
  2146. // turn off friendly fire when they blow up so the player doesn't get accidental friendly fire mission failure
  2147. level thread set_disable_friendlyfire_value_delayed( 1 );
  2148.  
  2149. if ( isSP() )
  2150. {
  2151. if ( level.gameskill == 0 && !self player_touching_post_clip() )
  2152. self RadiusDamage( damageLocation, range, maxdamage, mindamage, self, "MOD_RIFLE_BULLET" );
  2153. else
  2154. self RadiusDamage( damageLocation, range, maxdamage, mindamage, self );
  2155.  
  2156. if ( IsDefined( self.damageOwner ) && isVehicle )
  2157. {
  2158. self.damageOwner notify( "destroyed_car" );
  2159. level notify( "player_destroyed_car", self.damageOwner, damageLocation );
  2160. }
  2161. }
  2162. else
  2163. {
  2164. if ( !isdefined( self.damageOwner ) )
  2165. {
  2166. self RadiusDamage( damageLocation, range, maxdamage, mindamage, self );
  2167. }
  2168. else
  2169. {
  2170. self RadiusDamage( damageLocation, range, maxdamage, mindamage, self.damageOwner );
  2171. if ( isVehicle )
  2172. {
  2173. self.damageOwner notify( "destroyed_car" );
  2174. level notify( "player_destroyed_car", self.damageOwner, damageLocation );
  2175. }
  2176. }
  2177. }
  2178.  
  2179. if ( IsDefined( earthQuakeScale ) && IsDefined( earthQuakeRadius ) )
  2180. Earthquake( earthQuakeScale, 2.0, damageLocation, earthQuakeRadius );
  2181.  
  2182. /#
  2183. if ( GetDvarInt( "destructibles_show_radiusdamage" ) == 1 )
  2184. thread debug_radiusdamage_circle( damageLocation, range, maxdamage, mindamage );
  2185. #/
  2186.  
  2187. // explosion damage done, resume friendly fire if it was enabled
  2188. level thread set_disable_friendlyfire_value_delayed( 0, 0.05 );
  2189.  
  2190. magnitudeScaler = 0.01;
  2191. magnitude = range * magnitudeScaler;
  2192. Assert( magnitude > 0 );
  2193. range *= .99;
  2194. PhysicsExplosionSphere( damageLocation, range, 0, magnitude );
  2195.  
  2196. if ( stopTakingDamage )
  2197. {
  2198. self SetCanDamage( false );
  2199. self thread cleanupVars();
  2200. }
  2201.  
  2202. self notify( "destroyed" );
  2203. }
  2204.  
  2205. cleanupVars()
  2206. {
  2207. // wait so we can make sure they are no longer needed
  2208. wait 0.05;
  2209.  
  2210. if ( !isdefined( self ) )
  2211. return;
  2212.  
  2213. if ( isdefined( self.waiting_for_queue ) )
  2214. self waittill( "queue_processed" );
  2215.  
  2216. if ( !isdefined( self ) )
  2217. return;
  2218.  
  2219. self.animsapplied = undefined;
  2220. self.attacker = undefined;
  2221. self.car_damage_owner_recorder = undefined;
  2222. self.caralarm = undefined;
  2223. self.damageowner = undefined;
  2224. self.destructible_parts = undefined;
  2225. self.destructible_type = undefined;
  2226. self.destuctableinfo = undefined;
  2227. self.healthdrain = undefined;
  2228. self.non_player_damage = undefined;
  2229. self.player_damage = undefined;
  2230. }
  2231.  
  2232. set_disable_friendlyfire_value_delayed( value, delay )
  2233. {
  2234. level notify( "set_disable_friendlyfire_value_delayed" );
  2235. level endon( "set_disable_friendlyfire_value_delayed" );
  2236.  
  2237. Assert( IsDefined( value ) );
  2238.  
  2239. if ( IsDefined( delay ) )
  2240. wait delay;
  2241.  
  2242. level.friendlyFireDisabledForDestructible = value;
  2243. }
  2244.  
  2245. /*
  2246. arcadeMode_car_kill()
  2247. {
  2248. if ( !isSP() )
  2249. return false;
  2250.  
  2251. if ( !arcadeMode() )
  2252. return false;
  2253.  
  2254. if ( level.script == "ac130" )
  2255. return false;
  2256.  
  2257. if ( IsDefined( level.allCarsDamagedByPlayer ) )
  2258. return true;
  2259.  
  2260. return self maps\_gameskill::player_did_most_damage();
  2261. }
  2262. */
  2263. connectTraverses()
  2264. {
  2265. clip = get_traverse_disconnect_brush();
  2266.  
  2267. if ( !isdefined( clip ) )
  2268. return;
  2269.  
  2270. Assert( IsDefined( level.connectPathsFunction ) );
  2271. clip call [[ level.connectPathsFunction ]]();
  2272. clip.origin -= ( 0, 0, 10000 );
  2273. }
  2274.  
  2275. disconnectTraverses()
  2276. {
  2277. clip = get_traverse_disconnect_brush();
  2278.  
  2279. if ( !isdefined( clip ) )
  2280. return;
  2281.  
  2282. clip.origin += ( 0, 0, 10000 );
  2283. Assert( IsDefined( level.disconnectPathsFunction ) );
  2284. clip call [[ level.disconnectPathsFunction ]]();
  2285. clip.origin -= ( 0, 0, 10000 );
  2286. }
  2287.  
  2288. get_traverse_disconnect_brush()
  2289. {
  2290. if ( !isdefined( self.target ) )
  2291. return undefined;
  2292.  
  2293. targets = GetEntArray( self.target, "targetname" );
  2294. foreach ( target in targets )
  2295. {
  2296. if ( IsSpawner( target ) )
  2297. continue;
  2298. if ( IsDefined( target.script_destruct_collision ) )
  2299. continue;
  2300. if ( target.code_classname == "light" )
  2301. continue;
  2302. if ( !target.spawnflags & 1 )
  2303. continue;
  2304. return target;
  2305. }
  2306. }
  2307.  
  2308. hideapart( tagName )
  2309. {
  2310. self HidePart( tagName );
  2311. }
  2312.  
  2313. showapart( tagName )
  2314. {
  2315. self ShowPart( tagName );
  2316. }
  2317.  
  2318. disable_explosion()
  2319. {
  2320. self.dontAllowExplode = true;
  2321. }
  2322.  
  2323. force_explosion()
  2324. {
  2325. self.dontAllowExplode = undefined;
  2326. self.forceExploding = true;
  2327. self notify( "damage", 100000, self, self.origin, self.origin, "MOD_EXPLOSIVE", "", "" );
  2328. }
  2329.  
  2330. get_dummy()
  2331. {
  2332. if ( !isSP() )
  2333. return self;
  2334.  
  2335. if ( self.modeldummyon )
  2336. eModel = self.modeldummy;
  2337. else
  2338. eModel = self;
  2339. return eModel;
  2340. }
  2341.  
  2342. play_loop_sound_on_destructible( alias, tag )
  2343. {
  2344. eModel = get_dummy();
  2345.  
  2346. org = Spawn( "script_origin", ( 0, 0, 0 ) );
  2347. if ( IsDefined( tag ) )
  2348. org.origin = eModel GetTagOrigin( tag );
  2349. else
  2350. org.origin = eModel.origin;
  2351.  
  2352. org PlayLoopSound( alias );
  2353.  
  2354. eModel thread force_stop_sound( alias );
  2355.  
  2356. eModel waittill( "stop sound" + alias );
  2357. if ( !isdefined( org ) )
  2358. return;
  2359.  
  2360. org StopLoopSound( alias );
  2361. org Delete();
  2362. }
  2363.  
  2364. force_stop_sound( alias )
  2365. {
  2366. self endon( "stop sound" + alias );
  2367.  
  2368. level waittill( "putout_fires" );
  2369. self notify( "stop sound" + alias );
  2370. }
  2371.  
  2372. notifyDamageAfterFrame( damage, attacker, direction_vec, point, damageType, modelName, tagName )
  2373. {
  2374. if ( IsDefined( level.notifyDamageAfterFrame ) )
  2375. return;
  2376.  
  2377. level.notifyDamageAfterFrame = true;
  2378. waittillframeend;
  2379. if ( IsDefined( self.exploded ) )
  2380. {
  2381. level.notifyDamageAfterFrame = undefined;
  2382. return;
  2383. }
  2384.  
  2385. if ( isSP() )
  2386. damage /= SP_DAMAGE_BIAS;
  2387. else
  2388. damage /= MP_DAMAGE_BIAS;
  2389.  
  2390. self notify( "damage", damage, attacker, direction_vec, point, damageType, modelName, tagName );
  2391. level.notifyDamageAfterFrame = undefined;
  2392. }
  2393.  
  2394. play_sound( alias, tag )
  2395. {
  2396. if ( IsDefined( tag ) )
  2397. {
  2398. org = Spawn( "script_origin", self GetTagOrigin( tag ) );
  2399. org Hide();
  2400. org LinkTo( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
  2401. }
  2402. else
  2403. {
  2404. org = Spawn( "script_origin", ( 0, 0, 0 ) );
  2405. org Hide();
  2406. org.origin = self.origin;
  2407. org.angles = self.angles;
  2408. org LinkTo( self );
  2409. }
  2410.  
  2411. org PlaySound( alias );
  2412. wait( 5.0 );
  2413. if ( IsDefined( org ) )
  2414. org Delete();
  2415. }
  2416.  
  2417. toString( num )
  2418. {
  2419. return( "" + num );
  2420. }
  2421.  
  2422. do_car_alarm()
  2423. {
  2424. if ( IsDefined( self.carAlarm ) )
  2425. return;
  2426. self.carAlarm = true;
  2427.  
  2428. if ( !should_do_car_alarm() )
  2429. return;
  2430.  
  2431. self.car_alarm_org = Spawn( "script_model", self.origin );
  2432. self.car_alarm_org Hide();
  2433. self.car_alarm_org PlayLoopSound( CAR_ALARM_ALIAS );
  2434.  
  2435. level.currentCarAlarms++;
  2436. Assert( level.currentCarAlarms <= MAX_SIMULTANEOUS_CAR_ALARMS );
  2437.  
  2438. self thread car_alarm_timeout();
  2439.  
  2440. self waittill( "stop_car_alarm" );
  2441.  
  2442. level.lastCarAlarmTime = GetTime();
  2443. level.currentCarAlarms--;
  2444.  
  2445. self.car_alarm_org StopLoopSound( CAR_ALARM_ALIAS );
  2446. self.car_alarm_org Delete();
  2447. }
  2448.  
  2449. car_alarm_timeout()
  2450. {
  2451. self endon( "stop_car_alarm" );
  2452.  
  2453. // Car alarm only lasts this long until it automatically shuts up
  2454. wait CAR_ALARM_TIMEOUT;
  2455.  
  2456. if ( !isdefined( self ) )
  2457. return;
  2458.  
  2459. self thread play_sound( CAR_ALARM_OFF_ALIAS );
  2460. self notify( "stop_car_alarm" );
  2461. }
  2462.  
  2463. should_do_car_alarm()
  2464. {
  2465. // If there is already car alarms going off then don't trigger another one
  2466. if ( level.currentCarAlarms >= MAX_SIMULTANEOUS_CAR_ALARMS )
  2467. return false;
  2468.  
  2469. // If the player hasn't heard a car alarm yet during this level
  2470. timeElapsed = undefined;
  2471. if ( !isdefined( level.lastCarAlarmTime ) )
  2472. {
  2473. if ( cointoss() )
  2474. return true;
  2475. timeElapsed = GetTime() - level.commonStartTime;
  2476. }
  2477. else
  2478. {
  2479. timeElapsed = GetTime() - level.lastCarAlarmTime;
  2480. }
  2481. Assert( IsDefined( timeElapsed ) );
  2482.  
  2483. // If the player hasn't heard a car alarm in a while then do one
  2484. if ( level.currentCarAlarms == 0 && timeElapsed >= NO_CAR_ALARM_MAX_ELAPSED_TIME )
  2485. return true;
  2486.  
  2487. if ( RandomInt( 100 ) <= 33 )
  2488. return true;
  2489.  
  2490. return false;
  2491. }
  2492.  
  2493. do_random_dynamic_attachment( tagName, attach_model_1, attach_model_2, clipToRemove )
  2494. {
  2495. Assert( IsDefined( tagName ) );
  2496. Assert( IsDefined( attach_model_1 ) );
  2497.  
  2498. spawnedModels = [];
  2499.  
  2500. if ( isSP() )
  2501. {
  2502. self Attach( attach_model_1, tagName, false );
  2503. if ( IsDefined( attach_model_2 ) && attach_model_2 != "" )
  2504. self Attach( attach_model_2, tagName, false );
  2505. }
  2506. else
  2507. {
  2508. //attach doesn't work in MP so fake it
  2509. spawnedModels[ 0 ] = Spawn( "script_model", self GetTagOrigin( tagName ) );
  2510. spawnedModels[ 0 ].angles = self GetTagAngles( tagName );
  2511. spawnedModels[ 0 ] SetModel( attach_model_1 );
  2512. spawnedModels[ 0 ] LinkTo( self, tagName );
  2513.  
  2514. if ( IsDefined( attach_model_2 ) && attach_model_2 != "" )
  2515. {
  2516. spawnedModels[ 1 ] = Spawn( "script_model", self GetTagOrigin( tagName ) );
  2517. spawnedModels[ 1 ].angles = self GetTagAngles( tagName );
  2518. spawnedModels[ 1 ] SetModel( attach_model_2 );
  2519. spawnedModels[ 1 ] LinkTo( self, tagName );
  2520. }
  2521. }
  2522.  
  2523. // remove collision that might not be used for this attachment
  2524. if ( isdefined( clipToRemove ) )
  2525. {
  2526. tagOrg = self getTagOrigin( tagName );
  2527. clip = get_closest_with_targetname( tagOrg, clipToRemove );
  2528. if ( isdefined( clip ) )
  2529. clip delete();
  2530. }
  2531.  
  2532. self waittill( "exploded" );
  2533.  
  2534. if ( isSP() )
  2535. {
  2536. self Detach( attach_model_1, tagName );
  2537. self Attach( attach_model_1 + DESTROYED_ATTACHMENT_SUFFIX, tagName, false );
  2538.  
  2539. if ( IsDefined( attach_model_2 ) && attach_model_2 != "" )
  2540. {
  2541. self Detach( attach_model_2, tagName );
  2542. self Attach( attach_model_2 + DESTROYED_ATTACHMENT_SUFFIX, tagName, false );
  2543. }
  2544. }
  2545. else
  2546. {
  2547. spawnedModels[ 0 ] SetModel( attach_model_1 + DESTROYED_ATTACHMENT_SUFFIX );
  2548. if ( IsDefined( attach_model_2 ) && attach_model_2 != "" )
  2549. spawnedModels[ 1 ] SetModel( attach_model_2 + DESTROYED_ATTACHMENT_SUFFIX );
  2550. }
  2551. }
  2552.  
  2553. get_closest_with_targetname( origin, targetname )
  2554. {
  2555. closestDist = undefined;
  2556. closestEnt = undefined;
  2557. ents = getentarray( targetname, "targetname" );
  2558. foreach ( ent in ents )
  2559. {
  2560. d = distanceSquared( origin, ent.origin );
  2561.  
  2562. if ( !isdefined( closestDist ) || ( d < closestDist ) )
  2563. {
  2564. closestDist = d;
  2565. closestEnt = ent;
  2566. }
  2567. }
  2568.  
  2569. return closestEnt;
  2570. }
  2571.  
  2572. player_touching_post_clip()
  2573. {
  2574. post_clip = undefined;
  2575.  
  2576. if ( !IsDefined( self.target ) )
  2577. {
  2578. return false;
  2579. }
  2580.  
  2581. targets = GetEntArray( self.target, "targetname" );
  2582. foreach ( target in targets )
  2583. {
  2584. if ( IsDefined( target.script_destruct_collision ) && target.script_destruct_collision == "post" )
  2585. {
  2586. post_clip = target;
  2587. break;
  2588. }
  2589. }
  2590.  
  2591. if ( !IsDefined( post_clip ) )
  2592. {
  2593. return false;
  2594. }
  2595.  
  2596. player = get_player_touching( post_clip );
  2597.  
  2598. if ( IsDefined( player ) )
  2599. {
  2600. return true;
  2601. }
  2602.  
  2603. return false;
  2604. }
  2605.  
  2606. get_player_touching( ent )
  2607. {
  2608. foreach ( player in level.players )
  2609. {
  2610. if ( !IsAlive( player ) )
  2611. {
  2612. continue;
  2613. }
  2614.  
  2615. if ( ent IsTouching( player ) )
  2616. {
  2617. return player;
  2618. }
  2619. }
  2620.  
  2621. return undefined;
  2622. }
  2623.  
  2624. is_so()
  2625. {
  2626. return GetDvar( "specialops" ) == "1";
  2627. }
  2628.  
  2629. destructible_handles_collision_brushes()
  2630. {
  2631. targets = GetEntArray( self.target, "targetname" );
  2632. collision_funcs = [];
  2633. collision_funcs[ "pre" ] = ::collision_brush_pre_explosion;
  2634. collision_funcs[ "post" ] = ::collision_brush_post_explosion;
  2635.  
  2636. foreach ( target in targets )
  2637. {
  2638. if ( !isdefined( target.script_destruct_collision ) )
  2639. continue;
  2640. self thread [[ collision_funcs[ target.script_destruct_collision ] ]]( target );
  2641. }
  2642. }
  2643.  
  2644. collision_brush_pre_explosion( clip )
  2645. {
  2646. waittillframeend;// wait for same area post brushes to connect before we disconnect
  2647.  
  2648. if ( isSP() )
  2649. clip call [[ level.disconnectPathsFunction ]]();
  2650.  
  2651. self waittill( "exploded" );
  2652.  
  2653. if ( isSP() )
  2654. clip call [[ level.connectPathsFunction ]]();
  2655.  
  2656. clip Delete();
  2657. }
  2658.  
  2659. collision_brush_post_explosion( clip )
  2660. {
  2661. clip NotSolid();
  2662.  
  2663. if ( isSP() )
  2664. clip call [[ level.connectPathsFunction ]]();
  2665.  
  2666. self waittill( "exploded" );
  2667. waittillframeend;// wait for same area pre brushes to connect before we disconnect
  2668.  
  2669. if ( isSP() )
  2670. {
  2671. clip call [[ level.disconnectPathsFunction ]]();
  2672.  
  2673. if ( is_so() )
  2674. {
  2675. player = get_player_touching( clip );
  2676. if ( isdefined( player ) )
  2677. {
  2678. assertex( isdefined( level.func_destructible_crush_player ), "Special Ops requires level.func_destructible_crush_player to be defined." );
  2679. self thread [[ level.func_destructible_crush_player ]]( player );
  2680. }
  2681. }
  2682. else
  2683. {
  2684. /#
  2685. thread debug_player_in_post_clip( clip );
  2686. #/
  2687. }
  2688. }
  2689.  
  2690. clip Solid();
  2691. }
  2692.  
  2693. debug_player_in_post_clip( clip )
  2694. {
  2695. /#
  2696. wait( 0.1 );
  2697. player = get_player_touching( clip );
  2698. if ( IsDefined( player ) )
  2699. {
  2700. AssertEx( !IsAlive( player ), "Player is in a clip of a destructible, but is still alive. He's either in godmode or we're doing something wrong. Player will be stuck now." );
  2701. }
  2702. #/
  2703. }
  2704.  
  2705.  
  2706. destructible_get_my_breakable_light( range )
  2707. {
  2708. AssertEx( !isdefined( self.breakable_light ), "Tried to define my breakable light twice" );
  2709.  
  2710. // light = getClosest( self.origin, GetEntArray("light_destructible","targetname") );
  2711. // beh getClosest is SP only.. to lazy to port right now.
  2712. // find the nearest light with targetname light_destructible within range and turn it out. scripting stuff in prefabs is still hard.
  2713. //
  2714. lights = GetEntArray( "light_destructible", "targetname" );
  2715. if ( isSP() )// mp lacks noteworthy powers
  2716. {
  2717. lights2 = GetEntArray( "light_destructible", "script_noteworthy" );
  2718. lights = array_combine( lights, lights2 );
  2719. }
  2720. if ( !lights.size )
  2721. return;
  2722.  
  2723. shortest_distance = range * range;
  2724. the_light = undefined;
  2725. foreach ( light in lights )
  2726. {
  2727. dist = DistanceSquared( self.origin, light.origin );
  2728. if ( dist < shortest_distance )
  2729. {
  2730. the_light = light;
  2731. shortest_distance = dist;
  2732. }
  2733. }
  2734.  
  2735. if ( !isdefined( the_light ) )
  2736. return;
  2737.  
  2738. self.breakable_light = the_light;
  2739. }
  2740.  
  2741. break_nearest_light( range )
  2742. {
  2743. if ( !isdefined( self.breakable_light ) )
  2744. return;
  2745.  
  2746. self.breakable_light SetLightIntensity( 0 );
  2747. }
  2748.  
  2749.  
  2750. debug_radiusdamage_circle( center, radius, maxdamage, mindamage )
  2751. {
  2752. circle_sides = 16;
  2753.  
  2754. angleFrac = 360 / circle_sides;
  2755.  
  2756. // Z circle
  2757. circlepoints = [];
  2758. for ( i = 0; i < circle_sides; i++ )
  2759. {
  2760. angle = ( angleFrac * i );
  2761. xAdd = Cos( angle ) * radius;
  2762. yAdd = Sin( angle ) * radius;
  2763. x = center[ 0 ] + xAdd;
  2764. y = center[ 1 ] + yAdd;
  2765. z = center[ 2 ];
  2766. circlepoints[ circlepoints.size ] = ( x, y, z );
  2767. }
  2768. thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center );
  2769.  
  2770. // X circle
  2771. circlepoints = [];
  2772. for ( i = 0; i < circle_sides; i++ )
  2773. {
  2774. angle = ( angleFrac * i );
  2775. xAdd = Cos( angle ) * radius;
  2776. yAdd = Sin( angle ) * radius;
  2777. x = center[ 0 ];
  2778. y = center[ 1 ] + xAdd;
  2779. z = center[ 2 ] + yAdd;
  2780. circlepoints[ circlepoints.size ] = ( x, y, z );
  2781. }
  2782. thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center );
  2783.  
  2784. // Y circle
  2785. circlepoints = [];
  2786. for ( i = 0; i < circle_sides; i++ )
  2787. {
  2788. angle = ( angleFrac * i );
  2789. xAdd = Cos( angle ) * radius;
  2790. yAdd = Sin( angle ) * radius;
  2791. x = center[ 0 ] + yAdd;
  2792. y = center[ 1 ];
  2793. z = center[ 2 ] + xAdd;
  2794. circlepoints[ circlepoints.size ] = ( x, y, z );
  2795. }
  2796. thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center );
  2797.  
  2798. // draw center and range with values
  2799. Print3d( center, maxdamage, ( 1, 1, 1 ), 1, 1, 100 );
  2800. Print3d( center + ( radius, 0, 0 ), mindamage, ( 1, 1, 1 ), 1, 1, 100 );
  2801. }
  2802.  
  2803. debug_circle_drawlines( circlepoints, duration, color, center )
  2804. {
  2805. Assert( IsDefined( center ) );
  2806. for ( i = 0; i < circlepoints.size; i++ )
  2807. {
  2808. start = circlepoints[ i ];
  2809. if ( i + 1 >= circlepoints.size )
  2810. end = circlepoints[ 0 ];
  2811. else
  2812. end = circlepoints[ i + 1 ];
  2813.  
  2814. thread debug_line( start, end, duration, color );
  2815. thread debug_line( center, start, duration, color );
  2816. }
  2817. }
  2818.  
  2819. debug_line( start, end, duration, color )
  2820. {
  2821. if ( !isdefined( color ) )
  2822. color = ( 1, 1, 1 );
  2823.  
  2824. for ( i = 0; i < ( duration * 20 ); i++ )
  2825. {
  2826. Line( start, end, color );
  2827. wait 0.05;
  2828. }
  2829. }
  2830.  
  2831. spotlight_tag_origin_cleanup( tag_origin )
  2832. {
  2833. tag_origin endon( "death" );
  2834. level waittill( "new_destructible_spotlight" );
  2835. tag_origin Delete();
  2836. }
  2837.  
  2838. spotlight_fizzles_out( action_v, eModel, damageType, partIndex, tag_origin )
  2839. {
  2840. level endon( "new_destructible_spotlight" );
  2841. thread spotlight_tag_origin_cleanup( tag_origin );
  2842.  
  2843. maxVal = action_v[ "spotlight_brightness" ];
  2844. noself_func( "setsaveddvar", "r_spotlightbrightness", maxVal );
  2845.  
  2846. wait( RandomFloatRange( 2, 5 ) );
  2847.  
  2848. steps = RandomIntRange( 5, 11 );
  2849. for ( i = 0; i < steps; i++ )
  2850. {
  2851. noself_func( "setsaveddvar", "r_spotlightbrightness", maxVal * 0.65 );
  2852. wait( 0.05 );
  2853. noself_func( "setsaveddvar", "r_spotlightbrightness", maxVal );
  2854. wait( 0.05 );
  2855. }
  2856.  
  2857. destructible_fx_think( action_v, eModel, damageType, partIndex );
  2858. level.destructible_spotlight Delete();
  2859. tag_origin Delete();
  2860. }
  2861.  
  2862. destructible_spotlight_think( action_v, eModel, damageType, partIndex )
  2863. {
  2864. // spotlights are MP only
  2865. if ( !isSP() )
  2866. return;
  2867.  
  2868. if ( !isdefined( self.breakable_light ) )
  2869. return;
  2870.  
  2871. emodel self_func( "startignoringspotLight" );
  2872.  
  2873. // set all the dvars for this light type
  2874. foreach ( dvar, val in action_v[ "dvars" ] )
  2875. {
  2876. noself_func( "setsaveddvar", dvar, val );
  2877. }
  2878.  
  2879. if ( !isdefined( level.destructible_spotlight ) )
  2880. {
  2881. level.destructible_spotlight = spawn_tag_origin();
  2882. fx = getfx( action_v[ "spotlight_fx" ] );
  2883. PlayFXOnTag( fx, level.destructible_spotlight, "tag_origin" );
  2884. }
  2885.  
  2886. //self.breakable_light thread maps\_debug::drawForwardForever( 200, (1,0,0) );
  2887. //level.destructible_spotlight thread maps\_debug::drawForwardForever( 200, (1,1,0) );
  2888.  
  2889. level notify( "new_destructible_spotlight" );
  2890.  
  2891.  
  2892. level.destructible_spotlight Unlink();
  2893.  
  2894.  
  2895. tag_origin = spawn_tag_origin();
  2896. tag_origin LinkTo( self, action_v[ "spotlight_tag" ], ( 0, 0, 0 ), ( 0, 0, 0 ) );
  2897.  
  2898. //eModel thread maps\_debug::drawTagForever( action_v[ "spotlight_tag" ] );
  2899.  
  2900. level.destructible_spotlight.origin = self.breakable_light.origin;
  2901. level.destructible_spotlight.angles = self.breakable_light.angles;
  2902. level.destructible_spotlight thread spotlight_fizzles_out( action_v, eModel, damageType, partIndex, tag_origin );
  2903.  
  2904. wait( 0.05 );// Wait for the spawned tag_origin to get to the right place before linking
  2905. if ( IsDefined( tag_origin ) )
  2906. {
  2907. // can be deleted during wait
  2908. level.destructible_spotlight LinkTo( tag_origin );
  2909. }
  2910.  
  2911. }
  2912.  
  2913. is_valid_damagetype( damageType, v, idx, groupNum )
  2914. {
  2915. valid_damagetype = undefined;
  2916. if ( IsDefined( v[ "fx_valid_damagetype" ] ) )
  2917. valid_damagetype = v[ "fx_valid_damagetype" ][ groupNum ][ idx ];
  2918.  
  2919. if ( !isdefined( valid_damagetype ) )
  2920. return true;
  2921.  
  2922. return IsSubStr( valid_damagetype, damageType );
  2923. }
  2924.  
  2925. destructible_sound_think( action_v, eModel, damageType, groupNum )
  2926. {
  2927. if ( isdefined( self.exploded ) )
  2928. return undefined;
  2929.  
  2930. if ( !isDefined( action_v[ "sound" ] ) )
  2931. return undefined;
  2932.  
  2933. if ( !isdefined( groupNum ) )
  2934. groupNum = 0;
  2935.  
  2936. assert( isDefined( action_v[ "sound" ][ groupNum ] ) );
  2937.  
  2938. for ( i = 0; i < action_v[ "sound" ][ groupNum ].size; i++ )
  2939. {
  2940. validSoundCause = self isValidSoundCause( "soundCause", action_v, i, damageType, groupNum );
  2941. if ( !validSoundCause )
  2942. continue;
  2943.  
  2944. soundAlias = action_v[ "sound" ][ groupNum ][ i ];
  2945. soundTagName = action_v[ "tagName" ]; //chad - dont think I need a groupnum index here, but now we probably can't support playing sounds on multiple tags within one group
  2946. eModel thread play_sound( soundAlias, soundTagName );
  2947. }
  2948.  
  2949. return groupNum;
  2950. }
  2951.  
  2952. destructible_fx_think( action_v, eModel, damageType, partIndex, groupNum )
  2953. {
  2954. if ( !isdefined( action_v[ "fx" ] ) )
  2955. return undefined;
  2956.  
  2957. if ( !isdefined( groupNum ) )
  2958. groupNum = randomInt( action_v[ "fx_filename" ].size );
  2959.  
  2960. if ( !isDefined( action_v[ "fx" ][ groupNum ] ) )
  2961. {
  2962. println( "^1destructible tried to use custom groupNum for FX but that group didn't exist" );
  2963. groupNum = randomInt( action_v[ "fx_filename" ].size );
  2964. }
  2965.  
  2966. assert( isDefined( action_v[ "fx" ][ groupNum ] ) );
  2967.  
  2968. fx_size = action_v[ "fx_filename" ][ groupNum ].size;
  2969.  
  2970. for ( idx = 0; idx < fx_size; idx++ )
  2971. {
  2972. if ( !is_valid_damagetype( damageType, action_v, idx, groupNum ) )
  2973. continue;
  2974.  
  2975. fx = action_v[ "fx" ][ groupNum ][ idx ];
  2976.  
  2977. if ( IsDefined( action_v[ "fx_tag" ][ groupNum ][ idx ] ) )
  2978. {
  2979. fx_tag = action_v[ "fx_tag" ][ groupNum ][ idx ];
  2980. self notify( "FX_State_Change" + partIndex );
  2981.  
  2982. if ( action_v[ "fx_useTagAngles" ][ groupNum ][ idx ] )
  2983. {
  2984. PlayFXOnTag( fx, eModel, fx_tag );
  2985. }
  2986. else
  2987. {
  2988. fxOrigin = eModel GetTagOrigin( fx_tag );
  2989. forward = ( fxOrigin + ( 0, 0, 100 ) ) - fxOrigin;
  2990. PlayFX( fx, fxOrigin, forward );
  2991. }
  2992. }
  2993. else
  2994. {
  2995. fxOrigin = eModel.origin;
  2996. forward = ( fxOrigin + ( 0, 0, 100 ) ) - fxOrigin;
  2997. PlayFX( fx, fxOrigin, forward );
  2998. }
  2999. }
  3000.  
  3001. return groupNum;
  3002. }
  3003.  
  3004. destructible_animation_think( action_v, eModel, damageType, partIndex )
  3005. {
  3006. if ( IsDefined( self.exploded ) )
  3007. return undefined;
  3008.  
  3009. if ( !isdefined( action_v[ "animation" ] ) )
  3010. return undefined;
  3011.  
  3012. if ( IsDefined( action_v[ "randomly_flip" ] ) && !isdefined( self.script_noflip ) )
  3013. {
  3014. if ( cointoss() )
  3015. {
  3016. // flip it around for randomness
  3017. self.angles += ( 0, 180, 0 );
  3018. }
  3019. }
  3020.  
  3021. // this stuff is SP only
  3022. if ( IsDefined( action_v[ "spotlight_tag" ] ) )
  3023. {
  3024. thread destructible_spotlight_think( action_v, eModel, damageType, partIndex );
  3025. wait( 0.05 );
  3026. }
  3027.  
  3028. array = random( action_v[ "animation" ] );
  3029.  
  3030. animName = array[ "anim" ];
  3031. animTree = array[ "animTree" ];
  3032. groupNum = array[ "groupNum" ];
  3033. mpAnim = array[ "mpAnim" ];
  3034.  
  3035. maxStartDelay = array[ "maxStartDelay" ];
  3036. animRateMin = array[ "animRateMin" ];
  3037. animRateMax = array[ "animRateMax" ];
  3038.  
  3039. if ( !isdefined( animRateMin ) )
  3040. animRateMin = 1.0;
  3041. if ( !isdefined( animRateMax ) )
  3042. animRateMax = 1.0;
  3043. if ( animRateMin == animRateMax )
  3044. animRate = animRateMin;
  3045. else
  3046. animRate = RandomFloatRange( animRateMin, animRateMax );
  3047.  
  3048. vehicle_dodge_part_animation = array[ "vehicle_exclude_anim" ];
  3049.  
  3050. if ( self.code_classname == "script_vehicle" && vehicle_dodge_part_animation )
  3051. return undefined;
  3052.  
  3053. eModel self_func( "useanimtree", animTree );
  3054.  
  3055. animType = array[ "animType" ];
  3056.  
  3057. if ( !isdefined( self.animsApplied ) )
  3058. self.animsApplied = [];
  3059. self.animsApplied[ self.animsApplied.size ] = animName;
  3060.  
  3061. if ( IsDefined( self.exploding ) )
  3062. self clear_anims( eModel );
  3063.  
  3064. if ( IsDefined( maxStartDelay ) && maxStartDelay > 0 )
  3065. wait RandomFloat( maxStartDelay );
  3066.  
  3067. // Multiplayer animations work now
  3068. if ( !isSP() )
  3069. {
  3070. if ( IsDefined( mpAnim ) )
  3071. self self_func( "scriptModelPlayAnim", mpAnim );
  3072. return groupNum;
  3073. }
  3074.  
  3075. if ( animType == "setanim" )
  3076. {
  3077. eModel self_func( "setanim", animName, 1.0, 1.0, animRate );
  3078. return groupNum;
  3079. }
  3080.  
  3081. if ( animType == "setanimknob" )
  3082. {
  3083. eModel self_func( "setanimknob", animName, 1.0, 0, animRate );
  3084. return groupNum;
  3085. }
  3086.  
  3087. AssertMsg( "Tried to play an animation on a destructible with an invalid animType: " + animType );
  3088. return undefined;
  3089. }
  3090.  
  3091. clear_anims( eModel )
  3092. {
  3093. //clear all previously blended anims if the vehicle is exploding so the explosion doesn't have to blend with anything
  3094. if ( IsDefined( self.animsApplied ) )
  3095. {
  3096. foreach ( animation in self.animsApplied )
  3097. {
  3098. if ( isSP() )
  3099. eModel self_func( "clearanim", animation, 0 );
  3100. else
  3101. eModel self_func( "scriptModelClearAnim" );
  3102. }
  3103. }
  3104. }
  3105.  
  3106. init_destroyed_count()
  3107. {
  3108. level.destroyedCount = 0;
  3109. level.destroyedCountTimeout = 0.5;
  3110.  
  3111. if ( isSP() )
  3112. level.maxDestructions = 20;
  3113. else
  3114. level.maxDestructions = 2;
  3115.  
  3116. }
  3117.  
  3118. add_to_destroyed_count()
  3119. {
  3120. level.destroyedCount++;
  3121.  
  3122. wait( level.destroyedCountTimeout );
  3123.  
  3124. level.destroyedCount--;
  3125.  
  3126. Assert( level.destroyedCount >= 0 );
  3127. }
  3128.  
  3129. get_destroyed_count()
  3130. {
  3131. return( level.destroyedCount );
  3132. }
  3133.  
  3134. get_max_destroyed_count()
  3135. {
  3136. return( level.maxDestructions );
  3137. }
  3138.  
  3139.  
  3140. init_destructible_frame_queue()
  3141. {
  3142. level.destructibleFrameQueue = [];
  3143. }
  3144.  
  3145. add_destructible_to_frame_queue( destructible, partInfo, damage )
  3146. {
  3147. entNum = self GetEntityNumber();
  3148.  
  3149. if ( !isDefined( level.destructibleFrameQueue[ entNum ] ) )
  3150. {
  3151. level.destructibleFrameQueue[ entNum ] = SpawnStruct();
  3152. level.destructibleFrameQueue[ entNum ].entNum = entNum;
  3153. level.destructibleFrameQueue[ entNum ].destructible = destructible;
  3154. level.destructibleFrameQueue[ entNum ].totalDamage = 0;
  3155. level.destructibleFrameQueue[ entNum ].nearDistance = 9999999;
  3156. level.destructibleFrameQueue[ entNum ].fxCost = 0;
  3157. }
  3158.  
  3159. level.destructibleFrameQueue[ entNum ].fxCost += partInfo.v[ "fxcost" ];
  3160.  
  3161. level.destructibleFrameQueue[ entNum ].totalDamage += damage;
  3162. if ( partInfo.v[ "distance" ] < level.destructibleFrameQueue[ entNum ].nearDistance )
  3163. level.destructibleFrameQueue[ entNum ].nearDistance = partInfo.v[ "distance" ];
  3164.  
  3165. thread handle_destructible_frame_queue();
  3166. }
  3167.  
  3168.  
  3169. handle_destructible_frame_queue()
  3170. {
  3171. level notify( "handle_destructible_frame_queue" );
  3172. level endon( "handle_destructible_frame_queue" );
  3173.  
  3174. wait( 0.05 );
  3175.  
  3176. currentQueue = level.destructibleFrameQueue;
  3177. level.destructibleFrameQueue = [];
  3178.  
  3179. sortedQueue = sort_destructible_frame_queue( currentQueue );
  3180.  
  3181. for ( i = 0; i < sortedQueue.size; i++ )
  3182. {
  3183. if ( get_destroyed_count() < get_max_destroyed_count() )
  3184. {
  3185. if ( sortedQueue[ i ].fxCost )
  3186. thread add_to_destroyed_count();
  3187.  
  3188. sortedQueue[ i ].destructible notify( "queue_processed", true );
  3189. }
  3190. else
  3191. {
  3192. sortedQueue[ i ].destructible notify( "queue_processed", false );
  3193. }
  3194. }
  3195. }
  3196.  
  3197.  
  3198. sort_destructible_frame_queue( unsortedQueue )
  3199. {
  3200. sortedQueue = [];
  3201. foreach ( destructibleInfo in unsortedQueue )
  3202. sortedQueue[ sortedQueue.size ] = destructibleInfo;
  3203.  
  3204. // insertion sort
  3205. for ( i = 1; i < sortedQueue.size; i++ )
  3206. {
  3207. queueStruct = sortedQueue[ i ];
  3208.  
  3209. for ( j = i - 1; j >= 0 && get_better_destructible( queueStruct, sortedQueue[ j ] ) == queueStruct; j-- )
  3210. sortedQueue[ j + 1 ] = sortedQueue[ j ];
  3211.  
  3212. sortedQueue[ j + 1 ] = queueStruct;
  3213. }
  3214.  
  3215. return sortedQueue;
  3216. }
  3217.  
  3218.  
  3219. get_better_destructible( destructibleInfo1, destructibleInfo2 )
  3220. {
  3221. // this is very basic; we can also account for distance, fxcost, etc... if we need to
  3222. if ( destructibleInfo1.totalDamage > destructibleInfo2.totalDamage )
  3223. return destructibleInfo1;
  3224. else
  3225. return destructibleInfo2;
  3226. }
  3227.  
  3228.  
  3229. get_part_FX_cost_for_action_state( partIndex, actionStateIndex )
  3230. {
  3231. fxCost = 0;
  3232.  
  3233. if ( !isDefined( level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ] ) )
  3234. return fxCost;
  3235.  
  3236. action_v = level.destructible_type[ self.destuctableInfo ].parts[ partIndex ][ actionStateIndex ].v;
  3237.  
  3238. if ( IsDefined( action_v[ "fx" ] ) )
  3239. {
  3240. foreach ( fxCostObj in action_v[ "fx_cost" ] )
  3241. {
  3242. foreach ( fxCostVal in fxCostObj )
  3243. fxCost += fxCostVal;
  3244. }
  3245. }
  3246.  
  3247. return fxCost;
  3248. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement