init
This commit is contained in:
63
maps/mp/_animatedmodels.gsc
Normal file
63
maps/mp/_animatedmodels.gsc
Normal file
@@ -0,0 +1,63 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
#using_animtree( "animated_props" );
|
||||
main()
|
||||
{
|
||||
//level.init_animatedmodels_dump = false;
|
||||
|
||||
if ( !isdefined( level.anim_prop_models ) )
|
||||
level.anim_prop_models = []; // this is what the LD puts in their map
|
||||
|
||||
// Do special MP anim precaching
|
||||
model_keys = GetArrayKeys( level.anim_prop_models );
|
||||
foreach ( model_key in model_keys )
|
||||
{
|
||||
anim_keys = GetArrayKeys( level.anim_prop_models[model_key] );
|
||||
foreach ( anim_key in anim_keys )
|
||||
PrecacheMpAnim( level.anim_prop_models[model_key][anim_key] );
|
||||
//PrecacheMpAnim( level.anim_prop_models[ "foliage_tree_palm_bushy_1" ][ "strong" ] );
|
||||
}
|
||||
|
||||
// wait until the end of the frame so that maps can init their trees
|
||||
// in their _anim instead of only above _load
|
||||
waittillframeend;
|
||||
|
||||
level.init_animatedmodels = [];
|
||||
|
||||
animated_models = getentarray( "animated_model", "targetname" );
|
||||
//array_thread( animated_models, ::model_init );
|
||||
|
||||
// one or more of the models initialized by model_init() was not setup by the map
|
||||
// so print this helpful note so the designer can see how to add it ot their level
|
||||
//if ( level.init_animatedmodels_dump )
|
||||
//assertmsg( "anims not cached for animated prop model, Repackage Zones and Rebuild Precache Script in Launcher:" );
|
||||
|
||||
array_thread_amortized( animated_models, ::animateModel, 0.05 );
|
||||
|
||||
level.init_animatedmodels = undefined;
|
||||
}
|
||||
|
||||
// Disabled for now since we are precaching .animation prefabs in a non-standard way
|
||||
/*model_init()
|
||||
{
|
||||
if ( !isdefined( level.anim_prop_models[ self.model ] ) )
|
||||
level.init_animatedmodels_dump = true;
|
||||
}*/
|
||||
|
||||
// TODO: When we have multiple animations, instead of choosing randomly, do round-robin to get an even spread
|
||||
animateModel()
|
||||
{
|
||||
if ( IsDefined( self.animation ) )
|
||||
{
|
||||
animation = self.animation;
|
||||
}
|
||||
else
|
||||
{
|
||||
keys = GetArrayKeys( level.anim_prop_models[ self.model ] );
|
||||
animkey = keys[ RandomInt( keys.size ) ];
|
||||
animation = level.anim_prop_models[ self.model ][ animkey ];
|
||||
}
|
||||
|
||||
self ScriptModelPlayAnim( animation );
|
||||
self willNeverChange();
|
||||
}
|
||||
98
maps/mp/_areas.gsc
Normal file
98
maps/mp/_areas.gsc
Normal file
@@ -0,0 +1,98 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*QUAKED trigger_multiple_area (0.12 0.23 1.0)
|
||||
defaulttexture="trigger"
|
||||
"script_area" - A localized string that names the area. e.g. "MP_FLOWER_SHOP"
|
||||
Defines an area that the player is in.*/
|
||||
|
||||
/*QUAKED trigger_multiple_softlanding (0.12 0.23 1.0)
|
||||
defaulttexture="trigger"
|
||||
"script_type" - "car", "boxes", "trash"
|
||||
Defines a soft landing area.*/
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
level.softLandingTriggers = getEntArray( "trigger_multiple_softlanding", "classname" );
|
||||
|
||||
destructibles = getEntArray( "destructible_vehicle", "targetname" );
|
||||
|
||||
foreach ( trigger in level.softLandingTriggers )
|
||||
{
|
||||
if ( trigger.script_type != "car" )
|
||||
continue;
|
||||
|
||||
foreach ( destructible in destructibles )
|
||||
{
|
||||
/*
|
||||
if ( !trigger isTouching( destructible ) )
|
||||
{
|
||||
println( distance( trigger.origin, destructible.origin ) );
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( distance( trigger.origin, destructible.origin ) > 64.0 )
|
||||
continue;
|
||||
|
||||
assert( !isDefined( trigger.destructible ) );
|
||||
|
||||
trigger.destructible = destructible;
|
||||
}
|
||||
}
|
||||
|
||||
//foreach ( trigger in level.softLandingTriggers )
|
||||
// trigger thread common_scripts\_dynamic_world::triggerTouchThink( ::playerEnterSoftLanding, ::playerLeaveSoftLanding );
|
||||
|
||||
thread onPlayerConnect();
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "connected", player );
|
||||
|
||||
player.softLanding = undefined;
|
||||
|
||||
player thread softLandingWaiter();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
playerEnterSoftLanding( trigger )
|
||||
{
|
||||
self.softLanding = trigger;
|
||||
}
|
||||
|
||||
|
||||
playerLeaveSoftLanding( trigger )
|
||||
{
|
||||
self.softLanding = undefined;
|
||||
}
|
||||
|
||||
|
||||
softLandingWaiter()
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill ( "soft_landing", trigger, damage );
|
||||
|
||||
//if ( damage < 10 )
|
||||
// continue;
|
||||
|
||||
if ( !isDefined( trigger.destructible ) )
|
||||
continue;
|
||||
|
||||
//magicBullet( "mp5_mp", self.origin, self.origin + (0,0,-100), self );
|
||||
|
||||
//self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, dflags );
|
||||
|
||||
//traceData = bulletTrace( self.origin, self.origin + (0,0,-100), true, self );
|
||||
|
||||
}
|
||||
}
|
||||
714
maps/mp/_art.gsc
Normal file
714
maps/mp/_art.gsc
Normal file
@@ -0,0 +1,714 @@
|
||||
// This function should take care of grain and glow settings for each map, plus anything else that artists
|
||||
// need to be able to tweak without bothering level designers.
|
||||
#include common_scripts\utility;
|
||||
#include common_scripts\_artCommon;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
/#
|
||||
PrecacheMenu( "dev_vision_noloc" );
|
||||
PrecacheMenu( "dev_vision_exec" );
|
||||
|
||||
setDevDvarIfUninitialized( "scr_art_tweak", 0 );
|
||||
setDevDvarIfUninitialized( "scr_cmd_plr_sun", "0" );
|
||||
SetDevDvarIfUninitialized( "scr_cmd_plr_sunflare", "0" );
|
||||
setDevDvarIfUninitialized( "scr_art_visionfile", level.script );
|
||||
SetDevDvar( "r_artUseTweaks", false );
|
||||
|
||||
thread tweakart();
|
||||
|
||||
tess_init();
|
||||
|
||||
if ( !isdefined( level.script ) )
|
||||
level.script = ToLower( GetDvar( "mapname" ) );
|
||||
#/
|
||||
}
|
||||
|
||||
/#
|
||||
initTweaks()
|
||||
{
|
||||
SetDevDvar( "r_artUseTweaks", true );
|
||||
|
||||
if ( IsDefined( level.parse_fog_func ) )
|
||||
[[level.parse_fog_func]]();
|
||||
|
||||
if ( !IsDefined( level.buttons ) )
|
||||
level.buttons = [];
|
||||
|
||||
level._clearalltextafterhudelem = false;
|
||||
|
||||
if ( !IsDefined( level.vision_set_names ) )
|
||||
level.vision_set_names = [];
|
||||
|
||||
if( !IsDefined( level.vision_set_fog ) )
|
||||
{
|
||||
level.vision_set_fog = [];
|
||||
create_default_vision_set_fog( level.script );
|
||||
common_scripts\_artCommon::setfogsliders();
|
||||
}
|
||||
|
||||
foreach( key, value in level.vision_set_fog )
|
||||
{
|
||||
common_scripts\_artCommon::add_vision_set_to_list( key );
|
||||
}
|
||||
|
||||
add_vision_sets_from_triggers();
|
||||
|
||||
update_current_vision_set_dvars();
|
||||
|
||||
if ( !IsDefined( level.current_vision_set ) )
|
||||
level.current_vision_set = GetDvar( "r_artTweaksLastVisionSet", "" );
|
||||
|
||||
IPrintLnBold( "ART TWEAK ENABLED" );
|
||||
hud_init();
|
||||
|
||||
last_vision_set = level.current_vision_set;
|
||||
if ( !IsDefined( last_vision_set ) || last_vision_set == "" )
|
||||
last_vision_set = level.script;
|
||||
|
||||
setcurrentgroup( last_vision_set );
|
||||
}
|
||||
|
||||
tweakart()
|
||||
{
|
||||
if ( !isdefined( level.tweakfile ) )
|
||||
level.tweakfile = false;
|
||||
|
||||
// not in DEVGUI
|
||||
SetDevDvar( "scr_fog_fraction", "1.0" );
|
||||
SetDevDvar( "scr_art_dump", "0" );
|
||||
|
||||
printed = false;
|
||||
|
||||
last_vision_set = "";
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
while ( GetDvarInt( "scr_art_tweak", 0 ) == 0 )
|
||||
wait .05;
|
||||
|
||||
if ( !printed )
|
||||
{
|
||||
printed = true;
|
||||
initTweaks();
|
||||
}
|
||||
|
||||
//translate the slider values to script variables
|
||||
common_scripts\_artCommon::translateFogSlidersToScript();
|
||||
|
||||
common_scripts\_artCommon::fogslidercheck();
|
||||
|
||||
updateSunFlarePosition();
|
||||
|
||||
dumpsettings();
|
||||
|
||||
updateFogFromScript();
|
||||
|
||||
if ( getdvarint( "scr_select_art_next" ) || button_down( "dpad_down", "kp_downarrow" ) )
|
||||
setgroup_down();
|
||||
else if ( getdvarint( "scr_select_art_prev" ) || button_down( "dpad_up", "kp_uparrow" ) )
|
||||
setgroup_up();
|
||||
else if( level.current_vision_set != last_vision_set )
|
||||
{
|
||||
last_vision_set = level.current_vision_set;
|
||||
setcurrentgroup( last_vision_set );
|
||||
}
|
||||
|
||||
wait .05;
|
||||
}
|
||||
}
|
||||
|
||||
tess_init()
|
||||
{
|
||||
using_tessellation = GetDvar( "r_tessellation" );
|
||||
if( using_tessellation == "" )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
level.tess = SpawnStruct();
|
||||
|
||||
// Default Tessellation Values - push base settings to game values
|
||||
level.tess.cutoff_distance_current = GetDvarFloat( "r_tessellationCutoffDistanceBase", 960.0 );
|
||||
level.tess.cutoff_distance_goal = level.tess.cutoff_distance_current;
|
||||
level.tess.cutoff_falloff_current = GetDvarFloat( "r_tessellationCutoffFalloffBase", 320.0 );
|
||||
level.tess.cutoff_falloff_goal = level.tess.cutoff_falloff_current;
|
||||
level.tess.time_remaining = 0.0;
|
||||
SetDvar( "r_tessellationCutoffDistance", level.tess.cutoff_distance_current );
|
||||
SetDvar( "r_tessellationCutoffFalloff" , level.tess.cutoff_falloff_current );
|
||||
|
||||
thread tess_update();
|
||||
}
|
||||
|
||||
tess_set_goal( cutoff_distance, cutoff_falloff, blend_time )
|
||||
{
|
||||
level.tess.cutoff_distance_goal = cutoff_distance;
|
||||
level.tess.cutoff_falloff_goal = cutoff_falloff;
|
||||
level.tess.time_remaining = blend_time;
|
||||
}
|
||||
|
||||
tess_update()
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
cutoff_distance_old = level.tess.cutoff_distance_current;
|
||||
cutoff_falloff_old = level.tess.cutoff_falloff_current;
|
||||
|
||||
waitframe();
|
||||
if ( level.tess.time_remaining > 0.0 )
|
||||
{
|
||||
frames = level.tess.time_remaining * 20;
|
||||
distance_increment = ( level.tess.cutoff_distance_goal - level.tess.cutoff_distance_current ) / frames;
|
||||
falloff_increment = ( level.tess.cutoff_falloff_goal - level.tess.cutoff_falloff_current ) / frames;
|
||||
level.tess.cutoff_distance_current += distance_increment;
|
||||
level.tess.cutoff_falloff_current += falloff_increment;
|
||||
level.tess.time_remaining -= 0.05;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.tess.cutoff_distance_current = level.tess.cutoff_distance_goal;
|
||||
level.tess.cutoff_falloff_current = level.tess.cutoff_falloff_goal;
|
||||
}
|
||||
|
||||
if( cutoff_distance_old != level.tess.cutoff_distance_current )
|
||||
{
|
||||
SetDvar( "r_tessellationCutoffDistance", level.tess.cutoff_distance_current );
|
||||
}
|
||||
if( cutoff_falloff_old != level.tess.cutoff_falloff_current )
|
||||
{
|
||||
SetDvar( "r_tessellationCutoffFalloff" , level.tess.cutoff_falloff_current );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateSunFlarePosition()
|
||||
{
|
||||
if ( GetDvarInt( "scr_cmd_plr_sunflare" ) )
|
||||
{
|
||||
SetDevDvar( "scr_cmd_plr_sunflare", 0 );
|
||||
|
||||
pos = level.players[0] GetPlayerAngles();
|
||||
|
||||
// Output the pos to the window
|
||||
pos_string = "Sun Flare = ( " + pos[0] + ", " + pos[1] + ", " + pos[2] + " )";
|
||||
IPrintLnBold( pos_string );
|
||||
Print( pos_string );
|
||||
}
|
||||
}
|
||||
|
||||
dumpsettings()
|
||||
{
|
||||
if ( GetDvarInt( "scr_art_dump" ) == 0 )
|
||||
return false;
|
||||
|
||||
SetdevDvar( "scr_art_dump", "0" );
|
||||
|
||||
////////////////// [level]_art.gsc
|
||||
fileprint_launcher_start_file();
|
||||
fileprint_launcher( "// _createart generated. modify at your own risk. Changing values should be fine." );
|
||||
fileprint_launcher( "main()" );
|
||||
fileprint_launcher( "{" );
|
||||
fileprint_launcher( "\tlevel.tweakfile = true;" );
|
||||
if ( IsDefined( level.parse_fog_func ) ) // Don't print this unless it already exists (otherwise [levelname]_fog.gsc will likely fail to compile)
|
||||
fileprint_launcher( "\tlevel.parse_fog_func = maps\\createart\\" + level.script + "_fog::main;" );
|
||||
fileprint_launcher( "" );
|
||||
fileprint_launcher( "\tsetDevDvar( \"scr_fog_disable\"" + ", " + "\"" + GetDvarInt( "scr_fog_disable" ) + "\"" + " );" );
|
||||
// Writing this out in case someone needs it
|
||||
if ( ! GetDvarInt( "scr_fog_disable" ) )
|
||||
{
|
||||
if ( level.sunFogEnabled )
|
||||
fileprint_launcher( "//\tsetExpFog( " + level.fognearplane + ", " + level.fogexphalfplane + ", " + level.fogcolor[0] + ", " + level.fogcolor[1] + ", " + level.fogcolor[2] + ", " + level.fogHDRColorIntensity + ", " + level.fogmaxopacity + ", 0, " + level.sunFogColor[0] + ", " + level.sunFogColor[1] + ", " + level.sunFogColor[2] + ", " + level.sunFogHDRColorIntensity + ", (" + level.sunFogDir[0] + ", " + level.sunFogDir[1] + ", " + level.sunFogDir[2] + "), " + level.sunFogBeginFadeAngle + ", " + level.sunFogEndFadeAngle + ", " + level.sunFogScale + ", " + level.skyFogIntensity + ", " + level.skyFogMinAngle + ", " + level.skyFogMaxAngle + " );" );
|
||||
else
|
||||
fileprint_launcher( "//\tsetExpFog( " + level.fognearplane + ", " + level.fogexphalfplane + ", " + level.fogcolor[0] + ", " + level.fogcolor[1] + ", " + level.fogcolor[2] + ", " + level.fogHDRColorIntensity + ", " + level.fogmaxopacity + ", 0, " + level.skyFogIntensity + ", " + level.skyFogMinAngle + ", " + level.skyFogMaxAngle + " );" );
|
||||
}
|
||||
fileprint_launcher( "\tVisionSetNaked( \"" + level.script + "\", 0 );" );
|
||||
fileprint_launcher( "}" );
|
||||
if ( !fileprint_launcher_end_file( "\\share\\raw\\maps\\createart\\" + level.script + "_art.gsc", true ) )
|
||||
return;
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
// MP doesn't write [level]_art.csv?
|
||||
|
||||
|
||||
////////////////// [level]_fog.gsc
|
||||
if ( IsDefined( level.parse_fog_func ) )
|
||||
{
|
||||
fileprint_launcher_start_file();
|
||||
fileprint_launcher( "// _createart generated. modify at your own risk. Do not use block comments." );
|
||||
fileprint_launcher( "main()" );
|
||||
fileprint_launcher( "{" );
|
||||
common_scripts\_artCommon::print_fog_ents( true );
|
||||
fileprint_launcher( "}" );
|
||||
if ( !fileprint_launcher_end_file( "\\share\\raw\\maps\\createart\\" + level.script + "_fog.gsc", true ) )
|
||||
return;
|
||||
}
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
////////////////// [level].vision
|
||||
if ( !common_scripts\_artCommon::print_vision( level.current_vision_set ) )
|
||||
return;
|
||||
|
||||
iprintlnbold( "Save successful!" );
|
||||
|
||||
PrintLn( "Art settings dumped success!" );
|
||||
}
|
||||
|
||||
add_vision_sets_from_triggers()
|
||||
{
|
||||
assert( IsDefined( level.vision_set_fog ) );
|
||||
|
||||
triggers = GetEntArray( "trigger_multiple_visionset" , "classname" );
|
||||
|
||||
// mkornkven: probably won't get anything -- need a way to get at the client trigger data?
|
||||
foreach( trigger in triggers )
|
||||
{
|
||||
name = undefined;
|
||||
|
||||
if( IsDefined( trigger.script_visionset ) )
|
||||
name = ToLower( trigger.script_visionset );
|
||||
else if ( IsDefined( trigger.script_visionset_start ) )
|
||||
name = ToLower( trigger.script_visionset_start );
|
||||
else if ( IsDefined( trigger.script_visionset_end ) )
|
||||
name = ToLower( trigger.script_visionset_end );
|
||||
|
||||
if ( IsDefined( name ) )
|
||||
add_vision_set( name );
|
||||
}
|
||||
}
|
||||
|
||||
add_vision_set( vision_set_name )
|
||||
{
|
||||
assert( vision_set_name == ToLower( vision_set_name ) );
|
||||
|
||||
if ( IsDefined( level.vision_set_fog[ vision_set_name ] ) )
|
||||
return;
|
||||
|
||||
create_default_vision_set_fog( vision_set_name );
|
||||
common_scripts\_artCommon::add_vision_set_to_list( vision_set_name );
|
||||
|
||||
IPrintLnBold( "new vision: " + vision_set_name );
|
||||
}
|
||||
|
||||
button_down( btn, btn2 )
|
||||
{
|
||||
pressed = level.players[0] ButtonPressed( btn );
|
||||
|
||||
if ( !pressed )
|
||||
{
|
||||
pressed = level.players[0] ButtonPressed( btn2 );
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.buttons[ btn ] ) )
|
||||
{
|
||||
level.buttons[ btn ] = 0;
|
||||
}
|
||||
|
||||
// To Prevent Spam
|
||||
if ( GetTime() < level.buttons[ btn ] )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
level.buttons[ btn ] = GetTime() + 400;
|
||||
return pressed;
|
||||
}
|
||||
|
||||
updateFogFromScript()
|
||||
{
|
||||
if ( GetDvarInt( "scr_cmd_plr_sun" ) )
|
||||
{
|
||||
SetDevDvar( "scr_sunFogDir", AnglesToForward( level.players[0] GetPlayerAngles() ) );
|
||||
SetDevDvar( "scr_cmd_plr_sun", 0 );
|
||||
}
|
||||
|
||||
ent = get_fog_ent_for_vision_set( level.current_vision_set );
|
||||
|
||||
if( IsDefined( ent ) && isdefined( ent.name ) )
|
||||
{
|
||||
ent.startDist = level.fognearplane;
|
||||
ent.halfwayDist = level.fogexphalfplane;
|
||||
ent.red = level.fogcolor[ 0 ];
|
||||
ent.green = level.fogcolor[ 1 ];
|
||||
ent.blue = level.fogcolor[ 2 ];
|
||||
ent.HDRColorIntensity = level.fogHDRColorIntensity;
|
||||
ent.maxOpacity = level.fogmaxopacity;
|
||||
|
||||
ent.sunFogEnabled = level.sunFogEnabled;
|
||||
ent.sunRed = level.sunFogColor[ 0 ];
|
||||
ent.sunGreen = level.sunFogColor[ 1 ];
|
||||
ent.sunBlue = level.sunFogColor[ 2 ];
|
||||
ent.HDRSunColorIntensity = level.sunFogHDRColorIntensity;
|
||||
ent.sunDir = level.sunFogDir;
|
||||
ent.sunBeginFadeAngle = level.sunFogBeginFadeAngle;
|
||||
ent.sunEndFadeAngle = level.sunFogEndFadeAngle;
|
||||
ent.normalFogScale = level.sunFogScale;
|
||||
|
||||
ent.skyFogIntensity = level.skyFogIntensity;
|
||||
ent.skyFogMinAngle = level.skyFogMinAngle;
|
||||
ent.skyFogMaxAngle = level.skyFogMaxAngle;
|
||||
|
||||
if ( GetDvarInt( "scr_fog_disable" ) )
|
||||
{
|
||||
ent.startDist = 2000000000;
|
||||
ent.halfwayDist = 2000000001;
|
||||
ent.red = 0;
|
||||
ent.green = 0;
|
||||
ent.blue = 0;
|
||||
ent.HDRSunColorIntensity = 1;
|
||||
ent.maxOpacity = 0;
|
||||
ent.skyFogIntensity = 0;
|
||||
}
|
||||
|
||||
if ( IsDefined( level.parse_fog_func ) ) // Otherwise, this is the default set, which we don't want to set
|
||||
set_fog_to_ent_values( ent, 0 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update_current_vision_set_dvars()
|
||||
{
|
||||
level.players[0] openpopupmenu("dev_vision_exec");
|
||||
wait( 0.05 );
|
||||
level.players[0] closepopupmenu();
|
||||
}
|
||||
|
||||
vision_set_changes( vision_set )
|
||||
{
|
||||
// Set the vision set and push the values to tweak dvars in code
|
||||
VisionSetNaked( vision_set, 0 );
|
||||
update_current_vision_set_dvars();
|
||||
level.current_vision_set = vision_set;
|
||||
}
|
||||
|
||||
vision_set_fog_changes( vision_set, transition_time )
|
||||
{
|
||||
vision_set_changes( vision_set );
|
||||
fog_ent = get_fog_ent_for_vision_set( vision_set );
|
||||
if ( IsDefined( fog_ent ) )
|
||||
{
|
||||
translateFogEntTosliders( fog_ent );
|
||||
if ( IsDefined( level.parse_fog_func ) ) // Otherwise, this is the default set, which we don't want to set
|
||||
set_fog_to_ent_values( fog_ent, transition_time);
|
||||
}
|
||||
}
|
||||
|
||||
get_fog_ent_for_vision_set( vision_set )
|
||||
{
|
||||
fog_ent = level.vision_set_fog[ ToLower( vision_set ) ];
|
||||
if ( using_hdr_fog() && IsDefined( fog_ent ) && IsDefined( fog_ent.HDROverride ) )
|
||||
fog_ent = level.vision_set_fog[ ToLower( fog_ent.HDROverride ) ];
|
||||
|
||||
return fog_ent;
|
||||
}
|
||||
|
||||
using_hdr_fog()
|
||||
{
|
||||
if ( !IsDefined( level.console ) )
|
||||
set_console_status();
|
||||
AssertEx( IsDefined( level.console ) && IsDefined( level.xb3 ) && IsDefined( level.ps4 ), "Expected platform defines to be complete." );
|
||||
|
||||
return is_gen4();
|
||||
}
|
||||
|
||||
translateFogEntTosliders( ent )
|
||||
{
|
||||
SetDevDvar( "scr_fog_exp_halfplane", ent.halfwayDist );
|
||||
SetDevDvar( "scr_fog_nearplane", ent.startDist );
|
||||
SetDevDvar( "scr_fog_color", ( ent.red, ent.green, ent.blue ) );
|
||||
SetDevDvar( "scr_fog_color_intensity", ent.HDRColorIntensity );
|
||||
SetDevDvar( "scr_fog_max_opacity", ent.maxOpacity );
|
||||
|
||||
SetDevDvar( "scr_skyFogIntensity", ent.skyFogIntensity );
|
||||
SetDevDvar( "scr_skyFogMinAngle", ent.skyFogMinAngle );
|
||||
SetDevDvar( "scr_skyFogMaxAngle", ent.skyFogMaxAngle );
|
||||
|
||||
if ( IsDefined( ent.sunFogEnabled ) && ent.sunFogEnabled )
|
||||
{
|
||||
SetDevDvar( "scr_sunFogEnabled", 1 );
|
||||
SetDevDvar( "scr_sunFogColor", ( ent.sunRed, ent.sunGreen,ent.sunBlue ) );
|
||||
SetDevDvar( "scr_sunFogColorIntensity", ent.HDRSunColorIntensity );
|
||||
SetDevDvar( "scr_sunFogDir", ent.sunDir );
|
||||
SetDevDvar( "scr_sunFogBeginFadeAngle", ent.sunBeginFadeAngle );
|
||||
SetDevDvar( "scr_sunFogEndFadeAngle", ent.sunEndFadeAngle );
|
||||
SetDevDvar( "scr_sunFogScale", ent.normalFogScale );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDevDvar( "scr_sunFogEnabled", 0 );
|
||||
}
|
||||
}
|
||||
|
||||
set_fog_to_ent_values( ent, transition_time )
|
||||
{
|
||||
if ( IsDefined( ent.sunFogEnabled) && ent.sunFogEnabled )
|
||||
{
|
||||
if ( !isPlayer( self ) )
|
||||
{
|
||||
SetExpFog(
|
||||
ent.startDist,
|
||||
ent.halfwayDist,
|
||||
ent.red,
|
||||
ent.green,
|
||||
ent.blue,
|
||||
ent.HDRColorIntensity,
|
||||
ent.maxOpacity,
|
||||
transition_time,
|
||||
ent.sunRed,
|
||||
ent.sunGreen,
|
||||
ent.sunBlue,
|
||||
ent.HDRSunColorIntensity,
|
||||
ent.sunDir,
|
||||
ent.sunBeginFadeAngle,
|
||||
ent.sunEndFadeAngle,
|
||||
ent.normalFogScale,
|
||||
ent.skyFogIntensity,
|
||||
ent.skyFogMinAngle,
|
||||
ent.skyFogMaxAngle );
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayerSetExpFog(
|
||||
ent.startDist,
|
||||
ent.halfwayDist,
|
||||
ent.red,
|
||||
ent.green,
|
||||
ent.blue,
|
||||
ent.HDRColorIntensity,
|
||||
ent.maxOpacity,
|
||||
transition_time,
|
||||
ent.sunRed,
|
||||
ent.sunGreen,
|
||||
ent.sunBlue,
|
||||
ent.HDRSunColorIntensity,
|
||||
ent.sunDir,
|
||||
ent.sunBeginFadeAngle,
|
||||
ent.sunEndFadeAngle,
|
||||
ent.normalFogScale,
|
||||
ent.skyFogIntensity,
|
||||
ent.skyFogMinAngle,
|
||||
ent.skyFogMaxAngle );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !isPlayer( self ) )
|
||||
{
|
||||
SetExpFog(
|
||||
ent.startDist,
|
||||
ent.halfwayDist,
|
||||
ent.red,
|
||||
ent.green,
|
||||
ent.blue,
|
||||
ent.HDRColorIntensity,
|
||||
ent.maxOpacity,
|
||||
transition_time,
|
||||
ent.skyFogIntensity,
|
||||
ent.skyFogMinAngle,
|
||||
ent.skyFogMaxAngle );
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayerSetExpFog(
|
||||
ent.startDist,
|
||||
ent.halfwayDist,
|
||||
ent.red,
|
||||
ent.green,
|
||||
ent.blue,
|
||||
ent.HDRColorIntensity,
|
||||
ent.maxOpacity,
|
||||
transition_time,
|
||||
ent.skyFogIntensity,
|
||||
ent.skyFogMinAngle,
|
||||
ent.skyFogMaxAngle );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create_default_vision_set_fog( name )
|
||||
{
|
||||
ent = create_vision_set_fog( name );
|
||||
ent.startDist = 3764.17;
|
||||
ent.halfwayDist = 19391;
|
||||
ent.red = 0.661137;
|
||||
ent.green = 0.554261;
|
||||
ent.blue = 0.454014;
|
||||
ent.maxOpacity = 0.7;
|
||||
ent.transitionTime = 0;
|
||||
ent.skyFogIntensity = 0;
|
||||
ent.skyFogMinAngle = 0;
|
||||
ent.skyFogMaxAngle = 0;
|
||||
}
|
||||
|
||||
hud_init()
|
||||
{
|
||||
listsize = 7;
|
||||
|
||||
hudelems = [];
|
||||
spacer = 15;
|
||||
div = int( listsize / 2 );
|
||||
org = 240 - div * spacer;
|
||||
alphainc = .5 / div;
|
||||
alpha = alphainc;
|
||||
|
||||
for ( i = 0;i < listsize;i++ )
|
||||
{
|
||||
hudelems[ i ] = _newhudelem();
|
||||
hudelems[ i ].location = 0;
|
||||
hudelems[ i ].alignX = "left";
|
||||
hudelems[ i ].alignY = "middle";
|
||||
hudelems[ i ].foreground = 1;
|
||||
hudelems[ i ].fontScale = 2;
|
||||
hudelems[ i ].sort = 20;
|
||||
if ( i == div )
|
||||
hudelems[ i ].alpha = 1;
|
||||
else
|
||||
hudelems[ i ].alpha = alpha;
|
||||
|
||||
hudelems[ i ].x = 20;
|
||||
hudelems[ i ].y = org;
|
||||
hudelems[ i ] _settext( "." );
|
||||
|
||||
if ( i == div )
|
||||
alphainc *= -1;
|
||||
|
||||
alpha += alphainc;
|
||||
|
||||
org += spacer;
|
||||
}
|
||||
|
||||
level.spam_group_hudelems = hudelems;
|
||||
}
|
||||
|
||||
_newhudelem()
|
||||
{
|
||||
if ( !isdefined( level.scripted_elems ) )
|
||||
level.scripted_elems = [];
|
||||
elem = newhudelem(); // client?
|
||||
level.scripted_elems[ level.scripted_elems.size ] = elem;
|
||||
return elem;
|
||||
}
|
||||
|
||||
_settext( text )
|
||||
{
|
||||
self.realtext = text;
|
||||
self setDevText( "_" );
|
||||
self thread _clearalltextafterhudelem();
|
||||
sizeofelems = 0;
|
||||
foreach ( elem in level.scripted_elems )
|
||||
{
|
||||
if ( isdefined( elem.realtext ) )
|
||||
{
|
||||
sizeofelems += elem.realtext.size;
|
||||
elem setDevText( elem.realtext );
|
||||
}
|
||||
}
|
||||
println( "Size of elems: " + sizeofelems );
|
||||
}
|
||||
|
||||
_clearalltextafterhudelem()
|
||||
{
|
||||
if ( level._clearalltextafterhudelem )
|
||||
return;
|
||||
level._clearalltextafterhudelem = true;
|
||||
self clearalltextafterhudelem();
|
||||
wait .05;
|
||||
level._clearalltextafterhudelem = false;
|
||||
}
|
||||
|
||||
setgroup_up()
|
||||
{
|
||||
reset_cmds();
|
||||
|
||||
current_vision_set_name = level.current_vision_set;
|
||||
|
||||
index = array_find( level.vision_set_names, current_vision_set_name );
|
||||
if ( !IsDefined( index ) )
|
||||
return;
|
||||
|
||||
index -= 1;
|
||||
|
||||
if ( index < 0 )
|
||||
return;
|
||||
|
||||
setcurrentgroup( level.vision_set_names[index] );
|
||||
}
|
||||
|
||||
setgroup_down()
|
||||
{
|
||||
reset_cmds();
|
||||
|
||||
current_vision_set_name = level.current_vision_set;
|
||||
|
||||
index = array_find( level.vision_set_names, current_vision_set_name );
|
||||
if ( !IsDefined( index ) )
|
||||
return;
|
||||
|
||||
index += 1;
|
||||
|
||||
if ( index >= level.vision_set_names.size )
|
||||
return;
|
||||
|
||||
setcurrentgroup( level.vision_set_names[index] );
|
||||
}
|
||||
|
||||
reset_cmds()
|
||||
{
|
||||
SetDevDvar( "scr_select_art_next", 0 );
|
||||
SetDevDvar( "scr_select_art_prev", 0 );
|
||||
}
|
||||
|
||||
setcurrentgroup( group )
|
||||
{
|
||||
level.spam_model_current_group = group;
|
||||
|
||||
index = array_find( level.vision_set_names, group );
|
||||
if ( !IsDefined( index ) )
|
||||
index = -1;
|
||||
|
||||
hud_list_size = level.spam_group_hudelems.size;
|
||||
hud_start_index = index - int( hud_list_size / 2 );
|
||||
|
||||
for ( i = 0; i < hud_list_size; i++ )
|
||||
{
|
||||
hud_index = hud_start_index + i;
|
||||
if ( hud_index < 0 || hud_index >= level.vision_set_names.size )
|
||||
{
|
||||
level.spam_group_hudelems[i] _settext( "." );
|
||||
continue;
|
||||
}
|
||||
|
||||
level.spam_group_hudelems[i] _settext( level.vision_set_names[hud_index] );
|
||||
}
|
||||
|
||||
group_name = "";
|
||||
if ( index >= 0 )
|
||||
group_name = level.vision_set_names[ index ];
|
||||
|
||||
vision_set_fog_changes( group_name, 0 );
|
||||
}
|
||||
#/
|
||||
|
||||
create_vision_set_fog( fogsetName )
|
||||
{
|
||||
/#
|
||||
if ( !isdefined( level.vision_set_fog ) )
|
||||
level.vision_set_fog = [];
|
||||
|
||||
ent = SpawnStruct();
|
||||
ent.name = fogsetName;
|
||||
|
||||
// Special init for variables that may not exist on every set of fog yet -- add variable defaults here to avoid IsDefined checks everywhere later on
|
||||
ent.HDRColorIntensity = 1;
|
||||
ent.HDRSunColorIntensity = 1;
|
||||
ent.skyFogIntensity = 0;
|
||||
ent.skyFogMinAngle = 0;
|
||||
ent.skyFogMaxAngle = 0;
|
||||
|
||||
level.vision_set_fog[ ToLower(fogsetName) ] = ent;
|
||||
return ent;
|
||||
#/
|
||||
}
|
||||
|
||||
139
maps/mp/_audio.gsc
Normal file
139
maps/mp/_audio.gsc
Normal file
@@ -0,0 +1,139 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init_audio()
|
||||
{
|
||||
if ( !IsDefined( level.audio ) )
|
||||
{
|
||||
level.audio = SpawnStruct();
|
||||
}
|
||||
|
||||
init_reverb();
|
||||
init_whizby();
|
||||
|
||||
level.onPlayerConnectAudioInit = ::OnPlayerConnectAudioInit;
|
||||
}
|
||||
|
||||
OnPlayerConnectAudioInit()
|
||||
{
|
||||
self apply_reverb( "default" );
|
||||
// self apply_whizby(); <-- Does not work yet... Looks like it overflows the server.
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Reverb Section
|
||||
//---------------------------------------------------------
|
||||
init_reverb()
|
||||
{
|
||||
add_reverb( "default", "generic", 0.15, 0.9, 2 );
|
||||
}
|
||||
|
||||
add_reverb( name, type, wetlevel, drylevel, fadetime )
|
||||
{
|
||||
Assert( IsDefined( type ) );
|
||||
Assert( IsDefined( wetlevel ) );
|
||||
Assert( IsDefined( drylevel ) );
|
||||
|
||||
reverb = [];
|
||||
|
||||
is_roomtype_valid( type );
|
||||
|
||||
reverb[ "roomtype" ] = type;
|
||||
reverb[ "wetlevel" ] = wetlevel;
|
||||
reverb[ "drylevel" ] = drylevel;
|
||||
reverb[ "fadetime" ] = fadetime;
|
||||
|
||||
level.audio.reverb_settings[ name ] = reverb;
|
||||
}
|
||||
|
||||
is_roomtype_valid( type )
|
||||
{
|
||||
/#
|
||||
switch ( type )
|
||||
{
|
||||
case "generic":
|
||||
case "paddedcell":
|
||||
case "room":
|
||||
case "bathroom":
|
||||
case "livingroom":
|
||||
case "stoneroom":
|
||||
case "auditorium":
|
||||
case "concerthall":
|
||||
case "cave":
|
||||
case "arena":
|
||||
case "hangar":
|
||||
case "carpetedhallway":
|
||||
case "hallway":
|
||||
case "stonecorridor":
|
||||
case "alley":
|
||||
case "forest":
|
||||
case "city":
|
||||
case "mountains":
|
||||
case "quarry":
|
||||
case "plain":
|
||||
case "parkinglot":
|
||||
case "sewerpipe":
|
||||
case "underwater":
|
||||
case "drugged":
|
||||
case "dizzy":
|
||||
case "psychotic":
|
||||
return;
|
||||
default:
|
||||
AssertMsg( type + " is an Invalid Roomtype" );
|
||||
break;
|
||||
}
|
||||
#/
|
||||
}
|
||||
|
||||
apply_reverb( name )
|
||||
{
|
||||
if ( !IsDefined( level.audio.reverb_settings[ name ] ) )
|
||||
{
|
||||
reverb = level.audio.reverb_settings[ "default" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
reverb = level.audio.reverb_settings[ name ];
|
||||
}
|
||||
|
||||
self SetReverb( "snd_enveffectsprio_level", reverb[ "roomtype" ], reverb[ "drylevel" ], reverb[ "wetlevel" ], reverb[ "fadetime" ] );
|
||||
// self SetClientDvar( "cg_levelReverbRoomType", reverb[ "roomtype" ] );
|
||||
// self SetClientDvar( "cg_levelReverbDryLevel", reverb[ "drylevel" ] );
|
||||
// self SetClientDvar( "cg_levelReverbWetLevel", reverb[ "wetlevel" ] );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// whizBy Section
|
||||
//---------------------------------------------------------
|
||||
|
||||
init_whizby()
|
||||
{
|
||||
SetDevDvar( "snd_newwhizby", 1 );
|
||||
|
||||
// Default settings -- Call wrappers in your level to overwrite.
|
||||
level.audio.whizby_settings = [];
|
||||
set_whizby_radius( 15.0, 30.0, 50.0 );
|
||||
set_whizby_spread( 150.0, 250.0, 350.0 );
|
||||
}
|
||||
|
||||
set_whizby_radius( near, medium, far )
|
||||
{
|
||||
assertex( near != 0, "whizby near radius cannot be zero" );
|
||||
level.audio.whizby_settings[ "radius" ] = [ near, medium, far ];
|
||||
}
|
||||
|
||||
set_whizby_spread( near, medium, far )
|
||||
{
|
||||
level.audio.whizby_settings[ "spread" ] = [ near, medium, far ];
|
||||
}
|
||||
|
||||
apply_whizby()
|
||||
{
|
||||
settings = level.audio.whizby_settings;
|
||||
|
||||
spread = settings[ "spread" ];
|
||||
rad = settings[ "radius" ];
|
||||
|
||||
self SetWhizbySpreads( spread[ 0 ], spread[ 1 ], spread[ 2 ] );
|
||||
self SetWhizbyRadii( rad[ 0 ], rad[ 1 ], rad[ 2 ] );
|
||||
}
|
||||
1084
maps/mp/_awards.gsc
Normal file
1084
maps/mp/_awards.gsc
Normal file
File diff suppressed because it is too large
Load Diff
335
maps/mp/_barrels_leak.gsc
Normal file
335
maps/mp/_barrels_leak.gsc
Normal file
@@ -0,0 +1,335 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// CONSTANTS //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
level_limit_barrel_fx = 8;
|
||||
max_fires_from_entity = 4;
|
||||
level_barrel_fx_chance = 33;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// LOGIC //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
main()
|
||||
{
|
||||
if ( IsDefined( level.barrels_init ) )
|
||||
return;
|
||||
|
||||
|
||||
level.barrels_init = true;
|
||||
//level._barrel_fx_time = 25; //handle this individually for different barrel types
|
||||
barrels = GetEntArray( "barrel_shootable", "targetname" );
|
||||
if ( !barrels.size )
|
||||
return;
|
||||
level._barrels = SpawnStruct();
|
||||
level._barrels.num_barrel_fx = 0;
|
||||
|
||||
barrels thread precacheFX();
|
||||
barrels thread methodsInit();
|
||||
|
||||
thread post_load( barrels );
|
||||
}
|
||||
|
||||
post_load( barrels )
|
||||
{
|
||||
waittillframeend;// insure that structs are initialized
|
||||
if( level.createFX_enabled )
|
||||
return;
|
||||
array_thread( barrels, ::barrelsetup );
|
||||
}
|
||||
|
||||
barrelsetup()
|
||||
{
|
||||
self SetCanDamage( true );
|
||||
self SetCanRadiusDamage( false ); // optimization
|
||||
self.barrel_fx_array = [];
|
||||
|
||||
|
||||
node = undefined;
|
||||
|
||||
if ( IsDefined( self.target ) )
|
||||
{
|
||||
node = getstruct( self.target, "targetname" );
|
||||
self.A = node.origin;
|
||||
vec = AnglesToForward( node.angles );
|
||||
vec = ( vec * 128 );
|
||||
self.B = self.A + vec;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec = AnglesToForward( self.angles );
|
||||
vec1 = ( vec * 64 );
|
||||
self.A = self.origin + vec1;
|
||||
vec1 = ( vec * -64 );
|
||||
self.B = self.origin + vec1;
|
||||
}
|
||||
|
||||
self thread barrel_wait_loop();
|
||||
}
|
||||
|
||||
barrel_wait_loop()
|
||||
{
|
||||
P = ( 0, 0, 0 );// just to initialize P as a vector
|
||||
|
||||
hasTakenDamage = false;
|
||||
remaining = max_fires_from_entity;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, P, type );
|
||||
|
||||
// random so we don't get so many fx, but the very first time is guarenteed
|
||||
if ( hasTakenDamage )
|
||||
{
|
||||
if ( randomint( 100 ) <= level_barrel_fx_chance )
|
||||
continue;
|
||||
}
|
||||
hasTakenDamage = true;
|
||||
|
||||
result = self barrel_logic( direction_vec, P, type, attacker );
|
||||
if ( result )
|
||||
remaining--;
|
||||
|
||||
if ( remaining <= 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
self SetCanDamage( false );
|
||||
}
|
||||
|
||||
barrel_logic( direction_vec, P, type, damageOwner )
|
||||
{
|
||||
if ( level._barrels.num_barrel_fx > level_limit_barrel_fx )
|
||||
return false;
|
||||
|
||||
if ( !isDefined( level._barrels._barrel_methods[ type ] ) )
|
||||
P = self barrel_calc_nofx( P, type );
|
||||
else
|
||||
P = self [[ level._barrels._barrel_methods[ type ] ]]( P, type );
|
||||
|
||||
if ( !isdefined( P ) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( damageOwner.classname ) && damageOwner.classname == "worldspawn" )
|
||||
return false;
|
||||
|
||||
foreach ( value in self.barrel_fx_array )
|
||||
{
|
||||
if ( DistanceSquared( P, value.origin ) < 25 )
|
||||
return false;
|
||||
}
|
||||
|
||||
//calculate the vector derived from the center line of our barrel and the point of damage
|
||||
|
||||
// generate a vector from the attacker's eye to the impact point (AI) or origin to impact point (non-AI)
|
||||
E = undefined;
|
||||
if( IsAI( damageOwner ))
|
||||
E = damageOwner GetEye();
|
||||
else
|
||||
E = damageOwner.origin;
|
||||
|
||||
temp_vec = P - E;
|
||||
|
||||
// Extend the vector (this is to ensure it intersects the damaged entity, tracing to the point itself generated new points which were slightly off and bad normals) and return a trace
|
||||
trace = BulletTrace ( E, E + 1.5 * temp_vec, false, damageOwner, false );
|
||||
if ( isdefined ( trace [ "normal" ] ) && isdefined ( trace [ "entity" ] ) && trace ["entity"] == self )
|
||||
{
|
||||
vec = trace[ "normal" ];
|
||||
|
||||
// Use the surface normal of the impact point to generate the angles for the burst effect
|
||||
self thread barrelfx( P, vec, damageOwner );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//DC_NOTE commented out all sound related stuff because it was giving me grief in mp_zulu
|
||||
barrelfx( P, vec, damageOwner )
|
||||
{
|
||||
time = level._barrels.fx_time[ self.script_noteworthy ] ;
|
||||
fx_time = level._barrels._barrel_fx_time[ self.script_noteworthy ] ;
|
||||
intervals = Int( fx_time / time );// loops for 25 seconds
|
||||
intervals_end = 30;
|
||||
hitsnd = level._barrels._sound[ self.script_noteworthy + "_hit" ];
|
||||
loopsnd = level._barrels._sound[ self.script_noteworthy + "_loop" ];
|
||||
endsnd = level._barrels._sound[ self.script_noteworthy + "_end" ];
|
||||
|
||||
snd = Spawn( "script_origin", P );
|
||||
// snd Hide();
|
||||
snd PlaySound( hitsnd );
|
||||
snd PlayLoopSound( loopsnd );
|
||||
self.barrel_fx_array[ self.barrel_fx_array.size ] = snd;
|
||||
|
||||
// if ( isSP() || self.script_noteworthy != "steam" )
|
||||
if ( isSP() )
|
||||
self thread barrel_damage( P, vec, damageOwner, snd );
|
||||
|
||||
//rotate the emitter angle over time
|
||||
efx_rot = Spawn( "script_model", P );
|
||||
efx_rot SetModel( "tag_origin" );
|
||||
efx_rot.angles = VectorToAngles( vec );
|
||||
wait .05;//cant play fx on same frame it's spawned in mp appearantly
|
||||
PlayFXOnTag( level._barrels._effect[ self.script_noteworthy ] , efx_rot, "tag_origin" );
|
||||
level._barrels.num_barrel_fx++;
|
||||
efx_rot RotatePitch( 90, time, 1, 1 );
|
||||
wait time;
|
||||
StopFXOnTag( level._barrels._effect[ self.script_noteworthy ] , efx_rot, "tag_origin" );
|
||||
intervals--;
|
||||
//now check for other fx and rest of intervals
|
||||
while ( level._barrels.num_barrel_fx <= level_limit_barrel_fx && intervals > 0 )
|
||||
{
|
||||
efx_rot = Spawn( "script_model", P );
|
||||
efx_rot SetModel( "tag_origin" );
|
||||
efx_rot.angles = VectorToAngles( vec );
|
||||
wait .05;//cant play fx on same frame it's spawned in mp appearantly
|
||||
PlayFXOnTag( level._barrels._effect[ self.script_noteworthy ] , efx_rot, "tag_origin" );
|
||||
level._barrels.num_barrel_fx++;
|
||||
efx_rot RotatePitch( 90, time, 1, 1 );
|
||||
wait time;
|
||||
StopFXOnTag( level._barrels._effect[ self.script_noteworthy ] , efx_rot, "tag_origin" );
|
||||
}
|
||||
|
||||
// snd PlaySound( endsnd );
|
||||
wait( .5 );
|
||||
// snd StopLoopSound( loopsnd );
|
||||
snd Delete();
|
||||
self.barrel_fx_array = array_removeUndefined( self.barrel_fx_array );
|
||||
|
||||
level._barrels.num_barrel_fx--;
|
||||
}
|
||||
|
||||
barrel_damage( P, vec, damageOwner, fx )
|
||||
{
|
||||
if ( !allow_barrel_damage() )
|
||||
return;
|
||||
|
||||
fx endon( "death" );
|
||||
|
||||
origin = fx.origin + ( VectorNormalize( vec ) * 40 );
|
||||
dmg = level._barrels._dmg[ self.script_noteworthy ];
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
// do not pass damage owner if they have disconnected before the barrels explode.. the barrels?
|
||||
if ( !isdefined( self.damageOwner ) )
|
||||
{
|
||||
// MOD_TRIGGER_HURT so they dont do dirt on the player's screen
|
||||
self RadiusDamage( origin, 36, dmg, dmg * 0.75, undefined, "MOD_TRIGGER_HURT" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// MOD_TRIGGER_HURT so they dont do dirt on the player's screen
|
||||
self RadiusDamage( origin, 36, dmg, dmg * 0.75, damageOwner, "MOD_TRIGGER_HURT" );
|
||||
}
|
||||
|
||||
wait( 0.4 );
|
||||
}
|
||||
}
|
||||
|
||||
allow_barrel_damage()
|
||||
{
|
||||
if( !isSP() )
|
||||
return false;
|
||||
|
||||
if ( !isDefined( level.barrelsDamage ) )
|
||||
return false;
|
||||
|
||||
return ( level.barrelsDamage );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// CALCULATIONS / SETUP //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
methodsInit()
|
||||
{
|
||||
level._barrels._barrel_methods = [];
|
||||
level._barrels._barrel_methods[ "MOD_UNKNOWN" ] = ::barrel_calc_splash;
|
||||
level._barrels._barrel_methods[ "MOD_PISTOL_BULLET" ] = ::barrel_calc_ballistic;
|
||||
level._barrels._barrel_methods[ "MOD_RIFLE_BULLET" ] = ::barrel_calc_ballistic;
|
||||
level._barrels._barrel_methods[ "MOD_GRENADE" ] = ::barrel_calc_splash;
|
||||
level._barrels._barrel_methods[ "MOD_GRENADE_SPLASH" ] = ::barrel_calc_splash;
|
||||
level._barrels._barrel_methods[ "MOD_PROJECTILE" ] = ::barrel_calc_splash;
|
||||
level._barrels._barrel_methods[ "MOD_PROJECTILE_SPLASH" ] = ::barrel_calc_splash;
|
||||
level._barrels._barrel_methods[ "MOD_TRIGGER_HURT" ] = ::barrel_calc_splash;
|
||||
level._barrels._barrel_methods[ "MOD_EXPLOSIVE" ] = ::barrel_calc_splash;
|
||||
level._barrels._barrel_methods[ "MOD_EXPLOSIVE_BULLET" ] = ::barrel_calc_splash;
|
||||
}
|
||||
|
||||
barrel_calc_ballistic( P, type )
|
||||
{
|
||||
return P;
|
||||
}
|
||||
|
||||
barrel_calc_splash( P, type )
|
||||
{
|
||||
vec = VectorNormalize( VectorFromLineToPoint( self.A, self.B, P ) );
|
||||
P = PointOnSegmentNearestToPoint( self.A, self.B, P );
|
||||
return( P + ( vec * 4 ) );
|
||||
}
|
||||
|
||||
barrel_calc_nofx( P, type )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
precacheFX()
|
||||
{
|
||||
oil_leak = false;
|
||||
oil_cap = false;
|
||||
beer_leak = false;
|
||||
foreach ( value in self )
|
||||
{
|
||||
if ( value.script_noteworthy == "oil_leak" )
|
||||
{
|
||||
value willNeverChange();
|
||||
oil_leak = true;
|
||||
}
|
||||
else if ( value.script_noteworthy == "oil_cap" )
|
||||
{
|
||||
value willNeverChange();
|
||||
oil_cap = true;
|
||||
}
|
||||
else if ( value.script_noteworthy == "beer_leak" )
|
||||
{
|
||||
value willNeverChange();
|
||||
beer_leak = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
println( "Unknown 'barrel_shootable' script_noteworthy type '%s'\n", value.script_noteworthy );
|
||||
}
|
||||
}
|
||||
|
||||
//DC_NOTE commented out all sound related stuff because it was giving me grief in mp_zulu
|
||||
if ( oil_leak )
|
||||
{
|
||||
level._barrels._effect[ "oil_leak" ] = loadfx( "fx/impacts/pipe_oil_barrel_spill" );
|
||||
//level._barrels._sound[ "oil_leak_hit" ] = "mtl_oil_barrel_hit";
|
||||
//level._barrels._sound[ "oil_leak_loop" ] = "mtl_oil_barrel_hiss_loop";
|
||||
//level._barrels._sound[ "oil_leak_end" ] = "mtl_oil_barrel_hiss_loop_end";
|
||||
level._barrels.fx_time[ "oil_leak" ] = 6;
|
||||
level._barrels._barrel_fx_time["oil_leak"] = 6;
|
||||
level._barrels._dmg[ "oil_leak" ] = 5;
|
||||
}
|
||||
if ( oil_cap )
|
||||
{
|
||||
level._barrels._effect[ "oil_cap" ] = loadfx( "fx/impacts/pipe_oil_barrel_squirt" );
|
||||
//level._barrels._sound[ "oil_cap_hit" ] = "mtl_oil_barrel_hit";
|
||||
//level._barrels._sound[ "oil_cap_loop" ] = "mtl_oil_barrel_hiss_loop";
|
||||
//level._barrels._sound[ "oil_cap_end" ] = "mtl_oil_barrel_hiss_loop_end";
|
||||
level._barrels.fx_time[ "oil_cap" ] = 3;
|
||||
level._barrels._dmg[ "oil_cap" ] = 5;
|
||||
level._barrels._barrel_fx_time["oil_cap"] = 5;
|
||||
}
|
||||
if ( beer_leak )//DC_TODO create beer fx
|
||||
{
|
||||
level._barrels._effect[ "beer_leak" ] = loadfx( "fx/impacts/beer_barrel_spill" );
|
||||
level._barrels._sound[ "beer_leak_hit" ] = "mtl_beer_keg_hit";
|
||||
level._barrels._sound[ "beer_leak_loop" ] = "mtl_beer_keg_hiss_loop";
|
||||
level._barrels._sound[ "beer_leak_end" ] = "mtl_beer_keg_hiss_loop_end";
|
||||
level._barrels.fx_time[ "beer_leak" ] = 6;
|
||||
level._barrels._barrel_fx_time["beer_leak"] = 6;
|
||||
level._barrels._dmg[ "beer_leak" ] = 5;
|
||||
}
|
||||
}
|
||||
753
maps/mp/_breach.gsc
Normal file
753
maps/mp/_breach.gsc
Normal file
@@ -0,0 +1,753 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
main()
|
||||
{
|
||||
thread main_thread();
|
||||
}
|
||||
|
||||
main_thread()
|
||||
{
|
||||
breach_precache();
|
||||
waitframe();
|
||||
|
||||
breach = getstructarray("breach", "targetname");
|
||||
array_thread(breach, ::breach_init);
|
||||
}
|
||||
|
||||
breach_precache()
|
||||
{
|
||||
level.breach_icon_count = -1;
|
||||
|
||||
level._effect["default"] = loadfx("fx/explosions/breach_room_cheap");
|
||||
}
|
||||
|
||||
breach_init()
|
||||
{
|
||||
if( getDvar( "r_reflectionProbeGenerate" ) == "1" )
|
||||
return;
|
||||
|
||||
if(!IsDefined(self.target))
|
||||
return;
|
||||
self.breach_targets = [];
|
||||
self.auto_breach_gametypes = [];
|
||||
|
||||
if(IsDefined(self.script_noteworthy))
|
||||
{
|
||||
toks = StrTok(self.script_noteworthy, ",");
|
||||
foreach(tok in toks)
|
||||
{
|
||||
if(GetSubStr(tok,0,7) == "not_in_")
|
||||
{
|
||||
self.auto_breach_gametypes[self.auto_breach_gametypes.size] = GetSubStr(tok,7,tok.size);
|
||||
}
|
||||
|
||||
if(tok == "only_if_allowClassChoice")
|
||||
{
|
||||
if(!allowClassChoice())
|
||||
{
|
||||
self.auto_breach_gametypes[self.auto_breach_gametypes.size] = level.gameType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ents = GetEntArray(self.target, "targetname");
|
||||
structs = getstructarray(self.target, "targetname");
|
||||
targets = array_combine(ents, structs);
|
||||
nodes = self getLinknameNodes();
|
||||
foreach (node in nodes)
|
||||
{
|
||||
node.isPathNode = true;
|
||||
}
|
||||
targets = array_combine(targets, nodes);
|
||||
|
||||
foreach(target in targets)
|
||||
{
|
||||
if(!IsDefined(target.script_noteworthy))
|
||||
{
|
||||
target.script_noteworthy = target.classname;
|
||||
}
|
||||
|
||||
if(target.script_noteworthy == "trigger_use_touch")
|
||||
{
|
||||
target UseTriggerRequireLookAt();
|
||||
target.script_noteworthy = "trigger_use";
|
||||
}
|
||||
if(!IsDefined(target.isPathNode) || target.isPathNode == false)
|
||||
target.script_noteworthy = ToLower(target.script_noteworthy);
|
||||
types = StrTok(target.script_noteworthy, ", ");
|
||||
|
||||
foreach(type in types)
|
||||
{
|
||||
event_name = undefined;
|
||||
toks = StrTok(type, "_");
|
||||
if(toks.size>=3 && toks[toks.size-2] == "on")
|
||||
{
|
||||
event_name = toks[toks.size-1];
|
||||
type = toks[0];
|
||||
for(i=1;i<toks.size-2;i++)
|
||||
type = type + "_" + toks[i];
|
||||
}
|
||||
|
||||
useSide = false;
|
||||
toks = StrTok(type, "_");
|
||||
if(toks.size>=2 && toks[toks.size-1] == "useside")
|
||||
{
|
||||
useSide = true;
|
||||
type = toks[0];
|
||||
for(i=1;i<toks.size-2;i++)
|
||||
type = type + "_" + toks[i];
|
||||
}
|
||||
|
||||
|
||||
add_breach_target(target, type, event_name, useSide);
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case "show":
|
||||
case "animated":
|
||||
target Hide();
|
||||
break;
|
||||
case "solid":
|
||||
if( IsDefined( target.spawnflags ) && ( target.spawnflags & 2 ) ) // AI_SIGHT_LINE
|
||||
{
|
||||
target SetAISightLineVisible( 0 );
|
||||
}
|
||||
target NotSolid();
|
||||
break;
|
||||
case "teleport_show":
|
||||
target trigger_off();
|
||||
break;
|
||||
case "use_icon":
|
||||
// self thread breach_icon("use", target.origin, target.angles, ["breach_used","breach_activated"] );
|
||||
break;
|
||||
case "trigger_damage":
|
||||
self thread breach_damage_watch(target);
|
||||
break;
|
||||
case "fx":
|
||||
if(!IsDefined(target.angles))
|
||||
target.angles = (0,0,0);
|
||||
break;
|
||||
case "connect_node":
|
||||
target DisconnectNode();
|
||||
break;
|
||||
case "disconnect_node":
|
||||
target ConnectNode();
|
||||
break;
|
||||
case "delete":
|
||||
if( IsDefined( target.spawnflags ) && ( target.spawnflags & 2 ) ) // AI_SIGHT_LINE
|
||||
{
|
||||
target SetAISightLineVisible( 1 );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(level.createFX_enabled)
|
||||
return;
|
||||
|
||||
use_trigger = get_breach_target("trigger_use");
|
||||
if(IsDefined(use_trigger))
|
||||
{
|
||||
|
||||
if(!IsDefined(get_breach_target("sound")))
|
||||
{
|
||||
script_origin = spawn("script_origin", use_trigger.origin);
|
||||
add_breach_target(script_origin, "sound");
|
||||
}
|
||||
if(!IsDefined(get_breach_target("damage")))
|
||||
{
|
||||
damage = SpawnStruct();
|
||||
damage.origin = use_trigger.origin;
|
||||
add_breach_target(damage, "damage");
|
||||
}
|
||||
|
||||
//init default damage info,
|
||||
damages = get_breach_targets("damage");
|
||||
foreach(damage in damages)
|
||||
{
|
||||
if(!IsDefined(damage.radius))
|
||||
damage.radius = 128;
|
||||
if(!IsDefined(damage.max_damage))
|
||||
damage.max_damage = 100;
|
||||
if(!IsDefined(damage.min_damage))
|
||||
damage.min_damage = 1;
|
||||
}
|
||||
self thread breach_use_watch();
|
||||
}
|
||||
|
||||
self thread breach_on_activate();
|
||||
|
||||
self breach_on_event("init");
|
||||
|
||||
foreach(type in self.auto_breach_gametypes)
|
||||
{
|
||||
if(level.gametype == type)
|
||||
{
|
||||
self notify("breach_activated", undefined, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
add_breach_target(target, action, event_name, useSide)
|
||||
{
|
||||
if(!IsDefined(event_name))
|
||||
event_name = "activate";
|
||||
if(!IsDefined(useSide))
|
||||
useSide = false;
|
||||
|
||||
if(!IsDefined(self.breach_targets[event_name]))
|
||||
self.breach_targets[event_name] = [];
|
||||
|
||||
if(!IsDefined(self.breach_targets[event_name][action]))
|
||||
self.breach_targets[event_name][action] = [];
|
||||
|
||||
s = spawnStruct();
|
||||
s.target = target;
|
||||
|
||||
//Check if object has a facing
|
||||
if(useSide)
|
||||
{
|
||||
s.facing_dot = 0;
|
||||
s.facing_angles3d = false;
|
||||
s.facing_dir = AnglesToForward(target.angles);
|
||||
|
||||
if(IsDefined(target.target))
|
||||
{
|
||||
target_targets = getstructarray(target.target, "targetname");
|
||||
foreach(target_target in target_targets)
|
||||
{
|
||||
if(!IsDefined(target_target.script_noteworthy))
|
||||
continue;
|
||||
|
||||
switch(target_target.script_noteworthy)
|
||||
{
|
||||
case "angles":
|
||||
case "angles_3d":
|
||||
s.facing_angles3d = true;
|
||||
//fall through
|
||||
case "angles_2d":
|
||||
if(!IsDefined(s.angles3d))
|
||||
s.facing_angles3d = false;
|
||||
s.facing_dir = AnglesToForward(target_target.angles);
|
||||
if(IsDefined(target_target.script_dot))
|
||||
s.facing_dot = target_target.script_dot;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size = self.breach_targets[event_name][action].size;
|
||||
self.breach_targets[event_name][action][size] = s;
|
||||
}
|
||||
|
||||
get_breach_target(action, event_name, player)
|
||||
{
|
||||
targets = get_breach_targets(action, event_name, player);
|
||||
if(targets.size>0)
|
||||
{
|
||||
return targets[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
get_breach_targets(action, event_name, player)
|
||||
{
|
||||
targets = [];
|
||||
|
||||
if(!IsDefined(event_name))
|
||||
event_name = "activate";
|
||||
|
||||
if(!IsDefined(self.breach_targets[event_name]))
|
||||
return targets;
|
||||
if(!IsDefined(self.breach_targets[event_name][action]))
|
||||
return targets;
|
||||
|
||||
foreach(s in self.breach_targets[event_name][action])
|
||||
{
|
||||
if(IsDefined(s.facing_dir) && IsDefined(player))
|
||||
{
|
||||
player_dir = player.origin - s.target.origin;
|
||||
if(!s.facing_angles3d)
|
||||
player_dir = (player_dir[0], player_dir[1], 0);
|
||||
player_dir = VectorNormalize(player_dir);
|
||||
|
||||
dot = VectorDot(player_dir, s.facing_dir);
|
||||
if(dot<s.facing_dot)
|
||||
continue;
|
||||
}
|
||||
|
||||
targets[targets.size] = s.target;
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
breach_damage_watch( trigger )
|
||||
{
|
||||
self endon( "breach_activated" );
|
||||
|
||||
trigger waittill( "trigger", player );
|
||||
|
||||
self notify( "breach_activated", player, false );
|
||||
}
|
||||
|
||||
breach_on_activate()
|
||||
{
|
||||
self waittill("breach_activated", player, no_fx);
|
||||
if(!isDefined(no_fx))
|
||||
no_fx = false;
|
||||
|
||||
if( IsDefined(self.useObject) && !no_fx )
|
||||
{
|
||||
self.useObject breach_set_2dIcon("hud_grenadeicon_back_red");
|
||||
self.useObject delayThread(3, ::breach_set_2dIcon, undefined);
|
||||
}
|
||||
|
||||
breach_on_event("activate", player, no_fx);
|
||||
|
||||
if(IsDefined(self.useObject))
|
||||
self.useObject.visuals = [];
|
||||
|
||||
breach_set_can_use(false);
|
||||
}
|
||||
|
||||
breach_on_event( event_name, player, no_fx )
|
||||
{
|
||||
if ( !IsDefined( no_fx ) )
|
||||
no_fx = false;
|
||||
|
||||
if ( event_name == "use" ) // breach is being manually planted
|
||||
{
|
||||
targets = get_breach_targets( "damage", "activate", player );
|
||||
foreach ( target in targets )
|
||||
{
|
||||
target.planted = true;
|
||||
}
|
||||
}
|
||||
|
||||
array_call( get_breach_targets( "solid", event_name, player ), ::Solid );
|
||||
array_call( get_breach_targets( "notsolid", event_name, player ), ::NotSolid );
|
||||
array_thread( get_breach_targets( "hide", event_name, player ), ::breach_hide );
|
||||
array_thread( get_breach_targets( "show", event_name, player ), ::breach_show );
|
||||
array_thread( get_breach_targets( "teleport_show", event_name, player ), ::trigger_on );
|
||||
array_thread( get_breach_targets( "delete", event_name, player ), ::breach_delete );
|
||||
array_thread( get_breach_targets( "teleport_hide", event_name, player ), ::trigger_off );
|
||||
array_call( get_breach_targets( "connect_node", event_name, player ), ::ConnectNode );
|
||||
array_call( get_breach_targets( "disconnect_node", event_name, player ), ::DisconnectNode );
|
||||
array_thread( get_breach_targets( "break_glass", event_name, player ), ::breach_break_glass );
|
||||
array_thread( get_breach_targets( "animated", event_name, player ), ::breach_animate_model );
|
||||
|
||||
if ( !no_fx )
|
||||
{
|
||||
array_thread( get_breach_targets( "fx", event_name, player ), ::breach_play_fx, self );
|
||||
array_thread( get_breach_targets( "damage", event_name, player ), ::breach_damage_radius, player );
|
||||
}
|
||||
}
|
||||
|
||||
breach_delete()
|
||||
{
|
||||
if(IsDefined(self.script_index))
|
||||
{
|
||||
exploder(self.script_index);
|
||||
}
|
||||
|
||||
self SetAISightLineVisible( 0 );
|
||||
self delete();
|
||||
}
|
||||
|
||||
breach_hide()
|
||||
{
|
||||
self SetAISightLineVisible( 0 );
|
||||
self Hide();
|
||||
}
|
||||
|
||||
breach_show()
|
||||
{
|
||||
self SetAISightLineVisible( 1 );
|
||||
self Show();
|
||||
}
|
||||
|
||||
breach_play_fx(root)
|
||||
{
|
||||
fx_name = undefined;
|
||||
if(isdefined(self.script_fxid))
|
||||
{
|
||||
fx_name = self.script_fxid;
|
||||
}
|
||||
else if(IsDefined(root.script_fxid))
|
||||
{
|
||||
fx_name = root.script_fxid;
|
||||
}
|
||||
|
||||
if(!IsDefined(fx_name) || !IsDefined(level._effect[fx_name]))
|
||||
{
|
||||
fx_name = "default";
|
||||
}
|
||||
|
||||
PlayFX(level._effect[fx_name], self.origin, AnglesToForward(self.angles), AnglesToUp(self.angles));
|
||||
}
|
||||
|
||||
breach_damage_radius(attacker)
|
||||
{
|
||||
stuntime = 2.0; // 2 seconds is the minimum a concussion grenade will do
|
||||
|
||||
if( IsDefined( self.planted ) && ( self.planted == true ) )
|
||||
{
|
||||
// RadiusDamage(self.origin, self.radius, self.max_damage, self.min_damage, attacker);
|
||||
foreach( participant in level.participants )
|
||||
{
|
||||
participant_to_breach_dist_sq = DistanceSquared( self.origin, participant.origin );
|
||||
if( participant_to_breach_dist_sq < ( self.radius * self.radius ) )
|
||||
{
|
||||
participant ShellShock( "mp_radiation_high", stuntime );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayRumbleOnPosition( "artillery_rumble", self.origin );
|
||||
}
|
||||
|
||||
breach_break_glass()
|
||||
{
|
||||
GlassRadiusDamage( self.origin, 128, 500, 500 );
|
||||
}
|
||||
|
||||
breach_animate_model()
|
||||
{
|
||||
self SetAISightLineVisible( 1 );
|
||||
self Show();
|
||||
|
||||
if ( IsDefined( self.animation ) )
|
||||
{
|
||||
self ScriptModelPlayAnim( self.animation );
|
||||
}
|
||||
}
|
||||
|
||||
breach_use_watch()
|
||||
{
|
||||
self endon("breach_activated");
|
||||
wait .05;
|
||||
|
||||
self.useObject = maps\mp\gametypes\_gameobjects::createUseObject( "neutral", get_breach_target("trigger_use"), get_breach_targets("use_model"), (0,0,0) );
|
||||
self.useObject.parent = self;
|
||||
self.useObject.useWeapon = "breach_plant_mp";
|
||||
self.useObject.id = "breach";
|
||||
|
||||
self breach_set_can_use(true);
|
||||
self.useObject maps\mp\gametypes\_gameobjects::setUseTime( 0.5 );
|
||||
self.useObject maps\mp\gametypes\_gameobjects::setUseText( &"MP_BREACHING" );
|
||||
self.useObject maps\mp\gametypes\_gameobjects::setUseHintText( &"MP_BREACH" );
|
||||
self.useObject maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
|
||||
self.useObject.onUse = ::breach_onUse;
|
||||
self.useObject.onEndUse = ::breach_onEndUse;
|
||||
}
|
||||
|
||||
breach_set_can_use(canUse)
|
||||
{
|
||||
if(!IsDefined(self.useObject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(canUse)
|
||||
{
|
||||
foreach(vis in self.useObject.visuals)
|
||||
{
|
||||
if( vis.model == "mil_semtex_belt" )
|
||||
{
|
||||
vis SetModel("mil_semtex_belt_obj");
|
||||
}
|
||||
}
|
||||
self.useObject maps\mp\gametypes\_gameobjects::allowUse( "any" );
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(vis in self.useObject.visuals)
|
||||
{
|
||||
if( vis.model == "mil_semtex_belt" )
|
||||
{
|
||||
vis SetModel("mil_semtex_belt");
|
||||
}
|
||||
}
|
||||
self.useObject notify( "disabled" );
|
||||
self.useObject maps\mp\gametypes\_gameobjects::allowUse( "none" );
|
||||
}
|
||||
}
|
||||
|
||||
breach_onUse(player)
|
||||
{
|
||||
self.parent endon("breach_activated");
|
||||
|
||||
self.parent notify("breach_used");
|
||||
self.parent breach_set_can_use(false);
|
||||
|
||||
self.parent breach_on_event("use", player);
|
||||
|
||||
self.parent thread breach_warning_icon(self);
|
||||
|
||||
sound_ent = undefined;
|
||||
sound_targets = self.parent get_breach_targets( "sound" );
|
||||
if ( sound_targets.size > 1 )
|
||||
{
|
||||
sound_targets = SortByDistance( sound_targets, player.origin );
|
||||
sound_ent = sound_targets[0];
|
||||
}
|
||||
else if ( sound_targets.size > 0 )
|
||||
{
|
||||
sound_ent = sound_targets[0];
|
||||
}
|
||||
|
||||
breaching_team = player.team; // need to store this in case the player switches teams between starting and finishing the breach
|
||||
|
||||
if(IsDefined(sound_ent))
|
||||
{
|
||||
for(i=0;i<3; i++)
|
||||
{
|
||||
sound_ent PlaySound("breach_beep");
|
||||
wait .75;
|
||||
}
|
||||
|
||||
if( IsDefined( sound_ent.script_parameters ) )
|
||||
{
|
||||
params = StrTok( sound_ent.script_parameters, ";" );
|
||||
foreach ( param in params )
|
||||
{
|
||||
toks = StrTok( param, "=" );
|
||||
if ( toks.size < 2 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch( toks[ 0 ] )
|
||||
{
|
||||
case "play_sound":
|
||||
switch( toks[ 1 ] )
|
||||
{
|
||||
case "concrete":
|
||||
sound_ent PlaySound( "detpack_explo_concrete" );
|
||||
break;
|
||||
case "wood":
|
||||
sound_ent PlaySound( "detpack_explo_wood" );
|
||||
break;
|
||||
case "custom":
|
||||
if ( toks.size == 3 )
|
||||
{
|
||||
sound_ent PlaySound( toks[2] );
|
||||
}
|
||||
break;
|
||||
case "metal":
|
||||
case "undefined":
|
||||
default:
|
||||
sound_ent PlaySound( "detpack_explo_metal" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
sound_ent PlaySound( "detpack_explo_metal" );
|
||||
}
|
||||
}
|
||||
|
||||
self.parent notify( "breach_activated", player, undefined, breaching_team );
|
||||
}
|
||||
|
||||
breach_onEndUse( team, player, success )
|
||||
{
|
||||
if( IsPlayer( player ) )
|
||||
{
|
||||
player maps\mp\gametypes\_gameobjects::updateUIProgress( self, false );
|
||||
}
|
||||
}
|
||||
|
||||
breach_set_2dIcon( icon )
|
||||
{
|
||||
self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy" , icon );
|
||||
self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", icon );
|
||||
}
|
||||
|
||||
breach_warning_icon( useObject )
|
||||
{
|
||||
icon_origin = useObject.curOrigin + ( 0, 0, 5 );
|
||||
if ( useobject.parent get_breach_targets( "use_icon" ).size )
|
||||
icon_origin = useobject.parent get_breach_targets( "use_icon" )[ 0 ].origin;
|
||||
|
||||
useObject.parent thread breach_icon( "warning", icon_origin, undefined, "breach_activated" );
|
||||
}
|
||||
|
||||
breach_icon( type, origin, angles, end_ons )
|
||||
{
|
||||
if ( level.createFX_enabled )
|
||||
return;
|
||||
|
||||
level.breach_icon_count++;
|
||||
|
||||
icon_id = "breach_icon_" + level.breach_icon_count;
|
||||
|
||||
breach_icon_update( type, origin, angles, icon_id, end_ons );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( IsDefined( player.breach_icons ) && IsDefined( player.breach_icons[ icon_id ] ) )
|
||||
{
|
||||
player.breach_icons[ icon_id ] thread breach_icon_fade_out();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
breach_icon_update( type, origin, angles, icon_id, end_ons )
|
||||
{
|
||||
if ( IsDefined( end_ons ) )
|
||||
{
|
||||
if ( IsString( end_ons ) )
|
||||
end_ons = [ end_ons ];
|
||||
|
||||
foreach ( end_on in end_ons )
|
||||
self endon( end_on );
|
||||
}
|
||||
|
||||
|
||||
show_dist = 100;
|
||||
show_z = 70;
|
||||
icon = "hud_grenadeicon";
|
||||
pin = true;
|
||||
switch( type )
|
||||
{
|
||||
case "use":
|
||||
show_dist = 300;
|
||||
icon = "breach_icon";
|
||||
pin = false;
|
||||
break;
|
||||
case "warning":
|
||||
show_dist = 400;
|
||||
damage_info = get_breach_target( "damage" );
|
||||
if ( IsDefined( damage_info ) )
|
||||
show_dist = damage_info.radius;
|
||||
icon = "hud_grenadeicon";
|
||||
pin = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
show_dir = undefined;
|
||||
if ( IsDefined( angles ) )
|
||||
{
|
||||
show_dir = AnglesToForward( angles );
|
||||
}
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !IsDefined( player.breach_icons ) )
|
||||
player.breach_icons = [];
|
||||
|
||||
if ( breach_icon_update_is_player_in_range( player, origin, show_dist, show_dir, show_z ) )
|
||||
{
|
||||
if ( !IsDefined( player.breach_icons[ icon_id ] ) )
|
||||
{
|
||||
player.breach_icons[ icon_id ] = breach_icon_create( player, icon, origin, pin );
|
||||
player.breach_icons[ icon_id ].alpha = 0;
|
||||
}
|
||||
|
||||
player.breach_icons[ icon_id ] notify( "stop_fade" );
|
||||
player.breach_icons[ icon_id ] thread breach_icon_fade_in();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( player.breach_icons[ icon_id ] ) )
|
||||
{
|
||||
player.breach_icons[ icon_id ] thread breach_icon_fade_out();
|
||||
}
|
||||
}
|
||||
}
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
breach_icon_update_is_player_in_range( player, origin, show_dist, show_dir, show_z )
|
||||
{
|
||||
test_origin = player.origin+(0,0,30);
|
||||
|
||||
if(IsDefined(show_z) && abs(test_origin[2] - origin[2]) > show_z)
|
||||
return false;
|
||||
|
||||
if(IsDefined(show_dist))
|
||||
{
|
||||
show_dist_sqr = show_dist*show_dist;
|
||||
dist_sqr = DistanceSquared(test_origin, origin);
|
||||
if(dist_sqr>show_dist_sqr)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if(IsDefined(show_dir))
|
||||
{
|
||||
dir_to_player = test_origin - origin;
|
||||
//Normalize dir_to_player if not checking against zero.
|
||||
if(VectorDot(show_dir, dir_to_player)<0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
breach_icon_create(player, icon, origin, pin)
|
||||
{
|
||||
icon = player createIcon( icon, 16, 16 );
|
||||
icon setWayPoint( true, pin );
|
||||
icon.x = origin[0];
|
||||
icon.y = origin[1];
|
||||
icon.z = origin[2];
|
||||
return icon;
|
||||
}
|
||||
|
||||
breach_icon_fade_in()
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
if(self.alpha == 1)
|
||||
return;
|
||||
|
||||
self FadeOverTime(.5);
|
||||
self.alpha = 1;
|
||||
}
|
||||
|
||||
breach_icon_fade_out()
|
||||
{
|
||||
self endon("death");
|
||||
self endon("stop_fade");
|
||||
|
||||
if(self.alpha == 0)
|
||||
return;
|
||||
|
||||
time = .5;
|
||||
self FadeOverTime(time);
|
||||
self.alpha = 0;
|
||||
wait time;
|
||||
|
||||
self Destroy();
|
||||
}
|
||||
85
maps/mp/_compass.gsc
Normal file
85
maps/mp/_compass.gsc
Normal file
@@ -0,0 +1,85 @@
|
||||
setupMiniMap(material)
|
||||
{
|
||||
// use 0 for no required map aspect ratio.
|
||||
requiredMapAspectRatio = level.requiredMapAspectRatio;
|
||||
|
||||
corners = getentarray("minimap_corner", "targetname");
|
||||
if (corners.size != 2)
|
||||
{
|
||||
println("^1Error: There are not exactly two \"minimap_corner\" entities in the map. Could not set up minimap.");
|
||||
return;
|
||||
}
|
||||
|
||||
corner0 = (corners[0].origin[0], corners[0].origin[1], 0);
|
||||
corner1 = (corners[1].origin[0], corners[1].origin[1], 0);
|
||||
|
||||
cornerdiff = corner1 - corner0;
|
||||
|
||||
north = (cos(getnorthyaw()), sin(getnorthyaw()), 0);
|
||||
west = (0 - north[1], north[0], 0);
|
||||
|
||||
// we need the northwest and southeast corners. all we know is that corner0 is opposite of corner1.
|
||||
if (vectordot(cornerdiff, west) > 0) {
|
||||
// corner1 is further west than corner0
|
||||
if (vectordot(cornerdiff, north) > 0) {
|
||||
// corner1 is northwest, corner0 is southeast
|
||||
northwest = corner1;
|
||||
southeast = corner0;
|
||||
}
|
||||
else {
|
||||
// corner1 is southwest, corner0 is northeast
|
||||
side = vecscale(north, vectordot(cornerdiff, north));
|
||||
northwest = corner1 - side;
|
||||
southeast = corner0 + side;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// corner1 is further east than corner0
|
||||
if (vectordot(cornerdiff, north) > 0) {
|
||||
// corner1 is northeast, corner0 is southwest
|
||||
side = vecscale(north, vectordot(cornerdiff, north));
|
||||
northwest = corner0 + side;
|
||||
southeast = corner1 - side;
|
||||
}
|
||||
else {
|
||||
// corner1 is southeast, corner0 is northwest
|
||||
northwest = corner0;
|
||||
southeast = corner1;
|
||||
}
|
||||
}
|
||||
|
||||
if( GetDvar("mapname") == "mp_boneyard_ns")
|
||||
{
|
||||
southeast -= (220,220,0);
|
||||
northwest += (220,220,0);
|
||||
}
|
||||
|
||||
// expand map area to fit required aspect ratio
|
||||
if ( requiredMapAspectRatio > 0 )
|
||||
{
|
||||
northportion = vectordot(northwest - southeast, north);
|
||||
westportion = vectordot(northwest - southeast, west);
|
||||
mapAspectRatio = westportion / northportion;
|
||||
if ( mapAspectRatio < requiredMapAspectRatio )
|
||||
{
|
||||
incr = requiredMapAspectRatio / mapAspectRatio;
|
||||
addvec = vecscale( west, westportion * (incr - 1) * 0.5 );
|
||||
}
|
||||
else
|
||||
{
|
||||
incr = mapAspectRatio / requiredMapAspectRatio;
|
||||
addvec = vecscale( north, northportion * (incr - 1) * 0.5 );
|
||||
}
|
||||
northwest += addvec;
|
||||
southeast -= addvec;
|
||||
}
|
||||
|
||||
level.mapSize = vectordot(northwest - southeast, north);
|
||||
|
||||
setMiniMap(material, northwest[0], northwest[1], southeast[0], southeast[1]);
|
||||
}
|
||||
|
||||
vecscale(vec, scalar)
|
||||
{
|
||||
return (vec[0]*scalar, vec[1]*scalar, vec[2]*scalar);
|
||||
}
|
||||
74
maps/mp/_createfx.gsc
Normal file
74
maps/mp/_createfx.gsc
Normal file
@@ -0,0 +1,74 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\_createFxMenu;
|
||||
#include common_scripts\_createfx;
|
||||
#include common_scripts\_fx;
|
||||
|
||||
createfx()
|
||||
{
|
||||
level.func_position_player = ::void;
|
||||
level.func_position_player_get = ::func_position_player_get;
|
||||
level.func_loopfxthread = ::loopfxthread;
|
||||
level.func_oneshotfxthread = ::oneshotfxthread;
|
||||
level.func_create_loopsound = ::create_loopsound;
|
||||
// level.func_exploder_preload = ::exploder_before_load;;
|
||||
// level.func_exploder_postload = ::exploder_after_load;;
|
||||
level.func_updatefx = ::restart_fx_looper;
|
||||
level.func_process_fx_rotater = ::process_fx_rotater;
|
||||
level.func_player_speed = ::func_player_speed;
|
||||
|
||||
level.mp_createfx = true;
|
||||
|
||||
// MP only stuff
|
||||
level.callbackStartGameType = ::void;
|
||||
level.callbackPlayerConnect = ::void;
|
||||
level.callbackPlayerDisconnect = ::void;
|
||||
level.callbackPlayerDamage = ::void;
|
||||
level.callbackPlayerKilled = ::void;
|
||||
level.callbackCodeEndGame = ::void;
|
||||
level.callbackPlayerLastStand = ::void;
|
||||
level.callbackPlayerConnect = ::Callback_PlayerConnect;
|
||||
level.callbackPlayerMigrated = ::void;
|
||||
|
||||
thread func_get_level_fx(); // start gettin these on start
|
||||
createfx_common();
|
||||
|
||||
level waittill( "eternity" );
|
||||
}
|
||||
|
||||
func_position_player_get( lastPlayerOrigin )
|
||||
{
|
||||
return level.player.origin;
|
||||
}
|
||||
|
||||
Callback_PlayerConnect()
|
||||
{
|
||||
self waittill( "begin" );
|
||||
|
||||
if ( !isdefined( level.player ) )
|
||||
{
|
||||
spawnpoints = getentarray( "mp_global_intermission", "classname" );
|
||||
self spawn( spawnpoints[0].origin, spawnpoints[0].angles );
|
||||
self updateSessionState( "playing", "" );
|
||||
self.maxhealth = 10000000;
|
||||
self.health = 10000000;
|
||||
level.player = self;
|
||||
|
||||
thread createFxLogic();
|
||||
//thread ufo_mode();
|
||||
}
|
||||
else
|
||||
kick( self GetEntityNumber() );
|
||||
}
|
||||
|
||||
//ufo_mode()
|
||||
//{
|
||||
// level.player openpopupmenu( "painter_mp" );// painter.menu execs some console commands( ufo mode ).. sneaky hacks.
|
||||
// level.player closepopupmenu( "painter_mp" );
|
||||
//}
|
||||
|
||||
func_player_speed()
|
||||
{
|
||||
scale = level._createfx.player_speed / 190;
|
||||
level.player SetMoveSpeedScale( scale );
|
||||
}
|
||||
727
maps/mp/_crib.gsc
Normal file
727
maps/mp/_crib.gsc
Normal file
@@ -0,0 +1,727 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
CONST_default_radial_radius = 8; // default distance between radial button and center
|
||||
CONST_radial_center_extrude_dist = 40; // this is the distance between the floating radial center and the oberver's eye
|
||||
CONST_direct_travel = 1; // no path, directly zoom to position
|
||||
CONST_view_travel_unit_dist = 1200; // distance unit per travel interval
|
||||
CONST_view_travel_unit_time = 1; // in seconds per travel interval
|
||||
CONST_blur_strength = 3; // blur strength during view travel, sine curved over travel duration
|
||||
|
||||
init()
|
||||
{
|
||||
precacheShellShock( "frag_grenade_mp" );
|
||||
|
||||
radial_button_definitions(); // define radial button sets and buttons
|
||||
radial_init(); // setup radial button mechanism
|
||||
view_path_setup(); // setup view flight paths
|
||||
player_init();
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == inits ==
|
||||
// ====================================================================================
|
||||
|
||||
radial_button_definitions()
|
||||
{
|
||||
newRadialButtonGroup( "main", "player_view1_start", "player_view1_end" );
|
||||
|
||||
// Main menu's buttons:
|
||||
bWeapons_a = newRadialButton( "main", "Primary Weapon", "radial_weapons_primary", ::action_weapons_primary );
|
||||
bWeapons_b = newRadialButton( "main", "Secondary Weapon", "radial_weapons_secondary", ::action_weapons_secondary );
|
||||
bGears = newRadialButton( "main", "Gears", "radial_gears", ::action_gears );
|
||||
bKillStreaks= newRadialButton( "main", "Kill Streaks", "radial_killstreaks", ::action_killstreak );
|
||||
bLeadboards = newRadialButton( "main", "Leaderboards", "radial_leaderboards", ::action_leaderboards );
|
||||
//
|
||||
|
||||
newRadialButtonGroup( "gears", "player_view2_start", "player_view2_end" );
|
||||
newRadialButtonGroup( "weapons_primary", "player_view3_start", "player_view3_end" );
|
||||
newRadialButtonGroup( "weapons_secondary", "player_view3_start", "player_view3_end" );
|
||||
newRadialButtonGroup( "killstreak", "player_view4_start", "player_view4_end" );
|
||||
newRadialButtonGroup( "leaderboards", "player_view5_start", "player_view5_end" );
|
||||
}
|
||||
|
||||
|
||||
radial_init()
|
||||
{
|
||||
// calculate start & end angles of all buttons for range selection
|
||||
foreach ( button_group in level.radial_button_group )
|
||||
{
|
||||
// sort buttons by angle so we can calculate mid angles in sequence
|
||||
sort_buttons_by_angle( button_group );
|
||||
|
||||
for ( i = 0; i < button_group.size; i ++ )
|
||||
{
|
||||
if ( isdefined( button_group[ i + 1 ] ) )
|
||||
{
|
||||
mid_angle = getMidAngle( button_group[ i ].pos_angle, button_group[ i + 1 ].pos_angle );
|
||||
button_group[ i ].end_angle = mid_angle;
|
||||
button_group[ i + 1 ].start_angle = mid_angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
mid_angle = getMidAngle( button_group[ i ].pos_angle, button_group[ 0 ].pos_angle ) + 180; // +180 to mirror angle
|
||||
if ( mid_angle > 360 )
|
||||
mid_angle -= 360;
|
||||
|
||||
button_group[ i ].end_angle = mid_angle;
|
||||
button_group[ 0 ].start_angle = mid_angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// monitors
|
||||
thread updateSelectedButton();
|
||||
thread watchSelectButtonPress();
|
||||
thread watchBackButtonPress();
|
||||
thread debug_toggle();
|
||||
}
|
||||
|
||||
|
||||
debug_toggle()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level.crib_debug = 1;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.observer ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
button_reset = true;
|
||||
while ( !( level.observer buttonPressed( "BUTTON_Y" ) ) )
|
||||
wait 0.05;
|
||||
|
||||
level.observer playsound("mouse_click");
|
||||
|
||||
if ( button_reset )
|
||||
{
|
||||
level.crib_debug *= -1;
|
||||
button_reset = false;
|
||||
}
|
||||
|
||||
while ( level.observer buttonPressed( "BUTTON_Y" ) )
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
player_init()
|
||||
{
|
||||
level thread onPlayerConnect();
|
||||
level thread return_hud();
|
||||
}
|
||||
|
||||
|
||||
return_hud()
|
||||
{
|
||||
level waittill( "game_ended" );
|
||||
setdvar( "cg_draw2d", 1 );
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
level waittill("connected", player);
|
||||
|
||||
player thread readyPlayer();
|
||||
player waittill( "spawned_player" );
|
||||
|
||||
wait 1;
|
||||
|
||||
player takeallweapons();
|
||||
setdvar( "cg_draw2d", 0 );
|
||||
|
||||
if ( !isdefined( player ) )
|
||||
return;
|
||||
else
|
||||
level.observer = player;
|
||||
|
||||
player thread get_right_stick_angle();
|
||||
|
||||
zoom_to_radial_menu( "main" ); // fly to the first radial menu
|
||||
}
|
||||
|
||||
|
||||
readyPlayer()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
team = "autoassign";
|
||||
|
||||
while(!isdefined(self.pers["team"]))
|
||||
wait .05;
|
||||
|
||||
self notify("menuresponse", game["menu_team"], team);
|
||||
wait 0.5;
|
||||
|
||||
classes = getArrayKeys( level.classMap );
|
||||
okclasses = [];
|
||||
for ( i = 0; i < classes.size; i++ )
|
||||
{
|
||||
if ( !isSubStr( classes[i], "custom" ) )
|
||||
okclasses[ okclasses.size ] = classes[i];
|
||||
}
|
||||
|
||||
assert( okclasses.size );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
class = okclasses[ 0 ];
|
||||
self notify("menuresponse", "changeclass", class);
|
||||
|
||||
self waittill( "spawned_player" );
|
||||
wait ( 0.10 );
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == Radial Mechanics ==
|
||||
// ====================================================================================
|
||||
|
||||
get_right_stick_angle()
|
||||
{
|
||||
// self is user
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
rs_vec = self GetNormalizedMovement();
|
||||
rs_angles = vectortoangles( rs_vec );
|
||||
level.rs_angle = int( rs_angles[1] );
|
||||
|
||||
wait 0.05; // update rate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
newRadialButtonGroup( group_name, view_start, view_end )
|
||||
{
|
||||
if ( isdefined( level.radial_button_group ) && level.radial_button_group.size )
|
||||
assertex( !isdefined( level.radial_button_group[ group_name ] ), "Radial button group: " + group_name + " is already defined." );
|
||||
|
||||
player_view_ent = getent( view_end, "targetname" );
|
||||
assertex( isdefined( player_view_ent ), "Missing player view entity, can not setup radial menu in space" );
|
||||
|
||||
extruded_vec = ( VectorNormalize( AnglesToForward( player_view_ent.angles ) ) * CONST_radial_center_extrude_dist );
|
||||
|
||||
level.radial_button_group[ group_name ] = [];
|
||||
level.radial_button_group_info[ group_name ][ "view_start" ] = view_start;
|
||||
level.radial_button_group_info[ group_name ][ "view_pos" ] = player_view_ent.origin + extruded_vec;
|
||||
level.radial_button_group_info[ group_name ][ "player_view_pos" ] = player_view_ent.origin;
|
||||
level.radial_button_group_info[ group_name ][ "view_angles" ] = player_view_ent.angles;
|
||||
}
|
||||
|
||||
|
||||
newRadialButton( button_group, button_label, button_ent_name, action_func )
|
||||
{
|
||||
assertex( isdefined( level.radial_button_group[ button_group ] ), "Radial button group: " + button_group + " does not exist." );
|
||||
|
||||
ent = getent( button_ent_name, "targetname" );
|
||||
new_button_angle = getRadialAngleFromEnt( button_group, ent );
|
||||
|
||||
button = spawnstruct();
|
||||
button.pos = ent.origin;
|
||||
button.label = button_label;
|
||||
button.font_size = 1;
|
||||
button.font_color = ( 0.5, 0.5, 1 );
|
||||
button.pos_angle = new_button_angle;
|
||||
button.action_func = action_func;
|
||||
button.radius_pos = CONST_default_radial_radius;
|
||||
|
||||
level.radial_button_group[ button_group ][ level.radial_button_group[ button_group ].size ] = button;
|
||||
return button;
|
||||
}
|
||||
|
||||
|
||||
updateSelectedButton()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.radial_button_current_group ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
last_active_button = level.active_button;
|
||||
|
||||
foreach ( button in level.radial_button_group[ level.radial_button_current_group ] )
|
||||
{
|
||||
if ( isInRange( button.start_angle, button.end_angle ) )
|
||||
level.active_button = button;
|
||||
else
|
||||
button.font_color = ( 0.5, 0.5, 1 );
|
||||
}
|
||||
|
||||
if ( isdefined ( level.active_button ) )
|
||||
{
|
||||
level.active_button.font_color = ( 1, 1, 0.5 );
|
||||
|
||||
if ( isdefined( last_active_button ) && last_active_button != level.active_button )
|
||||
level.observer playsound("mouse_over");
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watchSelectButtonPress()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.observer ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
button_reset = true;
|
||||
while ( !( level.observer buttonPressed( "BUTTON_A" ) ) )
|
||||
wait 0.05;
|
||||
|
||||
level.observer playsound("mouse_click");
|
||||
|
||||
if ( isdefined( level.active_button ) && button_reset )
|
||||
{
|
||||
level.active_button notify( "select_button_pressed" );
|
||||
[[level.active_button.action_func]]();
|
||||
button_reset = false;
|
||||
}
|
||||
|
||||
while ( level.observer buttonPressed( "BUTTON_A" ) )
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watchBackButtonPress()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !isdefined( level.observer ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
button_reset = true;
|
||||
while ( !( level.observer buttonPressed( "BUTTON_X" ) ) )
|
||||
wait 0.05;
|
||||
|
||||
level.observer playsound("mouse_click");
|
||||
|
||||
if ( button_reset )
|
||||
{
|
||||
action_back();
|
||||
button_reset = false;
|
||||
}
|
||||
|
||||
while ( level.observer buttonPressed( "BUTTON_X" ) )
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sort_buttons_by_angle( button_group )
|
||||
{
|
||||
// button_group is actual array
|
||||
// bubble sort buttons
|
||||
for ( i = 0; i < button_group.size - 1; i++ )
|
||||
{
|
||||
for ( j = 0; j < button_group.size - 1 - i; j++ )
|
||||
{
|
||||
if ( button_group[ j + 1 ].pos_angle < button_group[ j ].pos_angle )
|
||||
button_switch( button_group[ j ], button_group[ j + 1 ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
button_switch( button1, button2 )
|
||||
{
|
||||
temp_pos = button1.pos;
|
||||
temp_label = button1.label;
|
||||
temp_pos_angle = button1.pos_angle;
|
||||
temp_action_func = button1.action_func;
|
||||
temp_radius_pos = button1.radius_pos;
|
||||
|
||||
button1.pos = button2.pos;
|
||||
button1.label = button2.label;
|
||||
button1.pos_angle = button2.pos_angle;
|
||||
button1.action_func = button2.action_func;
|
||||
button1.radius_pos = button2.radius_pos;
|
||||
|
||||
button2.pos = temp_pos;
|
||||
button2.label = temp_label;
|
||||
button2.pos_angle = temp_pos_angle;
|
||||
button2.action_func = temp_action_func;
|
||||
button2.radius_pos = temp_radius_pos;
|
||||
}
|
||||
|
||||
|
||||
draw_radial_buttons( button_group )
|
||||
{
|
||||
foreach ( button in level.radial_button_group[ button_group ] )
|
||||
button thread draw_radial_button( button_group );
|
||||
}
|
||||
|
||||
|
||||
//print3d(<origin>, <text>, <color>, <alpha>, <scale>, <duration> )
|
||||
draw_radial_button( button_group )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "remove_button" );
|
||||
|
||||
floating_origin = level.radial_button_group_info[ button_group ][ "view_pos" ];
|
||||
button_radial_pos = floating_origin + radial_angle_to_vector( self.pos_angle, 4 );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
//line( level.radial_button_group_info[ button_group ][ "view_pos" ], self.pos, ( 0, 1, 0 ), 0.05, false );
|
||||
|
||||
range_color = ( 1, 0, 0 );
|
||||
if ( isInRange( self.start_angle, self.end_angle ) )
|
||||
range_color = ( 1, 1, 0 );
|
||||
|
||||
print3d( self.pos, self.label, self.font_color, 0.75, self.font_size, 1 );
|
||||
|
||||
if ( isdefined( level.crib_debug ) && level.crib_debug > 0 )
|
||||
{
|
||||
print3d( button_radial_pos, ".("+int(self.pos_angle)+")", range_color, 0.75, 0.05, 1 );
|
||||
|
||||
line( floating_origin, floating_origin + radial_angle_to_vector( self.start_angle, 2 ), range_color, 0.05 );
|
||||
line( floating_origin + radial_angle_to_vector( self.start_angle, 2 ), floating_origin + radial_angle_to_vector( self.end_angle, 2 ), range_color, 0.05 );
|
||||
|
||||
// right stick debug ling
|
||||
r_radial_pos = floating_origin + radial_angle_to_vector( level.rs_angle, 2 );
|
||||
line( floating_origin, r_radial_pos, ( 1, 1, 1 ), 0.05 );
|
||||
|
||||
|
||||
}
|
||||
print3d( floating_origin - ( 0, 0, 4.5 ), "(A)=Select (X)=Back", (1, 1, 1), 0.5, 0.05, 1 );
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Zoom_To_Radial_Menu( button_group, reverse )
|
||||
{
|
||||
level.active_button = undefined;
|
||||
|
||||
assertex( isdefined( level.observer ), "Missing observer (connected player), can not attach player to view path" );
|
||||
|
||||
if ( isdefined( level.radial_button_current_group ) && level.radial_button_current_group != "" )
|
||||
{
|
||||
level.radial_button_previous_group = level.radial_button_current_group;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.radial_button_previous_group = "main";
|
||||
level.radial_button_current_group = "main";
|
||||
}
|
||||
|
||||
foreach ( button in level.radial_button_group[ level.radial_button_previous_group ] )
|
||||
button notify( "remove_button" );
|
||||
|
||||
//iPrintLnBold( "flying to: " + button_group );
|
||||
|
||||
if ( isdefined( reverse ) && reverse )
|
||||
level.observer go_path_by_targetname_reverse( level.radial_button_group_info[ level.radial_button_previous_group ][ "view_start" ], button_group );
|
||||
else
|
||||
level.observer go_path_by_targetname( level.radial_button_group_info[ button_group ][ "view_start" ] );
|
||||
|
||||
level thread draw_radial_buttons( button_group );
|
||||
level.radial_button_current_group = button_group;
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================================
|
||||
// == Radial menu - math ==
|
||||
// ====================================================================================
|
||||
|
||||
// edit function with care, returns orientation-sensistive angles
|
||||
getRadialAngleFromEnt( button_group, ent )
|
||||
{
|
||||
assertex( isdefined( level.radial_button_group[ button_group ] ), "getRadialAngleFromEnt: Radial button group does not exist." );
|
||||
assertex( isdefined( ent ), "getRadialAngleFromEnt: Missing entity to be measured." );
|
||||
|
||||
rAngle = level.radial_button_group_info[ button_group ][ "view_angles" ];
|
||||
rPos = level.radial_button_group_info[ button_group ][ "view_pos" ];
|
||||
rPos += ( VectorNormalize( AnglesToForward( rAngle ) ) * CONST_radial_center_extrude_dist );
|
||||
rForward = AnglesToForward( rAngle );
|
||||
rUpwardNorm = VectorNormalize( AnglesToUp( rAngle ) );
|
||||
|
||||
eAngle = ent.angles;
|
||||
ePos = ent.origin;
|
||||
|
||||
projNorm = VectorNormalize( VectorFromLineToPoint( rPos, ( rPos + rForward ), ePos ) );
|
||||
radial_angle = Acos( VectorDot( projNorm, rUpwardNorm ) );
|
||||
|
||||
// vector mirroring
|
||||
if ( VectorDot( AnglesToRight( rAngle ), projNorm ) < 0 )
|
||||
radial_angle = 360 - radial_angle;
|
||||
|
||||
return radial_angle;
|
||||
}
|
||||
|
||||
|
||||
// converts projected angle into player's view plane into a vector
|
||||
radial_angle_to_vector( angle, scaler )
|
||||
{
|
||||
b_angles = ( 270 - ( angle ), 0 , 0 ); // 270 degrees offset to face the player
|
||||
b_vec = AnglesToForward( b_angles );
|
||||
b_vec_norm = VectorNormalize( b_vec );
|
||||
b_vec_final = ( b_vec_norm * scaler );
|
||||
|
||||
return b_vec_final;
|
||||
}
|
||||
|
||||
|
||||
getMidAngle( a1, a2 )
|
||||
{
|
||||
// 0 -> 360 domain
|
||||
mid_angle = ( ( a1 + a2 + 720 ) / 2 ) - 360;
|
||||
return mid_angle;
|
||||
}
|
||||
|
||||
|
||||
isInRange( start_angle, end_angle )
|
||||
{
|
||||
inside_big_angle = ( level.rs_angle > start_angle && level.rs_angle < 360 );
|
||||
inside_small_angle = ( level.rs_angle > 0 && level.rs_angle < end_angle );
|
||||
|
||||
if ( start_angle > end_angle )
|
||||
in_range = ( inside_big_angle || inside_small_angle );
|
||||
else
|
||||
in_range = ( level.rs_angle > start_angle && level.rs_angle < end_angle );
|
||||
|
||||
return in_range;
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == Button action functions ==
|
||||
// ====================================================================================
|
||||
|
||||
// close radial buttons
|
||||
action_back()
|
||||
{
|
||||
//if ( isdefined( level.radial_button_previous_group ) && level.radial_button_previous_group != "" )
|
||||
// zoom_to_radial_menu( level.radial_button_previous_group );
|
||||
/*else*/ if ( isdefined( level.radial_button_current_group ) && level.radial_button_current_group != "main" )
|
||||
zoom_to_radial_menu( "main", true );
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// ==== main ====
|
||||
action_weapons_primary()
|
||||
{
|
||||
iPrintLnBold( "action_weapons_primary" );
|
||||
zoom_to_radial_menu( "weapons_primary" );
|
||||
}
|
||||
|
||||
|
||||
action_weapons_secondary()
|
||||
{
|
||||
iPrintLnBold( "action_weapons_secondary" );
|
||||
zoom_to_radial_menu( "weapons_secondary" );
|
||||
}
|
||||
|
||||
action_gears()
|
||||
{
|
||||
iPrintLnBold( "action_gears" );
|
||||
zoom_to_radial_menu( "gears" );
|
||||
}
|
||||
|
||||
|
||||
action_killstreak()
|
||||
{
|
||||
iPrintLnBold( "action_killstreak" );
|
||||
zoom_to_radial_menu( "killstreak" );
|
||||
}
|
||||
|
||||
|
||||
action_leaderboards()
|
||||
{
|
||||
iPrintLnBold( "action_leaderboards" );
|
||||
zoom_to_radial_menu( "leaderboards" );
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// == Pathing functions ==
|
||||
// ====================================================================================
|
||||
|
||||
view_path_setup()
|
||||
{
|
||||
// setup all paths
|
||||
level.view_paths = [];
|
||||
|
||||
// build paths
|
||||
build_path_by_targetname( "player_view1_start" );
|
||||
build_path_by_targetname( "player_view2_start" );
|
||||
build_path_by_targetname( "player_view3_start" );
|
||||
build_path_by_targetname( "player_view4_start" );
|
||||
build_path_by_targetname( "player_view5_start" );
|
||||
}
|
||||
|
||||
|
||||
build_path_by_targetname( path_name )
|
||||
{
|
||||
level.view_paths[ path_name ] = [];
|
||||
|
||||
path_node = getent( path_name, "targetname" );
|
||||
level.view_paths[ path_name ][ level.view_paths[ path_name ].size ] = path_node;
|
||||
|
||||
while( isdefined( path_node ) && isdefined( path_node.target ) )
|
||||
{
|
||||
next_node = getent( path_node.target, "targetname" );
|
||||
level.view_paths[ path_name ][ level.view_paths[ path_name ].size ] = next_node;
|
||||
path_node = next_node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
go_path_by_targetname( path_name )
|
||||
{
|
||||
// self is player
|
||||
if ( !isdefined( level.dummy_mover ) )
|
||||
{
|
||||
start_node = level.view_paths[ path_name ][ 0 ];
|
||||
level.dummy_mover = spawn( "script_model", start_node.origin );
|
||||
level.dummy_mover.angles = start_node.angles;
|
||||
//self AllowedStances( "stand" );
|
||||
self setOrigin( level.dummy_mover.origin - ( 0, 0, 65 ) );
|
||||
self linkTo( level.dummy_mover );
|
||||
wait 0.05;
|
||||
self setplayerangles ( level.dummy_mover.angles );
|
||||
|
||||
self thread force_player_angles();
|
||||
}
|
||||
|
||||
/*
|
||||
travel_time = 1;
|
||||
dist = 0;
|
||||
foreach ( idx, node in level.view_paths[ path_name ] )
|
||||
{
|
||||
if ( isdefined( level.view_paths[ path_name ][ idx + 1 ] ) )
|
||||
dist += abs( distance( level.view_paths[ path_name ][ idx ].origin, level.view_paths[ path_name ][ idx + 1 ].origin ) );
|
||||
}*/
|
||||
|
||||
travel_speed = CONST_view_travel_unit_time;
|
||||
total_distance = abs( distance( level.dummy_mover.origin, level.view_paths[ path_name ][ level.view_paths[ path_name ].size - 1 ].origin ) );
|
||||
travel_speed *= total_distance / CONST_view_travel_unit_dist;
|
||||
travel_speed = max( travel_speed, 0.1 ); // due to repeated button presses, the travel distance can be cut to 0 travel speed at times.
|
||||
|
||||
blur_time = travel_speed;
|
||||
if ( !CONST_direct_travel )
|
||||
blur_time *= travel_speed * ( level.view_paths[ path_name ].size + 1 );
|
||||
self thread blur_sine( CONST_blur_strength, blur_time );
|
||||
|
||||
foreach ( idx, node in level.view_paths[ path_name ] )
|
||||
{
|
||||
//travel_speed = travel_time * ( abs( distance( level.dummy_mover.origin, node.origin ) ) / dist );
|
||||
//travel_speed += 0.05;
|
||||
|
||||
if ( CONST_direct_travel )
|
||||
{
|
||||
if ( idx != level.view_paths[ path_name ].size - 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
//level.dummy_mover MoveTo( node.origin, travel_speed );
|
||||
level.dummy_mover MoveTo( node.origin, travel_speed, travel_speed * 0.5, 0 );
|
||||
level.dummy_mover RotateTo( node.angles, travel_speed, travel_speed * 0.5, 0);
|
||||
wait travel_speed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
go_path_by_targetname_reverse( path_name, back_to_button_group )
|
||||
{
|
||||
assertex( isdefined( level.dummy_mover ), "go_path_by_targetname_reverse called before go_path_by_targetname" );
|
||||
|
||||
travel_speed = CONST_view_travel_unit_time;
|
||||
total_distance = abs( distance( level.dummy_mover.origin, level.radial_button_group_info[ back_to_button_group ][ "player_view_pos" ] ) );
|
||||
travel_speed *= total_distance / CONST_view_travel_unit_dist;
|
||||
travel_speed = max( travel_speed, 0.1 ); // due to repeated button presses, the travel distance can be cut to 0 travel speed at times.
|
||||
|
||||
blur_time = travel_speed;
|
||||
if ( !CONST_direct_travel )
|
||||
blur_time *= travel_speed * ( level.view_paths[ path_name ].size + 1 );
|
||||
self thread blur_sine( CONST_blur_strength, blur_time );
|
||||
|
||||
if ( !CONST_direct_travel )
|
||||
{
|
||||
for ( idx = level.view_paths[ path_name ].size - 1; idx >= 0; idx-- )
|
||||
{
|
||||
node = level.view_paths[ path_name ][ idx ];
|
||||
level.dummy_mover MoveTo( node.origin, travel_speed );
|
||||
level.dummy_mover RotateTo( node.angles, travel_speed );
|
||||
|
||||
//self thread travel_view_fx( travel_speed );
|
||||
wait travel_speed;
|
||||
}
|
||||
}
|
||||
|
||||
self thread blur_sine( CONST_blur_strength, travel_speed );
|
||||
|
||||
pos = level.radial_button_group_info[ back_to_button_group ][ "player_view_pos" ];
|
||||
angle = level.radial_button_group_info[ back_to_button_group ][ "view_angles" ];
|
||||
|
||||
level.dummy_mover MoveTo( pos, travel_speed, travel_speed * 0.5, 0 );
|
||||
level.dummy_mover RotateTo( angle, travel_speed, travel_speed * 0.5, 0 );
|
||||
wait travel_speed;
|
||||
}
|
||||
|
||||
|
||||
travel_view_fx( time )
|
||||
{
|
||||
self setblurforplayer( 20, ( time + 0.2 )/2 );
|
||||
self setblurforplayer( 0, ( time + 0.2 )/2 );
|
||||
self shellshock( "frag_grenade_mp", time + 0.2 );
|
||||
}
|
||||
|
||||
|
||||
blur_sine( strength, time )
|
||||
{
|
||||
time_scaled = int( time/0.05 );
|
||||
|
||||
for( i = 0; i < time_scaled; i ++ )
|
||||
{
|
||||
fraction = ( i / ( time_scaled ) );
|
||||
cos_fraction= sin( 180 * fraction );
|
||||
blur_amount = strength * cos_fraction;
|
||||
|
||||
setdvar( "r_blur", blur_amount );
|
||||
wait 0.05;
|
||||
}
|
||||
setdvar( "r_blur", 0 );
|
||||
}
|
||||
|
||||
|
||||
force_player_angles()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
level.dummy_mover endon( "remove_dummy" );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
self setplayerangles ( level.dummy_mover.angles );
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
137
maps/mp/_defcon.gsc
Normal file
137
maps/mp/_defcon.gsc
Normal file
@@ -0,0 +1,137 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
// if ( game["state"] == "postgame" && game["teamScores"][attacker.team] > game["teamScores"][level.otherTeam[attacker.team]] )
|
||||
|
||||
ICONSIZE = 20;
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !isDefined( level.defconMode ) || level.defconMode == false )
|
||||
return;
|
||||
|
||||
if ( !isDefined( game["defcon"] ) )
|
||||
game["defcon"] = 4;
|
||||
|
||||
SetDvar( "scr_defcon", game["defcon"] );
|
||||
|
||||
/# setDevDvarIfUninitialized( "scr_defconStreak", 10 ); #/
|
||||
|
||||
level.defconStreakAdd[5] = 0;
|
||||
level.defconStreakAdd[4] = 0;
|
||||
level.defconStreakAdd[3] = -1;
|
||||
level.defconStreakAdd[2] = -1;
|
||||
level.defconStreakAdd[1] = -1;
|
||||
|
||||
level.defconPointMod[5] = 1;
|
||||
level.defconPointMod[4] = 1;
|
||||
level.defconPointMod[3] = 1;
|
||||
level.defconPointMod[2] = 1;
|
||||
level.defconPointMod[1] = 2;
|
||||
|
||||
updateDefcon( game["defcon"] );
|
||||
thread defconKillstreakThread();
|
||||
}
|
||||
|
||||
defconKillstreakWait( streakCount )
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "player_got_killstreak_" + streakCount, player );
|
||||
level notify ( "defcon_killstreak", streakCount, player );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
defconKillstreakThread()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
requiredKillCount = 10;
|
||||
|
||||
/#
|
||||
requiredKillCount = getDvarInt( "scr_defconStreak" );
|
||||
#/
|
||||
|
||||
level thread defconKillstreakWait( requiredKillCount );
|
||||
level thread defconKillstreakWait( requiredKillCount - 1 );
|
||||
level thread defconKillstreakWait( requiredKillCount - 2 );
|
||||
|
||||
level thread defconKillstreakWait( (requiredKillCount * 2) );
|
||||
level thread defconKillstreakWait( (requiredKillCount * 2) - 1 );
|
||||
level thread defconKillstreakWait( (requiredKillCount * 2) - 2 );
|
||||
|
||||
level thread defconKillstreakWait( (requiredKillCount * 3) );
|
||||
level thread defconKillstreakWait( (requiredKillCount * 3) - 1 );
|
||||
level thread defconKillstreakWait( (requiredKillCount * 3) - 2 );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "defcon_killstreak", streakCount, changingPlayer );
|
||||
|
||||
if ( game["defcon"] <= 1 )
|
||||
continue;
|
||||
|
||||
if ( (streakCount % requiredKillCount) == requiredKillCount - 2 )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !isAlive( player ) )
|
||||
continue;
|
||||
|
||||
player thread maps\mp\gametypes\_hud_message::playerCardSplashNotify( "two_from_defcon", changingPlayer );
|
||||
}
|
||||
}
|
||||
else if ( (streakCount % requiredKillCount) == requiredKillCount - 1 )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !isAlive( player ) )
|
||||
continue;
|
||||
|
||||
player thread maps\mp\gametypes\_hud_message::playerCardSplashNotify( "one_from_defcon", changingPlayer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
updateDefcon( game["defcon"] - 1, changingPlayer, streakCount );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
updateDefcon( newDefcon, changingPlayer, streakCount )
|
||||
{
|
||||
newDefcon = int( newDefcon );
|
||||
oldDefcon = game["defcon"];
|
||||
game["defcon"] = newDefcon;
|
||||
|
||||
// level.killStreakMod = level.defconStreakAdd[newDefcon];
|
||||
level.objectivePointsMod = level.defconPointMod[newDefcon];
|
||||
|
||||
setDvar( "scr_defcon", game["defcon"] );
|
||||
|
||||
//isdefined used for variable init
|
||||
if( isDefined( changingPlayer ) )
|
||||
changingPlayer notify( "changed_defcon" );
|
||||
|
||||
if ( newDefcon == oldDefcon )
|
||||
return;
|
||||
|
||||
if ( game["defcon"] == 3 && isDefined( changingPlayer ) )
|
||||
{
|
||||
changingPlayer maps\mp\killstreaks\_killstreaks::giveKillstreak( "airdrop_mega" );
|
||||
changingPlayer thread maps\mp\gametypes\_hud_message::splashNotify( "caused_defcon" , streakCount );
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( isAlive( player ) )
|
||||
{
|
||||
player thread maps\mp\gametypes\_hud_message::defconSplashNotify( game["defcon"], newDefcon < oldDefcon );
|
||||
if ( isDefined( changingPlayer ) )
|
||||
player thread maps\mp\gametypes\_hud_message::playerCardSplashNotify( "changed_defcon", changingPlayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
82
maps/mp/_destructables.gsc
Normal file
82
maps/mp/_destructables.gsc
Normal file
@@ -0,0 +1,82 @@
|
||||
init()
|
||||
{
|
||||
// level.destructableFX = loadfx("fx/breakables/exp_wall_cinderblock_96");
|
||||
|
||||
ents = getentarray("destructable", "targetname");
|
||||
|
||||
if (getdvar("scr_destructables") == "0")
|
||||
{
|
||||
for (i = 0; i < ents.size; i++)
|
||||
ents[i] delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ents.size; i++)
|
||||
{
|
||||
ents[i] thread destructable_think();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destructable_think()
|
||||
{
|
||||
accumulate = 40;
|
||||
threshold = 0;
|
||||
|
||||
if (isdefined(self.script_accumulate))
|
||||
accumulate = self.script_accumulate;
|
||||
if (isdefined(self.script_threshold))
|
||||
threshold = self.script_threshold;
|
||||
|
||||
if (isdefined(self.script_destructable_area)) {
|
||||
areas = strtok(self.script_destructable_area, " ");
|
||||
for (i = 0; i < areas.size; i++)
|
||||
self blockArea(areas[i]);
|
||||
}
|
||||
|
||||
if ( isdefined( self.script_fxid ) )
|
||||
self.fx = loadfx( self.script_fxid );
|
||||
|
||||
dmg = 0;
|
||||
|
||||
self setcandamage(true);
|
||||
while(1)
|
||||
{
|
||||
self waittill("damage", amount, other);
|
||||
if (amount >= threshold)
|
||||
{
|
||||
dmg += amount;
|
||||
if (dmg >= accumulate)
|
||||
{
|
||||
self thread destructable_destruct();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destructable_destruct()
|
||||
{
|
||||
ent = self;
|
||||
if (isdefined(self.script_destructable_area)) {
|
||||
areas = strtok(self.script_destructable_area, " ");
|
||||
for (i = 0; i < areas.size; i++)
|
||||
self unblockArea(areas[i]);
|
||||
}
|
||||
if ( isdefined( ent.fx ) )
|
||||
playfx( ent.fx, ent.origin + (0,0,6) );
|
||||
ent delete();
|
||||
}
|
||||
|
||||
blockArea(area)
|
||||
{
|
||||
}
|
||||
blockEntsInArea(ents, area)
|
||||
{
|
||||
}
|
||||
unblockArea(area)
|
||||
{
|
||||
}
|
||||
unblockEntsInArea(ents, area)
|
||||
{
|
||||
}
|
||||
294
maps/mp/_dlcalienegg.gsc
Normal file
294
maps/mp/_dlcalienegg.gsc
Normal file
@@ -0,0 +1,294 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
CONST_EGG_ID = "dlcEggStatus";
|
||||
CONST_ALL_EGG_CHALLENGE = "ch_weekly_1";
|
||||
|
||||
init()
|
||||
{
|
||||
// this is a terrible hack.
|
||||
// We are tracking all of the egg state in one 32-bit integer.
|
||||
// The lower 16 bits track whether the player has shot the egg for that level, 4-bits per map-pack.
|
||||
|
||||
|
||||
|
||||
// the number of bits shifted should correspond to the order in mapNames.csv
|
||||
// we'll use this to check completion of player data
|
||||
level.dlcAlienEggs = [];
|
||||
// pack 1
|
||||
level.dlcAlienEggs[ "mp_boneyard_ns" ] = 1 << 0;
|
||||
level.dlcAlienEggs[ "mp_swamp" ] = 1 << 1;
|
||||
level.dlcAlienEggs[ "mp_ca_red_river" ] = 1 << 2;
|
||||
level.dlcAlienEggs[ "mp_ca_rumble" ] = 1 << 3;
|
||||
|
||||
// pack 2
|
||||
level.dlcAlienEggs[ "mp_dome_ns" ] = 1 << 4;
|
||||
level.dlcAlienEggs[ "mp_battery3" ] = 1 << 5;
|
||||
level.dlcAlienEggs[ "mp_ca_impact" ] = 1 << 6;
|
||||
level.dlcAlienEggs[ "mp_ca_behemoth" ] = 1 << 7;
|
||||
|
||||
// pack 3. bits 8-11
|
||||
level.dlcAlienEggs[ "mp_dig" ] = 1 << 8;
|
||||
level.dlcAlienEggs[ "mp_favela_iw6" ] = 1 << 9;
|
||||
level.dlcAlienEggs[ "mp_pirate" ] = 1 << 10;
|
||||
level.dlcAlienEggs[ "mp_zulu" ] = 1 << 11;
|
||||
|
||||
// pack 4. bits 12-15
|
||||
level.dlcAlienEggs[ "mp_conflict" ] = 1 << 12;
|
||||
level.dlcAlienEggs[ "mp_mine" ] = 1 << 13;
|
||||
level.dlcAlienEggs[ "mp_zerosub" ] = 1 << 14;
|
||||
level.dlcAlienEggs[ "mp_shipment_ns" ] = 1 << 15;
|
||||
|
||||
// Translate each map name to its pack number (0-based)
|
||||
level.dlcAliengEggMapToPack[ "mp_boneyard_ns" ] = 0;
|
||||
level.dlcAliengEggMapToPack[ "mp_swamp" ] = 0;
|
||||
level.dlcAliengEggMapToPack[ "mp_ca_red_river" ] = 0;
|
||||
level.dlcAliengEggMapToPack[ "mp_ca_rumble" ] = 0;
|
||||
|
||||
level.dlcAliengEggMapToPack[ "mp_dome_ns" ] = 1;
|
||||
level.dlcAliengEggMapToPack[ "mp_battery3" ] = 1;
|
||||
level.dlcAliengEggMapToPack[ "mp_ca_impact" ] = 1;
|
||||
level.dlcAliengEggMapToPack[ "mp_ca_behemoth" ] = 1;
|
||||
|
||||
level.dlcAliengEggMapToPack[ "mp_dig" ] = 2;
|
||||
level.dlcAliengEggMapToPack[ "mp_favela_iw6" ] = 2;
|
||||
level.dlcAliengEggMapToPack[ "mp_pirate" ] = 2;
|
||||
level.dlcAliengEggMapToPack[ "mp_zulu" ] = 2;
|
||||
|
||||
// pack 4. bits 12-15
|
||||
level.dlcAliengEggMapToPack[ "mp_conflict" ] = 3;
|
||||
level.dlcAliengEggMapToPack[ "mp_mine" ] = 3;
|
||||
level.dlcAliengEggMapToPack[ "mp_zerosub" ] = 3;
|
||||
level.dlcAliengEggMapToPack[ "mp_shipment_ns" ] = 3;
|
||||
|
||||
// # of set bits in 0 - 15
|
||||
// use to look up how many eggs have been achieved
|
||||
level.bitCounts = [ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 ];
|
||||
|
||||
level._effect[ "vfx_alien_easter_egg_hit" ] = loadfx( "vfx/gameplay/alien/vfx_alien_easter_egg_hit" );
|
||||
}
|
||||
|
||||
setupEggForMap( eggName )
|
||||
{
|
||||
if ( level.rankedMatch )
|
||||
{
|
||||
init();
|
||||
|
||||
flags = level.dlcAlienEggs[ getMapName() ];
|
||||
|
||||
AssertEx( IsDefined( flags ), "dlcAlienEggs bit flag not set up for map: " + getMapName() );
|
||||
|
||||
egg = GetEnt( eggName, "targetname" );
|
||||
if ( IsDefined( egg ) )
|
||||
{
|
||||
// add flags
|
||||
// playlistType = GetDvarInt( "scr_playlist_type", 0 );
|
||||
|
||||
if ( egg.classname == "script_model" )
|
||||
{
|
||||
egg SetCanDamage( true );
|
||||
}
|
||||
|
||||
egg thread eggTrackHits();
|
||||
}
|
||||
|
||||
/#
|
||||
thread eggDebug();
|
||||
#/
|
||||
}
|
||||
}
|
||||
|
||||
eggTrackHits()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self.health = 99999;
|
||||
|
||||
level.eggHits = [];
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction, point, damageType );
|
||||
|
||||
// play a sound and effect?
|
||||
PlayFX( getfx( "vfx_alien_easter_egg_hit" ), point, AnglesToForward( direction ), AnglesToUp( direction ) );
|
||||
|
||||
if ( IsPlayer( attacker ) && !IsAI( attacker ) )
|
||||
{
|
||||
attackerNum = attacker getUniqueId();
|
||||
// we have not hit this egg before
|
||||
if ( !IsDefined( level.eggHits[ attackerNum ] ) )
|
||||
{
|
||||
level.eggHits[ attackerNum ] = 1;
|
||||
self eggRegisterHit( damage, attacker, direction, point, damageType );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
eggRegisterHit( damage, attacker, direction, point, type )
|
||||
{
|
||||
self.health += damage; // don't let the health drop
|
||||
|
||||
if ( !( attacker eggHasCompletedForMap( getMapName() ) ) )
|
||||
{
|
||||
attacker eggSetCompletedForMap( getMapName() );
|
||||
}
|
||||
else if ( attacker eggAllFound()
|
||||
&& attacker ch_getState( CONST_ALL_EGG_CHALLENGE ) < 2
|
||||
)
|
||||
{
|
||||
attacker eggAwardPatch();
|
||||
}
|
||||
}
|
||||
|
||||
eggHasCompletedForMap( mapName ) // self == player
|
||||
{
|
||||
eggState = self GetRankedPlayerDataReservedInt( CONST_EGG_ID );
|
||||
|
||||
bitFlag = level.dlcAlienEggs[ mapName ];
|
||||
if ( IsDefined( bitFlag ) &&
|
||||
(eggState & bitFlag) != 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
eggSetCompletedForMap( mapName ) // self == player
|
||||
{
|
||||
bitFlag = level.dlcAlienEggs[ mapName ];
|
||||
|
||||
if ( IsDefined( bitFlag ) )
|
||||
{
|
||||
eggState = self GetRankedPlayerDataReservedInt( CONST_EGG_ID );
|
||||
|
||||
eggState |= bitFlag;
|
||||
self SetRankedPlayerDataReservedInt( CONST_EGG_ID, eggState );
|
||||
|
||||
packNum = level.dlcAliengEggMapToPack[ mapName ];
|
||||
AssertEx( IsDefined( packNum ), "MapPack ID not defined for " + mapName );
|
||||
|
||||
numCompleted = eggCountCompletedEggsForPack( packNum, eggState );
|
||||
packNum++; // the splashes are indexed 1-4, instead of 0-3
|
||||
if ( numCompleted < 4 )
|
||||
{
|
||||
self maps\mp\gametypes\_hud_message::playerCardSplashNotify( "dlc_eggFound_" + packNum, self, numCompleted );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if all the eggs are found, give ultimate award
|
||||
if ( self eggAllFound()
|
||||
&& ch_getState( CONST_ALL_EGG_CHALLENGE ) < 2
|
||||
)
|
||||
{
|
||||
self eggAwardPatch();
|
||||
}
|
||||
// otherwise give award for this pack
|
||||
else
|
||||
{
|
||||
self maps\mp\gametypes\_hud_message::playerCardSplashNotify( "dlc_eggAllFound_" + packNum, self );
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "dlc_egg_hunt" );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
self PlayLocalSound( "ui_extinction_egg_splash" );
|
||||
}
|
||||
}
|
||||
|
||||
eggAwardPatch()
|
||||
{
|
||||
self maps\mp\gametypes\_hud_message::playerCardSplashNotify( "dlc_eggAllFound", self );
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "dlc_egg_hunt_all" );
|
||||
|
||||
ch_setState( CONST_ALL_EGG_CHALLENGE, 2 ); // patch. Weekly challenges are unlocked with state set to 2.
|
||||
}
|
||||
|
||||
eggCountCompletedEggsForPack( packNum, eggState )
|
||||
{
|
||||
flags = eggState >> (packnum * 4); // move the bits to the first set of 4
|
||||
flags &= 15; // mask out everything but the first four bits
|
||||
|
||||
return level.bitCounts[ flags ];
|
||||
}
|
||||
|
||||
// packNum is 0-indexed
|
||||
eggAllFoundForPack( packNum )
|
||||
{
|
||||
eggState = self GetRankedPlayerDataReservedInt( CONST_EGG_ID );
|
||||
packEggState = (eggState >> (packnum *4)) & 15;
|
||||
|
||||
return (packEggState != 0);
|
||||
}
|
||||
|
||||
// all 16 eggs found
|
||||
CONST_ALL_EGGS_MASK = (1 << 16) - 1;
|
||||
eggAllFound()
|
||||
{
|
||||
eggState = self GetRankedPlayerDataReservedInt( CONST_EGG_ID );
|
||||
|
||||
return ( eggState == CONST_ALL_EGGS_MASK );
|
||||
}
|
||||
|
||||
/#
|
||||
eggDebug()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
level waittill( "connected", player );
|
||||
|
||||
player thread eggDebugPlayer();
|
||||
}
|
||||
|
||||
eggDebugPlayer() // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
SetDvarIfUninitialized( "scr_egg_set", "" );
|
||||
SetDvarIfUninitialized( "scr_egg_pack_set", 0 );
|
||||
SetDvarIfUninitialized( "scr_egg_clear", 0 );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
mapName = GetDvar( "scr_egg_set" );
|
||||
if ( mapName != "" )
|
||||
{
|
||||
self eggSetCompletedForMap( mapName );
|
||||
SetDvar( "scr_egg_set", "" );
|
||||
}
|
||||
|
||||
if ( GetDvarInt( "scr_egg_clear" ) != 0 )
|
||||
{
|
||||
self SetRankedPlayerDataReservedInt( CONST_EGG_ID, 0 );
|
||||
SetDvar( "scr_egg_clear", 0 );
|
||||
level.eggHits = [];
|
||||
|
||||
ch_setState( CONST_ALL_EGG_CHALLENGE, 0 );
|
||||
}
|
||||
|
||||
// set all the flags for one pack
|
||||
targetPackNum = GetDvarInt( "scr_egg_pack_set" );
|
||||
if ( targetPackNum > 0 )
|
||||
{
|
||||
targetPackNum--;
|
||||
|
||||
foreach ( mapName, packNum in level.dlcAliengEggMapToPack )
|
||||
{
|
||||
if ( packNum == targetPackNum && !( self eggHasCompletedForMap( mapName ) ) )
|
||||
{
|
||||
self eggSetCompletedForMap( mapName );
|
||||
wait (3.6); // a little bit more than the duration of the splash
|
||||
}
|
||||
}
|
||||
|
||||
SetDvar( "scr_egg_pack_set", "" );
|
||||
}
|
||||
|
||||
wait ( 0.25 );
|
||||
}
|
||||
}
|
||||
#/
|
||||
378
maps/mp/_elevator.gsc
Normal file
378
maps/mp/_elevator.gsc
Normal file
@@ -0,0 +1,378 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// ELEVATOR TEST
|
||||
ELEVATOR_DOOR_TIME = 2;
|
||||
ELEVATOR_FLOOR_MOVE_TIME = 5;
|
||||
ELEVATOR_AUTOCLOSE_TIMEOUT = 10;
|
||||
|
||||
ELEVATOR_DOOR_STATE_CLOSED = 0;
|
||||
ELEVATOR_DOOR_STATE_OPENING = 1;
|
||||
ELEVATOR_DOOR_STATE_OPEN = 2;
|
||||
ELEVATOR_DOOR_STATE_CLOSING = 3;
|
||||
|
||||
// should scale elevator door speed based on the actual distance moved
|
||||
// in case of interrupts
|
||||
|
||||
init_elevator( config )
|
||||
{
|
||||
elevator = GetEnt( config.name, "targetname" );
|
||||
AssertEx( IsDefined( elevator ), "Could not find an elevator entity named " + config.name );
|
||||
elevator.unresolved_collision_func = ::handleUnreslovedCollision;
|
||||
|
||||
elevator.doors = [];
|
||||
foreach ( floorname, doorset in config.doors )
|
||||
{
|
||||
list = [];
|
||||
foreach ( doorname in doorset )
|
||||
{
|
||||
list[ list.size ] = setupDoor( doorName + "left", false, config.doorMoveDist );
|
||||
list[ list.size ] = setupDoor( doorName + "right", true, config.doorMoveDist );
|
||||
}
|
||||
|
||||
elevator.doors[ floorname ] = list;
|
||||
}
|
||||
|
||||
elevator.trigBlock = GetEnt( config.trigBlockName, "targetname" );
|
||||
AssertEx( IsDefined( elevator.trigBlock ), "Could not find an elevator trigger named " + config.trigBlockName );
|
||||
|
||||
elevator.curFloor = "floor1";
|
||||
elevator.requestedFloor = elevator.curFloor;
|
||||
elevator.doorState = ELEVATOR_DOOR_STATE_CLOSED;
|
||||
|
||||
elevator.doorOpenTime = 2.0;
|
||||
elevator.doorSpeed = config.doorMoveDist / elevator.doorOpenTime;
|
||||
elevator.moveTime = 5.0;
|
||||
elevator.autoCloseTimeout = 10.0;
|
||||
|
||||
elevator.destinations = [];
|
||||
elevator.pathBlockers = [];
|
||||
elevator.buttons = GetEntArray( config.buttons, "targetname" );
|
||||
foreach ( button in elevator.buttons )
|
||||
{
|
||||
button setupButton( elevator );
|
||||
}
|
||||
|
||||
elevatorModels = GetEntArray( "elevator_models", "targetname" );
|
||||
foreach ( eleModel in elevatorModels )
|
||||
{
|
||||
eleModel LinkTo( elevator );
|
||||
}
|
||||
|
||||
elevator thread elevatorThink();
|
||||
|
||||
elevator thread openElevatorDoors( elevator.curFloor, false );
|
||||
}
|
||||
|
||||
setupDoor( doorName, isRightSide, moveDistance )
|
||||
{
|
||||
door = GetEnt( doorName, "targetname" );
|
||||
if (IsDefined(door))
|
||||
{
|
||||
door.closePos = door.origin;
|
||||
if (IsDefined(door.target))
|
||||
{
|
||||
targetStruct = getstruct( door.target, "targetname" );
|
||||
door.openPos = targetStruct.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = AnglesToForward( door.angles ) * moveDistance;
|
||||
/*
|
||||
if (isRightSide)
|
||||
{
|
||||
offset *= -1;
|
||||
}
|
||||
*/
|
||||
door.openPos = door.origin + offset;
|
||||
}
|
||||
|
||||
// door.unresolved_collision_func = ::handleUnreslovedCollision;
|
||||
|
||||
return door;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertEx( IsDefined( door ), "Could not find an elevator door entity named " + doorName );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setupButton( elevator ) // self == button
|
||||
{
|
||||
self.owner = elevator;
|
||||
|
||||
if ( IsDefined( self.target ) )
|
||||
{
|
||||
destination = getstruct( self.target, "targetname" );
|
||||
if ( IsDefined( destination ) )
|
||||
{
|
||||
elevator.destinations[ self.script_label ] = destination.origin;
|
||||
if ( IsDefined( destination.target ) )
|
||||
{
|
||||
blocker = GetEnt( destination.target, "targetname" );
|
||||
if ( IsDefined( blocker ) )
|
||||
{
|
||||
elevator.pathBlockers[ self.script_label ] = blocker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self enableButton();
|
||||
}
|
||||
|
||||
enableButton() // self == button
|
||||
{
|
||||
self SetHintString( &"MP_ELEVATOR_USE" );
|
||||
self MakeUsable();
|
||||
|
||||
self thread buttonThink();
|
||||
}
|
||||
|
||||
disableButton()
|
||||
{
|
||||
self MakeUnusable();
|
||||
}
|
||||
|
||||
buttonThink()
|
||||
{
|
||||
elevator = self.owner;
|
||||
elevator endon( "elevator_busy" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "trigger" );
|
||||
|
||||
if ( self.script_label == "elevator" )
|
||||
{
|
||||
// do some stuff
|
||||
if ( elevator.curFloor == "floor1" )
|
||||
{
|
||||
elevator.requestedFloor = "floor2";
|
||||
}
|
||||
else
|
||||
{
|
||||
elevator.requestedFloor = "floor1";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elevator.requestedFloor = self.script_label;
|
||||
}
|
||||
|
||||
elevator notify( "elevator_called" );
|
||||
}
|
||||
}
|
||||
|
||||
elevatorThink()
|
||||
{
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "elevator_called" );
|
||||
|
||||
foreach ( button in self.buttons )
|
||||
{
|
||||
button disableButton();
|
||||
}
|
||||
|
||||
if ( self.curFloor != self.requestedFloor )
|
||||
{
|
||||
if ( self.doorState != ELEVATOR_DOOR_STATE_CLOSED )
|
||||
{
|
||||
self notify ("elevator_stop_autoclose");
|
||||
self thread closeElevatorDoors( self.curFloor );
|
||||
self waittill( "elevator_doors_closed" );
|
||||
}
|
||||
|
||||
self elevatorMoveToFloor( self.requestedFloor );
|
||||
wait (0.25);
|
||||
}
|
||||
|
||||
self thread openElevatorDoors( self.curFloor, false );
|
||||
|
||||
self waittill ( "elevator_doors_open" );
|
||||
foreach ( button in self.buttons )
|
||||
{
|
||||
button enableButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elevatorMoveToFloor( targetFloor )
|
||||
{
|
||||
self PlaySound( "scn_elevator_startup" );
|
||||
self PlayLoopSound( "scn_elevator_moving_lp" );
|
||||
|
||||
destinationPos = self.destinations[ targetFloor ];
|
||||
deltaZ = destinationPos[2] - self.origin[2];
|
||||
|
||||
// move doors
|
||||
foreach ( door in self.doors[ "elevator" ] )
|
||||
{
|
||||
door MoveZ( deltaZ, self.moveTime );
|
||||
}
|
||||
// move the floor
|
||||
self MoveZ( deltaZ, self.moveTime );
|
||||
|
||||
wait ( self.moveTime );
|
||||
|
||||
self StopLoopSound ( "scn_elevator_moving_lp" );
|
||||
self PlaySound ( "scn_elevator_stopping" );
|
||||
self PlaySound ( "scn_elevator_beep" );
|
||||
|
||||
self.curFloor = self.requestedFloor;
|
||||
}
|
||||
|
||||
openElevatorDoors( floorName, autoClose ) // elevator
|
||||
{
|
||||
doorset = self.doors[ floorName ];
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_OPENING;
|
||||
|
||||
// figre out the time it takes to move 1 door, assume it's the same fo rall
|
||||
door = doorset[0];
|
||||
doorDest = (door.openPos[0], door.openPos[1], door.origin[2]);
|
||||
moveDelta = doorDest - door.origin;
|
||||
moveDist = Length( moveDelta );
|
||||
|
||||
// this might move 0 time / 0 dist
|
||||
// but we need it to counteract the closing elevator
|
||||
// wish I could just tell it to stop, instead
|
||||
movetime = moveDist / self.doorSpeed;
|
||||
accelTime = 0.25;
|
||||
if (moveTime == 0.0)
|
||||
{
|
||||
moveTime = 0.05;
|
||||
accelTime = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlaySound( "scn_elevator_doors_opening" );
|
||||
accelTime = min(accelTime, moveTime);
|
||||
}
|
||||
|
||||
foreach ( door in doorset )
|
||||
{
|
||||
door MoveTo( (door.openPos[0], door.openPos[1], door.origin[2]), movetime, 0.0, accelTime );
|
||||
}
|
||||
wait ( movetime );
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_OPEN;
|
||||
|
||||
self notify ( "elevator_doors_open" );
|
||||
|
||||
self elevatorClearPath( floorName );
|
||||
|
||||
if ( autoClose )
|
||||
{
|
||||
self thread elevatorDoorsAutoClose();
|
||||
}
|
||||
}
|
||||
|
||||
closeElevatorDoors( floorName )
|
||||
{
|
||||
self endon( "elevator_close_interrupted" );
|
||||
|
||||
self thread watchCloseInterrupted( floorName );
|
||||
|
||||
doorset = self.doors[ floorName ];
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_CLOSING;
|
||||
|
||||
// figre out the time it takes to move 1 door, assume it's the same fo rall
|
||||
door = doorset[0];
|
||||
doorDest = (door.closePos[0], door.closePos[1], door.origin[2]);
|
||||
moveDelta = doorDest - door.origin;
|
||||
moveDist = Length( moveDelta );
|
||||
|
||||
if ( moveDist != 0.0 )
|
||||
{
|
||||
movetime = moveDist / self.doorSpeed;
|
||||
foreach ( door in doorset )
|
||||
{
|
||||
// we assume the doors all begin closed,
|
||||
// so door.closePos should eventually be defined by the time we need it
|
||||
door MoveTo( (door.closePos[0], door.closePos[1], door.origin[2]), movetime, 0.0, 0.25 );
|
||||
}
|
||||
self PlaySound( "scn_elevator_doors_closing" );
|
||||
wait ( movetime );
|
||||
}
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_CLOSED;
|
||||
|
||||
self elevatorBlockPath( floorName );
|
||||
|
||||
self notify( "elevator_doors_closed" );
|
||||
}
|
||||
|
||||
watchCloseInterrupted( floorName )
|
||||
{
|
||||
// if the doors have closed successfully, we don't care any more
|
||||
self endon( "elevator_doors_closed" );
|
||||
|
||||
// make sure there is nothing in the way now
|
||||
nothingBlocking = true;
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
if ( character isTouchingTrigger( self.trigBlock ) )
|
||||
{
|
||||
nothingBlocking = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( nothingBlocking )
|
||||
{
|
||||
self.trigBlock waittill( "trigger" );
|
||||
}
|
||||
|
||||
self notify( "elevator_close_interrupted" );
|
||||
|
||||
self openElevatorDoors( floorName, true );
|
||||
}
|
||||
|
||||
isTouchingTrigger( trigger ) // self == player
|
||||
{
|
||||
return ( IsAlive( self ) && self IsTouching( trigger ) );
|
||||
}
|
||||
|
||||
elevatorDoorsAutoClose() // self == elevator
|
||||
{
|
||||
self endon( "elevator_doors_closed" );
|
||||
self endon( "elevator_stop_autoclose" );
|
||||
|
||||
wait ( self.autoCloseTimeout );
|
||||
|
||||
self closeElevatorDoors( self.curFloor);
|
||||
}
|
||||
|
||||
handleUnreslovedCollision( hitEnt ) // self == mover, hitEnt == player (usually)
|
||||
{
|
||||
if ( !IsPlayer( hitEnt ) )
|
||||
{
|
||||
hitEnt DoDamage( 1000, hitEnt.origin, self, self, "MOD_CRUSH" );
|
||||
}
|
||||
}
|
||||
|
||||
elevatorClearPath( floorName ) // self == elevator
|
||||
{
|
||||
blocker = self.pathBlockers[ floorName ];
|
||||
if ( IsDefined( blocker ) )
|
||||
{
|
||||
blocker ConnectPaths();
|
||||
blocker Hide();
|
||||
blocker NotSolid();
|
||||
}
|
||||
}
|
||||
|
||||
elevatorBlockPath( floorName )
|
||||
{
|
||||
blocker = self.pathBlockers[ floorName ];
|
||||
if ( IsDefined( blocker ) )
|
||||
{
|
||||
blocker Show();
|
||||
blocker Solid();
|
||||
blocker DisconnectPaths();
|
||||
}
|
||||
}
|
||||
468
maps/mp/_elevator_v2.gsc
Normal file
468
maps/mp/_elevator_v2.gsc
Normal file
@@ -0,0 +1,468 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// _elevator_v2: used in mp_mines.gsc (original was used in mp_fahrenheit).
|
||||
// Making adjustments to allow for multiple types of elevators
|
||||
|
||||
// ELEVATOR TEST
|
||||
ELEVATOR_DOOR_TIME = 2;
|
||||
ELEVATOR_FLOOR_MOVE_TIME = 5;
|
||||
ELEVATOR_AUTOCLOSE_TIMEOUT = 10;
|
||||
|
||||
ELEVATOR_DOOR_STATE_CLOSED = 0;
|
||||
ELEVATOR_DOOR_STATE_OPENING = 1;
|
||||
ELEVATOR_DOOR_STATE_OPEN = 2;
|
||||
ELEVATOR_DOOR_STATE_CLOSING = 3;
|
||||
|
||||
// should scale elevator door speed based on the actual distance moved
|
||||
// in case of interrupts
|
||||
|
||||
init_elevator( config )
|
||||
{
|
||||
elevator = GetEnt( config.name, "targetname" );
|
||||
AssertEx( IsDefined( elevator ), "Could not find an elevator entity named " + config.name );
|
||||
elevator.unresolved_collision_func = ::handleUnreslovedCollision;
|
||||
|
||||
elevator.doors = [];
|
||||
if ( IsDefined( config.doors ) )
|
||||
{
|
||||
foreach ( floorname, doorset in config.doors )
|
||||
{
|
||||
list = [];
|
||||
foreach ( doorname in doorset )
|
||||
{
|
||||
list[ list.size ] = setupDoor( doorName + "left", false, config.doorMoveDist );
|
||||
list[ list.size ] = setupDoor( doorName + "right", true, config.doorMoveDist );
|
||||
}
|
||||
|
||||
elevator.doors[ floorname ] = list;
|
||||
}
|
||||
|
||||
if ( IsDefined( config.doorOpenTime ) )
|
||||
elevator.doorOpenTime = config.doorOpenTime;
|
||||
else
|
||||
elevator.doorOpenTime = ELEVATOR_DOOR_TIME;
|
||||
|
||||
elevator.doorSpeed = config.doorMoveDist / elevator.doorOpenTime;
|
||||
|
||||
if ( IsDefined( config.autoCloseTimeout ) )
|
||||
elevator.autoCloseTimeout = config.autoCloseTimeout;
|
||||
else
|
||||
elevator.autoCloseTimeout = ELEVATOR_AUTOCLOSE_TIMEOUT;
|
||||
|
||||
elevator.trigBlock = GetEnt( config.trigBlockName, "targetname" );
|
||||
AssertEx( IsDefined( elevator.trigBlock ), "Could not find an elevator trigger named " + config.trigBlockName );
|
||||
|
||||
if ( IsDefined( config.autoCloseTimeout ) )
|
||||
elevator.autoCloseTimeout = config.autoCloseTimeout;
|
||||
else
|
||||
elevator.autoCloseTimeout = ELEVATOR_AUTOCLOSE_TIMEOUT;
|
||||
|
||||
elevator.doorOpenSfx = config.doorOpenSfx; // scn_elevator_doors_opening
|
||||
elevator.doorCloseSfx = config.doorCloseSfx; // scn_elevator_doors_closing
|
||||
}
|
||||
|
||||
if ( IsDefined( config.moveTime ) )
|
||||
elevator.moveTime = config.moveTime;
|
||||
else
|
||||
elevator.moveTime = ELEVATOR_FLOOR_MOVE_TIME;
|
||||
|
||||
elevator.destinations = [];
|
||||
elevator.pathBlockers = [];
|
||||
elevator.buttons = GetEntArray( config.buttons, "targetname" );
|
||||
foreach ( button in elevator.buttons )
|
||||
{
|
||||
button setupButton( elevator );
|
||||
}
|
||||
|
||||
// set up destinations
|
||||
destinationStructs = getstructarray( config.destinations, "targetname" );
|
||||
foreach ( destination in destinationStructs )
|
||||
{
|
||||
elevator setupDestination( destination );
|
||||
}
|
||||
elevator.destinationNames = config.destinationNames;
|
||||
|
||||
elevator.curFloor = config.destinationNames[0]; // "floor1"; should this be determined from destinations?
|
||||
elevator.requestedFloor = elevator.curFloor;
|
||||
elevator.doorState = ELEVATOR_DOOR_STATE_CLOSED;
|
||||
|
||||
if ( IsDefined( config.models ) )
|
||||
{
|
||||
elevatorModels = GetEntArray( config.models, "targetname" );
|
||||
if ( IsDefined( elevatorModels ) )
|
||||
{
|
||||
foreach ( eleModel in elevatorModels )
|
||||
{
|
||||
eleModel LinkTo( elevator );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elevator thread elevatorThink();
|
||||
|
||||
if (elevator.doors.size > 0)
|
||||
{
|
||||
elevator thread openElevatorDoors( elevator.curFloor, false );
|
||||
}
|
||||
|
||||
// sounds
|
||||
elevator.startSfx = config.startSfx; // "scn_elevator_startup"
|
||||
elevator.stopSfx = config.stopSfx; // "scn_elevator_stopping"
|
||||
elevator.loopSfx = config.loopSfx; // "scn_elevator_moving_lp"
|
||||
elevator.beepSfx = config.beepSfx; // "scn_elevator_beep"
|
||||
|
||||
|
||||
// callback on init
|
||||
elevator.onMoveCallback = config.onMoveCallback;
|
||||
elevator.onArrivedCallback = config.onArrivedCallback;
|
||||
|
||||
return elevator;
|
||||
}
|
||||
|
||||
setupDoor( doorName, isRightSide, moveDistance )
|
||||
{
|
||||
door = GetEnt( doorName, "targetname" );
|
||||
if (IsDefined(door))
|
||||
{
|
||||
door.closePos = door.origin;
|
||||
if (IsDefined(door.target))
|
||||
{
|
||||
targetStruct = getstruct( door.target, "targetname" );
|
||||
door.openPos = targetStruct.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = AnglesToForward( door.angles ) * moveDistance;
|
||||
/*
|
||||
if (isRightSide)
|
||||
{
|
||||
offset *= -1;
|
||||
}
|
||||
*/
|
||||
door.openPos = door.origin + offset;
|
||||
}
|
||||
|
||||
// door.unresolved_collision_func = ::handleUnreslovedCollision;
|
||||
|
||||
return door;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertEx( IsDefined( door ), "Could not find an elevator door entity named " + doorName );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setupButton( elevator ) // self == button
|
||||
{
|
||||
self.owner = elevator;
|
||||
|
||||
if ( IsDefined( self.target ) )
|
||||
{
|
||||
destination = getstruct( self.target, "targetname" );
|
||||
self setupDestination( destination );
|
||||
}
|
||||
|
||||
self enableButton();
|
||||
}
|
||||
|
||||
setupDestination( destination ) // self == elevator
|
||||
{
|
||||
|
||||
if ( IsDefined( destination ) )
|
||||
{
|
||||
self.destinations[ destination.script_label ] = destination.origin;
|
||||
if ( IsDefined( destination.target ) )
|
||||
{
|
||||
blocker = GetEnt( destination.target, "targetname" );
|
||||
if ( IsDefined( blocker ) )
|
||||
{
|
||||
self.pathBlockers[ destination.script_label ] = blocker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enableButton() // self == button
|
||||
{
|
||||
self SetHintString( &"MP_ELEVATOR_USE" );
|
||||
self MakeUsable();
|
||||
|
||||
self thread buttonThink();
|
||||
}
|
||||
|
||||
disableButton()
|
||||
{
|
||||
self MakeUnusable();
|
||||
}
|
||||
|
||||
buttonThink()
|
||||
{
|
||||
elevator = self.owner;
|
||||
elevator endon( "elevator_busy" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "trigger" );
|
||||
|
||||
if ( !IsDefined( self.script_label ) || self.script_label == "elevator" )
|
||||
{
|
||||
// do some stuff
|
||||
if ( elevator.curFloor == elevator.destinationNames[0] )
|
||||
{
|
||||
elevator.requestedFloor = elevator.destinationNames[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
elevator.requestedFloor = elevator.destinationNames[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elevator.requestedFloor = self.script_label;
|
||||
}
|
||||
|
||||
elevator notify( "elevator_called" );
|
||||
}
|
||||
}
|
||||
|
||||
elevatorThink()
|
||||
{
|
||||
hasDoors = self.doors.size > 0;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "elevator_called" );
|
||||
|
||||
foreach ( button in self.buttons )
|
||||
{
|
||||
button disableButton();
|
||||
}
|
||||
|
||||
if ( self.curFloor != self.requestedFloor )
|
||||
{
|
||||
if ( self.doorState != ELEVATOR_DOOR_STATE_CLOSED )
|
||||
{
|
||||
self notify ("elevator_stop_autoclose");
|
||||
self thread closeElevatorDoors( self.curFloor );
|
||||
self waittill( "elevator_doors_closed" );
|
||||
}
|
||||
else if ( !hasDoors )
|
||||
{
|
||||
self elevatorBlockPath( self.curFloor );
|
||||
}
|
||||
|
||||
self elevatorMoveToFloor( self.requestedFloor );
|
||||
wait (0.25);
|
||||
}
|
||||
|
||||
if ( hasDoors )
|
||||
{
|
||||
self thread openElevatorDoors( self.curFloor, false );
|
||||
self waittill ( "elevator_doors_open" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self elevatorClearPath( self.curFloor );
|
||||
}
|
||||
|
||||
foreach ( button in self.buttons )
|
||||
{
|
||||
button enableButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elevatorMoveToFloor( targetFloor )
|
||||
{
|
||||
//self PlaySound( self.startSfx );
|
||||
//self PlayLoopSound( self.loopSfx );
|
||||
|
||||
destinationPos = self.destinations[ targetFloor ];
|
||||
deltaZ = destinationPos[2] - self.origin[2]; // may need to change this to full vector later
|
||||
|
||||
if ( IsDefined( self.doors[ "elevator" ] ) )
|
||||
{
|
||||
// move doors
|
||||
foreach ( door in self.doors[ "elevator" ] )
|
||||
{
|
||||
door MoveZ( deltaZ, self.moveTime );
|
||||
}
|
||||
}
|
||||
|
||||
// move the floor
|
||||
self MoveZ( deltaZ, self.moveTime );
|
||||
|
||||
if ( IsDefined( self.onMoveCallback ) )
|
||||
{
|
||||
self thread [[ self.onMoveCallback ]]( targetFloor );
|
||||
}
|
||||
|
||||
wait ( self.moveTime );
|
||||
|
||||
//self StopLoopSound ( self.loopSfx );
|
||||
//self PlaySound ( self.stopSfx );
|
||||
if ( IsDefined( self.beepSfx ) )
|
||||
self PlaySound ( self.beepSfx );
|
||||
|
||||
self.curFloor = self.requestedFloor;
|
||||
|
||||
if ( IsDefined( self.onArrivedCallback ) )
|
||||
{
|
||||
self thread [[ self.onArrivedCallback ]]( self.curFloor );
|
||||
}
|
||||
}
|
||||
|
||||
openElevatorDoors( floorName, autoClose ) // elevator
|
||||
{
|
||||
doorset = self.doors[ floorName ];
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_OPENING;
|
||||
|
||||
// figre out the time it takes to move 1 door, assume it's the same fo rall
|
||||
door = doorset[0];
|
||||
doorDest = (door.openPos[0], door.openPos[1], door.origin[2]);
|
||||
moveDelta = doorDest - door.origin;
|
||||
moveDist = Length( moveDelta );
|
||||
|
||||
// this might move 0 time / 0 dist
|
||||
// but we need it to counteract the closing elevator
|
||||
// wish I could just tell it to stop, instead
|
||||
movetime = moveDist / self.doorSpeed;
|
||||
accelTime = 0.25;
|
||||
if (moveTime == 0.0)
|
||||
{
|
||||
moveTime = 0.05;
|
||||
accelTime = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlaySound( self.doorOpenSfx );
|
||||
accelTime = min(accelTime, moveTime);
|
||||
}
|
||||
|
||||
foreach ( door in doorset )
|
||||
{
|
||||
door MoveTo( (door.openPos[0], door.openPos[1], door.origin[2]), movetime, 0.0, accelTime );
|
||||
}
|
||||
wait ( movetime );
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_OPEN;
|
||||
|
||||
self notify ( "elevator_doors_open" );
|
||||
|
||||
self elevatorClearPath( floorName );
|
||||
|
||||
if ( autoClose )
|
||||
{
|
||||
self thread elevatorDoorsAutoClose();
|
||||
}
|
||||
}
|
||||
|
||||
closeElevatorDoors( floorName )
|
||||
{
|
||||
self endon( "elevator_close_interrupted" );
|
||||
|
||||
self thread watchCloseInterrupted( floorName );
|
||||
|
||||
doorset = self.doors[ floorName ];
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_CLOSING;
|
||||
|
||||
// figre out the time it takes to move 1 door, assume it's the same fo rall
|
||||
door = doorset[0];
|
||||
doorDest = (door.closePos[0], door.closePos[1], door.origin[2]);
|
||||
moveDelta = doorDest - door.origin;
|
||||
moveDist = Length( moveDelta );
|
||||
|
||||
if ( moveDist != 0.0 )
|
||||
{
|
||||
movetime = moveDist / self.doorSpeed;
|
||||
foreach ( door in doorset )
|
||||
{
|
||||
// we assume the doors all begin closed,
|
||||
// so door.closePos should eventually be defined by the time we need it
|
||||
door MoveTo( (door.closePos[0], door.closePos[1], door.origin[2]), movetime, 0.0, 0.25 );
|
||||
}
|
||||
self PlaySound( self.doorCloseSfx );
|
||||
wait ( movetime );
|
||||
}
|
||||
|
||||
self.doorState = ELEVATOR_DOOR_STATE_CLOSED;
|
||||
|
||||
self elevatorBlockPath( floorName );
|
||||
|
||||
self notify( "elevator_doors_closed" );
|
||||
}
|
||||
|
||||
watchCloseInterrupted( floorName )
|
||||
{
|
||||
// if the doors have closed successfully, we don't care any more
|
||||
self endon( "elevator_doors_closed" );
|
||||
|
||||
// make sure there is nothing in the way now
|
||||
nothingBlocking = true;
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
if ( character isTouchingTrigger( self.trigBlock ) )
|
||||
{
|
||||
nothingBlocking = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( nothingBlocking )
|
||||
{
|
||||
self.trigBlock waittill( "trigger" );
|
||||
}
|
||||
|
||||
self notify( "elevator_close_interrupted" );
|
||||
|
||||
self openElevatorDoors( floorName, true );
|
||||
}
|
||||
|
||||
isTouchingTrigger( trigger ) // self == player
|
||||
{
|
||||
return ( IsAlive( self ) && self IsTouching( trigger ) );
|
||||
}
|
||||
|
||||
elevatorDoorsAutoClose() // self == elevator
|
||||
{
|
||||
self endon( "elevator_doors_closed" );
|
||||
self endon( "elevator_stop_autoclose" );
|
||||
|
||||
wait ( self.autoCloseTimeout );
|
||||
|
||||
self closeElevatorDoors( self.curFloor);
|
||||
}
|
||||
|
||||
handleUnreslovedCollision( hitEnt ) // self == mover, hitEnt == player (usually)
|
||||
{
|
||||
if ( !IsPlayer( hitEnt ) )
|
||||
{
|
||||
hitEnt DoDamage( 1000, hitEnt.origin, self, self, "MOD_CRUSH" );
|
||||
}
|
||||
}
|
||||
|
||||
elevatorClearPath( floorName ) // self == elevator
|
||||
{
|
||||
blocker = self.pathBlockers[ floorName ];
|
||||
if ( IsDefined( blocker ) )
|
||||
{
|
||||
blocker ConnectPaths();
|
||||
blocker Hide();
|
||||
blocker NotSolid();
|
||||
}
|
||||
}
|
||||
|
||||
elevatorBlockPath( floorName )
|
||||
{
|
||||
blocker = self.pathBlockers[ floorName ];
|
||||
if ( IsDefined( blocker ) )
|
||||
{
|
||||
blocker Show();
|
||||
blocker Solid();
|
||||
blocker DisconnectPaths();
|
||||
}
|
||||
}
|
||||
171
maps/mp/_empgrenade.gsc
Normal file
171
maps/mp/_empgrenade.gsc
Normal file
@@ -0,0 +1,171 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\killstreaks\_emp_common;
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
thread onPlayerConnect();
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
self thread monitorEMPGrenade();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
monitorEMPGrenade()
|
||||
{
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
self endon("faux_spawn");
|
||||
self.empEndTime = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
// self waittill( "emp_grenaded", attacker );
|
||||
self waittill( "emp_damage", attacker, duration );
|
||||
|
||||
if ( !IsAlive( self )
|
||||
|| IsDefined( self.usingRemote )
|
||||
|| ( self _hasPerk( "specialty_empimmune" ) ) //MW3 emp resistance perk
|
||||
|| !IsDefined( attacker )
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hurtVictim = true;
|
||||
hurtAttacker = false;
|
||||
|
||||
assert( IsDefined(self.pers["team"]) );
|
||||
|
||||
if ( level.teamBased
|
||||
&& attacker != self
|
||||
&& IsDefined(attacker.pers["team"])
|
||||
&& attacker.pers["team"] == self.pers["team"]
|
||||
)
|
||||
{
|
||||
if(level.friendlyfire == 0) // no FF
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(level.friendlyfire == 1) // FF
|
||||
{
|
||||
hurtattacker = false;
|
||||
hurtvictim = true;
|
||||
}
|
||||
else if(level.friendlyfire == 2) // reflect
|
||||
{
|
||||
hurtvictim = false;
|
||||
hurtattacker = true;
|
||||
}
|
||||
else if(level.friendlyfire == 3) // share
|
||||
{
|
||||
hurtattacker = true;
|
||||
hurtvictim = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker notify( "emp_hit" );
|
||||
|
||||
if( attacker != self )
|
||||
{
|
||||
attacker maps\mp\gametypes\_missions::processChallenge( "ch_onthepulse" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( hurtvictim && IsDefined(self) )
|
||||
{
|
||||
self thread applyEMP( duration );
|
||||
}
|
||||
if ( hurtattacker )
|
||||
{
|
||||
attacker thread applyEMP( duration );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyEMP( duration )
|
||||
{
|
||||
self notify( "applyEmp" );
|
||||
self endon( "applyEmp" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self endon( "death" );
|
||||
|
||||
wait .05;
|
||||
|
||||
self.empGrenaded = true;
|
||||
self shellshock( "flashbang_mp", 1 );
|
||||
self.empEndTime = GetTime() + (duration * 1000);
|
||||
|
||||
self applyPerPlayerEMPEffects_OnDetonate();
|
||||
self applyPerPlayerEMPEffects();
|
||||
self thread empRumbleLoop( .75 );
|
||||
self thread empGrenadeDeathWaiter();
|
||||
|
||||
wait ( duration );
|
||||
|
||||
self notify( "empGrenadeTimedOut" );
|
||||
self checkToTurnOffEmp();
|
||||
}
|
||||
|
||||
empGrenadeDeathWaiter()
|
||||
{
|
||||
self notify( "empGrenadeDeathWaiter" );
|
||||
self endon( "empGrenadeDeathWaiter" );
|
||||
|
||||
self endon( "empGrenadeTimedOut" );
|
||||
|
||||
self waittill( "death" );
|
||||
self checkToTurnOffEmp();
|
||||
}
|
||||
|
||||
checkToTurnOffEmp()
|
||||
{
|
||||
self.empGrenaded = false;
|
||||
|
||||
if ( !(self shouldPlayerBeAffectedByEMP()) )
|
||||
{
|
||||
self removePerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
empRumbleLoop( duration )
|
||||
{
|
||||
self endon("emp_rumble_loop");
|
||||
self notify("emp_rumble_loop");
|
||||
|
||||
goalTime = GetTime() + duration * 1000;
|
||||
|
||||
while ( GetTime() < goalTime )
|
||||
{
|
||||
self PlayRumbleOnEntity( "damage_heavy" );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
// 2013-06-30 wallace: is isEMPGrenaded being used any more? Not sure if we even need the timestamp
|
||||
isEMPGrenaded()
|
||||
{
|
||||
return isDefined( self.empEndTime ) && gettime() < self.empEndTime;
|
||||
}
|
||||
331
maps/mp/_entityheadicons.gsc
Normal file
331
maps/mp/_entityheadicons.gsc
Normal file
@@ -0,0 +1,331 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
init()
|
||||
{
|
||||
if (isdefined(level.initedEntityHeadIcons))
|
||||
return;
|
||||
level.initedEntityHeadIcons = true;
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
foreach ( teamName in level.teamNameList )
|
||||
{
|
||||
str_team_headicon = "entity_headicon_" + teamName;
|
||||
game[ str_team_headicon ] = maps\mp\gametypes\_teams::MT_getTeamHeadIcon( teamName );
|
||||
precacheShader( game[ str_team_headicon ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
game["entity_headicon_allies"] = maps\mp\gametypes\_teams::getTeamHeadIcon( "allies" );
|
||||
game["entity_headicon_axis"] = maps\mp\gametypes\_teams::getTeamHeadIcon( "axis" );
|
||||
|
||||
precacheShader( game["entity_headicon_allies"] );
|
||||
precacheShader( game["entity_headicon_axis"] );
|
||||
}
|
||||
}
|
||||
|
||||
// This can show to single players or to teams. Showing to a single player destroys instances of
|
||||
// the icon that are shown to their team. Showing to a team destroys instances of the icon that
|
||||
// are shown to players on that team
|
||||
setHeadIcon( showTo, icon, offset, width, height, archived, delay, constantSize, pinToScreenEdge, fadeOutPinnedIcon, is3D )
|
||||
{
|
||||
if ( IsGameParticipant( showTo ) && !IsPlayer( showTo ) )
|
||||
return; // Doesn't work for Agents, etc.
|
||||
|
||||
if ( !isDefined( self.entityHeadIcons ) )
|
||||
self.entityHeadIcons = [];
|
||||
|
||||
if( !IsDefined( archived ) )
|
||||
archived = true;
|
||||
|
||||
if( !IsDefined( delay ) )
|
||||
delay = 0.05;
|
||||
|
||||
if( !IsDefined( constantSize ) )
|
||||
constantSize = true;
|
||||
|
||||
if( !IsDefined( pinToScreenEdge ) )
|
||||
pinToScreenEdge = true;
|
||||
|
||||
if( !IsDefined( fadeOutPinnedIcon ) )
|
||||
fadeOutPinnedIcon = false;
|
||||
|
||||
if( !IsDefined( is3D ) )
|
||||
is3D = true;
|
||||
|
||||
if ( !isPlayer( showTo ) && showTo == "none" )
|
||||
{
|
||||
foreach ( key, headIcon in self.entityHeadIcons )
|
||||
{
|
||||
// TODO: remove and fix properly after ship
|
||||
if ( isDefined( headIcon ) )
|
||||
headIcon destroy();
|
||||
|
||||
self.entityHeadIcons[ key ] = undefined;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isPlayer( showTo ) )
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcons[ showTo.guid ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ showTo.guid ] destroy();
|
||||
self.entityHeadIcons[ showTo.guid ] = undefined;
|
||||
}
|
||||
|
||||
if ( icon == "" )
|
||||
return;
|
||||
|
||||
if ( isDefined(showTo.team) )
|
||||
{
|
||||
// remove from team or we'd have two icons
|
||||
if ( isDefined( self.entityHeadIcons[ showTo.team ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ showTo.team ] destroy();
|
||||
self.entityHeadIcons[ showTo.team ] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
headIcon = newClientHudElem( showTo );
|
||||
self.entityHeadIcons[ showTo.guid ] = headIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( showTo == "axis" || showTo == "allies" || isSubStr( showTo, "team_" ));
|
||||
assert( level.teamBased );
|
||||
|
||||
if ( isDefined( self.entityHeadIcons[ showTo ] ) )
|
||||
{
|
||||
self.entityHeadIcons[ showTo ] destroy();
|
||||
self.entityHeadIcons[ showTo ] = undefined;
|
||||
}
|
||||
|
||||
if ( icon == "" )
|
||||
return;
|
||||
|
||||
foreach ( key, hudIcon in self.entityHeadIcons )
|
||||
{
|
||||
if ( key == "axis" || key == "allies" )
|
||||
continue;
|
||||
|
||||
player = getPlayerForGuid( key );
|
||||
if ( player.team == showTo )
|
||||
{
|
||||
self.entityHeadIcons[ key ] destroy();
|
||||
self.entityHeadIcons[ key ] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
headIcon = newTeamHudElem( showTo );
|
||||
self.entityHeadIcons[ showTo ] = headIcon;
|
||||
}
|
||||
|
||||
if ( !isDefined( width ) || !isDefined( height ) )
|
||||
{
|
||||
width = 10;
|
||||
height = 10;
|
||||
}
|
||||
|
||||
headIcon.archived = archived;
|
||||
headIcon.x = self.origin[0] + offset[0];
|
||||
headIcon.y = self.origin[1] + offset[1];
|
||||
headIcon.z = self.origin[2] + offset[2];
|
||||
headIcon.alpha = 0.85;
|
||||
headIcon setShader( icon, width, height );
|
||||
headIcon setWaypoint( constantSize, pinToScreenEdge, fadeOutPinnedIcon, is3D );
|
||||
|
||||
headIcon thread keepPositioned( self, offset, delay );
|
||||
self thread destroyIconsOnDeath();
|
||||
if ( isPlayer( showTo ) )
|
||||
headIcon thread destroyOnOwnerDisconnect( showTo );
|
||||
if ( isPlayer( self ) )
|
||||
headIcon thread destroyOnOwnerDisconnect( self );
|
||||
|
||||
return headIcon;
|
||||
}
|
||||
|
||||
|
||||
destroyOnOwnerDisconnect( owner )
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
owner waittill ( "disconnect" );
|
||||
|
||||
self destroy();
|
||||
}
|
||||
|
||||
|
||||
destroyIconsOnDeath()
|
||||
{
|
||||
self notify ( "destroyIconsOnDeath" );
|
||||
self endon ( "destroyIconsOnDeath" );
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
if ( !isDefined( self.entityHeadIcons ) )
|
||||
return;
|
||||
|
||||
foreach ( key, headIcon in self.entityHeadIcons )
|
||||
{
|
||||
// TODO: remove and fix properly after ship
|
||||
if( !isDefined(headIcon) ) //needed for FFA host migration (when host has active head icons)
|
||||
continue;
|
||||
|
||||
headIcon destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
keepPositioned( owner, offset, delay )
|
||||
{
|
||||
self endon ( "death" );
|
||||
owner endon ( "death" );
|
||||
owner endon ( "disconnect" );
|
||||
|
||||
allowCodeLink = ( IsDefined(owner.classname) && !isOwnerCarePakage(owner) );
|
||||
|
||||
if( allowCodeLink )
|
||||
{
|
||||
self LinkWaypointToTargetWithOffset( owner, offset );
|
||||
}
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if( !IsDefined( owner ) )
|
||||
return;
|
||||
|
||||
if( !allowCodeLink )
|
||||
{
|
||||
pos = owner.origin;
|
||||
|
||||
self.x = pos[0] + offset[0];
|
||||
self.y = pos[1] + offset[1];
|
||||
self.z = pos[2] + offset[2];
|
||||
}
|
||||
|
||||
if ( delay > 0.05 )
|
||||
{
|
||||
self.alpha = 0.85;
|
||||
self FadeOverTime( delay );
|
||||
self.alpha = 0;
|
||||
}
|
||||
|
||||
wait delay;
|
||||
}
|
||||
}
|
||||
|
||||
isOwnerCarePakage( owner )
|
||||
{
|
||||
return ( IsDefined(owner.targetname) && ( owner.targetname == "care_package" ) );
|
||||
}
|
||||
|
||||
setTeamHeadIcon( team, offset ) // "allies", "axis", "all", "none"
|
||||
{
|
||||
if ( !level.teamBased )
|
||||
return;
|
||||
|
||||
if ( !isDefined( self.entityHeadIconTeam ) )
|
||||
{
|
||||
self.entityHeadIconTeam = "none";
|
||||
self.entityHeadIcon = undefined;
|
||||
}
|
||||
|
||||
shader = game["entity_headicon_" + team];
|
||||
|
||||
self.entityHeadIconTeam = team;
|
||||
|
||||
if ( isDefined( offset ) )
|
||||
self.entityHeadIconOffset = offset;
|
||||
else
|
||||
self.entityHeadIconOffset = (0,0,0);
|
||||
|
||||
self notify( "kill_entity_headicon_thread" );
|
||||
|
||||
if ( team == "none" )
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcon ) )
|
||||
self.entityHeadIcon destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
headIcon = newTeamHudElem( team );
|
||||
headIcon.archived = true;
|
||||
headIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
|
||||
headIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
|
||||
headIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
|
||||
headIcon.alpha = .8;
|
||||
headIcon setShader( shader, 10, 10 );
|
||||
headIcon setWaypoint( false, false, false, true );
|
||||
self.entityHeadIcon = headIcon;
|
||||
|
||||
self thread keepIconPositioned();
|
||||
self thread destroyHeadIconsOnDeath();
|
||||
}
|
||||
|
||||
setPlayerHeadIcon( player, offset ) // "allies", "axis", "all", "none"
|
||||
{
|
||||
if ( level.teamBased )
|
||||
return;
|
||||
|
||||
if ( !isDefined( self.entityHeadIconTeam ) )
|
||||
{
|
||||
self.entityHeadIconTeam = "none";
|
||||
self.entityHeadIcon = undefined;
|
||||
}
|
||||
|
||||
self notify( "kill_entity_headicon_thread" );
|
||||
|
||||
if ( !isDefined( player ) )
|
||||
{
|
||||
if ( isDefined( self.entityHeadIcon ) )
|
||||
self.entityHeadIcon destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
team = player.team;
|
||||
self.entityHeadIconTeam = team;
|
||||
|
||||
if ( isDefined( offset ) )
|
||||
self.entityHeadIconOffset = offset;
|
||||
else
|
||||
self.entityHeadIconOffset = (0,0,0);
|
||||
|
||||
shader = game["entity_headicon_" + team];
|
||||
|
||||
headIcon = newClientHudElem( player );
|
||||
headIcon.archived = true;
|
||||
headIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
|
||||
headIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
|
||||
headIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
|
||||
headIcon.alpha = .8;
|
||||
headIcon setShader( shader, 10, 10 );
|
||||
headIcon setWaypoint( false, false, false, true );
|
||||
self.entityHeadIcon = headIcon;
|
||||
|
||||
self thread keepIconPositioned();
|
||||
self thread destroyHeadIconsOnDeath();
|
||||
}
|
||||
|
||||
keepIconPositioned()
|
||||
{
|
||||
self.entityHeadIcon LinkWaypointToTargetWithOffset( self, self.entityHeadIconOffset );
|
||||
}
|
||||
|
||||
destroyHeadIconsOnDeath()
|
||||
{
|
||||
self endon( "kill_entity_headicon_thread" );
|
||||
self waittill ( "death" );
|
||||
|
||||
// TODO: remove and fix properly after ship
|
||||
if( !isDefined(self.entityHeadIcon) )
|
||||
return;
|
||||
|
||||
self.entityHeadIcon destroy();
|
||||
}
|
||||
|
||||
|
||||
984
maps/mp/_events.gsc
Normal file
984
maps/mp/_events.gsc
Normal file
@@ -0,0 +1,984 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
// load all of the scoring data for the current game mode
|
||||
game_type_col = [];
|
||||
game_type_col[ "dm" ] = 3; // free for all
|
||||
game_type_col[ "war" ] = 4; // team deathmatch
|
||||
game_type_col[ "sd" ] = 5; // search and destroy
|
||||
game_type_col[ "dom" ] = 6; // domination
|
||||
game_type_col[ "conf" ] = 7; // kill confirmed
|
||||
game_type_col[ "sr" ] = 8; // search and rescue
|
||||
game_type_col[ "bnty" ] = 9; // bounty
|
||||
game_type_col[ "grind" ] = 10; // grind
|
||||
game_type_col[ "blitz" ] = 11; // blitz
|
||||
game_type_col[ "cranked" ] = 12; // cranked
|
||||
game_type_col[ "infect" ] = 13; // infected
|
||||
game_type_col[ "sotf" ] = 14; // survival of the fittest
|
||||
game_type_col[ "sotf_ffa" ] = 15; // survival of the fittest FFA
|
||||
game_type_col[ "horde" ] = 16; // horde
|
||||
game_type_col[ "mugger" ] = 17; // mugger
|
||||
game_type_col[ "aliens" ] = 18; // aliens
|
||||
game_type_col[ "gun" ] = 19; // gun game
|
||||
game_type_col[ "grnd" ] = 20; // drop zone
|
||||
game_type_col[ "siege" ] = 21; // reinforce
|
||||
|
||||
game_type = level.gameType;
|
||||
if( !IsDefined( game_type ) )
|
||||
game_type = GetDvar( "g_gametype" );
|
||||
|
||||
row = 0;
|
||||
while( true )
|
||||
{
|
||||
value = TableLookupByRow( "mp/xp_event_table.csv", row, game_type_col[ game_type ] );
|
||||
if( !IsDefined( value ) || value == "" )
|
||||
break;
|
||||
|
||||
ref = TableLookupByRow( "mp/xp_event_table.csv", row, 0 );
|
||||
|
||||
if( ref == "win" || ref == "loss" || ref == "tie" )
|
||||
value = float( value );
|
||||
else
|
||||
value = int( value );
|
||||
|
||||
if( value != -1 )
|
||||
maps\mp\gametypes\_rank::registerScoreInfo( ref, value );
|
||||
|
||||
row++;
|
||||
}
|
||||
// end scoring data
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "damage", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "heavy_damage", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "damaged", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "kill", 1 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "killed", 0 );
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "healed", 0);
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "headshot", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "melee", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "backstab", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "longshot", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "pointblank", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "assistedsuicide", 0);
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "defender", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "avenger", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "execution", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "comeback", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "revenge", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "buzzkill", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "double", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "triple", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "multi", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "assist", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "firstBlood", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "capture", 1 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "assistedCapture", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "plant", 1 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "defuse", 1 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "vehicleDestroyed", 1);
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "3streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "4streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "5streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "6streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "7streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "8streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "9streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "10streak", 0 );
|
||||
maps\mp\killstreaks\_killstreaks::registerAdrenalineInfo( "regen", 0 );
|
||||
|
||||
precacheShader( "crosshair_red" );
|
||||
|
||||
level._effect["money"] = loadfx ("fx/props/cash_player_drop");
|
||||
|
||||
level.numKills = 0;
|
||||
|
||||
level thread onPlayerConnect();
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
player.killedPlayers = [];
|
||||
player.killedPlayersCurrent = [];
|
||||
player.ch_extremeCrueltyComplete = false; // setting a player var to throttle challenge completion rate
|
||||
player.ch_tangoDownComplete = false; // for iw7 we should handle this in the challenge table
|
||||
player.killedBy = [];
|
||||
player.lastKilledBy = undefined;
|
||||
player.greatestUniquePlayerKills = 0;
|
||||
|
||||
player.recentKillCount = 0;
|
||||
player.lastKillTime = 0;
|
||||
player.lastKillDogTime = 0;
|
||||
player.damagedPlayers = [];
|
||||
|
||||
player thread monitorCrateJacking();
|
||||
player thread monitorObjectives();
|
||||
player thread monitorHealed();
|
||||
}
|
||||
}
|
||||
|
||||
damagedPlayer( victim, damage, weapon )
|
||||
{
|
||||
if ( damage < 50 && damage > 10 )
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "damage" );
|
||||
else
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "heavy_damage" );
|
||||
}
|
||||
|
||||
//notifies killed player over necessary frames
|
||||
killedPlayerNotifySys( killId, victim, weapon, meansOfDeath )
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self notify ( "killedPlayerNotify" );
|
||||
self endon ( "killedPlayerNotify" );
|
||||
|
||||
if( !isDefined( self.killsInAFrameCount ) )
|
||||
self.killsInAFrameCount = 0;
|
||||
|
||||
self.killsInAFrameCount++;
|
||||
|
||||
wait ( 0.05 );
|
||||
|
||||
if ( self.killsInAFrameCount > 1 )
|
||||
self thread notifyKilledPlayer( killId, victim, weapon, meansOfDeath, self.killsInAFrameCount );
|
||||
else
|
||||
self notify( "got_a_kill", victim, weapon, meansOfDeath );
|
||||
|
||||
self.killsInAFrameCount = 0;
|
||||
}
|
||||
|
||||
//possible loss of proper killID etc here. Using last killed properties
|
||||
notifyKilledPlayer( killId, victim, weapon, meansOfDeath, numKills )
|
||||
{
|
||||
for( i = 0; i < numKills; i++ )
|
||||
{
|
||||
//used by intel
|
||||
self notify( "got_a_kill", victim, weapon, meansOfDeath );
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
killedPlayer( killId, victim, weapon, meansOfDeath )
|
||||
{
|
||||
victimGuid = victim.guid;
|
||||
myGuid = self.guid;
|
||||
curTime = getTime();
|
||||
|
||||
self thread killedPlayerNotifySys( killId, victim, weapon, meansOfDeath );
|
||||
self thread updateRecentKills( killId );
|
||||
self.lastKillTime = getTime();
|
||||
self.lastKilledPlayer = victim;
|
||||
|
||||
self.modifiers = [];
|
||||
|
||||
level.numKills++;
|
||||
|
||||
// a player is either damaged, or killed; never both
|
||||
self.damagedPlayers[victimGuid] = undefined;
|
||||
|
||||
if ( !isKillstreakWeapon( weapon ) && !self isJuggernaut() && !self _hasPerk( "specialty_explosivebullets" ) )
|
||||
{
|
||||
if ( weapon == "none" )
|
||||
return false;
|
||||
|
||||
// added a failsafe here because this could be the victim killing themselves with something like the dead man's hand deathstreak
|
||||
if ( victim.attackers.size == 1 && !IsDefined( victim.attackers[victim.guid] ) )
|
||||
{
|
||||
/#
|
||||
if ( !isDefined( victim.attackers[self.guid] ) )
|
||||
{
|
||||
println("Weapon: " + weapon );
|
||||
println("meansOfDeath: " + meansOfDeath );
|
||||
println("Attacker GUID: " + self.guid + " (name: " + self.name + ")" );
|
||||
|
||||
i = 0;
|
||||
foreach ( key,value in victim.attackers )
|
||||
{
|
||||
println( "victim.attackers " + i + " GUID: " + key + " (name: " + value.name + ")" );
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#/
|
||||
assertEx( isDefined( victim.attackers[self.guid] ), "See console log for details" );
|
||||
|
||||
weaponClass = getWeaponClass( weapon );
|
||||
|
||||
if( weaponClass == "weapon_sniper" &&
|
||||
meansOfDeath != "MOD_MELEE" &&
|
||||
getTime() == victim.attackerData[self.guid].firstTimeDamaged )
|
||||
{
|
||||
self.modifiers["oneshotkill"] = true;
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "one_shot_kill" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDefined( victim.throwingGrenade ) && victim.throwingGrenade == "frag_grenade_mp" )
|
||||
self.modifiers["cooking"] = true;
|
||||
|
||||
if ( isDefined(self.assistedSuicide) && self.assistedSuicide )
|
||||
self assistedSuicide( killId, weapon, meansOfDeath );
|
||||
|
||||
if ( level.numKills == 1 )
|
||||
self firstBlood( killId, weapon, meansOfDeath );
|
||||
|
||||
if ( self.pers["cur_death_streak"] > 3 )
|
||||
self comeBack( killId, weapon, meansOfDeath );
|
||||
|
||||
if ( meansOfDeath == "MOD_HEAD_SHOT" )
|
||||
{
|
||||
if ( isDefined( victim.lastStand ) )
|
||||
execution( killId, weapon, meansOfDeath );
|
||||
else
|
||||
headShot( killId, weapon, meansOfDeath );
|
||||
}
|
||||
|
||||
if ( isDefined(self.wasti) && self.wasti && getTime() - self.spawnTime <= 5000 )
|
||||
self.modifiers["jackintheboxkill"] = true;
|
||||
|
||||
if ( !isAlive( self ) && self.deathtime + 800 < getTime() )
|
||||
postDeathKill( killId );
|
||||
|
||||
if ( level.teamBased && curTime - victim.lastKillTime < 500 )
|
||||
{
|
||||
if ( victim.lastkilledplayer != self )
|
||||
self avengedPlayer( killId, weapon, meansOfDeath );
|
||||
}
|
||||
|
||||
if ( IsDefined( victim.lastKillDogTime ) && curTime - victim.lastKillDogTime < 2000 )
|
||||
{
|
||||
self avengedDog( killId, weapon, meansOfDeath );
|
||||
}
|
||||
|
||||
foreach ( guid, damageTime in victim.damagedPlayers )
|
||||
{
|
||||
if ( guid == self.guid )
|
||||
continue;
|
||||
|
||||
if ( level.teamBased && curTime - damageTime < 500 )
|
||||
self defendedPlayer( killId, weapon, meansOfDeath );
|
||||
}
|
||||
|
||||
if ( isDefined( victim.attackerPosition ) )
|
||||
attackerPosition = victim.attackerPosition;
|
||||
else
|
||||
attackerPosition = self.origin;
|
||||
|
||||
if ( isPointBlank ( self, weapon, meansOfDeath, attackerPosition, victim ) )
|
||||
self thread pointblank( killId, weapon, meansOfDeath );
|
||||
else if( isLongShot( self, weapon, meansOfDeath, attackerPosition, victim ) )
|
||||
self thread longshot( killId, weapon, meansOfDeath );
|
||||
|
||||
victim_pers_cur_kill_streak = victim.pers[ "cur_kill_streak" ];
|
||||
if ( victim_pers_cur_kill_streak > 0 && isDefined( victim.killstreaks[ victim_pers_cur_kill_streak + 1 ] ) )
|
||||
{
|
||||
// playercard splash for the killstreak stopped
|
||||
self buzzKill( killId, victim, weapon, meansOfDeath );
|
||||
}
|
||||
|
||||
self thread checkMatchDataKills( killId, victim, weapon, meansOfDeath);
|
||||
|
||||
}
|
||||
else if( weapon == "guard_dog_mp" )
|
||||
{
|
||||
if( !isAlive( self ) && self.deathtime < GetTime() )
|
||||
postDeathDogKill();
|
||||
}
|
||||
|
||||
if ( !isDefined( self.killedPlayers[victimGuid] ) )
|
||||
self.killedPlayers[victimGuid] = 0;
|
||||
|
||||
if ( !isDefined( self.killedPlayersCurrent[victimGuid] ) )
|
||||
self.killedPlayersCurrent[victimGuid] = 0;
|
||||
|
||||
if ( !isDefined( victim.killedBy[myGuid] ) )
|
||||
victim.killedBy[myGuid] = 0;
|
||||
|
||||
self.killedPlayers[victimGuid]++;
|
||||
|
||||
//this sets player stat for routine customer award
|
||||
if ( self.killedPlayers[victimGuid] > self.greatestUniquePlayerKills )
|
||||
self setPlayerStat( "killedsameplayer", self.killedPlayers[victimGuid] );
|
||||
|
||||
self.killedPlayersCurrent[victimGuid]++;
|
||||
victim.killedBy[myGuid]++;
|
||||
|
||||
victim.lastKilledBy = self;
|
||||
}
|
||||
|
||||
isLongShot( attacker, weapon, meansOfDeath, attackerPosition, victim )
|
||||
{
|
||||
if( isAlive( attacker ) &&
|
||||
!attacker isUsingRemote() &&
|
||||
( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" || meansOfDeath == "MOD_HEAD_SHOT" ) &&
|
||||
!isKillstreakWeapon( weapon ) && !isDefined( attacker.assistedSuicide ) )
|
||||
{
|
||||
// check depending on the weapon being used to kill
|
||||
thisWeaponClass = getWeaponClass( weapon );
|
||||
switch( thisWeaponClass )
|
||||
{
|
||||
case "weapon_pistol":
|
||||
weapDist = 800;
|
||||
break;
|
||||
case "weapon_smg":
|
||||
weapDist = 1200;
|
||||
break;
|
||||
case "weapon_dmr":
|
||||
case "weapon_assault":
|
||||
case "weapon_lmg":
|
||||
weapDist = 1500;
|
||||
break;
|
||||
case "weapon_sniper":
|
||||
weapDist = 2000;
|
||||
break;
|
||||
case "weapon_shotgun":
|
||||
weapDist = 500;
|
||||
break;
|
||||
case "weapon_projectile":
|
||||
default:
|
||||
weapDist = 1536; // the old number
|
||||
break;
|
||||
}
|
||||
|
||||
weapDistSq = weapDist * weapDist;
|
||||
if( DistanceSquared( attackerPosition, victim.origin ) > weapDistSq )
|
||||
{
|
||||
if( attacker IsItemUnlocked( "specialty_holdbreath" ) && attacker _hasPerk( "specialty_holdbreath" ) )
|
||||
attacker maps\mp\gametypes\_missions::processChallenge( "ch_longdistance" );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isPointBlank( attacker, weapon, meansOfDeath, attackerPosition, victim )
|
||||
{
|
||||
if( isAlive( attacker ) &&
|
||||
!attacker isUsingRemote() &&
|
||||
( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" || meansOfDeath == "MOD_HEAD_SHOT" ) &&
|
||||
!isKillstreakWeapon( weapon ) && !isDefined( attacker.assistedSuicide ) )
|
||||
{
|
||||
// point blank is the same for all classes of weapons, about 8'
|
||||
weapDistSq = 96 * 96;
|
||||
if( DistanceSquared( attackerPosition, victim.origin ) < weapDistSq )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
checkMatchDataKills( killId, victim, weapon, meansOfDeath )
|
||||
{
|
||||
weaponClass = getWeaponClass( weapon );
|
||||
alreadyUsed = false;
|
||||
|
||||
self thread camperCheck();
|
||||
|
||||
if ( isDefined( self.lastKilledBy ) && self.lastKilledBy == victim )
|
||||
{
|
||||
self.lastKilledBy = undefined;
|
||||
self revenge( killId );
|
||||
}
|
||||
|
||||
if ( victim.iDFlags & level.iDFLAGS_PENETRATION )
|
||||
self incPlayerStat( "bulletpenkills", 1 );
|
||||
|
||||
self_pers_rank = self.pers["rank"];
|
||||
victim_pers_rank = victim.pers["rank"];
|
||||
if ( self_pers_rank < victim_pers_rank )
|
||||
self incPlayerStat( "higherrankkills", 1 );
|
||||
|
||||
if ( self_pers_rank > victim_pers_rank )
|
||||
self incPlayerStat( "lowerrankkills", 1 );
|
||||
|
||||
if ( isDefined( self.inFinalStand ) && self.inFinalStand )
|
||||
self incPlayerStat( "laststandkills", 1 );
|
||||
|
||||
if ( isDefined( victim.inFinalStand ) && victim.inFinalStand )
|
||||
self incPlayerStat( "laststanderkills", 1 );
|
||||
|
||||
if ( self getCurrentWeapon() != self.primaryWeapon && self getCurrentWeapon() != self.secondaryWeapon )
|
||||
self incPlayerStat( "otherweaponkills", 1 );
|
||||
|
||||
timeAlive = getTime() - victim.spawnTime ;
|
||||
|
||||
if( !matchMakingGame() )
|
||||
victim setPlayerStatIfLower( "shortestlife", timeAlive );
|
||||
|
||||
victim setPlayerStatIfGreater( "longestlife", timeAlive );
|
||||
|
||||
if( meansOfDeath != "MOD_MELEE" )
|
||||
{
|
||||
switch( weaponClass )
|
||||
{
|
||||
case "weapon_pistol":
|
||||
case "weapon_smg":
|
||||
case "weapon_assault":
|
||||
case "weapon_projectile":
|
||||
case "weapon_dmr":
|
||||
case "weapon_sniper":
|
||||
case "weapon_shotgun":
|
||||
case "weapon_lmg":
|
||||
self checkMatchDataWeaponKills( victim, weapon, meansOfDeath, weaponClass );
|
||||
break;
|
||||
case "weapon_grenade":
|
||||
case "weapon_explosive":
|
||||
self checkMatchDataEquipmentKills( victim, weapon, meansOfDeath );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to make sure these only apply to kills of an enemy, not friendlies or yourself
|
||||
checkMatchDataWeaponKills( victim, weapon, meansOfDeath, weaponType )
|
||||
{
|
||||
attacker = self;
|
||||
kill_ref = undefined;
|
||||
headshot_ref = undefined;
|
||||
death_ref = undefined;
|
||||
|
||||
switch( weaponType )
|
||||
{
|
||||
case "weapon_pistol":
|
||||
kill_ref = "pistolkills";
|
||||
headshot_ref = "pistolheadshots";
|
||||
break;
|
||||
case "weapon_smg":
|
||||
kill_ref = "smgkills";
|
||||
headshot_ref = "smgheadshots";
|
||||
break;
|
||||
case "weapon_assault":
|
||||
kill_ref = "arkills";
|
||||
headshot_ref = "arheadshots";
|
||||
break;
|
||||
case "weapon_projectile":
|
||||
if ( weaponClass( weapon ) == "rocketlauncher" )
|
||||
kill_ref = "rocketkills";
|
||||
break;
|
||||
case "weapon_dmr":
|
||||
kill_ref = "dmrkills";
|
||||
headshot_ref = "dmrheadshots";
|
||||
break;
|
||||
case "weapon_sniper":
|
||||
kill_ref = "sniperkills";
|
||||
headshot_ref = "sniperheadshots";
|
||||
break;
|
||||
case "weapon_shotgun":
|
||||
kill_ref = "shotgunkills";
|
||||
headshot_ref = "shotgunheadshots";
|
||||
death_ref = "shotgundeaths";
|
||||
break;
|
||||
case "weapon_lmg":
|
||||
kill_ref = "lmgkills";
|
||||
headshot_ref = "lmgheadshots";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( isDefined ( kill_ref ) )
|
||||
attacker incPlayerStat( kill_ref, 1 );
|
||||
|
||||
if ( isDefined ( headshot_ref ) && meansOfDeath == "MOD_HEAD_SHOT" )
|
||||
attacker incPlayerStat( headshot_ref, 1 );
|
||||
|
||||
if ( isDefined ( death_ref ) && !matchMakingGame() )
|
||||
victim incPlayerStat( death_ref, 1 );
|
||||
|
||||
if ( attacker isPlayerAds() )
|
||||
{
|
||||
attacker incPlayerStat( "adskills", 1 );
|
||||
|
||||
isThermal = IsSubStr( weapon, "thermal" );
|
||||
|
||||
// If weapon sniper, or acog or scope. Scope covers weapon_dmr with default scope
|
||||
if ( isThermal || IsSubStr( weapon, "acog" ) || IsSubStr( weapon, "scope" ) )
|
||||
attacker incPlayerStat( "scopedkills", 1 );
|
||||
|
||||
if ( isThermal )
|
||||
attacker incPlayerStat( "thermalkills", 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker incPlayerStat( "hipfirekills", 1 );
|
||||
}
|
||||
}
|
||||
|
||||
// Need to make sure these only apply to kills of an enemy, not friendlies or yourself
|
||||
checkMatchDataEquipmentKills( victim, weapon, meansOfDeath )
|
||||
{
|
||||
attacker = self;
|
||||
|
||||
// equipment kills
|
||||
switch( weapon )
|
||||
{
|
||||
case "frag_grenade_mp":
|
||||
attacker incPlayerStat( "fragkills", 1 );
|
||||
attacker incPlayerStat( "grenadekills", 1 );
|
||||
isEquipment = true;
|
||||
break;
|
||||
case "c4_mp":
|
||||
attacker incPlayerStat( "c4kills", 1 );
|
||||
isEquipment = true;
|
||||
break;
|
||||
case "semtex_mp":
|
||||
attacker incPlayerStat( "semtexkills", 1 );
|
||||
attacker incPlayerStat( "grenadekills", 1 );
|
||||
isEquipment = true;
|
||||
break;
|
||||
case "claymore_mp":
|
||||
attacker incPlayerStat( "claymorekills", 1 );
|
||||
isEquipment = true;
|
||||
break;
|
||||
case "throwingknife_mp":
|
||||
attacker incPlayerStat( "throwingknifekills", 1 );
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "knifethrow" );
|
||||
isEquipment = true;
|
||||
break;
|
||||
default:
|
||||
isEquipment = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( isEquipment )
|
||||
attacker incPlayerStat( "equipmentkills", 1 );
|
||||
}
|
||||
|
||||
camperCheck()
|
||||
{
|
||||
self.lastKillWasCamping = false;
|
||||
if ( !isDefined ( self.lastKillLocation ) )
|
||||
{
|
||||
self.lastKillLocation = self.origin;
|
||||
self.lastCampKillTime = getTime();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Distance( self.lastKillLocation, self.origin ) < 512 && getTime() - self.lastCampKillTime > 5000 )
|
||||
{
|
||||
self incPlayerStat( "mostcamperkills", 1 );
|
||||
self.lastKillWasCamping = true;
|
||||
}
|
||||
|
||||
self.lastKillLocation = self.origin;
|
||||
self.lastCampKillTime = getTime();
|
||||
}
|
||||
|
||||
consolation( killId )
|
||||
{
|
||||
/*
|
||||
value = int( maps\mp\gametypes\_rank::getScoreInfoValue( "kill" ) * 0.25 );
|
||||
|
||||
self thread maps\mp\gametypes\_hud_message::SplashNotifyDelayed( "consolation", value );
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "consolation", value );
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
proximityAssist( killId )
|
||||
{
|
||||
self.modifiers["proximityAssist"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "proximityassist" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "proximityassist" );
|
||||
//self thread maps\mp\_matchdata::logKillEvent( killId, "proximityAssist" );
|
||||
}
|
||||
|
||||
|
||||
proximityKill( killId )
|
||||
{
|
||||
self.modifiers["proximityKill"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "proximitykill" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "proximitykill" );
|
||||
//self thread maps\mp\_matchdata::logKillEvent( killId, "proximityKill" );
|
||||
}
|
||||
|
||||
|
||||
longshot( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["longshot"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "longshot" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "longshot", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "longshot" );
|
||||
self incPlayerStat( "longshots", 1 );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "longshot" );
|
||||
}
|
||||
|
||||
pointblank( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["pointblank"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "pointblank" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "pointblank", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "pointblank" );
|
||||
// self incPlayerStat( "pointblank", 1 );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "pointblank" );
|
||||
}
|
||||
|
||||
|
||||
execution( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["execution"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "execution" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "execution", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "execution" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "execution" );
|
||||
}
|
||||
|
||||
|
||||
headShot( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["headshot"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "headshot" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "headshot", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "headshot" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "headshot" );
|
||||
}
|
||||
|
||||
|
||||
avengedPlayer( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["avenger"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "avenger" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "avenger", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "avenger" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "avenger" );
|
||||
|
||||
self incPlayerStat( "avengekills", 1 );
|
||||
}
|
||||
|
||||
avengedDog( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "dog_avenger" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "dog_avenger", undefined, weapon, meansOfDeath );
|
||||
}
|
||||
|
||||
assistedSuicide( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["assistedsuicide"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "assistedsuicide" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "assistedsuicide", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "assistedsuicide" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "assistedsuicide" );
|
||||
}
|
||||
|
||||
defendedPlayer( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["defender"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "defender" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "defender", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "defender" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "defender" );
|
||||
|
||||
self incPlayerStat( "rescues", 1 );
|
||||
}
|
||||
|
||||
|
||||
postDeathKill( killId )
|
||||
{
|
||||
self.modifiers[ "posthumous" ] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "posthumous" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "posthumous" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "posthumous" );
|
||||
}
|
||||
|
||||
postDeathDogKill()
|
||||
{
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "martyrdog" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "martyrdog" );
|
||||
}
|
||||
|
||||
backStab( killId )
|
||||
{
|
||||
self iPrintLnBold( "backstab" );
|
||||
}
|
||||
|
||||
|
||||
revenge( killId )
|
||||
{
|
||||
self.modifiers["revenge"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "revenge" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "revenge" );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "revenge" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "revenge" );
|
||||
|
||||
self incPlayerStat( "revengekills", 1 );
|
||||
}
|
||||
|
||||
|
||||
multiKill( killId, killCount )
|
||||
{
|
||||
assert( killCount > 1 );
|
||||
|
||||
if ( killCount == 2 )
|
||||
{
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "double" );
|
||||
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "double" );
|
||||
}
|
||||
else if ( killCount == 3 )
|
||||
{
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "triple" );
|
||||
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "triple" );
|
||||
thread teamPlayerCardSplash( "callout_3xkill", self );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "multi" );
|
||||
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "multi" );
|
||||
thread teamPlayerCardSplash( "callout_3xpluskill", self );
|
||||
}
|
||||
|
||||
self thread maps\mp\_matchdata::logMultiKill( killId, killCount );
|
||||
|
||||
// update player multikill record
|
||||
self setPlayerStatIfGreater( "multikill", killCount );
|
||||
|
||||
// update player multikill count
|
||||
self incPlayerStat( "mostmultikills", 1 );
|
||||
}
|
||||
|
||||
|
||||
firstBlood( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["firstblood"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "firstblood" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "firstblood", undefined, weapon, meansOfDeath );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "firstblood" );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "firstBlood" );
|
||||
|
||||
thread teamPlayerCardSplash( "callout_firstblood", self );
|
||||
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_bornready" );
|
||||
}
|
||||
|
||||
|
||||
winningShot( killId )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
buzzKill( killId, victim, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["buzzkill"] = victim.pers["cur_kill_streak"];
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "buzzkill" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "buzzkill", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "buzzkill" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "buzzkill" );
|
||||
}
|
||||
|
||||
|
||||
comeBack( killId, weapon, meansOfDeath )
|
||||
{
|
||||
self.modifiers["comeback"] = true;
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "comeback" );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "comeback", undefined, weapon, meansOfDeath );
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "comeback" );
|
||||
self thread maps\mp\_matchdata::logKillEvent( killId, "comeback" );
|
||||
|
||||
self incPlayerStat( "comebacks", 1 );
|
||||
}
|
||||
|
||||
|
||||
disconnected()
|
||||
{
|
||||
myGuid = self.guid;
|
||||
|
||||
for ( entry = 0; entry < level.players.size; entry++ )
|
||||
{
|
||||
if ( isDefined( level.players[entry].killedPlayers[myGuid] ) )
|
||||
level.players[entry].killedPlayers[myGuid] = undefined;
|
||||
|
||||
if ( isDefined( level.players[entry].killedPlayersCurrent[myGuid] ) )
|
||||
level.players[entry].killedPlayersCurrent[myGuid] = undefined;
|
||||
|
||||
if ( isDefined( level.players[entry].killedBy[myGuid] ) )
|
||||
level.players[entry].killedBy[myGuid] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
monitorHealed()
|
||||
{
|
||||
level endon( "end_game" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
self waittill( "healed");
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "healed" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
updateRecentKills( killId )
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self notify ( "updateRecentKills" );
|
||||
self endon ( "updateRecentKills" );
|
||||
|
||||
self.recentKillCount++;
|
||||
|
||||
wait ( 1.0 );
|
||||
|
||||
if ( self.recentKillCount > 1 )
|
||||
self multiKill( killId, self.recentKillCount );
|
||||
|
||||
self.recentKillCount = 0;
|
||||
}
|
||||
|
||||
monitorCrateJacking()
|
||||
{
|
||||
level endon( "end_game" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self waittill( "hijacker", crateType, owner );
|
||||
|
||||
self thread maps\mp\gametypes\_rank::xpEventPopup( "hijacker" );
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "hijacker" );
|
||||
|
||||
splashName = "hijacked_airdrop";
|
||||
challengeName = "ch_hijacker";
|
||||
|
||||
switch( crateType )
|
||||
{
|
||||
case "sentry":
|
||||
splashName = "hijacked_sentry";
|
||||
break;
|
||||
case "juggernaut":
|
||||
splashName = "hijacked_juggernaut";
|
||||
break;
|
||||
case "maniac":
|
||||
splashname = "hijacked_maniac";
|
||||
break;
|
||||
case "juggernaut_swamp_slasher":
|
||||
splashname = "hijacked_juggernaut_swamp_slasher";
|
||||
break;
|
||||
case "juggernaut_predator":
|
||||
splashname = "hijacked_juggernaut_predator";
|
||||
break;
|
||||
case "juggernaut_death_mariachi":
|
||||
splashname = "hijacked_juggernaut_death_mariachi";
|
||||
break;
|
||||
case "remote_tank":
|
||||
splashName = "hijacked_remote_tank";
|
||||
break;
|
||||
case "mega":
|
||||
case "emergency_airdrop":
|
||||
splashName = "hijacked_emergency_airdrop";
|
||||
challengeName = "ch_newjack";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( IsDefined( owner ) )
|
||||
owner maps\mp\gametypes\_hud_message::playerCardSplashNotify( splashName, self );
|
||||
self notify( "process", challengeName );
|
||||
}
|
||||
}
|
||||
|
||||
monitorObjectives()
|
||||
{
|
||||
level endon( "end_game" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
self waittill( "objective", objType );
|
||||
|
||||
switch( objType )
|
||||
{
|
||||
case "captured":
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "capture" );
|
||||
if ( isDefined( self.lastStand ) && self.lastStand )
|
||||
{
|
||||
self thread maps\mp\gametypes\_hud_message::SplashNotifyDelayed( "heroic", maps\mp\gametypes\_rank::getScoreInfoValue( "reviver" ) );
|
||||
self thread maps\mp\gametypes\_rank::giveRankXP( "reviver" );
|
||||
}
|
||||
break;
|
||||
case "plant":
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "plant" );
|
||||
break;
|
||||
case "defuse":
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "defuse" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this gets called directly from the game mode gscs instead of leaning on the above notify system
|
||||
giveObjectivePointStreaks()
|
||||
{
|
||||
halfTick_activate = true; // turn this to true in ffotd / patch to activate the objective point system
|
||||
if ( halfTick_activate )
|
||||
{
|
||||
// DM - 5/2/14 - make sure this isn't getting applied to squadmates
|
||||
if( !IsAgent( self ) )
|
||||
{
|
||||
self.pers["objectivePointStreak"]++;
|
||||
// for every two objective points earned, give 1 killstreak tick
|
||||
should_give_point = ( self.pers["objectivePointStreak"] % 2 == 0 );
|
||||
|
||||
if ( should_give_point )
|
||||
{
|
||||
self maps\mp\killstreaks\_killstreaks::giveAdrenaline( "kill" );
|
||||
}
|
||||
// if this results false, set a half killstreak tick in lua
|
||||
// NOTE: this gets overridden in KillstreakHud.lua if the player is using specialist and is maxed to avoid showing the half tick
|
||||
self SetClientOmnvar( "ui_half_tick", !should_give_point );
|
||||
}
|
||||
}
|
||||
}
|
||||
176
maps/mp/_flashgrenades.gsc
Normal file
176
maps/mp/_flashgrenades.gsc
Normal file
@@ -0,0 +1,176 @@
|
||||
#include maps\mp\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
startMonitoringFlash()
|
||||
{
|
||||
self thread monitorFlash();
|
||||
}
|
||||
|
||||
|
||||
stopMonitoringFlash(disconnected)
|
||||
{
|
||||
self notify("stop_monitoring_flash");
|
||||
}
|
||||
|
||||
|
||||
flashRumbleLoop( duration )
|
||||
{
|
||||
self endon("stop_monitoring_flash");
|
||||
|
||||
self endon("flash_rumble_loop");
|
||||
self notify("flash_rumble_loop");
|
||||
|
||||
goalTime = getTime() + duration * 1000;
|
||||
|
||||
while ( getTime() < goalTime )
|
||||
{
|
||||
self PlayRumbleOnEntity( "damage_heavy" );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
monitorFlash()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
self notify("monitorFlash");
|
||||
self endon("monitorFlash");
|
||||
|
||||
self.flashEndTime = 0;
|
||||
|
||||
durationMultiplier = 1; // how long the flash lasts
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill( "flashbang", origin, percent_distance, percent_angle, attacker, teamName, extraDuration );
|
||||
|
||||
if ( !IsAlive( self ) )
|
||||
break;
|
||||
|
||||
if ( IsDefined( self.usingRemote ) )
|
||||
continue;
|
||||
|
||||
// an agent killstreak should not be flashed by its owner
|
||||
if( IsDefined(self.owner) && IsDefined(attacker) && (attacker == self.owner) )
|
||||
continue;
|
||||
|
||||
if( !IsDefined( extraDuration ) )
|
||||
extraDuration = 0;
|
||||
|
||||
hurtattacker = false;
|
||||
hurtvictim = true;
|
||||
|
||||
// with this being the 9-bang we shouldn't consider angle as a variable because of the multiple blasts
|
||||
//if ( percent_angle < 0.25 )
|
||||
// percent_angle = 0.25;
|
||||
//else if ( percent_angle > 0.8 )
|
||||
percent_angle = 1;
|
||||
|
||||
duration = percent_distance * percent_angle * durationMultiplier;
|
||||
duration += extraDuration;
|
||||
|
||||
// MW3 stun resistance perk
|
||||
duration = maps\mp\perks\_perkfunctions::applyStunResistence( duration );
|
||||
|
||||
if ( duration < 0.25 )
|
||||
continue;
|
||||
|
||||
rumbleduration = undefined;
|
||||
if ( duration > 2 )
|
||||
rumbleduration = 0.75;
|
||||
else
|
||||
rumbleduration = 0.25;
|
||||
|
||||
assert(IsDefined(self.team));
|
||||
if (level.teamBased && IsDefined(attacker) && IsDefined(attacker.team) && attacker.team == self.team && attacker != self)
|
||||
{
|
||||
if(level.friendlyfire == 0) // no FF
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(level.friendlyfire == 1) // FF
|
||||
{
|
||||
}
|
||||
else if(level.friendlyfire == 2) // reflect
|
||||
{
|
||||
duration = duration * .5;
|
||||
rumbleduration = rumbleduration * .5;
|
||||
hurtvictim = false;
|
||||
hurtattacker = true;
|
||||
}
|
||||
else if(level.friendlyfire == 3) // share
|
||||
{
|
||||
duration = duration * .5;
|
||||
rumbleduration = rumbleduration * .5;
|
||||
hurtattacker = true;
|
||||
}
|
||||
}
|
||||
else if( IsDefined(attacker) )
|
||||
{
|
||||
attacker notify( "flash_hit" );
|
||||
if( attacker != self )
|
||||
attacker maps\mp\gametypes\_missions::processChallenge( "ch_indecentexposure" );
|
||||
}
|
||||
|
||||
if ( hurtvictim && IsDefined(self) )
|
||||
{
|
||||
self thread applyFlash(duration, rumbleduration);
|
||||
|
||||
if ( IsDefined(attacker) && attacker != self )
|
||||
{
|
||||
attacker thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "flash" );
|
||||
|
||||
// need this here because there are instances where you can not take damage from a flashbang but still get flashed
|
||||
// since you don't take damage the recon perk won't paint you when it should
|
||||
// show the victim on the minimap for N seconds
|
||||
victim = self;
|
||||
if( IsPlayer( attacker ) && attacker IsItemUnlocked( "specialty_paint" ) && attacker _hasPerk( "specialty_paint" ) )
|
||||
{
|
||||
if( !victim maps\mp\perks\_perkfunctions::isPainted() )
|
||||
attacker maps\mp\gametypes\_missions::processChallenge( "ch_paint_pro" );
|
||||
|
||||
victim thread maps\mp\perks\_perkfunctions::setPainted( attacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( hurtattacker && IsDefined(attacker) )
|
||||
{
|
||||
attacker thread applyFlash(duration, rumbleduration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyFlash(duration, rumbleduration)
|
||||
{
|
||||
// wait for the highest flash duration this frame,
|
||||
// and apply it in the following frame
|
||||
|
||||
if (!IsDefined(self.flashDuration) || duration > self.flashDuration)
|
||||
self.flashDuration = duration;
|
||||
if (!IsDefined(self.flashRumbleDuration) || rumbleduration > self.flashRumbleDuration)
|
||||
self.flashRumbleDuration = rumbleduration;
|
||||
|
||||
wait .05;
|
||||
|
||||
if (IsDefined(self.flashDuration)) {
|
||||
self shellshock( "flashbang_mp", self.flashDuration ); // TODO: avoid shellshock overlap
|
||||
self.flashEndTime = getTime() + (self.flashDuration * 1000);
|
||||
}
|
||||
if (IsDefined(self.flashRumbleDuration)) {
|
||||
self thread flashRumbleLoop( self.flashRumbleDuration ); //TODO: Non-hacky rumble.
|
||||
}
|
||||
|
||||
self.flashDuration = undefined;
|
||||
self.flashRumbleDuration = undefined;
|
||||
}
|
||||
|
||||
|
||||
isFlashbanged()
|
||||
{
|
||||
return IsDefined( self.flashEndTime ) && gettime() < self.flashEndTime;
|
||||
}
|
||||
272
maps/mp/_fx.gsc
Normal file
272
maps/mp/_fx.gsc
Normal file
@@ -0,0 +1,272 @@
|
||||
#include common_scripts\utility;
|
||||
#include common_scripts\_fx;
|
||||
#include common_scripts\_createfx;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\_createfx;
|
||||
|
||||
/*
|
||||
****************************************************************************************************************
|
||||
OneShotfx: Fires an effect once.
|
||||
maps\mp\_fx::OneShotfx( effectname, (x y z), predelay);
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::OneShotfx(level.medFire, // Medium fire effect
|
||||
(-701, -18361, 148), // Origin
|
||||
5); // Wait 5 seconds before doing effect
|
||||
****************************************************************************************************************
|
||||
|
||||
|
||||
****************************************************************************************************************
|
||||
Loopfx: Loops an effect with a waittime.
|
||||
maps\mp\_fx::loopfx( effectname, (x y z), delay_between_shots);
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::loopfx(level.medFire, // Medium fire effect
|
||||
(-701, -18361, 148), // Origin
|
||||
0.3); // Wait 0.3 seconds between shots
|
||||
****************************************************************************************************************
|
||||
|
||||
|
||||
****************************************************************************************************************
|
||||
GunFireLoopfx: Simulates bursts of fire.
|
||||
maps\mp\_fx::gunfireloopfx(fxId, fxPos, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax)
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::gunfireloopfx (level.medFire, // Medium fire effect
|
||||
(-701, -18361, 148), // Origin
|
||||
10, 15, // 10 to 15 shots
|
||||
0.1, 0.3, // 0.1 to 0.3 seconds between shots
|
||||
2.5, 9); // 2.5 to 9 seconds between sets of shots.
|
||||
****************************************************************************************************************
|
||||
|
||||
****************************************************************************************************************
|
||||
GrenadeExplosionfx: Creates a grenade explosion with view jitter.
|
||||
maps\mp\_fx::GrenadeExplosionfx((x y z));
|
||||
|
||||
Example:
|
||||
maps\mp\_fx::GrenadeExplosionfx( (-701, -18361, 148) ); // origin
|
||||
****************************************************************************************************************
|
||||
*/
|
||||
|
||||
script_print_fx()
|
||||
{
|
||||
if ( ( !isdefined( self.script_fxid ) ) || ( !isdefined( self.script_fxcommand ) ) || ( !isdefined( self.script_delay ) ) )
|
||||
{
|
||||
println( "Effect at origin ", self.origin, " doesn't have script_fxid/script_fxcommand/script_delay" );
|
||||
self delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isdefined( self.target ) )
|
||||
org = getent( self.target ).origin;
|
||||
else
|
||||
org = "undefined";
|
||||
|
||||
// println ("^a Command:", self.script_fxcommand, " Effect:", self.script_fxID, " Delay:", self.script_delay, " ", self.origin);
|
||||
if ( self.script_fxcommand == "OneShotfx" )
|
||||
println( "maps\mp\_fx::OneShotfx(\"" + self.script_fxid + "\", " + self.origin + ", " + self.script_delay + ", " + org + ");" );
|
||||
|
||||
if ( self.script_fxcommand == "loopfx" )
|
||||
println( "maps\mp\_fx::LoopFx(\"" + self.script_fxid + "\", " + self.origin + ", " + self.script_delay + ", " + org + ");" );
|
||||
|
||||
if ( self.script_fxcommand == "loopsound" )
|
||||
println( "maps\mp\_fx::LoopSound(\"" + self.script_fxid + "\", " + self.origin + ", " + self.script_delay + ", " + org + ");" );
|
||||
}
|
||||
|
||||
|
||||
GrenadeExplosionfx( pos )
|
||||
{
|
||||
playfx( level._effect[ "mechanical explosion" ], pos );
|
||||
earthquake( 0.15, 0.5, pos, 250 );
|
||||
// TODO: Add explosion effect and view jitter
|
||||
// println("The script command grenadeExplosionEffect has been removed. maps\\mp\\_fx::GrenadeExplosionfx must be set up to make an effect and jitter the view.");
|
||||
}
|
||||
|
||||
|
||||
soundfx( fxId, fxPos, endonNotify )
|
||||
{
|
||||
org = spawn( "script_origin", ( 0, 0, 0 ) );
|
||||
org.origin = fxPos;
|
||||
org playloopsound( fxId );
|
||||
if ( isdefined( endonNotify ) )
|
||||
org thread soundfxDelete( endonNotify );
|
||||
|
||||
/*
|
||||
ent = level thread createfx_showOrigin ( fxId, fxPos, undefined, undefined, "soundfx" );
|
||||
ent.delay = 0;
|
||||
ent endon ("effect deleted");
|
||||
ent.soundfx = org;
|
||||
*/
|
||||
}
|
||||
|
||||
soundfxDelete( endonNotify )
|
||||
{
|
||||
level waittill( endonNotify );
|
||||
self delete();
|
||||
}
|
||||
|
||||
func_glass_handler()
|
||||
{
|
||||
// FuncGlassIndex [ Index ]
|
||||
// FuncGlassDecals [ Glass Index ] [ Attached SINGLE Decal ]
|
||||
// Funcglass.Targetname == Decal.Script_noteworthy
|
||||
funcglass_indexies = [];
|
||||
funcglass_decals = [];
|
||||
temp_decals = GetEntArray("vfx_custom_glass","targetname");
|
||||
foreach ( decal in temp_decals)
|
||||
{
|
||||
if (IsDefined(decal.script_noteworthy))
|
||||
{
|
||||
attached_glass = GetGlass(decal.script_noteworthy);
|
||||
if (IsDefined (attached_glass))
|
||||
{
|
||||
funcglass_decals[attached_glass] = decal;
|
||||
funcglass_indexies[funcglass_indexies.size] = attached_glass;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcglass_alive_count = funcglass_indexies.size;
|
||||
funcglass_count = funcglass_indexies.size;
|
||||
|
||||
// Run up to 5 times on each frame, avoid too much going on
|
||||
max_iterations = 5;
|
||||
current_index = 0;
|
||||
while(funcglass_alive_count!=0)
|
||||
{
|
||||
max_index = current_index+max_iterations-1;
|
||||
if (max_index > funcglass_count)
|
||||
max_index = funcglass_count;
|
||||
if (current_index ==funcglass_count)
|
||||
current_index = 0;
|
||||
for (; current_index < max_index ; current_index++ )
|
||||
{
|
||||
glass_index = funcglass_indexies[current_index];
|
||||
decal = funcglass_decals[glass_index];
|
||||
|
||||
// Decal is defined, glass isn't destroyed yet (here)
|
||||
if (IsDefined(decal))
|
||||
{
|
||||
// Glass is destroyed in the world
|
||||
if (IsGlassDestroyed(glass_index))
|
||||
{
|
||||
decal delete();
|
||||
funcglass_alive_count--;
|
||||
funcglass_decals[glass_index] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
wait(.05);
|
||||
}
|
||||
}
|
||||
|
||||
/*rainfx( fxId, fxId2, fxPos )
|
||||
{
|
||||
org = spawn( "script_origin", ( 0, 0, 0 ) );
|
||||
org.origin = fxPos;
|
||||
org thread rainLoop( fxId, fxId2 );
|
||||
|
||||
//ent = level thread createfx_showOrigin( fxId, fxPos, undefined, undefined, "rainfx", undefined, fxId2 );
|
||||
//ent.delay = 0;
|
||||
//ent endon ("effect deleted");
|
||||
//ent.soundfx = org;
|
||||
}
|
||||
|
||||
rainLoop( hardRain, lightRain )
|
||||
{
|
||||
// org playloopsound (fxId);
|
||||
self endon( "death" );
|
||||
blend = spawn( "sound_blend", ( 0.0, 0.0, 0.0 ) );
|
||||
blend.origin = self.origin;
|
||||
self thread blendDelete( blend );
|
||||
|
||||
blend2 = spawn( "sound_blend", ( 0.0, 0.0, 0.0 ) );
|
||||
blend2.origin = self.origin;
|
||||
self thread blendDelete( blend2 );
|
||||
|
||||
|
||||
// lerp of 0 will play _null only
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, 0 );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, 1 );
|
||||
rain = "hard";
|
||||
blendTime = undefined;
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill( "rain_change", change, blendTime );
|
||||
blendTime *= 20;// internal framerate
|
||||
assert( change == "hard" || change == "light" || change == "none" );
|
||||
assert( blendtime > 0 );
|
||||
|
||||
if ( change == "hard" )
|
||||
{
|
||||
if ( rain == "none" )
|
||||
{
|
||||
blendTime *= 0.5;// gotta do 2 blends to go from none to hard
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, i / blendtime );
|
||||
wait( 0.05 );
|
||||
}
|
||||
rain = "light";
|
||||
}
|
||||
if ( rain == "light" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, 1 - ( i / blendtime ) );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, i / blendtime );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( change == "none" )
|
||||
{
|
||||
if ( rain == "hard" )
|
||||
{
|
||||
blendTime *= 0.5;// gotta do 2 blends to go from hard to none
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, ( i / blendtime ) );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, 1 - ( i / blendtime ) );
|
||||
wait( 0.05 );
|
||||
}
|
||||
rain = "light";
|
||||
}
|
||||
if ( rain == "light" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, 1 - ( i / blendtime ) );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( change == "light" )
|
||||
{
|
||||
if ( rain == "none" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, i / blendtime );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
if ( rain == "hard" )
|
||||
{
|
||||
for ( i = 0;i < blendtime;i++ )
|
||||
{
|
||||
blend setSoundBlend( lightRain + "_null", lightRain, i / blendtime );
|
||||
blend2 setSoundBlend( hardRain + "_null", hardRain, 1 - ( i / blendtime ) );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rain = change;
|
||||
}
|
||||
}*/
|
||||
|
||||
blendDelete( blend )
|
||||
{
|
||||
self waittill( "death" );
|
||||
blend delete();
|
||||
}
|
||||
50
maps/mp/_global_fx.gsc
Normal file
50
maps/mp/_global_fx.gsc
Normal file
@@ -0,0 +1,50 @@
|
||||
#include maps\mp\_global_fx_code;
|
||||
|
||||
// This script automaticly plays a users specified oneshot effect on all prefabs that have the
|
||||
// specified "script_struct" and "targetname" It also excepts angles from the "script_struct"
|
||||
// but will set a default angle of ( 0, 0, 0 ) if none is defined.
|
||||
|
||||
main()
|
||||
{
|
||||
// targetname fxFile
|
||||
global_FX( "ch_streetlight_02_FX_origin" , "fx/misc/lighthaze" );
|
||||
global_FX( "me_streetlight_01_FX_origin" , "fx/misc/lighthaze_bog_a" );
|
||||
global_FX( "ch_street_light_01_on" , "fx/misc/light_glow_white" );
|
||||
global_FX( "lamp_post_globe_on" , "fx/misc/light_glow_white" );
|
||||
global_FX( "highway_lamp_post" , "fx/misc/lighthaze_villassault" );
|
||||
global_FX( "cs_cargoship_spotlight_on_FX_origin" , "fx/misc/lighthaze" );
|
||||
global_FX( "com_tires_burning01_FX_origin" , "fx/fire/tire_fire_med" );
|
||||
global_FX( "icbm_powerlinetower_FX_origin" , "fx/misc/power_tower_light_red_blink" );
|
||||
global_FX( "icbm_mainframe_FX_origin" , "fx/props/icbm_mainframe_lightblink" );
|
||||
global_FX( "lighthaze_oilrig_FX_origin" , "fx/misc/lighthaze_oilrig" );
|
||||
global_FX( "lighthaze_white_FX_origin" , "fx/misc/lighthaze_white" );
|
||||
global_FX( "light_glow_walllight_white_FX_origin", "fx/misc/light_glow_walllight_white" );
|
||||
global_FX( "fluorescent_glow_FX_origin" , "fx/misc/fluorescent_glow" );
|
||||
global_FX( "light_glow_industrial_FX_origin" , "fx/misc/light_glow_industrial" );
|
||||
global_FX( "highrise_blinky_tower" , "fx/misc/power_tower_light_red_blink_large" );
|
||||
global_FX( "light_glow_white_bulb_FX_origin" , "fx/misc/light_glow_white_bulb" );
|
||||
global_FX( "light_glow_white_lamp_FX_origin" , "fx/misc/light_glow_white_lamp" );
|
||||
global_FX( "mp_snow_light_on" , "vfx/ambient/lights/vfx_bulb_prismatic_mp_snow" );
|
||||
global_FX( "vfx_fog_water_mp" , "vfx/ambient/atmospheric/vfx_fog_water_mp" );
|
||||
global_FX( "vfx_mp_handflare" , "vfx/ambient/props/vfx_handflare_sov" );
|
||||
global_FX( "vfx_mp_sov_ceiling_light" , "vfx/ambient/lights/vfx_glow_ceil_light_sov" );
|
||||
|
||||
|
||||
|
||||
|
||||
// targetname fxFile delay
|
||||
global_FX( "light_red_steady_FX_origin" , "fx/misc/tower_light_red_steady" , -2 );
|
||||
global_FX( "light_blue_steady_FX_origin" , "fx/misc/tower_light_blue_steady" , -2 );
|
||||
global_FX( "light_orange_steady_FX_origin" , "fx/misc/tower_light_orange_steady" , -2 );
|
||||
global_FX( "glow_stick_pile_FX_origin" , "fx/misc/glow_stick_glow_pile" , -2 );
|
||||
global_FX( "glow_stick_orange_pile_FX_origin", "fx/misc/glow_stick_glow_pile_orange" , -2 );
|
||||
global_FX( "light_pulse_red_FX_origin" , "fx/misc/light_glow_red_generic_pulse" , -2 );
|
||||
global_FX( "light_pulse_red_FX_origin" , "fx/misc/light_glow_red_generic_pulse" , -2 );
|
||||
global_FX( "light_pulse_orange_FX_origin" , "fx/misc/light_glow_orange_generic_pulse", -2 );
|
||||
global_FX( "light_red_blink_FX_origin" , "fx/misc/power_tower_light_red_blink" , -2 );
|
||||
|
||||
// targetname fxFile delay fxName soundalias
|
||||
global_FX( "flare_ambient_FX_origin" , "fx/misc/flare_ambient" , undefined, undefined, "emt_road_flare_burn" );
|
||||
global_FX( "me_dumpster_fire_FX_origin", "fx/fire/firelp_med_pm" , undefined, undefined, "fire_dumpster_medium" );
|
||||
global_FX( "barrel_fireFX_origin" , "fx/fire/firelp_barrel_pm", undefined, undefined, "fire_barrel_temp" );
|
||||
}
|
||||
35
maps/mp/_global_fx_code.gsc
Normal file
35
maps/mp/_global_fx_code.gsc
Normal file
@@ -0,0 +1,35 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
global_FX( targetname, fxFile, delay, fxName, soundalias )
|
||||
{
|
||||
// script_structs
|
||||
ents = getstructarray( targetname, "targetname" );
|
||||
if ( ents.size <= 0 )
|
||||
return;
|
||||
|
||||
if ( ! IsDefined( delay ) )
|
||||
delay = RandomFloatRange( -20, -15 );
|
||||
|
||||
if ( !IsDefined( fxName ) )
|
||||
fxName = fxFile;
|
||||
|
||||
foreach ( fxEnt in ents )
|
||||
{
|
||||
if ( !IsDefined( level._effect ) )
|
||||
level._effect = [];
|
||||
if ( !IsDefined( level._effect[ fxName ] ) )
|
||||
level._effect[ fxName ] = LoadFX( fxFile );
|
||||
|
||||
// default effect angles if they dont exist
|
||||
if ( !IsDefined( fxEnt.angles ) )
|
||||
fxEnt.angles = ( 0, 0, 0 );
|
||||
|
||||
ent = createOneshotEffect( fxName );
|
||||
ent.v[ "origin" ] = ( fxEnt.origin );
|
||||
ent.v[ "angles" ] = ( fxEnt.angles );
|
||||
ent.v[ "fxid" ] = fxName;
|
||||
ent.v[ "delay" ] = delay;
|
||||
if ( IsDefined( soundalias ) )
|
||||
ent.v[ "soundalias" ] = soundalias;
|
||||
}
|
||||
}
|
||||
16
maps/mp/_highlights.gsc
Normal file
16
maps/mp/_highlights.gsc
Normal file
@@ -0,0 +1,16 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
giveHighlight( ref, value )
|
||||
{
|
||||
highlightCount = getClientMatchData( "highlightCount" );
|
||||
if ( highlightCount < 18 ) // must match MaxHighlights in clientmatchdata definition
|
||||
{
|
||||
setClientMatchData( "highlights", highlightCount, "award", ref );
|
||||
setClientMatchData( "highlights", highlightCount, "clientId", self.clientMatchDataId );
|
||||
setClientMatchData( "highlights", highlightCount, "value", value );
|
||||
|
||||
highlightCount++;
|
||||
setClientMatchData( "highlightCount", highlightCount );
|
||||
}
|
||||
}
|
||||
522
maps/mp/_javelin.gsc
Normal file
522
maps/mp/_javelin.gsc
Normal file
@@ -0,0 +1,522 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
InitJavelinUsage()
|
||||
{
|
||||
self.javelinStage = undefined;
|
||||
self.javelinPoints = undefined;
|
||||
self.javelinNormals = undefined;
|
||||
self.javelinLockMisses = undefined;
|
||||
self.javelinTargetPoint = undefined;
|
||||
self.javelinTargetNormal = undefined;
|
||||
self.javelinLockStartTime = undefined;
|
||||
}
|
||||
|
||||
|
||||
ResetJavelinLocking()
|
||||
{
|
||||
if ( !IsDefined( self.javelinUseEntered ) )
|
||||
return;
|
||||
self.javelinUseEntered = undefined;
|
||||
|
||||
self notify( "stop_lockon_sound" );
|
||||
|
||||
self WeaponLockFree();
|
||||
self WeaponLockTargetTooClose( false );
|
||||
self WeaponLockNoClearance( false );
|
||||
self.currentlyLocking = false;
|
||||
self.currentlyLocked = false;
|
||||
self.javelinTarget = undefined;
|
||||
|
||||
self StopLocalSound( "javelin_clu_lock" );
|
||||
self StopLocalSound( "javelin_clu_aquiring_lock" );
|
||||
|
||||
InitJavelinUsage();
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
DrawStar( point, color )
|
||||
{
|
||||
Line( point + (10,0,0), point - (10,0,0), color );
|
||||
Line( point + (0,10,0), point - (0,10,0), color );
|
||||
Line( point + (0,0,10), point - (0,0,10), color );
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
EyeTraceForward()
|
||||
{
|
||||
origin = self GetEye();
|
||||
angles = self GetPlayerAngles();
|
||||
forward = AnglesToForward( angles );
|
||||
endpoint = origin + forward * 15000;
|
||||
|
||||
res = BulletTrace( origin, endpoint, false, undefined );
|
||||
|
||||
if ( res["surfacetype"] == "none" )
|
||||
return undefined;
|
||||
if ( res["surfacetype"] == "default" )
|
||||
return undefined;
|
||||
|
||||
ent = res["entity"];
|
||||
if ( IsDefined( ent ) )
|
||||
{
|
||||
if ( ent == level.ac130.planeModel )
|
||||
return undefined;
|
||||
}
|
||||
|
||||
results = [];
|
||||
results[0] = res["position"];
|
||||
results[1] = res["normal"];
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
LockMissesReset()
|
||||
{
|
||||
self.javelinLockMisses = undefined;
|
||||
}
|
||||
|
||||
|
||||
LockMissesIncr()
|
||||
{
|
||||
if ( !IsDefined( self.javelinLockMisses ) )
|
||||
self.javelinLockMisses = 1;
|
||||
else
|
||||
self.javelinLockMisses++;
|
||||
}
|
||||
|
||||
|
||||
LockMissesPassedThreshold()
|
||||
{
|
||||
MAX_MISSES = 4;
|
||||
|
||||
if ( IsDefined( self.javelinLockMisses ) && (self.javelinLockMisses >= MAX_MISSES) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TargetPointTooClose( targetPoint )
|
||||
{
|
||||
MY_MIN_DIST = 1100;
|
||||
|
||||
dist = Distance( self.origin, targetPoint );
|
||||
if ( dist < MY_MIN_DIST )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
LoopLocalSeekSound( alias, interval )
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
self endon ( "stop_lockon_sound" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self PlayLocalSound( alias );
|
||||
wait interval;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TopAttackPasses( targPoint, targNormal )
|
||||
{
|
||||
origin = targPoint + (targNormal * 10.0);
|
||||
endpoint = origin + (0,0,2000);
|
||||
|
||||
result = BulletTrace( origin, endpoint, false, undefined );
|
||||
|
||||
if ( SightTracePassed( origin, endpoint, false, undefined ) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
JavelinUsageLoop()
|
||||
{
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
self endon("faux_spawn");
|
||||
|
||||
LOCK_LENGTH = 1150;
|
||||
GATHER_DELAY = 25;
|
||||
ADS_DELAY = 100;
|
||||
MAX_POINT_PEER_DIST = 400;
|
||||
POINTS_NEEDED_TO_START_LOCK = 12;
|
||||
|
||||
lastGatherTime = 0;
|
||||
lastOutOfADS = 0;
|
||||
self.javelinTarget = undefined;
|
||||
|
||||
InitJavelinUsage();
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait 0.05;
|
||||
|
||||
weapon = self getCurrentWeapon();
|
||||
if ( (IsBot(self) && (weapon != "javelin_mp")) || !isSubStr( weapon, "javelin" ) || self isEMPed() )
|
||||
{
|
||||
if ( IsDefined( self.javelinUseEntered ) )
|
||||
ResetJavelinLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self PlayerADS() < 0.95 )
|
||||
{
|
||||
lastOutOfADS = GetTime();
|
||||
|
||||
ResetJavelinLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
debugDraw = false;
|
||||
if ( GetDVar( "missileDebugDraw" ) == "1" )
|
||||
debugDraw = true;
|
||||
|
||||
debugText = false;
|
||||
if ( GetDVar( "missileDebugText" ) == "1" )
|
||||
debugText = true;
|
||||
|
||||
self.javelinUseEntered = true;
|
||||
if ( !IsDefined( self.javelinStage ) )
|
||||
self.javelinStage = 1;
|
||||
|
||||
if ( self.javelinStage == 1 ) // SCANNING: try to find a good point to lock to
|
||||
{
|
||||
|
||||
targets = maps\mp\gametypes\_weapons::lockOnLaunchers_getTargetArray();
|
||||
if ( targets.size != 0 )
|
||||
{
|
||||
|
||||
targetsInReticle = [];
|
||||
foreach ( target in targets )
|
||||
{
|
||||
insideReticle = self WorldPointInReticle_Circle( target.origin, 65, 40 );
|
||||
if ( insideReticle )
|
||||
targetsInReticle[targetsInReticle.size] = target;
|
||||
}
|
||||
|
||||
if ( targetsInReticle.size != 0 )
|
||||
{
|
||||
|
||||
sortedTargets = SortByDistance( targetsInReticle, self.origin );
|
||||
|
||||
if ( !( self VehicleLockSightTest( sortedTargets[0] ) ) )
|
||||
continue;
|
||||
|
||||
if ( debugText )
|
||||
PrintLn( "Javelin found a vehicle target to lock to." );
|
||||
|
||||
self.javelinTarget = sortedTargets[0];
|
||||
|
||||
if ( !isDefined( self.javelinLockStartTime ) )
|
||||
self.javelinLockStartTime = GetTime();
|
||||
|
||||
self.javelinStage = 2;
|
||||
self.javelinLostSightlineTime = 0;
|
||||
|
||||
self javelinLockVehicle( LOCK_LENGTH );
|
||||
self.javelinStage = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( LockMissesPassedThreshold() )
|
||||
{
|
||||
ResetJavelinLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
timePassed = GetTime() - lastOutOfADS;
|
||||
if ( timePassed < ADS_DELAY )
|
||||
continue;
|
||||
|
||||
/#
|
||||
if ( debugDraw && IsDefined( self.javelinPoints ) )
|
||||
{
|
||||
foreach( javPoint in self.javelinPoints )
|
||||
DrawStar( javPoint, (0.8, 1.0, 0.8) );
|
||||
DrawStar( self.javelinPoints[self.javelinPoints.size - 1], (1, 1, 0.2) );
|
||||
DrawStar( AveragePoint( self.javelinPoints ), (0.2, 0.2, 1) );
|
||||
}
|
||||
#/
|
||||
|
||||
timePassed = GetTime() - lastGatherTime;
|
||||
if ( timePassed < GATHER_DELAY )
|
||||
continue;
|
||||
lastGatherTime = GetTime();
|
||||
|
||||
traceRes = (self EyeTraceForward());
|
||||
if ( !IsDefined( traceRes ) )
|
||||
{
|
||||
self LockMissesIncr();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self TargetPointTooClose( traceRes[0] ) )
|
||||
{
|
||||
self WeaponLockTargetTooClose( true );
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
self WeaponLockTargetTooClose( false );
|
||||
}
|
||||
|
||||
// make sure we haven't strayed too far
|
||||
if ( IsDefined( self.javelinPoints ) )
|
||||
{
|
||||
prevAvgPoint = AveragePoint( self.javelinPoints );
|
||||
dist = Distance( prevAvgPoint, traceRes[0] );
|
||||
//PrintLn( "[", self.javelinPoints.size, "] - Dist: ", dist );
|
||||
|
||||
if ( dist > MAX_POINT_PEER_DIST )
|
||||
{
|
||||
LockMissesIncr();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.javelinPoints = [];
|
||||
self.javelinNormals = [];
|
||||
}
|
||||
|
||||
self.javelinPoints[self.javelinPoints.size] = traceRes[0];
|
||||
self.javelinNormals[self.javelinNormals.size] = traceRes[1];
|
||||
|
||||
self LockMissesReset();
|
||||
if ( self.javelinPoints.size < POINTS_NEEDED_TO_START_LOCK )
|
||||
continue;
|
||||
|
||||
// Go to stage 2:
|
||||
self.javelinTargetPoint = AveragePoint( self.javelinPoints );;
|
||||
self.javelinTargetNormal = AverageNormal( self.javelinNormals );;
|
||||
self.javelinLockMisses = undefined;
|
||||
self.javelinPoints = undefined;
|
||||
self.javelinNormals = undefined;
|
||||
self.javelinLockStartTime = GetTime();
|
||||
|
||||
self WeaponLockStart( self.javelinTargetPoint );
|
||||
self thread LoopLocalSeekSound( "javelin_clu_aquiring_lock", 0.6 );
|
||||
|
||||
self.javelinStage = 2;
|
||||
}
|
||||
|
||||
if ( self.javelinStage == 2 ) // LOCKING: complete lockon to point
|
||||
{
|
||||
/#
|
||||
if ( debugDraw )
|
||||
DrawStar( self.javelinTargetPoint, (0.5, 1.0, 0.6) );
|
||||
#/
|
||||
|
||||
insideReticle = self WorldPointInReticle_Circle( self.javelinTargetPoint, 65, 45 );
|
||||
if ( !insideReticle )
|
||||
{
|
||||
ResetJavelinLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self TargetPointTooClose( self.javelinTargetPoint ) )
|
||||
self WeaponLockTargetTooClose( true );
|
||||
else
|
||||
self WeaponLockTargetTooClose( false );
|
||||
|
||||
timePassed = getTime() - self.javelinLockStartTime;
|
||||
//PrintLn( "Locking [", timePassed, "]..." );
|
||||
if ( timePassed < LOCK_LENGTH )
|
||||
continue;
|
||||
|
||||
self WeaponLockFinalize( self.javelinTargetPoint, (0,0,0), true );
|
||||
self notify( "stop_lockon_sound" );
|
||||
self PlayLocalSound( "javelin_clu_lock" );
|
||||
self.javelinStage = 3;
|
||||
}
|
||||
|
||||
if ( self.javelinStage == 3 ) // LOCKED: try and hold it
|
||||
{
|
||||
/#
|
||||
if ( debugDraw )
|
||||
DrawStar( self.javelinTargetPoint, (0.1, 0.15, 1.0) );
|
||||
#/
|
||||
|
||||
insideReticle = self WorldPointInReticle_Circle( self.javelinTargetPoint, 65, 45 );
|
||||
if ( !insideReticle )
|
||||
{
|
||||
ResetJavelinLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self TargetPointTooClose( self.javelinTargetPoint ) )
|
||||
self WeaponLockTargetTooClose( true );
|
||||
else
|
||||
self WeaponLockTargetTooClose( false );
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugSightLine( start, end, passed )
|
||||
{
|
||||
/#
|
||||
if ( GetDVar( "missileDebugDraw" ) != "1" )
|
||||
return;
|
||||
|
||||
if ( passed )
|
||||
color = ( 0.3, 1.0, 0.3 );
|
||||
else
|
||||
color = ( 1.0, 0.2, 0.2 );
|
||||
|
||||
MY_OFFSET = ( 0, 0, 5 );
|
||||
|
||||
Line( start + MY_OFFSET, end, color );
|
||||
#/
|
||||
}
|
||||
|
||||
VehicleLockSightTest( target )
|
||||
{
|
||||
eyePos = self GetEye();
|
||||
|
||||
center = target GetPointInBounds( 0, 0, 0 );
|
||||
passed = SightTracePassed( eyePos, center, false, target );
|
||||
DebugSightLine( eyePos, center, passed );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
front = target GetPointInBounds( 1, 0, 0 );
|
||||
passed = SightTracePassed( eyePos, front, false, target );
|
||||
DebugSightLine( eyePos, front, passed );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
back = target GetPointInBounds( -1, 0, 0 );
|
||||
passed = SightTracePassed( eyePos, back, false, target );
|
||||
DebugSightLine( eyePos, back, passed );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
javelinLockVehicle( lockLength )
|
||||
{
|
||||
if ( self.javelinStage == 2 ) // locking on to a target
|
||||
{
|
||||
self WeaponLockStart( self.javelinTarget );
|
||||
|
||||
if ( !(self StillValidJavelinLock( self.javelinTarget ) ) )
|
||||
{
|
||||
ResetJavelinLocking();
|
||||
self.javelinLockStartTime = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
passed = SoftSightTest();
|
||||
if ( !passed )
|
||||
{
|
||||
self.javelinLockStartTime = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !isDefined( self.currentlyLocking ) || !self.currentlyLocking )
|
||||
{
|
||||
self thread LoopLocalSeekSound( "javelin_clu_aquiring_lock", 0.6 );
|
||||
self.currentlyLocking = true;
|
||||
}
|
||||
|
||||
timePassed = getTime() - self.javelinLockStartTime;
|
||||
|
||||
if( self _hasPerk( "specialty_fasterlockon" ) )
|
||||
{
|
||||
if( timePassed < ( lockLength * 0.5 ) )
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( timePassed < lockLength )
|
||||
return;
|
||||
}
|
||||
|
||||
//strangely buggy with the moving target...
|
||||
//javTarg = eyeTraceForward();
|
||||
//if ( !isDefined( javTarg ) )
|
||||
// return;
|
||||
|
||||
//topAttack = TopAttackPasses( javTarg[0], javTarg[1] );
|
||||
|
||||
if ( isPlayer( self.javelinTarget ) )
|
||||
self WeaponLockFinalize( self.javelinTarget, (0,0,64), false );
|
||||
else
|
||||
self WeaponLockFinalize( self.javelinTarget, (0,0,0), false );
|
||||
|
||||
|
||||
self notify( "stop_lockon_sound" );
|
||||
|
||||
if ( !isDefined( self.currentlyLocked ) || !self.currentlyLocked )
|
||||
{
|
||||
self PlayLocalSound( "javelin_clu_lock" );
|
||||
self.currentlyLocked = true;
|
||||
}
|
||||
|
||||
self.javelinStage = 3;
|
||||
}
|
||||
|
||||
if ( self.javelinStage == 3 ) // target locked
|
||||
{
|
||||
passed = SoftSightTest();
|
||||
if ( !passed )
|
||||
return;
|
||||
|
||||
if ( !(self StillValidJavelinLock( self.javelinTarget ) ) )
|
||||
{
|
||||
ResetJavelinLocking();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StillValidJavelinLock( ent )
|
||||
{
|
||||
assert( IsDefined( self ) );
|
||||
|
||||
if ( !IsDefined( ent ) )
|
||||
return false;
|
||||
if ( !(self WorldPointInReticle_Circle( ent.origin, 65, 85 )) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SoftSightTest()
|
||||
{
|
||||
LOST_SIGHT_LIMIT = 500;
|
||||
|
||||
if ( self VehicleLockSightTest( self.javelinTarget ) )
|
||||
{
|
||||
self.javelinLostSightlineTime = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( self.javelinLostSightlineTime == 0 )
|
||||
self.javelinLostSightlineTime = getTime();
|
||||
|
||||
timePassed = GetTime() - self.javelinLostSightlineTime;
|
||||
|
||||
if ( timePassed >= LOST_SIGHT_LIMIT )
|
||||
{
|
||||
ResetJavelinLocking();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
674
maps/mp/_laserguidedlauncher.gsc
Normal file
674
maps/mp/_laserguidedlauncher.gsc
Normal file
@@ -0,0 +1,674 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Init
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
CONST_LOCK_ON_TIME_MSEC = 1500;
|
||||
|
||||
LGM_init( fxSplit, fxHoming )
|
||||
{
|
||||
level._effect[ "laser_guided_launcher_missile_split" ] = LoadFX( fxSplit );
|
||||
level._effect[ "laser_guided_launcher_missile_spawn_homing" ] = LoadFX( fxHoming );
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Monitor Player Weapon for Use as CAC Launcher
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
// Function to use if you want to have a laser guided launcher as
|
||||
// a launcher in CAC. Just thread this watch off in _weapons.gsc::init()
|
||||
LGM_update_launcherUsage( weaponName, weaponNameHoming )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
|
||||
self thread LGM_monitorLaser();
|
||||
|
||||
weaponCurr = self GetCurrentWeapon();
|
||||
while ( 1 )
|
||||
{
|
||||
while ( weaponCurr != weaponName )
|
||||
{
|
||||
self waittill( "weapon_change", weaponCurr );
|
||||
}
|
||||
|
||||
self childthread LGM_firing_monitorMissileFire( weaponCurr, weaponNameHoming );
|
||||
|
||||
self waittill( "weapon_change", weaponCurr );
|
||||
|
||||
self LGM_firing_endMissileFire();
|
||||
}
|
||||
}
|
||||
|
||||
LGM_monitorLaser()
|
||||
{
|
||||
self endon( "LGM_player_endMonitorFire" );
|
||||
|
||||
self waittill_any( "death", "disconnect" );
|
||||
|
||||
if ( IsDefined( self ) )
|
||||
{
|
||||
self LGM_disableLaser();
|
||||
}
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Monitor Player Firing
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
LASER_GUIDED_MISSILE_LASER_TRACE_CLOSE_TIME_MSEC = 400; // Time after firing that the trace distance is short. This forces the missiles forward.
|
||||
LASER_GUIDED_MISSILE_LASER_TRACE_LENGTH_SHORT = 800;
|
||||
LASER_GUIDED_MISSILE_LASER_TRACE_LENGTH = 8000;
|
||||
LASER_GUIDED_MISSILE_DELAY_CHILDREN_SPAWN = 0.35;
|
||||
LASER_GUIDED_MISSILE_DELAY_CHILDREN_TRACK = 0.1;
|
||||
LASER_GUIDED_MISSILE_PITCH_CHILDREN_DIVERGE = 20;
|
||||
LASER_GUIDED_MISSILE_YAW_CHILDREN_DIVERGE = 20;
|
||||
|
||||
LGM_firing_endMissileFire()
|
||||
{
|
||||
self LGM_disableLaser();
|
||||
|
||||
self notify( "LGM_player_endMonitorFire" );
|
||||
}
|
||||
|
||||
LGM_firing_monitorMissileFire( weaponName, weaponNameChild, weaponNameHoming )
|
||||
{
|
||||
self endon( "LGM_player_endMonitorFire" );
|
||||
|
||||
self LGM_enableLaser();
|
||||
|
||||
entTarget = undefined;
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
missile = undefined;
|
||||
self waittill( "missile_fire", missile, weaponNotified );
|
||||
|
||||
// Ignore missiles fired by script
|
||||
if ( IsDefined( missile.isMagicBullet ) && missile.isMagicBullet )
|
||||
continue;
|
||||
|
||||
// The hind magic bullets missiles on behalf of the player, so ignore without assert.
|
||||
if ( weaponNotified != weaponName )
|
||||
continue;
|
||||
|
||||
if ( !IsDefined( entTarget ) )
|
||||
{
|
||||
entTarget = LGM_requestMissileGuideEnt( self );
|
||||
}
|
||||
|
||||
self thread LGM_firing_delaySpawnChildren( weaponName, weaponNameChild, weaponNameHoming, LASER_GUIDED_MISSILE_DELAY_CHILDREN_SPAWN, LASER_GUIDED_MISSILE_DELAY_CHILDREN_TRACK, missile, entTarget );
|
||||
}
|
||||
}
|
||||
|
||||
LGM_firing_delaySpawnChildren( weaponName, weaponNameChild, weaponNameHoming, delaySpawn, delayTrack, missile, entTarget )
|
||||
{
|
||||
// If the player fires rapidly, cancel the previous missile spawn
|
||||
self notify( "monitor_laserGuidedMissile_delaySpawnChildren" );
|
||||
self endon( "monitor_laserGuidedMissile_delaySpawnChildren" );
|
||||
|
||||
// If the player dies this function needs to be cleared up immediately. This is because
|
||||
// the target ent gets scrubbed on death so any created missiles may have been
|
||||
// removed from the array
|
||||
self endon( "death" );
|
||||
self endon( "LGM_player_endMonitorFire" );
|
||||
|
||||
// Only let one set of rockets be guided by releasing
|
||||
// any previously fired rockets
|
||||
LGM_missilesNotifyAndRelease( entTarget );
|
||||
|
||||
wait( delaySpawn );
|
||||
|
||||
// Exit if missile has blown up
|
||||
if ( !IsValidMissile( missile ) )
|
||||
return;
|
||||
|
||||
missileOrigin = missile.origin;
|
||||
missileFwd = AnglesToForward( missile.angles );
|
||||
missileUp = AnglesToUp( missile.angles );
|
||||
missileRight = AnglesToRight( missile.angles );
|
||||
|
||||
missile Delete();
|
||||
|
||||
// Spawn missile break apart fx
|
||||
PlayFX( level._effect[ "laser_guided_launcher_missile_split" ], missileOrigin, missileFwd, missileUp );
|
||||
|
||||
missiles = [];
|
||||
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
pitch = LASER_GUIDED_MISSILE_PITCH_CHILDREN_DIVERGE; // Default Upwards Pitch: Forward and Up n degrees
|
||||
yaw = 0;
|
||||
|
||||
if ( i == 0 ) // Right Missile: Up 45 degrees and then 45 degrees around Z
|
||||
{
|
||||
yaw = LASER_GUIDED_MISSILE_YAW_CHILDREN_DIVERGE;
|
||||
}
|
||||
else if ( i == 1 ) // Left Missile: Up 45 degrees and then -45 degrees around Z
|
||||
{
|
||||
yaw = -1 * LASER_GUIDED_MISSILE_YAW_CHILDREN_DIVERGE;
|
||||
}
|
||||
else if ( i == 2 )// Middle Missile: Already rotated up
|
||||
{
|
||||
// All done
|
||||
}
|
||||
|
||||
childMissileFwd = RotatePointAroundVector( missileRight, missileFwd, pitch );
|
||||
childMissileFwd = RotatePointAroundVector( missileUp, childMissileFwd, yaw );
|
||||
|
||||
missileChild = MagicBullet( weaponNameChild, missileOrigin, missileOrigin + childMissileFwd * 180, self );
|
||||
missileChild.isMagicBullet = true;
|
||||
missiles[ missiles.size ] = missileChild;
|
||||
|
||||
// Don't spawn missiles on the same frame
|
||||
waitframe();
|
||||
}
|
||||
|
||||
wait( delayTrack );
|
||||
|
||||
missiles = LGM_removeInvalidMissiles( missiles );
|
||||
|
||||
if ( missiles.size > 0 )
|
||||
{
|
||||
foreach ( missChild in missiles )
|
||||
{
|
||||
entTarget.missilesChasing[ entTarget.missilesChasing.size ] = missChild;
|
||||
missChild Missile_SetTargetEnt( entTarget );
|
||||
|
||||
self thread LGM_onMissileNotifies( entTarget, missChild );
|
||||
}
|
||||
|
||||
self thread LGM_firing_monitorPlayerAim( entTarget, weaponNameHoming );
|
||||
}
|
||||
}
|
||||
|
||||
LGM_onMissileNotifies( entTarget, missile )
|
||||
{
|
||||
missile waittill_any( "death", "missile_pairedWithFlare", "LGM_missile_abandoned" );
|
||||
|
||||
if ( IsDefined( entTarget.missilesChasing ) && entTarget.missilesChasing.size > 0 )
|
||||
{
|
||||
entTarget.missilesChasing = array_remove( entTarget.missilesChasing, missile );
|
||||
entTarget.missilesChasing = LGM_removeInvalidMissiles( entTarget.missilesChasing );
|
||||
}
|
||||
|
||||
if ( !IsDefined( entTarget.missilesChasing ) || entTarget.missilesChasing.size == 0 )
|
||||
{
|
||||
self notify( "LGM_player_allMissilesDestroyed" );
|
||||
}
|
||||
}
|
||||
|
||||
// Handles initial lock visual / audio fx
|
||||
|
||||
LGM_firing_monitorPlayerAim( entTarget, weaponNameHoming )
|
||||
{
|
||||
self notify( "LGM_player_newMissilesFired" );
|
||||
self endon( "LGM_player_newMissilesFired" );
|
||||
|
||||
self endon( "LGM_player_allMissilesDestroyed" );
|
||||
self endon( "LGM_player_endMonitorFire" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
originGoal = undefined;
|
||||
targetVeh = undefined;
|
||||
lockOnTime = undefined;
|
||||
lockedOn = false;
|
||||
|
||||
timeTraceFar = GetTime() + LASER_GUIDED_MISSILE_LASER_TRACE_CLOSE_TIME_MSEC;
|
||||
|
||||
while ( IsDefined( entTarget.missilesChasing ) && entTarget.missilesChasing.size > 0 )
|
||||
{
|
||||
targetLook = self LGM_targetFind();
|
||||
|
||||
if ( !IsDefined( targetLook ) )
|
||||
{
|
||||
// If there was a previous target, clear that target and
|
||||
// notify systmes watching the missiles that the
|
||||
// target has changed
|
||||
if ( IsDefined( targetVeh ) )
|
||||
{
|
||||
self notify( "LGM_player_targetLost" );
|
||||
targetVeh = undefined;
|
||||
|
||||
foreach ( missile in entTarget.missilesChasing )
|
||||
{
|
||||
missile notify( "missile_targetChanged" );
|
||||
}
|
||||
}
|
||||
|
||||
lockOnTime = undefined;
|
||||
lockedOn = false;
|
||||
|
||||
traceDist = ter_op( GetTime() > timeTraceFar, LASER_GUIDED_MISSILE_LASER_TRACE_LENGTH, LASER_GUIDED_MISSILE_LASER_TRACE_LENGTH_SHORT );
|
||||
viewDir = AnglesToForward( self GetPlayerAngles() );
|
||||
startPos = self GetEye() + viewDir * 12;
|
||||
trace = BulletTrace( startPos, startPos + viewDir * traceDist, true, self, false, false, false );
|
||||
|
||||
originGoal = trace[ "position" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
originGoal = targetLook.origin;
|
||||
|
||||
newTarget = !IsDefined( targetVeh ) || targetLook != targetVeh;
|
||||
targetVeh = targetLook;
|
||||
|
||||
if ( newTarget || !IsDefined( lockOnTime ) )
|
||||
{
|
||||
lockOnTime = GetTime() + CONST_LOCK_ON_TIME_MSEC;
|
||||
level thread LGM_locking_think( targetVeh, self );
|
||||
}
|
||||
else if ( GetTime() >= lockOnTime )
|
||||
{
|
||||
// incoming notify was fired as soon as the player looked at
|
||||
// the target
|
||||
lockedOn = true;
|
||||
self notify( "LGM_player_lockedOn" );
|
||||
}
|
||||
|
||||
if ( lockedOn )
|
||||
{
|
||||
// In case the current live missiles are paired with flares
|
||||
// this script update wait untill after they're removed
|
||||
// from missilesChasing
|
||||
waittillframeend;
|
||||
|
||||
if ( entTarget.missilesChasing.size > 0 )
|
||||
{
|
||||
missileOrigins = [];
|
||||
|
||||
foreach ( missile in entTarget.missilesChasing )
|
||||
{
|
||||
if ( !IsValidMissile( missile ) )
|
||||
continue;
|
||||
|
||||
missileOrigins[ missileOrigins.size ] = missile.origin;
|
||||
|
||||
missile notify( "missile_targetChanged" );
|
||||
missile notify( "LGM_missile_abandoned" );
|
||||
missile Delete();
|
||||
}
|
||||
|
||||
if ( missileOrigins.size > 0 )
|
||||
{
|
||||
level thread LGM_locked_think( targetVeh, self, weaponNameHoming, missileOrigins );
|
||||
}
|
||||
|
||||
entTarget.missilesChasing = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
// All missiles were removed during the waittillframeend
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( newTarget )
|
||||
{
|
||||
LGM_targetNotifyMissiles( targetVeh, self, entTarget.missilesChasing );
|
||||
}
|
||||
}
|
||||
|
||||
entTarget.origin = originGoal;
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Monitor LaserGuided Missile Ent Pool
|
||||
// - Prevent too many ents being created
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
TARGET_ENT_COUNT_PREFERRED_MAX = 4; // The pool of target ents can get larger than this but should shrink back down when less are needed.
|
||||
|
||||
LGM_requestMissileGuideEnt( player )
|
||||
{
|
||||
if ( !IsDefined( level.laserGuidedMissileEnts_inUse ) )
|
||||
{
|
||||
level.laserGuidedMissileEnts_inUse = [];
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.laserGuidedMissileEnts_ready ) )
|
||||
{
|
||||
level.laserGuidedMissileEnts_ready = [];
|
||||
}
|
||||
|
||||
ent = undefined;
|
||||
|
||||
if ( level.laserGuidedMissileEnts_ready.size )
|
||||
{
|
||||
ent = level.laserGuidedMissileEnts_ready[ 0 ];
|
||||
level.laserGuidedMissileEnts_ready = array_remove( level.laserGuidedMissileEnts_ready, ent );
|
||||
}
|
||||
else
|
||||
{
|
||||
ent = spawn( "script_origin", player.origin );
|
||||
}
|
||||
|
||||
level.laserGuidedMissileEnts_inUse[ level.laserGuidedMissileEnts_inUse.size ] = ent;
|
||||
|
||||
level thread LGM_monitorLaserEntCleanUp( ent, player );
|
||||
|
||||
ent.missilesChasing = [];
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
LGM_monitorLaserEntCleanUp( entTarget, player )
|
||||
{
|
||||
player waittill_any( "death", "disconnect", "LGM_player_endMonitorFire" );
|
||||
|
||||
AssertEx( array_contains( level.laserGuidedMissileEnts_inUse, entTarget ), "LGM_monitorLaserEntCleanUp() attempting to clean up laser target ent not currently in use." );
|
||||
|
||||
// Scrub ent clean
|
||||
AssertEx( IsDefined( entTarget.missilesChasing ) && IsArray( entTarget.missilesChasing ), "LGM_monitorLaserEntCleanUp() given missile ent with now missile array." );
|
||||
foreach ( missile in entTarget.missilesChasing )
|
||||
{
|
||||
if ( IsValidMissile( missile ) )
|
||||
{
|
||||
missile Missile_ClearTarget();
|
||||
}
|
||||
}
|
||||
|
||||
entTarget.missilesChasing = undefined;
|
||||
|
||||
// Move ent fron in use to ready array
|
||||
level.laserGuidedMissileEnts_inUse = array_remove( level.laserGuidedMissileEnts_inUse, entTarget );
|
||||
|
||||
// If the too many ents exist delete it, otherwise add it to ready array
|
||||
if ( level.laserGuidedMissileEnts_ready.size + level.laserGuidedMissileEnts_inUse.size < TARGET_ENT_COUNT_PREFERRED_MAX )
|
||||
{
|
||||
level.laserGuidedMissileEnts_ready[ level.laserGuidedMissileEnts_ready.size ] = entTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
entTarget Delete();
|
||||
}
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Monitor Locking and Locked On
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
LGM_locking_think( targetVeh, player )
|
||||
{
|
||||
AssertEx( IsDefined( player ), "LGM_locking_think called with undefined player." );
|
||||
|
||||
outline = outlineEnableForPlayer( targetVeh, "orange", player, true, "killstreak_personal" );
|
||||
|
||||
level thread LGM_locking_loopSound( player, "maaws_reticle_tracking", 1.5, "LGM_player_lockingDone" );
|
||||
level thread LGM_locking_notifyOnTargetDeath( targetVeh, player );
|
||||
|
||||
player waittill_any(
|
||||
"death", // targetting player died
|
||||
"disconnect", // targetting player left game
|
||||
"LGM_player_endMonitorFire", // ks system stopped launcher logic
|
||||
"LGM_player_newMissilesFired", // player fired again, these missiles are going to be abandoned
|
||||
"LGM_player_targetLost", // player looked away or new enemy came into view during lock on
|
||||
"LGM_player_lockedOn", // player obtained full lock, this outlin is removed, a new outlin call is made
|
||||
"LGM_player_allMissilesDestroyed", // the current set of tracked missiles all died or were paired with flares
|
||||
"LGM_player_targetDied" // target destroyed
|
||||
);
|
||||
|
||||
// Some entities delete instantly on death and may
|
||||
// be removed at this point
|
||||
if ( IsDefined( targetVeh ) )
|
||||
{
|
||||
outlineDisable( outline, targetVeh );
|
||||
}
|
||||
|
||||
if ( IsDefined( player ) )
|
||||
{
|
||||
player notify( "LGM_player_lockingDone" );
|
||||
|
||||
player StopLocalSound( "maaws_reticle_tracking" );
|
||||
}
|
||||
}
|
||||
|
||||
LGM_locked_missileOnDeath( missile, targetVeh, groupID )
|
||||
{
|
||||
targetVeh endon( "death" );
|
||||
|
||||
missile waittill( "death" );
|
||||
|
||||
targetVeh.LG_missilesLocked[ groupID ] = array_remove( targetVeh.LG_missilesLocked[ groupID ], missile );
|
||||
|
||||
if ( targetVeh.LG_missilesLocked[ groupID ].size == 0 )
|
||||
{
|
||||
targetVeh.LG_missilesLocked[ groupID ] = undefined;
|
||||
targetVeh notify( "LGM_target_lockedMissilesDestroyed" );
|
||||
}
|
||||
}
|
||||
|
||||
LGM_locking_notifyOnTargetDeath( target, player )
|
||||
{
|
||||
player endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
player endon( "LGM_player_lockingDone" );
|
||||
|
||||
target waittill( "death" );
|
||||
|
||||
player notify( "LGM_player_targetDied" );
|
||||
}
|
||||
|
||||
LGM_locking_loopSound( player, sound, time, endonPlayer )
|
||||
{
|
||||
player endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
player endon( endOnPlayer );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
player PlayLocalSound( sound );
|
||||
wait( time );
|
||||
}
|
||||
}
|
||||
|
||||
LGM_locked_spawnMissiles( target, player, weaponNameHoming, missileOrigins )
|
||||
{
|
||||
target endon( "death" );
|
||||
player endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
|
||||
missilesLocked = [];
|
||||
|
||||
for ( i = 0; i < missileOrigins.size; i++ )
|
||||
{
|
||||
missileChild = MagicBullet( weaponNameHoming, missileOrigins[ i ], target.origin, player );
|
||||
missileChild.isMagicBullet = true;
|
||||
missilesLocked[ missilesLocked.size ] = missileChild;
|
||||
|
||||
PlayFX( level._effect[ "laser_guided_launcher_missile_spawn_homing" ], missileChild.origin, AnglesToForward( missileChild.angles ), AnglesToUp( missileChild.angles ) );
|
||||
|
||||
// Don't spawn missiles on the same frame
|
||||
waitframe();
|
||||
}
|
||||
|
||||
return missilesLocked;
|
||||
}
|
||||
|
||||
// Handles locked on visual / audio fx
|
||||
LGM_locked_think( targetVeh, player, weaponNameHoming, missileOrigins )
|
||||
{
|
||||
AssertEx( missileOrigins.size > 0, "LGM_locked_think() passed empty missile origin array." );
|
||||
|
||||
if ( missileOrigins.size == 0 )
|
||||
return;
|
||||
|
||||
missilesLocked = LGM_locked_spawnMissiles( targetVeh, player, weaponNameHoming, missileOrigins );
|
||||
|
||||
// If undefined the player died or the target died
|
||||
if ( !IsDefined( missilesLocked ) )
|
||||
return;
|
||||
|
||||
// In case a missile died after the above wait frame
|
||||
missilesLocked = LGM_removeInvalidMissiles( missilesLocked );
|
||||
if ( missilesLocked.size == 0 )
|
||||
return;
|
||||
|
||||
// Visual and audio fx
|
||||
player PlayLocalSound( "maaws_reticle_locked" );
|
||||
outlineID = outlineEnableForPlayer( targetVeh, "red", player, false, "killstreak_personal" );
|
||||
|
||||
// Give missiles their target with an offset
|
||||
targetOffset = LGM_getTargetOffset( targetVeh );
|
||||
|
||||
foreach ( mChild in missilesLocked )
|
||||
{
|
||||
mChild missile_setTargetAndFlightMode( targetVeh, "direct", targetOffset );
|
||||
|
||||
LGM_targetNotifyMissiles( targetVeh, player, missilesLocked );
|
||||
}
|
||||
|
||||
if ( !IsDefined( targetVeh.LG_missilesLocked ) )
|
||||
{
|
||||
targetVeh.LG_missilesLocked = [];
|
||||
}
|
||||
|
||||
// Because multiple sets of missiles from the same or different players
|
||||
// can be tracking the helicopter at the same timed, add the missiles
|
||||
// to an array by the unique outline ID
|
||||
targetVeh.LG_missilesLocked[ outlineID ] = missilesLocked;
|
||||
|
||||
foreach ( vMiss in missilesLocked )
|
||||
{
|
||||
level thread LGM_locked_missileOnDeath( vMiss, targetVeh, outlineID );
|
||||
}
|
||||
|
||||
outlineOn = true;
|
||||
while ( outlineOn )
|
||||
{
|
||||
msg = targetVeh waittill_any_return( "death", "LGM_target_lockedMissilesDestroyed" );
|
||||
|
||||
if ( msg == "death" )
|
||||
{
|
||||
outlineOn = false;
|
||||
if ( IsDefined( targetVeh ) )
|
||||
{
|
||||
targetVeh.LG_missilesLocked[ outlineID ] = undefined;
|
||||
}
|
||||
}
|
||||
else if ( msg == "LGM_target_lockedMissilesDestroyed" )
|
||||
{
|
||||
// Two sets of missiles could potentially throw the "LGM_target_lockedMissilesDestroyed"
|
||||
// notification on the same frame. Wait until frame end and then check to see if this
|
||||
// outline's missiles are all gone
|
||||
waittillframeend;
|
||||
|
||||
if ( !IsDefined( targetVeh.LG_missilesLocked[ outlineID ] ) || targetVeh.LG_missilesLocked[ outlineID ].size == 0 )
|
||||
{
|
||||
outlineOn = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Targetveh may be deleted at this point
|
||||
if ( IsDefined( targetVeh ) )
|
||||
{
|
||||
outlineDisable( outlineID, targetVeh );
|
||||
}
|
||||
}
|
||||
|
||||
LGM_targetFind()
|
||||
{
|
||||
targets = self maps\mp\gametypes\_weapons::lockOnLaunchers_getTargetArray();
|
||||
targets = SortByDistance( targets, self.origin );
|
||||
|
||||
targetLook = undefined;
|
||||
foreach ( target in targets )
|
||||
{
|
||||
if ( self WorldPointInReticle_Circle( target.origin, 65, 75 ) )
|
||||
{
|
||||
targetLook = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return targetLook;
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// LaserGuided Missile Utils
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
LGM_enableLaser()
|
||||
{
|
||||
if ( !IsDefined( self.laserGuidedLauncher_laserOn ) || self.laserGuidedLauncher_laserOn == false )
|
||||
{
|
||||
self.laserGuidedLauncher_laserOn = true;
|
||||
self enableWeaponLaser();
|
||||
}
|
||||
}
|
||||
|
||||
LGM_disableLaser()
|
||||
{
|
||||
if ( IsDefined( self.laserGuidedLauncher_laserOn ) && self.laserGuidedLauncher_laserOn == true )
|
||||
{
|
||||
self disableWeaponLaser();
|
||||
}
|
||||
|
||||
self.laserGuidedLauncher_laserOn = undefined;
|
||||
}
|
||||
|
||||
LGM_removeInvalidMissiles( missiles )
|
||||
{
|
||||
valid = [];
|
||||
foreach ( m in missiles )
|
||||
{
|
||||
if ( IsValidMissile( m ) )
|
||||
{
|
||||
valid[ valid.size ] = m;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
LGM_targetNotifyMissiles( targetVeh, attacker, missiles )
|
||||
{
|
||||
// General notifies to other systems to handle incoming missiles
|
||||
level notify( "laserGuidedMissiles_incoming", attacker, missiles, targetVeh );
|
||||
targetVeh notify( "targeted_by_incoming_missile", missiles );
|
||||
}
|
||||
|
||||
LGM_getTargetOffset( target )
|
||||
{
|
||||
targetPoint = undefined;
|
||||
//AH: HACK: The harrier doesn't have the tag_missile_target, but it does have a tag_body.
|
||||
// The code works fine without this check, but GetTagOrigin throws an SRE if the tag does not exist.
|
||||
if ( target.model != "vehicle_av8b_harrier_jet_mp" )
|
||||
targetPoint = target GetTagOrigin( "tag_missile_target" );
|
||||
else
|
||||
targetPoint = target GetTagOrigin( "tag_body" );
|
||||
|
||||
if ( !IsDefined( targetPoint ) )
|
||||
{
|
||||
targetPoint = target GetPointInBounds( 0, 0, 0 );
|
||||
AssertMsg( "LGM_getTargetOffset() failed to find tag_missile_target on entity." + target.classname );
|
||||
}
|
||||
|
||||
return targetPoint - target.origin;
|
||||
}
|
||||
|
||||
LGM_missilesNotifyAndRelease( entTarget )
|
||||
{
|
||||
if ( IsDefined( entTarget.missilesChasing ) && entTarget.missilesChasing.size > 0 )
|
||||
{
|
||||
foreach ( missChasing in entTarget.missilesChasing )
|
||||
{
|
||||
if ( IsValidMissile( missChasing ) )
|
||||
{
|
||||
// Let systems watching incoming missiles know the
|
||||
// target has changed
|
||||
missChasing notify( "missile_targetChanged" );
|
||||
missChasing notify( "LGM_missile_abandoned" );
|
||||
missChasing Missile_ClearTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entTarget.missilesChasing = [];
|
||||
}
|
||||
399
maps/mp/_load.gsc
Normal file
399
maps/mp/_load.gsc
Normal file
@@ -0,0 +1,399 @@
|
||||
#include common_scripts\utility;
|
||||
#include common_scripts\_fx;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
if ( isDefined( level._loadStarted ) )
|
||||
return;
|
||||
|
||||
level._loadStarted = true;
|
||||
|
||||
level.createFX_enabled = ( getdvar( "createfx" ) != "" );
|
||||
|
||||
struct_class_init();
|
||||
|
||||
initGameFlags();
|
||||
initLevelFlags();
|
||||
initGlobals();
|
||||
|
||||
level.generic_index = 0;
|
||||
// flag_struct is used as a placeholder when a flag is set without an entity
|
||||
|
||||
level.flag_struct = spawnstruct();
|
||||
level.flag_struct assign_unique_id();
|
||||
if ( !isdefined( level.flag ) )
|
||||
{
|
||||
level.flag = [];
|
||||
level.flags_lock = [];
|
||||
}
|
||||
|
||||
level.requiredMapAspectRatio = getDvarFloat( "scr_RequiredMapAspectratio", 1 );
|
||||
level.createClientFontString_func = maps\mp\gametypes\_hud_util::createFontString;
|
||||
level.HUDsetPoint_func = maps\mp\gametypes\_hud_util::setPoint;
|
||||
level.leaderDialogOnPlayer_func = maps\mp\_utility::leaderDialogOnPlayer;
|
||||
|
||||
thread maps\mp\gametypes\_tweakables::init();
|
||||
|
||||
|
||||
if ( !isdefined( level.func ) )
|
||||
level.func = [];
|
||||
level.func[ "precacheMpAnim" ] = ::precacheMpAnim;
|
||||
level.func[ "scriptModelPlayAnim" ] = ::scriptModelPlayAnim;
|
||||
level.func[ "scriptModelClearAnim" ] = ::scriptModelClearAnim;
|
||||
|
||||
// dodge this stuff for createfx tool.
|
||||
if( ! level.createFX_enabled )
|
||||
{
|
||||
thread maps\mp\_minefields::minefields();
|
||||
thread maps\mp\_radiation::radiation();
|
||||
thread maps\mp\_shutter::main();
|
||||
thread maps\mp\_movers::init();
|
||||
thread maps\mp\_destructables::init();
|
||||
thread common_scripts\_elevator::init();
|
||||
thread common_scripts\_dynamic_world::init();
|
||||
thread common_scripts\_destructible::init();
|
||||
level notify( "interactive_start" );
|
||||
}
|
||||
|
||||
game["thermal_vision"] = "thermal_mp";
|
||||
|
||||
VisionSetNaked( "", 0 ); // go to default visionset
|
||||
VisionSetNight( "default_night_mp" );
|
||||
VisionSetMissilecam( "missilecam" );
|
||||
VisionSetThermal( game[ "thermal_vision" ] );
|
||||
VisionSetPain( "", 0 );
|
||||
|
||||
lanterns = getentarray("lantern_glowFX_origin","targetname");
|
||||
for( i = 0 ; i < lanterns.size ; i++ )
|
||||
lanterns[i] thread lanterns();
|
||||
|
||||
maps\mp\_audio::init_audio();
|
||||
maps\mp\_art::main();
|
||||
/#
|
||||
thread common_scripts\_painter::main("painter_mp");
|
||||
#/
|
||||
|
||||
setupExploders();
|
||||
|
||||
thread common_scripts\_fx::initFX();
|
||||
if ( level.createFX_enabled )
|
||||
{
|
||||
maps\mp\gametypes\_spawnlogic::setMapCenterForDev();
|
||||
maps\mp\_createfx::createfx();
|
||||
}
|
||||
|
||||
if ( getdvar( "r_reflectionProbeGenerate" ) == "1" )
|
||||
{
|
||||
maps\mp\gametypes\_spawnlogic::setMapCenterForDev();
|
||||
maps\mp\_global_fx::main();
|
||||
level waittill( "eternity" );
|
||||
}
|
||||
|
||||
thread maps\mp\_global_fx::main();
|
||||
|
||||
// Do various things on triggers
|
||||
for ( p = 0;p < 6;p ++ )
|
||||
{
|
||||
switch( p )
|
||||
{
|
||||
case 0:
|
||||
triggertype = "trigger_multiple";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
triggertype = "trigger_once";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
triggertype = "trigger_use";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
triggertype = "trigger_radius";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
triggertype = "trigger_lookat";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( p == 5 );
|
||||
triggertype = "trigger_damage";
|
||||
break;
|
||||
}
|
||||
|
||||
triggers = getentarray( triggertype, "classname" );
|
||||
|
||||
for ( i = 0;i < triggers.size;i ++ )
|
||||
{
|
||||
if( isdefined( triggers[ i ].script_prefab_exploder) )
|
||||
triggers[i].script_exploder = triggers[ i ].script_prefab_exploder;
|
||||
|
||||
if( isdefined( triggers[ i ].script_exploder) )
|
||||
level thread maps\mp\_load::exploder_load( triggers[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
thread maps\mp\_animatedmodels::main();
|
||||
|
||||
// auto-sentry
|
||||
level.func[ "damagefeedback" ] = maps\mp\gametypes\_damagefeedback::updateDamageFeedback;
|
||||
level.func[ "setTeamHeadIcon" ] = maps\mp\_entityheadicons::setTeamHeadIcon;
|
||||
level.laserOn_func = ::laserOn;
|
||||
level.laserOff_func = ::laserOff;
|
||||
|
||||
level.connectPathsFunction = ::connectPaths;
|
||||
level.disconnectPathsFunction = ::disconnectPaths;
|
||||
|
||||
// defaults
|
||||
setDvar( "sm_sunShadowScale", 1 );
|
||||
setDvar( "sm_spotLightScoreModelScale", 0 );
|
||||
setDvar( "r_specularcolorscale", 2.5 );
|
||||
setDvar( "r_diffusecolorscale", 1 );
|
||||
setDvar( "r_lightGridEnableTweaks", 0 );
|
||||
setDvar( "r_lightGridIntensity", 1 );
|
||||
setDvar( "r_lightGridContrast", 0 );
|
||||
|
||||
// created these so we can turn things on/off individually without having to set the game to hardcore
|
||||
SetDvar( "ui_showInfo", 1 );
|
||||
SetDvar( "ui_showMinimap", 1 );
|
||||
|
||||
// setup kill cam ents on destructibles
|
||||
setupDestructibleKillCamEnts();
|
||||
|
||||
// precache the bomb site weapon
|
||||
PreCacheItem( "bomb_site_mp" );
|
||||
|
||||
// precache dog fur for PC
|
||||
if ( !level.console )
|
||||
{
|
||||
level.furFX = loadfx( "vfx/apex/nv_dog_a" );
|
||||
level.wolfFurFX = [];
|
||||
level.wolfFurFX[0] = loadfx( "vfx/apex/nv_wolf_b" );
|
||||
level.wolfFurFX[1] = loadfx( "vfx/apex/nv_wolf_c" );
|
||||
}
|
||||
|
||||
level.fauxVehicleCount = 0; // this keeps an artificial count so we can check airspace and vehicles limits preemptively
|
||||
level.littlebird_model = "vehicle_aas_72x_killstreak";
|
||||
|
||||
/#
|
||||
// when host migration happens, all of the dev dvars get cleared, so this will reset them all
|
||||
level thread reInitializeDevDvarsOnMigration();
|
||||
#/
|
||||
}
|
||||
|
||||
exploder_load( trigger )
|
||||
{
|
||||
level endon( "killexplodertridgers" + trigger.script_exploder );
|
||||
trigger waittill( "trigger" );
|
||||
if ( isdefined( trigger.script_chance ) && randomfloat( 1 ) > trigger.script_chance )
|
||||
{
|
||||
if ( isdefined( trigger.script_delay ) )
|
||||
wait trigger.script_delay;
|
||||
else
|
||||
wait 4;
|
||||
level thread exploder_load( trigger );
|
||||
return;
|
||||
}
|
||||
exploder( trigger.script_exploder );
|
||||
level notify( "killexplodertridgers" + trigger.script_exploder );
|
||||
}
|
||||
|
||||
|
||||
setupExploders()
|
||||
{
|
||||
// Hide exploder models.
|
||||
ents = getentarray( "script_brushmodel", "classname" );
|
||||
smodels = getentarray( "script_model", "classname" );
|
||||
for ( i = 0;i < smodels.size;i ++ )
|
||||
ents[ ents.size ] = smodels[ i ];
|
||||
|
||||
for ( i = 0;i < ents.size;i ++ )
|
||||
{
|
||||
if ( isdefined( ents[ i ].script_prefab_exploder ) )
|
||||
ents[ i ].script_exploder = ents[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( ents[ i ].script_exploder ) )
|
||||
{
|
||||
if ( ( ents[ i ].model == "fx" ) && ( ( !isdefined( ents[ i ].targetname ) ) || ( ents[ i ].targetname != "exploderchunk" ) ) )
|
||||
ents[ i ] hide();
|
||||
else if ( ( isdefined( ents[ i ].targetname ) ) && ( ents[ i ].targetname == "exploder" ) )
|
||||
{
|
||||
ents[ i ] hide();
|
||||
ents[ i ] notsolid();
|
||||
//if ( isdefined( ents[ i ].script_disconnectpaths ) )
|
||||
//ents[ i ] connectpaths();
|
||||
}
|
||||
else if ( ( isdefined( ents[ i ].targetname ) ) && ( ents[ i ].targetname == "exploderchunk" ) )
|
||||
{
|
||||
ents[ i ] hide();
|
||||
ents[ i ] notsolid();
|
||||
//if ( isdefined( ents[ i ].spawnflags ) && ( ents[ i ].spawnflags & 1 ) )
|
||||
//ents[ i ] connectpaths();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script_exploders = [];
|
||||
|
||||
potentialExploders = getentarray( "script_brushmodel", "classname" );
|
||||
for ( i = 0;i < potentialExploders.size;i ++ )
|
||||
{
|
||||
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
|
||||
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( potentialExploders[ i ].script_exploder ) )
|
||||
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
|
||||
}
|
||||
|
||||
potentialExploders = getentarray( "script_model", "classname" );
|
||||
for ( i = 0;i < potentialExploders.size;i ++ )
|
||||
{
|
||||
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
|
||||
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( potentialExploders[ i ].script_exploder ) )
|
||||
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
|
||||
}
|
||||
|
||||
potentialExploders = getentarray( "item_health", "classname" );
|
||||
for ( i = 0;i < potentialExploders.size;i ++ )
|
||||
{
|
||||
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
|
||||
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
|
||||
|
||||
if ( isdefined( potentialExploders[ i ].script_exploder ) )
|
||||
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
|
||||
}
|
||||
|
||||
if ( !isdefined( level.createFXent ) )
|
||||
level.createFXent = [];
|
||||
|
||||
acceptableTargetnames = [];
|
||||
acceptableTargetnames[ "exploderchunk visible" ] = true;
|
||||
acceptableTargetnames[ "exploderchunk" ] = true;
|
||||
acceptableTargetnames[ "exploder" ] = true;
|
||||
|
||||
for ( i = 0; i < script_exploders.size; i ++ )
|
||||
{
|
||||
exploder = script_exploders[ i ];
|
||||
ent = createExploder( exploder.script_fxid );
|
||||
ent.v = [];
|
||||
ent.v[ "origin" ] = exploder.origin;
|
||||
ent.v[ "angles" ] = exploder.angles;
|
||||
ent.v[ "delay" ] = exploder.script_delay;
|
||||
ent.v[ "firefx" ] = exploder.script_firefx;
|
||||
ent.v[ "firefxdelay" ] = exploder.script_firefxdelay;
|
||||
ent.v[ "firefxsound" ] = exploder.script_firefxsound;
|
||||
ent.v[ "firefxtimeout" ] = exploder.script_firefxtimeout;
|
||||
ent.v[ "earthquake" ] = exploder.script_earthquake;
|
||||
ent.v[ "damage" ] = exploder.script_damage;
|
||||
ent.v[ "damage_radius" ] = exploder.script_radius;
|
||||
ent.v[ "soundalias" ] = exploder.script_soundalias;
|
||||
ent.v[ "repeat" ] = exploder.script_repeat;
|
||||
ent.v[ "delay_min" ] = exploder.script_delay_min;
|
||||
ent.v[ "delay_max" ] = exploder.script_delay_max;
|
||||
ent.v[ "target" ] = exploder.target;
|
||||
ent.v[ "ender" ] = exploder.script_ender;
|
||||
ent.v[ "type" ] = "exploder";
|
||||
// ent.v[ "worldfx" ] = true;
|
||||
if ( !isdefined( exploder.script_fxid ) )
|
||||
ent.v[ "fxid" ] = "No FX";
|
||||
else
|
||||
ent.v[ "fxid" ] = exploder.script_fxid;
|
||||
ent.v[ "exploder" ] = exploder.script_exploder;
|
||||
assertEx( isdefined( exploder.script_exploder ), "Exploder at origin " + exploder.origin + " has no script_exploder" );
|
||||
|
||||
if ( !isdefined( ent.v[ "delay" ] ) )
|
||||
ent.v[ "delay" ] = 0;
|
||||
|
||||
if ( isdefined( exploder.target ) )
|
||||
{
|
||||
org = getent( ent.v[ "target" ], "targetname" ).origin;
|
||||
ent.v[ "angles" ] = vectortoangles( org - ent.v[ "origin" ] );
|
||||
// forward = anglestoforward( angles );
|
||||
// up = anglestoup( angles );
|
||||
}
|
||||
|
||||
// this basically determines if its a brush / model exploder or not
|
||||
if ( exploder.classname == "script_brushmodel" || isdefined( exploder.model ) )
|
||||
{
|
||||
ent.model = exploder;
|
||||
ent.model.disconnect_paths = exploder.script_disconnectpaths;
|
||||
}
|
||||
|
||||
if ( isdefined( exploder.targetname ) && isdefined( acceptableTargetnames[ exploder.targetname ] ) )
|
||||
ent.v[ "exploder_type" ] = exploder.targetname;
|
||||
else
|
||||
ent.v[ "exploder_type" ] = "normal";
|
||||
|
||||
ent common_scripts\_createfx::post_entity_creation_function();
|
||||
}
|
||||
}
|
||||
|
||||
lanterns()
|
||||
{
|
||||
if (!isdefined(level._effect["lantern_light"]))
|
||||
level._effect["lantern_light"] = loadfx("fx/props/glow_latern");
|
||||
|
||||
loopfx("lantern_light", self.origin, 0.3, self.origin + (0,0,1));
|
||||
}
|
||||
|
||||
setupDestructibleKillCamEnts()
|
||||
{
|
||||
// TODO: this needs to be done in code
|
||||
|
||||
destructible_vehicles = GetEntArray( "scriptable_destructible_vehicle", "targetname" );
|
||||
foreach( dest in destructible_vehicles )
|
||||
{
|
||||
bulletStart = dest.origin + ( 0, 0, 5 );
|
||||
bulletEnd = ( dest.origin + ( 0, 0, 128 ) );
|
||||
result = BulletTrace( bulletStart, bulletEnd, false, dest );
|
||||
dest.killCamEnt = Spawn( "script_model", result[ "position" ] );
|
||||
dest.killCamEnt.targetname = "killCamEnt_destructible_vehicle";
|
||||
dest.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
dest thread deleteDestructibleKillCamEnt();
|
||||
}
|
||||
|
||||
// Not needed anymore?
|
||||
// destructible_toys = GetEntArray( "destructible_toy", "targetname" );
|
||||
// foreach( dest in destructible_toys )
|
||||
// {
|
||||
// bulletStart = dest.origin + ( 0, 0, 5 );
|
||||
// bulletEnd = ( dest.origin + ( 0, 0, 128 ) );
|
||||
// result = BulletTrace( bulletStart, bulletEnd, false, dest );
|
||||
// dest.killCamEnt = Spawn( "script_model", result[ "position" ] );
|
||||
// dest.killCamEnt.targetname = "killCamEnt_destructible_toy";
|
||||
// dest.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
// dest thread deleteDestructibleKillCamEnt();
|
||||
// }
|
||||
//
|
||||
explodable_barrels = GetEntArray( "scriptable_destructible_barrel", "targetname" );
|
||||
foreach( dest in explodable_barrels )
|
||||
{
|
||||
bulletStart = dest.origin + ( 0, 0, 5 );
|
||||
bulletEnd = ( dest.origin + ( 0, 0, 128 ) );
|
||||
result = BulletTrace( bulletStart, bulletEnd, false, dest );
|
||||
dest.killCamEnt = Spawn( "script_model", result[ "position" ] );
|
||||
dest.killCamEnt.targetname = "killCamEnt_explodable_barrel";
|
||||
dest.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
dest thread deleteDestructibleKillCamEnt();
|
||||
}
|
||||
|
||||
// ADD MORE DESTRUCTIBLES HERE IF APPLICABLE
|
||||
}
|
||||
|
||||
deleteDestructibleKillCamEnt()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
killCamEnt = self.killCamEnt;
|
||||
killCamEnt endon( "death" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
wait( 10 );
|
||||
if( IsDefined( killCamEnt ) )
|
||||
killCamEnt delete();
|
||||
}
|
||||
598
maps/mp/_matchdata.gsc
Normal file
598
maps/mp/_matchdata.gsc
Normal file
@@ -0,0 +1,598 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !isDefined( game["gamestarted"] ) )
|
||||
{
|
||||
//setMatchDataDef( "mp/matchdata_" + level.gametype + ".def" );
|
||||
setMatchDataDef( "mp/matchdata.def" );
|
||||
setMatchData( "map", level.script );
|
||||
if( level.hardcoremode )
|
||||
{
|
||||
tmp = level.gametype + " hc";
|
||||
setMatchData( "gametype", tmp );
|
||||
}
|
||||
else
|
||||
{
|
||||
setMatchData( "gametype", level.gametype );
|
||||
}
|
||||
setMatchData( "buildVersion", getBuildVersion() );
|
||||
setMatchData( "buildNumber", getBuildNumber() );
|
||||
setMatchData( "dateTime", getSystemTime() );
|
||||
setMatchDataID();
|
||||
}
|
||||
|
||||
level.MaxLives = 285; // must match MaxKills in matchdata definition
|
||||
level.MaxNameLength = 26; // must match Player xuid size in clientmatchdata definition
|
||||
level.MaxEvents = 150;
|
||||
level.MaxKillstreaks = 64;
|
||||
level.MaxLogClients = 30;
|
||||
level.MaxNumChallengesPerPlayer = 10;
|
||||
level.MaxNumAwardsPerPlayer = 10;
|
||||
|
||||
if ( !is_aliens() )
|
||||
{
|
||||
level thread gameEndListener();
|
||||
level thread endOfGameSummaryLogger();
|
||||
}
|
||||
}
|
||||
|
||||
getMatchDateTime()
|
||||
{
|
||||
return GetMatchData( "dateTime" );
|
||||
}
|
||||
|
||||
logKillstreakEvent( event, position )
|
||||
{
|
||||
assertEx( IsGameParticipant( self ), "self is not a player: " + self.code_classname );
|
||||
|
||||
if ( !canLogClient( self ) || !canLogKillstreak() )
|
||||
return;
|
||||
|
||||
eventId = getMatchData( "killstreakCount" );
|
||||
setMatchData( "killstreakCount", eventId+1 );
|
||||
|
||||
setMatchData( "killstreaks", eventId, "eventType", event );
|
||||
setMatchData( "killstreaks", eventId, "player", self.clientid );
|
||||
setMatchData( "killstreaks", eventId, "eventTime", getTime() );
|
||||
setMatchData( "killstreaks", eventId, "eventPos", 0, int( position[0] ) );
|
||||
setMatchData( "killstreaks", eventId, "eventPos", 1, int( position[1] ) );
|
||||
setMatchData( "killstreaks", eventId, "eventPos", 2, int( position[2] ) );
|
||||
}
|
||||
|
||||
|
||||
logGameEvent( event, position )
|
||||
{
|
||||
assertEx( IsGameParticipant( self ), "self is not a player: " + self.code_classname );
|
||||
|
||||
if ( !canLogClient( self ) || !canLogEvent() )
|
||||
return;
|
||||
|
||||
eventId = getMatchData( "eventCount" );
|
||||
setMatchData( "eventCount", eventId+1 );
|
||||
|
||||
setMatchData( "events", eventId, "eventType", event );
|
||||
setMatchData( "events", eventId, "player", self.clientid );
|
||||
setMatchData( "events", eventId, "eventTime", getTime() );
|
||||
setMatchData( "events", eventId, "eventPos", 0, int( position[0] ) );
|
||||
setMatchData( "events", eventId, "eventPos", 1, int( position[1] ) );
|
||||
setMatchData( "events", eventId, "eventPos", 2, int( position[2] ) );
|
||||
}
|
||||
|
||||
|
||||
logKillEvent( lifeId, eventRef )
|
||||
{
|
||||
if ( !canLogLife( lifeId ) )
|
||||
return;
|
||||
|
||||
setMatchData( "lives", lifeId, "modifiers", eventRef, true );
|
||||
}
|
||||
|
||||
|
||||
logMultiKill( lifeId, multikillCount )
|
||||
{
|
||||
if ( !canLogLife( lifeId ) )
|
||||
return;
|
||||
|
||||
setMatchData( "lives", lifeId, "multikill", multikillCount );
|
||||
}
|
||||
|
||||
|
||||
logPlayerLife()
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
lifeId = level.MaxLives;
|
||||
|
||||
if ( self.curClass == "gamemode" )
|
||||
{
|
||||
lifeId = self LogMatchDataLife( self.clientid, self.spawnPos, self.spawnTime, self.wasTI );
|
||||
}
|
||||
else if ( IsSubStr( self.curClass, "custom" ) )
|
||||
{
|
||||
class_num = getClassIndex( self.curClass );
|
||||
|
||||
primaryWeapon = maps\mp\gametypes\_class::cac_getWeapon( class_num, 0 );
|
||||
primaryAttachment1 = maps\mp\gametypes\_class::cac_getWeaponAttachment( class_num, 0 );
|
||||
primaryAttachment2 = maps\mp\gametypes\_class::cac_getWeaponAttachmentTwo( class_num, 0 );
|
||||
|
||||
secondaryWeapon = maps\mp\gametypes\_class::cac_getWeapon( class_num, 1 );
|
||||
secondaryAttachment1 = maps\mp\gametypes\_class::cac_getWeaponAttachment( class_num, 1 );
|
||||
secondaryAttachment2 = maps\mp\gametypes\_class::cac_getWeaponAttachmentTwo( class_num, 1 );
|
||||
|
||||
offhandWeapon = "none"; //maps\mp\gametypes\_class::cac_getOffhand( class_num );
|
||||
|
||||
equipment = maps\mp\gametypes\_class::cac_getPerk( class_num, 0 );
|
||||
|
||||
lifeId = self LogMatchDataLife( self.clientid, self.spawnPos, self.spawnTime, self.wasTI, primaryWeapon, primaryAttachment1, primaryAttachment2, secondaryWeapon, secondaryAttachment1, secondaryAttachment2, offhandWeapon, equipment );
|
||||
self logPlayerAbilityPerks( lifeId );
|
||||
}
|
||||
else
|
||||
{
|
||||
class_num = getClassIndex( self.curClass );
|
||||
|
||||
primaryWeapon = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 0 );
|
||||
primaryAttachment1 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0 , 0);
|
||||
primaryAttachment2 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0, 1 );
|
||||
|
||||
secondaryWeapon = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 1 );
|
||||
secondaryAttachment1 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1 , 0);
|
||||
secondaryAttachment2 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1, 1 );;
|
||||
|
||||
offhandWeapon = maps\mp\gametypes\_class::table_getOffhand( level.classTableName, class_num );
|
||||
|
||||
equipment = maps\mp\gametypes\_class::table_getEquipment( level.classTableName, class_num, 0 );
|
||||
|
||||
lifeId = self LogMatchDataLife( self.clientid, self.spawnPos, self.spawnTime, self.wasTI, primaryWeapon, primaryAttachment1, primaryAttachment2, secondaryWeapon, secondaryAttachment1, secondaryAttachment2, offhandWeapon, equipment );
|
||||
self logPlayerAbilityPerks( lifeId );
|
||||
}
|
||||
|
||||
return lifeId;
|
||||
}
|
||||
|
||||
// 2014-09-10 wallace: in IW6, we changed how perks (abilities) are stored, so for much of the game's life, we weren't tracking
|
||||
// which perks players were using with their loadouts. We add this data now to try to capture some of that data in the live game
|
||||
// We don't care about game mode specific loadouts, so we skip that case
|
||||
logPlayerAbilityPerks( lifeId ) // self == player
|
||||
{
|
||||
// check dvar
|
||||
if ( GetDvarInt( "scr_trackPlayerAbilities", 0 ) != 0 )
|
||||
{
|
||||
if ( IsDefined( self.abilityFlags ) && self.abilityFlags.size == 2 )
|
||||
{
|
||||
SetMatchData( "lives", lifeId, "abilityFlags", 0, self.abilityFlags[0] );
|
||||
SetMatchData( "lives", lifeId, "abilityFlags", 1, self.abilityFlags[1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logPlayerXP( xp, xpName )
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
setMatchData( "players", self.clientid, xpName, xp );
|
||||
}
|
||||
|
||||
|
||||
logPlayerDeath( lifeId, attacker, iDamage, sMeansOfDeath, sWeapon, sPrimaryWeapon, sHitLoc )
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
if ( lifeId >= level.MaxLives )
|
||||
return;
|
||||
|
||||
// JC-ToDo: May need to tokenize the weapon name and change the attachments to their base name. That or code needs to do this in the LogMatchDataDeath()
|
||||
|
||||
if ( IsPlayer( attacker ) && canLogClient( attacker ) )
|
||||
self LogMatchDataDeath( lifeId, self.clientid, attacker, attacker.clientid, sWeapon, sMeansOfDeath, isKillstreakWeapon( sWeapon ), attacker isJuggernaut() );
|
||||
else
|
||||
self LogMatchDataDeath( lifeId, self.clientid, undefined, undefined, sWeapon, sMeansOfDeath, isKillstreakWeapon( sWeapon ), false );
|
||||
}
|
||||
|
||||
|
||||
logPlayerData()
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
setMatchData( "players", self.clientid, "score", self getPersStat( "score" ) );
|
||||
|
||||
if( self getPersStat( "assists" ) > 255 )
|
||||
setMatchData( "players", self.clientid, "assists", 255 );
|
||||
else
|
||||
setMatchData( "players", self.clientid, "assists", self getPersStat( "assists" ) );
|
||||
|
||||
if( self getPersStat( "longestStreak" ) > 255 )
|
||||
setMatchData( "players", self.clientid, "longestStreak", 255 );
|
||||
else
|
||||
setMatchData( "players", self.clientid, "longestStreak", self getPersStat( "longestStreak" ) );
|
||||
|
||||
if( self getPersStat( "validationInfractions" ) > 255 )
|
||||
setMatchData( "players", self.clientid, "validationInfractions", 255 );
|
||||
else
|
||||
setMatchData( "players", self.clientid, "validationInfractions", self getPersStat( "validationInfractions" ) );
|
||||
}
|
||||
|
||||
|
||||
// log the weapons and weaponXP to playerdata.
|
||||
endOfGameSummaryLogger()
|
||||
{
|
||||
level waittill ( "game_ended" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
wait( 0.05 );
|
||||
|
||||
//player may disconnect during waits
|
||||
if ( !isdefined( player ) )
|
||||
continue;
|
||||
|
||||
if ( isDefined( player.detectedExploit ) && player.detectedExploit && ( player rankingEnabled() ) )
|
||||
player setRankedPlayerData( "restXPGoal", player.detectedExploit );
|
||||
|
||||
if ( isDefined ( player.weaponsUsed ) )
|
||||
{
|
||||
player doubleBubbleSort();
|
||||
counter = 0;
|
||||
|
||||
if ( player.weaponsUsed.size > 3 )
|
||||
{
|
||||
for ( i = (player.weaponsUsed.size - 1); i > (player.weaponsUsed.size - 3); i-- )
|
||||
{
|
||||
player setCommonPlayerData( "round", "weaponsUsed", counter, player.weaponsUsed[i] );
|
||||
player setCommonPlayerData( "round", "weaponXpEarned", counter, player.weaponXpEarned[i] );
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( i = (player.weaponsUsed.size - 1); i >= 0; i-- )
|
||||
{
|
||||
player setCommonPlayerData( "round", "weaponsUsed", counter, player.weaponsUsed[i] );
|
||||
player setCommonPlayerData( "round", "weaponXpEarned", counter, player.weaponXpEarned[i] );
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player setCommonPlayerData( "round", "weaponsUsed", 0, "none" );
|
||||
player setCommonPlayerData( "round", "weaponsUsed", 1, "none" );
|
||||
player setCommonPlayerData( "round", "weaponsUsed", 2, "none" );
|
||||
player setCommonPlayerData( "round", "weaponXpEarned", 0, 0 );
|
||||
player setCommonPlayerData( "round", "weaponXpEarned", 1, 0 );
|
||||
player setCommonPlayerData( "round", "weaponXpEarned", 2, 0 );
|
||||
}
|
||||
|
||||
//log operations
|
||||
if ( isDefined ( player.operationsCompleted ) )
|
||||
{
|
||||
player setCommonPlayerData( "round", "operationNumCompleted", player.operationsCompleted.size );
|
||||
}
|
||||
else
|
||||
{
|
||||
player setCommonPlayerData( "round", "operationNumCompleted", 0 );
|
||||
}
|
||||
|
||||
for ( i = 0; i < 5; i++ )
|
||||
{
|
||||
if ( isDefined( player.operationsCompleted ) && isDefined( player.operationsCompleted[i] ) && player.operationsCompleted[i] != "ch_prestige" && !IsSubStr( player.operationsCompleted[i], "_daily" ) && !IsSubStr( player.operationsCompleted[i], "_weekly" ) )
|
||||
player setCommonPlayerData( "round", "operationsCompleted", i, player.operationsCompleted[i] );
|
||||
else
|
||||
player setCommonPlayerData( "round", "operationsCompleted", i, "" );
|
||||
}
|
||||
|
||||
//log challenges
|
||||
if ( isDefined ( player.challengesCompleted ) )
|
||||
{
|
||||
player setCommonPlayerData( "round", "challengeNumCompleted", player.challengesCompleted.size );
|
||||
}
|
||||
else
|
||||
{
|
||||
player setCommonPlayerData( "round", "challengeNumCompleted", 0 );
|
||||
}
|
||||
|
||||
for ( i = 0; i < 20; i++ )
|
||||
{
|
||||
if ( isDefined( player.challengesCompleted ) && isDefined( player.challengesCompleted[i] ) && player.challengesCompleted[i] != "ch_prestige" && !IsSubStr( player.challengesCompleted[i], "_daily" ) && !IsSubStr( player.challengesCompleted[i], "_weekly" ) )
|
||||
player setCommonPlayerData( "round", "challengesCompleted", i, player.challengesCompleted[i] );
|
||||
else
|
||||
player setCommonPlayerData( "round", "challengesCompleted", i, "" );
|
||||
}
|
||||
|
||||
|
||||
player setCommonPlayerData( "round", "gameMode", level.gametype );
|
||||
player setCommonPlayerData( "round", "map", ToLower( GetDvar( "mapname" ) ) );
|
||||
if ( IsSquadsMode() )
|
||||
{
|
||||
player setCommonPlayerData( "round", "squadMode", 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
player setCommonPlayerData( "round", "squadMode", 0 );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
doubleBubbleSort()
|
||||
{
|
||||
A = self.weaponXpEarned;
|
||||
n = self.weaponXpEarned.size;
|
||||
|
||||
for (i =(n-1); i > 0; i--)
|
||||
{
|
||||
for (j = 1; j <= i; j++)
|
||||
{
|
||||
if( A[j-1] < A[j] )
|
||||
{
|
||||
temp = self.weaponsUsed[j];
|
||||
self.weaponsUsed[j] = self.weaponsUsed[j-1];
|
||||
self.weaponsUsed[j-1] = temp;
|
||||
|
||||
temp2 = self.weaponXpEarned[j];
|
||||
self.weaponXpEarned[j] = self.weaponXpEarned[j-1];
|
||||
self.weaponXpEarned[j-1] = temp2;
|
||||
A = self.weaponXpEarned;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*Recursive nonsense sorts based on array 1 and sorts array 2's indexes (should be logn)
|
||||
quickDoubleSort()
|
||||
{
|
||||
quickDoubleSortMid( 0, self.weaponsUsed.size -1 );
|
||||
}
|
||||
quickDoubleSortMid( start, end )
|
||||
{
|
||||
i = start;
|
||||
k = end;
|
||||
|
||||
if (end - start >= 1)
|
||||
{
|
||||
pivot = self.weaponXpEarned[start];
|
||||
|
||||
while (k > i)
|
||||
{
|
||||
while (self.weaponXpEarned[i] <= pivot && i <= end && k > i)
|
||||
i++;
|
||||
while (self.weaponXpEarned[k] > pivot && k >= start && k >= i)
|
||||
k--;
|
||||
if (k > i)
|
||||
self.weaponXpEarned = doubleSwap( i, k );
|
||||
}
|
||||
array = doubleSwap( start, k );
|
||||
array = quickDoubleSortMid(start, k - 1);
|
||||
array = quickDoubleSortMid(k + 1, end);
|
||||
}
|
||||
}
|
||||
doubleSwap(index1, index2)
|
||||
{
|
||||
temp = self.weaponsUsed[index1];
|
||||
self.weaponsUsed[index1] = self.weaponsUsed[index2];
|
||||
self.weaponsUsed[index2] = temp;
|
||||
|
||||
temp2 = self.weaponXpEarned[index1];
|
||||
self.weaponXpEarned[index1] = self.weaponXpEarned[index2];
|
||||
self.weaponXpEarned[index2] = temp2;
|
||||
}
|
||||
*///end recursive nightmare sort.
|
||||
|
||||
|
||||
// log the lives of players who are still alive at match end.
|
||||
gameEndListener()
|
||||
{
|
||||
level waittill ( "game_ended" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player logPlayerData();
|
||||
|
||||
if ( !isAlive( player ) )
|
||||
continue;
|
||||
|
||||
player logPlayerLife();
|
||||
}
|
||||
}
|
||||
|
||||
canLogClient( client )
|
||||
{
|
||||
if ( IsAgent( client ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
assertEx( isPlayer( client ) , "Client is not a player: " + client.code_classname );
|
||||
return ( client.clientid < level.MaxLogClients );
|
||||
}
|
||||
|
||||
canLogEvent()
|
||||
{
|
||||
return ( getMatchData( "eventCount" ) < level.MaxEvents );
|
||||
}
|
||||
|
||||
canLogKillstreak()
|
||||
{
|
||||
return ( getMatchData( "killstreakCount" ) < level.MaxKillstreaks );
|
||||
}
|
||||
|
||||
canLogLife( lifeId )
|
||||
{
|
||||
return ( getMatchData( "lifeCount" ) < level.MaxLives );
|
||||
}
|
||||
|
||||
logWeaponStat( weaponName, statName, incValue )
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
// HACK for gold pdw / knife. This should use weaponMap() but in this
|
||||
// case the weapon is the script base name not the code base
|
||||
// name. Next project no script base names! - JC
|
||||
if ( weaponName == "iw6_pdwauto" )
|
||||
weaponName = "iw6_pdw";
|
||||
else if ( weaponName == "iw6_knifeonlyfast" )
|
||||
weaponName = "iw6_knifeonly";
|
||||
|
||||
if( isKillstreakWeapon( weaponName ) )
|
||||
return;
|
||||
|
||||
self storeWeaponAndAttachmentStats( "weaponStats", weaponName, statName, incValue );
|
||||
}
|
||||
|
||||
logAttachmentStat( weaponName, statName, incValue )
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
self storeWeaponAndAttachmentStats( "attachmentsStats", weaponName, statName, incValue );
|
||||
}
|
||||
|
||||
storeWeaponAndAttachmentStats( statCategory, weaponName, statName, incValue )
|
||||
{
|
||||
oldValue = GetMatchData( "players", self.clientid, statCategory, weaponName, statName );
|
||||
newValue = oldValue + incValue;
|
||||
|
||||
// these values are bytes - see itemStats in matchdata.def
|
||||
if( statName == "kills" || statName == "deaths" || statName == "headShots" )
|
||||
{
|
||||
if (newValue > 255)
|
||||
newValue = 255;
|
||||
}
|
||||
// we assume the rest to be shorts
|
||||
else if (newValue > 65535)
|
||||
{
|
||||
newValue = 65535;
|
||||
}
|
||||
|
||||
SetMatchData( "players", self.clientid, statCategory, weaponName, statName, newValue );
|
||||
}
|
||||
|
||||
buildBaseWeaponList()
|
||||
{
|
||||
baseWeapons = [];
|
||||
max_weapon_num = 149;
|
||||
for( weaponId = 0; weaponId <= max_weapon_num; weaponId++ )
|
||||
{
|
||||
weapon_name = tablelookup( "mp/statstable.csv", 0, weaponId, 4 );
|
||||
|
||||
// HACK - Make sure the gold knife stats table entries do not get put into the weapon list. - JC
|
||||
if (
|
||||
weapon_name == ""
|
||||
|| weapon_name == "uav"
|
||||
|| weapon_name == "iw6_knifeonlyfast"
|
||||
|| weapon_name == "laser_designator"
|
||||
|| weapon_name == "iw6_pdwauto"
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !isSubStr( tableLookup( "mp/statsTable.csv", 0, weaponId, 2 ), "weapon_" ) )
|
||||
continue;
|
||||
|
||||
if ( tableLookup( "mp/statsTable.csv", 0, weaponId, 2 ) == "weapon_other" )
|
||||
continue;
|
||||
|
||||
baseWeapons[baseWeapons.size] = weapon_name;
|
||||
}
|
||||
return baseWeapons;
|
||||
}
|
||||
|
||||
logChallenge( challengeName, tier )
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
// we don't want to log daily and weekly challenges
|
||||
if( IsSubStr( challengeName, "_daily" ) || IsSubStr( challengeName, "_weekly" ) )
|
||||
return;
|
||||
|
||||
challengeCount = getMatchData( "players", self.clientid, "challengeCount" );
|
||||
if( challengeCount < level.MaxNumChallengesPerPlayer )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "challenge", challengeCount, challengeName );
|
||||
setMatchData( "players", self.clientid, "tier", challengeCount, tier );
|
||||
setMatchData( "players", self.clientid, "challengeCount", challengeCount + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
logAward( awardName )
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
awardCount = getMatchData( "players", self.clientid, "awardCount" );
|
||||
if( awardCount < level.MaxNumAwardsPerPlayer )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "awards", awardCount, awardName );
|
||||
setMatchData( "players", self.clientid, "awardCount", awardCount + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
logKillsConfirmed()
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
setMatchData( "players", self.clientid, "killsConfirmed", self.pers["confirmed"] );
|
||||
}
|
||||
|
||||
logKillsDenied()
|
||||
{
|
||||
if ( !canLogClient( self ) )
|
||||
return;
|
||||
|
||||
setMatchData( "players", self.clientid, "killsDenied", self.pers["denied"] );
|
||||
}
|
||||
|
||||
logInitialStats()
|
||||
{
|
||||
if ( GetDvarInt( "mdsd" ) > 0 )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "startXp", self getRankedPlayerData( "experience" ) );
|
||||
setMatchData( "players", self.clientid, "startKills", self getRankedPlayerData( "kills" ) );
|
||||
setMatchData( "players", self.clientid, "startDeaths", self getRankedPlayerData( "deaths" ) );
|
||||
setMatchData( "players", self.clientid, "startWins", self getRankedPlayerData( "wins" ) );
|
||||
setMatchData( "players", self.clientid, "startLosses", self getRankedPlayerData( "losses" ) );
|
||||
setMatchData( "players", self.clientid, "startHits", self getRankedPlayerData( "hits" ) );
|
||||
setMatchData( "players", self.clientid, "startMisses", self getRankedPlayerData( "misses" ) );
|
||||
setMatchData( "players", self.clientid, "startGamesPlayed", self getRankedPlayerData( "gamesPlayed" ) );
|
||||
setMatchData( "players", self.clientid, "startTimePlayedTotal", self getRankedPlayerData( "timePlayedTotal" ) );
|
||||
setMatchData( "players", self.clientid, "startScore", self getRankedPlayerData( "score" ) );
|
||||
setMatchData( "players", self.clientid, "startUnlockPoints", self getRankedPlayerData( "unlockPoints" ) );
|
||||
setMatchData( "players", self.clientid, "startPrestige", self getRankedPlayerData( "prestige" ) );
|
||||
|
||||
for ( squadMember = 0; squadMember < 10; squadMember++ )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "startCharacterXP", squadMember, self getRankedPlayerData( "characterXP", squadMember ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logFinalStats()
|
||||
{
|
||||
if ( GetDvarInt( "mdsd" ) > 0 )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "endXp", self getRankedPlayerData( "experience" ) );
|
||||
setMatchData( "players", self.clientid, "endKills", self getRankedPlayerData( "kills" ) );
|
||||
setMatchData( "players", self.clientid, "endDeaths", self getRankedPlayerData( "deaths" ) );
|
||||
setMatchData( "players", self.clientid, "endWins", self getRankedPlayerData( "wins" ) );
|
||||
setMatchData( "players", self.clientid, "endLosses", self getRankedPlayerData( "losses" ) );
|
||||
setMatchData( "players", self.clientid, "endHits", self getRankedPlayerData( "hits" ) );
|
||||
setMatchData( "players", self.clientid, "endMisses", self getRankedPlayerData( "misses" ) );
|
||||
setMatchData( "players", self.clientid, "endGamesPlayed", self getRankedPlayerData( "gamesPlayed" ) );
|
||||
setMatchData( "players", self.clientid, "endTimePlayedTotal", self getRankedPlayerData( "timePlayedTotal" ) );
|
||||
setMatchData( "players", self.clientid, "endScore", self getRankedPlayerData( "score" ) );
|
||||
setMatchData( "players", self.clientid, "endUnlockPoints", self getRankedPlayerData( "unlockPoints" ) );
|
||||
setMatchData( "players", self.clientid, "endPrestige", self getRankedPlayerData( "prestige" ) );
|
||||
|
||||
for ( squadMember = 0; squadMember < 10; squadMember++ )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "endCharacterXP", squadMember, self getRankedPlayerData( "characterXP", squadMember ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
532
maps/mp/_matchevents.gsc
Normal file
532
maps/mp/_matchevents.gsc
Normal file
@@ -0,0 +1,532 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Initialization
|
||||
|
||||
|
||||
|
||||
// add this back to common_mp_weapons.csv
|
||||
// nodamage rocket for visual only
|
||||
// weapon,mp/nodamage_rocket_mp
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
level.match_events_fx["smoke"] = loadFx( "fx/smoke/smoke_grenade_11sec_mp" );
|
||||
level.match_events_fx["tracer"] = loadFx( "fx/misc/tracer_incoming" );
|
||||
level.match_events_fx["explosion"] = loadFx( "fx/explosions/building_explosion_huge_gulag" );
|
||||
|
||||
|
||||
//precacheItem( "nodamage_rocket_mp" );
|
||||
|
||||
level.matchEvents["mortar"] = ::doMortar;
|
||||
level.matchEvents["smoke"] = ::doSmoke;
|
||||
level.matchEvents["airstrike"] = ::doAirstrike;
|
||||
level.matchEvents["pavelow"] = ::doPavelow;
|
||||
level.matchEvents["heli_insertion"] = ::doHeliInsertion;
|
||||
level.matchEvents["osprey_insertion"] = ::doOspreyInsertion;
|
||||
|
||||
level.matchEventStarted = false;
|
||||
level thread onPlayerConnect();
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
if ( level.prematchPeriod > 0 )
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
//player thread onPlayerSpawned();
|
||||
//player thread doHeliInsertion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "matchevent_started" );
|
||||
//level.alliesInsertChopper endon ("stopLinking");
|
||||
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
if ( isDefined( level.alliesInsertChopper ) && !level.alliesInsertChopper.droppedOff && level.prematchPeriod > 0 && self.team == "allies")
|
||||
{
|
||||
self PlayerLinkTo( level.alliesInsertChopper );
|
||||
level.alliesInsertChopper.linkedPlayers[level.alliesInsertChopper.linkedPlayers.size] = self;
|
||||
}
|
||||
else if ( isDefined( level.alliesInsertChopper ) && !level.alliesInsertChopper.droppedOff && level.prematchPeriod > 0 && self.team == "axis")
|
||||
{
|
||||
self PlayerLinkTo( level.axisInsertChopper );
|
||||
level.axisInsertChopper.linkedPlayers[level.axisInsertChopper.linkedPlayers.size] = self;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Utilities
|
||||
|
||||
|
||||
getMapCenter()
|
||||
{
|
||||
if ( isDefined( level.mapCenter ) )
|
||||
return level.mapCenter;
|
||||
|
||||
alliesStart = GetSpawnArray( "mp_tdm_spawn_allies_start");
|
||||
axisStart = GetSpawnArray( "mp_tdm_spawn_axis_start");
|
||||
if ( isDefined( alliesStart ) && isDefined( alliesStart[0] ) && isDefined( axisStart ) && isDefined( axisStart[0] ) )
|
||||
{
|
||||
halfDist = Distance( alliesStart[0].origin, axisStart[0].origin ) / 2;
|
||||
dir = vectorToAngles( alliesStart[0].origin - axisStart[0].origin );
|
||||
dir = vectorNormalize( dir );
|
||||
return alliesStart[0].origin + dir*halfDist;
|
||||
}
|
||||
return (0,0,0);
|
||||
}
|
||||
|
||||
|
||||
getStartSpawns()
|
||||
{
|
||||
alliesStart = GetSpawnArray( "mp_tdm_spawn_allies_start");
|
||||
axisStart = GetSpawnArray( "mp_tdm_spawn_axis_start");
|
||||
|
||||
if ( isDefined( alliesStart ) && isDefined( alliesStart[0] ) && isDefined( axisStart ) && isDefined( axisStart[0] ) )
|
||||
{
|
||||
startSpawns = [];
|
||||
startSpawns["axis"] = axisStart;
|
||||
startSpawns["allies"] = alliesStart;
|
||||
|
||||
return startSpawns;
|
||||
}
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Event - Heli Insertion
|
||||
|
||||
doHeliInsertion( teamHeli, axisPoint, alliesPoint )
|
||||
{
|
||||
spawnHeight = 1200;
|
||||
hoverOffset = 1200;
|
||||
leaveOffset = 1000;
|
||||
|
||||
if( !isdefined( teamHeli ) )
|
||||
teamHeli = "both";
|
||||
|
||||
if ( teamHeli == "axis" )
|
||||
{
|
||||
self thread insertaxisInsertChopper( axisPoint );
|
||||
}
|
||||
else if ( teamHeli == "allies" )
|
||||
{
|
||||
self thread insertalliesInsertChopper( alliesPoint );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread insertalliesInsertChopper( alliesPoint );
|
||||
self thread insertaxisInsertChopper( axisPoint );
|
||||
}
|
||||
}
|
||||
|
||||
insertalliesInsertChopper( pointOverRide )
|
||||
{
|
||||
startSpawns = getStartSpawns();
|
||||
spawnHeight = 1200;
|
||||
hoverOffset = 1200;
|
||||
leaveOffset = 1000;
|
||||
|
||||
if ( !isDefined( pointOverRide ) )
|
||||
pointOverRide = startSpawns["allies"][0];
|
||||
|
||||
// allies chopper
|
||||
forward1 = AnglesToForward( startSpawns["allies"][0].angles ) * 300;
|
||||
up1 = AnglesToUp( startSpawns["allies"][0].angles ) * spawnHeight;
|
||||
right1 = AnglesToRight( startSpawns["allies"][0].angles ) * 3200;
|
||||
left1 = AnglesToRight( startSpawns["allies"][0].angles ) * -3200;
|
||||
rightPos1 = startSpawns["allies"][0].origin+forward1+up1+right1;
|
||||
leftPos1 = startSpawns["allies"][0].origin+forward1+up1+left1;
|
||||
|
||||
alliesInsertChopper = spawnHelicopter( self, rightPos1, startSpawns["allies"][0].angles, "pavelow_mp", "vehicle_pavelow" );
|
||||
if ( !isDefined( alliesInsertChopper ) )
|
||||
return;
|
||||
|
||||
level.alliesInsertChopper = alliesInsertChopper;
|
||||
level.alliesInsertChopper.linkedPlayers = [];
|
||||
level.alliesInsertChopper.droppedOff = false;
|
||||
|
||||
// move to spawn position
|
||||
alliesInsertChopper Vehicle_SetSpeed( 50, 15 );
|
||||
alliesInsertChopper setVehGoalPos( startSpawns["allies"][0].origin + (0,0,hoverOffset/2), 1 );
|
||||
alliesInsertChopper waittill ( "goal" );
|
||||
|
||||
// lower to drop off
|
||||
alliesInsertChopper setyawspeed( 0, 1, 1 );
|
||||
alliesInsertChopper setVehGoalPos( startSpawns["allies"][0].origin + (0,0,hoverOffset/6), 1 );
|
||||
alliesInsertChopper waittill ( "goal" );
|
||||
|
||||
level.alliesInsertChopper.droppedOff = true;
|
||||
|
||||
foreach( player in level.alliesInsertChopper.linkedPlayers )
|
||||
{
|
||||
player Unlink();
|
||||
}
|
||||
|
||||
wait( 2 );
|
||||
|
||||
alliesInsertChopper SetYawSpeed( 60, 40, 40, 0.3 );
|
||||
alliesInsertChopper setVehGoalPos( startSpawns["allies"][0].origin + (0,0,hoverOffset), 1 );
|
||||
alliesInsertChopper waittill ( "goal" );
|
||||
|
||||
// rise to leave
|
||||
|
||||
alliesInsertChopper Vehicle_SetSpeed( 80, 60 );
|
||||
alliesInsertChopper setVehGoalPos( rightPos1+(0,0,leaveOffset)+right1*2, 1 );
|
||||
|
||||
alliesInsertChopper waittill ( "goal" );
|
||||
|
||||
// leave
|
||||
|
||||
alliesInsertChopper Vehicle_SetSpeed( 120, 120 );
|
||||
alliesInsertChopper setVehGoalPos( rightPos1+(0,0,leaveOffset)+right1*2+forward1*-20, 1 );
|
||||
|
||||
alliesInsertChopper waittill ( "goal" );
|
||||
alliesInsertChopper delete();
|
||||
}
|
||||
|
||||
insertaxisInsertChopper( pointOverRide )
|
||||
{
|
||||
startSpawns = getStartSpawns();
|
||||
spawnHeight = 1200;
|
||||
hoverOffset = 1200;
|
||||
leaveOffset = 1000;
|
||||
|
||||
// axis chopper
|
||||
forward1 = AnglesToForward( startSpawns["axis"][0].angles ) * 300;
|
||||
up1 = AnglesToUp( startSpawns["axis"][0].angles ) * spawnHeight;
|
||||
right1 = AnglesToRight( startSpawns["axis"][0].angles ) * 3200;
|
||||
left1 = AnglesToRight( startSpawns["axis"][0].angles ) * -3200;
|
||||
rightPos1 = startSpawns["axis"][0].origin+forward1+up1+right1;
|
||||
leftPos1 = startSpawns["axis"][0].origin+forward1+up1+left1;
|
||||
|
||||
axisInsertChopper = spawnHelicopter( self, rightPos1, startSpawns["axis"][0].angles, "pavelow_mp", "vehicle_pavelow" );
|
||||
if ( !isDefined( axisInsertChopper ) )
|
||||
return;
|
||||
|
||||
level.axisInsertChopper = axisInsertChopper;
|
||||
level.axisInsertChopper.linkedPlayers = [];
|
||||
level.axisInsertChopper.droppedOff = false;
|
||||
|
||||
// move to spawn position
|
||||
axisInsertChopper Vehicle_SetSpeed( 50, 15 );
|
||||
axisInsertChopper setVehGoalPos( startSpawns["axis"][0].origin + (0,0,hoverOffset/2), 1 );
|
||||
axisInsertChopper waittill ( "goal" );
|
||||
|
||||
// lower to drop off
|
||||
axisInsertChopper setyawspeed( 0, 1, 1 );
|
||||
axisInsertChopper setVehGoalPos( startSpawns["axis"][0].origin + (0,0,hoverOffset/6), 1 );
|
||||
axisInsertChopper waittill ( "goal" );
|
||||
|
||||
level.axisInsertChopper.droppedOff = true;
|
||||
|
||||
foreach( player in level.axisInsertChopper.linkedPlayers )
|
||||
{
|
||||
player Unlink();
|
||||
}
|
||||
|
||||
wait( 2 );
|
||||
|
||||
axisInsertChopper SetYawSpeed( 60, 40, 40, 0.3 );
|
||||
axisInsertChopper setVehGoalPos( startSpawns["axis"][0].origin + (0,0,hoverOffset), 1 );
|
||||
axisInsertChopper waittill ( "goal" );
|
||||
|
||||
// rise to leave
|
||||
axisInsertChopper Vehicle_SetSpeed( 80, 60 );
|
||||
axisInsertChopper setVehGoalPos( rightPos1+(0,0,leaveOffset)+right1*2, 1 );
|
||||
|
||||
axisInsertChopper waittill ( "goal" );
|
||||
|
||||
// leave
|
||||
axisInsertChopper Vehicle_SetSpeed( 120, 120 );
|
||||
axisInsertChopper setVehGoalPos( rightPos1+(0,0,leaveOffset)+right1*2+forward1*-20, 1 );
|
||||
|
||||
axisInsertChopper waittill ( "goal" );
|
||||
axisInsertChopper delete();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Event - Mortar
|
||||
|
||||
|
||||
doMortar()
|
||||
{
|
||||
mapCenter = getMapCenter();
|
||||
offset = 1;
|
||||
for ( i=0; i<5; i++ )
|
||||
{
|
||||
mortarTarget = mapCenter + ( RandomIntRange(100, 600)*offset, RandomIntRange(100, 600)*offset, 0 );
|
||||
|
||||
traceData = BulletTrace( mortarTarget+(0,0,500), mortarTarget-(0,0,500), false );
|
||||
if ( isDefined( traceData["position"] ) )
|
||||
{
|
||||
PlayFx( level.match_events_fx["tracer"], mortarTarget );
|
||||
thread playSoundinSpace( "fast_artillery_round", mortarTarget );
|
||||
|
||||
wait( RandomFloatRange( 0.5, 1.5 ) );
|
||||
|
||||
PlayFx( level.match_events_fx["explosion"], mortarTarget );
|
||||
PlayRumbleOnPosition( "grenade_rumble", mortarTarget );
|
||||
Earthquake( 1.0, 0.6, mortarTarget, 2000 );
|
||||
thread playSoundinSpace( "exp_suitcase_bomb_main", mortarTarget );
|
||||
physicsExplosionSphere( mortarTarget + (0,0,30), 250, 125, 2 );
|
||||
|
||||
offset *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Event - Smoke
|
||||
|
||||
|
||||
doSmoke()
|
||||
{
|
||||
mapCenter = getMapCenter();
|
||||
offset = 1;
|
||||
for ( i=0; i<3; i++ )
|
||||
{
|
||||
smokeTarget = mapCenter + ( RandomIntRange(100, 600)*offset, RandomIntRange(100, 600)*offset, 0 );
|
||||
|
||||
PlayFx( level.match_events_fx["smoke"], smokeTarget );
|
||||
offset *= -1;
|
||||
wait( 2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Event - Airstrike
|
||||
|
||||
|
||||
doAirstrike()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
offset = 1;
|
||||
mapCenter = getMapCenter();
|
||||
for( i = 0; i < 3; i++ )
|
||||
{
|
||||
strikeTarget = mapCenter + ( RandomIntRange(100, 600)*offset, RandomIntRange(100, 600)*offset, 0 );
|
||||
traceData = BulletTrace( strikeTarget+(0,0,500), strikeTarget-(0,0,500), false );
|
||||
if ( isDefined( traceData["position"] ) )
|
||||
{
|
||||
thread doAirstrikeFlyBy( traceData["position"] );
|
||||
offset *= -1;
|
||||
wait ( randomIntRange( 2,4 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doAirstrikeFlyBy( strikeTarget )
|
||||
{
|
||||
randSpawn = randomInt( level.spawnPoints.size - 1 );
|
||||
targetPos = level.spawnPoints[randSpawn].origin * (1,1,0);
|
||||
|
||||
backDist = 8000;
|
||||
forwardDist = 8000;
|
||||
heightEnt = GetEnt( "airstrikeheight", "targetname" );
|
||||
|
||||
upVector = (0, 0, heightEnt.origin[2] + randomIntRange(-100, 600) );
|
||||
|
||||
forward = AnglesToForward( (0,randomInt(45),0) );
|
||||
|
||||
startpos = targetPos + upVector + forward * backDist * -1;
|
||||
endPos = targetPos + upVector + forward * forwardDist;
|
||||
|
||||
plane2StartPos = startpos + ( randomIntRange(400,500), randomIntRange(400,500), randomIntRange(200,300) );
|
||||
plane2EndPos = endPos + ( randomIntRange(400,500), randomIntRange(400,500), randomIntRange(200,300) );
|
||||
|
||||
plane = spawnplane( self, "script_model", startpos );
|
||||
plane2 = spawnplane( self, "script_model", plane2StartPos );
|
||||
|
||||
if ( cointoss() )
|
||||
{
|
||||
plane setModel( "vehicle_av8b_harrier_jet_mp" );
|
||||
plane2 setModel( "vehicle_av8b_harrier_jet_mp" );
|
||||
}
|
||||
else
|
||||
{
|
||||
plane setModel( "vehicle_av8b_harrier_jet_opfor_mp" );
|
||||
plane2 setModel( "vehicle_av8b_harrier_jet_opfor_mp" );
|
||||
}
|
||||
|
||||
plane.angles = vectorToAngles( endPos-startPos );
|
||||
plane playloopsound( "veh_mig29_dist_loop" );
|
||||
plane thread playPlaneFx();
|
||||
|
||||
plane2.angles = vectorToAngles( endPos-plane2StartPos );
|
||||
plane2 playloopsound( "veh_mig29_dist_loop" );
|
||||
plane2 thread playPlaneFx();
|
||||
|
||||
length = distance(startPos, endPos);
|
||||
plane moveTo( endPos * 2, length/2000, 0, 0 );
|
||||
wait( randomFloatRange( .25, .5 ) );
|
||||
plane2 moveTo( plane2EndPos * 2, length/2000, 0, 0 );
|
||||
|
||||
//MagicBullet( "nodamage_rocket_mp", plane.origin, strikeTarget );
|
||||
|
||||
wait( length/2000 );
|
||||
plane delete();
|
||||
plane2 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" );
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Event - Pavelow
|
||||
|
||||
|
||||
doPavelow()
|
||||
{
|
||||
mapCenter = getMapCenter();
|
||||
traceData = BulletTrace( mapCenter+(0,0,500), mapCenter-(0,0,500), false );
|
||||
if ( isDefined( traceData["position"] ) )
|
||||
{
|
||||
if ( cointoss() )
|
||||
vehicleModel = "vehicle_pavelow";
|
||||
else
|
||||
vehicleModel = "vehicle_pavelow_opfor";
|
||||
chopper = spawnHelicopter( self, traceData["position"]+(0,0,1000), (0,0,0), "pavelow_mp", vehicleModel );
|
||||
if ( !isDefined( chopper ) )
|
||||
return;
|
||||
|
||||
chopper.team = self.pers["team"];
|
||||
chopper.heli_type = level.heli_types[ vehicleModel ];
|
||||
chopper thread [[ level.lightFxFunc[ level.heli_types[ vehicleModel ] ] ]]();
|
||||
chopper.zOffset = (0,0,chopper getTagOrigin( "tag_origin" )[2] - chopper getTagOrigin( "tag_ground" )[2]);
|
||||
|
||||
wait( 1 );
|
||||
|
||||
playFxOnTag( level.chopper_fx["damage"]["on_fire"], chopper, "tag_engine_left" );
|
||||
chopper thread maps\mp\killstreaks\_helicopter::heli_crash();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//
|
||||
// Event - Osprey Insertion
|
||||
|
||||
|
||||
doOspreyInsertion()
|
||||
{
|
||||
startSpawns = getStartSpawns();
|
||||
if ( isDefined( startSpawns ) )
|
||||
{
|
||||
spawnHeight = 200;
|
||||
hoverOffset = 200;
|
||||
leaveOffset = 1000;
|
||||
|
||||
// allies osprey
|
||||
|
||||
forward1 = AnglesToForward( startSpawns["allies"][0].angles ) * 300;
|
||||
up1 = AnglesToUp( startSpawns["allies"][0].angles ) * spawnHeight;
|
||||
pos1 = startSpawns["allies"][0].origin+forward1+up1;
|
||||
airShip1 = spawnHelicopter( self, pos1, startSpawns["allies"][0].angles, "osprey_minigun_mp", "vehicle_v22_osprey_body_mp" );
|
||||
if ( !isDefined( airShip1 ) )
|
||||
return;
|
||||
|
||||
// axis osprey
|
||||
|
||||
forward2 = AnglesToForward( startSpawns["axis"][0].angles ) * 300;
|
||||
up2 = AnglesToUp( startSpawns["axis"][0].angles ) * spawnHeight;
|
||||
pos2 = startSpawns["axis"][0].origin+forward2+up2;
|
||||
airShip2 = spawnHelicopter( self, pos2, startSpawns["axis"][0].angles, "osprey_minigun_mp", "vehicle_v22_osprey_body_mp" );
|
||||
if ( !isDefined( airShip2 ) )
|
||||
{
|
||||
airShip1 delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// rise to hover
|
||||
|
||||
airship1 thread maps\mp\killstreaks\_escortairdrop::airShipPitchPropsUp();
|
||||
airship2 thread maps\mp\killstreaks\_escortairdrop::airShipPitchPropsUp();
|
||||
airShip1 thread maps\mp\killstreaks\_escortairdrop::airShipPitchHatchDown();
|
||||
airShip2 thread maps\mp\killstreaks\_escortairdrop::airShipPitchHatchDown();
|
||||
|
||||
airShip1 Vehicle_SetSpeed( 20, 10 );
|
||||
airShip1 SetYawSpeed( 3, 3, 3, 0.3 );
|
||||
airShip1 setVehGoalPos( pos1+(0,0,hoverOffset), 1 );
|
||||
|
||||
airShip2 Vehicle_SetSpeed( 20, 10 );
|
||||
airShip2 SetYawSpeed( 3, 3, 3, 0.3 );
|
||||
airShip2 setVehGoalPos( pos2+(0,0,hoverOffset), 1 );
|
||||
|
||||
airShip1 waittill ( "goal" );
|
||||
|
||||
airShip1 thread maps\mp\killstreaks\_escortairdrop::airShipPitchHatchUp();
|
||||
airShip2 thread maps\mp\killstreaks\_escortairdrop::airShipPitchHatchUp();
|
||||
|
||||
wait( 2 );
|
||||
|
||||
// rise to leave
|
||||
|
||||
airShip1 Vehicle_SetSpeed( 80, 60 );
|
||||
airShip1 SetYawSpeed( 30, 15, 15, 0.3 );
|
||||
airShip1 setVehGoalPos( pos1+(0,0,leaveOffset), 1 );
|
||||
|
||||
airShip2 Vehicle_SetSpeed( 80, 60 );
|
||||
airShip2 SetYawSpeed( 30, 15, 15, 0.3 );
|
||||
airShip2 setVehGoalPos( pos2+(0,0,leaveOffset), 1 );
|
||||
|
||||
airShip1 waittill ( "goal" );
|
||||
|
||||
// leave
|
||||
|
||||
airship1 thread maps\mp\killstreaks\_escortairdrop::airShipPitchPropsDown();
|
||||
airship2 thread maps\mp\killstreaks\_escortairdrop::airShipPitchPropsDown();
|
||||
|
||||
airShip1 Vehicle_SetSpeed( 120, 120 );
|
||||
airShip1 SetYawSpeed( 100, 100, 40, 0.3 );
|
||||
airShip1 setVehGoalPos( pos1+(0,0,leaveOffset)+forward1*-20, 1 );
|
||||
|
||||
airShip2 Vehicle_SetSpeed( 120, 120 );
|
||||
airShip2 SetYawSpeed( 100, 100, 40, 0.3 );
|
||||
airShip2 setVehGoalPos( pos2+(0,0,leaveOffset)+forward2*-20, 1 );
|
||||
|
||||
airShip1 waittill ( "goal" );
|
||||
|
||||
airShip1 delete();
|
||||
airShip2 delete();
|
||||
}
|
||||
}
|
||||
3
maps/mp/_menus.gsc
Normal file
3
maps/mp/_menus.gsc
Normal file
@@ -0,0 +1,3 @@
|
||||
init()
|
||||
{
|
||||
}
|
||||
50
maps/mp/_minefields.gsc
Normal file
50
maps/mp/_minefields.gsc
Normal file
@@ -0,0 +1,50 @@
|
||||
minefields()
|
||||
{
|
||||
minefields = getentarray("minefield", "targetname");
|
||||
if (minefields.size > 0)
|
||||
{
|
||||
level._effect["mine_explosion"] = loadfx ("vfx/gameplay/explosions/weap/gre/vfx_exp_gre_dirt_cg");
|
||||
}
|
||||
|
||||
for(i = 0; i < minefields.size; i++)
|
||||
{
|
||||
minefields[i] thread minefield_think();
|
||||
}
|
||||
}
|
||||
|
||||
minefield_think()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
self waittill ("trigger",other);
|
||||
|
||||
if(isPlayer(other))
|
||||
other thread minefield_kill(self);
|
||||
}
|
||||
}
|
||||
|
||||
minefield_kill(trigger)
|
||||
{
|
||||
if(isDefined(self.minefield))
|
||||
return;
|
||||
|
||||
self.minefield = true;
|
||||
// self playsound ("minefield_click");
|
||||
|
||||
wait(.5);
|
||||
wait(randomFloat(.5));
|
||||
|
||||
if(isdefined(self) && self istouching(trigger))
|
||||
{
|
||||
origin = self getorigin();
|
||||
range = 300;
|
||||
maxdamage = 2000;
|
||||
mindamage = 50;
|
||||
|
||||
// self playsound("explo_mine");
|
||||
// playfx(level._effect["mine_explosion"], origin);
|
||||
radiusDamage(origin, range, maxdamage, mindamage);
|
||||
}
|
||||
|
||||
self.minefield = undefined;
|
||||
}
|
||||
660
maps/mp/_movable_cover.gsc
Normal file
660
maps/mp/_movable_cover.gsc
Normal file
@@ -0,0 +1,660 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
///////
|
||||
// movable_cover
|
||||
///////
|
||||
init()
|
||||
{
|
||||
if( getDvar( "r_reflectionProbeGenerate" ) == "1" )
|
||||
return;
|
||||
|
||||
movable_cover_precache();
|
||||
waitframe();
|
||||
covers = GetEntArray("movable_cover", "targetname");
|
||||
cover_proxy = getstructarray("movable_cover", "targetname");
|
||||
foreach(proxy in cover_proxy)
|
||||
{
|
||||
if(IsDefined(proxy.target))
|
||||
{
|
||||
cover = GetEnt(proxy.target, "targetname");
|
||||
if(IsDefined(cover))
|
||||
{
|
||||
covers[covers.size] = cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
array_thread(covers, ::movable_cover_init);
|
||||
|
||||
}
|
||||
|
||||
movable_cover_precache()
|
||||
{
|
||||
level.movable_cover_use_icons = [];
|
||||
level.movable_cover_use_icons["dumpster"] = "hud_icon_push";
|
||||
|
||||
level.movable_cover_move_sounds = [];
|
||||
level.movable_cover_move_sounds["dumpster"]["start"] = "mp_dumpster_start";
|
||||
level.movable_cover_move_sounds["dumpster"]["move"] = "mp_dumpster_mvmt_loop";
|
||||
level.movable_cover_move_sounds["dumpster"]["stop"] = "mp_dumpster_end";
|
||||
|
||||
level.movable_cover_move_anim = [];
|
||||
level.movable_cover_move_anim["dumpster"] = [];
|
||||
level.movable_cover_move_anim["dumpster"]["move"] = "mp_lonestar_dumpster_jitter";
|
||||
level.movable_cover_move_anim["dumpster"]["idle"] = "mp_lonestar_dumpster_jitter_idle";
|
||||
|
||||
level.movable_cover_default_parameters = [];
|
||||
level.movable_cover_default_parameters["dumpster"] = "goal_radius=1;max_speed=80;accel_time=.8;decel_time=.8;stances=crouch,stand";
|
||||
|
||||
foreach(move_anim_type in level.movable_cover_move_anim)
|
||||
{
|
||||
foreach(move_anim in move_anim_type)
|
||||
{
|
||||
PrecacheMpAnim(move_anim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_init()
|
||||
{
|
||||
self.moving = false;
|
||||
self.stay = false;
|
||||
self.updatePaths = self.spawnflags & 1;
|
||||
|
||||
self.movable_type = "default";
|
||||
if(IsDefined(self.script_noteworthy))
|
||||
self.movable_type = self.script_noteworthy;
|
||||
|
||||
self movable_cover_parse_parameters();
|
||||
|
||||
//Find End points
|
||||
end_points = [];
|
||||
|
||||
links = self get_links();
|
||||
foreach(link in links)
|
||||
{
|
||||
point = getstruct(link, "script_linkname");
|
||||
if(IsDefined(point) && IsDefined(point.script_label))
|
||||
{
|
||||
point.auto = IsDefined(point.script_parameters) && point.script_parameters=="auto";
|
||||
point.stay = IsDefined(point.script_parameters) && point.script_parameters=="stay";
|
||||
end_points[point.script_label] = point;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
targets = [];
|
||||
if(IsDefined(self.target))
|
||||
{
|
||||
structs = GetStructArray(self.target, "targetname");
|
||||
ents = GetEntArray(self.target, "targetname");
|
||||
targets = array_combine(structs, ents);
|
||||
}
|
||||
|
||||
link_to_ent = self;
|
||||
link_to_tag = undefined;
|
||||
if(IsDefined(level.movable_cover_move_anim[self.movable_type]))
|
||||
{
|
||||
self.animate_ent = spawn("script_model", self.origin);
|
||||
self.animate_ent SetModel("generic_prop_raven");
|
||||
self.animate_ent.angles = self.angles;
|
||||
self.animate_ent linkTo(self);
|
||||
link_to_ent = self.animate_ent;
|
||||
link_to_tag = "j_prop_1";
|
||||
}
|
||||
|
||||
self.linked_ents = [];
|
||||
foreach(target in targets)
|
||||
{
|
||||
if(!IsDefined(target.script_noteworthy))
|
||||
continue;
|
||||
|
||||
switch(target.script_noteworthy)
|
||||
{
|
||||
case "move_trigger":
|
||||
if(!IsDefined(target.script_label) || !IsDefined(end_points[target.script_label]))
|
||||
continue;
|
||||
target EnableLinkTo();
|
||||
target LinkTo(self);
|
||||
self thread movable_cover_trigger(target, end_points[target.script_label]);
|
||||
self thread movable_cover_update_use_icon(target, end_points[target.script_label]);
|
||||
break;
|
||||
case "link":
|
||||
self.linked_ents[self.linked_ents.size] = target;
|
||||
break;
|
||||
case "angels":
|
||||
if(IsDefined(target.angles) && IsDefined(self.animate_ent))
|
||||
self.animate_ent.angles = target.angles;
|
||||
break;
|
||||
case "mantlebrush":
|
||||
self.linked_ents[self.linked_ents.size] = target;
|
||||
//self thread movable_cover_mantlebrush_think( target );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//link after to make sure the angels on animate_ent is set
|
||||
foreach(ent in self.linked_ents)
|
||||
{
|
||||
if(IsDefined(link_to_tag))
|
||||
{
|
||||
ent LinkTo(link_to_ent, link_to_tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
ent LinkTo(link_to_ent);
|
||||
}
|
||||
}
|
||||
|
||||
//Traversals
|
||||
all_nodes = self getLinknameNodes();
|
||||
self.traverse_nodes = [];
|
||||
foreach(node in all_nodes)
|
||||
{
|
||||
if(!IsDefined(node.type))
|
||||
continue;
|
||||
|
||||
node.node_type = node.script_noteworthy;
|
||||
if(!IsDefined(node.node_type))
|
||||
node.node_type = "closest";
|
||||
|
||||
node_type_valid = false;
|
||||
switch(node.node_type)
|
||||
{
|
||||
case "closest":
|
||||
node_type_valid= true;
|
||||
break;
|
||||
case "radius":
|
||||
case "radius3d": //fallthrough
|
||||
case "radius2d": //fallthrough
|
||||
if(IsDefined(node.target))
|
||||
{
|
||||
target = getstruct(node.target, "targetname");
|
||||
if(IsDefined(target) && IsDefined(target.radius))
|
||||
{
|
||||
node.test_origin = target.origin;
|
||||
node.test_radius = target.radius;
|
||||
node_type_valid= true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
node_type_valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!node_type_valid)
|
||||
continue;
|
||||
|
||||
if(node.type == "Begin" || node.type == "End")
|
||||
{
|
||||
if(node.type == "Begin")
|
||||
{
|
||||
node.connected_nodes = GetNodeArray(node.target, "targetname");
|
||||
}
|
||||
else //"End"
|
||||
{
|
||||
node.connected_nodes = GetNodeArray(node.targetname, "target");
|
||||
}
|
||||
|
||||
//Disconnect all conenctions to start
|
||||
foreach(connected_node in node.connected_nodes)
|
||||
{
|
||||
DisconnectNodePair(node, connected_node);
|
||||
}
|
||||
|
||||
self.traverse_nodes[self.traverse_nodes.size] = node;
|
||||
}
|
||||
}
|
||||
self movable_cover_connect_traversals(); //Reconnect the closest
|
||||
}
|
||||
|
||||
movable_cover_set_user(user, trigger)
|
||||
{
|
||||
if(!IsDefined(self.user) && IsDefined(user))
|
||||
self notify("new_user");
|
||||
else if(IsDefined(self.user) && IsDefined(user) && self.user!=user)
|
||||
self notify("new_user");
|
||||
else if(IsDefined(self.user) && !IsDefined(user))
|
||||
self notify("clear_user");
|
||||
|
||||
self.user = user;
|
||||
self.user_trigger= trigger;
|
||||
}
|
||||
|
||||
movable_cover_update_use_icon(trigger, move_to)
|
||||
{
|
||||
if(!IsDefined(level.movable_cover_use_icons[self.movable_type]))
|
||||
return;
|
||||
|
||||
while( !IsDefined( level.players ) )
|
||||
{
|
||||
waitframe();
|
||||
continue;
|
||||
}
|
||||
|
||||
show_dist=100;
|
||||
show_dist_sqr = show_dist*show_dist;
|
||||
|
||||
ent_num = trigger GetEntityNumber();
|
||||
trigger_name = "trigger_" + ent_num;
|
||||
while(1)
|
||||
{
|
||||
foreach(player in level.players)
|
||||
{
|
||||
if(!IsDefined(player.movable_cover_huds))
|
||||
player.movable_cover_huds = [];
|
||||
|
||||
dist_sqr = DistanceSquared(player.origin+(0,0,30), trigger.origin);
|
||||
if(dist_sqr<=show_dist_sqr && !self movable_cover_at_goal(move_to.origin))
|
||||
{
|
||||
if(!IsDefined(player.movable_cover_huds[trigger_name]))
|
||||
{
|
||||
player.movable_cover_huds[trigger_name] = movable_cover_use_icon(player, trigger);
|
||||
player.movable_cover_huds[trigger_name].alpha = 0;
|
||||
}
|
||||
|
||||
player.movable_cover_huds[trigger_name] notify("stop_fade");
|
||||
player.movable_cover_huds[trigger_name] thread movable_cover_fade_in_use_icon();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IsDefined(player.movable_cover_huds[trigger_name]))
|
||||
{
|
||||
player.movable_cover_huds[trigger_name] thread movable_cover_fade_out_use_icon();
|
||||
}
|
||||
}
|
||||
}
|
||||
wait .05;
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_fade_in_use_icon()
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
if(self.alpha == 1)
|
||||
return;
|
||||
|
||||
self FadeOverTime(.5);
|
||||
self.alpha = 1;
|
||||
}
|
||||
|
||||
movable_cover_fade_out_use_icon()
|
||||
{
|
||||
self endon("death");
|
||||
self endon("stop_fade");
|
||||
|
||||
if(self.alpha == 0)
|
||||
return;
|
||||
|
||||
time = .5;
|
||||
self FadeOverTime(time);
|
||||
self.alpha = 0;
|
||||
wait time;
|
||||
|
||||
self Destroy();
|
||||
}
|
||||
|
||||
movable_cover_use_icon(player, ent)
|
||||
{
|
||||
icon = player createIcon( level.movable_cover_use_icons[self.movable_type], 16, 16 );
|
||||
icon setWayPoint( true, false );
|
||||
icon SetTargetEnt(ent);
|
||||
icon.fading = false;
|
||||
return icon;
|
||||
}
|
||||
|
||||
movable_cover_parse_parameters()
|
||||
{
|
||||
//Init Delfaults
|
||||
self.goal_radius = 1;
|
||||
self.max_speed = 50;
|
||||
self.accel_time = 1;
|
||||
self.decel_time = 1;
|
||||
self.requires_push = 1;
|
||||
self.start_delay = .2;
|
||||
self.stances = ["stand","crouch"];
|
||||
|
||||
if(!IsDefined(self.script_parameters))
|
||||
self.script_parameters = "";
|
||||
|
||||
default_parameters = level.movable_cover_default_parameters[self.movable_type];
|
||||
if(IsDefined(default_parameters))
|
||||
self.script_parameters = default_parameters + self.script_parameters;
|
||||
|
||||
params = StrTok(self.script_parameters, ";");
|
||||
foreach(param in params)
|
||||
{
|
||||
toks = strtok(param,"=");
|
||||
if(toks.size!=2)
|
||||
continue;
|
||||
|
||||
switch(toks[0])
|
||||
{
|
||||
case "goal_radius":
|
||||
self.goal_radius = float(toks[1]);
|
||||
self.goal_radius = max(1,self.goal_radius);
|
||||
break;
|
||||
case "max_speed":
|
||||
self.max_speed = float(toks[1]);
|
||||
break;
|
||||
case "accel_time":
|
||||
self.accel_time = float(toks[1]);
|
||||
break;
|
||||
case "decel_time":
|
||||
self.decel_time = float(toks[1]);
|
||||
self.decel_time = max(0.05,self.decel_time);
|
||||
break;
|
||||
case "stances":
|
||||
self.stances = StrTok(toks[1], ",");
|
||||
break;
|
||||
case "requires_push":
|
||||
self.requires_push = int(toks[1]);
|
||||
break;
|
||||
case "start_delay":
|
||||
self.start_delay = float(toks[1]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_trigger(trigger, move_to)
|
||||
{
|
||||
auto = move_to.auto;
|
||||
stay = move_to.stay;
|
||||
while(1)
|
||||
{
|
||||
player=undefined;
|
||||
if(auto && !self.stay)
|
||||
{
|
||||
waitframe();
|
||||
if(IsDefined(self.user) && self.user_trigger != trigger)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
self movable_cover_set_user(undefined, undefined);
|
||||
while(1)
|
||||
{
|
||||
trigger waittill("trigger", player);
|
||||
if(isPlayer(player))
|
||||
break;
|
||||
}
|
||||
self movable_cover_set_user(player, trigger);
|
||||
}
|
||||
|
||||
if(self movable_cover_at_goal(move_to.origin))
|
||||
continue;
|
||||
|
||||
move_dir = VectorNormalize(move_to.origin - self.origin);
|
||||
if(!auto && !self movable_cover_move_delay(self.start_delay, player, trigger, move_dir) )
|
||||
continue;
|
||||
|
||||
dist = Distance(self.origin, move_to.origin);
|
||||
time = dist/self.max_speed;
|
||||
|
||||
if(auto && self.stay && !IsDefined(self.user))
|
||||
continue;
|
||||
|
||||
if(self.moving)
|
||||
continue;
|
||||
|
||||
if(self.updatePaths)
|
||||
self ConnectPaths();
|
||||
self movable_cover_disconnect_traversals();
|
||||
self.moving = true;
|
||||
self.stay = false;
|
||||
|
||||
self notify( "move_start" );
|
||||
|
||||
start_time = GetTime();
|
||||
accel_time = min(time, self.accel_time);
|
||||
if(auto)
|
||||
accel_time = time;
|
||||
|
||||
start_sound = movable_cover_get_sound("start");
|
||||
if(IsDefined(start_sound))
|
||||
self PlaySound(start_sound);
|
||||
|
||||
move_sound = movable_cover_get_sound("move");
|
||||
if(IsDefined(move_sound))
|
||||
self PlayLoopSound(move_sound);
|
||||
|
||||
if(IsDefined(self.animate_ent) && IsDefined(level.movable_cover_move_anim[self.movable_type]["move"]))
|
||||
{
|
||||
self.animate_ent scriptmodelPlayanim(level.movable_cover_move_anim[self.movable_type]["move"]);
|
||||
//self.animate_ent ScriptModelPlayAnimDeltaMotion(level.movable_cover_move_anim[self.movable_type]);
|
||||
}
|
||||
|
||||
self MoveTo(move_to.origin, time, accel_time);
|
||||
|
||||
if(auto)
|
||||
{
|
||||
self movable_cover_wait_for_user_or_timeout(time);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(movable_cover_is_pushed(player, trigger, move_dir) && !self movable_cover_at_goal(move_to.origin))
|
||||
{
|
||||
wait .05;
|
||||
}
|
||||
self movable_cover_set_user(undefined, undefined);
|
||||
}
|
||||
|
||||
if(!self movable_cover_at_goal(move_to.origin))
|
||||
{
|
||||
current_speed_scale = movable_cover_calc_move_speed_scale((GetTime()-start_time)/1000, time, accel_time);
|
||||
current_speed = self.max_speed * current_speed_scale;
|
||||
|
||||
dist = Distance(self.origin, move_to.origin);
|
||||
stop_dist = dist;
|
||||
if(current_speed>0)
|
||||
{
|
||||
stop_dist = min(dist,current_speed*self.decel_time);
|
||||
}
|
||||
time = (2*stop_dist)/self.max_speed;
|
||||
self MoveTo(self.origin+(stop_dist*move_dir),time, 0, time);
|
||||
wait time;
|
||||
}
|
||||
|
||||
self StopLoopSound();
|
||||
|
||||
stop_sound = movable_cover_get_sound("stop");
|
||||
if(IsDefined(stop_sound))
|
||||
self PlaySound(stop_sound);
|
||||
if(IsDefined(self.animate_ent) && IsDefined(level.movable_cover_move_anim[self.movable_type]["idle"]))
|
||||
{
|
||||
self.animate_ent scriptmodelPlayanim(level.movable_cover_move_anim[self.movable_type]["idle"]);
|
||||
}
|
||||
|
||||
if(stay && self movable_cover_at_goal(move_to.origin))
|
||||
{
|
||||
self.stay = true;
|
||||
}
|
||||
|
||||
if(self.updatePaths)
|
||||
self DisconnectPaths();
|
||||
self movable_cover_connect_traversals();
|
||||
self.moving = false;
|
||||
|
||||
self notify( "move_end" );
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_connect_traversals()
|
||||
{
|
||||
self movable_cover_disconnect_traversals();
|
||||
foreach(node in self.traverse_nodes)
|
||||
{
|
||||
switch(node.node_type)
|
||||
{
|
||||
case "closest":
|
||||
node.connected_to = getClosest(node.origin, node.connected_nodes);
|
||||
break;
|
||||
case "radius":
|
||||
case "radius3d": //fallthrough
|
||||
dist = Distance(node.origin, node.test_origin);
|
||||
if(dist<=node.test_radius)
|
||||
node.connected_to = node.connected_nodes[0];
|
||||
break;
|
||||
case "radius2d":
|
||||
dist2d = Distance2d(node.origin, node.test_origin);
|
||||
if(dist2d<=node.test_radius)
|
||||
node.connected_to = node.connected_nodes[0];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(IsDefined(node.connected_to))
|
||||
{
|
||||
ConnectNodePair(node, node.connected_to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_disconnect_traversals()
|
||||
{
|
||||
foreach(node in self.traverse_nodes)
|
||||
{
|
||||
if(IsDefined(node.connected_to))
|
||||
{
|
||||
DisconnectNodePair(node, node.connected_to);
|
||||
node.connected_to = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_get_sound(type)
|
||||
{
|
||||
if(!IsDefined(level.movable_cover_move_sounds[self.movable_type]))
|
||||
return undefined;
|
||||
|
||||
return level.movable_cover_move_sounds[self.movable_type][type];
|
||||
}
|
||||
|
||||
movable_cover_wait_for_user_or_timeout(time)
|
||||
{
|
||||
self endon("new_user");
|
||||
wait time;
|
||||
}
|
||||
|
||||
movable_cover_calc_move_speed_scale(current_time, move_time, accel_time, decel_time)
|
||||
{
|
||||
if(!IsDefined(accel_time))
|
||||
accel_time = 0;
|
||||
if(!IsDefined(decel_time))
|
||||
decel_time = 0;
|
||||
|
||||
if(current_time >= move_time || current_time <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if(current_time < accel_time)
|
||||
{
|
||||
return 1 - ((accel_time - current_time) / accel_time);
|
||||
}
|
||||
else if(current_time > (move_time - decel_time))
|
||||
{
|
||||
return 1 - ((current_time - (move_time - decel_time)) / decel_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_is_pushed(player, trigger, move_dir)
|
||||
{
|
||||
//We have the player check so that the trigger doesn't error out when a dog enters.
|
||||
if( !IsDefined( player ) || !IsReallyAlive( player ) || !IsPlayer( player) )
|
||||
return false;
|
||||
|
||||
if(!movable_cover_is_touched(trigger, player))
|
||||
return false;
|
||||
|
||||
if(player IsMantling())
|
||||
return false;
|
||||
|
||||
stance = player GetStance();
|
||||
if(!array_contains(self.stances, stance))
|
||||
return false;
|
||||
|
||||
if(self.requires_push)
|
||||
{
|
||||
player_move_dir = player GetNormalizedMovement();
|
||||
player_move_dir = RotateVector(player_move_dir, -1*player.angles);
|
||||
player_move_dir = VectorNormalize((player_move_dir[0], -1*player_move_dir[1], 0));
|
||||
dot = VectorDot(move_dir, player_move_dir);
|
||||
return dot>0.2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_is_touched(trigger, player)
|
||||
{
|
||||
return IsDefined( player ) && isReallyAlive( player ) && player IsTouching(trigger);
|
||||
}
|
||||
|
||||
movable_cover_move_delay(delay, player, trigger, move_dir)
|
||||
{
|
||||
endTime = (delay*1000)+GetTime();
|
||||
|
||||
while(1)
|
||||
{
|
||||
if( !IsDefined( player ) || !isReallyAlive( player ) )
|
||||
return false;
|
||||
|
||||
if(player IsMantling())
|
||||
return false;
|
||||
|
||||
if(!movable_cover_is_pushed(player, trigger, move_dir))
|
||||
return false;
|
||||
|
||||
if(self.moving)
|
||||
return false;
|
||||
|
||||
if(getTime()>=endTime)
|
||||
return true;
|
||||
|
||||
wait .05;
|
||||
}
|
||||
}
|
||||
|
||||
movable_cover_at_goal(goal)
|
||||
{
|
||||
distSqr = DistanceSquared(self.origin, goal);
|
||||
|
||||
return distSqr<=(self.goal_radius*self.goal_radius);
|
||||
}
|
||||
|
||||
// Make movers unmantleable while moving - avoids animating into solid architecture
|
||||
movable_cover_mantlebrush_think( mantlebrush )// self = mover
|
||||
{
|
||||
self endon("death");
|
||||
|
||||
while(1)
|
||||
{
|
||||
self waittill( "move_start" );
|
||||
if ( IsDefined( mantlebrush ) )
|
||||
{
|
||||
mantlebrush.old_contents = mantlebrush SetContents( 0 );
|
||||
mantlebrush Hide();
|
||||
}
|
||||
self waittill( "move_end" );
|
||||
if ( IsDefined( mantlebrush ) )
|
||||
{
|
||||
mantlebrush Show();
|
||||
mantlebrush SetContents( mantlebrush.old_contents );
|
||||
}
|
||||
}
|
||||
}
|
||||
1289
maps/mp/_movers.gsc
Normal file
1289
maps/mp/_movers.gsc
Normal file
File diff suppressed because it is too large
Load Diff
223
maps/mp/_radiation.gsc
Normal file
223
maps/mp/_radiation.gsc
Normal file
@@ -0,0 +1,223 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
radiation()
|
||||
{
|
||||
radiationFields = getentarray("radiation", "targetname");
|
||||
|
||||
if (radiationFields.size > 0)
|
||||
{
|
||||
foreach ( trigger in radiationFields )
|
||||
trigger thread common_scripts\_dynamic_world::triggerTouchThink( ::playerEnterArea, ::playerLeaveArea );
|
||||
|
||||
thread onPlayerConnect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "connected", player );
|
||||
player.numAreas = 0;
|
||||
}
|
||||
}
|
||||
|
||||
playerEnterArea( trigger )
|
||||
{
|
||||
self.numAreas++;
|
||||
|
||||
if ( self.numAreas == 1 )
|
||||
self radiationEffect();
|
||||
}
|
||||
|
||||
|
||||
playerLeaveArea( trigger )
|
||||
{
|
||||
self.numAreas--;
|
||||
assert( self.numAreas >= 0 );
|
||||
|
||||
if ( self.numAreas != 0 )
|
||||
return;
|
||||
|
||||
self.poison = 0;
|
||||
self notify( "leftTrigger");
|
||||
|
||||
if ( isDefined( self.radiationOverlay ) )
|
||||
self.radiationOverlay fadeoutBlackOut( .10, 0 );
|
||||
}
|
||||
|
||||
soundWatcher( soundOrg )
|
||||
{
|
||||
self waittill_any( "death", "leftTrigger" );
|
||||
|
||||
self stopLoopSound();
|
||||
}
|
||||
|
||||
radiationEffect()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leftTrigger" );
|
||||
|
||||
self.poison = 0;
|
||||
self thread soundWatcher( self );
|
||||
|
||||
while (1)
|
||||
{
|
||||
self.poison ++;
|
||||
|
||||
switch( self.poison )
|
||||
{
|
||||
case 1:
|
||||
self.radiationSound = "item_geigercouner_level2";
|
||||
self playLoopSound( self.radiationSound );
|
||||
self ViewKick( 1, self.origin );
|
||||
break;
|
||||
case 3:
|
||||
self shellshock( "mp_radiation_low", 4);
|
||||
self.radiationSound = "item_geigercouner_level3";
|
||||
self stopLoopSound();
|
||||
self playLoopSound( self.radiationSound );
|
||||
self ViewKick( 3, self.origin );
|
||||
self doRadiationDamage(15);
|
||||
break;
|
||||
case 4:
|
||||
self shellshock( "mp_radiation_med", 5);
|
||||
self.radiationSound = "item_geigercouner_level3";
|
||||
self stopLoopSound();
|
||||
self playLoopSound( self.radiationSound );
|
||||
self ViewKick( 15, self.origin );
|
||||
self thread blackout();
|
||||
self doRadiationDamage(25);
|
||||
break;
|
||||
case 6:
|
||||
self shellshock( "mp_radiation_high", 5);
|
||||
self.radiationSound = "item_geigercouner_level4";
|
||||
self stopLoopSound();
|
||||
self playLoopSound( self.radiationSound );
|
||||
self ViewKick( 75, self.origin );
|
||||
self doRadiationDamage(45);
|
||||
break;
|
||||
case 8:
|
||||
self shellshock( "mp_radiation_high", 5);
|
||||
self.radiationSound = "item_geigercouner_level4";
|
||||
self stopLoopSound();
|
||||
self playLoopSound( self.radiationSound );
|
||||
self ViewKick( 127, self.origin );
|
||||
self doRadiationDamage(175);
|
||||
|
||||
break;
|
||||
}
|
||||
wait(1);
|
||||
}
|
||||
wait(5);
|
||||
}
|
||||
blackout( )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leftTrigger" );
|
||||
|
||||
if ( !isDefined( self.radiationOverlay ) )
|
||||
{
|
||||
self.radiationOverlay = newClientHudElem( self );
|
||||
self.radiationOverlay.x = 0;
|
||||
self.radiationOverlay.y = 0;
|
||||
self.radiationOverlay setshader( "black", 640, 480 );
|
||||
self.radiationOverlay.alignX = "left";
|
||||
self.radiationOverlay.alignY = "top";
|
||||
self.radiationOverlay.horzAlign = "fullscreen";
|
||||
self.radiationOverlay.vertAlign = "fullscreen";
|
||||
self.radiationOverlay.alpha = 0;
|
||||
}
|
||||
|
||||
min_length = 1;
|
||||
max_length = 2;
|
||||
min_alpha = .25;
|
||||
max_alpha = 1;
|
||||
|
||||
min_percent = 5;
|
||||
max_percent = 100;
|
||||
|
||||
fraction = 0;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
while ( self.poison > 1 )
|
||||
{
|
||||
percent_range = max_percent - min_percent;
|
||||
fraction = ( self.poison - min_percent ) / percent_range;
|
||||
|
||||
if ( fraction < 0 )
|
||||
fraction = 0;
|
||||
else if ( fraction > 1 )
|
||||
fraction = 1;
|
||||
|
||||
length_range = max_length - min_length;
|
||||
length = min_length + ( length_range * ( 1 - fraction ) );
|
||||
|
||||
alpha_range = max_alpha - min_alpha;
|
||||
alpha = min_alpha + ( alpha_range * fraction );
|
||||
|
||||
end_alpha = fraction * 0.5;
|
||||
|
||||
if ( fraction == 1 )
|
||||
break;
|
||||
|
||||
duration = length / 2;
|
||||
|
||||
self.radiationOverlay fadeinBlackOut( duration, alpha );
|
||||
self.radiationOverlay fadeoutBlackOut( duration, end_alpha);
|
||||
|
||||
// wait a variable amount based on self.radiation.totalpercent, this is the space in between pulses
|
||||
//wait 1;
|
||||
wait( fraction * 0.5 );
|
||||
}
|
||||
|
||||
if ( fraction == 1 )
|
||||
break;
|
||||
|
||||
if ( self.radiationOverlay.alpha != 0 )
|
||||
self.radiationOverlay fadeoutBlackOut( 1, 0);
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
self.radiationOverlay fadeinBlackOut( 2, 0);
|
||||
}
|
||||
|
||||
doRadiationDamage( iDamage )
|
||||
{
|
||||
// TODO: make a radiation weapon instead of using the claymore, so we can track kills correctly, also so we can do the correct check in _damage.gsc for attackerIsInflictorVictim
|
||||
|
||||
self thread [[ level.callbackPlayerDamage ]](
|
||||
self,// eInflictor The entity that causes the damage.( e.g. a turret )
|
||||
self,// eAttacker The entity that is attacking.
|
||||
iDamage,// iDamage Integer specifying the amount of damage done
|
||||
0,// iDFlags Integer specifying flags that are to be applied to the damage
|
||||
"MOD_SUICIDE",// sMeansOfDeath Integer specifying the method of death
|
||||
"claymore_mp",// sWeapon The weapon number of the weapon used to inflict the damage
|
||||
self.origin,// vPoint The point the damage is from?
|
||||
( 0,0,0 ) - self.origin,// vDir The direction of the damage
|
||||
"none",// sHitLoc The location of the hit
|
||||
0// psOffsetTime The time offset for the damage
|
||||
);
|
||||
}
|
||||
|
||||
fadeinBlackOut( duration, alpha )
|
||||
{
|
||||
self fadeOverTime( duration );
|
||||
self.alpha = alpha;
|
||||
wait duration;
|
||||
}
|
||||
|
||||
fadeoutBlackOut( duration, alpha )
|
||||
{
|
||||
self fadeOverTime( duration );
|
||||
self.alpha = alpha;
|
||||
wait duration;
|
||||
}
|
||||
|
||||
221
maps/mp/_scoreboard.gsc
Normal file
221
maps/mp/_scoreboard.gsc
Normal file
@@ -0,0 +1,221 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
processLobbyScoreboards()
|
||||
{
|
||||
foreach ( player in level.placement["all"] )
|
||||
player setPlayerScoreboardInfo();
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
buildScoreboardType( "multiteam" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
player setCommonPlayerData( "round", "scoreboardType", "multiteam" );
|
||||
|
||||
setClientMatchData( "alliesScore", -1 );
|
||||
setClientMatchData( "axisScore", -1 );
|
||||
|
||||
setClientMatchData( "alliesKills", -1 );
|
||||
setClientMatchData( "alliesDeaths", -1 );
|
||||
}
|
||||
else if ( level.teamBased )
|
||||
{
|
||||
alliesScore = getTeamScore( "allies" );
|
||||
axisScore = getTeamScore( "axis" );
|
||||
|
||||
kills = 0;
|
||||
deaths = 0;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( isDefined( player.pers[ "team" ] ) && player.pers[ "team" ] == "allies" )
|
||||
{
|
||||
kills = kills + player.pers[ "kills" ];
|
||||
deaths = deaths + player.pers[ "deaths" ];
|
||||
}
|
||||
}
|
||||
|
||||
setClientMatchData( "alliesScore", alliesScore );
|
||||
setClientMatchData( "axisScore", axisScore );
|
||||
|
||||
setClientMatchData( "alliesKills", kills );
|
||||
setClientMatchData( "alliesDeaths", deaths );
|
||||
|
||||
if ( alliesScore == axisScore )
|
||||
winner = "tied";
|
||||
else if ( alliesScore > axisScore )
|
||||
winner = "allies";
|
||||
else
|
||||
winner = "axis";
|
||||
|
||||
if ( winner == "tied" )
|
||||
{
|
||||
// build both, assign type to your team
|
||||
buildScoreboardType( "allies" );
|
||||
buildScoreboardType( "axis" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player_pers_team = player.pers["team"];
|
||||
if ( !IsDefined(player_pers_team) )
|
||||
continue;
|
||||
|
||||
if ( player_pers_team == "spectator" )
|
||||
player setCommonPlayerData( "round", "scoreboardType", "allies" );
|
||||
else
|
||||
player setCommonPlayerData( "round", "scoreboardType", player_pers_team );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// build just winner, assign type to winner
|
||||
buildScoreboardType( winner );
|
||||
|
||||
foreach ( player in level.players )
|
||||
player setCommonPlayerData( "round", "scoreboardType", winner );
|
||||
}
|
||||
}
|
||||
else // not teambased
|
||||
{
|
||||
buildScoreboardType( "neutral" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
player setCommonPlayerData( "round", "scoreboardType", "neutral" );
|
||||
|
||||
setClientMatchData( "alliesScore", -1 );
|
||||
setClientMatchData( "axisScore", -1 );
|
||||
|
||||
setClientMatchData( "alliesKills", -1 );
|
||||
setClientMatchData( "alliesDeaths", -1 );
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
//log xp gains, and the squad member that earned them
|
||||
if ( !isAi(player) && ( privateMatch() || MatchMakingGame() ) )
|
||||
player setCommonPlayerData( "round", "squadMemberIndex", player.pers[ "activeSquadMember" ] );
|
||||
|
||||
player setCommonPlayerData( "round", "totalXp", player.pers["summary"]["xp"] );
|
||||
player setCommonPlayerData( "round", "scoreXp", player.pers["summary"]["score"] );
|
||||
player setCommonPlayerData( "round", "operationXp", player.pers["summary"]["operation"] );
|
||||
player setCommonPlayerData( "round", "challengeXp", player.pers["summary"]["challenge"] );
|
||||
player setCommonPlayerData( "round", "matchXp", player.pers["summary"]["match"] );
|
||||
player setCommonPlayerData( "round", "miscXp", player.pers["summary"]["misc"] );
|
||||
player setCommonPlayerDataReservedInt( "common_entitlement_xp", player.pers["summary"]["entitlementXP"] );
|
||||
player setCommonPlayerDataReservedInt( "common_clan_wars_xp", player.pers["summary"]["clanWarsXP"] );
|
||||
}
|
||||
}
|
||||
|
||||
setPlayerScoreboardInfo()
|
||||
{
|
||||
scoreboardPlayerCount = getClientMatchData( "scoreboardPlayerCount" );
|
||||
if ( scoreboardPlayerCount <= 24 ) //MaxPlayers
|
||||
{
|
||||
setClientMatchData( "players", self.clientMatchDataId, "score", self.pers["score"] );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][score]: " + self.pers["score"] );
|
||||
|
||||
if( IsDefined( level.isHorde ) )
|
||||
{
|
||||
kills = self.pers["hordeKills"];
|
||||
}
|
||||
else
|
||||
{
|
||||
kills = self.pers["kills"];
|
||||
}
|
||||
setClientMatchData( "players", self.clientMatchDataId, "kills", kills );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][kills]: " + kills );
|
||||
|
||||
if( IsDefined( level.isHorde ) )
|
||||
{
|
||||
// Horde revives are stored in the "assists" field for the in-game scoreboard, and also for clientmatchdata.
|
||||
assists = self.pers["hordeRevives"];
|
||||
}
|
||||
else if ( level.gameType == "dm" || level.gameType == "sotf_ffa" || level.gameType == "gun" )
|
||||
{
|
||||
// FFA stores max streak in assists
|
||||
assists = self.assists;
|
||||
}
|
||||
else
|
||||
{
|
||||
assists = self.pers["assists"];
|
||||
}
|
||||
setClientMatchData( "players", self.clientMatchDataId, "assists", assists );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][assists]: " + assists );
|
||||
|
||||
deaths = self.pers["deaths"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "deaths", deaths );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][deaths]: " + deaths );
|
||||
|
||||
team = self.pers["team"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "team", team );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][team]: " + team );
|
||||
|
||||
faction = game[self.pers["team"]];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "faction", faction );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][faction]: " + faction );
|
||||
|
||||
extrascore0 = self.pers["extrascore0"];
|
||||
setClientMatchData( "players", self.clientMatchDataId, "extrascore0", extrascore0 );
|
||||
println( "Scoreboard: [" + self.name + "(" + self.clientMatchDataId + ")][extrascore0]: " + extrascore0 );
|
||||
|
||||
println( "Scoreboard: scoreboardPlayerCount was " + scoreboardPlayerCount );
|
||||
scoreboardPlayerCount++;
|
||||
setClientMatchData( "scoreboardPlayerCount", scoreboardPlayerCount );
|
||||
println( "Scoreboard: scoreboardPlayerCount now " + scoreboardPlayerCount );
|
||||
}
|
||||
else
|
||||
{
|
||||
println( "Scoreboard: scoreboardPlayerCount is greater than 24 (" + scoreboardPlayerCount + ")" );
|
||||
}
|
||||
}
|
||||
|
||||
buildScoreboardType( team )
|
||||
{
|
||||
assert( team == "allies" || team == "axis" || team == "neutral" || team == "multiteam" );
|
||||
|
||||
println( "Scoreboard: Building scoreboard (" + team + ")" );
|
||||
|
||||
if ( team == "multiteam" )
|
||||
{
|
||||
index = 0;
|
||||
|
||||
foreach( teamname in level.teamNameList )
|
||||
{
|
||||
foreach ( player in level.placement[teamname] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", "multiteam", index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.clientMatchDataId + "]" );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( team == "neutral" )
|
||||
{
|
||||
index = 0;
|
||||
foreach ( player in level.placement["all"] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", team, index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.clientMatchDataId + "]" );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
otherTeam = getOtherTeam( team );
|
||||
|
||||
index = 0;
|
||||
foreach ( player in level.placement[team] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", team, index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.name + "(" + player.clientMatchDataId + ")]" );
|
||||
index++;
|
||||
}
|
||||
|
||||
foreach ( player in level.placement[otherTeam] )
|
||||
{
|
||||
setClientMatchData( "scoreboards", team, index, player.clientMatchDataId );
|
||||
println( "Scoreboard: [scoreboards][" + team + "][" + index + "][" + player.name + "(" + player.clientMatchDataId + ")]" );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
179
maps/mp/_shutter.gsc
Normal file
179
maps/mp/_shutter.gsc
Normal file
@@ -0,0 +1,179 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
main()
|
||||
{
|
||||
// thread windController();
|
||||
level.inc = 0;
|
||||
|
||||
array_levelthread (getentarray("wire","targetname"), ::wireWander);
|
||||
|
||||
leftShutters = getentarray ("shutter_left","targetname");
|
||||
addShutters = getentarray ("shutter_right_open","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
leftShutters[leftShutters.size] = addShutters[i];
|
||||
addShutters = getentarray ("shutter_left_closed","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
leftShutters[leftShutters.size] = addShutters[i];
|
||||
|
||||
for (i=0;i<leftShutters.size;i++)
|
||||
{
|
||||
shutter = leftShutters[i];
|
||||
shutter rotateto((shutter.angles[0], shutter.angles[1] + 180, shutter.angles[2]), 0.1);
|
||||
}
|
||||
wait (0.2);
|
||||
|
||||
for (i=0;i<leftShutters.size;i++)
|
||||
leftShutters[i].startYaw = leftShutters[i].angles[1];
|
||||
|
||||
rightShutters = getentarray ("shutter_right","targetname");
|
||||
addShutters = getentarray ("shutter_left_open","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
rightShutters[rightShutters.size] = addShutters[i];
|
||||
addShutters = getentarray ("shutter_right_closed","targetname");
|
||||
for (i=0;i<addShutters.size;i++)
|
||||
rightShutters[rightShutters.size] = addShutters[i];
|
||||
|
||||
for (i=0;i<rightShutters.size;i++)
|
||||
rightShutters[i].startYaw = rightShutters[i].angles[1];
|
||||
|
||||
addShutters = undefined;
|
||||
|
||||
windDirection = "left";
|
||||
for (;;)
|
||||
{
|
||||
array_levelthread (leftShutters, ::shutterWanderLeft, windDirection);
|
||||
array_levelthread (rightShutters, ::shutterWanderRight, windDirection);
|
||||
level waittill ("wind blows", windDirection);
|
||||
}
|
||||
}
|
||||
|
||||
windController()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
windDirection = "left";
|
||||
if (randomint(100) > 50)
|
||||
windDirection = "right";
|
||||
level notify ("wind blows", windDirection);
|
||||
wait (2 + randomfloat(10));
|
||||
}
|
||||
}
|
||||
|
||||
shutterWanderLeft(shutter, windDirection)
|
||||
{
|
||||
// println ("shutter angles ", shutter.angles[1]);
|
||||
// assert (shutter.angles[1] >= shutter.startYaw);
|
||||
// assert (shutter.angles[1] < shutter.startYaw + 180);
|
||||
|
||||
// println ("Wind + ", level.inc);
|
||||
level.inc++;
|
||||
level endon ("wind blows");
|
||||
|
||||
newYaw = shutter.startYaw;
|
||||
if (windDirection == "left")
|
||||
newYaw += 179.9;
|
||||
|
||||
newTime = 0.2;
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime);
|
||||
wait (newTime + 0.1);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rot = randomint(80);
|
||||
if (randomint(100) > 50)
|
||||
rot *= -1;
|
||||
|
||||
newYaw = shutter.angles[1] + rot;
|
||||
altYaw = shutter.angles[1] + (rot*-1);
|
||||
if ((newYaw < shutter.startYaw) || (newYaw > shutter.startYaw + 179))
|
||||
{
|
||||
newYaw = altYaw;
|
||||
}
|
||||
|
||||
dif = abs(shutter.angles[1] - newYaw);
|
||||
|
||||
newTime = dif*0.02 + randomfloat(2);
|
||||
if (newTime < 0.3)
|
||||
newTime = 0.3;
|
||||
// println ("startyaw " + shutter.startyaw + " newyaw " + newYaw);
|
||||
|
||||
// assert (newYaw >= shutter.startYaw);
|
||||
// assert (newYaw < shutter.startYaw + 179);
|
||||
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime, newTime * 0.5, newTime * 0.5);
|
||||
wait (newTime);
|
||||
}
|
||||
}
|
||||
|
||||
shutterWanderRight(shutter, windDirection)
|
||||
{
|
||||
// println ("shutter angles ", shutter.angles[1]);
|
||||
// assert (shutter.angles[1] >= shutter.startYaw);
|
||||
// assert (shutter.angles[1] < shutter.startYaw + 180);
|
||||
|
||||
// println ("Wind + ", level.inc);
|
||||
level.inc++;
|
||||
level endon ("wind blows");
|
||||
|
||||
newYaw = shutter.startYaw;
|
||||
if (windDirection == "left")
|
||||
newYaw += 179.9;
|
||||
|
||||
newTime = 0.2;
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime);
|
||||
wait (newTime + 0.1);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rot = randomint(80);
|
||||
if (randomint(100) > 50)
|
||||
rot *= -1;
|
||||
|
||||
newYaw = shutter.angles[1] + rot;
|
||||
altYaw = shutter.angles[1] + (rot*-1);
|
||||
if ((newYaw < shutter.startYaw) || (newYaw > shutter.startYaw + 179))
|
||||
{
|
||||
newYaw = altYaw;
|
||||
}
|
||||
|
||||
dif = abs(shutter.angles[1] - newYaw);
|
||||
|
||||
newTime = dif*0.02 + randomfloat(2);
|
||||
if (newTime < 0.3)
|
||||
newTime = 0.3;
|
||||
// println ("startyaw " + shutter.startyaw + " newyaw " + newYaw);
|
||||
|
||||
// assert (newYaw >= shutter.startYaw);
|
||||
// assert (newYaw < shutter.startYaw + 179);
|
||||
|
||||
shutter rotateto((shutter.angles[0], newYaw, shutter.angles[2]), newTime, newTime * 0.5, newTime * 0.5);
|
||||
wait (newTime);
|
||||
}
|
||||
}
|
||||
|
||||
wireWander (wire)
|
||||
{
|
||||
origins = getentarray (wire.target,"targetname");
|
||||
org1 = origins[0].origin;
|
||||
org2 = origins[1].origin;
|
||||
|
||||
angles = vectortoangles (org1 - org2);
|
||||
ent = spawn ("script_model",(0,0,0));
|
||||
ent.origin = ( org1 * 0.5 ) + ( org2 * 0.5 );
|
||||
// ent setmodel ("temp");
|
||||
ent.angles = angles;
|
||||
wire linkto (ent);
|
||||
rottimer = 2;
|
||||
rotrange = 0.9;
|
||||
dist = 4 + randomfloat(2);
|
||||
ent rotateroll(dist*0.5,0.2);
|
||||
wait (0.2);
|
||||
for (;;)
|
||||
{
|
||||
rottime = rottimer + randomfloat (rotRange) - (rotRange * 0.5);
|
||||
ent rotateroll(dist,rottime, rottime*0.5, rottime*0.5);
|
||||
wait (rottime);
|
||||
ent rotateroll(dist * -1,rottime, rottime*0.5, rottime*0.5);
|
||||
wait (rottime);
|
||||
}
|
||||
}
|
||||
319
maps/mp/_stinger.gsc
Normal file
319
maps/mp/_stinger.gsc
Normal file
@@ -0,0 +1,319 @@
|
||||
#include maps\mp\_utility;
|
||||
|
||||
InitStingerUsage()
|
||||
{
|
||||
self.stingerStage = undefined;
|
||||
self.stingerTarget = undefined;
|
||||
self.stingerLockStartTime = undefined;
|
||||
self.stingerLostSightlineTime = undefined;
|
||||
|
||||
self thread ResetStingerLockingOnDeath();
|
||||
level.stingerTargets = [];
|
||||
}
|
||||
|
||||
|
||||
ResetStingerLocking()
|
||||
{
|
||||
if ( !IsDefined( self.stingerUseEntered ) )
|
||||
return;
|
||||
self.stingerUseEntered = undefined;
|
||||
|
||||
self notify( "stop_javelin_locking_feedback" );
|
||||
self notify( "stop_javelin_locked_feedback" );
|
||||
|
||||
self WeaponLockFree();
|
||||
InitStingerUsage();
|
||||
}
|
||||
|
||||
|
||||
ResetStingerLockingOnDeath()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self notify ( "ResetStingerLockingOnDeath" );
|
||||
self endon ( "ResetStingerLockingOnDeath" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill( "death" );
|
||||
self ResetStingerLocking();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StillValidStingerLock( ent )
|
||||
{
|
||||
assert( IsDefined( self ) );
|
||||
|
||||
if ( !IsDefined( ent ) )
|
||||
return false;
|
||||
if ( !(self WorldPointInReticle_Circle( ent.origin, 65, 85 )) )
|
||||
return false;
|
||||
|
||||
if ( self.stingerTarget == level.ac130.planeModel && !isDefined( level.ac130player ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
LoopStingerLockingFeedback()
|
||||
{
|
||||
self endon( "stop_javelin_locking_feedback" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if ( isDefined( level.chopper ) && isDefined( level.chopper.gunner ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.chopper.gunner )
|
||||
level.chopper.gunner playLocalSound( "missile_locking" );
|
||||
|
||||
if ( isDefined( level.ac130player ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.ac130.planeModel )
|
||||
level.ac130player playLocalSound( "missile_locking" );
|
||||
|
||||
self playLocalSound( "stinger_locking" );
|
||||
self PlayRumbleOnEntity( "ac130_25mm_fire" );
|
||||
|
||||
wait 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LoopStingerLockedFeedback()
|
||||
{
|
||||
self endon( "stop_javelin_locked_feedback" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if ( isDefined( level.chopper ) && isDefined( level.chopper.gunner ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.chopper.gunner )
|
||||
level.chopper.gunner playLocalSound( "missile_locking" );
|
||||
|
||||
if ( isDefined( level.ac130player ) && isDefined( self.stingerTarget ) && self.stingerTarget == level.ac130.planeModel )
|
||||
level.ac130player playLocalSound( "missile_locking" );
|
||||
|
||||
self playLocalSound( "stinger_locked" );
|
||||
self PlayRumbleOnEntity( "ac130_25mm_fire" );
|
||||
|
||||
wait 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
DrawStar( point )
|
||||
{
|
||||
Line( point + (10,0,0), point - (10,0,0) );
|
||||
Line( point + (0,10,0), point - (0,10,0) );
|
||||
Line( point + (0,0,10), point - (0,0,10) );
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
LockSightTest( target )
|
||||
{
|
||||
eyePos = self GetEye();
|
||||
|
||||
if ( !isDefined( target ) ) //targets can disapear during targeting.
|
||||
return false;
|
||||
|
||||
passed = SightTracePassed( eyePos, target.origin, false, target );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
front = target GetPointInBounds( 1, 0, 0 );
|
||||
passed = SightTracePassed( eyePos, front, false, target );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
back = target GetPointInBounds( -1, 0, 0 );
|
||||
passed = SightTracePassed( eyePos, back, false, target );
|
||||
if ( passed )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
StingerDebugDraw( target )
|
||||
{
|
||||
/#
|
||||
if ( GetDVar( "missileDebugDraw" ) != "1" )
|
||||
return;
|
||||
if ( !IsDefined( target ) )
|
||||
return;
|
||||
|
||||
org = target.origin;
|
||||
DrawStar( org );
|
||||
org = target GetPointInBounds( 1, 0, 0 );
|
||||
DrawStar( org );
|
||||
org = target GetPointInBounds( -1, 0, 0 );
|
||||
DrawStar( org );
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
SoftSightTest()
|
||||
{
|
||||
LOST_SIGHT_LIMIT = 500;
|
||||
|
||||
if ( self LockSightTest( self.stingerTarget ) )
|
||||
{
|
||||
self.stingerLostSightlineTime = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( self.stingerLostSightlineTime == 0 )
|
||||
self.stingerLostSightlineTime = getTime();
|
||||
|
||||
timePassed = GetTime() - self.stingerLostSightlineTime;
|
||||
//PrintLn( "Losing sight of target [", timePassed, "]..." );
|
||||
|
||||
if ( timePassed >= LOST_SIGHT_LIMIT )
|
||||
{
|
||||
//PrintLn( "Lost sight of target." );
|
||||
ResetStingerLocking();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StingerUsageLoop()
|
||||
{
|
||||
if ( !IsPlayer(self) )
|
||||
return;
|
||||
|
||||
self endon("death");
|
||||
self endon("disconnect");
|
||||
self endon("faux_spawn");
|
||||
|
||||
LOCK_LENGTH = 1000;
|
||||
|
||||
InitStingerUsage();
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait 0.05;
|
||||
|
||||
if ( self PlayerADS() < 0.95 )
|
||||
{
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
weapon = self getCurrentWeapon();
|
||||
|
||||
if ( weapon != "stinger_mp" && weapon != "at4_mp" && weapon != "iw5_smaw_mp" )
|
||||
{
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
self.stingerUseEntered = true;
|
||||
|
||||
if ( !IsDefined( self.stingerStage ) )
|
||||
self.stingerStage = 0;
|
||||
|
||||
StingerDebugDraw( self.stingerTarget );
|
||||
|
||||
if ( self.stingerStage == 0 ) // searching for target
|
||||
{
|
||||
targets = maps\mp\gametypes\_weapons::lockOnLaunchers_getTargetArray();
|
||||
if ( targets.size == 0 )
|
||||
continue;
|
||||
|
||||
targetsInReticle = [];
|
||||
foreach ( target in targets )
|
||||
{
|
||||
if ( !isDefined( target ) )
|
||||
continue;
|
||||
|
||||
insideReticle = self WorldPointInReticle_Circle( target.origin, 65, 75 );
|
||||
|
||||
if ( insideReticle )
|
||||
targetsInReticle[targetsInReticle.size] = target;
|
||||
}
|
||||
if ( targetsInReticle.size == 0 )
|
||||
continue;
|
||||
|
||||
sortedTargets = SortByDistance( targetsInReticle, self.origin );
|
||||
if ( !( self LockSightTest( sortedTargets[0] ) ) )
|
||||
continue;
|
||||
|
||||
//PrintLn( "Found a target to lock to..." );
|
||||
thread LoopStingerLockingFeedback();
|
||||
self.stingerTarget = sortedTargets[0];
|
||||
self.stingerLockStartTime = GetTime();
|
||||
self.stingerStage = 1;
|
||||
self.stingerLostSightlineTime = 0;
|
||||
}
|
||||
|
||||
if ( self.stingerStage == 1 ) // locking on to a target
|
||||
{
|
||||
if ( !(self StillValidStingerLock( self.stingerTarget )) )
|
||||
{
|
||||
//PrintLn( "Failed to get lock." );
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
|
||||
passed = SoftSightTest();
|
||||
if ( !passed )
|
||||
continue;
|
||||
|
||||
timePassed = getTime() - self.stingerLockStartTime;
|
||||
//PrintLn( "Locking [", timePassed, "]..." );
|
||||
if( self _hasPerk( "specialty_fasterlockon" ) )
|
||||
{
|
||||
if( timePassed < ( LOCK_LENGTH * 0.5 ) )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( timePassed < LOCK_LENGTH )
|
||||
continue;
|
||||
}
|
||||
|
||||
self notify( "stop_javelin_locking_feedback" );
|
||||
thread LoopStingerLockedFeedback();
|
||||
|
||||
//PrintLn( "Locked!");
|
||||
if( checkVehicleModelForLock( self.stingerTarget.model ) )
|
||||
self WeaponLockFinalize( self.stingerTarget );
|
||||
else if ( isPlayer( self.stingerTarget ) )
|
||||
self WeaponLockFinalize( self.stingerTarget, ( 100,0, 64 ) );
|
||||
else
|
||||
self WeaponLockFinalize( self.stingerTarget, ( 100,0,-32 ) );
|
||||
|
||||
self.stingerStage = 2;
|
||||
}
|
||||
|
||||
if ( self.stingerStage == 2 ) // target locked
|
||||
{
|
||||
passed = SoftSightTest();
|
||||
if ( !passed )
|
||||
continue;
|
||||
|
||||
if ( !(self StillValidStingerLock( self.stingerTarget )) )
|
||||
{
|
||||
//PrintLn( "Gave up lock." );
|
||||
ResetStingerLocking();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkVehicleModelForLock( model )
|
||||
{
|
||||
switch( model )
|
||||
{
|
||||
case "vehicle_av8b_harrier_jet_opfor_mp":
|
||||
case "vehicle_av8b_harrier_jet_mp":
|
||||
case "vehicle_ugv_talon_mp":
|
||||
return true;
|
||||
default:
|
||||
if( model == level.littlebird_model )
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
1474
maps/mp/_teleport.gsc
Normal file
1474
maps/mp/_teleport.gsc
Normal file
File diff suppressed because it is too large
Load Diff
7455
maps/mp/_utility.gsc
Normal file
7455
maps/mp/_utility.gsc
Normal file
File diff suppressed because it is too large
Load Diff
425
maps/mp/_water.gsc
Normal file
425
maps/mp/_water.gsc
Normal file
@@ -0,0 +1,425 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
waterShallowFx()
|
||||
{
|
||||
level._effect[ "water_kick" ] = LoadFX( "vfx/moments/flood/flood_db_foam_allie_ch_fast_cheap" );
|
||||
level._effect[ "water_splash_emerge" ] = LoadFX( "vfx/moments/flood/flood_db_foam_allie_vertical_splash_sml" );
|
||||
level._effect[ "water_splash_large" ] = LoadFX( "vfx/moments/flood/flood_db_foam_allie_vertical_splash_lrg" );
|
||||
}
|
||||
|
||||
waterShallowInit( waterDeleteZ, waterShallowSplashZ )
|
||||
{
|
||||
if ( IsDefined( waterDeleteZ ) )
|
||||
{
|
||||
level.waterDeleteZ = waterDeleteZ;
|
||||
}
|
||||
|
||||
level.trigUnderWater = GetEnt( "trigger_underwater", "targetname" );
|
||||
level.trigAboveWater = GetEnt( "trigger_abovewater", "targetname" );
|
||||
level.trigUnderWater thread watchPlayerEnterWater( level.trigAboveWater, waterShallowSplashZ );
|
||||
level thread clearWaterVarsOnSpawn( level.trigUnderWater );
|
||||
|
||||
if ( IsDefined( waterDeleteZ ) )
|
||||
{
|
||||
level thread watchEntsInDeepWater( level.trigUnderWater, waterDeleteZ );
|
||||
}
|
||||
}
|
||||
|
||||
watchEntsInDeepWater( trigUnderWater, waterDeleteZ )
|
||||
{
|
||||
level childthread watchEntsInDeepWater_mines( trigUnderWater, waterDeleteZ );
|
||||
level childthread watchEntsInDeepWater_ks( trigUnderWater, waterDeleteZ );
|
||||
}
|
||||
|
||||
CONST_SENTRY_AND_AGENT_OFFSET = 35;
|
||||
CONST_TROPHY_OFFSET = 28;
|
||||
watchEntsInDeepWater_ks( trigUnderWater, waterDeleteZ )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
wait( 0.05 );
|
||||
|
||||
ksToDestroy = level.placedIMS;
|
||||
ksToDestroy = array_combine( ksToDestroy, level.uplinks );
|
||||
ksToDestroy = array_combine( ksToDestroy, level.turrets );
|
||||
|
||||
foreach ( kstreak in ksToDestroy )
|
||||
{
|
||||
if ( !IsDefined( kstreak ) )
|
||||
continue;
|
||||
waterZoverride = ter_op( IsDefined( kstreak.sentrytype ) && kstreak.sentrytype == "sentry_minigun", waterDeleteZ - CONST_SENTRY_AND_AGENT_OFFSET, waterDeleteZ );
|
||||
if ( kstreak.origin[ 2 ] <= waterZoverride && kstreak IsTouching( trigUnderWater ) )
|
||||
{
|
||||
// Destroy ims, uplink or sentry
|
||||
kstreak notify( "death" );
|
||||
}
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
// Damage bots and agents that fall into deep water to prevent AI
|
||||
// from getting stuck. This will do 8% damage 10 times a second.
|
||||
if ( IsDefined( character )
|
||||
&& IsAlive( character )
|
||||
&& IsAI( character )
|
||||
&& character.origin[ 2 ] <= waterDeleteZ - CONST_SENTRY_AND_AGENT_OFFSET // Most AI will early out here
|
||||
&& character IsTouching( trigUnderWater )
|
||||
)
|
||||
{
|
||||
if ( IsAgent( character )
|
||||
&& IsDefined( character.agent_type )
|
||||
&& character.agent_type == "dog"
|
||||
)
|
||||
{
|
||||
if ( !IsDefined(character.spawnTime) || (GetTime() - character.spawnTime) > 2000 ) // Wait till dog has existed for 2s before we destroy him (so that his body can be visible)
|
||||
{
|
||||
// Dogs block DoDamage() code call so use agent damage func
|
||||
character [[ character maps\mp\agents\_agent_utility::agentFunc( "on_damaged" ) ]]( level, undefined, Int( ceil( character.maxhealth * 0.08 ) ), 0, "MOD_CRUSH", "none", ( 0, 0, 0 ), (0, 0, 0), "none", 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character DoDamage( Int( ceil( character.maxhealth * 0.08 ) ), character.origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchEntsInDeepWater_mines( trigUnderWater, waterDeleteZ )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "mine_planted" );
|
||||
|
||||
// In case multiple mines land this frame
|
||||
waittillframeend;
|
||||
|
||||
mines = level.mines;
|
||||
foreach ( uId, mine in mines )
|
||||
{
|
||||
if ( !IsDefined( mine ) )
|
||||
continue;
|
||||
|
||||
offset = 0;
|
||||
if ( IsDefined( mine.isTallForWaterChecks ) )
|
||||
offset = CONST_TROPHY_OFFSET;
|
||||
|
||||
if ( mine.origin[ 2 ] <= waterDeleteZ - offset && mine IsTouching( trigUnderWater ) )
|
||||
{
|
||||
mine maps\mp\gametypes\_weapons::deleteExplosive();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearWaterVarsOnSpawn( underWater )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "player_spawned", player );
|
||||
|
||||
if ( !player IsTouching( underWater ) )
|
||||
{
|
||||
player.inWater = undefined;
|
||||
player.underWater = undefined;
|
||||
player notify( "out_of_water" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchPlayerEnterWater( aboveWater, waterShallowSplashZ )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "trigger", ent );
|
||||
|
||||
if ( !IsPlayer( ent ) && !IsAgent( ent ) )
|
||||
continue;
|
||||
|
||||
// Don't let dogs die from water because they take
|
||||
// damage all the time due to height
|
||||
if ( !IsAlive( ent ) || ( IsAgent( ent ) && IsDefined( ent.agent_type ) && ent.agent_type == "dog" ) )
|
||||
continue;
|
||||
|
||||
if ( !IsDefined( ent.inWater ) )
|
||||
{
|
||||
ent.inWater = true;
|
||||
ent thread playerInWater( self, aboveWater, waterShallowSplashZ );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerSplash()
|
||||
{
|
||||
self endon( "out_of_water" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
vel = self GetVelocity();
|
||||
if ( vel[ 2 ] > -100 )
|
||||
return;
|
||||
|
||||
wait( 0.2 );
|
||||
|
||||
vel = self GetVelocity();
|
||||
if ( vel[ 2 ] <= -100 )
|
||||
{
|
||||
self PlaySound( "watersplash_lrg" );
|
||||
PlayFX( level._effect[ "water_splash_large" ], self.origin + (0,0,36), (0,0,1), AnglesToForward( (0,self.angles[1],0) ) );
|
||||
}
|
||||
}
|
||||
|
||||
playerInWater( underWater, aboveWater, waterShallowSplashZ )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self thread inWaterWake( waterShallowSplashZ );
|
||||
self thread playerWaterClearWait();
|
||||
|
||||
self thread playerSplash();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( !self IsTouching( underWater ) )
|
||||
{
|
||||
self.inWater = undefined;
|
||||
self.underWater = undefined;
|
||||
self notify( "out_of_water" );
|
||||
stopWaterVisuals();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !IsDefined( self.underWater ) && !self IsTouching( aboveWater ) )
|
||||
{
|
||||
// if we do want remotes to function in water, we'll need to filter by type
|
||||
if( self.classname == "script_vehicle" )
|
||||
{
|
||||
self notify( "death" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.underWater = true;
|
||||
self thread playerUnderWater();
|
||||
}
|
||||
}
|
||||
|
||||
// Player emerged from under water and touched above water trigger
|
||||
if ( IsDefined( self.underWater ) && self IsTouching( aboveWater ) )
|
||||
{
|
||||
self.underWater = undefined;
|
||||
self notify( "above_water" );
|
||||
stopWaterVisuals();
|
||||
|
||||
if ( IsPlayer( self ) )
|
||||
{
|
||||
if ( self hasFemaleCustomizationModel() )
|
||||
{
|
||||
self playLocalSound("Fem_breathing_better");
|
||||
}
|
||||
else
|
||||
{
|
||||
self playLocalSound("breathing_better");
|
||||
}
|
||||
}
|
||||
PlayFX( level._effect[ "water_splash_emerge" ], self.origin + (0,0,24) );
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
IsActiveKillstreakPoolRestricted( player )
|
||||
{
|
||||
if ( IsDefined( player.killstreakIndexWeapon ) )
|
||||
{
|
||||
streakName = self.pers[ "killstreaks" ][ self.killstreakIndexWeapon ].streakName;
|
||||
if ( IsDefined( streakName ) )
|
||||
{
|
||||
switch ( streakName )
|
||||
{
|
||||
case "remote_uav":
|
||||
case "remote_mg_turret":
|
||||
case "minigun_turret":
|
||||
case "ims":
|
||||
case "sentry":
|
||||
case "remote_tank":
|
||||
case "sam_turret":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
playerWaterClearWait()
|
||||
{
|
||||
self waittill_any( "death", "disconnect", "out_of_water" );
|
||||
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
self.inWater = undefined;
|
||||
self.underWater = undefined;
|
||||
}
|
||||
|
||||
CONST_WATER_KICK_MIN_MSEC = 200;
|
||||
|
||||
inWaterWake( waterShallowSplashZ )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "out_of_water" );
|
||||
|
||||
zGround = ter_op( IsDefined( waterShallowSplashZ ), waterShallowSplashZ, self.origin[2] );
|
||||
fxPlayedRecently = false;
|
||||
while ( true )
|
||||
{
|
||||
if ( fxPlayedRecently )
|
||||
wait( 0.05 );
|
||||
else
|
||||
wait( 0.3 );
|
||||
|
||||
if ( !IsDefined( level.waterKickTimeNext ) )
|
||||
{
|
||||
level.waterKickTimeNext = GetTime() + CONST_WATER_KICK_MIN_MSEC;
|
||||
}
|
||||
else
|
||||
{
|
||||
time = GetTime();
|
||||
if ( GetTime() < level.waterKickTimeNext )
|
||||
{
|
||||
fxPlayedRecently = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.waterKickTimeNext = time + CONST_WATER_KICK_MIN_MSEC;
|
||||
}
|
||||
}
|
||||
|
||||
fxPlayedRecently = false;
|
||||
|
||||
vel = self GetVelocity();
|
||||
|
||||
if ( !IsDefined( waterShallowSplashZ ) )
|
||||
{
|
||||
if ( abs( vel[ 2 ] ) <= 1 )
|
||||
{
|
||||
zGround = self.origin[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Jump
|
||||
if ( abs( vel[ 2 ] ) > 30 )
|
||||
{
|
||||
PlayFX( level._effect[ "water_kick" ], (self.origin[0], self.origin[1], min( zGround, self.origin[2]) ) );
|
||||
}
|
||||
// Running
|
||||
else if ( Length2DSquared( vel ) > 60 * 60 )
|
||||
{
|
||||
fwd = VectorNormalize( ( vel[0], vel[1], 0 ) );
|
||||
PlayFX( level._effect[ "water_kick" ], (self.origin[0], self.origin[1], min( zGround, self.origin[2] )) + fwd * 36, fwd, (0,0,1) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
playerUnderWater()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "above_water" );
|
||||
self endon( "out_of_water" );
|
||||
|
||||
if ( !self maps\mp\_utility::isUsingRemote() )
|
||||
{
|
||||
self startWaterVisuals();
|
||||
// Disabled underwater bubbles for flooded / all under water maps -JC
|
||||
//self thread underWaterBubbles();
|
||||
// players can glitch into a remote after going underwater
|
||||
self thread stopWaterVisualsOnRemote();
|
||||
}
|
||||
|
||||
PlayFX( level._effect[ "water_splash_emerge" ], self GetEye() - (0,0,24) );
|
||||
|
||||
wait( 2 );
|
||||
self thread onPlayerDrowned();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self DoDamage( 20, self.origin );
|
||||
wait( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerDrowned()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "above_water" );
|
||||
self endon( "out_of_water" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self.inWater = undefined;
|
||||
self.underWater = undefined;
|
||||
}
|
||||
|
||||
underWaterBubbles()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "using_remote" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "above_water" );
|
||||
self endon( "out_of_water" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
PlayFX( level._effect[ "water_bubbles" ], self GetEye() + ( AnglesToUp( self.angles ) * -13 ) + ( AnglesToForward( self.angles ) * 25 ) );
|
||||
wait( 0.75 );
|
||||
}
|
||||
}
|
||||
|
||||
startWaterVisuals()
|
||||
{
|
||||
self ShellShock( "mp_flooded_water", 8 );
|
||||
if ( IsPlayer( self ) )
|
||||
self SetBlurForPlayer( 10, 0.0 );
|
||||
}
|
||||
|
||||
stopWaterVisuals()
|
||||
{
|
||||
self StopShellShock();
|
||||
if ( IsPlayer( self ) )
|
||||
self SetBlurForPlayer( 0, 0.85 );
|
||||
}
|
||||
|
||||
stopWaterVisualsOnRemote()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "above_water" );
|
||||
self endon( "out_of_water" );
|
||||
|
||||
self waittill( "using_remote" );
|
||||
self stopWaterVisuals();
|
||||
}
|
||||
136
maps/mp/_zipline.gsc
Normal file
136
maps/mp/_zipline.gsc
Normal file
@@ -0,0 +1,136 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// prototype for generic zipline / rappel
|
||||
// - player can use their weapon while travelling on line
|
||||
// - player can drop off line by jumping
|
||||
|
||||
// SETUP:
|
||||
// - trigger_touch marks the area where the player will be prompted to use the zipline (suggest 32x32x32 trigger touching ground)
|
||||
// - script_origin marked as the target of the trigger_use_touch is the destination
|
||||
|
||||
init()
|
||||
{
|
||||
visuals = [];
|
||||
triggers = getentarray("zipline", "targetname");
|
||||
|
||||
for( i = 0; i < triggers.size; i++ )
|
||||
{
|
||||
zipline = maps\mp\gametypes\_gameobjects::createUseObject( "neutral", triggers[i], visuals, (0,0,0) );
|
||||
zipline maps\mp\gametypes\_gameobjects::allowUse( "any" );
|
||||
zipline maps\mp\gametypes\_gameobjects::setUseTime( 0.25 );
|
||||
zipline maps\mp\gametypes\_gameobjects::setUseText( &"MP_ZIPLINE_USE" );
|
||||
zipline maps\mp\gametypes\_gameobjects::setUseHintText( &"MP_ZIPLINE_USE" );
|
||||
zipline maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
|
||||
zipline.onBeginUse = ::onBeginUse;
|
||||
zipline.onUse = ::onUse;
|
||||
|
||||
targets = [];
|
||||
target = getEnt( triggers[i].target, "targetname");
|
||||
|
||||
if ( !isDefined( target ) )
|
||||
assertmsg( "No target found for zipline trigger located at: ( " + triggers[i].origin[0] + ", " + triggers[i].origin[1] + ", " + triggers[i].origin[2] + " )" );
|
||||
|
||||
while ( isDefined( target ) )
|
||||
{
|
||||
targets[targets.size] = target;
|
||||
if ( isDefined( target.target ) )
|
||||
target = getEnt( target.target, "targetname");
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
zipline.targets = targets;
|
||||
}
|
||||
|
||||
precacheModel( "tag_player" );
|
||||
}
|
||||
|
||||
|
||||
onBeginUse( player )
|
||||
{
|
||||
player playSound( "scrambler_pullout_lift_plr" );
|
||||
}
|
||||
|
||||
|
||||
onUse( player )
|
||||
{
|
||||
player thread zip( self );
|
||||
}
|
||||
|
||||
|
||||
zip( useObj )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "zipline_drop" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
// make the carrier
|
||||
carrier = spawn( "script_origin", useObj.trigger.origin );
|
||||
carrier.origin = useObj.trigger.origin;
|
||||
carrier.angles = self.angles;
|
||||
carrier setModel( "tag_player" );
|
||||
|
||||
// link the player
|
||||
self playerLinkToDelta( carrier, "tag_player", 1, 180, 180, 180, 180 );
|
||||
|
||||
// monitor player
|
||||
self thread watchDeath( carrier );
|
||||
self thread watchDrop( carrier );
|
||||
|
||||
// loop through the path of targets
|
||||
targets = useObj.targets;
|
||||
for( i=0; i < targets.size; i++ )
|
||||
{
|
||||
// time
|
||||
// JDS TODO: look into LDs specifying speed, accelleration, deceleration on the nodes
|
||||
time = distance( carrier.origin, targets[i].origin ) / 600;
|
||||
|
||||
// send it on its way
|
||||
acceleration = 0.0;
|
||||
if ( i==0 )
|
||||
acceleration = time*0.2;
|
||||
carrier moveTo( targets[i].origin, time, acceleration );
|
||||
if ( carrier.angles != targets[i].angles )
|
||||
carrier rotateTo( targets[i].angles, time*0.8 );
|
||||
|
||||
// wait
|
||||
wait( time );
|
||||
}
|
||||
|
||||
// all done
|
||||
self notify( "destination" );
|
||||
self unlink();
|
||||
carrier delete();
|
||||
}
|
||||
|
||||
|
||||
watchDrop( carrier )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "destination" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self notifyOnPlayerCommand( "zipline_drop", "+gostand" );
|
||||
|
||||
self waittill( "zipline_drop" );
|
||||
|
||||
self unlink();
|
||||
carrier delete();
|
||||
}
|
||||
|
||||
|
||||
watchDeath( carrier )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "destination" );
|
||||
self endon( "zipline_drop" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self unlink();
|
||||
carrier delete();
|
||||
}
|
||||
129
maps/mp/agents/_agent_common.gsc
Normal file
129
maps/mp/agents/_agent_common.gsc
Normal file
@@ -0,0 +1,129 @@
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentAdded
|
||||
//=======================================================
|
||||
CodeCallback_AgentAdded()
|
||||
{
|
||||
self initAgentScriptVariables();
|
||||
|
||||
agentTeam = "axis";
|
||||
|
||||
if( (level.numagents % 2) == 0 )
|
||||
{
|
||||
agentTeam = "allies";
|
||||
}
|
||||
|
||||
level.numagents++;
|
||||
self set_agent_team( agentTeam );
|
||||
|
||||
level.agentArray[ level.agentArray.size ] = self;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentDamaged
|
||||
//=======================================================
|
||||
CodeCallback_AgentDamaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
eAttacker = _validateAttacker( eAttacker );
|
||||
|
||||
self [[ self agentFunc( "on_damaged" ) ]]( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentKilled
|
||||
//=======================================================
|
||||
CodeCallback_AgentKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
eAttacker = _validateAttacker( eAttacker );
|
||||
|
||||
self thread [[ self agentFunc("on_killed") ]](eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration);
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// init
|
||||
//========================================================
|
||||
init()
|
||||
{
|
||||
initAgentLevelVariables();
|
||||
|
||||
// add all the agents we're supposed to have in the game with us
|
||||
level thread add_agents_to_game();
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// connectNewAgent
|
||||
//=======================================================
|
||||
connectNewAgent( agent_type, team, class )
|
||||
{
|
||||
agent = getFreeAgent( agent_type );
|
||||
|
||||
if ( IsDefined( agent ) )
|
||||
{
|
||||
agent.connectTime = GetTime();
|
||||
|
||||
if ( IsDefined( team ) )
|
||||
agent set_agent_team( team );
|
||||
else
|
||||
agent set_agent_team( agent.team );
|
||||
|
||||
if ( IsDefined( class ) )
|
||||
agent.class_override = class;
|
||||
|
||||
if( IsDefined(level.agent_funcs[agent_type]["onAIConnect"]) )
|
||||
agent [[ agent agentFunc("onAIConnect") ]]();
|
||||
|
||||
agent maps\mp\gametypes\_spawnlogic::addToCharactersArray();
|
||||
|
||||
AssertEx(agent.connectTime == GetTime(), "Agent spawn took too long - there should be no waits in connectNewAgent");
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// initAgentLevelVariables
|
||||
//========================================================
|
||||
initAgentLevelVariables()
|
||||
{
|
||||
level.agentArray = [];
|
||||
level.numagents = 0;
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// add_agents_to_game
|
||||
//========================================================
|
||||
add_agents_to_game()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level waittill("connected", player);
|
||||
|
||||
maxagents = GetMaxAgents();
|
||||
|
||||
while( level.agentArray.size < maxagents )
|
||||
{
|
||||
agent = AddAgent();
|
||||
|
||||
if( !IsDefined( agent) )
|
||||
{
|
||||
waitframe();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// set_agent_health
|
||||
//========================================================
|
||||
set_agent_health( health )
|
||||
{
|
||||
self.agenthealth = health;
|
||||
self.health = health;
|
||||
self.maxhealth = health;
|
||||
}
|
||||
447
maps/mp/agents/_agent_utility.gsc
Normal file
447
maps/mp/agents/_agent_utility.gsc
Normal file
@@ -0,0 +1,447 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
//========================================================
|
||||
// agentFunc
|
||||
//========================================================
|
||||
agentFunc( func_name )
|
||||
{
|
||||
assert( IsAgent( self ) );
|
||||
assert( IsDefined(func_name) );
|
||||
assert( isDefined(self.agent_type) );
|
||||
assert( isDefined(level.agent_funcs[self.agent_type]) );
|
||||
assert( isDefined(level.agent_funcs[self.agent_type][func_name]) );
|
||||
|
||||
return level.agent_funcs[self.agent_type][func_name];
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// set_agent_team
|
||||
//========================================================
|
||||
set_agent_team( team, optional_owner )
|
||||
{
|
||||
// since an agent entity has both a "sentient" and an "agent", we need both
|
||||
// these to understand the team the entity is on (much as client entities
|
||||
// have a "sentient" and a "client"). The "team" field sets the "sentient"
|
||||
// team and the "agentteam" field sets the "agent" team.
|
||||
self.team = team;
|
||||
self.agentteam = team;
|
||||
self.pers["team"] = team;
|
||||
|
||||
self.owner = optional_owner;
|
||||
self SetOtherEnt( optional_owner );
|
||||
self SetEntityOwner( optional_owner );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// initAgentScriptVariables
|
||||
//=======================================================
|
||||
initAgentScriptVariables()
|
||||
{
|
||||
self.agent_type = "player"; // TODO: communicate this to code?
|
||||
self.pers = [];
|
||||
self.hasDied = false;
|
||||
self.isActive = false;
|
||||
self.isAgent = true;
|
||||
self.wasTI = false;
|
||||
self.isSniper = false;
|
||||
self.spawnTime = 0;
|
||||
self.entity_number = self GetEntityNumber();
|
||||
self.agent_teamParticipant = false;
|
||||
self.agent_gameParticipant = false;
|
||||
self.canPerformClientTraces = false;
|
||||
self.agentname = undefined;
|
||||
|
||||
self DetachAll();
|
||||
|
||||
self initPlayerScriptVariables( false );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// initPlayerScriptVariables
|
||||
//========================================================
|
||||
initPlayerScriptVariables( asPlayer )
|
||||
{
|
||||
if ( !asPlayer )
|
||||
{
|
||||
// Not as a player
|
||||
self.class = undefined;
|
||||
self.lastClass = undefined;
|
||||
self.moveSpeedScaler = undefined;
|
||||
self.avoidKillstreakOnSpawnTimer = undefined;
|
||||
self.guid = undefined;
|
||||
self.name = undefined;
|
||||
self.saved_actionSlotData = undefined;
|
||||
self.perks = undefined;
|
||||
self.weaponList = undefined;
|
||||
self.omaClassChanged = undefined;
|
||||
self.objectiveScaler = undefined;
|
||||
self.touchTriggers = undefined;
|
||||
self.carryObject = undefined;
|
||||
self.claimTrigger = undefined;
|
||||
self.canPickupObject = undefined;
|
||||
self.killedInUse = undefined;
|
||||
self.sessionteam = undefined;
|
||||
self.sessionstate = undefined;
|
||||
self.lastSpawnTime = undefined;
|
||||
self.lastspawnpoint = undefined;
|
||||
self.disabledWeapon = undefined;
|
||||
self.disabledWeaponSwitch = undefined;
|
||||
self.disabledOffhandWeapons = undefined;
|
||||
self.disabledUsability = undefined;
|
||||
self.shieldDamage = undefined;
|
||||
self.shieldBulletHits = undefined;
|
||||
self.recentShieldXP = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
// As a player
|
||||
self.moveSpeedScaler = 1;
|
||||
self.avoidKillstreakOnSpawnTimer = 5;
|
||||
self.guid = self getUniqueId();
|
||||
self.name = self.guid;
|
||||
self.sessionteam = self.team;
|
||||
self.sessionstate = "playing";
|
||||
self.shieldDamage = 0;
|
||||
self.shieldBulletHits = 0;
|
||||
self.recentShieldXP = 0;
|
||||
self.agent_gameParticipant = true; // If initialized as a player, always make agent a game participant
|
||||
|
||||
self maps\mp\gametypes\_playerlogic::setupSavedActionSlots();
|
||||
self thread maps\mp\perks\_perks::onPlayerSpawned();
|
||||
|
||||
if ( IsGameParticipant( self ) )
|
||||
{
|
||||
self.objectiveScaler = 1;
|
||||
self maps\mp\gametypes\_gameobjects::init_player_gameobjects();
|
||||
self.disabledWeapon = 0;
|
||||
self.disabledWeaponSwitch = 0;
|
||||
self.disabledOffhandWeapons = 0;
|
||||
}
|
||||
}
|
||||
|
||||
self.disabledUsability = 1;
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// getFreeAgent
|
||||
//===========================================
|
||||
getFreeAgent( agent_type )
|
||||
{
|
||||
freeAgent = undefined;
|
||||
|
||||
if( IsDefined( level.agentArray ) )
|
||||
{
|
||||
foreach( agent in level.agentArray )
|
||||
{
|
||||
if( !IsDefined( agent.isActive ) || !agent.isActive )
|
||||
{
|
||||
if ( IsDefined(agent.waitingToDeactivate) && agent.waitingToDeactivate )
|
||||
continue;
|
||||
|
||||
freeAgent = agent;
|
||||
|
||||
freeAgent initAgentScriptVariables();
|
||||
|
||||
if ( IsDefined( agent_type ) )
|
||||
freeAgent.agent_type = agent_type; // TODO: communicate this to code?
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return freeAgent;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// activateAgent
|
||||
//=======================================================
|
||||
activateAgent()
|
||||
{
|
||||
/#
|
||||
if ( !self.isActive )
|
||||
{
|
||||
// Activating this agent, ensure that he has connected on the same frame
|
||||
AssertEx(self.connectTime == GetTime(), "Agent spawn took too long - there should be no waits in between connectNewAgent and spawning the agent");
|
||||
}
|
||||
#/
|
||||
self.isActive = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======================================================
|
||||
// deactivateAgent
|
||||
//=======================================================
|
||||
deactivateAgent()
|
||||
{
|
||||
self thread deactivateAgentDelayed();
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// deactivateAgentDelayed
|
||||
//=======================================================
|
||||
deactivateAgentDelayed()
|
||||
{
|
||||
self notify("deactivateAgentDelayed");
|
||||
self endon("deactivateAgentDelayed");
|
||||
|
||||
// During the 0.05s wait in deactivateAgentDelayed, the agent's script variables are all cleared out
|
||||
// So we need to do this now while IsGameParticipant can still be checked
|
||||
if ( IsGameParticipant(self) )
|
||||
self maps\mp\gametypes\_spawnlogic::removeFromParticipantsArray();
|
||||
|
||||
self maps\mp\gametypes\_spawnlogic::removeFromCharactersArray();
|
||||
|
||||
// Wait till next frame before we "disconnect"
|
||||
// That way things waiting on "death" but have endon("disconnect") will still function
|
||||
// e.g. maps\mp\killstreaks\_juggernaut::juggRemover()
|
||||
wait 0.05;
|
||||
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
self.owner = undefined;
|
||||
self.connectTime = undefined;
|
||||
self.waitingToDeactivate = undefined;
|
||||
|
||||
// Clear this agent from any other character's attackers array
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
if ( IsDefined( character.attackers ) )
|
||||
{
|
||||
foreach ( index, attacker in character.attackers )
|
||||
{
|
||||
if ( attacker == self )
|
||||
character.attackers[index] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( self.headModel ) )
|
||||
{
|
||||
self Detach( self.headModel );
|
||||
self.headModel = undefined;
|
||||
}
|
||||
|
||||
self notify("disconnect");
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getNumActiveAgents
|
||||
//===========================================
|
||||
getNumActiveAgents( type )
|
||||
{
|
||||
if ( !IsDefined(type) )
|
||||
type = "all";
|
||||
|
||||
agents = getActiveAgentsOfType(type);
|
||||
return agents.size;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getActiveAgentsOfType
|
||||
//===========================================
|
||||
getActiveAgentsOfType( type )
|
||||
{
|
||||
Assert(IsDefined(type));
|
||||
agents = [];
|
||||
|
||||
if ( !IsDefined( level.agentArray ) )
|
||||
return agents;
|
||||
|
||||
foreach ( agent in level.agentArray )
|
||||
{
|
||||
if ( IsDefined( agent.isActive ) && agent.isActive )
|
||||
{
|
||||
if ( type == "all" || agent.agent_type == type )
|
||||
agents[agents.size] = agent;
|
||||
}
|
||||
}
|
||||
|
||||
return agents;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getNumOwnedActiveAgents
|
||||
//===========================================
|
||||
getNumOwnedActiveAgents( player )
|
||||
{
|
||||
return getNumOwnedActiveAgentsByType( player, "all" );
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// getNumOwnedActiveAgentsByType
|
||||
//===========================================
|
||||
getNumOwnedActiveAgentsByType( player, type )
|
||||
{
|
||||
Assert(IsDefined(type));
|
||||
numOwnedActiveAgents = 0;
|
||||
|
||||
if( !IsDefined(level.agentArray) )
|
||||
{
|
||||
return numOwnedActiveAgents;
|
||||
}
|
||||
|
||||
foreach( agent in level.agentArray )
|
||||
{
|
||||
if( IsDefined( agent.isActive ) && agent.isActive )
|
||||
{
|
||||
if ( IsDefined(agent.owner) && (agent.owner == player) )
|
||||
{
|
||||
// Adding exclusion for "alien" type from a request for "all" to prevent the Seeker killstreak in mp_dome_ns from overloading the max allowable agents per player.
|
||||
if ( ( type == "all" && agent.agent_type != "alien" ) || agent.agent_type == type )
|
||||
numOwnedActiveAgents++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numOwnedActiveAgents;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// getValidSpawnPathNodeNearPlayer
|
||||
//=======================================================
|
||||
getValidSpawnPathNodeNearPlayer( bDoPhysicsTraceToPlayer, bDoPhysicsTraceToValidateNode ) // self = player
|
||||
{
|
||||
assert( isPlayer( self ) );
|
||||
|
||||
nodeArray = GetNodesInRadius( self.origin, 350, 64, 128, "Path" );
|
||||
|
||||
if( !IsDefined(nodeArray) || (nodeArray.size == 0) )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ( IsDefined(level.waterDeleteZ) && IsDefined(level.trigUnderWater) )
|
||||
{
|
||||
// Ignore any nodes where the agent would die immediately upon spawning
|
||||
nodeArrayOld = nodeArray;
|
||||
nodeArray = [];
|
||||
foreach( node in nodeArrayOld )
|
||||
{
|
||||
if ( node.origin[ 2 ] > level.waterDeleteZ || !IsPointInVolume( node.origin, level.trigUnderWater ) )
|
||||
nodeArray[nodeArray.size] = node;
|
||||
}
|
||||
}
|
||||
|
||||
playerDirection = AnglesToForward( self.angles );
|
||||
bestDot = -10;
|
||||
|
||||
playerHeight = maps\mp\gametypes\_spawnlogic::getPlayerTraceHeight( self );
|
||||
zOffset = ( 0, 0, playerHeight );
|
||||
|
||||
if ( !IsDefined(bDoPhysicsTraceToPlayer) )
|
||||
bDoPhysicsTraceToPlayer = false;
|
||||
|
||||
if ( !IsDefined(bDoPhysicsTraceToValidateNode) )
|
||||
bDoPhysicsTraceToValidateNode = false;
|
||||
|
||||
pathNodeSortedByDot = [];
|
||||
pathNodeDotValues = [];
|
||||
foreach( pathNode in nodeArray )
|
||||
{
|
||||
if ( !pathNode DoesNodeAllowStance("stand") || isDefined ( pathnode.no_agent_spawn) )
|
||||
continue;
|
||||
|
||||
|
||||
directionToNode = VectorNormalize( pathNode.origin - self.origin );
|
||||
dot = VectorDot( playerDirection, directionToNode );
|
||||
|
||||
i = 0;
|
||||
for ( ; i < pathNodeDotValues.size; i++ )
|
||||
{
|
||||
if ( dot > pathNodeDotValues[i] )
|
||||
{
|
||||
for ( j = pathNodeDotValues.size; j > i; j-- )
|
||||
{
|
||||
pathNodeDotValues[j] = pathNodeDotValues[j-1];
|
||||
pathNodeSortedByDot[j] = pathNodeSortedByDot[j-1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pathNodeSortedByDot[i] = pathNode;
|
||||
pathNodeDotValues[i] = dot;
|
||||
}
|
||||
|
||||
// pick a path node in the player's view
|
||||
for ( i = 0; i < pathNodeSortedByDot.size; i++ )
|
||||
{
|
||||
pathNode = pathNodeSortedByDot[i];
|
||||
|
||||
traceStart = self.origin + zOffset;
|
||||
traceEnd = pathNode.origin + zOffset;
|
||||
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
// prevent selecting a node that the player cannot see
|
||||
if( !SightTracePassed( traceStart, traceEnd, false, self ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bDoPhysicsTraceToValidateNode )
|
||||
{
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
hitPos = PlayerPhysicsTrace( pathNode.origin + zOffset, pathNode.origin );
|
||||
if ( DistanceSquared( hitPos, pathNode.origin ) > 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bDoPhysicsTraceToPlayer )
|
||||
{
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
hitPos = PhysicsTrace( traceStart, traceEnd );
|
||||
if ( DistanceSquared( hitPos, traceEnd ) > 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
return pathNode;
|
||||
}
|
||||
|
||||
// always return a node for safeguard
|
||||
if( (pathNodeSortedByDot.size > 0) && IsDefined(level.isHorde) )
|
||||
return pathNodeSortedByDot[0];
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// killAgent
|
||||
//=======================================================
|
||||
killAgent( agent )
|
||||
{
|
||||
// do enough damage to kill the agent regardless of any damage mitigation
|
||||
agent DoDamage( agent.health + 500000, agent.origin );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// killDog
|
||||
//=======================================================
|
||||
killDog() // self == dog
|
||||
{
|
||||
self [[ self agentFunc( "on_damaged" ) ]](
|
||||
level, // eInflictor The entity that causes the damage.(e.g. a turret)
|
||||
undefined, // eAttacker The entity that is attacking.
|
||||
self.health + 1, // iDamage Integer specifying the amount of damage done
|
||||
0, // iDFlags Integer specifying flags that are to be applied to the damage
|
||||
"MOD_CRUSH", // sMeansOfDeath Integer specifying the method of death
|
||||
"none", // sWeapon The weapon number of the weapon used to inflict the damage
|
||||
( 0, 0, 0 ), // vPoint The point the damage is from?
|
||||
(0, 0, 0), // vDir The direction of the damage
|
||||
"none", // sHitLoc The location of the hit
|
||||
0 // psOffsetTime The time offset for the damage
|
||||
);
|
||||
}
|
||||
502
maps/mp/agents/_agents.gsc
Normal file
502
maps/mp/agents/_agents.gsc
Normal file
@@ -0,0 +1,502 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
//=======================================================================
|
||||
// main
|
||||
// This is functions is called directly from native code on game startup
|
||||
// The particular gametype's main() is called from native code afterward
|
||||
//=======================================================================
|
||||
main()
|
||||
{
|
||||
if( IsDefined( level.createFX_enabled ) && level.createFX_enabled )
|
||||
return;
|
||||
|
||||
setup_callbacks();
|
||||
|
||||
// Enable badplaces in destructibles
|
||||
level.badplace_cylinder_func = ::badplace_cylinder;
|
||||
level.badplace_delete_func = ::badplace_delete;
|
||||
|
||||
/#
|
||||
level thread monitor_scr_agent_players();
|
||||
#/
|
||||
|
||||
level thread maps\mp\agents\_agent_common::init();
|
||||
level thread maps\mp\killstreaks\_agent_killstreak::init();
|
||||
level thread maps\mp\killstreaks\_dog_killstreak::init();
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// setup_callbacks
|
||||
//========================================================
|
||||
setup_callbacks()
|
||||
{
|
||||
if ( !IsDefined( level.agent_funcs ) )
|
||||
level.agent_funcs = [];
|
||||
|
||||
level.agent_funcs["player"] = [];
|
||||
|
||||
level.agent_funcs["player"]["spawn"] = ::spawn_agent_player;
|
||||
level.agent_funcs["player"]["think"] = maps\mp\bots\_bots_gametype_war::bot_war_think;
|
||||
level.agent_funcs["player"]["on_killed"] = ::on_agent_player_killed;
|
||||
level.agent_funcs["player"]["on_damaged"] = ::on_agent_player_damaged;
|
||||
level.agent_funcs["player"]["on_damaged_finished"] = ::agent_damage_finished;
|
||||
|
||||
maps\mp\killstreaks\_agent_killstreak::setup_callbacks();
|
||||
maps\mp\killstreaks\_dog_killstreak::setup_callbacks();
|
||||
}
|
||||
|
||||
wait_till_agent_funcs_defined()
|
||||
{
|
||||
while( !IsDefined(level.agent_funcs) )
|
||||
wait(0.05);
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
//=======================================================
|
||||
// new_scr_agent_team
|
||||
//=======================================================
|
||||
new_scr_agent_team()
|
||||
{
|
||||
teamCounts = [];
|
||||
teamCounts["allies"] = 0;
|
||||
teamCounts["axis"] = 0;
|
||||
minTeam = undefined;
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if ( !IsDefined( teamCounts[player.team] ) )
|
||||
teamCounts[player.team] = 0;
|
||||
if ( IsTeamParticipant( player ) )
|
||||
teamCounts[player.team]++;
|
||||
}
|
||||
foreach ( team, count in teamCounts )
|
||||
{
|
||||
if ( (team != "spectator") && (!IsDefined(minTeam) || teamCounts[minTeam] > count) )
|
||||
minTeam = team;
|
||||
}
|
||||
|
||||
return minTeam;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// monitor_scr_agent_players
|
||||
//=======================================================
|
||||
monitor_scr_agent_players()
|
||||
{
|
||||
SetDevDvarIfUninitialized( "scr_agent_players_add", "0" );
|
||||
SetDevDvarIfUninitialized( "scr_agent_players_drop", "0" );
|
||||
|
||||
while(level.players.size == 0)
|
||||
wait(0.05); // Agents don't exist until a player connects
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait(0.1);
|
||||
|
||||
add_agent_players = getdvarInt("scr_agent_players_add");
|
||||
drop_agent_players = getdvarInt("scr_agent_players_drop");
|
||||
|
||||
if ( add_agent_players != 0 )
|
||||
SetDevDvar( "scr_agent_players_add", 0 );
|
||||
|
||||
if ( drop_agent_players != 0 )
|
||||
SetDevDvar( "scr_agent_players_drop", 0 );
|
||||
|
||||
for ( i = 0; i < add_agent_players; i++ )
|
||||
{
|
||||
agent = add_humanoid_agent( "player", new_scr_agent_team(), undefined, undefined, undefined, undefined, true, true );
|
||||
if ( IsDefined( agent ) )
|
||||
agent.agent_teamParticipant = true;
|
||||
}
|
||||
|
||||
foreach ( agent in level.agentArray )
|
||||
{
|
||||
if ( !IsDefined( agent.isActive ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( agent.isActive ) && agent.isActive && agent.agent_type == "player" )
|
||||
{
|
||||
if ( drop_agent_players > 0 )
|
||||
{
|
||||
agent maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
agent Suicide();
|
||||
drop_agent_players--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
//=======================================================
|
||||
// add_humanoid_agent
|
||||
//=======================================================
|
||||
add_humanoid_agent( agent_type, team, class, optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( agent_type, team, class );
|
||||
|
||||
if( IsDefined( agent ) )
|
||||
{
|
||||
agent thread [[ agent agentFunc("spawn") ]]( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty );
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// spawn_agent_player
|
||||
//========================================================
|
||||
spawn_agent_player( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while( !IsDefined(level.getSpawnPoint) )
|
||||
{
|
||||
waitframe();
|
||||
}
|
||||
|
||||
if( self.hasDied )
|
||||
{
|
||||
wait( RandomIntRange(6, 10) );
|
||||
}
|
||||
|
||||
self initPlayerScriptVariables( true );
|
||||
|
||||
// allow killstreaks to pass in specific spawn locations
|
||||
if( IsDefined(optional_spawnOrigin) && IsDefined(optional_spawnAngles) )
|
||||
{
|
||||
spawnOrigin = optional_spawnOrigin;
|
||||
spawnAngles = optional_spawnAngles;
|
||||
|
||||
self.lastSpawnPoint = SpawnStruct();
|
||||
self.lastSpawnPoint.origin = spawnOrigin;
|
||||
self.lastSpawnPoint.angles = spawnAngles;
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
|
||||
// Player specific variables needed in damage processing
|
||||
self.lastSpawnPoint = spawnpoint;
|
||||
}
|
||||
self activateAgent();
|
||||
self.lastSpawnTime = GetTime();
|
||||
self.spawnTime = GetTime();
|
||||
|
||||
phys_trace_start = spawnOrigin + (0,0,25);
|
||||
phys_trace_end = spawnOrigin;
|
||||
newSpawnOrigin = PlayerPhysicsTrace(phys_trace_start, phys_trace_end);
|
||||
if ( DistanceSquared( newSpawnOrigin, phys_trace_start ) > 1 )
|
||||
{
|
||||
// If the result from the physics trace wasn't immediately in solid, then use it instead
|
||||
spawnOrigin = newSpawnOrigin;
|
||||
}
|
||||
|
||||
// called from code when an agent is done initializing after AddAgent is called
|
||||
// this should set up any state specific to this agent and game
|
||||
self SpawnAgent( spawnOrigin, spawnAngles );
|
||||
|
||||
if ( IsDefined(use_randomized_personality) && use_randomized_personality )
|
||||
{
|
||||
/#
|
||||
self maps\mp\bots\_bots::bot_set_personality_from_dev_dvar();
|
||||
#/
|
||||
self maps\mp\bots\_bots_personality::bot_assign_personality_functions(); // Randomized personality was already set, so just need to setup functions
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\bots\_bots_util::bot_set_personality( "default" );
|
||||
}
|
||||
|
||||
if ( IsDefined( difficulty ) )
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( difficulty );
|
||||
|
||||
self initPlayerClass();
|
||||
|
||||
self maps\mp\agents\_agent_common::set_agent_health( 100 );
|
||||
if ( IsDefined(respawn_on_death) && respawn_on_death )
|
||||
self.respawn_on_death = true;
|
||||
|
||||
// must set the team after SpawnAgent to fix a bug with weapon crosshairs and nametags
|
||||
if( IsDefined(optional_owner) )
|
||||
self set_agent_team( optional_owner.team, optional_owner );
|
||||
|
||||
if( isDefined( self.owner ) )
|
||||
self thread destroyOnOwnerDisconnect( self.owner );
|
||||
|
||||
self thread maps\mp\_flashgrenades::monitorFlash();
|
||||
|
||||
// switch to agent bot mode and wipe all AI info clean
|
||||
self EnableAnimState( false );
|
||||
|
||||
self [[level.onSpawnPlayer]]();
|
||||
self maps\mp\gametypes\_class::giveLoadout( self.team, self.class, true );
|
||||
|
||||
self thread maps\mp\bots\_bots::bot_think_watch_enemy( true );
|
||||
self thread maps\mp\bots\_bots::bot_think_crate();
|
||||
if ( self.agent_type == "player" )
|
||||
self thread maps\mp\bots\_bots::bot_think_level_actions();
|
||||
else if ( self.agent_type == "odin_juggernaut" )
|
||||
self thread maps\mp\bots\_bots::bot_think_level_actions( 128 );
|
||||
self thread maps\mp\bots\_bots_strategy::bot_think_tactical_goals();
|
||||
self thread [[ self agentFunc("think") ]]();
|
||||
|
||||
if ( !self.hasDied )
|
||||
self maps\mp\gametypes\_spawnlogic::addToParticipantsArray();
|
||||
|
||||
self.hasDied = false;
|
||||
|
||||
self thread maps\mp\gametypes\_weapons::onPlayerSpawned();
|
||||
self thread maps\mp\gametypes\_healthoverlay::playerHealthRegen();
|
||||
self thread maps\mp\gametypes\_battlechatter_mp::onPlayerSpawned();
|
||||
|
||||
level notify( "spawned_agent_player", self );
|
||||
level notify( "spawned_agent", self );
|
||||
self notify( "spawned_player" );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// destroyOnOwnerDisconnect
|
||||
//========================================================
|
||||
destroyOnOwnerDisconnect( owner )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
owner waittill( "killstreak_disowned" );
|
||||
|
||||
self notify( "owner_disconnect" );
|
||||
|
||||
// Wait till host migration finishes before suiciding
|
||||
if ( maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone() )
|
||||
wait 0.05;
|
||||
|
||||
// kill the agent
|
||||
self Suicide();
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// agent_damage_finished
|
||||
//========================================================
|
||||
agent_damage_finished( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if( IsDefined( eInflictor ) || IsDefined( eAttacker ) )
|
||||
{
|
||||
if( !IsDefined( eInflictor ) )
|
||||
eInflictor = eAttacker;
|
||||
|
||||
if( isdefined(self.allowVehicleDamage) && !self.allowVehicleDamage )
|
||||
{
|
||||
if( IsDefined( eInflictor.classname ) && eInflictor.classname == "script_vehicle" )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( IsDefined( eInflictor.classname ) && eInflictor.classname == "auto_turret" )
|
||||
eAttacker = eInflictor;
|
||||
|
||||
if( IsDefined( eAttacker ) && sMeansOfDeath != "MOD_FALLING" && sMeansOfDeath != "MOD_SUICIDE" )
|
||||
{
|
||||
if( level.teamBased )
|
||||
{
|
||||
if( IsDefined( eAttacker.team ) && eAttacker.team != self.team )
|
||||
{
|
||||
self SetAgentAttacker( eAttacker );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self SetAgentAttacker( eAttacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert(IsDefined(self.isActive) && self.isActive);
|
||||
self FinishAgentDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset, 0.0 );
|
||||
if ( !IsDefined(self.isActive) )
|
||||
{
|
||||
// Agent just died and cleared out all his script variables
|
||||
// So don't allow this agent to be freed up until he is properly deactivated in deactivateAgentDelayed
|
||||
self.waitingToDeactivate = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_generic_damaged
|
||||
//=======================================================
|
||||
on_agent_generic_damaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
attckerIsOwner = IsDefined(eAttacker) && IsDefined(self.owner) && (self.owner == eAttacker);
|
||||
attackerIsTeammate = attackerIsHittingTeam( self.owner, eAttacker ) || attckerIsOwner;
|
||||
|
||||
// ignore friendly fire damage for team based modes
|
||||
if( level.teambased && attackerIsTeammate && !level.friendlyfire )
|
||||
return false;
|
||||
|
||||
// ignore damage from owner in non team based modes
|
||||
if( !level.teambased && attckerIsOwner )
|
||||
return false;
|
||||
|
||||
// don't let helicopters and other vehicles crush a player, if we want it to then put in a special case here
|
||||
if( IsDefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_CRUSH" && IsDefined( eInflictor ) && IsDefined( eInflictor.classname ) && eInflictor.classname == "script_vehicle" )
|
||||
return false;
|
||||
|
||||
if ( !IsDefined( self ) || !isReallyAlive( self ) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( eAttacker ) && eAttacker.classname == "script_origin" && IsDefined( eAttacker.type ) && eAttacker.type == "soft_landing" )
|
||||
return false;
|
||||
|
||||
if ( sWeapon == "killstreak_emp_mp" )
|
||||
return false;
|
||||
|
||||
if ( sWeapon == "bouncingbetty_mp" && !maps\mp\gametypes\_weapons::mineDamageHeightPassed( eInflictor, self ) )
|
||||
return false;
|
||||
|
||||
// JC-ToDo: - Kept this here in case I bring back mine logic for the mk32
|
||||
// if ( sWeapon == "xm25_mp" && sMeansOfDeath == "MOD_IMPACT" )
|
||||
// iDamage = 95;
|
||||
|
||||
// ensure throwing knife death
|
||||
if ( ( sWeapon == "throwingknife_mp" || sWeapon == "throwingknifejugg_mp" ) && sMeansOfDeath == "MOD_IMPACT" )
|
||||
iDamage = self.health + 1;
|
||||
|
||||
// ensures stuck death
|
||||
if ( IsDefined( eInflictor ) && IsDefined( eInflictor.stuckEnemyEntity ) && eInflictor.stuckEnemyEntity == self )
|
||||
iDamage = self.health + 1;
|
||||
|
||||
if( iDamage <= 0 )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( eAttacker ) && eAttacker != self && iDamage > 0 && ( !IsDefined( sHitLoc ) || sHitLoc != "shield" ) )
|
||||
{
|
||||
if( iDFlags & level.iDFLAGS_STUN )
|
||||
typeHit = "stun";
|
||||
else if( !shouldWeaponFeedback( sWeapon ) )
|
||||
typeHit = "none";
|
||||
else
|
||||
typeHit = ter_op( iDamage >= self.health, "hitkill" ,"standard" ); // adds final kill hitmarker to dogs
|
||||
|
||||
eAttacker thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback( typeHit );
|
||||
}
|
||||
|
||||
if ( IsDefined( level.modifyPlayerDamage ) )
|
||||
iDamage = [[level.modifyPlayerDamage]]( self, eAttacker, iDamage, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc );
|
||||
|
||||
return self [[ self agentFunc( "on_damaged_finished" ) ]]( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// on_agent_player_damaged
|
||||
//========================================================
|
||||
on_agent_player_damaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
attckerIsOwner = IsDefined(eAttacker) && IsDefined(self.owner) && (self.owner == eAttacker);
|
||||
|
||||
// ignore damage from owner in non team based modes
|
||||
if( !level.teambased && attckerIsOwner )
|
||||
return false;
|
||||
|
||||
Callback_PlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_player_killed
|
||||
//=======================================================
|
||||
on_agent_player_killed(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
self on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, true);
|
||||
|
||||
// award XP for killing agents
|
||||
if( isPlayer( eAttacker ) && (!isDefined(self.owner) || eAttacker != self.owner) )
|
||||
{
|
||||
// TODO: should play vo for killing the agent
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_squad_mate" );
|
||||
}
|
||||
|
||||
self maps\mp\gametypes\_weapons::dropScavengerForDeath( eAttacker );
|
||||
|
||||
if ( self.isActive )
|
||||
{
|
||||
self.hasDied = true;
|
||||
|
||||
if ( getGametypeNumLives() != 1 && ( IsDefined(self.respawn_on_death) && self.respawn_on_death ) )
|
||||
{
|
||||
self thread [[ self agentFunc("spawn") ]]();
|
||||
}
|
||||
else
|
||||
{
|
||||
self deactivateAgent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_humanoid_agent_killed_common
|
||||
//=======================================================
|
||||
on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, dropWeapons )
|
||||
{
|
||||
// Things that happen on every type of humanoid agent that dies
|
||||
|
||||
if ( self.hasRiotShieldEquipped )
|
||||
{
|
||||
self LaunchShield( iDamage, sMeansofDeath );
|
||||
|
||||
if ( !dropWeapons )
|
||||
{
|
||||
// If not dropping weapons, need to make sure we at least drop the riot shield
|
||||
item = self dropItem( self GetCurrentWeapon() );
|
||||
|
||||
if( IsDefined(item) )
|
||||
{
|
||||
item thread maps\mp\gametypes\_weapons::deletePickupAfterAWhile();
|
||||
item.owner = self;
|
||||
item.ownersattacker = eAttacker;
|
||||
item MakeUnusable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( dropWeapons )
|
||||
self [[level.weaponDropFunction]]( eAttacker, sMeansOfDeath );
|
||||
|
||||
// ragdoll
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
thread delayStartRagdoll( self.body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath );
|
||||
|
||||
self riotShield_clear();
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// initPlayerClass
|
||||
//===========================================
|
||||
initPlayerClass()
|
||||
{
|
||||
// Must be called AFTER agent has been spawned as a bot agent
|
||||
if ( IsDefined(self.class_override) )
|
||||
{
|
||||
self.class = self.class_override;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self maps\mp\bots\_bots_loadout::bot_setup_loadout_callback() )
|
||||
self.class = "callback";
|
||||
else
|
||||
self.class = "class1";
|
||||
}
|
||||
}
|
||||
193
maps/mp/agents/_agents_civ_hvt.gsc
Normal file
193
maps/mp/agents/_agents_civ_hvt.gsc
Normal file
@@ -0,0 +1,193 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
// #include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["civ_hvt"] = [];
|
||||
|
||||
level.agent_funcs["civ_hvt"]["spawn"] = ::onSpawn;
|
||||
level.agent_funcs["civ_hvt"]["think"] = ::agentThink;
|
||||
level.agent_funcs["civ_hvt"]["on_killed"] = ::onAgentKilled;
|
||||
level.agent_funcs["civ_hvt"]["on_damaged"] = maps\mp\agents\_agents::on_agent_player_damaged;
|
||||
level.agent_funcs["civ_hvt"]["on_damaged_finished"] = maps\mp\agents\_agents::agent_damage_finished;
|
||||
}
|
||||
|
||||
onSpawn( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
self.hvtIsFollowing = false;
|
||||
|
||||
self maps\mp\agents\_agents::spawn_agent_player( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty );
|
||||
|
||||
self thread handlePlayerUse();
|
||||
}
|
||||
|
||||
onAgentKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
self.defendNode = undefined;
|
||||
self.hvtTrigger MakeUnusable();
|
||||
self.hvtTrigger = undefined;
|
||||
|
||||
// ragdoll
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
thread delayStartRagdoll( self.body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath );
|
||||
|
||||
if ( IsDefined( self.onKilledCallback ) )
|
||||
{
|
||||
self [[ self.onKilledCallback ]]( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
}
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
|
||||
// send a message to owner
|
||||
self.owner notify( "hvt_killed" );
|
||||
}
|
||||
|
||||
agentThink()
|
||||
{
|
||||
self notify( "agent_think" );
|
||||
self endon( "agent_think" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( self.hvtIsFollowing )
|
||||
{
|
||||
self followThink();
|
||||
}
|
||||
else
|
||||
{
|
||||
self waitThink( 150 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
waitThink( radius )
|
||||
{
|
||||
self BotSetStance("none");
|
||||
self BotClearScriptGoal();
|
||||
self bot_disable_tactical_goals();
|
||||
|
||||
defendNode = self.owner getValidSpawnPathNodeNearPlayer();
|
||||
|
||||
self.cur_defend_node = undefined;
|
||||
self.bot_defending = true;
|
||||
self.bot_defending_center = defendNode.origin;
|
||||
self.bot_defending_radius = radius;
|
||||
self.cur_defend_stance = "crouch";
|
||||
self.bot_defending_type = "protect";
|
||||
|
||||
result = "";
|
||||
while( result != "goal" )
|
||||
{
|
||||
self.cur_defend_node = defendNode;
|
||||
|
||||
self BotSetScriptGoalNode( self.cur_defend_node, "tactical" );
|
||||
result = self waittill_any_return( "goal", "bad_path" );
|
||||
|
||||
self.node_closest_to_defend_center = defendNode;
|
||||
|
||||
self.cur_defend_node = undefined;
|
||||
}
|
||||
|
||||
self childthread defense_watch_entrances_at_goal();
|
||||
|
||||
self waittill( "hvt_toggle" );
|
||||
}
|
||||
|
||||
followThink() // self == agent
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
self bot_disable_tactical_goals();
|
||||
|
||||
if ( !self bot_is_guarding_player( self.owner ) )
|
||||
{
|
||||
self bot_guard_player( self.owner, 250 );
|
||||
}
|
||||
|
||||
self waittill( "hvt_toggle" );
|
||||
}
|
||||
|
||||
handlePlayerUse() // self == agent
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
if ( !IsDefined( self.hvtTrigger ) )
|
||||
{
|
||||
self.hvtTrigger = Spawn( "script_model", self.origin );
|
||||
self.hvtTrigger LinkTo( self );
|
||||
}
|
||||
|
||||
self.hvtTrigger MakeUsable();
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player != self.owner )
|
||||
{
|
||||
self.hvtTrigger DisablePlayerUse( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.hvtTrigger EnablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
|
||||
self thread waitForPlayerConnect();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self setFollowerHintString();
|
||||
|
||||
self.hvtTrigger waittill ( "trigger", player );
|
||||
|
||||
assert( player == self.owner );
|
||||
|
||||
self.hvtIsFollowing = !self.hvtIsFollowing;
|
||||
// do something with the AI
|
||||
|
||||
print( "Is Following: " + self.hvtIsFollowing );
|
||||
|
||||
self notify( "hvt_toggle" );
|
||||
}
|
||||
}
|
||||
|
||||
setFollowerHintString()
|
||||
{
|
||||
hintString = &"MP_HVT_FOLLOW";
|
||||
if ( self.hvtIsFollowing )
|
||||
{
|
||||
hintString = &"MP_HVT_WAIT";
|
||||
}
|
||||
self.hvtTrigger setHintString( hintString );
|
||||
}
|
||||
|
||||
waitForPlayerConnect()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
self.hvtTrigger disablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
7
maps/mp/agents/_agents_gametype_aliens.gsc
Normal file
7
maps/mp/agents/_agents_gametype_aliens.gsc
Normal file
@@ -0,0 +1,7 @@
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_blitz.gsc
Normal file
14
maps/mp/agents/_agents_gametype_blitz.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
68
maps/mp/agents/_agents_gametype_conf.gsc
Normal file
68
maps/mp/agents/_agents_gametype_conf.gsc
Normal file
@@ -0,0 +1,68 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"] = ::agent_squadmember_conf_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_conf_think;
|
||||
}
|
||||
|
||||
agent_player_conf_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_conf::bot_conf_think();
|
||||
}
|
||||
|
||||
agent_squadmember_conf_think()
|
||||
{
|
||||
// Returning true means the "think" was handled here. "False" means use the default think
|
||||
|
||||
if ( !IsDefined(self.tags_seen_by_owner) )
|
||||
self.tags_seen_by_owner = [];
|
||||
|
||||
if ( !IsDefined(self.next_time_check_tags) )
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
if ( GetTime() > self.next_time_check_tags )
|
||||
{
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
current_player_fov = 0.78; // approximation
|
||||
nearest_node_to_player = self.owner GetNearestNode();
|
||||
if ( IsDefined(nearest_node_to_player) )
|
||||
{
|
||||
new_visible_tags_to_player = self.owner maps\mp\bots\_bots_gametype_conf::bot_find_visible_tags( true, nearest_node_to_player, current_player_fov );
|
||||
self.tags_seen_by_owner = maps\mp\bots\_bots_gametype_conf::bot_combine_tag_seen_arrays( new_visible_tags_to_player, self.tags_seen_by_owner );
|
||||
}
|
||||
}
|
||||
|
||||
self.tags_seen_by_owner = self maps\mp\bots\_bots_gametype_conf::bot_remove_invalid_tags( self.tags_seen_by_owner );
|
||||
best_tag = self maps\mp\bots\_bots_gametype_conf::bot_find_best_tag_from_array( self.tags_seen_by_owner, false );
|
||||
|
||||
if ( IsDefined(best_tag) )
|
||||
{
|
||||
if ( !IsDefined(self.tag_getting) || DistanceSquared(best_tag.curorigin, self.tag_getting.curorigin) > 1 )
|
||||
{
|
||||
self.tag_getting = best_tag;
|
||||
self bot_defend_stop();
|
||||
self BotSetScriptGoal( self.tag_getting.curorigin, 0, "objective", undefined, level.bot_tag_obj_radius );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( IsDefined(self.tag_getting) )
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
self.tag_getting = undefined;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_cranked.gsc
Normal file
14
maps/mp/agents/_agents_gametype_cranked.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_dm.gsc
Normal file
14
maps/mp/agents/_agents_gametype_dm.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
48
maps/mp/agents/_agents_gametype_dom.gsc
Normal file
48
maps/mp/agents/_agents_gametype_dom.gsc
Normal file
@@ -0,0 +1,48 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"]= ::agent_squadmember_dom_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_dom_think;
|
||||
}
|
||||
|
||||
agent_player_dom_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_dom::bot_dom_think();
|
||||
}
|
||||
|
||||
agent_squadmember_dom_think()
|
||||
{
|
||||
// Returning true means the "think" was handled here. "False" means use the default think
|
||||
|
||||
owner_flag = undefined;
|
||||
foreach( trigger in self.owner.touchTriggers )
|
||||
{
|
||||
if ( trigger.useobj.id == "domFlag" )
|
||||
owner_flag = trigger;
|
||||
}
|
||||
|
||||
if ( IsDefined(owner_flag) )
|
||||
{
|
||||
owner_flag_team = owner_flag maps\mp\gametypes\dom::getFlagTeam();
|
||||
if ( owner_flag_team != self.team )
|
||||
{
|
||||
if ( !self maps\mp\bots\_bots_gametype_dom::bot_is_capturing_flag( owner_flag ) )
|
||||
self maps\mp\bots\_bots_gametype_dom::capture_flag(owner_flag, "critical", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
20
maps/mp/agents/_agents_gametype_grind.gsc
Normal file
20
maps/mp/agents/_agents_gametype_grind.gsc
Normal file
@@ -0,0 +1,20 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"] = maps\mp\agents\_agents_gametype_conf::agent_squadmember_conf_think;
|
||||
level.agent_funcs["player"]["think"] = maps\mp\agents\_agents_gametype_conf::agent_player_conf_think;
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_gun.gsc
Normal file
14
maps/mp/agents/_agents_gametype_gun.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
550
maps/mp/agents/_agents_gametype_horde.gsc
Normal file
550
maps/mp/agents/_agents_gametype_horde.gsc
Normal file
@@ -0,0 +1,550 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_horde_util;
|
||||
#include maps\mp\gametypes\_horde_crates;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
/#
|
||||
CONST_FORCE_DOG_SPAWN = false;
|
||||
CONST_FORCE_PLAYER_ENEMY_SPAWN = false;
|
||||
CONST_DISABLE_AUTO_AI_REMOVAL = false;
|
||||
#/
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
level thread runRoundSpawning();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["player"]["think"] = ::enemyAgentThink;
|
||||
level.agent_funcs["player"]["on_killed"] = ::onAgentKilled;
|
||||
|
||||
level.agent_funcs["squadmate"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["squadmate"]["think"] = ::allyAgentThink;
|
||||
|
||||
level.agent_funcs["dog"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["dog"]["think"] = ::agentDogThink;
|
||||
level.agent_funcs["dog"]["on_killed"] = ::onDogKilled;
|
||||
}
|
||||
|
||||
onAIConnect()
|
||||
{
|
||||
self.gameModefirstSpawn = true;
|
||||
self.agentname = &"HORDE_INFECTED";
|
||||
self.horde_type = "";
|
||||
}
|
||||
|
||||
runRoundSpawning()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "start_round" );
|
||||
|
||||
if( isSpecialRound() )
|
||||
{
|
||||
runSpecialRound();
|
||||
}
|
||||
else
|
||||
{
|
||||
runNormalRound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runSpecialRound()
|
||||
{
|
||||
runLootDrop();
|
||||
}
|
||||
|
||||
runNormalRound()
|
||||
{
|
||||
level childthread highlightLastEnemies();
|
||||
|
||||
while( level.currentEnemyCount < level.maxEnemyCount )
|
||||
{
|
||||
while( level.currentAliveEnemyCount < level.maxAliveEnemyCount )
|
||||
{
|
||||
createEnemy();
|
||||
|
||||
if( level.currentEnemyCount == level.maxEnemyCount )
|
||||
break;
|
||||
}
|
||||
|
||||
level waittill( "enemy_death" );
|
||||
}
|
||||
}
|
||||
|
||||
createEnemy()
|
||||
{
|
||||
/#
|
||||
if( CONST_FORCE_DOG_SPAWN )
|
||||
{
|
||||
createDogEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
/#
|
||||
if( CONST_FORCE_PLAYER_ENEMY_SPAWN )
|
||||
{
|
||||
createHumanoidEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
if( isDogRound() && (RandomIntRange(1, 101) < level.chanceToSpawnDog) )
|
||||
{
|
||||
createDogEnemy();
|
||||
}
|
||||
else
|
||||
{
|
||||
createHumanoidEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
createHumanoidEnemy()
|
||||
{
|
||||
agent = undefined;
|
||||
|
||||
while( !IsDefined(agent) )
|
||||
{
|
||||
agent = maps\mp\agents\_agents::add_humanoid_agent( "player", level.enemyTeam, "class1" );
|
||||
|
||||
if( IsDefined(agent) )
|
||||
{
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
createDogEnemy()
|
||||
{
|
||||
agent = undefined;
|
||||
|
||||
while( !IsDefined(agent) )
|
||||
{
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( "dog", level.enemyTeam );
|
||||
|
||||
if( IsDefined(agent) )
|
||||
{
|
||||
agent thread [[ agent agentFunc("spawn") ]]();
|
||||
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
playAISpawnEffect()
|
||||
{
|
||||
PlayFX( level._effect["spawn_effect"], self.origin );
|
||||
}
|
||||
|
||||
highlightLastEnemies()
|
||||
{
|
||||
level endon( "round_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "enemy_death" );
|
||||
|
||||
if( level.currentEnemyCount != level.maxEnemyCount )
|
||||
continue;
|
||||
|
||||
if( level.currentAliveEnemyCount < 3 )
|
||||
{
|
||||
foreach( player in level.characters )
|
||||
{
|
||||
if( isOnHumanTeam(player) )
|
||||
continue;
|
||||
|
||||
if( isReallyAlive(player) )
|
||||
{
|
||||
player HudOutlineEnable( level.enemyOutlineColor, false );
|
||||
player.outlineColor = level.enemyOutlineColor;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAgentKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
if( !isOnHumanTeam(self) )
|
||||
self hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
self HudOutlineDisable();
|
||||
self maps\mp\agents\_agents::on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, false);
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
}
|
||||
|
||||
onDogKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
if( !isOnHumanTeam(self) )
|
||||
self hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
self HudOutlineDisable();
|
||||
self maps\mp\killstreaks\_dog_killstreak::on_agent_dog_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
}
|
||||
|
||||
hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
AssertEx( (level.currentAliveEnemyCount > 0), "currentAliveEnemyCount is below zero" );
|
||||
|
||||
level.currentAliveEnemyCount--;
|
||||
trackIntelKills( sWeapon, sMeansOfDeath );
|
||||
|
||||
level thread maps\mp\gametypes\horde::chanceToSpawnPickup( self );
|
||||
level notify( "enemy_death" );
|
||||
|
||||
// player attacker
|
||||
if( IsPlayer(eAttacker) )
|
||||
{
|
||||
awardHordeKill( eAttacker );
|
||||
|
||||
if( eAttacker _hasPerk("specialty_triggerhappy") )
|
||||
{
|
||||
eAttacker thread maps\mp\perks\_perkfunctions::setTriggerHappyInternal();
|
||||
}
|
||||
}
|
||||
|
||||
// killstreak entity attacker
|
||||
if( IsDefined(eAttacker) && IsDefined(eAttacker.owner) && IsPlayer(eAttacker.owner) && IsDefined(eAttacker.owner.killz) )
|
||||
{
|
||||
awardHordeKill( eAttacker.owner );
|
||||
}
|
||||
}
|
||||
|
||||
trackIntelKills( sWeapon, sMeansOfDeath )
|
||||
{
|
||||
if( level.isTeamIntelComplete )
|
||||
return;
|
||||
|
||||
if( sWeapon == "none" )
|
||||
return;
|
||||
|
||||
if( sMeansOfDeath == "MOD_MELEE" )
|
||||
level.numMeleeKillsIntel++;
|
||||
|
||||
if( !isKillstreakWeapon( sWeapon ) && (sMeansOfDeath == "MOD_HEAD_SHOT") )
|
||||
level.numHeadShotsIntel++;
|
||||
|
||||
if( isKillstreakWeapon( sWeapon ) && (sWeapon != level.intelMiniGun) )
|
||||
level.numKillStreakKillsIntel++;
|
||||
|
||||
if( maps\mp\gametypes\_class::isValidEquipment( sWeapon, false ) || maps\mp\gametypes\_class::isValidOffhand( sWeapon, false ) )
|
||||
level.numEquipmentKillsIntel++;
|
||||
}
|
||||
|
||||
enemyAgentThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self BotSetFlag("no_enemy_search", true);
|
||||
|
||||
self thread monitorBadHumanoidAI();
|
||||
self thread locateEnemyPositions();
|
||||
}
|
||||
|
||||
monitorBadHumanoidAI()
|
||||
{
|
||||
/#
|
||||
if( CONST_DISABLE_AUTO_AI_REMOVAL )
|
||||
return;
|
||||
#/
|
||||
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
spawnTime = GetTime();
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait( 5.0 );
|
||||
|
||||
if( !bot_in_combat(120 * 1000) )
|
||||
{
|
||||
outlineStuckAI( self );
|
||||
|
||||
if( !bot_in_combat(240 * 1000) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 240, 480 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
killAgent( self );
|
||||
}
|
||||
|
||||
monitorBadDogAI()
|
||||
{
|
||||
/#
|
||||
if( CONST_DISABLE_AUTO_AI_REMOVAL )
|
||||
return;
|
||||
#/
|
||||
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
spawnTime = GetTime();
|
||||
lastPosition = self.origin;
|
||||
lastPositionTime = spawnTime;
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait( 5.0 );
|
||||
|
||||
positionDelta = DistanceSquared( self.origin, lastPosition );
|
||||
positionTime = (GetTime() - lastPositionTime) / 1000;
|
||||
|
||||
if( positionDelta > (128 * 128) )
|
||||
{
|
||||
lastPosition = self.origin;
|
||||
lastPositionTime = GetTime();
|
||||
}
|
||||
else if( positionTime > 25 )
|
||||
{
|
||||
outlineStuckAI( self );
|
||||
|
||||
if( positionTime > 55 )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 120, 240 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
killAgent( self );
|
||||
}
|
||||
|
||||
checkExpireTime( spawnTime, highLightTime, expireTime )
|
||||
{
|
||||
aliveTime = (GetTime() - spawnTime) / 1000;
|
||||
|
||||
if( aliveTime > highLightTime )
|
||||
{
|
||||
outlineStuckAI( self );
|
||||
|
||||
if( aliveTime > expireTime )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
outlineStuckAI( agent )
|
||||
{
|
||||
agent HudOutlineEnable( level.enemyOutlineColor, false );
|
||||
agent.outlineColor = level.enemyOutlineColor;
|
||||
|
||||
/#
|
||||
agent HudOutlineEnable( 2, false );
|
||||
#/
|
||||
}
|
||||
|
||||
SCR_CONST_ALLY_AGENT_LOW_HEALTH_BEHAVIOR = 0.6;
|
||||
SCR_CONST_PLAYER_LOW_HEALTH_BEHAVIOR = 0.5;
|
||||
|
||||
allyAgentThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
self BotSetFlag("force_sprint",true);
|
||||
holding_till_health_regen = false;
|
||||
next_time_protect_player = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if ( float(self.owner.health) / self.owner.maxhealth < SCR_CONST_PLAYER_LOW_HEALTH_BEHAVIOR && GetTime() > next_time_protect_player )
|
||||
{
|
||||
nodes = GetNodesInRadiusSorted(self.owner.origin, 256, 0);
|
||||
if ( nodes.size >= 2 )
|
||||
{
|
||||
self.defense_force_next_node_goal = nodes[1]; // Send agent to the second-closest node to the player
|
||||
self notify("defend_force_node_recalculation");
|
||||
next_time_protect_player = GetTime() + 1000;
|
||||
}
|
||||
}
|
||||
else if ( float(self.health) / self.maxhealth >= SCR_CONST_ALLY_AGENT_LOW_HEALTH_BEHAVIOR )
|
||||
{
|
||||
holding_till_health_regen = false;
|
||||
}
|
||||
else if ( !holding_till_health_regen )
|
||||
{
|
||||
// Pick node on the opposite side of the player and hide at it
|
||||
node = self bot_find_node_to_guard_player( self.owner.origin, 350, true );
|
||||
if ( IsDefined(node) )
|
||||
{
|
||||
self.defense_force_next_node_goal = node;
|
||||
self notify("defend_force_node_recalculation");
|
||||
|
||||
holding_till_health_regen = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !self bot_is_guarding_player( self.owner ) )
|
||||
{
|
||||
optional_params["override_goal_type"] = "critical";
|
||||
optional_params["min_goal_time"] = 20;
|
||||
optional_params["max_goal_time"] = 30;
|
||||
self bot_guard_player( self.owner, 350, optional_params );
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
hordeSetupDogState()
|
||||
{
|
||||
self _setNameplateMaterial( "player_name_bg_green_dog", "player_name_bg_red_dog" );
|
||||
self.enableExtendedKill = false;
|
||||
self.agentname = &"HORDE_QUAD";
|
||||
self.horde_type = "Quad";
|
||||
|
||||
// pathing variables
|
||||
self.lasSetGoalPos = (0,0,0);
|
||||
self.bHasNoPath = false;
|
||||
self.randomPathStopTime = 0;
|
||||
|
||||
maps\mp\gametypes\horde::setEnemyAgentHealth( self );
|
||||
}
|
||||
|
||||
agentDogThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
self maps\mp\agents\dog\_dog_think::setupDogState();
|
||||
self hordeSetupDogState();
|
||||
|
||||
self thread locateEnemyPositions();
|
||||
self thread [[self.watchAttackStateFunc]]();
|
||||
self thread WaitForBadPathHorde();
|
||||
self thread monitorBadDogAI();
|
||||
|
||||
/#
|
||||
self thread maps\mp\agents\dog\_dog_think::debug_dog();
|
||||
#/
|
||||
|
||||
while ( true )
|
||||
{
|
||||
/#
|
||||
if ( self maps\mp\agents\dog\_dog_think::ProcessDebugMode() )
|
||||
continue;
|
||||
#/
|
||||
|
||||
if ( self.aiState != "melee" && !self.stateLocked && self maps\mp\agents\dog\_dog_think::readyToMeleeTarget() && !self maps\mp\agents\dog\_dog_think::DidPastMeleeFail() )
|
||||
self ScrAgentBeginMelee( self.curMeleeTarget );
|
||||
|
||||
if( self.randomPathStopTime > GetTime() )
|
||||
{
|
||||
wait(0.05);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !IsDefined(self.enemy) || self.bHasNoPath )
|
||||
{
|
||||
pathNodes = GetNodesInRadiusSorted( self.origin, 1024, 256, 128, "Path" );
|
||||
|
||||
if( pathNodes.size > 0 )
|
||||
{
|
||||
nodeNum = RandomIntRange(int(pathNodes.size*0.9), pathNodes.size); //Pick from the furthest 10%
|
||||
self ScrAgentSetGoalPos( pathNodes[nodeNum].origin );
|
||||
self.bHasNoPath = false;
|
||||
self.randomPathStopTime = GetTime() + 2500;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attackPoint = self maps\mp\agents\dog\_dog_think::GetAttackPoint( self.enemy );
|
||||
self.curMeleeTarget = self.enemy;
|
||||
self.moveMode = "sprint";
|
||||
self.bArrivalsEnabled = false;
|
||||
|
||||
if( DistanceSquared(attackPoint, self.lasSetGoalPos) > (64 * 64) )
|
||||
{
|
||||
self ScrAgentSetGoalPos( attackPoint );
|
||||
self.lasSetGoalPos = attackPoint;
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
WaitForBadPathHorde()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "bad_path", badGoalPos );
|
||||
self.bHasNoPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
locateEnemyPositions()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if( isOnHumanTeam(player) )
|
||||
self GetEnemyInfo( player );
|
||||
}
|
||||
|
||||
wait(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
findClosestPlayer()
|
||||
{
|
||||
closestPlayer = undefined;
|
||||
closestDistance = 100000 * 100000;
|
||||
|
||||
// find the nearest player
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( isReallyAlive(player) && isOnHumanTeam(player) && !isPlayerInLastStand(player) )
|
||||
{
|
||||
distSquared = DistanceSquared( player.origin, self.origin );
|
||||
|
||||
if ( distSquared < closestDistance )
|
||||
{
|
||||
closestPlayer = player;
|
||||
closestDistance = distSquared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestPlayer;
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_infect.gsc
Normal file
14
maps/mp/agents/_agents_gametype_infect.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
75
maps/mp/agents/_agents_gametype_mugger.gsc
Normal file
75
maps/mp/agents/_agents_gametype_mugger.gsc
Normal file
@@ -0,0 +1,75 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"] = ::agent_squadmember_mugger_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_mugger_think;
|
||||
}
|
||||
|
||||
agent_player_mugger_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_mugger::bot_mugger_think();
|
||||
}
|
||||
|
||||
agent_squadmember_mugger_think()
|
||||
{
|
||||
// Returning true means the "think" was handled here. "False" means use the default think
|
||||
|
||||
if ( !IsDefined(self.tags_seen_by_owner) )
|
||||
self.tags_seen_by_owner = [];
|
||||
|
||||
if ( !IsDefined(self.next_time_check_tags) )
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
if ( GetTime() > self.next_time_check_tags )
|
||||
{
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
current_player_fov = 0.78; // approximation
|
||||
if (IsBot( self.owner ) )
|
||||
{
|
||||
current_player_fov = self BotGetFovDot();
|
||||
}
|
||||
nearest_node_to_player = self.owner GetNearestNode();
|
||||
if ( IsDefined(nearest_node_to_player) )
|
||||
{
|
||||
new_visible_tags_to_player = self.owner maps\mp\bots\_bots_gametype_mugger::bot_find_visible_tags_mugger( nearest_node_to_player, current_player_fov );
|
||||
self.tags_seen_by_owner = maps\mp\bots\_bots_gametype_conf::bot_combine_tag_seen_arrays( new_visible_tags_to_player, self.tags_seen_by_owner );
|
||||
}
|
||||
}
|
||||
|
||||
self.tags_seen_by_owner = self maps\mp\bots\_bots_gametype_conf::bot_remove_invalid_tags( self.tags_seen_by_owner );
|
||||
best_tag = self maps\mp\bots\_bots_gametype_conf::bot_find_best_tag_from_array( self.tags_seen_by_owner, false );
|
||||
|
||||
if ( IsDefined(best_tag) )
|
||||
{
|
||||
if ( !IsDefined(self.tag_getting) || DistanceSquared(best_tag.curorigin, self.tag_getting.curorigin) > 1 )
|
||||
{
|
||||
self.tag_getting = best_tag;
|
||||
self bot_defend_stop();
|
||||
self BotSetScriptGoal( self.tag_getting.curorigin, 0, "objective", undefined, level.bot_tag_obj_radius );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( IsDefined(self.tag_getting) )
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
self.tag_getting = undefined;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
26
maps/mp/agents/_agents_gametype_sd.gsc
Normal file
26
maps/mp/agents/_agents_gametype_sd.gsc
Normal file
@@ -0,0 +1,26 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_sd_think;
|
||||
}
|
||||
|
||||
agent_player_sd_think()
|
||||
{
|
||||
self _enableUsability();
|
||||
|
||||
self thread maps\mp\bots\_bots_gametype_sd::bot_sd_think();
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_siege.gsc
Normal file
14
maps/mp/agents/_agents_gametype_siege.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// Place Holder
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_sotf.gsc
Normal file
14
maps/mp/agents/_agents_gametype_sotf.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_sotf_ffa.gsc
Normal file
14
maps/mp/agents/_agents_gametype_sotf_ffa.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_sr.gsc
Normal file
14
maps/mp/agents/_agents_gametype_sr.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
14
maps/mp/agents/_agents_gametype_war.gsc
Normal file
14
maps/mp/agents/_agents_gametype_war.gsc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
||||
247
maps/mp/agents/_scriptedagents.gsc
Normal file
247
maps/mp/agents/_scriptedagents.gsc
Normal file
@@ -0,0 +1,247 @@
|
||||
//
|
||||
// Scripted agent common functions.
|
||||
//
|
||||
|
||||
// called from code when animation state changes.
|
||||
OnEnterState( prevState, nextState )
|
||||
{
|
||||
if ( IsDefined( self.OnEnterAnimState ) )
|
||||
self [[ self.OnEnterAnimState ]]( prevState, nextState );
|
||||
}
|
||||
|
||||
// called from code when the agent is freed.
|
||||
OnDeactivate()
|
||||
{
|
||||
self notify( "killanimscript" );
|
||||
}
|
||||
|
||||
|
||||
// util function
|
||||
PlayAnimUntilNotetrack( animState, animLabel, notetrack, customFunction )
|
||||
{
|
||||
PlayAnimNUntilNotetrack( animState, 0, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex );
|
||||
|
||||
if ( !IsDefined( notetrack ) )
|
||||
notetrack = "end";
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction );
|
||||
}
|
||||
|
||||
PlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
|
||||
if ( !IsDefined( notetrack ) )
|
||||
notetrack = "end";
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction );
|
||||
}
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction )
|
||||
{
|
||||
startTime = getTime();
|
||||
animTime = undefined;
|
||||
animLength = undefined;
|
||||
|
||||
if ( isDefined ( animState ) && isDefined ( animIndex ) )
|
||||
animLength = getAnimLength( self GetAnimEntry( animState, animIndex ));
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( animLabel, note );
|
||||
|
||||
if ( isDefined ( animLength ) )
|
||||
animTime = ( getTime() - startTime ) * 0.001 / animLength;
|
||||
|
||||
if ( !isDefined( animLength ) || animTime > 0 )
|
||||
{
|
||||
if ( note == notetrack || note == "end" || note == "anim_will_finish" || note == "finish" )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( customFunction ) )
|
||||
[[ customFunction ]]( note, animState, animIndex, animTime );
|
||||
}
|
||||
}
|
||||
|
||||
PlayAnimForTime( animState, time )
|
||||
{
|
||||
PlayAnimNForTime( animState, 0, time );
|
||||
}
|
||||
|
||||
PlayAnimNForTime( animState, animIndex, time )
|
||||
{
|
||||
self SetAnimState( animState, animIndex );
|
||||
wait( time );
|
||||
}
|
||||
|
||||
PlayAnimNAtRateForTime( animState, animIndex, animRate, time )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
wait( time );
|
||||
}
|
||||
|
||||
GetAnimScaleFactors( delta, animDelta, bAnimInWorldSpace )
|
||||
{
|
||||
distXY = Length2D( delta );
|
||||
distZ = delta[2];
|
||||
animXY = Length2D( animDelta );
|
||||
animZ = animDelta[2];
|
||||
|
||||
scaleXY = 1;
|
||||
scaleZ = 1;
|
||||
if ( IsDefined( bAnimInWorldSpace ) && bAnimInWorldSpace )
|
||||
{
|
||||
animDelta2D = ( animDelta[0], animDelta[1], 0 );
|
||||
animDeltaDir = VectorNormalize( animDelta2D );
|
||||
if ( VectorDot( animDeltaDir, delta ) < 0 )
|
||||
scaleXY = 0;
|
||||
else if ( animXY > 0 )
|
||||
scaleXY = distXY / animXY;
|
||||
}
|
||||
else if ( animXY > 0 )
|
||||
scaleXY = distXY / animXY;
|
||||
|
||||
assert( scaleXY >= 0 );
|
||||
|
||||
if ( abs(animZ) > 0.001 && animZ * distZ >= 0 ) // animZ & distZ have to be same sign.
|
||||
scaleZ = distZ / animZ;
|
||||
|
||||
assert( scaleZ >= 0 );
|
||||
|
||||
scaleFactors = SpawnStruct();
|
||||
scaleFactors.xy = scaleXY;
|
||||
scaleFactors.z = scaleZ;
|
||||
|
||||
return scaleFactors;
|
||||
}
|
||||
|
||||
// -180, -135, -90, -45, 0, 45, 90, 135, 180
|
||||
// favor underturning, unless you're within <threshold> degrees of the next one up.
|
||||
GetAngleIndex( angle, threshold )
|
||||
{
|
||||
if ( !IsDefined( threshold ) )
|
||||
threshold = 10;
|
||||
|
||||
if ( angle < 0 )
|
||||
return int( ceil( ( 180 + angle - threshold ) / 45 ) );
|
||||
else
|
||||
return int( floor( ( 180 + angle + threshold ) / 45 ) );
|
||||
}
|
||||
|
||||
|
||||
DropPosToGround( position, drop_distance )
|
||||
{
|
||||
//droppedPos = GetGroundPosition( position, radius, 64, 64 );
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
if ( !IsDefined( drop_distance ) )
|
||||
drop_distance = 18;
|
||||
|
||||
startPos = position + (0, 0, drop_distance);
|
||||
endPos = position + (0, 0, drop_distance * -1 );
|
||||
|
||||
droppedPos = self AIPhysicsTrace( startPos, endPos, self.radius, self.height, true );
|
||||
|
||||
if ( abs( droppedPos[2] - startPos[2] ) < 0.1 )
|
||||
return undefined;
|
||||
|
||||
if ( abs( droppedPos[2] - endPos[2] ) < 0.1 )
|
||||
return undefined;
|
||||
|
||||
return droppedPos;
|
||||
}
|
||||
|
||||
|
||||
TRACE_RADIUS_BUFFER = 4;
|
||||
CanMovePointToPoint( startPos, endPos, stepSize )
|
||||
{
|
||||
if ( !isDefined( stepSize ) )
|
||||
{
|
||||
stepSize = 6;
|
||||
}
|
||||
|
||||
step_offset = (0, 0, 1) * stepSize;
|
||||
startPosRaised = startPos + step_offset;
|
||||
endPosRaised = endPos + step_offset;
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
assert( stepSize < self.height );
|
||||
|
||||
return self AIPhysicsTracePassed( startPosRaised, endPosRaised, self.radius, self.height - stepSize, true );
|
||||
}
|
||||
|
||||
GetValidPointToPointMoveLocation( startPos, endPos, stepSize )
|
||||
{
|
||||
if ( !isDefined( stepSize ) )
|
||||
{
|
||||
stepSize = 6;
|
||||
}
|
||||
|
||||
step_offset = (0, 0, 1) * stepSize;
|
||||
startPosRaised = startPos + step_offset;
|
||||
endPosRaised = endPos + step_offset;
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
assert( stepSize < self.height );
|
||||
|
||||
return self AIPhysicsTrace( startPosRaised, endPosRaised, self.radius + TRACE_RADIUS_BUFFER, self.height - stepSize, true );
|
||||
}
|
||||
|
||||
GetSafeAnimMoveDeltaPercentage( moveAnim )
|
||||
{
|
||||
animTranslation = GetMoveDelta( moveAnim );
|
||||
endPos = self LocalToWorldCoords( animTranslation );
|
||||
validMovePosition = GetValidPointToPointMoveLocation( self.origin, endPos );
|
||||
validMoveDistance = Distance( self.origin, validMovePosition );
|
||||
desiredMoveDistance = Distance( self.origin, endPos );
|
||||
|
||||
return Min( 1.0, validMoveDistance / desiredMoveDistance );
|
||||
}
|
||||
|
||||
SafelyPlayAnimUntilNotetrack( animState, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimAtRateUntilNotetrack( animState, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
SafelyPlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animToPlay = self GetAnimEntry( animState, animIndex );
|
||||
moveScale = GetSafeAnimMoveDeltaPercentage( animToPlay );
|
||||
self ScrAgentSetAnimScale( moveScale, 1.0 );
|
||||
self PlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
}
|
||||
|
||||
GetRandomAnimEntry( state )
|
||||
{
|
||||
count = self GetAnimEntryCount( state );
|
||||
return RandomInt( count );
|
||||
}
|
||||
|
||||
GetAngleIndexFromSelfYaw( targetVector )
|
||||
{
|
||||
targetAngles = VectorToAngles( targetVector );
|
||||
angleDiff = AngleClamp180( targetAngles[1] - self.angles[1] );
|
||||
return GetAngleIndex( angleDiff );
|
||||
}
|
||||
590
maps/mp/agents/alien/_alien_agents.gsc
Normal file
590
maps/mp/agents/alien/_alien_agents.gsc
Normal file
@@ -0,0 +1,590 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
//=======================================================================
|
||||
// main
|
||||
// This is functions is called directly from native code on game startup
|
||||
// The particular gametype's main() is called from native code afterward
|
||||
//=======================================================================
|
||||
main()
|
||||
{
|
||||
if( IsDefined( level.createFX_enabled ) && level.createFX_enabled )
|
||||
return;
|
||||
|
||||
setup_callbacks();
|
||||
|
||||
// Enable badplaces in destructibles
|
||||
level.badplace_cylinder_func = ::badplace_cylinder;
|
||||
level.badplace_delete_func = ::badplace_delete;
|
||||
|
||||
level thread maps\mp\agents\_agent_common::init();
|
||||
|
||||
level.spitter_last_cloud_time = 0;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// initAliens
|
||||
//=======================================================
|
||||
setup_callbacks()
|
||||
{
|
||||
if ( !IsDefined( level.agent_funcs ) )
|
||||
level.agent_funcs = [];
|
||||
|
||||
level.agent_funcs["alien"] = [];
|
||||
|
||||
level.agent_funcs["alien"]["spawn"] = ::alienAgentSpawn;
|
||||
|
||||
level.agent_funcs["alien"]["think"] = ::alienAgentThink;
|
||||
level.agent_funcs["alien"]["on_killed"] = maps\mp\alien\_death::onAlienAgentKilled;
|
||||
level.agent_funcs["alien"]["on_damaged"] = maps\mp\alien\_damage::onAlienAgentDamaged;
|
||||
level.agent_funcs["alien"]["on_damaged_finished"] = maps\mp\agents\alien\_alien_think::onDamageFinish;
|
||||
|
||||
level.alien_funcs["goon"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["minion"]["approach"] = maps\mp\agents\alien\_alien_minion::minion_approach;
|
||||
level.alien_funcs["spitter"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["elite"]["approach"] = maps\mp\agents\alien\_alien_elite::elite_approach;
|
||||
level.alien_funcs["brute"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["locust"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["leper"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
|
||||
level.alien_funcs["goon"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["minion"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["spitter"]["combat"] = maps\mp\agents\alien\_alien_spitter::spitter_combat;
|
||||
level.alien_funcs["elite"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["brute"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["locust"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["leper"]["combat"] = maps\mp\agents\alien\_alien_leper::leper_combat;
|
||||
|
||||
level.alien_funcs["goon"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["minion"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["spitter"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["elite"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["brute"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["locust"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["leper"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
|
||||
level.used_nodes = [];
|
||||
level.used_nodes_list_size = 20;
|
||||
level.used_nodes_list_index = 0;
|
||||
|
||||
level.alien_jump_melee_speed = 1.05;
|
||||
level.alien_jump_melee_gravity = 900;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// alienAgentThink
|
||||
//=======================================================
|
||||
alienAgentThink()
|
||||
{
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// alienAgentSpawn
|
||||
//=======================================================
|
||||
alienAgentSpawn( spawnOrigin, spawnAngles, alienType, introVignetteAnim )
|
||||
{
|
||||
if ( !isDefined( alienType ) )
|
||||
alienType = "wave goon";
|
||||
|
||||
alien_type = remove_spawn_type( alienType );
|
||||
|
||||
if ( !isDefined( spawnOrigin ) || !isDefined( spawnAngles ) )
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
}
|
||||
|
||||
self set_alien_model( alien_type );
|
||||
|
||||
// escape sequence, move aliens closer to players post spawn
|
||||
if ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) && self.agentteam == "axis" )
|
||||
{
|
||||
if ( !flag_exist( "nuke_went_off" ) || !flag( "nuke_went_off" ) )
|
||||
{
|
||||
self.noTriggerHurt = true;
|
||||
port_failed = false;
|
||||
|
||||
tokens = strtok( alienType, " " );
|
||||
type = tokens[ 0 ];
|
||||
if ( tokens.size > 1 )
|
||||
type = tokens[ 1 ];
|
||||
|
||||
prof_begin( "port_to_player_loc" );
|
||||
if ( alien_type == "spitter" && IsDefined( level.escape_spitter_target_node ) )
|
||||
{
|
||||
spawnOrigin = self maps\mp\alien\_spawnlogic::port_to_escape_spitter_location();
|
||||
}
|
||||
else
|
||||
{
|
||||
port_to_data = self maps\mp\alien\_spawnlogic::port_to_player_loc( type );
|
||||
if ( !isdefined( port_to_data ) )
|
||||
{
|
||||
port_failed = true;
|
||||
/#
|
||||
if ( GetDvarInt( "alien_debug_escape" ) > 0 )
|
||||
IPrintLnBold( "^1Failed to port alien" );
|
||||
#/
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnOrigin = port_to_data[ 0 ];
|
||||
spawnAngles = port_to_data[ 1 ];
|
||||
}
|
||||
}
|
||||
prof_end( "port_to_player_loc" );
|
||||
|
||||
if ( !port_failed )
|
||||
{
|
||||
spawnOrigin = GetGroundPosition( spawnOrigin, 16 );
|
||||
spawnOrigin -= ( 0, 0, 90 );
|
||||
|
||||
introVignetteAnim = level.cycle_data.spawn_node_info[ "queen_test" ].vignetteInfo[ type ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self spawn_alien_agent( spawnOrigin, spawnAngles, alien_type );
|
||||
level notify( "spawned_agent", self );
|
||||
|
||||
self set_alien_attributes( alienType );
|
||||
|
||||
self set_code_fields( alien_type );
|
||||
|
||||
self set_script_fields( spawnOrigin );
|
||||
|
||||
self set_threat_bias_group( alien_type );
|
||||
|
||||
self type_specific_init();
|
||||
|
||||
self setup_watcher();
|
||||
|
||||
self misc_setup();
|
||||
|
||||
if ( isDefined ( introVignetteAnim ) )
|
||||
self doIntroVignetteAnim( introVignetteAnim );
|
||||
|
||||
if ( isdefined( self.noTriggerHurt ) )
|
||||
self.noTriggerHurt = undefined;
|
||||
|
||||
self maps\mp\alien\_ffotd::onSpawnAlien();
|
||||
|
||||
//self ScrAgentUseModelCollisionBounds();
|
||||
|
||||
self thread maps\mp\agents\alien\_alien_think::main();
|
||||
}
|
||||
|
||||
set_code_fields( alien_type )
|
||||
{
|
||||
self.allowJump = true;
|
||||
self.allowladders = 1;
|
||||
self.moveMode = self get_default_movemode();
|
||||
self.radius = 15;
|
||||
self.height = 72;
|
||||
self.turnrate = 0.3;
|
||||
self.sharpTurnNotifyDist = 48;
|
||||
self.traverseSoonNotifyDist = level.alienAnimData.jumpLaunchArrival_maxMoveDelta;
|
||||
self.stopSoonNotifyDist = level.alienAnimData.stopSoon_NotifyDist;
|
||||
self.jumpCost = level.alien_types[ alien_type ].attributes[ "jump_cost" ];
|
||||
|
||||
// escape mode = jumps more
|
||||
if ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) )
|
||||
self.jumpCost = max( 0.85, self.jumpCost * 0.66 );
|
||||
|
||||
self.traverseCost = level.alien_types[ alien_type ].attributes[ "traverse_cost" ];
|
||||
self.runCost = level.alien_types[ alien_type ].attributes[ "run_cost" ];
|
||||
if ( IsDefined( level.alien_types[ alien_type ].attributes[ "wall_run_cost" ] ) )
|
||||
self ScrAgentSetWallRunCost( level.alien_types[ alien_type ].attributes[ "wall_run_cost" ] );
|
||||
}
|
||||
|
||||
get_default_movemode()
|
||||
{
|
||||
alien_type = self get_alien_type();
|
||||
switch( alien_type )
|
||||
{
|
||||
case "minion":
|
||||
return "walk";
|
||||
default:
|
||||
return "run";
|
||||
}
|
||||
}
|
||||
|
||||
set_threat_bias_group( alien_type )
|
||||
{
|
||||
if ( !can_attack_drill( alien_type ) )
|
||||
{
|
||||
self SetThreatBiasGroup( "dontattackdrill" );
|
||||
return;
|
||||
}
|
||||
|
||||
self SetThreatBiasGroup( "other_aliens" );
|
||||
}
|
||||
|
||||
can_attack_drill( alien_type )
|
||||
{
|
||||
if ( IsDefined( level.dlc_alien_can_attack_drill_override_func ) )
|
||||
{
|
||||
canAttackDrill = [[level.dlc_alien_can_attack_drill_override_func]]( alien_type );
|
||||
if ( IsDefined( canAttackDrill ) )
|
||||
return canAttackDrill;
|
||||
}
|
||||
|
||||
switch( alien_type )
|
||||
{
|
||||
case "elite":
|
||||
case "minion":
|
||||
case "locust":
|
||||
case "gargoyle":
|
||||
case "mammoth":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
set_script_fields( spawnOrigin )
|
||||
{
|
||||
self.species = "alien";
|
||||
self.enableStop = true;
|
||||
self activateAgent();
|
||||
self.spawnTime = GetTime();
|
||||
self.attacking_player = false;
|
||||
self.spawnOrigin = spawnOrigin;
|
||||
self.recentDamages = [];
|
||||
self.damageListIndex = 0;
|
||||
self.swipeChance = 0.5;
|
||||
self.leapEndPos = undefined;
|
||||
self.trajectoryActive = false;
|
||||
self.melee_in_move_back = false;
|
||||
self.melee_in_posture = false;
|
||||
}
|
||||
|
||||
remove_spawn_type( alienType )
|
||||
{
|
||||
// if spawn type is passed in, it is the first token delimited by a space, second token is alien type
|
||||
spawnTypeConfig = strtok( alienType, " " );
|
||||
if ( isdefined( spawnTypeConfig ) && spawnTypeConfig.size == 2 )
|
||||
return spawnTypeConfig[ 1 ];
|
||||
else
|
||||
return alienType;
|
||||
}
|
||||
|
||||
set_alien_model( alien_type )
|
||||
{
|
||||
// During kraken fight
|
||||
if ( isDefined( level.get_alien_model_func ) )
|
||||
{
|
||||
alien_model = [[level.get_alien_model_func]]( alien_type );
|
||||
}
|
||||
else
|
||||
{
|
||||
alien_model = level.alien_types[ alien_type ].attributes[ "model" ];
|
||||
}
|
||||
|
||||
self SetModel( alien_model );
|
||||
self show();
|
||||
self MotionBlurHQEnable();
|
||||
}
|
||||
|
||||
spawn_alien_agent( spawnOrigin, spawnAngles, alien_type )
|
||||
{
|
||||
// the self.OnEnterAnimState field needs to be set before SpawnAgent
|
||||
self.OnEnterAnimState = maps\mp\agents\alien\_alien_think::onEnterAnimState;
|
||||
anim_class = get_anim_class( alien_type );
|
||||
self SpawnAgent( spawnOrigin, spawnAngles, anim_class, 15, 50 );
|
||||
}
|
||||
|
||||
get_anim_class( alien_type )
|
||||
{
|
||||
return level.alien_types[ alien_type ].attributes[ "animclass" ];
|
||||
}
|
||||
|
||||
set_alien_attributes( alienType )
|
||||
{
|
||||
self maps\mp\alien\_spawnlogic::assign_alien_attributes( alienType );
|
||||
}
|
||||
|
||||
type_specific_init()
|
||||
{
|
||||
switch ( maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "elite":
|
||||
maps\mp\agents\alien\_alien_elite::elite_init();
|
||||
break;
|
||||
case "minion":
|
||||
maps\mp\agents\alien\_alien_minion::minion_init();
|
||||
break;
|
||||
case "spitter":
|
||||
maps\mp\agents\alien\_alien_spitter::spitter_init();
|
||||
break;
|
||||
case "leper":
|
||||
maps\mp\agents\alien\_alien_leper::leper_init();
|
||||
break;
|
||||
default:
|
||||
// Check for level specific override
|
||||
if( isDefined( level.dlc_alien_init_override_func ))
|
||||
{
|
||||
[[level.dlc_alien_init_override_func]]();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
misc_setup()
|
||||
{
|
||||
self ScrAgentSetClipMode( "agent" );
|
||||
self TakeAllWeapons();
|
||||
//self maps\mp\agents\alien\_alien_anim_utils::calculateAnimData();
|
||||
}
|
||||
|
||||
setup_watcher()
|
||||
{
|
||||
self thread maps\mp\agents\alien\_alien_think::watch_for_scripted();
|
||||
self thread maps\mp\agents\alien\_alien_think::watch_for_badpath();
|
||||
self thread maps\mp\agents\alien\_alien_think::watch_for_insolid();
|
||||
self thread maps\mp\_flashgrenades::MonitorFlash();
|
||||
self thread maps\mp\agents\alien\_alien_think::MonitorFlash();
|
||||
|
||||
/#
|
||||
if ( GetDvarInt( "scr_aliendebugvelocity" ) == 1 )
|
||||
self thread maps\mp\alien\_debug::alienDebugVelocity();
|
||||
#/
|
||||
}
|
||||
|
||||
doIntroVignetteAnim( vignetteAnimInfo )
|
||||
{
|
||||
CONST_ANIM_STATE_INDEX = 0;
|
||||
CONST_ANIM_INDEX_ARRAY_INDEX = 1;
|
||||
CONST_LABEL_INDEX = 2;
|
||||
CONST_END_NOTETRACK_INDEX = 3;
|
||||
CONST_FX_INDEX = 4;
|
||||
CONST_SCRIPTABLE_TARGETNAME_INDEX = 5;
|
||||
CONST_SCRIPTABLE_STATE_INDEX = 6;
|
||||
CONST_SPAWN_NODE_ID_INDEX = 7;
|
||||
|
||||
self ScrAgentSetScripted( true );
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
vignetteAnimInfo = StrTok( vignetteAnimInfo, ";" );
|
||||
|
||||
self.vignetteAnimInfo = [];
|
||||
self.vignetteAnimInfo["FX"] = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_FX_INDEX] );
|
||||
self.vignetteAnimInfo["scriptableName"] = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SCRIPTABLE_TARGETNAME_INDEX] ), "," );
|
||||
self.vignetteAnimInfo["scriptableState"] = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SCRIPTABLE_STATE_INDEX] ), "," );
|
||||
self.vignetteAnimInfo["spawnNodeID"] = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SPAWN_NODE_ID_INDEX] );
|
||||
|
||||
animState = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_ANIM_STATE_INDEX] );
|
||||
indexArray = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_ANIM_INDEX_ARRAY_INDEX] ), "," );
|
||||
animIndex = int( indexArray [ randomInt ( indexArray.size ) ] );
|
||||
animLabel = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_LABEL_INDEX] );
|
||||
endNotetrack = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_END_NOTETRACK_INDEX] );
|
||||
|
||||
animEntry = self GetAnimEntry( animState, animIndex );
|
||||
|
||||
if ( shouldDoGroundLerp( animEntry ) )
|
||||
doLerpToEndOnGround( animState, animIndex );
|
||||
|
||||
if ( willPlayScriptables( animEntry ) )
|
||||
resetAllScriptables( self.vignetteAnimInfo["scriptableName"], self.origin );
|
||||
|
||||
result = maps\mp\agents\alien\_alien_traverse::needFlexibleHeightSupport( animEntry );
|
||||
|
||||
if( result.need_support )
|
||||
doSpawnVignetteWithFlexibleHeight( animState, animIndex, animLabel, animEntry, result.start_notetrack, result.end_notetrack, ::vignetteNotetrackHandler );
|
||||
else
|
||||
maps\mp\agents\_scriptedAgents::PlayAnimNUntilNotetrack( animState, animIndex, animLabel, endNotetrack, ::vignetteNotetrackHandler );
|
||||
|
||||
self ScrAgentSetScripted( false );
|
||||
}
|
||||
|
||||
shouldDoGroundLerp( animEntry )
|
||||
{
|
||||
return !( AnimHasNotetrack ( animEntry, "skip_ground_lerp" ) );
|
||||
}
|
||||
|
||||
willPlayScriptables( animEntry )
|
||||
{
|
||||
return ( AnimHasNotetrack( animEntry, "play_scriptable" ) && can_play_scriptable( self.vignetteAnimInfo["spawnNodeID"], self.vignetteAnimInfo["scriptableName"] ) );
|
||||
}
|
||||
|
||||
doSpawnVignetteWithFlexibleHeight( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, notetrackHandlerFunc )
|
||||
{
|
||||
maps\mp\agents\_scriptedAgents::PlayAnimNUntilNotetrack( animState, animIndex, animLabel, startNotetrack, notetrackHandlerFunc );
|
||||
|
||||
ground_pos = getEndLocOnGround( animEntry );
|
||||
maps\mp\agents\alien\_alien_traverse::doTraversalWithFlexibleHeight_internal( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, ground_pos , 1, ::vignetteNotetrackHandler );
|
||||
}
|
||||
|
||||
getEndLocOnGround( animEntry )
|
||||
{
|
||||
DROP_TO_GROUND_UP_DIST = 32;
|
||||
DROP_TO_GROUND_DOWN_DIST = -300;
|
||||
|
||||
AnimEndLoc = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( animEntry, self.origin, self.angles, GetAnimLength( animEntry ) );
|
||||
return drop_to_ground( AnimEndLoc, DROP_TO_GROUND_UP_DIST, DROP_TO_GROUND_DOWN_DIST );
|
||||
}
|
||||
|
||||
replaceNoneWithEmptyString( string )
|
||||
{
|
||||
if( string == "NONE" )
|
||||
return "";
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
vignetteNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "alien_drone_spawn_underground":
|
||||
case "play_fx":
|
||||
if ( !is_empty_string( self.vignetteAnimInfo["FX"] ) )
|
||||
playSpawnVignetteFX( self.vignetteAnimInfo["FX"] );
|
||||
break;
|
||||
|
||||
case "play_scriptable":
|
||||
if ( can_play_scriptable( self.vignetteAnimInfo["spawnNodeID"], self.vignetteAnimInfo["scriptableName"] ) )
|
||||
{
|
||||
playAnimOnAllScriptables( self.vignetteAnimInfo["scriptableName"], self.origin, self.vignetteAnimInfo["scriptableState"] );
|
||||
|
||||
if ( is_one_off_scriptable( self.vignetteAnimInfo["spawnNodeID"] ) )
|
||||
inactivate_scriptable_for_node( self.vignetteAnimInfo["spawnNodeID"] );
|
||||
}
|
||||
break;
|
||||
|
||||
case "play_earthquake":
|
||||
Earthquake( 0.5, 1.5, self.origin, 800 );
|
||||
break;
|
||||
|
||||
case "delete_spawn_clip":
|
||||
if ( isDefined( self.intro_clips ) )
|
||||
delete_items( self.intro_clips );
|
||||
break;
|
||||
|
||||
case "frontal_cone_knock_player_back":
|
||||
frontal_cone_knock_player_back();
|
||||
break;
|
||||
|
||||
case "apply_physics":
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
can_play_scriptable( node_id, scriptable_name_list )
|
||||
{
|
||||
return ( ( is_scriptable_status( node_id, "always_on" ) || is_scriptable_status( node_id, "one_off" ) ) && scriptable_name_list.size > 0 );
|
||||
}
|
||||
|
||||
is_scriptable_status( node_id, state )
|
||||
{
|
||||
return ( level.cycle_data.spawn_node_info[node_id].scriptableStatus == state );
|
||||
}
|
||||
|
||||
is_one_off_scriptable( node_id )
|
||||
{
|
||||
return is_scriptable_status( node_id, "one_off" );
|
||||
}
|
||||
|
||||
inactivate_scriptable_for_node( node_id )
|
||||
{
|
||||
level.cycle_data.spawn_node_info[node_id].scriptableStatus = "inactive";
|
||||
}
|
||||
|
||||
delete_items( item_array )
|
||||
{
|
||||
foreach( item in item_array )
|
||||
{
|
||||
if ( isDefined( item ) )
|
||||
item delete();
|
||||
}
|
||||
}
|
||||
|
||||
frontal_cone_knock_player_back()
|
||||
{
|
||||
KNOCK_BACK_ACTIVATION_DIST_SQ = 22500; // 150 * 150
|
||||
KNOCK_BACK_FORCE_MAGNITUDE = 650;
|
||||
FRONT_CONE_LIMIT = 0.2588; //cos( 70 )
|
||||
|
||||
self_forward = anglesToForward( self.angles);
|
||||
|
||||
foreach ( player in level.players)
|
||||
{
|
||||
self_to_player = vectorNormalize ( player.origin - self.origin );
|
||||
|
||||
if( VectorDot( self_to_player, self_forward ) > FRONT_CONE_LIMIT && distanceSquared( player.origin , self.origin ) <= KNOCK_BACK_ACTIVATION_DIST_SQ )
|
||||
{
|
||||
player SetVelocity( VectorNormalize( player.origin - self.origin ) * KNOCK_BACK_FORCE_MAGNITUDE );
|
||||
player DoDamage( ( player.health / 10 ) , self.origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetAllScriptables( scriptable_name_list, position )
|
||||
{
|
||||
for( i = 0; i < scriptable_name_list.size; i++ )
|
||||
maps\mp\agents\alien\_alien_anim_utils::resetScriptable( scriptable_name_list[i], position );
|
||||
}
|
||||
|
||||
playAnimOnAllScriptables( scriptable_name_list, position, scriptable_state_list )
|
||||
{
|
||||
/# AssertEx( scriptable_name_list.size == scriptable_state_list.size, "The scriptable name lists and state lists have mismatch with their size near position ( " + position + " )." ); #/
|
||||
|
||||
for( i = 0; i < scriptable_name_list.size; i++ )
|
||||
maps\mp\agents\alien\_alien_anim_utils::playAnimOnScriptable( scriptable_name_list[i], position, int( scriptable_state_list[i] ) );
|
||||
}
|
||||
|
||||
is_empty_string( string )
|
||||
{
|
||||
return ( string == "" );
|
||||
}
|
||||
|
||||
playSpawnVignetteFX( effect_key )
|
||||
{
|
||||
effect_id = level._effect[effect_key];
|
||||
AssertEx( isDefined( effect_id ), "'" + effect_key + "' is not a valid key for the spawn vignette FX. Load the effect in alien type specific script." );
|
||||
ground_position = GetGroundPosition( self.origin + ( 0, 0, 100 ), 16 ); // play fx on ground surface
|
||||
PlayFX( effect_id, ground_position, (0,0,1) );
|
||||
}
|
||||
|
||||
doLerpToEndOnGround( animState, animIndex )
|
||||
{
|
||||
VERTICAL_DELTA_BUFFER = 2;
|
||||
|
||||
anime = self GetAnimEntry( animState, animIndex );
|
||||
|
||||
lerp_time = maps\mp\agents\alien\_alien_anim_utils::getLerpTime( anime );
|
||||
lerp_target_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( anime, self.origin, self.angles, lerp_time );
|
||||
|
||||
z_delta = getVerticalDeltaToEndGroud( anime );
|
||||
lerp_target_pos += ( 0, 0, z_delta + VERTICAL_DELTA_BUFFER );
|
||||
|
||||
thread maps\mp\agents\alien\_alien_anim_utils::doLerp( lerp_target_pos, lerp_time );
|
||||
}
|
||||
|
||||
getVerticalDeltaToEndGroud( anime )
|
||||
{
|
||||
GET_GROUND_DROP_HEIGHT = 100;
|
||||
AI_PHYSICS_TRACE_RADIUS = 32;
|
||||
AI_PHYSICS_TRACE_HEIGHT = 72;
|
||||
|
||||
anime_delta = GetMoveDelta( anime, 0, 1 );
|
||||
anime_delta = RotateVector( anime_delta, self.angles );
|
||||
anime_height = anime_delta[2];
|
||||
|
||||
anime_end_pos = self.origin + anime_delta;
|
||||
trace_start_pos = anime_end_pos + ( 0, 0, GET_GROUND_DROP_HEIGHT );
|
||||
trace_end_pos = anime_end_pos - ( 0, 0, GET_GROUND_DROP_HEIGHT );
|
||||
ground_pos = self AIPhysicsTrace( trace_start_pos, trace_end_pos, AI_PHYSICS_TRACE_RADIUS, AI_PHYSICS_TRACE_HEIGHT );
|
||||
self_to_ground_height = ( ground_pos - self.origin )[2];
|
||||
|
||||
return ( self_to_ground_height - anime_height );
|
||||
}
|
||||
829
maps/mp/agents/alien/_alien_anim_utils.gsc
Normal file
829
maps/mp/agents/alien/_alien_anim_utils.gsc
Normal file
@@ -0,0 +1,829 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
//=======================================================
|
||||
// initAlienAnims
|
||||
//=======================================================
|
||||
initAlienAnims()
|
||||
{
|
||||
level.alienAnimData = SpawnStruct();
|
||||
|
||||
initAlienCannedTraverses( level.alienAnimData );
|
||||
|
||||
initAlienJumpTraverses( level.alienAnimData );
|
||||
|
||||
initAlienPain( level.alienAnimData );
|
||||
|
||||
initAlienDeath( level.alienAnimData );
|
||||
|
||||
initMoveBackAnims();
|
||||
|
||||
//<NOTE JC> The following values are hard-coded here. Need to run calculateAnimData() if the specific animation assets are
|
||||
// changed.
|
||||
level.alienAnimData.jumpLaunchArrival_maxMoveDelta = 107.659;
|
||||
level.alienAnimData.stopSoon_NotifyDist = 99.4488;
|
||||
}
|
||||
|
||||
calculateAnimData()
|
||||
{
|
||||
//<NOTE JC> The reason for this function is that we can only GetAnimEntry running on an agent. Only need to run this when
|
||||
// specific animation set is updated
|
||||
|
||||
calculate_jumpLaunchArrivalMaxMoveDelta();
|
||||
calculate_stopSoonNotifyDist();
|
||||
}
|
||||
|
||||
calculate_jumpLaunchArrivalMaxMoveDelta()
|
||||
{
|
||||
iprintln( "level.alienAnimData.jumpLaunchArrival_maxMoveDelta = " + calculate_maxMoveDeltaInAnimState( "jump_launch_arrival" ) );
|
||||
}
|
||||
|
||||
calculate_stopSoonNotifyDist()
|
||||
{
|
||||
iprintln( "level.alienAnimData.stopSoon_NotifyDist = " + calculate_maxMoveDeltaInAnimState( "run_stop" ) );
|
||||
}
|
||||
|
||||
calculate_maxMoveDeltaInAnimState( animState )
|
||||
{
|
||||
maxMoveDeltaSq = 0;
|
||||
animCount = self GetAnimEntryCount( animState );
|
||||
|
||||
for ( i = 0; i < animCount; i++ )
|
||||
{
|
||||
animEntry = self GetAnimEntry( animState, i );
|
||||
moveDelta = GetMoveDelta( animEntry, 0, 1 );
|
||||
|
||||
deltaDistSq = LengthSquared( moveDelta );
|
||||
if ( deltaDistSq > maxMoveDeltaSq )
|
||||
maxMoveDeltaSq = deltaDistSq;
|
||||
}
|
||||
|
||||
return sqrt( maxMoveDeltaSq );
|
||||
}
|
||||
|
||||
initAlienCannedTraverses( alienAnimData )
|
||||
{
|
||||
alienAnimData.cannedTraverseAnims = [];
|
||||
|
||||
// Canned traversals
|
||||
// Group 1 --- Each group can hold 32 animations
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_door" ] = registerTraverseData( "traverse_group_1", [ 0 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_sidewall_l" ] = registerTraverseData( "traverse_group_1", [ 1 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_sidewall_r" ] = registerTraverseData( "traverse_group_1", [ 2 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_leap_clear_height_54" ] = registerTraverseData( "traverse_group_1", [ 3 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_drone_traverse_corner_wall_crawl" ] = registerTraverseData( "traverse_group_1", [ 4 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_leap_clear_height_36" ] = registerTraverseData( "traverse_group_1", [ 5 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_leap_tree" ] = registerTraverseData( "traverse_group_1", [ 6 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_under_car" ] = registerTraverseData( "traverse_group_1", [ 7 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_on_car" ] = registerTraverseData( "traverse_group_1", [ 8 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_step_up_56" ] = registerTraverseData( "traverse_group_1", [ 9 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_step_down_56" ] = registerTraverseData( "traverse_group_1", [ 10 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_deadtree" ] = registerTraverseData( "traverse_group_1", [ 11 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_back_humvee" ] = registerTraverseData( "traverse_group_1", [ 12 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_car" ] = registerTraverseData( "traverse_group_1", [ 13 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_humvee" ] = registerTraverseData( "traverse_group_1", [ 14 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_sidecar" ] = registerTraverseData( "traverse_group_1", [ 15 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_sidehumvee" ] = registerTraverseData( "traverse_group_1", [ 16 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_under_fence" ] = registerTraverseData( "traverse_group_1", [ 17,24 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_spiral_tree" ] = registerTraverseData( "traverse_group_1", [ 18 ], true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_gutter_L" ] = registerTraverseData( "traverse_group_1", [ 19 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_gutter_R" ] = registerTraverseData( "traverse_group_1", [ 20 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_over_fence_112" ] = registerTraverseData( "traverse_group_1", [ 21,22,23 ], false );
|
||||
|
||||
// Group 2 --- This group supports the flexible height notetrack. Does not have 'restart' on the animstate. Hold 32 animations max
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_36" ] = registerTraverseData( "traverse_group_2", [ 0 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_drone_traverse_climb_vault_8" ] = registerTraverseData( "traverse_group_2", [ 1 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_drone_traverse_climb_over_fence" ] = registerTraverseData( "traverse_group_2", [ 2 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_rail_vault_lodge" ] = registerTraverseData( "traverse_group_2", [ 3 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_rail_lodge" ] = registerTraverseData( "traverse_group_2", [ 4 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_roof_to_ceiling" ] = registerTraverseData( "traverse_group_2", [ 5 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_over_fence_88" ] = registerTraverseData( "traverse_group_2", [ 6 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_100" ] = registerTraverseData( "traverse_group_2", [ 7 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_200" ] = registerTraverseData( "traverse_group_2", [ 8 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_70" ] = registerTraverseData( "traverse_group_2", [ 9 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_200" ] = registerTraverseData( "traverse_group_2", [ 10 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_straight" ] = registerTraverseData( "traverse_group_2", [ 11 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_roof_to_ground" ] = registerTraverseData( "traverse_group_2", [ 12 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_128_rail_32" ] = registerTraverseData( "traverse_group_2", [ 13 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_128_rail_36" ] = registerTraverseData( "traverse_group_2", [ 14 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_128_rail_48" ] = registerTraverseData( "traverse_group_2", [ 15 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_rail_32_idle" ] = registerTraverseData( "traverse_group_2", [ 16 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_rail_32_run" ] = registerTraverseData( "traverse_group_2", [ 17 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_32" ] = registerTraverseData( "traverse_group_2", [ 18 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_48" ] = registerTraverseData( "traverse_group_2", [ 19 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_128_rail_32" ] = registerTraverseData( "traverse_group_2", [ 20 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_128_rail_36" ] = registerTraverseData( "traverse_group_2", [ 21 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_128_rail_48" ] = registerTraverseData( "traverse_group_2", [ 22 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_down_128_rail_36" ] = registerTraverseData( "traverse_group_2", [ 23 ], true, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_crate_48" ] = registerTraverseData( "traverse_group_2", [ 24 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_crate_64" ] = registerTraverseData( "traverse_group_2", [ 25 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_56_idle" ] = registerTraverseData( "traverse_group_2", [ 26 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_56_run" ] = registerTraverseData( "traverse_group_2", [ 27 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_56_idle" ] = registerTraverseData( "traverse_group_2", [ 28 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_56_run" ] = registerTraverseData( "traverse_group_2", [ 29 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_88_enter_scale" ] = registerTraverseData( "traverse_group_2", [ 30 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_88_exit_scale" ] = registerTraverseData( "traverse_group_2", [ 31 ], false, true );
|
||||
|
||||
// Group 3 --- This group supports the flexible height notetrack. Does not have 'restart' on the animstate. Hold 32 animations max
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_90_rail_32" ] = registerTraverseData( "traverse_group_3", [ 0 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_high_to_low" ] = registerTraverseData( "traverse_group_3", [ 1 ], false,false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_low_to_high" ] = registerTraverseData( "traverse_group_3", [ 2 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_straight_forward_56" ] = registerTraverseData( "traverse_group_3", [ 3 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_straight_360_dlc" ] = registerTraverseData( "traverse_group_3", [ 4 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_rail_32_jump_down_idle_dlc" ] = registerTraverseData( "traverse_group_3", [ 5 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_rail_36_jump_down_idle_dlc" ] = registerTraverseData( "traverse_group_3", [ 6 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_rail_48_jump_down_idle_dlc" ] = registerTraverseData( "traverse_group_3", [ 7 ], false, true );
|
||||
|
||||
// Special traversals
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up" ] = registerTraverseData( "traverse_climb_up" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_down" ] = registerTraverseData( "traverse_climb_down" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_over_56" ] = registerTraverseData( "traverse_climb_up_over_56" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_over_56_down" ] = registerTraverseData( "traverse_climb_over_56_down" );
|
||||
alienAnimData.cannedTraverseAnims[ "climb_up_end_jump_side_l" ] = registerTraverseData( "climb_up_end_jump_side_l" );
|
||||
alienAnimData.cannedTraverseAnims[ "climb_up_end_jump_side_r" ] = registerTraverseData( "climb_up_end_jump_side_r" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_ledge_18_run" ] = registerTraverseData( "traverse_climb_up_ledge_18_run" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_ledge_18_idle" ] = registerTraverseData( "traverse_climb_up_ledge_18_idle" );
|
||||
|
||||
alienAnimData.cannedTraverseAnims[ "alien_wall_run" ] = registerTraverseData( "run" );
|
||||
}
|
||||
|
||||
initAlienJumpTraverses( alienAnimData )
|
||||
{
|
||||
level.alienAnimData.jumpGravity = 20.0 / 0.02205; // cScrAgent_JumpGravity
|
||||
|
||||
level.alienAnimData.jumpLaunchGroundDelta = 16.8476;
|
||||
level.alienAnimData.jumpLaunchInAirAnimLength = 0.111111;
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection = [];
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ] = [];
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ] = [];
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ] = [];
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ][0] = (0.338726, 0, 0.940885);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ][1] = (0.688542, 0, 0.725196);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ][2] = (0.906517, 0, 0.422169);
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ][0] = (0.248516, 0, 0.968628);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ][1] = (0.579155, 0, 0.815218);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ][2] = (0.906514, 0, 0.422177);
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ][0] = (0.333125, 0, 0.942883);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ][1] = (0.518112, 0, 0.855313);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ][2] = (0.892489, 0, 0.451068);
|
||||
|
||||
level.alienAnimData.inAirAnimEntry = [];
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ] = [];
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ] = [];
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ] = [];
|
||||
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_up" ] = 0;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_level" ] = 1;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_down" ] = 2;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_up" ] = 3;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_level" ] = 4;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_down" ] = 5;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_up" ] = 6;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_level" ] = 7;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_down" ] = 8;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_sidewall_high" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_sidewall_high" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_sidewall_high" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_sidewall_low" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_sidewall_low" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_sidewall_low" ] = 9;
|
||||
}
|
||||
|
||||
initAlienPain( alienAnimData )
|
||||
{
|
||||
alienAnimData.painAnims = [];
|
||||
|
||||
//Idle, anim states are: idle_pain_light, idle_pain_heavy
|
||||
idlePainAnims = [];
|
||||
idlePainAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "front" ][ "low_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "front" ][ "up_body_L" ] = [ 1 ];
|
||||
idlePainAnims [ "front" ][ "up_body_R" ] = [ 2 ];
|
||||
idlePainAnims [ "front" ][ "low_body_L" ] = [ 2 ];
|
||||
idlePainAnims [ "front" ][ "low_body_R" ] = [ 2 ];
|
||||
idlePainAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
idlePainAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
idlePainAnims [ "right" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "right" ][ "up_chest" ] = [ 3 ];
|
||||
idlePainAnims [ "right" ][ "low_chest" ] = [ 3 ];
|
||||
idlePainAnims [ "right" ][ "up_body_L" ] = [ 3 ];
|
||||
idlePainAnims [ "right" ][ "up_body_R" ] = [ 2 ];
|
||||
idlePainAnims [ "right" ][ "low_body_L" ] = [ 4 ];
|
||||
idlePainAnims [ "right" ][ "low_body_R" ] = [ 4 ];
|
||||
idlePainAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
idlePainAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
idlePainAnims [ "left" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "left" ][ "up_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "left" ][ "low_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
idlePainAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
idlePainAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
idlePainAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
idlePainAnims [ "left" ][ "armor" ] = [ 2 ];
|
||||
idlePainAnims [ "left" ][ "soft" ] = [ 2 ];
|
||||
idlePainAnims [ "back" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "back" ][ "up_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "back" ][ "low_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "back" ][ "up_body_L" ] = [ 1 ];
|
||||
idlePainAnims [ "back" ][ "up_body_R" ] = [ 7 ];
|
||||
idlePainAnims [ "back" ][ "low_body_L" ] = [ 7 ];
|
||||
idlePainAnims [ "back" ][ "low_body_R" ] = [ 7 ];
|
||||
idlePainAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
idlePainAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "idle" ] = idlePainAnims;
|
||||
|
||||
//Run, anim states are: run_stumble_light, run_stumble_heavy
|
||||
/*
|
||||
0 alien_drone_run_pain_upbody_f_light
|
||||
1 alien_drone_run_pain_upbody_f_medium
|
||||
2 alien_drone_run_pain_upbody_v2_f_medium
|
||||
3 alien_drone_run_pain_Lshoulder_f_heavy
|
||||
4 alien_drone_run_pain_Rshoulder_f_heavy
|
||||
5 alien_drone_run_pain_Lupbody_l_light
|
||||
6 alien_drone_run_pain_Llowbody_l_light
|
||||
7 alien_drone_run_pain_Rupbody_r_light
|
||||
8 alien_drone_run_pain_Lupbody_f_light
|
||||
9 alien_drone_run_pain_Rupbody_f_light
|
||||
10 alien_drone_run_pain_lowbody_f_light
|
||||
11 alien_drone_run_pain_Rlowbody_r_light
|
||||
12 alien_drone_run_pain_upbody_b_light
|
||||
13 alien_drone_run_pain_lowbody_b_light
|
||||
*/
|
||||
runPainAnims = [];
|
||||
runPainAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
runPainAnims [ "front" ][ "up_chest" ] = [ 9 ];
|
||||
runPainAnims [ "front" ][ "low_chest" ] = [ 8 ];
|
||||
runPainAnims [ "front" ][ "up_body_L" ] = [ 8 ];
|
||||
runPainAnims [ "front" ][ "up_body_R" ] = [ 9 ];
|
||||
runPainAnims [ "front" ][ "low_body_L" ] = [ 10 ];
|
||||
runPainAnims [ "front" ][ "low_body_R" ] = [ 10 ];
|
||||
runPainAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
runPainAnims [ "right" ][ "head" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "up_chest" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "low_chest" ] = [ 11 ];
|
||||
runPainAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "low_body_L" ] = [ 11 ];
|
||||
runPainAnims [ "right" ][ "low_body_R" ] = [ 11 ];
|
||||
runPainAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
runPainAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
runPainAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
runPainAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
runPainAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
runPainAnims [ "back" ][ "head" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "up_chest" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "low_chest" ] = [ 13 ];
|
||||
runPainAnims [ "back" ][ "up_body_L" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "up_body_R" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "low_body_L" ] = [ 13 ];
|
||||
runPainAnims [ "back" ][ "low_body_R" ] = [ 13 ];
|
||||
runPainAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "run" ] = runPainAnims;
|
||||
|
||||
//Jump, anim states are: jump_pain_light, jump_pain_heavy
|
||||
jumpPainAnims = [];
|
||||
jumpPainAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
jumpPainAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
jumpPainAnims [ "front" ][ "low_chest" ] = [ 1 ];
|
||||
jumpPainAnims [ "front" ][ "up_body_L" ] = [ 2 ];
|
||||
jumpPainAnims [ "front" ][ "up_body_R" ] = [ 3 ];
|
||||
jumpPainAnims [ "front" ][ "low_body_L" ] = [ 4 ];
|
||||
jumpPainAnims [ "front" ][ "low_body_R" ] = [ 4 ];
|
||||
jumpPainAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
jumpPainAnims [ "right" ][ "head" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "up_chest" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "low_chest" ] = [ 8 ];
|
||||
jumpPainAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "low_body_L" ] = [ 8 ];
|
||||
jumpPainAnims [ "right" ][ "low_body_R" ] = [ 8 ];
|
||||
jumpPainAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
jumpPainAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
jumpPainAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
jumpPainAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
jumpPainAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
jumpPainAnims [ "back" ][ "head" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "up_chest" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "low_chest" ] = [ 10 ];
|
||||
jumpPainAnims [ "back" ][ "up_body_L" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "up_body_R" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "low_body_L" ] = [ 10 ];
|
||||
jumpPainAnims [ "back" ][ "low_body_R" ] = [ 10 ];
|
||||
jumpPainAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "jump" ] = jumpPainAnims;
|
||||
|
||||
//Push back, anim state is: pain_pushback
|
||||
pushbackPainAnims = [];
|
||||
pushbackPainAnims [ "front" ] = [ 0, 1 ];
|
||||
pushbackPainAnims [ "right" ] = [ 2 ];
|
||||
pushbackPainAnims [ "left" ] = [ 3 ];
|
||||
pushbackPainAnims [ "back" ] = [ 4 ];
|
||||
alienAnimData.painAnims[ "push_back" ] = pushbackPainAnims;
|
||||
|
||||
//Move back, anim state are: move_back_pain_light, move_back_pain_heavy
|
||||
movebackPainAnims = [];
|
||||
movebackPainAnims [ "front" ] = [ 0 ];
|
||||
movebackPainAnims [ "right" ] = [ 0 ];
|
||||
movebackPainAnims [ "left" ] = [ 0 ];
|
||||
movebackPainAnims [ "back" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "move_back" ] = movebackPainAnims;
|
||||
|
||||
//Melee, anim state are: melee_pain_light, melee_pain_heavy
|
||||
meleePainAnims = [];
|
||||
meleePainAnims [ "front" ] = [ 0, 1, 2 ];
|
||||
meleePainAnims [ "right" ] = [ 0, 1, 2 ];
|
||||
meleePainAnims [ "left" ] = [ 0, 1, 2 ];
|
||||
meleePainAnims [ "back" ] = [ 0, 1, 2 ];
|
||||
alienAnimData.painAnims[ "melee" ] = meleePainAnims;
|
||||
|
||||
combinedHitLoc = [];
|
||||
combinedHitLoc [ "head" ] = "head";
|
||||
combinedHitLoc [ "neck" ] = "head";
|
||||
combinedHitLoc [ "torso_upper" ] = "up_chest";
|
||||
combinedHitLoc [ "none" ] = "up_chest";
|
||||
combinedHitLoc [ "torso_lower" ] = "low_chest";
|
||||
combinedHitLoc [ "left_arm_upper" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_arm_lower" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_hand" ] = "up_body_L";
|
||||
combinedHitLoc [ "right_arm_upper" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_arm_lower" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_hand" ] = "up_body_R";
|
||||
combinedHitLoc [ "left_leg_upper" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_leg_lower" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_foot" ] = "low_body_L";
|
||||
combinedHitLoc [ "right_leg_upper" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_leg_lower" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_foot" ] = "low_body_R";
|
||||
combinedHitLoc [ "armor" ] = "armor";
|
||||
combinedHitLoc [ "soft" ] = "soft";
|
||||
alienAnimData.painAnims[ "hitLoc" ] = combinedHitLoc;
|
||||
|
||||
hitDirection = [];
|
||||
hitDirection[ 0 ] = "back";
|
||||
hitDirection[ 1 ] = "back";
|
||||
hitDirection[ 2 ] = "right";
|
||||
hitDirection[ 3 ] = "right";
|
||||
hitDirection[ 4 ] = "front";
|
||||
hitDirection[ 5 ] = "left";
|
||||
hitDirection[ 6 ] = "left";
|
||||
hitDirection[ 7 ] = "back";
|
||||
hitDirection[ 8 ] = "back";
|
||||
alienAnimData.painAnims[ "hitDirection" ] = hitDirection;
|
||||
|
||||
jumpPainIdleToImpactMap = [];
|
||||
// *idle index *Impact index
|
||||
jumpPainIdleToImpactMap[ 0 ] = [ 0 ];
|
||||
jumpPainIdleToImpactMap[ 1 ] = [ 1 ];
|
||||
jumpPainIdleToImpactMap[ 2 ] = [ 2 ];
|
||||
jumpPainIdleToImpactMap[ 3 ] = [ 3 ];
|
||||
jumpPainIdleToImpactMap[ 4 ] = [ 4 ];
|
||||
jumpPainIdleToImpactMap[ 5 ] = [ 5 ];
|
||||
jumpPainIdleToImpactMap[ 6 ] = [ 6 ];
|
||||
jumpPainIdleToImpactMap[ 7 ] = [ 7 ];
|
||||
jumpPainIdleToImpactMap[ 8 ] = [ 8 ];
|
||||
jumpPainIdleToImpactMap[ 9 ] = [ 9 ];
|
||||
jumpPainIdleToImpactMap[ 10 ] = [ 10 ];
|
||||
alienAnimData.painAnims[ "idleToImpactMap" ] = jumpPainIdleToImpactMap;
|
||||
}
|
||||
|
||||
initAlienDeath( alienAnimData )
|
||||
{
|
||||
alienAnimData.deathAnims = [];
|
||||
|
||||
//Idle, anim states are: idle_death_light, idle_death_heavy
|
||||
idleDeathAnims = [];
|
||||
idleDeathAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "front" ][ "low_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "front" ][ "up_body_L" ] = [ 1 ];
|
||||
idleDeathAnims [ "front" ][ "up_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "front" ][ "low_body_L" ] = [ 2 ];
|
||||
idleDeathAnims [ "front" ][ "low_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
idleDeathAnims [ "right" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "right" ][ "up_chest" ] = [ 4 ];
|
||||
idleDeathAnims [ "right" ][ "low_chest" ] = [ 3 ];
|
||||
idleDeathAnims [ "right" ][ "up_body_L" ] = [ 4 ];
|
||||
idleDeathAnims [ "right" ][ "up_body_R" ] = [ 4 ];
|
||||
idleDeathAnims [ "right" ][ "low_body_L" ] = [ 2 ];
|
||||
idleDeathAnims [ "right" ][ "low_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
idleDeathAnims [ "left" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "left" ][ "up_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "left" ][ "low_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "left" ][ "up_body_L" ] = [ 1 ];
|
||||
idleDeathAnims [ "left" ][ "up_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "left" ][ "low_body_L" ] = [ 5 ];
|
||||
idleDeathAnims [ "left" ][ "low_body_R" ] = [ 5 ];
|
||||
idleDeathAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
idleDeathAnims [ "back" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "back" ][ "up_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "back" ][ "low_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "back" ][ "up_body_L" ] = [ 1 ];
|
||||
idleDeathAnims [ "back" ][ "up_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "back" ][ "low_body_L" ] = [ 2 ];
|
||||
idleDeathAnims [ "back" ][ "low_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.deathAnims[ "idle" ] = idleDeathAnims;
|
||||
|
||||
//Run, anim states are: run_death_light, run_death_heavy
|
||||
runDeathAnims = [];
|
||||
runDeathAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
runDeathAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
runDeathAnims [ "front" ][ "low_chest" ] = [ 3 ];
|
||||
runDeathAnims [ "front" ][ "up_body_L" ] = [ 4 ];
|
||||
runDeathAnims [ "front" ][ "up_body_R" ] = [ 9 ];
|
||||
runDeathAnims [ "front" ][ "low_body_L" ] = [ 4 ];
|
||||
runDeathAnims [ "front" ][ "low_body_R" ] = [ 3 ];
|
||||
runDeathAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
runDeathAnims [ "right" ][ "head" ] = [ 2 ];
|
||||
runDeathAnims [ "right" ][ "up_chest" ] = [ 1 ];
|
||||
runDeathAnims [ "right" ][ "low_chest" ] = [ 0 ];
|
||||
runDeathAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
runDeathAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
runDeathAnims [ "right" ][ "low_body_L" ] = [ 3 ];
|
||||
runDeathAnims [ "right" ][ "low_body_R" ] = [ 4 ];
|
||||
runDeathAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
runDeathAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
runDeathAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "low_body_L" ] = [ 8 ];
|
||||
runDeathAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
runDeathAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
runDeathAnims [ "back" ][ "head" ] = [ 1 ];
|
||||
runDeathAnims [ "back" ][ "up_chest" ] = [ 5 ];
|
||||
runDeathAnims [ "back" ][ "low_chest" ] = [ 4 ];
|
||||
runDeathAnims [ "back" ][ "up_body_L" ] = [ 3 ];
|
||||
runDeathAnims [ "back" ][ "up_body_R" ] = [ 2 ];
|
||||
runDeathAnims [ "back" ][ "low_body_L" ] = [ 1 ];
|
||||
runDeathAnims [ "back" ][ "low_body_R" ] = [ 4 ];
|
||||
runDeathAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.deathAnims[ "run" ] = runDeathAnims;
|
||||
|
||||
//Jump, anim states are: jump_death_light, jump_death_heavy
|
||||
jumpDeathAnims = [];
|
||||
jumpDeathAnims [ "front" ][ "head" ] = [ 1 ];
|
||||
jumpDeathAnims [ "front" ][ "up_chest" ] = [ 0 ];
|
||||
jumpDeathAnims [ "front" ][ "low_chest" ] = [ 0 ];
|
||||
jumpDeathAnims [ "front" ][ "up_body_L" ] = [ 2 ];
|
||||
jumpDeathAnims [ "front" ][ "up_body_R" ] = [ 3 ];
|
||||
jumpDeathAnims [ "front" ][ "low_body_L" ] = [ 4 ];
|
||||
jumpDeathAnims [ "front" ][ "low_body_R" ] = [ 4 ];
|
||||
jumpDeathAnims [ "front" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "front" ][ "soft" ] = [ 1 ];
|
||||
jumpDeathAnims [ "right" ][ "head" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "up_chest" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "low_chest" ] = [ 8 ];
|
||||
jumpDeathAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "low_body_L" ] = [ 8 ];
|
||||
jumpDeathAnims [ "right" ][ "low_body_R" ] = [ 8 ];
|
||||
jumpDeathAnims [ "right" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "right" ][ "soft" ] = [ 1 ];
|
||||
jumpDeathAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
jumpDeathAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
jumpDeathAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
jumpDeathAnims [ "left" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "left" ][ "soft" ] = [ 1 ];
|
||||
jumpDeathAnims [ "back" ][ "head" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "up_chest" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "low_chest" ] = [ 10 ];
|
||||
jumpDeathAnims [ "back" ][ "up_body_L" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "up_body_R" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "low_body_L" ] = [ 10 ];
|
||||
jumpDeathAnims [ "back" ][ "low_body_R" ] = [ 10 ];
|
||||
jumpDeathAnims [ "back" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "back" ][ "soft" ] = [ 1 ];
|
||||
alienAnimData.deathAnims[ "jump" ] = jumpDeathAnims;
|
||||
|
||||
combinedHitLoc = [];
|
||||
combinedHitLoc [ "head" ] = "head";
|
||||
combinedHitLoc [ "neck" ] = "head";
|
||||
combinedHitLoc [ "torso_upper" ] = "up_chest";
|
||||
combinedHitLoc [ "none" ] = "up_chest";
|
||||
combinedHitLoc [ "torso_lower" ] = "low_chest";
|
||||
combinedHitLoc [ "left_arm_upper" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_arm_lower" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_hand" ] = "up_body_L";
|
||||
combinedHitLoc [ "right_arm_upper" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_arm_lower" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_hand" ] = "up_body_R";
|
||||
combinedHitLoc [ "left_leg_upper" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_leg_lower" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_foot" ] = "low_body_L";
|
||||
combinedHitLoc [ "right_leg_upper" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_leg_lower" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_foot" ] = "low_body_R";
|
||||
combinedHitLoc [ "armor" ] = "armor";
|
||||
combinedHitLoc [ "soft" ] = "soft";
|
||||
alienAnimData.deathAnims[ "hitLoc" ] = combinedHitLoc;
|
||||
|
||||
hitDirection = [];
|
||||
hitDirection[ 0 ] = "back";
|
||||
hitDirection[ 1 ] = "back";
|
||||
hitDirection[ 2 ] = "right";
|
||||
hitDirection[ 3 ] = "right";
|
||||
hitDirection[ 4 ] = "front";
|
||||
hitDirection[ 5 ] = "left";
|
||||
hitDirection[ 6 ] = "left";
|
||||
hitDirection[ 7 ] = "back";
|
||||
hitDirection[ 8 ] = "back";
|
||||
alienAnimData.deathAnims[ "hitDirection" ] = hitDirection;
|
||||
|
||||
//Special death
|
||||
specialDeathAnims = [];
|
||||
specialDeathAnims [ "electric_shock_death" ] = [ 0 ];
|
||||
specialDeathAnims [ "traverse" ] = [ 1 ];
|
||||
alienAnimData.deathAnims[ "special" ] = specialDeathAnims;
|
||||
}
|
||||
|
||||
initMoveBackAnims()
|
||||
{
|
||||
level.alienAnimData.alienMoveBackAnimChance[ 0 ] = 40;
|
||||
level.alienAnimData.alienMoveBackAnimChance[ 1 ] = 40;
|
||||
level.alienAnimData.alienMoveBackAnimChance[ 2 ] = 20;
|
||||
}
|
||||
|
||||
registerTraverseData( animState, animIndexArray, endInOriented, flexHeightEndAtTraverseEnd, traverseSound, traverseAnimScale )
|
||||
{
|
||||
assertEx( isDefined( animState));
|
||||
|
||||
traverseData = [];
|
||||
traverseData [ "animState" ] = animState;
|
||||
|
||||
if ( isDefined ( animIndexArray ) )
|
||||
traverseData [ "animIndexArray" ] = animIndexArray;
|
||||
|
||||
if ( isDefined ( endInOriented ) )
|
||||
traverseData [ "endInOriented" ] = endInOriented;
|
||||
|
||||
if ( isDefined ( flexHeightEndAtTraverseEnd ) )
|
||||
traverseData [ "flexHeightEndAtTraverseEnd" ] = flexHeightEndAtTraverseEnd;
|
||||
|
||||
if ( isDefined ( traverseSound ) )
|
||||
traverseData [ "traverseSound" ] = traverseSound;
|
||||
|
||||
if ( isDefined ( traverseAnimScale ) )
|
||||
traverseData [ "traverseAnimScale" ] = traverseAnimScale;
|
||||
|
||||
return traverseData;
|
||||
}
|
||||
|
||||
turnTowardsEntity( entity )
|
||||
{
|
||||
targetVector = entity.origin - self.origin;
|
||||
return turnTowardsVector( targetVector );
|
||||
}
|
||||
|
||||
turnTowardsVector( targetVector )
|
||||
{
|
||||
turnIndex = getTurnInPlaceIndex( AnglesToForward( self.angles ), targetVector, AnglesToUp( self.angles ) );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
if ( turnIndex != 4 )
|
||||
{
|
||||
self.stateLocked = true;
|
||||
if( self.oriented )
|
||||
self ScrAgentSetAnimMode( "anim angle delta" );
|
||||
else
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
animState = getTurnInPlaceAnimState();
|
||||
self PlayAnimNUntilNotetrack( animState, turnIndex, "turn_in_place", "code_move" );
|
||||
|
||||
if ( !self maps\mp\alien\_utility::is_idle_state_locked() )
|
||||
self.stateLocked = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getTurnInPlaceAnimState()
|
||||
{
|
||||
if ( IsDefined( level.dlc_alien_turn_in_place_anim_state_override_func ) )
|
||||
{
|
||||
animState = [[level.dlc_alien_turn_in_place_anim_state_override_func]]();
|
||||
if ( IsDefined( animState ) )
|
||||
return animState;
|
||||
}
|
||||
|
||||
return "turn_in_place";
|
||||
}
|
||||
|
||||
getTurnInPlaceIndex ( inVector, outVector, surfaceNormal )
|
||||
{
|
||||
turnAnim = undefined;
|
||||
index = undefined;
|
||||
|
||||
projData = getProjectionData( inVector, outVector, surfaceNormal );
|
||||
rotatedYaw = projData.rotatedYaw;
|
||||
projInToOutRight = projData.projInToOutRight;
|
||||
|
||||
// favor underturning, unless you're within <threshold> degrees of the next one up.
|
||||
threshold = 10;
|
||||
|
||||
if ( projInToOutRight > 0 ) //Entering from the right
|
||||
{
|
||||
index = int( ceil( ( 180 - rotatedYaw - threshold ) / 45 ) );
|
||||
}
|
||||
else //Entering from the left
|
||||
{
|
||||
index = int( floor( ( 180 + rotatedYaw + threshold ) / 45 ) );
|
||||
}
|
||||
index = int( clamp( index, 0, 8 )); //the threshold in getTurnInPlaceIndex might cause the index to be out of range
|
||||
return index;
|
||||
}
|
||||
|
||||
getProjectionData( inVector, outVector, surfaceNormal )
|
||||
{
|
||||
projectionData = SpawnStruct();
|
||||
|
||||
inVectorNoNormal = vectorNormalize( projectVectorToPlane( inVector, surfaceNormal));
|
||||
outVectorNoNormal = vectorNormalize( projectVectorToPlane( outVector, surfaceNormal ));
|
||||
|
||||
outVectorRight = VectorCross ( outVectorNoNormal, surfaceNormal );
|
||||
outVectorRightNoNormal = VectorNormalize ( projectVectorToPlane( outVectorRight, surfaceNormal ) );
|
||||
projInToOutRight = VectorDot ( inVectorNoNormal * -1, outVectorRightNoNormal );
|
||||
|
||||
ratio = vectorDot( outVectorNoNormal, inVectorNoNormal );
|
||||
|
||||
// Need to make sure the value is within the domain of Acos
|
||||
ratio = clamp( ratio, -1, 1 );
|
||||
|
||||
rotatedYaw = Acos ( ratio );
|
||||
|
||||
projectionData.rotatedYaw = rotatedYaw;
|
||||
projectionData.projInToOutRight = projInToOutRight;
|
||||
|
||||
return projectionData;
|
||||
}
|
||||
|
||||
projectVectorToPlane( vector, planeUp )
|
||||
{
|
||||
dotResult = VectorDot( vector, planeUp );
|
||||
projVector = vector - ( planeUp * dotResult );
|
||||
return projVector;
|
||||
}
|
||||
|
||||
pain_getCombinedHitLoc( hitLoc )
|
||||
{
|
||||
return ( level.alienAnimData.painAnims[ "hitLoc" ][ hitLoc ] );
|
||||
}
|
||||
|
||||
pain_getIncomingDirection( direction )
|
||||
{
|
||||
directionIndex = maps\mp\agents\_scriptedagents::GetAngleIndexFromSelfYaw( direction );
|
||||
return ( level.alienAnimData.painAnims[ "hitDirection" ][ directionIndex ] );
|
||||
}
|
||||
|
||||
death_getCombinedHitLoc( hitLoc )
|
||||
{
|
||||
return ( level.alienAnimData.deathAnims[ "hitLoc" ][ hitLoc ] );
|
||||
}
|
||||
|
||||
death_getIncomingDirection( direction )
|
||||
{
|
||||
directionIndex = maps\mp\agents\_scriptedagents::GetAngleIndexFromSelfYaw( direction );
|
||||
return ( level.alienAnimData.deathAnims[ "hitDirection" ][ directionIndex ] );
|
||||
}
|
||||
|
||||
getPainAnimState( state, iDamage, is_stun )
|
||||
{
|
||||
secondaryState = getDamageDegree( iDamage, is_stun );
|
||||
return ( state + "_" + secondaryState );
|
||||
}
|
||||
|
||||
getDamageDegree( iDamage, is_stun )
|
||||
{
|
||||
alienType = self maps\mp\alien\_utility::get_alien_type();
|
||||
damageThreshold = level.alien_types[ alienType ].attributes[ "heavy_damage_threshold" ];
|
||||
|
||||
if ( iDamage < damageThreshold && !is_stun )
|
||||
return "light";
|
||||
else
|
||||
return "heavy";
|
||||
}
|
||||
|
||||
getPainAnimIndex( state, damageDirection, hitLoc )
|
||||
{
|
||||
damageDirection = pain_getIncomingDirection( damageDirection * -1 );
|
||||
|
||||
if ( isDefined ( hitLoc ) )
|
||||
hitLoc = pain_getCombinedHitLoc( hitLoc );
|
||||
|
||||
return getPainDeathAnimIndex_Internal( state, damageDirection, hitLoc, level.alienAnimData.painAnims );
|
||||
}
|
||||
|
||||
GetImpactPainAnimIndex( jump_pain_index )
|
||||
{
|
||||
available_impact = level.alienAnimData.painAnims[ "idleToImpactMap" ][ jump_pain_index ];
|
||||
random_impact_index = randomIntRange( 0, available_impact.size );
|
||||
return available_impact [ random_impact_index ];
|
||||
}
|
||||
|
||||
getDeathAnimState( state, iDamage )
|
||||
{
|
||||
secondaryState = getDamageDegree( iDamage, false );
|
||||
return ( state + "_" + secondaryState );
|
||||
}
|
||||
|
||||
getDeathAnimIndex( state, damageDirection, hitLoc )
|
||||
{
|
||||
damageDirection = death_getIncomingDirection( damageDirection * -1 );
|
||||
hitLoc = death_getCombinedHitLoc( hitLoc );
|
||||
return getPainDeathAnimIndex_Internal( state, damageDirection, hitLoc, level.alienAnimData.deathAnims );
|
||||
}
|
||||
|
||||
getPainDeathAnimIndex_Internal( state, damageDirection, hitLoc, animArray )
|
||||
{
|
||||
if ( isDefined ( hitLoc ) )
|
||||
availableIndexList = animArray[ state ][ damageDirection ][ hitLoc ];
|
||||
else
|
||||
availableIndexList = animArray[ state ][ damageDirection ];
|
||||
|
||||
return ( availableIndexList[ randomInt ( availableIndexList.size ) ] );
|
||||
}
|
||||
|
||||
getSpecialDeathAnimIndex( state )
|
||||
{
|
||||
availableIndexList = level.alienAnimData.deathAnims[ "special" ][ state ];
|
||||
return ( availableIndexList[ randomInt ( availableIndexList.size ) ] );
|
||||
}
|
||||
|
||||
resetScriptable( scriptableName, endPos )
|
||||
{
|
||||
scriptable_obj = GetEnt( scriptableName, "targetname" );
|
||||
AssertEx( isDefined( scriptable_obj ), "Unable to find a scriptable object with targetname: " + scriptableName + " at location " + endPos );
|
||||
scriptable_obj SetScriptablePartState( 0, 0 );
|
||||
}
|
||||
|
||||
playAnimOnScriptable( scriptableName, endPos, scriptableState )
|
||||
{
|
||||
scriptable_obj = GetEnt( scriptableName, "targetname" );
|
||||
AssertEx( isDefined( scriptable_obj ), "Unable to find a scriptable object with targetname: " + scriptableName + " at location " + endPos );
|
||||
|
||||
if ( !isDefined( scriptableState ) )
|
||||
scriptableState = 1;
|
||||
|
||||
scriptable_obj SetScriptablePartState( 0, scriptableState );
|
||||
|
||||
level notify( "scriptable",scriptableName );
|
||||
}
|
||||
|
||||
getLerpTime( startAnim )
|
||||
{
|
||||
startAnimLength = GetAnimLength( startAnim );
|
||||
return min( 0.2, startAnimLength );
|
||||
}
|
||||
|
||||
getPosInSpaceAtAnimTime( anime, start_pos, start_angles, time )
|
||||
{
|
||||
animLength = GetAnimLength( anime );
|
||||
animDelta = GetMoveDelta( anime, 0, time / animLength );
|
||||
offsetFromStart = RotateVector( animDelta, start_angles );
|
||||
|
||||
return ( start_pos + offsetFromStart );
|
||||
}
|
||||
|
||||
doLerp( lerp_target_pos, lerp_time )
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, lerp_target_pos, lerp_time );
|
||||
wait lerp_time;
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
}
|
||||
649
maps/mp/agents/alien/_alien_elite.gsc
Normal file
649
maps/mp/agents/alien/_alien_elite.gsc
Normal file
@@ -0,0 +1,649 @@
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
ALIEN_CHARGE_ATTACK_DISTANCE_MAX = 500;
|
||||
ALIEN_CHARGE_ATTACK_DISTANCE_MIN = 350;
|
||||
ALIEN_CHARGE_COOLDOWN_MSEC = 12000;
|
||||
|
||||
ALIEN_SLAM_MIN_DISTANCE = 175;
|
||||
ALIEN_SLAM_RADIUS = 250;
|
||||
ELITE_SWIPE_OFFSET_XY = 125; // how far in front of player we want to swipe (so the player can actually see it.)
|
||||
ELITE_MAX_SWIPE_DAMAGE_DIST = 175;
|
||||
|
||||
ANGERED_DAMAGE_SCALAR = 1.25;
|
||||
|
||||
ELITE_ATTACK_START_SOUND = "";
|
||||
CHARGE_HIT_SOUND = "";
|
||||
CHARGE_ATTACK_START_SOUND = "";
|
||||
ELITE_REGEN_START_SOUND = "";
|
||||
|
||||
elite_approach( enemy, attack_counter )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "elite_approach" ); #/
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_attacker_state( "attacking" ); #/
|
||||
|
||||
// Run near enemy
|
||||
if ( DistanceSquared( enemy.origin, self.origin ) > ALIEN_CHARGE_ATTACK_DISTANCE_MAX * ALIEN_CHARGE_ATTACK_DISTANCE_MAX )
|
||||
self maps\mp\agents\alien\_alien_think::run_near_enemy( ALIEN_CHARGE_ATTACK_DISTANCE_MAX, enemy );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( can_do_charge_attack( enemy ) )
|
||||
{
|
||||
return "charge";
|
||||
}
|
||||
else if ( run_to_slam( enemy ) )
|
||||
{
|
||||
return "slam";
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
run_to_slam( enemy )
|
||||
{
|
||||
self thread monitor_charge_range( enemy );
|
||||
self thread run_to_enemy( enemy );
|
||||
|
||||
msg = self common_scripts\utility::waittill_any_return( "run_to_slam_complete", "in_charge_range", "enemy", "bad_path" );
|
||||
if ( !self AgentCanSeeSentient( enemy ) )
|
||||
return false;
|
||||
|
||||
return ( msg == "run_to_slam_complete" );
|
||||
}
|
||||
|
||||
run_to_enemy( enemy )
|
||||
{
|
||||
enemy endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "bad_path" );
|
||||
|
||||
startTime = GetTime();
|
||||
|
||||
self maps\mp\agents\alien\_alien_think::run_near_enemy( ALIEN_SLAM_MIN_DISTANCE, enemy );
|
||||
|
||||
// need to make sure a frame passes before we send the notify
|
||||
if ( startTime == GetTime() )
|
||||
wait 0.05;
|
||||
|
||||
self notify( "run_to_slam_complete" );
|
||||
}
|
||||
|
||||
monitor_charge_range( enemy )
|
||||
{
|
||||
self endon( "goal_reached" );
|
||||
enemy endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "bad_path" );
|
||||
|
||||
chargeRangeSquared = ALIEN_CHARGE_ATTACK_DISTANCE_MIN * ALIEN_CHARGE_ATTACK_DISTANCE_MIN;
|
||||
wait 0.05;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( DistanceSquared( self.origin, enemy.origin ) >= chargeRangeSquared )
|
||||
break;
|
||||
|
||||
wait 0.2;
|
||||
}
|
||||
|
||||
self notify( "in_charge_range" );
|
||||
}
|
||||
|
||||
can_do_charge_attack( enemy )
|
||||
{
|
||||
if ( gettime() < self.last_charge_time + ALIEN_CHARGE_COOLDOWN_MSEC )
|
||||
return false;
|
||||
|
||||
if ( DistanceSquared( self.origin, enemy.origin ) < ALIEN_CHARGE_ATTACK_DISTANCE_MIN * ALIEN_CHARGE_ATTACK_DISTANCE_MIN )
|
||||
return false;
|
||||
|
||||
if ( !maps\mp\agents\_scriptedagents::CanMovePointToPoint( self.origin, enemy.origin) )
|
||||
return false;
|
||||
|
||||
return self maps\mp\alien\_utility::is_normal_upright( anglesToUp( self.angles ) );
|
||||
}
|
||||
|
||||
ground_slam( enemy )
|
||||
{
|
||||
self.melee_type = "slam";
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
}
|
||||
|
||||
ALIEN_ELITE_GROUND_SLAM_IMPULSE = 800;
|
||||
|
||||
do_ground_slam( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( enemy );
|
||||
self ScrAgentSetOrientMode( "face enemy" );
|
||||
|
||||
self maps\mp\agents\alien\_alien_melee::try_preliminary_swipes( "swipe", enemy, ELITE_SWIPE_OFFSET_XY, ELITE_MAX_SWIPE_DAMAGE_DIST );
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimNUntilNotetrack( "attack_melee_swipe", 2, "attack_melee", "alien_slam_big" );
|
||||
|
||||
min_damage = level.alien_types[ self.alien_type ].attributes[ "slam_min_damage" ];
|
||||
max_damage = level.alien_types[ self.alien_type ].attributes[ "slam_max_damage" ];
|
||||
|
||||
if ( IsDefined( self.elite_angered ) )
|
||||
{
|
||||
min_damage *= get_angered_damage_scalar();
|
||||
max_damage *= get_angered_damage_scalar();
|
||||
}
|
||||
self area_damage_and_impulse( ALIEN_SLAM_RADIUS, min_damage, max_damage, ALIEN_ELITE_GROUND_SLAM_IMPULSE );
|
||||
self maps\mp\agents\_scriptedagents::WaitUntilNotetrack( "attack_melee", "end" );
|
||||
|
||||
if ( !isDefined( self.elite_angered ) )
|
||||
meleeSuccess = self maps\mp\agents\alien\_alien_melee::move_back( enemy, true );
|
||||
self set_alien_emissive_default( 0.2 );
|
||||
}
|
||||
|
||||
charge_attack( enemy )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "charge_attack" ); #/
|
||||
|
||||
if ( enemy being_charged() )
|
||||
{
|
||||
wait 0.2;
|
||||
return;
|
||||
}
|
||||
|
||||
self.melee_type = "charge";
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
enemy.being_charged = false;
|
||||
}
|
||||
|
||||
angered( enemy )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "health_regen" ); #/
|
||||
|
||||
self.melee_type = "angered";
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
}
|
||||
|
||||
do_charge_attack( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
enemy.being_charged = true;
|
||||
self.last_charge_time = gettime();
|
||||
self set_alien_emissive( 0.2, 1.0 );
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( enemy );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self ScrAgentSetOrientMode( "face enemy" );
|
||||
|
||||
charge_start_index = get_charge_start_index();
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimNAtRateUntilNotetrack( "charge_attack_start", charge_start_index, 1.15, "charge_attack_start", "end", ::chargeStartNotetrackHandler );
|
||||
|
||||
if ( isAlive( enemy ) && can_see_enemy( enemy ) )
|
||||
{
|
||||
self thread track_enemy( enemy );
|
||||
self SetAnimState( "charge_attack", charge_start_index, 1.0);
|
||||
|
||||
result = watch_charge_hit( enemy, charge_start_index );
|
||||
self notify( "charge_complete" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
if ( !IsDefined( result ) ) // enemy died mid charge, play stop anim
|
||||
result = "fail";
|
||||
|
||||
switch ( result )
|
||||
{
|
||||
case "success":
|
||||
self maps\mp\agents\_scriptedagents::SafelyPlayAnimNAtRateUntilNotetrack( "charge_attack_bump", charge_start_index, 1.0, "charge_attack_bump", "end", ::chargeEndNotetrackHandler );
|
||||
break;
|
||||
case "fail":
|
||||
self play_stop_anim( charge_start_index );
|
||||
break;
|
||||
default:
|
||||
assertmsg( "Unknown charge hit result: " + result );
|
||||
break;
|
||||
}
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
|
||||
self set_alien_emissive_default( 0.2 );
|
||||
}
|
||||
|
||||
can_see_enemy( enemy )
|
||||
{
|
||||
return SightTracePassed( self getEye(), enemy getEye(), false, self );
|
||||
}
|
||||
|
||||
track_enemy( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "charge_complete" );
|
||||
|
||||
STOP_TRACKING_DISTANCE_SQ = 325 * 325;
|
||||
self.charge_tracking_enemy = true;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( DistanceSquared( self.origin, enemy.origin ) < STOP_TRACKING_DISTANCE_SQ )
|
||||
break;
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self.charge_tracking_enemy = false;
|
||||
}
|
||||
|
||||
play_stop_anim( anim_index )
|
||||
{
|
||||
FORWARD_CLEARANCE = 120;
|
||||
|
||||
if ( hit_geo( FORWARD_CLEARANCE ) )
|
||||
go_hit_geo();
|
||||
else
|
||||
self maps\mp\agents\_scriptedagents::SafelyPlayAnimNAtRateUntilNotetrack( "charge_attack_stop", anim_index, 1.0, "charge_attack_stop", "end", ::chargeEndNotetrackHandler );
|
||||
}
|
||||
|
||||
go_hit_geo()
|
||||
{
|
||||
hit_geo_index = get_hit_geo_index();
|
||||
hit_geo_anim = self GetAnimEntry( "charge_hit_geo", hit_geo_index );
|
||||
notetrack_time = GetNotetrackTimes( hit_geo_anim, "forward_end" );
|
||||
forward_delta = length( GetMoveDelta( hit_geo_anim, 0.0, notetrack_time[ 0 ] ) );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( hit_geo( forward_delta ) )
|
||||
break;
|
||||
|
||||
common_scripts\utility::waitframe();
|
||||
}
|
||||
self maps\mp\agents\_scriptedagents::SafelyPlayAnimNAtRateUntilNotetrack( "charge_hit_geo", hit_geo_index, 1.0, "charge_hit_geo", "end", ::chargeEndNotetrackHandler );
|
||||
}
|
||||
|
||||
watch_charge_hit( enemy, anim_index )
|
||||
{
|
||||
self endon( "death" );
|
||||
enemy endon( "death" );
|
||||
|
||||
MIN_CHARGE_TIME = 3.0;
|
||||
MAX_CHARGE_TIME = 6.0;
|
||||
FRAME_TIME = 0.05;
|
||||
|
||||
chargeStopAnim = self GetAnimEntry( "charge_attack_stop", anim_index );
|
||||
num_loops = int( randomFloatRange( MIN_CHARGE_TIME, MAX_CHARGE_TIME ) / FRAME_TIME );
|
||||
animDistance = Length( GetMoveDelta( chargeStopAnim ) );
|
||||
animLength = GetAnimLength( chargeStopAnim );
|
||||
shortLookAheadDistance = (animDistance / animLength) * FRAME_TIME * 3;
|
||||
|
||||
for ( i = 0; i < num_loops; i++ )
|
||||
{
|
||||
if ( hit_player() )
|
||||
return "success";
|
||||
|
||||
if ( self.charge_tracking_enemy )
|
||||
lookAheadDistance = Distance( enemy.origin, self.origin);
|
||||
else
|
||||
lookAheadDistance = shortlookAheadDistance;
|
||||
|
||||
if ( hit_geo( lookAheadDistance ) )
|
||||
return "fail";
|
||||
|
||||
if ( !self.charge_tracking_enemy && missed_enemy( enemy ) )
|
||||
return "fail";
|
||||
|
||||
common_scripts\utility::waitframe();
|
||||
}
|
||||
return "fail"; //time out
|
||||
}
|
||||
|
||||
ALIEN_ELITE_CHARGE_IMPULSE = 1200;
|
||||
|
||||
hit_player()
|
||||
{
|
||||
CHARGE_HIT_DIST = 140;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( distanceSquared ( self.origin, player.origin ) < CHARGE_HIT_DIST * CHARGE_HIT_DIST
|
||||
&& might_hit_enemy( player )
|
||||
)
|
||||
{
|
||||
self maps\mp\agents\alien\_alien_melee::melee_DoDamage( player, "charge" );
|
||||
player player_fly_back( ALIEN_ELITE_CHARGE_IMPULSE, vectorNormalize( player.origin - self.origin ));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
hit_geo( lookAheadDistance )
|
||||
{
|
||||
OFFSET_HEIGHT = 18.0;
|
||||
COS_30 = 0.866;
|
||||
|
||||
traceStart = self.origin + ( 0, 0, OFFSET_HEIGHT );
|
||||
traceEnd = traceStart + AnglesToForward(self.angles ) * lookAheadDistance;
|
||||
|
||||
hitInfo = self AIPhysicsTrace( traceStart, traceEnd, self.radius, self.height - OFFSET_HEIGHT, true, true );
|
||||
return hitInfo["fraction"] < 1.0 && hitInfo["normal"][2] < COS_30;
|
||||
}
|
||||
|
||||
player_fly_back( impulse, direction )
|
||||
{
|
||||
MAX_SPEED = 600.0;
|
||||
original_velocity = self GetVelocity();
|
||||
impluse_velocity = direction * impulse;
|
||||
|
||||
final_velocity = ( original_velocity + impluse_velocity ) * ( 1, 1, 0 );
|
||||
speed = Length( final_velocity );
|
||||
|
||||
if ( speed >= 400.0 )
|
||||
{
|
||||
final_velocity = VectorNormalize( final_velocity ) * 400.0;
|
||||
}
|
||||
|
||||
self SetVelocity( final_velocity );
|
||||
}
|
||||
|
||||
might_hit_enemy( enemy )
|
||||
{
|
||||
CONE_LIMIT = 0.866; //cos( 30 )
|
||||
|
||||
can_see_enemy = can_see_enemy( enemy );
|
||||
|
||||
self_to_enemy = vectorNormalize ( enemy.origin - self.origin );
|
||||
self_forward = anglesToForward( self.angles);
|
||||
enemy_in_front_cone = VectorDot( self_to_enemy, self_forward ) > CONE_LIMIT;
|
||||
|
||||
return ( can_see_enemy && enemy_in_front_cone );
|
||||
}
|
||||
|
||||
missed_enemy( enemy )
|
||||
{
|
||||
pastEnemyDistance = -256;
|
||||
can_see_enemy = can_see_enemy( enemy );
|
||||
|
||||
if ( !can_see_enemy )
|
||||
return true;
|
||||
|
||||
self_to_enemy = enemy.origin - self.origin;
|
||||
self_forward = anglesToForward( self.angles);
|
||||
distancePast = VectorDot( self_to_enemy, self_forward );
|
||||
|
||||
if ( distancePast > 0 )
|
||||
return false;
|
||||
|
||||
return distancePast < pastEnemyDistance;
|
||||
}
|
||||
|
||||
being_charged()
|
||||
{
|
||||
return ( isDefined( self.being_charged ) && self.being_charged );
|
||||
}
|
||||
|
||||
get_charge_start_index()
|
||||
{
|
||||
animWeights = [ 40 /*Entry 0: ex. alien_queen_charge_start*/,
|
||||
30 /*Entry 1: ex. alien_queen_charge_start_v2*/,
|
||||
30 /*Entry 2: ex. alien_queen_charge_start_v3*/
|
||||
];
|
||||
return get_weighted_index( "charge_attack_start", animWeights );
|
||||
}
|
||||
|
||||
get_hit_geo_index()
|
||||
{
|
||||
animWeights = [ 15 /*alien_drone_run_bump_heavy*/,
|
||||
25 /*alien_drone_run_bump_medium*/,
|
||||
60 /*alien_drone_run_bump_light*/
|
||||
];
|
||||
return get_weighted_index( "charge_hit_geo", animWeights );
|
||||
}
|
||||
|
||||
get_weighted_index( animState, animWeights )
|
||||
{
|
||||
nEntries = self GetAnimEntryCount( animState );
|
||||
assert( animWeights.size == nEntries );
|
||||
return maps\mp\alien\_utility::GetRandomIndex( animWeights );
|
||||
}
|
||||
|
||||
load_queen_fx()
|
||||
{
|
||||
level._effect[ "queen_shield_impact" ] = Loadfx( "fx/impacts/large_metalhit_1" );
|
||||
level._effect[ "queen_ground_spawn" ] = LoadFX( "vfx/gameplay/alien/vfx_alien_elite_ground_spawn" );
|
||||
}
|
||||
|
||||
elite_init()
|
||||
{
|
||||
self.next_health_regen_time = getTime();
|
||||
self.last_charge_time = gettime();
|
||||
if ( !isPlayingSolo() )
|
||||
{
|
||||
self.elite_angered = true;
|
||||
self.moveplaybackrate = 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
activate_angered_state()
|
||||
{
|
||||
prepare_to_regenerate();
|
||||
|
||||
CONST_HEALTH_REGEN_TIME = 10.0; // in sec
|
||||
CONST_HEALTH_REGEN_COOL_DOWN = 60000; // in ms
|
||||
|
||||
self.elite_angered = true; // Regen is now an "angered" state
|
||||
self.moveplaybackrate = 1.2;
|
||||
|
||||
activate_health_regen_shield();
|
||||
}
|
||||
|
||||
activate_health_regen()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
prepare_to_regenerate();
|
||||
|
||||
CONST_HEALTH_REGEN_TIME = 10.0; // in sec
|
||||
CONST_HEALTH_REGEN_COOL_DOWN = 60000; // in ms
|
||||
|
||||
self.next_health_regen_time = getTime() + CONST_HEALTH_REGEN_COOL_DOWN;
|
||||
|
||||
thread play_health_regen_anim();
|
||||
|
||||
activate_health_regen_shield();
|
||||
thread queen_health_regen( CONST_HEALTH_REGEN_TIME );
|
||||
|
||||
self common_scripts\utility::waittill_any_timeout( CONST_HEALTH_REGEN_TIME, "stop_queen_health_regen" );
|
||||
|
||||
disable_health_regen_shield();
|
||||
}
|
||||
|
||||
ALIEN_ELITE_REGEN_IMPULSE_RADIUS = 200;
|
||||
ALIEN_ELITE_REGEN_IMPULSE = 800;
|
||||
|
||||
prepare_to_regenerate()
|
||||
{
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
maps\mp\agents\_scriptedagents::PlayAnimNAtRateUntilNotetrack( "prepare_to_regen", 0, 2.0, "prepare_to_regen", "end" );
|
||||
|
||||
// TODO: Play the FX off of a notetrack when we have the real prepare_to_regen anim
|
||||
// maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "prepare_to_regen", "prepare_to_regen", "impulse", ::handle_pre_regen_notetracks );
|
||||
// PlayFX( level._effect[ "queen_regen_AoE" ], self.origin, AnglesToForward( self.angles ), AnglesToUp( self.angles ) );
|
||||
|
||||
min_damage = level.alien_types[ self.alien_type ].attributes[ "explode_min_damage" ];
|
||||
max_damage = level.alien_types[ self.alien_type ].attributes[ "explode_max_damage" ];
|
||||
|
||||
if ( IsDefined( self.elite_angered ) )
|
||||
{
|
||||
min_damage *= get_angered_damage_scalar();
|
||||
max_damage *= get_angered_damage_scalar();
|
||||
}
|
||||
area_damage_and_impulse( ALIEN_ELITE_REGEN_IMPULSE_RADIUS, min_damage, max_damage, ALIEN_ELITE_REGEN_IMPULSE );
|
||||
}
|
||||
|
||||
play_health_regen_anim()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "stop_queen_health_regen" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
anim_state = "regen";
|
||||
|
||||
while ( true )
|
||||
{
|
||||
maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( anim_state, anim_state, "end" );
|
||||
}
|
||||
}
|
||||
|
||||
queen_health_regen( CONST_HEALTH_REGEN_TIME )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "stop_queen_health_regen" );
|
||||
|
||||
CONST_HEALTH_REGEN_INTERVAL = 1.0; // in sec
|
||||
|
||||
num_of_regen = int ( CONST_HEALTH_REGEN_TIME / CONST_HEALTH_REGEN_INTERVAL );
|
||||
total_health_to_regen = ( self.maxhealth - self.health ) / 2; // regen only to up the midpoint between current health and max health
|
||||
health_each_regen = int ( total_health_to_regen / num_of_regen );
|
||||
|
||||
for ( i = 0; i < num_of_regen; i++ )
|
||||
{
|
||||
wait ( CONST_HEALTH_REGEN_INTERVAL );
|
||||
self.health += health_each_regen;
|
||||
}
|
||||
}
|
||||
|
||||
activate_health_regen_shield()
|
||||
{
|
||||
/*self.shield_model = deploy_health_regen_shield();
|
||||
self.shield_FX = PlayLoopedFX ( level._effect[ "queen_shield" ], 10.0, self.origin,0, AnglesToForward(self.angles), (0,0,1) );
|
||||
|
||||
self.shield_model thread clean_up_on_owner_death( self );
|
||||
self.shield_FX thread clean_up_on_owner_death( self );*/
|
||||
}
|
||||
|
||||
disable_health_regen_shield()
|
||||
{
|
||||
self SetScriptablePartState( "body", "normal" );
|
||||
/*
|
||||
self.shield_model delete();
|
||||
self.shield_FX delete();*/
|
||||
}
|
||||
|
||||
clean_up_on_owner_death( owner )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
owner endon ( "stop_queen_health_regen" );
|
||||
|
||||
owner waittill ( "death" );
|
||||
self delete();
|
||||
}
|
||||
|
||||
deploy_health_regen_shield()
|
||||
{
|
||||
shield = spawn ( "script_model", self.origin );
|
||||
shield setModel ( "alien_shield_bubble_distortion" );
|
||||
shield linkTo ( self, "tag_origin" );
|
||||
shield setCanDamage ( true );
|
||||
|
||||
return shield;
|
||||
}
|
||||
|
||||
//<TODO JC> Remove this as the impact effect will eventually be played from the character model's surface type
|
||||
play_shield_impact_fx( vPoint, vDir )
|
||||
{
|
||||
if ( isDefined ( vDir ) )
|
||||
forward_vector = vDir * -1;
|
||||
else
|
||||
forward_vector = anglesToForward( self.angles );
|
||||
|
||||
up_vector = anglesToUp ( vectorToAngles ( forward_vector ) );
|
||||
PlayFX( level._effect[ "queen_shield_impact" ], vPoint, forward_vector, up_vector );
|
||||
}
|
||||
|
||||
ALIEN_ELITE_EXPLOSIVE_RESISTANCE = 0.5;
|
||||
|
||||
eliteDamageProcessing( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
// Explosive resistance
|
||||
switch ( sMeansOfDeath )
|
||||
{
|
||||
case "MOD_EXPLOSIVE":
|
||||
case "MOD_GRENADE_SPLASH":
|
||||
case "MOD_GRENADE":
|
||||
case "MOD_PROJECTILE":
|
||||
case "MOD_PROJECTILE_SPLASH":
|
||||
iDamage *= ALIEN_ELITE_EXPLOSIVE_RESISTANCE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return iDamage;
|
||||
}
|
||||
|
||||
ALIEN_ELITE_JUMP_IMPULSE = 500;
|
||||
|
||||
on_jump_impact()
|
||||
{
|
||||
DAMAGE_RADIUS = 256;
|
||||
MAX_DAMAGE = 30;
|
||||
MIN_DAMAGE = 10;
|
||||
|
||||
alienUp = anglesToUp( self.angles );
|
||||
if ( !maps\mp\alien\_utility::is_normal_upright( alienUp ) )
|
||||
return;
|
||||
|
||||
area_damage_and_impulse( DAMAGE_RADIUS, MIN_DAMAGE, MAX_DAMAGE, ALIEN_ELITE_JUMP_IMPULSE );
|
||||
}
|
||||
|
||||
area_damage_and_impulse( damage_radius, min_damage, max_damage, impulse )
|
||||
{
|
||||
RadiusDamage( self.origin, damage_radius, max_damage, min_damage, self, "MOD_EXPLOSIVE", "alienrhinoslam_mp" );
|
||||
damage_radius_squared = damage_radius * damage_radius;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( DistanceSquared( self.origin, player.origin ) > damage_radius_squared )
|
||||
continue;
|
||||
|
||||
pushDirection = VectorNormalize(player.origin - self.origin );
|
||||
player player_fly_back( impulse, pushDirection );
|
||||
}
|
||||
}
|
||||
|
||||
get_angered_damage_scalar()
|
||||
{
|
||||
return ANGERED_DAMAGE_SCALAR;
|
||||
}
|
||||
|
||||
chargeStartNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "queen_roll_start":
|
||||
self playLoopSound( "queen_roll" );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chargeEndNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "queen_roll_stop":
|
||||
self stopLoopSound( "queen_roll" );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
199
maps/mp/agents/alien/_alien_idle.gsc
Normal file
199
maps/mp/agents/alien/_alien_idle.gsc
Normal file
@@ -0,0 +1,199 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include common_scripts\utility;
|
||||
|
||||
MIN_IDLE_REPEAT_TIMES = 2;
|
||||
MAX_POSTURE_REPEAT_TIMES = 2;
|
||||
IDLE_POSTURE_VOICE = "alien_voice";
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self init_alien_idle();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( IsDefined( self.attractor_flare ) )
|
||||
{
|
||||
play_attractor_idle();
|
||||
}
|
||||
else if ( IsDefined( self.enemy_downed ) && self.enemy_downed )
|
||||
{
|
||||
play_enemy_downed_idle();
|
||||
|
||||
if ( level.gameEnded )
|
||||
self.enemy_downed = false; // Make sure play_enemy_downed_idle() is played only once when game ends
|
||||
}
|
||||
else
|
||||
{
|
||||
play_idle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_alien_idle()
|
||||
{
|
||||
self.idle_anim_counter = 0;
|
||||
self.consecutive_posture_counter = 0;
|
||||
if ( isDefined( self.xyanimscale ) )
|
||||
self ScrAgentSetAnimScale( self.xyanimscale, 1.0 );
|
||||
|
||||
if ( IsDefined( self.idle_state_locked ) && self.idle_state_locked )
|
||||
self.stateLocked = true;
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.previousAnimState = "idle";
|
||||
if ( IsDefined( self.idle_state_locked ) && self.idle_state_locked )
|
||||
{
|
||||
self.stateLocked = false;
|
||||
self.idle_state_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
play_enemy_downed_idle()
|
||||
{
|
||||
self faceTarget();
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "posture", "posture", "end" );
|
||||
}
|
||||
|
||||
play_attractor_idle()
|
||||
{
|
||||
facingAngles = VectorToAngles( self.attractor_flare.origin - self.origin );
|
||||
facingAngles = ( self.angles[0], facingAngles[1], self.angles[2] );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", facingAngles );
|
||||
attactorIndex = GetRandomAnimEntry( "idle_flare" );
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimNUntilNotetrack( "idle_flare", attactorIndex, "idle_flare", "end" );
|
||||
}
|
||||
|
||||
play_idle()
|
||||
{
|
||||
self faceTarget();
|
||||
idleState = selectIdleAnimState();
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self PlayAnimNUntilNotetrack( idleState, undefined, idleState, "end" );
|
||||
}
|
||||
|
||||
selectIdleAnimState()
|
||||
{
|
||||
if( isDefined( level.dlc_idle_anim_state_override_func ))
|
||||
{
|
||||
animState = self [[level.dlc_idle_anim_state_override_func]]( self.enemy );
|
||||
if ( IsDefined( animState ) )
|
||||
return animState;
|
||||
}
|
||||
|
||||
if ( isAlive( self.enemy ) )
|
||||
{
|
||||
// Possibly posture
|
||||
if ( cointoss() && self.consecutive_posture_counter < MAX_POSTURE_REPEAT_TIMES )
|
||||
{
|
||||
// <TODO J.C.> Need to move this into notetrack
|
||||
//self PlaySound( IDLE_POSTURE_VOICE );
|
||||
self.consecutive_posture_counter++;
|
||||
return "idle_posture";
|
||||
}
|
||||
}
|
||||
|
||||
self.consecutive_posture_counter = 0;
|
||||
|
||||
if ( self.idle_anim_counter < MIN_IDLE_REPEAT_TIMES + RandomIntRange ( 0, 1 ) )
|
||||
{
|
||||
resultState = "idle_default";
|
||||
self.idle_anim_counter += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultState = "idle";
|
||||
self.idle_anim_counter = 0;
|
||||
}
|
||||
|
||||
return resultState;
|
||||
}
|
||||
|
||||
faceTarget()
|
||||
{
|
||||
faceTarget = undefined;
|
||||
if ( IsAlive( self.enemy ) && DistanceSquared( self.enemy.origin, self.origin ) < 1600 * 1600 )
|
||||
faceTarget = self.enemy;
|
||||
else if ( IsDefined( self.owner ) )
|
||||
faceTarget = self.owner;
|
||||
|
||||
if ( IsDefined( faceTarget ) )
|
||||
{
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( faceTarget );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onDamage( eInflictor, eAttacker, iThatDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( IsDefined( level.dlc_can_do_pain_override_func ) )
|
||||
{
|
||||
painAllowed = [[level.dlc_can_do_pain_override_func]]( "idle" );
|
||||
if ( !painAllowed )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( maps\mp\alien\_utility::is_pain_available( eAttacker,sMeansOfDeath ) )
|
||||
self DoPain( iDFlags, vDir, sHitLoc, iThatDamage, sMeansOfDeath );
|
||||
}
|
||||
|
||||
DoPain( iDFlags, damageDirection, hitLocation, iDamage, sMeansOfDeath )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
is_stun = ( iDFlags & level.iDFLAGS_STUN );
|
||||
|
||||
if ( sMeansOfDeath == "MOD_MELEE" || is_stun )
|
||||
{
|
||||
animState = "pain_pushback";
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "push_back", damageDirection );
|
||||
pain_notify = "pain_pushback";
|
||||
}
|
||||
else
|
||||
{
|
||||
baseAnimState = getBasePainAnimState();
|
||||
animState = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimState( baseAnimState, iDamage, is_stun );
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "idle", damageDirection, hitLocation );
|
||||
pain_notify = "idle_pain";
|
||||
}
|
||||
|
||||
anime = self GetAnimEntry( animState, animIndex );
|
||||
self maps\mp\alien\_utility::always_play_pain_sound( anime );
|
||||
self maps\mp\alien\_utility::register_pain( anime );
|
||||
self.stateLocked = true;
|
||||
|
||||
if ( IsDefined( self.oriented ) && self.oriented )
|
||||
{
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
}
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, animIndex, pain_notify );
|
||||
|
||||
if ( !isdefined(self.idle_state_locked) || !self.idle_state_locked )
|
||||
self.stateLocked = false;
|
||||
|
||||
self SetAnimState( "idle" );
|
||||
}
|
||||
|
||||
getBasePainAnimState()
|
||||
{
|
||||
if ( IsDefined( level.dlc_alien_pain_anim_state_override_func ) )
|
||||
{
|
||||
animState = [[level.dlc_alien_pain_anim_state_override_func]]( "idle" );
|
||||
if ( IsDefined( animState ) )
|
||||
return animState;
|
||||
}
|
||||
|
||||
return "idle_pain";
|
||||
}
|
||||
776
maps/mp/agents/alien/_alien_jump.gsc
Normal file
776
maps/mp/agents/alien/_alien_jump.gsc
Normal file
@@ -0,0 +1,776 @@
|
||||
//
|
||||
// _alien_jump.gsc
|
||||
//
|
||||
// Common jump functionality.
|
||||
// Jump is called from multiple states. It does not correspond to any particular anim state.
|
||||
//
|
||||
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
Jump( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName )
|
||||
{
|
||||
|
||||
maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( endPos - startPos );
|
||||
oldTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
self thread JumpInternal( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName );
|
||||
self waittill( "jump_finished" );
|
||||
self JumpCleanup( oldTurnRate, endAngles );
|
||||
}
|
||||
|
||||
JumpCleanup( oldTurnRate, endAngles )
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
self ScrAgentSetMaxTurnSpeed( oldTurnRate );
|
||||
if ( self maps\mp\alien\_utility::is_normal_upright( AnglesToUp( endAngles ) ) )
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self.oriented = false;
|
||||
self.ignoreme = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self.oriented = true;
|
||||
self.ignoreme = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
JumpInternal( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon ("killanimscript" );
|
||||
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( endPos - startPos );
|
||||
|
||||
if ( isDefined ( scriptableName ) )
|
||||
maps\mp\agents\alien\_alien_anim_utils::resetScriptable( scriptableName, endPos );
|
||||
|
||||
self.trajectoryActive = false;
|
||||
|
||||
// figure out initial jump stuff
|
||||
jumpAnimStates = SpawnStruct();
|
||||
|
||||
jumpInfo = self GetJumpInfo( startPos, startAngles, endPos, endAngles, nextPos );
|
||||
|
||||
self GetJumpAnimStates( jumpInfo, jumpAnimStates );
|
||||
|
||||
if ( IsDefined( jumpCBs ) && IsDefined( jumpCBs.fnSetAnimStates ) )
|
||||
self [[ jumpCBs.fnSetAnimStates ]]( jumpInfo, jumpAnimStates );
|
||||
|
||||
anglesToEnd = GetJumpStartAngles( startPos, startAngles, endPos );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToEnd );
|
||||
|
||||
t = 0;
|
||||
|
||||
beginAnim = self GetAnimEntry( jumpAnimStates.launchAnimState, jumpAnimStates.launchAnimEntry );
|
||||
|
||||
/////////////
|
||||
// calculate the landing point such that the final position is at our goal
|
||||
endAnim = self GetAnimEntry( jumpAnimStates.landAnimState, jumpAnimStates.landAnimEntry );
|
||||
|
||||
endFinish = GetNotetrackTimes( endAnim, "finish" );
|
||||
if ( endFinish.size > 0 )
|
||||
{
|
||||
endAnimLength = endFinish[0] * GetAnimLength( endAnim );
|
||||
}
|
||||
else
|
||||
{
|
||||
endAnimLength = GetAnimLength( endAnim );
|
||||
}
|
||||
|
||||
endAnimTime = endAnimLength / jumpAnimStates.playbackRate;
|
||||
|
||||
endLandMoveFrame = floor( endAnimTime * 20.0 );
|
||||
endLandMoveTime = (endLandMoveFrame / 20.0) / endAnimTime;
|
||||
|
||||
endLand = GetNotetrackTimes( endAnim, "stop_teleport" );
|
||||
if ( endLand.size > 0 )
|
||||
{
|
||||
endLandTime = endLand[0] * endAnimTime;
|
||||
beginLandMoveFrame = ceil( endLandTime * 20.0 );
|
||||
beginLandMoveTime = (beginLandMoveFrame / 20.0) / endAnimTime;
|
||||
|
||||
endLandMove = GetMoveDelta( endAnim, beginLandMoveTime, endLandMoveTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
endLandTime = 0.8 * endAnimTime;
|
||||
beginLandMoveFrame = ceil( endLandTime * 20.0 );
|
||||
beginLandMoveTime = (beginLandMoveFrame / 20.0) / endAnimTime;
|
||||
|
||||
endLandMove = GetMoveDelta( endAnim, beginLandMoveTime, endLandMoveTime );
|
||||
}
|
||||
|
||||
endAngles = GetJumpEndAngles( startPos, endPos, endAngles );
|
||||
endLandMoveRot = RotateVector( endLandMove, endAngles );
|
||||
endLandOrigin = endPos - endLandMoveRot;
|
||||
|
||||
/////////////
|
||||
// jump begin
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self PlaySoundOnMovingEnt( get_jump_SFX_alias() );
|
||||
|
||||
if ( AnimHasNotetrack( beginAnim, "start_teleport" ) )
|
||||
{
|
||||
self PlayAnimNAtRateUntilNotetrack(jumpAnimStates.launchAnimState,
|
||||
jumpAnimStates.launchAnimEntry,
|
||||
jumpAnimStates.playbackRate,
|
||||
"jump_launch",
|
||||
"start_teleport" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayAnimNAtRateForTime(jumpAnimStates.launchAnimState,
|
||||
jumpAnimStates.launchAnimEntry,
|
||||
jumpAnimStates.playabackRate,
|
||||
0.5 * GetAnimLength( beginAnim ) / jumpAnimStates.playbackRate );
|
||||
}
|
||||
|
||||
/////////////
|
||||
// do the trajectory
|
||||
startTime = gettime();
|
||||
t = self ScrAgentDoTrajectory( self.origin, endLandOrigin, jumpInfo.jumpSpeed2D );
|
||||
self.trajectoryActive = true;
|
||||
|
||||
/////////////
|
||||
// Handle pain
|
||||
self endon( "jump_pain_interrupt" );
|
||||
self thread JumpPain( t, endPos );
|
||||
|
||||
self notify( "jump_launching" ); // for cloaker jump
|
||||
|
||||
/////////////
|
||||
// orient the agent to the plane of the landing position
|
||||
oldTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
self thread jumpOrient( jumpInfo, endAngles, oldTurnRate, t );
|
||||
|
||||
/////////////
|
||||
// finish the launch animation
|
||||
self WaitUntilNotetrack( "jump_launch", "end" );
|
||||
beginTime = ( gettime() - startTime ) / 1000;
|
||||
|
||||
|
||||
/////////////
|
||||
// calculate the time to spend in the air
|
||||
loopTime = t - beginTime - endLandTime;
|
||||
|
||||
/////////////
|
||||
// jump loop
|
||||
|
||||
if ( loopTime > 0 )
|
||||
{
|
||||
self PlayAnimNAtRateForTime(jumpAnimStates.inAirAnimState,
|
||||
jumpAnimStates.inAirAnimEntry,
|
||||
jumpAnimStates.playbackRate,
|
||||
loopTime );
|
||||
}
|
||||
|
||||
/////////////
|
||||
// jump land
|
||||
|
||||
// Allow a last minute animState change
|
||||
if ( IsDefined( jumpCBs ) && IsDefined( jumpCBs.fnLandAnimStateChoice ) )
|
||||
self [[ jumpCBs.fnLandAnimStateChoice ]]( jumpInfo, jumpAnimStates );
|
||||
|
||||
self SetAnimState( jumpAnimStates.landAnimState, jumpAnimStates.landAnimEntry, jumpAnimStates.playbackRate );
|
||||
self waittill( "traverse_complete" );
|
||||
self.trajectoryActive = false;
|
||||
|
||||
if ( isDefined ( scriptableName ) )
|
||||
maps\mp\agents\alien\_alien_anim_utils::playAnimOnScriptable( scriptableName, endPos );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 0.0 );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( 20.28318 ); // 2 pi
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
// make sure our rotation ends up at endAngles.
|
||||
// this would be even better if it was accounting for rotating entirely to the
|
||||
// direction of the next negotiation node
|
||||
self ScrAgentSetOrientMode( "face angle abs", endAngles );
|
||||
|
||||
self thread waitForLandImpact( "jump_land" );
|
||||
self WaitUntilNotetrack( "jump_land", "end" );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
/////////////
|
||||
// yay!
|
||||
|
||||
//delta_from_goal = endPos - self.origin;
|
||||
//iprintln( "Jump Delta: " + delta_from_goal );
|
||||
|
||||
self SetOrigin( endPos, false ); // Boo!
|
||||
|
||||
self notify( "jump_finished" );
|
||||
|
||||
}
|
||||
|
||||
waitForLandImpact( animName )
|
||||
{
|
||||
alienType = self maps\mp\alien\_utility::get_alien_type();
|
||||
|
||||
switch( alienType )
|
||||
{
|
||||
case "elite":
|
||||
self WaitUntilNotetrack( animName, "jump_land_impact" );
|
||||
maps\mp\agents\alien\_alien_elite::on_jump_impact();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jumpOrient( jumpInfo, endAngles, oldTurnRate, timeInAir )
|
||||
{
|
||||
self endon( "death" );
|
||||
UPRIGHT_VECTOR = ( 0, 0, 1 );
|
||||
UPRIGHT_DOT = 0.85;
|
||||
startUpright = maps\mp\alien\_utility::is_normal_upright( jumpInfo.startUpVector );
|
||||
endUpright = maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector );
|
||||
|
||||
if ( startUpright && !endUpright )
|
||||
{
|
||||
start_orient_time = 0.5;
|
||||
end_orient_time = 1.0;
|
||||
}
|
||||
else if ( !startUpright && endUpright )
|
||||
{
|
||||
start_orient_time = 0.0;
|
||||
end_orient_time = 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
start_orient_time = 0.0;
|
||||
end_orient_time = 1.0;
|
||||
}
|
||||
|
||||
total_orient_time = end_orient_time - start_orient_time;
|
||||
|
||||
if ( start_orient_time > 0 )
|
||||
{
|
||||
wait ( timeInAir * start_orient_time );
|
||||
}
|
||||
|
||||
threshold = 1.0;
|
||||
if ( DistanceSquared( self.angles, endAngles ) > threshold )
|
||||
{
|
||||
anglesDelta = AnglesDelta( self.angles, endAngles );
|
||||
|
||||
turnRate = anglesDelta / ( timeInAir * total_orient_time );
|
||||
turnRate = turnRate * 3.1415926 / 180.0; // deg to rad
|
||||
turnRate = turnRate / 20; // rads per frame
|
||||
self ScrAgentSetMaxTurnSpeed( turnRate );
|
||||
}
|
||||
self ScrAgentSetOrientMode( "face angle abs", endAngles );
|
||||
}
|
||||
|
||||
GetJumpInfo( startPos, startAngles, endPos, endAngles, nextPos )
|
||||
{
|
||||
jumpInfo = SpawnStruct();
|
||||
|
||||
startToEnd = endPos - startPos;
|
||||
startToEnd2D = startToEnd * ( 1, 1, 0 );
|
||||
startToEnd2D = VectorNormalize( startToEnd2D );
|
||||
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchGroundDelta ), "Jump launch table has not been initialized" );
|
||||
|
||||
jumpInfo.launchOrigin = startPos + startToEnd2D * level.alienAnimData.jumpLaunchGroundDelta;
|
||||
jumpInfo.landOrigin = endPos;
|
||||
|
||||
jumpInfo.jumpVector = jumpInfo.landOrigin - jumpInfo.launchOrigin;
|
||||
jumpInfo.jumpVector2D = jumpInfo.jumpVector * ( 1, 1, 0 );
|
||||
jumpInfo.jumpDistance2D = Length( jumpInfo.jumpVector2D );
|
||||
AssertEx( jumpInfo.jumpDistance2D != 0, "Trying to jump vertically. This is not handled." );
|
||||
|
||||
jumpInfo.jumpDirection2D = jumpInfo.jumpVector2D / jumpInfo.jumpDistance2D;
|
||||
|
||||
if ( IsDefined( nextPos ) )
|
||||
jumpInfo.landVector = nextPos - endPos;
|
||||
else if ( IsDefined( self.enemy ) )
|
||||
jumpInfo.landVector = self.enemy.origin - endPos;
|
||||
else
|
||||
jumpInfo.landVector = AnglesToForward( self.angles );
|
||||
|
||||
jumpInfo.startAngles = GetJumpAngles( jumpInfo.jumpVector, AnglesToUp( startAngles ) );
|
||||
jumpInfo.endAngles = GetJumpAngles( jumpInfo.jumpVector, AnglesToUp( endAngles ) );
|
||||
|
||||
jumpInfo.startUpVector = AnglesToUp( jumpInfo.startAngles );
|
||||
jumpInfo.endUpVector = AnglesToUp( jumpInfo.endAngles );
|
||||
|
||||
GetJumpVelocity( jumpInfo );
|
||||
|
||||
return jumpInfo;
|
||||
}
|
||||
|
||||
GetJumpAngles( jumpVector, vUp )
|
||||
{
|
||||
forwardVector = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpVector, vUp );
|
||||
right = VectorCross( forwardVector, vUp );
|
||||
angles = AxisToAngles( forwardVector, right, vUp );
|
||||
return angles;
|
||||
}
|
||||
|
||||
//GetLaunchAngle( speed, gravity, x, y )
|
||||
//{
|
||||
// // From: http://en.wikipedia.org/wiki/Trajectory_of_a_projectile
|
||||
// val = speed * speed * speed * speed - gravity * ( gravity * x * x + 2 * y * speed * speed );
|
||||
// AssertEx ( val >= 0, "The given velocity is unable to reach the target. Increase the velocity." );
|
||||
//
|
||||
// //requiredAngleHigh = ATan( ( speed * speed + Sqrt( val ) ) / ( gravity * x ) );
|
||||
// requiredAngleLow = ATan( ( speed * speed - Sqrt( val ) ) / ( gravity * x ) );
|
||||
//
|
||||
// return requiredAngleLow;
|
||||
//}
|
||||
|
||||
//GetMinimumLaunchSpeed( gravity, x, y )
|
||||
//{
|
||||
// // From: http://en.wikipedia.org/wiki/Range_of_a_projectile
|
||||
// // This code calculates the minimum speed required to reach a point by assuming
|
||||
// // the point being reached is the maximum range of the projectile fired at the
|
||||
// // desired velocity. The result is derived from the equation for the maximum
|
||||
// // range of a projectile: Range = ( vel / g ) * sqrt( vel ^ 2 + 2 * g * height )
|
||||
// a = 2 * gravity * y;
|
||||
// b = gravity * gravity * x * x;
|
||||
//
|
||||
// result = Sqrt( (a + Sqrt( a * a + 4 * b ) ) / 2 );
|
||||
//
|
||||
// return result;
|
||||
//}
|
||||
|
||||
GetJumpVelocity( jumpInfo )
|
||||
{
|
||||
x = jumpInfo.jumpDistance2D;
|
||||
y = jumpInfo.jumpVector[ 2 ];
|
||||
isWallJump = !maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector );
|
||||
g = GetJumpGravity( isWallJump );
|
||||
MIN_JUMP_SPEED_MULTIPLIER = 1.01;
|
||||
|
||||
minJumpSpeed = TrajectoryCalculateMinimumVelocity( jumpInfo.launchOrigin, jumpInfo.landOrigin, g );//GetMinimumLaunchSpeed( g, x, y );
|
||||
jumpSpeedMultiplier = GetJumpSpeedMultiplier( isWallJump );
|
||||
jumpSpeed = minJumpSpeed * MIN_JUMP_SPEED_MULTIPLIER * jumpSpeedMultiplier;
|
||||
AssertEx( jumpSpeed != 0, "Trying to jump but the jump doesn't go anywhere." );
|
||||
|
||||
jumpAngle = TrajectoryCalculateExitAngle( jumpSpeed, g, x, y );//GetLaunchAngle( jumpSpeed, g, x, y );
|
||||
|
||||
jumpAngleCos = Cos( jumpAngle );
|
||||
AssertEx( jumpAngleCos != 0, "Trying to jump vertically. This is not handled." );
|
||||
|
||||
jumpInfo.jumpTime = jumpInfo.jumpDistance2D / ( jumpSpeed * jumpAngleCos );
|
||||
|
||||
gravityVector = g * ( 0, 0, -1 );
|
||||
jumpInfo.launchVelocity = TrajectoryCalculateInitialVelocity( jumpInfo.launchOrigin, jumpInfo.landOrigin, gravityVector, jumpInfo.jumpTime ); //(jumpInfo.jumpVector - 0.5 * gravityVector * jumpInfo.jumpTime * jumpInfo.jumpTime) / jumpInfo.jumpTime;
|
||||
jumpInfo.launchVelocity2D = jumpInfo.launchVelocity * ( 1, 1, 0 );
|
||||
jumpInfo.jumpSpeed2D = Length( jumpInfo.launchVelocity2D );
|
||||
}
|
||||
|
||||
GetJumpSpeedMultiplier( is_wall_jump )
|
||||
{
|
||||
if ( IsDefined( self.melee_jumping ) && self.melee_jumping )
|
||||
{
|
||||
AssertEx( IsDefined( level.alien_jump_melee_speed, "Alien jump speed is not defined" ) );
|
||||
return level.alien_jump_melee_speed;
|
||||
}
|
||||
else if ( is_wall_jump )
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpWallSpeed" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpSpeed" );
|
||||
}
|
||||
}
|
||||
|
||||
GetJumpGravity( is_wall_jump )
|
||||
{
|
||||
if ( IsDefined( self.melee_jumping ) && self.melee_jumping )
|
||||
{
|
||||
AssertEx( IsDefined( level.alien_jump_melee_gravity, "Alien jump gravity is not defined" ) );
|
||||
return level.alien_jump_melee_gravity;
|
||||
}
|
||||
else if ( is_wall_jump )
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpWallGravity" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpGravity" );
|
||||
}
|
||||
}
|
||||
|
||||
GetJumpPlaybackRate( jumpInfo, animStates )
|
||||
{
|
||||
AssertEx( jumpInfo.jumpTime != 0);
|
||||
|
||||
launchAnim = self GetAnimEntry( animStates.launchAnimState, animStates.launchAnimEntry );
|
||||
inAirAnim = self GetAnimEntry( animStates.inAirAnimState, animStates.inAirAnimEntry );
|
||||
landAnim = self GetAnimEntry( animStates.landAnimState, animStates.landAnimEntry );
|
||||
|
||||
launchAnimLength = GetAnimLength( launchAnim );
|
||||
launchAnimInAirTime = launchAnimLength * 0.5;
|
||||
|
||||
launchAnimTakeoff = GetNotetrackTimes( launchAnim, "start_teleport" );
|
||||
if ( IsDefined( launchAnimTakeoff ) && launchAnimTakeoff.size > 0 )
|
||||
launchAnimInAirTime = launchAnimLength - launchAnimTakeoff[0] * launchAnimLength;
|
||||
Assert( launchAnimInAirTime < launchAnimLength );
|
||||
|
||||
landAnimLength = GetAnimLength( landAnim );
|
||||
landAnimInAirTime = landAnimLength * 0.5;
|
||||
|
||||
landAnimArrive = GetNotetrackTimes( landAnim, "stop_teleport" );
|
||||
if ( IsDefined( landAnimArrive ) && landAnimArrive.size > 0 )
|
||||
landAnimInAirTime = landAnimArrive[0] * landAnimLength;
|
||||
Assert( landAnimInAirTime < landAnimLength );
|
||||
|
||||
inAirAnimLength = GetAnimLength( inAirAnim );
|
||||
Assert( inAirAnimLength > 0 );
|
||||
|
||||
// calculate how long the physics needs to run and round up to the nearest frame
|
||||
// this ensures that the trajectory finishes before we move out of the trajectory
|
||||
// anim mode
|
||||
trajectoryFrameCount = ceil( jumpInfo.jumpTime * 20.0 );
|
||||
trajectoryPhysicsTime = trajectoryFrameCount / 20.0;
|
||||
|
||||
// calculate the amount to scale all animations to achieve the required time in the air
|
||||
trajectoryAnimTime = inAirAnimLength + launchAnimInAirTime + landAnimInAirTime;
|
||||
trajectoryAnimScale = trajectoryAnimTime / trajectoryPhysicsTime;
|
||||
|
||||
// add a two frames of trim since the in air animation will play for a fixed time
|
||||
// and that time may straddle a frame boundary on both ends
|
||||
inAirAnimTime = inAirAnimLength / trajectoryAnimScale + 0.1;
|
||||
inAirAnimScale = inAirAnimLength / inAirAnimTime;
|
||||
|
||||
return inAirAnimScale;
|
||||
}
|
||||
|
||||
GetJumpAnimStates( jumpInfo, animStates )
|
||||
{
|
||||
animStates.launchAnimState = GetLaunchAnimState( jumpInfo );
|
||||
animStates.launchAnimEntry = GetLaunchAnimEntry( jumpInfo, animStates.launchAnimState );
|
||||
|
||||
animStates.landAnimState = GetLandAnimState( jumpInfo );
|
||||
animStates.landAnimEntry = GetLandAnimEntry( jumpInfo, animStates.landAnimState );
|
||||
|
||||
animStates.inAirAnimState = GetInAirAnimState( jumpInfo, animStates.launchAnimState, animStates.landAnimState );
|
||||
animStates.inAirAnimEntry = GetInAirAnimEntry( jumpInfo, animStates.launchAnimState, animStates.landAnimState );
|
||||
|
||||
animStates.playbackRate = self GetJumpPlaybackRate( jumpInfo, animStates );
|
||||
}
|
||||
|
||||
GetJumpStartAngles( startPos, startAngles, endPos )
|
||||
{
|
||||
startUp = AnglesToUp( startAngles );
|
||||
startForward = VectorNormalize( endPos - startPos );
|
||||
if ( VectorDot( startUp, startForward ) > 0.98 )
|
||||
startForward = ( 0, 0, 1 );
|
||||
startLeft = VectorCross( startUp, startForward );
|
||||
startForward = VectorCross( startLeft, startUp );
|
||||
return AxisToAngles( startForward, -1 * startLeft, startUp );
|
||||
}
|
||||
|
||||
GetLaunchAnimState( jumpInfo )
|
||||
{
|
||||
LEVEL_DEGREE_RANGE = 20;
|
||||
cosLimitForLevel = Cos( 90 - LEVEL_DEGREE_RANGE );
|
||||
|
||||
startToEnd = VectorNormalize( jumpInfo.jumpVector );
|
||||
startToEndDotUp = VectorDot( startToEnd, jumpInfo.startUpVector );
|
||||
|
||||
if ( abs( startToEndDotUp ) <= cosLimitForLevel )
|
||||
{
|
||||
return "jump_launch_level";
|
||||
}
|
||||
else if ( startToEndDotUp > 0 )
|
||||
{
|
||||
return "jump_launch_up";
|
||||
}
|
||||
else if ( startToEndDotUp < 0 )
|
||||
{
|
||||
return "jump_launch_down";
|
||||
}
|
||||
}
|
||||
|
||||
GetLaunchAnimEntry( jumpInfo, launchAnimState )
|
||||
{
|
||||
launchDirection = VectorNormalize( jumpInfo.launchVelocity );
|
||||
launchDirection = RotateVector( launchDirection, jumpInfo.startAngles );
|
||||
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection ), "Alien jump table has not been initialized" );
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ] ),
|
||||
"Alien jump table has not been initialized for launch state " + launchAnimState );
|
||||
|
||||
launchEntryCount = self GetAnimEntryCount( launchAnimState );
|
||||
AssertEx( launchEntryCount > 0, "Alien launch state " + launchAnimState + " as no animations." );
|
||||
|
||||
launchEntry = 0;
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ launchEntry ] ),
|
||||
"Alien launch entry " + launchEntry + " for state " + launchAnimState + " has no direction." );
|
||||
|
||||
launchEntryDot = VectorDot( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ launchEntry ], launchDirection );
|
||||
|
||||
for ( nextLaunchEntry = 1; nextLaunchEntry < launchEntryCount; nextLaunchEntry++ )
|
||||
{
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ nextLaunchEntry ] ),
|
||||
"Alien launch entry " + nextLaunchEntry + " for state " + launchAnimState + " has no direction." );
|
||||
|
||||
nextLaunchEntryDot = VectorDot( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ nextLaunchEntry ], launchDirection );
|
||||
if ( nextLaunchEntryDot > launchEntryDot )
|
||||
{
|
||||
launchEntry = nextLaunchEntry;
|
||||
launchEntryDot = nextLaunchEntryDot;
|
||||
}
|
||||
}
|
||||
|
||||
return launchEntry;
|
||||
}
|
||||
|
||||
GetInAirAnimState( jumpInfo, launchAnimState, landAnimState )
|
||||
{
|
||||
return "jump_in_air";
|
||||
}
|
||||
|
||||
GetInAirAnimEntry( jumpInfo, launchAnimState, landAnimState )
|
||||
{
|
||||
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry ), "Alien in air table has not been initialized" );
|
||||
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry[ launchAnimState ] ),
|
||||
"Alien in air table has not been initialized for launch state " + launchAnimState );
|
||||
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry[ launchAnimState ][ landAnimState ] ),
|
||||
"Alien in air table has not been initialized for launch state " + launchAnimState + " and land anim state " + landAnimState );
|
||||
|
||||
return level.alienAnimData.inAirAnimEntry[ launchAnimState ][ landAnimState ];
|
||||
}
|
||||
|
||||
GetJumpEndAngles( startPos, endPos, endAngles )
|
||||
{
|
||||
endUp = AnglesToUp( endAngles );
|
||||
endForward = VectorNormalize( endPos - startPos );
|
||||
if ( VectorDot( endUp, endForward ) > 0.98 )
|
||||
endForward = ( 0, 0, 1 );
|
||||
endLeft = VectorCross( endUp, endForward );
|
||||
endForward = VectorCross( endLeft, endUp );
|
||||
return AxisToAngles( endForward, -1 * endLeft, endUp );
|
||||
}
|
||||
|
||||
GetLandAnimState( jumpInfo )
|
||||
{
|
||||
jumpVectorLength = length( jumpInfo.jumpVector );
|
||||
PITCH_THRESHOLD = 0.342; // sin(20)
|
||||
|
||||
if ( !maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector ) )
|
||||
{
|
||||
WORLD_UP = ( 0, 0, 1 );
|
||||
pitch = VectorDot( jumpInfo.jumpVector, WORLD_UP ) / jumpVectorLength;
|
||||
|
||||
if ( pitch > PITCH_THRESHOLD )
|
||||
{
|
||||
return "jump_land_sidewall_low";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "jump_land_sidewall_high";
|
||||
}
|
||||
}
|
||||
|
||||
pitch = VectorDot( jumpInfo.jumpVector, jumpInfo.endUpVector ) / jumpVectorLength;
|
||||
|
||||
if ( pitch > PITCH_THRESHOLD )
|
||||
{
|
||||
return "jump_land_down";
|
||||
}
|
||||
else if ( pitch < ( PITCH_THRESHOLD * -1 ) )
|
||||
{
|
||||
return "jump_land_up";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "jump_land_level";
|
||||
}
|
||||
}
|
||||
|
||||
GetLandAnimEntry( jumpInfo, landAnimState )
|
||||
{
|
||||
incomingVectorWithoutNormal = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpInfo.jumpVector, jumpInfo.endUpVector );
|
||||
outgoingVectorWithoutNormal = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpInfo.landVector, jumpInfo.endUpVector );
|
||||
thirdVector = incomingVectorWithoutNormal - outgoingVectorWithoutNormal;
|
||||
|
||||
outgoingRightVector = VectorCross( outgoingVectorWithoutNormal, jumpInfo.endUpVector );
|
||||
outgoingRightVectorWithoutNormal = VectorNormalize( maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( outgoingRightVector, jumpInfo.endUpVector ) ) * 100;
|
||||
|
||||
projectionIncomingToOutgoingRight = VectorDot ( incomingVectorWithoutNormal * -1, outgoingRightVectorWithoutNormal );
|
||||
|
||||
//Law of cosine
|
||||
a = Length( incomingVectorWithoutNormal );
|
||||
b = Length( outgoingVectorWithoutNormal );
|
||||
c = Length( thirdVector );
|
||||
|
||||
MIN_LENGTH = 0.001;
|
||||
|
||||
// Edge case: Return forward;
|
||||
if ( a < MIN_LENGTH || b < MIN_LENGTH )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ratio = ( a * a + b * b - c * c ) / ( 2 * a * b );
|
||||
if ( ratio <= -1 )
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
else if ( ratio >= 1 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rotatedYaw = Acos( ratio );
|
||||
if ( projectionIncomingToOutgoingRight > 0 ) //Entering from the right
|
||||
{
|
||||
if ( 0 <= rotatedYaw && rotatedYaw < 22.5 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if ( 22.5 <= rotatedYaw && rotatedYaw < 67.5 )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if ( 67.5 <= rotatedYaw && rotatedYaw < 112.5 )
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if ( 112.5 <= rotatedYaw && rotatedYaw < 157.5 )
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
else //Entering from the left
|
||||
{
|
||||
if ( 0 <= rotatedYaw && rotatedYaw < 22.5 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if ( 22.5 <= rotatedYaw && rotatedYaw < 67.5 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if ( 67.5 <= rotatedYaw && rotatedYaw < 112.5 )
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else if ( 112.5 <= rotatedYaw && rotatedYaw < 157.5 )
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JumpPain( duration, endPos )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
self endon( "jump_finished" );
|
||||
|
||||
start_time = gettime();
|
||||
duration_msec = duration*1000;
|
||||
self waittill( "jump_pain", damageDirection, hitLocation, iDamage, stun );
|
||||
|
||||
// Make sure we're still jumping
|
||||
if ( !self.trajectoryActive )
|
||||
{
|
||||
return; // Too late!
|
||||
}
|
||||
|
||||
// Stop normal jump animations and play the pain
|
||||
self notify( "jump_pain_interrupt" );
|
||||
|
||||
jump_pain_state = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimState( "jump_pain", iDamage, stun );
|
||||
jump_pain_index = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "jump", damageDirection, hitLocation );
|
||||
damage_degree = self maps\mp\agents\alien\_alien_anim_utils::getDamageDegree( iDamage, stun );
|
||||
jump_end_time_sec = start_time * 0.001 + duration;
|
||||
PlayInAirJumpPainAnims( jump_pain_state, jump_pain_index, jump_end_time_sec, damage_degree );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 0.0 );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
// this would be even better if it was accounting for rotating entirely to the
|
||||
// direction of the next negotiation node
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
impact_pain_anim = self GetImpactPainAnimState( damage_degree );
|
||||
impact_pain_index = self maps\mp\agents\alien\_alien_anim_utils::GetImpactPainAnimIndex( jump_pain_index );
|
||||
self SetAnimState( impact_pain_anim, impact_pain_index, 1.0 );
|
||||
self WaitUntilNotetrack( impact_pain_anim, "code_move" );
|
||||
|
||||
self notify ( "jump_finished" );
|
||||
}
|
||||
|
||||
PlayInAirJumpPainAnims( jump_pain_state, jump_pain_entry, jump_end_time_sec, damage_degree )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
self endon( "jump_finished" );
|
||||
|
||||
self SetAnimState( jump_pain_state, jump_pain_entry, 1.0 );
|
||||
msg = self common_scripts\utility::waittill_any_return( "jump_pain", "traverse_complete" );
|
||||
if ( msg == "traverse_complete" )
|
||||
return;
|
||||
|
||||
idle_time_remaining = jump_end_time_sec - GetTime() * 0.001;
|
||||
|
||||
if ( idle_time_remaining > 0 )
|
||||
{
|
||||
MAX_RATE_SCALE = 2.0;
|
||||
jump_pain_idle_state = self GetJumpPainIdleAnimState( damage_degree );
|
||||
jump_pain_idle_anim = self GetAnimEntry( jump_pain_idle_state, jump_pain_entry );
|
||||
jump_pain_idle_anim_length = GetAnimLength( jump_pain_idle_anim );
|
||||
jump_pain_idle_rate = Min( MAX_RATE_SCALE, jump_pain_idle_anim_length / idle_time_remaining );
|
||||
|
||||
self SetAnimState( jump_pain_idle_state, jump_pain_entry, jump_pain_idle_rate );
|
||||
}
|
||||
|
||||
self waittill( "traverse_complete" );
|
||||
}
|
||||
|
||||
GetJumpPainIdleAnimState( damage_degree )
|
||||
{
|
||||
return ( "jump_pain_idle_" + damage_degree );
|
||||
}
|
||||
|
||||
GetImpactPainAnimState( damage_degree )
|
||||
{
|
||||
return ( "jump_impact_pain_" + damage_degree );
|
||||
}
|
||||
|
||||
get_jump_SFX_alias()
|
||||
{
|
||||
switch( maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "elite":
|
||||
return "null"; // "No sound is better than the wrong sound" - Tim S.
|
||||
|
||||
case "spitter":
|
||||
return "spitter_jump";
|
||||
|
||||
case "seeder":
|
||||
return "seed_jump";
|
||||
|
||||
case "gargoyle":
|
||||
return "gg_jump";
|
||||
|
||||
default:
|
||||
return "alien_jump";
|
||||
}
|
||||
}
|
||||
190
maps/mp/agents/alien/_alien_leper.gsc
Normal file
190
maps/mp/agents/alien/_alien_leper.gsc
Normal file
@@ -0,0 +1,190 @@
|
||||
// _alien_leper
|
||||
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
LEPER_NODE_WAIT = 5.0;
|
||||
LEPER_SPAWN_DURATION = 35000; // Lifetime
|
||||
LEPER_MIN_SAFE_PLAYER_DIST_SQR = 1048576; // A player entering this distance triggers searching for a new node
|
||||
LEPER_DAMAGE_MOVE_DELAY = 1.5; // Waits this time after being damaged before choosing a new node
|
||||
|
||||
leper_init()
|
||||
{
|
||||
self.leperDespawnTime = getTime() + LEPER_SPAWN_DURATION;
|
||||
self thread handle_favorite_enemy();
|
||||
|
||||
}
|
||||
|
||||
leper_combat( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
enemy endon( "death" );
|
||||
|
||||
self leper_retreat( enemy );
|
||||
}
|
||||
|
||||
leper_retreat( enemy )
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
self leper_approach( enemy );
|
||||
self leper_wait_at_node( enemy );
|
||||
}
|
||||
}
|
||||
|
||||
leper_challenge_despawn( despawn_time )
|
||||
{
|
||||
self endon( "leper_despawn" );
|
||||
self endon( "death" );
|
||||
|
||||
wait despawn_time;
|
||||
self leper_despawn();
|
||||
}
|
||||
|
||||
|
||||
handle_favorite_enemy()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
// Our favorite enemy should always be the closest player
|
||||
while ( 1 )
|
||||
{
|
||||
self.favoriteenemy = self get_closest_living_player();
|
||||
wait 5;
|
||||
}
|
||||
}
|
||||
|
||||
leper_despawn()
|
||||
{
|
||||
self endon( "death" );
|
||||
self.health = 30000;
|
||||
self.maxhealth = 30000;
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin );
|
||||
self ScrAgentSetGoalRadius( 2048 );
|
||||
PlayFXOnTag( level._effect[ "alien_teleport" ], self, "tag_origin" );
|
||||
wait 1.0;
|
||||
self Suicide();
|
||||
}
|
||||
|
||||
|
||||
leper_approach( enemy )
|
||||
{
|
||||
retreat_node = self get_leper_retreat_node( enemy );
|
||||
if ( !isDefined( retreat_node ))
|
||||
{
|
||||
wait 1;
|
||||
return;
|
||||
}
|
||||
|
||||
self ScrAgentSetGoalNode( retreat_node );
|
||||
self ScrAgentSetGoalRadius( 64 );
|
||||
self waittill( "goal_reached" );
|
||||
}
|
||||
|
||||
leave_node_on_distance_breach( enemy )
|
||||
{
|
||||
enemy endon( "death" );
|
||||
self endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "alien_main_loop_restart" );
|
||||
self endon( "leave_node ");
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( DistanceSquared( enemy.origin, self.origin ) < LEPER_MIN_SAFE_PLAYER_DIST_SQR )
|
||||
{
|
||||
// go away
|
||||
self notify( "leave_node" );
|
||||
}
|
||||
wait 1;
|
||||
}
|
||||
}
|
||||
|
||||
leave_node_on_attacked( enemy )
|
||||
{
|
||||
enemy endon( "death" );
|
||||
self endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "alien_main_loop_restart" );
|
||||
self endon( "leave_node ");
|
||||
|
||||
self waittill( "damage" );
|
||||
wait LEPER_DAMAGE_MOVE_DELAY;
|
||||
|
||||
self notify( "leave_node" );
|
||||
}
|
||||
|
||||
leper_wait_at_node( enemy )
|
||||
{
|
||||
self endon( "leave_node" );
|
||||
|
||||
self thread leave_node_on_attacked( enemy );
|
||||
self thread leave_node_on_distance_breach( enemy );
|
||||
wait LEPER_NODE_WAIT;
|
||||
}
|
||||
|
||||
get_leper_retreat_node( enemy )
|
||||
{
|
||||
retreat_nodes = get_named_retreat_nodes();
|
||||
if ( !isDefined( retreat_nodes ) )
|
||||
retreat_nodes = get_possible_retreat_nodes();
|
||||
|
||||
filters = [];
|
||||
filters[ "direction" ] = "override";
|
||||
filters[ "direction_override" ] = get_direction_away_from_players();
|
||||
filters[ "direction_weight" ] = 2.0;
|
||||
filters[ "min_height" ] = 64.0;
|
||||
filters[ "max_height" ] = 500.0;
|
||||
filters[ "height_weight" ] = 2.0;
|
||||
filters[ "enemy_los" ] = false;
|
||||
filters[ "enemy_los_weight" ] = 2.0;
|
||||
filters[ "min_dist_from_enemy" ] = 500.0;
|
||||
filters[ "max_dist_from_enemy" ] = 2048.0;
|
||||
filters[ "desired_dist_from_enemy" ] = 1500.0;
|
||||
filters[ "dist_from_enemy_weight" ] = 3.0;
|
||||
filters[ "min_dist_from_all_enemies" ] = 800.0;
|
||||
filters[ "min_dist_from_all_enemies_weight" ] = 5.0;
|
||||
filters[ "not_recently_used_weight" ] = 4.0;
|
||||
filters[ "random_weight" ] = 1.5;
|
||||
|
||||
result = maps\mp\agents\alien\_alien_think::get_retreat_node_rated( enemy, filters, retreat_nodes );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
get_possible_retreat_nodes()
|
||||
{
|
||||
jump_nodes = GetNodesInRadius( self.origin, 1024, 400, 500, "jump" );
|
||||
return jump_nodes;
|
||||
}
|
||||
|
||||
get_direction_away_from_players()
|
||||
{
|
||||
if ( level.players.size == 0)
|
||||
return self.origin + AnglesToForward( self.angles ) * 100;
|
||||
|
||||
centralLocation = ( 0, 0, 0 );
|
||||
|
||||
foreach ( player in level.players )
|
||||
centralLocation += player.origin;
|
||||
|
||||
centralLocation = centralLocation / level.players.size;
|
||||
|
||||
return self.origin - centralLocation;
|
||||
}
|
||||
|
||||
// Lepers don't attack!
|
||||
leper_attack()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
get_named_retreat_nodes()
|
||||
{
|
||||
current_area = get_current_area_name();
|
||||
possible_nodes = getnodearray( current_area + "_leper_location","targetname" );
|
||||
if ( isDefined( possible_nodes ) && possible_nodes.size > 0 )
|
||||
return possible_nodes;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
1201
maps/mp/agents/alien/_alien_melee.gsc
Normal file
1201
maps/mp/agents/alien/_alien_melee.gsc
Normal file
File diff suppressed because it is too large
Load Diff
87
maps/mp/agents/alien/_alien_minion.gsc
Normal file
87
maps/mp/agents/alien/_alien_minion.gsc
Normal file
@@ -0,0 +1,87 @@
|
||||
#include maps\mp\agents\alien\_alien_think;
|
||||
#include maps\mp\alien\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
ALIEN_MINION_EXPLODE_DISTANCE = 80;
|
||||
ALIEN_MINION_EXPLODE_RADIUS = 200;
|
||||
EXPLODE_ATTACK_START_SOUND = "alien_minion_attack";
|
||||
ALIEN_MINION_CHARGE_SOUND = "alien_minion_alert";
|
||||
ALIEN_MINION_CHATTER_SOUND = "alien_minion_idle";
|
||||
CHATTER_MIN_INTERVAL = 8.0;
|
||||
CHATTER_MAX_INTERVAL = 15.0;
|
||||
|
||||
minion_init()
|
||||
{
|
||||
self thread minion_chatter_monitor();
|
||||
}
|
||||
|
||||
minion_chatter_monitor()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
chatterInterval = RandomFloatRange( CHATTER_MIN_INTERVAL, CHATTER_MAX_INTERVAL );
|
||||
wait chatterInterval;
|
||||
self PlaySoundOnMovingEnt( ALIEN_MINION_CHATTER_SOUND );
|
||||
}
|
||||
}
|
||||
|
||||
minion_approach( enemy, attack_counter )
|
||||
{
|
||||
/# debug_alien_ai_state( "default_approach" ); #/
|
||||
/# debug_alien_attacker_state( "attacking" ); #/
|
||||
|
||||
self.attacking_player = true;
|
||||
self.bypass_max_attacker_counter = false;
|
||||
|
||||
swipe_chance = 0.0; //self.swipeChance;
|
||||
should_swipe = ( RandomFloat( 1.0 ) < swipe_chance );
|
||||
|
||||
if ( should_swipe )
|
||||
{
|
||||
return go_for_swipe( enemy );
|
||||
}
|
||||
|
||||
self PlaySoundOnMovingEnt( ALIEN_MINION_CHARGE_SOUND );
|
||||
approach_node = approach_enemy( ALIEN_MINION_EXPLODE_DISTANCE, enemy, 3 );
|
||||
return "explode";
|
||||
}
|
||||
|
||||
explode_attack( enemy )
|
||||
{
|
||||
/# debug_alien_ai_state( "swipe_melee" ); #/
|
||||
self.melee_type = "explode";
|
||||
alien_melee( enemy );
|
||||
}
|
||||
|
||||
explode( enemy )
|
||||
{
|
||||
self set_alien_emissive( 0.2, 1.0 );
|
||||
self PlaySoundOnMovingEnt( EXPLODE_ATTACK_START_SOUND );
|
||||
playFxOnTag( level._effect[ "alien_minion_preexplode" ], self, "tag_origin" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
anim_rate = 1.25;
|
||||
|
||||
self SetAnimState( "minion_explode", 0, anim_rate );
|
||||
wait GetAnimLength( self GetAnimEntry( "minion_explode", 0 ) ) * ( 1 / anim_rate );
|
||||
self Suicide();
|
||||
}
|
||||
|
||||
load_minion_fx()
|
||||
{
|
||||
level._effect[ "alien_minion_explode" ] = Loadfx( "vfx/gameplay/alien/vfx_alien_minion_explode" );
|
||||
level._effect[ "alien_minion_preexplode" ] = loadfx( "vfx/gameplay/alien/vfx_alien_minion_preexplosion");
|
||||
}
|
||||
|
||||
minion_explode_on_death( loc )
|
||||
{
|
||||
waitframe(); //<NOTE J.C.> Prevent script stack overflow
|
||||
|
||||
PlayFx( level._effect[ "alien_minion_explode" ], loc + (0,0,32) );
|
||||
PlaySoundAtPos( loc, "alien_minion_explode" );
|
||||
|
||||
RadiusDamage( loc, ALIEN_MINION_EXPLODE_RADIUS, level.alien_types[ "minion" ].attributes[ "explode_max_damage" ], level.alien_types[ "minion" ].attributes[ "explode_min_damage" ],undefined,"MOD_EXPLOSIVE","alien_minion_explosion" );
|
||||
}
|
||||
959
maps/mp/agents/alien/_alien_move.gsc
Normal file
959
maps/mp/agents/alien/_alien_move.gsc
Normal file
@@ -0,0 +1,959 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
ALLOW_SLIDE_DIST_SQR = 20.0 * 20.0;
|
||||
DODGE_CHANCE_ENEMY_FACING_TOLERANCE = 0.985; // cos 10, how closely our enemy has to be looking at us
|
||||
DODGE_CHANCE_ALIEN_FACING_TOLERANCE = 0.766; // cos 40, how closely we have to be facing our enemy
|
||||
DODGE_CHANCE_LOOK_AT_TIME_MIN = 1000; // Time our enemy has to look at us before dodging
|
||||
DODGE_CHANCE_LOOK_AT_TIME_MAX = 2000;
|
||||
|
||||
DODGE_CHANCE_MAX_DISTANCE_SQ = 640000.0; // 800.0 * 800.0
|
||||
MIN_DODGE_DIST_SQUARED = 65536.0; // 256 * 256
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self EnterMove();
|
||||
self StartMove();
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
EnterMove()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
self.playing_pain_animation = false;
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
|
||||
StartMove()
|
||||
{
|
||||
if ( canDoStartMove())
|
||||
{
|
||||
switch( getStartMoveType() )
|
||||
{
|
||||
case "run-start":
|
||||
self doRunStart();
|
||||
break;
|
||||
case "walk-start":
|
||||
self doWalkStart();
|
||||
break;
|
||||
case "leap-to-run":
|
||||
self doLeapToRunStart();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
self.playing_pain_animation = false;
|
||||
self CancelAllBut( undefined );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.previousAnimState = "move";
|
||||
}
|
||||
|
||||
SetupMovement()
|
||||
{
|
||||
self.enableStop = true;
|
||||
self thread WaitForMovemodeChange();
|
||||
self thread WaitForJumpSoon();
|
||||
self thread WaitForSharpTurn();
|
||||
self thread WaitForStop();
|
||||
self thread WaitForStuck();
|
||||
|
||||
if ( self canDodge() )
|
||||
{
|
||||
self thread WaitForNearMiss();
|
||||
self thread WaitForDodgeChance();
|
||||
}
|
||||
}
|
||||
|
||||
ContinueMovement()
|
||||
{
|
||||
self SetupMovement();
|
||||
|
||||
// Oriented agents should maintain their orientation
|
||||
if( self.oriented )
|
||||
{
|
||||
forward = self GetLookaheadDir();
|
||||
up = AnglesToUp( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
self ScrAgentSetAnimMode( "code_move_slide" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimScale( self.xyanimscale, 1.0 );
|
||||
self SetMoveAnim( self.moveMode );
|
||||
}
|
||||
|
||||
WaitForMovemodeChange()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_runwalk" );
|
||||
curMovement = self.moveMode;
|
||||
while ( true )
|
||||
{
|
||||
if ( curMovement != self.moveMode )
|
||||
{
|
||||
self SetMoveAnim( self.moveMode );
|
||||
curMovement = self.moveMode;
|
||||
}
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
WaitForSharpTurn()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_sharpturn" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
|
||||
angleIndex = GetAngleIndexFromSelfYaw( newDir );
|
||||
|
||||
if ( angleIndex == 4 ) // 4 means this turn wasn't sharp enough for me to care. (angle ~= 0)
|
||||
{
|
||||
self thread WaitForSharpTurn();
|
||||
return;
|
||||
}
|
||||
|
||||
shouldMoveStraightAhead = !( self should_do_sharp_turn() );
|
||||
|
||||
if ( shouldMoveStraightAhead )
|
||||
angleIndex = 0;
|
||||
|
||||
//Try run-turn
|
||||
animState = "run_turn";
|
||||
turnAnim = self GetAnimEntry( animState, angleIndex );
|
||||
canDoTurn = shouldMoveStraightAhead || CanDoTurnAnim( turnAnim );
|
||||
|
||||
if ( !canDoTurn )
|
||||
{
|
||||
self thread WaitForSharpTurn();
|
||||
return;
|
||||
}
|
||||
|
||||
self CancelAllBut( "sharpturn" );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
self.enableStop = false;
|
||||
|
||||
if ( shouldMoveStraightAhead )
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( self GetLookaheadDir() );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, angleIndex, self.moveplaybackrate, animState, "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
self.bLockGoalPos = false;
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForStop()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_stop" );
|
||||
|
||||
self waittill( "stop_soon" );
|
||||
|
||||
if ( !self shouldDoStopAnim() || self.movemode == "walk" )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
goalPos = self GetPathGoalPos();
|
||||
//assert( IsDefined( goalPos ) );
|
||||
|
||||
if ( !isDefined( goalPos ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
meToStop = goalPos - self.origin;
|
||||
finalFaceDir = getStopEndFaceDir( goalPos );
|
||||
|
||||
animState = getStopAnimState();
|
||||
|
||||
if ( self should_move_straight_ahead() )
|
||||
{
|
||||
animIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
animIndex = getStopAnimIndex( animState, finalFaceDir );
|
||||
}
|
||||
|
||||
stopAnim = self GetAnimEntry( animState, animIndex );
|
||||
stopDelta = GetMoveDelta( stopAnim );
|
||||
stopAngleDelta = GetAngleDelta( stopAnim );
|
||||
|
||||
// not enough room left to play the animation. abort. (i'm willing to squish/scale the anim up to 48 units.)
|
||||
if ( Length( meToStop ) + 48 < Length( stopDelta ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopData = self GetStopData( goalPos );
|
||||
stopStartPos = self CalcAnimStartPos( stopData.pos, stopData.angles[1], stopDelta, stopAngleDelta );
|
||||
stopStartPosDropped = self DropPosToGround( stopStartPos );
|
||||
|
||||
if ( !IsDefined( stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !self CanMovePointToPoint( stopData.pos, stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
self CancelAllBut( "stop", "sharpturn" );
|
||||
|
||||
self thread WaitForPathSet( "alienmove_endwait_pathsetwhilestopping", "alienmove_endwait_stop" );
|
||||
|
||||
// scale the anim if necessary, to make sure we end up where we wanted to end up.
|
||||
scaleFactors = GetAnimScaleFactors( goalPos - self.origin, stopDelta );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", VectorToAngles( meToStop ) );
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, animIndex, animState, "end" );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
if ( self should_move_straight_ahead() )
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( self GetLookaheadDir() );
|
||||
|
||||
// Make sure we made it
|
||||
goalPos = self GetPathGoalPos();
|
||||
if ( DistanceSquared( self.origin, goalPos ) < ALLOW_SLIDE_DIST_SQR )
|
||||
{
|
||||
// Success
|
||||
self ScrAgentSetAnimMode( "code_move_slide" );
|
||||
self SetAnimState( "idle" ); // if all went well, idle state should kick in without this. if all didn't... cover it up.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failure - return to move
|
||||
StartMove();
|
||||
ContinueMovement();
|
||||
}
|
||||
}
|
||||
|
||||
getStopEndFaceDir( goalPos )
|
||||
{
|
||||
if ( isDefined ( self.enemy ) )
|
||||
return ( self.enemy.origin - goalPos );
|
||||
|
||||
return ( goalPos - self.origin );
|
||||
}
|
||||
|
||||
getStopAnimState()
|
||||
{
|
||||
switch( self.movemode )
|
||||
{
|
||||
case "run":
|
||||
case "jog":
|
||||
return "run_stop";
|
||||
case "walk":
|
||||
return "walk_stop";
|
||||
default:
|
||||
AssertMsg( "Trying to get stop animState for unknown movemode: " + self.movemode );
|
||||
}
|
||||
}
|
||||
|
||||
getStopAnimIndex( animState, meToStop )
|
||||
{
|
||||
switch( animState )
|
||||
{
|
||||
case "walk_stop":
|
||||
return 0;
|
||||
case "run_stop":
|
||||
return GetAngleIndexFromSelfYaw( meToStop );
|
||||
}
|
||||
}
|
||||
|
||||
WaitForPathSet( endOnNotify, killParentNotify )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( endOnNotify );
|
||||
|
||||
oldGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
self waittill( "path_set" );
|
||||
|
||||
newGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
if ( DistanceSquared( oldGoalPos, newGoalPos ) < 1 )
|
||||
{
|
||||
self thread WaitForPathSet( endOnNotify, killParentNotify );
|
||||
return;
|
||||
}
|
||||
|
||||
self notify( killParentNotify );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForJumpSoon()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_jumpsoon" );
|
||||
|
||||
self waittill( "traverse_soon" );
|
||||
self CancelAllBut( "jumpsoon" );
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
|
||||
// Check if alien should do the run-to-leap animations
|
||||
targetVector = endNode.origin - startNode.origin;
|
||||
angleIndex = GetAngleIndexFromSelfYaw( endNode.origin - startNode.origin );
|
||||
if ( !shouldDoLeapArrivalAnim( startNode, angleIndex ) )
|
||||
{
|
||||
self ContinueMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if alien can move from anim start position to the start node
|
||||
arrivalAnimState = "jump_launch_arrival";
|
||||
arrivalAnim = self GetAnimEntry( arrivalAnimState, angleIndex );
|
||||
moveDelta = GetMoveDelta( arrivalAnim );
|
||||
angleYawDelta = GetAngleDelta( arrivalAnim );
|
||||
if ( !self CanMovePointToPoint( self.origin, startNode.origin ) && !self.oriented )
|
||||
{
|
||||
self ContinueMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
self thread WaitForPathSet( "alienmove_endwait_pathsetwhilejumping", "alienmove_endwait_jumpsoon" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( startNode.origin - self.origin, moveDelta );
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNAtRateUntilNotetrack( arrivalAnimState, angleIndex, self.moveplaybackrate, "jump_launch_arrival", "anim_will_finish" );
|
||||
|
||||
forward = targetVector;
|
||||
up = AnglesToUp( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
if ( isDefined( startNode ) && distanceSquared( self.origin, startNode.origin ) < ALLOW_SLIDE_DIST_SQR || self.oriented )
|
||||
self ScrAgentSetAnimMode( "code_move_slide" ); // hope that we enter the traverse state at some point...
|
||||
else
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
SetMoveAnim( moveMode )
|
||||
{
|
||||
if ( moveMode == "run" )
|
||||
{
|
||||
nEntries = self GetAnimEntryCount( "run" );
|
||||
animWeights = [ /*01*/ 20, /*03*/ 80 ];
|
||||
assert( animWeights.size == nEntries );
|
||||
randIndex = maps\mp\alien\_utility::GetRandomIndex( animWeights );
|
||||
assert( randIndex < nEntries );
|
||||
self SetAnimState( "run", randIndex, self.moveplaybackrate );
|
||||
}
|
||||
else if ( moveMode == "jog" )
|
||||
{
|
||||
self SetAnimState( "jog", undefined, self.moveplaybackrate );
|
||||
}
|
||||
else if ( moveMode == "walk" )
|
||||
{
|
||||
self SetAnimState( "walk", undefined, self.moveplaybackrate );
|
||||
}
|
||||
else
|
||||
{
|
||||
assertmsg( "unimplemented move mode " + moveMode );
|
||||
}
|
||||
}
|
||||
|
||||
CancelAllBut( doNotCancel, doNotCancel2 )
|
||||
{
|
||||
cleanups = [ "runwalk", "sharpturn", "stop", "pathsetwhilestopping", "jumpsoon", "pathsetwhilejumping", "pathset", "nearmiss", "dodgechance", "stuck" ];
|
||||
|
||||
bCheckDoNotCancel = IsDefined( doNotCancel );
|
||||
bCheckDoNotCancel2 = IsDefined( doNotCancel2 );
|
||||
|
||||
foreach ( cleanup in cleanups )
|
||||
{
|
||||
if ( bCheckDoNotCancel && cleanup == doNotCancel )
|
||||
continue;
|
||||
if ( bCheckDoNotCancel2 && cleanup == doNotCancel2 )
|
||||
continue;
|
||||
self notify( "alienmove_endwait_" + cleanup );
|
||||
}
|
||||
}
|
||||
|
||||
GetStopData( goalPos )
|
||||
{
|
||||
stopData = SpawnStruct();
|
||||
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
stopData.pos = self.node.origin;
|
||||
stopData.angles = self.node.angles;
|
||||
}
|
||||
else if ( isDefined( self.enemy ) )
|
||||
{
|
||||
stopData.pos = goalPos;
|
||||
stopData.angles = vectorToAngles( self.enemy.origin - goalPos );
|
||||
}
|
||||
else
|
||||
{
|
||||
stopData.pos = goalPos;
|
||||
stopData.angles = self.angles;
|
||||
}
|
||||
|
||||
return stopData;
|
||||
}
|
||||
|
||||
CalcAnimStartPos( stopPos, stopAngle, animDelta, animAngleDelta )
|
||||
{
|
||||
dAngle = stopAngle - animAngleDelta;
|
||||
angles = ( 0, dAngle, 0 );
|
||||
vForward = AnglesToForward( angles );
|
||||
vRight = AnglesToRight( angles );
|
||||
|
||||
forward = vForward * animDelta[0];
|
||||
right = vRight * animDelta[1];
|
||||
|
||||
return stopPos - forward + right;
|
||||
}
|
||||
|
||||
|
||||
onFlashbanged()
|
||||
{
|
||||
self DoStumble();
|
||||
}
|
||||
|
||||
onDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( IsDefined( level.dlc_can_do_pain_override_func ) )
|
||||
{
|
||||
painAllowed = [[level.dlc_can_do_pain_override_func]]( "move" );
|
||||
if ( !painAllowed )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( maps\mp\alien\_utility::is_pain_available( eAttacker,sMeansOfDeath ) )
|
||||
self DoStumble( iDFlags, vDir, sHitLoc, iDamage, sMeansOfDeath, eAttacker );
|
||||
}
|
||||
|
||||
DoStumble( iDFlags, damageDirection, hitLocation, iDamage, sMeansOfDeath, eAttacker )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
if ( self.playing_pain_animation )
|
||||
return;
|
||||
|
||||
self CancelAllBut( undefined );
|
||||
self.stateLocked = true;
|
||||
self.playing_pain_animation = true;
|
||||
|
||||
is_stun = ( iDFlags & level.iDFLAGS_STUN );
|
||||
|
||||
if ( sMeansOfDeath == "MOD_MELEE" || is_stun )
|
||||
{
|
||||
animState = "pain_pushback";
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "push_back", damageDirection );
|
||||
pain_notify = "pain_pushback";
|
||||
}
|
||||
else
|
||||
{
|
||||
animState = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimState( "run_stumble", iDamage, is_stun );
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "run", damageDirection, hitLocation );
|
||||
pain_notify = "run_stumble";
|
||||
}
|
||||
|
||||
anime = self GetAnimEntry( animState, animIndex );
|
||||
self maps\mp\alien\_utility::always_play_pain_sound( anime );
|
||||
self maps\mp\alien\_utility::register_pain( anime );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, animIndex, self.movePlaybackRate, pain_notify, "code_move" );
|
||||
|
||||
self.playing_pain_animation = false;
|
||||
self.stateLocked = false;
|
||||
|
||||
if ( shouldStartMove() )
|
||||
self StartMove();
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
|
||||
WaitForNearMiss( enemy )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_nearmiss" );
|
||||
|
||||
DODGE_CHANCE = 0.5;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill_any( "bulletwhizby", "damage" );
|
||||
|
||||
if( RandomFloat( 1.0 ) < DODGE_CHANCE )
|
||||
continue;
|
||||
|
||||
if ( !self.playing_pain_animation )
|
||||
{
|
||||
DoDodge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WaitForDodgeChance()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_dodgechance" );
|
||||
|
||||
currentLookAtDuration = 0.0;
|
||||
currentDodgeTime = RandomIntRange( DODGE_CHANCE_LOOK_AT_TIME_MIN, DODGE_CHANCE_LOOK_AT_TIME_MAX );
|
||||
lastTimeStamp = GetTime();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
wait 0.1;
|
||||
|
||||
if ( IsAlive( self.enemy ) )
|
||||
{
|
||||
currentTime = GetTime();
|
||||
enemyToMe = VectorNormalize( self.origin - self.enemy.origin );
|
||||
enemyFacing = AnglesToForward( self.enemy.angles );
|
||||
|
||||
// Fail if enemy isn't looking at us
|
||||
if ( VectorDot( enemytoMe, enemyFacing ) < DODGE_CHANCE_ENEMY_FACING_TOLERANCE )
|
||||
{
|
||||
currentLookAtDuration = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
currentLookAtDuration += currentTime - lastTimeStamp;
|
||||
|
||||
// Fail if enemy is too far away
|
||||
if ( DistanceSquared( self.origin, self.enemy.origin ) > DODGE_CHANCE_MAX_DISTANCE_SQ )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fail if we're not navigating towards the enemy
|
||||
meToEnemy = enemyToMe * -1.0;
|
||||
myFacing = AnglesToForward( self.angles );
|
||||
if ( VectorDot( meToEnemy, myFacing ) < DODGE_CHANCE_ALIEN_FACING_TOLERANCE )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( currentLookAtDuration >= currentDodgeTime && !self.playing_pain_animation )
|
||||
{
|
||||
DoDodge( "dodgechance" );
|
||||
currentLookAtDuration = 0.0;
|
||||
currentDodgeTime = RandomIntRange( DODGE_CHANCE_LOOK_AT_TIME_MIN, DODGE_CHANCE_LOOK_AT_TIME_MAX );
|
||||
}
|
||||
|
||||
lastTimeStamp = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canDodge()
|
||||
{
|
||||
switch( self maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "elite":
|
||||
case "mammoth":
|
||||
case "spitter":
|
||||
case "seeder":
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DoDodge( endwait )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
DODGE_FREQUENCY = 1000; // 1 second
|
||||
|
||||
if( IsDefined( self.last_dodge_time ) && GetTime() - self.last_dodge_time < DODGE_FREQUENCY )
|
||||
return;
|
||||
|
||||
if ( IsAlive( self.enemy ) && DistanceSquared( self.origin, self.enemy.origin ) < MIN_DODGE_DIST_SQUARED )
|
||||
return;
|
||||
|
||||
primary_dodge_anim_state = get_primary_dodge_anim_state();
|
||||
|
||||
if ( cointoss() )
|
||||
{
|
||||
if ( !TryDodge( primary_dodge_anim_state + "_left", endwait ) )
|
||||
TryDodge( primary_dodge_anim_state + "_right", endwait );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !TryDodge( primary_dodge_anim_state + "_right", endwait ) )
|
||||
TryDodge( primary_dodge_anim_state + "_left", endwait );
|
||||
}
|
||||
}
|
||||
|
||||
get_primary_dodge_anim_state()
|
||||
{
|
||||
switch( self.movemode )
|
||||
{
|
||||
case "jog":
|
||||
return "jog_dodge";
|
||||
|
||||
default:
|
||||
return "run_dodge";
|
||||
}
|
||||
}
|
||||
|
||||
TryDodge( dodgeState, endwait )
|
||||
{
|
||||
MIN_DODGE_SCALE = 0.5;
|
||||
|
||||
dodgeEntry = self GetRandomAnimEntry( dodgeState );
|
||||
dodgeAnim = self GetAnimEntry( dodgeState, dodgeEntry );
|
||||
moveScale = GetSafeAnimMoveDeltaPercentage( dodgeAnim );
|
||||
moveScale = min( moveScale, self.xyanimscale );
|
||||
|
||||
if ( moveScale < MIN_DODGE_SCALE )
|
||||
return false;
|
||||
|
||||
self.last_dodge_time = GetTime();
|
||||
|
||||
self CancelAllBut( endwait );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self ScrAgentSetAnimScale( moveScale, 1.0 );
|
||||
self PlayAnimUntilNotetrack( dodgeState, dodgeState, "end" );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self ContinueMovement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WaitForStuck()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_stuck" );
|
||||
|
||||
STUCK_DURATION = 2000.0;
|
||||
nextStuckTime = GetTime() + STUCK_DURATION;
|
||||
lastPos = self.origin;
|
||||
STUCK_TOLERANCE = 1.0;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
currentTime = GetTime();
|
||||
LastDistance = Length( self.origin - lastPos );
|
||||
if ( LastDistance > STUCK_TOLERANCE )
|
||||
nextStuckTime = currentTime + STUCK_DURATION;
|
||||
|
||||
if ( nextStuckTime <= currentTime )
|
||||
{
|
||||
stuckLerp();
|
||||
nextStuckTime = currentTime + STUCK_DURATION;
|
||||
break;
|
||||
}
|
||||
|
||||
lastPos = self.origin;
|
||||
wait 0.1;
|
||||
}
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
stuckLerp()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_stuck" );
|
||||
self endon( "death" );
|
||||
|
||||
LERP_TIME = 0.2;
|
||||
|
||||
CancelAllBut( "stuck" );
|
||||
|
||||
currentAnim = self GetAnimEntry();
|
||||
currentAnimLength = GetAnimLength( currentAnim );
|
||||
currentAnimDistance = Length( GetMoveDelta( currentAnim ) );
|
||||
lerpDistance = ( LERP_TIME / currentAnimLength ) * currentAnimDistance;
|
||||
|
||||
lerpDirection = self GetLookaheadDir();
|
||||
endPos = self.origin + lerpDirection * lerpDistance;
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", VectorToAngles( lerpDirection ) );
|
||||
self ScrAgentDoAnimLerp( self.origin, endPos, LERP_TIME );
|
||||
wait LERP_TIME;
|
||||
|
||||
self SetOrigin( self.origin );
|
||||
}
|
||||
|
||||
doWalkStart()
|
||||
{
|
||||
animState = "walk_start";
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, animIndex, self.movePlaybackRate, animState, "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
doRunStart()
|
||||
{
|
||||
negStartNode = self GetNegotiationStartNode();
|
||||
if ( IsDefined( negStartNode ) )
|
||||
goalPos = negStartNode.origin;
|
||||
else
|
||||
goalPos = self GetPathGoalPos();
|
||||
|
||||
// GetPathGoalPos will return undefined if i don't have a path
|
||||
if ( !IsDefined( goalPos ) )
|
||||
return;
|
||||
|
||||
// don't play start if i have no room for the start.
|
||||
if ( DistanceSquared( goalPos, self.origin ) < 100 * 100 )
|
||||
return;
|
||||
|
||||
lookaheadDir = self GetLookaheadDir();
|
||||
|
||||
myVelocity = self GetVelocity();
|
||||
if ( LengthSquared( myVelocity ) > 16 )
|
||||
{
|
||||
// don't need a start if i'm wallrunning and about to turn a corner onto another plane.
|
||||
myUp = AnglesToUp( self.angles );
|
||||
if ( VectorDot( myUp, (0,0,1) ) < 0.707 )
|
||||
{
|
||||
angleCos = VectorDot( myUp, lookaheadDir );
|
||||
if ( angleCos > 0.707 || angleCos < -0.707 )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self doStartMoveAnim( "run_start" );
|
||||
}
|
||||
|
||||
doLeapToRunStart()
|
||||
{
|
||||
self doStartMoveAnim( "leap_to_run_start" );
|
||||
}
|
||||
|
||||
should_move_straight_ahead()
|
||||
{
|
||||
switch ( self maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "spitter":
|
||||
case "seeder":
|
||||
case "minion":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
should_do_sharp_turn()
|
||||
{
|
||||
switch ( self maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "spitter":
|
||||
case "seeder":
|
||||
case "minion":
|
||||
case "elite":
|
||||
case "mammoth":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
doStartMoveAnim( animState )
|
||||
{
|
||||
if ( self should_move_straight_ahead() )
|
||||
{
|
||||
angleIndex = 0;
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( self GetLookaheadDir() );
|
||||
}
|
||||
else
|
||||
{
|
||||
angleIndex = getStartMoveAngleIndex();
|
||||
}
|
||||
|
||||
// JohnW: Disabling trace check - mostly redundant from sharpturn traces in code
|
||||
//startAnim = self GetAnimEntry( animState, angleIndex );
|
||||
//startAnimTranslation = GetMoveDelta( startAnim );
|
||||
//endPos = RotateVector( startAnimTranslation, self.angles ) + self.origin;
|
||||
//if ( !self CanMovePointToPoint( self.origin, endPos ) )
|
||||
//return;
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, angleIndex, self.movePlaybackRate, animState, "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
|
||||
}
|
||||
|
||||
canDoStartMove()
|
||||
{
|
||||
if ( !isdefined( self.traverseComplete )
|
||||
&& !isdefined( self.skipStartMove )
|
||||
&& ( !isdefined( self.disableExits ) || self.disableExits == false ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getStartMoveType()
|
||||
{
|
||||
previousAnimState = self.previousAnimState;
|
||||
switch( previousAnimState )
|
||||
{
|
||||
case "traverse_jump":
|
||||
return "leap-to-run";
|
||||
default:
|
||||
switch( self.movemode )
|
||||
{
|
||||
case "run":
|
||||
return "run-start";
|
||||
case "walk":
|
||||
return "walk-start";
|
||||
default:
|
||||
return "run-start";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shouldDoStopAnim()
|
||||
{
|
||||
return ( isDefined( self.enableStop ) && self.enableStop == true );
|
||||
}
|
||||
|
||||
shouldDoLeapArrivalAnim( startNode, angleIndex )
|
||||
{
|
||||
if ( startNode.type == "Jump" || startNode.type == "Jump Attack" ) // For jump nodes, always do run-to-leap animation
|
||||
return true;
|
||||
else if ( traversalStartFromIdle( startNode.animscript ) ) // If the traversal animation starts at the idle position, need to play run-to-leap
|
||||
return true;
|
||||
else if ( incomingAngleStraightAhead( self maps\mp\alien\_utility::get_alien_type(), angleIndex ) ) // For other traversals, do not play when the incoming angle is either 45 or 0
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
incomingAngleStraightAhead( alienType, angleIndex )
|
||||
{
|
||||
switch( alienType )
|
||||
{
|
||||
case "elite":
|
||||
case "mammoth":
|
||||
return ( angleIndex == 4 );
|
||||
|
||||
default:
|
||||
return ( angleIndex == 3 || angleIndex == 4 || angleIndex == 5 );
|
||||
}
|
||||
}
|
||||
|
||||
traversalStartFromIdle( anim_script )
|
||||
{
|
||||
switch( anim_script )
|
||||
{
|
||||
case "alien_climb_up":
|
||||
case "alien_climb_up_over_56":
|
||||
case "climb_up_end_jump_side_l":
|
||||
case "climb_up_end_jump_side_r":
|
||||
case "alien_climb_up_ledge_18_run":
|
||||
case "alien_climb_up_ledge_18_idle":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CanDoTurnAnim( turnAnim )
|
||||
{
|
||||
HEIGHT_OFFSET = 16;
|
||||
RADIUS_OFFSET = 10;
|
||||
HEIGHT_OFFSET_COOR = ( 0, 0, 16 );
|
||||
|
||||
if ( !IsDefined( self GetPathGoalPos()) )
|
||||
return false;
|
||||
|
||||
assert( isDefined( turnAnim ));
|
||||
|
||||
codeMoveTimes = GetNotetrackTimes( turnAnim, "code_move" );
|
||||
assert( codeMoveTimes.size == 1 );
|
||||
|
||||
codeMoveTime = codeMoveTimes[ 0 ];
|
||||
assert( codeMoveTime <= 1 );
|
||||
|
||||
moveDelta = GetMoveDelta( turnAnim, 0, codeMoveTime );
|
||||
codeMovePoint = self LocalToWorldCoords( moveDelta );
|
||||
codeMovePoint = GetGroundPosition( codeMovePoint, self.radius );
|
||||
if ( !isDefined( codeMovePoint ) )
|
||||
return false;
|
||||
|
||||
trace_passed = self AIPhysicsTracePassed( self.origin + HEIGHT_OFFSET_COOR, codeMovePoint + HEIGHT_OFFSET_COOR, self.radius - RADIUS_OFFSET, self.height - HEIGHT_OFFSET );
|
||||
if ( trace_passed )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
shouldStartMove()
|
||||
{
|
||||
angleIndex = getStartMoveAngleIndex();
|
||||
|
||||
return ( angleIndex < 3 || angleIndex > 5 ); //We do not want to do start move if the look ahead direction is straight ahead or 45 degree to either side
|
||||
}
|
||||
|
||||
getStartMoveAngleIndex()
|
||||
{
|
||||
return GetAngleIndexFromSelfYaw( self GetLookaheadDir() );
|
||||
}
|
||||
842
maps/mp/agents/alien/_alien_spitter.gsc
Normal file
842
maps/mp/agents/alien/_alien_spitter.gsc
Normal file
@@ -0,0 +1,842 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
ALIEN_SPIT_ATTACK_DISTANCE_MAX_SQ = 1440000; // 1200 * 1200
|
||||
ALIEN_ESCAPE_SPIT_ATTACK_DISTANCE_MAX_SQ = 3240000; // 1800 * 1800
|
||||
MIN_SPIT_TIMES = 3;
|
||||
MAX_SPIT_TIMES = 6;
|
||||
|
||||
SPITTER_NODE_DURATION = 10; // Max length of time they stay at one spit node
|
||||
SPITTER_FIRE_INTERVAL_MIN = 1.5; // Min amount of time in between a projectile fire
|
||||
SPITTER_FIRE_INTERVAL_MAX = 3.0; // Max amount of time in between a projectile fire
|
||||
SPITTER_PROJECTILE_BARRAGE_SIZE_MIN = 2; // Number of small projectiles to shoot at a time when not shooting a gas cloud
|
||||
SPITTER_PROJECTILE_BARRAGE_SIZE_MAX = 3; // Number of small projectiles to shoot at a time when not shooting a gas cloud
|
||||
|
||||
SPITTER_GAS_CLOUD_FIRE_INTERVAL_MIN = 10.0; // Min amount of time in between a gas cloud projectile fire
|
||||
SPITTER_GAS_CLOUD_FIRE_INTERVAL_MAX = 15.0; // Max amount of time in between a gas cloud projectile fire
|
||||
SPITTER_GAS_CLOUD_MAX_COUNT = 3; // Max number of active gas clouds in a level
|
||||
|
||||
SPITTER_NODE_DAMAGE_DELAY = 0.1; // How long they wait to move from a spit node after getting damaged
|
||||
SPITTER_MIN_PLAYER_DISTANCE_SQ = 90000.0; // If player gets within 300 units, they'll move to a new spit node
|
||||
SPITTER_MOVE_MIN_PLAYER_DISTANCE_SQ = 40000.0; // If player gets within 200 units while alien is moving, they'll stop and spit a projectile at them
|
||||
SPITTER_NODE_INITIAL_FIRE_DELAY_SCALE = 0.5; // Initial scale on delay before a spitter can spit after getting to a node
|
||||
SPITTER_NO_TARGET_NODE_MOVE_TIME = 1.0; // If no targets at current node for this time, spitter will move
|
||||
|
||||
SPITTER_AOE_HEIGHT = 128; // Height of gas cloud
|
||||
SPITTER_AOE_RADIUS = 150; // Radius of gas cloud
|
||||
SPITTER_AOE_DURATION = 10.0; // How long the gas cloud lasts
|
||||
SPITTER_AOE_DELAY = 2.0; // How long after projectile explodes before gas cloud damage is applied
|
||||
SPITTER_AOE_DAMAGE_PER_SECOND = 12.0; // Damage per second at center of gas cloud
|
||||
|
||||
SPITTER_TIME_BETWEEN_SPITS = 3.33; // SPITTER_AOE_DURATION / SPITTER_GAS_CLOUD_MAX_COUNT
|
||||
|
||||
SPITTER_LOOK_AHEAD_PERCENTAGE = 0.5; // how accurately the spitters lead the players
|
||||
SPITTER_ESCAPE_LOOK_AHEAD_PERCENTAGE = 1.0; // how accurately the spitters lead the players during escape sequence
|
||||
|
||||
load_spitter_fx()
|
||||
{
|
||||
level._effect[ "spit_AOE" ] = LoadFX( "vfx/gameplay/alien/vfx_alien_spitter_gas_cloud" );
|
||||
level._effect[ "spit_AOE_small" ] = LoadFX( "vfx/gameplay/alien/vfx_alien_spitter_gas_cloud_64" );
|
||||
}
|
||||
|
||||
spitter_init()
|
||||
{
|
||||
self.gas_cloud_available = true;
|
||||
}
|
||||
|
||||
spitter_death()
|
||||
{
|
||||
release_spit_node();
|
||||
}
|
||||
|
||||
is_escape_sequence_active()
|
||||
{
|
||||
return ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) );
|
||||
}
|
||||
|
||||
get_max_spit_distance_squared()
|
||||
{
|
||||
if ( is_escape_sequence_active() )
|
||||
return ALIEN_ESCAPE_SPIT_ATTACK_DISTANCE_MAX_SQ;
|
||||
|
||||
return ALIEN_SPIT_ATTACK_DISTANCE_MAX_SQ;
|
||||
}
|
||||
|
||||
get_lookahead_percentage()
|
||||
{
|
||||
if ( is_escape_sequence_active() )
|
||||
return SPITTER_ESCAPE_LOOK_AHEAD_PERCENTAGE;
|
||||
|
||||
return SPITTER_LOOK_AHEAD_PERCENTAGE;
|
||||
}
|
||||
|
||||
spit_projectile( enemy )
|
||||
{
|
||||
if ( self.spit_type == "gas_cloud" )
|
||||
{
|
||||
level.spitter_last_cloud_time = gettime();
|
||||
}
|
||||
|
||||
self.melee_type = "spit";
|
||||
|
||||
self.spit_target = enemy;
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
}
|
||||
|
||||
spit_attack( enemy )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "spit_attack" ); #/
|
||||
self endon( "melee_pain_interrupt" );
|
||||
isEnemyChopper = isdefined( enemy ) && isdefined( enemy.code_classname ) && enemy.code_classname == "script_vehicle";
|
||||
|
||||
if ( isEnemyChopper )
|
||||
targetedEnemy = enemy;
|
||||
else
|
||||
targetedEnemy = self.spit_target;
|
||||
|
||||
targetedEnemy endon( "death" );
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( targetedEnemy );
|
||||
|
||||
if ( IsAlive( targetedEnemy ) )
|
||||
{
|
||||
self.spit_target = targetedEnemy;
|
||||
|
||||
if ( isEnemyChopper )
|
||||
{
|
||||
aim_ahead_factor = 5; // factor of speed MPH
|
||||
aim_ahead_unit_vec = VectorNormalize( AnglesToForward( targetedEnemy.angles ) ); // direction
|
||||
aim_ahead_speed_mag = Length( targetedEnemy Vehicle_GetVelocity() ) * aim_ahead_factor; // scaler
|
||||
aim_ahead_vec = aim_ahead_unit_vec * aim_ahead_speed_mag; // aim ahead offset vector
|
||||
|
||||
self.spit_target_location = targetedEnemy.origin + aim_ahead_vec + ( 0, 0, 32 ); // offset by 32 down from origin as origin is at rotor
|
||||
}
|
||||
else
|
||||
{
|
||||
self.spit_target_location = targetedEnemy.origin;
|
||||
}
|
||||
|
||||
self.looktarget = targetedEnemy;
|
||||
|
||||
self set_alien_emissive( 0.2, 1.0 );
|
||||
|
||||
if ( IsDefined ( self.current_spit_node ) && !maps\mp\alien\_utility::is_normal_upright( AnglesToUp( self.current_spit_node.angles ) ) )
|
||||
{
|
||||
up = AnglesToUp( self.current_spit_node.angles );
|
||||
forward = AnglesToForward( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
}
|
||||
else if ( IsDefined( self.enemy ) && targetedEnemy == self.enemy )
|
||||
{
|
||||
self ScrAgentSetOrientMode( "face enemy" );
|
||||
}
|
||||
else
|
||||
{
|
||||
forward = VectorNormalize( targetedEnemy.origin - self.origin );
|
||||
if ( IsDefined( self.current_spit_node ) )
|
||||
up = AnglesToUp( self.current_spit_node.angles );
|
||||
else
|
||||
up = AnglesToUp( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
}
|
||||
|
||||
if( self.oriented )
|
||||
self ScrAgentSetAnimMode( "anim angle delta" );
|
||||
else
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
play_spit_anim();
|
||||
}
|
||||
|
||||
self set_alien_emissive_default( 0.2 );
|
||||
self.looktarget = undefined;
|
||||
self.spit_target = undefined;
|
||||
self.spit_target_location = undefined;
|
||||
self.spit_type = undefined;
|
||||
}
|
||||
|
||||
play_spit_anim()
|
||||
{
|
||||
switch ( self.spit_type )
|
||||
{
|
||||
case "close_range":
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "close_spit_attack", "spit_attack", "end", ::handleAttackNotetracks );
|
||||
break;
|
||||
case "gas_cloud":
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "gas_spit_attack", "spit_attack", "end", ::handleAttackNotetracks );
|
||||
break;
|
||||
case "long_range":
|
||||
barrage_count = RandomIntRange( SPITTER_PROJECTILE_BARRAGE_SIZE_MIN, SPITTER_PROJECTILE_BARRAGE_SIZE_MAX );
|
||||
for ( spitIndex = 0; spitIndex < barrage_count; spitIndex++ )
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "long_range_spit_attack", "spit_attack", "end", ::handleAttackNotetracks );
|
||||
break;
|
||||
default:
|
||||
AssertMsg( self.spit_type + " is an invalid spit type!" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get_best_spit_target( targeted_enemy )
|
||||
{
|
||||
if ( cointoss() && get_alien_type() != "seeder" )
|
||||
{
|
||||
griefTargets = get_grief_targets();
|
||||
|
||||
foreach ( griefTarget in griefTargets )
|
||||
{
|
||||
if ( is_valid_spit_target( griefTarget, false ) )
|
||||
return griefTarget;
|
||||
}
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
if ( IsDefined( targeted_enemy ) )
|
||||
{
|
||||
if ( IsAlive( targeted_enemy ) && is_valid_spit_target( targeted_enemy, false ) )
|
||||
return targeted_enemy;
|
||||
}
|
||||
|
||||
possibleTargets = self get_current_possible_targets();
|
||||
MAX_TARGET_TESTS_PER_FRAME = 4;
|
||||
currentTestsThisFrame = 0;
|
||||
|
||||
foreach ( possibleTarget in possibleTargets )
|
||||
{
|
||||
if ( !IsAlive( possibleTarget ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( targeted_enemy ) && possibleTarget == targeted_enemy )
|
||||
continue;
|
||||
|
||||
if ( is_valid_spit_target( possibleTarget, true ) )
|
||||
return possibleTarget;
|
||||
|
||||
currentTestsThisFrame++;
|
||||
if ( currentTestsThisFrame >= MAX_TARGET_TESTS_PER_FRAME )
|
||||
{
|
||||
waitframe();
|
||||
currentTestsThisFrame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get_grief_targets()
|
||||
{
|
||||
griefTargets = [];
|
||||
if ( !can_spit_gas_cloud( ) || is_pet())
|
||||
return griefTargets;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( !IsAlive( player ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( player.inLastStand ) && player.inLastStand )
|
||||
griefTargets[griefTargets.size] = player;
|
||||
}
|
||||
|
||||
if ( IsDefined( level.drill ) && IsDefined( level.drill.state ) && level.drill.state == "offline" )
|
||||
griefTargets[griefTargets.size] = level.drill;
|
||||
|
||||
return array_randomize( griefTargets );
|
||||
}
|
||||
|
||||
is_valid_spit_target( spit_target, check_attacker_values )
|
||||
{
|
||||
if ( !isAlive( spit_target ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( check_attacker_values && IsPlayer( spit_target ) && !has_attacker_space( spit_target ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
maxValidDistanceSq = get_max_spit_distance_squared();
|
||||
|
||||
flatDistanceToTargetSquared = Distance2DSquared( self.origin, spit_target.origin );
|
||||
if ( flatDistanceToTargetSquared > maxValidDistanceSq )
|
||||
return false;
|
||||
|
||||
self.looktarget = spit_target;
|
||||
|
||||
if ( !isAlive( spit_target ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (( isPlayer( spit_target ) || IsSentient( spit_target )) && !IsDefined( spit_target.usingRemote ) )
|
||||
endPos = spit_target getEye();
|
||||
else
|
||||
endPos = spit_target.origin;
|
||||
|
||||
spitFirePos = self GetTagOrigin( "TAG_BREATH" );
|
||||
return BulletTracePassed( spitFirePos, endPos, false, self );
|
||||
}
|
||||
|
||||
get_spit_fire_pos( spit_target )
|
||||
{
|
||||
return self GetTagOrigin( "TAG_BREATH" );
|
||||
}
|
||||
|
||||
has_attacker_space( player )
|
||||
{
|
||||
maxValidAttackerValue = level.maxAlienAttackerDifficultyValue - level.alien_types[ self.alien_type ].attributes[ "attacker_difficulty" ];
|
||||
|
||||
targetedAttackerScore = maps\mp\agents\alien\_alien_think::get_current_attacker_value( player );
|
||||
|
||||
return ( targetedAttackerScore <= maxValidAttackerValue );
|
||||
}
|
||||
|
||||
handleAttackNotetracks( note, animState, animIndex, animTime )
|
||||
{
|
||||
if( isDefined( level.dlc_attacknotetrack_override_func ))
|
||||
{
|
||||
self [[level.dlc_attacknotetrack_override_func]]( note, animState, animIndex, animTime );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( note == "spit" )
|
||||
return self fire_spit_projectile();
|
||||
}
|
||||
|
||||
fire_spit_projectile()
|
||||
{
|
||||
if ( !IsDefined( self.spit_target ) && !IsDefined( self.spit_target_location ) )
|
||||
return;
|
||||
|
||||
hasValidTarget = IsAlive( self.spit_target );
|
||||
isTargetChopper = isdefined( self.spit_target.code_classname ) && self.spit_target.code_classname == "script_vehicle";
|
||||
if ( hasValidTarget && !isTargetChopper )
|
||||
targetLocation = self.spit_target.origin;
|
||||
else
|
||||
targetLocation = self.spit_target_location;
|
||||
|
||||
if ( self.spit_type == "gas_cloud" )
|
||||
{
|
||||
spit_gas_cloud_projectile( targetLocation );
|
||||
}
|
||||
else if ( hasValidTarget )
|
||||
{
|
||||
PROJECTILE_SPEED = 1400;
|
||||
targetLocation = get_lookahead_target_location( PROJECTILE_SPEED, self.spit_target, false );
|
||||
if ( !BulletTracePassed( targetLocation, get_spit_fire_pos( targetLocation ), false, self ) )
|
||||
targetLocation = get_lookahead_target_location( PROJECTILE_SPEED, self.spit_target, true );
|
||||
|
||||
spit_basic_projectile( targetLocation );
|
||||
}
|
||||
}
|
||||
|
||||
get_lookahead_target_location( projectile_speed, target, use_eye_location )
|
||||
{
|
||||
if ( !IsPlayer( target ) )
|
||||
return target.origin;
|
||||
|
||||
lookAheadPercentage = get_lookahead_percentage();
|
||||
|
||||
if ( use_eye_location && !IsDefined( target.usingRemote ))
|
||||
targetLocation = target GetEye();
|
||||
else
|
||||
targetLocation = target.origin;
|
||||
|
||||
distanceToTarget = Distance( self.origin, targetLocation);
|
||||
timeToImpact = distanceToTarget / projectile_speed;
|
||||
targetVelocity = target GetVelocity();
|
||||
|
||||
return targetLocation + targetVelocity * lookAheadPercentage * timeToImpact;
|
||||
}
|
||||
|
||||
can_spit_gas_cloud()
|
||||
{
|
||||
if ( !self.gas_cloud_available )
|
||||
return false;
|
||||
|
||||
if ( isdefined( self.enemy ) && isdefined( self.enemy.no_gas_cloud_attack ) && self.enemy.no_gas_cloud_attack )
|
||||
return false;
|
||||
|
||||
time_since_last_spit = (gettime() - level.spitter_last_cloud_time) * 0.001;
|
||||
|
||||
return level.spitter_gas_cloud_count < SPITTER_GAS_CLOUD_MAX_COUNT && time_since_last_spit > SPITTER_TIME_BETWEEN_SPITS;
|
||||
}
|
||||
|
||||
spit_basic_projectile( targetLocation )
|
||||
{
|
||||
spitFirePos = get_spit_fire_pos( targetLocation );
|
||||
spitProjectile = MagicBullet( "alienspit_mp", spitFirePos, targetLocation, self );
|
||||
spitProjectile.owner = self;
|
||||
|
||||
if ( IsDefined( spitProjectile ) )
|
||||
spitProjectile thread spit_basic_projectile_impact_monitor( self );
|
||||
}
|
||||
|
||||
spit_basic_projectile_impact_monitor( owner )
|
||||
{
|
||||
self waittill( "explode", explodeLocation );
|
||||
|
||||
if ( !IsDefined( explodeLocation ) )
|
||||
return;
|
||||
|
||||
PlayFx( level._effect[ "spit_AOE_small" ], explodeLocation + (0,0,8), (0,0,1), (1,0,0) );
|
||||
}
|
||||
|
||||
spit_gas_cloud_projectile( targetLocation )
|
||||
{
|
||||
spitFirePos = get_spit_fire_pos( targetLocation );
|
||||
spitProjectile = MagicBullet( "alienspit_gas_mp", spitFirePos, targetLocation, self );
|
||||
spitProjectile.owner = self;
|
||||
|
||||
if ( IsDefined( spitProjectile ) )
|
||||
spitProjectile thread spit_gas_cloud_projectile_impact_monitor( self );
|
||||
|
||||
self thread gas_cloud_available_timer();
|
||||
}
|
||||
|
||||
gas_cloud_available_timer()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self.gas_cloud_available = false;
|
||||
cloudInterval = RandomFloatRange( SPITTER_GAS_CLOUD_FIRE_INTERVAL_MIN, SPITTER_GAS_CLOUD_FIRE_INTERVAL_MAX );
|
||||
wait cloudInterval;
|
||||
self.gas_cloud_available = true;
|
||||
}
|
||||
|
||||
spit_gas_cloud_projectile_impact_monitor( owner )
|
||||
{
|
||||
self waittill( "explode", explodeLocation );
|
||||
|
||||
if ( !IsDefined( explodeLocation ) )
|
||||
return;
|
||||
|
||||
trigger = Spawn( "trigger_radius", explodeLocation, 0, SPITTER_AOE_RADIUS, SPITTER_AOE_HEIGHT );
|
||||
// sanity check. Need to come up with more robust fallback
|
||||
if ( !IsDefined( trigger ) )
|
||||
return;
|
||||
|
||||
level.spitter_gas_cloud_count++;
|
||||
trigger.onPlayer = true;
|
||||
PlayFx( level._effect[ "spit_AOE" ], explodeLocation + (0,0,8),(0,0,1), (1,0,0) );
|
||||
thread spit_aoe_cloud_damage( explodeLocation, trigger );
|
||||
level notify( "spitter_spit",explodeLocation );
|
||||
|
||||
wait SPITTER_AOE_DURATION;
|
||||
trigger Delete();
|
||||
level.spitter_gas_cloud_count--;
|
||||
}
|
||||
|
||||
spit_aoe_cloud_damage( impact_location, trigger )
|
||||
{
|
||||
trigger endon( "death" );
|
||||
|
||||
wait SPITTER_AOE_DELAY;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
trigger waittill( "trigger", player );
|
||||
|
||||
if ( !IsPlayer( player ) )
|
||||
continue;
|
||||
|
||||
if ( !IsAlive( player ) )
|
||||
continue;
|
||||
|
||||
disorient_player( player );
|
||||
damage_player( player, trigger );
|
||||
}
|
||||
}
|
||||
|
||||
damage_player( player, trigger )
|
||||
{
|
||||
DAMAGE_INTERVAL = 0.5;
|
||||
|
||||
currentTime = GetTime();
|
||||
|
||||
if ( !IsDefined( player.last_spitter_gas_damage_time ) )
|
||||
{
|
||||
elapsedTime = DAMAGE_INTERVAL;
|
||||
}
|
||||
else if (player.last_spitter_gas_damage_time + DAMAGE_INTERVAL * 1000.0 > currentTime )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
elapsedTime = Min( DAMAGE_INTERVAL, (currentTime - player.last_spitter_gas_damage_time) * 0.001 );
|
||||
}
|
||||
|
||||
gas_damage_scalar = player maps\mp\alien\_perk_utility::perk_GetGasDamageScalar();
|
||||
damageAmount = int(SPITTER_AOE_DAMAGE_PER_SECOND * elapsedTime * gas_damage_scalar );
|
||||
if ( damageAmount > 0 )
|
||||
{
|
||||
player thread [[ level.callbackPlayerDamage ]]( trigger, trigger, damageAmount, 0, "MOD_SUICIDE", "alienspit_gas_mp", trigger.origin, ( 0,0,0 ), "none", 0 );
|
||||
}
|
||||
player.last_spitter_gas_damage_time = currentTime;
|
||||
}
|
||||
|
||||
disorient_player( player )
|
||||
{
|
||||
if ( is_chaos_mode() && player maps\mp\alien\_perk_utility::perk_GetGasDamageScalar() == 0 )
|
||||
return;
|
||||
else if( !player maps\mp\alien\_perk_utility::has_perk( "perk_medic", [ 1,2,3,4 ] ) )
|
||||
{
|
||||
if ( isDefined( level.shell_shock_override ))
|
||||
player [[level.shell_shock_override]]( 0.5 );
|
||||
else
|
||||
player ShellShock( "alien_spitter_gas_cloud", 0.5 );
|
||||
}
|
||||
}
|
||||
|
||||
get_RL_toward( target )
|
||||
{
|
||||
//Return the right/left vector toward target
|
||||
self_to_target_angles = VectorToAngles( target.origin - self.origin );
|
||||
target_direction = anglesToRight( self_to_target_angles );
|
||||
|
||||
if ( common_scripts\utility::cointoss())
|
||||
target_direction *= -1;
|
||||
|
||||
return target_direction;
|
||||
}
|
||||
|
||||
spitter_combat( enemy )
|
||||
{
|
||||
self endon( "bad_path" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
attackNode = find_spitter_attack_node( self.enemy );
|
||||
|
||||
if ( IsDefined( attackNode ) )
|
||||
{
|
||||
move_to_spitter_attack_node( attackNode );
|
||||
spitter_attack( self.enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
release_spit_node()
|
||||
{
|
||||
if ( IsDefined( self.current_spit_node ) )
|
||||
{
|
||||
self ScrAgentRelinquishClaimedNode( self.current_spit_node );
|
||||
self.current_spit_node.claimed = false;
|
||||
self.current_spit_node = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
claim_spit_node( spit_node )
|
||||
{
|
||||
self.current_spit_node = spit_node;
|
||||
spit_node.claimed = true;
|
||||
self ScrAgentClaimNode( spit_node );
|
||||
}
|
||||
|
||||
move_to_spitter_attack_node( attack_node )
|
||||
{
|
||||
self endon( "player_proximity_during_move" );
|
||||
|
||||
release_spit_node();
|
||||
claim_spit_node( attack_node );
|
||||
|
||||
self ScrAgentSetGoalNode( attack_node );
|
||||
self ScrAgentSetGoalRadius( 64 );
|
||||
self thread enemy_proximity_during_move_monitor();
|
||||
self waittill( "goal_reached" );
|
||||
}
|
||||
|
||||
enemy_proximity_during_move_monitor()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "goal_reached" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
wait 0.05;
|
||||
|
||||
if ( !is_normal_upright( AnglesToUp( self.angles ) ) )
|
||||
continue;
|
||||
|
||||
if ( !self maps\mp\agents\alien\_alien_think::melee_okay() )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( self.valid_moving_spit_attack_time ) && GetTime() < self.valid_moving_spit_attack_time )
|
||||
continue;
|
||||
|
||||
closePlayer = find_player_within_distance( SPITTER_MOVE_MIN_PLAYER_DISTANCE_SQ );
|
||||
if ( IsDefined( closePlayer ) )
|
||||
break;
|
||||
}
|
||||
|
||||
release_spit_node();
|
||||
self notify( "player_proximity_during_move" );
|
||||
self ScrAgentSetGoalEntity( closePlayer );
|
||||
self ScrAgentSetGoalRadius( 2048.0 );
|
||||
self waittill( "goal_reached" );
|
||||
}
|
||||
|
||||
get_possible_spitter_attack_nodes( target_entity )
|
||||
{
|
||||
if( get_alien_type() == "seeder" )
|
||||
attackNodes = GetNodesInRadius( target_entity.origin, 768, 128, 512, "jump attack" );
|
||||
else
|
||||
attackNodes = GetNodesInRadius( target_entity.origin, 1000, 300, 512, "jump attack" );
|
||||
|
||||
validNodes = [];
|
||||
|
||||
foreach( attackNode in attackNodes )
|
||||
{
|
||||
if ( IsDefined( attackNode.claimed) && attackNode.claimed )
|
||||
continue;
|
||||
|
||||
validNodes[validNodes.size] = attackNode;
|
||||
}
|
||||
|
||||
return validNodes;
|
||||
|
||||
}
|
||||
|
||||
is_pet()
|
||||
{
|
||||
return ( IsDefined( self.pet ) && self.pet );
|
||||
}
|
||||
|
||||
get_current_possible_targets()
|
||||
{
|
||||
if ( is_pet() )
|
||||
return level.agentArray;
|
||||
else
|
||||
return level.players;
|
||||
}
|
||||
|
||||
find_spitter_attack_node( target_enemy )
|
||||
{
|
||||
nearbySpitNodes = [];
|
||||
|
||||
if ( is_escape_sequence_active() && IsDefined( level.escape_spitter_target_node ) )
|
||||
{
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( level.escape_spitter_target_node );
|
||||
if ( nearbySpitNodes.size > 0 )
|
||||
target_enemy = level.escape_spitter_target_node;
|
||||
}
|
||||
|
||||
if ( nearbySpitNodes.size == 0 && IsDefined( target_enemy ) )
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( target_enemy );
|
||||
|
||||
if ( nearbySpitNodes.size == 0 )
|
||||
{
|
||||
possibleTargets = self get_current_possible_targets();
|
||||
foreach ( possibleTarget in possibleTargets )
|
||||
{
|
||||
wait 0.05;
|
||||
|
||||
if ( !IsAlive( possibleTarget ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( target_enemy ) && possibleTarget == target_enemy )
|
||||
continue;
|
||||
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( possibleTarget );
|
||||
if ( nearbySpitNodes.size > 0 )
|
||||
{
|
||||
target_enemy = possibleTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( nearbySpitNodes.size == 0 )
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( self );
|
||||
|
||||
if ( nearbySpitNodes.size == 0 )
|
||||
return undefined;
|
||||
|
||||
filters = [];
|
||||
|
||||
if ( IsDefined( target_enemy ) )
|
||||
{
|
||||
filters[ "dist_from_enemy_weight" ] = 8.0;
|
||||
filters[ "enemy_los_weight" ] = 6.0;
|
||||
filters[ "height_weight" ] = 4.0;
|
||||
target_direction = get_RL_toward( target_enemy );
|
||||
target_enemy endon( "death" );
|
||||
}
|
||||
else
|
||||
{
|
||||
filters[ "dist_from_enemy_weight" ] = 0.0;
|
||||
filters[ "enemy_los_weight" ] = 0.0;
|
||||
filters[ "height_weight" ] = 0.0;
|
||||
target_direction = get_central_enemies_direction();
|
||||
}
|
||||
|
||||
filters[ "direction" ] = "override";
|
||||
filters[ "direction_override" ] = target_direction;
|
||||
filters[ "direction_weight" ] = 1.0;
|
||||
filters[ "min_height" ] = 64.0;
|
||||
filters[ "max_height" ] = 400.0;
|
||||
filters[ "enemy_los" ] = true;
|
||||
filters[ "min_dist_from_enemy" ] = 300.0;
|
||||
filters[ "max_dist_from_enemy" ] = 800.0;
|
||||
filters[ "desired_dist_from_enemy" ] = 600.0;
|
||||
filters[ "min_dist_from_all_enemies" ] = 300.0;
|
||||
filters[ "min_dist_from_all_enemies_weight" ] = 5.0;
|
||||
filters[ "not_recently_used_weight" ] = 10.0;
|
||||
filters[ "recently_used_time_limit" ] = 30.0;
|
||||
filters[ "random_weight" ] = 1.0;
|
||||
|
||||
result = maps\mp\agents\alien\_alien_think::get_retreat_node_rated( target_enemy, filters, nearbySpitNodes );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
get_central_enemies_direction()
|
||||
{
|
||||
possibleTargets = self get_current_possible_targets();
|
||||
|
||||
if ( possibleTargets.size == 0)
|
||||
return self.origin + AnglesToForward( self.angles ) * 100;
|
||||
|
||||
centralLocation = ( 0, 0, 0 );
|
||||
|
||||
foreach ( possibleTarget in possibleTargets )
|
||||
centralLocation += possibleTarget.origin;
|
||||
|
||||
centralLocation = centralLocation / possibleTargets.size;
|
||||
|
||||
return centralLocation - self.origin;
|
||||
}
|
||||
|
||||
spitter_attack( enemy )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
|
||||
if ( !IsDefined( self.current_spit_node ) )
|
||||
{
|
||||
choose_spit_type( "close_range" );
|
||||
spit_projectile( enemy );
|
||||
self.valid_moving_spit_attack_time = GetTime() + RandomFloatRange( SPITTER_FIRE_INTERVAL_MIN, SPITTER_FIRE_INTERVAL_MAX ) * 1000.0;
|
||||
return;
|
||||
}
|
||||
|
||||
set_up_attack_node_watchers();
|
||||
|
||||
if ( !is_escape_sequence_active() )
|
||||
wait RandomFloatRange( SPITTER_FIRE_INTERVAL_MIN, SPITTER_FIRE_INTERVAL_MAX ) * SPITTER_NODE_INITIAL_FIRE_DELAY_SCALE;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
targetedEnemy = undefined;
|
||||
no_target_time = 0.0;
|
||||
while ( !IsDefined( targetedEnemy ) )
|
||||
{
|
||||
no_target_time += 0.2;
|
||||
if ( no_target_time >= SPITTER_NO_TARGET_NODE_MOVE_TIME )
|
||||
{
|
||||
return;
|
||||
}
|
||||
wait 0.2;
|
||||
|
||||
if ( IsDefined( enemy ) && IsDefined( enemy.code_classname ) && enemy.code_classname == "script_vehicle" )
|
||||
{
|
||||
targetedEnemy = enemy;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetedEnemy = get_best_spit_target( enemy );
|
||||
}
|
||||
}
|
||||
|
||||
choose_spit_type( "long_range" );
|
||||
spit_projectile( targetedEnemy );
|
||||
wait RandomFloatRange( SPITTER_FIRE_INTERVAL_MIN, SPITTER_FIRE_INTERVAL_MAX );
|
||||
}
|
||||
}
|
||||
|
||||
choose_spit_type( default_type )
|
||||
{
|
||||
if ( !is_pet() && can_spit_gas_cloud() )
|
||||
self.spit_type = "gas_cloud";
|
||||
else
|
||||
self.spit_type = default_type;
|
||||
}
|
||||
|
||||
set_up_attack_node_watchers()
|
||||
{
|
||||
self thread spitter_node_duration_monitor( SPITTER_NODE_DURATION );
|
||||
self thread spitter_node_attacked_monitor( SPITTER_NODE_DAMAGE_DELAY );
|
||||
|
||||
if ( !is_pet() )
|
||||
self thread spitter_node_player_proximity( SPITTER_MIN_PLAYER_DISTANCE_SQ );
|
||||
}
|
||||
|
||||
spitter_node_duration_monitor( duration )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
wait duration;
|
||||
|
||||
self notify( "spitter_node_move_requested" );
|
||||
}
|
||||
|
||||
spitter_node_attacked_monitor( damage_delay )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
self waittill( "damage" );
|
||||
wait damage_delay;
|
||||
|
||||
self notify( "spitter_node_move_requested" );
|
||||
}
|
||||
|
||||
spitter_node_player_proximity( min_player_distances_sq )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
closePlayer = find_player_within_distance( min_player_distances_sq );
|
||||
if ( IsDefined( closePlayer ) )
|
||||
break;
|
||||
|
||||
wait 0.2;
|
||||
}
|
||||
|
||||
self notify( "spitter_node_move_requested" );
|
||||
}
|
||||
|
||||
find_player_within_distance( distance_sq )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( !IsAlive( player ) )
|
||||
continue;
|
||||
|
||||
flatDistanceToPlayerSq = Distance2DSquared( self.origin, player.origin );
|
||||
if ( flatDistanceToPlayerSq < distance_sq )
|
||||
return player;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
2949
maps/mp/agents/alien/_alien_think.gsc
Normal file
2949
maps/mp/agents/alien/_alien_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
681
maps/mp/agents/alien/_alien_traverse.gsc
Normal file
681
maps/mp/agents/alien/_alien_traverse.gsc
Normal file
@@ -0,0 +1,681 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
assert( IsDefined( startNode ) && IsDefined( endNode ) );
|
||||
|
||||
if ( startNode.type == "Jump" || startNode.type == "Jump Attack" )
|
||||
{
|
||||
nextNode = self GetNegotiationNextNode();
|
||||
|
||||
if ( IsDefined( startNode.target ) && IsDefined( endNode.targetname ) && startNode.target == endNode.targetname )
|
||||
{
|
||||
self.traverseType = "canned";
|
||||
self DoTraverse( startNode, endNode );
|
||||
return;
|
||||
}
|
||||
|
||||
attackableEnemy = find_attackable_enemy_at_node( endNode );
|
||||
if ( IsDefined( attackableEnemy ) )
|
||||
{
|
||||
self.traverseType = "jump_attack";
|
||||
self.leapEndPos = endNode.origin;
|
||||
|
||||
self maps\mp\agents\alien\_alien_melee::melee_leap( attackableEnemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.traverseType = "jump";
|
||||
self Jump( startNode, endNode, nextNode );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.traverseType = "canned";
|
||||
self doTraverse( startNode, endNode );
|
||||
}
|
||||
}
|
||||
|
||||
find_attackable_enemy_at_node( nodeToCheck )
|
||||
{
|
||||
if (( self maps\mp\alien\_utility::get_alien_type() == "spitter" ) ||
|
||||
( self maps\mp\alien\_utility::get_alien_type() == "seeder" ))
|
||||
return undefined;
|
||||
|
||||
CLOSE_PLAYER_DIST_SQ = 128 * 128;
|
||||
COS_45 = 0.707;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( DistanceSquared( player.origin, nodeToCheck.origin ) > CLOSE_PLAYER_DIST_SQ )
|
||||
continue;
|
||||
|
||||
playerToNode = VectorNormalize( nodeToCheck.origin - player.origin );
|
||||
playerForward = AnglesToForward( player.angles );
|
||||
forwardDot = VectorDot( playerToNode, playerForward );
|
||||
|
||||
if ( forwardDot > COS_45 )
|
||||
return player;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
if ( self.traverseType == "jump" )
|
||||
{
|
||||
self.previousAnimState = "traverse_jump";
|
||||
}
|
||||
else if ( self.traverseType == "jump_attack" )
|
||||
{
|
||||
self.previousAnimState = "traverse_jump_attack";
|
||||
}
|
||||
else
|
||||
{
|
||||
self.previousAnimState = "traverse_canned";
|
||||
}
|
||||
self.traverseType = undefined;
|
||||
}
|
||||
|
||||
|
||||
Jump( startNode, endNode, nextNode )
|
||||
{
|
||||
nextPos = undefined;
|
||||
if ( IsDefined( nextNode ) )
|
||||
nextPos = nextNode.origin;
|
||||
|
||||
if ( isDefined( level.dlc_alien_jump_override ) )
|
||||
{
|
||||
[[level.dlc_alien_jump_override]]( startNode, endNode, nextNode, nextPos );
|
||||
return;
|
||||
}
|
||||
|
||||
self maps\mp\agents\alien\_alien_jump::Jump( startNode.origin, startNode.angles, endNode.origin, endNode.angles, nextPos, undefined, endNode.script_noteworthy );
|
||||
}
|
||||
|
||||
doTraverse( startNode, endNode )
|
||||
{
|
||||
traverseData = level.alienAnimData.cannedTraverseAnims[ startNode.animscript ];
|
||||
AssertEx( isDefined( traverseData ), "Traversal '" + startNode.animscript + "' is not supported" );
|
||||
|
||||
animState = traverseData [ "animState" ];
|
||||
AssertEx( isDefined( animState ), "No animState specified for traversal '" + startNode.animscript + "'" );
|
||||
|
||||
self.startNode = startNode;
|
||||
self.endNode = endNode;
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", startNode.angles );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
if ( isdefined( traverseData[ "traverseSound" ] ) )
|
||||
self thread maps\mp\_utility::play_sound_on_tag( traverseData[ "traverseSound" ] );
|
||||
|
||||
if ( isdefined( traverseData[ "traverseAnimScale" ] ) )
|
||||
self ScrAgentSetAnimScale( traverseData[ "traverseAnimScale" ], traverseData[ "traverseAnimScale" ] );
|
||||
|
||||
switch ( animState )
|
||||
{
|
||||
case "traverse_climb_up":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up", self GetAnimEntry( "traverse_climb_up", 4 ) );
|
||||
break;
|
||||
|
||||
case "traverse_climb_up_over_56":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up_over_56" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_up_ledge_18_run":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up_ledge_18_run" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_up_ledge_18_idle":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up_ledge_18_idle" );
|
||||
break;
|
||||
|
||||
case "climb_up_end_jump_side_l":
|
||||
alienClimbUp( startNode, endNode, "climb_up_end_jump_side_l" );
|
||||
break;
|
||||
|
||||
case "climb_up_end_jump_side_r":
|
||||
alienClimbUp( startNode, endNode, "climb_up_end_jump_side_r" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_down":
|
||||
alienClimbDown( startNode, endNode, "traverse_climb_down" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_over_56_down":
|
||||
alienClimbDown( startNode, endNode, "traverse_climb_over_56_down" );
|
||||
break;
|
||||
case "run":
|
||||
alienWallRun( startNode, endNode, "run" );
|
||||
break;
|
||||
|
||||
default:
|
||||
alienRegularTraversal( startNode, animState, traverseData [ "animIndexArray" ], traverseData [ "endInOriented" ], traverseData [ "flexHeightEndAtTraverseEnd" ] );
|
||||
break;
|
||||
}
|
||||
|
||||
self.startNode = undefined;
|
||||
self.endNode = undefined;
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
|
||||
alienRegularTraversal( startNode, animState, animIndexArray, endInOriented, flexHeightEndAtTraverseEnd )
|
||||
{
|
||||
animIndex = animIndexArray [ RandomInt ( animIndexArray.size ) ];
|
||||
animEntry = self GetAnimEntry( animState, animIndex );
|
||||
result = needFlexibleHeightSupport( animEntry );
|
||||
animTime = GetAnimLength( animEntry );
|
||||
|
||||
self traverseAnimLerp( animEntry, startNode );
|
||||
|
||||
// If we have an apex, move us away from our wall on death
|
||||
if ( AnimHasNotetrack( animEntry, "highest_point" ) )
|
||||
self.apexTraversalDeathVector = VectorNormalize( self.startNode.origin - self.endNode.origin );
|
||||
|
||||
// If we are pointing to an entity, assume it's a scriptable
|
||||
scriptable = GetEnt( startnode.target, "targetname" );
|
||||
if ( IsDefined( scriptable ) )
|
||||
{
|
||||
scriptable thread runScriptableTraverse( animTime );
|
||||
}
|
||||
|
||||
if( result.need_support )
|
||||
doTraversalWithFlexibleHeight( animState, animIndex, animEntry, result.start_notetrack, result.end_notetrack, flexHeightEndAtTraverseEnd, ::alienTraverseNotetrackHandler );
|
||||
else
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, "canned_traverse", "end", ::alienTraverseNotetrackHandler );
|
||||
|
||||
endRegularTraversal( endInOriented );
|
||||
}
|
||||
|
||||
runScriptableTraverse( animTime )
|
||||
{
|
||||
self notify( "stop_previous_traversal" );
|
||||
self endon( "stop_previous_traversal" );
|
||||
self SetScriptablePartState( 0, 1 );//plays the animation
|
||||
wait animTime;
|
||||
self SetScriptablePartState( 0, 0 );//resets the scriptable state
|
||||
}
|
||||
|
||||
endRegularTraversal( endInOriented )
|
||||
{
|
||||
if( endInOriented )
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self.oriented = true;
|
||||
self.ignoreme = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self.oriented = false;
|
||||
self.ignoreme = false;
|
||||
}
|
||||
}
|
||||
|
||||
needFlexibleHeightSupport( animEntry )
|
||||
{
|
||||
result = spawnStruct();
|
||||
|
||||
if ( AnimHasNotetrack ( animEntry, "traverse_up" ) )
|
||||
{
|
||||
result.need_support = true;
|
||||
result.start_notetrack = "traverse_up";
|
||||
result.end_notetrack = "traverse_up_end";
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( AnimHasNotetrack ( animEntry, "traverse_drop" ) )
|
||||
{
|
||||
result.need_support = true;
|
||||
result.start_notetrack = "traverse_drop";
|
||||
result.end_notetrack = "traverse_drop_end";
|
||||
return result;
|
||||
}
|
||||
|
||||
result.need_support = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
doTraversalWithFlexibleHeight( animState, animIndex, animEntry, startNotetrack, endNotetrack, flexHeightEndAtTraverseEnd, notetrackHandlerFunc )
|
||||
{
|
||||
CONST_TRAVERSAL_ANIM_LABEL = "canned_traverse";
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, CONST_TRAVERSAL_ANIM_LABEL, startNotetrack, notetrackHandlerFunc );
|
||||
|
||||
if ( flexHeightEndAtTraverseEnd )
|
||||
{
|
||||
flex_height_end_pos = self.endNode.origin;
|
||||
flex_height_anim_end_time = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertEx( isDefined( self.endNode.target ), "Traversal " + animState + " " + animIndex + " at " + self.origin + ". Need to link a script struct from the traversal end point to mark the apex point for the animation" );
|
||||
flex_height_end_pos = common_scripts\utility::getstruct( self.endNode.target, "targetname" );
|
||||
AssertEx( isDefined( flex_height_end_pos ), "Traversal " + animState + " " + animIndex + " at " + self.origin + ". Unable to find the apex point struct" );
|
||||
flex_height_end_pos = flex_height_end_pos.origin;
|
||||
apexNotetrackTimes = GetNotetrackTimes( animEntry, "highest_point" );
|
||||
flex_height_anim_end_time = apexNotetrackTimes[ 0 ];
|
||||
AssertEx( isDefined( flex_height_anim_end_time ), "Traversal " + animState + " " + animIndex + " at " + self.origin + ". Missing 'highest_point' notetrack" );
|
||||
}
|
||||
|
||||
doTraversalWithFlexibleHeight_internal( animState, animIndex, CONST_TRAVERSAL_ANIM_LABEL, animEntry, startNotetrack, endNotetrack, flex_height_end_pos, flex_height_anim_end_time, notetrackHandlerFunc );
|
||||
}
|
||||
|
||||
doTraversalWithFlexibleHeight_internal( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, flexHeightEndPos, flexHeightAnimEndTime, notetrackHandlerFunc )
|
||||
{
|
||||
remaining_height = abs( self.origin[ 2 ] - flexHeightEndPos[ 2 ] );
|
||||
|
||||
startNotetrackTimes = GetNotetrackTimes( animEntry, startNotetrack );
|
||||
start_time = startNotetrackTimes[ 0 ];
|
||||
|
||||
endNotetrackTimes = GetNotetrackTimes( animEntry, endNotetrack );
|
||||
end_time = endNotetrackTimes[ 0 ];
|
||||
AssertEx( end_time > start_time, "Traversal " + animState + " " + animIndex + " has incorrectly placed flexible height notetracks." );
|
||||
|
||||
remaining_anim_delta = GetMoveDelta( animEntry, start_time, flexHeightAnimEndTime );
|
||||
remaining_anim_height = abs( remaining_anim_delta[ 2 ] );
|
||||
|
||||
anim_delta_between = GetMoveDelta( animEntry, start_time, end_time );
|
||||
scaled_anim_height = abs( anim_delta_between[ 2 ] );
|
||||
AssertEx( scaled_anim_height > 0.0, "Traversal " + animState + " " + animIndex + " has bad traverse notetracks." );
|
||||
not_scaled_anim_height = remaining_anim_height - scaled_anim_height;
|
||||
|
||||
//<TODO J.C.> When we have time, we need to investigate why this is happening on certain traversals
|
||||
//AssertEx( ( remaining_height - not_scaled_anim_height ) > 0, "Traversal " + animState + " " + animIndex + " at " + self.origin + " has no vertical space to do flexible height." );
|
||||
|
||||
if ( remaining_height <= not_scaled_anim_height )
|
||||
anim_scale = 1;
|
||||
else
|
||||
anim_scale = ( remaining_height - not_scaled_anim_height ) / scaled_anim_height;
|
||||
|
||||
anim_rate = 1 / anim_scale;
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, anim_scale );
|
||||
PlayAnimNAtRateUntilNotetrack( animState, animIndex, anim_rate, animLabel, endNotetrack, notetrackHandlerFunc );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, animLabel, "end", notetrackHandlerFunc );
|
||||
|
||||
self.apexTraversalDeathVector = undefined;
|
||||
}
|
||||
|
||||
alienTraverseNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "apply_physics":
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
break;
|
||||
|
||||
case "highest_point":
|
||||
if ( isDefined( self.apexTraversalDeathVector ) )
|
||||
self.apexTraversalDeathVector *= -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// Special traversals
|
||||
//===========================================
|
||||
|
||||
//////////////////
|
||||
// Climb up
|
||||
|
||||
alienClimbUp( startNode, endNode, animState, longerEndAnim )
|
||||
{
|
||||
startAnim = self GetAnimEntry( animState, 0 );
|
||||
scrabbleAnim = self GetAnimEntry( animState, 1 );
|
||||
loopAnim = self GetAnimEntry( animState, 2 );
|
||||
endAnim = self GetAnimEntry( animState, 3 );
|
||||
|
||||
totalHeight = endNode.origin[ 2 ] - startnode.origin[ 2 ];
|
||||
startAnimHeight = GetMoveDelta( startAnim, 0, 1 )[ 2 ];
|
||||
scrabbleAnimHeight = GetMoveDelta( scrabbleAnim, 0, 1 )[ 2 ];
|
||||
loopAnimHeight = GetMoveDelta( loopAnim, 0, 1 )[ 2 ];
|
||||
endAnimHeight = GetMoveDelta( endAnim, 0, 1 )[ 2 ];
|
||||
longerEndAnimHeight = undefined;
|
||||
|
||||
climbUpNotetrackTime = getNoteTrackTimes( startAnim, "climb_up_teleport" ) [ 0 ];
|
||||
climbUpAnimDeltaBeforeNotetrack = GetMoveDelta( startAnim, 0, climbUpNotetrackTime );
|
||||
climbUpAnimDeltaAfterNotetrack = GetMoveDelta( startAnim, climbUpNotetrackTime, 1 );
|
||||
startAnimHeightAfterNotetrack = climbUpAnimDeltaAfterNotetrack[ 2 ];
|
||||
|
||||
if ( totalHeight < ( startAnimHeight + endAnimHeight ) )
|
||||
Println( "ERROR: Height is too short for " + animState + ". Modify the geo or use another traversal." );
|
||||
|
||||
distForScrabbleAndLoop = totalHeight - ( startAnimHeight + endAnimHeight );
|
||||
canDoScrabble = false;
|
||||
numOfLoop = 0;
|
||||
if ( distForScrabbleAndLoop > 0 )
|
||||
{
|
||||
canDoScrabble = ( distForScrabbleAndLoop - scrabbleAnimHeight ) > 0;
|
||||
numOfLoop = max ( 0, floor ( ( distForScrabbleAndLoop - canDoScrabble * scrabbleAnimHeight ) / loopAnimHeight ) );
|
||||
}
|
||||
|
||||
teleportAnimHeight = canDoScrabble * scrabbleAnimHeight + numOfLoop * loopAnimHeight + startAnimHeightAfterNotetrack;
|
||||
teleportRealHeight = totalHeight - endAnimHeight - ( startAnimHeight - startAnimHeightAfterNotetrack );
|
||||
animScalerZ = teleportRealHeight / teleportAnimHeight;
|
||||
|
||||
canDoLongerEndAnim = false;
|
||||
if ( isDefined ( longerEndAnim ))
|
||||
{
|
||||
longerEndAnimHeight = GetMoveDelta( longerEndAnim, 0, 1 )[ 2 ];
|
||||
endAnimHeightDiff = longerEndAnimHeight - endAnimHeight;
|
||||
canDoLongerEndAnim = ( teleportRealHeight - teleportAnimHeight ) > endAnimHeightDiff;
|
||||
animScalerZ = ( teleportRealHeight - canDoLongerEndAnim * endAnimHeightDiff )/ teleportAnimHeight;
|
||||
}
|
||||
|
||||
selectedEndAnim = endAnim;
|
||||
if ( canDoLongerEndAnim )
|
||||
selectedEndAnim = longerEndAnim;
|
||||
|
||||
stopTeleportNotetrack = getNoteTrackTimes( selectedEndAnim, "stop_teleport" ) [ 0 ];
|
||||
endAnimHeightBeforeNotetrack = GetMoveDelta( selectedEndAnim, 0, stopTeleportNotetrack )[ 2 ];
|
||||
stopToEndAnimDelta = GetMoveDelta( selectedEndAnim, stopTeleportNotetrack, 1 );
|
||||
stopToEndAnimDeltaXY = length( stopToEndAnimDelta * ( 1, 1, 0 ) );
|
||||
|
||||
// startAnim: Play the anim normally until climb_up_teleport notetrack
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
|
||||
self traverseClimbUpLerp( startAnim, startNode );
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, 0, "canned_traverse", "climb_up_teleport" );
|
||||
|
||||
// startAnim: Initial horizontal scaling to make up for any XY displacement. Start to scale to Z.
|
||||
self ScrAgentSetAnimScale( 1, animScalerZ );
|
||||
self WaitUntilNotetrack( "canned_traverse", "end" );
|
||||
|
||||
// scrabble and loop animation: Continue the Z scaling.
|
||||
self ScrAgentSetAnimScale( 1, animScalerZ );
|
||||
if ( canDoScrabble )
|
||||
PlayAnimNUntilNotetrack( animState, 1, "canned_traverse", "finish" );
|
||||
|
||||
for ( i = 0; i < numOfLoop; i++ )
|
||||
{
|
||||
PlayAnimNUntilNotetrack( animState, 2, "canned_traverse", "end" );
|
||||
}
|
||||
|
||||
//Final height adjustment, making sure alien reach enough height and will not end up inside geo when finish the traversal
|
||||
selfToEndHeight = endNode.origin[ 2 ] - self.origin[ 2 ] - stopToEndAnimDelta[ 2 ];
|
||||
animScalerZ = 1.0;
|
||||
if ( selfToEndHeight > endAnimHeightBeforeNotetrack )
|
||||
animScalerZ = selfToEndHeight / endAnimHeightBeforeNotetrack;
|
||||
|
||||
self ScrAgentSetAnimScale( 1, animScalerZ );
|
||||
|
||||
if ( canDoLongerEndAnim )
|
||||
PlayAnimNUntilNotetrack( animState, 4, "canned_traverse", "stop_teleport", ::alienTraverseNotetrackHandler );
|
||||
else
|
||||
PlayAnimNUntilNotetrack( animState, 3, "canned_traverse", "stop_teleport", ::alienTraverseNotetrackHandler );
|
||||
|
||||
//Final horizontal adjustment, making sure alien will end at the traverse End node
|
||||
selfToEndXY = distance2D( self.origin, endNode.origin );
|
||||
animScalerXY = selfToEndXY / stopToEndAnimDeltaXY;
|
||||
|
||||
self ScrAgentSetAnimScale( animScalerXY, 1 );
|
||||
self WaitUntilNotetrack( "canned_traverse", "end" );
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Climb down
|
||||
|
||||
alienClimbDown( startNode, endNode, animState )
|
||||
{
|
||||
startAnim = self GetAnimEntry( animState, 0 );
|
||||
loopAnim = self GetAnimEntry( animState, 1 );
|
||||
slideAnim = self GetAnimEntry( animState, 2 );
|
||||
endAnim = self GetAnimEntry( animState, 3 );
|
||||
jumpOffEndAnim = self GetAnimEntry( animState, 4 );
|
||||
|
||||
totalHeight= startNode.origin[ 2 ] - endNode.origin[ 2 ];
|
||||
startAnimHeight = -1 * GetMoveDelta( startAnim, 0, 1 )[ 2 ];
|
||||
slideAnimHeight = -1 * GetMoveDelta( slideAnim, 0, 1 )[ 2 ];
|
||||
loopAnimHeight = -1 * GetMoveDelta( loopAnim, 0, 1 )[ 2 ];
|
||||
endAnimHeight = -1 * GetMoveDelta( endAnim, 0, 1 )[ 2 ];
|
||||
jumpOffEndAnimHeight = -1 * GetMoveDelta( jumpOffEndAnim, 0, 1 )[ 2 ];
|
||||
|
||||
if ( totalHeight < ( startAnimHeight + endAnimHeight ) )
|
||||
Println( "ERROR: Height is too short for " + animState + ". Modify the geo or use another traversal." );
|
||||
|
||||
endAnimToPlay = endAnim;
|
||||
endAnimToPlayHeight = endAnimHeight;
|
||||
canDoJump = false;
|
||||
|
||||
//Determine whether alien can play the jump off anim for end
|
||||
if ( self canDoJumpForEnd( startnode, endNode, startAnim, jumpOffEndAnim ))
|
||||
{
|
||||
endAnimToPlay = jumpOffEndAnim;
|
||||
endAnimToPlayHeight = jumpOffEndAnimHeight;
|
||||
canDoJump = true;
|
||||
}
|
||||
|
||||
distForSlideAndLoop = totalHeight - ( startAnimHeight + endAnimToPlayHeight );
|
||||
canDoSlide = false;
|
||||
numOfLoop = 0;
|
||||
if ( distForSlideAndLoop > 0 )
|
||||
{
|
||||
canDoSlide = ( distForSlideAndLoop - slideAnimHeight ) > 0;
|
||||
numOfLoop = max ( 0, floor (( distForSlideAndLoop - canDoSlide * slideAnimHeight ) / loopAnimHeight ));
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
|
||||
self traverseClimbDownLerp( startAnim, startNode );
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, 0, "canned_traverse", "end" );
|
||||
|
||||
slideAndLoopAnimHeight = canDoSlide * slideAnimHeight + numOfLoop * loopAnimHeight;
|
||||
if ( slideAndLoopAnimHeight > 0 )
|
||||
{
|
||||
animScaler = abs( ( distForSlideAndLoop )/ slideAndLoopAnimHeight );
|
||||
self ScrAgentSetAnimScale( 1, animScaler );
|
||||
}
|
||||
|
||||
//<Note J.C.>: Playing the loop and slide animation from the same anim state has caused the following issue.:
|
||||
// (1) The "will_finish_soon" notetrack will fire off immediately due to the short anim length for the slide anim,
|
||||
// causing the slide animation to not play
|
||||
// (2) When this happens, the alien will keep playing the loop animation even when the jump-off state is activated.
|
||||
// If time permits, we need to look into how situations like this should be prevented.
|
||||
for ( i = 0; i < numOfLoop; i++ )
|
||||
{
|
||||
PlayAnimNUntilNotetrack( "traverse_climb_down_loop", 0, "traverse_climb_down_loop", "end" );
|
||||
}
|
||||
if ( canDoSlide )
|
||||
PlayAnimNUntilNotetrack( "traverse_climb_down_slide", 0, "traverse_climb_down_slide", "end" );
|
||||
|
||||
//Final height adjustment, making sure alien ends up on the ground when finish
|
||||
teleportStartTime = getNoteTrackTimes( endAnimToPlay, "climb_down_teleport" ) [ 0 ];
|
||||
teleportEndTime = getNoteTrackTimes( endAnimToPlay, "stop_teleport" ) [ 0 ];
|
||||
animHeightAfterNotetrack = -1 * GetMoveDelta( endAnimToPlay, teleportStartTime, teleportEndTime )[ 2 ];
|
||||
heightAdjustment = abs( self.origin[ 2 ] - endNode.origin[ 2 ] - abs ( GetMoveDelta( endAnimToPlay, teleportEndTime, 1 )[ 2 ] ) );
|
||||
animScaler = heightAdjustment / animHeightAfterNotetrack;
|
||||
|
||||
self ScrAgentSetAnimScale( 1, animScaler );
|
||||
|
||||
if ( canDoJump )
|
||||
PlayAnimNUntilNotetrack( animState, 4, "canned_traverse", "stop_teleport" );
|
||||
else
|
||||
PlayAnimNUntilNotetrack( animState, 3, "canned_traverse", "stop_teleport" );
|
||||
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self WaitUntilNotetrack( "canned_traverse", "end" );
|
||||
}
|
||||
|
||||
traverseAnimLerp( startAnim, startNode )
|
||||
{
|
||||
// Make sure we're oriented exactly with the node -
|
||||
// lerp to the correct position for the first part of the anim
|
||||
|
||||
lerp_time = maps\mp\agents\alien\_alien_anim_utils::getLerpTime( startAnim );
|
||||
lerp_target_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( startAnim, startNode.origin, startNode.angles, lerp_time );
|
||||
|
||||
thread maps\mp\agents\alien\_alien_anim_utils::doLerp( lerp_target_pos, lerp_time );
|
||||
}
|
||||
|
||||
traverseClimbDownLerp( startAnim, startNode )
|
||||
{
|
||||
VERTICAL_DROP = -30; // For climb down, go down when attempt to locate the vertical edge
|
||||
HORIZONTAL_EXTENSION = 60; // Further extend horizontally for nodes places close to the vertical edge
|
||||
|
||||
doTraverseClimbLerp( startAnim, startNode, VERTICAL_DROP, HORIZONTAL_EXTENSION, true );
|
||||
}
|
||||
|
||||
traverseClimbUpLerp( startAnim, startNode )
|
||||
{
|
||||
VERTICAL_RAISE = 0; // For climb up, go up when attempt to locate the vertical edge
|
||||
HORIZONTAL_EXTENSION = 50; // Further extend horizontally for nodes places placed far from the vertical edge
|
||||
|
||||
doTraverseClimbLerp( startAnim, startNode, VERTICAL_RAISE, HORIZONTAL_EXTENSION, false );
|
||||
}
|
||||
|
||||
doTraverseClimbLerp( startAnim, startNode, verticalProbeDis, horizontalProbeDis, probeForward )
|
||||
{
|
||||
lerp_time = maps\mp\agents\alien\_alien_anim_utils::getLerpTime( startAnim );
|
||||
lerp_target_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( startAnim, startNode.origin, startNode.angles, lerp_time );
|
||||
|
||||
if ( probeForward )
|
||||
horizontal_probe_direction = ( lerp_target_pos - startNode.origin ) * ( 1, 1, 0 );
|
||||
else
|
||||
horizontal_probe_direction = ( startNode.origin - lerp_target_pos ) * ( 1, 1, 0 );
|
||||
|
||||
horizontal_offset = vectorNormalize( horizontal_probe_direction );
|
||||
horizontal_offset *= horizontalProbeDis;
|
||||
|
||||
anim_end_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( startAnim, startNode.origin, startNode.angles, GetAnimLength( startAnim ) );
|
||||
end_pos_aligned = alignToVerticalEdge( anim_end_pos, verticalProbeDis, horizontal_offset );
|
||||
|
||||
xy_displacement = end_pos_aligned - anim_end_pos;
|
||||
lerp_target_pos += xy_displacement;
|
||||
|
||||
thread maps\mp\agents\alien\_alien_anim_utils::doLerp( lerp_target_pos, lerp_time );
|
||||
}
|
||||
|
||||
alignToVerticalEdge( lerp_target_pos, vertical_displacement, horizontal_offset )
|
||||
{
|
||||
BACKWARD_SCALAR = 3.0; // When doing a backward trace toward the lerp_target_pos, extend the horizontal_offset further to
|
||||
// make sure we hit the vertical edge
|
||||
|
||||
lerp_target_pos += horizontal_offset;
|
||||
lerp_target_pos += ( 0, 0, vertical_displacement );
|
||||
|
||||
trace_end_pos = lerp_target_pos - horizontal_offset * BACKWARD_SCALAR;
|
||||
trace = bulletTrace( lerp_target_pos, trace_end_pos, false );
|
||||
lerp_target_pos = trace["position"];
|
||||
lerp_target_pos += ( 0, 0, -1 * vertical_displacement );
|
||||
|
||||
return lerp_target_pos;
|
||||
}
|
||||
|
||||
canDoJumpForEnd( startnode, endNode, startAnim, jumpAnim )
|
||||
{
|
||||
TRACE_START_FORWARD_PADDING = 10;
|
||||
TRACE_END_UP_PADDING = ( 0, 0, 10 );
|
||||
TRACE_CAPSULE_RADIUS = 5;
|
||||
TRACE_CAPSULE_HEIGHT = self.height;
|
||||
|
||||
startAnimDelta = GetMoveDelta( startAnim, 0, 1 );
|
||||
startAnimDeltaXY = Length2D ( startAnimDelta );
|
||||
startAnimDeltaZ = startAnimDelta [ 2 ] * -1;
|
||||
|
||||
jumpAnimDelta = GetMoveDelta( jumpAnim, 0, 1 );
|
||||
jumpAnimDeltaXY = Length2D ( jumpAnimDelta );
|
||||
jumpAnimDeltaZ = jumpAnimDelta[ 2 ] * -1;
|
||||
|
||||
startToEndXY = VectorNormalize (( endNode.origin - startnode.origin ) * ( 1, 1, 0 ));
|
||||
startAnimEndPos = startnode.origin + startToEndXY * startAnimDeltaXY - ( 0, 0, startAnimDeltaZ );
|
||||
|
||||
startAnimEndGroundPos = PhysicsTrace( startAnimEndPos, startAnimEndPos + ( 0, 0, -2000 ) );
|
||||
startAnimEndAboveGround = ( startAnimEndPos - startAnimEndGroundPos ) [ 2 ];
|
||||
|
||||
if ( startAnimEndAboveGround < jumpAnimDeltaZ )
|
||||
return false;
|
||||
|
||||
jumpStartPos = startAnimEndGroundPos + ( 0, 0, jumpAnimDeltaZ );
|
||||
jumpEndPos = startAnimEndGroundPos + startToEndXY * jumpAnimDeltaXY;
|
||||
|
||||
traceStartPos = jumpStartPos + startToEndXY * TRACE_START_FORWARD_PADDING;
|
||||
traceEndPos = jumpEndPos + TRACE_END_UP_PADDING;
|
||||
|
||||
return ( self AIPhysicsTracePassed( traceStartPos, traceEndPos, TRACE_CAPSULE_RADIUS, TRACE_CAPSULE_HEIGHT, false ) );
|
||||
}
|
||||
|
||||
alienWallRun( startNode, endNode, animState )
|
||||
{
|
||||
self.oriented = true;
|
||||
|
||||
startToEnd = endNode.origin - startNode.origin;
|
||||
up = AnglesToUp( endNode.angles );
|
||||
forward = VectorNormalize( startToEnd );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
|
||||
startToEndAngles = AxisToAngles( forward, right, up );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", startToEndAngles );
|
||||
|
||||
animEntry = self GetAnimEntry( animState, 0 );
|
||||
time = GetAnimLength( animEntry );
|
||||
moveDelta = GetMoveDelta( animEntry );
|
||||
dist = Length( moveDelta );
|
||||
distToEnd = Length( endNode.origin - self.origin );
|
||||
lerpTime = time * ( distToEnd / dist );
|
||||
self ScrAgentDoAnimLerp( self.origin, endNode.origin, lerpTime );
|
||||
|
||||
self SetAnimState( animState, 0 );
|
||||
|
||||
wait( lerpTime );
|
||||
|
||||
self alienWallRun_WaitForAngles( startToEndAngles );
|
||||
}
|
||||
|
||||
alienWallRun_AnglesAlmostEqual( angles1, angles2, diff )
|
||||
{
|
||||
if ( abs( angleClamp180( angles2[0] - angles1[0] ) > diff ) )
|
||||
return false;
|
||||
if ( abs( angleClamp180( angles2[1] - angles1[1] ) > diff ) )
|
||||
return false;
|
||||
if ( abs( angleClamp180( angles2[2] - angles1[2] ) > diff ) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// do a little extra wait, time out after 0.5s in case something changes.
|
||||
// make sure we're within 5 degrees of our desired angles, but also cancel
|
||||
// out if we're not actually closing in our desired angles because maybe
|
||||
// something changed out our desired angles.
|
||||
// *hocus-pocus-handwavey-insurance*
|
||||
alienWallRun_WaitForAngles( desiredAngles )
|
||||
{
|
||||
previousDiff = 360;
|
||||
waitTime = 0.5;
|
||||
|
||||
while ( waitTime > 0 )
|
||||
{
|
||||
// do an extra check, in addition to the anglesdelta, to see if the angles are close to
|
||||
// each other, because anglesdelta SREs if they're too close. which seems rather unuseful.
|
||||
if ( alienWallRun_AnglesAlmostEqual( self.angles, desiredAngles, 1 ) )
|
||||
break;
|
||||
|
||||
diff = AnglesDelta( desiredAngles, self.angles );
|
||||
if ( diff < 5 || diff >= previousDiff )
|
||||
break;
|
||||
previousDiff = diff;
|
||||
wait( 0.05 );
|
||||
waitTime -= 0.05;
|
||||
}
|
||||
}
|
||||
304
maps/mp/agents/dog/_dog_idle.gsc
Normal file
304
maps/mp/agents/dog/_dog_idle.gsc
Normal file
@@ -0,0 +1,304 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include common_scripts\utility;
|
||||
|
||||
main()
|
||||
{
|
||||
self.animSubstate = "none";
|
||||
|
||||
self SetTimeOfNextSound();
|
||||
self.timeOfNextSound += 2000;
|
||||
|
||||
self.bIdleHitReaction = false;
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
|
||||
self UpdateState();
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
if ( IsDefined( self.prevTurnRate ) )
|
||||
{
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "cancelidleloop" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
prevState = self.animSubstate;
|
||||
nextState = self DetermineState();
|
||||
if ( nextState != self.animSubstate )
|
||||
self EnterState( nextState );
|
||||
|
||||
self UpdateAngle();
|
||||
|
||||
switch ( self.animSubstate )
|
||||
{
|
||||
case "idle_combat":
|
||||
wait( 0.2 );
|
||||
break;
|
||||
case "idle_noncombat":
|
||||
if ( prevState == "none" )
|
||||
{
|
||||
if ( self.moveMode == "run" || self.moveMode == "sprint" )
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "anml_wolf_pants_mp_fast", "anml_dog_pants_mp_fast" ) );
|
||||
else
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "anml_wolf_pants_mp_med", "anml_dog_pants_mp_med" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetTime() > self.timeOfNextSound )
|
||||
{
|
||||
if ( RandomInt(10) < 4 )
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "anml_wolf_whine", "anml_dog_whine" ) );
|
||||
else
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "anml_wolf_pants_mp_med", "anml_dog_pants_mp_med" ) );
|
||||
self SetTimeOfNextSound();
|
||||
}
|
||||
}
|
||||
wait ( 0.5 );
|
||||
break;
|
||||
default:
|
||||
assertmsg( "unknown dog stop state " + self.animSubstate );
|
||||
wait( 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DetermineState()
|
||||
{
|
||||
if ( ShouldAttackIdle() )
|
||||
return "idle_combat";
|
||||
else
|
||||
return "idle_noncombat";
|
||||
}
|
||||
|
||||
|
||||
EnterState( state )
|
||||
{
|
||||
self ExitState( self.animSubstate );
|
||||
self.animSubstate = state;
|
||||
|
||||
PlayIdleAnim();
|
||||
}
|
||||
|
||||
|
||||
ExitState( prevState )
|
||||
{
|
||||
if ( IsDefined( self.prevTurnRate ) )
|
||||
{
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlayIdleAnim()
|
||||
{
|
||||
if ( self.animSubstate == "idle_combat" )
|
||||
self SetAnimState( "attack_idle" );
|
||||
else
|
||||
self SetAnimState( "casual_idle" );
|
||||
}
|
||||
|
||||
|
||||
UpdateAngle()
|
||||
{
|
||||
faceTarget = undefined;
|
||||
if ( IsDefined( self.enemy ) && DistanceSquared( self.enemy.origin, self.origin ) < 1024 * 1024 )
|
||||
faceTarget = self.enemy;
|
||||
else if ( IsDefined( self.owner ) && DistanceSquared( self.owner.origin, self.origin ) > 24 * 24 )
|
||||
faceTarget = self.owner;
|
||||
|
||||
if ( IsDefined( faceTarget ) )
|
||||
{
|
||||
meToTarget = faceTarget.origin - self.origin;
|
||||
meToTargetAngles = VectorToAngles( meToTarget );
|
||||
|
||||
if ( abs( AngleClamp180( meToTargetAngles[1] - self.angles[1] ) ) > 1 )
|
||||
self TurnToAngle( meToTargetAngles[1] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ShouldAttackIdle()
|
||||
{
|
||||
return isdefined( self.enemy )
|
||||
&& maps\mp\_utility::IsReallyAlive( self.enemy )
|
||||
&& distanceSquared( self.origin, self.enemy.origin ) < 1000000;
|
||||
//&& self SeeRecently( self.enemy, 5 );
|
||||
}
|
||||
|
||||
GetTurnAnimState( angleDiff )
|
||||
{
|
||||
if ( self ShouldAttackIdle() )
|
||||
{
|
||||
if ( angleDiff < -135 || angleDiff > 135 )
|
||||
return "attack_turn_180";
|
||||
else if ( angleDiff < 0 )
|
||||
return "attack_turn_right_90";
|
||||
else
|
||||
return "attack_turn_left_90";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( angleDiff < -135 || angleDiff > 135 )
|
||||
return "casual_turn_180";
|
||||
else if ( angleDiff < 0 )
|
||||
return "casual_turn_right_90";
|
||||
else
|
||||
return "casual_turn_left_90";
|
||||
}
|
||||
}
|
||||
|
||||
TurnToAngle( desiredAngle )
|
||||
{
|
||||
currentAngle = self.angles[1];
|
||||
angleDiff = AngleClamp180( desiredAngle - currentAngle );
|
||||
|
||||
if ( -0.5 < angleDiff && angleDiff < 0.5 )
|
||||
return;
|
||||
|
||||
if ( -10 < angleDiff && angleDiff < 10 )
|
||||
{
|
||||
RotateToAngle( desiredAngle, 2 );
|
||||
return;
|
||||
}
|
||||
|
||||
animState = GetTurnAnimState( angleDiff );
|
||||
|
||||
turnAnim = self GetAnimEntry( animState, 0 );
|
||||
|
||||
animLength = GetAnimLength( turnAnim );
|
||||
animAngleDelta = GetAngleDelta3D( turnAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim angle delta" );
|
||||
|
||||
if ( AnimHasNotetrack( turnAnim, "turn_begin" ) && AnimHasNotetrack( turnAnim, "turn_end" ) )
|
||||
{
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "turn_in_place" );
|
||||
|
||||
beginTimes = GetNotetrackTimes( turnAnim, "turn_begin" );
|
||||
endTimes = GetNotetrackTimes( turnAnim, "turn_end" );
|
||||
turnTime = (endTimes[0] - beginTimes[0]) * animLength;
|
||||
|
||||
turnAdjust = AngleClamp180( angleDiff - animAngleDelta[1] );
|
||||
|
||||
turnSpeed = abs(turnAdjust) / turnTime / 20;
|
||||
turnSpeed = turnSpeed * 3.14159 / 180; // radians per frame.
|
||||
|
||||
angles = ( 0, AngleClamp180( self.angles[1] + turnAdjust ), 0 );
|
||||
|
||||
self.prevTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( turnSpeed );
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
self WaitUntilNotetrack( "turn_in_place", "turn_end" );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
|
||||
self WaitUntilNotetrack( "turn_in_place", "end" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.prevTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
|
||||
turnSpeed = abs( AngleClamp180(angleDiff-animAngleDelta[1]) ) / animLength / 20;
|
||||
turnSpeed = turnSpeed * 3.14159 / 180;
|
||||
self ScrAgentSetMaxTurnSpeed( turnSpeed ); // radians per frame.
|
||||
|
||||
angles = ( 0, AngleClamp180( desiredAngle - animAngleDelta[1] ), 0 );
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "turn_in_place" );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
self PlayIdleAnim();
|
||||
}
|
||||
|
||||
RotateToAngle( desiredAngle, tolerance )
|
||||
{
|
||||
if ( abs( AngleClamp180( desiredAngle - self.angles[1] ) ) <= tolerance )
|
||||
return;
|
||||
|
||||
angles = ( 0, desiredAngle, 0 );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
while ( AngleClamp180( desiredAngle - self.angles[1] ) > tolerance )
|
||||
wait ( 0.1 );
|
||||
}
|
||||
|
||||
SetTimeOfNextSound()
|
||||
{
|
||||
self.timeOfNextSound = GetTime() + 8000 + RandomInt( 5000 );
|
||||
}
|
||||
|
||||
DoHitReaction( hitAngle )
|
||||
{
|
||||
self.bLockGoalPos = true;
|
||||
self.stateLocked = true;
|
||||
self.bIdleHitReaction = true;
|
||||
|
||||
// hitAngle is angle from me to damage
|
||||
angleDiff = AngleClamp180( hitAngle - self.angles[1] );
|
||||
|
||||
if ( angleDiff > 0 )
|
||||
animIndex = 1; // left
|
||||
else
|
||||
animIndex = 0; // right
|
||||
|
||||
self notify( "cancelidleloop" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( "stand_pain", animIndex, "stand_pain" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self.stateLocked = false;
|
||||
self.bIdleHitReaction = false;
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.animSubstate = "none";
|
||||
self thread UpdateState();
|
||||
}
|
||||
|
||||
OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( self.bIdleHitReaction )
|
||||
return;
|
||||
|
||||
hitDirToAngles = VectorToAngles( vDir );
|
||||
hitAngle = hitDirToAngles[1] - 180;
|
||||
|
||||
self DoHitReaction( hitAngle );
|
||||
}
|
||||
|
||||
OnFlashbanged( origin, percent_distance, percent_angle, attacker, teamName, extraDuration )
|
||||
{
|
||||
if ( self.bIdleHitReaction )
|
||||
return;
|
||||
|
||||
DoHitReaction( self.angles[1] + 180 );
|
||||
}
|
||||
351
maps/mp/agents/dog/_dog_melee.gsc
Normal file
351
maps/mp/agents/dog/_dog_melee.gsc
Normal file
@@ -0,0 +1,351 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
|
||||
assert( IsDefined( self.curMeleeTarget ) );
|
||||
|
||||
self.curMeleeTarget endon( "disconnect" );
|
||||
|
||||
// get desired end pos.
|
||||
meToTarget = self.curMeleeTarget.origin - self.origin;
|
||||
distMeToTarget = Length( meToTarget );
|
||||
|
||||
bTestCanMove = true;
|
||||
if ( distMeToTarget < self.attackOffset )
|
||||
{
|
||||
attackPos = self.origin;
|
||||
bTestCanMove = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = meToTarget / distMeToTarget;
|
||||
attackPos = self.curMeleeTarget.origin - meToTarget * self.attackOffset;
|
||||
}
|
||||
|
||||
bLerp = false;
|
||||
|
||||
startPos = self.origin + (0,0,30);
|
||||
endPos = self.curMeleeTarget.origin + (0,0,30);
|
||||
hitPos = PhysicsTrace( startPos, endPos );
|
||||
if ( DistanceSquared( hitPos, endPos ) > 1 )
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( bTestCanMove )
|
||||
bCanMoveToAttackPos = self CanMovePointToPoint( self.origin, attackPos );
|
||||
else
|
||||
bCanMoveToAttackPos = true;
|
||||
|
||||
animEntry = undefined;
|
||||
if ( !bCanMoveToAttackPos )
|
||||
{
|
||||
bShouldDoExtendedKill = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
animEntry = self ShouldDoExtendedKill( self.curMeleeTarget );
|
||||
bShouldDoExtendedKill = IsDefined( animEntry );
|
||||
}
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
if ( bShouldDoExtendedKill )
|
||||
{
|
||||
assert( IsDefined( animEntry ) );
|
||||
self DoExtendedKill( animEntry );
|
||||
}
|
||||
else
|
||||
{
|
||||
self DoStandardKill( attackPos, bCanMoveToAttackPos );
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetMeleeAnimState()
|
||||
{
|
||||
return "attack_run_and_jump";
|
||||
}
|
||||
|
||||
// returns kill direction, if any.
|
||||
ShouldDoExtendedKill( victim )
|
||||
{
|
||||
if( !self.enableExtendedKill )
|
||||
return undefined;
|
||||
|
||||
cMaxHeightDiff = 4;
|
||||
|
||||
if ( !IsGameParticipant( victim ) ) // humans only.
|
||||
return undefined;
|
||||
if ( self IsProtectedByRiotshield( victim ) )
|
||||
return undefined;
|
||||
if ( victim IsJuggernaut() )
|
||||
return undefined;
|
||||
|
||||
victimToMe = self.origin - victim.origin;
|
||||
if ( abs( victimToMe[2] ) > cMaxHeightDiff )
|
||||
return undefined;
|
||||
|
||||
victimToMe2D = VectorNormalize( (victimToMe[0], victimToMe[1], 0) );
|
||||
victimFacing = AnglesToForward( victim.angles );
|
||||
angleToMe = VectorDot( victimFacing, victimToMe2D );
|
||||
|
||||
if ( angleToMe > 0.707 )
|
||||
{
|
||||
animEntry = 0; // front
|
||||
snappedVictimToMe = RotateVector( ( 1, 0, 0 ), victim.angles );
|
||||
}
|
||||
else if ( angleToMe < -0.707 )
|
||||
{
|
||||
animEntry = 1; // back
|
||||
snappedVictimToMe = RotateVector( (-1, 0, 0), victim.angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
cross = maps\mp\agents\dog\_dog_think::cross2D( victimToMe, victimFacing );
|
||||
if ( cross > 0 )
|
||||
{
|
||||
animEntry = 3; // right
|
||||
snappedVictimToMe = RotateVector( (0, -1, 0), victim.angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
animEntry = 2; // left
|
||||
snappedVictimToMe = RotateVector( (0, 1, 0), victim.angles );
|
||||
}
|
||||
}
|
||||
|
||||
if ( animEntry == 1 )
|
||||
cClearanceRequired = 128;
|
||||
else
|
||||
cClearanceRequired = 96;
|
||||
landPos = victim.origin - cClearanceRequired * snappedVictimToMe;
|
||||
|
||||
landPosDropped = self DropPosToGround( landPos );
|
||||
if ( !IsDefined( landPosDropped ) )
|
||||
return undefined;
|
||||
|
||||
if ( abs( landPosDropped[2] - landPos[2] ) > cMaxHeightDiff )
|
||||
return undefined;
|
||||
|
||||
if ( !self AIPhysicsTracePassed( victim.origin + (0,0,4), landPosDropped + (0,0,4), self.radius, self.height ) )
|
||||
return undefined;
|
||||
|
||||
return animEntry;
|
||||
}
|
||||
|
||||
DoExtendedKill( animEntry )
|
||||
{
|
||||
meleeAnimState = "attack_extended";
|
||||
|
||||
self DoMeleeDamage( self.curMeleeTarget, self.curMeleeTarget.health, "MOD_MELEE_DOG" );
|
||||
|
||||
attackAnim = self GetAnimEntry( meleeAnimState, animEntry );
|
||||
self thread ExtendedKill_StickToVictim( attackAnim, self.curMeleeTarget.origin, self.curMeleeTarget.angles );
|
||||
|
||||
if ( animEntry == 1 ) // back
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "mp_wolf_attack_quick_back_npc", "mp_dog_attack_quick_back_npc" ) );
|
||||
else
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "mp_wolf_attack_short_npc", "mp_dog_attack_short_npc" ) );
|
||||
|
||||
self PlayAnimNUntilNotetrack( meleeAnimState, animEntry, "attack", "end" );
|
||||
|
||||
self notify( "kill_stick" );
|
||||
self.curMeleeTarget = undefined;
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self Unlink();
|
||||
}
|
||||
|
||||
ExtendedKill_StickToVictim( attackAnim, targetOrigin, targetAngles )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
self endon( "kill_stick" );
|
||||
|
||||
wait( 0.05 ); // must wait for anim to kick in before we can properly calculate our offsets.
|
||||
assert( IsDefined( self.curMeleeTarget ) );
|
||||
|
||||
if ( IsAlive( self.curMeleeTarget ) ) // godmode, etc.
|
||||
return;
|
||||
|
||||
corpse = self.curMeleeTarget GetCorpseEntity();
|
||||
assert( IsDefined( corpse ) );
|
||||
self LinkTo( corpse );
|
||||
|
||||
self ScrAgentDoAnimRelative( attackAnim, targetOrigin, targetAngles );
|
||||
}
|
||||
|
||||
DoStandardKill( attackPos, bCanMoveToAttackPos )
|
||||
{
|
||||
meleeAnimState = self GetMeleeAnimState();
|
||||
|
||||
bLerp = false;
|
||||
|
||||
if ( !bCanMoveToAttackPos )
|
||||
{
|
||||
if ( self AgentCanSeeSentient( self.curMeleeTarget ) )
|
||||
{
|
||||
groundPos = self DropPosToGround( self.curMeleeTarget.origin );
|
||||
if ( IsDefined( groundPos ) )
|
||||
{
|
||||
bLerp = true;
|
||||
attackPos = groundPos; // i'm going to clip the heck through him, but i need a guaranteed safe spot.
|
||||
}
|
||||
else
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.lastMeleeFailedMyPos = undefined;
|
||||
self.lastMeleeFailedPos = undefined;
|
||||
|
||||
attackAnim = self GetAnimEntry( meleeAnimState, 0 );
|
||||
animLength = GetAnimLength( attackAnim );
|
||||
meleeNotetracks = GetNotetrackTimes( attackAnim, "dog_melee" );
|
||||
if ( meleeNotetracks.size > 0 )
|
||||
lerpTime = meleeNotetracks[0] * animLength;
|
||||
else
|
||||
lerpTime = animLength;
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, attackPos, lerpTime );
|
||||
|
||||
self thread UpdateLerpPos( self.curMeleeTarget, lerpTime, bCanMoveToAttackPos );
|
||||
|
||||
self PlayAnimNUntilNotetrack( meleeAnimState, 0, "attack", "dog_melee" );
|
||||
|
||||
self notify( "cancel_updatelerppos" );
|
||||
|
||||
damageDealt = 0;
|
||||
if( IsDefined( self.curMeleeTarget ) )
|
||||
damageDealt = self.curMeleeTarget.health;
|
||||
if( IsDefined( self.meleeDamage ) )
|
||||
damageDealt = self.meleeDamage;
|
||||
|
||||
if( IsDefined( self.curMeleeTarget ) )
|
||||
self DoMeleeDamage( self.curMeleeTarget, damageDealt, "MOD_IMPACT" );
|
||||
|
||||
self.curMeleeTarget = undefined; // dude's dead now, or soon will be.
|
||||
|
||||
if ( bLerp )
|
||||
self ScrAgentSetAnimScale( 0, 1 );
|
||||
else
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
self WaitUntilNotetrack( "attack", "end" );
|
||||
}
|
||||
|
||||
UpdateLerpPos( enemy, lerpTime, bCanMoveToAttackPos )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "death" );
|
||||
self endon( "cancel_updatelerppos" );
|
||||
enemy endon( "disconnect" );
|
||||
enemy endon( "death" );
|
||||
|
||||
timeRemaining = lerpTime;
|
||||
interval = 0.05;
|
||||
while ( true )
|
||||
{
|
||||
wait( interval );
|
||||
timeRemaining -= interval;
|
||||
|
||||
if ( timeRemaining <= 0 )
|
||||
break;
|
||||
|
||||
attackPos = GetUpdatedAttackPos( enemy, bCanMoveToAttackPos );
|
||||
if ( !IsDefined( attackPos ) )
|
||||
break;
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, attackPos, timeRemaining );
|
||||
}
|
||||
}
|
||||
|
||||
GetUpdatedAttackPos( enemy, bCanMove )
|
||||
{
|
||||
if ( !bCanMove )
|
||||
{
|
||||
droppedPos = self DropPosToGround( enemy.origin );
|
||||
return droppedPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = enemy.origin - self.origin;
|
||||
distMeToTarget = Length( meToTarget );
|
||||
|
||||
if ( distMeToTarget < self.attackOffset )
|
||||
{
|
||||
return self.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = meToTarget / distMeToTarget;
|
||||
attackPos = enemy.origin - meToTarget * self.attackOffset;
|
||||
if ( self CanMovePointToPoint( self.origin, attackPos ) )
|
||||
return attackPos;
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IsProtectedByRiotshield( enemy )
|
||||
{
|
||||
if ( IsDefined( enemy.hasRiotShield ) && enemy.hasRiotShield )
|
||||
{
|
||||
enemyToMe = self.origin - enemy.origin;
|
||||
meToEnemy = VectorNormalize( ( enemyToMe[0], enemyToMe[1], 0 ) );
|
||||
|
||||
enemyFacing = AnglesToForward( enemy.angles );
|
||||
angleToMe = VectorDot( enemyFacing, enemyToMe );
|
||||
|
||||
if ( enemy.hasRiotShieldEquipped )
|
||||
{
|
||||
if ( angleToMe > 0.766 )
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( angleToMe < -0.766 )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DoMeleeDamage( enemy, damage, meansOfDeath )
|
||||
{
|
||||
if ( self IsProtectedByRiotshield( enemy ) )
|
||||
return;
|
||||
|
||||
enemy DoDamage( damage, self.origin, self, self, meansOfDeath );
|
||||
}
|
||||
|
||||
MeleeFailed()
|
||||
{
|
||||
self.lastMeleeFailedMyPos = self.origin;
|
||||
self.lastMeleeFailedPos = self.curMeleeTarget.origin;
|
||||
}
|
||||
490
maps/mp/agents/dog/_dog_move.gsc
Normal file
490
maps/mp/agents/dog/_dog_move.gsc
Normal file
@@ -0,0 +1,490 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
|
||||
self StartMove();
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
self CancelAllBut( undefined );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
|
||||
SetupMovement()
|
||||
{
|
||||
self thread WaitForRunWalkChange();
|
||||
self thread WaitForSharpTurn();
|
||||
self thread WaitForStop();
|
||||
//self thread WaitForStopEarly();
|
||||
//self thread HandleMoveLoopFootsteps();
|
||||
}
|
||||
|
||||
ContinueMovement()
|
||||
{
|
||||
self SetupMovement();
|
||||
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self SetMoveAnim( self.moveMode );
|
||||
}
|
||||
|
||||
SetMoveAnim( moveMode )
|
||||
{
|
||||
self SetAnimState( moveMode );
|
||||
}
|
||||
|
||||
WaitForRunWalkChange()
|
||||
{
|
||||
self endon( "dogmove_endwait_runwalk" );
|
||||
curMovement = self.moveMode;
|
||||
while ( true )
|
||||
{
|
||||
if ( curMovement != self.moveMode )
|
||||
{
|
||||
self SetMoveAnim( self.moveMode );
|
||||
curMovement = self.moveMode;
|
||||
}
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
DoSharpTurn( newDir )
|
||||
{
|
||||
lookaheadAngles = VectorToAngles( newDir );
|
||||
angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
|
||||
if ( angleIndex == 4 ) // 4 means this turn wasn't sharp enough for me to care. (angle ~= 0)
|
||||
{
|
||||
ContinueMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
animState = "sharp_turn";
|
||||
|
||||
turnAnim = self GetAnimEntry( animState, angleIndex );
|
||||
animAngleDelta = GetAngleDelta( turnAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - animAngleDelta ), 0 ) );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, angleIndex, "sharp_turn" );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForSharpTurn()
|
||||
{
|
||||
self endon( "dogmove_endwait_sharpturn" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
self CancelAllBut( "sharpturn" );
|
||||
|
||||
self DoSharpTurn( newDir );
|
||||
}
|
||||
|
||||
WaitForStop()
|
||||
{
|
||||
self endon( "dogmove_endwait_stop" );
|
||||
|
||||
self waittill( "stop_soon" );
|
||||
|
||||
if ( IsDefined( self.bArrivalsEnabled ) && !self.bArrivalsEnabled )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopState = self GetStopAnimState();
|
||||
|
||||
stopAnim = self GetAnimEntry( stopState.state, stopState.index );
|
||||
stopDelta = GetMoveDelta( stopAnim );
|
||||
stopAngleDelta = GetAngleDelta( stopAnim );
|
||||
|
||||
goalPos = self GetPathGoalPos();
|
||||
assert( IsDefined( goalPos ) );
|
||||
|
||||
meToStop = goalPos - self.origin;
|
||||
// not enough room left to play the animation. abort. (i'm willing to squish/scale the anim up to 12 units.)
|
||||
if ( Length( meToStop ) + 12 < Length( stopDelta ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopData = self GetStopData();
|
||||
stopStartPos = self CalcAnimStartPos( stopData.pos, stopData.angles[1], stopDelta, stopAngleDelta );
|
||||
stopStartPosDropped = DropPosToGround( stopStartPos );
|
||||
|
||||
if ( !IsDefined( stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !self CanMovePointToPoint( stopData.pos, stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
self CancelAllBut( "stop" );
|
||||
|
||||
self thread WaitForPathSetWhileStopping();
|
||||
self thread WaitForSharpTurnWhileStopping();
|
||||
if ( DistanceSquared( stopStartPos, self.origin ) > 4 )
|
||||
{
|
||||
self ScrAgentSetWaypoint( stopStartPos );
|
||||
self thread WaitForBlockedWhileStopping();
|
||||
self waittill( "waypoint_reached" );
|
||||
self notify( "dogmove_endwait_blockedwhilestopping" );
|
||||
}
|
||||
|
||||
facingDir = goalPos - self.origin;
|
||||
facingAngles = VectorToAngles( facingDir );
|
||||
facingYaw = ( 0, facingAngles[1] - stopAngleDelta, 0 );
|
||||
|
||||
// scale the anim if necessary, to make sure we end up where we wanted to end up.
|
||||
scaleFactors = GetAnimScaleFactors( goalPos - self.origin, stopDelta );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", facingYaw, ( 0, facingAngles[1], 0 ) );
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( stopState.state, stopState.index, "move_stop" );
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin ); // whether i got where i was going, get where i got.
|
||||
}
|
||||
|
||||
WaitForPathSetWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_pathsetwhilestopping" );
|
||||
|
||||
oldGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
self waittill( "path_set" );
|
||||
|
||||
newGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
if ( DistanceSquared( oldGoalPos, newGoalPos ) < 1 )
|
||||
{
|
||||
self thread WaitForPathSetWhileStopping();
|
||||
return;
|
||||
}
|
||||
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
self notify( "dogmove_endwait_sharpturnwhilestopping" );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForSharpTurnWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_sharpturnwhilestopping" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
|
||||
self notify( "dogmove_endwait_pathsetwhilestopping" );
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
|
||||
self DoSharpTurn( newDir );
|
||||
}
|
||||
|
||||
WaitForBlockedWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_blockedwhilestopping" );
|
||||
|
||||
self waittill( "path_blocked" );
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
self ScrAgentSetWaypoint( undefined );
|
||||
}
|
||||
|
||||
WaitForStopEarly()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_stopearly" );
|
||||
|
||||
stopAnim = self GetAnimEntry( "move_stop_4", 0 );
|
||||
stopAnimTranslation = GetMoveDelta( stopAnim );
|
||||
stoppingDistance = Length( stopAnimTranslation );
|
||||
offset = self.preferredOffsetFromOwner + stoppingDistance;
|
||||
offsetSq = offset * offset;
|
||||
|
||||
if ( DistanceSquared( self.origin, self.owner.origin ) <= offsetSq )
|
||||
return;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( !IsDefined( self.owner ) )
|
||||
break;
|
||||
|
||||
if ( DistanceSquared( self.origin, self.owner.origin ) < offsetSq )
|
||||
{
|
||||
stopPos = self LocalToWorldCoords( stopAnimTranslation );
|
||||
self ScrAgentSetGoalPos( stopPos );
|
||||
break;
|
||||
}
|
||||
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
CancelAllBut( doNotCancel )
|
||||
{
|
||||
cleanups = [ "runwalk", "sharpturn", "stop", "pathsetwhilestopping", "blockedwhilestopping", "sharpturnwhilestopping", "stopearly" ];
|
||||
|
||||
bCheckDoNotCancel = IsDefined( doNotCancel );
|
||||
|
||||
foreach ( cleanup in cleanups )
|
||||
{
|
||||
if ( bCheckDoNotCancel && cleanup == doNotCancel )
|
||||
continue;
|
||||
self notify( "dogmove_endwait_" + cleanup );
|
||||
}
|
||||
}
|
||||
|
||||
StartMove()
|
||||
{
|
||||
negStartNode = self GetNegotiationStartNode();
|
||||
if ( IsDefined( negStartNode ) )
|
||||
goalPos = negStartNode.origin;
|
||||
else
|
||||
goalPos = self GetPathGoalPos();
|
||||
|
||||
// don't play start if i have no room for the start.
|
||||
if ( DistanceSquared( goalPos, self.origin ) < 100 * 100 )
|
||||
return;
|
||||
|
||||
lookaheadDir = self GetLookaheadDir();
|
||||
lookaheadAngles = VectorToAngles( lookaheadDir );
|
||||
|
||||
myVelocity = self GetVelocity();
|
||||
if ( Length2DSquared( myVelocity ) > 16 )
|
||||
{
|
||||
myVelocity = VectorNormalize( myVelocity );
|
||||
if ( VectorDot( myVelocity, lookaheadDir ) > 0.707 )
|
||||
return; // don't need a start if i'm already moving in the direction i want to move.
|
||||
}
|
||||
|
||||
angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
|
||||
startAnim = self GetAnimEntry( "move_start", angleIndex );
|
||||
startAnimTranslation = GetMoveDelta( startAnim );
|
||||
|
||||
endPos = RotateVector( startAnimTranslation, self.angles ) + self.origin;
|
||||
if ( !self CanMovePointToPoint( self.origin, endPos ) )
|
||||
return;
|
||||
|
||||
startAnimAngles = GetAngleDelta3D( startAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
if ( 3 <= angleIndex && angleIndex <= 5 )
|
||||
self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - startAnimAngles[1] ), 0 ) );
|
||||
else
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
self PlayAnimNUntilNotetrack( "move_start", angleIndex, "move_start" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetStopData()
|
||||
{
|
||||
stopData = SpawnStruct();
|
||||
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
stopData.pos = self.node.origin;
|
||||
stopData.angles = self.node.angles;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathGoalPos = self GetPathGoalPos();
|
||||
assert( IsDefined( pathGoalPos ) );
|
||||
stopData.pos = pathGoalPos;
|
||||
//stopData.angles = self.angles;
|
||||
stopData.angles = VectorToAngles( self GetLookaheadDir() );
|
||||
}
|
||||
|
||||
return stopData;
|
||||
}
|
||||
|
||||
GetStopAnimState( angle )
|
||||
{
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
angleDiff = self.node.angles[1] - self.angles[1];
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
}
|
||||
else
|
||||
{
|
||||
angleIndex = 4;
|
||||
}
|
||||
|
||||
result = SpawnStruct();
|
||||
result.state = "move_stop";
|
||||
result.index = angleIndex;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CalcAnimStartPos( stopPos, stopAngle, animDelta, animAngleDelta )
|
||||
{
|
||||
dAngle = stopAngle - animAngleDelta;
|
||||
angles = ( 0, dAngle, 0 );
|
||||
vForward = AnglesToForward( angles );
|
||||
vRight = AnglesToRight( angles );
|
||||
|
||||
forward = vForward * animDelta[0];
|
||||
right = vRight * animDelta[1];
|
||||
|
||||
return stopPos - forward + right;
|
||||
}
|
||||
|
||||
Dog_AddLean()
|
||||
{
|
||||
leanFrac = Clamp( self.leanAmount / 25.0, -1, 1 );
|
||||
if ( leanFrac > 0 )
|
||||
{
|
||||
// set lean left( leanFrac );
|
||||
// set lean right( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// set lean left( 0 );
|
||||
// set lean right( 0 - leanFrac );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HandleFootstepNotetracks( note, animState, animIndex, animTime )
|
||||
{
|
||||
if ( true )
|
||||
return false;
|
||||
|
||||
switch ( note )
|
||||
{
|
||||
case "footstep_front_left_small":
|
||||
case "footstep_front_right_small":
|
||||
case "footstep_back_left_small":
|
||||
case "footstep_back_right_small":
|
||||
case "footstep_front_left_large":
|
||||
case "footstep_front_right_large":
|
||||
case "footstep_back_left_large":
|
||||
case "footstep_back_right_large":
|
||||
{
|
||||
surfaceType = undefined;
|
||||
if ( IsDefined( self.surfaceType ) )
|
||||
{
|
||||
surfaceType = self.surfaceType;
|
||||
self.lastSurfaceType = surfaceType;
|
||||
}
|
||||
else if ( IsDefined( self.lastSurfaceType ) )
|
||||
{
|
||||
surfaceType = self.lastSurfaceType;
|
||||
}
|
||||
else
|
||||
{
|
||||
surfaceType = "dirt";
|
||||
}
|
||||
|
||||
if ( surfaceType != "dirt" && surfaceType != "concrete" && surfaceType != "wood" && surfaceType != "metal" )
|
||||
surfaceType = "dirt";
|
||||
|
||||
if ( surfaceType == "concrete" ) // code == concrete, sound == cement.
|
||||
surfaceType = "cement";
|
||||
|
||||
//moveType = self.sound_animMoveType;
|
||||
//if ( !IsDefined( moveType ) )
|
||||
// moveType = "run";
|
||||
if ( self.aiState == "traverse" )
|
||||
moveType = "land";
|
||||
else if ( self.moveMode == "sprint" )
|
||||
moveType = "sprint";
|
||||
else if ( self.moveMode == "fastwalk" )
|
||||
moveType = "walk";
|
||||
else
|
||||
moveType = "run";
|
||||
|
||||
self PlaySoundOnMovingEnt( "dogstep_" + moveType + "_" + surfaceType );
|
||||
|
||||
if ( IsSubStr( note, "front_left" ) )
|
||||
{
|
||||
soundAlias1 = "anml_dog_mvmt_accent";
|
||||
soundAlias2 = "anml_dog_mvmt_vest";
|
||||
|
||||
if ( moveType == "walk" )
|
||||
suffix = "_npc";
|
||||
else
|
||||
suffix = "_run_npc";
|
||||
|
||||
self PlaySoundOnMovingEnt( soundAlias1 + suffix );
|
||||
self PlaySoundOnMovingEnt( soundAlias2 + suffix );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DoHitReaction( hitAngle )
|
||||
{
|
||||
self CancelAllBut( undefined );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
self.stateLocked = true;
|
||||
|
||||
// hitAngle is angle from me to damage
|
||||
angleDiff = AngleClamp180( hitAngle - self.angles[1] );
|
||||
|
||||
if ( angleDiff > 0 )
|
||||
animIndex = 1; // left
|
||||
else
|
||||
animIndex = 0; // right
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( "run_pain", animIndex, "run_pain" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self.stateLocked = false;
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( self.stateLocked )
|
||||
return;
|
||||
|
||||
hitDirToAngles = VectorToAngles( vDir );
|
||||
hitAngle = hitDirToAngles[1] - 180;
|
||||
|
||||
self DoHitReaction( hitAngle );
|
||||
}
|
||||
|
||||
OnFlashbanged( origin, percent_distance, percent_angle, attacker, teamName, extraDuration )
|
||||
{
|
||||
if ( self.stateLocked )
|
||||
return;
|
||||
|
||||
DoHitReaction( self.angles[1] + 180 );
|
||||
}
|
||||
1198
maps/mp/agents/dog/_dog_think.gsc
Normal file
1198
maps/mp/agents/dog/_dog_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
228
maps/mp/agents/dog/_dog_traverse.gsc
Normal file
228
maps/mp/agents/dog/_dog_traverse.gsc
Normal file
@@ -0,0 +1,228 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
if ( !IsDefined( level.dogTraverseAnims ) )
|
||||
InitDogTraverseAnims();
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
assert( IsDefined( startNode ) && IsDefined( endNode ) );
|
||||
|
||||
animState = undefined;
|
||||
|
||||
animState = level.dogTraverseAnims[ startNode.animscript ];
|
||||
|
||||
if ( !IsDefined( animState ) )
|
||||
{
|
||||
assertmsg( "no animation for traverse " + startNode.animscript );
|
||||
return;
|
||||
}
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
startToEnd = endNode.origin - startNode.origin;
|
||||
startToEnd2D = ( startToEnd[0], startToEnd[1], 0 );
|
||||
anglesToEnd = VectorToAngles( startToEnd2D );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToEnd );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
traverseAnim = self GetAnimEntry( animState, 0 );
|
||||
|
||||
codeMoveNotetracks = GetNotetrackTimes( traverseAnim, "code_move" );
|
||||
if ( codeMoveNotetracks.size > 0 )
|
||||
moveDelta = GetMoveDelta( traverseAnim, 0, codeMoveNotetracks[0] );
|
||||
else
|
||||
moveDelta = GetMoveDelta( traverseAnim, 0, 1 );
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( startToEnd, moveDelta );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
|
||||
// the end node is higher than the start node.
|
||||
if ( startToEnd[2] > 0 )
|
||||
{
|
||||
if ( moveDelta[2] > 0 )
|
||||
{
|
||||
jumpStartNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_start" );
|
||||
if ( jumpStartNotetracks.size > 0 )
|
||||
{
|
||||
xyScale = 1;
|
||||
zScale = 1;
|
||||
if ( Length2DSquared( startToEnd2D ) < 0.8 * 0.8 * Length2DSquared( moveDelta ) )
|
||||
xyScale = 0.4;
|
||||
if ( startToEnd[2] < 0.75 * moveDelta[2] )
|
||||
zScale = 0.5;
|
||||
|
||||
self ScrAgentSetAnimScale( xyScale, zScale );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "traverse_jump_start" );
|
||||
jumpEndNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_end" );
|
||||
assert( jumpEndNotetracks.size > 0 );
|
||||
jumpStartMoveDelta = GetMoveDelta( traverseAnim, 0, jumpStartNotetracks[0] );
|
||||
jumpEndMoveDelta = GetMoveDelta( traverseAnim, 0, jumpEndNotetracks[0] );
|
||||
|
||||
xyScale = 1;
|
||||
zScale = 1;
|
||||
currentToEnd = endNode.origin - self.origin;
|
||||
animToEnd = moveDelta - jumpStartMoveDelta;
|
||||
if ( Length2DSquared( currentToEnd ) < 0.75 * 0.75 * Length2DSquared( animToEnd ) )
|
||||
xyScale = 0.75;
|
||||
if ( currentToEnd[2] < 0.75 * animToEnd[2] )
|
||||
zScale = 0.75;
|
||||
|
||||
animJumpEndToEnd = moveDelta - jumpEndMoveDelta;
|
||||
scaledAnimJumpEndToEnd = ( animJumpEndToEnd[0] * xyScale, animJumpEndToEnd[1] * xyScale, animJumpEndToEnd[2] * zScale );
|
||||
worldAnimJumpEndToEnd = RotateVector( scaledAnimJumpEndToEnd, anglesToEnd );
|
||||
nodeJumpEndPos = endNode.origin - worldAnimJumpEndToEnd;
|
||||
|
||||
animJumpStartToJumpEnd = jumpEndMoveDelta - jumpStartMoveDelta;
|
||||
worldAnimJumpStartToJumpEnd = RotateVector( animJumpStartToJumpEnd, anglesToEnd );
|
||||
currentToNodeJumpEnd = nodeJumpEndPos - self.origin;
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( currentToNodeJumpEnd, worldAnimJumpStartToJumpEnd, true);
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self WaitUntilNotetrack( "traverse", "traverse_jump_end" );
|
||||
|
||||
self ScrAgentSetAnimScale( xyScale, zScale );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // can't do negative scale. use lerp.
|
||||
gravityOnNotetracks = GetNotetrackTimes( traverseAnim, "gravity on" );
|
||||
if ( gravityOnNotetracks.size > 0 )
|
||||
{
|
||||
targetEntPos = startNode GetTargetEntPos();
|
||||
if ( IsDefined( targetEntPos ) )
|
||||
{
|
||||
startToTarget = targetEntPos - self.origin;
|
||||
targetToEnd = endNode.origin - targetEntPos;
|
||||
|
||||
startDelta = GetMoveDelta( traverseAnim, 0, gravityOnNotetracks[0] );
|
||||
scaleFactors = self GetAnimScaleFactors( startToTarget, startDelta );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "gravity on" );
|
||||
|
||||
endDelta = GetMoveDelta( traverseAnim, gravityOnNotetracks[0], 1 );
|
||||
scaleFactors = self GetAnimScaleFactors( targetToEnd, endDelta );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
animLength = GetAnimLength( traverseAnim );
|
||||
self ScrAgentDoAnimLerp( startNode.origin, endNode.origin, animLength );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gravityOnNotetracks = GetNotetrackTimes( traverseAnim, "gravity on" );
|
||||
if ( gravityOnNotetracks.size > 0 )
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, 1 );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "gravity on" );
|
||||
|
||||
gravityOnMoveDelta = GetMoveDelta( traverseAnim, 0, gravityOnNotetracks[0] );
|
||||
zAnimDelta = gravityOnMoveDelta[2] - moveDelta[2];
|
||||
|
||||
if ( abs( zAnimDelta ) > 0 )
|
||||
{
|
||||
zMeToEnd = self.origin[2] - endNode.origin[2];
|
||||
|
||||
zScale = zMeToEnd / zAnimDelta;
|
||||
assert( zScale > 0 );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, zScale );
|
||||
|
||||
animrate = Clamp( 2 / zScale, 0.5, 1 );
|
||||
|
||||
norestart = animState + "_norestart";
|
||||
self SetAnimState( norestart, 0, animrate );
|
||||
}
|
||||
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
|
||||
animrate = Clamp( 2 / scaleFactors.z, 0.5, 1 );
|
||||
|
||||
jumpEndNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_end" );
|
||||
if ( jumpEndNotetracks.size > 0 )
|
||||
{
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, 0, animrate, "traverse", "traverse_jump_end" );
|
||||
norestart = animState + "_norestart";
|
||||
self SetAnimState( norestart, 0, 1 );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetTargetEntPos()
|
||||
{
|
||||
if ( IsDefined( self.targetEntPos ) )
|
||||
return self.targetEntPos;
|
||||
|
||||
targetEnt = GetEnt( self.target, "targetname" );
|
||||
if ( !IsDefined( targetEnt ) )
|
||||
return undefined;
|
||||
|
||||
self.targetEntPos = targetEnt.origin;
|
||||
targetEnt delete();
|
||||
return self.targetEntPos;
|
||||
}
|
||||
|
||||
InitDogTraverseAnims()
|
||||
{
|
||||
level.dogTraverseAnims = [];
|
||||
|
||||
level.dogTraverseAnims[ "hjk_tree_hop" ] = "traverse_jump_over_24";
|
||||
level.dogTraverseAnims[ "jump_across_72" ] = "traverse_jump_over_24";
|
||||
level.dogTraverseAnims[ "wall_hop" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_2" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "wall_over_40" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "wall_over" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_divethrough_36" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_over_40" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_over_quick" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "jump_up_80" ] = "traverse_jump_up_70";
|
||||
level.dogTraverseAnims[ "jump_standing_80" ] = "traverse_jump_up_70";
|
||||
level.dogTraverseAnims[ "jump_down_80" ] = "traverse_jump_down_70";
|
||||
level.dogTraverseAnims[ "jump_up_40" ] = "traverse_jump_up_40";
|
||||
level.dogTraverseAnims[ "jump_down_40" ] = "traverse_jump_down_40";
|
||||
level.dogTraverseAnims[ "step_up" ] = "traverse_jump_up_24";
|
||||
level.dogTraverseAnims[ "step_up_24" ] = "traverse_jump_up_24";
|
||||
level.dogTraverseAnims[ "step_down" ] = "traverse_jump_down_24";
|
||||
level.dogTraverseAnims[ "jump_down" ] = "traverse_jump_down_24";
|
||||
level.dogTraverseAnims[ "jump_across" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "jump_across_100" ] = "traverse_jump_over_36";
|
||||
}
|
||||
|
||||
331
maps/mp/alien/_achievement.gsc
Normal file
331
maps/mp/alien/_achievement.gsc
Normal file
@@ -0,0 +1,331 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
GOAL_KILL_WITH_TRAP = 50;
|
||||
GOAL_ESCAPE_IN_TIME = 90000; // 1 minute and 30 second in ms
|
||||
GOAL_ESCAPE_1ST_TIME = 1;
|
||||
GOAL_ESCAPE_ALL_CHALLENGE = 1;
|
||||
GOAL_ESCAPE_ALL_PLAYERS = 4;
|
||||
GOAL_ESCAPE_WITH_NERF_ON = 1;
|
||||
GOAL_SCAVENGE_ITEM = 40;
|
||||
|
||||
init_player_achievement()
|
||||
{
|
||||
self.achievement_list = [];
|
||||
|
||||
if ( isDefined( level.achievement_registration_func ) )
|
||||
[[level.achievement_registration_func]]();
|
||||
|
||||
if ( maps\mp\alien\_utility::is_true( level.include_default_achievements ) )
|
||||
register_default_achievements();
|
||||
}
|
||||
|
||||
register_default_achievements()
|
||||
{
|
||||
// reference goal init_func should_update_func is_goal_reached_func complete_in_casual
|
||||
register_achievement( "KILL_WITH_TRAP" , GOAL_KILL_WITH_TRAP, ::default_init , ::should_update_kill_with_trap , ::equal_to_goal );
|
||||
register_achievement( "ESCAPE_ALL_PLAYERS" , GOAL_ESCAPE_ALL_PLAYERS, ::default_init , ::default_should_update , ::at_least_goal );
|
||||
register_achievement( "ESCAPE_IN_TIME" , GOAL_ESCAPE_IN_TIME, ::default_init , ::default_should_update , ::at_least_goal );
|
||||
register_achievement( "ESCAPE_1ST_TIME" , GOAL_ESCAPE_1ST_TIME, ::default_init , ::default_should_update , ::at_least_goal );
|
||||
register_achievement( "ESCAPE_ALL_CHALLENGE" , GOAL_ESCAPE_ALL_CHALLENGE, ::default_init , ::should_update_all_challenge , ::at_least_goal );
|
||||
register_achievement( "ESCAPE_WITH_NERF_ON" , GOAL_ESCAPE_WITH_NERF_ON, ::default_init , ::default_should_update , ::at_least_goal );
|
||||
register_achievement( "REACH_CITY" , 1, ::default_init , ::default_should_update , ::at_least_goal );
|
||||
register_achievement( "REACH_CABIN" , 1, ::default_init , ::default_should_update , ::at_least_goal );
|
||||
register_achievement( "SCAVENGE_ITEM" , GOAL_SCAVENGE_ITEM, ::default_init , ::default_should_update , ::equal_to_goal );
|
||||
}
|
||||
|
||||
register_achievement( reference, goal, init_func, should_update_func, is_goal_reached_func, complete_in_casual )
|
||||
{
|
||||
achievement = spawnStruct();
|
||||
achievement [[init_func]]( goal, should_update_func, is_goal_reached_func, complete_in_casual );
|
||||
self.achievement_list[reference] = achievement;
|
||||
}
|
||||
|
||||
default_init( goal, should_update_func, is_goal_reached_func, complete_in_casual )
|
||||
{
|
||||
self.progress = 0;
|
||||
self.goal = goal;
|
||||
self.should_update_func = should_update_func;
|
||||
self.is_goal_reached_func = is_goal_reached_func;
|
||||
self.achievement_completed = false;
|
||||
if( isDefined( complete_in_casual ) )
|
||||
self.complete_in_casual =complete_in_casual;
|
||||
}
|
||||
|
||||
default_should_update( unused_1, unused_2, unused_3, unused_4, unused_5, unused_6, unused_7, unused_8, unused_9, unused_10 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
update_progress( progress_amount )
|
||||
{
|
||||
self.progress += progress_amount;
|
||||
}
|
||||
|
||||
at_least_goal()
|
||||
{
|
||||
return ( self.progress >= self.goal );
|
||||
}
|
||||
|
||||
equal_to_goal()
|
||||
{
|
||||
return ( self.progress == self.goal );
|
||||
}
|
||||
|
||||
is_completed()
|
||||
{
|
||||
return ( self.achievement_completed );
|
||||
}
|
||||
|
||||
can_complete_in_causal()
|
||||
{
|
||||
return ( maps\mp\alien\_utility::is_true( self.complete_in_casual ) );
|
||||
}
|
||||
|
||||
mark_completed()
|
||||
{
|
||||
self.achievement_completed = true;
|
||||
}
|
||||
|
||||
is_valid_achievement( achievement )
|
||||
{
|
||||
return ( isDefined ( achievement ) );
|
||||
}
|
||||
|
||||
update_achievement( reference, progress_amt, param_1, param_2, param_3, param_4, param_5, param_6, param_7, param_8, param_9, param_10 )
|
||||
{
|
||||
achievement = self.achievement_list[reference];
|
||||
|
||||
/#//<TODO J.C.> Move this back into release build
|
||||
if(maps\mp\alien\_utility::is_chaos_mode())
|
||||
return;
|
||||
#/
|
||||
|
||||
if ( !is_valid_achievement( achievement ) )
|
||||
return;
|
||||
|
||||
if ( achievement is_completed() )
|
||||
return;
|
||||
|
||||
if ( maps\mp\alien\_utility::is_casual_mode() && !achievement can_complete_in_causal() )
|
||||
return;
|
||||
|
||||
if ( achievement [[achievement.should_update_func]]( param_1, param_2, param_3, param_4, param_5, param_6, param_7, param_8, param_9, param_10 ) )
|
||||
{
|
||||
achievement update_progress( progress_amt );
|
||||
|
||||
if ( achievement [[achievement.is_goal_reached_func]]() )
|
||||
{
|
||||
/#
|
||||
maps\mp\alien\_debug::debug_print_achievement_unlocked( reference, progress_amt );
|
||||
//self IPrintLnBold( "ACHIEVEMENT: " + reference );
|
||||
#/
|
||||
self giveAchievement( reference );
|
||||
|
||||
achievement mark_completed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Related to aliens killed
|
||||
/////////////////////////////////////////
|
||||
update_alien_kill_achievements( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
if ( isdefined( level.update_alien_kill_achievements_func ) )
|
||||
[[ level.update_alien_kill_achievements_func ]]( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
if ( !isDefined( eAttacker ) || !isPlayer( eAttacker ) )
|
||||
return;
|
||||
|
||||
eAttacker update_achievement( "KILL_WITH_TRAP", 1, eInflictor );
|
||||
}
|
||||
|
||||
should_update_kill_with_trap( eInflictor, unused_2, unused_3, unused_4, unused_5, unused_6, unused_7, unused_8, unused_9, unused_10 )
|
||||
{
|
||||
if ( maps\mp\alien\_utility::is_trap( eInflictor ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// Related to escape
|
||||
///////////////////////////////////////
|
||||
update_escape_achievements( players_escaped, escape_time_remains )
|
||||
{
|
||||
escape_player_count = players_escaped.size;
|
||||
|
||||
foreach ( player in players_escaped )
|
||||
{
|
||||
times_escaped = player maps\mp\alien\_persistence::get_player_escaped();
|
||||
num_nerf_selected = player maps\mp\alien\_prestige::get_num_nerf_selected();
|
||||
player update_personal_escape_achievements( escape_player_count, escape_time_remains, times_escaped, num_nerf_selected );
|
||||
}
|
||||
}
|
||||
|
||||
update_personal_escape_achievements( escape_player_count, escape_time_remains, times_escaped, num_nerf_selected )
|
||||
{
|
||||
self update_achievement( "ESCAPE_ALL_PLAYERS" , escape_player_count );
|
||||
self update_achievement( "ESCAPE_IN_TIME" , escape_time_remains );
|
||||
self update_achievement( "ESCAPE_1ST_TIME" , times_escaped );
|
||||
self update_achievement( "ESCAPE_ALL_CHALLENGE", 1 );
|
||||
self update_achievement( "ESCAPE_WITH_NERF_ON" , num_nerf_selected );
|
||||
}
|
||||
|
||||
should_update_all_challenge( unused_1, unused_2, unused_3, unused_4, unused_5, unused_6, unused_7, unused_8, unused_9, unused_10 )
|
||||
{
|
||||
return level.all_challenge_completed;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Related to kill blocker hive
|
||||
/////////////////////////////////////////
|
||||
update_blocker_hive_achievements( hive_name )
|
||||
{
|
||||
switch( hive_name )
|
||||
{
|
||||
case "lodge_lung_3":
|
||||
update_achievement_all_players( "REACH_CITY", 1 );
|
||||
break;
|
||||
|
||||
case "city_lung_5":
|
||||
update_achievement_all_players( "REACH_CABIN", 1 );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update_achievement_all_players( reference, progress_amt )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
player update_achievement( reference, progress_amt );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Related to scavenge item
|
||||
/////////////////////////////////////////
|
||||
update_scavenge_achievement()
|
||||
{
|
||||
self update_achievement( "SCAVENGE_ITEM", 1 );
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Kill alien based on weapon
|
||||
/////////////////////////////////////////
|
||||
update_achievement_damage_weapon( sWeapon )
|
||||
{
|
||||
if ( isdefined( level.update_achievement_damage_weapon_func ) )
|
||||
self [[ level.update_achievement_damage_weapon_func ]] ( sWeapon );
|
||||
}
|
||||
|
||||
|
||||
// packNum is 0-indexed
|
||||
eggAllFoundForPack( packNum )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
println( "calculating eggstra xp..." );
|
||||
|
||||
//wait until the first hive is killed to give any awards.`
|
||||
level waittill_any( "regular_hive_destroyed", "obelisk_destroyed", "outpost_encounter_completed" );
|
||||
|
||||
//legacy variable - if its equal to 1015 then we have already awarded xp for packNum 0.
|
||||
legacyState = self GetCoopPlayerData( "alienPlayerStats", "deaths" );
|
||||
|
||||
eggState = self GetCoopPlayerDataReservedInt( "eggstra_state_flags" );
|
||||
packEggState = ( eggState >> ( packnum * 4 ) ) & 15;
|
||||
|
||||
if ( packEggstate == 15 )
|
||||
{
|
||||
//if we got here then the player has found all the eggs for packNum.
|
||||
eggstra_award_flags = self GetCoopPlayerDataReservedInt( "eggstra_award_flags" );
|
||||
hasModifiedFlags = false;
|
||||
|
||||
//update the new flags to reflect the legacy state
|
||||
//if we already awarded pack 0 xp, change eggstra_award_flags to reflect this
|
||||
if ( legacyState == 1015 && (( eggstra_award_flags & ( 1 << 0 )) != 1 ))
|
||||
{
|
||||
//mark the xp award as already given out. This is pack 0, so or a 1 into the 0th slot of the award_flags
|
||||
eggstra_award_flags |= ( 1 << 0 );
|
||||
hasModifiedFlags = true;
|
||||
}
|
||||
|
||||
// checks if we have given the xp award yet.
|
||||
if (( eggstra_award_flags & ( 1 << packNum )) == 0 )
|
||||
{
|
||||
//if we got here we have not awarded it yet.
|
||||
eggstra_award_flags |= ( 1 << packNum );
|
||||
hasModifiedFlags = true;
|
||||
|
||||
self SetClientOmnvar( "ui_alien_eggstra_xp", true );
|
||||
self thread maps\mp\alien\_persistence::wait_and_give_player_xp ( 10000, 5.0 ); //Give player 10,000 Egg-stra XP
|
||||
|
||||
}
|
||||
|
||||
//update the award flags
|
||||
if ( hasModifiedFlags == true )
|
||||
self SetCoopPlayerDataReservedInt( "eggstra_award_flags", eggstra_award_flags );
|
||||
|
||||
//needs updated to be a generic call.
|
||||
self update_mp_eggs_achievement( packnum );
|
||||
}
|
||||
}
|
||||
|
||||
update_mp_eggs_achievement( dlc_num )
|
||||
{
|
||||
switch ( dlc_num )
|
||||
{
|
||||
case 0:
|
||||
self update_achievement( "GOT_THEEGGSTRA_XP", 1 );
|
||||
break;
|
||||
case 1:
|
||||
self update_achievement( "GOT_THEEGGSTRA_XP_DLC2", 1 );
|
||||
break;
|
||||
case 2:
|
||||
self update_achievement( "GOT_THEEGGSTRA_XP_DLC3", 1 );
|
||||
break;
|
||||
case 3:
|
||||
self update_achievement( "GOT_THEEGGSTRA_XP_DLC4", 1 );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Related to scavenge item
|
||||
update_intel_achievement(dlc_num)
|
||||
{
|
||||
dlc_num = 0;
|
||||
map_name = getdvar("ui_mapname" );
|
||||
if(map_name == "mp_alien_armory")
|
||||
dlc_num = 1;
|
||||
if(map_name == "mp_alien_beacon")
|
||||
dlc_num = 2;
|
||||
if(map_name == "mp_alien_dlc3")
|
||||
dlc_num = 3;
|
||||
if(map_name == "mp_alien_last")
|
||||
dlc_num = 4;
|
||||
switch ( dlc_num )
|
||||
{
|
||||
case 1:
|
||||
self update_achievement( "FOUND_ALL_INTELS", 1 );
|
||||
break;
|
||||
case 2:
|
||||
self update_achievement( "FOUND_ALL_INTELS_MAYDAY", 1 );
|
||||
break;
|
||||
case 3:
|
||||
self update_achievement( "AWAKENING_ALL_INTEL", 1 );
|
||||
break;
|
||||
case 4:
|
||||
self update_achievement( "LAST_ALL_INTEL", 1 );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
2052
maps/mp/alien/_airdrop.gsc
Normal file
2052
maps/mp/alien/_airdrop.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1510
maps/mp/alien/_alien_class_skills_main.gsc
Normal file
1510
maps/mp/alien/_alien_class_skills_main.gsc
Normal file
File diff suppressed because it is too large
Load Diff
89
maps/mp/alien/_alien_fx.gsc
Normal file
89
maps/mp/alien/_alien_fx.gsc
Normal file
@@ -0,0 +1,89 @@
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
init_fx();
|
||||
}
|
||||
|
||||
init_fx()
|
||||
{
|
||||
level._effect[ "vfx_scrnfx_alien_spitter_mist" ] = LoadFX("vfx/gameplay/screen_effects/vfx_scrnfx_alien_spitter_mist");
|
||||
level._effect[ "vfx_scrnfx_alien_blood" ] = LoadFX("vfx/gameplay/screen_effects/vfx_scrnfx_alien_blood" );
|
||||
level._effect[ "vfx_scrnfx_tocam_slidedust_m" ] = LoadFX( "vfx/gameplay/screen_effects/vfx_scrnfx_tocam_slidedust_m" );
|
||||
level._effect[ "vfx_melee_blood_spray" ] = LoadFX( "vfx/gameplay/screen_effects/vfx_melee_blood_spray" );
|
||||
level._effect[ "vfx_blood_hit_left" ] = LoadFX( "vfx/gameplay/screen_effects/vfx_blood_hit_left" );
|
||||
level._effect[ "vfx_blood_hit_right" ] = LoadFX( "vfx/gameplay/screen_effects/vfx_blood_hit_right" );
|
||||
level._effect[ "vfx_alien_spitter_hit_left" ] = LoadFX( "vfx/gameplay/screen_effects/vfx_alien_spitter_hit_left" );
|
||||
level._effect[ "vfx_alien_spitter_hit_right" ] = LoadFX( "vfx/gameplay/screen_effects/vfx_alien_spitter_hit_right" );
|
||||
level._effect[ "vfx_alien_spitter_hit_center" ] = LoadFX( "vfx/gameplay/screen_effects/vfx_alien_spitter_hit_center" );
|
||||
|
||||
}
|
||||
|
||||
alien_fire_on()
|
||||
{
|
||||
if ( !isDefined( self.is_burning ) )
|
||||
{
|
||||
self.is_burning = 0;
|
||||
}
|
||||
self.is_burning++;
|
||||
|
||||
if ( self.is_burning == 1 )
|
||||
{
|
||||
self SetScriptablePartState( "body", "burning" );
|
||||
//self thread disable_fire_on_death();
|
||||
}
|
||||
}
|
||||
|
||||
alien_fire_off()
|
||||
{
|
||||
self.is_burning--;
|
||||
if ( self.is_burning > 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.is_burning = undefined;
|
||||
self notify( "fire_off" );
|
||||
self SetScriptablePartState( "body", "normal" );
|
||||
}
|
||||
|
||||
disable_fx_on_death()
|
||||
{
|
||||
self SetScriptablePartState( "body", "normal" );
|
||||
}
|
||||
|
||||
fx_stun_damage()
|
||||
{
|
||||
// Minion does not have stun scriptable state
|
||||
if ( self get_alien_type() == "minion" )
|
||||
return;
|
||||
|
||||
self endon ("death");
|
||||
self SetScriptablePartState("body", "shocked");
|
||||
wait 0.5;
|
||||
if ( IsAlive(self) )
|
||||
self SetScriptablePartState("body", "normal");
|
||||
|
||||
}
|
||||
|
||||
alien_cloak_fx_on()
|
||||
{
|
||||
if ( !isDefined( self.is_cloaking ) )
|
||||
self.is_cloaking = 0;
|
||||
|
||||
self playsound( "alien_teleport" );
|
||||
self.is_cloaking++;
|
||||
|
||||
if ( self.is_cloaking == 1 )
|
||||
self SetScriptablePartState( "body", "normal" );
|
||||
}
|
||||
|
||||
alien_cloak_fx_off()
|
||||
{
|
||||
self.is_cloaking--;
|
||||
if ( self.is_cloaking > 0 )
|
||||
return;
|
||||
self playsound( "alien_teleport_appear" );
|
||||
|
||||
self.is_cloaking = undefined;
|
||||
self SetScriptablePartState( "body", "normal" );
|
||||
}
|
||||
406
maps/mp/alien/_alien_matchdata.gsc
Normal file
406
maps/mp/alien/_alien_matchdata.gsc
Normal file
@@ -0,0 +1,406 @@
|
||||
MAX_NUM_ALIEN_HIVES = 25; // match the value defined as "MaxNumAliensHives" in matchdata.def
|
||||
MAX_NUM_PERKS_BOUGHT = 50; // match the value defined as "MaxNumAliensPerksBought" in matchdata.def
|
||||
MAX_NUM_UPGRADES = 32; // match the value defined as "MaxNumAliensUpgrades" in matchdata.def
|
||||
|
||||
CONST_MAX_BYTE = 127;
|
||||
CONST_MAX_SHORT = 32767;
|
||||
CONST_MAX_INT = 2147483647;
|
||||
|
||||
start_game_type()
|
||||
{
|
||||
init();
|
||||
|
||||
set_is_private_match();
|
||||
|
||||
override_gametype();
|
||||
|
||||
register_upgrade_types();
|
||||
|
||||
level thread wait_set_initial_player_count();
|
||||
}
|
||||
|
||||
set_is_private_match()
|
||||
{
|
||||
setMatchData( "aliensIsPrivateMatch", getDvarInt( "xblive_privatematch" ) );
|
||||
}
|
||||
|
||||
override_gametype() // We are overriding the matchdata "gametype" which is set from the MP logic at line 15 and 19 in _matchdata.gsc
|
||||
{
|
||||
setMatchData( "gametype", get_alien_game_type() );
|
||||
}
|
||||
|
||||
get_alien_game_type()
|
||||
{
|
||||
CONST_CHAOS_MODE = "aliens ch";
|
||||
CONST_HARDCORE_MODE = "aliens hc";
|
||||
CONST_CASUAL_MODE = "aliens ca";
|
||||
CONST_NORMAL_MODE = "aliens";
|
||||
|
||||
if ( maps\mp\alien\_utility::is_chaos_mode() )
|
||||
return CONST_CHAOS_MODE;
|
||||
|
||||
if ( maps\mp\alien\_utility::is_hardcore_mode() )
|
||||
return CONST_HARDCORE_MODE;
|
||||
else if ( maps\mp\alien\_utility::is_casual_mode() )
|
||||
return CONST_CASUAL_MODE;
|
||||
else
|
||||
return CONST_NORMAL_MODE;
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
alien_matchData = spawnStruct();
|
||||
|
||||
single_value_stats = [];
|
||||
single_value_stats["aliensTotalDrillDamage"] = get_single_value_struct( 0, "short" );
|
||||
alien_matchData.single_value_stats = single_value_stats;
|
||||
|
||||
challenge_results = [];
|
||||
alien_matchData.challenge_results = challenge_results;
|
||||
|
||||
level.alien_matchData = alien_matchData;
|
||||
}
|
||||
|
||||
wait_set_initial_player_count()
|
||||
{
|
||||
level endon( "gameEnded" );
|
||||
level waittill("prematch_over");
|
||||
setMatchData( "aliensInitialPlayerCount", validate_byte( level.players.size ) );
|
||||
}
|
||||
|
||||
on_player_connect()
|
||||
{
|
||||
player_init();
|
||||
|
||||
set_max_player_count();
|
||||
set_split_screen();
|
||||
set_alien_loadout();
|
||||
set_join_in_progress();
|
||||
set_relics_selected();
|
||||
set_upgrades_purchased();
|
||||
set_upgrades_enabled();
|
||||
}
|
||||
|
||||
player_init()
|
||||
{
|
||||
alien_matchData = spawnStruct();
|
||||
|
||||
single_value_stats = [];
|
||||
single_value_stats["aliensCashSpentOnWeapon"] = get_single_value_struct( 0, "int" );
|
||||
single_value_stats["aliensCashSpentOnAbility"] = get_single_value_struct( 0, "int" );
|
||||
single_value_stats["aliensCashSpentOnTrap"] = get_single_value_struct( 0, "int" );
|
||||
alien_matchData.single_value_stats = single_value_stats;
|
||||
|
||||
perk_upgraded = [];
|
||||
alien_matchData.perk_upgraded = perk_upgraded;
|
||||
|
||||
lastStand_record = [];
|
||||
lastStand_record["aliensTimesDowned"] = [];
|
||||
lastStand_record["aliensTimesRevived"] = [];
|
||||
lastStand_record["aliensTimesBledOut"] = [];
|
||||
alien_matchData.lastStand_record = lastStand_record;
|
||||
|
||||
self.alien_matchData = alien_matchData;
|
||||
}
|
||||
|
||||
set_max_player_count()
|
||||
{
|
||||
if ( !isDefined( level.max_player_count ) )
|
||||
level.max_player_count = 0;
|
||||
|
||||
if ( ( level.players.size + 1 ) > level.max_player_count )
|
||||
{
|
||||
level.max_player_count++;
|
||||
setMatchData( "aliensMaxPlayerCount", validate_byte( level.max_player_count ) );
|
||||
}
|
||||
}
|
||||
|
||||
set_split_screen()
|
||||
{
|
||||
setMatchData( "players", self.clientid, "isSplitscreen", self isSplitscreenPlayer() );
|
||||
}
|
||||
|
||||
set_join_in_progress()
|
||||
{
|
||||
if ( prematch_over() )
|
||||
setMatchData( "players", self.clientid, "aliensJIP", true );
|
||||
}
|
||||
|
||||
prematch_over()
|
||||
{
|
||||
if ( isDefined( level.startTime ) ) // level.startTime is defined after the prematch period is done
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
set_alien_loadout()
|
||||
{
|
||||
setMatchData( "players", self.clientid, "aliensLoadOut", 0, maps\mp\alien\_persistence::get_selected_perk_0() );
|
||||
setMatchData( "players", self.clientid, "aliensLoadOut", 1, maps\mp\alien\_persistence::get_selected_perk_1() );
|
||||
setMatchData( "players", self.clientid, "aliensLoadOut", 2, maps\mp\alien\_persistence::get_selected_dpad_up() );
|
||||
setMatchData( "players", self.clientid, "aliensLoadOut", 3, maps\mp\alien\_persistence::get_selected_dpad_down() );
|
||||
setMatchData( "players", self.clientid, "aliensLoadOut", 4, maps\mp\alien\_persistence::get_selected_dpad_left() );
|
||||
setMatchData( "players", self.clientid, "aliensLoadOut", 5, maps\mp\alien\_persistence::get_selected_dpad_right() );
|
||||
}
|
||||
|
||||
set_relics_selected()
|
||||
{
|
||||
num_enabled_nerfs = 0;
|
||||
|
||||
foreach( nerf in level.nerf_list )
|
||||
{
|
||||
if ( self alienscheckisrelicenabled( nerf ) )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "aliensRelics", num_enabled_nerfs, nerf );
|
||||
num_enabled_nerfs++;
|
||||
}
|
||||
}
|
||||
|
||||
for( i = num_enabled_nerfs; i < level.nerf_list.size; i++ )
|
||||
setMatchData( "players", self.clientid, "aliensRelics", i, "none" );
|
||||
}
|
||||
|
||||
set_upgrades_purchased()
|
||||
{
|
||||
num_upgrade_purchased = 0;
|
||||
|
||||
foreach( upgrade_ref in level.alien_upgrades )
|
||||
{
|
||||
if( self maps\mp\alien\_persistence::is_upgrade_purchased( upgrade_ref ) )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "aliensUpgradePurchased", num_upgrade_purchased, upgrade_ref );
|
||||
num_upgrade_purchased++;
|
||||
}
|
||||
}
|
||||
|
||||
for( index = num_upgrade_purchased; index < MAX_NUM_UPGRADES; index++ )
|
||||
setMatchData( "players", self.clientid, "aliensUpgradePurchased", index, "none" );
|
||||
}
|
||||
|
||||
set_upgrades_enabled()
|
||||
{
|
||||
num_upgrade_enabled = 0;
|
||||
|
||||
foreach( upgrade_ref in level.alien_upgrades )
|
||||
{
|
||||
if( self maps\mp\alien\_persistence::is_upgrade_enabled( upgrade_ref ) )
|
||||
{
|
||||
setMatchData( "players", self.clientid, "aliensUpgradeEnabled", num_upgrade_enabled, upgrade_ref );
|
||||
num_upgrade_enabled++;
|
||||
}
|
||||
}
|
||||
|
||||
for( index = num_upgrade_enabled; index < MAX_NUM_UPGRADES; index++ )
|
||||
setMatchData( "players", self.clientid, "aliensUpgradeEnabled", index, "none" );
|
||||
}
|
||||
|
||||
inc_drill_heli_damages( damage_amt )
|
||||
{
|
||||
level.alien_matchData.single_value_stats["aliensTotalDrillDamage"].value += damage_amt;
|
||||
}
|
||||
|
||||
set_escape_time_remaining( escape_time_remains )
|
||||
{
|
||||
setMatchData( "aliensEscapeTimeRemaining" , validate_int( escape_time_remains ) );
|
||||
}
|
||||
|
||||
update_challenges_status( challenge_name, result )
|
||||
{
|
||||
if ( level.alien_matchData.challenge_results.size > MAX_NUM_ALIEN_HIVES )
|
||||
return;
|
||||
|
||||
challenge_status = spawnStruct();
|
||||
challenge_status.challenge_name = challenge_name;
|
||||
challenge_status.result = result;
|
||||
|
||||
level.alien_matchData.challenge_results[level.alien_matchData.challenge_results.size] = challenge_status;
|
||||
}
|
||||
|
||||
record_perk_upgrade( perk_name )
|
||||
{
|
||||
if ( self.alien_matchData.perk_upgraded.size > MAX_NUM_PERKS_BOUGHT )
|
||||
return;
|
||||
|
||||
self.alien_matchData.perk_upgraded[self.alien_matchData.perk_upgraded.size] = perk_name;
|
||||
}
|
||||
|
||||
inc_downed_counts()
|
||||
{
|
||||
inc_lastStand_record( "aliensTimesDowned" );
|
||||
}
|
||||
|
||||
inc_revived_counts()
|
||||
{
|
||||
inc_lastStand_record( "aliensTimesRevived" );
|
||||
}
|
||||
|
||||
inc_bleedout_counts()
|
||||
{
|
||||
inc_lastStand_record( "aliensTimesBledOut" );
|
||||
}
|
||||
|
||||
inc_lastStand_record( field_name )
|
||||
{
|
||||
if ( !isDefined( self.alien_matchData.lastStand_record[field_name][level.num_hive_destroyed] ) )
|
||||
self.alien_matchData.lastStand_record[field_name][level.num_hive_destroyed] = 0;
|
||||
|
||||
self.alien_matchData.lastStand_record[field_name][level.num_hive_destroyed]++;
|
||||
}
|
||||
|
||||
update_spending_type( amount_spent, spending_type )
|
||||
{
|
||||
switch( spending_type )
|
||||
{
|
||||
case "weapon":
|
||||
self.alien_matchData.single_value_stats["aliensCashSpentOnWeapon"].value += amount_spent;
|
||||
break;
|
||||
|
||||
case "ability":
|
||||
self.alien_matchData.single_value_stats["aliensCashSpentOnAbility"].value += amount_spent;
|
||||
break;
|
||||
|
||||
case "trap":
|
||||
self.alien_matchData.single_value_stats["aliensCashSpentOnTrap"].value += amount_spent;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertMsg( "Spending type: " + spending_type + " is not recognized." );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EndGame( end_condition, play_time )
|
||||
{
|
||||
set_game_data( end_condition, play_time );
|
||||
|
||||
foreach( player in level.players )
|
||||
player set_player_game_data();
|
||||
|
||||
sendMatchData();
|
||||
}
|
||||
|
||||
set_game_data( end_condition, play_time )
|
||||
{
|
||||
CONST_CHALLENGES_COMPLETED = "aliensChallengesCompleted";
|
||||
|
||||
setMatchData( "aliensFinalPlayerCount" , validate_byte( level.players.size ) );
|
||||
setMatchData( "aliensHivesDestroyed" , validate_byte( level.num_hive_destroyed ) );
|
||||
setMatchData( "aliensGameOverCondition", end_condition );
|
||||
setMatchData( "aliensTotalTimeElapsed" , validate_int( play_time ) );
|
||||
|
||||
alien_matchData = level.alien_matchData;
|
||||
|
||||
foreach( matchData_field, value_struct in alien_matchData.single_value_stats )
|
||||
{
|
||||
value = validate_value( value_struct.value, value_struct.value_type );
|
||||
setMatchData( matchData_field , value );
|
||||
}
|
||||
|
||||
foreach( index, challenge_status in alien_matchData.challenge_results )
|
||||
{
|
||||
setMatchData( CONST_CHALLENGES_COMPLETED, index, "challengeId", challenge_status.challenge_name );
|
||||
setMatchData( CONST_CHALLENGES_COMPLETED, index, "success" , challenge_status.result );
|
||||
}
|
||||
}
|
||||
|
||||
set_player_game_data()
|
||||
{
|
||||
copy_from_playerData();
|
||||
set_perk_upgraded();
|
||||
set_lastStand_stats();
|
||||
set_single_value_stats();
|
||||
}
|
||||
|
||||
copy_from_playerData()
|
||||
{
|
||||
// Those fields are already tracked in the alienSession section in player data
|
||||
setMatchData( "players", self.clientid, "aliensFinalScore" , validate_int( self GetCoopPlayerData( "alienSession", "score" ) ) );
|
||||
setMatchData( "players", self.clientid, "aliensDrillRepairs", validate_byte( self GetCoopPlayerData( "alienSession", "repairs" ) ) );
|
||||
setMatchData( "players", self.clientid, "aliensXpEarned" , validate_int( self GetCoopPlayerData( "alienSession", "experience" ) ) );
|
||||
}
|
||||
|
||||
set_perk_upgraded()
|
||||
{
|
||||
foreach( index, perk_name in self.alien_matchData.perk_upgraded )
|
||||
setMatchData( "players", self.clientid, "aliensPerksBought", index, perk_name );
|
||||
}
|
||||
|
||||
set_lastStand_stats()
|
||||
{
|
||||
foreach( stat_type, info_array in self.alien_matchData.lastStand_record )
|
||||
{
|
||||
foreach( hive_index, counts in info_array )
|
||||
setMatchData( "players", self.clientid, stat_type, hive_index, validate_byte( counts ) );
|
||||
}
|
||||
}
|
||||
|
||||
set_single_value_stats()
|
||||
{
|
||||
foreach( field_name, value_struct in self.alien_matchData.single_value_stats )
|
||||
{
|
||||
value = validate_value( value_struct.value, value_struct.value_type );
|
||||
setMatchData( "players", self.clientid, field_name, value );
|
||||
}
|
||||
}
|
||||
|
||||
validate_value( value, data_type )
|
||||
{
|
||||
switch( data_type )
|
||||
{
|
||||
case "byte":
|
||||
return validate_byte( value );
|
||||
|
||||
case "short":
|
||||
return validate_short( value );
|
||||
|
||||
case "int":
|
||||
return validate_int( value );
|
||||
|
||||
default:
|
||||
AssertMsg( "Value type: " + data_type + " is not supported" );
|
||||
}
|
||||
}
|
||||
|
||||
validate_byte( value )
|
||||
{
|
||||
return int( min( value, CONST_MAX_BYTE ) );
|
||||
}
|
||||
|
||||
validate_short( value )
|
||||
{
|
||||
return int( min( value, CONST_MAX_SHORT ) );
|
||||
}
|
||||
|
||||
validate_int( value )
|
||||
{
|
||||
return int( min( value, CONST_MAX_INT ) );
|
||||
}
|
||||
|
||||
get_single_value_struct( initial_value, value_type )
|
||||
{
|
||||
value_struct = spawnStruct();
|
||||
value_struct.value = initial_value;
|
||||
value_struct.value_type = value_type;
|
||||
|
||||
return value_struct;
|
||||
}
|
||||
|
||||
register_upgrade_types()
|
||||
{
|
||||
UPGRADE_TABLE = "mp/alien/alien_purchasable_items.csv";
|
||||
TABLE_INDEX_COLUMN = 0;
|
||||
UPGRADE_REF_COLUMN = 1;
|
||||
|
||||
upgrades = [];
|
||||
for ( index = 0; index < MAX_NUM_UPGRADES; index++ )
|
||||
{
|
||||
upgrade_ref = tablelookup( UPGRADE_TABLE, TABLE_INDEX_COLUMN, index, UPGRADE_REF_COLUMN );
|
||||
if ( maps\mp\agents\alien\_alien_agents::is_empty_string( upgrade_ref ) )
|
||||
break;
|
||||
|
||||
upgrades[upgrades.size] = upgrade_ref;
|
||||
}
|
||||
|
||||
level.alien_upgrades = upgrades;
|
||||
}
|
||||
1848
maps/mp/alien/_autosentry_alien.gsc
Normal file
1848
maps/mp/alien/_autosentry_alien.gsc
Normal file
File diff suppressed because it is too large
Load Diff
407
maps/mp/alien/_challenge.gsc
Normal file
407
maps/mp/alien/_challenge.gsc
Normal file
@@ -0,0 +1,407 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\alien\_utility;
|
||||
#include maps\mp\alien\_challenge_function;
|
||||
|
||||
init_challenge()
|
||||
{
|
||||
//set up the challenge table automatically
|
||||
map_name = getdvar("ui_mapname" );
|
||||
level.alien_challenge_table = "mp/alien/" + map_name + "_challenges.csv";
|
||||
|
||||
if ( maps\mp\alien\_utility::is_hardcore_mode() )
|
||||
{
|
||||
level.alien_challenge_table = "mp/alien/" + map_name + "_hardcore_challenges.csv";
|
||||
if ( !TableExists( level.alien_challenge_table ) )
|
||||
level.alien_challenge_table = "mp/alien/" + map_name + "_challenges.csv";
|
||||
}
|
||||
|
||||
init_challenge_type();
|
||||
}
|
||||
|
||||
spawn_challenge()
|
||||
{
|
||||
if ( !alien_mode_has( "challenge" ) )
|
||||
return;
|
||||
|
||||
level.current_challenge_index = undefined;
|
||||
level thread spawn_challenge_internal();
|
||||
}
|
||||
|
||||
spawn_challenge_internal()
|
||||
{
|
||||
challenge = get_valid_challenge();
|
||||
if ( !isDefined ( challenge ) )
|
||||
{
|
||||
println( "***CHALLENGE_ERROR*** - no challenge found" );
|
||||
return;
|
||||
}
|
||||
/#
|
||||
if ( GetDvar( "scr_setactivechallenge" ) != "" )
|
||||
{
|
||||
challenge = GetDvar( "scr_setactivechallenge" );
|
||||
SetDvar( "scr_setactivechallenge" ,"" );
|
||||
}
|
||||
#/
|
||||
activate_new_challenge ( challenge );
|
||||
}
|
||||
|
||||
update_challenge ( challenge_name, param_1, param_2, param_3, param_4, param_5, param_6, param_7, param_8, param_9 )
|
||||
{
|
||||
if ( !current_challenge_is ( challenge_name ) || !alien_mode_has( "challenge" ) )
|
||||
return;
|
||||
|
||||
if ( level.pre_challenge_active )
|
||||
return;
|
||||
|
||||
current_challenge = level.challenge_data [ level.current_challenge ];
|
||||
current_challenge [[ current_challenge.updateFunc ]]( param_1, param_2, param_3, param_4, param_5, param_6, param_7, param_8, param_9 );
|
||||
}
|
||||
|
||||
end_current_challenge()
|
||||
{
|
||||
if ( current_challenge_exist() && alien_mode_has( "challenge" ) )
|
||||
deactivate_current_challenge();
|
||||
}
|
||||
|
||||
remove_all_challenge_cases()
|
||||
{
|
||||
level notify ( "remove_all_challenge_case" );
|
||||
}
|
||||
|
||||
get_valid_challenge()
|
||||
{
|
||||
valid_challenges = [];
|
||||
|
||||
foreach ( challenge in level.challenge_data )
|
||||
{
|
||||
if ( isDefined ( challenge.already_issued ) ) //don't repeat the challenges
|
||||
continue;
|
||||
if ( level.players.size == 1 && !is_true(challenge.allowedinsolo) ) //challenge not allowed in solo
|
||||
continue;
|
||||
|
||||
if ( !isDefined ( challenge.allowed_cycles ) )
|
||||
continue;
|
||||
|
||||
allowed_cycles = strTok( challenge.allowed_cycles, " " );
|
||||
|
||||
foreach ( cycle in allowed_cycles ) //see if this challenge is allowed
|
||||
{
|
||||
if ( level.cycle_count - 1 == int( cycle ) )
|
||||
{
|
||||
current_hive = maps\mp\alien\_spawn_director::get_current_encounter();
|
||||
if ( !isDefined ( current_hive ) )
|
||||
{
|
||||
println("***CHALLENGE ERROR*** - no current hive found" );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( should_skip_challenge( challenge ) )
|
||||
continue;
|
||||
|
||||
allowed_hives = StrTok( challenge.allowed_hives," " );
|
||||
foreach ( hive in allowed_hives ) //see if this challenge is allowed for this hive
|
||||
{
|
||||
if ( hive == current_hive )
|
||||
{
|
||||
valid_challenges[ valid_challenges.size ] = challenge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( valid_challenges.size > 0 )
|
||||
{
|
||||
valid_challenge = valid_challenges[randomint( valid_challenges.size )];
|
||||
valid_challenge.already_issued = true;
|
||||
|
||||
return valid_challenge.ref;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
should_skip_challenge( challenge )
|
||||
{
|
||||
is_weapon_challenge = (challenge.ref == "ar_only" ||
|
||||
challenge.ref == "smg_only" ||
|
||||
challenge.ref == "lmgs_only" ||
|
||||
challenge.ref == "shotguns_only" ||
|
||||
challenge.ref == "2_weapons_only" ||
|
||||
challenge.ref == "semi_autos_only" ||
|
||||
challenge.ref == "new_weapon" ||
|
||||
challenge.ref == "snipers_only" );
|
||||
|
||||
if( !is_weapon_challenge )
|
||||
return false;
|
||||
|
||||
num_pistol_prestige_players = 0;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player maps\mp\alien\_prestige::prestige_getPistolsOnly() == 1 )
|
||||
num_pistol_prestige_players++;
|
||||
}
|
||||
|
||||
if ( challenge.ref == "new_weapon" && num_pistol_prestige_players > 0 ) //skip this challenge if any single person has the nerf
|
||||
return true;
|
||||
|
||||
if ( num_pistol_prestige_players >= level.players.size - 1 ) //skip this challenge if the majority of the players have the nerf
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
deactivate_current_challenge()
|
||||
{
|
||||
if ( !current_challenge_exist() )
|
||||
return;
|
||||
|
||||
current_challenge = level.challenge_data [ level.current_challenge ];
|
||||
|
||||
unset_current_challenge();
|
||||
if ( current_challenge [[ current_challenge.isSuccessFunc ]]() )
|
||||
{
|
||||
display_challenge_message ( "challenge_success",false );
|
||||
current_challenge [[ current_challenge.rewardFunc ]]();
|
||||
maps\mp\alien\_gamescore::update_players_encounter_performance( maps\mp\alien\_gamescore::get_challenge_score_component_name(), "challenge_complete" );
|
||||
maps\mp\alien\_persistence::update_LB_alienSession_challenge( true );
|
||||
maps\mp\alien\_alien_matchdata::update_challenges_status( current_challenge.ref, true );
|
||||
level.num_challenge_completed++;
|
||||
|
||||
if ( !is_casual_mode() )
|
||||
{
|
||||
if ( level.num_challenge_completed == 10 )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
player maps\mp\alien\_persistence::give_player_tokens( 2, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
display_challenge_message ( "challenge_failed",false );
|
||||
current_challenge [[ current_challenge.failFunc ]]();
|
||||
level.all_challenge_completed = false;
|
||||
maps\mp\alien\_persistence::update_LB_alienSession_challenge( false );
|
||||
maps\mp\alien\_alien_matchdata::update_challenges_status( current_challenge.ref, false );
|
||||
}
|
||||
|
||||
current_challenge [[ current_challenge.deactivateFunc ]]();
|
||||
}
|
||||
|
||||
activate_new_challenge ( challenge_name )
|
||||
{
|
||||
assert( IsDefined( challenge_name ) && isDefined ( level.challenge_data [ challenge_name ] ) );
|
||||
|
||||
new_challenge = level.challenge_data [ challenge_name ];
|
||||
|
||||
if ( new_challenge [[ new_challenge.canActivateFunc ]]() )
|
||||
{
|
||||
|
||||
scalar = get_challenge_scalar( challenge_name );
|
||||
if ( isDefined ( scalar ) )
|
||||
{
|
||||
level.challenge_data[ challenge_name ].goal = scalar;
|
||||
level.current_challenge_scalar = scalar;
|
||||
}
|
||||
else
|
||||
level.current_challenge_scalar = -1;
|
||||
|
||||
display_challenge_message ( challenge_name, true, scalar ); //get_challenge_activate_string( new_challenge ) );
|
||||
set_current_challenge ( challenge_name );
|
||||
|
||||
level.pre_challenge_active = true;
|
||||
challenge_countdown();
|
||||
level.pre_challenge_active = false;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player setClientOmnvar ( "ui_intel_prechallenge", 0 );
|
||||
}
|
||||
|
||||
level.current_challenge_pre_challenge = 0;
|
||||
new_challenge [[ new_challenge.activateFunc ]]();
|
||||
}
|
||||
else
|
||||
{
|
||||
new_challenge [[ new_challenge.failActivateFunc ]]();
|
||||
}
|
||||
}
|
||||
|
||||
challenge_countdown()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
new_challenge_time = int ( gettime() + 5000 );
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player SetClientOmnvar( "ui_intel_timer",new_challenge_time );
|
||||
player SetClientOmnvar ( "ui_intel_title", 1 );
|
||||
}
|
||||
level.current_challenge_title = 1;
|
||||
wait ( 5 );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player SetClientOmnvar( "ui_intel_timer",-1 );
|
||||
player SetClientOmnvar ( "ui_intel_title", -1 );
|
||||
}
|
||||
level.current_challenge_title = -1;
|
||||
wait ( .5 );
|
||||
}
|
||||
|
||||
|
||||
can_pick_up_challenge ( player )
|
||||
{
|
||||
if ( !IsPlayer(player) )
|
||||
return false;
|
||||
|
||||
if ( isAI( player ) )
|
||||
return false;
|
||||
|
||||
if ( !isAlive( player ) || ( isDefined( player.fauxDead ) && player.fauxDead ) ) //there is a time when you kill your self with remote that this will pass
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
display_challenge_message ( message, activate , scalar )
|
||||
{
|
||||
index = TableLookup(level.alien_challenge_table, 1, message, 0 );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
|
||||
if ( activate )
|
||||
{
|
||||
if ( isDefined ( scalar ) )
|
||||
{
|
||||
player SetClientOmnvar( "ui_intel_challenge_scalar", scalar );
|
||||
}
|
||||
else
|
||||
{
|
||||
player SetClientOmnvar( "ui_intel_challenge_scalar",-1 );
|
||||
}
|
||||
player setClientOmnvar ( "ui_intel_prechallenge", 1 );
|
||||
player SetClientOmnvar( "ui_intel_active_index", int( index ) );
|
||||
level.current_challenge_index = int( index );
|
||||
level.current_challenge_pre_challenge = 1;
|
||||
player PlayLocalSound( "mp_intel_received" );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
player SetClientOmnvar( "ui_intel_active_index", -1 );
|
||||
player SetClientOmnvar ( "ui_intel_progress_current",-1 );
|
||||
player setClientOmnvar ( "ui_intel_progress_max",-1 );
|
||||
player setClientOmnvar ( "ui_intel_percent",-1 );
|
||||
player SetClientOmnvar ( "ui_intel_target_player" , -1 );
|
||||
player setClientOmnvar ( "ui_intel_prechallenge", 0 );
|
||||
player SetClientOmnvar( "ui_intel_timer",-1 );
|
||||
player SetClientOmnvar( "ui_intel_challenge_scalar" ,-1);
|
||||
|
||||
level.current_challenge_index = -1;
|
||||
level.current_challenge_progress_max = -1;
|
||||
level.current_challenge_progress_current = -1;
|
||||
level.current_challenge_percent = -1;
|
||||
level.current_challenge_target_player = -1;
|
||||
level.current_challenge_timer = -1;
|
||||
level.current_challenge_scalar = -1;
|
||||
level.current_challenge_pre_challenge = 0;
|
||||
}
|
||||
}
|
||||
if ( activate )
|
||||
return;
|
||||
|
||||
level thread show_challenge_outcome( message, index );
|
||||
|
||||
}
|
||||
|
||||
show_challenge_outcome( message, challenge_message_index )
|
||||
{
|
||||
level endon("game_ended");
|
||||
wait ( 1 );
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( message == "challenge_failed" )
|
||||
{
|
||||
player SetClientOmnvar("ui_intel_active_index", int( challenge_message_index ) );
|
||||
player PlayLocalSound( "mp_intel_fail" );
|
||||
}
|
||||
else
|
||||
{
|
||||
player SetClientOmnvar("ui_intel_active_index", int ( challenge_message_index ) );
|
||||
player PlayLocalSound( "mp_intel_success" );
|
||||
}
|
||||
}
|
||||
wait ( 4 );
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player SetClientOmnvar("ui_intel_active_index",-1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_challenge_exist() { return isDefined ( level.current_challenge ); }
|
||||
current_challenge_is ( challenge_name ) { return ( current_challenge_exist() && level.current_challenge == challenge_name ); }
|
||||
unset_current_challenge() { level.current_challenge = undefined; }
|
||||
set_current_challenge ( challenge_name ) { level.current_challenge = challenge_name; }
|
||||
|
||||
|
||||
handle_challenge_hotjoin()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
self setClientOmnvar ( "ui_intel_prechallenge", level.current_challenge_pre_challenge );
|
||||
|
||||
if ( current_challenge_exist() )
|
||||
{
|
||||
|
||||
self SetClientOmnvar( "ui_intel_active_index", int( level.current_challenge_index ) );
|
||||
self SetClientOmnvar ( "ui_intel_progress_current", int ( level.current_challenge_progress_current ) );
|
||||
self setClientOmnvar ( "ui_intel_progress_max", int ( level.current_challenge_progress_max ) );
|
||||
self setClientOmnvar ( "ui_intel_percent", int ( level.current_challenge_percent ) );
|
||||
self SetClientOmnvar ( "ui_intel_target_player" , int ( level.current_challenge_target_player ) );
|
||||
self setclientOmnvar ( "ui_intel_title", int ( level.current_challenge_title ) );
|
||||
if ( level.current_challenge_timer > 0 )
|
||||
self SetClientOmnvar( "ui_intel_timer", int ( gettime() + ( level.current_challenge_timer * 1000) ) );
|
||||
|
||||
self SetClientOmnvar( "ui_intel_challenge_scalar", level.current_challenge_scalar );
|
||||
}
|
||||
|
||||
if ( level.current_challenge == "50_percent_accuracy" || level.current_challenge == "75_percent_accuracy" )// accuracy challenges
|
||||
{
|
||||
challenge = level.challenge_data[ level.current_challenge ];
|
||||
self thread track_percent_accuracy_shots_fired_internal( challenge );
|
||||
}
|
||||
else if ( level.current_challenge == "no_reloads" ) //no reloads
|
||||
{
|
||||
self thread wait_for_reload();
|
||||
}
|
||||
else if ( level.current_challenge == "no_abilities" ) // use no abilities
|
||||
{
|
||||
self thread wait_for_ability_use();
|
||||
}
|
||||
|
||||
if ( isDefined ( level.current_drill_health ) )
|
||||
{
|
||||
SetOmnvar( "ui_alien_drill_health_text", int( level.current_drill_health ) );
|
||||
}
|
||||
if ( isDefined ( level.current_drill_time ) )
|
||||
{
|
||||
SetOmnvar( "ui_alien_drill_end_milliseconds", int( level.current_drill_time ) );
|
||||
}
|
||||
}
|
||||
|
||||
get_num_challenge_completed()
|
||||
{
|
||||
if ( !isDefined( level.num_challenge_completed ) )
|
||||
return 0;
|
||||
else
|
||||
return level.num_challenge_completed;
|
||||
}
|
||||
1722
maps/mp/alien/_challenge_function.gsc
Normal file
1722
maps/mp/alien/_challenge_function.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1383
maps/mp/alien/_chaos.gsc
Normal file
1383
maps/mp/alien/_chaos.gsc
Normal file
File diff suppressed because it is too large
Load Diff
135
maps/mp/alien/_chaos_laststand.gsc
Normal file
135
maps/mp/alien/_chaos_laststand.gsc
Normal file
@@ -0,0 +1,135 @@
|
||||
#include maps\mp\alien\_laststand;
|
||||
|
||||
CONST_CHAOS_SELF_REVIVE_TIME = 15;
|
||||
CONST_CHAOS_REVIVE_TIME = 3000; // in ms
|
||||
CONST_CHAOS_INITIAL_LASTSTANDS = 3;
|
||||
|
||||
chaos_PlayerLastStand( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, bleedOutSpawnEntity )
|
||||
{
|
||||
gameShouldEnd = chaos_gameShouldEnd( self );
|
||||
|
||||
if ( gameShouldEnd )
|
||||
maps\mp\alien\_chaos_utility::chaos_end_game();
|
||||
|
||||
if ( is_killed_by_kill_trigger( bleedOutSpawnEntity ) )
|
||||
return process_killed_by_kill_trigger( bleedOutSpawnEntity );
|
||||
|
||||
chaos_dropIntoLastStand( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, bleedOutSpawnEntity, gameShouldEnd );
|
||||
}
|
||||
|
||||
process_killed_by_kill_trigger( bleedOutSpawnEntity )
|
||||
{
|
||||
self setOrigin( bleedOutSpawnEntity.origin );
|
||||
maps\mp\alien\_death::set_kill_trigger_event_processed( self, false );
|
||||
|
||||
if ( !self.inLastStand )
|
||||
self DoDamage( 1000, self.origin ); // Do enough damage so code will drop player into laststand.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chaos_dropIntoLastStand( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, bleedOutSpawnEntity, gameShouldEnd )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self notify( "last_stand" );
|
||||
|
||||
enter_GamemodeSpecificAction();
|
||||
|
||||
enter_laststand();
|
||||
|
||||
if ( get_last_stand_count() > 0 )
|
||||
chaos_self_revive( gameShouldEnd );
|
||||
else
|
||||
wait_to_be_revived( self, self.origin, undefined, undefined, true, CONST_CHAOS_REVIVE_TIME, ( 0.33, 0.75, 0.24 ), undefined, false, gameShouldEnd, true );
|
||||
|
||||
self notify( "revive" );
|
||||
|
||||
exit_laststand();
|
||||
|
||||
exit_GamemodeSpecificAction();
|
||||
}
|
||||
|
||||
chaos_self_revive( gameShouldEnd )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "revive");
|
||||
level endon( "game_ended" );
|
||||
|
||||
/#
|
||||
if ( self maps\mp\alien\_debug::shouldSelfRevive() )
|
||||
return debug_self_revive();
|
||||
#/
|
||||
|
||||
self set_in_chaos_self_revive( self, true );
|
||||
self take_lastStand( self, 1 );
|
||||
self register_laststand_ammo();
|
||||
|
||||
return ( wait_for_chaos_self_revive( gameShouldEnd, CONST_CHAOS_SELF_REVIVE_TIME ) );
|
||||
}
|
||||
|
||||
wait_for_chaos_self_revive( gameShouldEnd, duration )
|
||||
{
|
||||
if ( gameShouldEnd )
|
||||
{
|
||||
level waittill( "forever" ); //<NOTE J.C.> When this happens, the "game_ended" notify will already happen. Wait here is to make sure the player stays in this state until game fully ended.
|
||||
return false; // Returning a false here is to be logically consistent of always returning true/false from this function
|
||||
}
|
||||
|
||||
maps\mp\alien\_hud::set_last_stand_timer( self, duration );
|
||||
self common_scripts\utility::waittill_any_timeout( duration, "revive_success" );
|
||||
maps\mp\alien\_hud::clear_last_stand_timer( self );
|
||||
return true;
|
||||
}
|
||||
|
||||
chaos_gameShouldEnd( player_just_down )
|
||||
{
|
||||
return ( get_team_self_revive_count() == 0 && everyone_else_all_in_lastStand( player_just_down ) && no_one_else_in_chaos_self_revive( player_just_down ) );
|
||||
}
|
||||
|
||||
no_one_else_in_chaos_self_revive( player_just_down )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( player == player_just_down )
|
||||
continue;
|
||||
|
||||
if ( is_in_chaos_self_revive( player ) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
get_team_self_revive_count()
|
||||
{
|
||||
total_self_revive_count = 0;
|
||||
|
||||
foreach( player in level.players )
|
||||
total_self_revive_count += player get_last_stand_count();
|
||||
|
||||
return total_self_revive_count;
|
||||
}
|
||||
|
||||
CONST_PRE_GAME_IS_OVER_FLAG = "chaos_pre_game_is_over";
|
||||
|
||||
chaos_player_init_laststand()
|
||||
{
|
||||
if ( common_scripts\utility::flag( CONST_PRE_GAME_IS_OVER_FLAG ) )
|
||||
return;
|
||||
|
||||
set_last_stand_count( self, CONST_CHAOS_INITIAL_LASTSTANDS );
|
||||
self thread init_selfrevive_icon( CONST_CHAOS_INITIAL_LASTSTANDS );
|
||||
}
|
||||
|
||||
chaos_exit_GamemodeSpecificAction( player )
|
||||
{
|
||||
player maps\mp\alien\_damage::setBodyArmor( level.deployablebox_vest_max );
|
||||
player notify( "enable_armor" );
|
||||
player set_in_chaos_self_revive( self, false );
|
||||
maps\mp\alien\_chaos::process_chaos_event( "refill_combo_meter" );
|
||||
}
|
||||
|
||||
set_in_chaos_self_revive( player, value ) { player.in_chaos_self_revive = value; }
|
||||
should_instant_revive( attacker ) { return ( isDefined( attacker ) && is_in_chaos_self_revive( attacker ) ); }
|
||||
is_in_chaos_self_revive( player ) { return maps\mp\alien\_utility::is_true( player.in_chaos_self_revive ); }
|
||||
542
maps/mp/alien/_chaos_utility.gsc
Normal file
542
maps/mp/alien/_chaos_utility.gsc
Normal file
@@ -0,0 +1,542 @@
|
||||
init_chaos_score_components()
|
||||
{
|
||||
level.combo_counter = 0;
|
||||
level.score_streak = 0;
|
||||
level.running_score_base = 0;
|
||||
level.total_score = 0;
|
||||
}
|
||||
|
||||
calculate_total_score()
|
||||
{
|
||||
level.total_score = level.running_score_base + level.combo_counter * level.score_streak;
|
||||
maps\mp\alien\_hud::set_total_score( level.total_score );
|
||||
return level.total_score;
|
||||
}
|
||||
|
||||
keep_running_score()
|
||||
{
|
||||
level.running_score_base += get_combo_counter() * get_score_streak();
|
||||
}
|
||||
|
||||
reset_combo_counter() { level.combo_counter = 0; }
|
||||
add_combo_counter( increment ) { level.combo_counter += increment ; }
|
||||
add_score_streak( increment ) { level.score_streak += increment; }
|
||||
get_combo_counter() { return level.combo_counter; }
|
||||
get_score_streak() { return level.score_streak; }
|
||||
get_total_score() { return level.total_score; }
|
||||
|
||||
TABLE_INDEX_COLUMN = 0;
|
||||
|
||||
CHAOS_EVENT_TABLE = "mp/alien/chaos_events.csv";
|
||||
GSC_ID_COLUMN = 1;
|
||||
LUA_EVENT_ID_COLUMN = 2;
|
||||
COMBO_INC_COLUMN = 4;
|
||||
SCORE_INC_COLUMN = 5;
|
||||
MAX_EVENT_INDEX = 100;
|
||||
|
||||
register_chaos_events()
|
||||
{
|
||||
level.chaos_events = [];
|
||||
|
||||
for ( entryIndex = 1; entryIndex <= MAX_EVENT_INDEX; entryIndex++ )
|
||||
{
|
||||
event_ID = table_look_up( CHAOS_EVENT_TABLE, entryIndex, GSC_ID_COLUMN );
|
||||
if ( maps\mp\agents\alien\_alien_agents::is_empty_string( event_ID ) )
|
||||
break;
|
||||
|
||||
event_info = [];
|
||||
event_info["LUA_event_ID"] = int( table_look_up( CHAOS_EVENT_TABLE, entryIndex, LUA_EVENT_ID_COLUMN ) );
|
||||
event_info["combo_inc"] = int( table_look_up( CHAOS_EVENT_TABLE, entryIndex, COMBO_INC_COLUMN ) );
|
||||
event_info["score_inc"] = int( table_look_up( CHAOS_EVENT_TABLE, entryIndex, SCORE_INC_COLUMN ) );
|
||||
|
||||
level.chaos_events[event_ID] = event_info;
|
||||
}
|
||||
}
|
||||
|
||||
WEAPON_START_INDEX = 1000;
|
||||
WEAPON_END_INDEX = 1099;
|
||||
|
||||
add_chaos_weapon( world_item_list )
|
||||
{
|
||||
for( index = WEAPON_START_INDEX; index <= WEAPON_END_INDEX; index++ )
|
||||
{
|
||||
if( is_empty_value( index ) )
|
||||
break;
|
||||
|
||||
weapon_struct = make_weapon_struct( index );
|
||||
world_item_list[world_item_list.size] = weapon_struct;
|
||||
}
|
||||
|
||||
return world_item_list;
|
||||
}
|
||||
|
||||
make_weapon_struct( index )
|
||||
{
|
||||
weapon_struct = spawnStruct();
|
||||
|
||||
weapon_struct.script_noteworthy = get_weapon_ref( index );
|
||||
weapon_struct.origin = get_weapon_origin( index );
|
||||
weapon_struct.angles = get_weapon_angles( index );
|
||||
|
||||
return weapon_struct;
|
||||
}
|
||||
|
||||
WEAPON_REF_COLUMN = 1;
|
||||
WEAPON_ORIGIN_COLUMN = 2;
|
||||
WEAPON_ANGLES_COLUMN = 3;
|
||||
|
||||
is_empty_value( index ) { return ( table_look_up( level.alien_cycle_table, index, WEAPON_REF_COLUMN ) == "" ); }
|
||||
get_weapon_ref( index ) { return get_weapon_info( index, WEAPON_REF_COLUMN ); }
|
||||
get_weapon_origin( index ) { return transform_to_coordinate( get_weapon_info( index, WEAPON_ORIGIN_COLUMN ) ); }
|
||||
get_weapon_angles( index ) { return transform_to_coordinate( get_weapon_info( index, WEAPON_ANGLES_COLUMN ) ); }
|
||||
get_weapon_info( index, column ) { return table_look_up( level.alien_cycle_table, index, column ); }
|
||||
|
||||
register_perk( perk_ref, activate_func, deactivate_func )
|
||||
{
|
||||
perk_info = [];
|
||||
perk_info["perk_ref"] = perk_ref;
|
||||
perk_info["activate_func"] = activate_func;
|
||||
perk_info["deactivate_func"] = deactivate_func;
|
||||
perk_info["LUA_perk_ID"] = get_LUA_perk_ID( perk_ref );
|
||||
perk_info["is_activated"] = false;
|
||||
|
||||
level.perk_progression[get_activation_level( perk_ref )] = perk_info;
|
||||
}
|
||||
|
||||
CHAOS_PERK_TABLE = "mp/alien/chaos_perks.csv";
|
||||
ACTIVATION_LEVEL_COLUMN = 1;
|
||||
LUA_PERK_ID_COLUMN = 2;
|
||||
|
||||
get_LUA_perk_ID( perk_ref ) { return int( table_look_up( CHAOS_PERK_TABLE, perk_ref, LUA_PERK_ID_COLUMN ) ); }
|
||||
get_activation_level( perk_ref ) { return int( table_look_up( CHAOS_PERK_TABLE, perk_ref, ACTIVATION_LEVEL_COLUMN ) ); }
|
||||
|
||||
DROP_LOC_START_INDEX = 4000;
|
||||
DROP_LOC_END_INDEX = 4099;
|
||||
DROP_LOC_COLUMN = 1;
|
||||
|
||||
register_drop_locations()
|
||||
{
|
||||
level.chaos_bonus_loc = [];
|
||||
level.chaos_bonus_loc_used = [];
|
||||
|
||||
for( index = DROP_LOC_START_INDEX; index <= DROP_LOC_END_INDEX; index++ )
|
||||
{
|
||||
drop_loc = table_look_up( level.alien_cycle_table, index, DROP_LOC_COLUMN );
|
||||
|
||||
if( maps\mp\agents\alien\_alien_agents::is_empty_string( drop_loc ) )
|
||||
break;
|
||||
|
||||
level.chaos_bonus_loc[level.chaos_bonus_loc.size] = transform_to_coordinate( drop_loc );
|
||||
}
|
||||
}
|
||||
|
||||
PROGRESSION_START_INDEX = 5000;
|
||||
PROGRESSION_END_INDEX = 5099;
|
||||
WAIT_DURATION_COLUMN = 1;
|
||||
NUM_OF_DROPS_COLUMN = 2;
|
||||
GROUP_TYPE_COLUMN = 3;
|
||||
GROUP_CHANCE_COLUMN = 4;
|
||||
ITEM_CHANCE_COLUMN = 5;
|
||||
|
||||
register_bonus_progression()
|
||||
{
|
||||
level.chaos_bonus_progression = [];
|
||||
max_num_of_drops = 0;
|
||||
|
||||
for( index = PROGRESSION_START_INDEX; index <= PROGRESSION_END_INDEX; index++ )
|
||||
{
|
||||
wait_duration = table_look_up( level.alien_cycle_table, index, WAIT_DURATION_COLUMN );
|
||||
|
||||
if( maps\mp\agents\alien\_alien_agents::is_empty_string( wait_duration ) )
|
||||
break;
|
||||
|
||||
bonus_info = [];
|
||||
bonus_info["wait_duration"] = int( wait_duration );
|
||||
bonus_info["num_of_drops"] = int( table_look_up( level.alien_cycle_table, index, NUM_OF_DROPS_COLUMN ) );
|
||||
bonus_info["package_group_type"] = strTok( table_look_up( level.alien_cycle_table, index, GROUP_TYPE_COLUMN ), " " );
|
||||
bonus_info["package_group_chance"] = convert_array_to_int( strTok( table_look_up( level.alien_cycle_table, index, GROUP_CHANCE_COLUMN ), " " ) );
|
||||
bonus_info["item_chance"] = strTok( table_look_up( level.alien_cycle_table, index, ITEM_CHANCE_COLUMN ), " " );
|
||||
AssertEx( bonus_info["num_of_drops"] <= bonus_info["package_group_type"].size, "For wait duration: " + wait_duration + ", there is not enough bonus packages to support " + bonus_info["num_of_drops"] + " drops." );
|
||||
|
||||
if ( bonus_info["num_of_drops"] > max_num_of_drops )
|
||||
max_num_of_drops = bonus_info["num_of_drops"];
|
||||
|
||||
level.chaos_bonus_progression[level.chaos_bonus_progression.size] = bonus_info;
|
||||
}
|
||||
|
||||
level.chaos_max_used_loc_stored = level.chaos_bonus_loc.size - max_num_of_drops;
|
||||
}
|
||||
|
||||
convert_array_to_int( string_array )
|
||||
{
|
||||
int_array = [];
|
||||
|
||||
foreach( string in string_array )
|
||||
int_array[int_array.size] = int( string );
|
||||
|
||||
return int_array;
|
||||
}
|
||||
|
||||
transform_to_coordinate( text_string )
|
||||
{
|
||||
tokenized = StrTok( text_string, "," );
|
||||
return ( int( tokenized[0] ), int( tokenized[1] ), int( tokenized[2] ) );
|
||||
}
|
||||
|
||||
init_chaos_deployable( boxType, iconName, onUseCallback )
|
||||
{
|
||||
boxConfig = SpawnStruct();
|
||||
boxConfig.modelBase = "mp_weapon_alien_crate";
|
||||
boxConfig.hintString = &"ALIEN_CHAOS_BONUS_PICKUP";
|
||||
boxConfig.capturingString = &"ALIEN_CHAOS_BONUS_TAKING";
|
||||
boxConfig.headIconOffset = 25;
|
||||
boxConfig.lifeSpan = 90.0;
|
||||
boxConfig.useXP = 0;
|
||||
boxConfig.voDestroyed = "ballistic_vest_destroyed";
|
||||
boxConfig.onUseSfx = "ammo_crate_use";
|
||||
boxConfig.onUseCallback = onUseCallback;
|
||||
boxConfig.canUseCallback = maps\mp\alien\_deployablebox::default_canUseDeployable;
|
||||
boxConfig.useTime = 500;
|
||||
boxConfig.maxHealth = 150;
|
||||
boxConfig.damageFeedback = "deployable_bag";
|
||||
boxConfig.maxUses = 1;
|
||||
boxConfig.icon_name = iconName;
|
||||
|
||||
add_to_chaos_bonus_package_type( boxType );
|
||||
maps\mp\alien\_deployablebox::init_deployable( boxType, boxConfig );
|
||||
}
|
||||
|
||||
get_random_player() { return level.players[ randomint( level.players.size ) ]; }
|
||||
table_look_up( table, index, target_column ) { return tableLookup( table, TABLE_INDEX_COLUMN, index, target_column ); }
|
||||
|
||||
get_drop_location_rated( desired_dir, base_pos )
|
||||
{
|
||||
MIN_DIST_SQD_FROM_ALL_PLAYER = 22500; // 150 * 150
|
||||
MAX_DIST_SQD_FROM_ALL_PLAYER = 90000; // 300 * 300
|
||||
|
||||
DIST_FROM_ALL_PLAYER_WEIGHT = 1.0;
|
||||
DESIRED_DIR_WEIGHT = 1.0;
|
||||
RANDOM_WEIGHT = 2.0;
|
||||
|
||||
best_location_rating = -1000.0;
|
||||
best_location = ( 0, 0, 0 );
|
||||
|
||||
foreach( location in level.chaos_bonus_loc )
|
||||
{
|
||||
if ( location_recently_used( location ) )
|
||||
continue;
|
||||
|
||||
rating = 0.0;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
player_to_location_distanceSquared = DistanceSquared( player.origin, location );
|
||||
|
||||
if ( player_to_location_distanceSquared > MIN_DIST_SQD_FROM_ALL_PLAYER )
|
||||
rating += DIST_FROM_ALL_PLAYER_WEIGHT;
|
||||
|
||||
if ( player_to_location_distanceSquared < MAX_DIST_SQD_FROM_ALL_PLAYER )
|
||||
rating += DIST_FROM_ALL_PLAYER_WEIGHT;
|
||||
}
|
||||
|
||||
base_pos_to_location = vectorNormalize( ( 0, vectorToYaw( location - base_pos ), 0 ) );
|
||||
rating += VectorDot( base_pos_to_location, desired_dir ) * DESIRED_DIR_WEIGHT;
|
||||
|
||||
rating += randomFloat( RANDOM_WEIGHT );
|
||||
|
||||
if ( rating > best_location_rating )
|
||||
{
|
||||
best_location_rating = rating;
|
||||
best_location = location;
|
||||
}
|
||||
}
|
||||
|
||||
register_location( best_location );
|
||||
return best_location;
|
||||
}
|
||||
|
||||
register_location( location )
|
||||
{
|
||||
if( level.chaos_bonus_loc_used.size == level.chaos_max_used_loc_stored )
|
||||
{
|
||||
for( i = 0; i < level.chaos_max_used_loc_stored - 1; i++ )
|
||||
level.chaos_bonus_loc_used[i] = level.chaos_bonus_loc_used[i+1];
|
||||
|
||||
level.chaos_bonus_loc_used[level.chaos_max_used_loc_stored - 1] = location;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.chaos_bonus_loc_used[level.chaos_bonus_loc_used.size] = location;
|
||||
}
|
||||
}
|
||||
|
||||
location_recently_used( location )
|
||||
{
|
||||
return common_scripts\utility::array_contains( level.chaos_bonus_loc_used, location );
|
||||
}
|
||||
|
||||
reset_alien_kill_streak() { level.current_alien_kill_streak = 0; }
|
||||
inc_alien_kill_streak() { level.current_alien_kill_streak++; }
|
||||
get_alien_kill_streak() { return level.current_alien_kill_streak; }
|
||||
|
||||
play_FX_on_package( package_loc, owner_angles )
|
||||
{
|
||||
CONST_XY_OFFSET = ( -0.5, 5.6, 0 ); // To correct the fact that tag_origin is not placed at the center for the model "mp_weapon_alien_crate"
|
||||
CONST_Z_OFFSET = ( 0, 0, 5 ); // To raise the FX up a little above the ground
|
||||
|
||||
XY_offset = RotateVector( CONST_XY_OFFSET, owner_angles );
|
||||
FX_loc = package_loc + XY_offset;
|
||||
FX_loc += CONST_Z_OFFSET;
|
||||
fx= SpawnFX( common_scripts\utility::getfx( "chaos_pre_bonus_drop" ), FX_loc );
|
||||
TriggerFx( fx );
|
||||
|
||||
return fx;
|
||||
}
|
||||
|
||||
clean_up_monitor( fx, box )
|
||||
{
|
||||
box waittill( "death" );
|
||||
fx delete();
|
||||
}
|
||||
|
||||
init_highest_combo() { level.highest_combo = 0; }
|
||||
|
||||
record_highest_combo( combo_counter )
|
||||
{
|
||||
if ( combo_counter <= level.highest_combo )
|
||||
return;
|
||||
|
||||
level.highest_combo = combo_counter ;
|
||||
foreach ( player in level.players )
|
||||
player maps\mp\alien\_persistence::LB_player_update_stat( "hits", combo_counter, true ); // In Chaos, we are using session data "hits" to record the highest combo
|
||||
}
|
||||
|
||||
CYCLE_PARAMETER_START_INDEX = 500;
|
||||
CYCLE_PARAMETER_END_INDEX = 599;
|
||||
CYCLE_NUMBER_COLUMN = 1;
|
||||
CYCLE_DURATION_COLUMN = 6;
|
||||
|
||||
register_cycle_duration()
|
||||
{
|
||||
level.chaos_cycle_duration = [];
|
||||
|
||||
for( index = CYCLE_PARAMETER_START_INDEX; index <= CYCLE_PARAMETER_END_INDEX; index++ )
|
||||
{
|
||||
cycle_number = table_look_up( level.alien_cycle_table, index, CYCLE_NUMBER_COLUMN );
|
||||
|
||||
if( maps\mp\agents\alien\_alien_agents::is_empty_string( cycle_number ) )
|
||||
break;
|
||||
|
||||
level.chaos_cycle_duration[level.chaos_cycle_duration.size] = int( table_look_up( level.alien_cycle_table, index, CYCLE_DURATION_COLUMN ) );
|
||||
}
|
||||
}
|
||||
|
||||
SPAWN_LOC_START_INDEX = 6000;
|
||||
SPAWN_LOC_END_INDEX = 6099;
|
||||
SPAWN_LOC_ORIGIN_COLUMN = 1;
|
||||
SPAWN_LOC_ANGLES_COLUMN = 2;
|
||||
SPAWN_LOC_LINKTO_COLUMN = 3;
|
||||
SPAWN_LOC_NOTEWORTHY_COLUMN = 4;
|
||||
|
||||
add_extra_spawn_locations()
|
||||
{
|
||||
extra_spawn_locations = [];
|
||||
|
||||
for( index = SPAWN_LOC_START_INDEX; index <= SPAWN_LOC_END_INDEX; index++ )
|
||||
{
|
||||
spawn_loc_origin = table_look_up( level.alien_cycle_table, index, SPAWN_LOC_ORIGIN_COLUMN );
|
||||
|
||||
if( maps\mp\agents\alien\_alien_agents::is_empty_string( spawn_loc_origin ) )
|
||||
break;
|
||||
|
||||
spawn_location = spawnStruct();
|
||||
spawn_location.origin = transform_to_coordinate( spawn_loc_origin );
|
||||
spawn_location.angles = transform_to_coordinate( table_look_up( level.alien_cycle_table, index, SPAWN_LOC_ANGLES_COLUMN ) );
|
||||
spawn_location.script_linkto = translate_to_actual_zone_name( table_look_up( level.alien_cycle_table, index, SPAWN_LOC_LINKTO_COLUMN ) );
|
||||
spawn_location.script_noteworthy = table_look_up( level.alien_cycle_table, index, SPAWN_LOC_NOTEWORTHY_COLUMN );
|
||||
|
||||
extra_spawn_locations[extra_spawn_locations.size] = spawn_location;
|
||||
}
|
||||
|
||||
maps\mp\alien\_spawn_director::put_spawnLocations_into_cycle_data( extra_spawn_locations, level.cycle_data );
|
||||
}
|
||||
|
||||
DEFAULT_INIT_COMBO_DURATION = 4.0; // Time (in sec) after a combo action / event that the player has to do another action to keep the combo going
|
||||
|
||||
init_combo_duration()
|
||||
{
|
||||
if ( !isDefined( level.combo_duration ) )
|
||||
level.combo_duration = DEFAULT_INIT_COMBO_DURATION;
|
||||
}
|
||||
|
||||
get_combo_duration() { return level.combo_duration; }
|
||||
adjust_combo_duration( delta ) { level.combo_duration += delta; }
|
||||
|
||||
COMBO_DURATION_START_INDEX = 7000;
|
||||
COMBO_DURATION_END_INDEX = 7099;
|
||||
COMBO_DURATION_PRE_DELTA_INTERVAL_COLUMN = 1;
|
||||
COMBO_DURATION_DELTA_COLUMN = 2;
|
||||
|
||||
register_combo_duration_schedule()
|
||||
{
|
||||
level.combo_duration_schedule = [];
|
||||
|
||||
for( index = COMBO_DURATION_START_INDEX; index <= COMBO_DURATION_END_INDEX; index++ )
|
||||
{
|
||||
pre_delta_interval = table_look_up( level.alien_cycle_table, index, COMBO_DURATION_PRE_DELTA_INTERVAL_COLUMN );
|
||||
|
||||
if( maps\mp\agents\alien\_alien_agents::is_empty_string( pre_delta_interval ) )
|
||||
break;
|
||||
|
||||
duration_delta = [];
|
||||
duration_delta["pre_delta_interval"] = float( pre_delta_interval );
|
||||
duration_delta["delta"] = float( table_look_up( level.alien_cycle_table, index, COMBO_DURATION_DELTA_COLUMN ) );
|
||||
|
||||
level.combo_duration_schedule[level.combo_duration_schedule.size] = duration_delta;
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_BONUS_PACKAGE_CAP = 3;
|
||||
|
||||
init_bonus_package_cap()
|
||||
{
|
||||
if ( !isDefined( level.chaos_bonus_package_cap ) )
|
||||
level.chaos_bonus_package_cap = DEFAULT_BONUS_PACKAGE_CAP;
|
||||
}
|
||||
|
||||
get_bonus_package_cap() { return level.chaos_bonus_package_cap; }
|
||||
init_chaos_bonus_package_type() { level.chaos_bonus_package_type = []; }
|
||||
add_to_chaos_bonus_package_type( package_type ) { level.chaos_bonus_package_type[level.chaos_bonus_package_type.size] = package_type; }
|
||||
|
||||
get_current_num_bonus_package()
|
||||
{
|
||||
result = 0;
|
||||
|
||||
foreach ( package_type in level.chaos_bonus_package_type )
|
||||
result += level.deployable_box[package_type].size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
chaos_end_game()
|
||||
{
|
||||
if ( chaos_should_end() )
|
||||
level thread maps\mp\gametypes\aliens::AlienEndGame( "axis", maps\mp\alien\_hud::get_end_game_string_index( "kia" ) );
|
||||
}
|
||||
|
||||
CONST_IN_HOST_MIGRATION_FLAG = "in_host_migration";
|
||||
|
||||
chaos_should_end()
|
||||
{
|
||||
/#
|
||||
if ( getDvarInt( "chaos_no_fail", 0 ) == 1 )
|
||||
return false;
|
||||
#/
|
||||
if ( common_scripts\utility::flag( CONST_IN_HOST_MIGRATION_FLAG ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/#
|
||||
reset_chaos_no_fail() { setDvar( "chaos_no_fail", 0 ); }
|
||||
#/
|
||||
|
||||
should_process_alien_killed_event( attacker )
|
||||
{
|
||||
return ( isPlayer( attacker ) || ( isDefined( attacker.owner ) && isPlayer( attacker.owner ) ) || ( isDefined( attacker.team ) && attacker.team == "allies" ) );
|
||||
}
|
||||
|
||||
should_process_alien_damaged_event( sWeapon )
|
||||
{
|
||||
if ( isDefined( sWeapon ) && sWeapon == "alien_minion_explosion" )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unset_player_perks( player )
|
||||
{
|
||||
foreach( perk_info in level.perk_progression )
|
||||
{
|
||||
if ( perk_info["is_activated"] )
|
||||
[[perk_info["deactivate_func"]]]( player, perk_info["perk_ref"] );
|
||||
}
|
||||
player PlayLocalSound( "mp_splash_screen_default" );
|
||||
}
|
||||
|
||||
give_activated_perks( player )
|
||||
{
|
||||
foreach( perk_info in level.perk_progression )
|
||||
{
|
||||
if ( perk_info["is_activated"] )
|
||||
[[perk_info["activate_func"]]]( player, perk_info["perk_ref"] );
|
||||
}
|
||||
}
|
||||
|
||||
set_all_perks_inactivated()
|
||||
{
|
||||
foreach( perk_info in level.perk_progression )
|
||||
perk_info["is_activated"] = false;
|
||||
}
|
||||
|
||||
get_attacker_as_player( attacker )
|
||||
{
|
||||
if ( isPlayer( attacker ) )
|
||||
return attacker;
|
||||
|
||||
if ( isDefined( attacker.owner ) && isPlayer( attacker.owner ) )
|
||||
return attacker.owner;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
MAX_EVENT_COUNT = 18; // How many choas events are listed in the chaos_events.csv
|
||||
|
||||
init_event_counts()
|
||||
{
|
||||
level.chaos_event_counts = [];
|
||||
|
||||
for( i = 1; i <= MAX_EVENT_COUNT; i++ )
|
||||
level.chaos_event_counts[i] = 0;
|
||||
}
|
||||
|
||||
update_HUD_event_counts()
|
||||
{
|
||||
for( i = 1; i <= MAX_EVENT_COUNT; i++ )
|
||||
maps\mp\alien\_hud::set_event_count( i, level.chaos_event_counts[i] );
|
||||
}
|
||||
|
||||
inc_event_count( event_ID ) { level.chaos_event_counts[event_ID]++; }
|
||||
register_pre_end_game_display_func() { level.pre_end_game_display_func = ::update_HUD_event_counts; }
|
||||
|
||||
translate_to_actual_zone_name( zone_name_list )
|
||||
{
|
||||
actual_zone_name_list = [];
|
||||
|
||||
zone_name_list = StrTok( zone_name_list, " " );
|
||||
foreach ( zone_name in zone_name_list )
|
||||
{
|
||||
foreach ( actual_zone_name, spawn_data in level.cycle_data.spawn_zones )
|
||||
{
|
||||
if ( IsSubStr( actual_zone_name, zone_name ) )
|
||||
actual_zone_name_list[actual_zone_name_list.size] = actual_zone_name;
|
||||
}
|
||||
}
|
||||
|
||||
/# AssertEx( actual_zone_name_list.size == zone_name_list.size, "Unable to find the actual zone name for some zones." ); #/
|
||||
|
||||
if ( actual_zone_name_list.size == 0 )
|
||||
{
|
||||
result_actual_name_string = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
result_actual_name_string = actual_zone_name_list[0];
|
||||
|
||||
for( i = 1; i < actual_zone_name_list.size; i++ )
|
||||
result_actual_name_string = result_actual_name_string + " " + actual_zone_name_list[i];
|
||||
}
|
||||
|
||||
return result_actual_name_string;
|
||||
}
|
||||
1794
maps/mp/alien/_collectibles.gsc
Normal file
1794
maps/mp/alien/_collectibles.gsc
Normal file
File diff suppressed because it is too large
Load Diff
2739
maps/mp/alien/_combat_resources.gsc
Normal file
2739
maps/mp/alien/_combat_resources.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1284
maps/mp/alien/_damage.gsc
Normal file
1284
maps/mp/alien/_damage.gsc
Normal file
File diff suppressed because it is too large
Load Diff
716
maps/mp/alien/_death.gsc
Normal file
716
maps/mp/alien/_death.gsc
Normal file
@@ -0,0 +1,716 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
//=======================================================
|
||||
// onPlayerKilled
|
||||
//=======================================================
|
||||
onPlayerKilled(eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, killId)
|
||||
{
|
||||
if ( level.gameEnded == true )
|
||||
return;
|
||||
|
||||
if ( kill_trigger_event_was_processed() )
|
||||
return;
|
||||
|
||||
set_kill_trigger_event_processed( self, true );
|
||||
|
||||
maps\mp\alien\_laststand::Callback_PlayerLastStandAlien( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, getKillTriggerSpawnLoc() );
|
||||
}
|
||||
|
||||
kill_trigger_event_was_processed() { return is_true( self.kill_trigger_event_processed ); }
|
||||
set_kill_trigger_event_processed( player, value ) { self.kill_trigger_event_processed = value; }
|
||||
|
||||
//=======================================================
|
||||
// onNormalDeath
|
||||
//=======================================================
|
||||
onNormalDeath( victim, attacker, lifeId )
|
||||
{
|
||||
if ( game["state"] == "postgame" && game["teamScores"][attacker.team] > game["teamScores"][level.otherTeam[attacker.team]] )
|
||||
attacker.finalKill = true;
|
||||
}
|
||||
|
||||
|
||||
CONST_DANGEROUS_RADIUS = 256;
|
||||
CONST_DANGEROUS_DURATION = 10;
|
||||
CONST_DANGEROUS_DURATION_TRAP = 3;
|
||||
CONST_KILLS_PER_TOKEN_AWARD = 300;
|
||||
|
||||
//=======================================================
|
||||
// onAlienAgentKilled
|
||||
//=======================================================
|
||||
onAlienAgentKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
// scene aliens suicide skips all regular alien dying logic
|
||||
if ( isdefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_SUICIDE" && isdefined( self.scene ) && self.scene )
|
||||
return;
|
||||
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
self.owner = undefined;
|
||||
type = self.alien_type;
|
||||
pet_spawned = false;
|
||||
|
||||
if ( !isDefined ( vDir ) )
|
||||
vDir = anglesToForward( self.angles );
|
||||
|
||||
self maps\mp\alien\_alien_fx::disable_fx_on_death();
|
||||
|
||||
if ( sMeansOfDeath == "MOD_TRIGGER_HURT" )
|
||||
return; // died by hurt trigger
|
||||
|
||||
// Mark nearby nodes as dangerous - less for traps
|
||||
dangerous_duration = CONST_DANGEROUS_DURATION;
|
||||
if ( maps\mp\alien\_utility::is_trap( eInflictor ) )
|
||||
{
|
||||
dangerous_duration = CONST_DANGEROUS_DURATION_TRAP;
|
||||
}
|
||||
level thread maps\mp\alien\_utility::mark_dangerous_nodes( self.origin, CONST_DANGEROUS_RADIUS, dangerous_duration );
|
||||
|
||||
isPetTrapKill = is_pettrap_kill( eInflictor );
|
||||
|
||||
//if killed with a special weapon then turn the alien into a pet
|
||||
if ( sWeapon == "alienthrowingknife_mp" && sMeansofDeath == "MOD_IMPACT" || isPetTrapKill || is_true( self.hypnoknifed ) )
|
||||
{
|
||||
if ( self maps\mp\alien\_utility::can_hypno( eAttacker, isPetTrapKill ) )
|
||||
{
|
||||
thread maps\mp\gametypes\aliens::spawnAllyPet( type, 1, self.origin, eAttacker, self.angles , isPetTrapKill );
|
||||
pet_spawned = true;
|
||||
if ( type == "elite" && isPetTrapKill && isDefined( level.update_achievement_hypno_trap_func ) )
|
||||
eAttacker [[level.update_achievement_hypno_trap_func]]();
|
||||
}
|
||||
//don't delete the pet trap!
|
||||
if ( !isPetTrapKill )
|
||||
eInflictor delete();
|
||||
}
|
||||
|
||||
should_do_custom_death = false;
|
||||
if ( isDefined( level.custom_alien_death_func ) )
|
||||
should_do_custom_death = self [[level.custom_alien_death_func]]( eInflictor, eAttacker,iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc );
|
||||
|
||||
if ( should_do_pipebomb_death( sWeapon ) )
|
||||
{
|
||||
self thread do_pipebomb_death();
|
||||
}
|
||||
else if ( self should_play_death() && sMeansOfDeath != "MOD_SUICIDE" && !pet_spawned && !should_do_custom_death )
|
||||
play_death_anim_and_ragdoll( eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc );
|
||||
|
||||
self on_alien_type_killed( pet_spawned );
|
||||
|
||||
self maps\mp\agents\alien\_alien_think::OnEnterAnimState( self.currentAnimState, "death" ); // we don't need to enter death so much as exit currentAnimState.
|
||||
|
||||
//notify for dlc vo
|
||||
eAttacker notify("dlc_vo_notify", get_alien_type() + "_killed", eAttacker);
|
||||
|
||||
switch ( get_alien_type() )
|
||||
{
|
||||
case "mammoth":
|
||||
self PlaySoundOnMovingEnt( "queen_death" );
|
||||
break;
|
||||
case "elite":
|
||||
self PlaySoundOnMovingEnt( "queen_death" );
|
||||
break;
|
||||
case "minion":
|
||||
self PlaySoundOnMovingEnt( "alien_minion_explode" );
|
||||
break;
|
||||
case "spitter":
|
||||
self PlaySoundOnMovingEnt( "spitter_death" );
|
||||
break;
|
||||
default:
|
||||
self PlaySoundOnMovingEnt( "alien_death" );
|
||||
break;
|
||||
}
|
||||
|
||||
// chopper reward
|
||||
if ( isdefined( level.attack_heli ) && eAttacker == level.attack_heli )
|
||||
{
|
||||
reward_point = self maps\mp\alien\_gamescore::get_reward_point_for_kill();
|
||||
|
||||
assertex( isdefined( eAttacker.reward_pool ) );
|
||||
reward_unit = reward_point / eAttacker.reward_pool.size;
|
||||
|
||||
// reset
|
||||
foreach ( player in eAttacker.reward_pool )
|
||||
{
|
||||
if ( isdefined( player ) )
|
||||
player.chopper_reward = 0;
|
||||
}
|
||||
|
||||
// add
|
||||
foreach ( player in eAttacker.reward_pool )
|
||||
{
|
||||
if ( isdefined( player ) )
|
||||
player.chopper_reward += reward_unit;
|
||||
}
|
||||
|
||||
// give
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( isdefined( player ) && isdefined( player.chopper_reward ) )
|
||||
maps\mp\alien\_gamescore::giveKillReward( player, int( player.chopper_reward ), "large" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( Isdefined( eAttacker.pet ) && ( eAttacker.pet == 1 ) )
|
||||
{
|
||||
maps\mp\alien\_gamescore::give_attacker_kill_rewards( eAttacker.owner );
|
||||
}
|
||||
else
|
||||
{
|
||||
maps\mp\alien\_gamescore::give_attacker_kill_rewards( eAttacker, sHitLoc );
|
||||
}
|
||||
|
||||
// weaponstats tracking: register weapon shot hit
|
||||
eAttacker thread maps\mp\alien\_persistence::update_weaponstats_kills( sWeapon, 1 );
|
||||
}
|
||||
|
||||
//update any challenges related to aliens being killed
|
||||
maps\mp\alien\_challenge_function::update_alien_death_challenges( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
//update any achievements related to aliens being killed
|
||||
maps\mp\alien\_achievement::update_alien_kill_achievements( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
//update alien session stats related to aliens being killed
|
||||
maps\mp\alien\_persistence::update_alien_kill_sessionStats( eInflictor, eAttacker );
|
||||
|
||||
if ( is_chaos_mode() )
|
||||
maps\mp\alien\_chaos::update_alien_killed_event( get_alien_type(), self.origin, eAttacker );
|
||||
|
||||
blackBox_alienKilled( eAttacker );
|
||||
|
||||
attacker_as_player = get_attacker_as_player( eAttacker );
|
||||
|
||||
if( IsDefined( attacker_as_player ) )
|
||||
{
|
||||
record_player_kills( attacker_as_player );
|
||||
check_award_token_for_kill( attacker_as_player );
|
||||
}
|
||||
|
||||
level notify( "alien_killed",self.origin, sMeansOfDeath, eAttacker );
|
||||
|
||||
}
|
||||
|
||||
get_attacker_as_player( eAttacker )
|
||||
{
|
||||
if( IsPlayer( eAttacker ) )
|
||||
return eAttacker;
|
||||
|
||||
if ( IsDefined( eAttacker.owner ) && IsPlayer( eAttacker.owner ) )
|
||||
return eAttacker.owner;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
record_player_kills( player )
|
||||
{
|
||||
player maps\mp\alien\_persistence::set_player_kills();
|
||||
|
||||
player maps\mp\alien\_persistence::eog_player_update_stat( "kills", 1 );
|
||||
}
|
||||
|
||||
check_award_token_for_kill( player )
|
||||
{
|
||||
killCount = player maps\mp\alien\_persistence::get_player_kills();
|
||||
if ( killCount % CONST_KILLS_PER_TOKEN_AWARD == 0 )
|
||||
{
|
||||
player maps\mp\alien\_persistence::give_player_tokens( 1, true );
|
||||
}
|
||||
}
|
||||
|
||||
on_alien_type_killed( pet_spawned )
|
||||
{
|
||||
switch ( self get_alien_type() )
|
||||
{
|
||||
case "minion":
|
||||
level thread maps\mp\agents\alien\_alien_minion::minion_explode_on_death( self.origin );
|
||||
break;
|
||||
|
||||
case "spitter":
|
||||
maps\mp\agents\alien\_alien_spitter::spitter_death();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Check for level specific overrides
|
||||
if( isDefined( level.dlc_alien_death_override_func ))
|
||||
self [[level.dlc_alien_death_override_func]]( pet_spawned );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
should_play_death()
|
||||
{
|
||||
switch( get_alien_type() )
|
||||
{
|
||||
case "minion":
|
||||
case "seeder":
|
||||
case "bomber":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
play_death_anim_and_ragdoll( eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc )
|
||||
{
|
||||
ORIENTED_DEATH_OFFSET = 24;
|
||||
APEX_TRAVERSAL_DEATH_OFFSET = 30;
|
||||
|
||||
if ( GetDvarInt( "alien_easter_egg" ) > 0 || ( isdefined( level.easter_egg_lodge_sign_active ) && level.easter_egg_lodge_sign_active ) )
|
||||
{
|
||||
PlayFX( level._effect[ "arcade_death" ], self.origin );
|
||||
//self thread alien_toy_death();
|
||||
}
|
||||
else
|
||||
{
|
||||
primary_animState = get_primary_death_anim_state();
|
||||
|
||||
if ( !is_normal_upright( AnglesToUp( self.angles ) ) )
|
||||
move_away_from_surface( AnglesToUp( self.angles ), ORIENTED_DEATH_OFFSET );
|
||||
|
||||
if ( isDefined( self.apexTraversalDeathVector ) )
|
||||
move_away_from_surface( self.apexTraversalDeathVector, APEX_TRAVERSAL_DEATH_OFFSET );
|
||||
|
||||
play_death_anim_and_ragdoll_internal( primary_animState, eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc );
|
||||
}
|
||||
}
|
||||
|
||||
should_do_immediate_ragdoll( deathAnimState )
|
||||
{
|
||||
if ( IsDefined( level.dlc_alien_should_immediate_ragdoll_on_death_override_func ) )
|
||||
{
|
||||
should_immediate_ragdoll = [[level.dlc_alien_should_immediate_ragdoll_on_death_override_func]]( deathAnimState );
|
||||
if ( IsDefined( should_immediate_ragdoll ) )
|
||||
return should_immediate_ragdoll;
|
||||
}
|
||||
|
||||
switch ( deathAnimState )
|
||||
{
|
||||
case "jump":
|
||||
case "traverse":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
play_death_anim_and_ragdoll_internal( primary_animState, eInflictor, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc )
|
||||
{
|
||||
if ( is_special_death( primary_animState ) )
|
||||
{
|
||||
animState = "special_death";
|
||||
animIndex = self maps\mp\agents\alien\_alien_anim_utils::getSpecialDeathAnimIndex( primary_animState );
|
||||
}
|
||||
else
|
||||
{
|
||||
animState = self maps\mp\agents\alien\_alien_anim_utils::getDeathAnimState( ( primary_animState + "_death" ), iDamage );
|
||||
animIndex = self maps\mp\agents\alien\_alien_anim_utils::getDeathAnimIndex( primary_animState , vDir, sHitLoc );
|
||||
}
|
||||
|
||||
do_immediate_ragdoll = should_do_immediate_ragdoll( primary_animState );
|
||||
|
||||
self ScrAgentSetPhysicsMode( get_death_anim_physics_mode( animState ) );
|
||||
self SetAnimState( animState, animIndex );
|
||||
|
||||
self.body = get_clone_agent( animState, animIndex );
|
||||
|
||||
self thread handle_ragdoll( self.body, animState, do_immediate_ragdoll );
|
||||
}
|
||||
|
||||
move_away_from_surface( direction, offset_length )
|
||||
{
|
||||
offsetLocation = self.origin + direction * offset_length;
|
||||
self SetOrigin( offsetLocation );
|
||||
}
|
||||
|
||||
get_primary_death_anim_state()
|
||||
{
|
||||
// special death case
|
||||
if ( isdefined( self.shocked ) && self.shocked ) //for electric fence, shock backwards anim
|
||||
return "electric_shock_death";
|
||||
|
||||
switch ( self.currentAnimState )
|
||||
{
|
||||
case "scripted":
|
||||
{
|
||||
return "idle";
|
||||
}
|
||||
case "move":
|
||||
{
|
||||
if ( self.trajectoryActive )
|
||||
return "jump";
|
||||
else
|
||||
return "run";
|
||||
}
|
||||
case "idle":
|
||||
{
|
||||
return "idle";
|
||||
}
|
||||
case "melee":
|
||||
{
|
||||
if ( self.trajectoryActive )
|
||||
return "jump";
|
||||
if ( self.melee_in_move_back || self.melee_in_posture )
|
||||
return "idle";
|
||||
else
|
||||
return "run";
|
||||
}
|
||||
case "traverse":
|
||||
{
|
||||
if ( self.trajectoryActive )
|
||||
return "jump";
|
||||
else
|
||||
return "traverse";
|
||||
}
|
||||
default:
|
||||
{
|
||||
AssertMsg( "currentAnimState: " + self.currentAnimState + " does not have a death anim mapping." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_special_death( primary_animState )
|
||||
{
|
||||
switch ( primary_animState )
|
||||
{
|
||||
case "traverse":
|
||||
case "electric_shock_death":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
get_death_anim_physics_mode( anim_state )
|
||||
{
|
||||
switch ( anim_state )
|
||||
{
|
||||
case "electric_shock": // so alien doesn't get stuck on electric fence geo
|
||||
return "noclip";
|
||||
default:
|
||||
return "gravity";
|
||||
}
|
||||
}
|
||||
|
||||
get_clone_agent( animState, animIndex )
|
||||
{
|
||||
animEntry = self GetAnimEntry( animState, animIndex );
|
||||
animLength = GetAnimLength( animEntry );
|
||||
if ( AnimHasNotetrack( animEntry, "start_ragdoll" ) )
|
||||
{
|
||||
notetracks = GetNotetrackTimes( animEntry, "start_ragdoll" );
|
||||
assert( notetracks.size > 0 );
|
||||
animLength *= notetracks[0];
|
||||
}
|
||||
|
||||
deathAnimDuration = int( animLength * 1000 ); // duration in milliseconds
|
||||
|
||||
return ( self CloneAgent( deathAnimDuration ) );
|
||||
}
|
||||
|
||||
handle_ragdoll( corpse, animState, do_immediate_ragdoll )
|
||||
{
|
||||
deathAnim = corpse getcorpseanim();
|
||||
|
||||
if ( !should_do_ragdoll( corpse, deathAnim ) )
|
||||
return;
|
||||
|
||||
if ( do_immediate_ragdoll )
|
||||
{
|
||||
corpse startragdoll();
|
||||
|
||||
if ( corpse isRagdoll() ) //Immediate ragdoll succeed
|
||||
return;
|
||||
|
||||
/#
|
||||
println( "Corpse failed immediate ragdoll at " + corpse.origin );
|
||||
#/
|
||||
}
|
||||
|
||||
delayStartRagdoll( corpse, deathAnim );
|
||||
|
||||
if ( !isDefined( corpse ) )
|
||||
return;
|
||||
|
||||
// electric fence shock does physics to send aliens flying
|
||||
// TODO: Remove once death animation for shock_death does this
|
||||
if ( animState == "shock_death" )
|
||||
{
|
||||
self notify( "in_ragdoll", corpse.origin );
|
||||
}
|
||||
}
|
||||
|
||||
delayStartRagdoll( corpse, deathAnim )
|
||||
{
|
||||
totalAnimTime = getanimlength( deathAnim );
|
||||
if ( animhasnotetrack( deathAnim, "start_ragdoll" ) )
|
||||
{
|
||||
times = getnotetracktimes( deathAnim, "start_ragdoll" );
|
||||
startFrac = times[ 0 ];
|
||||
waitTime = startFrac * totalAnimTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
waitTime = 0.2;
|
||||
}
|
||||
|
||||
wait( waitTime );
|
||||
|
||||
if ( !isDefined( corpse ) ) // Corpse can be deleted during host migration
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
corpse startragdoll();
|
||||
|
||||
if ( corpse isRagdoll() )
|
||||
return;
|
||||
}
|
||||
|
||||
/#
|
||||
println( "Corpse failed first ragdoll at " + corpse.origin );
|
||||
#/
|
||||
|
||||
// Ragdoll failed, do a final attempt
|
||||
if ( waitTime < totalAnimTime )
|
||||
{
|
||||
wait ( totalAnimTime - waitTime );
|
||||
|
||||
if ( !isDefined( corpse ) ) // Corpse can be deleted during host migration
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
corpse startragdoll();
|
||||
|
||||
if ( corpse isRagdoll() )
|
||||
return;
|
||||
}
|
||||
/#
|
||||
println( "Corpse failed second ragdoll at " + corpse.origin );
|
||||
#/
|
||||
}
|
||||
|
||||
// If final attempt failed, delete the corpse
|
||||
if ( isDefined( corpse ) )
|
||||
corpse delete();
|
||||
}
|
||||
|
||||
should_do_ragdoll( ent, deathAnim )
|
||||
{
|
||||
if ( ent isRagDoll() )
|
||||
return false;
|
||||
|
||||
if ( animhasnotetrack( deathAnim, "ignore_ragdoll" ) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( level.noRagdollEnts ) && level.noRagdollEnts.size )
|
||||
{
|
||||
foreach( noRag in level.noRagdollEnts )
|
||||
{
|
||||
if ( distanceSquared( ent.origin, noRag.origin ) < 65536 ) //256^2
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
blackBox_alienKilled( eAttacker )
|
||||
{
|
||||
// black box data tracking
|
||||
if ( isPlayer( eAttacker )
|
||||
|| ( isDefined( eAttacker.pet ) && ( eAttacker.pet == 1 ) && isPlayer( eAttacker.petowner ) )
|
||||
|| ( IsDefined( eAttacker.owner ) && IsPlayer( eAttacker.owner ) )
|
||||
)
|
||||
{
|
||||
level.alienBBData[ "aliens_killed" ]++;
|
||||
}
|
||||
|
||||
self notify( "alien_killed" );
|
||||
|
||||
// black box data tracking
|
||||
// =========================== blackbox print [START] ===========================
|
||||
// self is agent victim that died
|
||||
|
||||
// attacker_is_agent
|
||||
attacker_is_agent = IsAgent( eAttacker );
|
||||
|
||||
// attacker_alive_time, attacker_agent_type, attacker_name
|
||||
if ( attacker_is_agent )
|
||||
{
|
||||
attacker_alive_time = ( gettime() - eAttacker.birthtime ) / 1000;
|
||||
attacker_agent_type = "unknown agent";
|
||||
attacker_name = "none";
|
||||
|
||||
if ( isdefined( eAttacker.agent_type ) )
|
||||
{
|
||||
attacker_agent_type = eAttacker.agent_type;
|
||||
if ( isdefined( eAttacker.alien_type ) )
|
||||
attacker_agent_type = eAttacker.alien_type;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker_alive_time = 0;
|
||||
attacker_name = "none";
|
||||
|
||||
if ( isplayer( eAttacker ) )
|
||||
{
|
||||
attacker_agent_type = "player";
|
||||
|
||||
if ( isdefined( eAttacker.name ) )
|
||||
attacker_name = eAttacker.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker_agent_type = "nonagent";
|
||||
}
|
||||
}
|
||||
|
||||
// attacker origin
|
||||
attackerx = 0.0;
|
||||
attackery = 0.0;
|
||||
attackerz = 0.0;
|
||||
if ( isdefined( eAttacker ) && ( IsAgent( eAttacker ) || isPlayer( eAttacker ) ) )
|
||||
{
|
||||
attackerx = eAttacker.origin[ 0 ];
|
||||
attackery = eAttacker.origin[ 1 ];
|
||||
attackerz = eAttacker.origin[ 2 ];
|
||||
}
|
||||
|
||||
victim_alive_time = 0;
|
||||
if ( isdefined( self.birthtime ) )
|
||||
victim_alive_time = ( gettime() - self.birthtime ) / 1000;
|
||||
|
||||
victim_spawn_origin = ( 0, 0, 0 );
|
||||
if ( isdefined( self.spawnorigin ) )
|
||||
victim_spawn_origin = self.spawnorigin;
|
||||
|
||||
victim_dist_from_spawn = 0;
|
||||
if ( isdefined( self.spawnorigin ) )
|
||||
victim_dist_from_spawn = distance( self.origin, self.spawnorigin );
|
||||
|
||||
victim_damage_done = 0;
|
||||
if ( isdefined( self.damage_done ) )
|
||||
victim_damage_done = self.damage_done;
|
||||
|
||||
victim_agent_type = "unknown agent";
|
||||
if ( isdefined( self.agent_type ) )
|
||||
{
|
||||
victim_agent_type = self.agent_type;
|
||||
if ( isdefined( self.alien_type ) )
|
||||
victim_agent_type = self.alien_type;
|
||||
}
|
||||
|
||||
current_enemy_population = 0;
|
||||
foreach ( agent in level.agentArray )
|
||||
{
|
||||
if ( !IsDefined( agent.isActive ) || !agent.isActive )
|
||||
continue;
|
||||
|
||||
if ( isdefined( agent.team ) && agent.team == "axis" )
|
||||
current_enemy_population++;
|
||||
}
|
||||
|
||||
current_player_population = 0;
|
||||
if ( isdefined( level.players ) )
|
||||
current_player_population = level.players.size;
|
||||
|
||||
/#
|
||||
if ( GetDvarInt( "alien_bbprint_debug" ) > 0 )
|
||||
{
|
||||
IPrintLnBold( "^8bbprint: alienkilled (1/2)\n" +
|
||||
" attackerisagent=" + attacker_is_agent +
|
||||
" attackeralivetime=" + attacker_alive_time +
|
||||
" attackeragenttype=" + attacker_agent_type +
|
||||
" attackername=" + attacker_name +
|
||||
" attackerx=" + eAttacker.origin[ 0 ] +
|
||||
" attackery=" + eAttacker.origin[ 1 ] +
|
||||
" attackerz=" + eAttacker.origin[ 2 ] +
|
||||
" victimalivetime=" + victim_alive_time );
|
||||
|
||||
IPrintLnBold( "^8bbprint: alienkilled (2/2)\n" +
|
||||
" victimspawnoriginx=" + victim_spawn_origin[ 0 ] +
|
||||
" victimspawnoriginy=" + victim_spawn_origin[ 1 ] +
|
||||
" victimspawnoriginz=" + victim_spawn_origin[ 2 ] +
|
||||
" victimdistfromspawn=" + victim_dist_from_spawn +
|
||||
" victimdamagedone=" + victim_damage_done +
|
||||
" victimagenttype=" + victim_agent_type +
|
||||
" currentenemypopulation=" + current_enemy_population +
|
||||
" currentplayerpopulation=" + current_player_population );
|
||||
}
|
||||
#/
|
||||
|
||||
bbprint( "alienkilled",
|
||||
"attackerisagent %i attackeralivetime %f attackeragenttype %s attackername %s attackerx %f attackery %f attackerz %f victimalivetime %f victimspawnoriginx %f victimspawnoriginy %f victimspawnoriginz %f victimdistfromspawn %i victimdamagedone %i victimagenttype %s currentenemypopulation %i currentplayerpopulation %i ",
|
||||
attacker_is_agent,
|
||||
attacker_alive_time,
|
||||
attacker_agent_type,
|
||||
attacker_name,
|
||||
eAttacker.origin[ 0 ],
|
||||
eAttacker.origin[ 1 ],
|
||||
eAttacker.origin[ 2 ],
|
||||
victim_alive_time,
|
||||
victim_spawn_origin[ 0 ],
|
||||
victim_spawn_origin[ 1 ],
|
||||
victim_spawn_origin[ 2 ],
|
||||
victim_dist_from_spawn,
|
||||
victim_damage_done,
|
||||
victim_agent_type,
|
||||
current_enemy_population,
|
||||
current_player_population );
|
||||
|
||||
// =========================== [END] blackbox print ===========================
|
||||
}
|
||||
|
||||
KILL_TRIGGER_SPAWN_STRUCT_TARGET_NAME = "respawn_edge";
|
||||
kill_trigger_spawn_init()
|
||||
{
|
||||
level.killTriggerSpawnLocs = getstructArray( KILL_TRIGGER_SPAWN_STRUCT_TARGET_NAME, "targetname" );
|
||||
}
|
||||
|
||||
getKillTriggerSpawnLoc()
|
||||
{
|
||||
AssertEx( level.killTriggerSpawnLocs.size > 0, "Need to put script struct around kill triggers with KVP: 'targetname', '" + KILL_TRIGGER_SPAWN_STRUCT_TARGET_NAME + "'" );
|
||||
|
||||
return ( getClosest ( self.origin, level.killTriggerSpawnLocs ) );
|
||||
}
|
||||
|
||||
should_do_pipebomb_death( sWeapon )
|
||||
{
|
||||
alientype = self get_alien_type();
|
||||
|
||||
//minions already explode, and the Rhino/Elite aliens shouldn't gib
|
||||
if ( alientype == "minion" || alientype == "elite" || alientype == "mammoth" )
|
||||
return false;
|
||||
|
||||
return ( isDefined( sWeapon ) && sWeapon == "iw6_aliendlc22_mp" ); //pipe bomb
|
||||
}
|
||||
|
||||
do_pipebomb_death()
|
||||
{
|
||||
PlayFx( level._effect[ "alien_gib" ], self.origin + (0,0,32) );
|
||||
}
|
||||
|
||||
is_pettrap_kill( eInflictor )
|
||||
{
|
||||
return ( isDefined( eInflictor ) && isDefined( eInflictor.is_pet_trap ) );
|
||||
}
|
||||
|
||||
general_alien_custom_death( eInflictor, eAttacker,iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc )
|
||||
{
|
||||
is_player = IsDefined( eAttacker ) && isPlayer ( eAttacker );
|
||||
|
||||
if ( is_player && isDefined ( sWeapon ) && weapon_has_alien_attachment( sWeapon ) && sMeansOfDeath != "MOD_MELEE" && !is_true ( level.easter_egg_lodge_sign_active ) )
|
||||
{
|
||||
PlayFx( level._effect[ "alien_ark_gib" ], self.origin + (0,0,32) );
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
1168
maps/mp/alien/_debug.gsc
Normal file
1168
maps/mp/alien/_debug.gsc
Normal file
File diff suppressed because it is too large
Load Diff
989
maps/mp/alien/_deployablebox.gsc
Normal file
989
maps/mp/alien/_deployablebox.gsc
Normal file
@@ -0,0 +1,989 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
/*
|
||||
Deployable box killstreaks: the player will be able to place a box in the world and teammates can grab items from it
|
||||
this will be used on multiple killstreaks where you can place a box in the world with something in it
|
||||
*/
|
||||
|
||||
BOX_TIMEOUT_UPDATE_INTERVAL = 1.0;
|
||||
DEFAULT_USE_TIME = 3000;
|
||||
BOX_DEFAULT_HEALTH = 999999; // so that boxes aren't killed in code
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !IsDefined( level.boxSettings ) )
|
||||
{
|
||||
level.boxSettings = [];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// MARKER FUNCTIONS
|
||||
// 2012-06-21 wallace
|
||||
// Stole an updated version from _uplink.gsc. Should probably unify all these funcs eventually
|
||||
//////////////////////////////////////////////////
|
||||
beginDeployableViaMarker( lifeId, boxType )
|
||||
{
|
||||
self thread watchDeployableMarkerCancel( boxType );
|
||||
self thread watchDeployableMarkerPlacement( boxType, lifeId );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
result = self waittill_any_return( "deployable_canceled", "deployable_deployed", "death", "disconnect" );
|
||||
|
||||
return ( result == "deployable_deployed" );
|
||||
}
|
||||
}
|
||||
|
||||
tryUseDeployable( lifeId, boxType ) // self == player
|
||||
{
|
||||
self thread watchDeployableMarkerCancel( boxType );
|
||||
self thread watchDeployableMarkerPlacement( boxType, lifeId );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
result = self waittill_any_return( "deployable_canceled", "deployable_deployed", "death", "disconnect" );
|
||||
|
||||
return ( result == "deployable_deployed" );
|
||||
}
|
||||
}
|
||||
|
||||
watchDeployableMarkerCancel( boxType )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "deployable_deployed" );
|
||||
|
||||
boxConfig = level.boxSettings[ boxType ];
|
||||
currentWeapon = self getCurrentWeapon();
|
||||
|
||||
while( currentWeapon == boxConfig.weaponInfo )
|
||||
{
|
||||
self waittill( "weapon_change", currentWeapon );
|
||||
}
|
||||
|
||||
self notify( "deployable_canceled" );
|
||||
}
|
||||
|
||||
watchDeployableMarkerPlacement( boxType, lifeId )
|
||||
{
|
||||
self endon( "spawned_player" ); // you shouldn't do endon( "death" ) here because this thread needs to run
|
||||
self endon( "disconnect" );
|
||||
self endon( "deployable_canceled" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "grenade_fire", marker, weaponName );
|
||||
|
||||
if( isReallyAlive(self) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
marker Delete();
|
||||
}
|
||||
}
|
||||
|
||||
self notify( "deployable_deployed" );
|
||||
|
||||
marker.owner = self;
|
||||
marker.weaponName = weaponName;
|
||||
self.marker = marker;
|
||||
|
||||
marker PlaySoundToPlayer( level.boxSettings[ boxType ].deployedSfx, self );
|
||||
|
||||
marker thread markerActivate( lifeId, boxType, ::box_setActive );
|
||||
}
|
||||
|
||||
override_box_moving_platform_death( data )
|
||||
{
|
||||
self notify( "death" ); // we're doing this here instead of letting the mover code just delete us so that we can run our necessary clean-up functionality (like removal of the objective marker from the minimap)
|
||||
}
|
||||
|
||||
markerActivate( lifeId, boxType, usedCallback ) // self == marker
|
||||
{
|
||||
self notify( "markerActivate" );
|
||||
self endon( "markerActivate" );
|
||||
//self waittill( "explode", position );
|
||||
self waittill( "missile_stuck" );
|
||||
owner = self.owner;
|
||||
position = self.origin;
|
||||
|
||||
if ( !isDefined( owner ) )
|
||||
return;
|
||||
|
||||
box = createBoxForPlayer( boxType, position, owner );
|
||||
|
||||
// For moving platforms.
|
||||
data = SpawnStruct();
|
||||
data.linkParent = self GetLinkedParent();
|
||||
|
||||
//fixes wall hack exploit with linked items
|
||||
if ( isDefined( data.linkParent ) && isDefined( data.linkParent.model ) && DeployableExclusion( data.linkParent.model ) )
|
||||
{
|
||||
box.origin = data.linkParent.origin;
|
||||
|
||||
grandParent = data.linkParent GetLinkedParent();
|
||||
|
||||
if ( isDefined( grandParent ) )
|
||||
data.linkParent = grandParent;
|
||||
else
|
||||
data.linkParent = undefined;
|
||||
}
|
||||
|
||||
data.deathOverrideCallback = ::override_box_moving_platform_death;
|
||||
box thread maps\mp\_movers::handle_moving_platforms( data );
|
||||
|
||||
box.moving_platform = data.linkParent;
|
||||
|
||||
box SetOtherEnt(owner);
|
||||
|
||||
// ES - 2/24/14 - This waitframe is causing an issue where, when deployed on a moving platform, the "death" notification is sent instantly, but is never caught.
|
||||
wait 0.05;
|
||||
|
||||
//self playSound( "sentry_gun_beep" );
|
||||
box thread [[ usedCallback ]]();
|
||||
|
||||
self delete();
|
||||
|
||||
if( IsDefined(box) && (box touchingBadTrigger()) )
|
||||
{
|
||||
box notify( "death" );
|
||||
}
|
||||
}
|
||||
|
||||
DeployableExclusion( parentModel )
|
||||
{
|
||||
if ( parentModel == "weapon_alien_laser_drill" )
|
||||
return true;
|
||||
else if ( IsSubStr( parentModel, "crafting" ) )
|
||||
return true;
|
||||
else if ( IsSubStr( parentModel, "scorpion_body" ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isHoldingDeployableBox()
|
||||
{
|
||||
curWeap = self GetCurrentWeapon();
|
||||
if ( IsDefined( curWeap ) )
|
||||
{
|
||||
foreach( deplBoxWeap in level.boxSettings )
|
||||
{
|
||||
if ( curWeap == deplBoxWeap.weaponInfo )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
///////////////////////////////////////////////////
|
||||
// END MARKER FUNCTIONS
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// BOX HANDLER FUNCTIONS
|
||||
//////////////////////////////////////////////////
|
||||
get_box_icon( resourceType, dpadName, upgrade_rank )
|
||||
{
|
||||
return level.alien_combat_resources[ resourceType][ dpadName ].upgrades[upgrade_rank].dpad_icon;
|
||||
}
|
||||
|
||||
get_resource_type( dpadName )
|
||||
{
|
||||
if( !isDefined( dpadName ) )
|
||||
return undefined;
|
||||
|
||||
foreach ( resource_type_name, resource_type in level.alien_combat_resources )
|
||||
{
|
||||
if ( IsDefined( resource_type[ dpadName ] ) )
|
||||
{
|
||||
return resource_type_name;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
createBoxForPlayer( boxType, position, owner )
|
||||
{
|
||||
assertEx( isDefined( owner ), "createBoxForPlayer() called without owner specified" );
|
||||
|
||||
boxConfig = level.boxSettings[ boxType ];
|
||||
|
||||
box = Spawn( "script_model", position );
|
||||
box setModel( boxConfig.modelBase );
|
||||
box.health = BOX_DEFAULT_HEALTH;
|
||||
box.maxHealth = boxConfig.maxHealth;
|
||||
box.angles = owner.angles;
|
||||
box.boxType = boxType;
|
||||
box.owner = owner;
|
||||
box.team = owner.team;
|
||||
if ( IsDefined( boxConfig.dpadName ) )
|
||||
{
|
||||
box.dpadName = boxConfig.dpadName;
|
||||
}
|
||||
if ( IsDefined( boxConfig.maxUses ) )
|
||||
{
|
||||
box.usesRemaining = boxConfig.maxUses;
|
||||
}
|
||||
|
||||
player = box.owner;
|
||||
resource_type = get_resource_type( box.dpadName );
|
||||
|
||||
if ( is_combat_resource( resource_type ) )
|
||||
{
|
||||
box.upgrade_rank = player maps\mp\alien\_persistence::get_upgrade_level( resource_type );
|
||||
box.icon_name = get_box_icon( resource_type, box.dpadName, box.upgrade_rank );
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertEx( isDefined( boxConfig.icon_name ), "For non-combat-resource box, the .icon_name must be specified in the boxConfig struct" );
|
||||
|
||||
box.upgrade_rank = 0;
|
||||
box.icon_name = boxConfig.icon_name;
|
||||
}
|
||||
|
||||
// black box data tracking
|
||||
level.alienBBData[ "team_item_deployed" ]++;
|
||||
player maps\mp\alien\_persistence::eog_player_update_stat( "deployables", 1 );
|
||||
|
||||
/*
|
||||
ownername = "";
|
||||
if ( isdefined( owner.name ) )
|
||||
ownername = owner.name;
|
||||
|
||||
itemname = boxType;
|
||||
if ( isdefined( box.dpadName ) )
|
||||
itemname = box.dpadName;
|
||||
|
||||
/#
|
||||
if ( GetDvarInt( "alien_bbprint_debug" ) > 0 )
|
||||
{
|
||||
IPrintLnBold( "^8bbprint: aliendeployabledeployed \n" +
|
||||
" itemname=" + itemname +
|
||||
" itemlevel=" + box.upgrade_rank +
|
||||
" itemx,y,z=" + position +
|
||||
" ownername=" + ownername );
|
||||
}
|
||||
#/
|
||||
|
||||
bbprint( "aliendeployabledeployed",
|
||||
"itemname %s itemlevel %s itemx %f itemy %f itemz %f ownername %s ",
|
||||
itemname,
|
||||
box.upgrade_rank,
|
||||
position[0],
|
||||
position[1],
|
||||
position[2],
|
||||
ownername );
|
||||
*/
|
||||
|
||||
box box_setInactive();
|
||||
box thread box_handleOwnerDisconnect();
|
||||
box addBoxToLevelArray();
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
is_combat_resource( resource_type ) { return isDefined( resource_type ); }
|
||||
|
||||
box_setActive( skipOwnerUse ) // self == box
|
||||
{
|
||||
self setCursorHint( "HINT_NOICON" );
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
self setHintString( boxConfig.hintString );
|
||||
|
||||
self.inUse = false;
|
||||
|
||||
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
Objective_Add( curObjID, "invisible", (0,0,0) );
|
||||
Objective_Position( curObjID, self.origin );
|
||||
Objective_State( curObjID, "active" );
|
||||
|
||||
if( isDefined( boxConfig.shaderName ) )
|
||||
Objective_Icon( curObjID, boxConfig.shaderName );
|
||||
|
||||
self.objIdFriendly = curObjID;
|
||||
|
||||
// use the deployable on the owner once
|
||||
if ( ( !IsDefined( skipOwnerUse ) || !skipOwnerUse ) && IsDefined( boxConfig.onuseCallback )
|
||||
&& ( !IsDefined( boxconfig.canUseCallback ) || (self.owner [[ boxConfig.canUseCallback ]]() ) )
|
||||
)
|
||||
{
|
||||
if( isReallyAlive( self.owner ) )
|
||||
self.owner [[ boxConfig.onUseCallback ]]( self );
|
||||
}
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
Objective_Team( curObjID, self.team );
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( self.team == player.team
|
||||
&& (!IsDefined(boxConfig.canUseCallback) || player [[ boxConfig.canUseCallback ]](self) )
|
||||
)
|
||||
{
|
||||
self box_SetIcon( player, boxConfig.streakName, boxConfig.headIconOffset );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Objective_Player( curObjID, self.owner GetEntityNumber() );
|
||||
|
||||
if( !IsDefined(boxConfig.canUseCallback) || self.owner [[ boxConfig.canUseCallback ]](self) )
|
||||
{
|
||||
self box_SetIcon( self.owner, boxConfig.streakName, boxConfig.headIconOffset );
|
||||
}
|
||||
}
|
||||
|
||||
self MakeUsable();
|
||||
self.isUsable = true;
|
||||
self SetCanDamage( true );
|
||||
self thread box_handleDamage();
|
||||
self thread box_handleDeath();
|
||||
self thread box_timeOut();
|
||||
|
||||
self make_entity_sentient_mp( self.team, true );
|
||||
|
||||
if ( IsDefined( self.owner ) )
|
||||
self.owner notify( "new_deployable_box", self );
|
||||
|
||||
if (level.teamBased)
|
||||
{
|
||||
foreach ( player in level.participants )
|
||||
{
|
||||
_box_setActiveHelper( player, self.team == player.team, boxConfig.canUseCallback );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ( player in level.participants )
|
||||
{
|
||||
_box_setActiveHelper( player, IsDefined( self.owner ) && self.owner == player, boxConfig.canUseCallback );
|
||||
}
|
||||
}
|
||||
|
||||
if( ( !isdefined( self.air_dropped ) || !self.air_dropped ) && !isPlayingSolo() )
|
||||
level thread teamPlayerCardSplash( boxConfig.splashName, self.owner, self.team );
|
||||
|
||||
self thread box_playerConnected();
|
||||
self thread box_agentConnected();
|
||||
}
|
||||
|
||||
_box_setActiveHelper( player, bActivate, canUseFunc )
|
||||
{
|
||||
if ( bActivate )
|
||||
{
|
||||
if ( !IsDefined( canUseFunc ) || player [[ canUseFunc ]](self) )
|
||||
{
|
||||
self box_enablePlayerUse( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
self box_disablePlayerUse( player );
|
||||
// if this player is already a juggernaut then when they die, let them use the box
|
||||
self thread doubleDip( player );
|
||||
}
|
||||
self thread boxThink( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
self box_disablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
|
||||
box_playerConnected() // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
// when new players connect they need a boxthink thread run on them
|
||||
while( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
self childthread box_waittill_player_spawn_and_add_box( player );
|
||||
}
|
||||
}
|
||||
|
||||
box_agentConnected() // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
// when new agents connect they need a boxthink thread run on them
|
||||
while( true )
|
||||
{
|
||||
level waittill( "spawned_agent_player", agent );
|
||||
self box_addBoxForPlayer( agent );
|
||||
}
|
||||
}
|
||||
|
||||
box_waittill_player_spawn_and_add_box( player ) // self == box
|
||||
{
|
||||
player waittill( "spawned_player" );
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self box_addBoxForPlayer( player );
|
||||
}
|
||||
}
|
||||
|
||||
box_playerJoinedTeam( player ) // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
|
||||
// when new players connect they need a boxthink thread run on them
|
||||
while( true )
|
||||
{
|
||||
player waittill( "joined_team" );
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self box_addBoxForPlayer( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
box_addBoxForPlayer( player ) // self == box
|
||||
{
|
||||
if ( self.team == player.team )
|
||||
{
|
||||
self box_enablePlayerUse( player );
|
||||
self thread boxThink( player );
|
||||
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
||||
}
|
||||
else
|
||||
{
|
||||
self box_disablePlayerUse( player );
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
||||
}
|
||||
}
|
||||
|
||||
box_SetIcon( player, streakName, vOffset )
|
||||
{
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, self.icon_name, (0, 0, vOffset), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
|
||||
}
|
||||
|
||||
box_enablePlayerUse( player ) // self == box
|
||||
{
|
||||
if ( IsPlayer(player) )
|
||||
self EnablePlayerUse( player );
|
||||
|
||||
self.disabled_use_for[player GetEntityNumber()] = false;
|
||||
}
|
||||
|
||||
box_disablePlayerUse( player ) // self == box
|
||||
{
|
||||
if ( IsPlayer(player) )
|
||||
self DisablePlayerUse( player );
|
||||
|
||||
self.disabled_use_for[player GetEntityNumber()] = true;
|
||||
}
|
||||
|
||||
box_setInactive()
|
||||
{
|
||||
self makeUnusable();
|
||||
self.isUsable = false;
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( "none", "", (0,0,0) );
|
||||
if ( isDefined( self.objIdFriendly ) )
|
||||
_objective_delete( self.objIdFriendly );
|
||||
}
|
||||
|
||||
box_handleDamage() // self == box
|
||||
{
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
|
||||
self maps\mp\gametypes\_damage::monitorDamage(
|
||||
boxConfig.maxHealth,
|
||||
boxConfig.damageFeedback,
|
||||
::boxModifyDamage,
|
||||
::boxHandleDeathDamage,
|
||||
true // isKillstreak
|
||||
);
|
||||
}
|
||||
|
||||
boxModifyDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
modifiedDamage = damage;
|
||||
|
||||
if( IsExplosiveDamageMOD( type ) )
|
||||
{
|
||||
modifiedDamage = damage * 1.5;
|
||||
}
|
||||
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
||||
|
||||
return modifiedDamage;
|
||||
}
|
||||
|
||||
boxHandleDeathDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, boxConfig.xpPopup, boxConfig.voDestroyed );
|
||||
}
|
||||
|
||||
box_handleDeath()
|
||||
{
|
||||
self waittill ( "death" );
|
||||
|
||||
// this handles cases of deletion
|
||||
if ( !isDefined( self ) )
|
||||
return;
|
||||
|
||||
self box_setInactive();
|
||||
self removeBoxFromLevelArray();
|
||||
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
PlayFX( getfx( "deployablebox_crate_destroy" ), self.origin );
|
||||
// 2013-03-08 wsh: whould probably validate all the used fields...
|
||||
if ( IsDefined( boxConfig.deathDamageMax ) )
|
||||
{
|
||||
owner = undefined;
|
||||
if ( IsDefined(self.owner) )
|
||||
owner = self.owner;
|
||||
|
||||
// somewhat hacky:
|
||||
// shift the origin of the damage because it'll collide with the box otherwise
|
||||
// we could also apply the damage after we delete the item?
|
||||
RadiusDamage( self.origin + (0, 0, boxConfig.headIconOffset),
|
||||
boxConfig.deathDamageRadius,
|
||||
boxConfig.deathDamageMax,
|
||||
boxConfig.deathDamageMin,
|
||||
owner,
|
||||
"MOD_EXPLOSIVE",
|
||||
boxConfig.deathWeaponInfo
|
||||
);
|
||||
}
|
||||
|
||||
wait( 0.1 );
|
||||
|
||||
self notify( "deleting" );
|
||||
|
||||
self delete();
|
||||
}
|
||||
|
||||
box_handleOwnerDisconnect() // self == box
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self notify ( "box_handleOwner" );
|
||||
self endon ( "box_handleOwner" );
|
||||
|
||||
old_owner = self.owner;
|
||||
self.owner waittill( "killstreak_disowned" );
|
||||
|
||||
// special case for air dropped box to stay when fake owner leaves ( owner was randomly picked )
|
||||
if ( isdefined( self.air_dropped ) && self.air_dropped )
|
||||
{
|
||||
// reassign owner to next avaliable player
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !isdefined( player ) || ( isdefined( old_owner ) && old_owner == player ) )
|
||||
continue;
|
||||
|
||||
self.owner = player;
|
||||
self thread box_handleOwnerDisconnect(); // recurse
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// removed if not air dropped or if no host player found (which shouldn't happen)
|
||||
self notify( "death" );
|
||||
}
|
||||
|
||||
boxThink( player )
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
self thread boxCaptureThink( player );
|
||||
|
||||
if ( !IsDefined(player.boxes) )
|
||||
{
|
||||
player.boxes = [];
|
||||
}
|
||||
player.boxes[player.boxes.size] = self;
|
||||
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill ( "captured", capturer );
|
||||
|
||||
if (capturer == player)
|
||||
{
|
||||
player PlayLocalSound( boxConfig.onUseSfx );
|
||||
|
||||
if ( IsDefined( boxConfig.onuseCallback ) )
|
||||
{
|
||||
player [[ boxConfig.onUseCallback ]]( self );
|
||||
|
||||
if ( maps\mp\alien\_utility::is_chaos_mode() )
|
||||
maps\mp\alien\_chaos::update_pickup_deployable_box_event();
|
||||
}
|
||||
|
||||
// if this is not the owner then give the owner some xp
|
||||
if( IsDefined( self.owner ) && player != self.owner )
|
||||
{
|
||||
self.owner thread maps\mp\gametypes\_rank::xpEventPopup( boxConfig.event );
|
||||
self.owner thread maps\mp\gametypes\_rank::giveRankXP( "support", boxConfig.useXP );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.usesRemaining ) )
|
||||
{
|
||||
self.usesRemaining--;
|
||||
if ( self.usesRemaining == 0)
|
||||
{
|
||||
self box_leave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
||||
self box_disablePlayerUse( player );
|
||||
self thread doubleDip( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doubleDip( player ) // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
|
||||
// air dropped rewards can not be double dipped
|
||||
if( isdefined( self.air_dropped ) && self.air_dropped )
|
||||
return;
|
||||
|
||||
// once they die, let them take from the box again
|
||||
player waittill( "death" );
|
||||
|
||||
if( level.teamBased )
|
||||
{
|
||||
if( self.team == player.team )
|
||||
{
|
||||
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
||||
self box_enablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( self.owner ) && self.owner == player )
|
||||
{
|
||||
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
||||
self box_enablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boxCaptureThink( player ) // self == box
|
||||
{
|
||||
while( isDefined( self ) )
|
||||
{
|
||||
self waittill( "trigger", tiggerer );
|
||||
if ( is_aliens() )
|
||||
{
|
||||
if ( [[level.boxCaptureThink_alien_func]]( tiggerer ) )
|
||||
continue;
|
||||
}
|
||||
if ( is_chaos_mode() )
|
||||
{
|
||||
switch ( self.boxType )
|
||||
{
|
||||
case "medic_skill":
|
||||
case "specialist_skill":
|
||||
case "tank_skill":
|
||||
case "engineer_skill":
|
||||
if( is_true( tiggerer.hasChaosClassSkill ) )
|
||||
{
|
||||
tiggerer maps\mp\_utility::setLowerMessage( "cant_use", &"ALIEN_CHAOS_CANT_PICKUP_BONUS", 3 );
|
||||
continue;
|
||||
}
|
||||
else if ( is_true( tiggerer.chaosClassSkillInUse ) )
|
||||
{
|
||||
tiggerer maps\mp\_utility::setLowerMessage( "skill_in_use", &"ALIEN_CHAOS_SKILL_IN_USE", 3 );
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case "combo_freeze":
|
||||
if( is_true( tiggerer.hasComboFreeze ) )
|
||||
{
|
||||
tiggerer maps\mp\_utility::setLowerMessage( "cant_use", &"ALIEN_CHAOS_CANT_PICKUP_BONUS", 3 );
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tiggerer == player
|
||||
&& self useHoldThink( player, level.boxSettings[ self.boxType ].useTime )
|
||||
)
|
||||
{
|
||||
self notify( "captured", player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isFriendlyToBox( box )
|
||||
{
|
||||
return ( level.teamBased
|
||||
&& self.team == box.team );
|
||||
}
|
||||
|
||||
box_timeOut() // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
if ( box_should_leave_immediately() )
|
||||
{
|
||||
wait 0.05;
|
||||
}
|
||||
else
|
||||
{
|
||||
lifeSpan = level.boxSettings[ self.boxType ].lifeSpan;
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( lifeSpan );
|
||||
}
|
||||
|
||||
self box_leave();
|
||||
}
|
||||
|
||||
box_should_leave_immediately()
|
||||
{
|
||||
if ( ( self.boxtype == "deployable_ammo" && self.upgrade_rank == 4 ) || ( self.boxtype == "deployable_specialammo_comb" && self.upgrade_rank == 4 ) ) // stay to regen ammo
|
||||
return false;
|
||||
|
||||
if ( maps\mp\alien\_utility::isPlayingSolo() && ( !isdefined( self.air_dropped ) || !self.air_dropped ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
box_leave()
|
||||
{
|
||||
// TODO: get sound for this
|
||||
//if ( isDefined( self.owner ) )
|
||||
// self.owner thread leaderDialogOnPlayer( "sentry_gone" );
|
||||
PlayFX( getfx( "deployablebox_crate_destroy" ), self.origin );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
self notify( "death" );
|
||||
}
|
||||
|
||||
deleteOnOwnerDeath( owner ) // self == box.friendlyModel or box.enemyModel, owner == box
|
||||
{
|
||||
wait ( 0.25 );
|
||||
self linkTo( owner, "tag_origin", (0,0,0), (0,0,0) );
|
||||
|
||||
owner waittill ( "death" );
|
||||
|
||||
box_leave();
|
||||
}
|
||||
|
||||
box_ModelTeamUpdater( showForTeam ) // self == box model (enemy or friendly)
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useHoldThink( player, useTime )
|
||||
{
|
||||
if ( IsPlayer(player) )
|
||||
player playerLinkTo( self );
|
||||
else
|
||||
player LinkTo( self );
|
||||
player playerLinkedOffsetEnable();
|
||||
|
||||
player.boxParams = SpawnStruct();
|
||||
player.boxParams.curProgress = 0;
|
||||
player.boxParams.inUse = true;
|
||||
player.boxParams.useRate = 0;
|
||||
|
||||
if ( isDefined( useTime ) )
|
||||
{
|
||||
player.boxParams.useTime = useTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.boxParams.useTime = DEFAULT_USE_TIME;
|
||||
}
|
||||
|
||||
//player _disableWeapon();
|
||||
player disable_weapon_timeout( ( useTime + 0.05 ), "deployable_weapon_management" );
|
||||
|
||||
if ( IsPlayer(player) )
|
||||
player thread personalUseBar( self );
|
||||
|
||||
result = useHoldThinkLoop( player );
|
||||
assert ( isDefined( result ) );
|
||||
|
||||
if ( isAlive( player ) )
|
||||
{
|
||||
//player _enableWeapon();
|
||||
player enable_weapon_wrapper( "deployable_weapon_management" );
|
||||
player unlink();
|
||||
}
|
||||
|
||||
if ( !isDefined( self ) )
|
||||
return false;
|
||||
|
||||
player.boxParams.inUse = false;
|
||||
player.boxParams.curProgress = 0;
|
||||
|
||||
return ( result );
|
||||
}
|
||||
|
||||
personalUseBar( object ) // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
useBar = createPrimaryProgressBar( 0, 25 );
|
||||
useBarText = createPrimaryProgressBarText( 0, 25 );
|
||||
useBarText setText( level.boxSettings[ object.boxType ].capturingString );
|
||||
|
||||
lastRate = -1;
|
||||
while ( isReallyAlive( self ) && isDefined( object ) && self.boxParams.inUse && object.isUsable && !level.gameEnded )
|
||||
{
|
||||
if ( lastRate != self.boxParams.useRate )
|
||||
{
|
||||
if( self.boxParams.curProgress > self.boxParams.useTime)
|
||||
self.boxParams.curProgress = self.boxParams.useTime;
|
||||
|
||||
useBar updateBar( self.boxParams.curProgress / self.boxParams.useTime, (1000 / self.boxParams.useTime) * self.boxParams.useRate );
|
||||
|
||||
if ( !self.boxParams.useRate )
|
||||
{
|
||||
useBar hideElem();
|
||||
useBarText hideElem();
|
||||
}
|
||||
else
|
||||
{
|
||||
useBar showElem();
|
||||
useBarText showElem();
|
||||
}
|
||||
}
|
||||
lastRate = self.boxParams.useRate;
|
||||
wait ( 0.05 );
|
||||
}
|
||||
|
||||
useBar destroyElem();
|
||||
useBarText destroyElem();
|
||||
}
|
||||
|
||||
useHoldThinkLoop( player )
|
||||
{
|
||||
while( !level.gameEnded && isDefined( self ) && isReallyAlive( player ) && player useButtonPressed() && player.boxParams.curProgress < player.boxParams.useTime )
|
||||
{
|
||||
player.boxParams.curProgress += (50 * player.boxParams.useRate);
|
||||
|
||||
if ( isDefined( player.objectiveScaler ) )
|
||||
player.boxParams.useRate = 1 * player.objectiveScaler;
|
||||
else
|
||||
player.boxParams.useRate = 1;
|
||||
|
||||
if ( player.boxParams.curProgress >= player.boxParams.useTime )
|
||||
return ( isReallyAlive( player ) );
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
disableWhenJuggernaut() // self == box
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "juggernaut_equipped", player );
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
||||
self box_disablePlayerUse( player );
|
||||
self thread doubleDip( player );
|
||||
}
|
||||
}
|
||||
|
||||
addBoxToLevelArray() // self == box
|
||||
{
|
||||
// put the newly created box in the level array for the box type
|
||||
level.deployable_box[ self.boxType ][ self GetEntityNumber() ] = self;
|
||||
}
|
||||
|
||||
removeBoxFromLevelArray() // self == box
|
||||
{
|
||||
level.deployable_box[ self.boxType ][ self GetEntityNumber() ] = undefined;
|
||||
}
|
||||
|
||||
|
||||
default_canUseDeployable( boxEnt ) // self == player
|
||||
{
|
||||
if( ( isDefined( boxEnt ) && boxEnt.owner == self || self maps\mp\alien\_prestige::prestige_getNoDeployables() == 1.0 ) && !isdefined( boxEnt.air_dropped ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default_OnUseDeployable( boxent ) //self =a player
|
||||
{
|
||||
self thread maps\mp\alien\_persistence::deployablebox_used_track( boxEnt );
|
||||
maps\mp\alien\_utility::deployable_box_onuse_message( boxent );
|
||||
}
|
||||
|
||||
default_tryUseDeployable( lifeId, BOX_TYPE ) // self == player
|
||||
{
|
||||
result = self maps\mp\alien\_combat_resources::alien_beginDeployableViaMarker( lifeId, BOX_TYPE );
|
||||
|
||||
if( ( !IsDefined( result ) || !result ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
init_deployable( BOX_TYPE, boxconfig )
|
||||
{
|
||||
if ( !IsDefined( level.boxSettings ) )
|
||||
{
|
||||
level.boxSettings = [];
|
||||
}
|
||||
|
||||
level.boxSettings[ BOX_TYPE ] = boxConfig;
|
||||
|
||||
if ( !IsDefined( level.killStreakFuncs ) )
|
||||
{
|
||||
level.killStreakFuncs = [];
|
||||
}
|
||||
|
||||
//level.killStreakFuncs[ BOX_TYPE ] = ::default_tryUseDeployable;
|
||||
|
||||
level.deployable_box[ BOX_TYPE ] = []; // storing each created box in their own array
|
||||
}
|
||||
1620
maps/mp/alien/_deployablebox_functions.gsc
Normal file
1620
maps/mp/alien/_deployablebox_functions.gsc
Normal file
File diff suppressed because it is too large
Load Diff
65
maps/mp/alien/_dev.gsc
Normal file
65
maps/mp/alien/_dev.gsc
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
/#
|
||||
init()
|
||||
{
|
||||
SetDevDvarIfUninitialized( "debug_reflection", "0" );
|
||||
|
||||
level thread onPlayerConnect();
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
player thread updateReflectionProbe();
|
||||
}
|
||||
}
|
||||
|
||||
updateReflectionProbe()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if ( GetDvarInt( "debug_reflection" ) == 1 )
|
||||
{
|
||||
if ( !IsDefined( self.debug_reflectionobject ) )
|
||||
{
|
||||
self.debug_reflectionobject = spawn( "script_model", self geteye() + ( ( anglestoforward( self.angles ) * 100 ) ) );
|
||||
self.debug_reflectionobject setmodel( "test_sphere_silver" );
|
||||
self.debug_reflectionobject.origin = self geteye() + ( ( anglestoforward( self getplayerangles() ) * 100 ) );
|
||||
self thread reflectionProbeButtons();
|
||||
}
|
||||
}
|
||||
else if ( GetDvarInt( "debug_reflection" ) == 0 )
|
||||
{
|
||||
if ( IsDefined( self.debug_reflectionobject ) )
|
||||
self.debug_reflectionobject delete();
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
reflectionProbeButtons()
|
||||
{
|
||||
offset = 100;
|
||||
offsetinc = 50;
|
||||
|
||||
while ( GetDvarInt( "debug_reflection" ) == 1 )
|
||||
{
|
||||
if ( self buttonpressed( "BUTTON_X" ) )
|
||||
offset += offsetinc;
|
||||
if ( self buttonpressed( "BUTTON_Y" ) )
|
||||
offset -= offsetinc;
|
||||
if ( offset > 1000 )
|
||||
offset = 1000;
|
||||
if ( offset < 64 )
|
||||
offset = 64;
|
||||
|
||||
self.debug_reflectionobject.origin = self GetEye() + ( ( AnglesToForward( self GetPlayerAngles() ) * offset ) );
|
||||
|
||||
wait .05;
|
||||
}
|
||||
}
|
||||
#/
|
||||
210
maps/mp/alien/_director.gsc
Normal file
210
maps/mp/alien/_director.gsc
Normal file
@@ -0,0 +1,210 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
|
||||
// ================================================================
|
||||
// Alien Attribute Table
|
||||
// ================================================================
|
||||
|
||||
ATTRIBUTE_TABLE = "mp/alien/default_alien_definition.csv";
|
||||
|
||||
TABLE_COL_INDEX = 0;
|
||||
TABLE_COL_ATTRIBUTE = 1;
|
||||
TABLE_COL_AI_TYPE_BEGIN = 2;
|
||||
TABLE_COL_AI_TYPE_MAX_TYPES = 16;
|
||||
|
||||
alien_attribute_table_init()
|
||||
{
|
||||
// to be updated with default_alien_definition.csv
|
||||
// value variable type is defined in index values
|
||||
|
||||
if ( !isdefined( level.default_alien_definition ) )
|
||||
level.default_alien_definition = ATTRIBUTE_TABLE;
|
||||
|
||||
att_idx = [];
|
||||
att_idx[ "ref" ] = "0"; // string value
|
||||
att_idx[ "name" ] = "1";
|
||||
att_idx[ "model" ] = "2";
|
||||
att_idx[ "desc" ] = "3";
|
||||
att_idx[ "boss" ] = 4; // int value
|
||||
att_idx[ "animclass" ] = "5"; // string value
|
||||
|
||||
att_idx[ "health" ] = 10;
|
||||
att_idx[ "min_cumulative_pain_threshold" ] = 11;
|
||||
att_idx[ "min_cumulative_pain_buffer_time" ] = 12.0;
|
||||
att_idx[ "accuracy" ] = 13.0; // float value
|
||||
att_idx[ "speed" ] = 14.0;
|
||||
att_idx[ "scale" ] = 15.0;
|
||||
att_idx[ "xp" ] = 16;
|
||||
att_idx[ "attacker_difficulty" ] = 17.0;
|
||||
att_idx[ "attacker_priority" ] = 18;
|
||||
att_idx[ "jump_cost" ] = 19.0;
|
||||
att_idx[ "traverse_cost" ] = 20.0;
|
||||
att_idx[ "run_cost" ] = 21.0;
|
||||
att_idx[ "wall_run_cost" ] = 29.0;
|
||||
att_idx[ "heavy_damage_threshold" ] = 22.0;
|
||||
att_idx[ "pain_interval" ] = 23.0;
|
||||
att_idx[ "emissive_default" ] = 24.0;
|
||||
att_idx[ "emissive_max" ] = 25.0;
|
||||
att_idx[ "weight_scale" ] = 26.0;
|
||||
att_idx[ "reward" ] = 27.0;
|
||||
att_idx[ "view_height" ] = 28.0;
|
||||
|
||||
att_idx[ "behavior_cloak" ] = 100;
|
||||
att_idx[ "behavior_spit" ] = 101;
|
||||
att_idx[ "behavior_lead" ] = 102;
|
||||
att_idx[ "behavior_hives" ] = 103;
|
||||
|
||||
att_idx[ "swipe_min_damage" ] = 2000;
|
||||
att_idx[ "swipe_max_damage" ] = 2001;
|
||||
att_idx[ "leap_min_damage" ] = 2002;
|
||||
att_idx[ "leap_max_damage" ] = 2003;
|
||||
att_idx[ "wall_min_damage" ] = 2004;
|
||||
att_idx[ "wall_max_damage" ] = 2005;
|
||||
att_idx[ "charge_min_damage" ] = 2006;
|
||||
att_idx[ "charge_max_damage" ] = 2007;
|
||||
att_idx[ "explode_min_damage" ] = 2008;
|
||||
att_idx[ "explode_max_damage" ] = 2009;
|
||||
att_idx[ "slam_min_damage" ] = 2010;
|
||||
att_idx[ "slam_max_damage" ] = 2011;
|
||||
att_idx[ "synch_min_damage_per_second" ] = 2012;
|
||||
att_idx[ "synch_max_damage_per_second" ] = 2013;
|
||||
|
||||
// loots - float values
|
||||
loot_index = 1000;
|
||||
loot_index_max = 1100;
|
||||
for( i = loot_index; i < loot_index_max; i++ )
|
||||
{
|
||||
loot_ref = TableLookup( level.default_alien_definition, TABLE_COL_INDEX, i, TABLE_COL_ATTRIBUTE );
|
||||
if ( loot_ref == "" )
|
||||
break;
|
||||
|
||||
att_idx[ loot_ref ] = i * 1.00; // float
|
||||
}
|
||||
|
||||
level.alien_types = [];
|
||||
|
||||
// get types from table
|
||||
|
||||
maxIndex = TABLE_COL_AI_TYPE_BEGIN + TABLE_COL_AI_TYPE_MAX_TYPES;
|
||||
for ( typeIndex = TABLE_COL_AI_TYPE_BEGIN; typeIndex < maxIndex; typeIndex++ )
|
||||
setup_alien_type( att_idx, typeIndex );
|
||||
|
||||
if ( IsDefined( level.custom_alien_attribute_table_init ) )
|
||||
[[level.custom_alien_attribute_table_init]]();
|
||||
}
|
||||
|
||||
setup_alien_type( att_idx, type )
|
||||
{
|
||||
type_ref = TableLookup( level.default_alien_definition, TABLE_COL_INDEX, att_idx[ "ref" ], type );
|
||||
|
||||
// return if type does not exist
|
||||
if ( type_ref == "" )
|
||||
return;
|
||||
|
||||
level.alien_types[ type_ref ] = SpawnStruct();
|
||||
//level.alien_types[ type_ref ].attribute_index = att_idx;
|
||||
level.alien_types[ type_ref ].attributes = [];
|
||||
level.alien_types[ type_ref ].loots = [];
|
||||
|
||||
foreach( key, index in att_idx )
|
||||
{
|
||||
value = TableLookup( level.default_alien_definition, TABLE_COL_INDEX, index, type );
|
||||
|
||||
// cast the correct variable type
|
||||
if ( !isString( index ) )
|
||||
{
|
||||
if ( !IsSubStr( value, "." ) )
|
||||
value = int( value );
|
||||
else
|
||||
value = float( value );
|
||||
}
|
||||
|
||||
level.alien_types[ type_ref ].attributes[ key ] = value;
|
||||
|
||||
// loot!
|
||||
if ( IsSubStr( key, "loot_" ) && value > 0.0 )
|
||||
{
|
||||
level.alien_types[ type_ref ].loots[ key ] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============== Alien cloaking ==============
|
||||
CONST_DECLOAK_DIST = 800;
|
||||
CONST_CLOCK_CHANCE = 1;
|
||||
|
||||
alien_cloak()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self thread near_player_notify();
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if( any_player_nearby( self.origin, CONST_DECLOAK_DIST ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
self waittill( "jump_launching" );
|
||||
|
||||
|
||||
wait 0.20;
|
||||
|
||||
original_model = self.model;
|
||||
self maps\mp\alien\_alien_fx::alien_cloak_fx_on();
|
||||
self cloak_fx();
|
||||
self setmodel( original_model + "_cloak" ); // this _cloak model must exist
|
||||
|
||||
waittill_any_timeout( 1, "jump_finished", "damage" ); //, "near_player" );
|
||||
|
||||
|
||||
wait 0.20;
|
||||
|
||||
//self Show();
|
||||
self maps\mp\alien\_alien_fx::alien_cloak_fx_off();
|
||||
self uncloak_fx();
|
||||
self setmodel( original_model );
|
||||
}
|
||||
}
|
||||
|
||||
// WIP: SP>MP
|
||||
near_player_notify()
|
||||
{
|
||||
self endon( "death" );
|
||||
while ( 1 )
|
||||
{
|
||||
if ( any_player_nearby( self.origin, CONST_DECLOAK_DIST ) )
|
||||
self notify( "near_player" );
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
cloak_fx()
|
||||
{
|
||||
PlayFXOnTag( level._effect[ "alien_cloaking" ], self, "j_neck" );
|
||||
}
|
||||
|
||||
uncloak_fx()
|
||||
{
|
||||
PlayFXOnTag( level._effect[ "alien_uncloaking" ], self, "j_neck" );
|
||||
}
|
||||
|
||||
|
||||
smoke_puff()
|
||||
{
|
||||
PlayFXOnTag( level._effect[ "alien_teleport" ], self, "tag_origin" );
|
||||
|
||||
// somehow in MP the tags are not valid assets???
|
||||
|
||||
//PlayFXOnTag( level._effect[ "alien_teleport" ], self, "j_spineupper" );
|
||||
//PlayFXOnTag( level._effect[ "alien_teleport" ], self, "j_mainroot" );
|
||||
//PlayFXOnTag( level._effect[ "alien_teleport" ], self, "j_tail_3" );
|
||||
|
||||
PlayFXOnTag( level._effect[ "alien_teleport_dist" ], self, "tag_origin" );
|
||||
}
|
||||
2164
maps/mp/alien/_drill.gsc
Normal file
2164
maps/mp/alien/_drill.gsc
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user