3535 lines
73 KiB
Text
3535 lines
73 KiB
Text
|
|
|
|
function GetCurrentPlaylistVarFloat( val, useVal )
|
|
{
|
|
local result = GetCurrentPlaylistVarOrUseValue( val, useVal + "" )
|
|
if ( result == null || result == "" )
|
|
return 0
|
|
|
|
return result.tofloat()
|
|
}
|
|
|
|
/* Assert is now in code!
|
|
function Assert( expr, msg = "", offset = 0 )
|
|
{
|
|
if ( expr )
|
|
return;
|
|
|
|
// Don't change this header without also changing the error-reporting code that depends on it:
|
|
local messageHeader = "[" + Time() + "] Message : "
|
|
|
|
throw ("\n\n SCRIPT ASSERTION FAILED\n " + messageHeader + msg + "\n File : " + getstackinfos(2+offset)["src"] + " [" + getstackinfos(2+offset)["line"] + "]\n Function : " + getstackinfos(2+offset)["func"] + "()\n\n")
|
|
}
|
|
*/
|
|
|
|
function PlayerMustRevive( player )
|
|
{
|
|
return Flag( "PlayersMustRevive" )
|
|
}
|
|
|
|
function SetSkinForTeam( entity, team )
|
|
{
|
|
if ( team == TEAM_IMC )
|
|
entity.SetSkin( 0 )
|
|
else if( team == TEAM_MILITIA )
|
|
entity.SetSkin( 1 )
|
|
}
|
|
|
|
function TableDump( table, depth = 0 )
|
|
{
|
|
if ( depth > 4 )
|
|
return
|
|
|
|
foreach ( k, v in table )
|
|
{
|
|
printl( "Key: " + k + " Value: " + v )
|
|
if ( type( v ) == "table" && depth )
|
|
TableDump( v, depth + 1 )
|
|
}
|
|
}
|
|
|
|
function GetVortexWeapon( player )
|
|
{
|
|
for ( local weaponIndex = 0; weaponIndex < 2; weaponIndex++ )
|
|
{
|
|
local weapon = player.GetOffhandWeapon( weaponIndex )
|
|
if ( !IsValid( weapon ) )
|
|
continue
|
|
if ( weapon.GetClassname() != "mp_titanweapon_vortex_shield" )
|
|
continue
|
|
return weapon
|
|
}
|
|
}
|
|
|
|
function GetClosest( array, origin, maxdist = null )
|
|
{
|
|
Assert( typeof array == "array" )
|
|
Assert( array.len() > 0, "Empty array!" )
|
|
|
|
local ent = array[ 0 ]
|
|
local distSqr = ( ent.GetOrigin() - origin ).LengthSqr()
|
|
|
|
local newEnt
|
|
local newDistSqr
|
|
for ( local i = 1; i < array.len(); i++ )
|
|
{
|
|
newEnt = array[ i ]
|
|
newDistSqr = ( newEnt.GetOrigin() - origin ).LengthSqr()
|
|
|
|
if ( newDistSqr < distSqr )
|
|
{
|
|
ent = newEnt
|
|
distSqr = newDistSqr
|
|
}
|
|
}
|
|
|
|
if ( maxdist )
|
|
{
|
|
maxdist = maxdist * maxdist
|
|
|
|
if ( distSqr > maxdist )
|
|
return null
|
|
}
|
|
|
|
return ent
|
|
}
|
|
|
|
function GetClosest2D( array, origin, maxdist = null )
|
|
{
|
|
Assert( typeof array == "array" )
|
|
Assert( array.len() > 0, "Empty array!" )
|
|
|
|
local ent = array[ 0 ]
|
|
local distSqr = ( ent.GetOrigin() - origin ).Length2DSqr()
|
|
|
|
local newEnt
|
|
local newDistSqr
|
|
for ( local i = 1; i < array.len(); i++ )
|
|
{
|
|
newEnt = array[ i ]
|
|
newDistSqr = ( newEnt.GetOrigin() - origin ).Length2DSqr()
|
|
|
|
if ( newDistSqr < distSqr )
|
|
{
|
|
ent = newEnt
|
|
distSqr = newDistSqr
|
|
}
|
|
}
|
|
|
|
if ( maxdist )
|
|
{
|
|
maxdist = maxdist * maxdist
|
|
|
|
if ( distSqr > maxdist )
|
|
return null
|
|
}
|
|
|
|
return ent
|
|
}
|
|
|
|
function GameModeHasCapturePoints()
|
|
{
|
|
switch ( GAMETYPE )
|
|
{
|
|
case CAPTURE_POINT:
|
|
case COOPERATIVE:
|
|
case UPLINK:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
|
|
function GetFarthest( array, origin )
|
|
{
|
|
Assert( typeof array == "array" )
|
|
Assert( array.len() > 0, "Empty array!" )
|
|
|
|
local ent = array[ 0 ]
|
|
local distSqr = ( ent.GetOrigin() - origin ).LengthSqr()
|
|
|
|
local newEnt
|
|
local newDistSqr
|
|
for ( local i = 1; i < array.len(); i++ )
|
|
{
|
|
newEnt = array[ i ]
|
|
newDistSqr = ( newEnt.GetOrigin() - origin ).LengthSqr()
|
|
|
|
if ( newDistSqr > distSqr )
|
|
{
|
|
ent = newEnt
|
|
distSqr = newDistSqr
|
|
}
|
|
}
|
|
|
|
return ent
|
|
}
|
|
|
|
function GetClosestIndex( array, origin )
|
|
{
|
|
Assert( typeof array == "array" )
|
|
Assert( array.len() > 0 )
|
|
|
|
local index = 0
|
|
local distSqr = ( array[ index ].GetOrigin() - origin ).LengthSqr()
|
|
|
|
local newEnt
|
|
local newDistSqr
|
|
for ( local i = 1; i < array.len(); i++ )
|
|
{
|
|
newEnt = array[ i ]
|
|
newDistSqr = ( newEnt.GetOrigin() - origin ).LengthSqr()
|
|
|
|
if ( newDistSqr < distSqr )
|
|
{
|
|
index = i
|
|
distSqr = newDistSqr
|
|
}
|
|
}
|
|
|
|
return index
|
|
}
|
|
|
|
// nothing in the game uses the format "table.r/g/b/a"... wtf is the point of this function
|
|
function StringToColors( colorString, delimiter = " " )
|
|
{
|
|
PerfStart( PerfIndexShared.StringToColors + SharedPerfIndexStart )
|
|
local tokens = split( colorString, delimiter )
|
|
|
|
Assert( tokens.len() >= 3 )
|
|
|
|
local table = {}
|
|
table.r <- tokens[0].tointeger()
|
|
table.g <- tokens[1].tointeger()
|
|
table.b <- tokens[2].tointeger()
|
|
|
|
if ( tokens.len() == 4 )
|
|
table.a <- tokens[3].tointeger()
|
|
else
|
|
table.a <- 255
|
|
|
|
PerfEnd( PerfIndexShared.StringToColors + SharedPerfIndexStart )
|
|
return table
|
|
}
|
|
|
|
|
|
function ColorStringToArray( colorString )
|
|
{
|
|
local tokens = split( colorString, " " )
|
|
|
|
Assert( tokens.len() >= 3 && tokens.len() <= 4 )
|
|
|
|
local colorArray = []
|
|
foreach ( token in tokens )
|
|
colorArray.append( token.tointeger() )
|
|
|
|
return colorArray
|
|
}
|
|
|
|
// Evaluate a generic order (vargc -1 ) polynomial
|
|
// e.g. to evaluate (Ax + B), call EvaluatePolynomial(x, A, B)
|
|
// Note that EvaluatePolynomial(x) returns 0 and
|
|
// EvaluatePolynomial(x, A) returns A, which are technically correct
|
|
// but perhaps not what you expect
|
|
function EvaluatePolynomial( x, ... )
|
|
{
|
|
local sum = 0
|
|
for( local i = 0; i < vargc - 1; ++i )
|
|
{
|
|
sum += vargv[ i ] * pow( x, vargc - 1 - i )
|
|
}
|
|
if ( vargc >= 1 )
|
|
{
|
|
sum += vargv[ vargc - 1 ]
|
|
}
|
|
return sum
|
|
}
|
|
|
|
|
|
// graphing function.
|
|
// If you want to map a variable that can be 0-1 to a range of 300-500,
|
|
// the function would be:
|
|
//
|
|
// Graph( variable, 0, 1, 300, 500 )
|
|
//
|
|
|
|
/*
|
|
// Moved to code
|
|
function Graph( val, A, B, C, D )
|
|
{
|
|
if ( A == B )
|
|
return fsel( ( val - B ).tofloat(), D, C )
|
|
|
|
local cVal = (val - A).tofloat() / (B - A).tofloat()
|
|
|
|
return C + (D - C) * cVal
|
|
}
|
|
|
|
function GraphCapped( val, A, B, C, D )
|
|
{
|
|
if ( A == B )
|
|
return fsel( ( val - B ).tofloat(), D, C )
|
|
|
|
local cVal = (val - A).tofloat() / (B - A).tofloat()
|
|
|
|
cVal = clamp( cVal, 0.0, 1.0 )
|
|
return C + (D - C) * cVal
|
|
}
|
|
*/
|
|
|
|
function Merge( progress, start, end )
|
|
{
|
|
return end * ( 1.0 - progress ) + start * progress
|
|
}
|
|
|
|
|
|
function WrapVector( vector )
|
|
{
|
|
// range -180 -- 180
|
|
vector.x = ( ( vector.x + 540 ) % 360 ) - 180
|
|
vector.y = ( ( vector.y + 540 ) % 360 ) - 180
|
|
vector.z = ( ( vector.z + 540 ) % 360 ) - 180
|
|
return vector
|
|
}
|
|
|
|
/*
|
|
function CubicHermite( time, start, end, move1, move2 )
|
|
{
|
|
local time2 = ( time * time )
|
|
local time3 = ( time2 * time )
|
|
return ( 2 * time3 - 3 * time2 + 1 ) * start +
|
|
( -2 * time3 + 3 * time2 ) * end +
|
|
( time3 - 2 * time2 + time ) * move1 +
|
|
( time3 - time2 ) * move2
|
|
}
|
|
|
|
function Interpolate( startTime, moveTime, easeIn = 0, easeOut = 0 )
|
|
{
|
|
if ( Time() <= startTime )
|
|
{
|
|
return 0
|
|
}
|
|
|
|
if ( Time() > startTime + moveTime )
|
|
{
|
|
return 1
|
|
}
|
|
|
|
startTime = startTime.tofloat()
|
|
moveTime = moveTime.tofloat()
|
|
easeIn = easeIn.tofloat()
|
|
easeOut = easeOut.tofloat()
|
|
|
|
if ( easeOut == moveTime )
|
|
easeOut *= 0.99
|
|
if ( easeIn == moveTime )
|
|
easeIn *= 0.99
|
|
|
|
moveTime -= ( easeIn + easeOut )
|
|
Assert( moveTime > 0, "EaseIn + easeOut exceeded total movetime!" )
|
|
|
|
local t = Time() - startTime
|
|
|
|
local x1, x2, x3
|
|
local v
|
|
local x = 1
|
|
|
|
v = x / ( easeIn / 2 + moveTime + easeOut / 2 )
|
|
x1 = v * easeIn / 2
|
|
x2 = v * moveTime
|
|
x3 = v * easeOut / 2
|
|
|
|
local result
|
|
if ( t <= easeIn )
|
|
{
|
|
result = CubicHermite( t / easeIn, 0, x1, 0, x2 / moveTime * easeIn )
|
|
}
|
|
else
|
|
{
|
|
if ( t <= easeIn + moveTime )
|
|
{
|
|
result = x1 + x2 * ( t - easeIn ) / moveTime
|
|
}
|
|
else
|
|
{
|
|
result = CubicHermite( ( t - easeIn - moveTime ) / easeOut, x1 + x2, x, x2 / moveTime * easeOut, 0 )
|
|
}
|
|
}
|
|
|
|
if ( result < 0 )
|
|
result = 0
|
|
else
|
|
if ( result > 1 )
|
|
result = 1
|
|
|
|
return result
|
|
}
|
|
*/
|
|
|
|
function SortAlphabetize( a, b )
|
|
{
|
|
if ( a > b )
|
|
return 1
|
|
|
|
if ( a < b )
|
|
return -1
|
|
|
|
return 0
|
|
}
|
|
|
|
function WaitForever()
|
|
{
|
|
level.ent.WaitSignal( "forever" )
|
|
}
|
|
|
|
function CopyComplexRecursive( src, dest )
|
|
{
|
|
foreach ( k, v in src )
|
|
{
|
|
if ( type( v ) == "table" )
|
|
{
|
|
if ( type( dest ) == "array" )
|
|
dest[k] = {}
|
|
else
|
|
dest[k] <- {}
|
|
|
|
CopyComplexRecursive( v, dest[k] )
|
|
}
|
|
else if ( type( v ) == "array" )
|
|
{
|
|
if ( type( dest ) == "array" )
|
|
dest[k] = []
|
|
else
|
|
dest[k] <- []
|
|
|
|
dest[k].resize( v.len() )
|
|
|
|
CopyComplexRecursive( v, dest[k] )
|
|
}
|
|
else
|
|
{
|
|
if ( type( dest ) == "array" )
|
|
dest[k] = v
|
|
else
|
|
dest[k] <- v
|
|
}
|
|
}
|
|
}
|
|
|
|
function DrawArrow( origin, angles = null, time = 5, scale = 50, rgb = null )
|
|
{
|
|
if ( angles == null )
|
|
angles = Vector(0,0,0)
|
|
local forward = angles.AnglesToForward()
|
|
local right = angles.AnglesToRight()
|
|
local up = angles.AnglesToUp()
|
|
|
|
if ( rgb != null )
|
|
{
|
|
DebugDrawLine( origin, origin + forward * scale, rgb.r,rgb.g,rgb.b, true, time )
|
|
DebugDrawLine( origin, origin + up * scale, rgb.r,rgb.g,rgb.b, true, time )
|
|
DebugDrawLine( origin, origin + right * scale, rgb.r,rgb.g,rgb.b, true, time )
|
|
}
|
|
else
|
|
{
|
|
DebugDrawLine( origin, origin + forward * scale, 0, 255, 0, true, time )
|
|
DebugDrawLine( origin, origin + up * scale, 255, 0, 0, true, time )
|
|
DebugDrawLine( origin, origin + right * scale, 0, 0, 255, true, time )
|
|
}
|
|
}
|
|
|
|
function ShouldDoReplay( player, attacker )
|
|
{
|
|
if ( level.nv.replayDisabled )
|
|
return false
|
|
|
|
return ( player != attacker && AttackerShouldTriggerReplay( attacker ) && !player.IsBot() )
|
|
}
|
|
|
|
// Don't let things like killbrushes show replays
|
|
function AttackerShouldTriggerReplay( attacker )
|
|
{
|
|
if ( !IsValid( attacker ) )
|
|
return false
|
|
|
|
if ( attacker.IsPlayer() )
|
|
return true
|
|
|
|
if ( attacker.IsNPC() )
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function RandomVec( range )
|
|
{
|
|
// could rewrite so it doesnt make a box of random.
|
|
local vec
|
|
vec = Vector( 0, 0, 0 )
|
|
vec.x = RandomFloat( -range, range )
|
|
vec.y = RandomFloat( -range, range )
|
|
vec.z = RandomFloat( -range, range )
|
|
return vec
|
|
}
|
|
|
|
function ArrayValuesToTableKeys( arr )
|
|
{
|
|
Assert( type( arr ) == "array", "Not an array" )
|
|
|
|
local resultTable = {}
|
|
for ( local i = 0; i < arr.len(); ++ i)
|
|
{
|
|
resultTable[ arr[ i ] ] <- 1
|
|
}
|
|
|
|
return resultTable
|
|
}
|
|
|
|
function ArrayValuesToTableValues( arr )
|
|
{
|
|
Assert( type( arr ) == "array", "Not an array" )
|
|
|
|
local resultTable = {}
|
|
for ( local i = 0; i < arr.len(); ++ i)
|
|
{
|
|
resultTable[ i ] <- arr[ i ]
|
|
}
|
|
|
|
return resultTable
|
|
}
|
|
|
|
function TableKeysToArray( tab )
|
|
{
|
|
Assert( type( tab ) == "table", "Not a table" )
|
|
|
|
local resultArray = []
|
|
resultArray.resize( tab.len() )
|
|
local currentArrayIndex = 0
|
|
foreach( key, val in tab )
|
|
{
|
|
resultArray[ currentArrayIndex ] = key
|
|
++currentArrayIndex
|
|
}
|
|
|
|
return resultArray
|
|
}
|
|
|
|
function TableValuesToArray( tab )
|
|
{
|
|
Assert( type( tab ) == "table", "Not a table" )
|
|
|
|
local resultArray = []
|
|
resultArray.resize( tab.len() )
|
|
local currentArrayIndex = 0
|
|
foreach( key, val in tab )
|
|
{
|
|
resultArray[ currentArrayIndex ] = val
|
|
++currentArrayIndex
|
|
}
|
|
|
|
return resultArray
|
|
}
|
|
|
|
function IsTableEmpty( table )
|
|
{
|
|
foreach ( _ in table )
|
|
{
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
function TableRandom( table )
|
|
{
|
|
Assert( type( table ) == "table", "Not a table" )
|
|
|
|
local array = []
|
|
|
|
foreach ( entry, contents in table )
|
|
{
|
|
array.append( contents )
|
|
}
|
|
|
|
return Random( array )
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return a random entry from an array, weighted based on how close to index 0 it is
|
|
function RandomWeighted( array )
|
|
{
|
|
Assert( type( array ) == "array", "Not an array" )
|
|
|
|
local count = array.len()
|
|
if ( count == 0 )
|
|
return null
|
|
|
|
return array[ RandomWeightedIndex( array ) ]
|
|
}
|
|
|
|
function RandomWeightedIndex( array )
|
|
{
|
|
Assert( type( array ) == "array", "Not an array" )
|
|
local count = array.len()
|
|
if ( count == 0 )
|
|
return null
|
|
|
|
local sum = ( count * ( count + 1 ) ) / 2.0 // ( n * ( n + 1 ) ) / 2
|
|
local randInt = RandomInt( 0, sum )
|
|
for( local i = 0 ; i < count ; i++ )
|
|
{
|
|
local rangeForThisIndex = count - i
|
|
if ( randInt < rangeForThisIndex )
|
|
return i
|
|
randInt -= rangeForThisIndex
|
|
}
|
|
|
|
Assert( 0 )
|
|
}
|
|
|
|
function ArrayShuffleWeighted( array )
|
|
{
|
|
Assert( type( array ) == "array", "Not an array" )
|
|
local count = array.len()
|
|
if ( count <= 1 )
|
|
return array
|
|
|
|
local sortedArray = []
|
|
local rand
|
|
local count = array.len()
|
|
for ( local i = 0 ; i < count ; i++ )
|
|
{
|
|
rand = RandomWeightedIndex( array )
|
|
sortedArray.append( array[ rand ] )
|
|
array.remove( rand )
|
|
}
|
|
return sortedArray
|
|
}
|
|
|
|
function IsValid_ThisFrame( entity )
|
|
{
|
|
if ( entity == null )
|
|
return false
|
|
|
|
return entity.IsValidInternal()
|
|
}
|
|
|
|
|
|
|
|
function ClassicMP_CreateCallbackTable( func )
|
|
{
|
|
local callbackInfo = {}
|
|
callbackInfo.func <- func
|
|
callbackInfo.scope <- this
|
|
|
|
return callbackInfo
|
|
}
|
|
|
|
/* IsValid is now in code!
|
|
function IsValid( entity )
|
|
{
|
|
if ( entity == null )
|
|
return false
|
|
|
|
if ( !entity.IsValidInternal() )
|
|
return false
|
|
return !entity.IsMarkedForDeletion()
|
|
}
|
|
*/
|
|
|
|
function VectorCopy( vec, target = null )
|
|
{
|
|
if ( target )
|
|
{
|
|
target.x = vec.x
|
|
target.y = vec.y
|
|
target.z = vec.z
|
|
return
|
|
}
|
|
|
|
Assert( typeof vec == "Vector" )
|
|
local newVector = Vector( vec.x, vec.y, vec.z )
|
|
|
|
return newVector
|
|
}
|
|
|
|
function IsAlive( ent )
|
|
{
|
|
if ( ent == null )
|
|
return false
|
|
if ( !ent.IsValidInternal() )
|
|
return false
|
|
|
|
return ent.IsEntAlive()
|
|
}
|
|
RegisterFunctionDesc( "IsAlive", "Returns true if the given ent is not null, and is alive." )
|
|
|
|
function vduon()
|
|
{
|
|
PlayConversationToAll( "TitanReplacement" )
|
|
}
|
|
|
|
function playconvtest( conv )
|
|
{
|
|
local player = GetPlayerArray()[0]
|
|
local guys = GetAllSoldiers()
|
|
if ( !guys.len() )
|
|
{
|
|
printt( "No AI!!" )
|
|
return
|
|
}
|
|
local guy = GetClosest( guys, player.GetOrigin() )
|
|
if ( conv in player.s.lastAIConversationTime )
|
|
delete player.s.lastAIConversationTime[ conv ]
|
|
|
|
printt( "Play ai conversation " + conv )
|
|
PlaySquadConversationToAll( conv, guy )
|
|
}
|
|
|
|
function FighterExplodes( ship )
|
|
{
|
|
local origin = ship.GetOrigin()
|
|
local angles = ship.GetAngles()
|
|
EmitSoundAtPosition( origin, "AngelCity_Scr_RedeyeWeaponExplos" )
|
|
if ( IsServer() )
|
|
{
|
|
PlayFX( FX_HORNET_DEATH, origin )
|
|
}
|
|
else
|
|
{
|
|
local fxid = GetParticleSystemIndex( FX_HORNET_DEATH )
|
|
StartParticleEffectInWorld( fxid, origin, angles )
|
|
}
|
|
|
|
//angles.x = -60
|
|
//angles.z = 0
|
|
//local forward = angles.AnglesToForward()
|
|
//forward.Norm()
|
|
}
|
|
|
|
function PositionOffsetFromEnt( ent, offsetX, offsetY, offsetZ )
|
|
{
|
|
local angles = ent.GetAngles()
|
|
local origin = ent.GetOrigin()
|
|
origin += angles.AnglesToForward() * offsetX
|
|
origin += angles.AnglesToRight() * offsetY
|
|
origin += angles.AnglesToUp() * offsetZ
|
|
return origin
|
|
}
|
|
|
|
function PositionOffsetFromOriginAngles( origin, angles, offsetX, offsetY, offsetZ )
|
|
{
|
|
origin += angles.AnglesToForward() * offsetX
|
|
origin += angles.AnglesToRight() * offsetY
|
|
origin += angles.AnglesToUp() * offsetZ
|
|
return origin
|
|
}
|
|
|
|
|
|
function IsMenuLevel()
|
|
{
|
|
local mapName = GetMapName()
|
|
|
|
if ( mapName == "mp_prelobby" || mapName == "mp_lobby" )
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function CreatePilotTraceBounds()
|
|
{
|
|
// checked when a pilot spawns
|
|
level.traceMins[ "pilot" ] <- Vector(-16.000000, -16.000000, 0.000000)
|
|
level.traceMaxs[ "pilot" ] <- Vector(16.000000, 16.000000, 72.000000)
|
|
}
|
|
|
|
function Dump( package, depth = 0 )
|
|
{
|
|
if ( depth > 6 )
|
|
return
|
|
|
|
foreach ( k, v in package )
|
|
{
|
|
for( local i = 0; i < depth; i++ )
|
|
print( " ")
|
|
|
|
if ( IsTable( package ) )
|
|
printl( "Key: " + k + " Value: " + v )
|
|
if ( IsArray( package ) )
|
|
printl( "Index: " + k + " Value: " + v )
|
|
|
|
if ( IsTable( v ) || IsArray( v ) )
|
|
Dump( v, depth + 1 )
|
|
}
|
|
}
|
|
|
|
function GetOtherTeam( guy )
|
|
{
|
|
local team
|
|
if(GAMETYPE == FFA) {
|
|
return 2
|
|
}
|
|
if ( typeof guy == "integer" )
|
|
team = guy
|
|
else
|
|
team = guy.GetTeam()
|
|
|
|
|
|
|
|
if ( team == 2 )
|
|
return 3
|
|
|
|
return 2
|
|
}
|
|
|
|
function VectorDot_PlayerToOrigin( player, targetOrigin )
|
|
{
|
|
local playerEyePosition = player.EyePosition()
|
|
local vecToEnt = ( targetOrigin - playerEyePosition )
|
|
vecToEnt.Norm()
|
|
|
|
// GetViewVector() only works on the player
|
|
local dotVal = vecToEnt.Dot( player.GetViewVector() )
|
|
return dotVal
|
|
}
|
|
|
|
function VectorDot_DirectionToOrigin( player, direction, targetOrigin )
|
|
{
|
|
local playerEyePosition = player.EyePosition()
|
|
local vecToEnt = ( targetOrigin - playerEyePosition )
|
|
vecToEnt.Norm()
|
|
|
|
// GetViewVector() only works on the player
|
|
local dotVal = vecToEnt.Dot( direction )
|
|
return dotVal
|
|
}
|
|
|
|
function WaitUntilWithinDistance( player, titan, dist )
|
|
{
|
|
local distSqr = dist * dist
|
|
for ( ;; )
|
|
{
|
|
if ( !IsAlive( titan ) )
|
|
return
|
|
|
|
if ( IsAlive( player ) )
|
|
{
|
|
if ( DistanceSqr( player.GetOrigin(), titan.GetOrigin() ) <= distSqr )
|
|
return
|
|
}
|
|
wait 0.1
|
|
}
|
|
}
|
|
|
|
function WaitUntilBeyondDistance( player, titan, dist )
|
|
{
|
|
local distSqr = dist * dist
|
|
for ( ;; )
|
|
{
|
|
if ( !IsAlive( titan ) )
|
|
return
|
|
|
|
if ( IsAlive( player ) )
|
|
{
|
|
if ( DistanceSqr( player.GetOrigin(), titan.GetOrigin() ) > distSqr )
|
|
return
|
|
}
|
|
wait 0.1
|
|
}
|
|
}
|
|
|
|
function IsModelViewer()
|
|
{
|
|
return GetMapName() == "model_viewer"
|
|
}
|
|
|
|
if ( !IsMultiplayer() )
|
|
{
|
|
function GetCurrentPlaylistVar( var )
|
|
{
|
|
return null
|
|
}
|
|
|
|
function GetCurrentPlaylistVarInt( val, useVal )
|
|
{
|
|
return useVal
|
|
}
|
|
|
|
function GetCurrentPlaylistVarFloat( val, useVal )
|
|
{
|
|
return useVal
|
|
}
|
|
|
|
}
|
|
|
|
function PlayerEnhancement()
|
|
{
|
|
return true
|
|
}
|
|
|
|
|
|
|
|
//----------------------//
|
|
// Tweening functions //
|
|
// t = current time //
|
|
// b = start value //
|
|
// c = change in value //
|
|
// d = duration //
|
|
//----------------------//
|
|
|
|
// simple linear tweening - no easing, no acceleration
|
|
function Tween_Linear( t, b, c, d )
|
|
{
|
|
return c*t/d + b
|
|
}
|
|
|
|
// quadratic easing out - decelerating to zero velocity
|
|
function Tween_QuadEaseOut( t, b, c, d )
|
|
{
|
|
t /= d
|
|
return -c * t*(t-2) + b
|
|
}
|
|
|
|
// exponential easing out - decelerating to zero velocity
|
|
function Tween_ExpoEaseOut( t, b, c, d )
|
|
{
|
|
return c * ( -pow( 2, -10 * t/d ) + 1 ) + b
|
|
}
|
|
|
|
function Tween_ExpoEaseIn( t, b, c, d )
|
|
{
|
|
return c * pow( 2, 10 * ( t/d - 1 ) ) + b;
|
|
}
|
|
|
|
function CoinFlip()
|
|
{
|
|
return RandomInt( 0, 2 )
|
|
}
|
|
|
|
function LegalOrigin( origin )
|
|
{
|
|
if ( fabs( origin.x ) > 16000 )
|
|
return false
|
|
if ( fabs( origin.y ) > 16000 )
|
|
return false
|
|
if ( fabs( origin.z ) > 16000 )
|
|
return false
|
|
return true
|
|
}
|
|
|
|
function AnglesOnSurface( surfaceNormal, playerVelocity )
|
|
{
|
|
playerVelocity.Norm()
|
|
local right = playerVelocity.Cross( surfaceNormal )
|
|
local forward = surfaceNormal.Cross( right )
|
|
local angles = forward.GetAngles()
|
|
angles.z = ( atan2( right.z, surfaceNormal.z ) * -180 ) / PI
|
|
|
|
return angles
|
|
}
|
|
|
|
function ClampToWorldspace( origin )
|
|
{
|
|
// temp solution for start positions that are outside the world bounds
|
|
origin.x = clamp( origin.x, -16000, 16000 )
|
|
origin.y = clamp( origin.y, -16000, 16000 )
|
|
origin.z = clamp( origin.z, -16000, 16000 )
|
|
}
|
|
|
|
function UseReturnTrue( user, usee )
|
|
{
|
|
return true
|
|
}
|
|
|
|
function ControlPanel_CanUseFunction( playerUser, controlPanel )
|
|
{
|
|
// Does a simple cone FOV check from the screen to the player's eyes
|
|
local maxAngleToAxisAllowedDegrees = 60
|
|
|
|
local playerEyePos = playerUser.EyePosition()
|
|
local attachmentIndex = controlPanel.LookupAttachment( "PANEL_SCREEN_MIDDLE" )
|
|
|
|
Assert( attachmentIndex != 0 )
|
|
local controlPanelScreenPosition = controlPanel.GetAttachmentOrigin( attachmentIndex )
|
|
local controlPanelScreenAngles = controlPanel.GetAttachmentAngles( attachmentIndex )
|
|
local controlPanelScreenForward = controlPanelScreenAngles.AnglesToForward()
|
|
|
|
local screenToPlayerEyes = playerEyePos - controlPanelScreenPosition
|
|
screenToPlayerEyes.Normalize()
|
|
|
|
return screenToPlayerEyes.Dot( controlPanelScreenForward ) > deg_cos( maxAngleToAxisAllowedDegrees )
|
|
}
|
|
|
|
function ArrayRemoveInvalid( array )
|
|
{
|
|
Assert( type( array ) == "array" )
|
|
|
|
for ( local i = 0; i < array.len(); )
|
|
{
|
|
if ( !IsValid( array[ i ] ) )
|
|
{
|
|
array.remove( i )
|
|
}
|
|
else
|
|
{
|
|
//Increment i here instead of in the for loop declaration
|
|
//because after a call to array.remove, array[i] will point to
|
|
//the next element in the array yet to be examined.
|
|
i++
|
|
}
|
|
}
|
|
}
|
|
|
|
function SkipCinematicStart() //Doesn't quite work as advertised for now.
|
|
{
|
|
return IsTrainingLevel()
|
|
}
|
|
|
|
function SetIsTrainingLevel()
|
|
{
|
|
level.isTrainingLevel <- GetMapName() == "mp_trainer" || GetMapName() == "mp_npe"
|
|
}
|
|
|
|
function IsFirstCampaignLevel()
|
|
{
|
|
local levelname = GetMapName()
|
|
if ( !levelname )
|
|
return false
|
|
|
|
return levelname == "mp_fracture"
|
|
}
|
|
|
|
function HasDamageStates( ent )
|
|
{
|
|
if ( !IsValid( ent ) )
|
|
return false
|
|
return ( "damageStateInfo" in ent.s )
|
|
}
|
|
|
|
function HasHitData( ent )
|
|
{
|
|
return ( "hasHitData" in ent.s && ent.s.hasHitData )
|
|
}
|
|
|
|
function IncludeTitanEmbark()
|
|
{
|
|
IncludeFile( "_titan_embark" )
|
|
}
|
|
|
|
function GetFrontRightDots( baseEnt, relativeEnt, optionalTag = null )
|
|
{
|
|
if ( optionalTag )
|
|
{
|
|
local attachIndex = baseEnt.LookupAttachment( optionalTag )
|
|
local origin = baseEnt.GetAttachmentOrigin( attachIndex )
|
|
local angles = baseEnt.GetAttachmentAngles( attachIndex )
|
|
angles.x = 0
|
|
angles.z = 0
|
|
local forward = angles.AnglesToForward()
|
|
local right = angles.AnglesToRight()
|
|
|
|
local targetOrg = relativeEnt.GetOrigin()
|
|
local vecToEnt = ( targetOrg - origin )
|
|
// printt( "vecToEnt ", vecToEnt )
|
|
vecToEnt.z = 0
|
|
|
|
vecToEnt.Norm()
|
|
|
|
|
|
local table = {}
|
|
table.dotForward <- vecToEnt.Dot( forward )
|
|
table.dotRight <- vecToEnt.Dot( right )
|
|
|
|
// red: forward for incoming ent
|
|
//DebugDrawLine( origin, origin + vecToEnt * 150, 255, 0, 0, true, 5 )
|
|
|
|
// green: tag forward
|
|
//DebugDrawLine( origin, origin + forward * 150, 0, 255, 0, true, 5 )
|
|
|
|
// blue: tag right
|
|
//DebugDrawLine( origin, origin + right * 150, 0, 0, 255, true, 5 )
|
|
return table
|
|
}
|
|
|
|
local targetOrg = relativeEnt.GetOrigin()
|
|
local origin = baseEnt.GetOrigin()
|
|
local vecToEnt = ( targetOrg - origin )
|
|
vecToEnt.Norm()
|
|
|
|
local table = {}
|
|
table.dotForward <- vecToEnt.Dot( baseEnt.GetForwardVector() )
|
|
table.dotRight <- vecToEnt.Dot( baseEnt.GetRightVector() )
|
|
return table
|
|
}
|
|
|
|
RegisterSignal( "devForcedWin" )
|
|
|
|
function ForceMatchEnd()
|
|
{
|
|
local winner = TEAM_IMC
|
|
if ( GameRules.GetTeamScore2( TEAM_MILITIA ) > GameRules.GetTeamScore2( TEAM_IMC ) )
|
|
winner = TEAM_MILITIA
|
|
|
|
if ( IsRoundBased() )
|
|
{
|
|
local roundLimit = GetRoundScoreLimit_FromPlaylist()
|
|
GameRules.SetTeamScore2( winner, roundLimit )
|
|
}
|
|
|
|
if ( winner == TEAM_MILITIA )
|
|
ForceMilitiaWin()
|
|
else
|
|
ForceIMCWin()
|
|
}
|
|
|
|
function ForceIMCWin()
|
|
{
|
|
level.devForcedWin = true
|
|
level.ent.Signal( "devForcedWin" )
|
|
if ( IsRoundBased() )
|
|
{
|
|
SetWinner( TEAM_IMC )
|
|
return
|
|
}
|
|
else
|
|
{
|
|
local scoreLimit = GetScoreLimit_FromPlaylist()
|
|
GameScore.AddTeamScore( TEAM_IMC, scoreLimit )
|
|
ForceTimeLimitDone()
|
|
}
|
|
}
|
|
|
|
function ForceMilitiaWin()
|
|
{
|
|
level.devForcedWin = true
|
|
level.ent.Signal( "devForcedWin" )
|
|
if ( IsRoundBased() )
|
|
{
|
|
SetWinner( TEAM_MILITIA )
|
|
return
|
|
}
|
|
else
|
|
{
|
|
local scoreLimit = GetScoreLimit_FromPlaylist()
|
|
GameScore.AddTeamScore( TEAM_MILITIA, scoreLimit )
|
|
ForceTimeLimitDone()
|
|
}
|
|
}
|
|
|
|
function ForceDraw()
|
|
{
|
|
level.devForcedWin = true
|
|
level.ent.Signal( "devForcedWin" )
|
|
if ( !IsRoundBased() )
|
|
{
|
|
local scoreLimit = GetScoreLimit_FromPlaylist()
|
|
GameScore.SetTeamScore( TEAM_MILITIA, scoreLimit )
|
|
GameScore.SetTeamScore( TEAM_IMC, scoreLimit )
|
|
}
|
|
|
|
ForceTimeLimitDone()
|
|
}
|
|
|
|
function ForceTimeLimitDone()
|
|
{
|
|
level.devForcedWin = true
|
|
level.ent.Signal( "devForcedWin" )
|
|
ServerCommand( "mp_enabletimelimit 1" )
|
|
ServerCommand( "mp_enablematchending 1" )
|
|
if ( IsRoundBased() )
|
|
GetRoundTimeLimit_ForGameMode = GetTimeLimit_ForGameModeGameOver
|
|
else
|
|
GetTimeLimit_ForGameMode = GetTimeLimit_ForGameModeGameOver
|
|
}
|
|
|
|
function GetTimeLimit_ForGameModeGameOver()
|
|
{
|
|
return 0.1
|
|
}
|
|
|
|
|
|
function GetRoundScoreLimit_FromPlaylist()
|
|
{
|
|
switch ( GAMETYPE )
|
|
{
|
|
case CAPTURE_POINT:
|
|
return GetCurrentPlaylistVarInt( "CP_roundscorelimit", 10 )
|
|
|
|
case ATTRITION:
|
|
return GetCurrentPlaylistVarInt( "AT_roundscorelimit", 50 )
|
|
|
|
case CAPTURE_THE_FLAG:
|
|
return GetCurrentPlaylistVarInt( "CTF_roundscorelimit", 6 )
|
|
|
|
case CAPTURE_THE_FLAG_PRO:
|
|
return GetCurrentPlaylistVarInt( "CTFP_roundscorelimit", 6 )
|
|
|
|
case HEIST:
|
|
return GetCurrentPlaylistVarInt( "HEIST_roundscorelimit", 4 )
|
|
|
|
case BIG_BROTHER:
|
|
return GetCurrentPlaylistVarInt( "BB_roundscorelimit", 4 )
|
|
|
|
case COOPERATIVE:
|
|
return GetCurrentPlaylistVarInt( "COOP_roundscorelimit", COOP_RESTARTS )
|
|
|
|
default:
|
|
if ( !GameMode_IsDefined( GAMETYPE ) )
|
|
return GetCurrentPlaylistVarInt( "TDM_roundscorelimit", 10 )
|
|
else
|
|
return GameMode_GetRoundScoreLimit( GAMETYPE )
|
|
}
|
|
}
|
|
|
|
|
|
function GetScoreLimit_FromPlaylist()
|
|
{
|
|
switch ( GAMETYPE )
|
|
{
|
|
case CAPTURE_POINT:
|
|
return GetCurrentPlaylistVarInt( "CP_scorelimit", 10 )
|
|
break
|
|
|
|
case ATTRITION:
|
|
return GetCurrentPlaylistVarInt( "AT_scorelimit", 50 )
|
|
break
|
|
|
|
case CAPTURE_THE_FLAG:
|
|
return GetCurrentPlaylistVarInt( "CTF_scorelimit", 10 )
|
|
break
|
|
|
|
case PILOT_SKIRMISH:
|
|
return GetCurrentPlaylistVarInt( "PS_scorelimit", 100 )
|
|
|
|
case HEIST:
|
|
return GetCurrentPlaylistVarInt( "HEIST_scorelimit", 0 )
|
|
|
|
case BIG_BROTHER:
|
|
return GetCurrentPlaylistVarInt( "BB_scorelimit", 0 )
|
|
|
|
default:
|
|
if ( !GameMode_IsDefined( GAMETYPE ) )
|
|
return GetCurrentPlaylistVarInt( "TDM_scorelimit", 10 )
|
|
else
|
|
return GameMode_GetScoreLimit( GAMETYPE )
|
|
}
|
|
}
|
|
|
|
|
|
function GetTimeLimit_ForGameMode()
|
|
{
|
|
local timeLimit
|
|
switch ( GAMETYPE )
|
|
{
|
|
case CAPTURE_POINT:
|
|
timeLimit = GetCurrentPlaylistVarInt( "CP_timelimit", 10 )
|
|
break
|
|
|
|
case ATTRITION:
|
|
timeLimit = GetCurrentPlaylistVarInt( "AT_timelimit", 10 )
|
|
break
|
|
|
|
case CAPTURE_THE_FLAG:
|
|
timeLimit = GetCurrentPlaylistVarInt( "CTF_timelimit", 10 )
|
|
break
|
|
|
|
case PILOT_SKIRMISH:
|
|
timeLimit = GetCurrentPlaylistVarInt( "PS_timelimit", 15 )
|
|
break
|
|
|
|
case HEIST:
|
|
return GetCurrentPlaylistVarInt( "HEIST_timelimit", 6 )
|
|
break
|
|
|
|
case BIG_BROTHER:
|
|
return GetCurrentPlaylistVarInt( "BB_timelimit", 6 )
|
|
break
|
|
|
|
case SCAVENGER:
|
|
return GetCurrentPlaylistVarInt( "SCV_timelimit", 10 )
|
|
break // no real need to break after a return, but eh...
|
|
|
|
default:
|
|
if ( !GameMode_IsDefined( GAMETYPE ) )
|
|
return GetCurrentPlaylistVarInt( "TDM_timelimit", 10 )
|
|
else
|
|
return GameMode_GetTimeLimit( GAMETYPE )
|
|
}
|
|
|
|
if ( IsRoundBased() )
|
|
{
|
|
local scoreLimit = GetScoreLimit_FromPlaylist()
|
|
if ( scoreLimit )
|
|
timeLimit *= (scoreLimit * 2)
|
|
}
|
|
else if ( IsSwitchSidesBased() )
|
|
{
|
|
timeLimit *= 0.5
|
|
}
|
|
|
|
return timeLimit
|
|
}
|
|
|
|
function GetRoundTimeLimit_ForGameMode()
|
|
{
|
|
local timeLimit
|
|
switch ( GAMETYPE )
|
|
{
|
|
case BIG_BROTHER:
|
|
timeLimit = GetCurrentPlaylistVarInt( "BB_roundtimelimit", 5 )
|
|
break
|
|
|
|
case HEIST:
|
|
timeLimit = GetCurrentPlaylistVarInt( "HEIST_roundtimelimit", 5 )
|
|
break
|
|
|
|
default:
|
|
if ( !GameMode_IsDefined( GAMETYPE ) )
|
|
return GetCurrentPlaylistVarInt( "TDM_roundtimelimit", 10 )
|
|
else
|
|
return GameMode_GetRoundTimeLimit( GAMETYPE )
|
|
}
|
|
|
|
return timeLimit
|
|
}
|
|
|
|
function GetSuddenDeathTimeLimit_ForGameMode()
|
|
{
|
|
switch ( GAMETYPE )
|
|
{
|
|
case CAPTURE_THE_FLAG:
|
|
return GetCurrentPlaylistVarInt( "CTF_suddendeath_timelimit", 4 )
|
|
|
|
case TEAM_DEATHMATCH:
|
|
return GetCurrentPlaylistVarInt( "TDM_suddendeath_timelimit", 4 )
|
|
|
|
default:
|
|
return 4
|
|
}
|
|
}
|
|
|
|
function GetAllPointsOnBezier( points, numSegments, debugDrawTime = null )
|
|
{
|
|
Assert( points.len() >= 2 )
|
|
Assert( numSegments > 0 )
|
|
local curvePoints = []
|
|
|
|
// Debug draw the points used for the curve
|
|
if ( debugDrawTime != null )
|
|
{
|
|
for( local i = 0 ; i < points.len() - 1 ; i++ )
|
|
DebugDrawLine( points[i], points[i + 1], 150, 150, 150, true, debugDrawTime )
|
|
}
|
|
|
|
for( local i = 0 ; i < numSegments ; i++ )
|
|
{
|
|
local t = ( i.tofloat() / ( numSegments.tofloat() - 1.0 ) ).tofloat()
|
|
curvePoints.append( GetSinglePointOnBezier( points, t) )
|
|
}
|
|
|
|
return curvePoints
|
|
}
|
|
|
|
function GetSinglePointOnBezier( points, t )
|
|
{
|
|
// evaluate a point on a bezier-curve. t goes from 0 to 1.0
|
|
|
|
local numPoints = points.len()
|
|
|
|
local lastPoints = clone points
|
|
for(;;)
|
|
{
|
|
local newPoints = []
|
|
for ( local i = 0 ; i < lastPoints.len() - 1 ; i++ )
|
|
newPoints.append( lastPoints[i] + ( lastPoints[i+1] - lastPoints[i] ) * t )
|
|
|
|
if ( newPoints.len() == 1 )
|
|
return newPoints[0]
|
|
|
|
lastPoints = newPoints
|
|
}
|
|
|
|
return dest
|
|
}
|
|
|
|
if ( !reloadingScripts && IsMultiplayer() )
|
|
{
|
|
__GetCurrentPlaylistVar <- GetCurrentPlaylistVar
|
|
function GetCurrentPlaylistVar( val, opVal = null )
|
|
{
|
|
if ( opVal == null )
|
|
return __GetCurrentPlaylistVar( val )
|
|
|
|
if ( IsClient() && !IsConnected() )
|
|
return opVal
|
|
|
|
return GetCurrentPlaylistVarOrUseValue( val, opVal )
|
|
}
|
|
|
|
__GetCurrentPlaylistVarOrUseValue <- GetCurrentPlaylistVarOrUseValue
|
|
function GetCurrentPlaylistVarOrUseValue( val, opVal )
|
|
{
|
|
if ( typeof opVal != "string" )
|
|
{
|
|
opVal += ""
|
|
}
|
|
|
|
if ( IsClient() && !IsConnected() )
|
|
return opVal
|
|
|
|
return __GetCurrentPlaylistVarOrUseValue( val, opVal )
|
|
}
|
|
}
|
|
|
|
|
|
function TitanCoreInUse( player )
|
|
{
|
|
Assert( player.IsTitan() )
|
|
|
|
if ( !IsAlive( player ) )
|
|
return false
|
|
|
|
return Time() < player.GetTitanSoul().GetCoreChargeExpireTime()
|
|
}
|
|
|
|
|
|
|
|
function AddCallback_Generic( levelVarName, callbackFunc, scope )
|
|
{
|
|
Assert( levelVarName in level )
|
|
Assert( type( this ) == "table", FunctionName() + " can only be added on a table. " + type( this ) )
|
|
|
|
local callbackInfo = {}
|
|
callbackInfo.func <- callbackFunc
|
|
callbackInfo.scope <- scope
|
|
|
|
level[ levelVarName ].append( callbackInfo )
|
|
}
|
|
|
|
|
|
function GetTitanCoreTimeRemaining( player )
|
|
{
|
|
if ( !player.IsTitan() )
|
|
return null
|
|
|
|
local soul = player.GetTitanSoul()
|
|
|
|
if ( !soul )
|
|
return null
|
|
|
|
return soul.GetCoreChargeExpireTime() - Time()
|
|
}
|
|
|
|
|
|
function GetTitanCoreActiveTime( player )
|
|
{
|
|
local coreDuration = TITAN_CORE_ACTIVE_TIME
|
|
|
|
if ( PlayerHasPassive( player, PAS_MARATHON_CORE ) )
|
|
coreDuration *= TITAN_CORE_MARATHON_CORE_MULTIPLIER
|
|
|
|
return coreDuration
|
|
|
|
}
|
|
|
|
|
|
function GetTitanBuildTime( player )
|
|
{
|
|
if ( IsClient() )
|
|
return player.GetTitanBuildTime()
|
|
|
|
if ( !player.titansBuilt )
|
|
return GetCurrentPlaylistVarInt( "titan_build_time", 340 )
|
|
|
|
return GetCurrentPlaylistVarInt( "titan_rebuild_time", 150 )
|
|
}
|
|
|
|
|
|
function ResetTitanBuildTime( player )
|
|
{
|
|
if ( player.IsTitan() )
|
|
{
|
|
player.SetTitanBuildTime( GetCurrentPlaylistVarInt( "titan_core_build_time", TITAN_CORE_BUILD_TIME ) )
|
|
return
|
|
}
|
|
|
|
if ( !player.titansBuilt )
|
|
player.SetTitanBuildTime( GetCurrentPlaylistVarInt( "titan_build_time", 340 ) )
|
|
else
|
|
player.SetTitanBuildTime( GetCurrentPlaylistVarInt( "titan_rebuild_time", 150 ) )
|
|
}
|
|
|
|
|
|
function GetNuclearPayload( player )
|
|
{
|
|
local payload = 0
|
|
if ( PlayerHasPassive( player, PAS_NUCLEAR_CORE ) )
|
|
payload += 2
|
|
|
|
if ( PlayerHasPassive( player, PAS_BUILD_UP_NUCLEAR_CORE ) )
|
|
payload += 1
|
|
|
|
return payload
|
|
|
|
// if ( TitanCoreInUse( player ) )
|
|
// return true
|
|
//
|
|
// if ( player.GetNextTitanRespawnAvailable() < 0 )
|
|
// return false
|
|
//
|
|
// if ( Time() >= player.GetNextTitanRespawnAvailable() )
|
|
// return true
|
|
//
|
|
// return false
|
|
|
|
}
|
|
|
|
function DrawLineForPoints( points, color, duration )
|
|
{
|
|
Assert( points.len() >= 2 )
|
|
for ( local i = 0 ; i < points.len() - 1 ; i++ )
|
|
DebugDrawLine( points[i], points[i+1], color.r, color.g, color.b, true, duration )
|
|
}
|
|
|
|
function GetCloak( entity )
|
|
{
|
|
return GetOffhand( entity, "mp_ability_cloak" )
|
|
}
|
|
|
|
function GetOffhand( entity, classname )
|
|
{
|
|
local offhand = entity.GetOffhandWeapon( OFFHAND_LEFT )
|
|
if ( IsValid( offhand ) && offhand.GetClassname() == classname )
|
|
return offhand
|
|
|
|
local offhand = entity.GetOffhandWeapon( OFFHAND_RIGHT )
|
|
if ( IsValid( offhand ) && offhand.GetClassname() == classname )
|
|
return offhand
|
|
|
|
return null
|
|
}
|
|
|
|
function IsCloaked( entity )
|
|
{
|
|
return entity.IsCloaked( true ) //pass true to ignore flicker time -
|
|
}
|
|
|
|
function TimeSpentInCurrentState()
|
|
{
|
|
return Time() - level.nv.gameStateChangeTime
|
|
}
|
|
|
|
function DotToAngle( dot )
|
|
{
|
|
local angle = acos( dot ) * 180 / PI
|
|
return angle
|
|
}
|
|
|
|
function AngleToDot( angle )
|
|
{
|
|
local dot = cos( angle * PI / 180 )
|
|
return dot
|
|
}
|
|
|
|
function DebugDrawCircle( origin, angles, radius, r, g, b, time )
|
|
{
|
|
local start = null
|
|
local end = null
|
|
local firstend = null
|
|
local degrees = 22.5
|
|
local pointsOnCircle = []
|
|
|
|
for( local i = 0; i < 16; i++ )
|
|
{
|
|
local angles2 = angles.AnglesCompose( Vector( 0, degrees * i, 0 ) )
|
|
local forward = angles2.AnglesToForward()
|
|
end = origin + ( forward * radius )
|
|
|
|
if( !start )
|
|
firstend = VectorCopy( end )
|
|
if( start )
|
|
DebugDrawLine( start, end, r, g, b, true, time )
|
|
|
|
pointsOnCircle.append( end )
|
|
|
|
start = end
|
|
}
|
|
|
|
DebugDrawLine( end, firstend, r, g, b, true, time )
|
|
return pointsOnCircle
|
|
}
|
|
|
|
function DebugDrawSphere( origin, radius, r, g, b, time )
|
|
{
|
|
local degrees = 45
|
|
|
|
for( local i = 0; i < 8; i++ )
|
|
{
|
|
DebugDrawCircle( origin, Vector( 0, 0, degrees * i ), radius, r, g, b, time )
|
|
DebugDrawCircle( origin, Vector( degrees * i, 0, 0 ), radius, r, g, b, time )
|
|
}
|
|
}
|
|
|
|
function DebugDrawCircleTillSignal( ent, signalName, origin, radius, r, g, b )
|
|
{
|
|
EndSignal( ent, signalName )
|
|
while(1)
|
|
{
|
|
thread DebugDrawCircle( origin, Vector( 0,0,0 ), radius, r, g, b, 0.2 )
|
|
wait 0
|
|
}
|
|
}
|
|
|
|
function DebugDrawOriginMovement( ent, r, g, b, time = 9999.0, trailTime = 5.0 )
|
|
{
|
|
ent.EndSignal( "OnDeath" )
|
|
ent.EndSignal( "OnDestroy" )
|
|
|
|
local startTime = Time()
|
|
local endTime = startTime + time
|
|
local lastPos = ent.GetOrigin()
|
|
|
|
while ( Time() < endTime )
|
|
{
|
|
DebugDrawLine( lastPos, ent.GetOrigin(), r, g, b, true, trailTime )
|
|
lastPos = ent.GetOrigin()
|
|
wait( 0.0 )
|
|
}
|
|
}
|
|
|
|
function GetGameState()
|
|
{
|
|
return GetServerVar( "gameState" )
|
|
}
|
|
|
|
function GamePlaying()
|
|
{
|
|
return GetGameState() == eGameState.Playing
|
|
}
|
|
|
|
function GamePlayingOrSuddenDeath()
|
|
{
|
|
local gameState = GetGameState()
|
|
return gameState == eGameState.Playing || gameState == eGameState.SuddenDeath
|
|
}
|
|
|
|
function IsOdd( num )
|
|
{
|
|
return ( num % 2 )
|
|
}
|
|
|
|
function IsEven( num )
|
|
{
|
|
return !IsOdd( num )
|
|
}
|
|
|
|
function VectorReflectionAcrossNormal( vector, normal )
|
|
{
|
|
return ( vector - normal * ( 2 * vector.Dot(normal) ) )
|
|
}
|
|
|
|
// Return an array of entities ordered from farthest to closest to the specified origin
|
|
function ArrayFarthest( array, origin )
|
|
{
|
|
Assert( type( array ) == "array" )
|
|
local allResults = ArrayDistanceResults( array, origin )
|
|
|
|
allResults.sort( DistanceCompareFarthest )
|
|
|
|
local returnEntities = []
|
|
|
|
foreach ( index, result in allResults )
|
|
{
|
|
returnEntities.insert( index, result.ent )
|
|
}
|
|
|
|
// the actual distances aren't returned
|
|
return returnEntities
|
|
}
|
|
|
|
// Return an array of entities ordered from closest to furthest from the specified origin
|
|
function ArrayClosest( array, origin )
|
|
{
|
|
Assert( type( array ) == "array" )
|
|
local allResults = ArrayDistanceResults( array, origin )
|
|
|
|
allResults.sort( DistanceCompareClosest )
|
|
|
|
local returnEntities = []
|
|
|
|
foreach ( index, result in allResults )
|
|
{
|
|
returnEntities.insert( index, result.ent )
|
|
}
|
|
|
|
// the actual distances aren't returned
|
|
return returnEntities
|
|
}
|
|
|
|
|
|
function TableRemove( table, entry )
|
|
{
|
|
Assert( typeof table == "table" )
|
|
|
|
foreach ( index, tableEntry in table )
|
|
{
|
|
if ( tableEntry == entry )
|
|
{
|
|
table[ index ] = null
|
|
}
|
|
}
|
|
}
|
|
|
|
function TableInvert( table )
|
|
{
|
|
local invertedTable = {}
|
|
foreach( key, value in table )
|
|
invertedTable[ value ] <- key
|
|
|
|
return invertedTable
|
|
}
|
|
|
|
function DistanceCompareClosest( a, b )
|
|
{
|
|
if ( a.distanceSqr > b.distanceSqr )
|
|
return 1
|
|
else if ( a.distanceSqr < b.distanceSqr )
|
|
return -1
|
|
|
|
return 0;
|
|
}
|
|
|
|
function DistanceCompareFarthest( a, b )
|
|
{
|
|
if ( a.distanceSqr < b.distanceSqr )
|
|
return 1
|
|
else if ( a.distanceSqr > b.distanceSqr )
|
|
return -1
|
|
|
|
return 0;
|
|
}
|
|
|
|
function ArrayDistanceResults( array, origin )
|
|
{
|
|
Assert( type( array ) == "array" )
|
|
local allResults = []
|
|
|
|
foreach ( ent in array )
|
|
{
|
|
local results = {}
|
|
local testspot = null
|
|
|
|
if ( typeof ent == "Vector" )
|
|
testspot = ent
|
|
else
|
|
testspot = ent.GetOrigin()
|
|
|
|
results.distanceSqr <- ( testspot - origin ).LengthSqr()
|
|
results.ent <- ent
|
|
allResults.append( results )
|
|
}
|
|
|
|
return allResults
|
|
}
|
|
|
|
function GetGravityLandData( startPos, parentVelocity, objectVelocity, timeLimit, bDrawPath = false, bDrawPathDuration = 0, pathColor = [ 255, 255, 0 ] )
|
|
{
|
|
local returnData = {}
|
|
returnData.points <- []
|
|
returnData.traceResults <- null
|
|
returnData.elapsedTime <- 0
|
|
|
|
Assert( timeLimit > 0 )
|
|
|
|
local MAX_TIME_ELAPSE = 6.0
|
|
local timeElapsePerTrace = 0.1
|
|
|
|
local sv_gravity = 750
|
|
local ent_gravity = 1.0
|
|
local gravityScale = 1.0
|
|
|
|
local traceStart = VectorCopy( startPos )
|
|
local traceEnd = traceStart
|
|
local traceFrac
|
|
local traceCount = 0
|
|
|
|
objectVelocity += parentVelocity
|
|
|
|
while( returnData.elapsedTime <= timeLimit )
|
|
{
|
|
objectVelocity.z -= ( ent_gravity * sv_gravity * timeElapsePerTrace * gravityScale )
|
|
|
|
traceEnd += objectVelocity * timeElapsePerTrace
|
|
returnData.points.append( traceEnd )
|
|
if ( bDrawPath )
|
|
DebugDrawLine( traceStart, traceEnd, pathColor[0], pathColor[1], pathColor[2], false, bDrawPathDuration )
|
|
|
|
traceFrac = TraceLineSimple( traceStart, traceEnd, null )
|
|
traceCount++
|
|
if ( traceFrac < 1.0 )
|
|
{
|
|
local traceResults = TraceLine( traceStart, traceEnd, null, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_NONE )
|
|
returnData.traceResults = traceResults
|
|
return returnData
|
|
}
|
|
traceStart = traceEnd
|
|
returnData.elapsedTime += timeElapsePerTrace
|
|
}
|
|
|
|
return returnData
|
|
}
|
|
|
|
function DrawLineFromEntToEntForTime( ent1, ent2, duration, r = 255, g = 255, b = 0 )
|
|
{
|
|
local endTime = Time() + duration
|
|
while( Time() <= endTime )
|
|
{
|
|
if ( !IsValid( ent1 ) || !IsValid( ent2 ) )
|
|
return
|
|
|
|
DebugDrawLine( ent1.GetOrigin(), ent2.GetOrigin(), r, g, b, true, 0.05 )
|
|
wait 0
|
|
}
|
|
}
|
|
|
|
function AngleDiff( ang1, ang2 )
|
|
{
|
|
local res = ang1 - ang2;
|
|
|
|
// wrap to range [0, 360)
|
|
res -= floor( res / 360 ) * 360
|
|
|
|
// convert to range [-180, +180)
|
|
if ( res >= 180 )
|
|
res -= 360
|
|
|
|
return res
|
|
}
|
|
|
|
function GetPulseFrac( rate = 1, startTime = 0 )
|
|
{
|
|
return (1 - cos( ( Time() - startTime ) * (rate * (2*PI)) )) / 2
|
|
}
|
|
|
|
function IsPetTitan( titan )
|
|
{
|
|
Assert( titan.IsTitan() )
|
|
|
|
return titan.GetTitanSoul().GetBossPlayer() != null
|
|
}
|
|
|
|
function GetAllPilots( team = null )
|
|
{
|
|
local players
|
|
if ( team != null )
|
|
players = GetPlayerArrayOfTeam( team )
|
|
else
|
|
players = GetPlayerArray()
|
|
local pilots = []
|
|
|
|
foreach ( player in players )
|
|
{
|
|
if ( !IsValid( player ) )
|
|
continue
|
|
|
|
if ( player.IsTitan() )
|
|
continue
|
|
|
|
pilots.append( player )
|
|
}
|
|
|
|
return pilots
|
|
}
|
|
|
|
function CalcClosestPointOnLine( P, A, B, clamp = false )
|
|
{
|
|
local table = {}
|
|
local AP = P - A
|
|
local AB = B - A
|
|
|
|
local ab2 = AB.Dot( AB ) // AB.x*AB.x + AB.y*AB.y
|
|
local ap_ab = AP.Dot( AB ) // AP.x*AB.x + AP.y*AB.y
|
|
local t = ap_ab / ab2
|
|
|
|
if ( clamp )
|
|
{
|
|
if ( t < 0.0 )
|
|
t = 0.0
|
|
else
|
|
if ( t > 1.0 )
|
|
t = 1.0
|
|
}
|
|
table.t <- t
|
|
|
|
local closest = A + AB * t
|
|
table.point <- closest
|
|
return table
|
|
}
|
|
|
|
function CalcProgressOnLine( P, A, B )
|
|
{
|
|
local AP = P - A
|
|
local AB = B - A
|
|
|
|
local ab2 = AB.Dot( AB ) // AB.x*AB.x + AB.y*AB.y
|
|
local ap_ab = AP.Dot( AB ) // AP.x*AB.x + AP.y*AB.y
|
|
local t = ap_ab / ab2
|
|
return t
|
|
}
|
|
|
|
function StringToVector( vecString, delimiter = " " )
|
|
{
|
|
local tokens = split( vecString, delimiter )
|
|
|
|
Assert( tokens.len() >= 3 )
|
|
|
|
return Vector( tokens[0].tofloat(), tokens[1].tofloat(), tokens[2].tofloat() )
|
|
}
|
|
|
|
function GetShieldHealthFrac( titan )
|
|
{
|
|
if ( !IsAlive( titan ) )
|
|
return 0.0
|
|
|
|
local soul = titan.GetTitanSoul()
|
|
|
|
if ( !soul )
|
|
return 0.0
|
|
|
|
local shieldHealth = soul.GetShieldHealth()
|
|
local shieldMaxHealth = soul.GetShieldHealthMax()
|
|
|
|
if ( !shieldMaxHealth )
|
|
return 0.0
|
|
|
|
return shieldHealth.tofloat() / shieldMaxHealth.tofloat()
|
|
}
|
|
|
|
function EvacEnabled()
|
|
{
|
|
if ( !IsMultiplayer() )
|
|
return false
|
|
|
|
if ( GetCinematicMode() && GetMapName() == "mp_o2" ) //Special case for O2: No evac in campaign mode since EVERYONE DIES
|
|
return false
|
|
|
|
if ( GAMETYPE == COOPERATIVE )
|
|
return false
|
|
|
|
return true
|
|
}
|
|
|
|
function ShouldRunEvac()
|
|
{
|
|
Assert( GetGameState() >= eGameState.WinnerDetermined )
|
|
|
|
if ( !EvacEnabled() )
|
|
return false
|
|
|
|
if ( !level.evacEnabled )
|
|
return false
|
|
|
|
if ( level.privateMatchForcedEnd )
|
|
return false
|
|
|
|
if ( GameRules.GetGameMode() == "exfil" )
|
|
return false
|
|
|
|
if ( GameRules.GetGameMode() == MARKED_FOR_DEATH_PRO )
|
|
return false
|
|
|
|
if ( GameRules.GetGameMode() == CAPTURE_THE_FLAG_PRO )
|
|
return false
|
|
|
|
if ( GameRules.GetGameMode() == LAST_TITAN_STANDING || GameRules.GetGameMode() == WINGMAN_LAST_TITAN_STANDING )
|
|
{
|
|
if ( IsPilotEliminationBased() )
|
|
return false
|
|
}
|
|
|
|
local winningTeam = GetWinningTeam()
|
|
if ( winningTeam == TEAM_UNASSIGNED )
|
|
return false
|
|
|
|
if ( !GetCurrentPlaylistVarInt( "run_evac", 0 ) ) //If playlist var to always run evac don't do check for players being on both team
|
|
{
|
|
// We don't care about player count when playing coop
|
|
if ( GameRules.GetGameMode() == COOPERATIVE )
|
|
return true
|
|
|
|
//Return false if there are no players connected on either team
|
|
local losingTeam = GetOtherTeam( winningTeam )
|
|
|
|
if ( GetPlayerArrayOfTeam( winningTeam ).len() == 0 )
|
|
return false
|
|
|
|
if ( GetPlayerArrayOfTeam( losingTeam ).len() == 0 )
|
|
return false
|
|
|
|
if ( IsPilotEliminationBased() && IsTeamEliminated( losingTeam ) && GameTeams.GetNumLivingPlayers( losingTeam ) == 0 )
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
|
|
function HackGetDeltaToRef( origin, angles, ent, anim )
|
|
{
|
|
local animStartPos = ent.Anim_GetStartForRefPoint( anim, origin, angles )
|
|
local delta = origin - animStartPos.origin
|
|
return origin + delta
|
|
}
|
|
|
|
function GetPlayerViewTrace( player )
|
|
{
|
|
local traceStart = player.EyePosition()
|
|
local traceEnd = traceStart + (player.GetViewVector() * 56756) // longest possible trace given our map size limits
|
|
local ignoreEnts = [ player ]
|
|
return TraceLine( traceStart, traceEnd, ignoreEnts, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_NONE )
|
|
}
|
|
|
|
function IsAttritionMode()
|
|
{
|
|
return GameRules.GetGameMode() == ATTRITION
|
|
}
|
|
|
|
function IsTDMMode()
|
|
{
|
|
return GameRules.GetGameMode() == TEAM_DEATHMATCH
|
|
}
|
|
|
|
function IsCaptureMode()
|
|
{
|
|
return GameRules.GetGameMode() == CAPTURE_POINT
|
|
}
|
|
|
|
function GetModSourceID( modString )
|
|
{
|
|
foreach ( name, id in getconsttable().eModSourceId )
|
|
{
|
|
if ( name.tostring() == modString )
|
|
return id
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
function ArrayRemoveDead( array )
|
|
{
|
|
Assert( type( array ) == "array" )
|
|
for( local i = 0; i < array.len(); )
|
|
{
|
|
if ( !IsAlive( array[ i ] ) )
|
|
{
|
|
array.remove( i )
|
|
continue // don't increment i
|
|
}
|
|
i++
|
|
}
|
|
}
|
|
|
|
function GetSortedPlayers( compareFunc, team )
|
|
{
|
|
local players
|
|
|
|
if ( team )
|
|
players = GetPlayerArrayOfTeam( team )
|
|
else
|
|
players = GetPlayerArray()
|
|
|
|
players.sort( compareFunc )
|
|
|
|
return players
|
|
}
|
|
|
|
|
|
// Sorts by kills and resolves ties in this order: fewest deaths, most titan kills, most assists
|
|
function CompareKills( a, b )
|
|
{
|
|
local aKills = a.GetKillCount()
|
|
local bKills = b.GetKillCount()
|
|
|
|
if ( aKills < bKills )
|
|
return 1
|
|
else if ( aKills > bKills )
|
|
return -1
|
|
|
|
local aDeaths = a.GetDeathCount()
|
|
local bDeaths = b.GetDeathCount()
|
|
|
|
if ( aDeaths > bDeaths )
|
|
return 1
|
|
else if ( aDeaths < bDeaths )
|
|
return -1
|
|
|
|
local aTitanKills = a.GetTitanKillCount()
|
|
local bTitanKills = b.GetTitanKillCount()
|
|
|
|
if ( aTitanKills < bTitanKills )
|
|
return 1
|
|
else if ( aTitanKills > bTitanKills )
|
|
return -1
|
|
|
|
local aAssists = a.GetAssistCount()
|
|
local bAssists = b.GetAssistCount()
|
|
|
|
if ( aAssists < bAssists )
|
|
return 1
|
|
else if ( aAssists > bAssists )
|
|
return -1
|
|
|
|
return 0
|
|
}
|
|
|
|
// Sorts by kills and resolves ties in this order: fewest deaths, most titan kills, most assists
|
|
function CompareAssaultScore( a, b )
|
|
{
|
|
local aVal = a.GetAssaultScore()
|
|
local bVal = b.GetAssaultScore()
|
|
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
return 0
|
|
}
|
|
|
|
function CompareScore( a, b )
|
|
{
|
|
local aVal = a.GetScore()
|
|
local bVal = b.GetScore()
|
|
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
return 0
|
|
}
|
|
|
|
function CompareAssault( a, b )
|
|
{
|
|
local aVal = a.GetAssaultScore()
|
|
local bVal = b.GetAssaultScore()
|
|
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
return 0
|
|
}
|
|
|
|
function CompareDefense( a, b )
|
|
{
|
|
local aVal = a.GetDefenseScore()
|
|
local bVal = b.GetDefenseScore()
|
|
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
return 0
|
|
}
|
|
|
|
function CompareLTS( a, b )
|
|
{
|
|
local result = CompareTitanKills( a, b )
|
|
if ( result != 0 )
|
|
return result
|
|
|
|
return CompareKills( a, b )
|
|
}
|
|
|
|
function CompareCP( a, b )
|
|
{
|
|
// Capture Point sorting. Sort priority = assault + defense > pilot kills > titan kills > death
|
|
|
|
local aVal = a.GetAssaultScore()
|
|
local bVal = b.GetAssaultScore()
|
|
|
|
aVal += a.GetDefenseScore()
|
|
bVal += b.GetDefenseScore()
|
|
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 3) Pilot Kills
|
|
local aVal = a.GetKillCount()
|
|
local bVal = b.GetKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 3) Titan Kills
|
|
aVal = a.GetTitanKillCount()
|
|
bVal = b.GetTitanKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 4) Deaths
|
|
aVal = a.GetDeathCount()
|
|
bVal = b.GetDeathCount()
|
|
if ( aVal < bVal )
|
|
return -1
|
|
else if ( aVal > bVal )
|
|
return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
function CompareCTF( a, b )
|
|
{
|
|
// Capture the flag sorting. Sort priority = flag captures > flag returns > pilot kills > titan kills > death
|
|
|
|
// 1) Flag Captures
|
|
local result = CompareAssault( a, b )
|
|
if ( result != 0 )
|
|
return result
|
|
|
|
// 2) Flag Returns
|
|
result = CompareDefense( a, b )
|
|
if ( result != 0 )
|
|
return result
|
|
|
|
// 3) Pilot Kills
|
|
local aVal = a.GetKillCount()
|
|
local bVal = b.GetKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 3) Titan Kills
|
|
aVal = a.GetTitanKillCount()
|
|
bVal = b.GetTitanKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 4) Deaths
|
|
aVal = a.GetDeathCount()
|
|
bVal = b.GetDeathCount()
|
|
if ( aVal < bVal )
|
|
return -1
|
|
else if ( aVal > bVal )
|
|
return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
function CompareMFD( a,b )
|
|
{
|
|
// 1) Marks Killed
|
|
local result = CompareAssault( a, b )
|
|
if ( result != 0 )
|
|
return result
|
|
|
|
// 2) Marks Outlasted
|
|
result = CompareDefense( a, b )
|
|
if ( result != 0 )
|
|
return result
|
|
|
|
//3) Pilot Kills
|
|
local aVal = a.GetKillCount()
|
|
local bVal = b.GetKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 4) Titan Kills
|
|
aVal = a.GetTitanKillCount()
|
|
bVal = b.GetTitanKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 5) Deaths
|
|
aVal = a.GetDeathCount()
|
|
bVal = b.GetDeathCount()
|
|
if ( aVal < bVal )
|
|
return -1
|
|
else if ( aVal > bVal )
|
|
return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
function CompareScavenger( a,b )
|
|
{
|
|
// 1) Ore Captured
|
|
local result = CompareAssault( a, b )
|
|
if ( result != 0 )
|
|
return result
|
|
|
|
// 2) Pilot Kills
|
|
local aVal = a.GetKillCount()
|
|
local bVal = b.GetKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 3) Titan Kills
|
|
aVal = a.GetTitanKillCount()
|
|
bVal = b.GetTitanKillCount()
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
// 4) Deaths
|
|
aVal = a.GetDeathCount()
|
|
bVal = b.GetDeathCount()
|
|
if ( aVal < bVal )
|
|
return -1
|
|
else if ( aVal > bVal )
|
|
return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
function CompareTitanKills( a, b )
|
|
{
|
|
local aVal = a
|
|
local bVal = b
|
|
|
|
if ( IsServer() )
|
|
{
|
|
aVal = a.GetTitanKillCount()
|
|
bVal = b.GetTitanKillCount()
|
|
}
|
|
else
|
|
{
|
|
aVal = a.GetTitanKills()
|
|
bVal = b.GetTitanKills()
|
|
}
|
|
|
|
if ( aVal < bVal )
|
|
return 1
|
|
else if ( aVal > bVal )
|
|
return -1
|
|
|
|
return 0
|
|
}
|
|
|
|
function GetScoreboardCompareFunc( player = null )
|
|
{
|
|
return ScoreboardCompareFuncForGamemode( GameRules.GetGameMode() )
|
|
}
|
|
|
|
function ScoreboardCompareFuncForGamemode( gamemode )
|
|
{
|
|
switch ( gamemode )
|
|
{
|
|
case TEAM_DEATHMATCH:
|
|
case PILOT_SKIRMISH:
|
|
return CompareKills
|
|
case CAPTURE_POINT:
|
|
return CompareCP
|
|
case MARKED_FOR_DEATH:
|
|
case MARKED_FOR_DEATH_PRO:
|
|
return CompareMFD
|
|
case ATTRITION:
|
|
case COOPERATIVE:
|
|
return CompareAssaultScore
|
|
case CAPTURE_THE_FLAG:
|
|
case CAPTURE_THE_FLAG_PRO:
|
|
return CompareCTF
|
|
case LAST_TITAN_STANDING:
|
|
case WINGMAN_LAST_TITAN_STANDING:
|
|
return CompareLTS
|
|
case SCAVENGER:
|
|
return CompareScavenger
|
|
case EXFILTRATION:
|
|
case ELIMINATION:
|
|
case ATDM:
|
|
case BIG_BROTHER:
|
|
case HEIST:
|
|
default:
|
|
return CompareScore
|
|
}
|
|
}
|
|
|
|
function IsHitEffective( damageType )
|
|
{
|
|
if ( !( damageType & DF_CRITICAL ) && ( damageType & DF_BULLET || damageType & DF_MAX_RANGE ) )
|
|
return false
|
|
|
|
return true
|
|
}
|
|
|
|
function CreateFirstPersonSequence()
|
|
{
|
|
local sequence = {}
|
|
sequence.firstPersonAnim <- null
|
|
sequence.thirdPersonAnim <- null
|
|
sequence.firstPersonAnimIdle <- null
|
|
sequence.thirdPersonAnimIdle <- null
|
|
sequence.relativeAnim <- null
|
|
sequence.attachment <- null
|
|
sequence.teleport <- null
|
|
sequence.noParent <- null
|
|
sequence.blendTime <- null
|
|
sequence.noViewLerp <- null
|
|
sequence.hideProxy <- null
|
|
sequence.viewConeFunction <- null
|
|
sequence.origin <- null
|
|
sequence.angles <- null
|
|
sequence.enablePlanting <- null
|
|
sequence.newSequence <- null
|
|
sequence.setInitialTime <- null //set the starting point of the animation in seconds
|
|
sequence.useAnimatedRefAttachment <- false //Position entity using ref every frame instead of using root motion
|
|
sequence.renderWithViewModels <- false
|
|
sequence.gravity <- null // force gravity command on sequence
|
|
|
|
return sequence
|
|
}
|
|
|
|
function IsPilot( ent )
|
|
{
|
|
if ( IsValid( ent ) && !ent.IsTitan() && ent.IsPlayer() )
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function HardpointIDToString( id )
|
|
{
|
|
local hardpointIDString = [ "a", "b", "c" ]
|
|
|
|
if( id >= 0 && id < hardpointIDString.len() )
|
|
return hardpointIDString[ id ]
|
|
else
|
|
return -1
|
|
}
|
|
|
|
function Dev_TeamIDToString( id )
|
|
{
|
|
if ( id == TEAM_IMC )
|
|
return "IMC"
|
|
if ( id == TEAM_MILITIA )
|
|
return "MIL"
|
|
|
|
return "UNASSIGNED/UNKNOWN TEAM NAME"
|
|
}
|
|
|
|
function ArrayWithin( array, origin, maxDist )
|
|
{
|
|
maxDist *= maxDist
|
|
|
|
local resultArray = []
|
|
foreach( ent in array )
|
|
{
|
|
local dist = DistanceSqr( origin, ent.GetOrigin() )
|
|
if ( dist <= maxDist )
|
|
resultArray.append( ent )
|
|
}
|
|
return resultArray
|
|
}
|
|
RegisterFunctionDesc( "ArrayWithin", "Remove ents from array that are out of range" )
|
|
|
|
// short cut for the console
|
|
//script gp()[0].Die( gp()[1])
|
|
gp <- GetPlayerArray
|
|
|
|
function GetTitanChassis( titan )
|
|
{
|
|
if ( !("titanChassis" in titan.s ) )
|
|
{
|
|
if ( HasSoul( titan ) )
|
|
{
|
|
local soul = titan.GetTitanSoul()
|
|
titan.s.titanChassis <- GetSoulTitanType( soul )
|
|
}
|
|
else
|
|
{
|
|
return "Invalid Chassis"
|
|
}
|
|
}
|
|
|
|
return titan.s.titanChassis
|
|
}
|
|
|
|
function ClampVectorToCube( vecStart, vec, cubeOrigin, cubeSize )
|
|
{
|
|
local smallestClampScale = 1.0
|
|
local vecComponents = [ "x", "y", "z" ]
|
|
local min
|
|
local max
|
|
local scale
|
|
local clearance
|
|
local vecEnd = vecStart + vec
|
|
|
|
foreach( component in vecComponents )
|
|
{
|
|
//+
|
|
max = cubeOrigin[component] + ( cubeSize * 0.5 )
|
|
clearance = fabs( vecStart[component] - max )
|
|
if ( vecEnd[component] > max )
|
|
{
|
|
scale = fabs( clearance / ( ( vecStart[component] + vec[component] ) - vecStart[component] ) )
|
|
if ( scale > 0 && scale < smallestClampScale )
|
|
smallestClampScale = scale
|
|
}
|
|
|
|
//-
|
|
min = cubeOrigin[component] - ( cubeSize * 0.5 )
|
|
clearance = fabs( min - vecStart[component] )
|
|
if ( vecEnd[component] < min )
|
|
{
|
|
scale = fabs( clearance / ( ( vecStart[component] + vec[component] ) - vecStart[component] ) )
|
|
if ( scale > 0 && scale < smallestClampScale )
|
|
smallestClampScale = scale
|
|
}
|
|
}
|
|
|
|
return vec * smallestClampScale
|
|
}
|
|
|
|
function DebugDrawCube( cubeCenter, cubeSize, r, g, b, throughGeo, duration )
|
|
{
|
|
local corner0 = cubeCenter + Vector( -cubeSize * 0.5, -cubeSize * 0.5, cubeSize * 0.5 )
|
|
local corner1 = cubeCenter + Vector( cubeSize * 0.5, -cubeSize * 0.5, cubeSize * 0.5 )
|
|
local corner2 = cubeCenter + Vector( -cubeSize * 0.5, cubeSize * 0.5, cubeSize * 0.5 )
|
|
local corner3 = cubeCenter + Vector( cubeSize * 0.5, cubeSize * 0.5, cubeSize * 0.5 )
|
|
local corner4 = cubeCenter + Vector( -cubeSize * 0.5, -cubeSize * 0.5, -cubeSize * 0.5 )
|
|
local corner5 = cubeCenter + Vector( cubeSize * 0.5, -cubeSize * 0.5, -cubeSize * 0.5 )
|
|
local corner6 = cubeCenter + Vector( -cubeSize * 0.5, cubeSize * 0.5, -cubeSize * 0.5 )
|
|
local corner7 = cubeCenter + Vector( cubeSize * 0.5, cubeSize * 0.5, -cubeSize * 0.5 )
|
|
|
|
// top
|
|
DebugDrawLine( corner0, corner1, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner1, corner3, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner2, corner0, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner3, corner2, r, g, b, throughGeo, duration )
|
|
|
|
// bottom
|
|
DebugDrawLine( corner4, corner5, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner5, corner7, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner6, corner4, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner7, corner6, r, g, b, throughGeo, duration )
|
|
|
|
// sides
|
|
DebugDrawLine( corner0, corner4, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner1, corner5, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner2, corner6, r, g, b, throughGeo, duration )
|
|
DebugDrawLine( corner3, corner7, r, g, b, throughGeo, duration )
|
|
}
|
|
|
|
function DebugDrawAngles( position, angles, duration = 9999.0 )
|
|
{
|
|
local forward = angles.AnglesToForward()
|
|
local right = angles.AnglesToRight()
|
|
local up = angles.AnglesToUp()
|
|
|
|
DebugDrawLine( position, position + ( forward * 100.0 ), 255, 0, 0, true, duration )
|
|
DebugDrawLine( position, position + ( right * 100.0 ), 0, 255, 0, true, duration )
|
|
DebugDrawLine( position, position + ( up * 100.0 ), 0, 0, 255, true, duration )
|
|
}
|
|
|
|
function AngleDiff( ang, targetAng )
|
|
{
|
|
local delta = ( targetAng - ang ) % 360.0
|
|
if ( targetAng > ang )
|
|
{
|
|
if ( delta >= 180.0 )
|
|
delta -= 360.0;
|
|
}
|
|
else
|
|
{
|
|
if ( delta <= -180.0 )
|
|
delta += 360.0;
|
|
}
|
|
return delta
|
|
}
|
|
|
|
function ShortestRotation( ang, targetAng )
|
|
{
|
|
return Vector( AngleDiff( ang.x, targetAng.x ), AngleDiff( ang.y, targetAng.y ), AngleDiff( ang.z, targetAng.z ) )
|
|
}
|
|
|
|
function GetWinningTeam()
|
|
{
|
|
if ( level.nv.winningTeam )
|
|
return level.nv.winningTeam
|
|
|
|
if ( GameRules.GetTeamScore( TEAM_IMC ) > GameRules.GetTeamScore( TEAM_MILITIA ) )
|
|
return TEAM_IMC
|
|
|
|
if ( GameRules.GetTeamScore( TEAM_MILITIA ) > GameRules.GetTeamScore( TEAM_IMC ) )
|
|
return TEAM_MILITIA
|
|
|
|
return TEAM_UNASSIGNED
|
|
}
|
|
|
|
function EmitSkyboxSoundAtPosition( positionInSkybox, sound, skyboxScale = 0.001, clamp = false )
|
|
{
|
|
if ( IsServer() )
|
|
clamp = true // sounds cannot play outside 16k limit on server
|
|
local position = SkyboxToWorldPosition( positionInSkybox, skyboxScale, clamp )
|
|
EmitSoundAtPosition( position, sound )
|
|
}
|
|
|
|
function SkyboxToWorldPosition( positionInSkybox, skyboxScale = 0.001, clamp = true )
|
|
{
|
|
Assert( skyboxScale > 0 )
|
|
Assert( "skyboxCamOrigin" in level )
|
|
|
|
local position = Vector( 0.0, 0.0, 0.0 )
|
|
local skyOrigin = level.skyboxCamOrigin
|
|
|
|
if ( IsClient() )
|
|
{
|
|
|
|
position = ( positionInSkybox - skyOrigin ) * ( 1.0 / skyboxScale )
|
|
|
|
if ( clamp )
|
|
{
|
|
local localViewPlayer = GetLocalViewPlayer()
|
|
Assert( localViewPlayer )
|
|
local localViewPlayerOrg = localViewPlayer.GetOrigin()
|
|
|
|
position = localViewPlayerOrg + ClampVectorToCube( localViewPlayerOrg, position - localViewPlayerOrg, Vector( 0.0, 0.0, 0.0 ), 32000.0 )
|
|
}
|
|
}
|
|
else
|
|
{
|
|
position = ( positionInSkybox - skyOrigin ) * ( 1.0 / skyboxScale )
|
|
|
|
if ( clamp )
|
|
position = ClampVectorToCube( Vector( 0.0, 0.0, 0.0 ), position, Vector( 0.0, 0.0, 0.0 ), 32000.0 )
|
|
}
|
|
|
|
return position
|
|
}
|
|
|
|
function GetRandomKeyFromWeightedTable( table, weightTotal )
|
|
{
|
|
local randomValue = RandomFloat( 0, weightTotal )
|
|
|
|
foreach ( key, value in table )
|
|
{
|
|
if ( randomValue <= weightTotal && randomValue >= weightTotal - value)
|
|
return key
|
|
weightTotal -= value
|
|
}
|
|
}
|
|
|
|
function IsMatchOver()
|
|
{
|
|
if ( IsRoundBased() && level.nv.gameEndTime )
|
|
return true
|
|
else if ( !IsRoundBased() && level.nv.gameEndTime && Time() > level.nv.gameEndTime )
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function IsRoundBased()
|
|
{
|
|
return level.nv.roundBased
|
|
}
|
|
|
|
function IsAttackDefendBased()
|
|
{
|
|
return level.attackDefendBased
|
|
}
|
|
|
|
function GetRoundsPlayed()
|
|
{
|
|
return level.nv.roundsPlayed
|
|
}
|
|
|
|
function IsEliminationBased()
|
|
{
|
|
return Riff_EliminationMode() != eEliminationMode.Default
|
|
}
|
|
|
|
function IsPilotEliminationBased()
|
|
{
|
|
return ( Riff_EliminationMode() == eEliminationMode.Pilots || Riff_EliminationMode() == eEliminationMode.PilotsTitans )
|
|
}
|
|
|
|
function IsTitanEliminationBased()
|
|
{
|
|
return ( Riff_EliminationMode() == eEliminationMode.Titans || Riff_EliminationMode() == eEliminationMode.PilotsTitans )
|
|
}
|
|
|
|
function IsSuddenDeathGameMode()
|
|
{
|
|
if ( GAMETYPE == CAPTURE_THE_FLAG ) //|| GAMETYPE == TEAM_DEATHMATCH )
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function ShouldEnterSuddenDeath( winningTeam )
|
|
{
|
|
if ( GetGameState() == eGameState.SuddenDeath )
|
|
return false
|
|
|
|
if ( !( winningTeam == null || winningTeam == TEAM_UNASSIGNED ) )
|
|
return false
|
|
|
|
if ( !IsSuddenDeathGameMode() )
|
|
return false
|
|
|
|
if ( GetTeamPlayerCount( TEAM_MILITIA ) == 0 || GetTeamPlayerCount( TEAM_IMC ) == 0 )
|
|
return false
|
|
|
|
return true
|
|
}
|
|
|
|
const ATTRITION_SCORE_TITAN = 5
|
|
const ATTRITION_SCORE_PILOT = 4
|
|
const ATTRITION_SCORE_GRUNT = 1
|
|
const ATTRITION_SCORE_SPECTRE = 1
|
|
const ATTRITION_SCORE_MARVIN = 0
|
|
|
|
function AttritionScoreValueForVictim( victim )
|
|
{
|
|
if ( victim.IsPlayer() )
|
|
return ATTRITION_SCORE_PILOT
|
|
else if ( victim.IsTitan() && !victim.IsPlayer() )
|
|
return ATTRITION_SCORE_TITAN
|
|
else if ( victim.IsNPC() && victim.IsSpectre() )
|
|
return ATTRITION_SCORE_SPECTRE
|
|
else if ( victim.IsNPC() && victim.IsMarvin() )
|
|
return ATTRITION_SCORE_MARVIN
|
|
else if ( victim.IsNPC() )
|
|
return ATTRITION_SCORE_GRUNT
|
|
return 0
|
|
}
|
|
|
|
function GetAttritionScore( attacker, victim )
|
|
{
|
|
local scoreVal = 0
|
|
if ( attacker == victim )
|
|
return scoreVal
|
|
|
|
if ( IsValid( victim ) )
|
|
{
|
|
return AttritionScoreValueForVictim( victim )
|
|
}
|
|
|
|
return scoreVal
|
|
}
|
|
|
|
function __WarpInEffectShared( origin, angles, sfx )
|
|
{
|
|
local preWait = 2.0
|
|
local sfxWait = 0.1
|
|
local totalTime = WARPINFXTIME
|
|
|
|
if ( sfx == null )
|
|
sfx = "dropship_warpin"
|
|
|
|
wait preWait //this needs to go and the const for warpin fx time needs to change - but not this game - the intro system is too dependent on it
|
|
|
|
if ( IsClient() )
|
|
{
|
|
local fxIndex = GetParticleSystemIndex( FX_GUNSHIP_CRASH_EXPLOSION_ENTRANCE )
|
|
StartParticleEffectInWorld( fxIndex, origin, angles )
|
|
}
|
|
else
|
|
{
|
|
local fx = PlayFX( FX_GUNSHIP_CRASH_EXPLOSION_ENTRANCE, origin, angles )
|
|
fx.EnableRenderAlways()
|
|
}
|
|
|
|
wait sfxWait
|
|
EmitSoundAtPosition( origin, sfx )
|
|
|
|
wait totalTime - preWait -sfxWait
|
|
}
|
|
|
|
function __WarpOutEffectShared( dropship )
|
|
{
|
|
local attach = dropship.LookupAttachment( "origin" )
|
|
local origin = dropship.GetAttachmentOrigin( attach )
|
|
local angles = dropship.GetAttachmentAngles( attach )
|
|
|
|
if ( IsClient() )
|
|
{
|
|
local fxIndex = GetParticleSystemIndex( FX_GUNSHIP_CRASH_EXPLOSION_EXIT )
|
|
StartParticleEffectInWorld( fxIndex, origin, angles )
|
|
}
|
|
else
|
|
{
|
|
local fx = PlayFX( FX_GUNSHIP_CRASH_EXPLOSION_EXIT, origin, angles )
|
|
fx.EnableRenderAlways()
|
|
}
|
|
|
|
EmitSoundAtPosition( origin, "dropship_warpout" )
|
|
}
|
|
|
|
function IsSwitchSidesBased()
|
|
{
|
|
return (level.nv.switchedSides != null)
|
|
}
|
|
|
|
function HasSwitchedSides()
|
|
{
|
|
return level.nv.switchedSides
|
|
}
|
|
|
|
function IsFirstRoundAfterSwitchingSides()
|
|
{
|
|
if ( !IsSwitchSidesBased() )
|
|
return false
|
|
|
|
if ( IsRoundBased() )
|
|
return level.nv.switchedSides > 0 && GetRoundsPlayed() == level.nv.switchedSides
|
|
else
|
|
return level.nv.switchedSides > 0
|
|
}
|
|
|
|
function CamBlendFov( cam, oldFov, newFov, transTime, transAccel, transDecel )
|
|
{
|
|
if ( !IsValid( cam ) )
|
|
return
|
|
|
|
cam.EndSignal( "OnDestroy" )
|
|
|
|
local currentTime = Time()
|
|
local startTime = currentTime
|
|
local endTime = startTime + transTime
|
|
|
|
while ( endTime > currentTime )
|
|
{
|
|
local interp = Interpolate( startTime, endTime - startTime, transAccel, transDecel )
|
|
cam.SetFOV( GraphCapped( interp, 0.0, 1.0, oldFov, newFov ) )
|
|
wait( 0.0 )
|
|
currentTime = Time()
|
|
}
|
|
}
|
|
|
|
function CamFollowEnt( cam, ent, duration, offset = Vector( 0.0, 0.0, 0.0 ), attachment = "", isInSkybox = false )
|
|
{
|
|
if ( !IsValid( cam ) )
|
|
return
|
|
|
|
cam.EndSignal( "OnDestroy" )
|
|
|
|
local camOrg = Vector( 0.0, 0.0, 0.0 )
|
|
|
|
local targetPos = Vector( 0.0, 0.0, 0.0 )
|
|
local currentTime = Time()
|
|
local startTime = currentTime
|
|
local endTime = startTime + duration
|
|
local diff = Vector( 0.0, 0.0, 0.0 )
|
|
local attachID = ent.LookupAttachment( attachment )
|
|
|
|
while ( endTime > currentTime )
|
|
{
|
|
camOrg = cam.GetOrigin()
|
|
|
|
if ( attachID <= 0 )
|
|
targetPos = ent.GetOrigin()
|
|
else
|
|
targetPos = ent.GetAttachmentOrigin( attachID )
|
|
|
|
if ( isInSkybox )
|
|
targetPos = SkyboxToWorldPosition( targetPos )
|
|
diff = ( targetPos + offset ) - camOrg
|
|
|
|
cam.SetAngles( VectorToAngles( diff ) )
|
|
|
|
wait( 0.0 )
|
|
|
|
currentTime = Time()
|
|
}
|
|
}
|
|
|
|
function CamFacePos( cam, pos, duration )
|
|
{
|
|
if ( !IsValid( cam ) )
|
|
return
|
|
|
|
cam.EndSignal( "OnDestroy" )
|
|
|
|
local currentTime = Time()
|
|
local startTime = currentTime
|
|
local endTime = startTime + duration
|
|
local diff = Vector( 0.0, 0.0, 0.0 )
|
|
|
|
while ( endTime > currentTime )
|
|
{
|
|
diff = pos - cam.GetOrigin()
|
|
|
|
cam.SetAngles( VectorToAngles( diff ) )
|
|
|
|
wait( 0.0 )
|
|
|
|
currentTime = Time()
|
|
}
|
|
}
|
|
|
|
function CamBlendFromFollowToAng( cam, ent, endAng, transTime, transAccel, transDecel )
|
|
{
|
|
if ( !IsValid( cam ) )
|
|
return
|
|
|
|
cam.EndSignal( "OnDestroy" )
|
|
|
|
local camOrg = cam.GetOrigin()
|
|
|
|
local currentTime = Time()
|
|
local startTime = currentTime
|
|
local endTime = startTime + transTime
|
|
|
|
while ( endTime > currentTime )
|
|
{
|
|
local diff = ent.GetOrigin() - camOrg
|
|
local anglesToEnt = VectorToAngles( diff )
|
|
|
|
local frac = Interpolate( startTime, endTime - startTime, transAccel, transDecel )
|
|
|
|
local newAngs = anglesToEnt + ShortestRotation( anglesToEnt, endAng ) * frac
|
|
|
|
cam.SetAngles( newAngs )
|
|
|
|
wait( 0.0 )
|
|
|
|
currentTime = Time()
|
|
}
|
|
}
|
|
|
|
function CamBlendFromPosToPos( cam, startPos, endPos, transTime, transAccel, transDecel )
|
|
{
|
|
if ( !IsValid( cam ) )
|
|
return
|
|
|
|
cam.EndSignal( "OnDestroy" )
|
|
|
|
local currentTime = Time()
|
|
local startTime = currentTime
|
|
local endTime = startTime + transTime
|
|
local diff = endPos - startPos
|
|
|
|
while ( endTime > currentTime )
|
|
{
|
|
local frac = Interpolate( startTime, endTime - startTime, transAccel, transDecel )
|
|
|
|
local newAngs = startPos + diff * frac
|
|
|
|
cam.SetOrigin( newAngs )
|
|
|
|
wait( 0.0 )
|
|
|
|
currentTime = Time()
|
|
}
|
|
}
|
|
|
|
function CamBlendFromAngToAng( cam, startAng, endAng, transTime, transAccel, transDecel )
|
|
{
|
|
if ( !IsValid( cam ) )
|
|
return
|
|
|
|
cam.EndSignal( "OnDestroy" )
|
|
|
|
local currentTime = Time()
|
|
local startTime = currentTime
|
|
local endTime = startTime + transTime
|
|
|
|
while ( endTime > currentTime )
|
|
{
|
|
local frac = Interpolate( startTime, endTime - startTime, transAccel, transDecel )
|
|
|
|
local newAngs = startAng + ShortestRotation( startAng, endAng ) * frac
|
|
|
|
cam.SetAngles( newAngs )
|
|
|
|
wait( 0.0 )
|
|
|
|
currentTime = Time()
|
|
}
|
|
}
|
|
|
|
function AddBitMask( bitsExisting, bitsToAdd )
|
|
{
|
|
return bitsExisting | bitsToAdd
|
|
}
|
|
|
|
function RemoveBitMask( bitsExisting, bitsToRemove )
|
|
{
|
|
return bitsExisting & ( ~bitsToRemove )
|
|
}
|
|
|
|
function HasBitMask( bitsExisting, bitsToCheck )
|
|
{
|
|
local bitsCommon = bitsExisting & bitsToCheck
|
|
return bitsCommon == bitsToCheck
|
|
}
|
|
|
|
function GetDeathCamTime( player )
|
|
{
|
|
if ( !GamePlayingOrSuddenDeath() )
|
|
return DEATHCAM_TIME_SHORT
|
|
else
|
|
return DEATHCAM_TIME
|
|
}
|
|
|
|
function GetRespawnButtonCamTime( player )
|
|
{
|
|
if ( !GamePlayingOrSuddenDeath() )
|
|
return DEATHCAM_TIME_SHORT + RESPAWN_BUTTON_BUFFER
|
|
else
|
|
return DEATHCAM_TIME + RESPAWN_BUTTON_BUFFER
|
|
}
|
|
|
|
function GetKillReplayAfterTime( player )
|
|
{
|
|
if ( !GamePlayingOrSuddenDeath() )
|
|
return KILL_REPLAY_AFTER_KILL_TIME_SHORT
|
|
else
|
|
return KILL_REPLAY_AFTER_KILL_TIME
|
|
}
|
|
|
|
|
|
function CampaignLevelsComplete_IMC( player )
|
|
{
|
|
Assert( IsValid( player ) )
|
|
Assert( player.IsPlayer() )
|
|
local count = 0
|
|
for ( local i = 0 ; i < CAMPAIGN_LEVEL_COUNT ; i++ )
|
|
{
|
|
if ( player.GetPersistentVar( "campaignMapFinishedIMC[" + i + "]" ) )
|
|
count++
|
|
}
|
|
return count
|
|
}
|
|
|
|
function CampaignLevelsComplete_MIL( player )
|
|
{
|
|
Assert( IsValid( player ) )
|
|
Assert( player.IsPlayer() )
|
|
local count = 0
|
|
for ( local i = 0 ; i < CAMPAIGN_LEVEL_COUNT ; i++ )
|
|
{
|
|
if ( player.GetPersistentVar( "campaignMapFinishedMCOR[" + i + "]" ) )
|
|
count++
|
|
}
|
|
return count
|
|
}
|
|
|
|
function JustBeatCampaign( player, thisMapIndex )
|
|
{
|
|
local team = player.GetTeam()
|
|
if ( team == TEAM_IMC )
|
|
{
|
|
if( CampaignLevelsComplete_IMC( player ) < CAMPAIGN_LEVEL_COUNT )
|
|
return false
|
|
}
|
|
else
|
|
{
|
|
Assert( team == TEAM_MILITIA )
|
|
if( CampaignLevelsComplete_MIL( player ) < CAMPAIGN_LEVEL_COUNT )
|
|
return false
|
|
}
|
|
|
|
if ( thisMapIndex != CAMPAIGN_LEVEL_COUNT - 1 )
|
|
return false
|
|
|
|
return true
|
|
}
|
|
|
|
function JustBeatFinalCampaignLevel( player, thisMapIndex )
|
|
{
|
|
if ( thisMapIndex != CAMPAIGN_LEVEL_COUNT - 1 )
|
|
return false
|
|
|
|
return true
|
|
}
|
|
|
|
function IntroPreviewOn()
|
|
{
|
|
local bugnum = GetBugReproNum()
|
|
switch( bugnum )
|
|
{
|
|
case 1337:
|
|
case 13371:
|
|
case 13372:
|
|
case 13373:
|
|
case 1338:
|
|
case 13381:
|
|
case 13382:
|
|
case 13383:
|
|
return bugnum
|
|
|
|
default:
|
|
return null
|
|
}
|
|
}
|
|
|
|
function GetCPULevelWrapper()
|
|
{
|
|
return GetCPULevel()
|
|
}
|
|
|
|
function ResetNewPlayerExperience()
|
|
{
|
|
if ( IsClient() )
|
|
{
|
|
local player = GetLocalClientPlayer()
|
|
if ( player != GetLocalViewPlayer() || IsWatchingKillReplay() )
|
|
return
|
|
|
|
SetPlayerTrainingResumeChoice( -1 )
|
|
|
|
printt( "reset NPE on client" )
|
|
}
|
|
else
|
|
{
|
|
local players = GetPlayerArray()
|
|
if ( GetPlayerArray().len() != 1 )
|
|
return
|
|
|
|
local player = players[0]
|
|
|
|
local maxIdx = PersistenceGetEnumCount( "trainingModules" )
|
|
for ( local i = 0; i < maxIdx; i++ )
|
|
{
|
|
local setStr = "trainingModulesCompleted[" + i + "]"
|
|
player.SetPersistentVar( setStr, false )
|
|
}
|
|
|
|
printt( "reset NPE on server" )
|
|
}
|
|
}
|
|
|
|
function EntHasModelSet( ent )
|
|
{
|
|
local modelName = ent.GetModelName()
|
|
|
|
if ( modelName == "" )
|
|
return false
|
|
|
|
if ( modelName == "?" )
|
|
return false
|
|
|
|
return true
|
|
}
|
|
|
|
function UpdateDeaths( player )
|
|
{
|
|
if ( IsServer() )
|
|
return player.GetDeathCount()
|
|
return player.GetDeaths()
|
|
}
|
|
|
|
function UpdateKills( player )
|
|
{
|
|
if ( IsServer() )
|
|
return player.GetKillCount()
|
|
return player.GetKills()
|
|
}
|
|
|
|
function UpdateNPCKills( player )
|
|
{
|
|
if ( IsServer() )
|
|
return player.GetNPCKillCount()
|
|
return player.GetNPCKills()
|
|
}
|
|
|
|
function UpdateTitanKills( player )
|
|
{
|
|
if ( IsServer() )
|
|
return player.GetTitanKillCount()
|
|
return player.GetTitanKills()
|
|
}
|
|
|
|
function UpdateAssists( player )
|
|
{
|
|
if ( IsServer() )
|
|
return player.GetAssistCount()
|
|
return player.GetAssists()
|
|
}
|
|
|
|
function UpdateScore( player )
|
|
{
|
|
return player.GetScore()
|
|
}
|
|
|
|
function UpdateDefense( player )
|
|
{
|
|
return player.GetDefenseScore()
|
|
}
|
|
|
|
function UpdateAssault( player )
|
|
{
|
|
return player.GetAssaultScore()
|
|
}
|
|
|
|
function UpdateBadRepPresent()
|
|
{
|
|
local players = GetPlayerArray()
|
|
local found = false
|
|
|
|
foreach ( player in players )
|
|
{
|
|
if ( player.HasBadReputation() )
|
|
{
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
level.nv.badRepPresent = found
|
|
level.ui.badRepPresent = found
|
|
}
|
|
|
|
function GenerateTitanOSAlias( player, aliasPrefix, aliasSuffix )
|
|
{
|
|
local titanOSVoiceIndex = player.GetVoicePackIndex()
|
|
local titanOSEnumItemName = PersistenceGetEnumItemNameForIndex( "titanOS", titanOSVoiceIndex )
|
|
local modifiedAlias = aliasPrefix + TITAN_OS_VOICE_PACK[ titanOSEnumItemName ] + aliasSuffix
|
|
return modifiedAlias
|
|
}
|
|
|
|
|
|
function CreateScriptCylinderTrigger( origin, radius, top = null, bottom = null )
|
|
{
|
|
local trigger = CreateScriptRef( origin, Vector( 0, 0, 0 ) )
|
|
|
|
trigger.s.enabled <- false
|
|
trigger.s.radius <- radius
|
|
trigger.s.players <- {}
|
|
trigger.s.enterCallbacks <- []
|
|
trigger.s.leaveCallbacks <- []
|
|
trigger.s.top <- top
|
|
trigger.s.bottom <- bottom
|
|
|
|
thread CylinderTriggerThink( trigger )
|
|
|
|
return trigger
|
|
}
|
|
|
|
function ScriptTriggerEnable( trigger, state )
|
|
{
|
|
trigger.s.enabled = state
|
|
}
|
|
|
|
function CylinderTriggerThink( triggerEnt )
|
|
{
|
|
local wasEnabled = triggerEnt.s.enabled
|
|
local playerOrg = null
|
|
|
|
while ( IsValid( triggerEnt ) )
|
|
{
|
|
if ( !triggerEnt.s.enabled )
|
|
{
|
|
if ( wasEnabled )
|
|
{
|
|
local playersToRemove = [] // build an array since looping through a table and removing elements is undefined
|
|
foreach( player in triggerEnt.s.players )
|
|
{
|
|
playersToRemove.append( player )
|
|
}
|
|
|
|
foreach ( player in playersToRemove )
|
|
{
|
|
ScriptTriggerRemovePlayer( triggerEnt, player )
|
|
}
|
|
|
|
Assert( !triggerEnt.s.players.len() )
|
|
}
|
|
}
|
|
else
|
|
{
|
|
local players = GetPlayerArray()
|
|
|
|
foreach ( player in players )
|
|
{
|
|
if ( !IsAlive( player ) )
|
|
{
|
|
if ( player.GetEntIndex() in triggerEnt.s.players )
|
|
ScriptTriggerRemovePlayer( triggerEnt, player )
|
|
continue
|
|
}
|
|
|
|
playerOrg = player.GetOrigin()
|
|
|
|
if ( Distance2D( playerOrg, triggerEnt.GetOrigin() ) < triggerEnt.s.radius )
|
|
{
|
|
if ( triggerEnt.s.top != null && playerOrg.z > triggerEnt.s.top )
|
|
continue
|
|
|
|
if ( triggerEnt.s.bottom != null && playerOrg.z + 72.0 < triggerEnt.s.bottom )
|
|
continue
|
|
|
|
if ( !(player.GetEntIndex() in triggerEnt.s.players) )
|
|
ScriptTriggerAddPlayer( triggerEnt, player )
|
|
}
|
|
else if ( player.GetEntIndex() in triggerEnt.s.players )
|
|
{
|
|
ScriptTriggerRemovePlayer( triggerEnt, player )
|
|
}
|
|
}
|
|
}
|
|
|
|
wasEnabled = triggerEnt.s.enabled
|
|
wait 0
|
|
}
|
|
}
|
|
|
|
function ScriptTriggerRemovePlayer( triggerEnt, player )
|
|
{
|
|
Assert( player.GetEntIndex() in triggerEnt.s.players )
|
|
|
|
foreach ( callbackInfo in triggerEnt.s.leaveCallbacks )
|
|
{
|
|
callbackInfo.func.acall( [callbackInfo.scope, triggerEnt, player] )
|
|
}
|
|
|
|
delete triggerEnt.s.players[player.GetEntIndex()]
|
|
}
|
|
|
|
function ScriptTriggerAddPlayer( triggerEnt, player )
|
|
{
|
|
Assert( !(player.GetEntIndex() in triggerEnt.s.players) )
|
|
|
|
triggerEnt.s.players[player.GetEntIndex()] <- player
|
|
|
|
foreach ( callbackInfo in triggerEnt.s.enterCallbacks )
|
|
{
|
|
callbackInfo.func.acall( [callbackInfo.scope, triggerEnt, player] )
|
|
}
|
|
|
|
thread ScriptTriggerPlayerDisconnectThink( triggerEnt, player )
|
|
}
|
|
|
|
function ScriptTriggerPlayerDisconnectThink( triggerEnt, player )
|
|
{
|
|
triggerEnt.EndSignal( "OnDestroy" )
|
|
player.EndSignal( "Disconnected" )
|
|
|
|
OnThreadEnd(
|
|
function() : ( triggerEnt, player )
|
|
{
|
|
if ( !IsValid( triggerEnt ) )
|
|
return
|
|
|
|
if ( !IsValid( player ) )
|
|
return
|
|
|
|
if ( player.GetEntIndex() in triggerEnt.s.players )
|
|
ScriptTriggerRemovePlayer( triggerEnt, player )
|
|
}
|
|
)
|
|
|
|
player.WaitSignal( "Disconnected" )
|
|
}
|
|
|
|
function AddCallback_ScriptTriggerEnter( trigger, callbackFunc )
|
|
{
|
|
local callbackInfo = {}
|
|
callbackInfo.scope <- this
|
|
callbackInfo.func <- callbackFunc
|
|
|
|
trigger.s.enterCallbacks.append( callbackInfo )
|
|
}
|
|
|
|
function AddCallback_ScriptTriggerLeave( trigger, callbackFunc )
|
|
{
|
|
local callbackInfo = {}
|
|
callbackInfo.scope <- this
|
|
callbackInfo.func <- callbackFunc
|
|
|
|
trigger.s.leaveCallbacks.append( callbackInfo )
|
|
}
|
|
|
|
function SetWaveSpawnType( type )
|
|
{
|
|
level.waveSpawnType = type
|
|
}
|
|
|
|
function GetWaveSpawnType()
|
|
{
|
|
return level.waveSpawnType
|
|
}
|
|
|
|
function IsEMPTitan( npc )
|
|
{
|
|
return ( npc.GetSubclass() == eSubClass.empTitan )
|
|
}
|
|
|
|
function IsNukeTitan( npc )
|
|
{
|
|
return ( npc.GetSubclass() == eSubClass.nukeTitan )
|
|
}
|
|
|
|
function IsMortarTitan( npc )
|
|
{
|
|
return ( npc.GetSubclass() == eSubClass.mortarTitan )
|
|
}
|
|
|
|
function IsSuicideSpectre( npc )
|
|
{
|
|
return ( npc.GetSubclass() == eSubClass.suicideSpectre )
|
|
}
|
|
|
|
function IsSniperSpectre( npc )
|
|
{
|
|
return ( npc.GetSubclass() == eSubClass.sniperSpectre )
|
|
}
|
|
|
|
function IsBubbleShieldMinion( npc )
|
|
{
|
|
if ( npc.GetSubclass() == eSubClass.bubbleShieldGrunt )
|
|
return true
|
|
|
|
if ( npc.GetSubclass() == eSubClass.bubbleShieldSpectre )
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function GetTitanSubclassHudIcon( titan )
|
|
{
|
|
switch ( titan.GetSubclass() )
|
|
{
|
|
case eSubClass.NONE:
|
|
return "HUD/coop/coop_titan"
|
|
break
|
|
|
|
case eSubClass.empTitan:
|
|
return "HUD/coop/coop_emp_titan_square"
|
|
break
|
|
|
|
case eSubClass.mortarTitan:
|
|
return "HUD/coop/coop_mortar_titan_square"
|
|
break
|
|
|
|
case eSubClass.nukeTitan:
|
|
return "HUD/coop/coop_nuke_titan_square"
|
|
break
|
|
}
|
|
}
|
|
|
|
function GetRoundWinningKillEnabled()
|
|
{
|
|
return level.nv.roundWinningKillReplayEnabled
|
|
}
|
|
|
|
function HasRoundScoreLimitBeenReached() //Different from RoundScoreLimit_Complete in that it only checks to see if the score required has been reached. Allows us to use it on the client to cover 90% of the cases we want
|
|
{
|
|
if ( !IsRoundBased() )
|
|
return false
|
|
|
|
local roundLimit = GetRoundScoreLimit_FromPlaylist()
|
|
|
|
if ( !roundLimit )
|
|
return false
|
|
|
|
local militiaScore = GameRules.GetTeamScore2( TEAM_MILITIA )
|
|
local imcScore = GameRules.GetTeamScore2( TEAM_IMC )
|
|
|
|
if ( ( militiaScore >= roundLimit ) || ( imcScore >= roundLimit ) )
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function PointIsWithinBounds( point, mins, maxs )
|
|
{
|
|
Assert( mins.x < maxs.x )
|
|
Assert( mins.y < maxs.y )
|
|
Assert( mins.z < maxs.z )
|
|
|
|
return ( ( point.z >= mins.z && point.z <= maxs.z ) &&
|
|
( point.x >= mins.x && point.x <= maxs.x ) &&
|
|
( point.y >= mins.y && point.y <= maxs.y ) )
|
|
}
|
|
|
|
// Nexon bullshit
|
|
|
|
function GetTeamIndex(team)
|
|
{
|
|
if (team)
|
|
{
|
|
for (local count = 0; count < TEAM_COUNT; ++count)
|
|
{
|
|
if (team & (1 << count))
|
|
return count
|
|
}
|
|
}
|
|
|
|
return TEAM_UNASSIGNED
|
|
}
|
|
|
|
function GetOtherTeams( guy )
|
|
{
|
|
local team
|
|
|
|
if ( typeof guy == "integer" )
|
|
team = guy
|
|
else
|
|
team = 1 << guy.GetTeam()
|
|
|
|
if (team & (1 << TEAM_IMC))
|
|
return (1 << TEAM_MILITIA)
|
|
|
|
if (team & (1 << TEAM_MILITIA))
|
|
return (1 << TEAM_IMC)
|
|
|
|
// if (team & (1 << TEAM_SPECTRE))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SURVIVAL7))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL0))
|
|
// return ((1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SURVIVAL7) | (1 << TEAM_SPECTRE))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL1))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SURVIVAL7) | (1 << TEAM_SPECTRE))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL2))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SURVIVAL7) | (1 << TEAM_SPECTRE))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL3))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SURVIVAL7) | (1 << TEAM_SPECTRE))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL4))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SURVIVAL7) | (1 << TEAM_SPECTRE))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL5))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SURVIVAL7) | (1 << TEAM_SPECTRE))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL6))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL7) | (1 << TEAM_SPECTRE))
|
|
|
|
// if (team & (1 << TEAM_SURVIVAL7))
|
|
// return ((1 << TEAM_SURVIVAL0) | (1 << TEAM_SURVIVAL1) | (1 << TEAM_SURVIVAL2) | (1 << TEAM_SURVIVAL3) | (1 << TEAM_SURVIVAL4) | (1 << TEAM_SURVIVAL5) | (1 << TEAM_SURVIVAL6) | (1 << TEAM_SPECTRE))
|
|
|
|
return (1 << TEAM_UNASSIGNED)
|
|
}
|
|
|
|
function IsWinningTeam(team)
|
|
{
|
|
if (!level.nv.winningTeam)
|
|
return false
|
|
|
|
if (level.nv.winningTeam == team)
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
function GetTitanBuildRule()
|
|
{
|
|
return 0
|
|
}
|