From 34dbe7ca33bcbaa81ddfe1c86c11891d829ff436 Mon Sep 17 00:00:00 2001 From: INeedBots Date: Sun, 27 Sep 2020 12:38:53 -0600 Subject: [PATCH] common_mp --- userraw/maps/mp/killstreaks/_ac130.gsc | 1854 ++++++++++++++++++++ userraw/maps/mp/killstreaks/_airstrike.gsc | 1172 +++++++++++++ userraw/maps/mp/killstreaks/_harrier.gsc | 1155 ++++++++++++ userraw/maps/mp/killstreaks/_uav.gsc | 638 +++++++ 4 files changed, 4819 insertions(+) create mode 100644 userraw/maps/mp/killstreaks/_ac130.gsc create mode 100644 userraw/maps/mp/killstreaks/_airstrike.gsc create mode 100644 userraw/maps/mp/killstreaks/_harrier.gsc create mode 100644 userraw/maps/mp/killstreaks/_uav.gsc diff --git a/userraw/maps/mp/killstreaks/_ac130.gsc b/userraw/maps/mp/killstreaks/_ac130.gsc new file mode 100644 index 0000000..8ec3794 --- /dev/null +++ b/userraw/maps/mp/killstreaks/_ac130.gsc @@ -0,0 +1,1854 @@ +#include maps\mp\_utility; +#include common_scripts\utility; + + +init() +{ + level.ac130_use_duration = 40; + level.ac130_num_flares = 2; + + makeDvarServerInfo( "ui_ac130usetime", level.ac130_use_duration ); + + precacheShader( "black" ); + + precacheString( &"AC130_HUD_THERMAL_WHOT" ); + precacheString( &"AC130_HUD_THERMAL_BHOT" ); + precacheString( &"AC130_HUD_AGL" ); + precacheString( &"MP_CIVILIAN_AIR_TRAFFIC" ); + + precacheMenu( "ac130timer" ); + + precacheModel( "vehicle_ac130_coop" ); + + precacheItem("ac130_25mm_mp"); + precacheItem("ac130_40mm_mp"); + precacheItem("ac130_105mm_mp"); + + PrecacheMiniMapIcon( "compass_objpoint_ac130_friendly" ); + PrecacheMiniMapIcon( "compass_objpoint_ac130_enemy" ); + + precacheShellShock("ac130"); + + angelFlarePrecache(); + + level._effect[ "cloud" ] = loadfx( "misc/ac130_cloud" ); + level._effect[ "beacon" ] = loadfx( "misc/ir_beacon_coop" ); + level._effect[ "ac130_explode" ] = loadfx( "explosions/aerial_explosion_ac130_coop" ); + level._effect[ "ac130_flare" ] = loadfx( "misc/flares_cobra" ); + level._effect[ "ac130_light_red" ] = loadfx( "misc/aircraft_light_wingtip_red" ); + level._effect[ "ac130_light_white_blink" ] = loadfx( "misc/aircraft_light_white_blink" ); + level._effect[ "ac130_light_red_blink" ] = loadfx( "misc/aircraft_light_red_blink" ); + level._effect[ "ac130_engineeffect" ] = loadfx( "fire/jet_engine_ac130" ); + + // ac130 muzzleflash effects for player on ground to see + level._effect[ "coop_muzzleflash_105mm" ] = loadfx( "muzzleflashes/ac130_105mm" ); + level._effect[ "coop_muzzleflash_40mm" ] = loadfx( "muzzleflashes/ac130_40mm" ); + + level.radioForcedTransmissionQueue = []; + level.enemiesKilledInTimeWindow = 0; + level.lastRadioTransmission = getTime(); + + level.color[ "white" ] = ( 1, 1, 1 ); + level.color[ "red" ] = ( 1, 0, 0 ); + level.color[ "blue" ] = ( .1, .3, 1 ); + + level.cosine = []; + level.cosine[ "45" ] = cos( 45 ); + level.cosine[ "5" ] = cos( 5 ); + + level.HUDItem = []; + + level.physicsSphereRadius[ "ac130_25mm_mp" ] = 60; + level.physicsSphereRadius[ "ac130_40mm_mp" ] = 600; + level.physicsSphereRadius[ "ac130_105mm_mp" ] = 1000; + + level.physicsSphereForce[ "ac130_25mm_mp" ] = 0; + level.physicsSphereForce[ "ac130_40mm_mp" ] = 3.0; + level.physicsSphereForce[ "ac130_105mm_mp" ] = 6.0; + + level.weaponReloadTime[ "ac130_25mm_mp" ] = 1.5; + level.weaponReloadTime[ "ac130_40mm_mp" ] = 3.0; + level.weaponReloadTime[ "ac130_105mm_mp" ] = 5.0; + + level.ac130_Speed[ "move" ] = 250; + level.ac130_Speed[ "rotate" ] = 70; + + //flag_init( "ir_beakons_on" ); + flag_init( "allow_context_sensative_dialog" ); + flag_set( "allow_context_sensative_dialog" ); + minimapOrigins = getEntArray( "minimap_corner", "targetname" ); + ac130Origin = (0,0,0); + + if ( miniMapOrigins.size ) + ac130Origin = maps\mp\gametypes\_spawnlogic::findBoxCenter( miniMapOrigins[0].origin, miniMapOrigins[1].origin ); + + level.ac130 = spawn( "script_model", ac130Origin ); + level.ac130 setModel( "c130_zoomrig" ); + level.ac130.angles = ( 0, 115, 0 ); + level.ac130.owner = undefined; + + level.ac130 hide(); + + level.ac130InUse = false; + + init_sounds(); + + thread rotatePlane( "on" ); + thread ac130_spawn(); + thread onPlayerConnect(); + + thread handleIncomingStinger(); + + level.killstreakFuncs["ac130"] = ::tryUseAC130; + + level.ac130Queue = []; +} + + +tryUseAC130( lifeId ) +{ + if ( isDefined( level.civilianJetFlyBy ) ) + { + self iPrintLnBold( &"MP_CIVILIAN_AIR_TRAFFIC" ); + return false; + } + + if ( isDefined( level.ac130player ) || level.ac130InUse ) + { + self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" ); + return false; + } + + if ( self isUsingRemote() ) + { + return false; + } + + level.ac130InUse = true; + self setUsingRemote( "ac130" ); + result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak(); + if ( result != "success" ) + { + if ( result != "disconnect" ) + self clearUsingRemote(); + + level.ac130InUse = false; + return false; + } + + self maps\mp\_matchdata::logKillstreakEvent( "ac130", self.origin ); + + self.ac130LifeId = lifeId; + level.ac130.planeModel.crashed = undefined; + + thread setAC130Player( self ); + return true; +} + + +init_sounds() +{ + setAC130Ambience( "ambient_ac130_int1" ); + + level.scr_sound["foo"]["bar"] = ""; + //------------------------------------------------------------------------------------------------- + //CONTEXT SENSATIVE DIALOG + //------------------------------------------------------------------------------------------------- + + add_context_sensative_dialog( "ai", "in_sight", 0, "ac130_fco_moreenemy" ); // More enemy personnel. + add_context_sensative_dialog( "ai", "in_sight", 1, "ac130_fco_getthatguy" ); // Get that guy. + add_context_sensative_dialog( "ai", "in_sight", 2, "ac130_fco_guymovin" ); // Roger, guy movin'. + add_context_sensative_dialog( "ai", "in_sight", 3, "ac130_fco_getperson" ); // Get that person. + add_context_sensative_dialog( "ai", "in_sight", 4, "ac130_fco_guyrunnin" ); // Guy runnin'. + add_context_sensative_dialog( "ai", "in_sight", 5, "ac130_fco_gotarunner" ); // Uh, we got a runner here. + add_context_sensative_dialog( "ai", "in_sight", 6, "ac130_fco_backonthose" ); // Get back on those guys. + add_context_sensative_dialog( "ai", "in_sight", 7, "ac130_fco_gonnagethim" ); // You gonna get him? + add_context_sensative_dialog( "ai", "in_sight", 8, "ac130_fco_personnelthere" ); // Personnel right there. + add_context_sensative_dialog( "ai", "in_sight", 9, "ac130_fco_nailthoseguys" ); // Nail those guys. + add_context_sensative_dialog( "ai", "in_sight", 11, "ac130_fco_lightemup" ); // Light ‘em up. + add_context_sensative_dialog( "ai", "in_sight", 12, "ac130_fco_takehimout" ); // Yeah take him out. + add_context_sensative_dialog( "ai", "in_sight", 14, "ac130_plt_yeahcleared" ); // Yeah, cleared to engage. + add_context_sensative_dialog( "ai", "in_sight", 15, "ac130_plt_copysmoke" ); // Copy, smoke ‘em. + add_context_sensative_dialog( "ai", "in_sight", 16, "ac130_fco_rightthere" ); // Right there...tracking. + add_context_sensative_dialog( "ai", "in_sight", 17, "ac130_fco_tracking" ); // Tracking. + + add_context_sensative_dialog( "ai", "wounded_crawl", 0, "ac130_fco_movingagain" ); // Ok he’s moving again. + add_context_sensative_timeout( "ai", "wounded_crawl", undefined, 6 ); + + add_context_sensative_dialog( "ai", "wounded_pain", 0, "ac130_fco_doveonground" ); // Yeah, he just dove on the ground. + add_context_sensative_dialog( "ai", "wounded_pain", 1, "ac130_fco_knockedwind" ); // Probably just knocked the wind out of him. + add_context_sensative_dialog( "ai", "wounded_pain", 2, "ac130_fco_downstillmoving" ); // That guy's down but still moving. + add_context_sensative_dialog( "ai", "wounded_pain", 3, "ac130_fco_gettinbackup" ); // He's gettin' back up. + add_context_sensative_dialog( "ai", "wounded_pain", 4, "ac130_fco_yepstillmoving" ); // Yep, that guy’s still moving. + add_context_sensative_dialog( "ai", "wounded_pain", 5, "ac130_fco_stillmoving" ); // He's still moving. + add_context_sensative_timeout( "ai", "wounded_pain", undefined, 12 ); + + add_context_sensative_dialog( "weapons", "105mm_ready", 0, "ac130_gnr_gunready1" ); + + add_context_sensative_dialog( "weapons", "105mm_fired", 0, "ac130_gnr_shot1" ); + + add_context_sensative_dialog( "plane", "rolling_in", 0, "ac130_plt_rollinin" ); + + add_context_sensative_dialog( "explosion", "secondary", 0, "ac130_nav_secondaries1" ); + add_context_sensative_dialog( "explosion", "secondary", 1, "ac130_tvo_directsecondary1" ); + add_context_sensative_dialog( "explosion", "secondary", 1, "ac130_tvo_directsecondary2" ); + add_context_sensative_timeout( "explosion", "secondary", undefined, 7 ); + + add_context_sensative_dialog( "kill", "single", 0, "ac130_plt_gottahurt" ); // Ooo that's gotta hurt. + add_context_sensative_dialog( "kill", "single", 1, "ac130_fco_iseepieces" ); // Yeah, good kill. I see lots of little pieces down there. + add_context_sensative_dialog( "kill", "single", 2, "ac130_fco_oopsiedaisy" ); // (chuckling) Oopsie-daisy. + add_context_sensative_dialog( "kill", "single", 3, "ac130_fco_goodkill" ); // Good kill good kill. + add_context_sensative_dialog( "kill", "single", 4, "ac130_fco_yougothim" ); // You got him. + add_context_sensative_dialog( "kill", "single", 5, "ac130_fco_yougothim2" ); // You got him! + add_context_sensative_dialog( "kill", "single", 6, "ac130_fco_thatsahit" ); // That's a hit. + add_context_sensative_dialog( "kill", "single", 7, "ac130_fco_directhit" ); // Direct hit. + add_context_sensative_dialog( "kill", "single", 8, "ac130_fco_rightontarget" ); // Yep, that was right on target. + add_context_sensative_dialog( "kill", "single", 9, "ac130_fco_okyougothim" ); // Ok, you got him. Get back on the other guys. + add_context_sensative_dialog( "kill", "single", 10, "ac130_fco_within2feet" ); // All right you got the guy. That might have been within two feet of him. + + add_context_sensative_dialog( "kill", "small_group", 0, "ac130_fco_nice" ); // (chuckling) Niiiice. + add_context_sensative_dialog( "kill", "small_group", 1, "ac130_fco_directhits" ); // Yeah, direct hits right there. + add_context_sensative_dialog( "kill", "small_group", 2, "ac130_fco_iseepieces" ); // Yeah, good kill. I see lots of little pieces down there. + add_context_sensative_dialog( "kill", "small_group", 3, "ac130_fco_goodkill" ); // Good kill good kill. + add_context_sensative_dialog( "kill", "small_group", 4, "ac130_fco_yougothim" ); // You got him. + add_context_sensative_dialog( "kill", "small_group", 5, "ac130_fco_yougothim2" ); // You got him! + add_context_sensative_dialog( "kill", "small_group", 6, "ac130_fco_thatsahit" ); // That's a hit. + add_context_sensative_dialog( "kill", "small_group", 7, "ac130_fco_directhit" ); // Direct hit. + add_context_sensative_dialog( "kill", "small_group", 8, "ac130_fco_rightontarget" );// Yep, that was right on target. + add_context_sensative_dialog( "kill", "small_group", 9, "ac130_fco_okyougothim" ); // Ok, you got him. Get back on the other guys. + + add_context_sensative_dialog( "misc", "action", 0, "ac130_plt_scanrange" ); // Set scan range. + add_context_sensative_timeout( "misc", "action", 0, 70 ); + + add_context_sensative_dialog( "misc", "action", 1, "ac130_plt_cleanup" ); // Clean up that signal. + add_context_sensative_timeout( "misc", "action", 1, 80 ); + + add_context_sensative_dialog( "misc", "action", 2, "ac130_plt_targetreset" ); // Target reset. + add_context_sensative_timeout( "misc", "action", 2, 55 ); + + add_context_sensative_dialog( "misc", "action", 3, "ac130_plt_azimuthsweep" ); // Recalibrate azimuth sweep angle. Adjust elevation scan. + add_context_sensative_timeout( "misc", "action", 3, 100 ); +} + + +add_context_sensative_dialog( name1, name2, group, soundAlias ) +{ + assert( isdefined( name1 ) ); + assert( isdefined( name2 ) ); + assert( isdefined( group ) ); + assert( isdefined( soundAlias ) ); + + fullSoundAlias = maps\mp\gametypes\_teams::getTeamVoicePrefix( "allies" ) + soundAlias; + assertex( soundexists( fullSoundAlias ), "ERROR: missing soundalias " + fullSoundAlias ); + + fullSoundAlias = maps\mp\gametypes\_teams::getTeamVoicePrefix( "axis" ) + soundAlias; + assertex( soundexists( fullSoundAlias ), "ERROR: missing soundalias " + fullSoundAlias ); + + if( ( !isdefined( level.scr_sound[ name1 ] ) ) || ( !isdefined( level.scr_sound[ name1 ][ name2 ] ) ) || ( !isdefined( level.scr_sound[ name1 ][ name2 ][group] ) ) ) + { + // creating group for the first time + level.scr_sound[ name1 ][ name2 ][group] = spawnStruct(); + level.scr_sound[ name1 ][ name2 ][group].played = false; + level.scr_sound[ name1 ][ name2 ][group].sounds = []; + } + + //group exists, add the sound to the array + index = level.scr_sound[ name1 ][ name2 ][group].sounds.size; + level.scr_sound[ name1 ][ name2 ][group].sounds[index] = soundAlias; +} + + +add_context_sensative_timeout( name1, name2, groupNum, timeoutDuration ) +{ + if( !isdefined( level.context_sensative_dialog_timeouts ) ) + level.context_sensative_dialog_timeouts = []; + + createStruct = false; + if ( !isdefined( level.context_sensative_dialog_timeouts[ name1 ] ) ) + createStruct = true; + else if ( !isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ] ) ) + createStruct = true; + if ( createStruct ) + level.context_sensative_dialog_timeouts[ name1 ][ name2 ] = spawnStruct(); + + if ( isdefined( groupNum ) ) + { + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups = []; + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ] = spawnStruct(); + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ].v["timeoutDuration"] = timeoutDuration * 1000; + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ].v["lastPlayed"] = ( timeoutDuration * -1000 ); + } + else + { + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v["timeoutDuration"] = timeoutDuration * 1000; + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v["lastPlayed"] = ( timeoutDuration * -1000 ); + } +} + + +/* + ============= +///ScriptDocBegin +"Name: play_sound_on_entity( )" +"Summary: Play the specified sound alias on an entity at it's origin" +"Module: Sound" +"CallOn: An entity" +"MandatoryArg: : Sound alias to play" +"Example: level.player play_sound_on_entity( "breathing_better" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +play_sound_on_entity( alias ) +{ + play_sound_on_tag( alias ); +} + +within_fov( start_origin, start_angles, end_origin, fov ) +{ + normal = vectorNormalize( end_origin - start_origin ); + forward = anglestoforward( start_angles ); + dot = vectorDot( forward, normal ); + + return dot >= fov; +} + +/* +============= +///ScriptDocBegin +"Name: array_remove_nokeys( , )" +"Summary: array_remove used on non keyed arrays doesn't flip the array " +"Module: Utility" +"CallOn: Level" +"MandatoryArg: : array to remove from" +"MandatoryArg: : thing to remove from the array" +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +array_remove_nokeys( ents, remover ) +{ + newents = []; + for ( i = 0; i < ents.size; i++ ) + if ( ents[ i ] != remover ) + newents[ newents.size ] = ents[ i ]; + return newents; +} + +array_remove_index( array, index ) +{ + newArray = []; + keys = getArrayKeys( array ); + for ( i = ( keys.size - 1 );i >= 0 ; i -- ) + { + if ( keys[ i ] != index ) + newArray[ newArray.size ] = array[ keys[ i ] ]; + } + + return newArray; +} + + +string( num ) +{ + return( "" + num ); +} + + +onPlayerConnect() +{ + for(;;) + { + level waittill( "connected", player ); + + player thread onPlayerSpawned(); + } +} + + +onPlayerSpawned() +{ + self endon("disconnect"); + + for(;;) + { + self waittill("spawned_player"); + } +} + + +deleteOnAC130PlayerRemoved() +{ + level waittill ( "ac130player_removed" ); + + self delete(); +} + +setAC130Player( player ) +{ + self endon ( "ac130player_removed" ); + + assert( !isDefined( level.ac130player ) ); + level.ac130player = player; + level.ac130.owner = player; + level.ac130.planeModel show(); + level.ac130.planemodel thread playAC130Effects(); + level.ac130.incomingMissile = false; + + level.ac130.planeModel playLoopSound( "veh_ac130_ext_dist" ); + + level.ac130.planeModel.maxhealth = 1000; + level.ac130.planeModel.health = level.ac130.planeModel.maxhealth; + + objModel = spawnPlane( player, "script_model", level.ac130.planeModel.origin, "compass_objpoint_ac130_friendly", "compass_objpoint_ac130_enemy" ); + objModel notSolid(); + objModel linkTo( level.ac130, "tag_player", ( 0, 80, 32 ), ( 0, -90, 0 ) ); + objModel thread deleteOnAC130PlayerRemoved(); + + player startAC130(); + player openMenu( "ac130timer" ); + + level.ac130.numFlares = level.ac130_num_flares; + +/* + result = player maps\mp\killstreaks\_killstreaks::initRideKillstreak(); + if ( result != "success" ) + { + if ( result != "disconnect" ) + { + if ( result == "fail" ) + player maps\mp\killstreaks\_killstreaks::giveKillstreak( "ac130", player.ac130LifeId == player.pers["deaths"], false ); + + level thread removeAC130Player( player, result == "disconnect" ); + } + + return; + } +*/ + thread teamPlayerCardSplash( "used_ac130", player ); + + player VisionSetThermalForPlayer( "black_bw", 0 ); + player ThermalVisionOn(); + player ThermalVisionFOFOverlayOn(); + + if ( getDvarInt( "camera_thirdPerson" ) ) + player setThirdPersonDOF( false ); + + player _giveWeapon("ac130_105mm_mp"); + player _giveWeapon("ac130_40mm_mp"); + player _giveWeapon("ac130_25mm_mp"); + player SwitchToWeapon("ac130_105mm_mp"); + + player thread overlay( player ); + player thread attachPlayer( player ); + player thread changeWeapons(); + player thread weaponFiredThread(); + player thread thermalVision(); + player thread context_Sensative_Dialog(); + player thread shotFired(); + player thread clouds(); + //thread maps\_ac130_amb::main(); + + player thread removeAC130PlayerAfterTime( level.ac130_use_duration * player.killStreakScaler); + player thread removeAC130PlayerOnDisconnect(); + player thread removeAC130PlayerOnChangeTeams(); + player thread removeAC130PlayerOnSpectate(); + //player thread removeAC130PlayerOnDeath(); + player thread removeAC130PlayerOnCrash(); + //player thread removeAC130PlayerOnGameEnd(); + player thread removeAC130PlayerOnGameCleanup(); + + thread AC130_AltScene(); +} + +playAC130Effects() +{ + wait .05; + PlayFXOnTag( level._effect[ "ac130_light_red_blink" ] , self, "tag_light_belly" ); + PlayFXOnTag( level._effect[ "ac130_engineeffect" ] , self, "tag_body" ); + wait .5; + PlayFXOnTag( level._effect[ "ac130_light_white_blink" ] , self, "tag_light_tail" ); + PlayFXOnTag( level._effect[ "ac130_light_red" ] , self, "tag_light_top" ); +} + +AC130_AltScene() +{ + // need team check + foreach ( player in level.players ) + { + if ( player != level.ac130player && player.team == level.ac130player.team ) + player thread setAltSceneObj( level.ac130.cameraModel, "tag_origin", 20 ); + } +} + + +removeAC130PlayerOnGameEnd() +{ + self endon ( "ac130player_removed" ); + + level waittill ( "game_ended" ); + + level thread removeAC130Player( self, false ); +} + + +removeAC130PlayerOnGameCleanup() +{ + self endon ( "ac130player_removed" ); + + level waittill ( "game_cleanup" ); + + level thread removeAC130Player( self, false ); +} + + +removeAC130PlayerOnDeath() +{ + self endon ( "ac130player_removed" ); + + self waittill ( "death" ); + + level thread removeAC130Player( self, false ); +} + + +removeAC130PlayerOnCrash() +{ + self endon ( "ac130player_removed" ); + + level.ac130.planeModel waittill ( "crashing" ); + + level thread removeAC130Player( self, false ); +} + + +removeAC130PlayerOnDisconnect() +{ + self endon ( "ac130player_removed" ); + + self waittill ( "disconnect" ); + + level thread removeAC130Player( self, true ); +} + +removeAC130PlayerOnChangeTeams() +{ + self endon ( "ac130player_removed" ); + + self waittill ( "joined_team" ); + + level thread removeAC130Player( self, false); +} + +removeAC130PlayerOnSpectate() +{ + self endon ( "ac130player_removed" ); + + self waittill_any ( "joined_spectators", "spawned" ); + + level thread removeAC130Player( self, false); +} + +removeAC130PlayerAfterTime( removeDelay ) +{ + self endon ( "ac130player_removed" ); + + maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( removeDelay ); + + level thread removeAC130Player( self, false ); +} + + +removeAC130Player( player, disconnected ) +{ + player notify ( "ac130player_removed" ); + level notify ( "ac130player_removed" ); + + level.ac130.cameraModel notify ( "death" ); + + waittillframeend; + + if ( !disconnected ) + { + player clearUsingRemote(); + + player stopLocalSound( "missile_incoming" ); + + player show(); + player unlink(); + + player ThermalVisionOff(); + player ThermalVisionFOFOverlayOff(); + player visionSetThermalForPlayer( game["thermal_vision"], 0 ); + player setBlurForPlayer( 0, 0 ); + player stopAC130(); + + if ( getDvarInt( "camera_thirdPerson" ) ) + player setThirdPersonDOF( true ); + + weaponList = player GetWeaponsListExclusives(); + foreach ( weapon in weaponList ) + player takeWeapon( weapon ); + + if ( isDefined( player.darkScreenOverlay ) ) + player.darkScreenOverlay destroy(); + + keys = getArrayKeys( level.HUDItem ); + + foreach ( key in keys ) + { + level.HUDItem[key] destroy(); + level.HUDItem[key] = undefined; + } + } + + // delay before AC130 can be used again + wait ( 0.5 ); + + level.ac130.planeModel playSound( "veh_ac130_ext_dist_fade" ); + + wait ( 0.5 ); + + // TODO: this might already be undefined if the player disconnected... need a better solution. + // we could set it to "true" or something... but we'll have to check places it is used for potential issues with that. + level.ac130player = undefined; + level.ac130.planeModel hide(); + level.ac130.planeModel stopLoopSound(); + + if ( isDefined( level.ac130.planeModel.crashed ) ) + { + level.ac130InUse = false; + return; + } + + ac130model = spawn( "script_model", level.ac130.planeModel getTagOrigin( "tag_origin" ) ); + ac130model.angles = level.ac130.planeModel.angles; + ac130model setModel( "vehicle_ac130_coop" ); + destPoint = ac130model.origin + vector_multiply( anglestoright( ac130model.angles ), 20000 ); + + ac130model thread playAC130Effects(); + ac130model moveTo( destPoint, 40.0, 0.0, 0.0 ); + ac130model thread deployFlares( true ); + + wait ( 5.0 ); + ac130model thread deployFlares( true ); + + wait ( 5.0 ); + ac130model thread deployFlares( true ); + + level.ac130InUse = false; + + wait ( 30.0 ); + + ac130model delete(); +} + + +damageTracker() +{ + for ( ;; ) + { + self waittill ( "damage", damage, attacker, dir, point, type ); + + if ( isDefined( level.ac130player ) && level.teambased && isPlayer( attacker ) && attacker.team == level.ac130player.team && !isDefined( level.nukeDetonated ) ) + { + self.health += damage; + continue; + } + + if ( type == "MOD_RIFLE_BULLET" || type == "MOD_PISTOL_BULLET" || type == "MOD_EXPLOSIVE_BULLET" ) + { + self.health += damage; + continue; + } + + if ( isPlayer( attacker ) ) + { + attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" ); + if ( attacker _hasPerk( "specialty_armorpiercing" ) ) + { + damageAdd = damage*level.armorPiercingMod; + self.health -= int(damageAdd); + } + } + + if ( self.health <= 0 ) + { + + if ( isPlayer( attacker ) ) + { + thread maps\mp\gametypes\_missions::vehicleKilled( level.ac130player, self, undefined, attacker, damage, type ); + thread teamPlayerCardSplash( "callout_destroyed_ac130", attacker ); + attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 400 ); + attacker notify( "destroyed_killstreak" ); + } + + level thread crashPlane( 10.0 ); + } + } +} + + +ac130_spawn() +{ + wait 0.05; + + ac130model = spawn( "script_model", level.ac130 getTagOrigin( "tag_player" ) ); + ac130model setModel( "vehicle_ac130_coop" ); + + ac130model setCanDamage( true ); + ac130model.maxhealth = 1000; + ac130model.health = ac130model.maxhealth; + ac130model thread damageTracker(); + + //ac130model linkTo( level.ac130, "tag_player", ( 0, 0, 32 ), ( -25, 0, 0 ) ); + ac130model linkTo( level.ac130, "tag_player", ( 0, 80, 32 ), ( -25, 0, 0 ) ); + level.ac130.planeModel = ac130model; + level.ac130.planeModel hide(); + + ac130CameraModel = spawn( "script_model", level.ac130 getTagOrigin( "tag_player" ) ); + ac130CameraModel setModel( "tag_origin" ); + ac130CameraModel hide(); + + ac130CameraModel linkTo( level.ac130, "tag_player", ( 0, 0, 32 ), ( -25, 0, 0 ) ); + level.ac130.cameraModel = ac130CameraModel; + + level.ac130player = level.players[0]; +} + + +overlay( player ) +{ + level.HUDItem = []; + + thermalEnabled = getIntProperty( "ac130_hud_text_thermal", 0 ); + if ( thermalEnabled ) + { + level.HUDItem[ "thermal_mode" ] = newClientHudElem( player ); + level.HUDItem[ "thermal_mode" ].x = -80; + level.HUDItem[ "thermal_mode" ].y = 50; + level.HUDItem[ "thermal_mode" ].alignX = "right"; + level.HUDItem[ "thermal_mode" ].alignY = "top"; + level.HUDItem[ "thermal_mode" ].horzAlign = "right"; + level.HUDItem[ "thermal_mode" ].vertAlign = "top"; + level.HUDItem[ "thermal_mode" ].fontScale = 2.5; + level.HUDItem[ "thermal_mode" ] settext ( &"AC130_HUD_THERMAL_WHOT" ); + level.HUDItem[ "thermal_mode" ].alpha = 1.0; + } + + if( !level.splitscreen ) + player thread overlay_coords(); + + if ( thermalEnabled ) + { + player setBlurForPlayer( 1.2, 0 ); + } +} + + +hud_timer( duration ) +{ + self endon ( "ac130player_removed" ); + + level.HUDItem[ "timer" ] = newClientHudElem( self ); + level.HUDItem[ "timer" ].x = -100; + level.HUDItem[ "timer" ].y = 0; + level.HUDItem[ "timer" ].alignX = "right"; + level.HUDItem[ "timer" ].alignY = "bottom"; + level.HUDItem[ "timer" ].horzAlign = "right_adjustable"; + level.HUDItem[ "timer" ].vertAlign = "bottom_adjustable"; + level.HUDItem[ "timer" ].fontScale = 2.5; + level.HUDItem[ "timer" ] setTimer( 1.0 ); + level.HUDItem[ "timer" ].alpha = 1.0; + + level.HUDItem[ "timer" ] setTimer( duration ); +} + + +overlay_coords() +{ + self endon ( "ac130player_removed" ); + + level.HUDItem[ "coordinate_long" ] = newClientHudElem( self ); + level.HUDItem[ "coordinate_long" ].x = -100; + level.HUDItem[ "coordinate_long" ].y = 0; + level.HUDItem[ "coordinate_long" ].alignX = "right"; + level.HUDItem[ "coordinate_long" ].alignY = "top"; + level.HUDItem[ "coordinate_long" ].horzAlign = "right"; + level.HUDItem[ "coordinate_long" ].vertAlign = "top"; + level.HUDItem[ "coordinate_long" ].fontScale = 2.5; + level.HUDItem[ "coordinate_long" ].alpha = 1.0; + + level.HUDItem[ "coordinate_lat" ] = newClientHudElem( self ); + level.HUDItem[ "coordinate_lat" ].x = 0; + level.HUDItem[ "coordinate_lat" ].y = 0; + level.HUDItem[ "coordinate_lat" ].alignX = "right"; + level.HUDItem[ "coordinate_lat" ].alignY = "top"; + level.HUDItem[ "coordinate_lat" ].horzAlign = "right"; + level.HUDItem[ "coordinate_lat" ].vertAlign = "top"; + level.HUDItem[ "coordinate_lat" ].fontScale = 2.5; + level.HUDItem[ "coordinate_lat" ].alpha = 1.0; + + level.HUDItem[ "coordinate_agl" ] = newClientHudElem( self ); + level.HUDItem[ "coordinate_agl" ].x = 0; + level.HUDItem[ "coordinate_agl" ].y = 20; + level.HUDItem[ "coordinate_agl" ].alignX = "right"; + level.HUDItem[ "coordinate_agl" ].alignY = "top"; + level.HUDItem[ "coordinate_agl" ].horzAlign = "right"; + level.HUDItem[ "coordinate_agl" ].vertAlign = "top"; + level.HUDItem[ "coordinate_agl" ].fontScale = 2.5; + level.HUDItem[ "coordinate_agl" ].label = ( &"AC130_HUD_AGL" ); + level.HUDItem[ "coordinate_agl" ].alpha = 1.0; + + wait 0.05; + for(;;) + { + level.HUDItem[ "coordinate_long" ] setValue( abs( int( self.origin[0] ) ) ); + level.HUDItem[ "coordinate_lat" ] setValue( abs( int( self.origin[1] ) ) ); + + pos = physicstrace( self.origin, self.origin - ( 0, 0, 100000 ) ); + if( ( isdefined( pos ) ) && ( isdefined( pos[2] ) ) ) + { + alt = ( ( self.origin[2] - pos[2] ) * 1.5 ); + level.HUDItem[ "coordinate_agl" ] setValue( abs( int( alt ) ) ); + } + + wait ( 0.75 + randomfloat( 2 ) ); + } +} + + +ac130ShellShock() +{ + self endon ( "ac130player_removed" ); + + level endon( "post_effects_disabled" ); + duration = 5; + for (;;) + { + self shellshock( "ac130", duration ); + wait duration; + } +} + + +rotatePlane( toggle ) +{ + level notify("stop_rotatePlane_thread"); + level endon("stop_rotatePlane_thread"); + + if (toggle == "on") + { + rampupDegrees = 10; + rotateTime = ( level.ac130_Speed[ "rotate" ] / 360 ) * rampupDegrees; + level.ac130 rotateyaw( level.ac130.angles[ 2 ] + rampupDegrees, rotateTime, rotateTime, 0 ); + + for (;;) + { + level.ac130 rotateyaw( 360, level.ac130_Speed[ "rotate" ] ); + wait level.ac130_Speed[ "rotate" ]; + } + } + else if (toggle == "off") + { + slowdownDegrees = 10; + rotateTime = ( level.ac130_Speed[ "rotate" ] / 360 ) * slowdownDegrees; + level.ac130 rotateyaw( level.ac130.angles[ 2 ] + slowdownDegrees, rotateTime, 0, rotateTime ); + } +} + + +attachPlayer( player ) +{ + self PlayerLinkWeaponviewToDelta( level.ac130, "tag_player", 1.0, 35, 35, 35, 35 ); + self setPlayerAngles( level.ac130 getTagAngles( "tag_player" ) ); +} + + +changeWeapons() +{ + self endon ( "ac130player_removed" ); + + wait( 0.05 ); + self EnableWeapons(); + + for(;;) + { + self waittill ( "change_weapon", newWeapon ); + + self thread play_sound_on_entity( "ac130_weapon_switch" ); + } +} + + +weaponFiredThread() +{ + self endon ( "ac130player_removed" ); + + for(;;) + { + self waittill( "weapon_fired" ); + + weapon = self getCurrentWeapon(); + + if ( weapon == "ac130_105mm_mp" ) + { + self thread gun_fired_and_ready_105mm(); + earthquake (0.2, 1, level.ac130.planeModel.origin, 1000); + } + else if ( weapon == "ac130_40mm_mp" ) + { + earthquake (0.1, 0.5, level.ac130.planeModel.origin, 1000); + } + + if ( self getWeaponAmmoClip( weapon ) ) + continue; + + self thread weaponReload( weapon ); + } +} + + +weaponReload( weapon ) +{ + self endon ( "ac130player_removed" ); + + wait level.weaponReloadTime[ weapon ]; + + self setWeaponAmmoClip( weapon, 9999 ); + + // force the reload to stop if we're currently using the weapon + if ( self getCurrentWeapon() == weapon ) + { + self takeWeapon( weapon ); + self _giveWeapon( weapon ); + self switchToWeapon( weapon ); + } +} + + +thermalVision() +{ + self endon ( "ac130player_removed" ); + + if ( getIntProperty( "ac130_thermal_enabled", 1 ) == 0 ) + return; + + inverted = false; + + self visionSetThermalForPlayer( game["thermal_vision"], 1 ); + + self notifyOnPlayerCommand( "switch thermal", "+activate" ); + + for (;;) + { + self waittill ( "switch thermal" ); + + if ( !inverted ) + { + self visionSetThermalForPlayer( "missilecam", 0.62 ); + if ( isdefined( level.HUDItem[ "thermal_mode" ] ) ) + level.HUDItem[ "thermal_mode" ] settext ( &"AC130_HUD_THERMAL_BHOT" ); + } + else + { + self visionSetThermalForPlayer( game["thermal_vision"], 0.51 ); + if ( isdefined( level.HUDItem[ "thermal_mode" ] ) ) + level.HUDItem[ "thermal_mode" ] settext ( &"AC130_HUD_THERMAL_WHOT" ); + } + + inverted = !inverted; + } +} + + + + +clouds() +{ + self endon ( "ac130player_removed" ); + + wait 6; + clouds_create(); + for(;;) + { + wait( randomfloatrange( 40, 80 ) ); + clouds_create(); + } +} + + +clouds_create() +{ + if ( ( isdefined( level.playerWeapon ) ) && ( issubstr( tolower( level.playerWeapon ), "25" ) ) ) + return; + playfxontagforclients( level._effect[ "cloud" ], level.ac130, "tag_player", level.ac130player ); +} + + +gun_fired_and_ready_105mm() +{ + self endon ( "ac130player_removed" ); + level notify( "gun_fired_and_ready_105mm" ); + level endon( "gun_fired_and_ready_105mm" ); + + wait 0.5; + + if ( randomint( 2 ) == 0 ) + thread context_Sensative_Dialog_Play_Random_Group_Sound( "weapons", "105mm_fired" ); + + wait 5.0; + + thread context_Sensative_Dialog_Play_Random_Group_Sound( "weapons", "105mm_ready" ); +} + + +shotFired() +{ + self endon ( "ac130player_removed" ); + + for (;;) + { + self waittill( "projectile_impact", weaponName, position, radius ); + + if ( issubstr( tolower( weaponName ), "105" ) ) + { + earthquake( 0.4, 1.0, position, 3500 ); + self thread shotFiredDarkScreenOverlay(); + } + else if ( issubstr( tolower( weaponName ), "40" ) ) + { + earthquake( 0.2, 0.5, position, 2000 ); + } + + if ( getIntProperty( "ac130_ragdoll_deaths", 0 ) ) + thread shotFiredPhysicsSphere( position, weaponName ); + + wait 0.05; + } +} + + +shotFiredPhysicsSphere( center, weapon ) +{ + wait 0.1; + physicsExplosionSphere( center, level.physicsSphereRadius[ weapon ], level.physicsSphereRadius[ weapon ] / 2, level.physicsSphereForce[ weapon ] ); +} + + +shotFiredDarkScreenOverlay() +{ + self endon( "ac130player_removed" ); + self notify( "darkScreenOverlay" ); + self endon( "darkScreenOverlay" ); + + if ( !isdefined( self.darkScreenOverlay ) ) + { + self.darkScreenOverlay = newClientHudElem( self ); + self.darkScreenOverlay.x = 0; + self.darkScreenOverlay.y = 0; + self.darkScreenOverlay.alignX = "left"; + self.darkScreenOverlay.alignY = "top"; + self.darkScreenOverlay.horzAlign = "fullscreen"; + self.darkScreenOverlay.vertAlign = "fullscreen"; + self.darkScreenOverlay setshader ( "black", 640, 480 ); + self.darkScreenOverlay.sort = -10; + self.darkScreenOverlay.alpha = 0.0; + } + + self.darkScreenOverlay.alpha = 0.0; + self.darkScreenOverlay fadeOverTime( 0.2 ); + self.darkScreenOverlay.alpha = 0.6; + wait 0.4; + self.darkScreenOverlay fadeOverTime( 0.8 ); + self.darkScreenOverlay.alpha = 0.0; +} + + +add_beacon_effect() +{ + self endon( "death" ); + + flashDelay = 0.75; + + wait randomfloat(3.0); + for (;;) + { + if ( level.ac130player ) + playfxontagforclients( level._effect[ "beacon" ], self, "j_spine4", level.ac130player ); + wait flashDelay; + } +} + + +context_Sensative_Dialog() +{ + thread enemy_killed_thread(); + + thread context_Sensative_Dialog_Guy_In_Sight(); + thread context_Sensative_Dialog_Guy_Crawling(); + thread context_Sensative_Dialog_Guy_Pain(); + thread context_Sensative_Dialog_Secondary_Explosion_Vehicle(); + thread context_Sensative_Dialog_Kill_Thread(); + thread context_Sensative_Dialog_Locations(); + thread context_Sensative_Dialog_Filler(); +} + + +context_Sensative_Dialog_Guy_In_Sight() +{ + self endon ( "ac130player_removed" ); + + for (;;) + { + if ( context_Sensative_Dialog_Guy_In_Sight_Check() ) + thread context_Sensative_Dialog_Play_Random_Group_Sound( "ai", "in_sight" ); + wait randomfloatrange( 1, 3 ); + } +} + + +context_Sensative_Dialog_Guy_In_Sight_Check() +{ + prof_begin( "AI_in_sight_check" ); + + //enemies = getaiarray( "axis" ); + //replace with level of enemy team members? + enemies = []; + + for( i = 0 ; i < enemies.size ; i++ ) + { + if ( !isdefined( enemies[ i ] ) ) + continue; + + if ( !isalive( enemies[ i ] ) ) + continue; + + if ( within_fov( level.ac130player getEye(), level.ac130player getPlayerAngles(), enemies[ i ].origin, level.cosine[ "5" ] ) ) + { + prof_end( "AI_in_sight_check" ); + return true; + } + wait 0.05; + } + + prof_end( "AI_in_sight_check" ); + return false; +} + + +context_Sensative_Dialog_Guy_Crawling() +{ + self endon ( "ac130player_removed" ); + + for (;;) + { + level waittill ( "ai_crawling", guy ); + +/# + if ( ( isdefined( guy ) ) && ( isdefined( guy.origin ) ) ) + { + if ( getdvar( "ac130_debug_context_sensative_dialog", 0 ) == "1" ) + thread debug_line(level.ac130player.origin, guy.origin, 5.0, ( 0, 1, 0 ) ); + } +#/ + thread context_Sensative_Dialog_Play_Random_Group_Sound( "ai", "wounded_crawl" ); + } +} + + +context_Sensative_Dialog_Guy_Pain() +{ + self endon ( "ac130player_removed" ); + + for (;;) + { + level waittill ( "ai_pain", guy ); +/# + if ( ( isdefined( guy ) ) && ( isdefined( guy.origin ) ) ) + { + if ( getdvar( "ac130_debug_context_sensative_dialog" ) == "1" ) + thread debug_line( level.ac130player.origin, guy.origin, 5.0, ( 1, 0, 0 ) ); + } +#/ + thread context_Sensative_Dialog_Play_Random_Group_Sound( "ai", "wounded_pain" ); + } +} + + +context_Sensative_Dialog_Secondary_Explosion_Vehicle() +{ + self endon ( "ac130player_removed" ); + + for (;;) + { + level waittill ( "player_destroyed_car", player, vehicle_origin ); + + wait 1; +/# + if ( isdefined( vehicle_origin ) ) + { + if ( getdvar( "ac130_debug_context_sensative_dialog" ) == "1" ) + thread debug_line( level.ac130player.origin, vehicle_origin, 5.0, ( 0, 0, 1 ) ); + } +#/ + + thread context_Sensative_Dialog_Play_Random_Group_Sound( "explosion", "secondary" ); + } +} + + +enemy_killed_thread() +{ + self endon ( "ac130player_removed" ); + + for ( ;; ) + { + level waittill ( "ai_killed", guy ); + + // context kill dialog + thread context_Sensative_Dialog_Kill( guy, level.ac130player ); + } +} + + +context_Sensative_Dialog_Kill( guy, attacker ) +{ + if ( !isdefined( attacker ) ) + return; + + if ( !isplayer( attacker ) ) + return; + + level.enemiesKilledInTimeWindow++; + level notify ( "enemy_killed" ); + +/# + if ( ( isdefined( guy ) ) && ( isdefined( guy.origin ) ) ) + { + if ( getdvar( "ac130_debug_context_sensative_dialog" ) == "1" ) + thread debug_line( level.ac130player.origin, guy.origin, 5.0, ( 1, 1, 0 ) ); + } +#/ + +} + + +context_Sensative_Dialog_Kill_Thread() +{ + self endon ( "ac130player_removed" ); + + timeWindow = 1; + for (;;) + { + level waittill ( "enemy_killed" ); + wait timeWindow; + println ( "guys killed in time window: " ); + println ( level.enemiesKilledInTimeWindow ); + + soundAlias1 = "kill"; + soundAlias2 = undefined; + + if ( level.enemiesKilledInTimeWindow >= 2 ) + soundAlias2 = "small_group"; + else + { + soundAlias2 = "single"; + if ( randomint( 3 ) != 1 ) + { + level.enemiesKilledInTimeWindow = 0; + continue; + } + } + + level.enemiesKilledInTimeWindow = 0; + assert( isdefined( soundAlias2 ) ); + + thread context_Sensative_Dialog_Play_Random_Group_Sound( soundAlias1, soundAlias2, true ); + } +} + + +context_Sensative_Dialog_Locations() +{ + array_thread( getentarray( "context_dialog_car", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "car" ); + array_thread( getentarray( "context_dialog_truck", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "truck" ); + array_thread( getentarray( "context_dialog_building", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "building" ); + array_thread( getentarray( "context_dialog_wall", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "wall" ); + array_thread( getentarray( "context_dialog_field", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "field" ); + array_thread( getentarray( "context_dialog_road", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "road" ); + array_thread( getentarray( "context_dialog_church", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "church" ); + array_thread( getentarray( "context_dialog_ditch", "targetname" ), ::context_Sensative_Dialog_Locations_Add_Notify_Event, "ditch" ); + + thread context_Sensative_Dialog_Locations_Thread(); +} + +context_Sensative_Dialog_Locations_Thread() +{ + self endon ( "ac130player_removed" ); + + for (;;) + { + level waittill ( "context_location", locationType ); + + if ( !isdefined( locationType ) ) + { + assertMsg( "LocationType " + locationType + " is not valid" ); + continue; + } + + if ( !flag( "allow_context_sensative_dialog" ) ) + continue; + + thread context_Sensative_Dialog_Play_Random_Group_Sound( "location", locationType ); + + wait ( 5 + randomfloat( 10 ) ); + } +} + +context_Sensative_Dialog_Locations_Add_Notify_Event( locationType ) +{ + self endon ( "ac130player_removed" ); + + for (;;) + { + self waittill ( "trigger", triggerer ); + + if ( !isdefined( triggerer ) ) + continue; + + if ( ( !isdefined( triggerer.team) ) || ( triggerer.team != "axis" ) ) + continue; + + level notify ( "context_location", locationType ); + + wait 5; + } +} + +context_Sensative_Dialog_VehicleSpawn( vehicle ) +{ + if ( vehicle.script_team != "axis" ) + return; + + thread context_Sensative_Dialog_VehicleDeath( vehicle ); + + vehicle endon( "death" ); + + while( !within_fov( level.ac130player getEye(), level.ac130player getPlayerAngles(), vehicle.origin, level.cosine[ "45" ] ) ) + wait 0.5; + + context_Sensative_Dialog_Play_Random_Group_Sound( "vehicle", "incoming" ); +} + +context_Sensative_Dialog_VehicleDeath( vehicle ) +{ + vehicle waittill( "death" ); + thread context_Sensative_Dialog_Play_Random_Group_Sound( "vehicle", "death" ); +} + +context_Sensative_Dialog_Filler() +{ + self endon ( "ac130player_removed" ); + + for(;;) + { + if( ( isdefined( level.radio_in_use ) ) && ( level.radio_in_use == true ) ) + level waittill ( "radio_not_in_use" ); + + // if 3 seconds has passed and nothing has been transmitted then play a sound + currentTime = getTime(); + if ( ( currentTime - level.lastRadioTransmission ) >= 3000 ) + { + level.lastRadioTransmission = currentTime; + thread context_Sensative_Dialog_Play_Random_Group_Sound( "misc", "action" ); + } + + wait 0.25; + } +} + +context_Sensative_Dialog_Play_Random_Group_Sound( name1, name2, force_transmit_on_turn ) +{ + level endon ( "ac130player_removed" ); + + assert( isdefined( level.scr_sound[ name1 ] ) ); + assert( isdefined( level.scr_sound[ name1 ][ name2 ] ) ); + + if ( !isdefined( force_transmit_on_turn ) ) + force_transmit_on_turn = false; + + if ( !flag( "allow_context_sensative_dialog" ) ) + { + if ( force_transmit_on_turn ) + flag_wait( "allow_context_sensative_dialog" ); + else + return; + } + + validGroupNum = undefined; + + randGroup = randomint( level.scr_sound[ name1 ][ name2 ].size ); + + // if randGroup has already played + if ( level.scr_sound[ name1 ][ name2 ][ randGroup ].played == true ) + { + //loop through all groups and use the next one that hasn't played yet + + for( i = 0 ; i < level.scr_sound[ name1 ][ name2 ].size ; i++ ) + { + randGroup++; + if ( randGroup >= level.scr_sound[ name1 ][ name2 ].size ) + randGroup = 0; + if ( level.scr_sound[ name1 ][ name2 ][ randGroup ].played == true ) + continue; + validGroupNum = randGroup; + break; + } + + // all groups have been played, reset all groups to false and pick a new random one + if ( !isdefined( validGroupNum ) ) + { + for( i = 0 ; i < level.scr_sound[ name1 ][ name2 ].size ; i++ ) + level.scr_sound[ name1 ][ name2 ][ i ].played = false; + validGroupNum = randomint( level.scr_sound[ name1 ][ name2 ].size ); + } + } + else + validGroupNum = randGroup; + + assert( isdefined( validGroupNum ) ); + assert( validGroupNum >= 0 ); + + if ( context_Sensative_Dialog_Timedout( name1, name2, validGroupNum ) ) + return; + + level.scr_sound[ name1 ][ name2 ][ validGroupNum ].played = true; + randSound = randomint( level.scr_sound[ name1 ][ name2 ][ validGroupNum ].size ); + playSoundOverRadio( level.scr_sound[ name1 ][ name2 ][ validGroupNum ].sounds[ randSound ], force_transmit_on_turn ); +} + +context_Sensative_Dialog_Timedout( name1, name2, groupNum ) +{ + // dont play this sound if it has a timeout specified and the timeout has not expired + + if( !isdefined( level.context_sensative_dialog_timeouts ) ) + return false; + + if( !isdefined( level.context_sensative_dialog_timeouts[ name1 ] ) ) + return false; + + if( !isdefined( level.context_sensative_dialog_timeouts[ name1 ][name2 ] ) ) + return false; + + if( isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups ) && isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ] ) ) + { + assert( isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ].v[ "timeoutDuration" ] ) ); + assert( isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ].v[ "lastPlayed" ] ) ); + + currentTime = getTime(); + if( ( currentTime - level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ].v[ "lastPlayed" ] ) < level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ].v[ "timeoutDuration" ] ) + return true; + + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].groups[ string( groupNum ) ].v[ "lastPlayed" ] = currentTime; + } + else if ( isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v ) ) + { + assert( isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v[ "timeoutDuration" ] ) ); + assert( isdefined( level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v[ "lastPlayed" ] ) ); + + currentTime = getTime(); + if( ( currentTime - level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v[ "lastPlayed" ] ) < level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v[ "timeoutDuration" ] ) + return true; + + level.context_sensative_dialog_timeouts[ name1 ][ name2 ].v[ "lastPlayed" ] = currentTime; + } + + return false; +} + +playSoundOverRadio( soundAlias, force_transmit_on_turn, timeout ) +{ + if ( !isdefined( level.radio_in_use ) ) + level.radio_in_use = false; + if ( !isdefined( force_transmit_on_turn ) ) + force_transmit_on_turn = false; + if ( !isdefined( timeout ) ) + timeout = 0; + timeout = timeout * 1000; + soundQueueTime = gettime(); + + soundPlayed = false; + soundPlayed = playAliasOverRadio( soundAlias ); + if ( soundPlayed ) + return; + + // Dont make the sound wait to be played if force transmit wasn't set to true + if ( !force_transmit_on_turn ) + return; + + level.radioForcedTransmissionQueue[ level.radioForcedTransmissionQueue.size ] = soundAlias; + while( !soundPlayed ) + { + if ( level.radio_in_use ) + level waittill ( "radio_not_in_use" ); + + if ( ( timeout > 0 ) && ( getTime() - soundQueueTime > timeout ) ) + break; + + if ( !isDefined( level.ac130player ) ) + break; + + soundPlayed = playAliasOverRadio( level.radioForcedTransmissionQueue[ 0 ] ); + if ( !level.radio_in_use && isDefined( level.ac130player ) && !soundPlayed ) + assertMsg( "The radio wasn't in use but the sound still did not play. This should never happen." ); + } + level.radioForcedTransmissionQueue = array_remove_index( level.radioForcedTransmissionQueue, 0 ); +} + + +playAliasOverRadio( soundAlias ) +{ + if ( level.radio_in_use ) + return false; + + if ( !isDefined( level.ac130player ) ) + return false; + + level.radio_in_use = true; + if ( self.team == "allies" || self.team == "axis" ) + { + soundAlias = maps\mp\gametypes\_teams::getTeamVoicePrefix( self.team ) + soundAlias; + level.ac130player playLocalSound( soundAlias ); + } + wait ( 4.0 ); + level.radio_in_use = false; + level.lastRadioTransmission = getTime(); + level notify ( "radio_not_in_use" ); + return true; +} + + +debug_circle(center, radius, duration, color, startDelay, fillCenter) +{ + circle_sides = 16; + + angleFrac = 360/circle_sides; + circlepoints = []; + for(i=0;i= circlepoints.size) + end = circlepoints[0]; + else + end = circlepoints[i + 1]; + + thread debug_line( start, end, duration, color); + + if (fillCenter) + thread debug_line( center, start, duration, color); + } +} + + +debug_line(start, end, duration, color) +{ + if (!isdefined(color)) + color = (1,1,1); + + for ( i = 0; i < (duration * 20) ; i++ ) + { + line(start, end, color); + wait 0.05; + } +} + + +handleIncomingStinger() +{ + level endon ( "game_ended" ); + + for ( ;; ) + { + level waittill ( "stinger_fired", player, missile, lockTarget ); + + if ( !IsDefined( lockTarget ) || (lockTarget != level.ac130.planeModel) ) + continue; + + missile thread stingerProximityDetonate( player, player.team ); + } +} + +deleteAfterTime( delay ) +{ + wait ( delay ); + + self delete(); +} + + +stingerProximityDetonate( player, missileTeam ) +{ + self endon ( "death" ); + + if ( isDefined( level.ac130player ) ) + level.ac130player playLocalSound( "missile_incoming" ); + + level.ac130.incomingMissile = true; + + missileTarget = level.ac130.planeModel; + + self Missile_SetTargetEnt( missileTarget ); + + didSeatbelts = false; + minDist = distance( self.origin, missileTarget GetPointInBounds( 0, 0, 0 ) ); + + for ( ;; ) + { + center = missileTarget GetPointInBounds( 0, 0, 0 ); + + curDist = distance( self.origin, center ); + + if ( !isDefined( level.ac130player ) ) + { + self Missile_SetTargetPos( level.ac130.origin + (0,0,100000) ); + return; + } + + if ( curDist < 3000 && missileTarget == level.ac130.planeModel && level.ac130.numFlares > 0 ) + { + level.ac130.numFlares--; + + newTarget = missileTarget deployFlares(); + + self Missile_SetTargetEnt( newTarget ); + missileTarget = newTarget; + + if ( isDefined( level.ac130player ) ) + level.ac130player stopLocalSound( "missile_incoming" ); + } + + if ( curDist < minDist ) + { + speedPerFrame = (minDist - curDist) * 20; + eta = (curDist / speedPerFrame); + + if ( eta < 1.5 && !didSeatbelts && missileTarget == level.ac130.planeModel ) + { + if ( isDefined( level.ac130player ) ) + level.ac130player playLocalSound( "fasten_seatbelts" ); + + didSeatbelts = true; + } + + minDist = curDist; + } + + if ( curDist > minDist ) + { + if ( curDist > 1536 ) + return; + + if ( isDefined( level.ac130player ) ) + { + level.ac130player stopLocalSound( "missile_incoming" ); + + if ( level.ac130player.team != missileTeam ) + radiusDamage( self.origin, 1000, 1000, 1000, player ); + } + + /* + playFx( level.stingerFXid, self.origin ); + //thread crashPlane( 20.0 ); + + self playSound( "remotemissile_explode" ); + */ + self hide(); + + wait ( 0.05 ); + self delete(); + } + + wait ( 0.05 ); + } +} + + + +crashPlane( crashTime ) +{ + level.ac130.planeModel notify ( "crashing" ); + level.ac130.planeModel.crashed = true; + + playFxOnTag( level._effect[ "ac130_explode" ], level.ac130.planeModel, "tag_deathfx" ); + wait .25; + + level.ac130.planeModel hide(); +} + + +playFlareFx( flareCount ) +{ + for ( i = 0; i < flareCount; i++ ) + { + self thread angel_flare(); + + wait ( randomFloatRange( 0.1, 0.25 ) ); + } +} + + +deployFlares( fxOnly ) +{ + self playSound( "ac130_flare_burst" ); + + if ( !isDefined( fxOnly ) ) + { + flareObject = spawn( "script_origin", level.ac130.planemodel.origin ); + flareObject.angles = level.ac130.planemodel.angles; + + flareObject moveGravity( (0, 0, 0), 5.0 ); + + self thread playFlareFx( 10 ); + + flareObject thread deleteAfterTime( 5.0 ); + + return flareObject; + } + else + { + self thread playFlareFx( 5 ); + } +} + + +angelFlarePrecache() +{ + precacheModel( "angel_flare_rig" ); + + precacheMpAnim( "ac130_angel_flares01" ); + precacheMpAnim( "ac130_angel_flares02" ); + precacheMpAnim( "ac130_angel_flares03" ); + + level._effect[ "angel_flare_geotrail" ] = loadfx( "smoke/angel_flare_geotrail" ); + level._effect[ "angel_flare_swirl" ] = loadfx( "smoke/angel_flare_swirl_runner" ); +} + +angel_flare() +{ + rig = spawn( "script_model", self.origin ); + rig setModel( "angel_flare_rig" ); + + rig.origin = self getTagOrigin( "tag_flash_flares" ); + rig.angles = self getTagAngles( "tag_flash_flares" ); + + rig.angles = (rig.angles[0],rig.angles[1] + 180,rig.angles[2] + -90); + + fx_id = level._effect[ "angel_flare_geotrail" ]; + + rig ScriptModelPlayAnim( "ac130_angel_flares0" + (randomInt( 3 )+1) ); + + wait 0.1; + PlayFXOnTag( fx_id, rig, "flare_left_top" ); + PlayFXOnTag( fx_id, rig, "flare_right_top" ); + wait 0.05; + PlayFXOnTag( fx_id, rig, "flare_left_bot" ); + PlayFXOnTag( fx_id, rig, "flare_right_bot" ); + + //rig waittillmatch( "flare_anim", "end" ); + wait ( 3.0 ); + + StopFXOnTag( fx_id, rig, "flare_left_top" ); + StopFXOnTag( fx_id, rig, "flare_right_top" ); + StopFXOnTag( fx_id, rig, "flare_left_bot" ); + StopFXOnTag( fx_id, rig, "flare_right_bot" ); + + rig delete(); +} + +/* +#using_animtree( "script_model" ); +angel_flare_rig_anims() +{ + level.scr_animtree[ "angel_flare_rig" ] = #animtree; + level.scr_model[ "angel_flare_rig" ] = "angel_flare_rig"; + + level.scr_anim[ "angel_flare_rig" ][ "ac130_angel_flares" ][0] = %ac130_angel_flares01; + level.scr_anim[ "angel_flare_rig" ][ "ac130_angel_flares" ][1] = %ac130_angel_flares02; + level.scr_anim[ "angel_flare_rig" ][ "ac130_angel_flares" ][2] = %ac130_angel_flares03; + +} + +assign_model() +{ + AssertEx( IsDefined( level.scr_model[ self.animname ] ), "There is no level.scr_model for animname " + self.animname ); + + if ( IsArray( level.scr_model[ self.animname ] ) ) + { + randIndex = RandomInt( level.scr_model[ self.animname ].size ); + self SetModel( level.scr_model[ self.animname ][ randIndex ] ); + } + else + self SetModel( level.scr_model[ self.animname ] ); +} +assign_animtree( animname ) +{ + if ( IsDefined( animname ) ) + self.animname = animname; + + AssertEx( IsDefined( level.scr_animtree[ self.animname ] ), "There is no level.scr_animtree for animname " + self.animname ); + self UseAnimTree( level.scr_animtree[ self.animname ] ); +} + +spawn_anim_model( animname, origin ) +{ + if ( !isdefined( origin ) ) + origin = ( 0, 0, 0 ); + model = Spawn( "script_model", origin ); + model.animname = animname; + model assign_animtree(); + model assign_model(); + return model; +} + + +angel_flare_burst( flare_count ) +{ + // Angel Flare Swirl + PlayFXOnTag( getfx( "angel_flare_swirl" ), self, "tag_flash_flares" ); + + // Angel Flare Trails + for( i=0; i 1 ) + { + self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" ); + return false; + } + break; + case "super": + break; + } + + result = self selectAirstrikeLocation( lifeId, airStrikeType ); + + if ( !isDefined( result ) || !result ) + return false; + + return true; +} + + +doAirstrike( lifeId, origin, yaw, owner, team ) +{ + assert( isDefined( origin ) ); + assert( isDefined( yaw ) ); + + if ( isDefined( self.airStrikeType ) ) + airstrikeType = self.airStrikeType; + else + airstrikeType = "default"; + + if ( airStrikeType == "harrier" ) + level.planes++; + + if ( isDefined( level.airstrikeInProgress ) ) + { + while ( isDefined( level.airstrikeInProgress ) ) + level waittill ( "begin_airstrike" ); + + level.airstrikeInProgress = true; + wait ( 2.0 ); + } + + if ( !isDefined( owner ) ) + { + if ( airStrikeType == "harrier" ) + level.planes--; + + return; + } + + level.airstrikeInProgress = true; + + num = 17 + randomint(3); + trace = bullettrace(origin, origin + (0,0,-1000000), false, undefined); + targetpos = trace["position"]; + + if ( level.teambased ) + { + players = level.players; + + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[i]; + playerteam = player.pers["team"]; + if ( isdefined( playerteam ) ) + { + if ( playerteam == team && self.airStrikeType != "stealth" ) + player iprintln( &"MP_WAR_AIRSTRIKE_INBOUND", owner ); + } + } + } + else + { + if ( !level.hardcoreMode ) + { + if ( pointIsInAirstrikeArea( owner.origin, targetpos, yaw, airstrikeType ) ) + owner iprintlnbold(&"MP_WAR_AIRSTRIKE_INBOUND_NEAR_YOUR_POSITION"); + } + } + + dangerCenter = spawnstruct(); + dangerCenter.origin = targetpos; + dangerCenter.forward = anglesToForward( (0,yaw,0) ); + dangerCenter.airstrikeType = airstrikeType; + + level.artilleryDangerCenters[ level.artilleryDangerCenters.size ] = dangerCenter; + /# level thread debugArtilleryDangerCenters( airstrikeType ); #/ + + harrierEnt = callStrike( lifeId, owner, targetpos, yaw ); + + wait( 1.0 ); + level.airstrikeInProgress = undefined; + owner notify ( "begin_airstrike" ); + level notify ( "begin_airstrike" ); + + wait 7.5; + + found = false; + newarray = []; + for ( i = 0; i < level.artilleryDangerCenters.size; i++ ) + { + if ( !found && level.artilleryDangerCenters[i].origin == targetpos ) + { + found = true; + continue; + } + + newarray[ newarray.size ] = level.artilleryDangerCenters[i]; + } + assert( found ); + assert( newarray.size == level.artilleryDangerCenters.size - 1 ); + level.artilleryDangerCenters = newarray; + + if ( airStrikeType != "harrier" ) + return; + + while ( isDefined( harrierEnt ) ) + wait ( 0.1 ); + + level.planes--; +} + + +clearProgress( delay ) +{ + wait ( 2.0 ); + + level.airstrikeInProgress = undefined; +} + + +/# +debugArtilleryDangerCenters( airstrikeType ) +{ + level notify("debugArtilleryDangerCenters_thread"); + level endon("debugArtilleryDangerCenters_thread"); + + if ( getdvarint("scr_airstrikedebug") != 1 && getdvarint("scr_spawnpointdebug") == 0 ) + { + return; + } + + while( level.artilleryDangerCenters.size > 0 ) + { + for ( i = 0; i < level.artilleryDangerCenters.size; i++ ) + { + origin = level.artilleryDangerCenters[i].origin; + forward = level.artilleryDangerCenters[i].forward; + + origin += forward * level.dangerForwardPush[airstrikeType] * level.dangerMaxRadius[airstrikeType]; + + previnnerpos = (0,0,0); + prevouterpos = (0,0,0); + for ( j = 0; j <= 40; j++ ) + { + frac = (j * 1.0) / 40; + angle = frac * 360; + dir = anglesToForward((0,angle,0)); + forwardPart = vectordot( dir, forward ) * forward; + perpendicularPart = dir - forwardPart; + pos = forwardPart * level.dangerOvalScale[airstrikeType] + perpendicularPart; + innerpos = pos * level.dangerMinRadius[airstrikeType]; + innerpos += origin; + outerpos = pos * level.dangerMaxRadius[airstrikeType]; + outerpos += origin; + + if ( j > 0 ) + { + line( innerpos, previnnerpos, (1, 0, 0) ); + line( outerpos, prevouterpos, (1,.5,.5) ); + } + + previnnerpos = innerpos; + prevouterpos = outerpos; + } + } + wait .05; + } +} +#/ + +getAirstrikeDanger( point ) +{ + danger = 0; + for ( i = 0; i < level.artilleryDangerCenters.size; i++ ) + { + origin = level.artilleryDangerCenters[i].origin; + forward = level.artilleryDangerCenters[i].forward; + airstrikeType = level.artilleryDangerCenters[i].airstrikeType; + + danger += getSingleAirstrikeDanger( point, origin, forward, airstrikeType ); + } + return danger; +} + +getSingleAirstrikeDanger( point, origin, forward, airstrikeType ) +{ + center = origin + level.dangerForwardPush[airstrikeType] * level.dangerMaxRadius[airstrikeType] * forward; + + diff = point - center; + diff = (diff[0], diff[1], 0); + + forwardPart = vectorDot( diff, forward ) * forward; + perpendicularPart = diff - forwardPart; + + circlePos = perpendicularPart + forwardPart / level.dangerOvalScale[airstrikeType]; + + /* /# + if ( getdvar("scr_airstrikedebug") == "1" ) + { + thread airstrikeLine( center, center + perpendicularPart, (1,1,1), 50 ); + thread airstrikeLine( center + perpendicularPart, center + circlePos, (1,1,1), 50 ); + thread airstrikeLine( center + circlePos, point, (.5,.5,.5), 50 ); + } + #/ */ + + distsq = lengthSquared( circlePos ); + + if ( distsq > level.dangerMaxRadius[airstrikeType] * level.dangerMaxRadius[airstrikeType] ) + return 0; + + if ( distsq < level.dangerMinRadius[airstrikeType] * level.dangerMinRadius[airstrikeType] ) + return 1; + + dist = sqrt( distsq ); + distFrac = (dist - level.dangerMinRadius[airstrikeType]) / (level.dangerMaxRadius[airstrikeType] - level.dangerMinRadius[airstrikeType]); + + assertEx( distFrac >= 0 && distFrac <= 1, distFrac ); + + return 1 - distFrac; +} + + +pointIsInAirstrikeArea( point, targetpos, yaw, airstrikeType ) +{ + return distance2d( point, targetpos ) <= level.dangerMaxRadius[airstrikeType] * 1.25; + // TODO + //return getSingleAirstrikeDanger( point, targetpos, yaw ) > 0; +} + + +losRadiusDamage( pos, radius, max, min, owner, eInflictor, sWeapon ) +{ + ents = maps\mp\gametypes\_weapons::getDamageableEnts(pos, radius, true); + + glassRadiusDamage( pos, radius, max, min ); + + for (i = 0; i < ents.size; i++) + { + if (ents[i].entity == self) + continue; + + dist = distance(pos, ents[i].damageCenter); + + if ( ents[i].isPlayer || ( isDefined( ents[i].isSentry ) && ents[i].isSentry ) ) + { + // check if there is a path to this entity 130 units above his feet. if not, they're probably indoors + indoors = !BulletTracePassed( ents[i].entity.origin, ents[i].entity.origin + (0,0,130), false, undefined ); + if ( indoors ) + { + indoors = !BulletTracePassed( ents[i].entity.origin + (0,0,130), pos + (0,0,130 - 16), false, undefined ); + if ( indoors ) + { + // give them a distance advantage for being indoors. + dist *= 4; + if ( dist > radius ) + continue; + } + } + } + + ents[i].damage = int(max + (min-max)*dist/radius); + ents[i].pos = pos; + ents[i].damageOwner = owner; + ents[i].eInflictor = eInflictor; + level.airStrikeDamagedEnts[level.airStrikeDamagedEntsCount] = ents[i]; + level.airStrikeDamagedEntsCount++; + } + + thread airstrikeDamageEntsThread( sWeapon ); +} + + +airstrikeDamageEntsThread( sWeapon ) +{ + self notify ( "airstrikeDamageEntsThread" ); + self endon ( "airstrikeDamageEntsThread" ); + + for ( ; level.airstrikeDamagedEntsIndex < level.airstrikeDamagedEntsCount; level.airstrikeDamagedEntsIndex++ ) + { + if ( !isDefined( level.airstrikeDamagedEnts[level.airstrikeDamagedEntsIndex] ) ) + continue; + + ent = level.airstrikeDamagedEnts[level.airstrikeDamagedEntsIndex]; + + if ( !isDefined( ent.entity ) ) + continue; + + if ( !ent.isPlayer || isAlive( ent.entity ) ) + { + ent maps\mp\gametypes\_weapons::damageEnt( + ent.eInflictor, // eInflictor = the entity that causes the damage (e.g. a claymore) + ent.damageOwner, // eAttacker = the player that is attacking + ent.damage, // iDamage = the amount of damage to do + "MOD_PROJECTILE_SPLASH", // sMeansOfDeath = string specifying the method of death (e.g. "MOD_PROJECTILE_SPLASH") + sWeapon, // sWeapon = string specifying the weapon used (e.g. "claymore_mp") + ent.pos, // damagepos = the position damage is coming from + vectornormalize(ent.damageCenter - ent.pos) // damagedir = the direction damage is moving in + ); + + level.airstrikeDamagedEnts[level.airstrikeDamagedEntsIndex] = undefined; + + if ( ent.isPlayer ) + wait ( 0.05 ); + } + else + { + level.airstrikeDamagedEnts[level.airstrikeDamagedEntsIndex] = undefined; + } + } +} + + +radiusArtilleryShellshock(pos, radius, maxduration, minduration, team ) +{ + players = level.players; + + foreach ( player in level.players ) + { + if ( !isAlive( player ) ) + continue; + + if ( player.team == team || player.team == "spectator" ) + continue; + + playerPos = player.origin + (0,0,32); + dist = distance( pos, playerPos ); + + if ( dist > radius ) + continue; + + duration = int(maxduration + (minduration-maxduration)*dist/radius); + player thread artilleryShellshock( "default", duration ); + } +} + + +artilleryShellshock(type, duration) +{ + self endon ( "disconnect" ); + + if (isdefined(self.beingArtilleryShellshocked) && self.beingArtilleryShellshocked) + return; + self.beingArtilleryShellshocked = true; + + self shellshock(type, duration); + wait(duration + 1); + + self.beingArtilleryShellshocked = false; +} + + +/# +airstrikeLine( start, end, color, duration ) +{ + frames = duration * 20; + for ( i = 0; i < frames; i++ ) + { + line(start,end,color); + wait .05; + } +} + + +traceBomb() +{ + self endon("death"); + prevpos = self.origin; + while(1) + { + thread airstrikeLine( prevpos, self.origin, (.5,1,0), 40 ); + prevpos = self.origin; + wait .2; + } +} +#/ + + +doBomberStrike( lifeId, owner, requiredDeathCount, bombsite, startPoint, endPoint, bombTime, flyTime, direction, airStrikeType ) +{ + // plane spawning randomness = up to 125 units, biased towards 0 + // radius of bomb damage is 512 + + if ( !isDefined( owner ) ) + return; + + startPathRandomness = 100; + endPathRandomness = 150; + + pathStart = startPoint + ( (randomfloat(2) - 1)*startPathRandomness, (randomfloat(2) - 1)*startPathRandomness, 0 ); + pathEnd = endPoint + ( (randomfloat(2) - 1)*endPathRandomness , (randomfloat(2) - 1)*endPathRandomness , 0 ); + + // Spawn the planes + plane = spawnplane( owner, "script_model", pathStart, "compass_objpoint_b2_airstrike_friendly", "compass_objpoint_b2_airstrike_enemy" ); + + plane playLoopSound( "veh_b2_dist_loop" ); + plane setModel( "vehicle_b2_bomber" ); + plane thread handleEMP( owner ); + plane.lifeId = lifeId; + + plane.angles = direction; + forward = anglesToForward( direction ); + plane moveTo( pathEnd, flyTime, 0, 0 ); + + thread stealthBomber_killCam( plane, pathEnd, flyTime, airStrikeType ); + + thread bomberDropBombs( plane, bombsite, owner ); + + // Delete the plane after its flyby + wait ( flyTime ); + plane notify( "delete" ); + plane delete(); +} + + +bomberDropBombs( plane, bombSite, owner ) +{ + while ( !targetIsClose( plane, bombsite, 5000 ) ) + wait ( 0.05 ); + + //playfxontag( level.stealthbombfx, plane, "tag_left_alamo_missile" ); + //playfxontag( level.stealthbombfx, plane, "tag_right_alamo_missile" ); + + showFx = true; + sonicBoom = false; + + plane notify ( "start_bombing" ); + + plane thread playBombFx(); + + for ( dist = targetGetDist( plane, bombsite ); dist < 5000; dist = targetGetDist( plane, bombsite ) ) + { + if ( dist < 1500 && !sonicBoom ) + { + plane playSound( "veh_b2_sonic_boom" ); + sonicBoom = true; + } + + showFx = !showFx; + if ( dist < 4500 ) + plane thread callStrike_bomb( plane.origin, owner, (0,0,0), showFx ); + wait ( 0.1 ); + } + + plane notify ( "stop_bombing" ); + + //stopfxontag( level.stealthbombfx, plane, "tag_left_alamo_missile" ); + //stopfxontag( level.stealthbombfx, plane, "tag_right_alamo_missile" ); +} + + +playBombFx() +{ + self endon ( "stop_bombing" ); + + for ( ;; ) + { + playFxOnTag( level.stealthbombfx, self, "tag_left_alamo_missile" ); + playFxOnTag( level.stealthbombfx, self, "tag_right_alamo_missile" ); + + wait ( 0.5 ); + } +} + + +stealthBomber_killCam( plane, pathEnd, flyTime, typeOfStrike ) +{ + plane waittill ( "start_bombing" ); + + planedir = anglesToForward( plane.angles ); + + killCamEnt = spawn( "script_model", plane.origin + (0,0,100) - planedir * 200 ); + plane.killCamEnt = killCamEnt; + plane.airstrikeType = typeOfStrike; + killCamEnt.startTime = gettime(); + killCamEnt thread deleteAfterTime( 15.0 ); + + killCamEnt linkTo( plane, "tag_origin", (-256,768,768), ( 0,0,0 ) ); +} + + +callStrike_bomb( coord, owner, offset, showFx ) +{ + if ( !isDefined( owner ) || owner isEMPed() ) + { + self notify( "stop_bombing" ); + return; + } + + accuracyRadius = 512; + + randVec = ( 0, randomint( 360 ), 0 ); + bombPoint = coord + vector_multiply( anglestoforward( randVec ), randomFloat( accuracyRadius ) ); + trace = bulletTrace( bombPoint, bombPoint + (0,0,-10000), false, undefined ); + + bombPoint = trace["position"]; + + bombHeight = distance( coord, bombPoint ); + + if ( bombHeight > 5000 ) + return; + + wait ( 0.85 * (bombHeight / 2000) ); + + if ( !isDefined( owner ) || owner isEMPed() ) + { + self notify( "stop_bombing" ); + return; + } + + if ( showFx ) + { + playFx( level.mortareffect, bombPoint ); + + PlayRumbleOnPosition( "grenade_rumble", bombPoint ); + earthquake( 1.0, 0.6, bombPoint, 2000 ); + } + + thread playSoundInSpace( "exp_airstrike_bomb", bombPoint ); + radiusArtilleryShellshock( bombPoint, 512, 8, 4, owner.team ); + losRadiusDamage( bombPoint + (0,0,16), 896, 300, 50, owner, self, "stealth_bomb_mp" ); // targetpos, radius, maxdamage, mindamage, player causing damage +} + + +doPlaneStrike( lifeId, owner, requiredDeathCount, bombsite, startPoint, endPoint, bombTime, flyTime, direction, typeOfStrike ) +{ + // plane spawning randomness = up to 125 units, biased towards 0 + // radius of bomb damage is 512 + + if ( !isDefined( owner ) ) + return; + + startPathRandomness = 100; + endPathRandomness = 150; + + pathStart = startPoint + ( (randomfloat(2) - 1)*startPathRandomness, (randomfloat(2) - 1)*startPathRandomness, 0 ); + pathEnd = endPoint + ( (randomfloat(2) - 1)*endPathRandomness , (randomfloat(2) - 1)*endPathRandomness , 0 ); + + // Spawn the planes + if( typeOfStrike == "harrier" ) + plane = spawnplane( owner, "script_model", pathStart, "hud_minimap_harrier_green", "hud_minimap_harrier_red" ); + else + plane = spawnplane( owner, "script_model", pathStart, "compass_objpoint_airstrike_friendly", "compass_objpoint_airstrike_busy" ); + + if( typeOfStrike == "harrier" ) + { + if ( owner.team == "allies" ) + plane setModel( "vehicle_av8b_harrier_jet_mp" ); + else + plane setModel( "vehicle_av8b_harrier_jet_opfor_mp" ); + } + else + plane setModel( "vehicle_mig29_desert" ); + + plane playloopsound( "veh_mig29_dist_loop" ); + plane thread handleEMP( owner ); + + plane.lifeId = lifeId; + + plane.angles = direction; + forward = anglesToForward( direction ); + plane thread playPlaneFx(); + plane moveTo( pathEnd, flyTime, 0, 0 ); + + /# + if ( getdvar("scr_airstrikedebug") == "1" ) + thread airstrikeLine( pathStart, pathEnd, (1,1,1), 20 ); + #/ + + //thread callStrike_planeSound( plane, bombsite ); + thread callStrike_bombEffect( plane, pathEnd, flyTime, bombTime - 1.0, owner, requiredDeathCount, typeOfStrike ); + + // Delete the plane after its flyby + wait flyTime; + plane notify( "delete" ); + plane delete(); +} + +callStrike_bombEffect( plane, pathEnd, flyTime, launchTime, owner, requiredDeathCount, typeOfStrike ) +{ + wait ( launchTime ); + + if ( !isDefined( owner )|| owner isEMPed() ) + return; + + plane playSound( "veh_mig29_sonic_boom" ); + planedir = anglesToForward( plane.angles ); + + bomb = spawnbomb( plane.origin, plane.angles ); + bomb moveGravity( vector_multiply( anglestoforward( plane.angles ), 7000/1.5 ), 3.0 ); + + bomb.lifeId = requiredDeathCount; + + killCamEnt = spawn( "script_model", plane.origin + (0,0,100) - planedir * 200 ); + bomb.killCamEnt = killCamEnt; + bomb.airstrikeType = typeOfStrike; + killCamEnt.startTime = gettime(); + killCamEnt thread deleteAfterTime( 15.0 ); + killCamEnt.angles = planedir; + killCamEnt moveTo( pathEnd + (0,0,100), flyTime, 0, 0 ); + + /# + if ( getdvar("scr_airstrikedebug") == "1" ) + bomb thread traceBomb(); + #/ + + wait .4; + //plane stoploopsound(); + killCamEnt moveTo( killCamEnt.origin + planedir * 4000, 1, 0, 0 ); + + wait .45; + killCamEnt moveTo( killCamEnt.origin + (planedir + (0,0,-.2)) * 3500, 2, 0, 0 ); + + wait ( 0.15 ); + + newBomb = spawn( "script_model", bomb.origin ); + newBomb setModel( "tag_origin" ); + newBomb.origin = bomb.origin; + newBomb.angles = bomb.angles; + + bomb setModel( "tag_origin" ); + wait (0.10); // wait two server frames before playing fx + + bombOrigin = newBomb.origin; + bombAngles = newBomb.angles; + playfxontag( level.airstrikefx, newBomb, "tag_origin" ); + + wait .05; + killCamEnt moveTo( killCamEnt.origin + (planedir + (0,0,-.25)) * 2500, 2, 0, 0 ); + + wait .25; + killCamEnt moveTo( killCamEnt.origin + (planedir + (0,0,-.35)) * 2000, 2, 0, 0 ); + + wait .2; + killCamEnt moveTo( killCamEnt.origin + (planedir + (0,0,-.45)) * 1500, 2, 0, 0 ); + + + wait ( 0.5 ); + + repeat = 12; + minAngles = 5; + maxAngles = 55; + angleDiff = (maxAngles - minAngles) / repeat; + + hitpos = (0,0,0); + + for( i = 0; i < repeat; i++ ) + { + traceDir = anglesToForward( bombAngles + (maxAngles-(angleDiff * i),randomInt( 10 )-5,0) ); + traceEnd = bombOrigin + vector_multiply( traceDir, 10000 ); + trace = bulletTrace( bombOrigin, traceEnd, false, undefined ); + + traceHit = trace["position"]; + hitpos += traceHit; + + /# + if ( getdvar("scr_airstrikedebug") == "1" ) + thread airstrikeLine( bombOrigin, traceHit, (1,0,0), 40 ); + #/ + + thread losRadiusDamage( traceHit + (0,0,16), 512, 200, 30, owner, bomb, "artillery_mp" ); // targetpos, radius, maxdamage, mindamage, player causing damage, entity that player used to cause damage + + if ( i%3 == 0 ) + { + thread playsoundinspace( "exp_airstrike_bomb", traceHit ); + playRumbleOnPosition( "artillery_rumble", traceHit ); + earthquake( 0.7, 0.75, traceHit, 1000 ); + } + + wait ( 0.05 ); + } + + hitpos = hitpos / repeat + (0,0,128); + killCamEnt moveto( bomb.killCamEnt.origin * .35 + hitpos * .65, 1.5, 0, .5 ); + + wait ( 5.0 ); + newBomb delete(); + bomb delete(); +} + + +spawnbomb( origin, angles ) +{ + bomb = spawn( "script_model", origin ); + bomb.angles = angles; + bomb setModel( "projectile_cbu97_clusterbomb" ); + + return bomb; +} + + +deleteAfterTime( time ) +{ + self endon ( "death" ); + wait ( 10.0 ); + + self delete(); +} + +playPlaneFx() +{ + self endon ( "death" ); + + wait( 0.5); + playfxontag( level.fx_airstrike_afterburner, self, "tag_engine_right" ); + wait( 0.5); + playfxontag( level.fx_airstrike_afterburner, self, "tag_engine_left" ); + wait( 0.5); + playfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" ); + wait( 0.5); + playfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" ); +} + +callStrike( lifeId, owner, coord, yaw ) +{ + + heightEnt = undefined; + planeBombExplodeDistance = 0; + // Get starting and ending point for the plane + direction = ( 0, yaw, 0 ); + heightEnt = GetEnt( "airstrikeheight", "targetname" ); + + if ( self.airStrikeType == "stealth" ) + { + thread teamPlayerCardSplash( "used_stealth_airstrike", owner, owner.team ); + + planeHalfDistance = 12000; + planeFlySpeed = 2000; + + if ( !isDefined( heightEnt ) )//old system + { + println( "NO DEFINED AIRSTRIKE HEIGHT SCRIPT_ORIGIN IN LEVEL" ); + planeFlyHeight = 950; + planeBombExplodeDistance = 1500; + if ( isdefined( level.airstrikeHeightScale ) ) + planeFlyHeight *= level.airstrikeHeightScale; + } + else + { + planeFlyHeight = heightEnt.origin[2]; + planeBombExplodeDistance = getExplodeDistance( planeFlyHeight ); + } + + } + else + { + planeHalfDistance = 24000; + planeFlySpeed = 7000; + + if ( !isDefined( heightEnt ) )//old system + { + println( "NO DEFINED AIRSTRIKE HEIGHT SCRIPT_ORIGIN IN LEVEL" ); + planeFlyHeight = 850; + planeBombExplodeDistance = 1500; + if ( isdefined( level.airstrikeHeightScale ) ) + planeFlyHeight *= level.airstrikeHeightScale; + } + else + { + planeFlyHeight = heightEnt.origin[2]; + planeBombExplodeDistance = getExplodeDistance( planeFlyHeight ); + } + } + + startPoint = coord + vector_multiply( anglestoforward( direction ), -1 * planeHalfDistance ); + + if ( isDefined( heightEnt ) )// used in the new height system + startPoint *= (1,1,0); + + startPoint += ( 0, 0, planeFlyHeight ); + + if ( self.airStrikeType == "stealth" ) + endPoint = coord + vector_multiply( anglestoforward( direction ), planeHalfDistance*4 ); + else + endPoint = coord + vector_multiply( anglestoforward( direction ), planeHalfDistance ); + + if ( isDefined( heightEnt ) )// used in the new height system + endPoint *= (1,1,0); + + endPoint += ( 0, 0, planeFlyHeight ); + + // Make the plane fly by + d = length( startPoint - endPoint ); + flyTime = ( d / planeFlySpeed ); + + // bomb explodes planeBombExplodeDistance after the plane passes the center + d = abs( d/2 + planeBombExplodeDistance ); + bombTime = ( d / planeFlySpeed ); + + assert( flyTime > bombTime ); + + owner endon("disconnect"); + + requiredDeathCount = lifeId; + + level.airstrikeDamagedEnts = []; + level.airStrikeDamagedEntsCount = 0; + level.airStrikeDamagedEntsIndex = 0; + + if ( self.airStrikeType == "harrier" ) + { + level thread doPlaneStrike( lifeId, owner, requiredDeathCount, coord, startPoint+(0,0,randomInt(500)), endPoint+(0,0,randomInt(500)), bombTime, flyTime, direction, self.airStrikeType ); + + wait randomfloatrange( 1.5, 2.5 ); + maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone(); + level thread doPlaneStrike( lifeId, owner, requiredDeathCount, coord, startPoint+(0,0,randomInt(200)), endPoint+(0,0,randomInt(200)), bombTime, flyTime, direction, self.airStrikeType ); + + wait randomfloatrange( 1.5, 2.5 ); + maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone(); + harrier = beginHarrier( lifeId, startPoint, coord ); + owner thread defendLocation( harrier ); + + return harrier; + //owner thread harrierMissileStrike( startPoint, coord ); + + } + else if ( self.airStrikeType == "stealth" ) + { + level thread doBomberStrike( lifeId, owner, requiredDeathCount, coord, startPoint+(0,0,randomInt(1000)), endPoint+(0,0,randomInt(1000)), bombTime, flyTime, direction, self.airStrikeType ); + } + else //common airstrike + { + level thread doPlaneStrike( lifeId, owner, requiredDeathCount, coord, startPoint+(0,0,randomInt(500)), endPoint+(0,0,randomInt(500)), bombTime, flyTime, direction, self.airStrikeType ); + + wait randomfloatrange( 1.5, 2.5 ); + maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone(); + level thread doPlaneStrike( lifeId, owner, requiredDeathCount, coord, startPoint+(0,0,randomInt(200)), endPoint+(0,0,randomInt(200)), bombTime, flyTime, direction, self.airStrikeType ); + + wait randomfloatrange( 1.5, 2.5 ); + maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone(); + level thread doPlaneStrike( lifeId, owner, requiredDeathCount, coord, startPoint+(0,0,randomInt(200)), endPoint+(0,0,randomInt(200)), bombTime, flyTime, direction, self.airStrikeType ); + + if ( self.airStrikeType == "super" ) + { + wait randomfloatrange( 2.5, 3.5 ); + maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone(); + level thread doPlaneStrike( lifeId, owner, requiredDeathCount, coord, startPoint+(0,0,randomInt(200)), endPoint+(0,0,randomInt(200)), bombTime, flyTime, direction, self.airStrikeType ); + } + } +} + + +getExplodeDistance( height ) +{ + standardHeight = 850; + standardDistance = 1500; + distanceFrac = standardHeight/height; + + newDistance = distanceFrac * standardDistance; + + return newDistance; +} + + +targetGetDist( other, target ) +{ + infront = targetisinfront( other, target ); + if( infront ) + dir = 1; + else + dir = -1; + a = flat_origin( other.origin ); + b = a+vector_multiply( anglestoforward(flat_angle(other.angles)), (dir*100000) ); + point = pointOnSegmentNearestToPoint(a,b, target); + dist = distance(a,point); + + return dist; +} + +targetisclose(other, target, closeDist) +{ + if ( !isDefined( closeDist ) ) + closeDist = 3000; + + infront = targetisinfront(other, target); + if(infront) + dir = 1; + else + dir = -1; + a = flat_origin(other.origin); + b = a+vector_multiply(anglestoforward(flat_angle(other.angles)), (dir*100000)); + point = pointOnSegmentNearestToPoint(a,b, target); + dist = distance(a,point); + if (dist < closeDist) + return true; + else + return false; +} + + +targetisinfront(other, target) +{ + forwardvec = anglestoforward(flat_angle(other.angles)); + normalvec = vectorNormalize(flat_origin(target)-other.origin); + dot = vectordot(forwardvec,normalvec); + if(dot > 0) + return true; + else + return false; +} + +waitForAirstrikeCancel() +{ + self waittill( "cancel_location" ); + self setblurforplayer( 0, 0.3 ); +} + + +selectAirstrikeLocation( lifeId, airStrikeType ) +{ + assert( isDefined( airStrikeType ) ); + + self.airStrikeType = airStrikeType; + + if ( airStrikeType == "precision" || airStrikeType == "stealth" ) + chooseDirection = true; + else + chooseDirection = false; + + targetSize = level.mapSize / 5.625; // 138 in 720 + if ( level.splitscreen ) + targetSize *= 1.5; + + self beginLocationSelection( "map_artillery_selector", chooseDirection, targetSize ); + self.selectingLocation = true; + + self setblurforplayer( 4.0, 0.3 ); + self thread waitForAirstrikeCancel(); + + self thread endSelectionOn( "cancel_location" ); + self thread endSelectionOn( "death" ); + self thread endSelectionOn( "disconnect" ); + self thread endSelectionOn( "used" ); // so that this thread doesn't kill itself when we use an airstrike + self thread endSelectionOnGameEnd(); + self thread endSelectionOnEMP(); + + self endon( "stop_location_selection" ); + + // wait for the selection. randomize the yaw if we're not doing a precision airstrike. + self waittill( "confirm_location", location, directionYaw ); + if ( !chooseDirection ) + directionYaw = randomint(360); + + self setblurforplayer( 0, 0.3 ); + + if ( airStrikeType == "harrier" && level.planes > 1 ) + { + self notify ( "cancel_location" ); + self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" ); + return false; + } + + self thread finishAirstrikeUsage( lifeId, location, directionYaw ); + return true; +} + +finishAirstrikeUsage( lifeId, location, directionYaw ) +{ + self notify( "used" ); + + // find underside of top of skybox + trace = bullettrace( level.mapCenter + (0,0,1000000), level.mapCenter, false, undefined ); + location = (location[0], location[1], trace["position"][2] - 514); + + thread doAirstrike( lifeId, location, directionYaw, self, self.pers["team"] ); +} + + +endSelectionOn( waitfor ) +{ + self endon( "stop_location_selection" ); + self waittill( waitfor ); + self thread stopAirstrikeLocationSelection( (waitfor == "disconnect") ); +} + + +endSelectionOnGameEnd() +{ + self endon( "stop_location_selection" ); + level waittill( "game_ended" ); + self thread stopAirstrikeLocationSelection( false ); +} + + +endSelectionOnEMP() +{ + self endon( "stop_location_selection" ); + for ( ;; ) + { + level waittill( "emp_update" ); + + if ( !self isEMPed() ) + continue; + + self thread stopAirstrikeLocationSelection( false ); + return; + } +} + + +stopAirstrikeLocationSelection( disconnected ) +{ + if ( !disconnected ) + { + self setblurforplayer( 0, 0.3 ); + self endLocationSelection(); + self.selectingLocation = undefined; + } + self notify( "stop_location_selection" ); +} + + +useAirstrike( lifeId, pos, yaw ) +{ +} + + +handleEMP( owner ) +{ + self endon ( "death" ); + + if ( owner isEMPed() ) + { + playFxOnTag( level.onfirefx, self, "tag_engine_right" ); + playFxOnTag( level.onfirefx, self, "tag_engine_left" ); + return; + } + + for ( ;; ) + { + level waittill ( "emp_update" ); + + if ( !owner isEMPed() ) + continue; + + playFxOnTag( level.onfirefx, self, "tag_engine_right" ); + playFxOnTag( level.onfirefx, self, "tag_engine_left" ); + } +} \ No newline at end of file diff --git a/userraw/maps/mp/killstreaks/_harrier.gsc b/userraw/maps/mp/killstreaks/_harrier.gsc new file mode 100644 index 0000000..4c767b2 --- /dev/null +++ b/userraw/maps/mp/killstreaks/_harrier.gsc @@ -0,0 +1,1155 @@ +#include maps\mp\_utility; +#include common_scripts\utility; + +/******************************************************************* +// _harrier.gsc +// +// Holds all the harrier specific functions +// +// Jordan Hirsh Dec. 18th 2008 +********************************************************************/ + +beginHarrier( lifeId, startPoint, pos ) +{ + heightEnt = GetEnt( "airstrikeheight", "targetname" ); + + if ( isDefined( heightEnt ) ) + trueHeight = heightEnt.origin[2]; + else if( isDefined( level.airstrikeHeightScale ) ) + trueHeight = 850 * level.airstrikeHeightScale; + else + trueHeight = 850; + + pos *= (1,1,0); + pathGoal = pos + (0,0, trueHeight ); + + harrier = self spawnDefensiveHarrier( lifeId, self, startPoint, pathGoal ); + harrier.pathGoal = pathGoal; + + return harrier; +} + +getCorrectHeight( x, y, rand ) +{ + offGroundHeight = 1200; + groundHeight = self traceGroundPoint(x,y); + trueHeight = groundHeight + offGroundHeight; + + if( isDefined( level.airstrikeHeightScale ) && trueHeight < ( 850 * level.airstrikeHeightScale ) ) + trueHeight = ( 950 * level.airstrikeHeightScale ); + + trueHeight += RandomInt( rand ); + + return trueHeight; +} + +spawnDefensiveHarrier( lifeId, owner, pathStart, pathGoal ) +{ + forward = vectorToAngles( pathGoal - pathStart ); + + if ( owner.team == "allies" ) + harrier = spawnHelicopter( owner, pathStart, forward, "harrier_mp" , "vehicle_av8b_harrier_jet_mp" ); + else + harrier = spawnHelicopter( owner, pathStart, forward, "harrier_mp" , "vehicle_av8b_harrier_jet_opfor_mp" ); + + if ( !isDefined( harrier ) ) + return; + + harrier addToHeliList(); + harrier thread removeFromHeliListOnDeath(); + + harrier.speed = 250; + harrier.accel = 175; + harrier.health = 3000; + harrier.maxhealth = harrier.health; + harrier.team = owner.team; + harrier.owner = owner; + harrier setCanDamage( true ); + harrier.owner = owner; + harrier thread harrierDestroyed(); + harrier SetMaxPitchRoll( 0, 90 ); + harrier Vehicle_SetSpeed( harrier.speed, harrier.accel ); + harrier thread playHarrierFx(); + harrier setdamagestage( 3 ); + harrier.missiles = 6; + harrier.pers["team"] = harrier.team; + harrier SetHoverParams( 50, 100, 50 ); + harrier setTurningAbility( 0.05 ); + harrier setYawSpeed(45,25,25,.5); + harrier.defendLoc = pathGoal; + harrier.lifeId = lifeId; + + harrier.damageCallback = ::Callback_VehicleDamage; + + level.harriers = remove_undefined_from_array( level.harriers ); + + level.harriers[level.harriers.size] = harrier; + + return harrier; +} + +defendLocation( harrier ) +{ + harrier endon( "death" ); + assert ( isDefined( harrier ) ); + + harrier thread harrierTimer(); + + harrier setVehGoalPos( harrier.pathGoal, 1 ); + harrier thread closeToGoalCheck( harrier.pathGoal ); + + harrier waittill ( "goal" ); + harrier stopHarrierWingFx(); + harrier engageGround(); +} + +closeToGoalCheck( pathGoal ) +{ + self endon( "goal" ); + self endon( "death" ); + + for( ;; ) + { + if ( distance2d( self.origin, pathGoal ) < 768 ) + { + self SetMaxPitchRoll( 45, 25 ); + break; + } + + wait .05; + } +} + +engageGround() +{ + self notify( "engageGround" ); + self endon("engageGround"); + self endon("death"); + + self thread harrierGetTargets(); + self thread randomHarrierMovement(); + + pathGoal = self.defendLoc; + + self Vehicle_SetSpeed( 15, 5 ); + self setVehGoalPos( pathGoal, 1 ); + self waittill ( "goal" ); +} + +harrierLeave() +{ + self endon( "death" ); + + self SetMaxPitchRoll( 0, 0 ); + self notify( "leaving" ); + self breakTarget( true ); + self notify("stopRand"); + + for ( ;; ) + { + self Vehicle_SetSpeed( 35, 25 ); + pathGoal = self.origin + ( vector_multiply( anglestoforward( (0,RandomInt(360),0) ), 500 ) ); + pathGoal += ( 0,0,900); + + leaveTrace = BulletTrace(self.origin, self.origin+(0,0,900), false, self ); + if( leaveTrace["surfacetype"] == "none" ) + break; + + wait( 0.10 ); + } + + self setVehGoalPos( pathGoal, 1 ); + self thread startHarrierWingFx(); + self waittill ( "goal" ); + self playSound( "harrier_fly_away" ); + pathEnd = self getPathEnd(); + self Vehicle_SetSpeed( 250, 75 ); + self setVehGoalPos( pathEnd, 1 ); + self waittill ( "goal" ); + + level.airPlane[level.airPlane.size - 1] = undefined; + + self notify ( "harrier_gone" ); + self thread harrierDelete(); +} + + +harrierDelete() +{ + self delete(); +} + +harrierTimer() +{ + self endon( "death" ); + + maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 45 ); + self harrierLeave(); +} + +randomHarrierMovement() +{ + self notify( "randomHarrierMovement" ); + self endon("randomHarrierMovement"); + + self endon("stopRand"); + self endon("death"); + self endon( "acquiringTarget" ); + self endon( "leaving" ); + + pos = self.defendloc; + + for ( ;; ) + { + newpos = self GetNewPoint(self.origin); //crazy blocking call + self setVehGoalPos( newpos, 1 ); + self waittill ("goal"); + wait( randomIntRange( 3, 6) ); + self notify( "randMove" ); + } +} + +getNewPoint( pos, targ ) +{ + self endon("stopRand"); + self endon("death"); + self endon( "acquiringTarget" ); + self endon( "leaving" ); + + if ( !isDefined( targ ) ) + { + enemyPoints = []; + + foreach( player in level.players ) + { + if ( player == self ) + continue; + + if ( !level.teambased || player.team != self.team ) + enemyPoints[enemyPoints.size] = player.origin; + } + + if ( enemyPoints.size > 0 ) + { + gotoPoint = AveragePoint( enemyPoints ); + + pointX = gotoPoint[0]; + pointY = gotoPoint[1]; + } + else + { + center = level.mapCenter; + movementDist = ( level.mapSize / 6 ) - 200; + + pointX = RandomFloatRange( center[0]-movementDist, center[0]+movementDist ); + pointY = RandomFloatRange( center[1]-movementDist, center[1]+movementDist ); + } + + newHeight = self getCorrectHeight( pointX, PointY, 20 ); + } + else + { + if( coinToss() ) + { + directVector = self.origin - self.bestTarget.origin; + pointX = directVector[0]; + pointY = directVector[1] * -1; + newHeight = self getCorrectHeight( pointX, PointY, 20 ); + perpendicularVector = ( pointY,pointX,newHeight ); + + if ( distance2D( self.origin, perpendicularVector ) > 1200 ) + { + pointY *= .5; + pointX *= .5; + perpendicularVector = ( pointY,pointX,newHeight ); + } + } + else + { + if ( distance2D( self.origin, self.bestTarget.origin ) < 200 ) + return; + + yaw = self.angles[1]; + direction = (0,yaw,0); + moveToPoint = self.origin + vector_multiply( anglestoforward( direction ), randomIntRange( 200, 400 ) ); + newHeight = self getCorrectHeight( moveToPoint[0], moveToPoint[1], 20 ); + + pointX = moveToPoint[0]; + pointY = moveToPoint[1]; + } + } + for ( ;; ) + { + point = traceNewPoint( pointX, PointY, newHeight ); + + if ( point != 0 ) + return point; + + pointX = RandomFloatRange( pos[0]-1200, pos[0]+1200 ); + pointY = RandomFloatRange( pos[1]-1200, pos[1]+1200 ); + newHeight = self getCorrectHeight( pointX, PointY, 20 ); + } +} + +traceNewPoint(x,y,z) +{ + self endon("stopRand"); + self endon("death"); + self endon( "acquiringTarget" ); + self endon( "leaving" ); + self endon( "randMove" ); + + for( i = 1 ; i <= 10 ; i++ ) + { + + switch( i ) + { + case 1: + trc = BulletTrace( self.origin, (x,y,z), false, self ); + break; + case 2: + trc = BulletTrace( (self getTagOrigin( "tag_left_wingtip" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_left_wingtip" )), (x,y,z), 4 ); + break; + case 3: + trc = BulletTrace( (self getTagOrigin( "tag_right_wingtip" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_right_wingtip" )), (x,y,z), 4 ); + break; + case 4: + trc = BulletTrace( (self getTagOrigin( "tag_engine_left2" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_engine_left2" )), (x,y,z), 4 ); + break; + case 5: + trc = BulletTrace( (self getTagOrigin( "tag_engine_right2" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_engine_right2" )), (x,y,z), 4 ); + break; + case 6: + trc = BulletTrace( (self getTagOrigin( "tag_right_alamo_missile" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_right_alamo_missile" )), (x,y,z), 4 ); + break; + case 7: + trc = BulletTrace( (self getTagOrigin( "tag_left_alamo_missile" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_left_alamo_missile" )), (x,y,z), 4 ); + break; + case 8: + trc = BulletTrace( (self getTagOrigin( "tag_right_archer_missile" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_right_archer_missile" )), (x,y,z), 4 ); + break; + case 9: + trc = BulletTrace( (self getTagOrigin( "tag_left_archer_missile" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_left_archer_missile" )), (x,y,z), 4 ); + break; + case 10: + trc = BulletTrace( (self getTagOrigin( "tag_light_tail" )), (x,y,z), false, self ); + //self thread drawLine( (self getTagOrigin( "tag_light_tail" )), (x,y,z), 4 ); + break; + default: + trc = BulletTrace( self.origin, (x,y,z), false, self ); + } + + if ( trc["surfacetype"] != "none" ) + { + return 0; + } + + wait(0.05); + } + + pathGoal = ( x, y, z ); + return pathGoal; +} + + +traceGroundPoint(x,y) +{ + self endon("death"); + self endon( "acquiringTarget" ); + self endon( "leaving" ); + + highTrace = -9999999; + lowTrace = 9999999; + z = -9999999; + highz = self.origin[2]; + trace = undefined; + lTrace = undefined; + + for( i = 1 ; i <= 5 ; i++ ) + { + + switch( i ) + { + case 1: + trc = BulletTrace( (x,y,highz), (x,y,z), false, self ); + //self thread drawLine( ( x,y,highz ), (x,y,z), 4 ); + break; + case 2: + trc = BulletTrace( (x+20,y+20,highz), (x+20,y+20,z), false, self ); + //self thread drawLine( ( x+20,y+20,highz ), (x+20,y+20,z), 4 ); + break; + case 3: + trc = BulletTrace( (x-20,y-20,highz), (x-20,y-20,z), false, self ); + //self thread drawLine( ( x-20,y-20,highz ), (x-20,y-20,z), 4 ); + break; + case 4: + trc = BulletTrace( (x+20,y-20,highz), (x+20,y-20,z), false, self ); + //self thread drawLine( ( x+20,y-20,highz ), (x+20,y-20,z), 4 ); + break; + case 5: + trc = BulletTrace( (x-20,y+20,highz), (x-20,y+20,z), false, self ); + //self thread drawLine( ( x-20,y+20,highz ), (x-20,y+20,z), 4 ); + break; + default: + trc = BulletTrace( self.origin, (x,y,z), false, self ); + } + + if ( trc["position"][2] > highTrace ) + { + highTrace = trc["position"][2]; + trace = trc; + } + else if ( trc["position"][2] < lowTrace ) + { + lowTrace = trc["position"][2]; + lTrace = trc; + } + + wait(0.05); + } + + //thread drawLine( self.origin, lTrace["position"], 5, (0,1,0) ); + //thread drawLine( self.origin, trace["position"], 5, (1,0,0) ); + + return highTrace; +} + + +playHarrierFx() +{ + self endon ( "death" ); + + wait( 0.2 ); + playfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" ); + playfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" ); + wait( 0.2 ); + playfxontag( level.harrier_afterburnerfx, self, "tag_engine_right" ); + playfxontag( level.harrier_afterburnerfx, self, "tag_engine_left" ); + wait( 0.2 ); + playfxontag( level.harrier_afterburnerfx, self, "tag_engine_right2" ); + playfxontag( level.harrier_afterburnerfx, self, "tag_engine_left2" ); + wait( 0.2 ); + playFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_L_wing" ); + wait ( 0.2 ); + playFXOnTag( level.chopper_fx["light"]["right"], self, "tag_light_R_wing" ); + wait ( 0.2 ); + playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" ); + wait ( 0.2 ); + playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail" ); + +} + +stopHarrierWingFx() +{ + stopfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" ); + stopfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" ); +} + +startHarrierWingFx() +{ + wait ( 3.0); + + if ( !isDefined( self ) ) + return; + + playfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" ); + playfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" ); +} + +getPathStart( coord ) +{ + pathRandomness = 100; + harrierHalfDistance = 15000; + harrierFlyHeight = 850; + + yaw = randomFloat( 360 ); + direction = (0,yaw,0); + + startPoint = coord + vector_multiply( anglestoforward( direction ), -1 * harrierHalfDistance ); + startPoint += ( (randomfloat(2) - 1)*pathRandomness, (randomfloat(2) - 1)*pathRandomness, 0 ); + + return startPoint; +} + +getPathEnd() +{ + pathRandomness = 150; + harrierHalfDistance = 15000; + harrierFlyHeight = 850; + + yaw = self.angles[1]; + direction = (0,yaw,0); + + endPoint = self.origin + vector_multiply( anglestoforward( direction ), harrierHalfDistance ); + return endPoint; +} + +fireOnTarget( facingTolerance, zOffset ) +{ + self endon("leaving"); + self endon("stopfiring"); + self endon("explode"); + self endon("death"); + self.bestTarget endon( "death" ); + + acquiredTime = getTime(); + missileTime = getTime(); + missileReady = false; + + self setVehWeapon( "harrier_20mm_mp" ); + + if ( !isDefined( zOffset ) ) + zOffset = 50; + + for ( ;; ) + { + if ( self isReadyToFire( facingTolerance ) ) + break; + else + wait ( .25 ); + } + self SetTurretTargetEnt( self.bestTarget, ( 0,0,50 ) ); + + numShots = 25; + + for ( ;; ) + { + if ( numShots == 25 ) + self playLoopSound( "weap_cobra_20mm_fire_npc" ); + + numShots--; + self FireWeapon( "tag_flash", self.bestTarget, (0,0,0), .05 ); + wait ( .10); + + if ( numShots <= 0 ) + { + self stopLoopSound(); + wait (1); + numShots = 25; + } + } +} + +isReadyToFire( tolerance ) +{ + self endon( "death" ); + self endon( "leaving" ); + + if (! isdefined(tolerance) ) + tolerance = 10; + + harrierForwardVector = anglesToForward( self.angles ); + harrierToTarget = self.bestTarget.origin - self.origin; + harrierForwardVector *= (1,1,0); + harrierToTarget *= (1,1,0 ); + + harrierToTarget = VectorNormalize( harrierToTarget ); + harrierForwardVector = VectorNormalize( harrierForwardVector ); + + targetCosine = VectorDot( harrierToTarget, harrierForwardVector ); + facingCosine = Cos( tolerance ); + + if ( targetCosine >= facingCosine ) + return true; + else + return false; +} + +acquireGroundTarget( targets ) +{ + self endon( "death" ); + self endon( "leaving" ); + + if ( targets.size == 1 ) + self.bestTarget = targets[0]; + else + self.bestTarget = self getBestTarget( targets ); + + self backToDefendLocation( false ); + + self notify( "acquiringTarget" ); + + self SetTurretTargetEnt( self.bestTarget ); + self SetLookAtEnt( self.bestTarget ); + + newpos = self GetNewPoint(self.origin, true); + self setVehGoalPos( newpos, 1 ); + + self thread watchTargetDeath(); + self thread watchTargetLOS(); + + self setVehWeapon( "harrier_20mm_mp" ); + self thread fireOnTarget(); // fires on current target. +} + +backToDefendLocation( forced ) +{ + self setVehGoalPos( self.defendloc, 1 ); + + if ( isDefined( forced ) && forced ) + self waittill ( "goal" ); +} + + +wouldCollide( destination ) +{ + trace = BulletTrace( self.origin, destination, true, self ); + + if ( trace["position"] == destination ) + return false; + else + return true; +} + +watchTargetDeath() +{ + self notify( "watchTargetDeath" ); + self endon( "watchTargetDeath" ); + self endon( "newTarget" ); + + self endon( "death" ); + self endon( "leaving" ); + + self.bestTarget waittill( "death" ); + self thread breakTarget(); +} + +watchTargetLOS( tolerance ) +{ + self endon( "death" ); + self.bestTarget endon( "death" ); + self.bestTarget endon( "disconnect" ); + self endon( "leaving" ); + self endon( "newTarget" ); + lostTime = undefined; + + if ( !isDefined( tolerance ) ) + tolerance = 1000; + + for ( ;; ) + { + if ( !isTarget( self.bestTarget ) ) + { + self thread breakTarget(); + return; + } + + if ( !isDefined( self.bestTarget ) )//hack to cover host migration vehicle targets + { + self thread breakTarget(); + return; + } + + if ( self.bestTarget sightConeTrace( self.origin, self ) < 1 ) + { + if ( !isDefined(lostTime) ) + lostTime = getTime(); + + if ( getTime() - lostTime > tolerance ) + { + self thread breakTarget(); + return; + } + } + else + { + lostTime = undefined; + } + + wait( .25 ); + } +} + +breakTarget( noNewTarget ) +{ + self endon( "death" ); + + self ClearLookAtEnt(); + self stopLoopSound(); + self notify("stopfiring"); + + if ( isDefined(noNewTarget) && noNewTarget ) + return; + + self thread randomHarrierMovement(); + self notify( "newTarget" ); + self thread harrierGetTargets(); + +} + +harrierGetTargets() +{ + self notify( "harrierGetTargets" ); + self endon("harrierGetTargets"); + + self endon( "death" ); + self endon( "leaving" ); + targets = []; + + for ( ;; ) + { + targets = []; + players = level.players; + + if ( isDefined( level.chopper ) && level.chopper.team != self.team && isAlive( level.chopper ) ) + { + if ( !isDefined( level.chopper.nonTarget ) || ( isDefined( level.chopper.nonTarget ) && !level.chopper.nonTarget ) ) + { + self thread engageVehicle( level.chopper ); + return; + } + else + { + backToDefendLocation( true ); + } + } + + for (i = 0; i < players.size; i++) + { + potentialTarget = players[i]; + if ( isTarget( potentialTarget ) ) + { + if( isdefined( players[i] ) ) + targets[targets.size] = players[i]; + } + else + continue; + + wait( .05 ); + } + if ( targets.size > 0 ) + { + self acquireGroundTarget( targets ); + return; + } + wait( 1 ); + } +} + +isTarget( potentialTarget ) +{ + self endon( "death" ); + + if ( !isalive( potentialTarget ) || potentialTarget.sessionstate != "playing" ) + return false; + + if ( isDefined( self.owner ) && potentialTarget == self.owner ) + return false; + + if ( distance( potentialTarget.origin, self.origin ) > 8192 ) + return false; + + if ( Distance2D( potentialTarget.origin , self.origin ) < 768 ) + return false; + + if ( !isdefined( potentialTarget.pers["team"] ) ) + return false; + + if ( level.teamBased && potentialTarget.pers["team"] == self.team ) + return false; + + if ( potentialTarget.pers["team"] == "spectator" ) + return false; + + if ( isdefined( potentialTarget.spawntime ) && ( gettime() - potentialTarget.spawntime )/1000 <= 5 ) + return false; + + if ( potentialTarget _hasPerk( "specialty_coldblooded" ) ) + return false; + + harrier_centroid = self.origin + ( 0, 0, -160 ); + harrier_forward_norm = anglestoforward( self.angles ); + harrier_turret_point = harrier_centroid + 144 * harrier_forward_norm; + harrier_canSeeTarget = potentialTarget sightConeTrace( self.origin, self ); + + if ( harrier_canSeeTarget < 1 ) + return false; + + return true; +} + +getBestTarget( targets ) +{ + self endon( "death" ); + mainGunPointOrigin = self getTagOrigin( "tag_flash" ); + harrierOrigin = self.origin; + harrier_forward_norm = anglestoforward( self.angles ); + + bestYaw = undefined; + bestTarget = undefined; + targetHasRocket = false; + + foreach ( targ in targets ) + { + angle = abs ( vectorToAngles ( ( targ.origin - self.origin ) )[1] ); + noseAngle = abs( self getTagAngles( "tag_flash" )[1] ); + angle = abs ( angle - noseAngle ); + + // in this calculation having a rocket removes 40d of rotation cost from best target calculation + // to prioritize targeting dangerous targets. + weaponsArray = targ GetWeaponsListItems(); + foreach ( weapon in weaponsArray ) + { + if ( isSubStr( weapon, "at4" ) || isSubStr( weapon, "stinger" ) || isSubStr( weapon, "jav" ) ) + angle -= 40; + } + + if ( Distance( self.origin, targ.origin ) > 2000 ) + angle += 40; + + if ( !isDefined( bestYaw ) ) + { + bestYaw = angle; + bestTarget = targ; + } + else if ( bestYaw > angle ) + { + bestYaw = angle; + bestTarget = targ; + } + } + + return ( bestTarget ); +} + +fireMissile( missileTarget ) +{ + self endon( "death" ); + self endon( "leaving" ); + + assert( self.health > 0 ); + + if ( self.missiles <= 0 ) + return; + + friendlyInRadius = self checkForFriendlies( missileTarget, 256 ); + + if ( !isdefined( missileTarget ) ) + return; + + if ( Distance2D(self.origin, missileTarget.origin ) < 512 ) + return; + + if ( isDefined ( friendlyInRadius ) && friendlyInRadius ) + return; + + self.missiles--; + self setVehWeapon( "harrier_FFAR_mp" ); + + if ( isDefined( missileTarget.targetEnt ) ) + missile = self fireWeapon( "tag_flash", missileTarget.targetEnt, (0,0,-250) ); + else + missile = self fireWeapon( "tag_flash", missileTarget, (0,0,-250) ); + + missile Missile_SetFlightmodeDirect(); + missile Missile_SetTargetEnt( missileTarget ); +} + +checkForFriendlies( missileTarget, radiusSize ) +{ + self endon( "death" ); + self endon( "leaving" ); + + targets = []; + players = level.players; + strikePosition = missileTarget.origin; + + for (i = 0; i < players.size; i++) + { + potentialCollateral = players[i]; + + if ( potentialCollateral.team != self.team ) + continue; + + potentialPosition = potentialCollateral.origin; + + if ( distance2D( potentialPosition, strikePosition ) < 512 ) + return true; + } + return false; +} + +///------------------------------------------------------- +// +// Health Functions +// +///------------------------------------------------------ + + +Callback_VehicleDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName ) +{ + if ( ( attacker == self || ( isDefined( attacker.pers ) && attacker.pers["team"] == self.team ) && level.teamBased ) && ( attacker != self.owner ) ) + return; + + if ( self.health <= 0 ) + return; + + switch ( weapon ) + { + case "ac130_105mm_mp": + case "ac130_40mm_mp": + case "stinger_mp": + case "javelin_mp": + case "remotemissile_projectile_mp": + self.largeProjectileDamage = true; + damage = self.maxhealth + 1; + break; + case "rpg_mp": + case "at4_mp": + self.largeProjectileDamage = true; + damage = self.maxhealth - 900; + break; + default: + if ( weapon != "none" ) + damage = Int(damage/2); + self.largeProjectileDamage = false; + break; + } + + attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" ); + + if ( isPlayer( attacker ) && attacker _hasPerk( "specialty_armorpiercing" ) ) + { + damageAdd = int( damage*level.armorPiercingMod ); + damage += damageAdd; + } + + if ( self.health <= damage ) + { + if ( isPlayer( attacker ) && (!isDefined(self.owner) || attacker != self.owner) ) + { + thread teamPlayerCardSplash( "callout_destroyed_harrier", attacker ); + attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 300 ); + thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath ); + attacker notify( "destroyed_killstreak" ); + } + + self notify("death"); + } + + if ( self.health - damage <= 900 && ( !isDefined( self.smoking ) || !self.smoking ) ) + { + self thread playDamageEfx(); + self.smoking = true; + } + + self Vehicle_FinishDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName ); +} + +playDamageEfx() +{ + self endon( "death" ); + + stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_left" ); + playFxOnTag( level.harrier_smoke, self, "tag_engine_left" ); + + stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_right" ); + playFxOnTag( level.harrier_smoke, self, "tag_engine_right" ); + wait( 0.15 ); + + stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_left2" ); + playFxontag( level.harrier_smoke, self, "tag_engine_left2" ); + + stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_right2" ); + playFxontag( level.harrier_smoke, self, "tag_engine_right2" ); + playFxOnTag( level.chopper_fx["damage"]["heavy_smoke"], self, "tag_engine_left" ); +} + +harrierDestroyed() +{ + self endon( "harrier_gone" ); + + self waittill( "death" ); + + if (! isDefined(self) ) + return; + + if ( !isDefined( self.largeProjectileDamage ) ) + { + self Vehicle_SetSpeed( 25, 5 ); + self thread harrierSpin( RandomIntRange(180, 220) ); + + wait( RandomFloatRange( .5, 1.5 ) ); + } + + harrierExplode(); +} + +// crash explosion +harrierExplode() +{ + self playSound( "harrier_jet_crash" ); + level.airPlane[level.airPlane.size - 1] = undefined; + + deathAngles = self getTagAngles( "tag_deathfx" ); + playFx( level.harrier_deathfx, self getTagOrigin( "tag_deathfx" ), anglesToForward( deathAngles ), anglesToUp( deathAngles ) ); + + self notify ( "explode" ); + + wait ( 0.05 ); + + self thread harrierDelete(); +} + + +harrierSpin( speed ) +{ + self endon( "explode" ); + + playfxontag( level.chopper_fx["explode"]["medium"], self, "tag_origin" ); + + self setyawspeed( speed, speed, speed ); + while ( isdefined( self ) ) + { + self settargetyaw( self.angles[1]+(speed*0.9) ); + wait ( 1 ); + } +} + +engageVehicle( vehTarget ) +{ + vehTarget endon("death"); + vehTarget endon("leaving"); + vehTarget endon("crashing"); + self endon("death"); + + self acquireVehicleTarget( vehTarget ); + + self thread fireOnVehicleTarget(); +} + +fireOnVehicleTarget() +{ + self endon("leaving"); + self endon("stopfiring"); + self endon("explode"); + self.bestTarget endon ("crashing"); + self.bestTarget endon ("leaving"); + self.bestTarget endon ("death"); + + acquiredTime = getTime(); + + if ( isDefined( self.bestTarget ) && self.bestTarget.classname == "script_vehicle" ) + { + self SetTurretTargetEnt( self.bestTarget ); + + for ( ;; ) + { + curDist = distance2D( self.origin, self.bestTarget.origin ); + + if ( getTime() - acquiredTime > 2500 && curDist > 1000 ) + { + self fireMissile( self.bestTarget ); + acquiredTime = getTime(); + } + + wait ( .10); + } + } +} + +acquireVehicleTarget( vehTarget ) +{ + self endon( "death" ); + self endon( "leaving" ); + + self notify( "newTarget" ); + self.bestTarget = vehTarget; + self notify( "acquiringVehTarget" ); + self SetLookAtEnt( self.bestTarget ); + self thread watchVehTargetDeath(); + self thread watchVehTargetCrash(); + + self SetTurretTargetEnt( self.bestTarget ); +} + +watchVehTargetCrash() +{ + self endon( "death" ); + self endon( "leaving" ); + self.bestTarget endon ( "death" ); + self.bestTarget endon ( "drop_crate" ); + + self.bestTarget waittill( "crashing" ); + self breakVehTarget(); +} + +watchVehTargetDeath() +{ + self endon( "death" ); + self endon( "leaving" ); + self.bestTarget endon ( "crashing" ); + self.bestTarget endon ( "drop_crate" ); + + self.bestTarget waittill( "death" ); + breakVehTarget(); +} + +breakVehTarget() +{ + self ClearLookAtEnt(); + + if ( isDefined( self.bestTarget ) && !isDefined( self.bestTarget.nonTarget ) ) + self.bestTarget.nonTarget = true; + + self notify("stopfiring"); + self notify( "newTarget" ); + self thread stopHarrierWingFx(); + self thread engageGround(); +} + +evasiveManuverOne() +{ + self SetMaxPitchRoll( 15, 80); + self Vehicle_SetSpeed( 50, 100 ); + self setYawSpeed(90,30,30,.5); + + curOrg = self.origin; + + yaw = self.angles[1]; + if( cointoss() ) + direction = (0,yaw+90,0); + else + direction = (0,yaw-90,0); + + moveToPoint = self.origin + vector_multiply( anglestoforward( direction ), 500 ); + + self setVehGoalPos( moveToPoint, 1 ); + //println( "evasive manuver one" ); + self waittill ("goal"); +} + +drawLine( start, end, timeSlice, color ) +{ + if( !isdefined( color ) ) + color = ( 1,1,1 ); + + drawTime = int(timeSlice * 20); + for( time = 0; time < drawTime; time++ ) + { + line( start, end, color,false, 1 ); + wait ( 0.05 ); + } +} + +addToHeliList() +{ + level.helis[self getEntityNumber()] = self; +} + +removeFromHeliListOnDeath() +{ + entityNumber = self getEntityNumber(); + + self waittill ( "death" ); + + level.helis[entityNumber] = undefined; +} diff --git a/userraw/maps/mp/killstreaks/_uav.gsc b/userraw/maps/mp/killstreaks/_uav.gsc new file mode 100644 index 0000000..35630ee --- /dev/null +++ b/userraw/maps/mp/killstreaks/_uav.gsc @@ -0,0 +1,638 @@ +#include maps\mp\_utility; +#include maps\mp\gametypes\_hud_util; +#include common_scripts\utility; + +init() +{ + precacheString( &"MP_WAR_RADAR_ACQUIRED" ); + precacheString( &"MP_WAR_RADAR_ACQUIRED_ENEMY" ); + precacheString( &"MP_WAR_RADAR_EXPIRED" ); + precacheString( &"MP_WAR_RADAR_EXPIRED_ENEMY" ); + + precacheString( &"MP_WAR_COUNTER_RADAR_ACQUIRED" ); + precacheString( &"MP_WAR_COUNTER_RADAR_ACQUIRED_ENEMY" ); + precacheString( &"MP_WAR_COUNTER_RADAR_EXPIRED" ); + precacheString( &"MP_WAR_COUNTER_RADAR_EXPIRED_ENEMY" ); + + precacheModel( "vehicle_uav_static_mp" ); + + level.radarViewTime = 30; // time radar remains active + level.uavBlockTime = 30; // this only seems to be used for the FFA version. + + assert( level.radarViewTime > 7 ); + assert( level.uavBlockTime > 7 ); + + level.uav_fx[ "explode" ] = loadFx( "explosions/helicopter_explosion_cobra_low" ); + + level.killStreakFuncs["uav"] = ::tryUseUAV; + level.killStreakFuncs["double_uav"] = ::tryUseDoubleUAV; + level.killStreakFuncs["counter_uav"] = ::tryUseCounterUAV; + + minimapOrigins = getEntArray( "minimap_corner", "targetname" ); + if ( miniMapOrigins.size ) + uavOrigin = maps\mp\gametypes\_spawnlogic::findBoxCenter( miniMapOrigins[0].origin, miniMapOrigins[1].origin ); + else + uavOrigin = (0,0,0); + + level.UAVRig = spawn( "script_model", uavOrigin ); + level.UAVRig setModel( "c130_zoomrig" ); + level.UAVRig.angles = (0,115,0); + level.UAVRig hide(); + + level.UAVRig thread rotateUAVRig(); + + if ( level.teamBased ) + { + level.radarMode["allies"] = "normal_radar"; + level.radarMode["axis"] = "normal_radar"; + level.activeUAVs["allies"] = 0; + level.activeUAVs["axis"] = 0; + level.activeCounterUAVs["allies"] = 0; + level.activeCounterUAVs["axis"] = 0; + + level.uavModels["allies"] = []; + level.uavModels["axis"] = []; + } + else + { + level.radarMode = []; + level.activeUAVs = []; + level.activeCounterUAVs = []; + + level.uavModels = []; + + level thread onPlayerConnect(); + } + + level thread UAVTracker(); +} + + +onPlayerConnect() +{ + for(;;) + { + level waittill( "connected", player ); + + level.activeUAVs[ player.guid ] = 0; + level.activeCounterUAVs[ player.guid ] = 0; + + level.radarMode[ player.guid ] = "normal_radar"; + } +} + +rotateUAVRig() +{ + for (;;) + { + self rotateyaw( -360, 60 ); + wait ( 60 ); + } +} + + +launchUAV( owner, team, duration, isCounter ) +{ + UAVModel = spawn( "script_model", level.UAVRig getTagOrigin( "tag_origin" ) ); + + UAVModel setModel( "vehicle_uav_static_mp" ); + + UAVModel thread damageTracker( isCounter ); + UAVModel.team = team; + UAVModel.owner = owner; + + UAVModel thread handleIncomingStinger(); + + addUAVModel( UAVModel ); + + zOffset = randomIntRange( 3000, 5000 ); + + angle = randomInt( 360 ); + radiusOffset = randomInt( 2000 ) + 5000; + + xOffset = cos( angle ) * radiusOffset; + yOffset = sin( angle ) * radiusOffset; + + angleVector = vectorNormalize( (xOffset,yOffset,zOffset) ); + angleVector = vector_multiply( angleVector, randomIntRange( 6000, 7000 ) ); + + UAVModel linkTo( level.UAVRig, "tag_origin", angleVector, (0,angle - 90,0) ); + + UAVModel thread updateUAVModelVisibility(); + + if ( isCounter ) + UAVModel addActiveCounterUAV(); + else + UAVModel addActiveUAV(); + + level notify ( "uav_update" ); + + UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", duration - 7 ); + + if ( UAVModel.health <= 0 ) + { + forward = vector_multiply( anglesToRight( UAVModel.angles ), 200 ); + playFx ( level.uav_fx[ "explode" ], UAVModel.origin, forward ); + } + else + { + UAVModel unlink(); + + destPoint = UAVModel.origin + vector_multiply( anglestoforward( UAVModel.angles ), 20000 ); + UAVModel moveTo( destPoint, 60 ); + PlayFXOnTag( level._effect[ "ac130_engineeffect" ] , UAVModel, "tag_origin" ); + + UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 3 ); + + UAVModel moveTo( destPoint, 4, 4, 0.0 ); + + UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 4 ); + } + + if ( isCounter ) + UAVModel removeActiveCounterUAV(); + else + UAVModel removeActiveUAV(); + + UAVModel delete(); + removeUAVModel( UAVModel ); + + level notify ( "uav_update" ); +} + + +waittill_notify_or_timeout_hostmigration_pause( msg, timer ) +{ + self endon( msg ); + + maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timer ); +} + + +updateUAVModelVisibility() +{ + self endon ( "death" ); + + for ( ;; ) + { + level waittill_either ( "joined_team", "uav_update" ); + + self hide(); + foreach ( player in level.players ) + { + if ( level.teamBased ) + { + if ( player.team != self.team ) + self showToPlayer( player ); + } + else + { + if ( isDefined( self.owner ) && player == self.owner ) + continue; + + self showToPlayer( player ); + } + } + } +} + + +damageTracker( isCounterUAV ) +{ + level endon ( "game_ended" ); + + self setCanDamage( true ); + self.maxhealth = 700; + self.health = self.maxhealth; + + for ( ;; ) + { + self waittill ( "damage", damage, attacker, direction_vec, point, sMeansOfDeath ); + + if ( !isPlayer( attacker ) ) + { + if ( !isDefined( self ) ) + return; + + continue; + } + + attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" ); + + if ( attacker _hasPerk( "specialty_armorpiercing" ) && isDefined( self ) ) + { + damageAdd = damage*level.armorPiercingMod; + self.health -= int(damageAdd); + } + + if ( !isDefined( self ) ) + { + if ( isPlayer( attacker ) && (!isDefined(self.owner) || attacker != self.owner) ) + { + if ( isCounterUAV ) + thread teamPlayerCardSplash( "callout_destroyed_counter_uav", attacker ); + else + thread teamPlayerCardSplash( "callout_destroyed_uav", attacker ); + + thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, sMeansOfDeath ); + attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 50 ); + attacker notify( "destroyed_killstreak" ); + } + return; + } + } +} + + +tryUseUAV( lifeId ) +{ + return useUAV( "uav" ); +} + + +tryUseDoubleUAV( lifeId ) +{ + return useUAV( "double_uav" ); +} + + +tryUseCounterUAV( lifeId ) +{ + return useUAV( "counter_uav" ); +} + + +useUAV( uavType ) +{ + self maps\mp\_matchdata::logKillstreakEvent( uavType, self.origin ); + + team = self.pers["team"]; + useTime = level.radarViewTime; + + level thread launchUAV( self, team, useTime, uavType == "counter_uav" ); + + if ( uavType == "counter_uav" ) + self notify( "used_counter_uav" ); + else + self notify( "used_uav" ); + + return true; +} + + +UAVTracker() +{ + level endon ( "game_ended" ); + + for ( ;; ) + { + level waittill ( "uav_update" ); + + if ( level.teamBased ) + { + updateTeamUAVStatus( "allies" ); + updateTeamUAVStatus( "axis" ); + } + else + { + updatePlayersUAVStatus(); + } + } +} + + +updateTeamUAVStatus( team ) +{ + activeUAVs = level.activeUAVs[team]; + activeCounterUAVs = level.activeCounterUAVs[level.otherTeam[team]]; + + if ( !activeCounterUAVs ) + unblockTeamRadar( team ); + else + blockTeamRadar( team ); + + if ( !activeUAVs ) + { + setTeamRadarWrapper( team, 0 ); + return; + } + + if ( activeUAVs > 1 ) + level.radarMode[team] = "fast_radar"; + else + level.radarMode[team] = "normal_radar"; + + updateTeamUAVType(); + setTeamRadarWrapper( team, 1 ); +} + + +updatePlayersUAVStatus() +{ + totalActiveCounterUAVs = 0; + counterUAVPlayer = undefined; + + foreach ( player in level.players ) + { + activeUAVs = level.activeUAVs[ player.guid ]; + activeCounterUAVs = level.activeCounterUAVs[ player.guid ]; + + if ( activeCounterUAVs ) + { + totalActiveCounterUAVs++; + counterUAVPlayer = player; + } + + if ( !activeUAVs ) + { + player.hasRadar = false; + player.radarMode = "normal_radar"; + continue; + } + + if ( activeUAVs > 1 ) + player.radarMode = "fast_radar"; + else + player.radarMode = "normal_radar"; + + player.hasRadar = true; + } + + foreach ( player in level.players ) + { + if ( !totalActiveCounterUAVs ) + { + player.isRadarBlocked = false; + continue; + } + + if ( totalActiveCounterUAVs == 1 && player == counterUAVPlayer ) + player.isRadarBlocked = false; + else + player.isRadarBlocked = true; + } +} + + +blockPlayerUAV() +{ + self endon ( "disconnect" ); + + self notify ( "blockPlayerUAV" ); + self endon ( "blockPlayerUAV" ); + + self.isRadarBlocked = true; + + wait ( level.uavBlockTime ); + + self.isRadarBlocked = false; + + //self iPrintLn( &"MP_WAR_COUNTER_RADAR_EXPIRED" ); +} + + +updateTeamUAVType() +{ + foreach ( player in level.players ) + { + if ( player.team == "spectator" ) + continue; + + player.radarMode = level.radarMode[player.team]; + } +} + + + +usePlayerUAV( doubleUAV, useTime ) +{ + level endon("game_ended"); + self endon("disconnect"); + + self notify ( "usePlayerUAV" ); + self endon ( "usePlayerUAV" ); + + if ( doubleUAV ) + self.radarMode = "fast_radar"; + else + self.radarMode = "normal_radar"; + + self.hasRadar = true; + + wait ( useTime ); + + self.hasRadar = false; + + //self iPrintLn( &"MP_WAR_RADAR_EXPIRED" ); +} + + +setTeamRadarWrapper( team, value ) +{ + setTeamRadar( team, value ); + level notify( "radar_status_change", team ); +} + + + +handleIncomingStinger() +{ + level endon ( "game_ended" ); + self endon ( "death" ); + + for ( ;; ) + { + level waittill ( "stinger_fired", player, missile, lockTarget ); + + if ( !IsDefined( lockTarget ) || (lockTarget != self) ) + continue; + + missile thread stingerProximityDetonate( lockTarget, player ); + } +} + + +stingerProximityDetonate( targetEnt, player ) +{ + self endon ( "death" ); + + minDist = distance( self.origin, targetEnt GetPointInBounds( 0, 0, 0 ) ); + lastCenter = targetEnt GetPointInBounds( 0, 0, 0 ); + + for ( ;; ) + { + // UAV already destroyed + if ( !isDefined( targetEnt ) ) + center = lastCenter; + else + center = targetEnt GetPointInBounds( 0, 0, 0 ); + + lastCenter = center; + + curDist = distance( self.origin, center ); + + if ( curDist < minDist ) + minDist = curDist; + + if ( curDist > minDist ) + { + if ( curDist > 1536 ) + return; + + radiusDamage( self.origin, 1536, 600, 600, player ); + playFx( level.stingerFXid, self.origin ); + + //self playSound( "remotemissile_explode" ); + self hide(); + + self notify("deleted"); + wait ( 0.05 ); + self delete(); + player notify( "killstreak_destroyed" ); + } + + wait ( 0.05 ); + } +} + + +addUAVModel( UAVModel ) +{ + if ( level.teamBased ) + level.UAVModels[UAVModel.team][level.UAVModels[UAVModel.team].size] = UAVModel; + else + level.UAVModels[UAVModel.owner.guid + "_" + getTime()] = UAVModel; +} + + +removeUAVModel( UAVModel ) +{ + UAVModels = []; + + if ( level.teamBased ) + { + team = UAVModel.team; + + foreach ( uavModel in level.UAVModels[team] ) + { + if ( !isDefined( uavModel ) ) + continue; + + UAVModels[UAVModels.size] = uavModel; + } + + level.UAVModels[team] = UAVModels; + } + else + { + foreach ( uavModel in level.UAVModels ) + { + if ( !isDefined( uavModel ) ) + continue; + + UAVModels[UAVModels.size] = uavModel; + } + + level.UAVModels = UAVModels; + } +} + + +addActiveUAV() +{ + if ( level.teamBased ) + level.activeUAVs[self.team]++; + else + level.activeUAVs[self.owner.guid]++; +/* + if ( level.teamBased ) + { + foreach ( player in level.players ) + { + if ( player.team == self.team ) + player iPrintLn( &"MP_WAR_RADAR_ACQUIRED", self.owner, level.radarViewTime ); + else if ( player.team == level.otherTeam[self.team] ) + player iPrintLn( &"MP_WAR_RADAR_ACQUIRED_ENEMY", level.radarViewTime ); + } + } + else + { + foreach ( player in level.players ) + { + if ( player == self.owner ) + player iPrintLn( &"MP_WAR_RADAR_ACQUIRED", self.owner, level.radarViewTime ); + else + player iPrintLn( &"MP_WAR_RADAR_ACQUIRED_ENEMY", level.radarViewTime ); + } + } +*/ +} + + +addActiveCounterUAV() +{ + if ( level.teamBased ) + level.activeCounterUAVs[self.team]++; + else + level.activeCounterUAVs[self.owner.guid]++; +/* + if ( level.teamBased ) + { + foreach ( player in level.players ) + { + if ( player.team == self.team ) + player iPrintLn( &"MP_WAR_COUNTER_RADAR_ACQUIRED", self.owner, level.uavBlockTime ); + else if ( player.team == level.otherTeam[self.team] ) + player iPrintLn( &"MP_WAR_COUNTER_RADAR_ACQUIRED_ENEMY", level.uavBlockTime ); + } + } + else + { + foreach ( player in level.players ) + { + if ( player == self.owner ) + player iPrintLn( &"MP_WAR_COUNTER_RADAR_ACQUIRED", self.owner, level.uavBlockTime ); + else + player iPrintLn( &"MP_WAR_COUNTER_RADAR_ACQUIRED_ENEMY", level.uavBlockTime ); + } + } +*/ +} + + +removeActiveUAV() +{ + if ( level.teamBased ) + { + level.activeUAVs[self.team]--; + + if ( !level.activeUAVs[self.team] ) + { + //printOnTeam( &"MP_WAR_RADAR_EXPIRED", self.team ); + //printOnTeam( &"MP_WAR_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] ); + } + } + else if ( isDefined( self.owner ) ) + { + level.activeUAVs[self.owner.guid]--; + } +} + + +removeActiveCounterUAV() +{ + if ( level.teamBased ) + { + level.activeCounterUAVs[self.team]--; + + if ( !level.activeCounterUAVs[self.team] ) + { + //printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED", self.team ); + //printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] ); + } + } + else if ( isDefined( self.owner ) ) + { + level.activeCounterUAVs[self.owner.guid]--; + } +}