mirror of
https://github.com/JezuzLizard/t4sp_bot_warfare.git
synced 2025-04-23 15:05:43 +00:00
1674 lines
30 KiB
Plaintext
1674 lines
30 KiB
Plaintext
#include common_scripts\utility;
|
|
#include maps\_utility;
|
|
|
|
/*
|
|
Waits for the built-ins to be defined
|
|
*/
|
|
wait_for_builtins()
|
|
{
|
|
for ( i = 0; i < 20; i++ )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) )
|
|
return true;
|
|
|
|
if ( i < 18 )
|
|
waittillframeend;
|
|
else
|
|
wait 0.05;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Prints to console without dev script on
|
|
*/
|
|
BotBuiltinPrintConsole( s )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["printconsole"] ) )
|
|
{
|
|
[[ level.bot_builtins["printconsole" ]]]( s );
|
|
}
|
|
}
|
|
|
|
/*
|
|
Bot action, does a bot action
|
|
<client> botAction(<action string (+ or - then action like frag or smoke)>)
|
|
*/
|
|
BotBuiltinBotAction( action )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["botaction"] ) )
|
|
{
|
|
self [[ level.bot_builtins["botaction" ]]]( action );
|
|
}
|
|
}
|
|
|
|
/*
|
|
Clears the bot from movement and actions
|
|
<client> botStop()
|
|
*/
|
|
BotBuiltinBotStop()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["botstop"] ) )
|
|
{
|
|
self [[ level.bot_builtins["botstop" ]]]();
|
|
}
|
|
}
|
|
|
|
/*
|
|
Sets the bot's movement
|
|
<client> botMovement(<int left>, <int forward>)
|
|
*/
|
|
BotBuiltinBotMovement( left, forward )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["botmovement"] ) )
|
|
{
|
|
self [[ level.bot_builtins["botmovement" ]]]( left, forward );
|
|
}
|
|
}
|
|
|
|
/*
|
|
Test if is a bot
|
|
|
|
*/
|
|
BotBuiltinIsBot()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["isbot"] ) )
|
|
{
|
|
return self [[ level.bot_builtins["isbot" ]]]();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Generates a path
|
|
*/
|
|
BotBuiltinGeneratePath( from, to, team, best_effort )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["generatepath"] ) )
|
|
{
|
|
return [[ level.bot_builtins["generatepath" ]]]( from, to, team, best_effort );
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/*
|
|
Returns function pointer
|
|
*/
|
|
BotBuiltinGetFunction( file, threadname )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["getfunction"] ) )
|
|
{
|
|
return [[ level.bot_builtins["getfunction" ]]]( file, threadname );
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/*
|
|
waw sp doesnt have
|
|
*/
|
|
BotBuiltinGetMins()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["getmins"] ) )
|
|
{
|
|
return self [[ level.bot_builtins["getmins" ]]]();
|
|
}
|
|
|
|
return ( 0, 0, 0 );
|
|
}
|
|
|
|
/*
|
|
waw sp doesnt have
|
|
*/
|
|
BotBuiltinGetMaxs()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["getmaxs"] ) )
|
|
{
|
|
return self [[ level.bot_builtins["getmaxs" ]]]();
|
|
}
|
|
|
|
return ( 0, 0, 0 );
|
|
}
|
|
|
|
/*
|
|
waw sp doesnt have
|
|
*/
|
|
BotBuiltinGetGuid()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["getguid"] ) )
|
|
{
|
|
return self [[ level.bot_builtins["getguid" ]]]();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
BotBuiltinsSetAllowedTraversals( bot_allowed_negotiation_links )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["setallowedtraversals"] ) )
|
|
{
|
|
[[ level.bot_builtins["setallowedtraversals" ]]]( bot_allowed_negotiation_links );
|
|
}
|
|
}
|
|
|
|
/*
|
|
*/
|
|
BotBuiltinsSetIgnoredLinks( bot_ignore_links )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["setignoredlinks"] ) )
|
|
{
|
|
[[ level.bot_builtins["setignoredlinks" ]]]( bot_ignore_links );
|
|
}
|
|
}
|
|
|
|
/*
|
|
*/
|
|
BotBuiltinGetNodeNumber()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["getnodenumber"] ) )
|
|
{
|
|
return self [[ level.bot_builtins["getnodenumber" ]]]();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
BotBuiltinGetLinkedNodes()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["getlinkednodes"] ) )
|
|
{
|
|
return self [[ level.bot_builtins["getlinkednodes" ]]]();
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/*
|
|
*/
|
|
BotBuiltinAddTestClient()
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["addtestclient"] ) )
|
|
{
|
|
return [[ level.bot_builtins["addtestclient" ]]]();
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
BotBuiltinCmdExec( what )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["cmdexec"] ) )
|
|
{
|
|
[[ level.bot_builtins["cmdexec" ]]]( what );
|
|
}
|
|
}
|
|
|
|
/*
|
|
*/
|
|
BotBuiltinNotifyOnPlayerCommand( cmd, notif )
|
|
{
|
|
if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["notifyonplayercommand"] ) )
|
|
{
|
|
self [[ level.bot_builtins["notifyonplayercommand" ]]]( cmd, notif );
|
|
}
|
|
}
|
|
|
|
/*
|
|
Returns if player is the host
|
|
*/
|
|
is_host()
|
|
{
|
|
return ( isDefined( self.pers["bot_host"] ) && self.pers["bot_host"] );
|
|
}
|
|
|
|
/*
|
|
Setups the host variable on the player
|
|
*/
|
|
doHostCheck()
|
|
{
|
|
self.pers["bot_host"] = false;
|
|
|
|
if ( self is_bot() )
|
|
return;
|
|
|
|
result = false;
|
|
|
|
if ( getDvar( "bots_main_firstIsHost" ) != "0" )
|
|
{
|
|
BotBuiltinPrintConsole( "WARNING: bots_main_firstIsHost is enabled" );
|
|
|
|
if ( getDvar( "bots_main_firstIsHost" ) == "1" )
|
|
{
|
|
setDvar( "bots_main_firstIsHost", self BotBuiltinGetGuid() );
|
|
}
|
|
|
|
if ( getDvar( "bots_main_firstIsHost" ) == self BotBuiltinGetGuid() + "" )
|
|
result = true;
|
|
}
|
|
|
|
DvarGUID = getDvar( "bots_main_GUIDs" );
|
|
|
|
if ( DvarGUID != "" )
|
|
{
|
|
guids = strtok( DvarGUID, "," );
|
|
|
|
for ( i = 0; i < guids.size; i++ )
|
|
{
|
|
if ( self BotBuiltinGetGuid() + "" == guids[i] )
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
if ( !result )
|
|
return;
|
|
|
|
self.pers["bot_host"] = true;
|
|
}
|
|
|
|
/*
|
|
Returns if the player is a bot.
|
|
*/
|
|
is_bot()
|
|
{
|
|
return self BotBuiltinIsBot();
|
|
}
|
|
|
|
/*
|
|
Set the bot's stance
|
|
*/
|
|
BotSetStance( stance )
|
|
{
|
|
switch ( stance )
|
|
{
|
|
case "stand":
|
|
self maps\bots\_bot_internal::stand();
|
|
break;
|
|
|
|
case "crouch":
|
|
self maps\bots\_bot_internal::crouch();
|
|
break;
|
|
|
|
case "prone":
|
|
self maps\bots\_bot_internal::prone();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Bot presses the button for time.
|
|
*/
|
|
BotPressAttack( time )
|
|
{
|
|
self maps\bots\_bot_internal::pressFire( time );
|
|
}
|
|
|
|
/*
|
|
Bot presses the ads button for time.
|
|
*/
|
|
BotPressADS( time )
|
|
{
|
|
self maps\bots\_bot_internal::pressADS( time );
|
|
}
|
|
|
|
/*
|
|
Bot presses the use button for time.
|
|
*/
|
|
BotPressUse( time )
|
|
{
|
|
self maps\bots\_bot_internal::use( time );
|
|
}
|
|
|
|
/*
|
|
Bot presses the frag button for time.
|
|
*/
|
|
BotPressFrag( time )
|
|
{
|
|
self maps\bots\_bot_internal::frag( time );
|
|
}
|
|
|
|
/*
|
|
Bot presses the smoke button for time.
|
|
*/
|
|
BotPressSmoke( time )
|
|
{
|
|
self maps\bots\_bot_internal::smoke( time );
|
|
}
|
|
|
|
/*
|
|
Bot jumps
|
|
*/
|
|
BotJump()
|
|
{
|
|
self maps\bots\_bot_internal::jump();
|
|
}
|
|
|
|
/*
|
|
Returns the bot's random assigned number.
|
|
*/
|
|
BotGetRandom()
|
|
{
|
|
return self.bot.rand;
|
|
}
|
|
|
|
/*
|
|
Returns a random number thats different everytime it changes target
|
|
*/
|
|
BotGetTargetRandom()
|
|
{
|
|
if ( !isDefined( self.bot.target ) )
|
|
return undefined;
|
|
|
|
return self.bot.target.rand;
|
|
}
|
|
|
|
/*
|
|
Returns if the bot is fragging.
|
|
*/
|
|
IsBotFragging()
|
|
{
|
|
return self.bot.isfraggingafter;
|
|
}
|
|
|
|
/*
|
|
Returns if the bot is pressing smoke button.
|
|
*/
|
|
IsBotSmoking()
|
|
{
|
|
return self.bot.issmokingafter;
|
|
}
|
|
|
|
/*
|
|
Returns if the bot is sprinting.
|
|
*/
|
|
IsBotSprinting()
|
|
{
|
|
return self.bot.issprinting;
|
|
}
|
|
|
|
/*
|
|
Returns if the bot is reloading.
|
|
*/
|
|
IsBotReloading()
|
|
{
|
|
return self.bot.isreloading;
|
|
}
|
|
|
|
/*
|
|
Is bot knifing
|
|
*/
|
|
IsBotKnifing()
|
|
{
|
|
return self.bot.isknifingafter;
|
|
}
|
|
|
|
/*
|
|
Freezes the bot's controls.
|
|
*/
|
|
BotFreezeControls( what )
|
|
{
|
|
self.bot.isfrozen = what;
|
|
|
|
if ( what )
|
|
self notify( "kill_goal" );
|
|
}
|
|
|
|
/*
|
|
Returns if the bot is script frozen.
|
|
*/
|
|
BotIsFrozen()
|
|
{
|
|
return self.bot.isfrozen;
|
|
}
|
|
|
|
/*
|
|
Bot will stop moving
|
|
*/
|
|
BotStopMoving( what )
|
|
{
|
|
self.bot.stop_move = what;
|
|
|
|
if ( what )
|
|
self notify( "kill_goal" );
|
|
}
|
|
|
|
/*
|
|
Notify the bot chat message
|
|
*/
|
|
BotNotifyBotEvent( msg, a, b, c, d, e, f, g )
|
|
{
|
|
self notify( "bot_event", msg, a, b, c, d, e, f, g );
|
|
}
|
|
|
|
/*
|
|
Does the bot have an objective?
|
|
*/
|
|
BotHasObjective()
|
|
{
|
|
return self maps\bots\objectives\_utility::HasBotObjective();
|
|
}
|
|
|
|
/*
|
|
Returns if the bot has a script goal.
|
|
(like t5 gsc bot)
|
|
*/
|
|
HasScriptGoal()
|
|
{
|
|
return ( isDefined( self GetScriptGoal() ) );
|
|
}
|
|
|
|
/*
|
|
Returns the pos of the bot's goal
|
|
*/
|
|
GetScriptGoal()
|
|
{
|
|
return self.bot.script_goal;
|
|
}
|
|
|
|
/*
|
|
Sets the bot's goal, will acheive it when dist away from it.
|
|
*/
|
|
SetScriptGoal( goal, dist )
|
|
{
|
|
if ( !isDefined( dist ) )
|
|
dist = 16;
|
|
|
|
self.bot.script_goal = goal;
|
|
self.bot.script_goal_dist = dist;
|
|
waittillframeend;
|
|
self notify( "new_goal_internal" );
|
|
self notify( "new_goal" );
|
|
}
|
|
|
|
/*
|
|
Clears the bot's goal.
|
|
*/
|
|
ClearScriptGoal()
|
|
{
|
|
self SetScriptGoal( undefined, 0 );
|
|
}
|
|
|
|
/*
|
|
Returns whether the bot has a priority objective
|
|
*/
|
|
HasPriorityObjective()
|
|
{
|
|
return self.bot.prio_objective;
|
|
}
|
|
|
|
/*
|
|
Sets the bot to prioritize the objective over targeting enemies
|
|
*/
|
|
SetPriorityObjective()
|
|
{
|
|
self.bot.prio_objective = true;
|
|
self notify( "kill_goal" );
|
|
}
|
|
|
|
/*
|
|
Clears the bot's priority objective to allow the bot to target enemies automatically again
|
|
*/
|
|
ClearPriorityObjective()
|
|
{
|
|
self.bot.prio_objective = false;
|
|
self notify( "kill_goal" );
|
|
}
|
|
|
|
/*
|
|
Sets the aim position of the bot
|
|
*/
|
|
SetScriptAimPos( pos )
|
|
{
|
|
self.bot.script_aimpos = pos;
|
|
}
|
|
|
|
/*
|
|
Clears the aim position of the bot
|
|
*/
|
|
ClearScriptAimPos()
|
|
{
|
|
self SetScriptAimPos( undefined );
|
|
}
|
|
|
|
/*
|
|
Returns the aim position of the bot
|
|
*/
|
|
GetScriptAimPos()
|
|
{
|
|
return self.bot.script_aimpos;
|
|
}
|
|
|
|
/*
|
|
Returns if the bot has a aim pos
|
|
*/
|
|
HasScriptAimPos()
|
|
{
|
|
return isDefined( self GetScriptAimPos() );
|
|
}
|
|
|
|
/*
|
|
Sets the bot's target to be this ent.
|
|
*/
|
|
SetAttacker( att )
|
|
{
|
|
self.bot.target_this_frame = att;
|
|
}
|
|
|
|
/*
|
|
Sets the script enemy for a bot.
|
|
*/
|
|
SetScriptEnemy( enemy, offset )
|
|
{
|
|
self.bot.script_target = enemy;
|
|
self.bot.script_target_offset = offset;
|
|
}
|
|
|
|
/*
|
|
Removes the script enemy of the bot.
|
|
*/
|
|
ClearScriptEnemy()
|
|
{
|
|
self SetScriptEnemy( undefined, undefined );
|
|
}
|
|
|
|
/*
|
|
Returns the entity of the bot's target.
|
|
*/
|
|
GetThreat()
|
|
{
|
|
if ( !isdefined( self.bot.target ) )
|
|
return undefined;
|
|
|
|
return self.bot.target.entity;
|
|
}
|
|
|
|
/*
|
|
Returns if the bot has a script enemy.
|
|
*/
|
|
HasScriptEnemy()
|
|
{
|
|
return ( isDefined( self.bot.script_target ) );
|
|
}
|
|
|
|
/*
|
|
Returns if the bot has a threat.
|
|
*/
|
|
HasThreat()
|
|
{
|
|
return ( isDefined( self GetThreat() ) );
|
|
}
|
|
|
|
/*
|
|
Returns a valid grenade launcher weapon
|
|
*/
|
|
getValidTube()
|
|
{
|
|
weaps = self getweaponslist();
|
|
|
|
for ( i = 0; i < weaps.size; i++ )
|
|
{
|
|
weap = weaps[i];
|
|
|
|
if ( !self getAmmoCount( weap ) )
|
|
continue;
|
|
|
|
if ( isSubStr( weap, "gl_" ) && !isSubStr( weap, "_gl_" ) )
|
|
return weap;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/*
|
|
Returns a random grenade in the bot's inventory.
|
|
*/
|
|
getValidGrenade()
|
|
{
|
|
grenadeTypes = [];
|
|
grenadeTypes[grenadeTypes.size] = "stielhandgranate";
|
|
|
|
possibles = [];
|
|
|
|
for ( i = 0; i < grenadeTypes.size; i++ )
|
|
{
|
|
if ( !self hasWeapon( grenadeTypes[i] ) )
|
|
continue;
|
|
|
|
if ( !self getAmmoCount( grenadeTypes[i] ) )
|
|
continue;
|
|
|
|
possibles[possibles.size] = grenadeTypes[i];
|
|
}
|
|
|
|
return PickRandom( possibles );
|
|
}
|
|
|
|
/*
|
|
Picks a random thing
|
|
*/
|
|
PickRandom( arr )
|
|
{
|
|
if ( !arr.size )
|
|
return undefined;
|
|
|
|
return arr[randomInt( arr.size )];
|
|
}
|
|
|
|
/*
|
|
If weap is a secondary gnade
|
|
*/
|
|
isSecondaryGrenade( gnade )
|
|
{
|
|
return ( gnade == "zombie_cymbal_monkey" );
|
|
}
|
|
|
|
/*
|
|
CoD4
|
|
*/
|
|
getBaseWeaponName( weap )
|
|
{
|
|
return strtok( weap, "_" )[0];
|
|
}
|
|
|
|
/*
|
|
Returns if the given weapon is full auto.
|
|
*/
|
|
WeaponIsFullAuto( weap )
|
|
{
|
|
weaptoks = strtok( weap, "_" );
|
|
|
|
return isDefined( weaptoks[0] ) && isString( weaptoks[0] ) && isdefined( level.bots_fullautoguns[weaptoks[0]] );
|
|
}
|
|
|
|
/*
|
|
Returns what our eye height is.
|
|
*/
|
|
GetEyeHeight()
|
|
{
|
|
myEye = self GetEyePos();
|
|
|
|
return myEye[2] - self.origin[2];
|
|
}
|
|
|
|
/*
|
|
Returns (iw4) eye pos.
|
|
*/
|
|
GetEyePos()
|
|
{
|
|
return self getTagOrigin( "tag_eye" );
|
|
}
|
|
|
|
/*
|
|
helper
|
|
*/
|
|
waittill_either_return_( str1, str2 )
|
|
{
|
|
self endon( str1 );
|
|
self waittill( str2 );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Returns which string gets notified first
|
|
*/
|
|
waittill_either_return( str1, str2 )
|
|
{
|
|
if ( !isDefined( self waittill_either_return_( str1, str2 ) ) )
|
|
return str1;
|
|
|
|
return str2;
|
|
}
|
|
|
|
/*
|
|
Taken from iw4 script
|
|
*/
|
|
waittill_any_timeout( timeOut, string1, string2, string3, string4, string5 )
|
|
{
|
|
if ( ( !isdefined( string1 ) || string1 != "death" ) &&
|
|
( !isdefined( string2 ) || string2 != "death" ) &&
|
|
( !isdefined( string3 ) || string3 != "death" ) &&
|
|
( !isdefined( string4 ) || string4 != "death" ) &&
|
|
( !isdefined( string5 ) || string5 != "death" ) )
|
|
self endon( "death" );
|
|
|
|
ent = spawnstruct();
|
|
|
|
if ( isdefined( string1 ) )
|
|
self thread waittill_string( string1, ent );
|
|
|
|
if ( isdefined( string2 ) )
|
|
self thread waittill_string( string2, ent );
|
|
|
|
if ( isdefined( string3 ) )
|
|
self thread waittill_string( string3, ent );
|
|
|
|
if ( isdefined( string4 ) )
|
|
self thread waittill_string( string4, ent );
|
|
|
|
if ( isdefined( string5 ) )
|
|
self thread waittill_string( string5, ent );
|
|
|
|
ent thread _timeout( timeOut );
|
|
|
|
ent waittill( "returned", msg );
|
|
ent notify( "die" );
|
|
return msg;
|
|
}
|
|
|
|
/*
|
|
Used for waittill_any_timeout
|
|
*/
|
|
_timeout( delay )
|
|
{
|
|
self endon( "die" );
|
|
|
|
wait( delay );
|
|
self notify( "returned", "timeout" );
|
|
}
|
|
|
|
/*
|
|
Gets a player who is host
|
|
*/
|
|
GetHostPlayer()
|
|
{
|
|
for ( i = 0; i < level.players.size; i++ )
|
|
{
|
|
player = level.players[i];
|
|
|
|
if ( !player is_host() )
|
|
continue;
|
|
|
|
return player;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/*
|
|
Waits for a host player
|
|
*/
|
|
bot_wait_for_host()
|
|
{
|
|
host = undefined;
|
|
|
|
while ( !isDefined( level ) || !isDefined( level.players ) )
|
|
wait 0.05;
|
|
|
|
for ( i = getDvarFloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 )
|
|
{
|
|
host = GetHostPlayer();
|
|
|
|
if ( isDefined( host ) )
|
|
break;
|
|
|
|
wait 0.05;
|
|
}
|
|
|
|
if ( !isDefined( host ) )
|
|
return;
|
|
|
|
for ( i = getDvarFloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 )
|
|
{
|
|
if ( IsDefined( host.pers[ "team" ] ) )
|
|
break;
|
|
|
|
wait 0.05;
|
|
}
|
|
|
|
if ( !IsDefined( host.pers[ "team" ] ) )
|
|
return;
|
|
|
|
for ( i = getDvarFloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 )
|
|
{
|
|
if ( host.pers[ "team" ] == "allies" || host.pers[ "team" ] == "axis" )
|
|
break;
|
|
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Returns the cone dot (like fov, or distance from the center of our screen). 1.0 = directly looking at, 0.0 = completely right angle, -1.0, completely 180
|
|
*/
|
|
getConeDot( to, from, dir )
|
|
{
|
|
dirToTarget = VectorNormalize( to - from );
|
|
forward = AnglesToForward( dir );
|
|
return vectordot( dirToTarget, forward );
|
|
}
|
|
|
|
/*
|
|
Returns the distance squared in a 2d space
|
|
*/
|
|
DistanceSquared2D( to, from )
|
|
{
|
|
to = ( to[0], to[1], 0 );
|
|
from = ( from[0], from[1], 0 );
|
|
|
|
return DistanceSquared( to, from );
|
|
}
|
|
|
|
/*
|
|
Distance from box
|
|
*/
|
|
RectDistanceSquared( origin )
|
|
{
|
|
dx = 0;
|
|
dy = 0;
|
|
dz = 0;
|
|
|
|
if ( origin[0] < self.x0 )
|
|
dx = origin[0] - self.x0;
|
|
else if ( origin[0] > self.x1 )
|
|
dx = origin[0] - self.x1;
|
|
|
|
if ( origin[1] < self.y0 )
|
|
dy = origin[1] - self.y0;
|
|
else if ( origin[1] > self.y1 )
|
|
dy = origin[1] - self.y1;
|
|
|
|
|
|
if ( origin[2] < self.z0 )
|
|
dz = origin[2] - self.z0;
|
|
else if ( origin[2] > self.z1 )
|
|
dz = origin[2] - self.z1;
|
|
|
|
return dx * dx + dy * dy + dz * dz;
|
|
}
|
|
|
|
/*
|
|
Rounds to the nearest whole number.
|
|
*/
|
|
Round( x )
|
|
{
|
|
y = int( x );
|
|
|
|
if ( abs( x ) - abs( y ) > 0.5 )
|
|
{
|
|
if ( x < 0 )
|
|
return y - 1;
|
|
else
|
|
return y + 1;
|
|
}
|
|
else
|
|
return y;
|
|
}
|
|
|
|
/*
|
|
Rounds up the given value.
|
|
*/
|
|
RoundUp( floatVal )
|
|
{
|
|
i = int( floatVal );
|
|
|
|
if ( i != floatVal )
|
|
return i + 1;
|
|
else
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
Clamps between value
|
|
*/
|
|
Clamp( a, minv, maxv )
|
|
{
|
|
return max( min( a, maxv ), minv );
|
|
}
|
|
|
|
/*
|
|
converts a string into a float
|
|
*/
|
|
float( num )
|
|
{
|
|
setdvar( "temp_dvar_bot_util", num );
|
|
|
|
return GetDvarFloat( "temp_dvar_bot_util" );
|
|
}
|
|
|
|
/*
|
|
returns nodes in playable area
|
|
*/
|
|
get_nodes_in_playable_area()
|
|
{
|
|
total_nodes = getAllNodes();
|
|
filtered_nodes = [];
|
|
|
|
for ( i = 0; i < total_nodes.size; i++ )
|
|
{
|
|
if ( !is_point_in_playable_area( total_nodes[ i ].origin ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
filtered_nodes[ filtered_nodes.size ] = total_nodes[ i ];
|
|
|
|
if ( ( i % 10 ) == 0 )
|
|
{
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
return filtered_nodes;
|
|
}
|
|
|
|
/*
|
|
is the point in playable area?
|
|
*/
|
|
is_point_in_playable_area( point )
|
|
{
|
|
playable_area = getentarray( "playable_area", "targetname" );
|
|
|
|
in_playable_area = false;
|
|
|
|
if ( !isDefined( playable_area ) || playable_area.size < 1 )
|
|
{
|
|
in_playable_area = true;
|
|
}
|
|
|
|
temp_ent = spawn( "script_origin", point );
|
|
|
|
if ( !in_playable_area )
|
|
{
|
|
for ( p = 0; p < playable_area.size; p++ )
|
|
{
|
|
if ( temp_ent isTouching( playable_area[ p ] ) )
|
|
{
|
|
in_playable_area = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
temp_ent delete ();
|
|
|
|
return in_playable_area;
|
|
}
|
|
|
|
/*
|
|
Loads the waypoints. Populating everything needed for the waypoints.
|
|
*/
|
|
load_waypoints()
|
|
{
|
|
bot_allowed_negotiation_links = [];
|
|
bot_allowed_negotiation_links[ bot_allowed_negotiation_links.size ] = "zombie_jump_down_72";
|
|
bot_allowed_negotiation_links[ bot_allowed_negotiation_links.size ] = "zombie_jump_down_96";
|
|
bot_allowed_negotiation_links[ bot_allowed_negotiation_links.size ] = "zombie_jump_down_120";
|
|
bot_allowed_negotiation_links[ bot_allowed_negotiation_links.size ] = "zombie_jump_down_127";
|
|
bot_allowed_negotiation_links[ bot_allowed_negotiation_links.size ] = "zombie_jump_down_184";
|
|
bot_allowed_negotiation_links[ bot_allowed_negotiation_links.size ] = "zombie_jump_down_190";
|
|
|
|
bot_ignore_links = [];
|
|
|
|
switch ( level.script )
|
|
{
|
|
case "nazi_zombie_sumpf":
|
|
a = [];
|
|
a[a.size] = 1825;
|
|
a[a.size] = 1826;
|
|
a[a.size] = 1829;
|
|
a[a.size] = 1830;
|
|
a[a.size] = 1833;
|
|
a[a.size] = 1837;
|
|
bot_ignore_links[1603 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1829;
|
|
bot_ignore_links[1604 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1904;
|
|
bot_ignore_links[1823 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1603;
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
a[a.size] = 1906;
|
|
bot_ignore_links[1825 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1603;
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
a[a.size] = 1907;
|
|
bot_ignore_links[1826 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1904;
|
|
bot_ignore_links[1827 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1603;
|
|
a[a.size] = 1604;
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
a[a.size] = 1906;
|
|
a[a.size] = 1907;
|
|
bot_ignore_links[1829 + ""] = a;
|
|
|
|
|
|
a = [];
|
|
a[a.size] = 1603;
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
a[a.size] = 1907;
|
|
bot_ignore_links[1830 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1904;
|
|
bot_ignore_links[1831 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1603;
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
a[a.size] = 1906;
|
|
a[a.size] = 1907;
|
|
bot_ignore_links[1833 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
bot_ignore_links[1834 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1603;
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
a[a.size] = 1906;
|
|
a[a.size] = 1907;
|
|
bot_ignore_links[1837 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1903;
|
|
a[a.size] = 1904;
|
|
bot_ignore_links[1838 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1825;
|
|
a[a.size] = 1826;
|
|
a[a.size] = 1829;
|
|
a[a.size] = 1830;
|
|
a[a.size] = 1833;
|
|
a[a.size] = 1834;
|
|
a[a.size] = 1837;
|
|
a[a.size] = 1838;
|
|
bot_ignore_links[1903 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1823;
|
|
a[a.size] = 1825;
|
|
a[a.size] = 1826;
|
|
a[a.size] = 1827;
|
|
a[a.size] = 1829;
|
|
a[a.size] = 1830;
|
|
a[a.size] = 1831;
|
|
a[a.size] = 1833;
|
|
a[a.size] = 1834;
|
|
a[a.size] = 1837;
|
|
a[a.size] = 1838;
|
|
bot_ignore_links[1904 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1825;
|
|
a[a.size] = 1829;
|
|
a[a.size] = 1833;
|
|
a[a.size] = 1837;
|
|
bot_ignore_links[1906 + ""] = a;
|
|
|
|
a = [];
|
|
a[a.size] = 1826;
|
|
a[a.size] = 1829;
|
|
a[a.size] = 1830;
|
|
a[a.size] = 1833;
|
|
a[a.size] = 1837;
|
|
bot_ignore_links[1907 + ""] = a;
|
|
break;
|
|
}
|
|
|
|
// arrays are passed by value in gsc... hope this isnt gunna run out of vars
|
|
BotBuiltinsSetAllowedTraversals( bot_allowed_negotiation_links );
|
|
BotBuiltinsSetIgnoredLinks( bot_ignore_links );
|
|
level.bot_ignore_links = bot_ignore_links;
|
|
|
|
level.waypoints = GetAllNodes();
|
|
level.waypointCount = level.waypoints.size;
|
|
|
|
level.waypointsInPlayableArea = [];
|
|
level.waypointsInPlayableArea = get_nodes_in_playable_area();
|
|
}
|
|
|
|
/*
|
|
Returns a good amount of players.
|
|
*/
|
|
getGoodMapAmount()
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
Returns the friendly user name for a given map's codename
|
|
*/
|
|
getMapName( map )
|
|
{
|
|
return map;
|
|
}
|
|
|
|
/*
|
|
Returns an array of all the bots in the game.
|
|
*/
|
|
getBotArray()
|
|
{
|
|
result = [];
|
|
playercount = level.players.size;
|
|
|
|
for ( i = 0; i < playercount; i++ )
|
|
{
|
|
player = level.players[i];
|
|
|
|
if ( !player is_bot() )
|
|
continue;
|
|
|
|
result[result.size] = player;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
A heap invarient comparitor, used for numbers, numbers with the highest number will be first in the heap.
|
|
*/
|
|
Heap( item, item2 )
|
|
{
|
|
return item > item2;
|
|
}
|
|
|
|
/*
|
|
A heap invarient comparitor, used for numbers, numbers with the lowest number will be first in the heap.
|
|
*/
|
|
ReverseHeap( item, item2 )
|
|
{
|
|
return item < item2;
|
|
}
|
|
|
|
HeapPriority( item, item2 )
|
|
{
|
|
return item.fPriority > item2.fPriority;
|
|
}
|
|
|
|
/*
|
|
A heap invarient comparitor, used for traces. Wanting the trace with the largest length first in the heap.
|
|
*/
|
|
HeapTraceFraction( item, item2 )
|
|
{
|
|
return item["fraction"] > item2["fraction"];
|
|
}
|
|
|
|
/*
|
|
Returns a new heap.
|
|
*/
|
|
NewHeap( compare )
|
|
{
|
|
heap_node = spawnStruct();
|
|
heap_node.data = [];
|
|
heap_node.compare = compare;
|
|
|
|
return heap_node;
|
|
}
|
|
|
|
/*
|
|
Inserts the item into the heap. Called on a heap.
|
|
*/
|
|
HeapInsert( item )
|
|
{
|
|
insert = self.data.size;
|
|
self.data[insert] = item;
|
|
|
|
current = insert + 1;
|
|
|
|
while ( current > 1 )
|
|
{
|
|
last = current;
|
|
current = int( current / 2 );
|
|
|
|
if ( ![[self.compare]]( item, self.data[current - 1] ) )
|
|
break;
|
|
|
|
self.data[last - 1] = self.data[current - 1];
|
|
self.data[current - 1] = item;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Helper function to determine what is the next child of the bst.
|
|
*/
|
|
_HeapNextChild( node, hsize )
|
|
{
|
|
left = node * 2;
|
|
right = left + 1;
|
|
|
|
if ( left > hsize )
|
|
return -1;
|
|
|
|
if ( right > hsize )
|
|
return left;
|
|
|
|
if ( [[self.compare]]( self.data[left - 1], self.data[right - 1] ) )
|
|
return left;
|
|
else
|
|
return right;
|
|
}
|
|
|
|
/*
|
|
Removes an item from the heap. Called on a heap.
|
|
*/
|
|
HeapRemove()
|
|
{
|
|
remove = self.data.size;
|
|
|
|
if ( !remove )
|
|
return remove;
|
|
|
|
move = self.data[remove - 1];
|
|
self.data[0] = move;
|
|
self.data[remove - 1] = undefined;
|
|
remove--;
|
|
|
|
if ( !remove )
|
|
return remove;
|
|
|
|
last = 1;
|
|
next = self _HeapNextChild( 1, remove );
|
|
|
|
while ( next != -1 )
|
|
{
|
|
if ( [[self.compare]]( move, self.data[next - 1] ) )
|
|
break;
|
|
|
|
self.data[last - 1] = self.data[next - 1];
|
|
self.data[next - 1] = move;
|
|
|
|
last = next;
|
|
next = self _HeapNextChild( next, remove );
|
|
}
|
|
|
|
return remove;
|
|
}
|
|
|
|
/*
|
|
Returns the natural log of x using harmonic series.
|
|
*/
|
|
Log( x )
|
|
{
|
|
/* if (!isDefined(level.log_cache))
|
|
level.log_cache = [];
|
|
|
|
key = x + "";
|
|
|
|
if (isDefined(level.log_cache[key]))
|
|
return level.log_cache[key];*/
|
|
|
|
//thanks Bob__ at stackoverflow
|
|
old_sum = 0.0;
|
|
xmlxpl = ( x - 1 ) / ( x + 1 );
|
|
xmlxpl_2 = xmlxpl * xmlxpl;
|
|
denom = 1.0;
|
|
frac = xmlxpl;
|
|
sum = frac;
|
|
|
|
while ( sum != old_sum )
|
|
{
|
|
old_sum = sum;
|
|
denom += 2.0;
|
|
frac *= xmlxpl_2;
|
|
sum += frac / denom;
|
|
}
|
|
|
|
answer = 2.0 * sum;
|
|
|
|
//level.log_cache[key] = answer;
|
|
return answer;
|
|
}
|
|
|
|
/*
|
|
Taken from t5 gsc.
|
|
Returns an array of number's average.
|
|
*/
|
|
array_average( array )
|
|
{
|
|
assert( array.size > 0 );
|
|
total = 0;
|
|
|
|
for ( i = 0; i < array.size; i++ )
|
|
{
|
|
total += array[i];
|
|
}
|
|
|
|
return ( total / array.size );
|
|
}
|
|
|
|
/*
|
|
Taken from t5 gsc.
|
|
Returns an array of number's standard deviation.
|
|
*/
|
|
array_std_deviation( array, mean )
|
|
{
|
|
assert( array.size > 0 );
|
|
tmp = [];
|
|
|
|
for ( i = 0; i < array.size; i++ )
|
|
{
|
|
tmp[i] = ( array[i] - mean ) * ( array[i] - mean );
|
|
}
|
|
|
|
total = 0;
|
|
|
|
for ( i = 0; i < tmp.size; i++ )
|
|
{
|
|
total = total + tmp[i];
|
|
}
|
|
|
|
return Sqrt( total / array.size );
|
|
}
|
|
|
|
/*
|
|
Taken from t5 gsc.
|
|
Will produce a random number between lower_bound and upper_bound but with a bell curve distribution (more likely to be close to the mean).
|
|
*/
|
|
random_normal_distribution( mean, std_deviation, lower_bound, upper_bound )
|
|
{
|
|
x1 = 0;
|
|
x2 = 0;
|
|
w = 1;
|
|
y1 = 0;
|
|
|
|
while ( w >= 1 )
|
|
{
|
|
x1 = 2 * RandomFloatRange( 0, 1 ) - 1;
|
|
x2 = 2 * RandomFloatRange( 0, 1 ) - 1;
|
|
w = x1 * x1 + x2 * x2;
|
|
}
|
|
|
|
w = Sqrt( ( -2.0 * Log( w ) ) / w );
|
|
y1 = x1 * w;
|
|
number = mean + y1 * std_deviation;
|
|
|
|
if ( IsDefined( lower_bound ) && number < lower_bound )
|
|
{
|
|
number = lower_bound;
|
|
}
|
|
|
|
if ( IsDefined( upper_bound ) && number > upper_bound )
|
|
{
|
|
number = upper_bound;
|
|
}
|
|
|
|
return ( number );
|
|
}
|
|
|
|
/*
|
|
If the player is in laststand
|
|
*/
|
|
inLastStand()
|
|
{
|
|
func = BotBuiltinGetFunction( "maps/_laststand", "player_is_in_laststand" );
|
|
|
|
return self [[func]]();
|
|
}
|
|
|
|
/*
|
|
Is reviving player
|
|
*/
|
|
isReviving( revivee )
|
|
{
|
|
func = BotBuiltinGetFunction( "maps/_laststand", "is_reviving" );
|
|
|
|
return self [[func]]( revivee );
|
|
}
|
|
|
|
/*
|
|
getRandomGoal
|
|
*/
|
|
getRandomGoal()
|
|
{
|
|
if ( !level.waypointsInPlayableArea.size )
|
|
{
|
|
return self.origin;
|
|
}
|
|
|
|
return PickRandom( level.waypointsInPlayableArea ).origin;
|
|
}
|
|
|
|
/*
|
|
Checks if target is dog
|
|
*/
|
|
targetIsDog()
|
|
{
|
|
return isDefined( self.targetname ) && self.targetname == "zombie_dog";
|
|
}
|
|
|
|
/*
|
|
Checks if target is gibbed
|
|
*/
|
|
targetIsGibbed()
|
|
{
|
|
return isDefined( self.gibbed ) && self.gibbed;
|
|
}
|
|
|
|
/*
|
|
is weap primary?
|
|
*/
|
|
isWeaponPrimary( weap )
|
|
{
|
|
weaps = self GetWeaponsListPrimaries();
|
|
|
|
for ( i = 0; i < weaps.size; i++ )
|
|
{
|
|
if ( weap == weaps[i] )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Checks whether the path generated by the ASTAR path finding is inaccessible
|
|
*/
|
|
GetPathIsInaccessible( from, to, team, best_effort )
|
|
{
|
|
path = BotBuiltinGeneratePath( from, to, team, best_effort );
|
|
return ( !isDefined( path ) || ( path.size <= 0 ) );
|
|
}
|
|
|
|
/*
|
|
Returns how long the path is
|
|
*/
|
|
get_path_dist( start, end, team )
|
|
{
|
|
path = BotBuiltinGeneratePath( start, end, team, 192.0 );
|
|
|
|
if ( !isDefined( path ) || path.size <= 0 )
|
|
{
|
|
return 999999999;
|
|
}
|
|
|
|
dist = 0;
|
|
prev_node = undefined;
|
|
|
|
for ( i = 0; i < path.size; i++ )
|
|
{
|
|
if ( i == 0 )
|
|
{
|
|
prev_node = path[ i ];
|
|
continue;
|
|
}
|
|
|
|
dist += distance( prev_node.origin, path[ i ].origin );
|
|
prev_node = path[ i ];
|
|
}
|
|
|
|
return dist;
|
|
}
|
|
|
|
/*
|
|
Clamp lerp,
|
|
*/
|
|
ClampLerp( dist, min_dist, max_dist, max_bonus, min_bonus )
|
|
{
|
|
answer = 0;
|
|
|
|
if ( dist <= min_dist )
|
|
{
|
|
answer += max_bonus;
|
|
}
|
|
else if ( dist >= max_dist )
|
|
{
|
|
answer += min_bonus;
|
|
}
|
|
else
|
|
{
|
|
dist_multi = 1 - ( ( dist - min_dist ) / ( max_dist - min_dist ) );
|
|
answer += min_bonus + ( ( max_bonus - min_bonus ) * dist_multi );
|
|
}
|
|
|
|
return answer;
|
|
}
|
|
|
|
/*
|
|
Base an origin offset from an ent
|
|
*/
|
|
get_angle_offset_node( forward_size, angle_offset, offset )
|
|
{
|
|
if ( !isDefined( forward_size ) )
|
|
{
|
|
forward_size = 40;
|
|
}
|
|
|
|
if ( !isDefined( angle_offset ) )
|
|
{
|
|
angle_offset = ( 0, 0, 0 );
|
|
}
|
|
|
|
if ( !isDefined( offset ) )
|
|
{
|
|
offset = ( 0, 0, 0 );
|
|
}
|
|
|
|
angles = ( 0, self.angles[ 1 ], 0 );
|
|
angles += angle_offset;
|
|
node = self.origin + ( AnglesToForward( angles ) * forward_size ) + offset;
|
|
node = clamp_to_ground( node );
|
|
|
|
if ( getDvarInt( "bots_main_debug" ) )
|
|
{
|
|
self thread debug_offset_line( node );
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
Draw the offset
|
|
*/
|
|
debug_offset_line( node )
|
|
{
|
|
self endon( "death" );
|
|
self notify( "debug_offset_line" );
|
|
self endon( "debug_offset_line" );
|
|
|
|
while ( isDefined( self ) )
|
|
{
|
|
line( self.origin, node );
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Is the point inside this use trigger?
|
|
*/
|
|
PointInsideUseTrigger( point )
|
|
{
|
|
if ( getDvarInt( "bots_main_debug" ) )
|
|
{
|
|
self thread debug_bounding_box_for_ent();
|
|
}
|
|
|
|
mins = self BotBuiltinGetMins();
|
|
maxs = self BotBuiltinGetMaxs();
|
|
|
|
box = spawnstruct();
|
|
box.x0 = self.origin[0] + mins[0];
|
|
box.x1 = self.origin[0] + maxs[0];
|
|
box.y0 = self.origin[1] + mins[1];
|
|
box.y1 = self.origin[1] + maxs[1];
|
|
box.z0 = self.origin[2] + mins[2];
|
|
box.z1 = self.origin[2] + maxs[2];
|
|
|
|
if ( box RectDistanceSquared( self.origin ) > 72 * 72 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !sightTracePassed( point, self.origin, false, undefined ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Draw the aabb of the ent
|
|
*/
|
|
debug_bounding_box_for_ent( color )
|
|
{
|
|
self endon( "death" );
|
|
self notify( "debug_bounding_box_for_ent" );
|
|
self endon( "debug_bounding_box_for_ent" );
|
|
|
|
if ( !isDefined( color ) )
|
|
color = ( randomFloatRange( 0, 1 ), randomFloatRange( 0, 1 ), randomFloatRange( 0, 1 ) );
|
|
|
|
while ( isDefined( self ) )
|
|
{
|
|
mins = self BotBuiltinGetMins();
|
|
maxs = self BotBuiltinGetMaxs();
|
|
|
|
line( self.origin + ( mins[0], mins[1], mins[2] ), self.origin + ( mins[0], mins[1], maxs[2] ), color );
|
|
line( self.origin + ( mins[0], mins[1], mins[2] ), self.origin + ( mins[0], maxs[1], mins[2] ), color );
|
|
line( self.origin + ( mins[0], mins[1], mins[2] ), self.origin + ( maxs[0], mins[1], mins[2] ), color );
|
|
|
|
line( self.origin + ( maxs[0], maxs[1], maxs[2] ), self.origin + ( maxs[0], maxs[1], mins[2] ), color );
|
|
line( self.origin + ( maxs[0], maxs[1], maxs[2] ), self.origin + ( maxs[0], mins[1], maxs[2] ), color );
|
|
line( self.origin + ( maxs[0], maxs[1], maxs[2] ), self.origin + ( mins[0], maxs[1], maxs[2] ), color );
|
|
|
|
line( self.origin + ( maxs[0], mins[1], mins[2] ), self.origin + ( maxs[0], maxs[1], mins[2] ), color );
|
|
line( self.origin + ( maxs[0], mins[1], mins[2] ), self.origin + ( maxs[0], mins[1], maxs[2] ), color );
|
|
|
|
line( self.origin + ( mins[0], mins[1], maxs[2] ), self.origin + ( maxs[0], mins[1], maxs[2] ), color );
|
|
line( self.origin + ( mins[0], mins[1], maxs[2] ), self.origin + ( mins[0], maxs[1], maxs[2] ), color );
|
|
|
|
line( self.origin + ( mins[0], maxs[1], mins[2] ), self.origin + ( maxs[0], maxs[1], mins[2] ), color );
|
|
line( self.origin + ( mins[0], maxs[1], mins[2] ), self.origin + ( mins[0], maxs[1], maxs[2] ), color );
|
|
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Clamp the origin to the ground
|
|
*/
|
|
clamp_to_ground( org )
|
|
{
|
|
trace = playerPhysicsTrace( org + ( 0, 0, 20 ), org - ( 0, 0, 2000 ) );
|
|
return trace;
|
|
}
|