1320 lines
34 KiB
Plaintext
1320 lines
34 KiB
Plaintext
#include maps\mp\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\mp\agents\_agent_utility;
|
|
|
|
CONST_KILLSTREAK_NAME = "mine_level_killstreak";
|
|
CONST_KILLSTREAK_WEAPON = "killstreak_minemarker_mp";
|
|
CONST_DEBUG_NAME = "Mine Killstreak";
|
|
CONST_KILLSTREAK_LOC_NAME = &"MP_MINE_LEVEL_KILLSTREAK";
|
|
CONST_KILLSTREAK_PICKUP = &"MP_MINE_LEVEL_KILLSTREAK_PICKUP";
|
|
CONST_CRATE_WEIGHT = 90;
|
|
|
|
main()
|
|
{
|
|
maps\mp\mp_mine_precache::main();
|
|
maps\createart\mp_mine_art::main();
|
|
maps\mp\mp_mine_fx::main();
|
|
|
|
maps\mp\_load::main();
|
|
|
|
// AmbientPlay( "ambient_mp_setup_template" );
|
|
|
|
maps\mp\_compass::setupMiniMap( "compass_map_mp_mine" );
|
|
|
|
setdvar( "r_lightGridEnableTweaks", 1 );
|
|
setdvar( "r_lightGridIntensity", 1.33 );
|
|
SetDvar( "r_tessellationCutoffFalloffBase", 600 );
|
|
SetDvar( "r_tessellationCutoffDistanceBase", 2000 );
|
|
SetDvar( "r_tessellationCutoffFalloff", 600 );
|
|
SetDvar( "r_tessellationCutoffDistance", 2000 );
|
|
SetDvar( "r_reactiveMotionWindFrequencyScale", 0.1);
|
|
SetDvar( "sm_sunSampleSizeNear", 0.43);
|
|
|
|
game["attackers"] = "allies";
|
|
game["defenders"] = "axis";
|
|
|
|
game[ "allies_outfit" ] = "urban";
|
|
game[ "axis_outfit" ] = "woodland";
|
|
|
|
// wheel anims are set in the script_noteworthy. wheelSpeed=0 -> idle; wheelSpeed=2 -> fast
|
|
level.minecartWheelAnims = [ "mp_cart_idle_anim", "mp_cart_spin_slow_anim", "mp_cart_spin_mid_anim", "mp_cart_spin_fast_anim" ];
|
|
|
|
thread setupElevator();
|
|
thread MineCartSetup("cart1", "cart1TrackStart", "cart1AttachedModels","cart1dmg", "cart1_inside", "cart1_front" );
|
|
thread MineCartSetup("cart2", "cart2TrackStart", "cart2AttachedModels","cart2dmg", "cart2_interior", "cart2_front" );
|
|
thread setupElevatorKillTrigger();
|
|
|
|
initKillstreak();
|
|
|
|
thread maps\mp\_dlcalienegg::setupEggForMap( "alienEasterEgg" );
|
|
|
|
wildlife();
|
|
thread ambientAnimations();
|
|
|
|
level._effect[ "gear_blood" ] = LoadFX( "vfx/moments/mp_zerosub/vfx_blood_explosion" );
|
|
|
|
thread setupPushTrigger( "pushTrigger01", (10, 90, 0) );
|
|
thread setupPushTrigger( "pushTrigger02", (10, 180, 0) );
|
|
// thread setupPushTrigger( "pushTrigger03", (10, 90, 0) );
|
|
thread setupPushTrigger( "pushTrigger04", (10, 270, 0) );
|
|
}
|
|
|
|
setupGameTypeFlags( elevator )
|
|
{
|
|
level.mineBFlag = undefined;
|
|
objectives = [];
|
|
|
|
if ( level.gameType == "dom" || level.gameType == "siege" )
|
|
{
|
|
objectives = GetEntArray( "flag_primary", "targetname" );
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
// cache the result so we don't have to find it each time
|
|
foreach ( f in objectives )
|
|
{
|
|
if ( IsDefined( f.script_label ) && f.script_label == "_b" )
|
|
{
|
|
level.mineBFlag = f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( level.mineBFlag ) )
|
|
{
|
|
level thread updateBFlagObjIcon();
|
|
|
|
domFlag = getDomFlagB();
|
|
domflag EnableLinkTo();
|
|
domflag LinkTo( ELEVATOR );
|
|
|
|
while (!IsDefined(domflag.useObj))
|
|
{
|
|
waitframe();
|
|
}
|
|
foreach ( vis in domflag.useObj.visuals )
|
|
{
|
|
vis LinkTo( ELEVATOR );
|
|
}
|
|
}
|
|
}
|
|
|
|
getDomFlagB()
|
|
{
|
|
return level.mineBFlag;
|
|
}
|
|
|
|
BASE_EFFECT_OFFSET_GOING_DOWN = 1.5;
|
|
BASE_EFFECT_OFFSET_GOING_UP = 16;
|
|
|
|
updateBFlagFxPos( goingUp, loop )
|
|
{
|
|
level endon( "mp_mine_elevator_stopped" );
|
|
|
|
bFlag = getDomFlagB();
|
|
if ( !IsDefined( bFlag ) )
|
|
return;
|
|
|
|
offset = ter_op( goingUp, BASE_EFFECT_OFFSET_GOING_UP, BASE_EFFECT_OFFSET_GOING_DOWN );
|
|
|
|
while ( true )
|
|
{
|
|
fxOrigin = bFlag.origin + ( 0, 0, offset );
|
|
bFlag.useObj.baseEffectPos = fxOrigin;
|
|
|
|
if ( IsDefined( bFlag.useObj.neutralFlagFx ) )
|
|
{
|
|
bFlag.useObj.neutralFlagFx.origin = fxOrigin;
|
|
TriggerFX( bFlag.useObj.neutralFlagFx );
|
|
}
|
|
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( IsDefined( player._domFlagEffect ) && IsDefined( player._domFlagEffect[ "_b" ] ) )
|
|
{
|
|
player._domFlagEffect[ "_b" ].origin = fxOrigin;
|
|
TriggerFX( player._domFlagEffect[ "_b" ] );
|
|
}
|
|
}
|
|
|
|
if ( !loop )
|
|
{
|
|
break;
|
|
}
|
|
wait 0.25;
|
|
}
|
|
}
|
|
|
|
updateBFlagObjIcon()
|
|
{
|
|
bFlag = getDomFlagB();
|
|
while ( !IsDefined( bFlag.useObj ) )
|
|
{
|
|
waitframe();
|
|
}
|
|
|
|
// Force the objective icons to update
|
|
tag_origin = spawn_tag_origin();
|
|
tag_origin show();
|
|
tag_origin.origin = bFlag.origin + ( 0, 0, 100 );
|
|
tag_origin LinkTo( bFlag );
|
|
bFlag.useObj.objIconEnt = tag_origin;
|
|
bFlag.useObj maps\mp\gametypes\_gameobjects::updateWorldIcons();
|
|
}
|
|
|
|
setupElevator()
|
|
{
|
|
ELEVATOR = GetEnt("elevator", "targetname");
|
|
GEARS = GetEnt("elevatorGears", "targetname");
|
|
BLOCKER = GetEnt("elevatorPathNodeHolders", "targetname");
|
|
BLOCKERTOP = GetEnt("elevatorPathNodeTop", "targetname");
|
|
BLOCKERMID = GetEnt("elevatorPathNodeMid", "targetname");
|
|
BLOCKERBOT = GetEnt("elevatorPathNodeBot", "targetname");
|
|
MOVETIME = 6; //how long it takes for elevator to move from floor to floor
|
|
WAITTIME = 20; //how long elevator stays at floor
|
|
STRUCTTOP = getstruct("elevatorTop", "targetname");
|
|
STRUCTBOT = getstruct("elevatorBot", "targetname");
|
|
|
|
//Link attached models to elevator
|
|
ELEVATORMODELS = GetEntArray( "elevatorAttachedModels", "targetname" );
|
|
foreach ( detail in ELEVATORMODELS )
|
|
{
|
|
detail LinkTo( ELEVATOR );
|
|
}
|
|
ELEVATORKILL = GetEnt("elevatorDamage", "targetname");
|
|
ELEVATORKILL EnableLinkTo();
|
|
ELEVATORKILL LinkTo(ELEVATOR);
|
|
|
|
GEARKILL = ELEVATOR linkTrigger( "elevatorGearCrushTrigger" );
|
|
|
|
BEAMKILL = GetEnt("elevatorSquish", "targetname");
|
|
BEAMKILL.dmg = 0;
|
|
//make all path blockers non solid so dogs and stuff can run through them.
|
|
ELEVATOR ConnectPaths();
|
|
BLOCKER Hide();
|
|
BLOCKER NotSolid();
|
|
BLOCKERTOP Hide();
|
|
BLOCKERTOP NotSolid();
|
|
//BLOCKERMID Hide();
|
|
//BLOCKERMID NotSolid();
|
|
//BLOCKERBOT Hide();
|
|
//BLOCKERBOT NotSolid();
|
|
|
|
setupGameTypeFlags( ELEVATOR );
|
|
|
|
//Elevator Starts in TOP position, so enable those paths
|
|
blockerConnect(BLOCKERTOP);
|
|
|
|
|
|
// setup sound entities
|
|
leftSoundEnt = GetEnt( "elevatorWheelLeft", "targetname" );
|
|
leftSoundEnt LinkTo( GEARS );
|
|
|
|
rightSoundEnt = GetEnt( "elevatorWheelRight", "targetname" );
|
|
rightSoundEnt LinkTo( GEARS );
|
|
|
|
wait 10;
|
|
|
|
//movement loop
|
|
while (true)
|
|
{
|
|
//MOVE DOWN
|
|
GEARS RotatePitch( -1 * 256.1, MOVETIME, 1, 1);
|
|
GEARS MoveTo((-59, 256, 287), MOVETIME, 1, 1);
|
|
ELEVATOR MoveTo(STRUCTBOT.origin, MOVETIME, 1, 1);
|
|
ELEVATORKILL.dmg = 1000;
|
|
ELEVATOR.destroyDroneOnCollision = true;
|
|
ELEVATOR.destroyAirdropOnCollision = true;
|
|
|
|
level thread updateBFlagFxPos( false, true );
|
|
|
|
// elevator down sound
|
|
leftSoundEnt PlaySoundOnMovingEnt( "mine_elev_big_01" );
|
|
rightSoundEnt PlaySoundOnMovingEnt( "mine_elev_big_02" );
|
|
|
|
blockerConnect(BLOCKERMID);
|
|
wait 2;
|
|
//blockerDisconnect(BLOCKERTOP);
|
|
wait 2;
|
|
blockerDisconnect(BLOCKERTOP);
|
|
blockerConnect(BLOCKERBOT);
|
|
wait 2;
|
|
blockerDisconnect(BLOCKERMID);
|
|
|
|
level notify( "mp_mine_elevator_stopped" );
|
|
level updateBFlagFxPos( false, false );
|
|
wait WAITTIME;
|
|
|
|
//MOVE UP
|
|
GEARS RotatePitch( 1 * 256.1, MOVETIME, 1, 1);
|
|
GEARS MoveTo((-59, 256, 543), MOVETIME, 1, 1);
|
|
ELEVATOR MoveTo(STRUCTTOP.origin, MOVETIME, 1, 1);
|
|
ELEVATORKILL.dmg = 0;
|
|
ELEVATOR.destroyDroneOnCollision = false;
|
|
ELEVATOR.destroyAirdropOnCollision = false;
|
|
|
|
level thread updateBFlagFxPos( true, true );
|
|
|
|
// elevator up sound
|
|
leftSoundEnt PlaySoundOnMovingEnt( "mine_elev_big_01" );
|
|
rightSoundEnt PlaySoundOnMovingEnt( "mine_elev_big_02" );
|
|
|
|
blockerConnect(BLOCKERMID);
|
|
wait 2;
|
|
|
|
GEARKILL thread crushPlayerInTrigger();
|
|
BEAMKILL.dmg = 1000;
|
|
|
|
blockerDisconnect(BLOCKERBOT);
|
|
blockerConnect(BLOCKERTOP);
|
|
wait 2;
|
|
//blockerConnect(BLOCKERTOP);
|
|
wait 2;
|
|
blockerDisconnect(BLOCKERMID);
|
|
|
|
GEARKILL notify( "stopCrushing" );
|
|
BEAMKILL.dmg = 0;
|
|
|
|
level notify( "mp_mine_elevator_stopped" );
|
|
level updateBFlagFxPos( false, false );
|
|
wait WAITTIME;
|
|
}
|
|
|
|
/*if( level.gametype == "horde" )
|
|
return;
|
|
|
|
elevatorCfg = SpawnStruct();
|
|
elevatorCfg.name = "elevator";
|
|
// elevatorCfg.destinations = [ "elevatorBot", "elevatorTop" ];
|
|
elevatorCfg.destinations = "elevatorDestination";
|
|
elevatorCfg.destinationNames = ["elevatorTop","elevatorBot"];
|
|
elevatorCfg.buttons = "elevatorButton";
|
|
elevatorCfg.models = "elevatorAttachedModels";
|
|
|
|
elevatorCfg.moveTime = 5.0;
|
|
|
|
elevatorCfg.startSfx = "scn_elevator_startup";
|
|
elevatorCfg.stopSfx = "scn_elevator_stopping";
|
|
elevatorCfg.loopSfx = "scn_elevator_moving_lp";
|
|
// elevatorCfg.beepSfx = "scn_elevator_beep";
|
|
|
|
elevatorCfg.onMoveCallback = ::elevatorStartGears;
|
|
|
|
// add start and stop functions
|
|
|
|
elevator = maps\mp\_elevator_v2::init_elevator( elevatorCfg );
|
|
|
|
//if( level.gametype == "dom" )
|
|
//{
|
|
// wait(10);
|
|
// level.domFlags[1] LinkTo( elevator );
|
|
//}
|
|
|
|
gears = GetEntArray( "elevatorGears", "targetname" );
|
|
foreach ( gear in gears )
|
|
{
|
|
//gear LinkTo( elevator );
|
|
}
|
|
|
|
elevator.pathBlockers[ "elevatorMid" ] = GetEnt( "elevatorPathNodeMid", "targetname" );
|
|
elevator maps\mp\_elevator_v2::elevatorClearPath( elevator.curFloor );
|
|
*/
|
|
}
|
|
|
|
blockerConnect(BLOCKENT)
|
|
{
|
|
BLOCKENT Show();
|
|
BLOCKENT Solid();
|
|
BLOCKENT ConnectPaths();
|
|
BLOCKENT Hide();
|
|
BLOCKENT NotSolid();
|
|
}
|
|
|
|
blockerDisconnect(BLOCKENT)
|
|
{
|
|
BLOCKENT Show();
|
|
BLOCKENT Solid();
|
|
BLOCKENT DisconnectPaths();
|
|
}
|
|
|
|
elevatorStartGears( floorName ) // self == elevator
|
|
{
|
|
// can't rotate things that are linked?
|
|
|
|
gears = GetEntArray( "elevatorGears", "targetname" );
|
|
if ( floorName == self.destinationNames[0] )
|
|
{
|
|
// going down
|
|
foreach ( gear in gears )
|
|
{
|
|
gear RotatePitch( 1 * 360, self.moveTime, 0, 0 );
|
|
gear MoveTo((-59, 256, 543), self.moveTime, 0, 0);
|
|
}
|
|
|
|
//wait( .5 );
|
|
|
|
self maps\mp\_elevator_v2::elevatorClearPath( "elevatorMid" );
|
|
|
|
wait ( 5 );
|
|
|
|
self maps\mp\_elevator_v2::elevatorBlockPath( "elevatorMid" );
|
|
}
|
|
else
|
|
{
|
|
// going up
|
|
foreach ( gear in gears )
|
|
{
|
|
gear RotatePitch( -1 * 360, self.moveTime, 0, 0 );
|
|
gear MoveTo((-59, 256, 287), self.moveTime, 0, 0);
|
|
}
|
|
|
|
//wait( .5 );
|
|
|
|
self maps\mp\_elevator_v2::elevatorClearPath( "elevatorMid" );
|
|
|
|
wait ( 5 );
|
|
|
|
self maps\mp\_elevator_v2::elevatorBlockPath( "elevatorMid" );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
MINE_CART_SLOW_SPEED_LIMIT = 250;
|
|
MineCartSetup(CartName, FirstNode, LinkedModels, DamageTrigger, insideTriggerName, frontTriggerName )
|
|
{
|
|
MINE_CART = GetEnt( CartName, "targetname" );
|
|
MINE_CART_SPEED = 1.0/MINE_CART_SLOW_SPEED_LIMIT;
|
|
MINE_CART_DESTINATION = getstruct( FirstNode, "targetname" );
|
|
|
|
// MINE_CART_DAMAGE = MINE_CART linkTrigger( DamageTrigger );
|
|
insideTrigger = MINE_CART linkTrigger( insideTriggerName );
|
|
MINE_CART thread setupMineCartInsideTrigger( insideTrigger );
|
|
|
|
frontTrigger = MINE_CART linkTrigger( frontTriggerName );
|
|
MINE_CART thread setupMineCartFrontTrigger( frontTrigger );
|
|
|
|
CartModels = GetEntArray( LinkedModels, "targetname" );
|
|
foreach ( detail in CartModels )
|
|
{
|
|
detail LinkTo( MINE_CART );
|
|
detail.destroyDroneOnCollision = false;
|
|
detail.destroyAirdropOnCollision = true;
|
|
}
|
|
MINE_CART.destroyDroneOnCollision = false; // this flag allows drones to clip through for a couple of seconds after spawning.
|
|
MINE_CART.destroyAirdropOnCollision = true;
|
|
|
|
killCamEnt = Spawn( "script_model", MINE_CART.origin + ( 0, 0, 60 ) );
|
|
killCamEnt LinkTo( MINE_CART );
|
|
MINE_CART.killCamEnt = killCamEnt;
|
|
MINE_CART.killCamEnt SetScriptMoverKillCam( "explosive" );
|
|
|
|
MINE_CART.unresolved_collision_func = ::cart_unresolved_collision_func;
|
|
MINE_CART.unresolved_collision_notify_min = 6;
|
|
|
|
MINE_CART mineCartSetupSparks();
|
|
|
|
MINE_CART moveTo(MINE_CART_DESTINATION.origin, .1, 0, 0);
|
|
MINE_CART rotateTo(MINE_CART_DESTINATION.angles, .1, 0, 0);
|
|
|
|
MINE_CART.cartmodel = CartModels[0];
|
|
MINE_CART mineCartHandleEvents( MINE_CART_DESTINATION.script_noteworthy );
|
|
|
|
MINE_CART.speed = 0;
|
|
|
|
wait(.5);
|
|
|
|
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
|
Objective_Add( curObjID, "active", MINE_CART.origin, "mine_cart_icon" );
|
|
Objective_OnEntityWithRotation( curObjID, MINE_CART );
|
|
MINE_CART.curObjID = curObjID;
|
|
|
|
while(true)
|
|
{
|
|
if(IsDefined(MINE_CART_DESTINATION.script_label))
|
|
{
|
|
speed = float(MINE_CART_DESTINATION.script_label);
|
|
MINE_CART_SPEED = 1.0/speed;
|
|
MINE_CART.speed = speed;
|
|
|
|
MINE_CART mineCartPlaySparksOnSpeedChange( int(MINE_CART_DESTINATION.script_label) );
|
|
}
|
|
if(MINE_CART_DESTINATION.targetname == "cart2TrackStart")
|
|
{
|
|
thread MineCartElevatorMove(MINE_CART_SPEED);
|
|
}
|
|
MINE_CART_DESTINATION = MineCartMove(MINE_CART, MINE_CART_DESTINATION, MINE_CART_SPEED);
|
|
//wait(MINE_CART_SPEED);
|
|
}
|
|
}
|
|
|
|
MineCartMove(Cart, CurrentNode, CartSpeed)
|
|
{
|
|
Cart endon( "death" );
|
|
|
|
NEXT_NODE = getstruct(CurrentNode.target, "targetname" );
|
|
MOVE_TIME = abs(Distance(Cart.origin, NEXT_NODE.origin)*CartSpeed);
|
|
Cart MoveTo(NEXT_NODE.origin, MOVE_TIME, 0, 0);
|
|
Cart RotateTo(NEXT_NODE.angles, MOVE_TIME, 0, 0);
|
|
wait(MOVE_TIME);
|
|
|
|
// we have arrived at the next node
|
|
Cart mineCartHandleEvents( NEXT_NODE.script_noteworthy );
|
|
|
|
return NEXT_NODE;
|
|
}
|
|
|
|
MineCartElevatorMove(ElevatorSpeed)
|
|
{
|
|
CARTELEVATE = GetEnt("cart2Elevator", "targetname");
|
|
ELEVATENODEBOT = getstruct("cart2TrackStart", "targetname" );
|
|
ELEVATENODETOP = getstruct("cart2ElevatorTop", "targetname" );
|
|
BLOCKERTOP = GetEnt("cartElevatorPathNodeTop", "targetname");
|
|
BLOCKERBOT = GetEnt("cartElevatorPathNodeBot", "targetname");
|
|
ELEVATEMOVETIME = abs(Distance(ELEVATENODEBOT.origin, ELEVATENODETOP.origin)*ElevatorSpeed);
|
|
|
|
CARTELEVATE.unresolved_collision_kill = true;
|
|
|
|
// move up
|
|
CARTELEVATE.destroyDroneOnCollision = false;
|
|
CARTELEVATE PlaySoundOnMovingEnt( "minecart2_elevator_up" );
|
|
CARTELEVATE MoveTo(ELEVATENODETOP.origin, ELEVATEMOVETIME, 0, 0);
|
|
blockerDisconnect(BLOCKERBOT);
|
|
wait(ELEVATEMOVETIME);
|
|
blockerConnect(BLOCKERTOP);
|
|
wait(5);
|
|
blockerDisconnect(BLOCKERTOP);
|
|
|
|
// move down
|
|
CARTELEVATE.destroyDroneOnCollision = true;
|
|
CARTELEVATE PlaySoundOnMovingEnt( "minecart2_elevator_down" );
|
|
CARTELEVATE MoveTo(ELEVATENODEBOT.origin, ELEVATEMOVETIME, 0, 0);
|
|
wait(ELEVATEMOVETIME-2);
|
|
trigger_on("cart2ElevatorKill", "targetname" );
|
|
wait(2);
|
|
trigger_off("cart2ElevatorKill", "targetname" );
|
|
blockerConnect(BLOCKERBOT);
|
|
}
|
|
|
|
setupElevatorKillTrigger()
|
|
{
|
|
trigger_off("cart2ElevatorKill", "targetname" );
|
|
}
|
|
|
|
|
|
linkTrigger( triggerName )
|
|
{
|
|
trigger = GetEnt( triggerName, "targetname" );
|
|
if ( IsDefined( trigger ) )
|
|
{
|
|
trigger EnableLinkTo();
|
|
trigger LinkTo( self );
|
|
}
|
|
|
|
return trigger;
|
|
}
|
|
|
|
setupMineCartFrontTrigger( frontTrigger )
|
|
{
|
|
level endon( "game_ended" );
|
|
|
|
if ( !IsDefined( frontTrigger ) )
|
|
return;
|
|
|
|
while ( true )
|
|
{
|
|
frontTrigger waittill( "trigger", otherPlayer );
|
|
|
|
if ( IsPlayer( otherPlayer ) || IsAgent( otherPlayer ) )
|
|
{
|
|
// react only when the cart is moving fast enough
|
|
if ( isReallyAlive( otherPlayer ) && self.speed >= MINE_CART_SLOW_SPEED_LIMIT )
|
|
{
|
|
if ( otherPlayer IsMantling() )
|
|
continue;
|
|
|
|
// find a valid attacker in the cart
|
|
enemyRider = undefined;
|
|
if ( self.playersInCart.size > 0 )
|
|
{
|
|
foreach ( rider in self.playersInCart )
|
|
{
|
|
if ( isReallyAlive( rider ) && otherPlayer isEnemy( rider ) )
|
|
{
|
|
enemyRider = rider;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
damageDirection = otherPlayer.origin - self.origin;
|
|
attacker = undefined;
|
|
inflictor = undefined;
|
|
damageVal = 20;
|
|
if ( IsDefined( enemyRider ) )
|
|
{
|
|
damageVal = 100;
|
|
attacker = enemyRider;
|
|
inflictor = self.killCamEnt;
|
|
}
|
|
else if ( level.hardcoreMode )
|
|
{
|
|
damageVal = 100;
|
|
}
|
|
else if ( IsAgent( otherPlayer ) )
|
|
{
|
|
damageVal = 50;
|
|
}
|
|
|
|
// unhappy about calling callback directly, but there's no way to specify weapon to DoDamage
|
|
damageCallback = level.callbackPlayerDamage;
|
|
if ( IsAgent( otherPlayer ) )
|
|
{
|
|
damageCallback = maps\mp\agents\_agent_common::CodeCallback_AgentDamaged;
|
|
}
|
|
|
|
otherPlayer thread [[ damageCallback ]](
|
|
inflictor,
|
|
attacker,
|
|
damageVal,
|
|
0,
|
|
"MOD_EXPLOSIVE",
|
|
"iw6_minecart_mp",
|
|
self.origin,
|
|
damageDirection,
|
|
"none",
|
|
0
|
|
);
|
|
|
|
wait(.2);
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
setupMineCartInsideTrigger( insideTrigger )
|
|
{
|
|
level endon( "game_ended" );
|
|
|
|
self.playersInCart = [];
|
|
|
|
if ( !IsDefined( insideTrigger ) )
|
|
return;
|
|
|
|
while ( true )
|
|
{
|
|
insideTrigger waittill( "trigger", player );
|
|
|
|
if ( IsPlayer( player ) && isReallyAlive( player ) )
|
|
{
|
|
entNum = player GetEntityNumber();
|
|
|
|
if ( !IsDefined( self.playersInCart[ entNum ] ) )
|
|
{
|
|
self.playersInCart[ entNum ] = player;
|
|
|
|
if ( self.playersInCart.size == 1 )
|
|
{
|
|
self thread waitForRiderExit( insideTrigger );
|
|
}
|
|
}
|
|
}
|
|
|
|
wait (0.05);
|
|
}
|
|
}
|
|
|
|
waitForRiderExit( insideTrigger )
|
|
{
|
|
level endon ("game_ended");
|
|
|
|
while ( self.playersInCart.size > 0 )
|
|
{
|
|
foreach ( index, player in self.playersInCart )
|
|
{
|
|
if ( !IsDefined( player )
|
|
|| !isReallyAlive( player )
|
|
|| !player IsTouching( insideTrigger )
|
|
)
|
|
{
|
|
self.playersInCart[ index ] = undefined;
|
|
}
|
|
}
|
|
|
|
wait ( 0.05 );
|
|
}
|
|
}
|
|
|
|
mineCartSetupSparks()
|
|
{
|
|
level._effect[ "cart_sparks" ] = LoadFX( "vfx/moments/mp_mine/vfx_track_sparks_child" );
|
|
level._effect[ "cart_sparks_loop" ] = LoadFX( "vfx/moments/mp_mine/vfx_track_sparks_loop" );
|
|
self.lastSpeed = 10;
|
|
self.sparkTimeStamp = 0;
|
|
}
|
|
|
|
CONST_SPARK_SPEED_LIMIT = 20;
|
|
CONST_SPARK_FREQUENCY = 3500; // in ms
|
|
mineCartPlaySparksOnSpeedChange( targetSpeed )
|
|
{
|
|
// check velocity relative to target nodeSpeed
|
|
if ( IsDefined( targetSpeed )
|
|
&& GetTime() > self.sparkTimeStamp
|
|
&& abs( targetSpeed - self.lastSpeed ) > CONST_SPARK_SPEED_LIMIT
|
|
)
|
|
{
|
|
self.lastSpeed = targetSpeed;
|
|
self.sparkTimeStamp = GetTime() + CONST_SPARK_FREQUENCY;
|
|
|
|
self thread mineCartPlaySparks( "cart_sparks" );
|
|
}
|
|
}
|
|
|
|
CONST_REAR_SCALE = 1;
|
|
CONST_RIGHT_SCALE = 18;
|
|
CONST_TAG_WHEEL_L = "tag_wheelL";
|
|
CONST_TAG_WHEEL_R = "tag_wheelR";
|
|
mineCartPlaySparks( fxName )
|
|
{
|
|
// play sound
|
|
/*
|
|
forward = -1 * AnglesToForward( self.angles );
|
|
right = AnglesToRight( self.angles );
|
|
up = AnglesToUp( self.angles );
|
|
|
|
lrpos = self.origin + CONST_REAR_SCALE * forward - CONST_RIGHT_SCALE * right;
|
|
rrpos = self.origin + CONST_REAR_SCALE * forward + CONST_RIGHT_SCALE * right;
|
|
|
|
PlayFX( getfx( "cart_sparks" ), lrpos, forward, up );
|
|
wait( 0.05 );
|
|
PlayFX( getfx( "cart_sparks" ), rrpos, forward, up );
|
|
|
|
// Sphere( lrpos, 5, (1, 0, 0), false, 20 );
|
|
// Sphere( rrpos, 5, (0, 1, 0), false, 20 );
|
|
|
|
*/
|
|
|
|
PlayFXOnTag( getfx( fxName ), self.cartmodel, CONST_TAG_WHEEL_L );
|
|
PlayFXOnTag( getfx( fxName ), self.cartmodel, CONST_TAG_WHEEL_R );
|
|
}
|
|
|
|
mineCartStopSparks( fxName )
|
|
{
|
|
StopFXOnTag( getfx( fxName ), self.cartmodel, CONST_TAG_WHEEL_L );
|
|
StopFXOnTag( getfx( fxName ), self.cartmodel, CONST_TAG_WHEEL_R );
|
|
}
|
|
|
|
mineCartHandleEvents( node_noteworthy ) // self == cart
|
|
{
|
|
if ( !IsDefined( node_noteworthy ) )
|
|
return;
|
|
|
|
tokens = StrTok( node_noteworthy, "," );
|
|
|
|
foreach ( token in tokens )
|
|
{
|
|
if ( isStrStart( token, "sfx=" ) )
|
|
{
|
|
sfxName = GetSubStr( token, 4, token.size );
|
|
|
|
self PlaySoundOnMovingEnt( sfxName );
|
|
}
|
|
else if ( isStrStart( token, "loop=" ) )
|
|
{
|
|
sfxName = GetSubStr( token, 5, token.size );
|
|
self PlayLoopSound( sfxName );
|
|
}
|
|
else if ( token == "loopstop" )
|
|
{
|
|
self StopLoopSound();
|
|
}
|
|
else if ( token == "vfx" )
|
|
{
|
|
self thread mineCartPlaySparks( "cart_sparks" );
|
|
}
|
|
else if ( token == "vfxStart" )
|
|
{
|
|
self thread mineCartPlaySparks( "cart_sparks_loop" );
|
|
}
|
|
else if ( token == "vfxStop" )
|
|
{
|
|
self mineCartStopSparks( "cart_sparks_loop" );
|
|
}
|
|
else if ( isStrStart( token, "wheelSpeed=" ) )
|
|
{
|
|
wheelAnimId = Int( GetSubStr( token, 11, token.size ) );
|
|
if ( wheelAnimId < level.minecartWheelAnims.size )
|
|
{
|
|
self.cartmodel ScriptModelPlayAnim( level.minecartWheelAnims[ wheelAnimId ] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// Killstreaks
|
|
// --------------------------------------------------
|
|
initKillstreak()
|
|
{
|
|
level.mapCustomKillstreakFunc = ::customKillstreakFunc;
|
|
level.mapCustomCrateFunc = ::customCrateFunc;
|
|
level.mapCustomBotKillstreakFunc = ::customBotKillstreakFunc;
|
|
|
|
/#
|
|
AddDebugCommand( "bind p \"set scr_givekillstreak " + CONST_KILLSTREAK_NAME + "\"\n" );
|
|
#/
|
|
}
|
|
|
|
customCrateFunc()
|
|
{
|
|
if(!IsDefined(game["player_holding_level_killstrek"]))
|
|
game["player_holding_level_killstrek"] = false;
|
|
|
|
if(!allowLevelKillstreaks() || game["player_holding_level_killstrek"])
|
|
return;
|
|
|
|
maps\mp\killstreaks\_airdrop::addCrateType( "airdrop_assault",
|
|
CONST_KILLSTREAK_NAME,
|
|
CONST_CRATE_WEIGHT,
|
|
maps\mp\killstreaks\_airdrop::killstreakCrateThink,
|
|
maps\mp\killstreaks\_airdrop::get_friendly_crate_model(),
|
|
maps\mp\killstreaks\_airdrop::get_enemy_crate_model(),
|
|
CONST_KILLSTREAK_PICKUP
|
|
);
|
|
level thread watchForCrateUse();
|
|
}
|
|
|
|
watchForCrateUse()
|
|
{
|
|
while( true )
|
|
{
|
|
level waittill("createAirDropCrate", dropCrate);
|
|
|
|
if( IsDefined( dropCrate ) && IsDefined( dropCrate.crateType ) && dropCrate.crateType == CONST_KILLSTREAK_NAME )
|
|
{
|
|
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", CONST_KILLSTREAK_NAME, 0);
|
|
captured = wait_for_capture(dropCrate);
|
|
|
|
if(!captured)
|
|
{
|
|
//reEnable warhawk mortars care packages if it expires with out anyone picking it up
|
|
maps\mp\killstreaks\_airdrop::changeCrateWeight( "airdrop_assault", CONST_KILLSTREAK_NAME, CONST_CRATE_WEIGHT );
|
|
}
|
|
else
|
|
{
|
|
//Once its picked up it needs to remain off.
|
|
game["player_holding_level_killstrek"] = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//death and capture are sent on the same frame but death is processed first :(
|
|
wait_for_capture(dropCrate)
|
|
{
|
|
result = watch_for_air_drop_death(dropCrate);
|
|
return !IsDefined(result); //If !isdefined the captured notify was also sent.
|
|
}
|
|
|
|
watch_for_air_drop_death(dropCrate)
|
|
{
|
|
dropCrate endon("captured");
|
|
|
|
dropCrate waittill("death");
|
|
waittillframeend;
|
|
|
|
return true;
|
|
}
|
|
|
|
customKillstreakFunc()
|
|
{
|
|
AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/Care Package/" + CONST_DEBUG_NAME + "\" \"set scr_devgivecarepackage " + CONST_KILLSTREAK_NAME + "; set scr_devgivecarepackagetype airdrop_assault\"\n");
|
|
AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/" + CONST_DEBUG_NAME + "\" \"set scr_givekillstreak " + CONST_KILLSTREAK_NAME + "\"\n");
|
|
|
|
// initAirdropKS();
|
|
|
|
maps\mp\killstreaks\mp_wolfpack_killstreak::init();
|
|
|
|
// this is a hack so the mine cart will show a correct killcam icon
|
|
level.killstreakWeildWeapons[ "iw6_minecart_mp" ] = "iw6_minecart_mp";
|
|
}
|
|
|
|
customBotKillstreakFunc()
|
|
{
|
|
AddDebugCommand("devgui_cmd \"MP/Bots(Killstreak)/Level Events:5/" + CONST_DEBUG_NAME + "\" \"set scr_testclients_givekillstreak " + CONST_KILLSTREAK_NAME + "\"\n");
|
|
maps\mp\bots\_bots_ks::bot_register_killstreak_func( CONST_KILLSTREAK_NAME, maps\mp\bots\_bots_ks::bot_killstreak_simple_use );
|
|
// maps\mp\bots\_bots_ks::bot_register_killstreak_func( CONST_KILLSTREAK_NAME, maps\mp\bots\_bots_ks::bot_killstreak_drop_outside );
|
|
}
|
|
|
|
ambientAnimations()
|
|
{
|
|
wait( 3 );
|
|
|
|
wheels = GetEnt( "spinny_wheels", "targetname" );
|
|
if ( IsDefined( wheels ) )
|
|
{
|
|
wheels ScriptModelPlayAnim( "mp_mine_spinning_wheels" );
|
|
}
|
|
}
|
|
|
|
wildlife()
|
|
{
|
|
thread maps\interactive_models\vulture_mp::vulture_circling((565, -1870, 1500), 3);
|
|
thread maps\interactive_models\vulture_mp::vulture_circling((-300, 1210, 1650), 2);
|
|
/#thread maps\interactive_models\vulture_mp::vultures_toggle_thread();#/
|
|
|
|
thread maps\interactive_models\batcave::vfxBatCaveWaitInit( "bats_flyaway_1", 1, "vfx_bats_flyaway_1", (-2028.06, 464.427, 413.921) );
|
|
thread maps\interactive_models\batcave::vfxBatCaveWaitInit( "bats_flyaway_2", 2, "vfx_bats_flyaway_2", (-264.3, 927.7, 397.8), 2 );
|
|
}
|
|
|
|
crushPlayerInTrigger() // self == gear
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "stopCrushing" );
|
|
|
|
while ( true )
|
|
{
|
|
self waittill( "trigger", player );
|
|
|
|
if ( isReallyAlive( player ) )
|
|
{
|
|
player DoDamage( 1000, player.origin, undefined, undefined, "MOD_CRUSH" );
|
|
|
|
player notify( "notify_moving_platform_invalid" );
|
|
|
|
if ( IsPlayer( player ) || IsAgent( player ) )
|
|
{
|
|
thread cleanupCrushedbody( player GetCorpseEntity() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cleanupCrushedBody( body )
|
|
{
|
|
PlayFX( getfx( "gear_blood" ), body.origin, -1 * AnglesToForward( body.angles ), AnglesToUp( body.angles ) );
|
|
|
|
wait( 0.7 );
|
|
|
|
if ( IsDefined( body ) )
|
|
{
|
|
PlayFX( getfx( "gear_blood" ), body.origin, -1 * AnglesToForward( body.angles ), AnglesToUp( body.angles ) );
|
|
|
|
body Hide();
|
|
}
|
|
}
|
|
|
|
cart_unresolved_collision_func(player, bAllowSuicide)
|
|
{
|
|
if ( IsPlayer( player ) && player IsLinked() )
|
|
{
|
|
player Unlink();
|
|
}
|
|
|
|
// disable node teleporting for the cart and allow players to clip through the cart briefly
|
|
// self maps\mp\_movers::unresolved_collision_nearest_node( player, true );
|
|
self maps\mp\_movers::unresolved_collision_owner_damage( player );
|
|
}
|
|
|
|
// 2014-07-03 wallace: there are some low overhangs on the mine cart tracks (mostly track1) that are too late to change.
|
|
// Instead, we'll push players who hit these triggers away and hopefully off the minecart.
|
|
setupPushTrigger( triggerName, pushAngles )
|
|
{
|
|
level endon( "game_ended" );
|
|
trigger = GetEnt( triggerName, "targetname" );
|
|
|
|
if ( !IsDefined( trigger ) )
|
|
return;
|
|
|
|
while ( true )
|
|
{
|
|
trigger waittill( "trigger", player );
|
|
|
|
if ( IsPlayer( player ) || IsAgent( player ) )
|
|
{
|
|
// if the player is using a box or crate, allow him to be disconnected
|
|
if ( player IsLinked() )
|
|
{
|
|
player Unlink();
|
|
player.startUseMover = undefined;
|
|
}
|
|
|
|
dir = 100 * AnglesToForward( pushAngles );
|
|
player SetVelocity( dir );
|
|
|
|
// Sphere( player.origin, 10, (1, 0, 0), false, 1000 );
|
|
// Line( player.origin, player.origin + dir, (1, 0, 0), 1, false, 1000 );
|
|
}
|
|
|
|
wait 0.1;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------
|
|
// airstrike
|
|
// ---------------------------------------------------
|
|
/*
|
|
addDropObj( name, baseModel, fallSfx, fallVfx, impactSfx, impactVfx, impactModel, impactRadius )
|
|
{
|
|
obj = [];
|
|
obj["model"] = baseModel;
|
|
obj["fallSfx"] = fallSfx;
|
|
if ( IsDefined( fallVfx ) )
|
|
{
|
|
obj["fallTrailVfx"] = getfx( fallVfx );
|
|
}
|
|
obj["impactSfx"] = impactSfx;
|
|
if ( IsDefined( impactVfx ) )
|
|
{
|
|
obj["impactVfx"] = getfx( impactVfx );
|
|
}
|
|
obj["impactModel"] = impactModel;
|
|
obj["impactRadius"] = impactRadius;
|
|
|
|
// self.droppedObjs[ self.droppedObjs.size ] = obj;
|
|
self.droppedObjs[ name ] = obj;
|
|
}
|
|
|
|
initAirdropKS()
|
|
{
|
|
config = SpawnStruct();
|
|
config.modelNames = [];
|
|
config.modelNames[ "allies" ] = "vehicle_ac130_low_mp";
|
|
config.modelNames[ "axis" ] = "vehicle_ac130_low_mp";
|
|
config.inboundSfx = "veh_ac130_dist_loop";
|
|
// veh_ac130_sonic_boom
|
|
config.compassIconFriendly = "compass_objpoint_c130_friendly";
|
|
config.compassIconEnemy = "compass_objpoint_c130_enemy";
|
|
config.noLightFx = true;
|
|
// sonic boom?
|
|
config.speed = 2000;
|
|
config.halfDistance = 12000;
|
|
config.heightRange = 500;
|
|
//config.attackTime = 2.0;
|
|
// config.outboundFlightAnim = "airstrike_mp_roll";
|
|
config.onAttackDelegate = ::dropBombs;
|
|
config.onFlybyCompleteDelegate = ::cleanupFlyby;
|
|
config.chooseDirection = true;
|
|
config.selectLocationVO = "KS_hqr_airstrike";
|
|
config.inboundVO = "KS_ast_inbound";
|
|
config.chooseDirection = false;
|
|
|
|
config.droppedObjs = [];
|
|
|
|
// config addDropObj( "mine_anvil_bomb", undefined, "falling_trail", undefined, "cart_explode", undefined, 200 );
|
|
config addDropObj( "piano", "mine_piano_bomb", undefined, "falling_trail", undefined, "cart_explode", "undefined", 300 );
|
|
config addDropObj( "bomb", "mine_large_bomb", undefined, "falling_trail", undefined, "cart_explode", undefined, 300 );
|
|
|
|
config.bombModel = "projectile_cbu97_clusterbomb";
|
|
config.numBombs = 8;
|
|
// should be 2x the effect radius to have no gaps/overlap
|
|
config.distanceBetweenBombs = 350;
|
|
config.effectRadius = 200;
|
|
config.effectHeight = 120;
|
|
|
|
level.planeConfigs[ CONST_KILLSTREAK_NAME ] = config;
|
|
|
|
level.killstreakFuncs[CONST_KILLSTREAK_NAME] = ::onUse;
|
|
level.killstreakWeildWeapons[ CONST_KILLSTREAK_WEAPON ] = CONST_KILLSTREAK_NAME;
|
|
}
|
|
|
|
onUse( lifeId, streakName )
|
|
{
|
|
assert( isDefined( self ) );
|
|
|
|
// check for active air_superiority strikes
|
|
otherTeam = getOtherTeam( self.team );
|
|
|
|
result = selectAirstrikeLocationWithGrenade( lifeId, streakName, getKillstreakWeapon( streakName ), ::doStrike );
|
|
|
|
return ( IsDefined( result ) && result );
|
|
}
|
|
|
|
doStrike( lifeId, location, directionYaw, streakName )
|
|
{
|
|
wait ( 1 );
|
|
|
|
directionYaw = self.angles[1] + 90;
|
|
|
|
planeFlyHeight = maps\mp\killstreaks\_plane::getPlaneFlyHeight();
|
|
|
|
dirVector = AnglesToForward( (0, directionYaw, 0) );
|
|
|
|
doOneFlyby( streakName, lifeId, location, dirVector, planeFlyHeight );
|
|
|
|
self waittill( "airstrike_flyby_complete" );
|
|
|
|
// play outbound vo
|
|
}
|
|
|
|
doOneFlyby( streakName, lifeId, targetPos, dir, flyHeight )
|
|
{
|
|
config = level.planeConfigs[ streakName ];
|
|
|
|
// absolute height should be derived from the heightEnt
|
|
flightPath = maps\mp\killstreaks\_plane::getFlightPath( targetPos, dir, config.halfDistance, true, flyHeight, config.speed, 0, streakName );
|
|
|
|
// Box( targetPos, dir[1], (0, 0, 1), false, 200);
|
|
|
|
// may want to break this up into spawn, move, cleanup components
|
|
// so that we can reuse the plane
|
|
level thread maps\mp\killstreaks\_plane::doFlyby( lifeId, self, lifeId,
|
|
flightPath["startPoint"] + (0, 0, randomInt(config.heightRange) ),
|
|
flightPath["endPoint"] + (0, 0, randomInt(config.heightRange) ),
|
|
flightPath["attackTime"],
|
|
flightPath["flyTime"],
|
|
dir,
|
|
streakName );
|
|
}
|
|
|
|
cleanupFlyby( owner, plane, streakName )
|
|
{
|
|
owner notify( "airstrike_flyby_complete" );
|
|
}
|
|
|
|
|
|
dropBombs( pathEnd, flyTime, beginAttackTime, owner, streakName ) // self == plane
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait ( 0.95 * beginAttackTime);
|
|
|
|
config = level.planeConfigs[ streakName ];
|
|
|
|
numBombsLeft = config.numBombs;
|
|
timeBetweenBombs = config.distanceBetweenBombs / config.speed;
|
|
|
|
while (numBombsLeft > 0)
|
|
{
|
|
bombPos = pickRandomTargetPoint( self.origin, 50 );
|
|
|
|
self thread dropOneBomb( owner, streakName, bombPos, "bomb" );
|
|
|
|
numBombsLeft--;
|
|
|
|
wait ( 0.1 );
|
|
}
|
|
|
|
bombPos = pickRandomTargetPoint( self.origin, 50 );
|
|
self thread dropOneBomb( owner, streakName, bombPos, "piano" );
|
|
|
|
}
|
|
|
|
pickBombType( config )
|
|
{
|
|
index = RandomInt( config.droppedObjs.size );
|
|
|
|
return config.droppedObjs[ index ];
|
|
}
|
|
|
|
CONST_DRAW_TIME = 200;
|
|
GRAVITY_UNITS_PER_SEC = 800;
|
|
GRAVITY_UNITS_PER_SEC_DIV = 0.00125;
|
|
dropOneBomb( owner, streakName, spawnPoint, bombType ) // self == plane
|
|
{
|
|
plane = self;
|
|
|
|
config = level.planeConfigs[ streakName ];
|
|
|
|
planeDir = AnglesToForward( plane.angles );
|
|
|
|
// bombConfig = pickBombType( config );
|
|
bombConfig = config.droppedObjs[ bombType ];
|
|
|
|
bomb = spawnBomb( bombConfig["model"], spawnPoint, plane.angles );
|
|
bomb.owner = owner;
|
|
|
|
|
|
trace = BulletTrace( bomb.origin, bomb.origin + (0,0,-1000000), false, plane );
|
|
impactPosition = trace["position"];
|
|
|
|
// calculate time it takes to fall distance to impac
|
|
timeSquared = Length( bomb.origin - impactPosition ) * 2 * GRAVITY_UNITS_PER_SEC_DIV;
|
|
fallTime = sqrt( timeSquared );
|
|
|
|
/#
|
|
// Sphere( bomb.origin, 10, (0, 1, 0), false, CONST_DRAW_TIME );
|
|
// Line( bomb.origin, impactPosition, (0, 1, 0), 1, false, CONST_DRAW_TIME );
|
|
// Sphere( bomb.origin, 20, (0, 1, 0), false, CONST_DRAW_TIME );
|
|
#/
|
|
|
|
bomb MoveGravity( (0, 0, 0), fallTime );
|
|
|
|
// add trail vfx
|
|
// add falling sfx
|
|
|
|
wait ( fallTime );
|
|
|
|
PlayFX( bombConfig["impactVfx"], bomb.origin );
|
|
|
|
bomb onBombImpact( owner, self, impactPosition, streakName, bombConfig );
|
|
}
|
|
|
|
|
|
spawnBomb( modelName, origin, angles )
|
|
{
|
|
bomb = Spawn( "script_model", origin );
|
|
bomb.angles = angles;
|
|
bomb SetModel( modelname );
|
|
|
|
return bomb;
|
|
}
|
|
|
|
onBombImpact( owner, plane, position, streakName, bombConfig ) // self == bomb?
|
|
{
|
|
config = level.planeConfigs[ streakName ];
|
|
|
|
PlayFX( bombConfig["impactVfx"], position );
|
|
// play impact sfx
|
|
|
|
// damage radius - need amount, weapon
|
|
self RadiusDamage( position, bombConfig["impactRadius"], 200, 80, owner, "MOD_CRUSH", CONST_KILLSTREAK_WEAPON );
|
|
|
|
/#
|
|
// Sphere( position, 10, (1, 0, 0), false, CONST_DRAW_TIME );
|
|
// Sphere( position, bombConfig["impactRadius"], (1, 0, 0), false, CONST_DRAW_TIME );
|
|
#/
|
|
|
|
self Hide();
|
|
|
|
wait ( 5 );
|
|
|
|
self Delete();
|
|
}
|
|
|
|
pickRandomTargetPoint( targetPoint, strikeRadius )
|
|
{
|
|
x = RandomFloatRange( -1 * strikeRadius, strikeRadius );
|
|
y = RandomFloatRange( -1 * strikeRadius, strikeRadius );
|
|
return targetPoint + (x, y, 0);
|
|
}
|
|
|
|
selectAirstrikeLocationWithGrenade( lifeId, killstreakName, weaponName, onLocationSelected )
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "disconnect" );
|
|
self endon( "death" );
|
|
|
|
self notify( "selectAirstrikeLocationWithGrenade" );
|
|
self endon( "selectAirstrikeLocationWithGrenade" );
|
|
|
|
self childthread waitForMarkerUse( lifeId, killstreakName, weaponName, onLocationSelected );
|
|
self childthread waitForWeaponSwitch( killstreakName, weaponName );
|
|
|
|
result = self waittill_any_return( "airstrike_selected", "airstrke_canceled" );
|
|
|
|
return ( result == "airstrike_selected" );
|
|
}
|
|
|
|
waitForMarkerUse( lifeId, killstreakName, markerName, onLocationSelected )
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "disconnect" );
|
|
self endon( "death" );
|
|
self endon( "airstrke_canceled" );
|
|
|
|
self.threwAirDropMarker = undefined;
|
|
while( true )
|
|
{
|
|
self waittill( "grenade_pullback", weaponName );
|
|
|
|
if ( weaponName != markerName )
|
|
continue;
|
|
|
|
self _disableUsability();
|
|
|
|
// need to thread something to enableUsability in case airstrike is cancelled
|
|
self thread restoreUsability();
|
|
|
|
self waittill( "grenade_fire", marker, weaponName );
|
|
|
|
self _enableUsability();
|
|
|
|
if ( weaponName != markerName )
|
|
continue;
|
|
|
|
marker.owner = self;
|
|
marker.weaponName = weaponName;
|
|
self TakeWeapon( weaponName );
|
|
|
|
marker thread detonateOnStuck( lifeId, killstreakName, onLocationSelected );
|
|
break;
|
|
}
|
|
}
|
|
|
|
waitForWeaponSwitch( killstreakName, weaponName )
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "disconnect" );
|
|
self endon( "death" );
|
|
self endon( "airstrike_selected" );
|
|
|
|
currentWeapon = self GetCurrentWeapon();
|
|
// first, wait until we raise the marker
|
|
while ( currentWeapon != weaponName )
|
|
{
|
|
self waittill( "weapon_change", currentWeapon );
|
|
}
|
|
|
|
// then wait until we switch away from the marker
|
|
while ( currentWeapon == weaponName )
|
|
{
|
|
self waittill( "weapon_change", currentWeapon );
|
|
}
|
|
|
|
// check if we selected airstrike?
|
|
if ( !IsDefined( self.threwAirDropMarker ) || !self.threwAirDropMarker )
|
|
{
|
|
self notify( "airstrke_canceled" );
|
|
}
|
|
}
|
|
|
|
detonateOnStuck( lifeId, killstreakName, onLocationSelected ) // self == grenade
|
|
{
|
|
owner = self.owner;
|
|
owner.threwAirDropMarker = true;
|
|
|
|
self waittill( "missile_stuck" );
|
|
|
|
position = self.origin;
|
|
|
|
self Detonate();
|
|
|
|
BadPlace_Cylinder( "", 10, position, 600, 100, "axis", "allies" );
|
|
|
|
// validate the grenade location?
|
|
|
|
owner notify( "airstrike_selected" );
|
|
|
|
owner thread [[ onLocationSelected ]]( lifeId, position, 0, killstreakName );
|
|
|
|
}
|
|
|
|
restoreUsability()
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "disconnect" );
|
|
self endon( "death" );
|
|
self endon( "airstrke_canceled" );
|
|
self endon( "airstrike_selected" );
|
|
|
|
self waittill( "weapon_change" );
|
|
|
|
self _enableUsability();
|
|
}
|
|
|
|
deleteAfterTime( time )
|
|
{
|
|
self endon ( "death" );
|
|
wait ( time );
|
|
|
|
self Delete();
|
|
}
|
|
*/
|