688 lines
14 KiB
Plaintext
688 lines
14 KiB
Plaintext
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
|
|
main()
|
|
{
|
|
level.lastAutoSaveTime = 0;
|
|
flag_init( "game_saving" );
|
|
flag_init( "can_save" );
|
|
flag_set( "can_save" );
|
|
flag_init( "disable_autosaves" );
|
|
if ( !isdefined( level._extra_autosave_checks ) )
|
|
level._extra_autosave_checks = [];
|
|
|
|
level.autosave_proximity_threat_func = ::autosave_proximity_threat_func;
|
|
}
|
|
|
|
getDescription()
|
|
{
|
|
// autosave
|
|
return( &"AUTOSAVE_AUTOSAVE" );
|
|
}
|
|
|
|
getnames( num )
|
|
{
|
|
if ( num == 0 )
|
|
// Begin Game Autosave
|
|
savedescription = &"AUTOSAVE_GAME";
|
|
else
|
|
// No Name Specified
|
|
savedescription = &"AUTOSAVE_NOGAME";
|
|
|
|
return savedescription;
|
|
}
|
|
|
|
|
|
beginningOfLevelSave()
|
|
{
|
|
// Wait for introscreen to finish
|
|
level waittill( "finished final intro screen fadein" );
|
|
|
|
if ( level.MissionFailed )
|
|
return;
|
|
|
|
if ( maps\_arcademode::arcademode_complete() )
|
|
return;
|
|
|
|
if ( flag( "game_saving" ) )
|
|
return;
|
|
|
|
flag_set( "game_saving" );
|
|
|
|
imagename = "levelshots / autosave / autosave_" + level.script + "start";
|
|
|
|
// "levelstart" is recognized by the saveGame command as a special save game
|
|
// Start
|
|
SaveGame( "levelstart", &"AUTOSAVE_LEVELSTART", imagename, true );
|
|
SetDvar( "ui_grenade_death", "0" );
|
|
PrintLn( "Saving level start saved game" );
|
|
|
|
flag_clear( "game_saving" );
|
|
}
|
|
|
|
trigger_autosave_stealth( trigger )
|
|
{
|
|
trigger waittill( "trigger" );
|
|
autosave_stealth();
|
|
}
|
|
|
|
trigger_autosave_tactical( trigger )
|
|
{
|
|
trigger waittill( "trigger" );
|
|
autosave_tactical();
|
|
}
|
|
|
|
trigger_autosave( trigger )
|
|
{
|
|
if ( !isdefined( trigger.script_autosave ) )
|
|
trigger.script_autosave = 0;
|
|
|
|
autosaves_think( trigger );
|
|
}
|
|
|
|
autosaves_think( trigger )
|
|
{
|
|
savedescription = getnames( trigger.script_autosave );
|
|
|
|
if ( !( IsDefined( savedescription ) ) )
|
|
{
|
|
PrintLn( "autosave", self.script_autosave, " with no save description in _autosave.gsc!" );
|
|
return;
|
|
}
|
|
|
|
trigger waittill( "trigger" );
|
|
|
|
num = trigger.script_autosave;
|
|
imagename = "levelshots / autosave / autosave_" + level.script + num;
|
|
|
|
tryAutoSave( num, savedescription, imagename );
|
|
thread maps\_quotes::setDeadQuote();
|
|
|
|
if ( IsDefined( trigger ) )
|
|
trigger Delete();
|
|
}
|
|
|
|
|
|
autoSaveNameThink( trigger )
|
|
{
|
|
if ( level.start_point == "no_game" )
|
|
return;
|
|
|
|
trigger waittill( "trigger" );
|
|
if ( IsDefined( level.customautosavecheck ) )
|
|
if ( ![[ level.customautosavecheck ]]() )
|
|
return;
|
|
name = trigger.script_autosavename;
|
|
maps\_utility::autosave_by_name( name );
|
|
|
|
trigger Delete();
|
|
}
|
|
|
|
|
|
trigger_autosave_immediate( trigger )
|
|
{
|
|
trigger waittill( "trigger" );
|
|
// Start
|
|
// saveId = SaveGameNoCommit( 1, &"AUTOSAVE_LEVELSTART", "autosave_image" );
|
|
// CommitSave( saveId );
|
|
}
|
|
|
|
AutoSavePrint( msg, msg2 )
|
|
{
|
|
/#
|
|
SetDvarIfUninitialized( "scr_autosave_debug", "0" );
|
|
if ( GetDebugDvarInt( "scr_autosave_debug" ) == 1 )
|
|
{
|
|
if ( IsDefined( msg2 ) )
|
|
IPrintLn( msg + " [ localized description ]" );
|
|
else
|
|
IPrintLn( msg );
|
|
return;
|
|
}
|
|
#/
|
|
|
|
if ( IsDefined( msg2 ) )
|
|
PrintLn( msg, msg2 );
|
|
else
|
|
PrintLn( msg );
|
|
}
|
|
|
|
autosave_timeout( timeout )
|
|
{
|
|
level endon( "trying_new_autosave" );
|
|
level endon( "autosave_complete" );
|
|
wait( timeout );
|
|
flag_clear( "game_saving" );
|
|
level notify( "autosave_timeout" );
|
|
}
|
|
|
|
_autosave_game_now_nochecks()
|
|
{
|
|
imagename = "levelshots / autosave / autosave_" + level.script + "start";
|
|
// Start
|
|
SaveGame( "levelstart", &"AUTOSAVE_LEVELSTART", imagename, true );
|
|
}
|
|
|
|
_autosave_game_now( suppress_print )
|
|
{
|
|
if ( isdefined( level.MissionFailed ) && level.MissionFailed )
|
|
return;
|
|
|
|
if ( flag( "game_saving" ) )
|
|
return false;
|
|
|
|
if ( maps\_arcademode::arcademode_complete() )
|
|
return false;
|
|
|
|
for ( i = 0; i < level.players.size; i++ )
|
|
{
|
|
player = level.players[ i ];
|
|
if ( !isalive( player ) )
|
|
return false;
|
|
}
|
|
|
|
filename = "save_now";
|
|
descriptionString = getDescription();
|
|
|
|
if ( IsDefined( suppress_print ) )
|
|
saveId = SaveGameNoCommit( filename, descriptionString, "$default", true );
|
|
else
|
|
saveId = SaveGameNoCommit( filename, descriptionString );
|
|
|
|
wait( 0.05 );// code request
|
|
if ( IsSaveRecentlyLoaded() )
|
|
{
|
|
level.lastAutoSaveTime = GetTime();
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/# AutoSavePrint( "Saving game " + filename + " with desc ", descriptionString ); #/
|
|
|
|
if ( saveId < 0 )
|
|
{
|
|
/# AutoSavePrint( "Savegame failed - save error.: " + filename + " with desc ", descriptionString ); #/
|
|
return false;
|
|
}
|
|
|
|
|
|
if ( !try_to_autosave_now() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
flag_set( "game_saving" );
|
|
wait 2;
|
|
flag_clear( "game_saving" );
|
|
|
|
if ( !CommitWouldBeValid( saveId ) )
|
|
{
|
|
/# AutoSavePrint( "Save is no longer valid, another save was run from elsewhere" ); #/
|
|
return false;
|
|
}
|
|
|
|
|
|
// are we still healthy 2 seconds later? k save then
|
|
if ( try_to_autosave_now() )
|
|
{
|
|
if ( !isdefined( suppress_print ) )
|
|
thread maps\_arcademode::arcademode_checkpoint_print();
|
|
|
|
CommitSave( saveId );
|
|
SetDvar( "ui_grenade_death", "0" );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
autosave_now_trigger( trigger )
|
|
{
|
|
trigger waittill( "trigger" );
|
|
autosave_now();
|
|
}
|
|
|
|
try_to_autosave_now()
|
|
{
|
|
if ( !issavesuccessful() )
|
|
return false;
|
|
|
|
for ( i = 0; i < level.players.size; i++ )
|
|
{
|
|
player = level.players[ i ];
|
|
if ( !player autoSaveHealthCheck() )
|
|
return false;
|
|
}
|
|
|
|
if ( !flag( "can_save" ) )
|
|
{
|
|
/# AutoSavePrint( "Can_save flag was clear" ); #/
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
tryAutoSave( filename, description, image, timeout, doStealthChecks, suppress_print )
|
|
{
|
|
if ( flag( "disable_autosaves" ) )
|
|
return false;
|
|
|
|
level endon( "nextmission" );
|
|
level.player endon( "death" );
|
|
if ( is_coop() )
|
|
level.player2 endon( "death" );
|
|
|
|
level notify( "trying_new_autosave" );
|
|
|
|
if ( flag( "game_saving" ) )
|
|
return false;
|
|
|
|
if ( IsDefined( level.nextmission ) )
|
|
return false;
|
|
|
|
time1 = 1.25;
|
|
time2 = 1.25;
|
|
|
|
if ( IsDefined( timeout ) && timeout < time1 + time2 )
|
|
{
|
|
AssertMsg( "Warning, tried to do an autosave_or_timeout with a time less than " + ( time1 + time2 ) );
|
|
}
|
|
|
|
if ( !isdefined( suppress_print ) )
|
|
suppress_print = false;
|
|
if ( !isdefined( image ) )
|
|
image = "$default";
|
|
|
|
if ( !isdefined( doStealthChecks ) )
|
|
doStealthChecks = false;
|
|
|
|
flag_set( "game_saving" );
|
|
|
|
descriptionString = getDescription();
|
|
start_save_time = GetTime();
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( autoSaveCheck( undefined, doStealthChecks ) )
|
|
{
|
|
saveId = SaveGameNoCommit( filename, descriptionString, image, suppress_print );
|
|
/# AutoSavePrint( "Saving game " + filename + " with desc ", descriptionString ); #/
|
|
|
|
if ( saveId < 0 )
|
|
{
|
|
/# AutoSavePrint( "Savegame failed - save error.: " + filename + " with desc ", descriptionString ); #/
|
|
break;
|
|
}
|
|
|
|
wait( 0.05 );// code request
|
|
if ( IsSaveRecentlyLoaded() )
|
|
{
|
|
level.lastAutoSaveTime = GetTime();
|
|
break;
|
|
}
|
|
|
|
wait time1;
|
|
|
|
if ( extra_autosave_checks_failed() )
|
|
continue;
|
|
|
|
if ( !autoSaveCheck( undefined, doStealthChecks ) )
|
|
{
|
|
/# AutoSavePrint( "Savegame invalid: 1.25 second check failed" ); #/
|
|
continue;
|
|
}
|
|
|
|
wait time2;
|
|
|
|
if ( !autoSaveCheck_not_picky() )
|
|
{
|
|
/# AutoSavePrint( "Savegame invalid: 2.5 second check failed" ); #/
|
|
continue;
|
|
}
|
|
|
|
if ( IsDefined( timeout ) )
|
|
{
|
|
if ( GetTime() > start_save_time + timeout * 1000 )
|
|
break;
|
|
}
|
|
|
|
if ( !flag( "can_save" ) )
|
|
{
|
|
/# AutoSavePrint( "Can_save flag was clear" ); #/
|
|
break;
|
|
}
|
|
|
|
if ( !CommitWouldBeValid( saveId ) )
|
|
{
|
|
/# AutoSavePrint( "Save is no longer valid, another save was run from elsewhere" ); #/
|
|
flag_clear( "game_saving" );
|
|
return false;
|
|
}
|
|
|
|
|
|
thread maps\_arcademode::arcademode_checkpoint_print();
|
|
CommitSave( saveId );
|
|
level.lastSaveTime = GetTime();
|
|
SetDvar( "ui_grenade_death", "0" );
|
|
break;
|
|
}
|
|
|
|
wait 0.25;
|
|
}
|
|
|
|
flag_clear( "game_saving" );
|
|
return true;
|
|
}
|
|
|
|
extra_autosave_checks_failed()
|
|
{
|
|
foreach ( func in level._extra_autosave_checks )
|
|
{
|
|
if ( ![[ func[ "func" ] ]]() )
|
|
{
|
|
AutoSavePrint( "autosave failed: " + func[ "msg" ] );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
autoSaveCheck_not_picky()
|
|
{
|
|
return autoSaveCheck( false, false );
|
|
}
|
|
|
|
autoSaveCheck( doPickyChecks, doStealthChecks )
|
|
{
|
|
if ( IsDefined( level.special_autosavecondition ) && ![[ level.special_autosavecondition ]]() )
|
|
return false;
|
|
|
|
if ( level.MissionFailed )
|
|
return false;
|
|
|
|
if ( maps\_arcademode::arcademode_complete() )
|
|
return false;
|
|
|
|
if ( !isdefined( doPickyChecks ) )
|
|
doPickyChecks = level.doPickyAutosaveChecks;
|
|
|
|
if ( !isdefined( doStealthChecks ) )
|
|
doStealthChecks = false;
|
|
|
|
if ( doStealthChecks )
|
|
{
|
|
if ( ! [[ level.global_callbacks[ "_autosave_stealthcheck" ] ]]() )
|
|
return false;
|
|
}
|
|
|
|
// health check
|
|
for ( i = 0; i < level.players.size; i++ )
|
|
{
|
|
player = level.players[ i ];
|
|
if ( !player autoSaveHealthCheck() )
|
|
return false;
|
|
|
|
// ammo check
|
|
if ( doPickyChecks && !player autoSaveAmmoCheck() )
|
|
return false;
|
|
}
|
|
|
|
// ai / tank threat check
|
|
if ( level.autosave_threat_check_enabled )
|
|
{
|
|
if ( !autoSaveThreatCheck( doPickyChecks ) )
|
|
return false;
|
|
}
|
|
|
|
// player state check
|
|
for ( i = 0; i < level.players.size; i++ )
|
|
{
|
|
player = level.players[ i ];
|
|
if ( !player autoSavePlayerCheck( doPickyChecks ) )
|
|
return false;
|
|
}
|
|
|
|
// safe save check for level specific gameplay conditions
|
|
if ( IsDefined( level.savehere ) && !level.savehere )
|
|
return false;
|
|
|
|
// safe save check for level specific gameplay conditions
|
|
if ( IsDefined( level.canSave ) && !level.canSave )
|
|
return false;
|
|
|
|
// save was unsuccessful for internal reasons, such as lack of memory
|
|
if ( !issavesuccessful() )
|
|
{
|
|
AutoSavePrint( "autosave failed: save call was unsuccessful" );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
autoSavePlayerCheck( doPickyChecks )
|
|
{
|
|
Assert( IsPlayer( self ) );
|
|
|
|
if ( IsDefined( level.ac130gunner ) && level.ac130gunner == self )
|
|
return true;
|
|
|
|
if ( self IsMeleeing() && doPickyChecks )
|
|
{
|
|
AutoSavePrint( "autosave failed:player is meleeing" );
|
|
return false;
|
|
}
|
|
|
|
if ( self IsThrowingGrenade() && doPickyChecks )
|
|
{
|
|
AutoSavePrint( "autosave failed:player is throwing a grenade" );
|
|
return false;
|
|
}
|
|
|
|
if ( self IsFiring() && doPickyChecks )
|
|
{
|
|
AutoSavePrint( "autosave failed:player is firing" );
|
|
return false;
|
|
}
|
|
|
|
if ( IsDefined( self.shellshocked ) && self.shellshocked )
|
|
{
|
|
AutoSavePrint( "autosave failed:player is in shellshock" );
|
|
return false;
|
|
}
|
|
|
|
if ( self isFlashed() )
|
|
{
|
|
AutoSavePrint( "autosave failed:player is flashbanged" );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
autoSaveAmmoCheck()
|
|
{
|
|
Assert( IsPlayer( self ) );
|
|
|
|
if ( IsDefined( level.ac130gunner ) && level.ac130gunner == self )
|
|
return true;
|
|
|
|
weapons = self GetWeaponsListPrimaries();
|
|
|
|
for ( idx = 0; idx < weapons.size; idx++ )
|
|
{
|
|
fraction = self GetFractionMaxAmmo( weapons[ idx ] );
|
|
if ( fraction > 0.1 )
|
|
return( true );
|
|
}
|
|
|
|
AutoSavePrint( "autosave failed: ammo too low" );
|
|
return( false );
|
|
}
|
|
|
|
autoSaveHealthCheck()
|
|
{
|
|
Assert( IsPlayer( self ) );
|
|
|
|
if ( IsDefined( level.ac130gunner ) && level.ac130gunner == self )
|
|
return true;
|
|
|
|
if ( self ent_flag_exist( "coop_downed" ) && self ent_flag( "coop_downed" ) )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: health too low" ); #/
|
|
return false;
|
|
}
|
|
|
|
healthFraction = self.health / self.maxhealth;
|
|
if ( healthFraction < 0.5 )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: health too low" ); #/
|
|
return false;
|
|
}
|
|
|
|
if ( flag( "_radiation_poisoning" ) )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: player has radiation sickness" ); #/
|
|
return false;
|
|
}
|
|
|
|
|
|
if ( self ent_flag( "player_has_red_flashing_overlay" ) )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: player has red flashing overlay" ); #/
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
autoSaveThreatCheck( doPickyChecks )
|
|
{
|
|
if ( IsDefined( level.ac130gunner ) && level.ac130gunner == self )
|
|
return true;
|
|
|
|
enemies = GetAISpeciesArray( "bad_guys", "all" );
|
|
|
|
foreach ( enemy in enemies )
|
|
{
|
|
if ( !isdefined( enemy.enemy ) )
|
|
continue;
|
|
|
|
if ( !isplayer( enemy.enemy ) )
|
|
continue;
|
|
|
|
if ( enemy.type == "dog" )
|
|
{
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( Distance( enemy.origin, player.origin ) < 384 )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: Dog near player" ); #/
|
|
return( false );
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// is trying to melee the player
|
|
if ( IsDefined( enemy.Melee ) && IsDefined( enemy.melee.target ) && IsPlayer( enemy.melee.target ) )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: AI meleeing player" ); #/
|
|
return( false );
|
|
}
|
|
|
|
|
|
if ( enemy.finalAccuracy < 0.021 && enemy.finalAccuracy > -1 )
|
|
{
|
|
// enemy lacks the accuracy to be a threat
|
|
continue;
|
|
}
|
|
|
|
proximity_threat = [[ level.autosave_proximity_threat_func ]]( enemy );
|
|
|
|
if ( proximity_threat == "return" )
|
|
return false;
|
|
|
|
if ( proximity_threat == "none" )
|
|
{
|
|
// enemy isn't close enough to be a threat
|
|
continue;
|
|
}
|
|
|
|
// recently shot at the player
|
|
if ( enemy.a.lastShootTime > GetTime() - 500 )
|
|
{
|
|
if ( doPickyChecks || enemy animscripts\utility::canSeeEnemy( 0 ) && enemy CanShootEnemy( 0 ) )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: AI firing on player" ); #/
|
|
return( false );
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( enemy.a.aimIdleThread ) && enemy animscripts\utility::canSeeEnemy( 0 ) && enemy CanShootEnemy( 0 ) )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: AI aiming at player" ); #/
|
|
return( false );
|
|
}
|
|
}
|
|
|
|
if ( player_is_near_live_grenade() )
|
|
return false;
|
|
|
|
vehicles = GetEntArray( "destructible", "classname" );
|
|
foreach ( vehicle in vehicles )
|
|
{
|
|
if ( !isDefined( vehicle.healthDrain ) )
|
|
continue;
|
|
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( Distance( vehicle.origin, player.origin ) < 400 )// grenade radius is 220
|
|
{
|
|
/# AutoSavePrint( "autosave failed: burning car too close to player" ); #/
|
|
return( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
|
|
enemy_is_a_threat()
|
|
{
|
|
// AI must have a reasonable chance of hitting the player
|
|
if ( self.finalAccuracy >= 0.021 )
|
|
return true;
|
|
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( Distance( self.origin, player.origin ) < 500 )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
autosave_proximity_threat_func( enemy )
|
|
{
|
|
foreach ( player in level.players )
|
|
{
|
|
dist = Distance( enemy.origin, player.origin );
|
|
|
|
if ( dist < 360 )
|
|
{
|
|
/# AutoSavePrint( "autosave failed: AI too close to player" ); #/
|
|
return "return";
|
|
}
|
|
else
|
|
if ( dist < 1000 )
|
|
{
|
|
return "threat_exists";
|
|
}
|
|
}
|
|
|
|
return "none";
|
|
} |