2762 lines
77 KiB
Plaintext
2762 lines
77 KiB
Plaintext
#using scripts\shared\challenges_shared;
|
|
#using scripts\shared\clientfield_shared;
|
|
#using scripts\shared\callbacks_shared;
|
|
#using scripts\shared\demo_shared;
|
|
#using scripts\shared\gameobjects_shared;
|
|
#using scripts\shared\hud_message_shared;
|
|
#using scripts\shared\hud_util_shared;
|
|
#using scripts\shared\math_shared;
|
|
#using scripts\shared\popups_shared;
|
|
#using scripts\shared\rank_shared;
|
|
#using scripts\shared\scoreevents_shared;
|
|
#using scripts\shared\sound_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\array_shared;
|
|
#using scripts\shared\_oob;
|
|
#using scripts\shared\killstreaks_shared;
|
|
|
|
|
|
|
|
|
|
|
|
#using scripts\shared\abilities\_ability_player;
|
|
|
|
#using scripts\mp\_armor;
|
|
#using scripts\mp\gametypes\_globallogic;
|
|
#using scripts\mp\gametypes\_globallogic_audio;
|
|
#using scripts\mp\gametypes\_globallogic_defaults;
|
|
#using scripts\mp\gametypes\_globallogic_player;
|
|
#using scripts\mp\gametypes\_globallogic_score;
|
|
#using scripts\mp\gametypes\_globallogic_ui;
|
|
#using scripts\mp\gametypes\_globallogic_utils;
|
|
#using scripts\mp\gametypes\_hud_message;
|
|
#using scripts\mp\gametypes\_spawning;
|
|
#using scripts\mp\gametypes\_spawnlogic;
|
|
|
|
#using scripts\mp\_challenges;
|
|
#using scripts\mp\_util;
|
|
#using scripts\mp\teams\_teams;
|
|
|
|
|
|
|
|
//"p7_mp_uplink_ball"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#precache( "string", "OBJECTIVES_BALL" );
|
|
//#precache( "string", "OBJECTIVES_BALL_SCORE" );
|
|
#precache( "string", "OBJECTIVES_BALL_HINT" );
|
|
#precache( "string", "MP_BALL_OVERTIME_ROUND_1" );
|
|
#precache( "string", "MP_BALL_OVERTIME_ROUND_1" );
|
|
#precache( "string", "MP_BALL_OVERTIME_ROUND_2_WINNER" );
|
|
#precache( "string", "MP_BALL_OVERTIME_ROUND_2_LOSER" );
|
|
#precache( "string", "MP_BALL_OVERTIME_ROUND_2_TIE" );
|
|
#precache( "string", "MP_BALL_OVERTIME_ROUND_2_TIE" );
|
|
#precache( "string", "MPUI_BALL_OVERTIME_FASTEST_CAP_TIME" );
|
|
#precache( "string", "MPUI_BALL_OVERTIME_DEFEAT_TIMELIMIT" );
|
|
#precache( "string", "MPUI_BALL_OVERTIME_DEFEAT_DID_NOT_DEFEND" );
|
|
|
|
#precache( "string", "MP_BALL_PICKED_UP" );
|
|
#precache( "string", "MP_BALL_DROPPED" );
|
|
#precache( "string", "MP_BALL_CAPTURE" );
|
|
|
|
#precache( "fx", "ui/fx_uplink_ball_trail" );
|
|
#precache( "fx", "ui/fx_uplink_ball_vanish" );
|
|
|
|
#precache( "objective", "ball_ball" );
|
|
#precache( "objective", "ball_goal_allies" );
|
|
#precache( "objective", "ball_goal_axis" );
|
|
|
|
/*
|
|
BALL
|
|
|
|
Level requirements
|
|
------------------
|
|
Allied Spawnpoints:
|
|
classname mp_sd_spawn_attacker
|
|
Allied players spawn from these. Place at least 16 of these relatively close together.
|
|
|
|
Axis Spawnpoints:
|
|
classname mp_sd_spawn_defender
|
|
Axis players spawn from these. Place at least 16 of these relatively close together.
|
|
|
|
Spectator Spawnpoints:
|
|
classname mp_global_intermission
|
|
Spectators spawn from these and intermission is viewed from these positions.
|
|
Atleast one is required, any more and they are randomly chosen between.
|
|
|
|
Goal:
|
|
Ball:
|
|
*/
|
|
|
|
function autoexec __init__sytem__() { system::register("ball",&__init__,undefined,undefined); }
|
|
|
|
function __init__()
|
|
{
|
|
clientfield::register( "allplayers", "ballcarrier" , 1, 1, "int" );
|
|
clientfield::register( "allplayers", "passoption" , 1, 1, "int" );
|
|
clientfield::register( "world", "ball_away" , 1, 1, "int" );
|
|
clientfield::register( "world", "ball_score_allies" , 1, 1, "int" );
|
|
clientfield::register( "world", "ball_score_axis" , 1, 1, "int" );
|
|
}
|
|
|
|
function main()
|
|
{
|
|
globallogic::init();
|
|
|
|
util::registerTimeLimit( 0, 1440 );
|
|
util::registerRoundLimit( 0, 10 );
|
|
util::registerRoundWinLimit( 0, 10 );
|
|
util::registerRoundSwitch( 0, 9 );
|
|
util::registerNumLives( 0, 100 );
|
|
util::registerRoundScoreLimit( 0, 5000 );
|
|
util::registerScoreLimit( 0, 5000 );
|
|
|
|
level.scoreRoundWinBased = ( GetGametypeSetting( "cumulativeRoundScores" ) == false );
|
|
|
|
level.teamKillPenaltyMultiplier = GetGametypeSetting( "teamKillPenalty" );
|
|
level.teamKillScoreMultiplier = GetGametypeSetting( "teamKillScore" );
|
|
level.enemyObjectivePingTime = GetGametypeSetting( "objectivePingTime" );
|
|
|
|
if ( level.roundScoreLimit )
|
|
{
|
|
level.carryScore = math::clamp( GetGametypeSetting( "carryScore" ), 0, level.roundScoreLimit );
|
|
level.throwScore = math::clamp( GetGametypeSetting( "throwScore" ), 0, level.roundScoreLimit );
|
|
}
|
|
else
|
|
{
|
|
level.carryScore = GetGametypeSetting( "carryScore" );
|
|
level.throwScore = GetGametypeSetting( "throwScore" );
|
|
}
|
|
|
|
level.carryArmor = GetGametypeSetting( "carrierArmor" );
|
|
|
|
level.ballCount = GetGametypeSetting( "ballCount" );
|
|
level.enemyCarrierVisible = GetGametypeSetting( "enemyCarrierVisible" );
|
|
level.idleFlagReturnTime = GetGametypeSetting( "idleFlagResetTime" );
|
|
|
|
globallogic::registerFriendlyFireDelay( level.gameType, 15, 0, 1440 );
|
|
|
|
level.teamBased = true;
|
|
level.overrideTeamScore = true;
|
|
level.clampScoreLimit = false;
|
|
level.doubleOvertime = true;
|
|
|
|
level.onPrecacheGameType =&onPrecacheGameType;
|
|
level.onStartGameType =&onStartGameType;
|
|
level.onSpawnPlayer =&onSpawnPlayer;
|
|
level.onPlayerKilled =&onPlayerKilled;
|
|
level.onRoundSwitch =&onRoundSwitch;
|
|
level.onRoundScoreLimit = &onRoundScoreLimit;
|
|
level.onEndGame =&onEndGame;
|
|
level.onRoundEndGame =&onRoundEndGame;
|
|
level.getTeamKillPenalty =&ball_getTeamKillPenalty;
|
|
level.getTeamKillScore =&ball_getTeamKillScore;
|
|
level.setMatchScoreHUDElemForTeam =&setMatchScoreHUDElemForTeam;
|
|
level.shouldPlayOvertimeRound =&shouldPlayOvertimeRound;
|
|
level.onTimeLimit = &ball_onTimeLimit;
|
|
|
|
gameobjects::register_allowed_gameobject( level.gameType );
|
|
|
|
globallogic_audio::set_leader_gametype_dialog ( "startUplink", "hcStartUplink", "uplOrders", "uplOrders" );
|
|
|
|
// Sets the scoreboard columns and determines with data is sent across the network
|
|
if ( !SessionModeIsSystemlink() && !SessionModeIsOnlineGame() && IsSplitScreen() )
|
|
// local matches only show the first three columns
|
|
globallogic::setvisiblescoreboardcolumns( "score", "kills", "carries", "throws", "deaths" );
|
|
else
|
|
globallogic::setvisiblescoreboardcolumns( "score", "kills", "deaths", "carries", "throws" );
|
|
}
|
|
|
|
function onPrecacheGameType()
|
|
{
|
|
game["strings"]["score_limit_reached"] = &"MP_CAP_LIMIT_REACHED";
|
|
|
|
// game["ball_dropped_sound"] = "mp_war_objective_lost";
|
|
// game["ball_recovered_sound"] = "mp_war_objective_taken";
|
|
}
|
|
|
|
|
|
function onStartGameType()
|
|
{
|
|
level.useStartSpawns = true;
|
|
level.ballWorldWeapon = GetWeapon( "ball_world" );
|
|
level.passingBallWeapon = GetWeapon( "ball_world_pass" );
|
|
|
|
if ( !isdefined( game["switchedsides"] ) )
|
|
{
|
|
game["switchedsides"] = false;
|
|
}
|
|
|
|
setClientNameMode("auto_change");
|
|
|
|
if ( level.scoreRoundWinBased )
|
|
{
|
|
globallogic_score::resetTeamScores();
|
|
}
|
|
|
|
util::setObjectiveText( "allies", &"OBJECTIVES_BALL" );
|
|
util::setObjectiveText( "axis", &"OBJECTIVES_BALL" );
|
|
|
|
if ( level.splitscreen )
|
|
{
|
|
util::setObjectiveScoreText( "allies", &"OBJECTIVES_BALL" );
|
|
util::setObjectiveScoreText( "axis", &"OBJECTIVES_BALL" );
|
|
}
|
|
else
|
|
{
|
|
util::setObjectiveScoreText( "allies", &"OBJECTIVES_BALL_SCORE" );
|
|
util::setObjectiveScoreText( "axis", &"OBJECTIVES_BALL_SCORE" );
|
|
}
|
|
util::setObjectiveHintText( "allies", &"OBJECTIVES_BALL_HINT" );
|
|
util::setObjectiveHintText( "axis", &"OBJECTIVES_BALL_HINT" );
|
|
|
|
if ( isdefined( game["overtime_round"] ) )
|
|
{
|
|
// This is only necessary when cumulativeRoundScores is on so that the game doesn't immediately end due to scorelimit being set to 1 in OT
|
|
if ( !isdefined( game["ball_game_score"] ) )
|
|
{
|
|
game["ball_game_score"] = [];
|
|
game["ball_game_score"]["allies"] = [[level._getTeamScore]]( "allies" );
|
|
game["ball_game_score"]["axis"] = [[level._getTeamScore]]( "axis" );
|
|
}
|
|
[[level._setTeamScore]]( "allies", 0 );
|
|
[[level._setTeamScore]]( "axis", 0 );
|
|
|
|
if ( isdefined( game["ball_overtime_score_to_beat"] ) )
|
|
{
|
|
util::registerScoreLimit( game["ball_overtime_score_to_beat"], game["ball_overtime_score_to_beat"] );
|
|
}
|
|
else
|
|
{
|
|
util::registerScoreLimit( 1, 1 );
|
|
}
|
|
|
|
if ( isdefined( game["ball_overtime_time_to_beat"] ) )
|
|
{
|
|
util::registerTimeLimit( game["ball_overtime_time_to_beat"] / 60000, game["ball_overtime_time_to_beat"] / 60000 );
|
|
}
|
|
else
|
|
{
|
|
util::registerTimeLimit( 0, 1440 ); // Reset the time limit from the round_time_to_beat
|
|
}
|
|
|
|
if ( game["overtime_round"] == 1 )
|
|
{
|
|
util::setObjectiveHintText( "allies", &"MP_BALL_OVERTIME_ROUND_1" );
|
|
util::setObjectiveHintText( "axis", &"MP_BALL_OVERTIME_ROUND_1" );
|
|
}
|
|
else if ( isdefined( game["ball_overtime_first_winner"] ) )
|
|
{
|
|
level.onTimeLimit = &ballOvertimeRound2_onTimeLimit;
|
|
game["teamSuddenDeath"][game["ball_overtime_first_winner"]] = true;
|
|
util::setObjectiveHintText( game["ball_overtime_first_winner"], &"MP_BALL_OVERTIME_ROUND_2_WINNER" );
|
|
util::setObjectiveHintText( util::getOtherTeam( game["ball_overtime_first_winner"] ), &"MP_BALL_OVERTIME_ROUND_2_LOSER" );
|
|
}
|
|
else
|
|
{
|
|
level.onTimeLimit = &ballOvertimeRound2_onTimeLimit;
|
|
util::setObjectiveHintText( "allies", &"MP_BALL_OVERTIME_ROUND_2_TIE" );
|
|
util::setObjectiveHintText( "axis", &"MP_BALL_OVERTIME_ROUND_2_TIE" );
|
|
}
|
|
}
|
|
else if ( isdefined( game["round_time_to_beat"] ) )
|
|
{
|
|
util::registerTimeLimit( game["round_time_to_beat"] / 60000, game["round_time_to_beat"] / 60000 );
|
|
}
|
|
|
|
// Spawn Points
|
|
|
|
spawning::create_map_placed_influencers();
|
|
|
|
level.spawnMins = ( 0, 0, 0 );
|
|
level.spawnMaxs = ( 0, 0, 0 );
|
|
spawnlogic::place_spawn_points( "mp_ctf_spawn_allies_start" );
|
|
spawnlogic::place_spawn_points( "mp_ctf_spawn_axis_start" );
|
|
spawnlogic::add_spawn_points( "allies", "mp_ctf_spawn_allies" );
|
|
spawnlogic::add_spawn_points( "axis", "mp_ctf_spawn_axis" );
|
|
|
|
spawning::add_fallback_spawnpoints( "allies", "mp_tdm_spawn" );
|
|
spawning::add_fallback_spawnpoints( "axis", "mp_tdm_spawn" );
|
|
|
|
spawning::updateAllSpawnPoints();
|
|
spawning::update_fallback_spawnpoints();
|
|
|
|
level.mapCenter = math::find_box_center( level.spawnMins, level.spawnMaxs );
|
|
setMapCenter( level.mapCenter );
|
|
|
|
spawnpoint = spawnlogic::get_random_intermission_point();
|
|
setDemoIntermissionPoint( spawnpoint.origin, spawnpoint.angles );
|
|
|
|
level.spawn_axis = spawnlogic::get_spawnpoint_array( "mp_ctf_spawn_axis" );
|
|
level.spawn_allies = spawnlogic::get_spawnpoint_array( "mp_ctf_spawn_allies" );
|
|
|
|
level.spawn_start = [];
|
|
|
|
foreach( team in level.teams )
|
|
{
|
|
level.spawn_start[ team ] = spawnlogic::get_spawnpoint_array("mp_ctf_spawn_" + team + "_start");
|
|
}
|
|
|
|
level thread setup_objectives();
|
|
}
|
|
|
|
function anyBallsInTheAir()
|
|
{
|
|
foreach( ball in level.balls )
|
|
{
|
|
if ( isdefined( ball.carrier ) )
|
|
continue;
|
|
if ( isdefined( ball.projectile ) )
|
|
{
|
|
if ( !(ball.projectile IsOnGround()) )
|
|
return ball;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
function waitForBallToComeToRest()
|
|
{
|
|
self endon("reset");
|
|
self endon("pickup_object");
|
|
|
|
if ( isdefined( self.projectile ) )
|
|
{
|
|
if ( self.projectile IsOnGround() )
|
|
return;
|
|
|
|
self.projectile endon("death");
|
|
self.projectile endon("stationary");
|
|
self.projectile endon("grenade_bounce");
|
|
while(1)
|
|
{
|
|
wait(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
function freezePlayersForRoundEnd()
|
|
{
|
|
self endon("disconnect");
|
|
|
|
self globallogic_player::freezePlayerForRoundEnd();
|
|
self thread globallogic::roundEndDoF( 4.0 );
|
|
|
|
// incase they were dead at the time
|
|
self waittill( "spawned" );
|
|
|
|
if ( self.sessionstate == "playing" )
|
|
{
|
|
self globallogic_player::freezePlayerForRoundEnd();
|
|
self thread globallogic::roundEndDoF( 4.0 );
|
|
}
|
|
}
|
|
|
|
function waitForAllBallsToComeToRest()
|
|
{
|
|
// wait for the ball to hit the ground
|
|
ball = anyBallsInTheAir();
|
|
|
|
if ( isdefined( ball ) )
|
|
{
|
|
//because we are waiting the check time limit keeps firing
|
|
//we dont want to rerun this or the endgame code
|
|
level.onTimeLimit = &ball_onTimeLimit_doNothing;
|
|
ball waitForBallToComeToRest();
|
|
}
|
|
}
|
|
|
|
function ball_onTimeLimit()
|
|
{
|
|
waitForAllBallsToComeToRest();
|
|
|
|
globallogic_defaults::default_onTimeLimit();
|
|
}
|
|
|
|
function ball_onTimeLimit_doNothing()
|
|
{
|
|
}
|
|
|
|
function ballOvertimeRound2_onTimeLimit()
|
|
{
|
|
waitForAllBallsToComeToRest();
|
|
|
|
winner = undefined;
|
|
|
|
if ( level.teamBased )
|
|
{
|
|
foreach( team in level.teams )
|
|
{
|
|
if( game["teamSuddenDeath"][team] )
|
|
{
|
|
winner = team;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !isDefined( winner ) )
|
|
{
|
|
winner = globallogic::determineTeamWinnerByGameStat( "teamScores" );
|
|
}
|
|
|
|
globallogic_utils::logTeamWinString( "time limit", winner );
|
|
}
|
|
else
|
|
{
|
|
winner = globallogic_score::getHighestScoringPlayer();
|
|
|
|
/#
|
|
if ( isdefined( winner ) )
|
|
print( "time limit, win: " + winner.name );
|
|
else
|
|
print( "time limit, tie" );
|
|
#/
|
|
}
|
|
|
|
// i think these two lines are obsolete
|
|
//makeDvarServerInfo( "ui_text_endreason", game["strings"]["time_limit_reached"] );
|
|
SetDvar( "ui_text_endreason", game["strings"]["time_limit_reached"] );
|
|
|
|
thread globallogic::endGame( winner, game["strings"]["time_limit_reached"] );
|
|
}
|
|
|
|
|
|
function onSpawnPlayer(predictedSpawn)
|
|
{
|
|
self.isBallCarrier = false;
|
|
self.ballCarried = undefined;
|
|
self clientfield::set( "ctf_flag_carrier", 0 );
|
|
|
|
self thread ballConsistencySwitchThread();
|
|
|
|
spawning::onSpawnPlayer(predictedSpawn);
|
|
}
|
|
|
|
function ballConsistencySwitchThread()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "delete" );
|
|
// failsafe thread to watch if we have the ball but it is not primary
|
|
player = self;
|
|
ball = GetWeapon( "ball" );
|
|
while( 1 )
|
|
{
|
|
if( isdefined( ball ) && player HasWeapon( ball ) )
|
|
{
|
|
curWeapon = player GetCurrentWeapon();
|
|
if( isdefined( curWeapon ) && ( curWeapon != ball ) && !(player IsSwitchingWeapons()) )
|
|
{
|
|
if( curWeapon.isHeroWeapon )
|
|
{
|
|
slot = self GadgetGetSlot( curWeapon );
|
|
if( !self ability_player::gadget_is_in_use( slot ) )
|
|
{
|
|
{wait(.05);};
|
|
continue;
|
|
}
|
|
}
|
|
/#
|
|
println( "player has ball" );
|
|
#/
|
|
player switchToWeapon( ball );
|
|
player DisableWeaponCycling();
|
|
player DisableOffhandWeapons();
|
|
}
|
|
}
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function onPlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration )
|
|
{
|
|
if ( isdefined( self.carryObject ) )
|
|
{
|
|
otherTeam = util::getOtherTeam( self.team );
|
|
|
|
self RecordGameEvent("return");
|
|
|
|
if ( isdefined( attacker ) && IsPlayer( attacker ) && attacker != self )
|
|
{
|
|
attacker RecordGameEvent("kill_carrier");
|
|
if( attacker.team != self.team )
|
|
{
|
|
scoreevents::processScoreEvent( "kill_ball_carrier", attacker, undefined, weapon );
|
|
attacker AddPlayerStat( "kill_carrier", 1 );
|
|
}
|
|
|
|
// TODO: Add ball # to objectiveId
|
|
globallogic_audio::leader_dialog( "uplWeDrop", self.team, undefined, "uplink_ball" );
|
|
globallogic_audio::leader_dialog( "uplTheyDrop", otherTeam, undefined, "uplink_ball" );
|
|
|
|
globallogic_audio::play_2d_on_team( "mpl_balldrop_sting_friend", self.team );
|
|
globallogic_audio::play_2d_on_team( "mpl_balldrop_sting_enemy", otherTeam );
|
|
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_DROPPED", self, self.team );
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_DROPPED", self, otherTeam );
|
|
}
|
|
}
|
|
else if ( isdefined( attacker.carryObject ) && ( attacker.team != self.team ) )
|
|
{
|
|
scoreevents::processScoreEvent( "kill_enemy_while_carrying_ball", attacker, undefined, weapon );
|
|
}
|
|
|
|
foreach( ball in level.balls )
|
|
{
|
|
ballCarrier = ball.carrier;
|
|
if ( isdefined( ballCarrier ) )
|
|
{
|
|
ballOrigin = ball.carrier.origin;
|
|
isCarried = true;
|
|
}
|
|
else
|
|
{
|
|
ballOrigin = ball.curorigin;
|
|
isCarried = false;
|
|
}
|
|
|
|
if ( isCarried && isdefined( attacker ) && isdefined( attacker.team ) && ( attacker != self ) && ( ballCarrier != attacker ) )
|
|
{
|
|
if ( attacker.team == ball.carrier.team )
|
|
{
|
|
dist = Distance2dSquared(self.origin, ballOrigin);
|
|
if ( dist < level.defaultOffenseRadiusSQ )
|
|
{
|
|
attacker addplayerstat( "defend_carrier", 1 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
victim = self;
|
|
|
|
foreach( ball_goal in level.ball_goals )
|
|
{
|
|
if ( isdefined( attacker ) && isdefined( attacker.team ) && ( attacker != victim ) && isdefined( victim.team ) && isPlayer( attacker ) )
|
|
{
|
|
dist_to_goal = Distance2dSquared( attacker.origin, ball_goal.origin );
|
|
victim_dist_to_goal = Distance2dSquared( victim.origin, ball_goal.origin );
|
|
if ( dist_to_goal < level.defaultOffenseRadiusSQ || victim_dist_to_goal < level.defaultOffenseRadiusSQ )
|
|
{
|
|
if ( victim.team == ball_goal.team )
|
|
{
|
|
attacker thread challenges::killedBaseDefender( ball_goal.trigger );
|
|
}
|
|
else
|
|
{
|
|
attacker thread challenges::killedBaseOffender( ball_goal.trigger, weapon );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function onRoundSwitch()
|
|
{
|
|
if ( !isdefined( game["switchedsides"] ) )
|
|
game["switchedsides"] = false;
|
|
|
|
level.halftimeType = "halftime";
|
|
game["switchedsides"] = !game["switchedsides"];
|
|
}
|
|
|
|
function onRoundScoreLimit()
|
|
{
|
|
if ( !isdefined( game["overtime_round"] ) )
|
|
{
|
|
timeLimit = GetGametypeSetting( "timeLimit" ) * 60000;
|
|
timeToBeat = globallogic_utils::getTimePassed();
|
|
if ( timeLimit > 0 && timeToBeat < timeLimit )
|
|
{
|
|
game["round_time_to_beat"] = timeToBeat;
|
|
}
|
|
}
|
|
|
|
return globallogic_defaults::default_onRoundScoreLimit();
|
|
}
|
|
|
|
function onEndGame( winningTeam )
|
|
{
|
|
if ( !isdefined( winningTeam ) || ( winningTeam == "tie" ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( isdefined( game["overtime_round"] ) )
|
|
{
|
|
if ( game["overtime_round"] == 1 )
|
|
{
|
|
game["ball_overtime_first_winner"] = winningTeam;
|
|
game["ball_overtime_score_to_beat"] = GetTeamScore( winningTeam );
|
|
game["ball_overtime_time_to_beat"] = globallogic_utils::getTimePassed();
|
|
}
|
|
else
|
|
{
|
|
game["ball_overtime_second_winner"] = winningTeam;
|
|
game["ball_overtime_best_score"] = GetTeamScore( winningTeam );
|
|
game["ball_overtime_best_time"] = globallogic_utils::getTimePassed();
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateTeamScoreByRoundsWon()
|
|
{
|
|
if ( level.scoreRoundWinBased )
|
|
{
|
|
foreach( team in level.teams )
|
|
{
|
|
[[level._setTeamScore]]( team, game["roundswon"][team] );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function onRoundEndGame( winningTeam )
|
|
{
|
|
if ( isdefined( game["overtime_round"] ) )
|
|
{
|
|
if ( isdefined( game["ball_overtime_first_winner"] ) )
|
|
{
|
|
losing_team_score = 0;
|
|
if ( !isdefined( winningTeam ) || ( winningTeam == "tie" ) )
|
|
{
|
|
winningTeam = game["ball_overtime_first_winner"];
|
|
}
|
|
|
|
if ( game["ball_overtime_first_winner"] == winningTeam )
|
|
{
|
|
level.endVictoryReasonText = &"MPUI_BALL_OVERTIME_FASTEST_CAP_TIME";
|
|
level.endDefeatReasonText = &"MPUI_BALL_OVERTIME_DEFEAT_TIMELIMIT";
|
|
}
|
|
else
|
|
{
|
|
level.endVictoryReasonText = &"MPUI_BALL_OVERTIME_FASTEST_CAP_TIME";
|
|
level.endDefeatReasonText = &"MPUI_BALL_OVERTIME_DEFEAT_DID_NOT_DEFEND";
|
|
}
|
|
}
|
|
else if ( !isdefined( winningTeam ) || ( winningTeam == "tie" ) )
|
|
{
|
|
updateTeamScoreByRoundsWon();
|
|
return "tie";
|
|
}
|
|
|
|
if ( level.scoreRoundWinBased )
|
|
{
|
|
foreach( team in level.teams )
|
|
{
|
|
score = game["roundswon"][team];
|
|
if ( team === winningTeam )
|
|
{
|
|
score++;
|
|
}
|
|
[[level._setTeamScore]]( team, score );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( isdefined( game["ball_overtime_score_to_beat"] ) && ( game["ball_overtime_score_to_beat"] > game["ball_overtime_best_score"] ) )
|
|
{
|
|
added_score = game["ball_overtime_score_to_beat"];
|
|
}
|
|
else
|
|
{
|
|
added_score = game["ball_overtime_best_score"];
|
|
}
|
|
|
|
foreach( team in level.teams )
|
|
{
|
|
score = game["ball_game_score"][team];
|
|
if ( team === winningTeam )
|
|
{
|
|
score += added_score;
|
|
}
|
|
[[level._setTeamScore]]( team, score );
|
|
}
|
|
}
|
|
return winningTeam;
|
|
}
|
|
|
|
if ( level.scoreRoundWinBased )
|
|
{
|
|
updateTeamScoreByRoundsWon();
|
|
|
|
winner = globallogic::determineTeamWinnerByGameStat( "roundswon" );
|
|
}
|
|
else
|
|
{
|
|
winner = globallogic::determineTeamWinnerByTeamScore();
|
|
}
|
|
|
|
return winner;
|
|
}
|
|
|
|
|
|
function setMatchScoreHUDElemForTeam()
|
|
{
|
|
self setText( &"" );
|
|
}
|
|
|
|
function shouldPlayOvertimeRound()
|
|
{
|
|
if ( isdefined( game["overtime_round"] ) )
|
|
{
|
|
if ( game["overtime_round"] == 1 || !level.gameEnded ) // If we've only played 1 round or we're in the middle of the 2nd keep going
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( !level.scoreRoundWinBased )
|
|
{
|
|
// Only go to overtime if both teams are tied and it's either the last round or both teams are one away from winning
|
|
if ( ( game["teamScores"]["allies"] == game["teamScores"]["axis"] ) &&
|
|
( util::hitRoundLimit() || ( game["teamScores"]["allies"] == level.scoreLimit-1 ) ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only go to overtime if both teams are one round away from winning
|
|
alliesRoundsWon = util::getRoundsWon( "allies" );
|
|
axisRoundsWon = util::getRoundsWon( "axis" );
|
|
if ( ( level.roundWinLimit > 0 ) && ( axisRoundsWon == level.roundWinLimit-1 ) && ( alliesRoundsWon == level.roundWinLimit-1 ) )
|
|
{
|
|
return true;
|
|
}
|
|
if ( util::hitRoundLimit() && ( alliesRoundsWon == axisRoundsWon ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function ball_getTeamKillPenalty( eInflictor, attacker, sMeansOfDeath, weapon )
|
|
{
|
|
teamkill_penalty = globallogic_defaults::default_getTeamKillPenalty( eInflictor, attacker, sMeansOfDeath, weapon );
|
|
|
|
if ( ( isdefined( self.isBallCarrier ) && self.isBallCarrier ) )
|
|
{
|
|
teamkill_penalty = teamkill_penalty * level.teamKillPenaltyMultiplier;
|
|
}
|
|
|
|
return teamkill_penalty;
|
|
}
|
|
|
|
function ball_getTeamKillScore( eInflictor, attacker, sMeansOfDeath, weapon )
|
|
{
|
|
teamkill_score = rank::getScoreInfoValue( "kill" );
|
|
|
|
if ( ( isdefined( self.isBallCarrier ) && self.isBallCarrier ) )
|
|
{
|
|
teamkill_score = teamkill_score * level.teamKillScoreMultiplier;
|
|
}
|
|
|
|
return int(teamkill_score);
|
|
}
|
|
|
|
//////////////////////
|
|
|
|
function get_real_ball_location( startPos, startAngles, index, count, defaultDistance, rotation )
|
|
{
|
|
currentAngle = startAngles[1] + ( (( 360 / count ) * 0.5) + (( 360 / count ) * index) );
|
|
cosCurrent = cos( currentAngle + rotation );
|
|
sinCurrent = sin( currentAngle + rotation );
|
|
|
|
new_position = startPos + ( defaultDistance * cosCurrent, defaultDistance * sinCurrent, 0 );
|
|
|
|
clip_mask = (1 << 0) | (1 << 3);
|
|
trace = PhysicsTrace( startPos, new_position, (-5, -5, -5), (5, 5, 5), self, clip_mask );
|
|
return trace[ "position" ];
|
|
}
|
|
|
|
function setup_objectives()
|
|
{
|
|
level.ball_goals = [];
|
|
level.ball_starts = [];
|
|
level.balls = [];
|
|
|
|
level.ball_starts = getEntArray( "ball_start" ,"targetname");
|
|
|
|
foreach( ball_start in level.ball_starts )
|
|
{
|
|
level.balls[level.balls.size] = spawn_ball( ball_start );
|
|
}
|
|
|
|
if ( level.ballCount > level.ball_starts.size )
|
|
{
|
|
width = 48;
|
|
height = 48;
|
|
count = level.ballCount - level.ball_starts.size;
|
|
|
|
for ( index = 0; index < count; index++ )
|
|
{
|
|
position = get_real_ball_location( level.ball_starts[0].origin, level.ball_starts[0].angles, index, count, width, 0 );
|
|
trigger = spawn( "trigger_radius", position, 0, width, height );
|
|
level.ball_starts[level.ball_starts.size] = trigger;
|
|
level.balls[level.balls.size] = spawn_ball( trigger );
|
|
}
|
|
}
|
|
|
|
foreach( team in level.teams )
|
|
{
|
|
if( !game["switchedsides"] )
|
|
{
|
|
trigger = GetEnt( "ball_goal_" + team, "targetname" );
|
|
}
|
|
else
|
|
{
|
|
trigger = GetEnt( "ball_goal_" + util::getOtherTeam( team ), "targetname" );
|
|
}
|
|
|
|
level.ball_goals[team] = setup_goal( trigger, team );
|
|
}
|
|
}
|
|
|
|
// Goals
|
|
//========================================
|
|
|
|
function setup_goal( trigger, team )
|
|
{
|
|
// Goal Object
|
|
useObj = gameobjects::create_use_object( team, trigger, [], ( 0, 0, trigger.height * 0.5 ), istring("ball_goal_"+team) );
|
|
useObj gameobjects::set_visible_team( "any" );
|
|
useObj gameobjects::set_model_visibility( true );
|
|
useObj gameobjects::allow_use( "enemy" );
|
|
useObj gameobjects::set_use_time( 0 );
|
|
|
|
foreach(ball in level.balls)
|
|
{
|
|
useObj gameobjects::set_key_object( ball );
|
|
}
|
|
|
|
useObj.canUseObj = &can_use_goal;
|
|
useObj.onUse = &on_use_goal;
|
|
|
|
useObj.ball_in_goal = false;
|
|
useObj.radiusSq = trigger.radius * trigger.radius;
|
|
useObj.center = trigger.origin + ( 0, 0, trigger.height * 0.5 );
|
|
|
|
// TODO Killcam
|
|
//useObj.killcamEnt = spawn( "script_model", goal.origin );
|
|
|
|
return useObj;
|
|
}
|
|
|
|
function can_use_goal( player )
|
|
{
|
|
return !self.ball_in_goal;
|
|
}
|
|
|
|
function on_use_goal(player)
|
|
{
|
|
if ( !IsDefined(player) || !IsDefined(player.carryObject) )
|
|
return;
|
|
|
|
if ( isDefined( player.carryObject.scoreFrozenUntil ) && player.carryObject.scoreFrozenUntil > getTime() )
|
|
return;
|
|
|
|
self play_goal_score_fx();
|
|
|
|
player.carryObject.scoreFrozenUntil = getTime() + 10000;
|
|
|
|
//player maps\mp\_events::touchdownEvent(score); TODO Score event
|
|
ball_check_assist( player, true );
|
|
|
|
team = self.team;
|
|
otherTeam = util::getOtherTeam( team );
|
|
|
|
// TODO: Ball ID
|
|
globallogic_audio::flush_objective_dialog( "uplink_ball" );
|
|
globallogic_audio::leader_dialog( "uplWeUplink", otherTeam );
|
|
globallogic_audio::leader_dialog( "uplTheyUplink", team );
|
|
|
|
globallogic_audio::play_2d_on_team( "mpl_ballcapture_sting_friend", otherTeam );
|
|
globallogic_audio::play_2d_on_team( "mpl_ballcapture_sting_enemy", team );
|
|
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_CAPTURE", player, team );
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_CAPTURE", player, otherTeam );
|
|
|
|
if( should_record_final_score_cam( otherTeam, level.carryScore ) )
|
|
{
|
|
// killcamentity = self.goal.killcamEnt;
|
|
// killcamentityindex = killcamentity GetEntityNumber();
|
|
// killcamentitystarttime = killcamentity.birthtime;
|
|
// if ( !IsDefined( killcamentitystarttime ) )
|
|
// {
|
|
// killcamentitystarttime = 0;
|
|
// }
|
|
// player.deathTime = GetTime();
|
|
//maps\mp\gametypes\_damage::recordFinalKillCam( 5.0, player, player, player GetEntityNumber(), killcamentityindex, killcamentitystarttime, "none", 0, 0, undefined, "score" ); TODO Killcam
|
|
}
|
|
|
|
//ball_play_score_fx(self.goal); -- TODO FX
|
|
|
|
if(IsDefined(player.shoot_charge_bar))
|
|
{
|
|
player.shoot_charge_bar.inUse = false;
|
|
}
|
|
|
|
ball = player.carryObject;
|
|
ball.lastCarrierScored = true;
|
|
|
|
player gameobjects::take_carry_weapon( ball.carryWeapon );
|
|
ball ball_set_dropped( true );
|
|
ball thread upload_ball( self );
|
|
|
|
if( isdefined(player.pers["carries"]) )
|
|
{
|
|
player.pers["carries"]++;
|
|
player.carries = player.pers["carries"];
|
|
}
|
|
|
|
bbPrint( "mpobjective", "gametime %d objtype %s team %s playerx %d playery %d playerz %d", gettime(), "ball_capture", team, player.origin );
|
|
player RecordGameEvent("capture");
|
|
|
|
player challenges::capturedObjective( gettime(), self.trigger );
|
|
player AddPlayerStatWithGameType( "CARRIES", 1 );
|
|
player AddPlayerStatWithGameType( "captures", 1 ); // counts towards Destroyer challenge
|
|
scoreevents::processScoreEvent( "ball_capture_carry", player );
|
|
ball_give_score( otherTeam, level.carryScore );
|
|
}
|
|
|
|
// Balls
|
|
//========================================
|
|
|
|
function spawn_ball( trigger )
|
|
{
|
|
visuals = [];
|
|
visuals[0] = spawn("script_model", trigger.origin );
|
|
visuals[0] SetModel( "wpn_t7_uplink_ball_world" );
|
|
visuals[0] notsolid();
|
|
|
|
trigger EnableLinkTo();
|
|
trigger LinkTo( visuals[0] );
|
|
trigger.no_moving_platfrom_unlink = true;
|
|
|
|
ballObj = gameobjects::create_carry_object( "neutral", trigger, visuals, (0,0,0), istring("ball_ball"), "mpl_hit_alert_ballholder" );
|
|
ballObj gameobjects::allow_carry( "any" );
|
|
ballObj gameobjects::set_visible_team( "any" );
|
|
ballObj gameobjects::set_drop_offset( 8 ); // the radius of the ball model so it looks like its on the ground
|
|
|
|
ballObj.objectiveOnVisuals = true;
|
|
ballObj.allowWeapons = false;
|
|
ballObj.carryWeapon = GetWeapon( "ball" );
|
|
ballObj.keepCarryWeapon = true;
|
|
ballObj.waterBadTrigger = false;
|
|
ballObj.disallowRemoteControl = true;
|
|
ballObj.disallowPlaceablePickup = true;
|
|
//ballObj.requiresLOS = true; I don't think this does anything
|
|
|
|
ballObj gameobjects::update_objective();
|
|
|
|
ballObj.canUseObject = &can_use_ball;
|
|
ballObj.onPickup = &on_pickup_ball;
|
|
ballObj.setDropped = &ball_set_dropped;
|
|
ballObj.onReset = &on_reset_ball;
|
|
ballObj.pickupTimeoutOverride = &ball_physics_timeout;
|
|
ballObj.carryWeaponThink = &carry_think_ball;
|
|
|
|
ballObj.in_goal = false;
|
|
ballObj.lastCarrierScored = false;
|
|
ballObj.lastCarrierTeam = "neutral";
|
|
|
|
if ( level.enemyCarrierVisible == 2 )
|
|
{
|
|
ballObj.objIDPingFriendly = true;
|
|
}
|
|
|
|
if ( level.idleFlagReturnTime > 0 )
|
|
{
|
|
ballObj.autoResetTime = level.idleFlagReturnTime;
|
|
}
|
|
else
|
|
{
|
|
ballObj.autoResetTime = undefined;
|
|
}
|
|
|
|
PlayFXOnTag( "ui/fx_uplink_ball_trail", ballObj.visuals[0], "tag_origin" );
|
|
|
|
return ballObj;
|
|
}
|
|
|
|
// Ball Events
|
|
//========================================
|
|
|
|
function can_use_ball(player)
|
|
{
|
|
if(!isDefined(player))
|
|
return false;
|
|
|
|
if ( !self gameobjects::can_interact_with( player ) )
|
|
return false;
|
|
|
|
if ( IsDefined(self.dropTime) && self.dropTime >= GetTime() )
|
|
return false;
|
|
|
|
//if ( player IsMeleeing() )
|
|
//return false;
|
|
|
|
if( isdefined( player.resurrect_weapon ) && ( player getcurrentweapon() == player.resurrect_weapon ) )
|
|
return false;
|
|
|
|
if ( player isCarryingTurret() )
|
|
return false;
|
|
|
|
currentWeapon = player GetCurrentWeapon();
|
|
if(isDefined(currentWeapon))
|
|
{
|
|
if( !valid_ball_pickup_weapon( currentWeapon ) )
|
|
return false;
|
|
}
|
|
|
|
nextWeapon = player.changingWeapon;
|
|
if(IsDefined(nextWeapon) && player IsSwitchingWeapons() )
|
|
{
|
|
if( !valid_ball_pickup_weapon( nextWeapon ) )
|
|
return false;
|
|
}
|
|
|
|
if ( player player_no_pickup_time() )
|
|
return false;
|
|
|
|
ball = self.visuals[0];
|
|
thresh = 15;
|
|
dist2 = Distance2DSquared( ball.origin, player.origin );
|
|
if( dist2 < thresh * thresh )
|
|
return true;
|
|
|
|
start = player getEye();
|
|
|
|
end = ( self.curorigin[0], self.curorigin[1], self.curorigin[2] + 5 );
|
|
if ( isdefined( ball ) )
|
|
{
|
|
end = ( ball.origin[0], ball.origin[1], ball.origin[2] + 5 );
|
|
}
|
|
|
|
if ( isdefined( self.carrier ) && isPlayer( self.carrier ) )
|
|
{
|
|
end = self.carrier getEye();
|
|
}
|
|
|
|
// in the case of a player to player pass
|
|
// if the ball hits the intended pass recipient the passed ball gets deleted and
|
|
// a new projectile gets launched to handle the bounce. This all happens before the
|
|
// gameobject trigger fires and tests the pickup. Ideally the bounce would never happen
|
|
// but its a timing issue with the engine and order of notify processing. Unfortunatly
|
|
// the old grenade might not be fully deleted when we do this test to see if the player
|
|
// can pick it up. So both the new missile and the old can block the test below.
|
|
first_skip_ent = ball;
|
|
second_skip_ent = ball;
|
|
if ( isdefined( self.projectile ) )
|
|
{
|
|
first_skip_ent = self.projectile;
|
|
}
|
|
if ( isdefined( self.lastProjectile ) )
|
|
{
|
|
second_skip_ent = self.lastProjectile;
|
|
}
|
|
|
|
if ( !BulletTracePassed( end, start, false, first_skip_ent, second_skip_ent, false, false ) )
|
|
{
|
|
player_origin = (player.origin[0], player.origin[1], player.origin[2] + 10 );
|
|
if ( !BulletTracePassed( end, player_origin, false, first_skip_ent, second_skip_ent, false, false ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function chief_mammal_reset()
|
|
{
|
|
self.isResetting = true;
|
|
|
|
self notify ( "reset" );
|
|
|
|
origin = self.curOrigin;
|
|
if( isdefined( self.projectile ) )
|
|
origin = self.projectile.origin;
|
|
|
|
foreach( visual in self.visuals )
|
|
{
|
|
visual.origin = origin;
|
|
visual.angles = visual.baseAngles;
|
|
visual DontInterpolate();
|
|
visual show();
|
|
}
|
|
|
|
if( isdefined( self.projectile ) )
|
|
{
|
|
self.projectile Delete();
|
|
self.lastProjectile = undefined;
|
|
}
|
|
|
|
self gameobjects::clear_carrier();
|
|
gameobjects::update_world_icons();
|
|
gameobjects::update_compass_icons();
|
|
gameobjects::update_objective();
|
|
|
|
self.isResetting = false;
|
|
}
|
|
|
|
function on_pickup_ball( player )
|
|
{
|
|
self gameobjects::set_flags( 0 );
|
|
|
|
if( !isalive( player ) )
|
|
{
|
|
self chief_mammal_reset();
|
|
return;
|
|
}
|
|
|
|
player DisableUsability();
|
|
player DisableOffhandWeapons();
|
|
|
|
level.useStartSpawns = false;
|
|
|
|
level clientfield::set( "ball_away", 1 );
|
|
|
|
//Physics objects get linked to entities if they come to rest on them
|
|
linkedParent = self.visuals[0] GetLinkedEnt();
|
|
if(IsDefined(linkedParent))
|
|
self.visuals[0] unlink();
|
|
|
|
player resetflashback();
|
|
//self.current_start.in_use = false;
|
|
|
|
pass = false;
|
|
ball_velocity = 0.0;
|
|
if(IsDefined(self.projectile))
|
|
{
|
|
pass = true;
|
|
ball_velocity = self.projectile GetVelocity();
|
|
self.projectile Delete();
|
|
self.lastProjectile = undefined;
|
|
}
|
|
|
|
if( pass )
|
|
{
|
|
if( self.lastCarrierTeam == player.team )
|
|
{
|
|
if ( self.lastCarrier != player )
|
|
{
|
|
player.passTime = GetTime();
|
|
player.passPlayer = self.lastcarrier;
|
|
|
|
// TODO: Add ball # to objectiveId
|
|
globallogic_audio::leader_dialog( "uplTransferred", player.team, undefined, "uplink_ball" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( Length( ball_velocity ) > 0.1 )
|
|
{
|
|
scoreevents::processScoreEvent( "ball_intercept", player );
|
|
}
|
|
}
|
|
}
|
|
|
|
otherTeam = util::getOtherTeam( player.team );
|
|
|
|
if( self.lastCarrierTeam != player.team )
|
|
{
|
|
// TODO: Add ball # to objectiveId
|
|
globallogic_audio::leader_dialog( "uplWeTake", player.team, undefined, "uplink_ball" );
|
|
globallogic_audio::leader_dialog( "uplTheyTake", otherTeam, undefined, "uplink_ball" );
|
|
}
|
|
|
|
globallogic_audio::play_2d_on_team( "mpl_ballget_sting_friend", player.team );
|
|
globallogic_audio::play_2d_on_team( "mpl_ballget_sting_enemy", otherTeam );
|
|
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_PICKED_UP", player, player.team );
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_PICKED_UP", player, otherTeam );
|
|
|
|
//self ball_fx_stop(); -- TODO FX
|
|
|
|
self.lastCarrierScored = false;
|
|
self.lastcarrier = player;
|
|
self.lastCarrierTeam = player.team;
|
|
|
|
self gameobjects::set_owner_team( player.team );
|
|
|
|
//player GiveWeapon( GetWeapon( CONST_BALL_WEAPON ));
|
|
|
|
player.ballDropDelay = GetDvarInt( "scr_ball_water_drop_delay", 10 ); //server frames to wait while underwater before dropping the ball
|
|
player.objective = 1;
|
|
|
|
player.hasPerkSprintFire = player HasPerk("specialty_sprintfire" );
|
|
player setPerk("specialty_sprintfire" );
|
|
|
|
player clientfield::set( "ballcarrier", 1 );
|
|
|
|
if ( level.carryArmor > 0 )
|
|
player thread armor::setLightArmor(level.carryArmor);
|
|
else
|
|
player thread armor::unsetLightArmor();
|
|
|
|
player thread player_update_pass_target(self);
|
|
|
|
player RecordGameEvent("pickup");
|
|
}
|
|
|
|
function ball_carrier_cleanup( )
|
|
{
|
|
self gameobjects::set_owner_team( "neutral" );
|
|
|
|
if ( isdefined(self.carrier) )
|
|
{
|
|
self.carrier clientfield::set( "ballcarrier", 0 );
|
|
self.carrier.ballDropDelay = undefined;
|
|
self.carrier.noPickupTime = GetTime() + 500;
|
|
|
|
self.carrier player_clear_pass_target();
|
|
self.carrier notify("cancel_update_pass_target");
|
|
|
|
self.carrier thread armor::unsetLightArmor();
|
|
|
|
if ( !self.carrier.hasPerkSprintFire )
|
|
{
|
|
self.carrier unsetPerk( "specialty_sprintfire" );
|
|
}
|
|
|
|
self.carrier EnableUsability();
|
|
self.carrier EnableOffhandWeapons();
|
|
|
|
self.carrier SetBallPassAllowed(false);
|
|
self.carrier.objective = 0;
|
|
}
|
|
}
|
|
|
|
function ball_set_dropped( skip_physics )
|
|
{
|
|
if(!isdefined(skip_physics))skip_physics=false;
|
|
|
|
self.isResetting = true;
|
|
self.dropTime = GetTime();
|
|
|
|
self notify ( "dropped" );
|
|
|
|
dropAngles = (0,0,0);
|
|
|
|
carrier = self.carrier;
|
|
if(IsDefined(carrier) && carrier.team != "spectator")
|
|
{
|
|
dropOrigin = carrier.origin;
|
|
dropAngles = carrier.angles;
|
|
}
|
|
else
|
|
{
|
|
dropOrigin = self.origin;
|
|
}
|
|
|
|
if( !isdefined( dropOrigin ) )
|
|
dropOrigin = self.safeorigin;
|
|
|
|
dropOrigin += ( 0, 0, 40 );
|
|
|
|
if( isdefined( self.projectile ) )
|
|
self.projectile Delete();
|
|
|
|
self ball_carrier_cleanup();
|
|
self gameobjects::clear_carrier();
|
|
self gameobjects::set_position( dropOrigin, dropAngles );
|
|
self gameobjects::update_icons_and_objective();
|
|
self thread gameobjects::pickup_timeout( dropOrigin[2], dropOrigin[2] - 40);
|
|
|
|
self.isResetting = false;
|
|
|
|
if(!skip_physics)
|
|
{
|
|
angles = ( 0, dropAngles[1], 0 );
|
|
forward = AnglesToForward( angles );
|
|
velocity = forward * 200 + (0,0,80);
|
|
ball_physics_launch(velocity);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function on_reset_ball( prev_origin )
|
|
{
|
|
if ( ( isdefined( level.gameEnded ) && level.gameEnded ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//self ball_assign_random_start();
|
|
visual = self.visuals[0];
|
|
|
|
//Physics objects get linked to entities if they come to rest on them
|
|
linkedParent = visual GetLinkedEnt();
|
|
if( IsDefined(linkedParent) )
|
|
{
|
|
visual unlink();
|
|
}
|
|
|
|
if( IsDefined( self.projectile ) )
|
|
{
|
|
self.projectile Delete();
|
|
}
|
|
|
|
if( !self gameobjects::get_flags( 1 ) )
|
|
{
|
|
PlayFx( "ui/fx_uplink_ball_vanish", prev_origin );
|
|
self play_return_vo();
|
|
}
|
|
self.lastCarrierTeam = "none";
|
|
self thread download_ball();
|
|
|
|
if ( isdefined( self.killcamEnt ) && isEntity( self.killcamEnt ) )
|
|
{
|
|
self.killcamEnt RecordGameEventNonPlayer( "ball_reset" );
|
|
}
|
|
}
|
|
|
|
// Ball Functions
|
|
//========================================
|
|
|
|
function reset_ball()
|
|
{
|
|
self thread gameobjects::return_home();
|
|
}
|
|
|
|
function upload_ball( goal )
|
|
{
|
|
self notify( "score_event" );
|
|
|
|
self.in_goal = true;
|
|
goal.ball_in_goal = true;
|
|
|
|
if(IsDefined(self.projectile))
|
|
{
|
|
self.projectile Delete();
|
|
}
|
|
|
|
self gameobjects::allow_carry( "none" );
|
|
|
|
move_to_center_time = .4;
|
|
move_up_time = 1.2;
|
|
rotate_time = 1.0;
|
|
|
|
in_enemyGoal_time = move_to_center_time + rotate_time;
|
|
total_time = in_enemyGoal_time + move_up_time;
|
|
|
|
self gameobjects::set_flags( 1 );
|
|
|
|
visual = self.visuals[0];
|
|
|
|
visual MoveTo( goal.center, move_to_center_time, 0, move_to_center_time);
|
|
visual RotateVelocity( (1080,1080,0), total_time, total_time, 0);
|
|
|
|
wait in_enemyGoal_time;
|
|
|
|
goal.ball_in_goal = false;
|
|
|
|
self.visibleTeam = "neutral";
|
|
self gameobjects::update_world_icon( "friendly", false );
|
|
self gameobjects::update_world_icon( "enemy", false );
|
|
self gameobjects::update_objective();
|
|
|
|
visual MoveZ(4000, move_up_time, move_up_time*.1, 0);
|
|
|
|
wait move_up_time;
|
|
|
|
self thread gameobjects::return_home();
|
|
}
|
|
|
|
function download_ball()
|
|
{
|
|
self endon ( "pickup_object" );
|
|
|
|
self gameobjects::allow_carry( "any" );
|
|
self gameobjects::set_owner_team( "neutral" );
|
|
self gameobjects::set_flags( 2 );
|
|
|
|
visual = self.visuals[0];
|
|
|
|
visual.origin = visual.baseOrigin + (0,0,4000);
|
|
visual DontInterpolate();
|
|
|
|
fall_time = 3;
|
|
visual MoveTo( visual.baseOrigin, fall_time, 0, fall_time);
|
|
|
|
visual RotateVelocity( (0,720,0), fall_time, 0, fall_time);
|
|
|
|
self.visibleTeam = "any";
|
|
self gameobjects::update_world_icon( "friendly", true );
|
|
self gameobjects::update_world_icon( "enemy", true );
|
|
self gameobjects::update_objective();
|
|
|
|
wait( fall_time );
|
|
|
|
self gameobjects::set_flags( 0 );
|
|
level clientfield::set( "ball_away", 0 );
|
|
|
|
//PlayFX( level._effect["ball_download_end"], self.current_start.ground_origin ); - TODO FX
|
|
|
|
PlayFXOnTag( "ui/fx_uplink_ball_trail", visual, "tag_origin" );
|
|
|
|
self thread ball_download_fx(visual, fall_time);
|
|
|
|
self.in_goal = false;
|
|
}
|
|
|
|
|
|
// Ball Carry Watch
|
|
//========================================
|
|
|
|
function carry_think_ball()
|
|
{
|
|
self endon("disconnect");
|
|
|
|
self thread ball_pass_watch();
|
|
self thread ball_shoot_watch();
|
|
self thread ball_weapon_change_watch(); // change to hero weapons is allowed
|
|
}
|
|
|
|
function ball_pass_watch()
|
|
{
|
|
level endon ( "game_ended" );
|
|
|
|
self endon ( "disconnect" );
|
|
self endon ( "death" );
|
|
self endon ( "drop_object" );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "ball_pass", weapon );
|
|
|
|
if( !isDefined(self.pass_target) )
|
|
{
|
|
// self IPrintLnBold( "No Pass Target" );
|
|
playerAngles = self GetPlayerAngles();
|
|
playerAngles = ( math::Clamp( playerAngles[0], -85, 85 ), playerAngles[1], playerAngles[2] );
|
|
dir = AnglesToForward( playerAngles );
|
|
force = 90;
|
|
self.carryObject thread ball_physics_launch_drop( dir * force, self );
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if( isDefined( self.carryObject ) )
|
|
{
|
|
self thread ball_pass_or_throw_active();
|
|
pass_target = self.pass_target;
|
|
last_target_origin = self.pass_target.origin;
|
|
wait .15;
|
|
|
|
if( isdefined( self.pass_target ) )
|
|
{ // pass the ball
|
|
pass_target = self.pass_target;
|
|
self.carryObject thread ball_pass_projectile( self, pass_target, last_target_origin );
|
|
}
|
|
else
|
|
{ // drop the ball
|
|
playerAngles = self GetPlayerAngles();
|
|
playerAngles = ( math::Clamp( playerAngles[0], -85, 85 ), playerAngles[1], playerAngles[2] );
|
|
dir = AnglesToForward( playerAngles );
|
|
force = 90;
|
|
self.carryObject thread ball_physics_launch_drop( dir * force, self );
|
|
}
|
|
}
|
|
}
|
|
|
|
function ball_shoot_watch()
|
|
{
|
|
level endon ( "game_ended" );
|
|
|
|
self endon ( "disconnect" );
|
|
self endon ( "death" );
|
|
self endon ( "drop_object" );
|
|
|
|
extra_pitch = GetDvarFloat("scr_ball_shoot_extra_pitch", 0);
|
|
force = GetDvarFloat("scr_ball_shoot_force", 900 );
|
|
|
|
while(1)
|
|
{
|
|
self waittill("weapon_fired", weapon);
|
|
|
|
if( weapon != GetWeapon( "ball" ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( IsDefined( self.carryObject ) )
|
|
{
|
|
playerAngles = self GetPlayerAngles();
|
|
playerAngles += (extra_pitch,0,0);
|
|
playerAngles = (math::Clamp(playerAngles[0], -85, 85), playerAngles[1], playerAngles[2]);
|
|
dir = AnglesToForward(playerAngles);
|
|
self thread ball_pass_or_throw_active();
|
|
self thread ball_check_pass_kill_pickup( self.carryObject );
|
|
self.carryObject ball_create_killcam_ent();
|
|
self.carryObject thread ball_physics_launch_drop(dir*force, self, true );
|
|
}
|
|
}
|
|
|
|
function ball_weapon_change_watch()
|
|
{
|
|
level endon ( "game_ended" );
|
|
|
|
self endon ( "disconnect" );
|
|
self endon ( "death" );
|
|
self endon ( "drop_object" );
|
|
|
|
ballWeapon = GetWeapon( "ball" );
|
|
while( 1 )
|
|
{
|
|
if( ballWeapon == self GetCurrentWeapon() )
|
|
break;
|
|
self waittill ( "weapon_change" );
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill ( "weapon_change", weapon, lastWeapon );
|
|
if( isdefined( weapon ) && ( weapon.gadget_type == 14 ) )
|
|
break;
|
|
if( ( weapon === level.weaponNone ) && ( lastWeapon === ballWeapon ) ) // swtiching away from the ball for some reason - gravity spikes would be an example
|
|
break;
|
|
}
|
|
|
|
playerAngles = self GetPlayerAngles();
|
|
playerAngles = ( math::Clamp( playerAngles[0], -85, 85 ), AbsAngleClamp360( playerAngles[1] + 20 ), playerAngles[2] );
|
|
dir = AnglesToForward( playerAngles );
|
|
force = 90;
|
|
self.carryObject thread ball_physics_launch_drop( dir * force, self );
|
|
|
|
//thread gameobjects::gameObjects_dropped();
|
|
}
|
|
|
|
// Ball Pickup Helpers
|
|
//========================================
|
|
|
|
function valid_ball_pickup_weapon( weapon )
|
|
{
|
|
if( weapon == level.weaponNone )
|
|
return false;
|
|
|
|
if( weapon == GetWeapon( "ball" ) )
|
|
return false;
|
|
|
|
if( killstreaks::is_killstreak_weapon( weapon ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
function player_no_pickup_time()
|
|
{
|
|
return isDefined(self.noPickupTime) && self.noPickupTime > GetTime();
|
|
}
|
|
|
|
|
|
//self == player
|
|
function watchUnderwater( trigger )
|
|
{
|
|
self endon ("death" );
|
|
self endon ("disconnect" );
|
|
|
|
while( 1 )
|
|
{
|
|
if( self isplayerunderwater() )
|
|
{
|
|
foreach(ball in level.balls)
|
|
{
|
|
if( isDefined(ball.carrier) && ball.carrier == self )
|
|
{
|
|
ball gameobjects::set_dropped();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.ballDropDelay = undefined;
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function ball_physics_launch_drop( force, droppingPlayer, switchWeapon )
|
|
{
|
|
ball_set_dropped( true );
|
|
ball_physics_launch( force, droppingPlayer );
|
|
if( ( isdefined( switchWeapon ) && switchWeapon ) )
|
|
droppingPlayer killstreaks::switch_to_last_non_killstreak_weapon( undefined, true );
|
|
}
|
|
|
|
function ball_check_pass_kill_pickup( carryObj )
|
|
{
|
|
self endon("death");
|
|
self endon("disconnect");
|
|
|
|
carryObj endon("reset");
|
|
|
|
timer = spawnStruct();
|
|
timer endon("timer_done");
|
|
|
|
timer thread timer_run(1.5);
|
|
carryObj waittill("pickup_object");
|
|
timer timer_cancel();
|
|
|
|
if(!IsDefined(carryObj.carrier) || carryObj.carrier.team == self.team)
|
|
{
|
|
return;
|
|
}
|
|
|
|
carryObj.carrier endon("disconnect");
|
|
|
|
timer thread timer_run(5);
|
|
carryObj.carrier waittill("death", attacker);
|
|
timer timer_cancel();
|
|
|
|
if(!IsDefined(attacker) || attacker != self)
|
|
{
|
|
return;
|
|
}
|
|
|
|
timer thread timer_run(2);
|
|
carryObj waittill("pickup_object");
|
|
timer timer_cancel();
|
|
|
|
// if( IsDefined(carryObj.carrier) && carryObj.carrier == self) TODO Event
|
|
// self maps\mp\_events::passKillPickupEvent();
|
|
}
|
|
|
|
function timer_run(time)
|
|
{
|
|
self endon("cancel_timer");
|
|
wait time;
|
|
self notify("timer_done");
|
|
}
|
|
|
|
function timer_cancel()
|
|
{
|
|
self notify("cancel_timer");
|
|
}
|
|
|
|
function adjust_for_stance( ball )
|
|
{
|
|
target = self;
|
|
target endon("pass_end");
|
|
|
|
offs = 0;
|
|
while( isdefined( target ) && isdefined( ball ) )
|
|
{
|
|
newoffs = 50;
|
|
switch( target GetStance() )
|
|
{
|
|
case "crouch":
|
|
newoffs = 30;
|
|
break;
|
|
case "prone":
|
|
newoffs = 15;
|
|
break;
|
|
}
|
|
if( newoffs != offs )
|
|
{
|
|
ball ballsettarget( target, ( 0, 0, newoffs ) );
|
|
newoffs = offs;
|
|
}
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function ball_pass_projectile(passer, target, last_target_origin)
|
|
{
|
|
ball_set_dropped(true);
|
|
|
|
if(IsDefined(target))
|
|
{
|
|
last_target_origin = target.origin;
|
|
}
|
|
|
|
offset = ( 0, 0, 60 );
|
|
|
|
if ( target GetStance() == "prone" )
|
|
{
|
|
offset = ( 0, 0, 15 );
|
|
}
|
|
else if ( target GetStance() == "crouch" )
|
|
{
|
|
offset = ( 0, 0, 30 );
|
|
}
|
|
|
|
playerAngles = passer GetPlayerAngles();
|
|
playerAngles = (0, playerAngles[1], 0 );
|
|
dir = AnglesToForward(playerAngles);
|
|
|
|
delta = dir * 50;
|
|
origin = self.visuals[0].origin + delta;
|
|
|
|
size = 5;
|
|
trace = physicstrace( self.visuals[0].origin, origin, ( -size, -size, -size ), ( size, size, size ), passer, (1 << 0) );
|
|
|
|
if( trace["fraction"] < 1 )
|
|
{
|
|
t = 0.7 * trace["fraction"];
|
|
self gameobjects::set_position( self.visuals[0].origin + delta * t, self.visuals[0].angles );
|
|
}
|
|
else
|
|
{
|
|
self gameobjects::set_position( trace["position"], self.visuals[0].angles );
|
|
}
|
|
|
|
//self gameobjects::set_position( origin, playerAngles );
|
|
|
|
pass_dir = VectorNormalize((last_target_origin+offset) - self.visuals[0].origin);
|
|
//pass_dir = ( pass_dir[0], pass_dir[1], 0 );
|
|
pass_vel = pass_dir * 850;
|
|
|
|
self.lastProjectile = self.projectile;
|
|
self.projectile = passer MagicMissile( level.passingBallWeapon, self.visuals[0].origin, pass_vel );
|
|
|
|
target thread adjust_for_stance( self.projectile );
|
|
|
|
self.visuals[0] LinkTo(self.projectile);
|
|
self gameobjects::ghost_visuals();
|
|
//self ball_dont_interpolate(); TODO FX
|
|
|
|
self ball_create_killcam_ent();
|
|
|
|
self ball_clear_contents(); //Prevent magic grenade from hitting the ball visuals
|
|
|
|
self thread ball_on_projectile_hit_client(passer);
|
|
self thread ball_on_projectile_death();
|
|
self thread ball_watch_touch_enemy_goal();
|
|
|
|
passer killstreaks::switch_to_last_non_killstreak_weapon( undefined, true );
|
|
}
|
|
|
|
function ball_on_projectile_death()
|
|
{
|
|
self.projectile waittill("death");
|
|
ball = self.visuals[0];
|
|
if(!IsDefined(self.carrier) && !self.in_goal)
|
|
{
|
|
// There's a bug where the ball will be reset during the same frame that the above trigger is notified,
|
|
// but the notification is processed after the reset. This turns physics back on, overrides the MoveTo,
|
|
// and causes the ball to bounce really hard in an arbitrary direction.
|
|
// This hack is a way to test whether the ball was just reset or not. If it was just reset, don't fake bounce. TODO THIS IS FROM AW - WTF DO WE NEED THIS?
|
|
if ( ball.origin != ball.baseOrigin + (0, 0, 4000) )
|
|
{
|
|
self ball_physics_launch((0,0,10));
|
|
}
|
|
}
|
|
self ball_restore_contents();
|
|
|
|
ball notify("pass_end");
|
|
}
|
|
|
|
function ball_restore_contents()
|
|
{
|
|
if(IsDefined(self.visuals[0].old_contents))
|
|
{
|
|
self.visuals[0] SetContents(self.visuals[0].old_contents);
|
|
self.visuals[0].old_contents = undefined;
|
|
}
|
|
}
|
|
|
|
function ball_on_projectile_hit_client(passer)
|
|
{
|
|
self endon("pass_end");
|
|
self.projectile waittill( "projectile_impact_player", player );
|
|
self.trigger notify( "trigger", player );
|
|
self.projectile notify( "kill_ball_on_projectile_death");
|
|
|
|
if ( IsDefined( passer ) )
|
|
{
|
|
passer RecordGameEvent("pass");
|
|
}
|
|
}
|
|
|
|
function ball_clear_contents()
|
|
{
|
|
self.visuals[0].old_contents = self.visuals[0] SetContents(0);
|
|
}
|
|
|
|
function ball_create_killcam_ent()
|
|
{
|
|
if(IsDefined(self.killcamEnt))
|
|
self.killcamEnt Delete();
|
|
self.killcamEnt = spawn( "script_model", self.visuals[0].origin );
|
|
self.killcamEnt linkTo(self.visuals[0]);
|
|
self.killcamEnt SetContents(0);
|
|
//self.killcamEnt SetScriptMoverKillCam( "explosive" ); TODO KILLCAM
|
|
}
|
|
|
|
function ball_pass_or_throw_active()
|
|
{
|
|
self endon("death");
|
|
self endon("disconnect");
|
|
|
|
self.pass_or_throw_active = true;
|
|
|
|
self AllowMelee( false );
|
|
while( GetWeapon( "ball" ) == self GetCurrentWeapon() )
|
|
{
|
|
{wait(.05);};
|
|
}
|
|
|
|
self AllowMelee( true );
|
|
self.pass_or_throw_active = false;
|
|
}
|
|
|
|
function ball_download_fx(ball_model, waitTime)
|
|
{
|
|
// PlayFXOnTag( level._effect["ball_download"], ball_model, "tag_origin"); - TODO FX
|
|
//
|
|
// self waittill_notify_or_timeout("pickup_object", waitTime);
|
|
//
|
|
// StopFXOnTag( level._effect["ball_download"], ball_model, "tag_origin");
|
|
|
|
// bit of a hack, but this seems to be the most reliable place to put this...
|
|
self.scoreFrozenUntil = 0;
|
|
}
|
|
|
|
|
|
function ball_assign_random_start()
|
|
{
|
|
new_start = undefined;
|
|
|
|
rand_starts = array::randomize( level.ball_starts );
|
|
foreach(start in rand_starts)
|
|
{
|
|
if(start.in_use)
|
|
continue;
|
|
|
|
new_start = start;
|
|
break;
|
|
}
|
|
|
|
if(!isDefined(new_start))
|
|
return;
|
|
|
|
ball_assign_start(new_start);
|
|
}
|
|
|
|
function ball_assign_start(start)
|
|
{
|
|
foreach(vis in self.visuals)
|
|
{
|
|
vis.baseOrigin = start.origin;
|
|
}
|
|
|
|
self.trigger.baseOrigin = start.origin;
|
|
|
|
self.current_start = start;
|
|
start.in_use = true;
|
|
}
|
|
|
|
function ball_physics_launch(force, droppingPlayer)
|
|
{
|
|
visuals = self.visuals[0];
|
|
|
|
visuals.origin_prev = undefined;
|
|
|
|
origin = visuals.origin;
|
|
owner = visuals;
|
|
|
|
if( isDefined( droppingPlayer ) )
|
|
{
|
|
owner = droppingPlayer;
|
|
|
|
origin = droppingPlayer getweaponmuzzlepoint();
|
|
right = AnglesToRight( force );
|
|
origin = origin + ( right[0], right[1], 0 ) * 7;
|
|
startPos = origin;// + ( 0, 0, 20 );
|
|
delta = VectorNormalize(force) * 80;
|
|
|
|
///#sphere( startPos, 5, ( 0, 0, 1 ), 1, true, 10, 200 );#/
|
|
|
|
size = 5;
|
|
trace = physicstrace( startPos, startPos + delta, ( -size, -size, -size ), ( size, size, size ), droppingPlayer, (1 << 0) );
|
|
|
|
if( trace["fraction"] < 1 )
|
|
{
|
|
t = 0.7 * trace["fraction"];
|
|
self gameobjects::set_position( startPos + delta * t, visuals.angles );
|
|
}
|
|
else
|
|
{
|
|
self gameobjects::set_position( trace["position"], visuals.angles );
|
|
}
|
|
}
|
|
|
|
//self ball_fx_start(); -- TODO FX
|
|
|
|
grenade = owner MagicMissile( level.ballWorldWeapon, visuals.origin, force );
|
|
visuals linkto( grenade );
|
|
self gameobjects::ghost_visuals();
|
|
self.lastProjectile = self.projectile;
|
|
self.projectile = grenade;
|
|
|
|
///#sphere( visuals.origin, 5, ( 1, 0, 0 ), 1, true, 10, 200 );#/
|
|
visuals DontInterpolate(); // This triggers teleport to avoid interpolation from the previous position.
|
|
|
|
self thread ball_physics_out_of_level();
|
|
self thread ball_watch_touch_enemy_goal();
|
|
self thread ball_physics_touch_cant_pickup_player(droppingPlayer);
|
|
self thread ball_check_oob();
|
|
|
|
}
|
|
|
|
function ball_check_oob()
|
|
{
|
|
self endon ( "reset" );
|
|
self endon ( "pickup_object" );
|
|
|
|
visual = self.visuals[0];
|
|
|
|
while( 1 )
|
|
{
|
|
skip_oob_check = ( isdefined( self.in_goal ) && self.in_goal ) || ( isdefined( self.isResetting ) && self.isResetting );
|
|
if( !skip_oob_check )
|
|
{
|
|
if( visual oob::IsTouchingAnyOOBTrigger() || visual is_touching_any_ball_return_trigger() || self gameobjects::should_be_reset( visual.origin[2], visual.origin[2] + 10, true ) )
|
|
{
|
|
self reset_ball();
|
|
return;
|
|
}
|
|
}
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
|
|
function ball_physics_touch_cant_pickup_player(droppingPlayer)
|
|
{
|
|
self endon ( "reset" );
|
|
self endon ( "pickup_object" );
|
|
|
|
ball = self.visuals[0];
|
|
trigger = self.trigger;
|
|
|
|
while(1)
|
|
{
|
|
trigger waittill("trigger", player);
|
|
//Dont stop on the throwing player
|
|
if ( IsDefined(droppingPlayer) && droppingPlayer == player && player player_no_pickup_time() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( self.dropTime >= GetTime() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// There's a bug where the ball will be reset during the same frame that the above trigger is notified,
|
|
// but the notification is processed after the reset. This turns physics back on, overrides the MoveTo,
|
|
// and causes the ball to bounce really hard in an arbitrary direction.
|
|
// This hack is a way to test whether the ball was just reset or not. If it was just reset, don't fake bounce. -- TODO - WTF
|
|
if ( ball.origin == ball.baseOrigin + (0, 0, 4000) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if( !can_use_ball( player ) && ( self.dropTime + 200 < GetTime() ) )
|
|
// {
|
|
// //self thread ball_physics_fake_bounce();
|
|
// }
|
|
}
|
|
}
|
|
|
|
function ball_physics_fake_bounce()
|
|
{
|
|
ball = self.visuals[0]; //-- TODO Since we can use the physics, we need to kick the ball by doing another magic grenade, or apply new movement to existing projectile
|
|
// Test this existing projectile and then do stuff with it
|
|
vel = ball GetVelocity();
|
|
bounceForce = Length(vel)/10;
|
|
bounceDir = -1*VectorNormalize(vel);
|
|
|
|
//ball PhysicsStop();
|
|
//ball PhysicsLaunch(ball.origin, bounceDir*bounceForce);
|
|
}
|
|
|
|
|
|
function ball_watch_touch_enemy_goal( )
|
|
{
|
|
self endon ( "reset" );
|
|
self endon ( "pickup_object" );
|
|
|
|
enemyGoal = level.ball_goals[util::getotherteam( self.lastCarrierTeam )];
|
|
|
|
while(1)
|
|
{
|
|
if ( !enemyGoal can_use_goal() )
|
|
{
|
|
{wait(.05);};
|
|
continue;
|
|
}
|
|
|
|
ballVisual = self.visuals[0];
|
|
|
|
distSq = DistanceSquared( ballVisual.origin, enemyGoal.center );
|
|
if ( distSq <= enemyGoal.radiusSq )
|
|
{
|
|
self thread ball_touched_goal( enemyGoal );
|
|
return;
|
|
}
|
|
|
|
if ( isdefined( ballVisual.origin_prev ) )
|
|
{
|
|
result = line_intersect_sphere( ballVisual.origin_prev, ballVisual.origin, enemyGoal.center, enemyGoal.trigger.radius );
|
|
if ( result )
|
|
{
|
|
self thread ball_touched_goal( enemyGoal );
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
|
|
function line_intersect_sphere(line_start, line_end, sphere_center, sphere_radius)
|
|
{
|
|
dir = VectorNormalize(line_end - line_start);
|
|
|
|
a = VectorDot(dir,(line_start-sphere_center));
|
|
a*=a;
|
|
b = (line_start-sphere_center);
|
|
b *= b;
|
|
c = sphere_radius*sphere_radius;
|
|
|
|
return (a-b+c)>=0;
|
|
}
|
|
|
|
|
|
function ball_touched_goal(goal)
|
|
{
|
|
//ball_play_score_fx(goal); -- TODO FX
|
|
|
|
if ( isdefined( self.claimPlayer ) ) // We are about to give the ball to another player this frame
|
|
return;
|
|
|
|
if ( isDefined( self.scoreFrozenUntil ) && self.scoreFrozenUntil > getTime() )
|
|
return;
|
|
|
|
self gameobjects::allow_carry( "none" );
|
|
|
|
goal play_goal_score_fx();
|
|
|
|
self.scoreFrozenUntil = getTime() + 10000;
|
|
|
|
team = goal.team;
|
|
otherTeam = util::getOtherTeam( team );
|
|
|
|
// TODO: Ball ID
|
|
globallogic_audio::flush_objective_dialog( "uplink_ball" );
|
|
globallogic_audio::leader_dialog( "uplWeUplinkRemote", otherTeam );
|
|
globallogic_audio::leader_dialog( "uplTheyUplinkRemote", team );
|
|
|
|
globallogic_audio::play_2d_on_team( "mpl_ballcapture_sting_friend", otherTeam );
|
|
globallogic_audio::play_2d_on_team( "mpl_ballcapture_sting_enemy", team );
|
|
|
|
if ( isDefined(self.lastCarrier) )
|
|
{
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_CAPTURE", self.lastCarrier, team );
|
|
level thread popups::DisplayTeamMessageToTeam( &"MP_BALL_CAPTURE", self.lastCarrier, otherTeam );
|
|
|
|
if( isdefined(self.lastCarrier.pers["throws"]) )
|
|
{
|
|
self.lastCarrier.pers["throws"]++;
|
|
self.lastCarrier.throws = self.lastCarrier.pers["throws"];
|
|
}
|
|
|
|
bbPrint( "mpobjective", "gametime %d objtype %s team %s playerx %d playery %d playerz %d", gettime(), "ball_throw", team, self.lastCarrier.origin );
|
|
self.lastCarrier RecordGameEvent("throw");
|
|
|
|
self.lastCarrier AddPlayerStatWithGameType( "THROWS", 1 );
|
|
scoreevents::processScoreEvent( "ball_capture_throw", self.lastCarrier );
|
|
self.lastCarrierScored = true;
|
|
ball_check_assist( self.lastcarrier, false );
|
|
|
|
if(IsDefined(self.killcamEnt) && should_record_final_score_cam( otherTeam, level.throwScore) )
|
|
{
|
|
// killcamentity = self.killcamEnt;
|
|
// killcamentityindex = killcamentity GetEntityNumber();
|
|
// killcamentitystarttime = killcamentity.birthtime;
|
|
// if ( !IsDefined( killcamentitystarttime ) )
|
|
// killcamentitystarttime = 0;
|
|
// player = self.lastCarrier;
|
|
// goal.killcamEnt.deathTime = GetTime();
|
|
//maps\mp\gametypes\_damage::recordFinalKillCam( 5.0, goal.killcamEnt, player, player GetEntityNumber(), killcamentityindex, killcamentitystarttime, "none", 0, 0, undefined, "score" ); -- TODO killcam
|
|
}
|
|
|
|
self.lastCarrier challenges::capturedObjective( gettime(), self.trigger );
|
|
self.lastCarrier AddPlayerStatWithGameType( "CAPTURES", 1 );
|
|
}
|
|
|
|
if(IsDefined(self.killcamEnt))
|
|
{
|
|
self.killcamEnt Unlink();
|
|
}
|
|
|
|
self thread upload_ball( goal );
|
|
|
|
ball_give_score( otherTeam, level.throwScore );
|
|
}
|
|
|
|
function ball_give_score( team, score )
|
|
{
|
|
level globallogic_score::giveTeamScoreForObjective( team, score );
|
|
|
|
if ( isdefined( game["overtime_round"] ) )
|
|
{
|
|
if( game["overtime_round"] == 1 )
|
|
{
|
|
// level thread maps\mp\gametypes\_gamelogic::endGame( "overtime_halftime", game[ "end_reason" ][ "switching_sides" ] ); //-- TODO we have callback for endgame
|
|
}
|
|
else
|
|
{
|
|
if ( game["ball_overtime_first_winner"] === team )
|
|
{
|
|
thread globallogic::endGame( team, game["strings"]["score_limit_reached"] );
|
|
}
|
|
|
|
team_score = [[level._getTeamScore]]( team );
|
|
other_team_score = [[level._getTeamScore]]( util::getOtherTeam(team));
|
|
|
|
// if( team_score >= other_team_score)
|
|
// level thread maps\mp\gametypes\_gamelogic::endGame( toTeam, game[ "end_reason" ][ "score_limit_reached" ] ); //-- TODO we have callback for endgame
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function should_record_final_score_cam(team, score_to_add)
|
|
{
|
|
//Don't record kill cam if the team scoring would still be losing
|
|
team_score = [[level._getTeamScore]]( team );
|
|
other_team_score = [[level._getTeamScore]]( util::getOtherTeam(team));
|
|
|
|
return (team_score + score_to_add) >= other_team_score;
|
|
}
|
|
|
|
function ball_check_assist( player, wasDunk )
|
|
{
|
|
//Was the player passed to
|
|
if(!IsDefined(player.passTime) || !IsDefined(player.passPlayer))
|
|
return;
|
|
|
|
//Was it a recent pass
|
|
if(player.passTime+3000 < GetTime())
|
|
return;
|
|
|
|
scoreevents::processScoreEvent( "ball_capture_assist",player.passPlayer );
|
|
}
|
|
|
|
function watch_ball_physics_endons( projectile, timeout )
|
|
{
|
|
projectile endon( "stationary" );
|
|
|
|
ret = self util::waittill_any_timeout( timeout, "reset", "pickup_object", "score_event" );
|
|
if ( ret != "timeout" && isdefined( projectile ) )
|
|
{
|
|
projectile notify( "abort_ball_physics" );
|
|
}
|
|
}
|
|
|
|
function ball_physics_timeout( )
|
|
{
|
|
self endon( "reset" );
|
|
self endon( "pickup_object" );
|
|
self endon( "score_event" );
|
|
|
|
if ( isdefined( self.autoResetTime ) && self.autoResetTime > 15 )
|
|
{
|
|
physicsTime = self.autoResetTime;
|
|
}
|
|
else
|
|
{
|
|
physicsTime = 15;
|
|
}
|
|
|
|
if( isdefined( self.projectile ) )
|
|
{
|
|
self.projectile endon( "abort_ball_physics" );
|
|
self thread watch_ball_physics_endons( self.projectile, physicsTime );
|
|
timeoutReason = self.projectile util::waittill_any_timeout( physicsTime, "stationary", "abort_ball_physics" );
|
|
iF( !isdefined( timeoutReason ) )
|
|
return;
|
|
|
|
if ( timeoutReason == "stationary" )
|
|
{
|
|
if ( isdefined( self.autoResetTime ) )
|
|
{
|
|
wait self.autoResetTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
self reset_ball();
|
|
}
|
|
|
|
function ball_physics_out_of_level()
|
|
{
|
|
self endon ( "reset" );
|
|
self endon ( "pickup_object" );
|
|
|
|
ball = self.visuals[0];
|
|
|
|
self waittill ( "entity_oob" );
|
|
|
|
self reset_ball();
|
|
}
|
|
|
|
function player_update_pass_target(ballObj)
|
|
{
|
|
self notify( "update_pass_target" );
|
|
self endon( "update_pass_target" );
|
|
self endon("disconnect");
|
|
self endon("cancel_update_pass_target");
|
|
|
|
//self player_update_pass_target_hudoutline(); -- TODO outlines using the sitrep
|
|
//self childthread player_joined_update_pass_target_hudoutline();
|
|
|
|
test_dot = 0.8;
|
|
while(1)
|
|
{
|
|
new_target = undefined;
|
|
|
|
if ( !self IsOnLadder() )
|
|
{
|
|
playerDir = AnglesToForward( self GetPlayerAngles() );
|
|
playerEye = self GetEye();
|
|
|
|
possible_pass_targets = [];
|
|
foreach(target in level.players)
|
|
{
|
|
if ( self == target )
|
|
continue;
|
|
|
|
if ( target.team != self.team )
|
|
continue;
|
|
|
|
if ( !isAlive( target ) )
|
|
continue;
|
|
|
|
if( !ballObj can_use_ball(target) )
|
|
continue;
|
|
|
|
targetEye = target GetEye();
|
|
distSq = DistanceSquared( targetEye, playerEye );
|
|
if ( distSq > ( 1000 * 1000 ) )
|
|
continue;
|
|
|
|
dirToTarget = VectorNormalize( targetEye - playerEye );
|
|
dot = VectorDot( playerDir, dirToTarget );
|
|
if ( dot > test_dot )
|
|
{
|
|
target.pass_dot = dot;
|
|
target.pass_origin = targetEye;
|
|
possible_pass_targets[possible_pass_targets.size] = target;
|
|
}
|
|
}
|
|
|
|
//possible_pass_targets = ArraySort(possible_pass_targets, self.origin );
|
|
possible_pass_targets = array::quicksort( possible_pass_targets, &compare_player_pass_dot );
|
|
|
|
foreach(target in possible_pass_targets)
|
|
{
|
|
if ( SightTracePassed(playerEye, target.pass_origin, false, target ) )
|
|
{
|
|
new_target = target;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
self player_set_pass_target(new_target);
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function play_return_vo()
|
|
{
|
|
foreach( team in level.teams )
|
|
{
|
|
globallogic_audio::play_2d_on_team( "mpl_ballreturn_sting", team );
|
|
|
|
// TODO: Add ball # to objectiveId
|
|
globallogic_audio::leader_dialog( "uplReset", team, undefined, "uplink_ball" );
|
|
}
|
|
}
|
|
|
|
function compare_player_pass_dot(left, right)
|
|
{
|
|
return left.pass_dot>=right.pass_dot;
|
|
}
|
|
|
|
function player_set_pass_target(new_target)
|
|
{
|
|
//No Change
|
|
if ( IsDefined(self.pass_target) && IsDefined(new_target) && self.pass_target == new_target )
|
|
return;
|
|
|
|
if ( !IsDefined(self.pass_target) && !IsDefined(new_target) )
|
|
return;
|
|
|
|
self player_clear_pass_target();
|
|
|
|
if(IsDefined(new_target))
|
|
{
|
|
offset = ( 0, 0, 80 );
|
|
//self.pass_icon = new_target maps\mp\_entityheadIcons::setHeadIcon( self, "waypoint_ball_pass", offset, 10, 10, false, 0.05, false, true, false, false, "tag_origin" ); -- TODO HUD
|
|
|
|
new_target clientfield::set( "passoption", 1 );
|
|
self.pass_target = new_target;
|
|
|
|
team_players = [];
|
|
foreach(player in level.players)
|
|
{
|
|
if(player.team == self.team && player != self && player != new_target)
|
|
team_players[team_players.size] = player;
|
|
}
|
|
|
|
self SetBallPassAllowed(true);
|
|
}
|
|
|
|
//self player_update_pass_target_hudoutline(); -- TODO siterepscan outlines
|
|
}
|
|
|
|
function player_clear_pass_target()
|
|
{
|
|
if(IsDefined(self.pass_icon))
|
|
self.pass_icon Destroy();
|
|
|
|
team_players = [];
|
|
foreach(player in level.players)
|
|
{
|
|
if( player.team == self.team && player != self )
|
|
team_players[team_players.size] = player;
|
|
}
|
|
|
|
if( isDefined( self.pass_target ) )
|
|
{
|
|
self.pass_target clientfield::set( "passoption", 0 );
|
|
}
|
|
self.pass_target = undefined;
|
|
self SetBallPassAllowed(false);
|
|
|
|
//self player_update_pass_target_hudoutline(); -- TODO siterepscan outlines
|
|
}
|
|
|
|
|
|
function ball_create_start( minStartingBalls )
|
|
{
|
|
ball_starts = getEntArray( "ball_start" ,"targetname");
|
|
|
|
ball_starts = array::randomize( ball_starts );
|
|
foreach(new_start in ball_starts)
|
|
{
|
|
ballAddStart(new_start.origin);
|
|
}
|
|
|
|
//Add a default start if none exist
|
|
default_ball_height = 30;
|
|
if( ball_starts.size==0 )
|
|
{
|
|
origin = level.default_ball_origin;
|
|
if(!IsDefined(origin))
|
|
{
|
|
origin = (0,0,0);
|
|
}
|
|
|
|
ballAddStart(origin);
|
|
}
|
|
|
|
//Add extra default starts to support multi ball
|
|
add_num = minStartingBalls - level.ball_starts.size;
|
|
if( add_num <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
default_start = level.ball_starts[0].origin;
|
|
|
|
near_nodes = GetNodesInRadius(default_start, 200, 20, 50);
|
|
near_nodes = array::randomize(near_nodes);
|
|
|
|
for ( i = 0; i < add_num && i<near_nodes.size; i++ )
|
|
{
|
|
ballAddStart(near_nodes[i].origin);
|
|
}
|
|
}
|
|
|
|
function ballAddStart(origin)
|
|
{
|
|
ball_spawn_height = 30;
|
|
|
|
new_start = SpawnStruct();
|
|
new_start.origin = origin;
|
|
new_start ballFindGround();
|
|
new_start.origin = new_start.ground_origin + (0,0,ball_spawn_height);
|
|
new_start.in_use = false;
|
|
|
|
level.ball_starts[level.ball_starts.size] = new_start;
|
|
}
|
|
|
|
function ballFindGround(z_offset)
|
|
{
|
|
traceStart = self.origin + (0,0,32);
|
|
traceEnd = self.origin + (0,0,-1000);
|
|
trace = bulletTrace( traceStart, traceEnd, false, undefined );
|
|
|
|
self.ground_origin = trace["position"];
|
|
|
|
return trace["fraction"] != 0 && trace["fraction"] != 1;
|
|
}
|
|
|
|
function play_goal_score_fx( )
|
|
{
|
|
key = "ball_score_" + self.team;
|
|
level clientfield::set( key, !(level clientfield::get( key )) );
|
|
}
|
|
|
|
function is_touching_any_ball_return_trigger()
|
|
{
|
|
if ( !IsDefined( level.ball_return_trigger ) )
|
|
return false;
|
|
|
|
triggers_to_remove = [];
|
|
result = false;
|
|
|
|
foreach( trigger in level.ball_return_trigger )
|
|
{
|
|
if( !isdefined( trigger ) )
|
|
{
|
|
if ( !isdefined( triggers_to_remove ) ) triggers_to_remove = []; else if ( !IsArray( triggers_to_remove ) ) triggers_to_remove = array( triggers_to_remove ); triggers_to_remove[triggers_to_remove.size]=trigger;;
|
|
continue;
|
|
}
|
|
|
|
if( !trigger IsTriggerEnabled() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( self IsTouching( trigger ) )
|
|
{
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach( trigger in triggers_to_remove )
|
|
{
|
|
ArrayRemoveValue( level.ball_return_trigger, trigger );
|
|
}
|
|
|
|
triggers_to_remove = [];
|
|
triggers_to_remove = undefined;
|
|
|
|
return result;
|
|
}
|
|
|
|
// TODO THE FX FOR THIS MODE THAT ARE USED BY AW ARE BELOW, REMOVE AFTER WE HAVE OUR OWN FX REPLACEMENTS
|
|
|
|
//
|
|
//ball_get_path_dist(from_origin, to_origin)
|
|
//{
|
|
// if( maps\mp\gametypes\_spawnlogic::isPathDataAvailable() )
|
|
// {
|
|
// dist = GetPathDist( from_origin, to_origin, 999999 );
|
|
// if( IsDefined(dist) && (dist >= 0) )
|
|
// return dist;
|
|
// }
|
|
//
|
|
// // fail safe for bad pathing data
|
|
// return distance( from_origin, to_origin );
|
|
//}
|
|
//
|
|
|
|
|
|
//ball_goal_fx()
|
|
//{
|
|
// foreach(team, goal in level.ball_goals)
|
|
// {
|
|
// goal.score_fx["friendly"] = SpawnFx(getfx("ball_goal_activated_friendly"), goal.origin, (1,0,0));
|
|
// goal.score_fx["enemy"] = SpawnFx(getfx("ball_goal_activated_enemy"), goal.origin, (1,0,0));
|
|
// }
|
|
//
|
|
// level thread ball_play_fx_joined_team();
|
|
// foreach(player in level.players)
|
|
// {
|
|
// ball_goal_fx_for_player(player);
|
|
// }
|
|
//}
|
|
|
|
|
|
//
|
|
//ball_play_score_fx(goal)
|
|
//{
|
|
// //Update who can see what fx
|
|
// goal.score_fx["friendly"] Hide();
|
|
// goal.score_fx["enemy"] Hide();
|
|
//
|
|
// foreach(player in level.players)
|
|
// {
|
|
// team = ball_get_view_team(player);
|
|
//
|
|
// if( team == goal.team )
|
|
// {
|
|
// goal.score_fx["friendly"] ShowToPlayer(player);
|
|
// }
|
|
// else
|
|
// {
|
|
// goal.score_fx["enemy"] ShowToPlayer(player);
|
|
// }
|
|
// }
|
|
//
|
|
// TriggerFx(goal.score_fx["friendly"]);
|
|
// TriggerFx(goal.score_fx["enemy"]);
|
|
//}
|
|
//
|
|
//ball_score_sound(scoring_team)
|
|
//{
|
|
// ball_play_local_team_sound(scoring_team, "mp_obj_notify_pos_lrg", "mp_obj_notify_neg_lrg");
|
|
//}
|
|
//
|
|
//ball_play_local_team_sound(team, teamSound, otherTeamSound)
|
|
//{
|
|
// otherTeam = getOtherTeam(team);
|
|
// foreach(player in level.players)
|
|
// {
|
|
// if( player.team == team )
|
|
// player PlayLocalSound( teamSound );
|
|
// else if ( player.team == otherTeam )
|
|
// player PlayLocalSound( otherTeamSound );
|
|
// }
|
|
//}
|
|
//
|
|
|
|
|
|
//
|
|
//compare_script_index(left, right)
|
|
//{
|
|
// return left.script_index<=right.script_index;
|
|
//}
|
|
//
|
|
//ball_on_connect()
|
|
//{
|
|
// while ( true )
|
|
// {
|
|
// level waittill( "connected", player );
|
|
//
|
|
// player.ball_goal_fx = [];
|
|
//
|
|
// player thread player_on_disconnect();
|
|
// }
|
|
//}
|
|
//
|
|
//player_on_disconnect() // self == player
|
|
//{
|
|
// self waittill ("disconnect" );
|
|
//
|
|
// player_delete_ball_goal_fx();
|
|
//}
|
|
//
|
|
//ball_goal_fx_for_player(player)
|
|
//{
|
|
// viewTeam = ball_get_view_team(player);
|
|
//
|
|
// player player_delete_ball_goal_fx();
|
|
//
|
|
// foreach(team, goal in level.ball_goals)
|
|
// {
|
|
// fx_name = ter_op(team == viewTeam, "ball_goal_friendly", "ball_goal_enemy");
|
|
//
|
|
// fx = SpawnFXForClient( getfx(fx_name), goal.origin, player, (1,0,0));
|
|
// SetFXKillOnDelete( fx, true );
|
|
//
|
|
// player.ball_goal_fx[fx_name] = fx;
|
|
// TriggerFX( fx );
|
|
// }
|
|
//}
|
|
//
|
|
//ball_get_view_team(player)
|
|
//{
|
|
// viewTeam = player.team;
|
|
// if( viewTeam != "allies" && viewTeam != "axis")
|
|
// viewTeam = "allies";
|
|
//
|
|
// return viewTeam;
|
|
//}
|
|
//
|
|
//player_delete_ball_goal_fx()
|
|
//{
|
|
// foreach (effect in self.ball_goal_fx)
|
|
// {
|
|
// if ( IsDefined( effect ) )
|
|
// effect Delete();
|
|
// }
|
|
//}
|
|
//
|
|
//ball_play_fx_joined_team()
|
|
//{
|
|
// while(1)
|
|
// {
|
|
// level waittill ("joined_team", player );
|
|
// ball_goal_fx_for_player(player);
|
|
// }
|
|
//}
|
|
//
|
|
|
|
|
|
///// FX
|
|
|
|
|
|
//
|
|
//function WatchPhysics()
|
|
//{
|
|
// self endon("death");
|
|
//
|
|
// while(1)
|
|
// {
|
|
// self waittill("physics_impact", position, normal, velocity, surface);
|
|
//
|
|
// //Print("Impact("+GetTime()+") p:" + position + " n:" + velocity + " v:" + velocity + " s:" + surface + "\n");
|
|
// fxID = level._effect["ball_physics_impact"];
|
|
// if(IsDefined(surface) && IsDefined(level._effect["ball_physics_impact_"+surface]))
|
|
// fxID = level._effect["ball_physics_impact_"+surface];
|
|
//
|
|
// PlayFX(fxID, position, normal );
|
|
//
|
|
// wait .3;
|
|
// }
|
|
//}
|
|
|
|
//
|
|
//ball_fx_start()
|
|
//{
|
|
// if (!self ball_fx_active())
|
|
// {
|
|
// ball = self.visuals[0];
|
|
// PlayFXOnTag(getfx("ball_trail"), ball, "tag_origin");
|
|
// PlayFXOnTag(getfx("ball_idle"), ball, "tag_origin");
|
|
// self.ball_fx_active = true;
|
|
// }
|
|
//}
|
|
//
|
|
//ball_fx_start_player(player)
|
|
//{
|
|
// if(self ball_fx_active())
|
|
// {
|
|
// ball = self.visuals[0];
|
|
// PlayFXOnTagForClients(getfx("ball_trail"), ball, "tag_origin", player);
|
|
// PlayFXOnTagForClients(getfx("ball_idle"), ball, "tag_origin", player);
|
|
// }
|
|
//}
|
|
//
|
|
//ball_fx_stop()
|
|
//{
|
|
// if ( self ball_fx_active() )
|
|
// {
|
|
// ball = self.visuals[0];
|
|
// StopFXOnTag(getfx("ball_trail"), ball, "tag_origin");
|
|
// killFXOnTag(getfx("ball_idle"), ball, "tag_origin");
|
|
// }
|
|
// self.ball_fx_active = false;
|
|
//}
|
|
//
|
|
//ball_fx_active()
|
|
//{
|
|
// return IsDefined(self.ball_fx_active) && self.ball_fx_active;
|
|
//}
|
|
|
|
|
|
//function ball_dont_interpolate()
|
|
//{
|
|
// self.visuals[0] DontInterpolate();
|
|
// self.ball_fx_active = false; //DontInterpolate kill fx so need to pretent they were turned off.
|
|
//}
|
|
|
|
|
|
//// HUD
|
|
|
|
//
|
|
//function player_update_pass_target_hudoutline()
|
|
//{
|
|
// if(!IsDefined(self))
|
|
// return;
|
|
//
|
|
// self HudOutlineDisableForClients( level.players );
|
|
// foreach(player in level.players)
|
|
// {
|
|
// player HudOutlineDisableForClient(self);
|
|
// }
|
|
//
|
|
// team_players = [];
|
|
// other_team_players = [];
|
|
// other_team = getOtherTeam(self.team);
|
|
//
|
|
// foreach(player in level.players)
|
|
// {
|
|
// if( player == self )
|
|
// continue;
|
|
//
|
|
// if( player.team == self.team )
|
|
// team_players[team_players.size] = player;
|
|
// else if ( player.team == other_team )
|
|
// other_team_players[other_team_players.size] = player;
|
|
// }
|
|
//
|
|
// if(IsDefined(self.carryObject))
|
|
// {
|
|
// foreach(player in team_players)
|
|
// {
|
|
// isPassTarget = IsDefined(self.pass_target) && (self.pass_target == player);
|
|
// if(!isPassTarget)
|
|
// player HudOutlineEnableForClient(self, 4, false);
|
|
// }
|
|
//
|
|
// if(IsDefined(self.pass_target))
|
|
// self.pass_target HudOutlineEnableForClient(self, 5, false);
|
|
//
|
|
// if(other_team_players.size>0)
|
|
// self HudOutlineEnableForClients( other_team_players, 0, true);
|
|
//
|
|
// if(team_players.size>0)
|
|
// self HudOutlineEnableForClients( team_players, 5, false );
|
|
// }
|
|
//}
|
|
|
|
//
|
|
//player_joined_update_pass_target_hudoutline()
|
|
//{
|
|
// while(1)
|
|
// {
|
|
// level waittill( "joined_team", player );
|
|
// self player_update_pass_target_hudoutline();
|
|
// }
|
|
//}
|
|
// |