diff --git a/maps/mp/bots/_bot.gsc b/maps/mp/bots/_bot.gsc index 6a7956d..da36b57 100644 --- a/maps/mp/bots/_bot.gsc +++ b/maps/mp/bots/_bot.gsc @@ -277,6 +277,7 @@ init() level.bots_fullautoguns[ "peacekeeper" ] = true; level thread fixGamemodes(); + level thread fixPredMissile(); level thread onPlayerConnect(); level thread addNotifyOnAirdrops(); @@ -286,6 +287,23 @@ init() level thread onPlayerChat(); } +/* + Change func pointer to ours, so that we can link the player ref to the rocket +*/ +fixPredMissile() +{ + for ( i = 0; i < 19; i++ ) + { + if ( isdefined( level.killstreakfuncs ) && isdefined( level.killstreakfuncs[ "predator_missile" ] ) ) + { + level.killstreakfuncs[ "predator_missile" ] = ::tryUsePredatorMissileFix; + break; + } + + wait 0.05; + } +} + /* Starts the threads for bots. */ @@ -606,7 +624,7 @@ watchScrabler() onDisconnectPlayer() { name = self.name; - + self waittill( "disconnect" ); waittillframeend; @@ -1168,11 +1186,11 @@ addBots_loop() if ( fillMode == 0 || fillMode == 2 ) { amount += players; - } - - if ( getdvarint( "bots_manage_fill_spec" ) ) - { - amount += spec; + + if ( getdvarint( "bots_manage_fill_spec" ) ) + { + amount += spec; + } } if ( amount < fillAmount ) diff --git a/maps/mp/bots/_bot_internal.gsc b/maps/mp/bots/_bot_internal.gsc index 4e3a789..46fc2d2 100644 --- a/maps/mp/bots/_bot_internal.gsc +++ b/maps/mp/bots/_bot_internal.gsc @@ -234,12 +234,6 @@ onWeaponChange() { first = false; newWeapon = self getcurrentweapon(); - - // hack fix for botstop overridding weapon - if ( newWeapon != "none" ) - { - self switchtoweapon( newWeapon ); - } } else { @@ -375,11 +369,208 @@ watchUsingRemote() self watchUsingAc130(); } + if ( isdefined( self.rocket ) ) + { + self watchUsingPred(); + self BotBuiltinBotAction( "-remote" ); + } + self.bot.targets = []; self notify( "kill_goal" ); } } +/* + Returns the angle delta +*/ +getRemoteAngleSpeed( len ) +{ + furthest = 10.0; + max_speed = 127; + + switch ( self.pers[ "bots" ][ "skill" ][ "base" ] ) + { + case 1: + furthest = 5.0; + max_speed = 20; + break; + + case 2: + furthest = 6.0; + max_speed = 35; + break; + + case 3: + furthest = 7.0; + max_speed = 55; + break; + + case 4: + furthest = 8.0; + max_speed = 65; + break; + + case 5: + furthest = 9.0; + max_speed = 75; + break; + + case 6: + furthest = 10.0; + max_speed = 100; + break; + + case 7: + furthest = 15.0; + max_speed = 127; + break; + } + + if ( len >= furthest ) + { + return max_speed; + } + + if ( len <= 0.0 ) + { + return 0; + } + + return Round( ( len / furthest ) * max_speed ); +} + +/* + time to boost the rocket +*/ +getRemoteBoostTime() +{ + switch ( self.pers[ "bots" ][ "skill" ][ "base" ] ) + { + case 1: + return 99999; + + case 2: + return 15000; + + case 3: + return 10000; + + case 4: + return 5000; + + case 5: + return 2500; + + case 6: + return 1000; + + case 7: + return 500; + + default: + return 500; + } +} + +/* + While in rocket +*/ +watchUsingPred() +{ + self.rocket endon( "death" ); + + self BotBuiltinBotRemoteAngles( 0, 0 ); + self BotBuiltinBotAction( "+remote" ); + + pressedFire = false; + sTime = gettime(); + + while ( isdefined( self.rocket ) ) + { + self.bot.targets = []; // dont want to fire from aim thread + // because geteye doesnt return the eye of the missile + + target = undefined; + myeye = self.rocket.origin; + myangles = self.rocket.angles; + bestfov = 0.0; + + for ( i = level.players.size - 1; i >= 0; i-- ) + { + player = level.players[ i ]; + + if ( !isdefined( player ) || !isdefined( player.team ) ) + { + continue; + } + + if ( player == self || ( level.teambased && player.team == self.team ) ) + { + continue; + } + + if ( player.sessionstate != "playing" || !isreallyalive( player ) ) + { + continue; + } + + if ( player _hasperk( "specialty_coldblooded" ) ) + { + continue; + } + + if ( !bullettracepassed( myeye, player.origin + ( 0, 0, 25 ), false, self.rocket ) ) + { + continue; + } + + thisfov = getConeDot( player.origin, myeye, myangles ); + + if ( thisfov < 0.75 ) + { + continue; + } + + if ( isdefined( target ) && thisfov < bestfov ) + { + continue; + } + + target = player; + bestfov = thisfov; + } + + if ( isdefined( target ) ) + { + if ( !pressedFire && gettime() - sTime > self getRemoteBoostTime() ) + { + pressedFire = true; + self thread pressFire(); + } + + if ( bestfov < 0.999995 && distancesquared( target.origin, myeye ) > 256 * 256 ) + { + angles = vectortoangles( ( target.origin - myeye ) - anglestoforward( myangles ) ); + angles -= myangles; + angles = ( angleclamp180( angles[ 0 ] ), angleclamp180( angles[ 1 ] ), 0 ); + angles = vectornormalize( angles ) * self getRemoteAngleSpeed( length( angles ) ); + + self BotBuiltinBotRemoteAngles( int( angles[ 0 ] ), int( angles[ 1 ] ) ); + } + else + { + self BotBuiltinBotRemoteAngles( 0, 0 ); + } + } + else + { + self BotBuiltinBotRemoteAngles( 0, 0 ); + } + + wait 0.05; + } +} + /* WHen it uses the helicopter minigun */ @@ -1736,7 +1927,7 @@ aim_loop() { self thread bot_lookat( target gettagorigin( "j_spine4" ), 0.05 ); } - else if ( !nadeAimOffset && conedot > 0.999 && lengthsquared( aimoffset ) < 0.05 ) + else if ( !nadeAimOffset && conedot > 0.999995 && lengthsquared( aimoffset ) < 0.05 ) { self thread bot_lookat( aimpos, 0.05 ); } @@ -1754,7 +1945,7 @@ aim_loop() conedot = getConeDot( aimpos, eyePos, angles ); - if ( !nadeAimOffset && conedot > 0.999 && lengthsquared( aimoffset ) < 0.05 ) + if ( !nadeAimOffset && conedot > 0.999995 && lengthsquared( aimoffset ) < 0.05 ) { self thread bot_lookat( aimpos, 0.05 ); } @@ -3127,7 +3318,7 @@ bot_lookat( pos, time, vel, doAimPredict ) for ( i = 0; i < steps; i++ ) { myAngle = ( angleclamp180( myAngle[ 0 ] + X ), angleclamp180( myAngle[ 1 ] + Y ), 0 ); - self setplayerangles( myAngle ); + self BotBuiltinBotAngles( myAngle ); wait 0.05; } } diff --git a/maps/mp/bots/_bot_script.gsc b/maps/mp/bots/_bot_script.gsc index 125da8f..7ad1cf7 100644 --- a/maps/mp/bots/_bot_script.gsc +++ b/maps/mp/bots/_bot_script.gsc @@ -5024,27 +5024,6 @@ getKillstreakTargetLocation() return location; } -/* - Clears remote usage when bot dies -*/ -clear_remote_on_death( isac130 ) -{ - self endon( "bot_clear_remote_on_death" ); - level endon( "game_ended" ); - - self waittill_either( "death", "disconnect" ); - - if ( isdefined( isac130 ) && isac130 ) - { - level.ac130inuse = false; - } - - if ( isdefined( self ) ) - { - self clearusingremote(); - } -} - /* Returns if any harriers exists that is an enemy */ @@ -5142,6 +5121,11 @@ bot_killstreak_think_loop( data ) self thread BotPressAttack( 0.05 ); } + if ( iskillstreakweapon( curWeap ) ) + { + self thread changeToWeapon( self getlastweapon() ); + return; + } streakName = self.pers[ "killstreaks" ][ 0 ].streakname; @@ -5246,50 +5230,11 @@ bot_killstreak_think_loop( data ) self BotNotifyBotEvent( "killstreak", "call", streakName, location ); self BotRandomStance(); - self setusingremote( "remotemissile" ); - self thread clear_remote_on_death(); self BotStopMoving( true ); + self changeToWeapon( ksWeap ); - if ( !self changeToWeapon( ksWeap ) ) - { - self clearusingremote(); - self notify( "bot_clear_remote_on_death" ); - self BotStopMoving( false ); - return; - } - - wait 0.05; - self thread changeToWeapon( ksWeap ); // prevent script from changing back - - wait 1; - self notify( "bot_clear_remote_on_death" ); + wait 3; self BotStopMoving( false ); - - if ( self isemped() ) - { - self clearusingremote(); - self thread changeToWeapon( curWeap ); - return; - } - - self BotFreezeControls( true ); - - self maps\mp\killstreaks\_killstreaks::usedkillstreak( "predator_missile", true ); - self maps\mp\killstreaks\_killstreaks::shufflekillstreaksfilo( "predator_missile" ); - self maps\mp\killstreaks\_killstreaks::giveownedkillstreakitem(); - - rocket = magicbullet( "remotemissile_projectile_mp", self.origin + ( 0.0, 0.0, 7000.0 - ( self.pers[ "bots" ][ "skill" ][ "base" ] * 400 ) ), location, self ); - rocket.lifeid = lifeId; - rocket.type = "remote"; - - rocket thread maps\mp\gametypes\_weapons::addmissiletosighttraces( self.pers[ "team" ] ); - rocket thread maps\mp\killstreaks\_remotemissile::handledamage(); - thread maps\mp\killstreaks\_remotemissile::missileeyes( self, rocket ); - - self waittill( "stopped_using_remote" ); - - wait 1; - self BotFreezeControls( false ); } else if ( streakName == "ac130" ) { diff --git a/maps/mp/bots/_bot_utility.gsc b/maps/mp/bots/_bot_utility.gsc index 3fcf81d..72dae8a 100644 --- a/maps/mp/bots/_bot_utility.gsc +++ b/maps/mp/bots/_bot_utility.gsc @@ -134,6 +134,28 @@ BotBuiltinBotMeleeParams( yaw, dist ) } } +/* + Sets remote angles +*/ +BotBuiltinBotRemoteAngles( pitch, yaw ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botremoteangles" ] ) ) + { + self [[ level.bot_builtins[ "botremoteangles" ] ]]( pitch, yaw ); + } +} + +/* + Sets angles +*/ +BotBuiltinBotAngles( angles ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botangles" ] ) ) + { + self [[ level.bot_builtins[ "botangles" ] ]]( angles ); + } +} + /* Returns if player is the host */ @@ -3349,3 +3371,167 @@ botPlayerModelForWeapon( weapon, secondary ) break; } } + +/* + Make player ref attach to rocket ent +*/ +tryUsePredatorMissileFix( lifeId ) +{ + if ( isdefined( level.civilianjetflyby ) ) + { + self iprintlnbold( &"MP_CIVILIAN_AIR_TRAFFIC" ); + return false; + } + + self setusingremote( "remotemissile" ); + result = self maps\mp\killstreaks\_killstreaks::initridekillstreak(); + + if ( result != "success" ) + { + if ( result != "disconnect" ) + { + self clearusingremote(); + } + + return false; + } + + level thread _fireFix( lifeId, self ); + + return true; +} + +/* + Make player ref attach to rocket ent +*/ +_fireFix( lifeId, player ) +{ + remoteMissileSpawnArray = getentarray( "remoteMissileSpawn", "targetname" ); + //assertEX( remoteMissileSpawnArray.size > 0 && getMapCustom( "map" ) != "", "No remote missile spawn points found. Contact friendly neighborhood designer" ); + + foreach ( spawn in remoteMissileSpawnArray ) + { + if ( isdefined( spawn.target ) ) + { + spawn.targetent = getent( spawn.target, "targetname" ); + } + } + + if ( remoteMissileSpawnArray.size > 0 ) + { + remoteMissileSpawn = player maps\mp\killstreaks\_remotemissile::getbestspawnpoint( remoteMissileSpawnArray ); + } + else + { + remoteMissileSpawn = undefined; + } + + if ( isdefined( remoteMissileSpawn ) ) + { + startPos = remoteMissileSpawn.origin; + targetPos = remoteMissileSpawn.targetent.origin; + + //thread drawLine( startPos, targetPos, 30, (0,1,0) ); + + vector = vectornormalize( startPos - targetPos ); + startPos = vector_multiply( vector, 14000 ) + targetPos; + + //thread drawLine( startPos, targetPos, 15, (1,0,0) ); + + rocket = magicbullet( "remotemissile_projectile_mp", startpos, targetPos, player ); + } + else + { + upVector = ( 0, 0, level.missileremotelaunchvert ); + backDist = level.missileremotelaunchhorz; + targetDist = level.missileremotelaunchtargetdist; + + forward = anglestoforward( player.angles ); + startpos = player.origin + upVector + forward * backDist * -1; + targetPos = player.origin + forward * targetDist; + + rocket = magicbullet( "remotemissile_projectile_mp", startpos, targetPos, player ); + } + + if ( !isdefined( rocket ) ) + { + player clearusingremote(); + return; + } + + rocket thread maps\mp\gametypes\_weapons::addmissiletosighttraces( player.team ); + + rocket thread maps\mp\killstreaks\_remotemissile::handledamage(); + + rocket.lifeid = lifeId; + rocket.type = "remote"; + MissileEyesFix( player, rocket ); +} + +/* + Make player ref attach to rocket ent +*/ +MissileEyesFix( player, rocket ) +{ + //level endon ( "game_ended" ); + player endon( "joined_team" ); + player endon( "joined_spectators" ); + + rocket thread maps\mp\killstreaks\_remotemissile::rocket_cleanupondeath(); + player thread maps\mp\killstreaks\_remotemissile::player_cleanupongameended( rocket ); + player thread maps\mp\killstreaks\_remotemissile::player_cleanuponteamchange( rocket ); + + player visionsetmissilecamforplayer( "black_bw", 0 ); + + player endon ( "disconnect" ); + + if ( isdefined( rocket ) ) + { + player visionsetmissilecamforplayer( game["thermal_vision"], 1.0 ); + player thread maps\mp\killstreaks\_remotemissile::delayedfofoverlay(); + player cameralinkto( rocket, "tag_origin" ); + player controlslinkto( rocket ); + + // our additions + player.rocket = rocket; + rocket.owner = player; + + if ( getdvarint( "camera_thirdPerson" ) ) + { + player setthirdpersondof( false ); + } + + rocket waittill( "death" ); + + // is defined check required because remote missile doesnt handle lifetime explosion gracefully + // instantly deletes its self after an explode and death notify + if ( isdefined( rocket ) ) + { + player maps\mp\_matchdata::logkillstreakevent( "predator_missile", rocket.origin ); + } + + player controlsunlink(); + player freezecontrolswrapper( true ); + player.rocket = undefined; // our addition + + // If a player gets the final kill with a hellfire, level.gameEnded will already be true at this point + if ( !level.gameended || isdefined( player.finalkill ) ) + { + player thread maps\mp\killstreaks\_remotemissile::staticeffect( 0.5 ); + } + + wait ( 0.5 ); + + player thermalvisionfofoverlayoff(); + + player cameraunlink(); + + if ( getdvarint( "camera_thirdPerson" ) ) + { + player setthirdpersondof( true ); + } + + } + + player clearusingremote(); +} diff --git a/scripts/mp/bots_adapter_iw4x.gsc b/scripts/mp/bots_adapter_iw4x.gsc index a0de4ef..2e15c97 100644 --- a/scripts/mp/bots_adapter_iw4x.gsc +++ b/scripts/mp/bots_adapter_iw4x.gsc @@ -8,6 +8,8 @@ init() level.bot_builtins[ "botstop" ] = ::do_botstop; level.bot_builtins[ "botmovement" ] = ::do_botmovement; level.bot_builtins[ "botmeleeparams" ] = ::do_botmeleeparams; + level.bot_builtins[ "botremoteangles" ] = ::do_botremoteangles; + level.bot_builtins[ "botangles" ] = ::do_botangles; } do_printconsole( s ) @@ -52,3 +54,13 @@ do_botmeleeparams( yaw, dist ) { self botmeleeparams( yaw, dist ); } + +do_botremoteangles( pitch, yaw ) +{ + self botremoteangles( pitch, yaw ); +} + +do_botangles( angles ) +{ + self botangles( angles[ 0 ], angles[ 1 ], angles[ 2 ] ); +}