mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 13:00:25 +02:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc45338214 | ||
|
|
a663d1b100 | ||
|
|
94b17a7913 |
+560
-28
@@ -24,6 +24,14 @@ player holding the action button does), ammunition for one it has when it
|
||||
runs low, an overshield, camouflage, a health pack when hurt, grenades.
|
||||
In a fight it changes to its other weapon when the one in hand is empty,
|
||||
and now and then lobs a grenade at an enemy some way off.
|
||||
|
||||
In the objective games a bot plays for the objective, fighting on the way:
|
||||
- capture the flag: the carrier of the enemy's flag runs it home; the rest
|
||||
hunt whoever carries their own flag, return it where it lies dropped, or
|
||||
else go for the enemy's flag (every third bot stays to guard its own);
|
||||
- king of the hill: to the hill, and around in it;
|
||||
- oddball: to the ball (taken as a weapon is), hunting whoever carries it;
|
||||
with it, away from the enemy, striking those who come near.
|
||||
*/
|
||||
|
||||
#include "cseries.h"
|
||||
@@ -38,6 +46,7 @@ and now and then lobs a grenade at an enemy some way off.
|
||||
#include "items/items.h"
|
||||
#include "items/weapons.h"
|
||||
#include "tag_files/tag_files.h"
|
||||
#include "scenario/scenario_definitions.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "scenario/scenario.h"
|
||||
@@ -94,6 +103,14 @@ struct bot
|
||||
short error_ticks;
|
||||
real_point3d last_position;
|
||||
short stuck_ticks;
|
||||
/* the point it walks to, how near it was, and a step aside (ticks
|
||||
left, and which way) when it makes no way */
|
||||
real_point3d walk_point;
|
||||
real walk_distance;
|
||||
short sidestep_ticks;
|
||||
real sidestep;
|
||||
/* whether it walks to a point this tick (not only weaving in a fight) */
|
||||
boolean walking;
|
||||
real wander_yaw;
|
||||
short weave_phase;
|
||||
/* where it is going, and the way there */
|
||||
@@ -107,12 +124,35 @@ struct bot
|
||||
long item_look_time;
|
||||
long grenade_time;
|
||||
long weapon_switch_time;
|
||||
/* the objective, as it played it this tick: where it goes, whom it
|
||||
hunts first, and whether it carries the flag or ball (no firing) */
|
||||
boolean has_objective;
|
||||
real_point3d objective;
|
||||
long objective_unit_index;
|
||||
boolean carrying;
|
||||
};
|
||||
|
||||
/* the objective game's things this tick: the flags or balls (weapons), whose
|
||||
team or ball number, where, and who carries them */
|
||||
enum
|
||||
{
|
||||
MAXIMUM_OBJECTIVE_ITEMS = 8,
|
||||
};
|
||||
|
||||
struct objective_item
|
||||
{
|
||||
long item_index;
|
||||
short owner;
|
||||
real_point3d position;
|
||||
long carrier_player_index;
|
||||
boolean handled;
|
||||
};
|
||||
|
||||
/* ---------- globals */
|
||||
|
||||
/* (one search at a time, shared: 80 kB) */
|
||||
static struct path_state bot_path_state;
|
||||
static struct path_debug_storage bot_path_debug;
|
||||
|
||||
static struct
|
||||
{
|
||||
@@ -125,6 +165,14 @@ static struct
|
||||
long grenades;
|
||||
long pickups;
|
||||
long switches;
|
||||
long stucks;
|
||||
long long_stucks;
|
||||
long fail_start;
|
||||
long fail_goal;
|
||||
long fail_search;
|
||||
/* the objective game's things (bots_update_objectives) */
|
||||
short objective_item_count;
|
||||
struct objective_item objective_items[MAXIMUM_OBJECTIVE_ITEMS];
|
||||
/* how well they play (bots.skill, as the game began) */
|
||||
short reaction_ticks;
|
||||
real turn_rate;
|
||||
@@ -230,7 +278,10 @@ static boolean bot_find_path(
|
||||
|
||||
bot->path.valid = FALSE;
|
||||
if (start_surface == NONE)
|
||||
{
|
||||
bots.fail_start++;
|
||||
return FALSE;
|
||||
}
|
||||
/* (the goal's surface: under it, as a biped would stand there) */
|
||||
{
|
||||
struct collision_result collision;
|
||||
@@ -246,15 +297,42 @@ static boolean bot_find_path(
|
||||
}
|
||||
}
|
||||
if (goal_surface == NONE)
|
||||
{
|
||||
bots.fail_goal++;
|
||||
return FALSE;
|
||||
}
|
||||
/* (a little wider than a biped: paths that keep off walls and corners) */
|
||||
path_input_new(&input, 0.25f, FALSE, unit_index);
|
||||
path_input_set_start(&input, &start, start_surface);
|
||||
path_state_new(&input, &bot_path_state, NULL);
|
||||
bot_path_debug.path_traverse_result = 0;
|
||||
bot_path_debug.path_build_result = 0;
|
||||
path_state_new(&input, &bot_path_state, &bot_path_debug);
|
||||
path_state_destination(&bot_path_state, &goal, goal_surface, BOT_GOAL_RADIUS);
|
||||
if (!path_state_find(&bot_path_state) || !path_state_build_path(&bot_path_state, &bot->path))
|
||||
if (path_state_find(&bot_path_state) && !path_state_build_path(&bot_path_state, &bot->path) &&
|
||||
bot_path_debug.path_build_result == _path_build_result_obstacle_avoidance_failed &&
|
||||
bot_path_debug.smoothed_step_count > 0)
|
||||
{
|
||||
/* (a way there, but none round the objects on it, the other players
|
||||
among them: the way, smoothed, without; a step aside settles a
|
||||
bump) */
|
||||
short step;
|
||||
|
||||
csmemset(&bot->path, 0, sizeof(bot->path));
|
||||
bot->path.valid = TRUE;
|
||||
bot->path.step_count = (char)MIN(bot_path_debug.smoothed_step_count, 4);
|
||||
for (step = 0; step < bot->path.step_count; step++)
|
||||
bot->path.steps[step] = bot_path_debug.smoothed_steps[step];
|
||||
bot->path.endpoint.point = bot->path.steps[bot->path.step_count - 1].point;
|
||||
bot->path.endpoint.surface_index = bot->path.steps[bot->path.step_count - 1].surface_index;
|
||||
bot->path.steps_finish_path = bot_path_debug.raw_step_count <= 4;
|
||||
bots.paths_found++;
|
||||
return TRUE;
|
||||
}
|
||||
if (!bot->path.valid)
|
||||
{
|
||||
bot->path.valid = FALSE;
|
||||
bots.paths_failed++;
|
||||
bots.fail_search++;
|
||||
return FALSE;
|
||||
}
|
||||
bots.paths_found += bot->path.valid != FALSE;
|
||||
@@ -262,14 +340,69 @@ static boolean bot_find_path(
|
||||
}
|
||||
|
||||
/* the point it walks to now: the path's next, or the goal itself */
|
||||
/* whether a biped walks straight from one point to the other: nothing of
|
||||
the level between them at its knees */
|
||||
static boolean bot_clear_walk(
|
||||
long unit_index,
|
||||
real_point3d const *from,
|
||||
real_point3d const *to)
|
||||
{
|
||||
struct collision_result collision;
|
||||
real_point3d start = *from;
|
||||
real_vector3d vector;
|
||||
|
||||
start.z += 0.3f;
|
||||
vector.i = to->x - from->x;
|
||||
vector.j = to->y - from->y;
|
||||
vector.k = to->z - from->z;
|
||||
return !collision_test_vector(FLAG(_collision_test_structure_bit) | FLAG(_collision_test_front_facing_surfaces_bit),
|
||||
&start, &vector, unit_index, &collision);
|
||||
}
|
||||
|
||||
static boolean bot_next_point_now(
|
||||
struct bot *bot,
|
||||
long unit_index,
|
||||
real_point3d const *position,
|
||||
real_point3d *point);
|
||||
|
||||
/* the point it walks to now (kept, to tell whether it gets nearer) */
|
||||
static boolean bot_next_point(
|
||||
struct bot *bot,
|
||||
long unit_index,
|
||||
real_point3d const *position,
|
||||
real_point3d *point)
|
||||
{
|
||||
if (!bot_next_point_now(bot, unit_index, position, point))
|
||||
return FALSE;
|
||||
if (distance3d(point, &bot->walk_point) > 0.2f)
|
||||
bot->walk_distance = 1000000.0f;
|
||||
bot->walk_point = *point;
|
||||
bot->walking = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static boolean bot_next_point_now(
|
||||
struct bot *bot,
|
||||
long unit_index,
|
||||
real_point3d const *position,
|
||||
real_point3d *point)
|
||||
{
|
||||
if (!bot->has_goal)
|
||||
return FALSE;
|
||||
/* (the last steps: straight there, over what the path goes round, as
|
||||
a flag on its stand, where a carrier must step to score, when nothing
|
||||
of the level is in the way) */
|
||||
{
|
||||
real dx = bot->goal.x - position->x;
|
||||
real dy = bot->goal.y - position->y;
|
||||
|
||||
if (dx * dx + dy * dy < 3.0f * 3.0f && fabsf(bot->goal.z - position->z) < 0.6f &&
|
||||
bot_clear_walk(unit_index, position, &bot->goal))
|
||||
{
|
||||
*point = bot->goal;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
if (game_time_get() - bot->path_time >= PATH_REFRESH_TICKS || game_time_get() < bot->path_time)
|
||||
{
|
||||
bot->path_time = game_time_get();
|
||||
@@ -285,6 +418,20 @@ static boolean bot_next_point(
|
||||
|
||||
if (dx * dx + dy * dy > BOT_STEP_RADIUS * BOT_STEP_RADIUS)
|
||||
{
|
||||
/* (the next point in plain view: on to it, not round by this
|
||||
one, a corner's) */
|
||||
real_point3d const *next = bot->path.step_index + 1 < bot->path.step_count ?
|
||||
&bot->path.steps[(short)bot->path.step_index + 1].point : &bot->path.endpoint.point;
|
||||
|
||||
/* (on its own level only: one above may be in view over a
|
||||
ledge that a ramp leads round to) */
|
||||
if (fabsf(next->z - position->z) < 0.6f && bot_clear_walk(unit_index, position, next))
|
||||
{
|
||||
bot->path.step_index++;
|
||||
if (bot->path.step_index >= bot->path.step_count)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
*point = step->point;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -398,7 +545,9 @@ static real bot_weapon_suits(
|
||||
if (strstr(name, "plasma pistol") || strstr(name, "needler"))
|
||||
return worth;
|
||||
if (strstr(name, "pistol"))
|
||||
return distance > 10.0f ? worth + 2.0f : worth;
|
||||
return distance > 10.0f ? worth + 1.0f : worth - 0.5f;
|
||||
if (strstr(name, "assault rifle") || strstr(name, "plasma rifle"))
|
||||
return distance < 12.0f ? worth + 1.5f : worth;
|
||||
return worth;
|
||||
}
|
||||
|
||||
@@ -463,9 +612,11 @@ static real bot_item_worth(
|
||||
return bot_weapon_has_ammunition(carried_index) ? 0.0f : 4.0f;
|
||||
worst = MIN(worst, bot_weapon_worth(carried_weapon->definition_index));
|
||||
}
|
||||
/* (with two, worth what it betters the worse by, and a little for a
|
||||
change of weapon) */
|
||||
if (carried < 2)
|
||||
return worth;
|
||||
return worth - worst > 0.5f ? worth - worst : 0.0f;
|
||||
return worth - worst > 0.5f ? worth - worst + 1.5f : 0.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
@@ -473,7 +624,9 @@ static real bot_item_worth(
|
||||
/* the thing most worth going for near the bot, or NONE */
|
||||
static long bot_find_item(
|
||||
struct unit_datum const *unit,
|
||||
real_point3d const *position)
|
||||
real_point3d const *position,
|
||||
real range,
|
||||
real least_worth)
|
||||
{
|
||||
struct object_iterator iterator;
|
||||
long best = NONE;
|
||||
@@ -489,12 +642,13 @@ static long bot_find_item(
|
||||
|
||||
object_get_origin(iterator.index, &origin);
|
||||
distance = distance3d(position, &origin);
|
||||
if (distance > BOT_ITEM_RANGE)
|
||||
if (distance > range)
|
||||
continue;
|
||||
worth = bot_item_worth(unit, iterator.index);
|
||||
if (worth <= 0.0f)
|
||||
if (worth <= 0.0f || worth < least_worth)
|
||||
continue;
|
||||
score = worth / (1.0f + distance / 8.0f);
|
||||
/* (the strong ones worth a longer way) */
|
||||
score = worth * worth / (1.0f + distance / 15.0f);
|
||||
if (score > best_score)
|
||||
{
|
||||
best_score = score;
|
||||
@@ -548,6 +702,249 @@ static void bot_take_item(
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- objectives */
|
||||
|
||||
static long bots_game_type(
|
||||
void)
|
||||
{
|
||||
return game_engine ? game_engine->type : NONE;
|
||||
}
|
||||
|
||||
/* the flags or balls this tick (once for all bots) */
|
||||
static void bots_update_objectives(
|
||||
void)
|
||||
{
|
||||
struct object_iterator iterator;
|
||||
long type = bots_game_type();
|
||||
|
||||
bots.objective_item_count = 0;
|
||||
if (type != game_engine_ctf && type != game_engine_oddball)
|
||||
return;
|
||||
object_iterator_new(&iterator, _object_mask_weapon, 0);
|
||||
while (object_iterator_next(&iterator) && bots.objective_item_count < MAXIMUM_OBJECTIVE_ITEMS)
|
||||
{
|
||||
struct weapon_datum *weapon;
|
||||
struct objective_item *item;
|
||||
short index;
|
||||
|
||||
if (!weapon_is_flag(iterator.index))
|
||||
continue;
|
||||
weapon = weapon_get(iterator.index);
|
||||
item = &bots.objective_items[bots.objective_item_count++];
|
||||
item->item_index = iterator.index;
|
||||
item->owner = weapon->object.owner_team_index;
|
||||
/* (bit 6: the flag was taken from its stand, game_engine_ctf.c) */
|
||||
item->handled = TEST_FLAG(weapon->weapon.flags, 6);
|
||||
item_get_position_even_if_in_inventory(iterator.index, &item->position);
|
||||
item->carrier_player_index = NONE;
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *player = distributed_player(index);
|
||||
|
||||
if (player && player->unit_index != NONE && unit_has_weapon(player->unit_index, iterator.index))
|
||||
{
|
||||
item->carrier_player_index = DATUM_INDEX_NEW(index, player->identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* a team's flag stand (FALSE: none) */
|
||||
static boolean bots_ctf_base(
|
||||
long team_index,
|
||||
real_point3d *position)
|
||||
{
|
||||
struct scenario *scenario = global_scenario_get();
|
||||
struct game_variant *variant = game_engine_get_variant();
|
||||
long slot = variant && variant->game_engine_variant.ctf.assault ? (team_index + 1) % 2 : team_index;
|
||||
long index;
|
||||
|
||||
if (!scenario)
|
||||
return FALSE;
|
||||
for (index = 0; index < scenario->netgame_flags.count; index++)
|
||||
{
|
||||
struct scenario_netgame_flag *flag =
|
||||
TAG_BLOCK_GET_ELEMENT(&scenario->netgame_flags, index, struct scenario_netgame_flag);
|
||||
|
||||
if (flag->type == _netgame_flag_ctf_flag && flag->team_index == slot)
|
||||
{
|
||||
*position = flag->position;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* what the objective asks of the bot now (bot->has_objective and the rest) */
|
||||
static void bot_play_objective_now(
|
||||
struct bot *bot,
|
||||
struct player_datum const *player,
|
||||
long player_index,
|
||||
real_point3d const *position);
|
||||
|
||||
static void bot_play_objective(
|
||||
struct bot *bot,
|
||||
struct player_datum const *player,
|
||||
long player_index,
|
||||
real_point3d const *position)
|
||||
{
|
||||
boolean was_carrying = bot->carrying;
|
||||
|
||||
bot_play_objective_now(bot, player, player_index, position);
|
||||
if (bot->carrying != was_carrying)
|
||||
{
|
||||
platform_log("bots: player %ld %s", DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index),
|
||||
bot->carrying ? "has taken the flag or ball" : "no longer carries it");
|
||||
}
|
||||
}
|
||||
|
||||
static void bot_play_objective_now(
|
||||
struct bot *bot,
|
||||
struct player_datum const *player,
|
||||
long player_index,
|
||||
real_point3d const *position)
|
||||
{
|
||||
long type = bots_game_type();
|
||||
short index;
|
||||
|
||||
bot->has_objective = FALSE;
|
||||
bot->objective_unit_index = NONE;
|
||||
bot->carrying = FALSE;
|
||||
if (type == game_engine_king)
|
||||
{
|
||||
if (game_engine_get_goal_in_use(0))
|
||||
{
|
||||
real_point3d center;
|
||||
real dx, dy;
|
||||
|
||||
game_engine_get_goal_position(¢er, 0);
|
||||
center.z -= 0.63f;
|
||||
dx = center.x - position->x;
|
||||
dy = center.y - position->y;
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = center;
|
||||
/* (on the hill: about in it, not on its middle) */
|
||||
if (dx * dx + dy * dy < 1.5f * 1.5f)
|
||||
{
|
||||
bot->objective.x += (real)cos(bot->weave_phase * 0.05f) * 1.0f;
|
||||
bot->objective.y += (real)sin(bot->weave_phase * 0.05f) * 1.0f;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type == game_engine_ctf)
|
||||
{
|
||||
struct objective_item *own = NULL;
|
||||
struct objective_item *enemy = NULL;
|
||||
real_point3d base;
|
||||
|
||||
for (index = 0; index < bots.objective_item_count; index++)
|
||||
{
|
||||
struct objective_item *item = &bots.objective_items[index];
|
||||
|
||||
if (item->owner == player->team_index)
|
||||
own = item;
|
||||
else
|
||||
enemy = item;
|
||||
}
|
||||
/* carrying theirs: home */
|
||||
if (enemy && enemy->carrier_player_index == player_index)
|
||||
{
|
||||
bot->carrying = TRUE;
|
||||
if (bots_ctf_base(player->team_index, &base))
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = base;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* ours carried off: after its carrier */
|
||||
if (own && own->carrier_player_index != NONE)
|
||||
{
|
||||
struct player_datum *carrier = player_try_and_get(own->carrier_player_index);
|
||||
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = own->position;
|
||||
bot->objective_unit_index = carrier ? carrier->unit_index : NONE;
|
||||
return;
|
||||
}
|
||||
/* ours dropped: touched, it goes home */
|
||||
if (own && own->handled)
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = own->position;
|
||||
return;
|
||||
}
|
||||
/* (every third guards its own) */
|
||||
if ((DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index) % 3) == 2 && bots_ctf_base(player->team_index, &base))
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = base;
|
||||
if (distance3d(position, &base) < 4.0f)
|
||||
{
|
||||
bot->objective.x += (real)cos(bot->weave_phase * 0.03f) * 3.0f;
|
||||
bot->objective.y += (real)sin(bot->weave_phase * 0.03f) * 3.0f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* theirs: to take it, or to cover its carrier */
|
||||
if (enemy)
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = enemy->position;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type == game_engine_oddball)
|
||||
{
|
||||
struct objective_item *nearest = NULL;
|
||||
real nearest_distance = 1000000.0f;
|
||||
|
||||
for (index = 0; index < bots.objective_item_count; index++)
|
||||
{
|
||||
struct objective_item *item = &bots.objective_items[index];
|
||||
real distance;
|
||||
|
||||
if (item->carrier_player_index == player_index)
|
||||
{
|
||||
bot->carrying = TRUE;
|
||||
return;
|
||||
}
|
||||
/* (one a teammate carries: not to be chased) */
|
||||
if (item->carrier_player_index != NONE && game_engine_has_teams())
|
||||
{
|
||||
struct player_datum *carrier = player_try_and_get(item->carrier_player_index);
|
||||
|
||||
if (carrier && carrier->team_index == player->team_index)
|
||||
continue;
|
||||
}
|
||||
distance = distance3d(position, &item->position);
|
||||
if (distance < nearest_distance)
|
||||
{
|
||||
nearest_distance = distance;
|
||||
nearest = item;
|
||||
}
|
||||
}
|
||||
if (nearest)
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = nearest->position;
|
||||
if (nearest->carrier_player_index != NONE)
|
||||
{
|
||||
struct player_datum *carrier = player_try_and_get(nearest->carrier_player_index);
|
||||
|
||||
bot->objective_unit_index = carrier ? carrier->unit_index : NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* (lying there: taken as a weapon is) */
|
||||
bot->item_index = nearest->item_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* the throttle that walks a unit facing yaw towards a point */
|
||||
static void bot_walk_to(
|
||||
struct player_action *action,
|
||||
@@ -582,6 +979,7 @@ static void bot_think(
|
||||
real moved;
|
||||
|
||||
csmemset(action, 0, sizeof(*action));
|
||||
bot->walking = FALSE;
|
||||
action->desired_weapon_index = NONE;
|
||||
action->desired_grenade_index = NONE;
|
||||
action->desired_zoom_level = NONE;
|
||||
@@ -617,10 +1015,23 @@ static void bot_think(
|
||||
bot->wander_yaw = bot->yaw;
|
||||
}
|
||||
|
||||
/* the enemy: noticed after a moment, kept while in sight */
|
||||
/* what the objective game asks */
|
||||
bot_play_objective(bot, player, bot->player_index, &position);
|
||||
|
||||
/* the enemy: noticed after a moment, kept while in sight (the one the
|
||||
objective hunts first, when it is in sight) */
|
||||
{
|
||||
long target = bot_find_target(player, &eye);
|
||||
|
||||
if (bot->objective_unit_index != NONE)
|
||||
{
|
||||
real_point3d head;
|
||||
|
||||
unit_get_camera_position(bot->objective_unit_index, &head);
|
||||
if (distance3d(&eye, &head) < bots.sight_range && bot_can_see(player->unit_index, &eye, &head))
|
||||
target = bot->objective_unit_index;
|
||||
}
|
||||
|
||||
if (target != bot->target_unit_index)
|
||||
{
|
||||
bot->target_unit_index = target;
|
||||
@@ -684,9 +1095,52 @@ static void bot_think(
|
||||
bots.switches++;
|
||||
}
|
||||
}
|
||||
/* closes on it along its way, weaving, until near */
|
||||
bot->weave_phase++;
|
||||
if (distance > BOT_CLOSE_RANGE)
|
||||
/* (a good weapon or powerup close by: taken in the fight too) */
|
||||
if (!bot->carrying && (game_time_get() - bot->item_look_time >= ITEM_LOOK_TICKS / 2 ||
|
||||
game_time_get() < bot->item_look_time))
|
||||
{
|
||||
bot->item_look_time = game_time_get();
|
||||
bot->item_index = bot_find_item(unit, &position, 10.0f, 4.0f);
|
||||
}
|
||||
if (bot->item_index != NONE && !bot->carrying && object_try_and_get(bot->item_index) &&
|
||||
bot_item_worth(unit, bot->item_index) > 0.0f)
|
||||
{
|
||||
real_point3d item_position;
|
||||
real_point3d point;
|
||||
|
||||
object_get_origin(bot->item_index, &item_position);
|
||||
bot->goal = item_position;
|
||||
bot->has_goal = TRUE;
|
||||
if (bot_next_point(bot, player->unit_index, &position, &point))
|
||||
bot_walk_to(action, bot->yaw, &position, &point);
|
||||
if (distance3d(&position, &item_position) < 2.0f)
|
||||
bot_take_item(bot, player, unit, action);
|
||||
}
|
||||
/* closes on it along its way, weaving, until near */
|
||||
else if (bot->has_objective && bot->objective_unit_index != bot->target_unit_index)
|
||||
{
|
||||
/* (the objective first: it fights on the way) */
|
||||
real_point3d point;
|
||||
|
||||
bot->goal = bot->objective;
|
||||
bot->has_goal = TRUE;
|
||||
if (bot_next_point(bot, player->unit_index, &position, &point))
|
||||
{
|
||||
bot_walk_to(action, bot->yaw, &position, &point);
|
||||
/* (carrying it: looking where it runs, unless struck at) */
|
||||
if (bot->carrying && distance > 1.5f)
|
||||
{
|
||||
real dx = point.x - position.x;
|
||||
real dy = point.y - position.y;
|
||||
|
||||
if (dx * dx + dy * dy > 0.0001f)
|
||||
wanted_yaw = (real)atan2(dy, dx);
|
||||
wanted_pitch = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (distance > BOT_CLOSE_RANGE)
|
||||
{
|
||||
real_point3d point;
|
||||
|
||||
@@ -696,14 +1150,14 @@ static void bot_think(
|
||||
bot_walk_to(action, bot->yaw, &position, &point);
|
||||
}
|
||||
action->throttle.j += (real)sin(bot->weave_phase * 0.08f) * 0.6f;
|
||||
firing = distance < BOT_FIRE_RANGE &&
|
||||
firing = !bot->carrying && distance < BOT_FIRE_RANGE &&
|
||||
fabsf(bot_angle_difference(wanted_yaw, bot->yaw)) < BOT_FIRE_CONE &&
|
||||
fabsf(wanted_pitch - bot->pitch) < BOT_FIRE_CONE;
|
||||
/* (a melee blow when it is upon it) */
|
||||
if (distance < 1.0f)
|
||||
SET_FLAG(action->control_flags, _unit_control_use_equipment_bit, TRUE);
|
||||
/* (a grenade now and then, lobbed at one some way off) */
|
||||
if (distance > BOT_GRENADE_MINIMUM && distance < BOT_GRENADE_MAXIMUM &&
|
||||
if (!bot->carrying && distance > BOT_GRENADE_MINIMUM && distance < BOT_GRENADE_MAXIMUM &&
|
||||
game_time_get() - bot->grenade_time > GRENADE_INTERVAL_TICKS && bot_random() < 0.02f)
|
||||
{
|
||||
short type;
|
||||
@@ -729,10 +1183,22 @@ static void bot_think(
|
||||
{
|
||||
real_point3d point;
|
||||
|
||||
/* something worth having near: there first */
|
||||
if (game_time_get() - bot->item_look_time >= ITEM_LOOK_TICKS || game_time_get() < bot->item_look_time)
|
||||
/* the objective: there (and the ball, taken there) */
|
||||
if (bot->has_objective)
|
||||
{
|
||||
long item = bot_find_item(unit, &position);
|
||||
bot->goal = bot->objective;
|
||||
bot->has_goal = TRUE;
|
||||
bot->goal_time = game_time_get();
|
||||
if (bots_game_type() == game_engine_oddball && bot->item_index != NONE &&
|
||||
distance3d(&position, &bot->goal) < 2.0f)
|
||||
{
|
||||
bot_take_item(bot, player, unit, action);
|
||||
}
|
||||
}
|
||||
/* something worth having near: there first */
|
||||
else if (game_time_get() - bot->item_look_time >= ITEM_LOOK_TICKS || game_time_get() < bot->item_look_time)
|
||||
{
|
||||
long item = bot_find_item(unit, &position, BOT_ITEM_RANGE, 0.0f);
|
||||
|
||||
bot->item_look_time = game_time_get();
|
||||
if (item != bot->item_index)
|
||||
@@ -747,7 +1213,7 @@ static void bot_think(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bot->item_index != NONE)
|
||||
if (bot->item_index != NONE && !bot->has_objective)
|
||||
{
|
||||
/* (taken or gone: on) */
|
||||
if (!object_try_and_get(bot->item_index) || bot_item_worth(unit, bot->item_index) <= 0.0f)
|
||||
@@ -764,7 +1230,7 @@ static void bot_think(
|
||||
}
|
||||
}
|
||||
/* hunts, or roams: along its way, looking where it goes */
|
||||
if (bot->item_index == NONE &&
|
||||
if (bot->item_index == NONE && !bot->has_objective &&
|
||||
(!bot->has_goal || game_time_get() - bot->goal_time > 90 ||
|
||||
distance3d(&position, &bot->goal) < BOT_GOAL_RADIUS))
|
||||
{
|
||||
@@ -780,6 +1246,18 @@ static void bot_think(
|
||||
bot->wander_yaw = wanted_yaw = (real)atan2(dy, dx);
|
||||
bot_walk_to(action, bot->yaw, &position, &point);
|
||||
}
|
||||
if (bot->carrying && !bot->has_objective &&
|
||||
(game_time_get() - bot->goal_time > 60 || distance3d(&position, &bot->goal) < BOT_GOAL_RADIUS))
|
||||
{
|
||||
/* (the ball: away, somewhere) */
|
||||
real angle = bot_random() * 2.0f * _pi;
|
||||
|
||||
bot->goal = position;
|
||||
bot->goal.x += (real)cos(angle) * 8.0f;
|
||||
bot->goal.y += (real)sin(angle) * 8.0f;
|
||||
bot->has_goal = TRUE;
|
||||
bot->goal_time = game_time_get();
|
||||
}
|
||||
if (bot->stuck_ticks > 20)
|
||||
{
|
||||
/* (stuck: somewhere else) */
|
||||
@@ -810,16 +1288,54 @@ static void bot_think(
|
||||
SET_FLAG(action->control_flags, _unit_control_weapon_primary_trigger_bit, TRUE);
|
||||
}
|
||||
|
||||
/* stuck: hardly moved while walking (it jumps, and then turns) */
|
||||
/* stuck: making no way to the point it walks to (against a wall, in
|
||||
a corner): soon a step aside and its way found anew, then a jump, and
|
||||
in the end somewhere else */
|
||||
if (bot->sidestep_ticks > 0)
|
||||
{
|
||||
bot->sidestep_ticks--;
|
||||
action->throttle.j = bot->sidestep;
|
||||
action->throttle.i *= 0.3f;
|
||||
}
|
||||
moved = distance3d(&position, &bot->last_position);
|
||||
bot->last_position = position;
|
||||
action->throttle.i = PIN(action->throttle.i, -1.0f, 1.0f);
|
||||
action->throttle.j = PIN(action->throttle.j, -1.0f, 1.0f);
|
||||
if ((fabsf(action->throttle.i) > 0.5f || fabsf(action->throttle.j) > 0.5f) && moved < 0.02f)
|
||||
if (bot->walking && (fabsf(action->throttle.i) > 0.5f || fabsf(action->throttle.j) > 0.5f))
|
||||
{
|
||||
bot->stuck_ticks++;
|
||||
if (bot->stuck_ticks == 8)
|
||||
SET_FLAG(action->control_flags, _unit_control_jump_bit, TRUE);
|
||||
real distance = (real)sqrt((bot->walk_point.x - position.x) * (bot->walk_point.x - position.x) +
|
||||
(bot->walk_point.y - position.y) * (bot->walk_point.y - position.y));
|
||||
boolean progress = distance < bot->walk_distance - 0.015f || moved > 0.06f;
|
||||
|
||||
bot->walk_distance = MIN(bot->walk_distance, distance);
|
||||
if (progress && moved > 0.02f)
|
||||
{
|
||||
bot->stuck_ticks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bot->stuck_ticks++;
|
||||
if (bot->stuck_ticks == 6)
|
||||
{
|
||||
bot->sidestep = bot_random() < 0.5f ? 1.0f : -1.0f;
|
||||
bot->sidestep_ticks = 10;
|
||||
/* (near a point of its path it cannot reach, at a corner: the
|
||||
next one) */
|
||||
if (distance < 1.5f && bot->path.valid && bot->path.step_index < bot->path.step_count)
|
||||
{
|
||||
bot->path.step_index++;
|
||||
bot->sidestep_ticks = 4;
|
||||
}
|
||||
bots.stucks++;
|
||||
|
||||
bot->path_time = game_time_get() - PATH_REFRESH_TICKS;
|
||||
bot->walk_distance = 1000000.0f;
|
||||
}
|
||||
if (bot->stuck_ticks == 12)
|
||||
SET_FLAG(action->control_flags, _unit_control_jump_bit, TRUE);
|
||||
if (bot->stuck_ticks == 24)
|
||||
bots.long_stucks++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -891,6 +1407,7 @@ void bots_tick(
|
||||
bsp->pathfinding_surfaces.count, bsp->pathfinding_edges.count);
|
||||
}
|
||||
}
|
||||
bots_update_objectives();
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *player = distributed_player(index);
|
||||
@@ -915,9 +1432,10 @@ void bots_tick(
|
||||
if (count && game_time_get() - bots.logged_time >= 300)
|
||||
{
|
||||
bots.logged_time = game_time_get();
|
||||
platform_log("bots: driving %d; paths found %ld, not %ld; weapon swaps asked %ld; grenades %ld; changes %ld",
|
||||
(int)count, bots.paths_found, bots.paths_failed, bots.pickups, bots.grenades, bots.switches);
|
||||
bots.paths_found = bots.paths_failed = bots.pickups = bots.grenades = bots.switches = 0;
|
||||
platform_log("bots: driving %d; paths found %ld, not %ld; weapon swaps asked %ld; grenades %ld; changes %ld; stuck %ld, long %ld (no start %ld, no goal %ld, no way %ld)",
|
||||
(int)count, bots.paths_found, bots.paths_failed, bots.pickups, bots.grenades, bots.switches, bots.stucks, bots.long_stucks, bots.fail_start, bots.fail_goal, bots.fail_search);
|
||||
bots.fail_start = bots.fail_goal = bots.fail_search = 0;
|
||||
bots.paths_found = bots.paths_failed = bots.pickups = bots.grenades = bots.switches = bots.stucks = bots.long_stucks = 0;
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *player = distributed_player(index);
|
||||
@@ -942,8 +1460,22 @@ void bots_tick(
|
||||
strncat(text, slot == unit->unit.current_weapon_index ? "* " : " ", sizeof(text) - strlen(text) - 1);
|
||||
}
|
||||
}
|
||||
platform_log("bots: player %d carries %s, grenades %d/%d", index, text, unit->unit.grenade_counts[0],
|
||||
unit->unit.grenade_counts[1]);
|
||||
{
|
||||
struct bot *bot = bot_get(DATUM_INDEX_NEW(index, player->identifier));
|
||||
|
||||
platform_log("bots: player %d (team %ld) carries %s, grenades %d/%d%s%s", index, player->team_index, text,
|
||||
unit->unit.grenade_counts[0], unit->unit.grenade_counts[1],
|
||||
bot && bot->has_objective ? ", to the objective" : "", bot && bot->carrying ? ", CARRYING" : "");
|
||||
if (bot && bot->carrying && bot->has_objective)
|
||||
{
|
||||
real_point3d origin;
|
||||
|
||||
object_get_origin(player->unit_index, &origin);
|
||||
platform_log("bots: carrier at (%.1f %.1f %.1f), goal (%.1f %.1f %.1f), %.2f off; path %s",
|
||||
origin.x, origin.y, origin.z, bot->objective.x, bot->objective.y, bot->objective.z,
|
||||
distance3d(&origin, &bot->objective), bot->path.valid ? "found" : "none");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user