diff --git a/scripts/sp/T4ZM_zbots_main.gsc b/scripts/sp/T4ZM_zbots_main.gsc index bdcb326..2f5f11e 100644 --- a/scripts/sp/T4ZM_zbots_main.gsc +++ b/scripts/sp/T4ZM_zbots_main.gsc @@ -6,6 +6,8 @@ #include scripts\sp\bots\bot_objective_common; #include scripts\sp\bots\bot_difficulty_presets_common; #include scripts\sp\bots\bot_personality_presets_common; +#include scripts\sp\bots\bot_pathing; +#include scripts\sp\bots\bot_target_common; #include scripts\sp\bots\actions\combat; #include scripts\sp\bots\actions\movement; #include scripts\sp\bots\actions\objective; @@ -116,7 +118,7 @@ on_player_connect() while ( true ) { level waittill( "connected", player ); - player.id = i; + player.client_id = i; if ( player isBot() ) { player.successfully_grabbed_powerup = false; @@ -213,6 +215,7 @@ init() level.callbackActorSpawned = ::zbots_actor_spawned; level.callbackActorKilled = ::zbots_actor_killed; + level.callbackActorDamage = ::zbots_actor_damage; level thread watch_for_downed_players(); @@ -223,6 +226,7 @@ init() zbots_actor_spawned() { + self.is_actor = true; self thread add_actor_to_target_glob(); } @@ -244,6 +248,30 @@ zbots_actor_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir free_bot_target( self.targetname, self.target_id ); } +zbots_actor_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, iModelIndex, iTimeOffset ) +{ + if ( isPlayer( eAttacker ) && iDamage > 0 ) + { + eAttacker set_target_damaged_by( self.targetname, self.target_id ); + eAttacker thread remove_target_damaged_by_after_time( self, self.target_id ); + } + + self FinishActorDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, iModelIndex, iTimeOffset ); +} + +remove_target_damaged_by_after_time( target_ent, id ) +{ + player_entnum = self getEntityNumber(); + self endon( "disconnect" ); + target_ent notify( "damaged_by_player_" + player_entnum ); + target_ent endon( "damaged_by_player_" + player_entnum ); + target_ent endon( "death" ); + + wait 6; + + self clear_target_damaged_by( target_ent.targetname, id ); +} + spawn_bots() { level waittill( "connected", player ); @@ -271,6 +299,7 @@ spawn_bots() bot.action_queue[ "objective" ] = []; bot.action_queue[ "combat" ] = []; bot.action_queue[ "movement" ] = []; + bot.action_queue[ "look" ] = []; bot register_action_queue_actions(); bot thread bot_think(); bot_count++; @@ -436,7 +465,7 @@ watch_for_downed_players() { continue; } - add_possible_bot_objective( "revive", player.id, true, player ); + add_possible_bot_objective( "revive", player.client_id, true, player ); player thread free_revive_objective_when_needed(); } } diff --git a/scripts/sp/bots/actions/look.gsc b/scripts/sp/bots/actions/look.gsc index e1d19e4..10f985f 100644 --- a/scripts/sp/bots/actions/look.gsc +++ b/scripts/sp/bots/actions/look.gsc @@ -55,7 +55,14 @@ bot_lookatobjective_priority() bot_lookattarget() { - + self endon( "disconnect" ); + while ( self bot_has_target() && isAlive( self.zbot_current_target.target_ent ) ) + { + target = self.zbot_current_target; + target_ent = target.target_ent; + self bot_lookat( target_ent getTagOrigin( "j_head" ), time, vel, doAimPredict ); + wait 0.05; + } } bot_lookattarget_process_order() @@ -65,12 +72,12 @@ bot_lookattarget_process_order() bot_should_lookattarget() { - return false; + return self bot_has_target(); } bot_check_complete_lookattarget() { - return false; + return !self bot_has_target(); } bot_set_complete_lookattarget() @@ -171,9 +178,6 @@ bot_lookat( pos, time, vel, doAimPredict ) self endon( "player_downed" ); level endon( "end_game" ); - if ( level.gameEnded || level.inPrematchPeriod || self.bot.isfrozen || !getDvarInt( "bots_play_aim" ) ) - return; - if ( !isDefined( pos ) ) return; diff --git a/scripts/sp/bots/bot_objective_common.gsc b/scripts/sp/bots/bot_objective_common.gsc index eb3ff69..909784b 100644 --- a/scripts/sp/bots/bot_objective_common.gsc +++ b/scripts/sp/bots/bot_objective_common.gsc @@ -61,6 +61,11 @@ set_objective_for_bot( objective_group, id ) self.zbot_current_objective = objective; } +clear_objective_for_bot() +{ + self.zbot_current_objective = undefined; +} + set_bot_objective_blocked_by_objective( primary_objective_group, primary_id, blocked_by_objective_group, blocked_by_id ) { primary_active_objectives = level.zbot_objective_glob[ primary_objective_group ].active_objectives; @@ -164,5 +169,17 @@ free_bot_objective( objective_group, id ) return; } + players = getPlayers(); + for ( i = 0; i < players.size; i++ ) + { + if ( players[ i ].pers[ "isBot" ] ) + { + if ( players[ i ].zbot_current_objective == objective ) + { + players[ i ].zbot_current_objective = undefined; + } + } + } + objective = undefined; } \ No newline at end of file diff --git a/scripts/sp/bots/bot_pathing b/scripts/sp/bots/bot_pathing new file mode 100644 index 0000000..e69de29 diff --git a/scripts/sp/bots/bot_target_common.gsc b/scripts/sp/bots/bot_target_common.gsc index 7ae3a3e..ddbeaf9 100644 --- a/scripts/sp/bots/bot_target_common.gsc +++ b/scripts/sp/bots/bot_target_common.gsc @@ -46,13 +46,18 @@ get_all_targets_for_group( target_group ) return level.zbot_target_glob[ target_group ].active_targets; } +bot_has_target() +{ + return isDefined( self.zbot_current_target ); +} + set_target_for_bot( target_group, id ) { - possible_targets = level.zbot_target_glob[ primary_target_group ].active_targets; + possible_targets = level.zbot_target_glob[ target_group ].active_targets; target = possible_targets[ "targ_id_" + id ]; - target_exists = isDefined( primary_target ); + target_exists = isDefined( target ); assert( target_exists, "Target with " + id + " id does not point to a target in group " + target_group ); if ( !target_exists ) @@ -73,6 +78,81 @@ set_target_for_bot( target_group, id ) target.targeted_by[ target.targeted_by.size ] = self; } +clear_target_for_bot( target_group, id ) +{ + possible_targets = level.zbot_target_glob[ target_group ].active_targets; + + target = possible_targets[ "targ_id_" + id ]; + + target_exists = isDefined( target ); + + assert( target_exists, "Target with " + id + " id does not point to a target in group " + target_group ); + if ( !target_exists ) + { + return; + } + + for ( i = 0; i < target.targeted_by.size; i++ ) + { + if ( target.targeted_by[ i ] == self ) + { + target.targeted_by[ i ] = undefined; + return; + } + } + + self.zbot_current_target = undefined; +} + +set_target_damaged_by( target_group, id ) +{ + possible_targets = level.zbot_target_glob[ target_group ].active_targets; + + target = possible_targets[ "targ_id_" + id ]; + + target_exists = isDefined( target ); + + assert( target_exists, "Target with " + id + " id does not point to a target in group " + target_group ); + if ( !target_exists ) + { + return; + } + + for ( i = 0; i < target.damaged_by.size; i++ ) + { + if ( target.damaged_by[ i ] == self ) + { + return; + } + } + + target.damaged_by[ target.damaged_by.size ] = self; +} + +clear_target_damaged_by( target_group, id ) +{ + possible_targets = level.zbot_target_glob[ target_group ].active_targets; + + target = possible_targets[ "targ_id_" + id ]; + + target_exists = isDefined( target ); + + assert( target_exists, "Target with " + id + " id does not point to a target in group " + target_group ); + if ( !target_exists ) + { + return; + } + + for ( i = 0; i < target.damaged_by.size; i++ ) + { + if ( target.damaged_by[ i ] == self ) + { + target.damaged_by[ i ] = undefined; + return; + } + } +} + free_bot_target( target_group, id ) { active_targets = level.zbot_global_shared_target_glob[ target_group ].active_targets; @@ -80,14 +160,234 @@ free_bot_target( target_group, id ) target = active_targets[ "targ_id_" + id ]; target_exists = isDefined( target ); + assert( target_exists, "Target with " + id + " id number does not point to a target in group " + target_group ); if ( !target_exists ) { return; } + players = getPlayers(); + for ( i = 0; i < players.size; i++ ) + { + if ( players[ i ].pers[ "isBot" ] ) + { + if ( players[ i ].zbot_current_target == target ) + { + players[ i ].zbot_current_target = undefined; + } + } + } + target.damaged_by = undefined; target.targeted_by = undefined; target = undefined; +} + +/* + The main target thread, will update the bot's main target. Will auto target enemy players and handle script targets. +*/ +target_loop() +{ + myEye = self GetEyePos(); + theTime = getTime(); + myAngles = self GetPlayerAngles(); + myFov = self.pers["bots"]["skill"]["fov"]; + bestTargets = []; + bestTime = 2147483647; + rememberTime = self.pers["bots"]["skill"]["remember_time"]; + initReactTime = self.pers["bots"]["skill"]["init_react_time"]; + hasTarget = isDefined( self.bot.target ); + adsAmount = self PlayerADS(); + adsFovFact = self.pers["bots"]["skill"]["ads_fov_multi"]; + + if ( hasTarget && !isDefined( self.bot.target.entity ) ) + { + self.bot.target = undefined; + hasTarget = false; + } + + // reduce fov if ads'ing + if ( adsAmount > 0 ) + { + myFov *= 1 - adsFovFact * adsAmount; + } + + playercount = level.players.size; + + for ( i = -1; i < playercount; i++ ) + { + obj = undefined; + + if ( i == -1 ) + { + if ( !isDefined( self.bot.script_target ) ) + continue; + + ent = self.bot.script_target; + key = ent getEntityNumber() + ""; + daDist = distanceSquared( self.origin, ent.origin ); + obj = self.bot.targets[key]; + isObjDef = isDefined( obj ); + entOrigin = ent.origin; + + if ( isDefined( self.bot.script_target_offset ) ) + entOrigin += self.bot.script_target_offset; + + if ( SmokeTrace( myEye, entOrigin, level.smokeRadius ) && bulletTracePassed( myEye, entOrigin, false, ent ) ) + { + if ( !isObjDef ) + { + obj = self createTargetObj( ent, theTime ); + obj.offset = self.bot.script_target_offset; + + self.bot.targets[key] = obj; + } + + self targetObjUpdateTraced( obj, daDist, ent, theTime, true ); + } + else + { + if ( !isObjDef ) + continue; + + self targetObjUpdateNoTrace( obj ); + + if ( obj.no_trace_time > rememberTime ) + { + self.bot.targets[key] = undefined; + continue; + } + } + } + else + { + player = level.players[i]; + + if ( !player IsPlayerModelOK() ) + continue; + + if ( player == self ) + continue; + + key = player getEntityNumber() + ""; + obj = self.bot.targets[key]; + daDist = distanceSquared( self.origin, player.origin ); + isObjDef = isDefined( obj ); + + if ( ( level.teamBased && self.team == player.team ) || player.sessionstate != "playing" || !isAlive( player ) ) + { + if ( isObjDef ) + self.bot.targets[key] = undefined; + + continue; + } + + targetHead = player getTagOrigin( "j_head" ); + targetAnkleLeft = player getTagOrigin( "j_ankle_le" ); + targetAnkleRight = player getTagOrigin( "j_ankle_ri" ); + + traceHead = bulletTrace( myEye, targetHead, false, undefined ); + traceAnkleLeft = bulletTrace( myEye, targetAnkleLeft, false, undefined ); + traceAnkleRight = bulletTrace( myEye, targetAnkleRight, false, undefined ); + + canTargetPlayer = ( ( sightTracePassed( myEye, targetHead, false, undefined ) || + sightTracePassed( myEye, targetAnkleLeft, false, undefined ) || + sightTracePassed( myEye, targetAnkleRight, false, undefined ) ) + + && ( ( traceHead["fraction"] >= 1.0 || traceHead["surfacetype"] == "glass" ) || + ( traceAnkleLeft["fraction"] >= 1.0 || traceAnkleLeft["surfacetype"] == "glass" ) || + ( traceAnkleRight["fraction"] >= 1.0 || traceAnkleRight["surfacetype"] == "glass" ) ) + + && ( SmokeTrace( myEye, player.origin, level.smokeRadius ) || + daDist < level.bots_maxKnifeDistance * 4 ) + + && ( getConeDot( player.origin, self.origin, myAngles ) >= myFov || + ( isObjDef && obj.trace_time ) ) ); + + if ( isDefined( self.bot.target_this_frame ) && self.bot.target_this_frame == player ) + { + self.bot.target_this_frame = undefined; + + canTargetPlayer = true; + } + + if ( canTargetPlayer ) + { + if ( !isObjDef ) + { + obj = self createTargetObj( player, theTime ); + + self.bot.targets[key] = obj; + } + + self targetObjUpdateTraced( obj, daDist, player, theTime, false ); + } + else + { + if ( !isObjDef ) + continue; + + self targetObjUpdateNoTrace( obj ); + + if ( obj.no_trace_time > rememberTime ) + { + self.bot.targets[key] = undefined; + continue; + } + } + } + + if ( !isdefined( obj ) ) + continue; + + if ( theTime - obj.time < initReactTime ) + continue; + + timeDiff = theTime - obj.trace_time_time; + + if ( timeDiff < bestTime ) + { + bestTargets = []; + bestTime = timeDiff; + } + + if ( timeDiff == bestTime ) + bestTargets[key] = obj; + } + + if ( hasTarget && isDefined( bestTargets[self.bot.target.entity getEntityNumber() + ""] ) ) + return; + + closest = 2147483647; + toBeTarget = undefined; + + bestKeys = getArrayKeys( bestTargets ); + + for ( i = bestKeys.size - 1; i >= 0; i-- ) + { + theDist = bestTargets[bestKeys[i]].dist; + + if ( theDist > closest ) + continue; + + closest = theDist; + toBeTarget = bestTargets[bestKeys[i]]; + } + + beforeTargetID = -1; + newTargetID = -1; + + if ( hasTarget && isDefined( self.bot.target.entity ) ) + beforeTargetID = self.bot.target.entity getEntityNumber(); + + if ( isDefined( toBeTarget ) && isDefined( toBeTarget.entity ) ) + newTargetID = toBeTarget.entity getEntityNumber(); + + if ( beforeTargetID != newTargetID ) + { + self.bot.target = toBeTarget; + self notify( "new_enemy" ); + } } \ No newline at end of file