2023-04-13 17:30:38 +02:00

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