#include maps\_utility; #include common_scripts\utility; #using_animtree( "generic_human" ); DRONE_RUN_SPEED = 170; TRACE_HEIGHT = 100; MAX_DRONES_ALLIES = 99999; MAX_DRONES_AXIS = 99999; MAX_DRONES_TEAM3 = 99999; MAX_DRONES_CIVILIAN = 99999; DEATH_DELETE_FOV = 0.5; // cos(60) initGlobals() { //############################################################################ //############################################################################ // NEVER CALL THIS FUNCTION MANUALLY, THIS IS MEANT TO BE CALLED INTERNALLY!!! // NEVER CALL THIS FUNCTION MANUALLY, THIS IS MEANT TO BE CALLED INTERNALLY!!! // NEVER CALL THIS FUNCTION MANUALLY, THIS IS MEANT TO BE CALLED INTERNALLY!!! //############################################################################ //############################################################################ if ( getdvar( "debug_drones" ) == "" ) setdvar( "debug_drones", "0" ); assert( isdefined( level.drone_anims ) ); // lookahead value - how far the character will lookahead for movement direction // larger number makes smother, more linear travel. small value makes character go almost exactly point to point if ( !isdefined( level.lookAhead_value ) ) level.drone_lookAhead_value = 200; if ( !isdefined( level.max_drones ) ) level.max_drones = []; if ( !isdefined( level.max_drones[ "allies" ] ) ) level.max_drones[ "allies" ] = MAX_DRONES_ALLIES; if ( !isdefined( level.max_drones[ "axis" ] ) ) level.max_drones[ "axis" ] = MAX_DRONES_AXIS; if ( !isdefined( level.max_drones[ "team3" ] ) ) level.max_drones[ "team3" ] = MAX_DRONES_TEAM3; if ( !isdefined( level.max_drones[ "neutral" ] ) ) level.max_drones[ "neutral" ] = MAX_DRONES_CIVILIAN; if ( !isdefined( level.drones ) ) level.drones = []; if ( !isdefined( level.drones[ "allies" ] ) ) level.drones[ "allies" ] = struct_arrayspawn(); if ( !isdefined( level.drones[ "axis" ] ) ) level.drones[ "axis" ] = struct_arrayspawn(); if ( !isdefined( level.drones[ "team3" ] ) ) level.drones[ "team3" ] = struct_arrayspawn(); if ( !isdefined( level.drones[ "neutral" ] ) ) level.drones[ "neutral" ] = struct_arrayspawn(); level.drone_spawn_func = ::drone_init; } drone_give_soul() { // Tell drone which animtree to use self useAnimTree( #animtree ); // Tell drone to use hero-only lighting so they look like AI self startUsingHeroOnlyLighting(); if ( isdefined( self.script_moveplaybackrate ) ) self.moveplaybackrate = self.script_moveplaybackrate; else self.moveplaybackrate = 1; // Put a friendly name on the drone so they look like AI if ( self.team == "allies" ) { // asign name self maps\_names::get_name(); // string not found for self setlookattext( self.name, &"" ); } if ( isdefined( level.droneCallbackThread ) ) self thread [[ level.droneCallbackThread ]](); // Run the friendly fire thread on this drone so the mission can be failed for killing friendly drones // Runs on all teams since friendly fire script also keeps track of enemies killed, etc. level thread maps\_friendlyfire::friendly_fire_think( self ); } drone_init() { // Dont keep this drone if we've reached the max population for that team of drones assertEx( isdefined( level.max_drones ), "You need to put maps\_drone::init(); in your level script!" ); if ( level.drones[ self.team ].array.size >= level.max_drones[ self.team ] ) { self delete(); return; } thread drone_array_handling( self ); // Give the drone default health and make it take damage like an AI does self setCanDamage( true ); drone_give_soul(); if ( isdefined( self.script_drone_override ) ) return; // Wait until this drone loses it's health so it can die thread drone_death_thread(); // If the drone targets something then make it move, otherwise just idle in place if ( isdefined( self.target ) ) { if( !isdefined( self.script_moveoverride ) ) self thread drone_move(); else self thread drone_wait_move(); } if ( ( isdefined( self.script_looping ) ) && ( self.script_looping == 0 ) ) { return; } self thread drone_idle(); } drone_array_handling( drone ) { structarray_add( level.drones[ drone.team ], drone ); team = drone.team; drone waittill( "death" ); if ( isdefined( drone ) && isdefined( drone.struct_array_index ) ) structarray_remove_index( level.drones[ team ], drone.struct_array_index ); else structarray_remove_undefined( level.drones[ team ] ); } drone_death_thread() { // Wait until the drone reaches 0 health while ( isdefined( self ) ) { self waittill( "damage" ); if ( isdefined( self.damageShield ) && self.damageShield ) { self.health = 100000; continue; } if ( self.health <= 0 ) break; } deathanim = level.drone_anims[ self.team ][ "stand" ][ "death" ]; if( isdefined( self.deathanim ) ) deathanim = self.deathanim; // Make drone die self notify( "death" ); if ( !( isdefined( self.noragdoll ) && isdefined( self.skipDeathAnim ) ) ) { if ( isdefined( self.noragdoll ) ) { self drone_play_scripted_anim( deathanim, "deathplant" ); } else if ( isdefined( self.skipDeathAnim ) ) { self startragdoll(); self drone_play_scripted_anim( deathanim, "deathplant" ); } else { self drone_play_scripted_anim( deathanim, "deathplant" ); self startragdoll(); } } self notsolid(); if( isdefined( self ) && isdefined( self.nocorpsedelete ) ) return; wait 10; while( isdefined( self ) ) { if ( !within_fov( level.player.origin, level.player.angles, self.origin, DEATH_DELETE_FOV ) ) self delete(); wait( 5 ); } } // non-blocking loop animation used for idle/movement drone_play_looping_anim( droneAnim, rate ) { self ClearAnim( %body, 0.2 ); self StopAnimScripted(); self SetFlaggedAnimKnobAllRestart( "drone_anim", droneAnim, %body, 1, 0.2, rate ); } //blocking/scripted animation (when we're not moving) drone_play_scripted_anim( droneAnim, deathplant ) { self clearAnim( %body, 0.2 ); self stopAnimScripted(); mode = "normal"; if ( isdefined( deathplant ) ) mode = "deathplant"; flag = "drone_anim"; self animscripted( flag, self.origin, self.angles, droneAnim, mode ); //self animRelative( "drone_anim", self.origin, self.angles, droneAnim ); self waittillmatch( "drone_anim", "end" ); } /* ============= ///ScriptDocBegin "Name: drone_drop_real_weapon_on_death()" "Summary: Call this on a drone to have him drop a real weapon that can be picked up by the player" "Module: Utility" "CallOn: A spawned drone" "Example: myDrone thread drone_drop_real_weapon_on_death()" "SPMP: singleplayer" ///ScriptDocEnd ============= */ drone_drop_real_weapon_on_death() { if ( !isdefined( self ) ) return; self waittill( "death" ); if( !isdefined( self ) ) //abort if deleted manually return; weapon_model = getWeaponModel( self.weapon ); weapon = self.weapon; if ( isdefined( weapon_model ) ) { //self waittill_match_or_timeout( "deathanim", "end", 4 ); self detach( weapon_model, "tag_weapon_right" ); org = self gettagorigin( "tag_weapon_right" ); ang = self gettagangles( "tag_weapon_right" ); gun = Spawn( "weapon_" + weapon, ( 0, 0, 0 ) ); gun.angles = ang; gun.origin = org; } } drone_idle( lastNode, moveToDest ) { if ( ( isdefined( lastNode ) ) && ( isdefined( lastNode[ "script_noteworthy" ] ) ) && ( isdefined( level.drone_anims[ self.team ][ lastNode[ "script_noteworthy" ] ] ) ) ) { //if the last node has a valid fight behavior in its script_noteworthy, fight from that node self thread drone_fight( lastNode[ "script_noteworthy" ], lastNode, moveToDest ); } else { // Otherwise, just loop standard idle animation self drone_play_looping_anim( level.drone_anims[ self.team ][ "stand" ][ "idle" ], 1 ); } } drone_get_goal_loc_with_arrival( dist, node ) { animset = node[ "script_noteworthy" ]; if ( !isdefined( level.drone_anims[ self.team ][ animset ][ "arrival" ] ) ) return dist; animDelta = GetMoveDelta( level.drone_anims[ self.team ][ animset ][ "arrival" ], 0, 1 ); animDelta = length( animDelta ); assertex( animDelta < dist, "Drone with export " + self.export + " does not have enough room to play an arrival anim. Space nodes out more and ensure he has a straight path into the last node" ); dist = ( dist - animDelta ); return dist; } /* ent_cleanup( drone ) { //cleanup the script_origin used to make arrivals work on uneven terrain self endon( "death" ); drone waittill( "death" ); if ( isdefined( self ) ) self delete(); } */ drone_fight( animset, struct, moveToDest ) { self endon( "death" ); self endon( "stop_drone_fighting" ); self.animset = animset; self.weaponsound = undefined; iRand = randomintrange( 1, 4 ); //assign drone a random weapon for sound variety if( self.team == "axis" ) { if( iRand == 1 ) self.weaponsound = "drone_ak47_fire_npc"; else if( iRand == 2 ) self.weaponsound = "drone_g36c_fire_npc"; if( iRand == 3 ) self.weaponsound = "drone_fnp90_fire_npc"; } else { if( iRand == 1 ) self.weaponsound = "drone_m4carbine_fire_npc"; else if( iRand == 2 ) self.weaponsound = "drone_m16_fire_npc"; if( iRand == 3 ) self.weaponsound = "drone_m249saw_fire_npc"; } //commenting out arrival stuff for now....causes floating and other unpredictible behavior //attach drone to dummy node for arrival so he can arrive on uneven terrain // dummy = spawn( "script_origin", moveToDest ); // dummy thread ent_cleanup( self ); // dummy thread debug_message( "ORG", undefined, 9999, dummy ); // dummy.origin = ( dummy.origin[ 0 ], dummy.origin[ 1 ], self.origin[ 2 ] ); // self linkTo( dummy ); // dummyTime = getanimlength( level.drone_anims[ self.team ][ animset ][ "arrival" ] ); // dummyTime = dummyTime - 1; // dummy moveto( moveToDest + ( 0, 0, 2 ), dummyTime ); // self drone_play_scripted_anim( level.drone_anims[ self.team ][ animset ][ "arrival" ] ); // self unlink(); // dummy delete(); //make sure drone has no UP angle...should just play anim flat on the ground and not be tilted self.angles = ( 0, self.angles[ 1 ], self.angles[ 2 ] ); //move up a few units if prone...keeps getting buried in ground if ( animset == "coverprone" ) self moveto( self.origin + ( 0, 0, 8 ), .05 ); //set deathanim to one that will work with cover behavior self.noragdoll = true; self.deathanim = level.drone_anims[ self.team ][ animset ][ "death" ]; bPopUpToFire = 1; while( isdefined( self ) ) { //play random cover loop/twitch self drone_play_scripted_anim( level.drone_anims[ self.team ][ animset ][ "idle" ][ randomint( level.drone_anims[ self.team ][ animset ][ "idle" ].size ) ] ); //pop up and fire if ( ( cointoss() ) && ( !isdefined( self.ignoreall ) ) ) { //dont always pop up if prone if ( animset == "coverprone" ) { if( cointoss() ) bPopUpToFire = 0; else bPopUpToFire = 1; } //never pop up if coverguard else if ( animset == "coverguard" ) bPopUpToFire = 0; if ( bPopUpToFire == 1 ) { self drone_play_scripted_anim( level.drone_anims[ self.team ][ animset ][ "hide_2_aim" ] ); wait( getanimlength( level.drone_anims[ self.team ][ animset ][ "hide_2_aim" ] ) - .5 ); } //fire some blank bullets if ( isdefined( level.drone_anims[ self.team ][ animset ][ "fire" ] ) ) { if ( ( animset == "coverprone" ) && ( bPopUpToFire == 1 ) ) self thread drone_play_looping_anim( level.drone_anims[ self.team ][ animset ][ "fire_exposed" ], 1 ); else self thread drone_play_looping_anim( level.drone_anims[ self.team ][ animset ][ "fire" ], 1 ); self drone_fire_randomly(); } else { //if no fire idle anim...just put out a quick burst self drone_shoot(); wait(.15); self drone_shoot(); wait(.15); self drone_shoot(); wait(.15); self drone_shoot(); } //dont always pop up if prone..never pop up if coverguard if ( bPopUpToFire == 1 ) self drone_play_scripted_anim( level.drone_anims[ self.team ][ animset ][ "aim_2_hide" ] ); //reload weapon self drone_play_scripted_anim( level.drone_anims[ self.team ][ animset ][ "reload" ] ); } } } drone_fire_randomly() { self endon( "death" ); //randomly do bursts or single shots if( cointoss() ) { self drone_shoot(); wait( .1 ); self drone_shoot(); wait( .1 ); self drone_shoot(); if( cointoss() ) { wait( .1 ); self drone_shoot(); } if( cointoss() ) { wait( .1 ); self drone_shoot(); wait( .1 ); self drone_shoot(); wait( .1 ); } if( cointoss() ) wait( randomfloatrange( 1, 2 ) ); } else { self drone_shoot(); wait( randomfloatrange( .25, .75 ) ); self drone_shoot(); wait( randomfloatrange( .15, .75 ) ); self drone_shoot(); wait( randomfloatrange( .15, .75 ) ); self drone_shoot(); wait( randomfloatrange( .15, .75 ) ); } } drone_shoot() { self endon( "death" ); self notify( "firing" ); self endon( "firing" ); drone_shoot_fx(); fireAnim = %exposed_crouch_shoot_auto_v2; //SetAnimKnobRestart( , ,