//-- // Strogg Dropship Functionality aweldon Apr 05 //-- // The following functions are designed for quick and easy implementation of the strogg dropship as a means of // bringing baddies into an area - preferably outdoors. This setup requires minimal scripting but some very // careful entity work. Be especially careful of target order on the carrier - targeting these out of order may make // for some interesting accidents. // Create an env_strogg_troopcarrier entity and orient it so the nose of the model is facing east (right) on the grid. // Inside the model, place whatever enemies you feel like deploying that will fit inside the carrier. The monsters // require no special keys, just an angle direction matching the way they should face relative to the carrier. You can // place as many as you wish, provided they all fit inside the ship and don't make the game run at 2fps. // Once the enemies are placed, add a target null near your dropship (for convenience) and target it from the // env_strogg_troopcarrier. Make sure it is the first target. Once targeted, targed all of the monsters within the ship // from the target_null. This will serve as a master listing for all monsters assigned to this particular carrier. // Now create a second target_null, also targeted from the carrier. This should target any entities you want triggered // when the dropship lands (tethers, ai_triggers, etc. ). You will probably want to target at least two tethers (one // per side) to nudge the monsters away from the ship before it takes off. // Finally, create two invisible func_mover entities. One should be centered at the carrier's origin; the other should // be set back (relative to the carrier) a few units for convenience, but still on the same horizontal axis. Bind the // carrier to the second mover, then bind that to the first mover. The first mover will serve as the spline path base // to bring the carrier into the scene. (This is unnecessary if using a custom anim.) The second will control rotation // as needed. Also be sure to target the second mover, the one the carrier is bound to, from the carrier. This will be // passed into script and bound/unbound during the landing cycle. // A fourth target can be added to the dropship as a toggleable solid base. In the example map, this is just a clip brush, // as attempts with func_aas_obstacles and func_statics failed. However, if you desire a toggleable version, you can add // that here with no major changes. // Your entity target keys on the carrier entity should be as follows: // KEY: VALUE: USE: // target [target_null] Target all monsters on board // target1 [target_null] Target all entities to trigger on landing // target2 [optional] Trigger something after the ship lands. Typically this will be a func_static // base used for monster collision, but it could also be a trigger_relay for a // series of events as the ship lands (warning lights, etc). Any entity used here // that is not removed after being triggered will be triggered twice, once when the // ship lands and once when it lifts off. // The only scripting required to use this system is starting the spline movement and binding the entities to the ship // via the main() function. For each dropship add: // thread strogg_dropship::bindMonsters( [CARRIER ENTITY NAME] ); // For each dropship in the map. When calling the land function, add: // strogg_dropship::carrierLand( [CARRIER ENTITY NAME] ); // This will automatically handle the landing and takeoff process. Simply add a spline path or animation in and out with // this function in between and all targets intact, and as long as all entity targets are correct, the functions below // will handle the rest. And, finally, always make sure your dropship's origin is exactly 1112 units above a flat surface. // Binds all monsters to the carrier void bindMonsters( entity carrier ) { // Declare all variables for the function entity list = carrier.getTarget( 0 ); entity monster; float currentMonster; float numMonsters = list.numTargets(); for( currentMonster = 0; currentMonster < numMonsters; currentMonster++) { monster = list.getTarget( currentMonster ); monster.bindToJoint( carrier , "still" , 1 ); monster.hide(); monster.disableClip(); monster.becomePassive( 1 ); //sys.println( "Monster " + monster.getName() + " is now bound to " + carrier.getName() + "and hidden" ); sys.waitFrame(); } } // Unbinds all monsters from the carrier void unbindMonsters( entity carrier ) { // Declare all variables for the function entity list = carrier.getTarget( 0 ); entity monster; float currentMonster; float numMonsters = list.numTargets(); for( currentMonster = 0; currentMonster < numMonsters; currentMonster++) { monster = list.getTarget( currentMonster ); monster.becomeAggressive(); sys.waitFrame(); monster.enableClip(); monster.unbind(); //sys.println( "Monster " + monster.getName() + " is now unbound from " + carrier.getName() ); sys.waitFrame(); } } // Unhides all monsters on the carrier void showMonsters( entity carrier ) { // Declare all variables for the function entity list = carrier.getTarget( 0 ); entity monster; float currentMonster; float numMonsters = list.numTargets(); for( currentMonster = 0; currentMonster < numMonsters; currentMonster++) { monster = list.getTarget( currentMonster ); monster.show(); sys.waitFrame(); //sys.println( "Monster " + monster.getName() + " is now visible " ); sys.waitFrame(); } } // Threaded function for each guy getting clear without attacking void getClearThread( entity monster , entity monsterTo ) { monster.becomePassive( 1 ); aiScriptedMoveWait( monster , monsterTo , 64 , 1 ); monster.becomeAggressive(); //sys.println( "Monster " + monster.getName() + " is now moving to " + moveMove.getName() ); } void getClear( entity carrier ) { // Declare all variables for the function entity list = carrier.getTarget( 0 ); entity monster; float currentMonster; float numMonsters = list.numTargets(); for( currentMonster = 0; currentMonster < numMonsters; currentMonster++) { monster = list.getTarget( currentMonster ); entity moveMove = monster.getTarget(0); if( isValidEntity( moveMove ) ) { thread getClearThread( monster , moveMove ); } } } // Nothing to see here void hackForDefFile( entity carrier ) { thread showMonsters( carrier ); } // A poor man's animWait function void checkAnim( entity animating ) { while ( !animating.animDone ( ANIMCHANNEL_ALL, 0 ) ) { sys.waitFrame ( ); } } // Liftoff and re-bind to the rotational mover void liftoff( entity carrier ) { // Play liftoff animation carrier.playAnim( ANIMCHANNEL_ALL, "lift" ); checkAnim( carrier ); } // Landing cycle void carrierLand( entity carrier ) { // Define variables for this function entity list = carrier.getTarget( 1 ); entity base = list.getTarget( 2 ); float targetCount; float totalTargets = list.numTargets(); entity aiTarget; // Make with the landing cycle! carrier.playAnim( ANIMCHANNEL_ALL, "drop" ); checkAnim( carrier ); sys.wait( .25 ); // Trigger solid base and unbind monsters unbindMonsters( carrier ); if( isValidEntity( base ) ) { sys.trigger( base ); } sys.waitFrame(); getClear( carrier ); sys.wait( .5 ); // Trigger any additional targets (tethers, ai triggers, other events) for( targetCount = 0; targetCount < totalTargets; targetCount++) { aiTarget = list.getTarget( targetCount ); sys.trigger( aiTarget ); sys.waitFrame(); } // Give some time for the monsters to clear the area sys.wait( 2 ); // Re-Trigger base and lift off! if( isValidEntity( base ) ) { sys.trigger( base ); } liftoff( carrier ); }