This commit is contained in:
2024-12-11 11:28:08 +01:00
commit 12ac62a956
444 changed files with 303964 additions and 0 deletions

View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

335
maps/mp/_barrels_leak.gsc Normal file
View 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
View 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
View 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
View 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
View 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
View 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 );
}
}
}

View 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
View 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
View 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
View 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
View 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;
}

View 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
View 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
View 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
View 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
View 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" );
}

View 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
View 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
View 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;
}

View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
init()
{
}

50
maps/mp/_minefields.gsc Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

223
maps/mp/_radiation.gsc Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

7455
maps/mp/_utility.gsc Normal file

File diff suppressed because it is too large Load Diff

425
maps/mp/_water.gsc Normal file
View 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
View 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();
}

View 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;
}

View 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
View 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";
}
}

View 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 );
}
}

View File

@ -0,0 +1,7 @@
//=======================================================
// main
//=======================================================
main()
{
// nothing for now...
}

View 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...
}

View 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;
}

View 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...
}

View 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...
}

View 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;
}

View 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;
}

View 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...
}

View 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;
}

View 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...
}

View 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;
}

View 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();
}

View 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...
}

View 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...
}

View 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...
}

View 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...
}

View 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...
}

View 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 );
}

View 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 );
}

View 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" );
}

View 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;
}
}

View 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";
}

View 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";
}
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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" );
}

View 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() );
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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 );
}

View 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;
}

View 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 );
}

File diff suppressed because it is too large Load Diff

View 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";
}

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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" );
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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;
}

File diff suppressed because it is too large Load Diff

1383
maps/mp/alien/_chaos.gsc Normal file

File diff suppressed because it is too large Load Diff

View 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 ); }

View 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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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
View 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

File diff suppressed because it is too large Load Diff

View 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
}

File diff suppressed because it is too large Load Diff

65
maps/mp/alien/_dev.gsc Normal file
View 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
View 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

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