Advertisement
Guest User

Untitled

a guest
Sep 29th, 2010
362
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 50.66 KB | None | 0 0
  1. #include maps\mp\_utility;
  2. #include common_scripts\utility;
  3. //#include _vehicleLogic.gsc;
  4.  
  5. init()
  6. {
  7. //tank support cut
  8. return;
  9. //tank support cut
  10. /*
  11. PrecacheVehicle( "bmp_mp" );
  12. PrecacheVehicle( "m1a1_mp" );
  13. PrecacheVehicle( "bradley_mp" );
  14.  
  15. precacheModel("vehicle_bmp");
  16. precacheModel("vehicle_bradley");
  17.  
  18. precacheModel("sentry_gun");
  19. precacheModel("vehicle_m1a1_abrams_d_static");
  20. precacheTurret( "abrams_minigun_mp" );
  21.  
  22. /#
  23. setDevDvar( "tankDir", "" );
  24. setDevDvar( "tankForceTrigger", 0 );
  25. if ( getDvar( "tankDebug" ) == "" )
  26. setDevDvar( "tankDebug", 0 );
  27. #/
  28.  
  29. level.killstreakFuncs["tank"] = ::useTank;
  30. level.tankFire = loadfx( "explosions/large_vehicle_explosion" );
  31. level.tankCover = loadfx( "props/american_smoke_grenade_mp" );
  32.  
  33. level.otherDir["forward"] = "reverse";
  34. level.otherDir["reverse"] = "forward";
  35.  
  36. tankSpawners = Vehicle_GetSpawnerArray();
  37.  
  38. if ( !tankSpawners.size )
  39. return;
  40.  
  41. if (!isDefined( getVehicleNode( "startnode", "targetname" ) ) )
  42. {
  43. assertEx ( !isDefined( getVehicleNode( "startnode", "targetname" ) ), "Vehicle spawn is setup but tank path is not setup in this level bug your friendly neighborhood LD." );
  44. return false;
  45. }
  46.  
  47. level.tankSpawner["allies"] = tankSpawners[0];
  48. level.tankSpawner["axis"] = tankSpawners[0];
  49. level.pathCount = 0;
  50.  
  51. foreach ( spawner in tankSpawners )
  52. {
  53. if ( isSubStr( spawner.model, "bradley" ) )
  54. level.tankSpawner["allies"] = spawner;
  55.  
  56. if ( isSubStr( spawner.model, "bmp" ) )
  57. level.tankSpawner["axis"] = spawner;
  58. }
  59.  
  60. level setupPaths();
  61.  
  62. */
  63. }
  64.  
  65.  
  66. spawnArmor( owner, vehicletype, model )
  67. {
  68. armor = self Vehicle_DoSpawn( "tank", owner );
  69. //armor setModel( model );
  70.  
  71. armor.health = 3000;
  72. armor.targeting_delay = 1;
  73. armor.team = owner.team;
  74. armor.pers["team"] = armor.team;
  75. armor.owner = owner;
  76. armor setCanDamage( true );
  77. armor.standardSpeed = 12;
  78.  
  79. armor thread deleteOnZ();
  80. armor addToTankList();
  81. armor.damageCallback = ::Callback_VehicleDamage;
  82.  
  83. return armor;
  84. }
  85.  
  86. deleteOnZ()
  87. {
  88. self endon ( "death" );
  89.  
  90. originalZ = self.origin[2];
  91.  
  92. for ( ;; )
  93. {
  94. if ( originalZ - self.origin[2] > 2048 )
  95. {
  96. self.health = 0;
  97. self notify( "death" );
  98. return;
  99. }
  100. wait ( 1.0 );
  101. }
  102. }
  103.  
  104.  
  105. useTank( lifeId )
  106. {
  107. return ( self tryUseTank( ) );
  108. }
  109.  
  110. tryUseTank( )
  111. {
  112. if ( isDefined( level.tankInUse ) && level.tankInUse )
  113. {
  114. self iPrintLnBold( "Armor support unavailable." );
  115. return false;
  116. }
  117.  
  118. if (!isDefined( getVehicleNode( "startnode", "targetname" ) ) )
  119. {
  120. self iPrintLnBold( "Tank is currently not supported in this level, bug your friendly neighborhood LD." );
  121. return false;
  122. }
  123.  
  124. if ( !Vehicle_GetSpawnerArray().size )
  125. return false;
  126.  
  127. if ( self.team == "allies" )
  128. tank = level.tankSpawner["allies"] spawnArmor( self, "vehicle_bradley" );
  129. else
  130. tank = level.tankSpawner["axis"] spawnArmor( self, "vehicle_bmp" );
  131.  
  132. //level.tank = tank;
  133. tank startTank();
  134. return true;
  135. }
  136.  
  137.  
  138. startTank( tankType )
  139. {
  140. startNode = getVehicleNode( "startnode", "targetname" );
  141. waitNode = getVehicleNode( "waitnode", "targetname" );
  142. self.nodes = GetVehicleNodeArray( "info_vehicle_node", "classname" );
  143.  
  144. level.tankInUse = true;
  145. self thread tankUpdate( startNode, waitNode );
  146. //self thread tankUpdateReverse( startNode, waitNode );
  147.  
  148. self thread tankDamageMonitor();
  149. level.tank = self;
  150.  
  151.  
  152. if ( level.teamBased )
  153. {
  154. objIDAllies = maps\mp\gametypes\_gameobjects::getNextObjID();
  155. objective_add( objIDAllies, "invisible", (0,0,0) );
  156. objective_team( objIDAllies, "allies" );
  157. level.tank.objID["allies"] = objIDAllies;
  158.  
  159. objIDAxis = maps\mp\gametypes\_gameobjects::getNextObjID();
  160. objective_add( objIDAxis, "invisible", (0,0,0) );
  161. objective_team( objIDAxis, "axis" );
  162. level.tank.objID["axis"] = objIDAxis;
  163.  
  164. team = self.team;
  165. level.tank.team = team;
  166. level.tank.pers[ "team" ] = team;
  167. }
  168.  
  169. mgTurret = spawnTurret( "misc_turret", self.origin, "abrams_minigun_mp" );
  170. mgTurret linkTo( self, "tag_engine_left", (0,0,-20), (0,0,0) );
  171. mgTurret setModel( "sentry_minigun" );
  172. mgTurret.angles = self.angles;
  173. mgTurret.owner = self.owner;
  174. mgTurret makeTurretInoperable();
  175. self.mgTurret = mgTurret;
  176. self.mgTurret SetDefaultDropPitch( 0 );
  177.  
  178. oldangles = self.angles;
  179. self.angles = (0,0,0);
  180. tankTurretPoint = self getTagOrigin( "tag_flash" );
  181. self.angles = oldangles;
  182. offset = tankTurretPoint - self.origin;
  183.  
  184. self thread waitForChangeTeams();
  185. self thread waitForDisco();
  186.  
  187. self.timeLastFired = getTime();
  188.  
  189. neutralTargetEnt = spawn("script_origin", self getTagOrigin("tag_flash") );
  190. neutralTargetEnt linkTo( self, "tag_origin", offset, (0,0,0) );
  191. neutralTargetEnt hide();
  192. self.neutralTarget = neutralTargetEnt;
  193.  
  194. self thread tankGetTargets();
  195. self thread destroyTank();
  196. self thread tankGetMiniTargets();
  197. self thread checkDanger();
  198. self thread watchForThreat(); //reacts to players about to fire with rockets
  199.  
  200. /#
  201. self thread forceDirection();
  202. #/
  203.  
  204. }
  205.  
  206. waitForChangeTeams()
  207. {
  208. self endon ( "death" );
  209. self.owner endon ( "disconnect" );
  210.  
  211. self.owner waittill ( "joined_team" );
  212. self.health = 0;
  213. self notify( "death" );
  214. }
  215.  
  216. waitForDisco()
  217. {
  218. self endon ( "death" );
  219.  
  220. self.owner waittill ( "disconnect" );
  221. self.health = 0;
  222. self notify( "death" );
  223. }
  224.  
  225. /#
  226. forceDirection()
  227. {
  228. for ( ;; )
  229. {
  230. if ( getDvar( "tankDir" ) != "" )
  231. {
  232. forceDir = getDvar( "tankDir" );
  233. if ( self.veh_pathdir != forceDir )
  234. {
  235. if ( forceDir == "forward" )
  236. self stopToForward();
  237. else
  238. self stopToReverse();
  239. }
  240. }
  241.  
  242. wait ( 0.05 );
  243. }
  244. }
  245. #/
  246.  
  247. //=================================================================
  248. //
  249. // Movement/Update Functions
  250. //
  251. //=================================================================
  252.  
  253. setDirection( direction )
  254. {
  255. if ( self.veh_pathdir != direction )
  256. {
  257. if ( direction == "forward" )
  258. self stopToForward();
  259. else
  260. self stopToReverse();
  261. }
  262. }
  263.  
  264. setEngagementSpeed()
  265. {
  266. self endon( "death" );
  267.  
  268. self notify ( "path_abandoned" );
  269.  
  270. while ( isDefined( self.changingDirection ) )
  271. wait ( 0.05 );
  272.  
  273. newSpeed = 2;
  274. self vehicle_SetSpeed( newSpeed, 10, 10 );
  275. self.speedType = "engage";
  276. }
  277.  
  278. setMiniEngagementSpeed()
  279. {
  280. self endon( "death" );
  281.  
  282. self notify ( "path_abandoned" );
  283.  
  284. while ( isDefined( self.changingDirection ) )
  285. wait ( 0.05 );
  286.  
  287. newSpeed = 2;
  288. self vehicle_SetSpeed( newSpeed, 10, 10 );
  289. self.speedType = "engage";
  290. }
  291.  
  292. setStandardSpeed()
  293. {
  294. self endon( "death" );
  295.  
  296. while ( isDefined( self.changingDirection ) )
  297. wait ( 0.05 );
  298.  
  299. self vehicle_SetSpeed( self.standardSpeed, 10, 10 );
  300. self.speedType = "standard";
  301. }
  302.  
  303. setEvadeSpeed()
  304. {
  305. self endon( "death" );
  306.  
  307. while ( isDefined( self.changingDirection ) )
  308. wait ( 0.05 );
  309.  
  310. self vehicle_setSpeed( 15, 15, 15 );
  311. self.speedType = "evade";
  312. wait(1.5);
  313. self vehicle_setSpeed( self.standardSpeed, 10, 10);
  314. }
  315.  
  316. setDangerSpeed()
  317. {
  318. self endon( "death" );
  319.  
  320. while ( isDefined( self.changingDirection ) )
  321. wait ( 0.05 );
  322.  
  323. self vehicle_SetSpeed( 5, 5, 5 );
  324. self.speedType = "danger";
  325. }
  326.  
  327. stopToReverse()
  328. {
  329. debugPrintLn2( "tank changing direction at " + getTime() );
  330. self vehicle_setSpeed( 0, 5, 6 );
  331.  
  332. self.changingDirection = true;
  333. while ( self.veh_speed > 0 )
  334. wait ( 0.05 );
  335.  
  336. wait( 0.25 );
  337. self.changingDirection = undefined;
  338. debugPrintLn2( "tank done changing direction" );
  339.  
  340. self.veh_transmission = "reverse";
  341. self.veh_pathDir = "reverse";
  342. self vehicle_setSpeed( self.standardSpeed, 5, 6 );
  343. }
  344.  
  345. stopToForward()
  346. {
  347. debugPrintLn2( "tank changing direction at " + getTime() );
  348. self vehicle_setSpeed( 0, 5, 6 );
  349.  
  350. self.changingDirection = true;
  351. while ( self.veh_speed > 0 )
  352. wait ( 0.05 );
  353.  
  354. wait( 0.25 );
  355. self.changingDirection = undefined;
  356. debugPrintLn2( "tank done changing direction" );
  357.  
  358. self.veh_transmission = "forward";
  359. self.veh_pathDir = "forward";
  360. self vehicle_setSpeed( self.standardSpeed, 5, 6 );
  361. }
  362.  
  363. checkDanger()
  364. {
  365. self endon( "death" );
  366.  
  367. targets = [];
  368. players = level.players;
  369. self.numEnemiesClose = 0;
  370.  
  371. for( ;; )
  372. {
  373. foreach (potentialTarget in players)
  374. {
  375. if ( !isDefined( potentialTarget ) )
  376. continue;
  377.  
  378. if ( potentialTarget.team == self.team )
  379. {
  380. wait( .05 );
  381. continue;
  382. }
  383.  
  384. dist = Distance2d( potentialTarget.origin, self.origin );
  385.  
  386. if ( dist < 2048 )
  387. {
  388. self.numEnemiesClose++;
  389. }
  390. wait( .05 );
  391. }
  392.  
  393. if ( isDefined( self.speedType ) && ( self.speedType == "evade" || self.speedType == "engage" ) )
  394. {
  395. self.numEnemiesClose = 0;
  396. continue;
  397. }
  398.  
  399. if ( self.numEnemiesClose > 1 )
  400. self thread setDangerSpeed();
  401. else
  402. self thread setStandardSpeed();
  403.  
  404. self.numEnemiesClose = 0;
  405. wait( .05 );
  406. }
  407. }
  408.  
  409. tankUpdate( startNode, waitNode )
  410. {
  411. self endon( "tankDestroyed" );
  412. self endon( "death" );
  413.  
  414. if ( !isDefined( level.graphNodes ) )
  415. {
  416. self startPath( startNode );
  417. return;
  418. }
  419.  
  420. self attachPath( startNode );
  421. self startPath( startNode );
  422. startNode notify ( "trigger", self, true );
  423.  
  424. wait ( 0.05 );
  425.  
  426. for ( ;; )
  427. {
  428. /#
  429. while ( getDvar( "tankDir" ) != "" )
  430. wait ( 0.05 );
  431. #/
  432.  
  433. while ( isDefined( self.changingDirection ) )
  434. wait ( 0.05 );
  435.  
  436. endNode = self getNodeNearEnemies();
  437.  
  438. if ( isDefined( endNode ) )
  439. self.endNode = endNode;
  440. else
  441. self.endNode = undefined;
  442.  
  443. wait ( 0.65 );
  444. }
  445. }
  446.  
  447.  
  448. Callback_VehicleDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName )
  449. {
  450. if ( ( attacker == self || attacker == self.mgTurret || ( isDefined( attacker.pers ) && attacker.pers["team"] == self.team ) ) && ( attacker != self.owner || meansOfDeath == "MOD_MELEE" ) )
  451. return;
  452.  
  453. tankDamage = modifyDamage( meansOfDeath, damage, attacker );
  454.  
  455. self Vehicle_FinishDamage( inflictor, attacker, tankDamage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName );
  456. }
  457.  
  458.  
  459. // accumulate damage and react
  460. tankDamageMonitor()
  461. {
  462. self endon( "death" );
  463. self.damageTaken = 0;
  464. speed = self vehicle_GetSpeed();
  465. maxHealth = self.health;
  466. stage1 = false;
  467. stage2 = false;
  468. stage3 = false;
  469.  
  470. for( ;; )
  471. {
  472. self waittill( "damage", amount, attacker, direction_vec, point, damageType );
  473.  
  474. if ( isDefined( attacker.classname ) && attacker.classname == "script_vehicle" )
  475. {
  476. if ( isDefined( self.bestTarget ) && self.bestTarget != attacker )
  477. {
  478. self.forcedTarget = attacker;
  479. println( "Abandoning Target due to vehicle attacker" );
  480. self thread explicitAbandonTarget();
  481. }
  482. }
  483. else
  484. {
  485. if ( isPlayer( attacker ) )
  486. {
  487. attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "hitHelicopter" );
  488.  
  489. if ( attacker _hasPerk( "specialty_armorpiercing" ) )
  490. {
  491. damageAdd = amount*level.armorPiercingMod;
  492. self.health -= int(damageAdd);
  493. }
  494. }
  495. }
  496.  
  497. //stages will be used to effect effeciency of the tank
  498. //accuracy, speed, smoke emitters etc....
  499. if ( self.health <= 0 )
  500. {
  501. self notify( "death" );
  502. print("sent death notify via script");
  503. return;
  504. }
  505. else if (self.health < (maxHealth/4) && stage3 == false )
  506. {
  507. //newSpeed = 4;
  508. //self vehicle_SetSpeed( newSpeed, 10, 10 );
  509. //self.standardSpeed = newSpeed;
  510. stage3 = true;
  511.  
  512.  
  513. }
  514. else if (self.health < (maxHealth/2) && stage2 == false )
  515. {
  516. //newSpeed = 6;
  517. //self vehicle_SetSpeed( newSpeed, 10, 10 );
  518. //self.standardSpeed = newSpeed;
  519. stage2 = true;
  520.  
  521.  
  522. }
  523. else if (self.health < (maxHealth/1.5) && stage1 == false )
  524. {
  525. //newSpeed = 10;
  526. //self vehicle_SetSpeed( newSpeed, 10, 10 );
  527. //self.standardSpeed = newSpeed;
  528. stage1 = true;
  529. }
  530.  
  531. if ( amount > 1000 )
  532. {
  533. self handleThreat( attacker );
  534. }
  535. }
  536. }
  537.  
  538. handleThreat( attacker )
  539. {
  540. self endon( "death" );
  541.  
  542. rand = randomInt(100);
  543.  
  544. if ( isDefined( self.bestTarget) && self.bestTarget != attacker && rand > 30 )
  545. {
  546. targ = [];
  547. targ[0] = self.bestTarget;
  548. explicitAbandonTarget( true, self.bestTarget );
  549. self thread acquireTarget( targ );
  550. }
  551. else if ( !isDefined( self.bestTarget ) && rand > 30 )
  552. {
  553. targ = [];
  554. targ[0] = attacker;
  555. self thread acquireTarget( targ );
  556. }
  557. else if ( rand < 30 )
  558. {
  559. // all we know here is that it didnt hit the 70%
  560. playFX( level.tankCover, self.origin);
  561. self thread setEvadeSpeed();
  562. }
  563. else
  564. {
  565. self fireWeapon();
  566. self playSound( "bmp_fire" );
  567. }
  568. }
  569.  
  570. handlePossibleThreat( attacker )
  571. {
  572. self endon( "death" );
  573.  
  574. position = relativeAngle( attacker );
  575. distance = distance( self.origin, attacker.origin );
  576.  
  577. if ( RandomInt( 4 ) < 3 )
  578. return;
  579.  
  580. if( position == "front" && distance < 768 ) //attempts to crush player
  581. {
  582. self thread setEvadeSpeed();
  583. }
  584. else if ( position == "rear_side" || ( position == "rear" && distance >= 768 ) )
  585. {
  586. playFX( level.tankCover, self.origin);
  587. self thread setEvadeSpeed();
  588. }
  589. else if( position == "rear" && distance < 768 ) //attempts to crush player
  590. {
  591.  
  592. self stopToReverse();
  593. self setEvadeSpeed();
  594. wait( 4 );
  595. self stopToForward();
  596.  
  597. }
  598. else if( position == "front_side" || position == "front" )
  599. {
  600. playFX( level.tankCover, self.origin);
  601. self stopToReverse();
  602. self setEvadeSpeed();
  603. wait( 8 );
  604. self stopToForward();
  605. }
  606. }
  607.  
  608. relativeAngle( ent1 )
  609. {
  610. self endon( "death" );
  611. ent1 endon( "death" );
  612. ent1 endon( "disconnect" );
  613.  
  614. tankForwardVector = anglesToForward( self.angles );
  615. tankToEnt = ent1.origin - self.origin;
  616. tankForwardVector *= (1,1,0);
  617. tankToEnt *= (1,1,0 );
  618.  
  619. tankToEnt = VectorNormalize( tankToEnt );
  620. TankForwardVector = VectorNormalize( tankForwardVector );
  621.  
  622. targetCosine = VectorDot( tankToEnt, tankForwardVector );
  623.  
  624. if ( targetCosine > 0 )
  625. {
  626. if ( targetCosine > .9 )
  627. return "front";
  628. else
  629. return "front_side";
  630. }
  631. else
  632. {
  633. if ( targetCosine < -.9 )
  634. return "rear";
  635. else
  636. return "rear_side";
  637. }
  638.  
  639. ent1 iPrintLnBold( targetCosine );
  640. }
  641.  
  642. watchForThreat()
  643. {
  644. self endon( "death" );
  645.  
  646. for ( ;; )
  647. {
  648. targets = [];
  649. players = level.players;
  650.  
  651. foreach (player in players)
  652. {
  653. if ( !isDefined( player ) )
  654. {
  655. wait( .05 );
  656. continue;
  657. }
  658.  
  659. if ( !isTarget( player ) )
  660. {
  661. wait ( .05 );
  662. continue;
  663. }
  664.  
  665. currentWeapon = player GetCurrentWeapon();
  666.  
  667. if ( isSubStr( currentWeapon, "at4" ) || isSubStr( currentWeapon, "stinger" ) || isSubStr( currentWeapon, "javelin" ) )
  668. {
  669. self thread handlePossibleThreat( player );
  670. wait( 8 );
  671. }
  672.  
  673. wait( .15 );
  674. }
  675. }
  676. }
  677.  
  678. //=================================================================
  679. //
  680. // Accessory Functions
  681. //
  682. //=================================================================
  683.  
  684. // checks if owner is valid, returns false if not valid
  685. checkOwner()
  686. {
  687. if ( !isdefined( self.owner ) || !isdefined( self.owner.pers["team"] ) || self.owner.pers["team"] != self.team )
  688. {
  689. self notify ( "abandoned" );
  690. return false;
  691. }
  692. return true;
  693. }
  694.  
  695. drawLine( start, end, timeSlice, color )
  696. {
  697. drawTime = int(timeSlice * 20);
  698. for( time = 0; time < drawTime; time++ )
  699. {
  700. line( start, end, color,false, 1 );
  701. wait ( 0.05 );
  702. }
  703. }
  704.  
  705. modifyDamage( damageType, amount, attacker )
  706. {
  707. if ( damageType == "MOD_RIFLE_BULLET" )
  708. return ( amount );
  709. else if ( damageType == "MOD_PISTOL_BULLET" )
  710. return ( amount );
  711. else if ( damageType == "MOD_IMPACT" )
  712. return ( amount );
  713. else if (damageType == "MOD_MELEE" )
  714. return ( 0 );
  715. else if (damageType == "MOD_EXPLOSIVE_BULLET" )
  716. return ( amount );
  717. else if (damageType == "MOD_GRENADE" )
  718. return ( amount * 5 );
  719. else if (damageType == "MOD_GRENADE_SPLASH" )
  720. return ( amount * 5 );
  721. else
  722. return amount * 10;
  723. }
  724.  
  725. destroyTank()
  726. {
  727. self waittill ( "death" );
  728.  
  729. if ( level.teamBased )
  730. {
  731. team = level.tank.team;
  732. objective_state( level.tank.objID[team], "invisible" );
  733. objective_state( level.tank.objID[level.otherTeam[team]], "invisible" );
  734. }
  735.  
  736. /* get the current team
  737. if ( isDefined( level.tankSpawner["axis"] ) )
  738. destroyedModel = ;
  739. else
  740. destroyedModel = ;
  741. */
  742.  
  743. // award attacker
  744. self notify( "tankDestroyed" );
  745. self Vehicle_SetSpeed( 0,10,10 );
  746. level.tankInUse = false;
  747. playFX( level.spawnFire, self.origin);
  748.  
  749. playFX( level.tankFire, self.origin);
  750.  
  751. self removeFromTankList();
  752.  
  753. destroyedTank = Spawn( "script_model", self.origin );
  754. // set model to current destroyed model.
  755. destroyedTank setModel( "vehicle_m1a1_abrams_d_static" );
  756. destroyedTank.angles = self.angles;
  757. self.mgTurret delete();
  758. self delete();
  759.  
  760. wait(4);
  761. destroyedTank delete();
  762. }
  763.  
  764. //=================================================================
  765. //
  766. // Main Weapon Targeting Functions
  767. //
  768. //=================================================================
  769.  
  770. onHitPitchClamp()
  771. {
  772. self notify( "onTargOrTimeOut" );
  773.  
  774. self endon( "onTargOrTimeOut" );
  775. self endon( "turret_on_target" );
  776.  
  777. self waittill( "turret_pitch_clamped" );
  778. println( "Abandoning Target due to turret not being able to reach target" );
  779. self thread explicitAbandonTarget( false, self.bestTarget );
  780. }
  781.  
  782. fireOnTarget()
  783. {
  784. self endon( "abandonedTarget" );
  785. self endon( "killedTarget" );
  786. self endon( "death" );
  787. self endon( "targetRemoved" );
  788. self endon( "lostLOS" );
  789.  
  790.  
  791. for ( ;; )
  792. {
  793. self onHitPitchClamp();
  794.  
  795. if ( !isDefined( self.bestTarget ) )
  796. continue;
  797.  
  798. flashOrigin = self GetTagOrigin( "tag_flash" );
  799. trace = BulletTrace( self.origin, flashOrigin, false, self );
  800. if ( trace["position"] != flashOrigin )
  801. {
  802. println( "Abandoning Target due to turret not being able to reach target without clipping" );
  803. self thread explicitAbandonTarget( false, self.bestTarget );
  804. }
  805.  
  806. trace = BulletTrace( flashOrigin, self.bestTarget.origin, true, self );
  807. distance = Distance(self.origin, trace["position"] );
  808. realDistance = Distance( self.bestTarget.origin, self.origin );
  809.  
  810. //hitting somthing not even close
  811. if ( distance < 384 || distance + 256 < realDistance )
  812. {
  813. wait( .5 );
  814.  
  815. if ( distance > 384 )
  816. {
  817. self waitForTurretReady();
  818. self FireWeapon();
  819. self playSound( "bmp_fire" );
  820. self.timeLastFired = getTime();
  821. }
  822. println( "Abandoning due to not hitting intended space" );
  823.  
  824. // Adjust forward or backward to hit target...
  825. // check angle of target
  826. position = relativeAngle( self.bestTarget );
  827.  
  828. //if ( position == "rear_side" )
  829. // backup
  830. //if ( position == "front_side" )
  831.  
  832.  
  833. self thread explicitAbandonTarget( false, self.bestTarget );
  834. return;
  835. }
  836.  
  837. self waitForTurretReady();
  838.  
  839. self FireWeapon();
  840. self playSound( "bmp_fire" );
  841. self.timeLastFired = getTime();
  842. }
  843. }
  844.  
  845. waitForTurretReady()
  846. {
  847. self endon( "abandonedTarget" );
  848. self endon( "killedTarget" );
  849. self endon( "death" );
  850. self endon( "targetRemoved" );
  851. self endon( "lostLOS" );
  852.  
  853. timeWaited = getTime() - self.timeLastFired;
  854.  
  855. if ( timeWaited < 1499 )
  856. wait( 1.5 - timeWaited/1000 );
  857. }
  858.  
  859. tankGetTargets( badTarget )
  860. {
  861. self endon( "death" );
  862. self endon( "leaving" );
  863. targets = [];
  864.  
  865. prof_begin( "tankTargets" );
  866.  
  867. for ( ;; )
  868. {
  869. targets = [];
  870. players = level.players;
  871.  
  872. if ( isDefined( self.forcedTarget ) )
  873. {
  874. targets = [];
  875. targets[0] = self.ForcedTarget;
  876. self acquireTarget( targets );
  877. self.forcedTarget = undefined;
  878. }
  879.  
  880. if ( isDefined( level.harrier ) && level.harrier.team != self.team && isAlive( level.harrier ) )
  881. {
  882. if( isVehicleTarget( level.tank ) )
  883. targets[targets.size] = level.tank;
  884. }
  885.  
  886. if ( isDefined( level.chopper ) && level.chopper.team != self.team && isAlive( level.chopper ) )
  887. {
  888. if( isVehicleTarget( level.chopper ) )
  889. targets[targets.size] = level.chopper;
  890. }
  891.  
  892. foreach ( potentialTarget in players )
  893. {
  894. if (!isDefined( potentialTarget ) )
  895. {
  896. wait(.05);
  897. continue;
  898. }
  899.  
  900. if ( isDefined( badTarget ) && potentialTarget == badTarget )
  901. continue;
  902.  
  903. if ( isTarget( potentialTarget ) )
  904. {
  905. if( isDefined( potentialTarget ) )
  906. targets[targets.size] = potentialTarget;
  907. }
  908. else
  909. continue;
  910. }
  911. if ( targets.size > 0 )
  912. {
  913. self acquireTarget( targets );
  914. }
  915. else
  916. wait( 1 );
  917. }
  918. prof_end( "tankTargets" );
  919. }
  920.  
  921. acquireTarget( targets )
  922. {
  923. self endon( "death" );
  924.  
  925. if ( targets.size == 1 )
  926. self.bestTarget = targets[0];
  927. else
  928. self.bestTarget = self getBestTarget( targets );
  929.  
  930. self thread setEngagementSpeed(); // slows tank down to fire on target
  931.  
  932. // checks to abandon target
  933. //self thread lostTarget(); // sets lost LOS and time of lost target
  934. //self thread abandonTarget(); // if target is lost for 3+ seconds drops target and gets new one
  935. self thread watchTargetDeath( targets ); //abandons target when target killed
  936.  
  937.  
  938. self SetTurretTargetEnt( self.bestTarget ); // sets turret to target entity
  939. self fireOnTarget(); // fires on current target.
  940. self thread setNoTarget();
  941. }
  942.  
  943. setNoTarget()
  944. {
  945. self endon( "death" );
  946.  
  947. self setStandardSpeed();
  948. self removeTarget();
  949. self setTurretTargetEnt( self.neutralTarget );
  950. }
  951.  
  952. getBestTarget( targets )
  953. {
  954. self endon( "death" );
  955. mainGunPointOrigin = self getTagOrigin( "tag_flash" );
  956. tankOrigin = self.origin;
  957.  
  958. bestYaw = undefined;
  959. bestTarget = undefined;
  960. targetHasRocket = false;
  961.  
  962. foreach ( targ in targets )
  963. {
  964. angle = abs ( vectorToAngles ( ( targ.origin - self.origin ) )[1] );
  965. cannonAngle = abs( self getTagAngles( "tag_flash" )[1] );
  966. angle = abs ( angle - cannonAngle );
  967.  
  968. //vehicle priorities
  969. if ( isDefined( level.chopper ) && targ == level.chopper )
  970. return targ;
  971.  
  972. if ( isDefined( level.harrier ) && targ == level.harrier )
  973. return targ;
  974.  
  975. // in this calculation having a rocket removes 40d of rotation cost from best target calculation
  976. // to prioritize targeting dangerous targets.
  977. weaponsArray = targ GetWeaponsListItems();
  978. foreach ( weapon in weaponsArray )
  979. {
  980. if ( isSubStr( weapon, "at4" ) || isSubStr( weapon, "jav" ) || isSubStr( weapon, "c4" ) )
  981. angle -= 40;
  982. }
  983.  
  984. if ( !isDefined( bestYaw ) )
  985. {
  986. bestYaw = angle;
  987. bestTarget = targ;
  988. }
  989. else if ( bestYaw > angle )
  990. {
  991. bestYaw = angle;
  992. bestTarget = targ;
  993. }
  994. }
  995.  
  996. return ( bestTarget );
  997. }
  998.  
  999. watchTargetDeath( targets )
  1000. {
  1001. self endon( "abandonedTarget" );
  1002. self endon( "lostLOS" );
  1003. self endon( "death" );
  1004. self endon( "targetRemoved" );
  1005.  
  1006. bestTarg = self.bestTarget;
  1007. bestTarg endon ( "disconnect" );
  1008.  
  1009. bestTarg waittill( "death" );
  1010.  
  1011. self notify( "killedTarget" );
  1012. self removeTarget();
  1013. self setStandardSpeed();
  1014. self thread setNoTarget();
  1015. }
  1016.  
  1017. explicitAbandonTarget( noNewTarget, targ )
  1018. {
  1019. self endon( "death" );
  1020.  
  1021. self notify( "abandonedTarget" );
  1022. println("ABANDON TARGET EXPLICIT");
  1023. self setStandardSpeed();
  1024. self thread Setnotarget();
  1025. self removeTarget();
  1026.  
  1027. if ( isDefined(targ) )
  1028. {
  1029. self.badTarget = targ;
  1030. badTargetReset();
  1031. }
  1032.  
  1033. if ( isDefined(noNewTarget) && noNewTarget )
  1034. return;
  1035.  
  1036. return;
  1037. }
  1038.  
  1039. badTargetReset()
  1040. {
  1041. self endon("death");
  1042.  
  1043. wait (1.5);
  1044. self.badTarget = undefined;
  1045. }
  1046.  
  1047. removeTarget()
  1048. {
  1049. self notify( "targetRemoved" );
  1050.  
  1051. self.bestTarget = undefined;
  1052. self.lastLostTime = undefined;
  1053. }
  1054.  
  1055. isVehicleTarget( potentialTarget )
  1056. {
  1057. if ( distance2D( potentialTarget.origin, self.origin ) > 4096 )
  1058. return false;
  1059.  
  1060. if ( distance( potentialTarget.origin , self.origin ) < 512 )
  1061. return false;
  1062.  
  1063. return turretSightTrace( potentialTarget, false );
  1064. }
  1065.  
  1066. isTarget( potentialTarget )
  1067. {
  1068. self endon( "death" );
  1069.  
  1070. dist = distanceSquared( potentialTarget.origin, self.origin );
  1071.  
  1072. if ( !level.teamBased && isDefined( self.owner ) && potentialTarget == self.owner )
  1073. return false;
  1074.  
  1075. if ( !isalive( potentialTarget ) || potentialTarget.sessionstate != "playing" )
  1076. return false;
  1077.  
  1078. if ( dist > 4096*4096 )
  1079. return false;
  1080.  
  1081. if ( dist < 512*512 )
  1082. return false;
  1083.  
  1084. if ( !isdefined( potentialTarget.pers["team"] ) )
  1085. return false;
  1086.  
  1087. if ( potentialTarget == self.owner )
  1088. return false;
  1089.  
  1090. if ( level.teamBased && potentialTarget.pers["team"] == self.team )
  1091. return false;
  1092.  
  1093. if ( potentialTarget.pers["team"] == "spectator" )
  1094. return false;
  1095.  
  1096. if ( isdefined( potentialTarget.spawntime ) && ( gettime() - potentialTarget.spawntime )/1000 <= 5 )
  1097. return false;
  1098.  
  1099. if ( potentialTarget _hasPerk( "specialty_coldblooded" ) )
  1100. return false;
  1101.  
  1102. return self Vehicle_CanTurretTargetPoint( potentialTarget.origin, 1, self );
  1103.  
  1104. //return self turretSightTrace( potentialTarget, false );
  1105. }
  1106.  
  1107. turretSightTrace( targ, debug )
  1108. {
  1109. turretCanSeeTarget = targ sightConeTrace( self getTagOrigin( "tag_turret" ), self );
  1110.  
  1111. if ( turretCanSeeTarget < .7 )
  1112. {
  1113. return false;
  1114. }
  1115.  
  1116. if ( isDefined(debug) && debug )
  1117. self thread drawLine( targ.origin, self getTagOrigin( "tag_turret" ), 10, (1,0,0) );
  1118.  
  1119. return true;
  1120. }
  1121.  
  1122. //=================================================================
  1123. //
  1124. // Secondary Weapon Targeting Functions
  1125. //
  1126. //=================================================================
  1127.  
  1128. isMiniTarget( potentialTarget )
  1129. {
  1130. self endon( "death" );
  1131.  
  1132. if ( !isalive( potentialTarget ) || potentialTarget.sessionstate != "playing" )
  1133. return false;
  1134.  
  1135. if ( !isdefined( potentialTarget.pers["team"] ) )
  1136. return false;
  1137.  
  1138. if ( potentialTarget == self.owner )
  1139. return false;
  1140.  
  1141. if ( distanceSquared( potentialTarget.origin , self.origin ) > 1024*1024 )
  1142. return false;
  1143.  
  1144. if ( level.teamBased && potentialTarget.pers["team"] == self.team )
  1145. return false;
  1146.  
  1147. if ( potentialTarget.pers["team"] == "spectator" )
  1148. return false;
  1149.  
  1150. if ( isdefined( potentialTarget.spawntime ) && ( gettime() - potentialTarget.spawntime )/1000 <= 5 )
  1151. return false;
  1152.  
  1153.  
  1154. if ( isDefined( self ) )
  1155. {
  1156. minTurretEye = self.mgTurret.origin + ( 0, 0, 64 );
  1157. minTurretCanSeeTarget = potentialTarget sightConeTrace( minTurretEye, self );
  1158.  
  1159. if ( minTurretCanSeeTarget < 1 )
  1160. return false;
  1161. }
  1162.  
  1163. return true;
  1164. }
  1165.  
  1166. tankGetMiniTargets()
  1167. {
  1168. self endon( "death" );
  1169. self endon( "leaving" );
  1170. miniTargets = [];
  1171. println( "Geting Mini Targets" );
  1172.  
  1173. for ( ;; )
  1174. {
  1175. miniTargets = [];
  1176. players = level.players;
  1177.  
  1178. for (i = 0; i <= players.size; i++)
  1179. {
  1180. if ( isMiniTarget( players[i] ) )
  1181. {
  1182. if( isdefined( players[i] ) )
  1183. miniTargets[miniTargets.size] = players[i];
  1184. }
  1185. else
  1186. continue;
  1187.  
  1188. wait( .05 );
  1189. }
  1190. if ( miniTargets.size > 0 )
  1191. {
  1192. self acquireMiniTarget( miniTargets );
  1193. return;
  1194. }
  1195. else
  1196. wait( .5 );
  1197. }
  1198. }
  1199.  
  1200. getBestMiniTarget( targets )
  1201. {
  1202. self endon( "death" );
  1203. tankOrigin = self.origin;
  1204.  
  1205. closest = undefined;
  1206. bestTarget = undefined;
  1207.  
  1208. foreach ( targ in targets )
  1209. {
  1210. curDist = Distance( self.origin, targ.origin );
  1211.  
  1212. // in this calculation having a rocket javelin or c4 increases mini turret priority
  1213. // to prioritize targeting dangerous targets.
  1214. curWeaon = targ GetCurrentWeapon();
  1215. if ( isSubStr( curWeaon, "at4" ) || isSubStr( curWeaon, "jav" ) || isSubStr( curWeaon, "c4" ) || isSubStr( curWeaon, "smart" ) || isSubStr( curWeaon, "grenade" ) )
  1216. curDist -= 200;
  1217.  
  1218. if ( !isDefined( closest ) )
  1219. {
  1220. closest = curDist;
  1221. bestTarget = targ;
  1222. }
  1223. else if ( closest > curDist )
  1224. {
  1225. closest = curDist;
  1226. bestTarget = targ;
  1227. }
  1228. }
  1229. return ( bestTarget );
  1230. }
  1231.  
  1232. acquireMiniTarget( targets )
  1233. {
  1234. self endon( "death" );
  1235.  
  1236. if ( targets.size == 1 )
  1237. self.bestMiniTarget = targets[0];
  1238. else
  1239. self.bestMiniTarget = self getBestMiniTarget( targets );
  1240.  
  1241. if ( distance2D( self.origin, self.bestMiniTarget.origin) > 768 )
  1242. self thread setMiniEngagementSpeed();
  1243.  
  1244. self notify( "acquiringMiniTarget" );
  1245. self.mgTurret SetTargetEntity( self.bestMiniTarget, ( 0,0,64 ) ); // sets turret to target entity
  1246. wait( .15 );
  1247. self thread fireMiniOnTarget(); // fires on current target.
  1248. self thread watchMiniTargetDeath( targets ); //abandons target when target killed
  1249. self thread watchMiniTargetDistance( targets );
  1250. self thread watchMiniTargetThreat( self.bestMiniTarget );
  1251. }
  1252.  
  1253. fireMiniOnTarget()
  1254. {
  1255. self endon( "death" );
  1256. self endon( "abandonedMiniTarget" );
  1257. self endon( "killedMiniTarget" );
  1258. noTargTime = undefined;
  1259. miniAcquiredTime = getTime();
  1260.  
  1261. if ( !isDefined( self.bestMiniTarget ) )
  1262. {
  1263. println("No Targ to fire on");
  1264. return;
  1265. }
  1266.  
  1267. println("firing on best target");
  1268.  
  1269. while( 1 )
  1270. {
  1271. if ( !isDefined ( self.mgTurret getTurretTarget( true ) ) )
  1272. {
  1273. if ( !isDefined( noTargTime ) )
  1274. noTargTime = getTime();
  1275.  
  1276. curTime = getTime();
  1277.  
  1278. if ( noTargTime - curTime > 1 )
  1279. {
  1280. noTargTime = undefined;
  1281. self thread explicitAbandonMiniTarget();
  1282. return;
  1283. }
  1284.  
  1285. //println("Waiting because the turret doesnt have a target" );
  1286.  
  1287. wait ( .5 );
  1288. continue;
  1289. }
  1290.  
  1291. if ( getTime() > miniAcquiredTime + 1000 && !isDefined( self.bestTarget ) )
  1292. {
  1293. if ( distance2D(self.origin, self.bestMiniTarget.origin ) > 768 )
  1294. {
  1295. targets[0] = self.bestMiniTarget;
  1296. self acquireTarget( targets );
  1297. }
  1298. }
  1299.  
  1300. numShots = randomIntRange( 10, 16 );
  1301. for ( i = 0; i < numShots; i++ )
  1302. {
  1303. self.mgTurret ShootTurret();
  1304. wait ( .1 );
  1305. }
  1306. wait ( randomFloatRange( 0.5, 3.0 ) );
  1307. }
  1308. }
  1309.  
  1310. watchMiniTargetDeath( targets )
  1311. {
  1312. self endon( "abandonedMiniTarget" );
  1313. self endon( "death" );
  1314. if ( ! isDefined( self.bestMiniTarget ) )
  1315. return;
  1316.  
  1317. self.bestMiniTarget waittill( "death" );
  1318.  
  1319. self notify( "killedMiniTarget" );
  1320. println( "Killed Mini Target" );
  1321.  
  1322. self.bestMiniTarget = undefined;
  1323. self.mgTurret ClearTargetEntity();
  1324. self tankGetMiniTargets();
  1325. }
  1326.  
  1327. watchMiniTargetDistance( targets )
  1328. {
  1329. self endon( "abandonedMiniTarget" );
  1330. self endon( "death" );
  1331.  
  1332. for ( ;; )
  1333. {
  1334. if (! isDefined( self.bestMiniTarget ) )
  1335. return;
  1336.  
  1337. trace = BulletTrace( self.mgTurret.origin, self.bestMiniTarget.origin, false, self );
  1338. traceDistance = Distance(self.origin, trace["position"] );
  1339.  
  1340. if ( traceDistance > 1024 )
  1341. {
  1342. println( "MINI TARGET DIST TOO FAR!!!" );
  1343. self thread explicitAbandonMiniTarget();
  1344. return;
  1345. }
  1346. println( traceDistance );
  1347. wait ( 2 );
  1348. }
  1349. }
  1350.  
  1351. watchMiniTargetThreat( curTarget )
  1352. {
  1353. self endon( "abandonedMiniTarget" );
  1354. self endon( "death" );
  1355. self endon( "killedMiniTarget" );
  1356.  
  1357. for ( ;; )
  1358. {
  1359. miniTargets = [];
  1360. players = level.players;
  1361.  
  1362. for (i = 0; i <= players.size; i++)
  1363. {
  1364. if ( isMiniTarget( players[i] ) )
  1365. {
  1366. if( !isdefined( players[i] ) )
  1367. continue;
  1368.  
  1369. if( !isdefined(curTarget) )
  1370. return;
  1371.  
  1372. traceOldTarg = Distance(self.origin, CurTarget.origin );
  1373. traceNewTarg = Distance(self.origin, players[i].origin );
  1374.  
  1375. if ( traceNewTarg < traceOldTarg )
  1376. {
  1377. self thread explicitAbandonMiniTarget();
  1378. return;
  1379. }
  1380. }
  1381.  
  1382. wait( .05 );
  1383. }
  1384.  
  1385. wait( .25 );
  1386. }
  1387. }
  1388.  
  1389. explicitAbandonMiniTarget( noNewTarget )
  1390. {
  1391.  
  1392. self notify( "abandonedMiniTarget" );
  1393.  
  1394. println( "ABANDONED MINI TARGET" );
  1395.  
  1396. self.bestMiniTarget = undefined;
  1397. self.mgTurret ClearTargetEntity();
  1398.  
  1399. if ( isDefined(noNewTarget) && noNewTarget )
  1400. return;
  1401.  
  1402. self thread tankGetMiniTargets();
  1403. return;
  1404. }
  1405.  
  1406.  
  1407. addToTankList()
  1408. {
  1409. level.tanks[self getEntityNumber()] = self;
  1410. }
  1411.  
  1412. removeFromTankList()
  1413. {
  1414. level.tanks[self getEntityNumber()] = undefined;
  1415. }
  1416.  
  1417.  
  1418. /*************************************************************************
  1419. *
  1420. * PATHFINDING AND PATH NODE FUNCTIONS
  1421. *
  1422. ***************************************************************************/
  1423.  
  1424. getNodeNearEnemies()
  1425. {
  1426. validEnemies = [];
  1427.  
  1428. foreach ( player in level.players )
  1429. {
  1430. if ( player.team == "spectator" )
  1431. continue;
  1432.  
  1433. if ( player.team == self.team )
  1434. continue;
  1435.  
  1436. if ( !isAlive( player ) )
  1437. continue;
  1438.  
  1439. player.dist = 0;
  1440. validEnemies[validEnemies.size] = player;
  1441. }
  1442.  
  1443. if ( !validEnemies.size )
  1444. return undefined;
  1445.  
  1446. for ( i = 0; i < validEnemies.size; i++ )
  1447. {
  1448. for ( j = i + 1; j < validEnemies.size; j++ )
  1449. {
  1450. dist = distanceSquared( validEnemies[i].origin, validEnemies[j].origin );
  1451.  
  1452. validEnemies[i].dist += dist;
  1453. validEnemies[j].dist += dist;
  1454. }
  1455. }
  1456.  
  1457. bestPlayer = validEnemies[0];
  1458. foreach ( player in validEnemies )
  1459. {
  1460. if ( player.dist < bestPlayer.dist )
  1461. bestPlayer = player;
  1462. }
  1463.  
  1464. bestOrigin = bestPlayer.origin;
  1465.  
  1466. sortedNodes = sortByDistance( level.graphNodes, bestOrigin );
  1467.  
  1468. //thread drawLine( bestOrigin, sortedNodes[0].origin, 10.0, (1,0,1) );
  1469.  
  1470. return ( sortedNodes[0] );
  1471. }
  1472.  
  1473.  
  1474. setupPaths()
  1475. {
  1476. tankNodes = [];
  1477. startNodes = [];
  1478. endNodes = [];
  1479. aStarGraphNodes = [];
  1480.  
  1481. // setup the start node
  1482. tankNode = GetVehicleNode( "startnode", "targetname" );
  1483. tankNodes[tankNodes.size] = tankNode;
  1484. startNodes[startNodes.size] = tankNode;
  1485.  
  1486. while ( isDefined( tankNode.target ) )
  1487. {
  1488. lastNode = tankNode;
  1489. tankNode = GetVehicleNode( tankNode.target, "targetname" );
  1490. tankNode.prev = lastNode;
  1491.  
  1492. // case for connected path
  1493. if ( tankNode == tankNodes[0] )
  1494. break;
  1495.  
  1496. tankNodes[tankNodes.size] = tankNode;
  1497.  
  1498. // case for disconnected path
  1499. if ( !isDefined( tankNode.target ) )
  1500. return;
  1501. }
  1502.  
  1503. tankNodes[0].branchNodes = [];
  1504. tankNodes[0] thread handleBranchNode( "forward" );
  1505. aStarGraphNodes[aStarGraphNodes.size] = tankNodes[0];
  1506.  
  1507. // find the start and end nodes of the branches
  1508. branchNodes = GetVehicleNodeArray( "branchnode", "targetname" );
  1509. foreach ( branchNode in branchNodes )
  1510. {
  1511. tankNode = branchNode;
  1512. tankNodes[tankNodes.size] = tankNode;
  1513. startNodes[startNodes.size] = tankNode;
  1514.  
  1515. while ( isDefined( tankNode.target ) )
  1516. {
  1517. lastNode = tankNode;
  1518. tankNode = GetVehicleNode( tankNode.target, "targetname" );
  1519. tankNodes[tankNodes.size] = tankNode;
  1520. tankNode.prev = lastNode;
  1521.  
  1522. if ( !isDefined( tankNode.target ) )
  1523. endNodes[endNodes.size] = tankNode;
  1524. }
  1525. }
  1526.  
  1527. // detect and initialize the branch nodes. These will be used for the aStar node graph
  1528. foreach ( tankNode in tankNodes )
  1529. {
  1530. isBranchNode = false;
  1531. foreach ( startNode in startNodes )
  1532. {
  1533. if ( startNode == tankNode )
  1534. continue;
  1535.  
  1536. if ( startNode.target == tankNode.targetname )
  1537. continue;
  1538.  
  1539. if ( isDefined( tankNode.target ) && tankNode.target == startNode.targetname )
  1540. continue;
  1541.  
  1542. if ( distance2d( tankNode.origin, startNode.origin ) > 80 )
  1543. continue;
  1544.  
  1545. startNode thread handleCapNode( tankNode, "reverse" );
  1546. startNode.prev = tankNode;
  1547.  
  1548. if ( !isDefined( tankNode.branchNodes ) )
  1549. tankNode.branchNodes = [];
  1550.  
  1551. tankNode.branchNodes[tankNode.branchNodes.size] = startNode;
  1552.  
  1553. isBranchNode = true;
  1554. }
  1555.  
  1556. if ( isBranchNode )
  1557. tankNode thread handleBranchNode( "forward" );
  1558.  
  1559. isJoinNode = false;
  1560. foreach ( endNode in endNodes)
  1561. {
  1562. if ( endNode == tankNode )
  1563. continue;
  1564.  
  1565. if ( !isDefined( tankNode.target ) )
  1566. continue;
  1567.  
  1568. if ( tankNode.target == endNode.targetname )
  1569. continue;
  1570.  
  1571. if ( isDefined( endNode.target ) && endNode.target == tankNode.targetname )
  1572. continue;
  1573.  
  1574. if ( distance2d( tankNode.origin, endNode.origin ) > 80 )
  1575. continue;
  1576.  
  1577. endNode thread handleCapNode( tankNode, "forward" );
  1578. endNode.next = getVehicleNode( tankNode.targetname, "targetname" );
  1579. //endNode.target = tankNode.targetname; // READ-ONLY field...
  1580. endNode.length = distance( endNode.origin, tankNode.origin );
  1581.  
  1582. if ( !isDefined( tankNode.branchNodes ) )
  1583. tankNode.branchNodes = [];
  1584.  
  1585. tankNode.branchNodes[tankNode.branchNodes.size] = endNode;
  1586.  
  1587. isJoinNode = true;
  1588. }
  1589.  
  1590. if ( isJoinNode )
  1591. {
  1592. assert( !isBranchNode );
  1593. tankNode thread handleBranchNode( "reverse" );
  1594. }
  1595.  
  1596. if ( isJoinNode || isBranchNode )
  1597. aStarGraphNodes[aStarGraphNodes.size] = tankNode;
  1598. }
  1599.  
  1600. if ( aStarGraphNodes.size < 3 )
  1601. {
  1602. level notify ( "end_tankPathHandling" );
  1603. return;
  1604. }
  1605.  
  1606. // subdivide the path a bit...
  1607. segmentNodes = [];
  1608. foreach( tankNode in tankNodes )
  1609. {
  1610. if ( !isDefined( tankNode.branchNodes ) )
  1611. continue;
  1612.  
  1613. segmentNodes[segmentNodes.size] = tankNode;
  1614. }
  1615.  
  1616. foreach ( segmentNode in segmentNodes )
  1617. {
  1618. tankNode = segmentNode;
  1619. pathLength = 0;
  1620.  
  1621. while ( isDefined( tankNode.target ) )
  1622. {
  1623. prevNode = tankNode;
  1624. tankNode = GetVehicleNode( tankNode.target, "targetname" );
  1625. pathLength += distance( tankNode.origin, prevNode.origin );
  1626.  
  1627. if ( tankNode == segmentNode )
  1628. break;
  1629.  
  1630. if ( isDefined( tankNode.branchNodes ) )
  1631. break;
  1632. }
  1633.  
  1634. if ( pathLength > 1000 )
  1635. {
  1636. tankNode = segmentNode;
  1637. curLength = 0;
  1638.  
  1639. while ( isDefined( tankNode.target ) )
  1640. {
  1641. prevNode = tankNode;
  1642. tankNode = GetVehicleNode( tankNode.target, "targetname" );
  1643.  
  1644. curLength += distance( tankNode.origin, prevNode.origin );
  1645. if ( curLength < pathLength / 2 )
  1646. continue;
  1647.  
  1648. tankNode.branchNodes = []; // necessary?
  1649. tankNode thread handleBranchNode( "forward" );
  1650. aStarGraphNodes[aStarGraphNodes.size] = tankNode;
  1651. break;
  1652. }
  1653. }
  1654. }
  1655.  
  1656. level.graphNodes = initNodeGraph( aStarGraphNodes );
  1657.  
  1658. foreach ( tankNode in tankNodes )
  1659. {
  1660. if ( !isDefined( tankNode.graphId ) )
  1661. tankNode thread nodeTracker();
  1662. }
  1663. }
  1664.  
  1665.  
  1666.  
  1667. getRandomBranchNode( direction )
  1668. {
  1669. branchNodes = [];
  1670. foreach ( graphId, linkNode in self.links )
  1671. {
  1672. // pick a branch in the direction we're already heading
  1673. if ( self.linkDirs[graphId] != direction )
  1674. continue;
  1675.  
  1676. branchNodes[branchNodes.size] = linkNode;
  1677. }
  1678.  
  1679. return ( branchNodes[randomInt( branchNodes.size )] );
  1680. }
  1681.  
  1682.  
  1683. getNextNodeForEndNode( endNode, direction )
  1684. {
  1685. graphNode = level.graphNodes[self.graphId];
  1686.  
  1687. continuePath = generatePath( graphNode, endNode, undefined, direction );
  1688. continueG = continuePath[0].g;
  1689.  
  1690. changePath = generatePath( graphNode, endNode, undefined, level.otherDir[direction] );
  1691. changeG = changePath[0].g;
  1692.  
  1693. // temporarily force the tank to only go forward
  1694. if ( !getDvarInt( "tankDebug" ) )
  1695. changeG = 9999999;
  1696.  
  1697. if ( continueG <= changeG )
  1698. return ( continuePath[1] );
  1699. }
  1700.  
  1701.  
  1702. handleBranchNode( direction )
  1703. {
  1704. level endon ( "end_tankPathHandling" );
  1705. for ( ;; )
  1706. {
  1707. self waittill( "trigger", tank, wasForced );
  1708.  
  1709. graphNode = level.graphNodes[self.graphId];
  1710.  
  1711. tank.node = self;
  1712.  
  1713. nextGraphNode = undefined;
  1714. if ( isDefined( tank.endNode ) && tank.endNode != graphNode )
  1715. {
  1716. nextGraphNode = getNextNodeForEndNode( tank.endNode, tank.veh_pathdir );
  1717.  
  1718. if ( !isDefined( nextGraphNode ) )
  1719. tank thread setDirection( level.otherDir[tank.veh_pathdir] );
  1720. }
  1721.  
  1722. if ( !isDefined( nextGraphNode ) || nextGraphNode == graphNode )
  1723. {
  1724. nextGraphNode = graphNode getRandomBranchNode( tank.veh_pathdir );
  1725. }
  1726.  
  1727. goalNode = graphNode.linkStartNodes[nextGraphNode.graphId];
  1728.  
  1729. if ( tank.veh_pathdir == "forward" )
  1730. nextLinkNode = self getNextNode();
  1731. else
  1732. nextLinkNode = self getPrevNode();
  1733.  
  1734. // if we're already on this path, just keep going
  1735. if ( nextLinkNode != goalNode )
  1736. tank startPath( goalNode );
  1737. }
  1738. }
  1739.  
  1740.  
  1741. handleCapNode( joinNode, direction )
  1742. {
  1743. for ( ;; )
  1744. {
  1745. self waittill( "trigger", tank );
  1746.  
  1747. if ( tank.veh_pathdir != direction )
  1748. continue;
  1749.  
  1750. debugPrintLn2( "tank starting path at join node: " + joinNode.graphId );
  1751.  
  1752. tank startPath( joinNode );
  1753. }
  1754. }
  1755.  
  1756.  
  1757. nodeTracker()
  1758. {
  1759. self.forwardGraphId = getForwardGraphNode().graphId;
  1760. self.reverseGraphId = getReverseGraphNode().graphId;
  1761.  
  1762. for ( ;; )
  1763. {
  1764. self waittill ( "trigger", tank, wasForced );
  1765.  
  1766. tank.node = self;
  1767.  
  1768. /#
  1769. if ( getDvarInt( "tankForceTrigger" ) )
  1770. {
  1771. if ( tank.veh_pathdir == "forward" )
  1772. tank thread forceTrigger( self, self getNextNode(), tank );
  1773. else
  1774. tank thread forceTrigger( self, self getPrevNode(), tank );
  1775. }
  1776. #/
  1777.  
  1778. tank.forwardGraphId = self.forwardGraphId;
  1779. tank.reverseGraphId = self.reverseGraphId;
  1780.  
  1781. if ( !isDefined( self.target ) || self.targetname == "branchnode" )
  1782. nodeType = "TRANS";
  1783. else
  1784. nodeType = "NODE";
  1785.  
  1786. if ( isDefined( wasForced ) )
  1787. debugPrint3D( self.origin, nodeType, (1,0.5,0), 1, 2, 100 );
  1788. else
  1789. debugPrint3D( self.origin, nodeType, (0,1,0), 1, 2, 100 );
  1790. }
  1791. }
  1792.  
  1793.  
  1794. forceTrigger( prevNode, nextNode, tank )
  1795. {
  1796. nextNode endon ( "trigger" );
  1797. prevNode endon ( "trigger" );
  1798. tank endon ( "death" );
  1799.  
  1800. minDist = distanceSquared( tank.origin, nextNode.origin );
  1801. tankDir = tank.veh_pathdir;
  1802.  
  1803. debugPrint3D( prevNode.origin+(0,0,30), "LAST", (0,0,1), 0.5, 1, 100 );
  1804. debugPrint3D( nextNode.origin+(0,0,60), "NEXT", (0,1,0), 0.5, 1, 100 );
  1805.  
  1806. timeOutNextFrame = false;
  1807. for ( ;; )
  1808. {
  1809. wait ( 0.05 );
  1810.  
  1811. // tank changed direction
  1812. if ( tankDir != tank.veh_pathdir )
  1813. {
  1814. debugPrintLn2( "tank missed node: reversing direction" );
  1815. tank thread forceTrigger( nextNode, prevNode, tank );
  1816. return;
  1817. }
  1818.  
  1819. if ( timeOutNextFrame )
  1820. {
  1821. debugPrintLn2( "... sending notify." );
  1822. nextNode notify ( "trigger", tank, true );
  1823. return;
  1824. }
  1825.  
  1826. curDist = distanceSquared( tank.origin, nextNode.origin );
  1827.  
  1828. if ( curDist > minDist )
  1829. {
  1830. timeOutNextFrame = true;
  1831. debugPrintLn2( "tank missed node: forcing notify in one frame..." );
  1832. }
  1833.  
  1834. minDist = curDist;
  1835. }
  1836. }
  1837.  
  1838.  
  1839. getForwardGraphNode()
  1840. {
  1841. assert( !isDefined( self.graphId ) );
  1842.  
  1843. checkNode = self;
  1844. while ( !isDefined( checkNode.graphId ) )
  1845. checkNode = checkNode getNextNode();
  1846.  
  1847. return checkNode;
  1848. }
  1849.  
  1850.  
  1851. getReverseGraphNode()
  1852. {
  1853. assert( !isDefined( self.graphId ) );
  1854.  
  1855. checkNode = self;
  1856. while ( !isDefined( checkNode.graphId ) )
  1857. checkNode = checkNode getPrevNode();
  1858.  
  1859. return checkNode;
  1860. }
  1861.  
  1862.  
  1863. getNextNode()
  1864. {
  1865. if ( isDefined( self.target ) )
  1866. return ( GetVehicleNode( self.target, "targetname" ) );
  1867. else
  1868. return ( self.next );
  1869. }
  1870.  
  1871.  
  1872. getPrevNode()
  1873. {
  1874. return self.prev;
  1875. }
  1876.  
  1877.  
  1878.  
  1879. // Builds the aStar node graph
  1880. initNodeGraph( astarBaseNodes )
  1881. {
  1882. graphNodes = [];
  1883. foreach ( pathNode in aStarBaseNodes )
  1884. {
  1885. graphNode = spawnStruct();
  1886. graphNode.linkInfos = [];
  1887. graphNode.links = [];
  1888. graphNode.linkLengths = [];
  1889. graphNode.linkDirs = [];
  1890. graphNode.linkStartNodes = [];
  1891. graphNode.node = pathNode;
  1892. graphNode.origin = pathNode.origin;
  1893. graphNode.graphId = graphNodes.size;
  1894. pathNode.graphId = graphNodes.size;
  1895.  
  1896. debugPrint3D( graphNode.origin + (0,0,80), graphNode.graphId, (1,1,1), 0.65, 2, 100000 );
  1897.  
  1898. graphNodes[graphNodes.size] = graphNode;
  1899. }
  1900.  
  1901. foreach ( pathNode in aStarBaseNodes )
  1902. {
  1903. graphId = pathNode.graphId;
  1904.  
  1905. checkNode = GetVehicleNode( pathNode.target, "targetname" );
  1906. linkLength = distance( pathNode.origin, checkNode.origin );
  1907. linkStartNode = checkNode;
  1908.  
  1909. while ( !isDefined( checkNode.graphId ) )
  1910. {
  1911. linkLength += distance( checkNode.origin, checkNode.prev.origin );
  1912.  
  1913. if ( isDefined( checkNode.target ) )
  1914. checkNode = GetVehicleNode( checkNode.target, "targetname" );
  1915. else
  1916. checkNode = checkNode.next;
  1917. }
  1918.  
  1919. assert( checkNode != pathNode );
  1920. graphNodes[graphId] addLinkNode( graphNodes[checkNode.graphId], linkLength, "forward", linkStartNode );
  1921.  
  1922. checkNode = pathNode.prev;
  1923. linkLength = distance( pathNode.origin, checkNode.origin );
  1924. linkStartNode = checkNode;
  1925.  
  1926. while ( !isDefined( checkNode.graphId ) )
  1927. {
  1928. linkLength += distance( checkNode.origin, checkNode.prev.origin );
  1929. checkNode = checkNode.prev;
  1930. }
  1931.  
  1932. assert( checkNode != pathNode );
  1933. graphNodes[graphId] addLinkNode( graphNodes[checkNode.graphId], linkLength, "reverse", linkStartNode );
  1934.  
  1935. foreach ( branchNode in pathNode.branchNodes )
  1936. {
  1937. checkNode = branchNode;
  1938. linkLength = distance( pathNode.origin, checkNode.origin );
  1939. linkStartNode = checkNode;
  1940.  
  1941. if ( checkNode.targetname == "branchnode" )
  1942. {
  1943. while ( !isDefined( checkNode.graphId ) )
  1944. {
  1945. if ( isDefined( checkNode.target ) )
  1946. nextNode = GetVehicleNode( checkNode.target, "targetname" );
  1947. else
  1948. nextNode = checkNode.next;
  1949.  
  1950. linkLength += distance( checkNode.origin, nextNode.origin );
  1951. checkNode = nextNode;
  1952. }
  1953.  
  1954. assert( checkNode != pathNode );
  1955. graphNodes[graphId] addLinkNode( graphNodes[checkNode.graphId], linkLength, "forward", linkStartNode );
  1956. }
  1957. else
  1958. {
  1959. while ( !isDefined( checkNode.graphId ) )
  1960. {
  1961. linkLength += distance( checkNode.origin, checkNode.prev.origin );
  1962. checkNode = checkNode.prev;
  1963. }
  1964.  
  1965. assert( checkNode != pathNode );
  1966. graphNodes[graphId] addLinkNode( graphNodes[checkNode.graphId], linkLength, "reverse", linkStartNode );
  1967. }
  1968. }
  1969. }
  1970.  
  1971. return graphNodes;
  1972. }
  1973.  
  1974.  
  1975. addLinkNode( graphNode, linkLength, linkDir, linkStartNode )
  1976. {
  1977. assert( self.graphId != graphNode.graphId );
  1978. assert( !isDefined( self.links[graphNode.graphId] ) );
  1979.  
  1980. self.links[graphNode.graphId] = graphNode;
  1981. self.linkLengths[graphNode.graphId] = linkLength;
  1982. self.linkDirs[graphNode.graphId] = linkDir;
  1983. self.linkStartNodes[graphNode.graphId] = linkStartNode;
  1984.  
  1985. linkInfo = spawnStruct();
  1986. linkInfo.toGraphNode = graphNode;
  1987. linkInfo.toGraphId = graphNode.graphId;
  1988. linkInfo.length = linkLength;
  1989. linkInfo.direction = linkDir;
  1990. linkInfo.startNode = linkStartNode;
  1991.  
  1992. self.linkInfos[graphNode.graphId] = linkInfo;
  1993. }
  1994.  
  1995.  
  1996. // call function as generatePath(startNode, destNode), otherwise paths will be reversed
  1997. generatePath( destNode, startNode, blockedNodes, direction )
  1998. {
  1999. level.openList = [];
  2000. level.closedList = [];
  2001. foundPath = false;
  2002. pathNodes = [];
  2003.  
  2004. if ( !isDefined( blockedNodes ) )
  2005. blockedNodes = [];
  2006.  
  2007. startNode.g = 0;
  2008. startNode.h = getHValue( startNode, destNode );
  2009. startNode.f = startNode.g + startNode.h;
  2010.  
  2011. addToClosedList( startNode );
  2012.  
  2013. curNode = startNode;
  2014. for ( ;; )
  2015. {
  2016. foreach ( linkId, checkNode in curNode.links )
  2017. {
  2018. if ( is_in_array( blockedNodes, checkNode ) )
  2019. continue;
  2020.  
  2021. if ( is_in_array( level.closedList, checkNode ) )
  2022. continue;
  2023.  
  2024. if ( isDefined( direction ) && checkNode.linkDirs[curNode.graphId] != direction )
  2025. continue;
  2026.  
  2027. if ( !is_in_array( level.openList, checkNode ) )
  2028. {
  2029. addToOpenList( checkNode );
  2030.  
  2031. checkNode.parentNode = curNode;
  2032. checkNode.g = getGValue( checkNode, curNode );
  2033. checkNode.h = getHValue( checkNode, destNode );
  2034. checkNode.f = checkNode.g + checkNode.h;
  2035.  
  2036. if ( checkNode == destNode )
  2037. foundPath = true;
  2038. }
  2039. else
  2040. {
  2041. if ( checkNode.g < getGValue( curNode, checkNode ) )
  2042. continue;
  2043.  
  2044. checkNode.parentNode = curNode;
  2045. checkNode.g = getGValue( checkNode, curNode );
  2046. checkNode.f = checkNode.g + checkNode.h;
  2047. }
  2048. }
  2049.  
  2050. if ( foundPath )
  2051. break;
  2052.  
  2053. addToClosedList( curNode );
  2054.  
  2055. bestNode = level.openList[0];
  2056.  
  2057. foreach ( testNode in level.openList )
  2058. {
  2059. if ( testNode.f > bestNode.f )
  2060. continue;
  2061.  
  2062. bestNode = testNode;
  2063. }
  2064.  
  2065. assert( isDefined( bestNode ) ); // the tank should always have a path
  2066.  
  2067. addToClosedList( bestNode );
  2068. curNode = bestNode;
  2069. }
  2070.  
  2071. assert( isDefined( destNode.parentNode ) );
  2072.  
  2073. curNode = destNode;
  2074. while (curNode != startNode)
  2075. {
  2076. pathNodes[pathNodes.size] = curNode;
  2077. curNode = curNode.parentNode;
  2078. }
  2079. pathNodes[pathNodes.size] = curNode;
  2080.  
  2081. return pathNodes;
  2082. }
  2083.  
  2084.  
  2085. addToOpenList( node )
  2086. {
  2087. node.openListID = level.openList.size;
  2088. level.openList[level.openList.size] = node;
  2089. node.closedListID = undefined;
  2090. }
  2091.  
  2092.  
  2093. addToClosedList( node )
  2094. {
  2095. if (isdefined (node.closedListID))
  2096. return;
  2097.  
  2098. node.closedListID = level.closedList.size;
  2099. level.closedList[level.closedList.size] = node;
  2100.  
  2101. if (!is_in_array (level.openList, node))
  2102. return;
  2103.  
  2104. level.openList[node.openListID] = level.openList[level.openList.size - 1];
  2105. level.openList[node.openListID].openListID = node.openListID;
  2106. level.openList[level.openList.size - 1] = undefined;
  2107. node.openListID = undefined;
  2108. }
  2109.  
  2110.  
  2111. getHValue (node1, node2)
  2112. {
  2113. return (distance (node1.node.origin, node2.node.origin));
  2114. }
  2115.  
  2116.  
  2117. getGValue(node1, node2)
  2118. {
  2119. return ( node1.parentNode.g + node1.linkLengths[node2.graphId] );
  2120. }
  2121.  
  2122.  
  2123. is_in_array( aeCollection, eFindee )
  2124. {
  2125. for ( i = 0; i < aeCollection.size; i++ )
  2126. {
  2127. if ( aeCollection[ i ] == eFindee )
  2128. return( true );
  2129. }
  2130.  
  2131. return( false );
  2132. }
  2133.  
  2134.  
  2135. drawPath( pathNodes )
  2136. {
  2137. for ( i = 1; i < pathNodes.size; i++ )
  2138. {
  2139. startNode = pathNodes[i-1];
  2140. endNode = pathNodes[i];
  2141.  
  2142. if ( startNode.linkDirs[endNode.graphId] == "reverse" )
  2143. level thread drawLink( startNode.node.origin, endNode.node.origin, (1,0,0) );
  2144. else
  2145. level thread drawLink( startNode.node.origin, endNode.node.origin, (0,1,0) );
  2146.  
  2147. vehNode = startNode.linkStartNodes[endNode.graphId];
  2148. level thread drawLink( startNode.node.origin + (0,0,4), vehNode.origin + (0,0,4), (0,0,1) );
  2149.  
  2150. if ( startNode.linkDirs[endNode.graphId] == "reverse" )
  2151. {
  2152. while ( !isDefined( vehNode.graphId ) )
  2153. {
  2154. lastVehNode = vehNode;
  2155. vehNode = vehNode.prev;
  2156. level thread drawLink( lastVehNode.origin + (0,0,4), vehNode.origin + (0,0,4), (0,1,1) );
  2157. }
  2158. }
  2159. else
  2160. {
  2161. while ( !isDefined( vehNode.graphId ) )
  2162. {
  2163. lastVehNode = vehNode;
  2164.  
  2165. if ( isDefined( vehNode.target ) )
  2166. vehNode = GetVehicleNode( vehNode.target, "targetname" );
  2167. else
  2168. vehNode = vehNode.next;
  2169.  
  2170. level thread drawLink( lastVehNode.origin + (0,0,4), vehNode.origin + (0,0,4), (0,1,1) );
  2171. }
  2172. }
  2173. }
  2174. }
  2175.  
  2176.  
  2177. drawGraph( pathNodes )
  2178. {
  2179. /*
  2180. level.pathZOffset = 0;
  2181. foreach ( node in pathNodes )
  2182. {
  2183. println( node.links.size );
  2184. foreach ( linkId, graphNode in node.links )
  2185. {
  2186. if ( node.linkDirs[linkId] == "reverse" )
  2187. level thread drawLink( node.node.origin, graphNode.node.origin, (0,1,0) );
  2188. else
  2189. level thread drawLink( node.node.origin, graphNode.node.origin, (1,0,0) );
  2190.  
  2191. //if ( node.linkDirs[linkId] == "reverse" )
  2192. // continue;
  2193.  
  2194. //level thread drawLink( pathNodes[graphId].node.origin, pathNodes[node.graphId].node.origin, (randomFloat( 2 ), randomFloat( 2 ), randomFloat( 2 )) );
  2195. }
  2196. }
  2197. */
  2198. }
  2199.  
  2200.  
  2201. drawLink( start, end, color )
  2202. {
  2203. level endon ( "endpath" );
  2204. for ( ;; )
  2205. {
  2206. line(start, end, color, true);
  2207. wait 0.05;
  2208. }
  2209. }
  2210.  
  2211. debugPrintLn2( printString )
  2212. {
  2213. /#
  2214. if ( getDvarInt( "tankDebug" ) )
  2215. printLn( printString );
  2216. #/
  2217. }
  2218.  
  2219. debugPrint( printString )
  2220. {
  2221. /#
  2222. if ( getDvarInt( "tankDebug" ) )
  2223. print( printString );
  2224. #/
  2225. }
  2226.  
  2227.  
  2228. debugPrint3D( origin, printString, color, alpha, scale, duration )
  2229. {
  2230. /#
  2231. if ( getDvarInt( "tankDebug" ) )
  2232. {
  2233. print3d( origin, printString, color, alpha, scale, duration );
  2234. println( "3D: " + printString );
  2235. }
  2236. #/
  2237. }
  2238.  
  2239.  
  2240. drawTankGraphIds()
  2241. {
  2242. /#
  2243. if ( getDvarInt( "tankDebug" ) )
  2244. {
  2245. self notify ( "drawTankGraphIds" );
  2246. self endon ( "drawTankGraphIds" );
  2247.  
  2248. for ( ;; )
  2249. {
  2250. print3d( self.origin + (0,0,128), "FW: " + self.forwardGraphId + " RV: " + self.reverseGraphId, (0,1,0), 1, 3, 1 );
  2251. wait ( 0.05 );
  2252. }
  2253. }
  2254. #/
  2255. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement