682 lines
28 KiB
Plaintext
682 lines
28 KiB
Plaintext
#using scripts\codescripts\struct;
|
|
|
|
#using scripts\shared\_oob;
|
|
#using scripts\shared\array_shared;
|
|
#using scripts\shared\audio_shared;
|
|
#using scripts\shared\challenges_shared;
|
|
#using scripts\shared\clientfield_shared;
|
|
#using scripts\shared\hostmigration_shared;
|
|
#using scripts\shared\hud_shared;
|
|
#using scripts\shared\killstreaks_shared;
|
|
#using scripts\shared\popups_shared;
|
|
#using scripts\shared\scoreevents_shared;
|
|
#using scripts\shared\tweakables_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\weapons\_flashgrenades;
|
|
#using scripts\shared\weapons\_weaponobjects;
|
|
#using scripts\shared\vehicle_shared;
|
|
#using scripts\shared\vehicle_death_shared;
|
|
|
|
#using scripts\mp\_challenges;
|
|
#using scripts\mp\_util;
|
|
#using scripts\mp\gametypes\_globallogic_audio;
|
|
#using scripts\mp\gametypes\_globallogic_utils;
|
|
#using scripts\mp\gametypes\_hostmigration;
|
|
#using scripts\mp\gametypes\_shellshock;
|
|
#using scripts\mp\gametypes\_spawning;
|
|
#using scripts\mp\killstreaks\_emp;
|
|
#using scripts\mp\killstreaks\_killstreak_bundles;
|
|
#using scripts\mp\killstreaks\_killstreak_detect;
|
|
#using scripts\mp\killstreaks\_killstreak_hacking;
|
|
#using scripts\mp\killstreaks\_killstreakrules;
|
|
#using scripts\mp\killstreaks\_killstreaks;
|
|
#using scripts\mp\killstreaks\_remote_weapons;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#precache( "string", "KILLSTREAK_EARNED_RCBOMB" );
|
|
#precache( "string", "KILLSTREAK_RCBOMB_NOT_AVAILABLE" );
|
|
#precache( "string", "KILLSTREAK_RCBOMB_INBOUND" );
|
|
#precache( "string", "KILLSTREAK_RCBOMB_HACKED" );
|
|
#precache( "string", "KILLSTREAK_DESTROYED_RCBOMB" );
|
|
#precache( "string", "mpl_killstreak_rcbomb" );
|
|
#precache( "fx", "_t6/weapon/grenade/fx_spark_disabled_rc_car" );
|
|
#precache( "fx", "killstreaks/fx_rcxd_lights_grn" );
|
|
#precache( "fx", "killstreaks/fx_rcxd_lights_red" );
|
|
#precache( "fx", "killstreaks/fx_rcxd_exp" );
|
|
|
|
#namespace rcbomb;
|
|
|
|
|
|
|
|
|
|
|
|
function init()
|
|
{
|
|
level._effect["rcbombexplosion"] = "killstreaks/fx_rcxd_exp";
|
|
|
|
killstreaks::register( "rcbomb", "rcbomb", "killstreak_rcbomb", "rcbomb_used",&ActivateRCBomb );
|
|
killstreaks::register_strings( "rcbomb", &"KILLSTREAK_EARNED_RCBOMB", &"KILLSTREAK_RCBOMB_NOT_AVAILABLE", &"KILLSTREAK_RCBOMB_INBOUND", undefined, &"KILLSTREAK_RCBOMB_HACKED", false );
|
|
killstreaks::register_dialog( "rcbomb", "mpl_killstreak_rcbomb", "rcBombDialogBundle", undefined, "friendlyRcBomb", "enemyRcBomb", "enemyRcBombMultiple", "friendlyRcBombHacked", "enemyRcBombHacked", "requestRcBomb" );
|
|
killstreaks::allow_assists( "rcbomb", true);
|
|
killstreaks::register_alt_weapon( "rcbomb", "killstreak_remote" );
|
|
killstreaks::register_alt_weapon( "rcbomb", "rcbomb_turret" );
|
|
remote_weapons::RegisterRemoteWeapon( "rcbomb", &"", &StartRemoteControl, &EndRemoteControl, false );
|
|
|
|
vehicle::add_main_callback( "rc_car_mp", &InitRCBomb );
|
|
|
|
clientfield::register( "vehicle", "rcbomb_stunned", 1, 1, "int" );
|
|
}
|
|
|
|
function InitRCBomb()
|
|
{
|
|
rcbomb = self;
|
|
|
|
rcbomb clientfield::set( "enemyvehicle", 1 );
|
|
rcbomb.allowFriendlyFireDamageOverride = &RCCarAllowFriendlyFireDamage;
|
|
rcbomb EnableAimAssist();
|
|
rcbomb SetDrawInfrared( true );
|
|
rcbomb.delete_on_death = true;
|
|
rcbomb.death_enter_cb = &waitRemoteControl;
|
|
rcbomb.disableRemoteWeaponSwitch = true;
|
|
rcbomb.overrideVehicleDamage = &OnDamage;
|
|
rcbomb.overrideVehicleDeath = &OnDeath;
|
|
//rcbomb.remoteWeaponShutdownDelay = RCBOMB_SHUTDOWN_DELAY;
|
|
rcbomb.watch_remote_weapon_death = true;
|
|
rcbomb.watch_remote_weapon_death_duration = ( 0.3 );
|
|
|
|
if ( IsSentient( rcbomb ) == false )
|
|
rcbomb MakeSentient(); // so other sentients will consider this as a potential enemy
|
|
}
|
|
|
|
|
|
function waitRemoteControl()
|
|
{
|
|
remote_controlled = ( isdefined( self.control_initiated ) && self.control_initiated ) || ( isdefined( self.controlled ) && self.controlled );
|
|
|
|
if( remote_controlled )
|
|
{
|
|
notifyString = self util::waittill_any_return( "remote_weapon_end", "rcbomb_shutdown" );
|
|
if( notifyString == "remote_weapon_end" )
|
|
self waittill( "rcbomb_shutdown" );
|
|
else
|
|
self waittill( "remote_weapon_end" );
|
|
}
|
|
else
|
|
self waittill( "rcbomb_shutdown" );
|
|
}
|
|
|
|
function toggleLightsOnAfterTime( time )
|
|
{
|
|
self notify("toggleLightsOnAfterTime_singleton");
|
|
self endon ("toggleLightsOnAfterTime_singleton");
|
|
|
|
rcbomb = self;
|
|
rcbomb endon( "death" );
|
|
wait( time );
|
|
rcbomb clientfield::set( "toggle_lights", 0 );
|
|
}
|
|
|
|
function HackedPreFunction( hacker )
|
|
{
|
|
rcbomb = self;
|
|
rcbomb clientfield::set( "toggle_lights", 1 );
|
|
rcbomb.owner unlink();
|
|
rcbomb clientfield::set( "vehicletransition", 0 );
|
|
rcbomb.owner killstreaks::clear_using_remote();
|
|
rcbomb MakeVehicleUnusable();
|
|
}
|
|
|
|
function HackedPostFunction( hacker )
|
|
{
|
|
rcbomb = self;
|
|
hacker remote_weapons::UseRemoteWeapon( rcbomb, "rcbomb", true, false );
|
|
rcbomb MakeVehicleUnusable();
|
|
hacker killstreaks::set_killstreak_delay_killcam( "rcbomb" );
|
|
hacker killstreak_hacking::set_vehicle_drivable_time_starting_now( rcbomb );
|
|
}
|
|
|
|
function ConfigureTeamPost( owner, isHacked )
|
|
{
|
|
rcbomb = self;
|
|
rcbomb thread WatchOwnerGameEvents();
|
|
}
|
|
|
|
|
|
function ActivateRCBomb( hardpointType )
|
|
{
|
|
assert( IsPlayer( self ) );
|
|
player = self;
|
|
|
|
if( !player killstreakrules::isKillstreakAllowed( hardpointType, player.team ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( player UseButtonPressed() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
placement = CalculateSpawnOrigin( self.origin, self.angles );
|
|
if( !isdefined( placement ) || !self IsOnGround() || self util::isUsingRemote() || killstreaks::is_interacting_with_object() || self oob::IsTouchingAnyOOBTrigger() || self killstreaks::is_killstreak_start_blocked() )
|
|
{
|
|
self iPrintLnBold( &"KILLSTREAK_RCBOMB_NOT_PLACEABLE" );
|
|
return false;
|
|
}
|
|
|
|
killstreak_id = player killstreakrules::killstreakStart( "rcbomb", player.team, false, true );
|
|
if( killstreak_id == (-1) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
rcbomb = SpawnVehicle( "rc_car_mp", placement.origin, placement.angles, "rcbomb" );
|
|
|
|
rcbomb killstreaks::configure_team( "rcbomb", killstreak_id, player, "small_vehicle", undefined, &ConfigureTeamPost );
|
|
rcbomb killstreak_hacking::enable_hacking( "rcbomb", &HackedPreFunction, &HackedPostFunction );
|
|
rcbomb.damageTaken = 0;
|
|
rcbomb.abandoned = false;
|
|
rcbomb.killstreak_id = killstreak_id;
|
|
rcbomb.activatingKillstreak = true;
|
|
rcbomb SetInvisibleToAll();
|
|
|
|
rcbomb thread WatchShutdown();
|
|
rcbomb.health = killstreak_bundles::get_max_health( hardpointType );
|
|
rcbomb.maxhealth = killstreak_bundles::get_max_health( hardpointType );
|
|
rcbomb.hackedhealth = killstreak_bundles::get_hacked_health( hardpointType );
|
|
rcbomb.hackedHealthUpdateCallback = &rcbomb_hacked_health_update;
|
|
rcbomb.ignore_vehicle_underneath_splash_scalar = true;
|
|
|
|
self thread killstreaks::play_killstreak_start_dialog( "rcbomb", self.team, killstreak_id );
|
|
self AddWeaponStat( GetWeapon( "rcbomb" ) , "used", 1 );
|
|
|
|
remote_weapons::UseRemoteWeapon( rcbomb, "rcbomb", true, false );
|
|
|
|
if ( !isdefined( player ) || !isAlive( player ) || ( isdefined( player.laststand ) && player.laststand ) || player IsEMPJammed() )
|
|
{
|
|
if ( isdefined( rcbomb ) )
|
|
{
|
|
rcbomb notify( "remote_weapon_shutdown" );
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
rcbomb SetVisibleToAll();
|
|
rcbomb.activatingKillstreak = false;
|
|
Target_Set( rcbomb );
|
|
|
|
rcbomb thread WatchGameEnded();
|
|
|
|
return true;
|
|
}
|
|
|
|
function rcbomb_hacked_health_update( hacker )
|
|
{
|
|
rcbomb = self;
|
|
if ( rcbomb.health > rcbomb.hackedhealth )
|
|
{
|
|
rcbomb.health = rcbomb.hackedhealth;
|
|
}
|
|
}
|
|
|
|
|
|
function StartRemoteControl( rcbomb )
|
|
{
|
|
player = self;
|
|
|
|
rcbomb UseVehicle( player, 0 );
|
|
rcbomb clientfield::set( "vehicletransition", 1 );
|
|
|
|
rcbomb thread audio::sndUpdateVehicleContext(true);
|
|
|
|
rcbomb thread WatchTimeout();
|
|
rcbomb thread WatchDetonation();
|
|
rcbomb thread WatchHurtTriggers();
|
|
rcbomb thread WatchWater();
|
|
|
|
player vehicle::set_vehicle_drivable_time_starting_now( ( 40 * 1000 ) );
|
|
}
|
|
|
|
function EndRemoteControl( rcbomb, exitRequestedByOwner )
|
|
{
|
|
if ( exitrequestedbyowner == false )
|
|
{
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
rcbomb thread audio::sndUpdateVehicleContext(false);
|
|
}
|
|
rcbomb clientfield::set( "vehicletransition", 0 );
|
|
}
|
|
|
|
function WatchDetonation()
|
|
{
|
|
rcbomb = self;
|
|
rcbomb endon( "rcbomb_shutdown" );
|
|
rcbomb endon( "death" );
|
|
|
|
while( !rcbomb.owner attackbuttonpressed() )
|
|
{
|
|
{wait(.05);};
|
|
}
|
|
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function WatchWater()
|
|
{
|
|
self endon( "rcbomb_shutdown" );
|
|
|
|
inWater = false;
|
|
while( !inWater )
|
|
{
|
|
wait ( 0.5 );
|
|
trace = physicstrace( self.origin + ( 0, 0, 10 ), self.origin + ( 0, 0, 6 ), ( -2, -2, -2 ), ( 2, 2, 2 ), self, ( (1 << 2) ));
|
|
inWater = trace["fraction"] < 1.0;
|
|
}
|
|
|
|
self.abandoned = true;
|
|
self notify( "rcbomb_shutdown" );
|
|
}
|
|
|
|
function WatchOwnerGameEvents()
|
|
{
|
|
self notify("WatchOwnerGameEvents_singleton");
|
|
self endon ("WatchOwnerGameEvents_singleton");
|
|
|
|
rcbomb = self;
|
|
rcbomb endon( "rcbomb_shutdown" );
|
|
|
|
rcbomb.owner util::waittill_any( "joined_team", "disconnect", "joined_spectators" );
|
|
|
|
rcbomb.abandoned = true;
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
}
|
|
|
|
function WatchTimeout()
|
|
{
|
|
rcbomb = self;
|
|
rcbomb thread killstreaks::WaitForTimeout( "rcbomb", ( 40 * 1000 ), &rc_shutdown, "rcbomb_shutdown" );
|
|
}
|
|
|
|
function rc_shutdown()
|
|
{
|
|
rcbomb = self;
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
}
|
|
|
|
function WatchShutdown()
|
|
{
|
|
rcbomb = self;
|
|
rcbomb endon( "death" );
|
|
|
|
rcbomb waittill( "rcbomb_shutdown" );
|
|
|
|
if ( isdefined( rcbomb.activatingKillstreak ) && rcbomb.activatingKillstreak )
|
|
{
|
|
// we can delete since it should not have been made visible yet
|
|
killstreakrules::killstreakStop( "rcbomb", rcbomb.originalteam, rcbomb.killstreak_id );
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
rcbomb delete(); // still need to delete here
|
|
}
|
|
else
|
|
{
|
|
attacker = ( isdefined( rcbomb.owner ) ? rcbomb.owner : undefined );
|
|
rcbomb DoDamage( rcbomb.health + 1, rcbomb.origin + (0, 0, 10), attacker, attacker, "none", "MOD_EXPLOSIVE", 0 );
|
|
}
|
|
}
|
|
|
|
function WatchHurtTriggers()
|
|
{
|
|
rcbomb = self;
|
|
rcbomb endon( "rcbomb_shutdown" );
|
|
|
|
while( true )
|
|
{
|
|
rcbomb waittill ( "touch", ent );
|
|
if( isdefined( ent.classname ) && ( ent.classname == "trigger_hurt" || ent.classname == "trigger_out_of_bounds" ) )
|
|
{
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
}
|
|
}
|
|
}
|
|
|
|
function OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
|
|
{
|
|
if ( self.activatingKillstreak )
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
if ( !isdefined( eAttacker ) || eAttacker != self.owner )
|
|
{
|
|
iDamage = killstreaks::OnDamagePerWeapon( "rcbomb", eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, self.maxhealth, undefined, self.maxhealth*0.4, undefined, 0, undefined, true, 1.0 );
|
|
}
|
|
|
|
if( isdefined( eAttacker ) && isdefined( eAttacker.team ) && eAttacker.team != self.team )
|
|
{
|
|
if( weapon.isEmp )
|
|
{
|
|
self.damage_on_death = false;
|
|
self.died_by_emp = true;
|
|
iDamage = self.health + 1; // destroy if hit by emp
|
|
//thread remote_weapons::do_static_fx();
|
|
}
|
|
}
|
|
|
|
// C4 destroys the HC-XD with any damage (TODO: consider creating a more robust solution if need be)
|
|
if ( weapon.name == "satchel_charge" && sMeansOfDeath == "MOD_EXPLOSIVE" )
|
|
{
|
|
iDamage = self.health + 1;
|
|
}
|
|
|
|
return iDamage;
|
|
}
|
|
|
|
|
|
|
|
function OnDeath( eInflictor, eAttacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime )
|
|
{
|
|
rcbomb = self;
|
|
player = rcbomb.owner;
|
|
|
|
player endon( "disconnect" );
|
|
player endon( "joined_team" );
|
|
player endon( "joined_spectators" );
|
|
|
|
killstreakrules::killstreakStop( "rcbomb", rcbomb.originalTeam, rcbomb.killstreak_id );
|
|
|
|
rcbomb clientfield::set( "enemyvehicle", 0 );
|
|
|
|
//if( isdefined( player ) && ( !isdefined( eAttacker ) || ( eAttacker.team != rcbomb.team ) ) && !level.gameEnded )
|
|
//rcbomb remote_weapons::do_static_fx();
|
|
|
|
rcbomb Explode( eAttacker, weapon );
|
|
|
|
hide_after_wait_time = ( ( rcbomb.died_by_emp === true ) ? ( 0.2 ) : ( 0.1 ) );
|
|
if( isdefined( player ) )
|
|
{
|
|
player util::freeze_player_controls( true );
|
|
rcbomb thread HideAfterWait( hide_after_wait_time );
|
|
//rcbomb util::DeleteAfterTime( RCBOMB_SHUTDOWN_DELAY );
|
|
wait( ( 0.2 ) );
|
|
player util::freeze_player_controls( false );
|
|
}
|
|
else
|
|
{
|
|
rcbomb thread HideAfterWait( hide_after_wait_time );
|
|
//rcbomb util::DeleteAfterTime( RCBOMB_SHUTDOWN_DELAY_ABANDONED );
|
|
}
|
|
|
|
if ( isdefined( rcbomb ) )
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
}
|
|
|
|
function WatchGameEnded()
|
|
{
|
|
rcbomb = self;
|
|
rcbomb endon( "death" );
|
|
|
|
level waittill("game_ended");
|
|
|
|
rcbomb.abandoned = true;
|
|
rcbomb.selfDestruct = true;
|
|
rcbomb notify( "rcbomb_shutdown" );
|
|
}
|
|
|
|
function HideAfterWait( waitTime )
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait waitTime;
|
|
self SetInvisibleToAll();
|
|
}
|
|
|
|
function Explode( attacker, weapon )
|
|
{
|
|
self endon ("death");
|
|
owner = self.owner;
|
|
|
|
if ( !isdefined( attacker ) && isdefined( self.owner ) )
|
|
{
|
|
attacker = self.owner;
|
|
}
|
|
|
|
self vehicle_death::death_fx();
|
|
self thread vehicle_death::death_radius_damage();
|
|
self thread vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
|
|
|
|
self vehicle::toggle_tread_fx( false );
|
|
self vehicle::toggle_exhaust_fx( false );
|
|
self vehicle::toggle_sounds( false );
|
|
self vehicle::lights_off();
|
|
|
|
self PlayRumbleOnEntity( "rcbomb_explosion" );
|
|
|
|
if ( !self.abandoned && attacker != self.owner && isPlayer( attacker ) )
|
|
{
|
|
attacker challenges::destroyRCBomb( weapon );
|
|
if ( self.owner util::IsEnemyPlayer( attacker ) )
|
|
{
|
|
scoreevents::processScoreEvent( "destroyed_hover_rcxd", attacker, self.owner, weapon );
|
|
LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_RCBOMB", attacker.entnum );
|
|
if ( isdefined( weapon ) && weapon.isValid )
|
|
{
|
|
weaponStatName = "destroyed";
|
|
level.globalKillstreaksDestroyed++;
|
|
// increment the destroyed stat for this, we aren't using the weaponStatName variable from above because it could be "kills" and we don't want that
|
|
weapon_rcbomb = GetWeapon( "rcbomb" );
|
|
attacker AddWeaponStat( weapon_rcbomb, "destroyed", 1 );
|
|
attacker AddWeaponStat( weapon, "destroyed_controlled_killstreak", 1 );
|
|
}
|
|
|
|
self killstreaks::play_destroyed_dialog_on_owner( "rcbomb", self.killstreak_id );
|
|
}
|
|
}
|
|
}
|
|
|
|
function RCCarAllowFriendlyFireDamage( eInflictor, eAttacker, sMeansOfDeath, weapon )
|
|
{
|
|
if ( isdefined( eAttacker ) && eAttacker == self.owner )
|
|
return true;
|
|
|
|
if ( isdefined( eInflictor ) && eInflictor islinkedto( self ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function GetPlacementStartHeight()
|
|
{
|
|
startheight = ( 50 );
|
|
|
|
switch( self GetStance() )
|
|
{
|
|
case "crouch":
|
|
startheight = ( 30 );
|
|
break;
|
|
case "prone":
|
|
startheight = ( 15 );
|
|
break;
|
|
}
|
|
return startheight;
|
|
}
|
|
|
|
function CalculateSpawnOrigin( origin, angles )
|
|
{
|
|
startheight = GetPlacementStartHeight();
|
|
|
|
mins = (-5,-5,0); // keep the min z 0 so the return point is the exact height of the collision
|
|
maxs = (5,5,10);
|
|
|
|
startPoints = [];
|
|
startAngles = [];
|
|
wheelCounts = [];
|
|
testCheck = [];
|
|
largestCount = 0;
|
|
largestCountIndex = 0;
|
|
|
|
testangles = [];
|
|
testangles[0] = (0,0,0);
|
|
testangles[1] = (0,20,0);
|
|
testangles[2] = (0,-20,0);
|
|
testangles[3] = (0,45,0);
|
|
testangles[4] = (0,-45,0);
|
|
|
|
heightoffset = 5;
|
|
|
|
for (i = 0; i < testangles.size; i++ )
|
|
{
|
|
testCheck[i] = false;
|
|
|
|
startAngles[i] = ( 0, angles[1], 0 );
|
|
startPoint = origin + VectorScale( anglestoforward( startAngles[i] + testangles[i]), ( 70 ) );
|
|
endPoint = startPoint - (0,0,100);
|
|
startPoint = startPoint + (0,0,startheight);
|
|
|
|
// ignore water on this one
|
|
mask = (1 << 0) | (1 << 1);
|
|
|
|
// using physicstrace so we dont slip through small cracks
|
|
trace = physicstrace( startPoint, endPoint, mins, maxs, self, mask);
|
|
|
|
// if any player intersection then skip
|
|
if ( isdefined(trace["entity"]) && IsPlayer(trace["entity"]))
|
|
{
|
|
wheelCounts[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
startPoints[i] = trace["position"] + (0,0,heightoffset);
|
|
wheelCounts[i] = TestWheelLocations(startPoints[i],startAngles[i],heightoffset);
|
|
|
|
if ( positionWouldTelefrag( startPoints[i] ) )
|
|
continue;
|
|
|
|
if ( largestCount < wheelCounts[i] )
|
|
{
|
|
largestCount = wheelCounts[i];
|
|
largestCountIndex = i;
|
|
}
|
|
|
|
// going to early out on the first I find with valid tire positions
|
|
if ( wheelCounts[i] >= 3 )
|
|
{
|
|
testCheck[i] = true;
|
|
|
|
if ( TestSpawnOrigin( startPoints[i], startAngles[i] ) )
|
|
{
|
|
placement = SpawnStruct();
|
|
placement.origin = startPoints[i];
|
|
placement.angles = startAngles[i];
|
|
return placement;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < testangles.size; i++ )
|
|
{
|
|
if ( !testCheck[i] )
|
|
{
|
|
if ( wheelCounts[i] >= 2 )
|
|
{
|
|
if ( TestSpawnOrigin( startPoints[i], startAngles[i] ) )
|
|
{
|
|
placement = SpawnStruct();
|
|
placement.origin = startPoints[i];
|
|
placement.angles = startAngles[i];
|
|
return placement;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function TestWheelLocations( origin, angles, heightoffset )
|
|
{
|
|
forward = 13;
|
|
side = 10;
|
|
|
|
wheels = [];
|
|
wheels[0] = ( forward, side, 0 );
|
|
wheels[1] = ( forward, -1 * side, 0 );
|
|
wheels[2] = ( -1 * forward, -1 * side, 0 );
|
|
wheels[3] = ( -1 * forward, side, 0 );
|
|
|
|
height = 5;
|
|
touchCount = 0;
|
|
|
|
yawangles = (0,angles[1],0);
|
|
|
|
for (i = 0; i < 4; i++ )
|
|
{
|
|
wheel = RotatePoint( wheels[i], yawangles );
|
|
startPoint = origin + wheel;
|
|
endPoint = startPoint + (0,0,(-1 * height) - heightoffset);
|
|
startPoint = startPoint + (0,0,height - heightoffset) ;
|
|
|
|
trace = bulletTrace( startPoint, endPoint, false, self );
|
|
if ( trace["fraction"] < 1 )
|
|
{
|
|
touchCount++;
|
|
}
|
|
}
|
|
|
|
return touchCount;
|
|
}
|
|
|
|
function TestSpawnOrigin( origin, angles )
|
|
{
|
|
liftedorigin = origin + (0,0,5);
|
|
size = 12;
|
|
height = 15;
|
|
mins = (-1 * size,-1 * size,0 );
|
|
maxs = ( size,size,height );
|
|
absmins = liftedorigin + mins;
|
|
absmaxs = liftedorigin + maxs;
|
|
|
|
if( BoundsWouldTelefrag( absmins, absmaxs ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
startheight = getPlacementStartHeight();
|
|
|
|
mask = (1 << 0) | (1 << 1) | (1 << 2);
|
|
|
|
// test the volume where we are going to place the car
|
|
// note that this physics trace is not an oriented box.
|
|
trace = physicstrace( liftedorigin, (origin +(0,0,1)), mins, maxs, self, mask);
|
|
|
|
if ( trace["fraction"] < 1 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// swept trace of a small bounding box from head height to where we are placing the car
|
|
// to make sure there is no wall between us and the car
|
|
size = 2.5;
|
|
height = size * 2;
|
|
mins = (-1 * size,-1 * size,0 );
|
|
maxs = ( size,size,height );
|
|
|
|
sweeptrace = physicstrace( (self.origin + (0,0,startheight)), liftedorigin, mins, maxs, self, mask);
|
|
|
|
if ( sweeptrace["fraction"] < 1 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|