burc_2004

zm_transit_openings.gsc

Oct 21st, 2015
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 61.96 KB | None | 0 0
  1. #include maps/mp/zm_transit_cling;
  2. #include maps/mp/zombies/_zm_buildables;
  3. #include maps/mp/zombies/_zm_audio;
  4. #include maps/mp/animscripts/zm_run;
  5. #include maps/mp/zombies/_zm_spawner;
  6. #include maps/mp/zombies/_zm_ai_basic;
  7. #include maps/mp/animscripts/zm_shared;
  8. #include maps/mp/animscripts/zm_utility;
  9. #include maps/mp/zm_transit_bus;
  10. #include maps/mp/zombies/_zm_powerups;
  11. #include maps/mp/zm_transit_utility;
  12. #include maps/mp/zombies/_zm_utility;
  13. #include maps/mp/zombies/_zm_blockers;
  14. #include maps/mp/_utility;
  15. #include common_scripts/utility;
  16.  
  17. main()
  18. {
  19.     self _businittags();
  20.     self _setupnumattachtable();
  21.     self.openings = [];
  22.     x = 1;
  23.     while ( x <= 10 )
  24.     {
  25.         script_noteworthy = undefined;
  26.         switch( x )
  27.         {
  28.             case 10:
  29.                 script_noteworthy = "front";
  30.                 break;
  31.             case 11:
  32.             case 12:
  33.                 script_noteworthy = "door";
  34.                 break;
  35.         }
  36.         self busaddopening( "tag_entry_point" + x, script_noteworthy );
  37.         x++;
  38.     }
  39.     self bussetupladder();
  40.     self bussetuproofopening();
  41.     self businitmantle();
  42.     self businitrightandleft();
  43. }
  44.  
  45. busaddopening( tag_name, script_noteworthy )
  46. {
  47.     index = self.openings.size;
  48.     self.openings[ index ] = spawnstruct();
  49.     opening = self.openings[ index ];
  50.     opening.name = script_noteworthy;
  51.     opening.enabled = 1;
  52.     opening.zombie = undefined;
  53.     opening.boards = [];
  54.     opening.boardsnum = 0;
  55.     opening.blockertrigger = undefined;
  56.     opening.zombietrigger = undefined;
  57.     opening.rebuildtrigger = undefined;
  58.     opening.tagname = tag_name;
  59.     opening.bindtag = _busfindclosesttag( self gettagorigin( tag_name ) );
  60. /#
  61.     assert( isDefined( opening.bindtag ) );
  62. #/
  63.     opening.jumptag = _busgetjumptagfrombindtag( opening.bindtag );
  64.     opening.jumpent = level.the_bus;
  65.     opening.roofjoint = _busgetroofjointfrombindtag( opening.bindtag );
  66.     opening.origin = level.the_bus gettagorigin( opening.bindtag );
  67.     opening.angles = self gettagangles( opening.bindtag );
  68.     targets = getentarray( tag_name, "targetname" );
  69.     if ( !isDefined( targets ) )
  70.     {
  71. /#
  72.         assert( 0 );
  73. #/
  74.         return;
  75.     }
  76.     if ( !is_classic() )
  77.     {
  78.         i = 0;
  79.         while ( i < targets.size )
  80.         {
  81.             if ( targets[ i ] iszbarrier() )
  82.             {
  83.                 targets[ i ] delete();
  84.             }
  85.             i++;
  86.         }
  87.         return;
  88.     }
  89.     i = 0;
  90.     while ( i < targets.size )
  91.     {
  92.         target = targets[ i ];
  93.         if ( target iszbarrier() )
  94.         {
  95.             hasbarriers = 1;
  96.             if ( isDefined( script_noteworthy ) && script_noteworthy == "front" )
  97.             {
  98.                 hasbarriers = 0;
  99.             }
  100.             opening.zbarrier = target;
  101.             opening.zbarrier setmovingplatformenabled( 1 );
  102.             while ( hasbarriers )
  103.             {
  104.                 opening.zbarrier.chunk_health = [];
  105.                 opening.zbarrier setzbarriercolmodel( "p6_anim_zm_barricade_board_bus_collision" );
  106.                 maps/mp/zombies/_zm_powerups::register_carpenter_node( opening, ::post_carpenter_callback );
  107.                 j = 0;
  108.                 while ( j < opening.zbarrier getnumzbarrierpieces() )
  109.                 {
  110.                     opening.zbarrier.chunk_health[ j ] = 0;
  111.                     j++;
  112.                 }
  113.             }
  114.             target.origin = level.the_bus gettagorigin( opening.bindtag );
  115.         }
  116.         else if ( target.script_noteworthy == "blocker" )
  117.         {
  118.             target delete();
  119.             i++;
  120.             continue;
  121.         }
  122.         else if ( target.script_noteworthy == "rebuild" )
  123.         {
  124.             opening.rebuildtrigger = target;
  125.             opening.rebuildtrigger enablelinkto();
  126.             opening.rebuildtrigger linkto( self );
  127.             opening.rebuildtrigger setcursorhint( "HINT_NOICON" );
  128.             opening.rebuildtrigger set_hint_string( self, "default_reward_barrier_piece" );
  129.             opening.rebuildtrigger triggerignoreteam();
  130.             opening.rebuildtrigger setinvisibletoall();
  131.             opening.rebuildtrigger setmovingplatformenabled( 1 );
  132.             self thread busopeningrebuildthink( opening );
  133.             i++;
  134.             continue;
  135.         }
  136.         else if ( target.script_noteworthy == "zombie" )
  137.         {
  138.             opening.zombietrigger = target;
  139.             opening.zombietrigger enablelinkto();
  140.             opening.zombietrigger linkto( self );
  141.             opening.zombietrigger setmovingplatformenabled( 1 );
  142.             opening.zombietrigger setteamfortrigger( level.zombie_team );
  143.             self thread busopeningzombiethink( opening );
  144.         }
  145.         target linkto( self, "", self worldtolocalcoords( target.origin ), target.angles - self.angles );
  146.         i++;
  147.     }
  148.     if ( isDefined( opening.zbarrier ) )
  149.     {
  150.         opening blocker_attack_spots();
  151.     }
  152.     opening notify( "opening_init_complete" );
  153. /#
  154.     if ( opening.boardsnum != 0 )
  155.     {
  156.         assert( opening.boardsnum == opening.boards.size );
  157.     }
  158. #/
  159. }
  160.  
  161. post_carpenter_callback()
  162. {
  163.     if ( isDefined( self.rebuildtrigger ) )
  164.     {
  165.         self.rebuildtrigger setinvisibletoall();
  166.     }
  167. }
  168.  
  169. businitmantle()
  170. {
  171.     mantlebrush = getentarray( "window_mantle", "targetname" );
  172.     while ( isDefined( mantlebrush ) && mantlebrush.size > 0 )
  173.     {
  174.         i = 0;
  175.         while ( i < mantlebrush.size )
  176.         {
  177.             mantlebrush[ i ] delete();
  178.             i++;
  179.         }
  180.     }
  181. }
  182.  
  183. busattachjumpent( ent, opening )
  184. {
  185.     jump_origin = self gettagorigin( opening.jumptag );
  186.     jump_angles = self gettagangles( opening.jumptag );
  187.     ent.origin = jump_origin;
  188.     ent.angles = jump_angles;
  189.     ent linkto( self, "", self worldtolocalcoords( ent.origin ), ent.angles - self.angles );
  190. }
  191.  
  192. busopeningbyname( name )
  193. {
  194.     i = 0;
  195.     while ( i < self.openings.size )
  196.     {
  197.         opening = self.openings[ i ];
  198.         if ( isDefined( opening.name ) && opening.name == name )
  199.         {
  200.             return opening;
  201.         }
  202.         i++;
  203.     }
  204.     return undefined;
  205. }
  206.  
  207. busopeningsetenabled( name, enabled )
  208. {
  209.     i = 0;
  210.     while ( i < self.openings.size )
  211.     {
  212.         opening = self.openings[ i ];
  213.         if ( isDefined( opening.name ) && opening.name == name )
  214.         {
  215.             opening.enabled = enabled;
  216.         }
  217.         i++;
  218.     }
  219. }
  220.  
  221. _businittags()
  222. {
  223.     self.openingtags = [];
  224.     self.openingtags[ self.openingtags.size ] = "window_right_front_jnt";
  225.     self.openingtags[ self.openingtags.size ] = "window_left_front_jnt";
  226.     self.openingtags[ self.openingtags.size ] = "door_front_jnt";
  227.     self.openingtags[ self.openingtags.size ] = "door_rear_jnt";
  228.     self.openingtags[ self.openingtags.size ] = "window_right_1_jnt";
  229.     self.openingtags[ self.openingtags.size ] = "window_right_2_jnt";
  230.     self.openingtags[ self.openingtags.size ] = "window_right_3_jnt";
  231.     self.openingtags[ self.openingtags.size ] = "window_right_4_jnt";
  232.     self.openingtags[ self.openingtags.size ] = "window_right_rear_jnt";
  233.     self.openingtags[ self.openingtags.size ] = "window_left_rear_jnt";
  234.     self.openingtags[ self.openingtags.size ] = "window_left_1_jnt";
  235.     self.openingtags[ self.openingtags.size ] = "window_left_2_jnt";
  236.     self.openingtags[ self.openingtags.size ] = "window_left_3_jnt";
  237.     self.openingtags[ self.openingtags.size ] = "window_left_4_jnt";
  238.     self.openingtags[ self.openingtags.size ] = "window_left_5_jnt";
  239. /#
  240.     i = 0;
  241.     while ( i < self.openingtags.size )
  242.     {
  243.         adddebugcommand( "devgui_cmd "Zombies:1/Bus:14/Window Openings:3/Select Tag:1/" + self.openingtags[ i ] + ":" + self.openingtags.size + "" "zombie_devgui attach_tag " + self.openingtags[ i ] + ""\n" );
  244.         i++;
  245. #/
  246.     }
  247. }
  248.  
  249. _busfindclosesttag( checkpos )
  250. {
  251.     closest = undefined;
  252.     closestdist = -1;
  253. /#
  254.     assert( isDefined( self.openingtags ) );
  255. #/
  256.     i = 0;
  257.     while ( i < self.openingtags.size )
  258.     {
  259.         tag = self.openingtags[ i ];
  260.         pos = self gettagorigin( tag );
  261.         dist2 = distancesquared( checkpos, pos );
  262.         if ( !isDefined( closest ) || dist2 < closestdist )
  263.         {
  264.             closest = tag;
  265.             closestdist = dist2;
  266.         }
  267.         i++;
  268.     }
  269.     return closest;
  270. }
  271.  
  272. _busgetjumptagfrombindtag( tag )
  273. {
  274.     jump_tag = undefined;
  275.     switch( tag )
  276.     {
  277.         case "window_right_1_jnt":
  278.             jump_tag = "window_right_1_jmp_jnt";
  279.             break;
  280.         case "window_right_2_jnt":
  281.             jump_tag = "window_right_2_jmp_jnt";
  282.             break;
  283.         case "window_right_3_jnt":
  284.             jump_tag = "window_right_3_jmp_jnt";
  285.             break;
  286.         case "window_right_4_jnt":
  287.             jump_tag = "window_right_4_jmp_jnt";
  288.             break;
  289.         case "window_left_1_jnt":
  290.             jump_tag = "window_left_1_jmp_jnt";
  291.             break;
  292.         case "window_left_2_jnt":
  293.             jump_tag = "window_left_2_jmp_jnt";
  294.             break;
  295.         case "window_left_3_jnt":
  296.             jump_tag = "window_left_3_jmp_jnt";
  297.             break;
  298.         case "window_left_4_jnt":
  299.             jump_tag = "window_left_4_jmp_jnt";
  300.             break;
  301.         case "window_left_5_jnt":
  302.             jump_tag = "window_left_5_jmp_jnt";
  303.             break;
  304.         case "window_right_rear_jnt":
  305.             jump_tag = "window_right_rear_jmp_jnt";
  306.             break;
  307.         case "window_left_rear_jnt":
  308.             jump_tag = "window_left_rear_jmp_jnt";
  309.             break;
  310.         case "window_right_front_jnt":
  311.             jump_tag = "window_right_front_jmp_jnt";
  312.             break;
  313.         case "window_left_front_jnt":
  314.             jump_tag = "window_left_front_jmp_jnt";
  315.             break;
  316.         case "door_rear_jnt":
  317.             jump_tag = "door_rear_jmp_jnt";
  318.             break;
  319.         case "door_front_jnt":
  320.             jump_tag = "door_front_jmp_jnt";
  321.             break;
  322.         default:
  323.         }
  324.         return jump_tag;
  325.     }
  326. }
  327.  
  328. _busgetroofjointfrombindtag( tag )
  329. {
  330.     roofjoint = undefined;
  331.     switch( tag )
  332.     {
  333.         case "window_left_1_jnt":
  334.         case "window_left_front_jnt":
  335.         case "window_right_1_jnt":
  336.         case "window_right_front_jnt":
  337.             roofjoint = "window_roof_1_jnt";
  338.             break;
  339.         case "window_left_2_jnt":
  340.         case "window_left_3_jnt":
  341.         case "window_left_4_jnt":
  342.         case "window_left_5_jnt":
  343.         case "window_left_rear_jnt":
  344.         case "window_right_2_jnt":
  345.         case "window_right_3_jnt":
  346.         case "window_right_4_jnt":
  347.         case "window_right_rear_jnt":
  348.             roofjoint = "window_roof_2_jnt";
  349.             default:
  350.                 break;
  351.         }
  352.         return roofjoint;
  353.     }
  354. }
  355.  
  356. _setupnumattachtable()
  357. {
  358.     level.numattachtable = [];
  359.     level.numattachtable[ 0 ] = -1;
  360.     level.numattachtable[ 1 ] = 6;
  361.     level.numattachtable[ 2 ] = 8;
  362.     level.numattachtable[ 3 ] = 10;
  363.     level.numattachtable[ 4 ] = -1;
  364. }
  365.  
  366. busgetopeningfortag( tagname )
  367. {
  368.     i = 0;
  369.     while ( i < self.openings.size )
  370.     {
  371.         if ( self.openings[ i ].bindtag == tagname )
  372.         {
  373.             return self.openings[ i ];
  374.         }
  375.         i++;
  376.     }
  377.     return undefined;
  378. }
  379.  
  380. zombieanimnotetrackthink( notifystring, chunk, node )
  381. {
  382.     self endon( "death" );
  383.     while ( 1 )
  384.     {
  385.         self waittill( notifystring, notetrack );
  386.         if ( notetrack == "end" )
  387.         {
  388.             return;
  389.             continue;
  390.         }
  391.         else if ( notetrack == "board" || notetrack == "destroy_piece" )
  392.         {
  393.             node.zbarrier setzbarrierpiecestate( chunk, "opening" );
  394.             if ( isDefined( node.rebuildtrigger ) )
  395.             {
  396.                 node.rebuildtrigger setvisibletoall();
  397.             }
  398.             continue;
  399.         }
  400.         else
  401.         {
  402.             while ( notetrack == "fire" )
  403.             {
  404.                 attackplayers = self zombiegetplayerstoattack();
  405.                 while ( attackplayers.size )
  406.                 {
  407.                     i = 0;
  408.                     while ( i < attackplayers.size )
  409.                     {
  410.                         attackplayers[ i ] dodamage( self.meleedamage, self.origin, self, self, "none", "MOD_MELEE" );
  411.                         i++;
  412.                     }
  413.                 }
  414.             }
  415.         }
  416.     }
  417. }
  418.  
  419. busopeningblockerthink( opening )
  420. {
  421.     self endon( "intermission" );
  422.     while ( 1 )
  423.     {
  424.         opening.blockertrigger waittill( "trigger", player );
  425.         while ( isDefined( opening.zombie ) )
  426.         {
  427.             continue;
  428.         }
  429.     }
  430.     self notify( "OnBlockerPlaced" );
  431. }
  432.  
  433. buswatchtriggervisibility( opening )
  434. {
  435.     self endon( "intermission" );
  436.     opening waittill( "opening_init_complete" );
  437.     if ( !isDefined( opening.zbarrier ) || opening.zbarrier getnumzbarrierpieces() < 1 )
  438.     {
  439.         return;
  440.     }
  441.     if ( !isDefined( level.buswatchtriggervisibility_spread ) )
  442.     {
  443.         level.buswatchtriggervisibility_spread = 0;
  444.     }
  445.     else
  446.     {
  447.         level.buswatchtriggervisibility_spread++;
  448.         wait ( 0,05 * level.buswatchtriggervisibility_spread );
  449.     }
  450.     while ( 1 )
  451.     {
  452.         if ( no_valid_repairable_boards( opening ) )
  453.         {
  454.             opening.rebuildtrigger setinvisibletoall();
  455.         }
  456.         else
  457.         {
  458.             opening.rebuildtrigger setvisibletoall();
  459.         }
  460.         wait 1;
  461.     }
  462. }
  463.  
  464. busopeningrebuildthink( opening )
  465. {
  466.     self endon( "intermission" );
  467.     self thread buswatchtriggervisibility( opening );
  468.     cost = 10;
  469.     if ( isDefined( opening.rebuildtrigger.zombie_cost ) )
  470.     {
  471.         cost = opening.rebuildtrigger.zombie_cost;
  472.     }
  473.     while ( 1 )
  474.     {
  475.         opening.rebuildtrigger waittill( "trigger", player );
  476.         players = get_players();
  477.         has_perk = player has_blocker_affecting_perk();
  478.         while ( player_fails_blocker_repair_trigger_preamble( player, players, opening.rebuildtrigger, 0 ) )
  479.         {
  480.             continue;
  481.         }
  482.         while ( all_chunks_intact( opening ) )
  483.         {
  484.             continue;
  485.         }
  486.         while ( no_valid_repairable_boards( opening ) )
  487.         {
  488.             continue;
  489.         }
  490.         chunk = get_random_destroyed_chunk( opening );
  491.         if ( isDefined( player.pers_upgrades_awarded[ "board" ] ) )
  492.         {
  493.             self thread replace_chunk( opening, chunk, has_perk, player.pers_upgrades_awarded[ "board" ] );
  494.         }
  495.         opening do_post_chunk_repair_delay( has_perk );
  496.         while ( !is_player_valid( player ) )
  497.         {
  498.             if ( no_valid_repairable_boards( opening ) )
  499.             {
  500.                 opening.rebuildtrigger setinvisibletoall();
  501.             }
  502.         }
  503.         player handle_post_board_repair_rewards( cost );
  504.         if ( no_valid_repairable_boards( opening ) )
  505.         {
  506.             opening.rebuildtrigger setinvisibletoall();
  507.         }
  508.     }
  509. }
  510.  
  511. _determinejumpfromorigin( opening )
  512. {
  513.     return level.the_bus gettagorigin( opening.jumptag );
  514. }
  515.  
  516. _getsideofbusopeningison( opening_tag )
  517. {
  518.     side = undefined;
  519.     switch( opening_tag )
  520.     {
  521.         case "door_front_jnt":
  522.         case "door_rear_jnt":
  523.         case "window_right_1_jnt":
  524.         case "window_right_2_jnt":
  525.         case "window_right_3_jnt":
  526.         case "window_right_4_jnt":
  527.             side = "right";
  528.             break;
  529.         case "window_left_1_jnt":
  530.         case "window_left_2_jnt":
  531.         case "window_left_3_jnt":
  532.         case "window_left_4_jnt":
  533.         case "window_left_5_jnt":
  534.             side = "left";
  535.             break;
  536.         case "window_left_front_jnt":
  537.         case "window_right_front_jnt":
  538.             side = "front";
  539.             break;
  540.         case "window_left_rear_jnt":
  541.         case "window_right_rear_jnt":
  542.             side = "back";
  543.             break;
  544.     }
  545.     return side;
  546. }
  547.  
  548. busopeningzombiethink( opening )
  549. {
  550.     self endon( "intermission" );
  551.     if ( _isopeningdoor( opening.bindtag ) )
  552.     {
  553.         opening.enabled = 0;
  554.         return;
  555.     }
  556.     while ( 1 )
  557.     {
  558.         opening.zombietrigger waittill( "trigger", zombie );
  559.         while ( zombie.isdog )
  560.         {
  561.             continue;
  562.         }
  563.         if ( isDefined( zombie.isscreecher ) && zombie.isscreecher )
  564.         {
  565.             continue;
  566.         }
  567.         if ( isDefined( zombie.is_avogadro ) && zombie.is_avogadro )
  568.         {
  569.             continue;
  570.         }
  571.         while ( !isalive( zombie ) )
  572.         {
  573.             continue;
  574.         }
  575.         while ( !opening.enabled )
  576.         {
  577.             continue;
  578.         }
  579.         if ( _isopeningdoor( opening.bindtag ) && self.doorsclosed )
  580.         {
  581.             continue;
  582.         }
  583.         if ( isDefined( zombie.favoriteenemy ) )
  584.         {
  585.             enemy_on_roof = zombie.favoriteenemy.isonbusroof;
  586.         }
  587.         while ( self.ismoving && isDefined( self.disabled_by_emp ) && self.disabled_by_emp && !self.doorsclosed && !enemy_on_roof )
  588.         {
  589.             continue;
  590.         }
  591.         monkey = undefined;
  592.         if ( isDefined( zombie.enemyoverride ) )
  593.         {
  594.             monkey = zombie.enemyoverride[ 1 ];
  595.         }
  596.         if ( isDefined( monkey ) && !monkey maps/mp/zm_transit_bus::entity_is_on_bus( 1 ) )
  597.         {
  598.             continue;
  599.         }
  600.         if ( !isDefined( zombie.favoriteenemy ) || !zombie.favoriteenemy.isonbus )
  601.         {
  602.             continue;
  603.         }
  604.         if ( isDefined( zombie.isonbus ) && zombie.isonbus )
  605.         {
  606.             continue;
  607.         }
  608.         while ( isDefined( zombie.opening ) )
  609.         {
  610.             continue;
  611.         }
  612.         while ( isDefined( opening.zombie ) )
  613.         {
  614.             continue;
  615.         }
  616.         if ( isDefined( zombie.is_inert ) && zombie.is_inert )
  617.         {
  618.             continue;
  619.         }
  620.         if ( isDefined( zombie.cannotattachtobus ) && zombie.cannotattachtobus )
  621.         {
  622.             continue;
  623.         }
  624.         while ( !level.the_bus.ismoving )
  625.         {
  626.             jump_origin = _determinejumpfromorigin( opening );
  627.             distance_from_jump_origin2 = distance2dsquared( jump_origin, zombie.origin );
  628.             while ( distance_from_jump_origin2 > 256 )
  629.             {
  630.                 continue;
  631.             }
  632.         }
  633.         if ( isDefined( zombie.dismount_start ) && zombie.dismount_start )
  634.         {
  635.             continue;
  636.         }
  637.         zombie thread zombieattachtobus( self, opening );
  638.     }
  639. }
  640.  
  641. _buscanzombieattach( zombie )
  642. {
  643.     currentlyattached = 0;
  644.     i = 0;
  645.     while ( i < self.openings.size )
  646.     {
  647.         if ( isDefined( self.openings[ i ].zombie ) )
  648.         {
  649.             currentlyattached++;
  650.         }
  651.         i++;
  652.     }
  653.     players = get_players();
  654.     maxattach = level.numattachtable[ players.size ];
  655.     if ( maxattach < 0 )
  656.     {
  657.         return currentlyattached < maxattach;
  658.     }
  659. }
  660.  
  661. zombieplayattachedanim( animname )
  662. {
  663.     self endon( "death" );
  664.     anim_index = self getanimsubstatefromasd( "zm_bus_attached", animname );
  665.     animationid = self getanimfromasd( "zm_bus_attached", anim_index );
  666.     tag_origin = self.attachent gettagorigin( self.attachtag );
  667.     tag_angles = self.attachent gettagangles( self.attachtag );
  668.     start_origin = getstartorigin( tag_origin, tag_angles, animationid );
  669.     start_angles = getstartangles( tag_origin, tag_angles, animationid );
  670.     self animscripted( start_origin, start_angles, "zm_bus_attached", anim_index );
  671.     self zombieanimnotetrackthink( "bus_attached_anim" );
  672. }
  673.  
  674. debugline( frompoint, color, durationframes )
  675. {
  676. /#
  677.     i = 0;
  678.     while ( i < durationframes )
  679.     {
  680.         line( frompoint, ( frompoint[ 0 ], frompoint[ 1 ], frompoint[ 2 ] + 50 ), color );
  681.         wait 0,05;
  682.         i++;
  683. #/
  684.     }
  685. }
  686.  
  687. debugbox( frompoint, color, durationframes )
  688. {
  689. /#
  690.     i = 0;
  691.     while ( i < durationframes )
  692.     {
  693.         box( frompoint, ( 0, -1, 0 ), ( 0, -1, 0 ), 0, color );
  694.         wait 0,05;
  695.         i++;
  696. #/
  697.     }
  698. }
  699.  
  700. _isopeningdoor( opening_tag )
  701. {
  702.     is_door = 0;
  703.     switch( opening_tag )
  704.     {
  705.         case "door_front_jnt":
  706.         case "door_rear_jnt":
  707.             is_door = 1;
  708.             break;
  709.         default:
  710.         }
  711.         return is_door;
  712.     }
  713. }
  714.  
  715. busexitthink( trigger )
  716. {
  717.     while ( 1 )
  718.     {
  719.         trigger waittill( "trigger", zombie );
  720.         if ( isDefined( zombie.is_inert ) && zombie.is_inert )
  721.         {
  722.             continue;
  723.         }
  724.         if ( isDefined( zombie.walk_to_exit ) && !zombie.walk_to_exit )
  725.         {
  726.             continue;
  727.         }
  728.         if ( isDefined( zombie.exiting_window ) && zombie.exiting_window )
  729.         {
  730.             continue;
  731.         }
  732.         going_to_roof = 0;
  733.         while ( isDefined( zombie.favoriteenemy ) && zombie.favoriteenemy.isonbusroof == 1 )
  734.         {
  735.             going_to_roof = 1;
  736.             while ( trigger.substate == 2 )
  737.             {
  738.                 continue;
  739.             }
  740.         }
  741.         while ( !going_to_roof && isDefined( level.the_bus.doorsclosed ) && !level.the_bus.doorsclosed && zombie.ai_state != "zombieWindowToRoof" )
  742.         {
  743.             continue;
  744.         }
  745.         zombie thread zombieexitwindow( self, trigger, going_to_roof );
  746.     }
  747. }
  748.  
  749. zombieexitwindow( bus, trigger, going_to_roof )
  750. {
  751.     self endon( "death" );
  752.     self.ai_state = "zombieExitWindow";
  753.     self.inert_delay = ::zombieexitwindowdelay;
  754.     self.exiting_window = 1;
  755.     self linkto( bus, trigger.tag );
  756.     tag_origin = bus gettagorigin( trigger.tag );
  757.     tag_angles = bus gettagangles( trigger.tag );
  758.     exit_anim = "zm_window_exit";
  759.     if ( going_to_roof )
  760.     {
  761.         exit_anim = "zm_bus_window2roof";
  762.     }
  763.     animstate = maps/mp/animscripts/zm_utility::append_missing_legs_suffix( exit_anim );
  764.     self animscripted( tag_origin, tag_angles, animstate, trigger.substate );
  765.     maps/mp/animscripts/zm_shared::donotetracks( "window_exit_anim" );
  766.     self.exiting_window = undefined;
  767.     self.walk_to_exit = undefined;
  768.     self unlink();
  769.     self setgoalpos( self.origin );
  770.     self.inert_delay = undefined;
  771.     if ( going_to_roof )
  772.     {
  773.         return;
  774.     }
  775.     self animmode( "normal" );
  776.     self orientmode( "face enemy" );
  777.     self.forcemovementscriptstate = 0;
  778.     self thread maps/mp/zombies/_zm_ai_basic::find_flesh();
  779. }
  780.  
  781. zombieexitwindowdelay()
  782. {
  783.     self endon( "death" );
  784.     while ( isDefined( self.exiting_window ) && self.exiting_window )
  785.     {
  786.         wait 0,1;
  787.     }
  788.     while ( 1 )
  789.     {
  790.         if ( self.ai_state == "find_flesh" )
  791.         {
  792.             break;
  793.         }
  794.         else
  795.         {
  796.             wait 0,1;
  797.         }
  798.     }
  799.     self notify( "stop_find_flesh" );
  800.     self notify( "zombie_acquire_enemy" );
  801.     self.inert_delay = undefined;
  802. }
  803.  
  804. teleportthreadex( verticaloffset, delay, frames )
  805. {
  806.     amount = verticaloffset / frames;
  807.     if ( amount > 10 )
  808.     {
  809.         amount = 10;
  810.     }
  811.     else
  812.     {
  813.         if ( amount < -10 )
  814.         {
  815.             amount = -10;
  816.         }
  817.     }
  818.     offset = ( 0, 0, amount );
  819.     i = 0;
  820.     while ( i < frames )
  821.     {
  822.         self teleport( self.origin + offset );
  823.         wait 0,05;
  824.         i++;
  825.     }
  826. }
  827.  
  828. zombieopeningdelay()
  829. {
  830.     self endon( "death" );
  831.     self maps/mp/zombies/_zm_spawner::zombie_history( "opening delay detach " + getTime() );
  832.     while ( isDefined( self.jumpingtowindow ) && self.jumpingtowindow )
  833.     {
  834.         while ( 1 )
  835.         {
  836.             if ( isDefined( self.jumpingtowindow ) && !self.jumpingtowindow )
  837.             {
  838.                 break;
  839.             }
  840.             else
  841.             {
  842.                 wait 0,1;
  843.             }
  844.         }
  845.     }
  846.     self zombiedetachfrombus( self.left_or_right );
  847.     while ( 1 )
  848.     {
  849.         if ( self.ai_state == "find_flesh" )
  850.         {
  851.             break;
  852.         }
  853.         else
  854.         {
  855.             wait 0,1;
  856.         }
  857.     }
  858.     self notify( "stop_find_flesh" );
  859.     self notify( "zombie_acquire_enemy" );
  860.     self.inert_delay = undefined;
  861. }
  862.  
  863. zombieattachtobus( thebus, opening, removeafterdone )
  864. {
  865.     self endon( "death" );
  866.     self endon( "removed" );
  867.     self endon( "sonicBoom" );
  868.     level endon( "intermission" );
  869.     self notify( "stop_find_flesh" );
  870.     self notify( "zombie_acquire_enemy" );
  871.     self endon( "detach_on_window" );
  872.     self endon( "exploding" );
  873.     self.ai_state = "zombieAttachToBus";
  874.     self maps/mp/zombies/_zm_spawner::zombie_history( "zombieAttachToBus " + getTime() );
  875.     opening.zombie = self;
  876.     self.dont_throw_gib = 1;
  877.     self.forcemovementscriptstate = 1;
  878.     self.opening = opening;
  879.     self.left_or_right = self zombieattachleftorright( thebus );
  880.     self.attachent = level.the_bus;
  881.     self.attachtag = self.opening.bindtag;
  882.     self linkto( self.attachent, self.attachtag );
  883.     from_front = 0;
  884.     from_rear = 0;
  885.     self.inert_delay = ::zombieopeningdelay;
  886.     if ( _isopeningdoor( opening.bindtag ) )
  887.     {
  888.         self animscripted( self.origin, self.angles, "zm_jump_on_bus", 0 );
  889.         if ( opening.bindtag == "door_front_jnt" )
  890.         {
  891.             from_front = 1;
  892.         }
  893.         else
  894.         {
  895.             from_rear = 1;
  896.         }
  897.     }
  898.     else
  899.     {
  900.         self.jumpingtowindow = 1;
  901.         asd_name = "zm_zbarrier_jump_on_bus";
  902.         side = _getsideofbusopeningison( opening.bindtag );
  903.         if ( isDefined( side ) && side == "front" )
  904.         {
  905.             asd_name = "zm_zbarrier_jump_on_bus_front";
  906.         }
  907.         animstate = maps/mp/animscripts/zm_utility::append_missing_legs_suffix( asd_name );
  908.         tag_origin = self.attachent gettagorigin( self.attachtag );
  909.         tag_angles = self.attachent gettagangles( self.attachtag );
  910.         self animmode( "noclip" );
  911.         self animscripted( tag_origin, tag_angles, animstate, "jump_window" + self.left_or_right );
  912.     }
  913.     self zombieanimnotetrackthink( "jump_on_bus_anim" );
  914.     self animmode( "gravity" );
  915.     self.jumpingtowindow = 0;
  916.     if ( isDefined( self.a.gib_ref ) || self.a.gib_ref == "left_arm" && self.a.gib_ref == "right_arm" )
  917.     {
  918.         self dodamage( self.health + 666, self.origin );
  919.         self startragdoll();
  920.         self launchragdoll( ( 0, -1, 0 ) );
  921.         opening.zombie = undefined;
  922.     }
  923.     if ( 1 )
  924.     {
  925.         hitpos = self.attachent gettagorigin( self.attachtag );
  926.         hitposinbus = pointonsegmentnearesttopoint( thebus.frontworld, thebus.backworld, hitpos );
  927.         hitdir = vectornormalize( hitposinbus - hitpos );
  928.         hitforce = vectorScale( hitdir, 100 );
  929.         hitpos += vectorScale( ( 0, -1, 0 ), 50 );
  930.         earthquake( randomfloatrange( 0,3, 0,4 ), randomfloatrange( 0,2, 0,4 ), hitpos, 150 );
  931.         play_sound_at_pos( "grab_metal_bar", hitpos );
  932.     }
  933.     if ( self zombiecanjumponroof( opening ) )
  934.     {
  935.         self zombiejumponroof( thebus, opening, removeafterdone, self.left_or_right );
  936.         self zombiesetnexttimetojumponroof();
  937.         self maps/mp/animscripts/zm_run::needsupdate();
  938.         if ( !self.isdog )
  939.         {
  940.             self maps/mp/animscripts/zm_run::moverun();
  941.         }
  942.     }
  943.     else
  944.     {
  945.         for ( ;; )
  946.         {
  947.             while ( 1 )
  948.             {
  949.                 if ( !isDefined( opening.zbarrier ) || maps/mp/zombies/_zm_spawner::get_attack_spot( opening ) )
  950.                 {
  951.                     break;
  952.                 }
  953.                 else
  954.                 {
  955. /#
  956.                     println( "Zombie failed to get bus attack spot" );
  957. #/
  958.                     wait 0,5;
  959.                 }
  960.             }
  961.         }
  962.         while ( !all_chunks_destroyed( opening ) )
  963.         {
  964.             if ( zombieshoulddetachfromwindow() )
  965.             {
  966.                 zombiedetachfrombus( self.left_or_right );
  967.                 return;
  968.             }
  969.             self.onbuswindow = 1;
  970.             chunk = get_closest_non_destroyed_chunk( self.origin, opening );
  971.             waited = 0;
  972.             if ( isDefined( chunk ) )
  973.             {
  974.                 waited = 1;
  975.                 opening.zbarrier setzbarrierpiecestate( chunk, "targetted_by_zombie" );
  976.                 opening thread check_zbarrier_piece_for_zombie_death( chunk, opening.zbarrier, self );
  977.                 self thread maps/mp/zombies/_zm_audio::do_zombies_playvocals( "teardown", self.animname );
  978.                 animstatebase = opening.zbarrier getzbarrierpieceanimstate( chunk );
  979.                 animsubstate = "spot_" + self.attacking_spot_index + self.left_or_right + "_piece_" + opening.zbarrier getzbarrierpieceanimsubstate( chunk );
  980.                 anim_sub_index = self getanimsubstatefromasd( animstatebase + "_in", animsubstate );
  981.                 tag_origin = self.attachent gettagorigin( self.attachtag );
  982.                 tag_angles = self.attachent gettagangles( self.attachtag );
  983.                 self animscripted( tag_origin, tag_angles, maps/mp/animscripts/zm_utility::append_missing_legs_suffix( animstatebase + "_in" ), anim_sub_index );
  984.                 self zombieanimnotetrackthink( "board_tear_bus_anim", chunk, opening );
  985.                 while ( opening.zbarrier.chunk_health[ chunk ] >= 0 )
  986.                 {
  987.                     tag_origin = self.attachent gettagorigin( self.attachtag );
  988.                     tag_angles = self.attachent gettagangles( self.attachtag );
  989.                     self animscripted( tag_origin, tag_angles, maps/mp/animscripts/zm_utility::append_missing_legs_suffix( animstatebase + "_loop" ), anim_sub_index );
  990.                     self zombieanimnotetrackthink( "board_tear_bus_anim", chunk, opening );
  991.                     opening.zbarrier.chunk_health[ chunk ]--;
  992.  
  993.                 }
  994.                 tag_origin = self.attachent gettagorigin( self.attachtag );
  995.                 tag_angles = self.attachent gettagangles( self.attachtag );
  996.                 self animscripted( tag_origin, tag_angles, maps/mp/animscripts/zm_utility::append_missing_legs_suffix( animstatebase + "_out" ), anim_sub_index );
  997.                 self zombieanimnotetrackthink( "board_tear_bus_anim", chunk, opening );
  998.             }
  999.             tried_attack = self zombietryattackthroughwindow( 1, self.left_or_right );
  1000.             if ( !tried_attack && !waited )
  1001.             {
  1002.                 wait 0,1;
  1003.             }
  1004.         }
  1005.         self.onbuswindow = undefined;
  1006.         if ( !_isopeningdoor( opening.bindtag ) )
  1007.         {
  1008.             self zombiekeepattackingthroughwindow( self.left_or_right );
  1009.             side = _getsideofbusopeningison( opening.bindtag );
  1010.             if ( side == "front" )
  1011.             {
  1012.                 from_front = 1;
  1013.             }
  1014.             anim_state = "window_climbin";
  1015.             if ( from_front )
  1016.             {
  1017.                 anim_state += "_front";
  1018.                 anim_state += self.left_or_right;
  1019.             }
  1020.             else if ( from_rear )
  1021.             {
  1022.                 anim_state += "_back";
  1023.             }
  1024.             else
  1025.             {
  1026.                 anim_state += self.left_or_right;
  1027.             }
  1028.             min_chance_at_round = 5;
  1029.             max_chance_at_round = 12;
  1030.             if ( level.round_number >= min_chance_at_round )
  1031.             {
  1032.                 round = min( level.round_number, max_chance_at_round );
  1033.                 range = max_chance_at_round - min_chance_at_round;
  1034.                 chance = ( 100 / range ) * ( round - min_chance_at_round );
  1035.                 if ( randomintrange( 0, 100 ) <= chance )
  1036.                 {
  1037.                     anim_state += "_fast";
  1038.                 }
  1039.             }
  1040.             anim_index = self getanimsubstatefromasd( "zm_zbarrier_climbin_bus", anim_state );
  1041.             enter_anim = self getanimfromasd( "zm_zbarrier_climbin_bus", anim_index );
  1042.             tag_origin = self.attachent gettagorigin( self.attachtag );
  1043.             tag_angles = self.attachent gettagangles( self.attachtag );
  1044.             self.climbing_into_bus = 1;
  1045.             self.entering_bus = 1;
  1046.             self animmode( "noclip" );
  1047.             self animscripted( tag_origin, tag_angles, "zm_zbarrier_climbin_bus", anim_index );
  1048.             self zombieanimnotetrackthink( "climbin_bus_anim" );
  1049.             self animmode( "gravity" );
  1050.             self maps/mp/animscripts/zm_run::needsupdate();
  1051.             if ( !self.isdog )
  1052.             {
  1053.                 self maps/mp/animscripts/zm_run::moverun();
  1054.             }
  1055.         }
  1056.         opening.zombie = undefined;
  1057.         self.opening = undefined;
  1058.         self unlink();
  1059.         self setgoalpos( self.origin );
  1060.     }
  1061.     self reset_attack_spot();
  1062.     self.climbing_into_bus = 0;
  1063.     if ( isDefined( removeafterdone ) && removeafterdone )
  1064.     {
  1065.         self delete();
  1066.         return;
  1067.     }
  1068.     self.inert_delay = undefined;
  1069. }
  1070.  
  1071. zombieattachleftorright( bus )
  1072. {
  1073.     tag = self.opening.bindtag;
  1074.     if ( isDefined( bus.doorsclosed ) && !bus.doorsclosed )
  1075.     {
  1076.         if ( tag != "window_right_1_jnt" || tag == "window_right_2_jnt" && tag == "window_right_3_jnt" )
  1077.         {
  1078.             return "_r";
  1079.         }
  1080.         else
  1081.         {
  1082.             if ( tag == "window_right_4_jnt" )
  1083.             {
  1084.                 return "_l";
  1085.             }
  1086.         }
  1087.     }
  1088.     side = getopeningside( tag );
  1089.     while ( isDefined( side ) )
  1090.     {
  1091.         if ( side == "right" )
  1092.         {
  1093.             openings = bus.openingright;
  1094.         }
  1095.         else
  1096.         {
  1097.             if ( side == "left" )
  1098.             {
  1099.                 openings = bus.openingleft;
  1100.             }
  1101.         }
  1102.         _a1332 = openings;
  1103.         _k1332 = getFirstArrayKey( _a1332 );
  1104.         while ( isDefined( _k1332 ) )
  1105.         {
  1106.             opening = _a1332[ _k1332 ];
  1107.             if ( opening == self.opening )
  1108.             {
  1109.             }
  1110.             else
  1111.             {
  1112.                 if ( isDefined( opening.zombie ) )
  1113.                 {
  1114.                     return opening.zombie.left_or_right;
  1115.                 }
  1116.             }
  1117.             _k1332 = getNextArrayKey( _a1332, _k1332 );
  1118.         }
  1119.     }
  1120.     left_or_right = "_l";
  1121.     if ( randomint( 10 ) > 5 )
  1122.     {
  1123.         left_or_right = "_r";
  1124.     }
  1125.     return left_or_right;
  1126. }
  1127.  
  1128. businitrightandleft()
  1129. {
  1130.     self.openingright = [];
  1131.     self.openingleft = [];
  1132.     _a1367 = self.openings;
  1133.     _k1367 = getFirstArrayKey( _a1367 );
  1134.     while ( isDefined( _k1367 ) )
  1135.     {
  1136.         opening = _a1367[ _k1367 ];
  1137.         side = getopeningside( opening.bindtag );
  1138.         if ( isDefined( side ) )
  1139.         {
  1140.             if ( side == "right" )
  1141.             {
  1142.                 self.openingright[ self.openingright.size ] = opening;
  1143.                 break;
  1144.             }
  1145.             else
  1146.             {
  1147.                 if ( side == "left" )
  1148.                 {
  1149.                     self.openingleft[ self.openingleft.size ] = opening;
  1150.                 }
  1151.             }
  1152.         }
  1153.         _k1367 = getNextArrayKey( _a1367, _k1367 );
  1154.     }
  1155. }
  1156.  
  1157. getopeningside( tag )
  1158. {
  1159.     i = 1;
  1160.     while ( i <= 4 )
  1161.     {
  1162.         window_tag = "window_right_" + i + "_jnt";
  1163.         if ( tag == window_tag )
  1164.         {
  1165.             return "right";
  1166.         }
  1167.         i++;
  1168.     }
  1169.     i = 1;
  1170.     while ( i <= 5 )
  1171.     {
  1172.         window_tag = "window_left_" + i + "_jnt";
  1173.         if ( tag == window_tag )
  1174.         {
  1175.             return "left";
  1176.         }
  1177.         i++;
  1178.     }
  1179.     return undefined;
  1180. }
  1181.  
  1182. zombiegetwindowanimrate()
  1183. {
  1184.     animrate = 1;
  1185.     animrateroundscalar = 0;
  1186.     players = get_players();
  1187.     if ( players.size > 1 )
  1188.     {
  1189.         target_rate = 1,5;
  1190.         target_round = 25;
  1191.         target_num_players = 4;
  1192.         rate = ( target_rate - 1 ) / target_round / target_num_players;
  1193.         animrateroundscalar = rate * players.size;
  1194.         animrate = 1 + ( animrateroundscalar * level.round_number );
  1195.         if ( animrate > target_rate )
  1196.         {
  1197.             animrate = target_rate;
  1198.         }
  1199.     }
  1200.     return animrate;
  1201. }
  1202.  
  1203. zombiedetachfrombus( postfix )
  1204. {
  1205.     if ( !isDefined( self.opening ) )
  1206.     {
  1207.         return;
  1208.     }
  1209.     if ( isDefined( self.opening.zombie ) && self.opening.zombie == self )
  1210.     {
  1211.         self.opening.zombie = undefined;
  1212.     }
  1213.     is_right = 0;
  1214.     if ( postfix == "_r" )
  1215.     {
  1216.         is_right = 1;
  1217.     }
  1218.     bindtag = self.opening.bindtag;
  1219.     side = _getsideofbusopeningison( bindtag );
  1220.     tag_origin = level.the_bus gettagorigin( bindtag );
  1221.     tag_angles = level.the_bus gettagangles( bindtag );
  1222.     self.opening = undefined;
  1223.     self.isonbusroof = 0;
  1224.     self.onbuswindow = undefined;
  1225.     self.ai_state = "zombieDetachFromBus";
  1226.     self.entering_bus = 0;
  1227.     self.isonbus = 0;
  1228.     asd_name = "zm_window_dismount";
  1229.     if ( isDefined( side ) && side == "front" )
  1230.     {
  1231.         asd_name = "zm_front_window_dismount";
  1232.     }
  1233.     animstate = maps/mp/animscripts/zm_utility::append_missing_legs_suffix( asd_name );
  1234.     self animscripted( tag_origin, tag_angles, animstate, is_right );
  1235.     self.dismount_start = 1;
  1236.     self thread dismount_timer();
  1237.     maps/mp/animscripts/zm_shared::donotetracks( "window_dismount_anim" );
  1238.     self unlink();
  1239.     self reset_attack_spot();
  1240.     self.dismount_start = 0;
  1241.     self.forcemovementscriptstate = 0;
  1242.     self thread maps/mp/zombies/_zm_ai_basic::find_flesh();
  1243.     self.dont_throw_gib = undefined;
  1244.     self notify( "detach_on_window" );
  1245. }
  1246.  
  1247. dismount_timer()
  1248. {
  1249.     self endon( "death" );
  1250.     wait 1,5;
  1251.     self.dismount_start = 0;
  1252. }
  1253.  
  1254. zombiegetcymbalmonkey()
  1255. {
  1256.     if ( isDefined( self.monkey_time ) && getTime() < self.monkey_time )
  1257.     {
  1258.         return self.monkey;
  1259.     }
  1260.     poi = undefined;
  1261.     if ( level.cymbal_monkeys.size > 0 )
  1262.     {
  1263.         poi = self get_zombie_point_of_interest( self.origin, level.cymbal_monkeys );
  1264.     }
  1265.     if ( isDefined( poi ) )
  1266.     {
  1267.         self.monkey = poi[ 1 ];
  1268.         self.monkey_time = getTime() + 250;
  1269.         return poi[ 1 ];
  1270.     }
  1271.     return undefined;
  1272. }
  1273.  
  1274. zombieshoulddetachfromwindow()
  1275. {
  1276.     monkey = self zombiegetcymbalmonkey();
  1277.     if ( isDefined( monkey ) )
  1278.     {
  1279.         if ( monkey maps/mp/zm_transit_bus::entity_is_on_bus( 1 ) )
  1280.         {
  1281.             return 0;
  1282.         }
  1283.         else
  1284.         {
  1285.             return 1;
  1286.         }
  1287.     }
  1288.     enemy = self.favoriteenemy;
  1289.     if ( isDefined( enemy ) && !self.favoriteenemy.isonbus )
  1290.     {
  1291.         return 1;
  1292.     }
  1293.     return 0;
  1294. }
  1295.  
  1296. zombiecanjumponroof( opening )
  1297. {
  1298.     if ( level.the_bus.numplayersonroof == 0 )
  1299.     {
  1300.         if ( all_chunks_destroyed( opening ) )
  1301.         {
  1302.             return 0;
  1303.         }
  1304.         if ( isDefined( level.bus_zombie_on_roof ) )
  1305.         {
  1306.             return 0;
  1307.         }
  1308.         if ( level.bus_roof_next_time > getTime() )
  1309.         {
  1310.             return 0;
  1311.         }
  1312.     }
  1313.     percentchance = 0;
  1314.     if ( level.round_number <= 5 )
  1315.     {
  1316.         percentchance = 5;
  1317.     }
  1318.     else if ( level.round_number <= 10 )
  1319.     {
  1320.         percentchance = 20;
  1321.     }
  1322.     else if ( level.round_number <= 20 )
  1323.     {
  1324.         percentchance = 30;
  1325.     }
  1326.     else if ( level.round_number <= 25 )
  1327.     {
  1328.         percentchance = 40;
  1329.     }
  1330.     else
  1331.     {
  1332.         percentchance = 50;
  1333.     }
  1334.     percentofplayersonroof = 1;
  1335.     if ( level.the_bus.numplayersnear > 0 )
  1336.     {
  1337.         percentofplayersonroof = level.the_bus.numplayersonroof / level.the_bus.numplayersnear;
  1338.     }
  1339.     percentofplayersonroof *= 100;
  1340.     if ( percentchance < percentofplayersonroof )
  1341.     {
  1342.         percentchance = percentofplayersonroof;
  1343.     }
  1344.     if ( randomint( 100 ) < percentchance )
  1345.     {
  1346.         return 1;
  1347.     }
  1348.     return 0;
  1349. }
  1350.  
  1351. zombiesetnexttimetojumponroof()
  1352. {
  1353.     level.bus_roof_next_time = getTime() + level.bus_roof_min_interval_time + randomint( level.bus_roof_max_interval_time - level.bus_roof_min_interval_time );
  1354. }
  1355.  
  1356. zombiejumponroof( thebus, opening, removeafterdone, postfix )
  1357. {
  1358.     level.bus_zombie_on_roof = self;
  1359.     self.climbing_onto_bus = 1;
  1360.     self.entering_bus = 1;
  1361.     self animscripted( self.opening.zbarrier.origin, self.opening.zbarrier.angles, "zm_zbarrier_window_climbup", "window_climbup" + postfix );
  1362.     self zombieanimnotetrackthink( "bus_window_climbup" );
  1363.     play_sound_at_pos( "grab_metal_bar", self.origin );
  1364.     opening.zombie = undefined;
  1365.     self.opening = undefined;
  1366.     self unlink();
  1367.     self setgoalpos( self.origin );
  1368.     self.climbing_onto_bus = 0;
  1369.     if ( level.the_bus.numplayersonroof > 0 )
  1370.     {
  1371.         level.bus_zombie_on_roof = undefined;
  1372.     }
  1373. }
  1374.  
  1375. bussetupladder()
  1376. {
  1377.     trigger = getent( "bus_ladder_trigger", "targetname" );
  1378.     if ( !isDefined( trigger ) )
  1379.     {
  1380.         return;
  1381.     }
  1382.     trigger enablelinkto();
  1383.     trigger linkto( level.the_bus );
  1384.     trigger setmovingplatformenabled( 1 );
  1385.     trigger setinvisibletoall();
  1386.     mantlebrush = getentarray( "ladder_mantle", "targetname" );
  1387.     while ( isDefined( mantlebrush ) && mantlebrush.size > 0 )
  1388.     {
  1389.         i = 0;
  1390.         while ( i < mantlebrush.size )
  1391.         {
  1392.             self thread busdeferredinitladdermantle( mantlebrush[ i ] );
  1393.             i++;
  1394.         }
  1395.     }
  1396.     thread busladderthink();
  1397. }
  1398.  
  1399. busdeferredinitladdermantle( mantle )
  1400. {
  1401.     origin = self worldtolocalcoords( mantle.origin );
  1402.     mantle linkto( self, "", origin, ( 0, -1, 0 ) );
  1403.     mantle setmovingplatformenabled( 1 );
  1404.     wait_for_buildable( "busladder" );
  1405.     mantle delete();
  1406. }
  1407.  
  1408. busladderthink()
  1409. {
  1410.     origin = level.the_bus gettagorigin( "tag_ladder_attach" );
  1411.     origin += ( 10, 3, -30 );
  1412.     angles = level.the_bus gettagangles( "tag_ladder_attach" );
  1413.     angles += vectorScale( ( 0, -1, 0 ), 90 );
  1414.     level.the_bus.ladder = spawn( "script_model", origin );
  1415.     level.the_bus.ladder.angles = angles;
  1416.     level.the_bus.ladder setmodel( "com_stepladder_large_closed" );
  1417.     level.the_bus.ladder notsolid();
  1418.     level.the_bus.ladder linkto( level.the_bus, "tag_ladder_attach" );
  1419.     level.the_bus.ladder setmovingplatformenabled( 1 );
  1420.     level.the_bus.ladder hide();
  1421.     player = wait_for_buildable( "busladder" );
  1422.     flag_set( "ladder_attached" );
  1423.     level.the_bus.ladder show();
  1424.     player maps/mp/zombies/_zm_buildables::track_placed_buildables( "busladder" );
  1425. }
  1426.  
  1427. bussetuproofopening()
  1428. {
  1429.     level.bus_roof_open = 0;
  1430.     level.bus_zombie_on_roof = undefined;
  1431.     level.bus_roof_next_time = 0;
  1432.     level.bus_roof_min_interval_time = 10000;
  1433.     level.bus_roof_max_interval_time = 20000;
  1434.     trigger = getent( "bus_hatch_bottom_trigger", "targetname" );
  1435.     if ( !isDefined( trigger ) )
  1436.     {
  1437.         return;
  1438.     }
  1439.     trigger enablelinkto();
  1440.     trigger linkto( level.the_bus );
  1441.     trigger setmovingplatformenabled( 1 );
  1442.     self thread bus_hatch_wait();
  1443.     self thread bus_hatch_tearin_wait();
  1444.     clipbrush = getentarray( "hatch_clip", "targetname" );
  1445.     while ( isDefined( clipbrush ) && clipbrush.size > 0 )
  1446.     {
  1447.         i = 0;
  1448.         while ( i < clipbrush.size )
  1449.         {
  1450.             self thread businithatchclip( clipbrush[ i ] );
  1451.             i++;
  1452.         }
  1453.     }
  1454.     mantlebrush = getentarray( "hatch_mantle", "targetname" );
  1455.     while ( isDefined( mantlebrush ) && mantlebrush.size > 0 )
  1456.     {
  1457.         i = 0;
  1458.         while ( i < mantlebrush.size )
  1459.         {
  1460.             self thread busdeferredinithatchmantle( mantlebrush[ i ] );
  1461.             i++;
  1462.         }
  1463.     }
  1464.     hatch_location = spawn( "script_origin", level.the_bus localtoworldcoords( ( 227, -1,7, 48 ) ) );
  1465.     hatch_location enablelinkto();
  1466.     hatch_location linkto( level.the_bus );
  1467.     hatch_location setmovingplatformenabled( 1 );
  1468.     level.the_bus.hatch_location = hatch_location;
  1469. /#
  1470.     adddebugcommand( "devgui_cmd "Zombies:1/Bus:14/Hatch:4/Allow Traverse:1" "zombie_devgui hatch_available"\n" );
  1471.     self thread wait_open_sesame();
  1472. #/
  1473. }
  1474.  
  1475. wait_open_sesame()
  1476. {
  1477.     level waittill( "open_sesame" );
  1478.     self notify( "hatch_mantle_allowed" );
  1479.     if ( isDefined( level.bus_tearin_roof ) )
  1480.     {
  1481.         level.bus_tearin_roof hide();
  1482.     }
  1483.     level.the_bus showpart( "tag_hatch_attach_ladder" );
  1484.     level.the_bus hidepart( "tag_hatch_pristine" );
  1485.     level.the_bus hidepart( "tag_hatch_damaged" );
  1486.     level.bus_roof_open = 1;
  1487.     level.bus_roof_tearing = 0;
  1488. }
  1489.  
  1490. bus_hatch_wait()
  1491. {
  1492.     level.the_bus hidepart( "tag_hatch_attach_ladder" );
  1493.     player = wait_for_buildable( "bushatch" );
  1494.     flag_set( "hatch_attached" );
  1495.     level.the_bus showpart( "tag_hatch_attach_ladder" );
  1496.     level.the_bus hidepart( "tag_hatch_pristine" );
  1497.     level.the_bus hidepart( "tag_hatch_damaged" );
  1498.     level.bus_roof_open = 1;
  1499.     level.bus_roof_tearing = 0;
  1500.     self notify( "hatch_mantle_allowed" );
  1501.     player maps/mp/zombies/_zm_buildables::track_placed_buildables( "bushatch" );
  1502. }
  1503.  
  1504. bus_hatch_tearin_wait()
  1505. {
  1506.     self endon( "hatch_mantle_allowed" );
  1507.     level.the_bus hidepart( "tag_hatch_damaged" );
  1508.     self waittill( "hatch_ripped_open" );
  1509.     level.the_bus hidepart( "tag_hatch_pristine" );
  1510.     level.the_bus showpart( "tag_hatch_damaged" );
  1511.     self notify( "hatch_drop_allowed" );
  1512.     playfxontag( level._effect[ "bus_hatch_bust" ], self, "tag_headlights" );
  1513. }
  1514.  
  1515. businithatchclip( clip )
  1516. {
  1517.     origin = self worldtolocalcoords( clip.origin );
  1518.     clip linkto( self, "", origin, ( 0, -1, 0 ) );
  1519.     clip setmovingplatformenabled( 1 );
  1520.     self waittill_any( "hatch_mantle_allowed", "hatch_drop_allowed" );
  1521.     clip delete();
  1522. }
  1523.  
  1524. busdeferredinithatchmantle( mantle )
  1525. {
  1526.     origin = self worldtolocalcoords( mantle.origin );
  1527.     mantle.origin = vectorScale( ( 0, -1, 0 ), 100 );
  1528.     self waittill( "hatch_mantle_allowed" );
  1529.     mantle linkto( self, "", origin, ( 0, -1, 0 ) );
  1530.     mantle setmovingplatformenabled( 1 );
  1531. }
  1532.  
  1533. zombieonbusenemy()
  1534. {
  1535.     new_enemy = undefined;
  1536.     if ( isDefined( level.the_bus.bus_riders_alive ) && level.the_bus.bus_riders_alive.size > 0 )
  1537.     {
  1538.         new_enemy = getclosest( self.origin, level.the_bus.bus_riders_alive );
  1539.     }
  1540.     if ( isDefined( new_enemy ) && isDefined( self.favoriteenemy ) && isDefined( new_enemy ) && self.favoriteenemy != new_enemy )
  1541.     {
  1542.         if ( isDefined( self.favoriteenemy.isonbus ) && !self.favoriteenemy.isonbus )
  1543.         {
  1544.             self.favoriteenemy = new_enemy;
  1545.             return;
  1546.         }
  1547.         else
  1548.         {
  1549.             if ( self.isonbusroof == new_enemy.isonbusroof )
  1550.             {
  1551.                 self.favoriteenemy = new_enemy;
  1552.             }
  1553.         }
  1554.     }
  1555. }
  1556.  
  1557. zombiemoveonbus()
  1558. {
  1559.     self endon( "death" );
  1560.     self endon( "removed" );
  1561.     level endon( "intermission" );
  1562.     self notify( "endOnBus" );
  1563.     self endon( "endOnBus" );
  1564.     self notify( "stop_find_flesh" );
  1565.     self notify( "zombie_acquire_enemy" );
  1566.     wait 0,05;
  1567.     self orientmode( "face enemy" );
  1568.     self.goalradius = 32;
  1569.     self.forcemovementscriptstate = 1;
  1570.     self.ai_state = "zombieMoveOnBus";
  1571.     self maps/mp/zombies/_zm_spawner::zombie_history( "zombieMoveOnBus " + getTime() );
  1572.     bus_nodes = getnodearray( "the_bus", "target" );
  1573.     while ( 1 )
  1574.     {
  1575.         self zombieonbusenemy();
  1576.         if ( !isDefined( self.favoriteenemy ) )
  1577.         {
  1578.             break;
  1579.         }
  1580.         else if ( isDefined( self.isonbus ) )
  1581.         {
  1582.             self_on_bus = self.isonbus;
  1583.         }
  1584.         if ( isDefined( self.isonbusroof ) )
  1585.         {
  1586.             self_is_on_bus_roof = self.isonbusroof;
  1587.         }
  1588.         if ( !self_on_bus )
  1589.         {
  1590.             break;
  1591.         }
  1592.         else
  1593.         {
  1594.             if ( isDefined( self.is_inert ) && self.is_inert )
  1595.             {
  1596.                 self.ignoreall = 1;
  1597.                 while ( isDefined( self.is_inert ) && self.is_inert )
  1598.                 {
  1599.                     wait 0,1;
  1600.                 }
  1601.                 self.ignoreall = 0;
  1602.             }
  1603.             poi_override = 0;
  1604.             if ( self.forcemovementscriptstate )
  1605.             {
  1606.                 monkey = self zombiegetcymbalmonkey();
  1607.                 if ( isDefined( monkey ) )
  1608.                 {
  1609.                     poi_override = 1;
  1610.                     self.ignoreall = 1;
  1611.                     self animmode( "normal" );
  1612.                     self orientmode( "face motion" );
  1613.                     if ( !level.the_bus maps/mp/zm_transit_bus::busispointinside( monkey.origin ) )
  1614.                     {
  1615.                         self zombiewalktoexit();
  1616.                     }
  1617.                     else
  1618.                     {
  1619.                         self setgoalpos( monkey.origin );
  1620.                     }
  1621.                     break;
  1622.                 }
  1623.                 else
  1624.                 {
  1625.                     self.ignoreall = 0;
  1626.                     self animmode( "gravity" );
  1627.                     self orientmode( "face enemy" );
  1628.                     while ( isDefined( level.the_bus.doorsclosed ) && !level.the_bus.doorsclosed )
  1629.                     {
  1630.                         doorstrigger = getentarray( "bus_door_trigger", "targetname" );
  1631.                         _a1984 = doorstrigger;
  1632.                         _k1984 = getFirstArrayKey( _a1984 );
  1633.                         while ( isDefined( _k1984 ) )
  1634.                         {
  1635.                             trigger = _a1984[ _k1984 ];
  1636.                             if ( self istouching( trigger ) )
  1637.                             {
  1638.                                 self orientmode( "face motion" );
  1639.                                 break;
  1640.                             }
  1641.                             else
  1642.                             {
  1643.                                 _k1984 = getNextArrayKey( _a1984, _k1984 );
  1644.                             }
  1645.                         }
  1646.                     }
  1647.                     dist_sq = distancesquared( self.favoriteenemy.origin, bus_nodes[ 0 ].origin );
  1648.                     goal_node = bus_nodes[ 0 ];
  1649.                     i = 1;
  1650.                     while ( i < bus_nodes.size )
  1651.                     {
  1652.                         bus_sq = distancesquared( self.favoriteenemy.origin, bus_nodes[ i ].origin );
  1653.                         if ( bus_sq < dist_sq )
  1654.                         {
  1655.                             dist_sq = bus_sq;
  1656.                             goal_node = bus_nodes[ i ];
  1657.                         }
  1658.                         i++;
  1659.                     }
  1660.                     if ( self_is_on_bus_roof )
  1661.                     {
  1662.                         self.goalradius = 16;
  1663.                     }
  1664.                     else
  1665.                     {
  1666.                         self.goalradius = 32;
  1667.                     }
  1668.                     self setgoalnode( goal_node );
  1669.                 }
  1670.             }
  1671.             enemy = self.favoriteenemy;
  1672.             if ( isDefined( enemy ) )
  1673.             {
  1674.                 enemy_on_bus = enemy.isonbus;
  1675.             }
  1676.             if ( enemy_on_bus && isDefined( enemy.isonbusroof ) )
  1677.             {
  1678.                 enemy_on_roof = enemy.isonbusroof;
  1679.             }
  1680.             if ( !enemy_on_bus && !self_is_on_bus_roof )
  1681.             {
  1682.                 monkey = self zombiegetcymbalmonkey();
  1683.                 if ( !isDefined( monkey ) || !monkey maps/mp/zm_transit_bus::entity_is_on_bus( 1 ) )
  1684.                 {
  1685.                     self thread zombieexitbus();
  1686.                     return;
  1687.                 }
  1688.             }
  1689.             else
  1690.             {
  1691.                 if ( enemy_on_bus && self_is_on_bus_roof != enemy_on_roof )
  1692.                 {
  1693.                     self thread zombieheighttraverse();
  1694.                     return;
  1695.                     break;
  1696.                 }
  1697.                 else
  1698.                 {
  1699.                     if ( self_is_on_bus_roof && !enemy_on_bus )
  1700.                     {
  1701.                         self thread zombiejumpoffroof();
  1702.                         return;
  1703.                         break;
  1704.                     }
  1705.                     else
  1706.                     {
  1707.                         if ( isDefined( poi_override ) && !poi_override )
  1708.                         {
  1709.                             disttoenemy = distance2d( self.origin, self.favoriteenemy.origin );
  1710.                             shouldbeinforcemovement = disttoenemy > 32;
  1711.                             if ( !shouldbeinforcemovement && self.forcemovementscriptstate )
  1712.                             {
  1713.                                 self.forcemovementscriptstate = 0;
  1714.                                 self animmode( "normal" );
  1715.                                 self orientmode( "face enemy" );
  1716.                             }
  1717.                             if ( shouldbeinforcemovement && !self.forcemovementscriptstate )
  1718.                             {
  1719.                                 self.forcemovementscriptstate = 1;
  1720.                                 self animmode( "gravity" );
  1721.                                 self orientmode( "face enemy" );
  1722.                             }
  1723.                         }
  1724.                     }
  1725.                 }
  1726.             }
  1727.             wait 0,1;
  1728.         }
  1729.     }
  1730.     self animmode( "normal" );
  1731.     self orientmode( "face enemy" );
  1732.     self.forcemovementscriptstate = 0;
  1733.     self thread maps/mp/zombies/_zm_ai_basic::find_flesh();
  1734. }
  1735.  
  1736. zombiewalktoexit()
  1737. {
  1738.     self endon( "death" );
  1739.     bus = level.the_bus;
  1740.     self.walk_to_exit = 1;
  1741.     check_pos = self.origin;
  1742.     enemy = self.favoriteenemy;
  1743.     if ( isDefined( enemy ) )
  1744.     {
  1745.         check_pos = enemy.origin;
  1746.     }
  1747.     if ( !level.the_bus.doorsclosed )
  1748.     {
  1749.         front_dist = distance2dsquared( check_pos, level.the_bus.front_door.origin );
  1750.         back_dist = distance2dsquared( check_pos, level.the_bus.back_door.origin );
  1751.         link_one = undefined;
  1752.         link_two = undefined;
  1753.         door_node = undefined;
  1754.         door_dist = undefined;
  1755.         if ( front_dist < back_dist )
  1756.         {
  1757.             door_dist = distance2dsquared( self.origin, level.the_bus.front_door.origin );
  1758.             if ( door_dist < 256 )
  1759.             {
  1760.                 door_node = level.the_bus.front_door;
  1761.             }
  1762.         }
  1763.         else
  1764.         {
  1765.             door_dist = distance2dsquared( self.origin, level.the_bus.back_door.origin );
  1766.             if ( door_dist < 256 )
  1767.             {
  1768.                 door_node = level.the_bus.back_door;
  1769.             }
  1770.         }
  1771.         if ( isDefined( door_node ) )
  1772.         {
  1773.             if ( isDefined( door_node.links ) )
  1774.             {
  1775.                 if ( isDefined( door_node.links[ 0 ] ) )
  1776.                 {
  1777.                     link_one = door_node.links[ 0 ];
  1778.                 }
  1779.                 if ( isDefined( door_node.links[ 1 ] ) )
  1780.                 {
  1781.                     link_two = door_node.links[ 1 ];
  1782.                 }
  1783.             }
  1784.             link_goal = undefined;
  1785.             if ( isDefined( link_one ) )
  1786.             {
  1787.                 link_goal = link_one;
  1788.             }
  1789.             if ( isDefined( link_one ) && isDefined( link_two ) )
  1790.             {
  1791.                 link_one_dist = distance2dsquared( check_pos, link_one.origin );
  1792.                 link_two_dist = distance2dsquared( check_pos, link_two.origin );
  1793.                 link_goal = link_one;
  1794.                 if ( link_two_dist < link_one_dist )
  1795.                 {
  1796.                     link_goal = link_two;
  1797.                 }
  1798.             }
  1799.             if ( isDefined( link_goal ) )
  1800.             {
  1801.                 self setgoalnode( link_goal );
  1802.                 return;
  1803.             }
  1804.         }
  1805.         door_goal = level.the_bus.front_door;
  1806.         if ( back_dist < front_dist )
  1807.         {
  1808.             door_goal = level.the_bus.back_door;
  1809.         }
  1810.         self setgoalnode( door_goal );
  1811.     }
  1812.     else
  1813.     {
  1814.         dist_f = distancesquared( check_pos, bus.front_door_inside.origin );
  1815.         dist_bl = distancesquared( check_pos, bus.exit_back_l.origin );
  1816.         dist_br = distancesquared( check_pos, bus.exit_back_r.origin );
  1817.         goal_node = bus.front_door_inside;
  1818.         if ( dist_bl < dist_br )
  1819.         {
  1820.             if ( dist_bl < dist_f )
  1821.             {
  1822.                 goal_node = bus.exit_back_l;
  1823.             }
  1824.         }
  1825.         else
  1826.         {
  1827.             if ( dist_br < dist_f )
  1828.             {
  1829.                 goal_node = bus.exit_back_r;
  1830.             }
  1831.         }
  1832.         self setgoalnode( goal_node );
  1833.     }
  1834. }
  1835.  
  1836. zombiewindowtoroof()
  1837. {
  1838.     self endon( "death" );
  1839.     self animmode( "normal" );
  1840.     self orientmode( "face motion" );
  1841.     self.ai_state = "zombieWindowToRoof";
  1842.     self maps/mp/zombies/_zm_spawner::zombie_history( "zombieWindowToRoof " + getTime() );
  1843.     while ( 1 )
  1844.     {
  1845.         self zombiewalktowindow();
  1846.         if ( isDefined( self.exiting_window ) && self.exiting_window )
  1847.         {
  1848.             break;
  1849.         }
  1850.         else
  1851.         {
  1852.             if ( isDefined( self.isonbus ) || !self.isonbus && isDefined( self.ai_state == "find_flesh" ) && self.ai_state == "find_flesh" )
  1853.             {
  1854.                 return;
  1855.             }
  1856.             wait 0,1;
  1857.         }
  1858.     }
  1859.     while ( isDefined( self.exiting_window ) && self.exiting_window )
  1860.     {
  1861.         wait 0,1;
  1862.     }
  1863. }
  1864.  
  1865. zombiewalktowindow()
  1866. {
  1867.     self endon( "death" );
  1868.     bus = level.the_bus;
  1869.     self.walk_to_exit = 1;
  1870.     check_pos = self.origin;
  1871.     dist_f = distancesquared( check_pos, bus.front_door_inside.origin );
  1872.     dist_bl = distancesquared( check_pos, bus.exit_back_l.origin );
  1873.     goal_node = bus.front_door_inside;
  1874.     if ( dist_bl < dist_f )
  1875.     {
  1876.         goal_node = bus.exit_back_l;
  1877.     }
  1878.     self setgoalnode( goal_node );
  1879. }
  1880.  
  1881. zombieattackplayerclinging( player )
  1882. {
  1883.     self endon( "death" );
  1884.     level endon( "intermission" );
  1885.     self.goalradius = 15;
  1886.     enemy_clinging = 1;
  1887.     while ( enemy_clinging )
  1888.     {
  1889.         best_attack_pos = maps/mp/zm_transit_cling::_getbusattackposition( player );
  1890.         dist_from_pos2 = distance2dsquared( best_attack_pos, self.origin );
  1891.         enemy_origin = player _playergetorigin();
  1892.         direction = enemy_origin - self.origin;
  1893.         direction_angles = vectorToAngle( direction );
  1894.         direction_angles = ( direction_angles[ 0 ], direction_angles[ 1 ], 0 );
  1895.         if ( dist_from_pos2 > 400 )
  1896.         {
  1897.             self orientmode( "face point", best_attack_pos );
  1898.             self setgoalpos( best_attack_pos );
  1899.         }
  1900.         else
  1901.         {
  1902.             self zombiescriptedattack( player, direction_angles, ::zombiedamageplayercling );
  1903.         }
  1904.         enemy_clinging = player maps/mp/zm_transit_cling::playerisclingingtobus();
  1905.         wait 0,1;
  1906.     }
  1907.     self.goalradius = 32;
  1908. }
  1909.  
  1910. zombieattackplayeronturret( player )
  1911. {
  1912.     self endon( "death" );
  1913.     level endon( "intermission" );
  1914.     enemy_on_turret = 1;
  1915.     while ( enemy_on_turret )
  1916.     {
  1917.         enemy_origin = self.favoriteenemy _playergetorigin();
  1918.         dist_from_turret2 = distance2dsquared( enemy_origin, self.origin );
  1919.         direction = enemy_origin - self.origin;
  1920.         direction_angles = vectorToAngle( direction );
  1921.         direction_angles = ( direction_angles[ 0 ], direction_angles[ 1 ], 0 );
  1922.         if ( dist_from_turret2 > 1024 )
  1923.         {
  1924.             self setgoalpos( enemy_origin, direction_angles );
  1925.         }
  1926.         else
  1927.         {
  1928.             self zombiescriptedattack( player, direction_angles, ::zombiedamageplayerturret );
  1929.         }
  1930.         wait 0,1;
  1931.         if ( isDefined( self.favoriteenemy.onbusturret ) && isDefined( self.favoriteenemy.busturret ) )
  1932.         {
  1933.             enemy_on_turret = self.favoriteenemy.onbusturret;
  1934.         }
  1935.     }
  1936. }
  1937.  
  1938. zombiescriptedattack( player, direction_angles, damage_func )
  1939. {
  1940.     self orientmode( "face angle", direction_angles[ 1 ] );
  1941.     zombie_attack_anim = self zombiepickunmovingattackanim();
  1942.     self thread maps/mp/zombies/_zm_audio::do_zombies_playvocals( "attack", self.animname );
  1943.     while ( 1 )
  1944.     {
  1945.         self waittill( "meleeanim", note );
  1946.         if ( note == "end" || note == "stop" )
  1947.         {
  1948.             return;
  1949.         }
  1950.         else if ( note == "fire" )
  1951.         {
  1952.             if ( isDefined( damage_func ) )
  1953.             {
  1954.                 [[ damage_func ]]( player );
  1955.             }
  1956.         }
  1957.     }
  1958. }
  1959.  
  1960. zombiedamageplayerturret( player )
  1961. {
  1962.     if ( player.onbusturret )
  1963.     {
  1964.         player dodamage( self.meleedamage, self.origin, self );
  1965.     }
  1966. }
  1967.  
  1968. zombiedamageplayercling( player )
  1969. {
  1970.     if ( player maps/mp/zm_transit_cling::playerisclingingtobus() )
  1971.     {
  1972.         player dodamage( self.meleedamage, self.origin, self );
  1973.     }
  1974. }
  1975.  
  1976. _playergetorigin()
  1977. {
  1978.     if ( isDefined( self.onbusturret ) && isDefined( self.busturret ) && self.onbusturret )
  1979.     {
  1980.         turret = self.busturret;
  1981.         turret_exit = getent( turret.target, "targetname" );
  1982.         return turret_exit.origin;
  1983.     }
  1984.     return self.origin;
  1985. }
  1986.  
  1987. zombiepickunmovingattackanim()
  1988. {
  1989.     melee_anim = undefined;
  1990.     if ( self.has_legs )
  1991.     {
  1992.         rand_num = randomint( 4 );
  1993.         melee_anim = level._zombie_melee[ self.animname ][ rand_num ];
  1994.     }
  1995.     else if ( self.a.gib_ref == "no_legs" )
  1996.     {
  1997.         melee_anim = random( level._zombie_stumpy_melee[ self.animname ] );
  1998.     }
  1999.     else
  2000.     {
  2001.         melee_anim = level._zombie_melee_crawl[ self.animname ][ 0 ];
  2002.     }
  2003.     return melee_anim;
  2004. }
  2005.  
  2006. zombieexitbus()
  2007. {
  2008.     self endon( "death" );
  2009.     self endon( "stop_zombieExitBus" );
  2010.     level endon( "intermission" );
  2011.     self animmode( "normal" );
  2012.     self orientmode( "face motion" );
  2013.     self maps/mp/zombies/_zm_spawner::zombie_history( "zombieExitBus " + getTime() );
  2014.     while ( 1 )
  2015.     {
  2016.         if ( isDefined( self.exiting_window ) && self.exiting_window )
  2017.         {
  2018.             break;
  2019.         }
  2020.         else
  2021.         {
  2022.             if ( isDefined( self.isonbus ) && !self.isonbus )
  2023.             {
  2024.                 self.walk_to_exit = 0;
  2025.                 return;
  2026.             }
  2027.             if ( isDefined( self.solo_revive_exit ) && !self.solo_revive_exit )
  2028.             {
  2029.                 monkey = undefined;
  2030.                 if ( isDefined( self.enemyoverride ) )
  2031.                 {
  2032.                     monkey = self.enemyoverride[ 1 ];
  2033.                 }
  2034.                 ignore_enemy = 0;
  2035.                 if ( isDefined( monkey ) && !monkey maps/mp/zm_transit_bus::entity_is_on_bus( 1 ) )
  2036.                 {
  2037.                     ignore_enemy = 1;
  2038.                 }
  2039.                 if ( !ignore_enemy )
  2040.                 {
  2041.                     enemy = self.favoriteenemy;
  2042.                     if ( isDefined( enemy ) )
  2043.                     {
  2044.                         if ( enemy.isonbus )
  2045.                         {
  2046.                             self.walk_to_exit = 0;
  2047.                             self thread zombiemoveonbus();
  2048.                             return;
  2049.                         }
  2050.                     }
  2051.                 }
  2052.             }
  2053.             self zombiewalktoexit();
  2054.             wait 0,1;
  2055.         }
  2056.     }
  2057.     while ( isDefined( self.exiting_window ) && self.exiting_window )
  2058.     {
  2059.         wait 0,1;
  2060.     }
  2061.     self.dont_throw_gib = undefined;
  2062.     self orientmode( "face enemy" );
  2063.     self.forcemovementscriptstate = 0;
  2064.     self thread maps/mp/zombies/_zm_ai_basic::find_flesh();
  2065. }
  2066.  
  2067. zombiejumpoffroof()
  2068. {
  2069.     self endon( "death" );
  2070.     self endon( "removed" );
  2071.     level endon( "intermission" );
  2072.     self animmode( "gravity" );
  2073.     self orientmode( "face goal" );
  2074.     self maps/mp/zombies/_zm_spawner::zombie_history( "zombieJumpOffRoof " + getTime() );
  2075.     while ( 1 )
  2076.     {
  2077.         enemy = self.favoriteenemy;
  2078.         if ( isDefined( enemy ) )
  2079.         {
  2080.             enemy_on_bus = enemy.isonbus;
  2081.         }
  2082.         enemiesonbus = level.the_bus.numaliveplayersridingbus > 0;
  2083.         if ( isDefined( self.isonbus ) )
  2084.         {
  2085.             selfisonbus = self.isonbus;
  2086.         }
  2087.         if ( enemy_on_bus )
  2088.         {
  2089.             self thread zombiemoveonbus();
  2090.             return;
  2091.         }
  2092.         if ( !selfisonbus )
  2093.         {
  2094.             return;
  2095.         }
  2096.         else closest_jump_tag = self zombiegetclosestroofjumptag();
  2097.         goal_pos = level.the_bus gettagorigin( closest_jump_tag );
  2098.         if ( distance2dsquared( self.origin, goal_pos ) <= 1024 )
  2099.         {
  2100.             self.attachent = level.the_bus;
  2101.             self.attachtag = closest_jump_tag;
  2102.             self linkto( self.attachent, self.attachtag, ( 0, -1, 0 ), ( 0, -1, 0 ) );
  2103.             wait 0,1;
  2104.             self zombieplayattachedanim( "jump_down_127" );
  2105.             self setgoalpos( self.origin );
  2106.             self unlink();
  2107.             return;
  2108.         }
  2109.         else
  2110.         {
  2111.             self setgoalpos( goal_pos );
  2112.             self orientmode( "face point", goal_pos );
  2113.             wait 0,1;
  2114.         }
  2115.     }
  2116. }
  2117.  
  2118. zombieheighttraverse()
  2119. {
  2120.     level endon( "intermission" );
  2121.     self endon( "death" );
  2122.     self endon( "removed" );
  2123.     self endon( "zombieHeightTraverseStop" );
  2124.     self animmode( "gravity" );
  2125.     self orientmode( "face goal" );
  2126.     self.zombie_ladder_stage = 1;
  2127.     self.goal_local_offset = level.the_bus.ladder_local_offset;
  2128.     self.goal_local_offset = ( self.goal_local_offset[ 0 ], self.goal_local_offset[ 1 ], level.the_bus.floor );
  2129.     self.goal_local_angles = vectorScale( ( 0, -1, 0 ), 90 );
  2130.     self maps/mp/zombies/_zm_spawner::zombie_history( "zombieHeightTraverse " + getTime() );
  2131.     if ( !isDefined( self.isonbusroof ) || !self.isonbusroof )
  2132.     {
  2133.         bus = level.the_bus;
  2134.         hatch_dist = distancesquared( self.origin, bus.hatch_location.origin );
  2135.         front_dist = distancesquared( self.origin, bus.front_door_inside.origin );
  2136.         back_dist = distancesquared( self.origin, bus.exit_back_l.origin );
  2137.         closer_to_hatch = 1;
  2138.         if ( front_dist < hatch_dist || back_dist < hatch_dist )
  2139.         {
  2140.             closer_to_hatch = 0;
  2141.         }
  2142.         if ( isDefined( level.bus_roof_open ) && level.bus_roof_open && isDefined( level.bus_roof_tearing ) || level.bus_roof_tearing && !closer_to_hatch )
  2143.         {
  2144.             self zombiewindowtoroof();
  2145.         }
  2146.         else
  2147.         {
  2148.             if ( self zombiepathtoladder() )
  2149.             {
  2150.                 self zombieclimbtoroof();
  2151.             }
  2152.         }
  2153.     }
  2154.     else
  2155.     {
  2156.         if ( self zombiepathtoroofopening() )
  2157.         {
  2158.             self zombiejumpdownhatch();
  2159.         }
  2160.         else
  2161.         {
  2162.             enemy = self.favoriteenemy;
  2163.             if ( isDefined( enemy ) && isDefined( enemy.isonbus ) && !enemy.isonbus )
  2164.             {
  2165.                 self thread zombiejumpoffroof();
  2166.                 return;
  2167.             }
  2168.         }
  2169.     }
  2170.     if ( isDefined( self.isonbus ) && self.isonbus )
  2171.     {
  2172.         self thread zombiemoveonbus();
  2173.     }
  2174. }
  2175.  
  2176. zombiepathtoladder()
  2177. {
  2178.     self.goalradius = 2;
  2179.     while ( isDefined( self.enemy ) && self.enemy.isonbusroof )
  2180.     {
  2181.         if ( !self.isonbus )
  2182.         {
  2183.             self.goalradius = 32;
  2184.             return 0;
  2185.         }
  2186.         goal_dir = anglesToForward( level.the_bus.angles + self.goal_local_angles );
  2187.         goal_pos = level.the_bus.hatch_location.origin;
  2188.         if ( distancesquared( self.origin, goal_pos ) <= 1600 )
  2189.         {
  2190.             self.goalradius = 32;
  2191.             self.ladder_pos = goal_pos;
  2192.             return 1;
  2193.         }
  2194.         self setgoalpos( goal_pos );
  2195.         self orientmode( "face point", goal_pos );
  2196.         wait 0,1;
  2197.     }
  2198.     self.goalradius = 32;
  2199.     return 0;
  2200. }
  2201.  
  2202. zombiepathtoroofopening()
  2203. {
  2204.     self.goalradius = 2;
  2205.     goal_tag = self zombiegetclosestroofopeningjumptag();
  2206.     while ( 1 )
  2207.     {
  2208.         self zombieonbusenemy();
  2209.         enemy = self.favoriteenemy;
  2210.         if ( isDefined( enemy ) && isDefined( enemy.isonbusroof ) && enemy.isonbusroof )
  2211.         {
  2212.             break;
  2213.         }
  2214.         else
  2215.         {
  2216.             if ( isDefined( enemy ) && isDefined( enemy.isonbus ) && !enemy.isonbus )
  2217.             {
  2218.                 break;
  2219.             }
  2220.             else
  2221.             {
  2222.                 if ( !self.isonbus )
  2223.                 {
  2224.                     break;
  2225.                 }
  2226.                 else
  2227.                 {
  2228.                     goal_dir = level.the_bus gettagangles( goal_tag );
  2229.                     goal_pos = level.the_bus gettagorigin( goal_tag );
  2230.                     if ( distance2d( self.origin, goal_pos ) <= 32 )
  2231.                     {
  2232.                         self.goalradius = 32;
  2233.                         return 1;
  2234.                     }
  2235.                     self setgoalpos( goal_pos );
  2236.                     self orientmode( "face point", goal_pos );
  2237.                     wait 0,1;
  2238.                 }
  2239.             }
  2240.         }
  2241.     }
  2242.     self.goalradius = 32;
  2243.     return 0;
  2244. }
  2245.  
  2246. zombiejumpdownhatch()
  2247. {
  2248.     self endon( "death" );
  2249.     self thread zombiejumpdownhatchkilled();
  2250.     enemy = self.favoriteenemy;
  2251.     if ( isDefined( enemy ) && isDefined( enemy.isonbusroof ) && enemy.isonbusroof )
  2252.     {
  2253.         self unlink();
  2254.         return;
  2255.     }
  2256.     while ( isDefined( level.bus_roof_tearing ) && level.bus_roof_tearing )
  2257.     {
  2258.         wait 0,1;
  2259.     }
  2260.     roof_tag = self zombiegetclosestroofopeningjumptag();
  2261.     if ( !level.bus_roof_open )
  2262.     {
  2263.         self.inert_delay = ::zombieroofteardelay;
  2264.         level.bus_roof_open = 1;
  2265.         level.bus_roof_tearing = 1;
  2266.         self linkto( level.the_bus, roof_tag );
  2267.         tag_origin = level.the_bus gettagorigin( roof_tag );
  2268.         tag_angles = level.the_bus gettagangles( roof_tag );
  2269.         substate = 1;
  2270.         if ( roof_tag == "window_roof_2_jnt" )
  2271.         {
  2272.             substate = 2;
  2273.         }
  2274.         animstate = "zm_bus_attached";
  2275.         self animscripted( tag_origin, tag_angles, animstate, substate );
  2276.         maps/mp/animscripts/zm_shared::donotetracks( "bus_attached_anim" );
  2277.         self.inert_delay = undefined;
  2278.         level.bus_roof_tearing = 0;
  2279.         level.the_bus notify( "hatch_ripped_open" );
  2280.     }
  2281.     self.hatch_jump = 1;
  2282.     self.inert_delay = ::zombiehatchjumpdelay;
  2283.     hatch_tag = "tag_hatch_attach_ladder";
  2284.     self linkto( level.the_bus, hatch_tag );
  2285.     tag_origin = level.the_bus gettagorigin( hatch_tag );
  2286.     tag_angles = level.the_bus gettagangles( hatch_tag );
  2287.     substate = 0;
  2288.     if ( roof_tag == "window_roof_1_jnt" )
  2289.     {
  2290.         substate = 1;
  2291.     }
  2292.     animstate = maps/mp/animscripts/zm_utility::append_missing_legs_suffix( "zm_bus_hatch_jump_down" );
  2293.     self animscripted( tag_origin, tag_angles, animstate, substate );
  2294.     maps/mp/animscripts/zm_shared::donotetracks( "bus_hatch_jump_anim" );
  2295.     self unlink();
  2296.     self.hatch_jump = 0;
  2297.     self.inert_delay = undefined;
  2298.     self setgoalpos( self.origin );
  2299.     level.bus_zombie_on_roof = undefined;
  2300. }
  2301.  
  2302. zombiehatchjumpdelay()
  2303. {
  2304. /#
  2305.     iprintln( "hatch delay" );
  2306. #/
  2307.     while ( isDefined( self.hatch_jump ) && self.hatch_jump )
  2308.     {
  2309.         wait 0,1;
  2310.     }
  2311. }
  2312.  
  2313. zombieroofteardelay()
  2314. {
  2315.     self endon( "death" );
  2316.     self notify( "zombieHeightTraverseStop" );
  2317.     self unlink();
  2318.     level.bus_roof_open = 0;
  2319.     level.bus_roof_tearing = 0;
  2320.     self.inert_wakeup_override = ::zombierooftearwakeup;
  2321. }
  2322.  
  2323. zombierooftearwakeup()
  2324. {
  2325.     self endon( "death" );
  2326.     self.inert_wakeup_override = undefined;
  2327.     self thread zombieheighttraverse();
  2328. }
  2329.  
  2330. zombiejumpdownhatchkilled()
  2331. {
  2332.     while ( isDefined( level.bus_roof_tearing ) && level.bus_roof_tearing )
  2333.     {
  2334.         if ( !isDefined( self ) )
  2335.         {
  2336.             level.bus_roof_tearing = 0;
  2337.             return;
  2338.         }
  2339.         else
  2340.         {
  2341.             wait 0,1;
  2342.         }
  2343.     }
  2344. }
  2345.  
  2346. zombieclimbtoroof()
  2347. {
  2348.     self endon( "death" );
  2349.     hatch_tag = "tag_hatch_attach_ladder";
  2350.     self linkto( level.the_bus, hatch_tag );
  2351.     tag_origin = level.the_bus gettagorigin( hatch_tag );
  2352.     tag_angles = level.the_bus gettagangles( hatch_tag );
  2353.     hatch_vec = vectornormalize( anglesToForward( tag_angles ) );
  2354.     player = self.favoriteenemy;
  2355.     player_vec = vectornormalize( player.origin - tag_origin );
  2356.     substate = 0;
  2357.     dot = vectordot( hatch_vec, player_vec );
  2358.     if ( dot > 0 )
  2359.     {
  2360.         substate = 1;
  2361.     }
  2362.     animstate = maps/mp/animscripts/zm_utility::append_missing_legs_suffix( "zm_bus_hatch_jump_up" );
  2363.     self animscripted( tag_origin, tag_angles, animstate, substate );
  2364.     maps/mp/animscripts/zm_shared::donotetracks( "bus_hatch_jump_anim" );
  2365.     self unlink();
  2366.     self setgoalpos( self.origin );
  2367. }
  2368.  
  2369. zombiegetclosestroofjumptag()
  2370. {
  2371.     root = "tag_roof_jump_off";
  2372.     best = root + "1";
  2373.     best_pos = level.the_bus gettagorigin( best );
  2374.     best_pos_dist2 = distance2dsquared( self.origin, best_pos );
  2375.     i = 1;
  2376.     while ( i <= 4 )
  2377.     {
  2378.         next_pos = level.the_bus gettagorigin( root + i );
  2379.         next_pos_dist2 = distance2dsquared( self.origin, next_pos );
  2380.         if ( next_pos_dist2 < best_pos_dist2 )
  2381.         {
  2382.             best = root + i;
  2383.             best_pos = next_pos;
  2384.             best_pos_dist2 = next_pos_dist2;
  2385.         }
  2386.         i++;
  2387.     }
  2388.     return best;
  2389. }
  2390.  
  2391. zombiegetclosestroofopeningjumptag()
  2392. {
  2393.     pos1 = level.the_bus gettagorigin( "window_roof_1_jnt" );
  2394.     pos2 = level.the_bus gettagorigin( "window_roof_2_jnt" );
  2395.     closest = "window_roof_1_jnt";
  2396.     pos1_dist2 = distance2dsquared( self.origin, pos1 );
  2397.     pos2_dist2 = distance2dsquared( self.origin, pos2 );
  2398.     if ( pos2_dist2 < pos1_dist2 )
  2399.     {
  2400.         closest = "window_roof_2_jnt";
  2401.     }
  2402.     return closest;
  2403. }
  2404.  
  2405. zombiegetclosestdoortag()
  2406. {
  2407.     pos1 = level.the_bus gettagorigin( "door_rear_jnt" );
  2408.     pos2 = level.the_bus gettagorigin( "door_front_jnt" );
  2409.     closest = "door_rear_jnt";
  2410.     pos1_dist2 = distance2dsquared( self.origin, pos1 );
  2411.     pos2_dist2 = distance2dsquared( self.origin, pos2 );
  2412.     if ( pos2_dist2 < pos1_dist2 )
  2413.     {
  2414.         closest = "door_front_jnt";
  2415.     }
  2416.     return closest;
  2417. }
  2418.  
  2419. zombiekeepattackingthroughwindow( left_or_right )
  2420. {
  2421.     while ( self zombietryattackthroughwindow( 0, left_or_right ) )
  2422.     {
  2423.         tag_origin = self.attachent gettagorigin( self.attachtag );
  2424.         tag_angles = self.attachent gettagangles( self.attachtag );
  2425.         asd_name = "zm_zbarrier_window_idle";
  2426.         side = _getsideofbusopeningison( self.attachtag );
  2427.         if ( side == "front" )
  2428.         {
  2429.             asd_name = "zm_zbarrier_front_window_idle";
  2430.         }
  2431.         self animscripted( tag_origin, tag_angles, asd_name, "window_idle" + left_or_right );
  2432.         self zombieanimnotetrackthink( "bus_window_idle" );
  2433.     }
  2434. }
  2435.  
  2436. zombietryattackthroughwindow( is_random, postfix )
  2437. {
  2438.     attackplayers = self zombiegetplayerstoattack();
  2439.     if ( attackplayers.size == 0 )
  2440.     {
  2441.         return 0;
  2442.     }
  2443.     should_attack = 1;
  2444.     if ( is_random )
  2445.     {
  2446.         rand = randomint( 100 );
  2447.         should_attack = rand < 80;
  2448.     }
  2449.     if ( should_attack )
  2450.     {
  2451.         self thread maps/mp/zombies/_zm_audio::do_zombies_playvocals( "attack", self.animname );
  2452.         tag_origin = self.attachent gettagorigin( self.attachtag );
  2453.         tag_angles = self.attachent gettagangles( self.attachtag );
  2454.         asd_name = "zm_zbarrier_window_attack";
  2455.         side = _getsideofbusopeningison( self.attachtag );
  2456.         if ( isDefined( side ) && side == "front" )
  2457.         {
  2458.             asd_name = "zm_zbarrier_front_window_attack";
  2459.         }
  2460.         self animscripted( tag_origin, tag_angles, asd_name, "window_attack" + postfix );
  2461.         self zombieanimnotetrackthink( "bus_window_attack" );
  2462.         return 1;
  2463.     }
  2464.     return 0;
  2465. }
  2466.  
  2467. zombiegetplayerstoattack()
  2468. {
  2469.     playerstoattack = [];
  2470.     players = get_players();
  2471.     attackrange = 72;
  2472.     attackrange *= attackrange;
  2473.     attackheight = 37;
  2474.     attackheight *= attackheight;
  2475.     i = 0;
  2476.     while ( i < players.size )
  2477.     {
  2478.         if ( isDefined( self.opening ) && isDefined( self.opening.rebuildtrigger ) )
  2479.         {
  2480.             if ( players[ i ] istouching( self.opening.rebuildtrigger ) )
  2481.             {
  2482.                 playerstoattack[ playerstoattack.size ] = players[ i ];
  2483.             }
  2484.             i++;
  2485.             continue;
  2486.         }
  2487.         else
  2488.         {
  2489.             xydist = distance2dsquared( self.origin, players[ i ].origin );
  2490.             zdist = self.origin[ 2 ] - players[ i ].origin[ 2 ];
  2491.             zdist2 = zdist * zdist;
  2492.             if ( xydist <= attackrange && zdist2 <= attackheight )
  2493.             {
  2494.                 playerstoattack[ playerstoattack.size ] = players[ i ];
  2495.             }
  2496.         }
  2497.         i++;
  2498.     }
  2499.     return playerstoattack;
  2500. }
Advertisement
Add Comment
Please, Sign In to add comment