#include maps\mp\_utility; #include common_scripts\utility; #include maps\mp\gametypes\_hud_util; /*QUAKED mp_airdrop_point (1.0 0.5 0.0) (-36 -12 0) (36 12 20) An airdrop can land here.*/ init() { precacheVehicle( "littlebird_mp" ); precacheModel( "com_plasticcase_friendly" ); precacheModel( "com_plasticcase_enemy"); precacheModel( "vehicle_little_bird_armed" ); precacheModel( "vehicle_ac130_low_mp" ); precacheModel( "sentry_minigun_folded" ); precacheString( &"PLATFORM_GET_RANDOM" ); precacheString( &"PLATFORM_GET_KILLSTREAK" ); precacheString( &"PLATFORM_CALL_NUKE" ); precacheString( &"MP_CAPTURING_CRATE" ); precacheString( &"MP_CIVILIAN_AIR_TRAFFIC" ); precacheModel( maps\mp\gametypes\_teams::getTeamCrateModel( "allies" ) ); precacheModel( maps\mp\gametypes\_teams::getTeamCrateModel( "axis" ) ); precacheShader( maps\mp\gametypes\_teams::getTeamHudIcon( "allies" ) ); precacheShader( maps\mp\gametypes\_teams::getTeamHudIcon( "axis" ) ); precacheShader( "waypoint_ammo_friendly" ); precacheShader( "compass_objpoint_ammo_friendly" ); precacheShader( "compass_objpoint_ammo_enemy" ); precacheMiniMapIcon( "compass_objpoint_c130_friendly" ); precacheMiniMapIcon( "compass_objpoint_c130_enemy" ); game["strings"]["ammo_hint"] = &"MP_AMMO_PICKUP"; game["strings"]["uav_hint"] = &"MP_UAV_PICKUP"; game["strings"]["counter_uav_hint"] = &"MP_COUNTER_UAV_PICKUP"; game["strings"]["sentry_hint"] = &"MP_SENTRY_PICKUP"; game["strings"]["predator_missile_hint"] = &"MP_PREDATOR_MISSILE_PICKUP"; game["strings"]["airstrike_hint"] = &"MP_AIRSTRIKE_PICKUP"; game["strings"]["precision_airstrike_hint"] = &"MP_PRECISION_AIRSTRIKE_PICKUP"; game["strings"]["harrier_airstrike_hint"] = &"MP_HARRIER_AIRSTRIKE_PICKUP"; game["strings"]["helicopter_hint"] = &"MP_HELICOPTER_PICKUP"; game["strings"]["helicopter_flares_hint"] = &"MP_HELICOPTER_FLARES_PICKUP"; game["strings"]["stealth_airstrike_hint"] = &"MP_STEALTH_AIRSTRIKE_PICKUP"; game["strings"]["helicopter_minigun_hint"] = &"MP_HELICOPTER_MINIGUN_PICKUP"; game["strings"]["ac130_hint"] = &"MP_AC130_PICKUP"; game["strings"]["emp_hint"] = &"MP_EMP_PICKUP"; game["strings"]["nuke_hint"] = &"MP_NUKE_PICKUP"; level.airDropCrates = getEntArray( "care_package", "targetname" ); level.oldAirDropCrates = getEntArray( "airdrop_crate", "targetname" ); if ( !level.airDropCrates.size ) { level.airDropCrates = level.oldAirDropCrates; assert( level.airDropCrates.size ); level.airDropCrateCollision = getEnt( level.airDropCrates[0].target, "targetname" ); } else { foreach ( crate in level.oldAirDropCrates ) crate delete(); level.airDropCrateCollision = getEnt( level.airDropCrates[0].target, "targetname" ); level.oldAirDropCrates = getEntArray( "airdrop_crate", "targetname" ); } if ( level.airDropCrates.size ) { foreach ( crate in level.AirDropCrates ) crate delete(); } level.killStreakFuncs["airdrop"] = ::tryUseAirdrop; level.killStreakFuncs["airdrop_predator_missile"] = ::tryUseAirdropPredatorMissile; level.killStreakFuncs["airdrop_sentry_minigun"] = ::tryUseAirdropSentryMinigun; level.killStreakFuncs["airdrop_mega"] = ::tryUseMegaAirdrop; level.littleBirds = 0; level.littlebird = []; level.crateTypes = []; // Drop Type Type Weight Function addCrateType( "airdrop", "artillery", 15, ::artilleryCrateThink ); addCrateType( "airdrop", "napalm", 10, ::napalmCrateThink ); addCrateType( "airdrop", "aoe", 1, ::aoeCrateThink ); addCrateType( "airdrop", "adrenaline", 20, ::adrenalineCrateThink ); addCrateType( "airdrop", "thermal", 24, ::thermalCrateThink ); addCrateType( "airdrop", "suicide", 23, ::suicideCrateThink ); addCrateType( "airdrop", "deadly", 7, ::deadlyCrateThink ); addCrateType( "airdrop", "ammo", getDvarInt( "scr_airdrop_ammo", 0 ), ::ammoCrateThink ); addCrateType( "airdrop", "uav", getDvarInt( "scr_airdrop_uav", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "counter_uav", getDvarInt( "scr_airdrop_counter_uav", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "sentry", getDvarInt( "scr_airdrop_sentry", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "predator_missile", getDvarInt( "scr_airdrop_predator_missile", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "precision_airstrike", getDvarInt( "scr_airdrop_precision_airstrike", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "harrier_airstrike", getDvarInt( "scr_airdrop_harrier_airstrike", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "helicopter", getDvarInt( "scr_airdrop_helicopter", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "helicopter_flares", getDvarInt( "scr_airdrop_helicopter_flares", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "stealth_airstrike", getDvarInt( "scr_airdrop_stealth_airstrike", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "helicopter_minigun", getDvarInt( "scr_airdrop_helicopter_minigun", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "ac130", getDvarInt( "scr_airdrop_ac130", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "emp", getDvarInt( "scr_airdrop_emp", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop", "nuke", getDvarInt( "scr_airdrop_nuke", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "ammo", getDvarInt( "scr_airdrop_mega_ammo", 12 ), ::ammoCrateThink ); addCrateType( "airdrop_mega", "uav", getDvarInt( "scr_airdrop_mega_uav", 12 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "counter_uav", getDvarInt( "scr_airdrop_mega_counter_uav", 16 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "sentry", getDvarInt( "scr_airdrop_mega_sentry", 16 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "predator_missile", getDvarInt( "scr_airdrop_mega_predator_missile", 14 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "precision_airstrike", getDvarInt( "scr_airdrop_mega_precision_airstrike", 10 ),::killstreakCrateThink ); addCrateType( "airdrop_mega", "harrier_airstrike", getDvarInt( "scr_airdrop_mega_harrier_airstrike", 5 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "helicopter", getDvarInt( "scr_airdrop_mega_helicopter", 5 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "helicopter_flares", getDvarInt( "scr_airdrop_mega_helicopter_flares", 3 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "stealth_airstrike", getDvarInt( "scr_airdrop_mega_stealth_airstrike", 3 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "helicopter_minigun", getDvarInt( "scr_airdrop_mega_helicopter_minigun", 2 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "ac130", getDvarInt( "scr_airdrop_mega_ac130", 2 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "emp", getDvarInt( "scr_airdrop_mega_emp", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop_mega", "nuke", getDvarInt( "scr_airdrop_mega_nuke", 0 ), ::killstreakCrateThink ); addCrateType( "airdrop_sentry_minigun", "sentry", 0, ::killstreakCrateThink ); addCrateType( "nuke_drop", "nuke", 100, ::nukeCrateThink ); // generate the max weighted value foreach ( dropType, crateTypes in level.crateTypes ) { level.crateMaxVal[dropType] = 0; foreach ( crateType, crateWeight in level.crateTypes[dropType] ) { if ( !crateWeight ) continue; level.crateMaxVal[dropType] += crateWeight; level.crateTypes[dropType][crateType] = level.crateMaxVal[dropType]; } } tdmSpawns = getEntArray( "mp_tdm_spawn" , "classname" ); lowSpawn = undefined; foreach ( lspawn in tdmSpawns ) { if ( !isDefined( lowSpawn ) || lspawn.origin[2] < lowSpawn.origin[2] ) { lowSpawn = lspawn; } } level.lowSpawn = lowSpawn; } artilleryCrateThink( dropType ) { self dealCrateThink( dropType, "Devastate"); } napalmCrateThink( dropType ) { self dealCrateThink( dropType, "Napalm Strike"); } adrenalineCrateThink( dropType ) { self dealCrateThink( dropType, "Adrenaline"); } deadlyCrateThink( dropType ) { self dealCrateThink( dropType, "Exploding Bullets"); } suicideCrateThink( dropType ) { self dealCrateThink( dropType, "Suicide Bomber"); } thermalCrateThink( dropType ) { self dealCrateThink( dropType, "Death Vision"); } aoeCrateThink( dropType ) { self dealCrateThink( dropType, "Area of Effect"); } dealCrateThink( dropType, killstreakname ) { self endon ( "death" ); self setHintString( "Press and hold ^3[{+activate}] ^7for "+killstreakname); crateSetupForUse( "Press and hold ^3[{+activate}] ^7for "+killstreakname, "all", level.strIcon[killstreakname] ); self makeUsable(); self thread crateOwnerCaptureThink(); self thread crateOtherCaptureThink(); for ( ;; ){ self waittill ( "captured", player ); player playLocalSound( "ammo_crate_use" ); player thread maps\mp\gametypes\_killstreaks::dealStreak(killstreakname); self deleteCrate(); } } addCrateType( dropType, crateType, crateWeight, crateFunc ) { level.crateTypes[dropType][crateType] = crateWeight; level.crateFuncs[dropType][crateType] = crateFunc; } getRandomCrateType( dropType ) { value = randomInt( level.crateMaxVal[dropType] ); selectedCrateType = undefined; foreach ( crateType, crateWeight in level.crateTypes[dropType] ) { if ( !crateWeight ) continue; selectedCrateType = crateType; if ( crateWeight > value ) break; } return( selectedCrateType ); } getCrateTypeForDropType( dropType ) { switch ( dropType ) { case "airdrop_sentry_minigun": return "sentry"; case "airdrop_predator_missile": return "predator_missile"; case "airdrop": return getRandomCrateType( "airdrop" ); case "airdrop_mega": return getRandomCrateType( "airdrop_mega" ); case "nuke_drop": return "nuke"; default: return getRandomCrateType( "airdrop" ); } } /********************************************************** * Helper/Debug functions ***********************************************************/ drawLine( start, end, timeSlice ) { drawTime = int(timeSlice * 20); for( time = 0; time < drawTime; time++ ) { line( start, end, (1,0,0),false, 1 ); wait ( 0.05 ); } } /********************************************************** * Usage functions ***********************************************************/ tryUseAirdropPredatorMissile( lifeId ) { return ( self tryUseAirdrop( lifeId , "airdrop_predator_missile" ) ); } tryUseAirdropSentryMinigun( lifeId ) { return ( self tryUseAirdrop( lifeId, "airdrop_sentry_minigun" ) ); } tryUseMegaAirdrop( lifeId ) { return ( self tryUseAirdrop( lifeId, "airdrop_mega" ) ); } tryUseAirdrop( lifeId, dropType ) { result = undefined; if ( !isDefined( dropType ) ) dropType = "airdrop"; if ( level.littleBirds >= 3 && dropType != "airdrop_mega" ) { self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" ); return false; } if ( isDefined( level.civilianJetFlyBy ) ) { self iPrintLnBold( &"MP_CIVILIAN_AIR_TRAFFIC" ); return false; } if ( self isUsingRemote() ) { return false; } if ( dropType != "airdrop_mega" ) { level.littleBirds++; self thread watchDisconnect(); } result = self beginAirdropViaMarker( lifeId, dropType ); if ( (!isDefined( result ) || !result) && !isDefined( self.airDropMarker ) ) { self notify( "markerDetermined" ); if ( dropType != "airdrop_mega" ) level.littleBirds--; return false; } if ( dropType == "airdrop_mega" ) thread teamPlayerCardSplash( "used_airdrop_mega", self ); self notify( "markerDetermined" ); return true; } watchDisconnect() { self endon( "markerDetermined" ); self waittill( "disconnect" ); level.littleBirds--; return; } /********************************************************** * Marker functions ***********************************************************/ beginAirdropViaMarker( lifeId, dropType ) { self endon ( "death" ); self endon ( "grenade_fire" ); self.airDropMarker = undefined; self thread watchAirDropMarkerUsage( dropType ); while( self isChangingWeapon() ) wait ( 0.05 ); currentWeapon = self getCurrentWeapon(); if ( isAirdropMarker( currentWeapon ) ) airdropMarkerWeapon = currentWeapon; else airdropMarkerWeapon = undefined; while( isAirdropMarker( currentWeapon ) ) { self waittill( "weapon_change", currentWeapon ); if ( isAirdropMarker( currentWeapon ) ) airdropMarkerWeapon = currentWeapon; } self notify ( "stopWatchingAirDropMarker" ); if ( !isDefined( airdropMarkerWeapon ) ) return false; return( !(self getAmmoCount( airdropMarkerWeapon ) && self hasWeapon( airdropMarkerWeapon )) ); } watchAirDropMarkerUsage( dropType ) { self notify( "watchAirDropMarkerUsage" ); self endon( "disconnect" ); self endon( "watchAirDropMarkerUsage" ); self endon( "stopWatchingAirDropMarker" ); thread watchAirDropMarker( dropType ); for ( ;; ) { self waittill( "grenade_pullback", weaponName ); if ( !isAirdropMarker( weaponName ) ) continue; self _disableUsability(); self beginAirDropMarkerTracking(); } } watchAirDropMarker( dropType ) { self notify( "watchAirDropMarker" ); self endon( "watchAirDropMarker" ); self endon( "spawned_player" ); self endon( "disconnect" ); for ( ;; ) { self waittill( "grenade_fire", airDropWeapon, weapname ); if ( !isAirdropMarker( weapname ) ) continue; airDropWeapon thread airdropDetonateOnStuck(); airDropWeapon.owner = self; airDropWeapon.weaponName = weapname; self.airDropMarker = airDropWeapon; airDropWeapon thread airDropMarkerActivate( dropType ); } } beginAirDropMarkerTracking() { self notify( "beginAirDropMarkerTracking" ); self endon( "beginAirDropMarkerTracking" ); self endon( "death" ); self endon( "disconnect" ); self waittill_any( "grenade_fire", "weapon_change" ); self _enableUsability(); } airDropMarkerActivate( dropType ) { self notify( "airDropMarkerActivate" ); self endon( "airDropMarkerActivate" ); self waittill( "explode", position ); owner = self.owner; if ( !isDefined( owner ) ) return; if ( owner isEMPed() ) return; if ( dropType != "airdrop_mega" ) owner maps\mp\_matchdata::logKillstreakEvent( dropType, position ); wait 0.05; if ( dropType != "airdrop_mega" ) level doFlyBy( owner, position, randomFloat( 360 ), dropType ); else level doC130FlyBy( owner, position, randomFloat( 360 ), dropType ); } /********************************************************** * crate functions ***********************************************************/ initAirDropCrate() { self.inUse = false; self hide(); if ( isDefined( self.target ) ) { self.collision = getEnt( self.target, "targetname" ); self.collision notSolid(); } else { self.collision = undefined; } } deleteOnOwnerDeath( owner ) { wait ( 0.25 ); self linkTo( owner, "tag_origin", (0,0,0), (0,0,0) ); owner waittill ( "death" ); self delete(); } crateModelTeamUpdater( showForTeam ) { self endon ( "death" ); self hide(); foreach ( player in level.players ) { if ( player.team == showForTeam ) self showToPlayer( player ); } for ( ;; ) { level waittill ( "joined_team" ); self hide(); foreach ( player in level.players ) { if ( player.team == showForTeam ) self showToPlayer( player ); } } } createAirDropCrate( owner, dropType, crateType, startPos ) { dropCrate = spawn( "script_model", startPos ); dropCrate.curProgress = 0; dropCrate.useTime = 0; dropCrate.useRate = 0; dropCrate.team = self.team; if ( isDefined( owner ) ) dropCrate.owner = owner; else dropCrate.owner = undefined; dropCrate.crateType = crateType; dropCrate.dropType = dropType; dropCrate.targetname = "care_package"; dropCrate setModel( maps\mp\gametypes\_teams::getTeamCrateModel( dropCrate.team ) ); dropCrate.friendlyModel = spawn( "script_model", startPos ); dropCrate.friendlyModel setModel( "com_plasticcase_friendly" ); dropCrate.enemyModel = spawn( "script_model", startPos ); dropCrate.enemyModel setModel( "com_plasticcase_enemy" ); dropCrate.friendlyModel thread deleteOnOwnerDeath( dropCrate ); dropCrate.friendlyModel thread crateModelTeamUpdater( dropCrate.team ); dropCrate.enemyModel thread deleteOnOwnerDeath( dropCrate ); dropCrate.enemyModel thread crateModelTeamUpdater( level.otherTeam[dropCrate.team] ); dropCrate.inUse = false; dropCrate CloneBrushmodelToScriptmodel( level.airDropCrateCollision ); return dropCrate; } crateSetupForUse( hintString, mode, icon ) { if ( mode != "nukeDrop" ) { self setCursorHint( "HINT_NOICON" ); self setHintString( hintString ); self makeUsable(); } self.mode = mode; if ( level.teamBased ) { curObjID = maps\mp\gametypes\_gameobjects::getNextObjID(); objective_add( curObjID, "invisible", (0,0,0) ); objective_position( curObjID, self.origin ); objective_state( curObjID, "active" ); objective_team( curObjID, self.team ); objective_icon( curObjID, "compass_objpoint_ammo_friendly" ); self.objIdFriendly = curObjID; curObjID = maps\mp\gametypes\_gameobjects::getNextObjID(); objective_add( curObjID, "invisible", (0,0,0) ); objective_position( curObjID, self.origin ); objective_state( curObjID, "active" ); objective_team( curObjID, level.otherTeam[self.team] ); objective_icon( curObjID, "compass_objpoint_ammo_enemy" ); self.objIdEnemy = curObjID; } if ( !level.teamBased ) { curObjID = maps\mp\gametypes\_gameobjects::getNextObjID(); objective_add( curObjID, "invisible", (0,0,0) ); objective_position( curObjID, self.origin ); objective_state( curObjID, "active" ); objective_icon( curObjID, "compass_objpoint_ammo_friendly" ); self.objIdFriendly = curObjID; if ( isDefined( self.owner ) ) self maps\mp\_entityheadIcons::setHeadIcon( self.owner, icon, (0,0,24), 14, 14 ); } else if ( mode == "single" && isDefined( self.owner ) ) { setSelfAndEnemyUsable( self.owner ); self maps\mp\_entityheadIcons::setHeadIcon( self.owner, icon, (0,0,24), 14, 14 ); } else if ( mode == "team" ) { self setUsableOnceByTeam( self.team ); self thread checkChange( self.team ); self maps\mp\_entityheadIcons::setHeadIcon( self.team, icon, (0,0,24), 14, 14 ); } else if ( mode == "nukeDrop" ) { self maps\mp\_entityheadIcons::setHeadIcon( self.team, icon, (0,0,24), 14, 14 ); self maps\mp\_entityheadIcons::setHeadIcon( getOtherTeam( self.team ), icon, (0,0,24), 14, 14 ); level.nukeCrate = self; level.nukeCrate notify( "nukeLanded" ); } else { self setUsableByTeam(); self maps\mp\_entityheadIcons::setHeadIcon( self.team, icon, (0,0,24), 14, 14 ); } } checkChange( team ) { self endon ( "death" ); for ( ;; ) { level waittill ( "joined_team" ); self setUnUsable( team ); wait .25; } } setSelfAndEnemyUsable( owner ) { foreach ( player in level.players ) { if ( player != owner && player.team == self.team ) self disablePlayerUse( player ); else self enablePlayerUse( player ); } } setUnUsable( team ) { foreach ( player in level.players ) { if (team != player.team) self disablePlayerUse( player ); } } setUsableByTeam( team ) { foreach ( player in level.players ) { if ( !isDefined( team ) ) self enablePlayerUse( player ); else if ( team == player.team ) self enablePlayerUse( player ); else self disablePlayerUse( player ); } } setUsableOnceByTeam( team ) { foreach ( player in level.players ) { if ( isDefined( self.usedBy[ player.guid ] ) ) self disablePlayerUse( player ); else if ( !isDefined( team ) ) self enablePlayerUse( player ); else if ( team == player.team ) self enablePlayerUse( player ); else self disablePlayerUse( player ); } } dropTheCrate( dropPoint, dropType, lbHeight, dropImmediately, crateOverride, startPos ) { dropCrate = []; self.owner endon ( "disconnect" ); if ( !isDefined( crateOverride ) ) crateType = getCrateTypeForDropType( dropType ); else crateType = crateOverride; dropCrate = createAirDropCrate( self.owner, dropType, crateType, startPos ); if( dropType == "airdrop_mega" || dropType == "nuke_drop") dropCrate LinkTo( self, "tag_ground" , (64,32,-128) , (0,0,0) ); else dropCrate LinkTo( self, "tag_ground" , (32,0,5) , (0,0,0) ); dropCrate.angles = (0,0,0); dropCrate show(); dropSpeed = self.veh_speed; self waittill ( "drop_crate" ); dropCrate Unlink(); dropCrate PhysicsLaunchServer( (0,0,0), (randomInt(5),randomInt(5),randomInt(5)) ); dropCrate thread physicsWaiter( dropType, crateType ); } physicsWaiter( dropType, crateType ) { self waittill( "physics_finished" ); self thread [[ level.crateFuncs[ dropType ][ crateType ] ]]( dropType ); level thread dropTimeOut( self, self.owner ); if ( abs(self.origin[2] - level.lowSpawn.origin[2]) > 3000 ) { if ( isDefined( self.objIdFriendly ) ) _objective_delete( self.objIdFriendly ); if ( isDefined( self.objIdEnemy ) ) _objective_delete( self.objIdEnemy ); self delete(); } } //deletes if crate wasnt used after 90 seconds dropTimeOut( dropCrate, owner ) { level endon ( "game_ended" ); dropCrate endon( "death" ); if ( dropCrate.dropType == "nuke_drop" ) return; maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 90.0 ); while ( dropCrate.curProgress != 0 ) wait 1; if ( isDefined( dropcrate.objIdFriendly ) ) _objective_delete( dropcrate.objIdFriendly ); if ( isDefined( dropcrate.objIdEnemy ) ) _objective_delete( dropcrate.objIdEnemy ); dropCrate delete(); } getPathStart( coord, yaw ) { pathRandomness = 100; lbHalfDistance = 15000; direction = (0,yaw,0); startPoint = coord + vector_multiply( anglestoforward( direction ), -1 * lbHalfDistance ); startPoint += ( (randomfloat(2) - 1)*pathRandomness, (randomfloat(2) - 1)*pathRandomness, 0 ); return startPoint; } getPathEnd( coord, yaw ) { pathRandomness = 150; lbHalfDistance = 15000; direction = (0,yaw,0); endPoint = coord + vector_multiply( anglestoforward( direction + ( 0,90,0 ) ), lbHalfDistance ); endPoint += ( (randomfloat(2) - 1)*pathRandomness , (randomfloat(2) - 1)*pathRandomness , 0 ); return endPoint; } getFlyHeightOffset( dropSite ) { lbFlyHeight = 850; heightEnt = GetEnt( "airstrikeheight", "targetname" ); if ( !isDefined( heightEnt ) )//old system { /# println( "NO DEFINED AIRSTRIKE HEIGHT SCRIPT_ORIGIN IN LEVEL" ); #/ if ( isDefined( level.airstrikeHeightScale ) ) { if ( level.airstrikeHeightScale > 2 ) { lbFlyHeight = 1500; return( lbFlyHeight * (level.airStrikeHeightScale ) ); } return( lbFlyHeight * level.airStrikeHeightScale + 256 + dropSite[2] ); } else return ( lbFlyHeight + dropsite[2] ); } else { return heightEnt.origin[2]; } } /********************************************************** * Helicopter Functions ***********************************************************/ doFlyBy( owner, dropSite, dropYaw, dropType, heightAdjustment ) { flyHeight = self getFlyHeightOffset( dropSite ); if ( !isDefined(heightAdjustment) ) heightAdjustment = 0; flyHeight += heightAdjustment; if ( !isDefined( owner ) ) return; pathGoal = dropSite * (1,1,0) + (0,0,flyHeight); pathStart = getPathStart( pathGoal, dropYaw ); pathEnd = getPathEnd( pathGoal, dropYaw ); pathGoal = pathGoal + vector_multiply( anglestoforward( (0,dropYaw,0) ), -50 ); chopper = heliSetup( owner, pathStart, pathGoal ); chopper endon( "death" ); chopper.dropType = dropType; assert ( isDefined( chopper ) ); chopper setVehGoalPos( pathGoal, 1 ); chopper thread dropTheCrate( dropSite, dropType, flyHeight, false, undefined , pathStart ); wait ( 2 ); chopper Vehicle_SetSpeed( 75, 40 ); chopper SetYawSpeed( 180, 180, 180, .3 ); chopper waittill ( "goal" ); wait( .10 ); chopper notify( "drop_crate" ); chopper setvehgoalpos( pathEnd, 1 ); chopper Vehicle_SetSpeed( 300, 75 ); chopper.leaving = true; chopper waittill ( "goal" ); chopper notify( "leaving" ); chopper trimActiveBirdList(); level.littleBirds--; chopper notify( "delete" ); chopper delete(); } doMegaFlyBy( owner, dropSite, dropYaw, dropType ) { level thread doFlyBy( owner, dropSite, dropYaw, dropType, 0 ); wait( RandomIntRange( 1,2 ) ); level thread doFlyBy( owner, dropSite + (128,128,0), dropYaw, dropType, 128 ); wait( RandomIntRange( 1,2 ) ); level thread doFlyBy( owner, dropSite + (172,256,0), dropYaw, dropType, 256 ); wait( RandomIntRange( 1,2 ) ); level thread doFlyBy( owner, dropSite + (64,0,0), dropYaw, dropType, 0 ); } doC130FlyBy( owner, dropSite, dropYaw, dropType ) { planeHalfDistance = 24000; planeFlySpeed = 2000; yaw = vectorToYaw( dropsite - owner.origin ); direction = ( 0, yaw, 0 ); flyHeight = self getFlyHeightOffset( dropSite ); pathStart = dropSite + vector_multiply( anglestoforward( direction ), -1 * planeHalfDistance ); pathStart = pathStart * ( 1, 1, 0 ) + ( 0, 0, flyHeight ); pathEnd = dropSite + vector_multiply( anglestoforward( direction ), planeHalfDistance ); pathEnd = pathEnd * ( 1, 1, 0 ) + ( 0, 0, flyHeight ); d = length( pathStart - pathEnd ); flyTime = ( d / planeFlySpeed ); c130 = c130Setup( owner, pathStart, pathEnd ); c130.veh_speed = planeFlySpeed; c130.dropType = dropType; c130 playloopsound( "veh_ac130_dist_loop" ); c130.angles = direction; forward = anglesToForward( direction ); c130 moveTo( pathEnd, flyTime, 0, 0 ); minDist = distance2D( c130.origin, dropSite ); boomPlayed = false; for(;;) { dist = distance2D( c130.origin, dropSite ); // handle missing our target if ( dist < minDist ) minDist = dist; else if ( dist > minDist ) break; if ( dist < 256 ) { break; } else if ( dist < 768 ) { earthquake( 0.15, 1.5, dropSite, 1500 ); if ( !boomPlayed ) { c130 playSound( "veh_ac130_sonic_boom" ); //c130 thread stopLoopAfter( 0.5 ); boomPlayed = true; } } wait ( .05 ); } wait( 0.05 ); c130 thread dropTheCrate( dropSite, dropType, flyHeight, false, undefined , pathStart ); wait ( 0.05 ); c130 notify ( "drop_crate" ); wait ( 0.05 ); c130 thread dropTheCrate( dropSite, dropType, flyHeight, false, undefined , pathStart ); wait ( 0.05 ); c130 notify ( "drop_crate" ); wait ( 0.05 ); c130 thread dropTheCrate( dropSite, dropType, flyHeight, false, undefined , pathStart ); wait ( 0.05 ); c130 notify ( "drop_crate" ); wait ( 0.05 ); c130 thread dropTheCrate( dropSite, dropType, flyHeight, false, undefined , pathStart ); wait ( 0.05 ); c130 notify ( "drop_crate" ); wait ( 4 ); c130 delete(); } dropNuke( dropSite, owner, dropType ) { planeHalfDistance = 24000; planeFlySpeed = 2000; yaw = RandomInt( 360 ); direction = ( 0, yaw, 0 ); flyHeight = self getFlyHeightOffset( dropSite ); pathStart = dropSite + vector_multiply( anglestoforward( direction ), -1 * planeHalfDistance ); pathStart = pathStart * ( 1, 1, 0 ) + ( 0, 0, flyHeight ); pathEnd = dropSite + vector_multiply( anglestoforward( direction ), planeHalfDistance ); pathEnd = pathEnd * ( 1, 1, 0 ) + ( 0, 0, flyHeight ); d = length( pathStart - pathEnd ); flyTime = ( d / planeFlySpeed ); c130 = c130Setup( owner, pathStart, pathEnd ); c130.veh_speed = planeFlySpeed; c130.dropType = dropType; c130 playloopsound( "veh_ac130_dist_loop" ); c130.angles = direction; forward = anglesToForward( direction ); c130 moveTo( pathEnd, flyTime, 0, 0 ); // TODO: fix this... it's bad. if we miss our distance (which could happen if plane speed is changed in the future) we stick in this thread forever boomPlayed = false; minDist = distance2D( c130.origin, dropSite ); for(;;) { dist = distance2D( c130.origin, dropSite ); // handle missing our target if ( dist < minDist ) minDist = dist; else if ( dist > minDist ) break; if ( dist < 256 ) { break; } else if ( dist < 768 ) { earthquake( 0.15, 1.5, dropSite, 1500 ); if ( !boomPlayed ) { c130 playSound( "veh_ac130_sonic_boom" ); //c130 thread stopLoopAfter( 0.5 ); boomPlayed = true; } } wait ( .05 ); } c130 thread dropTheCrate( dropSite, dropType, flyHeight, false, "nuke", pathStart ); wait ( 0.05 ); c130 notify ( "drop_crate" ); wait ( 4 ); c130 delete(); } stopLoopAfter( delay ) { self endon ( "death" ); wait ( delay ); self stoploopsound(); } playloopOnEnt( alias ) { soundOrg = spawn( "script_origin", ( 0, 0, 0 ) ); soundOrg hide(); soundOrg endon( "death" ); thread delete_on_death( soundOrg ); soundOrg.origin = self.origin; soundOrg.angles = self.angles; soundOrg linkto( self ); soundOrg playloopsound( alias ); self waittill( "stop sound" + alias ); soundOrg stoploopsound( alias ); soundOrg delete(); } // spawn C130 at a start node and monitors it c130Setup( owner, pathStart, pathGoal ) { forward = vectorToAngles( pathGoal - pathStart ); c130 = spawnplane( owner, "script_model", pathStart, "compass_objpoint_c130_friendly", "compass_objpoint_c130_enemy" ); c130 setModel( "vehicle_ac130_low_mp" ); if ( !isDefined( c130 ) ) return; //chopper playLoopSound( "littlebird_move" ); c130.owner = owner; c130.team = owner.team; level.c130 = c130; return c130; } // spawn helicopter at a start node and monitors it heliSetup( owner, pathStart, pathGoal ) { forward = vectorToAngles( pathGoal - pathStart ); chopper = spawnHelicopter( owner, pathStart, forward, "littlebird_mp" , "vehicle_little_bird_armed" ); if ( !isDefined( chopper ) ) return; //chopper playLoopSound( "littlebird_move" ); chopper.health = 500; chopper setCanDamage( true ); chopper.owner = owner; chopper.team = owner.team; chopper thread heli_existence(); chopper thread heliDestroyed(); chopper SetMaxPitchRoll( 45, 85 ); chopper Vehicle_SetSpeed( 250, 175 ); level.littlebird[level.littlebird.size] = chopper; chopper.damageCallback = ::Callback_VehicleDamage; return chopper; } heli_existence() { self waittill_any( "crashing", "leaving" ); self trimActiveBirdList(); self notify( "helicopter_gone" ); } 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 ) ) && ( attacker != self.owner || meansOfDeath == "MOD_MELEE" ) ) return; attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" ); if ( self.health <= damage ) attacker notify( "destroyed_killstreak" ); self Vehicle_FinishDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName ); } heliDestroyed() { self endon( "leaving" ); self endon( "helicopter_gone" ); self waittill( "death" ); if (! isDefined(self) ) return; self trimActiveBirdList(); level.littleBirds--; self Vehicle_SetSpeed( 25, 5 ); self thread lbSpin( RandomIntRange(180, 220) ); wait( RandomFloatRange( .5, 1.5 ) ); self notify( "drop_crate" ); lbExplode(); } // crash explosion lbExplode() { forward = ( self.origin + ( 0, 0, 1 ) ) - self.origin; playfx ( level.chopper_fx["explode"]["death"]["cobra"], self.origin, forward ); // play heli explosion sound self playSound( "cobra_helicopter_crash" ); self notify ( "explode" ); self delete(); } lbSpin( speed ) { self endon( "explode" ); // tail explosion that caused the spinning playfxontag( level.chopper_fx["explode"]["medium"], self, "tail_rotor_jnt" ); playfxontag( level.chopper_fx["fire"]["trail"]["medium"], self, "tail_rotor_jnt" ); self setyawspeed( speed, speed, speed ); while ( isdefined( self ) ) { self settargetyaw( self.angles[1]+(speed*0.9) ); wait ( 1 ); } } trimActiveBirdList() { for ( i=0; i < level.littlebird.size; i++ ) { if ( level.littlebird.size == 1 ) { level.littlebird = []; } else if ( level.littlebird[i] == self ) { level.littlebird[i] = level.littlebird[ level.littlebird.size - 1 ]; level.littlebird[ level.littlebird.size - 1 ] = undefined; } } } /********************************************************** * crate trigger functions ***********************************************************/ nukeCaptureThink() { while ( isDefined( self ) ) { self waittill ( "trigger", player ); if ( !player isOnGround() ) continue; if ( !useHoldThink( player ) ) continue; self notify ( "captured", player ); } } crateOtherCaptureThink() { while ( isDefined( self ) ) { self waittill ( "trigger", player ); //if ( !player isOnGround() ) // continue; if ( isDefined( self.owner ) && player == self.owner ) continue; useEnt = self createUseEnt(); result = useEnt useHoldThink( player ); if ( isDefined( useEnt ) ) useEnt delete(); if ( !result ) continue; self notify ( "captured", player ); } } crateOwnerCaptureThink() { while ( isDefined( self ) ) { self waittill ( "trigger", player ); //if ( !player isOnGround() ) // continue; if ( isDefined( self.owner ) && player != self.owner ) continue; if ( !useHoldThink( player, 500 ) ) continue; self notify ( "captured", player ); } } killstreakCrateThink( dropType ) { self endon ( "death" ); if ( isDefined( game["strings"][self.crateType + "_hint"] ) ) crateHint = game["strings"][self.crateType + "_hint"]; else crateHint = &"PLATFORM_GET_KILLSTREAK"; crateSetupForUse( crateHint, "all", maps\mp\killstreaks\_killstreaks::getKillstreakCrateIcon( self.crateType ) ); self thread crateOtherCaptureThink(); self thread crateOwnerCaptureThink(); for ( ;; ) { self waittill ( "captured", player ); if ( isDefined( self.owner ) && player != self.owner ) { if ( !level.teamBased || player.team != self.team ) { if ( dropType == "airdrop" ) { player thread maps\mp\gametypes\_missions::genericChallenge( "hijacker_airdrop" ); player thread hijackNotify( self, "airdrop" ); } else { player thread maps\mp\gametypes\_missions::genericChallenge( "hijacker_airdrop_mega" ); player thread hijackNotify( self, "emergency_airdrop" ); } } else { self.owner thread maps\mp\gametypes\_rank::giveRankXP( "killstreak_giveaway", maps\mp\killstreaks\_killstreaks::getStreakCost( self.crateType ) * 50 ); //self.owner maps\mp\gametypes\_hud_message::playerCardSplashNotify( "giveaway_airdrop", player ); self.owner thread maps\mp\gametypes\_hud_message::SplashNotifyDelayed( "sharepackage", maps\mp\killstreaks\_killstreaks::getStreakCost( self.crateType ) * 50 ); } } player playLocalSound( "ammo_crate_use" ); player thread maps\mp\killstreaks\_killstreaks::giveKillstreak( self.crateType, false, false, self.owner ); player maps\mp\gametypes\_hud_message::killstreakSplashNotify( self.crateType, undefined, "pickup" ); self deleteCrate(); } } nukeCrateThink( dropType ) { self endon ( "death" ); crateSetupForUse( &"PLATFORM_CALL_NUKE", "nukeDrop", maps\mp\killstreaks\_killstreaks::getKillstreakCrateIcon( self.crateType ) ); self thread nukeCaptureThink(); for ( ;; ) { self waittill ( "captured", player ); player thread [[ level.killstreakFuncs[ self.crateType ] ]]( level.gtnw ); level notify( "nukeCaptured", player ); if ( isDefined( level.gtnw ) && level.gtnw ) player.capturedNuke = 1; player playLocalSound( "ammo_crate_use" ); self deleteCrate(); } } sentryCrateThink( dropType ) { self endon ( "death" ); crateSetupForUse( game["strings"]["sentry_hint"], "all", maps\mp\killstreaks\_killstreaks::getKillstreakCrateIcon( self.crateType ) ); self thread crateOtherCaptureThink(); self thread crateOwnerCaptureThink(); for ( ;; ) { self waittill ( "captured", player ); if ( isDefined( self.owner ) && player != self.owner ) { if ( !level.teamBased || player.team != self.team ) { if ( isSubStr(dropType, "airdrop_sentry" ) ) player thread hijackNotify( self, "sentry" ); else player thread hijackNotify( self, "emergency_airdrop" ); } else { self.owner thread maps\mp\gametypes\_rank::giveRankXP( "killstreak_giveaway", maps\mp\killstreaks\_killstreaks::getStreakCost( "sentry" ) * 50 ); self.owner maps\mp\gametypes\_hud_message::playerCardSplashNotify( "giveaway_sentry", player ); } } player playLocalSound( "ammo_crate_use" ); player thread sentryUseTracker(); self deleteCrate(); } } deleteCrate() { if ( isDefined( self.objIdFriendly ) ) _objective_delete( self.objIdFriendly ); if ( isDefined( self.objIdEnemy ) ) _objective_delete( self.objIdEnemy ); self delete(); } sentryUseTracker() { if ( !self maps\mp\killstreaks\_autosentry::giveSentry( "sentry_minigun" ) ) self maps\mp\killstreaks\_killstreaks::giveKillstreak( "sentry" ); } ammoCrateThink( dropType ) { self endon ( "death" ); self.usedBy = []; if ( dropType == "airdrop" || !level.teamBased ) crateSetupForUse( game["strings"]["ammo_hint"], "all", "waypoint_ammo_friendly" ); else crateSetupForUse( game["strings"]["ammo_hint"], "all", "waypoint_ammo_friendly" ); self thread crateOtherCaptureThink(); self thread crateOwnerCaptureThink(); for ( ;; ) { self waittill ( "captured", player ); if ( isDefined( self.owner ) && player != self.owner ) { if ( !level.teamBased || player.team != self.team ) { if ( dropType == "airdrop" ) player thread hijackNotify( self, "airdrop" ); else player thread hijackNotify( self, "emergency_airdrop" ); } } player playLocalSound( "ammo_crate_use" ); player refillAmmo(); self deleteCrate(); } } hijackNotify( crate, crateType ) { self notify( "hijacker", crateType, crate.owner ); } refillAmmo() { weaponList = self GetWeaponsListAll(); if ( self _hasPerk( "specialty_tacticalinsertion" ) && self getAmmoCount( "flare_mp" ) < 1 ) self _setPerk( "specialty_tacticalinsertion"); foreach ( weaponName in weaponList ) { if ( isSubStr( weaponName, "grenade" ) ) { if ( self getAmmoCount( weaponName ) >= 1 ) continue; } self giveMaxAmmo( weaponName ); } } /********************************************************** * Capture crate functions ***********************************************************/ useHoldThink( player, useTime ) { player playerLinkTo( self ); player playerLinkedOffsetEnable(); player _disableWeapon(); self.curProgress = 0; self.inUse = true; self.useRate = 0; if ( isDefined( useTime ) ) self.useTime = useTime; else self.useTime = 3000; player thread personalUseBar( self ); result = useHoldThinkLoop( player ); assert ( isDefined( result ) ); if ( isAlive( player ) ) { player _enableWeapon(); player unlink(); } if ( !isDefined( self ) ) return false; self.inUse = false; self.curProgress = 0; return ( result ); } personalUseBar( object ) { self endon( "disconnect" ); useBar = createPrimaryProgressBar( -25 ); useBarText = createPrimaryProgressBarText( -25 ); useBarText setText( &"MP_CAPTURING_CRATE" ); lastRate = -1; while ( isReallyAlive( self ) && isDefined( object ) && object.inUse && !level.gameEnded ) { if ( lastRate != object.useRate ) { if( object.curProgress > object.useTime) object.curProgress = object.useTime; useBar updateBar( object.curProgress / object.useTime, (1000 / object.useTime) * object.useRate ); if ( !object.useRate ) { useBar hideElem(); useBarText hideElem(); } else { useBar showElem(); useBarText showElem(); } } lastRate = object.useRate; wait ( 0.05 ); } useBar destroyElem(); useBarText destroyElem(); } useHoldThinkLoop( player ) { while( !level.gameEnded && isDefined( self ) && isReallyAlive( player ) && player useButtonPressed() && self.curProgress < self.useTime ) { self.curProgress += (50 * self.useRate); if ( isDefined(self.objectiveScaler) ) self.useRate = 1 * self.objectiveScaler; else self.useRate = 1; if ( self.curProgress >= self.useTime ) return ( isReallyAlive( player ) ); wait 0.05; } return false; } isAirdropMarker( weaponName ) { switch ( weaponName ) { case "airdrop_marker_mp": case "airdrop_mega_marker_mp": case "airdrop_sentry_marker_mp": return true; default: return false; } } createUseEnt() { useEnt = spawn( "script_origin", self.origin ); useEnt.curProgress = 0; useEnt.useTime = 0; useEnt.useRate = 3000; useEnt.inUse = false; useEnt thread deleteUseEnt( self ); return ( useEnt ); } deleteUseEnt( owner ) { self endon ( "death" ); owner waittill ( "death" ); self delete(); } airdropDetonateOnStuck() { self endon ( "death" ); self waittill( "missile_stuck" ); self detonate(); }