mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 13:00:25 +02:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
770f8881e0 | ||
|
|
38ab192749 | ||
|
|
6299503191 | ||
|
|
c5d86c8d8b | ||
|
|
1c8e651bb1 |
No files matched your search
+333
-22
@@ -32,6 +32,12 @@ In the objective games a bot plays for the objective, fighting on the way:
|
||||
- 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.
|
||||
|
||||
In a game of teams a bot plays with its team: it holds its fire while a
|
||||
teammate is in the line of it, takes on first the enemies near its
|
||||
teammates (people above all), escorts a teammate who carries the enemy's
|
||||
flag or the ball, and, with nothing to do, every other bot keeps near the
|
||||
nearest person on its team.
|
||||
*/
|
||||
|
||||
#include "cseries.h"
|
||||
@@ -111,6 +117,12 @@ struct bot
|
||||
real sidestep;
|
||||
/* whether it walks to a point this tick (not only weaving in a fight) */
|
||||
boolean walking;
|
||||
/* the paths to its goal not found in a row, and goals given up for a
|
||||
while on that account: following a person, a thing to pick up */
|
||||
short path_failures;
|
||||
long follow_again_time;
|
||||
long ignored_item_index;
|
||||
long ignored_item_time;
|
||||
real wander_yaw;
|
||||
short weave_phase;
|
||||
/* where it is going, and the way there */
|
||||
@@ -127,6 +139,8 @@ struct bot
|
||||
/* 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;
|
||||
/* the objective is a place to hold (the hill, its own flag's stand) */
|
||||
boolean holding;
|
||||
real_point3d objective;
|
||||
long objective_unit_index;
|
||||
boolean carrying;
|
||||
@@ -232,6 +246,124 @@ static boolean bot_can_see(
|
||||
from, &vector, unit_index, &collision);
|
||||
}
|
||||
|
||||
/* ---------- the team */
|
||||
|
||||
/* whether a player is a person (on a machine of its own, not a bot's) */
|
||||
static boolean bot_player_is_person(
|
||||
struct player_datum const *player)
|
||||
{
|
||||
return !network_game_server_is_bot_machine(player->network_player_data.machine_index);
|
||||
}
|
||||
|
||||
/* how much nearer an enemy at this point counts for being near the bot's
|
||||
teammates: up to 10 world units for one by a person, 6 by a bot */
|
||||
static real bot_threat_to_team(
|
||||
struct player_datum const *player,
|
||||
real_point3d const *enemy)
|
||||
{
|
||||
real threat = 0.0f;
|
||||
short index;
|
||||
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *mate = distributed_player(index);
|
||||
long unit_index;
|
||||
real_point3d head;
|
||||
real distance;
|
||||
real most;
|
||||
|
||||
if (!mate || mate == player || mate->team_index != player->team_index)
|
||||
continue;
|
||||
unit_index = distributed_living_unit(mate);
|
||||
if (unit_index == NONE)
|
||||
continue;
|
||||
unit_get_camera_position(unit_index, &head);
|
||||
distance = distance3d(enemy, &head);
|
||||
most = bot_player_is_person(mate) ? 10.0f : 6.0f;
|
||||
if (distance < 12.0f)
|
||||
threat = MAX(threat, most * (1.0f - distance / 12.0f));
|
||||
}
|
||||
return threat;
|
||||
}
|
||||
|
||||
/* whether a teammate stands in the line of fire to a point (nearer than
|
||||
it, within 0.8 world units of the line) */
|
||||
static boolean bot_teammate_in_line(
|
||||
struct player_datum const *player,
|
||||
real_point3d const *eye,
|
||||
real_point3d const *target)
|
||||
{
|
||||
real_vector3d line;
|
||||
real length;
|
||||
short index;
|
||||
|
||||
if (!game_engine_has_teams())
|
||||
return FALSE;
|
||||
vector_from_points3d(eye, target, &line);
|
||||
length = magnitude3d(&line);
|
||||
if (length < 0.01f)
|
||||
return FALSE;
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *mate = distributed_player(index);
|
||||
long unit_index;
|
||||
real_point3d center;
|
||||
real_vector3d offset;
|
||||
real along;
|
||||
real_point3d nearest;
|
||||
|
||||
if (!mate || mate == player || mate->team_index != player->team_index)
|
||||
continue;
|
||||
unit_index = distributed_living_unit(mate);
|
||||
if (unit_index == NONE)
|
||||
continue;
|
||||
unit_get_camera_position(unit_index, ¢er);
|
||||
center.z -= 0.25f;
|
||||
vector_from_points3d(eye, ¢er, &offset);
|
||||
along = dot_product3d(&offset, &line) / length;
|
||||
if (along <= 0.0f || along >= length)
|
||||
continue;
|
||||
nearest.x = eye->x + line.i * along / length;
|
||||
nearest.y = eye->y + line.j * along / length;
|
||||
nearest.z = eye->z + line.k * along / length;
|
||||
if (distance3d(&nearest, ¢er) < 0.8f)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* the nearest person on the bot's team, alive (NONE: none) */
|
||||
static long bot_nearest_person(
|
||||
struct player_datum const *player,
|
||||
real_point3d const *position)
|
||||
{
|
||||
long best = NONE;
|
||||
real best_distance = 40.0f;
|
||||
short index;
|
||||
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *mate = distributed_player(index);
|
||||
long unit_index;
|
||||
real_point3d origin;
|
||||
real distance;
|
||||
|
||||
if (!mate || mate == player || mate->team_index != player->team_index || !bot_player_is_person(mate))
|
||||
continue;
|
||||
unit_index = distributed_living_unit(mate);
|
||||
if (unit_index == NONE)
|
||||
continue;
|
||||
object_get_origin(unit_index, &origin);
|
||||
distance = distance3d(position, &origin);
|
||||
if (distance < best_distance)
|
||||
{
|
||||
best_distance = distance;
|
||||
best = unit_index;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/* the nearest enemy unit the bot can see, or NONE */
|
||||
static long bot_find_target(
|
||||
struct player_datum const *player,
|
||||
@@ -256,6 +388,11 @@ static long bot_find_target(
|
||||
continue;
|
||||
unit_get_camera_position(unit_index, &head);
|
||||
distance = distance3d(eye, &head);
|
||||
if (distance >= bots.sight_range)
|
||||
continue;
|
||||
/* (one near a teammate counts as nearer: helped first) */
|
||||
if (teams)
|
||||
distance -= bot_threat_to_team(player, &head);
|
||||
if (distance < best_distance && bot_can_see(player->unit_index, eye, &head))
|
||||
{
|
||||
best = unit_index;
|
||||
@@ -299,6 +436,7 @@ static boolean bot_find_path(
|
||||
if (goal_surface == NONE)
|
||||
{
|
||||
bots.fail_goal++;
|
||||
bot->path_failures++;
|
||||
return FALSE;
|
||||
}
|
||||
/* (a little wider than a biped: paths that keep off walls and corners) */
|
||||
@@ -333,9 +471,11 @@ static boolean bot_find_path(
|
||||
bot->path.valid = FALSE;
|
||||
bots.paths_failed++;
|
||||
bots.fail_search++;
|
||||
bot->path_failures++;
|
||||
return FALSE;
|
||||
}
|
||||
bots.paths_found += bot->path.valid != FALSE;
|
||||
bot->path_failures = 0;
|
||||
return bot->path.valid;
|
||||
}
|
||||
|
||||
@@ -776,6 +916,88 @@ static boolean bots_ctf_base(
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* near a teammate who carries something, a few world units off on the bot's
|
||||
side of it (an escort, not on the carrier's heels) */
|
||||
static void bot_escort_point(
|
||||
real_point3d const *position,
|
||||
real_point3d const *carried,
|
||||
real_point3d *point)
|
||||
{
|
||||
real dx = position->x - carried->x;
|
||||
real dy = position->y - carried->y;
|
||||
real length = (real)sqrt(dx * dx + dy * dy);
|
||||
|
||||
*point = *carried;
|
||||
if (length > 0.01f)
|
||||
{
|
||||
point->x += dx / length * 2.5f;
|
||||
point->y += dy / length * 2.5f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bots with one place to go spread out: each has a direction of its own
|
||||
(the golden angle on from the last bot's), a place of its own about a
|
||||
point to hold, and comes to a point from far off by a way of its own,
|
||||
wide of the others', going straight to it only for the last stretch. */
|
||||
|
||||
static real bot_spread_angle(
|
||||
long player_index)
|
||||
{
|
||||
return (real)DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index) * 2.39996f;
|
||||
}
|
||||
|
||||
/* the bot's place about a point (radius: world units) */
|
||||
static void bot_spot_around(
|
||||
real_point3d const *center,
|
||||
long player_index,
|
||||
real radius,
|
||||
real_point3d *spot)
|
||||
{
|
||||
real angle = bot_spread_angle(player_index);
|
||||
|
||||
*spot = *center;
|
||||
spot->x += (real)cos(angle) * radius;
|
||||
spot->y += (real)sin(angle) * radius;
|
||||
}
|
||||
|
||||
/* its place about a point, or the point itself when there is no way to the
|
||||
place (in a wall, past a ledge) */
|
||||
static void bot_hold_spot(
|
||||
struct bot const *bot,
|
||||
real_point3d const *center,
|
||||
real radius,
|
||||
real_point3d *spot)
|
||||
{
|
||||
if (bot->path_failures >= 3)
|
||||
*spot = *center;
|
||||
else
|
||||
bot_spot_around(center, bot->player_index, radius, spot);
|
||||
}
|
||||
|
||||
/* where it heads for a point from where it is: from far off, out to a side
|
||||
of the straight way (its own side and width), in from 10 world units */
|
||||
static void bot_approach(
|
||||
struct bot const *bot,
|
||||
real_point3d const *position,
|
||||
real_point3d const *goal,
|
||||
real_point3d *point)
|
||||
{
|
||||
long index = DATUM_INDEX_TO_ABSOLUTE_INDEX(bot->player_index);
|
||||
real dx = goal->x - position->x;
|
||||
real dy = goal->y - position->y;
|
||||
real distance = (real)sqrt(dx * dx + dy * dy);
|
||||
real side = (index & 1 ? 1.0f : -1.0f) * (3.0f + (real)((index >> 1) % 3) * 1.5f);
|
||||
real share;
|
||||
|
||||
*point = *goal;
|
||||
/* (no way to the side: straight) */
|
||||
if (distance < 10.0f || bot->path_failures >= 2)
|
||||
return;
|
||||
share = MIN((distance - 10.0f) / 10.0f, 1.0f);
|
||||
point->x += -dy / distance * side * share;
|
||||
point->y += dx / distance * side * share;
|
||||
}
|
||||
|
||||
/* what the objective asks of the bot now (bot->has_objective and the rest) */
|
||||
static void bot_play_objective_now(
|
||||
struct bot *bot,
|
||||
@@ -809,6 +1031,7 @@ static void bot_play_objective_now(
|
||||
short index;
|
||||
|
||||
bot->has_objective = FALSE;
|
||||
bot->holding = FALSE;
|
||||
bot->objective_unit_index = NONE;
|
||||
bot->carrying = FALSE;
|
||||
if (type == game_engine_king)
|
||||
@@ -823,13 +1046,12 @@ static void bot_play_objective_now(
|
||||
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;
|
||||
}
|
||||
bot->holding = TRUE;
|
||||
/* (near: a place of its own on the hill; far: a way of its own) */
|
||||
if (dx * dx + dy * dy < 6.0f * 6.0f)
|
||||
bot_hold_spot(bot, ¢er, 1.2f, &bot->objective);
|
||||
else
|
||||
bot_approach(bot, position, ¢er, &bot->objective);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -876,23 +1098,42 @@ static void bot_play_objective_now(
|
||||
bot->objective = own->position;
|
||||
return;
|
||||
}
|
||||
/* (every third guards its own) */
|
||||
/* (every third guards its own: about the stand, or, every other one
|
||||
of them, ahead of it towards the enemy's) */
|
||||
if ((DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index) % 3) == 2 && bots_ctf_base(player->team_index, &base))
|
||||
{
|
||||
real_point3d enemy_base;
|
||||
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = base;
|
||||
if (distance3d(position, &base) < 4.0f)
|
||||
bot->holding = TRUE;
|
||||
if (((DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index) / 3) & 1) &&
|
||||
bots_ctf_base((player->team_index + 1) % 2, &enemy_base))
|
||||
{
|
||||
bot->objective.x += (real)cos(bot->weave_phase * 0.03f) * 3.0f;
|
||||
bot->objective.y += (real)sin(bot->weave_phase * 0.03f) * 3.0f;
|
||||
real dx = enemy_base.x - base.x;
|
||||
real dy = enemy_base.y - base.y;
|
||||
real length = (real)sqrt(dx * dx + dy * dy);
|
||||
|
||||
bot->objective = base;
|
||||
if (length > 0.01f)
|
||||
{
|
||||
bot->objective.x += dx / length * 7.0f;
|
||||
bot->objective.y += dy / length * 7.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bot_hold_spot(bot, &base, 3.0f, &bot->objective);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* theirs: to take it, or to cover its carrier */
|
||||
/* theirs: to take it, or to escort the teammate carrying it */
|
||||
if (enemy)
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = enemy->position;
|
||||
if (enemy->carrier_player_index != NONE)
|
||||
bot_hold_spot(bot, &enemy->position, 3.0f, &bot->objective);
|
||||
else
|
||||
bot_approach(bot, position, &enemy->position, &bot->objective);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -911,13 +1152,20 @@ static void bot_play_objective_now(
|
||||
bot->carrying = TRUE;
|
||||
return;
|
||||
}
|
||||
/* (one a teammate carries: not to be chased) */
|
||||
/* (one a teammate carries: escorted, not 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)
|
||||
{
|
||||
if (!nearest)
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot_hold_spot(bot, &item->position, 3.0f, &bot->objective);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
distance = distance3d(position, &item->position);
|
||||
if (distance < nearest_distance)
|
||||
@@ -929,7 +1177,7 @@ static void bot_play_objective_now(
|
||||
if (nearest)
|
||||
{
|
||||
bot->has_objective = TRUE;
|
||||
bot->objective = nearest->position;
|
||||
bot_approach(bot, position, &nearest->position, &bot->objective);
|
||||
if (nearest->carrier_player_index != NONE)
|
||||
{
|
||||
struct player_datum *carrier = player_try_and_get(nearest->carrier_player_index);
|
||||
@@ -976,6 +1224,7 @@ static void bot_think(
|
||||
real wanted_yaw;
|
||||
real wanted_pitch = 0.0f;
|
||||
boolean firing = FALSE;
|
||||
boolean following = FALSE;
|
||||
real moved;
|
||||
|
||||
csmemset(action, 0, sizeof(*action));
|
||||
@@ -1097,13 +1346,13 @@ static void bot_think(
|
||||
}
|
||||
bot->weave_phase++;
|
||||
/* (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 ||
|
||||
if (!bot->carrying && !bot->has_objective && (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) &&
|
||||
if (bot->item_index != NONE && !bot->carrying && !bot->has_objective && object_try_and_get(bot->item_index) &&
|
||||
bot_item_worth(unit, bot->item_index) > 0.0f)
|
||||
{
|
||||
real_point3d item_position;
|
||||
@@ -1153,6 +1402,13 @@ static void bot_think(
|
||||
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 teammate in the line of fire: held, and a step aside for a
|
||||
clear shot) */
|
||||
if (firing && bot_teammate_in_line(player, &eye, &head))
|
||||
{
|
||||
firing = FALSE;
|
||||
action->throttle.j = (bot->weave_phase / 30) & 1 ? 1.0f : -1.0f;
|
||||
}
|
||||
/* (a melee blow when it is upon it) */
|
||||
if (distance < 1.0f)
|
||||
SET_FLAG(action->control_flags, _unit_control_use_equipment_bit, TRUE);
|
||||
@@ -1201,6 +1457,8 @@ static void bot_think(
|
||||
long item = bot_find_item(unit, &position, BOT_ITEM_RANGE, 0.0f);
|
||||
|
||||
bot->item_look_time = game_time_get();
|
||||
if (item == bot->ignored_item_index && game_time_get() - bot->ignored_item_time < 600)
|
||||
item = NONE;
|
||||
if (item != bot->item_index)
|
||||
{
|
||||
bot->item_index = item;
|
||||
@@ -1215,8 +1473,16 @@ static void bot_think(
|
||||
}
|
||||
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)
|
||||
/* (taken or gone, or no way to it: on) */
|
||||
if (bot->path_failures >= 3)
|
||||
{
|
||||
bot->ignored_item_index = bot->item_index;
|
||||
bot->ignored_item_time = game_time_get();
|
||||
bot->path_failures = 0;
|
||||
bot->item_index = NONE;
|
||||
bot->has_goal = FALSE;
|
||||
}
|
||||
else if (!object_try_and_get(bot->item_index) || bot_item_worth(unit, bot->item_index) <= 0.0f)
|
||||
{
|
||||
bot->item_index = NONE;
|
||||
bot->has_goal = FALSE;
|
||||
@@ -1229,12 +1495,56 @@ static void bot_think(
|
||||
bot_take_item(bot, player, unit, action);
|
||||
}
|
||||
}
|
||||
/* (in a team, every other bot with nothing to do keeps near the
|
||||
nearest person on its team) */
|
||||
following = FALSE;
|
||||
if (bot->item_index == NONE && !bot->has_objective && game_engine_has_teams() &&
|
||||
(DATUM_INDEX_TO_ABSOLUTE_INDEX(bot->player_index) & 1) == 0 &&
|
||||
game_time_get() >= bot->follow_again_time)
|
||||
{
|
||||
long person = bot_nearest_person(player, &position);
|
||||
|
||||
if (person != NONE)
|
||||
{
|
||||
real_point3d origin;
|
||||
|
||||
following = TRUE;
|
||||
object_get_origin(person, &origin);
|
||||
/* (near: where it is, not on top of them) */
|
||||
if (distance3d(&position, &origin) > 6.0f)
|
||||
bot_escort_point(&position, &origin, &bot->goal);
|
||||
else
|
||||
bot->goal = position;
|
||||
bot->has_goal = TRUE;
|
||||
bot->goal_time = game_time_get();
|
||||
/* (no way to them, as up where only a teleporter goes: not for
|
||||
ten seconds) */
|
||||
if (bot->path_failures >= 3)
|
||||
{
|
||||
bot->follow_again_time = game_time_get() + 300;
|
||||
bot->path_failures = 0;
|
||||
following = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* hunts, or roams: along its way, looking where it goes */
|
||||
if (bot->item_index == NONE && !bot->has_objective &&
|
||||
(!bot->has_goal || game_time_get() - bot->goal_time > 90 ||
|
||||
if (bot->item_index == NONE && !bot->has_objective && !following &&
|
||||
(!bot->has_goal || game_time_get() - bot->goal_time > 90 || bot->path_failures >= 3 ||
|
||||
distance3d(&position, &bot->goal) < BOT_GOAL_RADIUS))
|
||||
{
|
||||
boolean unreachable = bot->path_failures >= 3;
|
||||
|
||||
bot_choose_goal(bot, player, &position);
|
||||
/* (no way to the last: somewhere near at random instead) */
|
||||
if (unreachable)
|
||||
{
|
||||
real angle = bot_random() * 2.0f * _pi;
|
||||
|
||||
bot->goal = position;
|
||||
bot->goal.x += (real)cos(angle) * 6.0f;
|
||||
bot->goal.y += (real)sin(angle) * 6.0f;
|
||||
}
|
||||
bot->path_failures = 0;
|
||||
}
|
||||
wanted_yaw = bot->wander_yaw;
|
||||
if (bot_next_point(bot, player->unit_index, &position, &point))
|
||||
@@ -1363,6 +1673,7 @@ static struct bot *bot_get(
|
||||
free_bot->player_index = player_index;
|
||||
free_bot->target_unit_index = NONE;
|
||||
free_bot->item_index = NONE;
|
||||
free_bot->ignored_item_index = NONE;
|
||||
}
|
||||
return free_bot;
|
||||
}
|
||||
|
||||
@@ -190,6 +190,11 @@ enum
|
||||
};
|
||||
void halo_vr_set_seat(short local_player_index, int seat, int armed, float vehicle_yaw);
|
||||
void halo_vr_set_seat_frame(short local_player_index, float vehicle_yaw);
|
||||
/* At an open vehicle's wheel (a Warthog's), each frame (render.c): the
|
||||
driver's hands on it as drawn (world units, from the game's camera), or
|
||||
NULL; the player's own hands turn it. And whether they hold it. */
|
||||
void halo_vr_set_wheel(short local_player_index, const float left[3], const float right[3]);
|
||||
int halo_vr_wheel_held(void);
|
||||
/* whether a seated player's view stays in first person (director.c) */
|
||||
int halo_vr_vehicle_first_person(void);
|
||||
/* VR test aid (debug.vehicle_spawn): both sticks held in for a moment asks
|
||||
|
||||
@@ -221,6 +221,13 @@ static const struct config_setting config_settings[] =
|
||||
{ "vr.vehicle_pitch_degrees", _config_real, "45", "HALO_VR_VEHICLE_PITCH", _environment_value, _platform_android,
|
||||
"Driving, the right stick pushed fully up or down aims this many degrees\n"
|
||||
"above or below the horizon (a Banshee climbs or dives)." },
|
||||
{ "vr.vehicle_wheel", _config_boolean, "true", "HALO_VR_VEHICLE_WHEEL", _environment_value, _platform_android,
|
||||
"At an open vehicle's wheel (a Warthog's), grip it with one hand or both and\n"
|
||||
"turn it to steer, the right trigger the throttle and the left the brake;\n"
|
||||
"let go, and the right stick steers." },
|
||||
{ "vr.vehicle_wheel_degrees", _config_real, "90", "HALO_VR_VEHICLE_WHEEL_DEGREES", _environment_value,
|
||||
_platform_android,
|
||||
"The wheel turned this many degrees either way steers fully." },
|
||||
{ "vr.comfort_vignette", _config_real, "0.7", "HALO_VR_COMFORT_VIGNETTE", _environment_value, _platform_android,
|
||||
"How far the view darkens from its edges while a vehicle turns you\n"
|
||||
"(0 never, 1 to a small window)." },
|
||||
|
||||
+158
-1
@@ -107,6 +107,16 @@ static struct
|
||||
float vehicle_steer_angle; /* radians */
|
||||
float vehicle_gun_steer_angle; /* radians: steering that aims guns too */
|
||||
float vehicle_pitch_angle;
|
||||
/* the wheel of an open vehicle (a Warthog's: wheel_update) */
|
||||
int vehicle_wheel; /* vr.vehicle_wheel: the hands turn it */
|
||||
float vehicle_wheel_angle; /* radians: turned this far, it steers fully */
|
||||
int wheel_valid; /* where it is, as last drawn */
|
||||
float wheel_hands_drawn[2][3]; /* the driver's drawn hands on it, from the camera (world units) */
|
||||
int wheel_held[HALO_XR_HAND_COUNT];
|
||||
int wheel_hands; /* the hands that held it last frame (bits) */
|
||||
float wheel_reference; /* their turn about the column last frame */
|
||||
float wheel_turn; /* the wheel's turn (radians, anticlockwise as the driver sees it) */
|
||||
int wheel_logged;
|
||||
float vehicle_steer_curve; /* the stick's deflection to this power */
|
||||
float vehicle_steer_smoothing; /* seconds */
|
||||
float steer[2]; /* the steering as smoothed: aside, up (-1..1) */
|
||||
@@ -455,6 +465,10 @@ void halo_vr_initialize(void)
|
||||
vr.vehicle_steer_angle = (float)config_real("vr.vehicle_steer_degrees") * PI / 180.0f;
|
||||
vr.vehicle_gun_steer_angle = (float)config_real("vr.vehicle_gun_steer_degrees") * PI / 180.0f;
|
||||
vr.vehicle_pitch_angle = (float)config_real("vr.vehicle_pitch_degrees") * PI / 180.0f;
|
||||
vr.vehicle_wheel = config_boolean("vr.vehicle_wheel");
|
||||
vr.vehicle_wheel_angle = (float)config_real("vr.vehicle_wheel_degrees") * PI / 180.0f;
|
||||
if (vr.vehicle_wheel_angle < 10.0f * PI / 180.0f)
|
||||
vr.vehicle_wheel_angle = 10.0f * PI / 180.0f;
|
||||
vr.vehicle_steer_curve = (float)config_real("vr.vehicle_steer_curve");
|
||||
vr.vehicle_steer_smoothing = (float)config_real("vr.vehicle_steer_smoothing");
|
||||
vr.comfort_vignette = (float)config_real("vr.comfort_vignette");
|
||||
@@ -557,7 +571,8 @@ static void grenade_update(void)
|
||||
const struct halo_xr_hand *hand = &vr.frame.hands[HALO_XR_HAND_LEFT];
|
||||
unsigned long long now = now_us();
|
||||
int valid = hand->active && (hand->flags & HALO_XR_POSE_VALID);
|
||||
int held = valid && vr.last_overlay_mode != HALO_XR_OVERLAY_SCREEN &&
|
||||
/* (at a wheel the left trigger brakes: xinput_sdl.c) */
|
||||
int held = valid && vr.last_overlay_mode != HALO_XR_OVERLAY_SCREEN && !vr.wheel_hands &&
|
||||
hand->trigger > (vr.grenade_held ? 0.2f : 0.3f);
|
||||
int count = vr.grenade_sample_count;
|
||||
|
||||
@@ -737,7 +752,9 @@ static void melee_update(void)
|
||||
continue;
|
||||
}
|
||||
/* (a grenade's throw is not a blow) */
|
||||
/* (nor is turning the wheel) */
|
||||
if (vr.hand_last_valid[hand] && seconds > 0.0f && seconds < 0.1f && vr.melee_swing_speed > 0.0f &&
|
||||
!vr.wheel_held[hand] &&
|
||||
vr.last_overlay_mode != HALO_XR_OVERLAY_SCREEN && now >= vr.melee_ready_us &&
|
||||
!(hand == HALO_XR_HAND_LEFT && (vr.grenade_held || now - vr.grenade_released_us < 500000)))
|
||||
{
|
||||
@@ -823,6 +840,138 @@ static void turn_update(void)
|
||||
seat_body();
|
||||
}
|
||||
|
||||
/* At an open vehicle's wheel (a Warthog's, vr.vehicle_wheel), a hand that
|
||||
grips near it takes hold of it, and the hands turn it about its column: both
|
||||
by the line between them, one about the wheel's middle. Where it is, the
|
||||
driver's hands as drawn hold it (halo_vr_set_wheel); its column runs from
|
||||
there back towards the driver, tipped up WHEEL_COLUMN_TILT. Held, its turn
|
||||
steers (halo_vr_aim); let go, the right stick does. */
|
||||
#define WHEEL_REACH 0.35f /* metres from its middle */
|
||||
#define WHEEL_COLUMN_TILT (45.0f * PI / 180.0f) /* from the horizontal */
|
||||
|
||||
static void wheel_release(void)
|
||||
{
|
||||
int hand;
|
||||
|
||||
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
|
||||
vr.wheel_held[hand] = 0;
|
||||
vr.wheel_hands = 0;
|
||||
}
|
||||
|
||||
static void wheel_update(void)
|
||||
{
|
||||
static const float local_up[3] = { 0.0f, 1.0f, 0.0f };
|
||||
float world[3], centre[3], forward_game[3], forward[3], column[3], up[3], side[3], line[3];
|
||||
float heading = seat_heading(), along, angle;
|
||||
int hand, axis, hands = 0;
|
||||
|
||||
if (vr.seat_mode != HALO_VR_SEAT_STEER || !vr.vehicle_wheel || !vr.wheel_valid || !vr.frame.focused ||
|
||||
vr.last_overlay_mode == HALO_XR_OVERLAY_SCREEN)
|
||||
{
|
||||
wheel_release();
|
||||
return;
|
||||
}
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
{
|
||||
world[axis] = vr.game_camera[axis] +
|
||||
(vr.wheel_hands_drawn[0][axis] + vr.wheel_hands_drawn[1][axis]) * 0.5f;
|
||||
}
|
||||
world_to_local(world, centre);
|
||||
if (!vr.wheel_logged)
|
||||
{
|
||||
float apart[3];
|
||||
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
apart[axis] = (vr.wheel_hands_drawn[0][axis] - vr.wheel_hands_drawn[1][axis]) * METRES_PER_WORLD_UNIT;
|
||||
platform_log("VR: wheel at %.2f %.2f %.2f (LOCAL), the hands %.2f m apart", centre[0], centre[1], centre[2],
|
||||
sqrtf(apart[0] * apart[0] + apart[1] * apart[1] + apart[2] * apart[2]));
|
||||
vr.wheel_logged = 1;
|
||||
}
|
||||
/* its column, and the wheel's up and left about it */
|
||||
forward_game[0] = cosf(heading);
|
||||
forward_game[1] = sinf(heading);
|
||||
forward_game[2] = 0.0f;
|
||||
game_to_local(forward_game, forward);
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
column[axis] = -forward[axis] * cosf(WHEEL_COLUMN_TILT) + local_up[axis] * sinf(WHEEL_COLUMN_TILT);
|
||||
along = local_up[0] * column[0] + local_up[1] * column[1] + local_up[2] * column[2];
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
up[axis] = local_up[axis] - column[axis] * along;
|
||||
normalize3(up);
|
||||
cross3(column, up, side);
|
||||
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
|
||||
{
|
||||
const struct halo_xr_hand *state = &vr.frame.hands[hand];
|
||||
float apart[3];
|
||||
|
||||
if (!state->active || !(state->flags & HALO_XR_POSE_VALID) || state->squeeze < 0.35f)
|
||||
{
|
||||
vr.wheel_held[hand] = 0;
|
||||
continue;
|
||||
}
|
||||
if (!vr.wheel_held[hand])
|
||||
{
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
apart[axis] = state->grip.position[axis] - centre[axis];
|
||||
if (state->squeeze < 0.6f ||
|
||||
sqrtf(apart[0] * apart[0] + apart[1] * apart[1] + apart[2] * apart[2]) > WHEEL_REACH)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
vr.wheel_held[hand] = 1;
|
||||
host_xr_haptic(hand, 0.3f, 0.05f);
|
||||
}
|
||||
hands |= 1 << hand;
|
||||
}
|
||||
if (!hands)
|
||||
{
|
||||
vr.wheel_hands = 0;
|
||||
return;
|
||||
}
|
||||
/* the hands' turn about the column */
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
{
|
||||
line[axis] = hands == ((1 << HALO_XR_HAND_LEFT) | (1 << HALO_XR_HAND_RIGHT)) ?
|
||||
vr.frame.hands[HALO_XR_HAND_LEFT].grip.position[axis] -
|
||||
vr.frame.hands[HALO_XR_HAND_RIGHT].grip.position[axis] :
|
||||
vr.frame.hands[hands & (1 << HALO_XR_HAND_LEFT) ? HALO_XR_HAND_LEFT : HALO_XR_HAND_RIGHT]
|
||||
.grip.position[axis] - centre[axis];
|
||||
}
|
||||
angle = atan2f(line[0] * side[0] + line[1] * side[1] + line[2] * side[2],
|
||||
line[0] * up[0] + line[1] * up[1] + line[2] * up[2]);
|
||||
if (!vr.wheel_hands)
|
||||
{
|
||||
/* taken hold of: turned as far as it steers now */
|
||||
vr.wheel_turn = -vr.steer[0] * vr.vehicle_wheel_angle;
|
||||
}
|
||||
else if (hands == vr.wheel_hands)
|
||||
{
|
||||
vr.wheel_turn += wrap_angle(angle - vr.wheel_reference);
|
||||
}
|
||||
if (vr.wheel_turn > vr.vehicle_wheel_angle)
|
||||
vr.wheel_turn = vr.vehicle_wheel_angle;
|
||||
if (vr.wheel_turn < -vr.vehicle_wheel_angle)
|
||||
vr.wheel_turn = -vr.vehicle_wheel_angle;
|
||||
vr.wheel_reference = angle;
|
||||
vr.wheel_hands = hands;
|
||||
}
|
||||
|
||||
void halo_vr_set_wheel(short local_player_index, const float left[3], const float right[3])
|
||||
{
|
||||
if (!vr.active || local_player_index != 0)
|
||||
return;
|
||||
vr.wheel_valid = left && right;
|
||||
if (!vr.wheel_valid)
|
||||
return;
|
||||
memcpy(vr.wheel_hands_drawn[0], left, sizeof(vr.wheel_hands_drawn[0]));
|
||||
memcpy(vr.wheel_hands_drawn[1], right, sizeof(vr.wheel_hands_drawn[1]));
|
||||
}
|
||||
|
||||
int halo_vr_wheel_held(void)
|
||||
{
|
||||
return vr.active && vr.wheel_hands != 0;
|
||||
}
|
||||
|
||||
/* The comfort vignette: while a vehicle turns the player (from 15 degrees a
|
||||
second, fully at 60), the eye images darken from their edges; it comes in
|
||||
quickly and goes slowly. */
|
||||
@@ -1043,6 +1192,7 @@ void halo_vr_frame_begin(void)
|
||||
vr_recentre();
|
||||
}
|
||||
turn_update();
|
||||
wheel_update();
|
||||
vignette_update();
|
||||
vehicle_spawn_update();
|
||||
look_update();
|
||||
@@ -1394,6 +1544,13 @@ int halo_vr_aim(short local_player_index, float current_yaw, float current_pitch
|
||||
amount = amount > 0.0f ? powf(amount > 1.0f ? 1.0f : amount, vr.vehicle_steer_curve) : 0.0f;
|
||||
if (stick[axis] < 0.0f)
|
||||
amount = -amount;
|
||||
if (axis == 0 && vr.wheel_hands)
|
||||
{
|
||||
/* (the hands on the wheel steer, as they turn it: clockwise
|
||||
to the right) */
|
||||
vr.steer[0] = -vr.wheel_turn / vr.vehicle_wheel_angle;
|
||||
continue;
|
||||
}
|
||||
vr.steer[axis] += (amount - vr.steer[axis]) * blend;
|
||||
}
|
||||
/* (a Ghost or a Banshee aims its guns as it steers: less, finer) */
|
||||
|
||||
@@ -540,12 +540,17 @@ In play, laid out for VR shooters rather than by position:
|
||||
right stick turns (vr.c); click zooms left stick move; click crouches
|
||||
menu pause (start)
|
||||
|
||||
Holding a Warthog's wheel (vr.c), the right trigger is the throttle and the
|
||||
left one the brake (backwards, once stopped): the left stick pushed up or
|
||||
down as far, unless it is pushed further.
|
||||
|
||||
In the menus the buttons are the Xbox's by position: A accepts, B goes back. */
|
||||
|
||||
#include "guest_host.h"
|
||||
#include "halo_xr_abi.h"
|
||||
|
||||
int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menus);
|
||||
int halo_vr_wheel_held(void);
|
||||
|
||||
static SHORT vr_stick(float value)
|
||||
{
|
||||
@@ -577,6 +582,14 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad)
|
||||
pad->bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] = (BYTE)(right->trigger * 255.0f);
|
||||
if ((BYTE)(left->trigger * 255.0f) > pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER])
|
||||
pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] = (BYTE)(left->trigger * 255.0f);
|
||||
if (!menus && halo_vr_wheel_held())
|
||||
{
|
||||
/* at the wheel: the triggers drive, and neither fire nor throw */
|
||||
pad->bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] = 0;
|
||||
pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] = 0;
|
||||
value = vr_stick(right->trigger - left->trigger);
|
||||
if (abs(value) > abs(pad->sThumbLY)) pad->sThumbLY = value;
|
||||
}
|
||||
if (menus)
|
||||
{
|
||||
/* the right stick scrolls too; turning is off in the menus */
|
||||
@@ -592,7 +605,9 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad)
|
||||
}
|
||||
merge_button(pad, XINPUT_GAMEPAD_A, (right->buttons & HALO_XR_BUTTON_A) != 0);
|
||||
merge_button(pad, XINPUT_GAMEPAD_X, (left->buttons & HALO_XR_BUTTON_A) != 0);
|
||||
merge_button(pad, XINPUT_GAMEPAD_B, (right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 || halo_vr_melee());
|
||||
/* (the grips holding a Warthog's wheel are not a blow) */
|
||||
merge_button(pad, XINPUT_GAMEPAD_B,
|
||||
((right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 && !halo_vr_wheel_held()) || halo_vr_melee());
|
||||
merge_button(pad, XINPUT_GAMEPAD_Y, (left->buttons & HALO_XR_BUTTON_B) != 0);
|
||||
{
|
||||
/* B: a tap is the flashlight, holding it switches grenades; the
|
||||
|
||||
@@ -667,8 +667,25 @@ static void render_vr_player_frame(
|
||||
long vehicle_index = unit_index == NONE ? NONE : object_get(unit_index)->object.parent_object_index;
|
||||
real_matrix4x3 *nodes = vehicle_index == NONE ? NULL : render_interpolation_object_node_matrices(vehicle_index);
|
||||
|
||||
real_point3d left_hand;
|
||||
real_point3d right_hand;
|
||||
|
||||
if (nodes)
|
||||
halo_vr_set_seat_frame(window->local_player_index, (float)atan2(nodes[0].forward.j, nodes[0].forward.i));
|
||||
if (nodes && render_object_vr_wheel_hands(unit_index, &left_hand, &right_hand))
|
||||
{
|
||||
/* (at a wheel: the hands that hold it) */
|
||||
float left[3] = { left_hand.x - game_position[0], left_hand.y - game_position[1],
|
||||
left_hand.z - game_position[2] };
|
||||
float right[3] = { right_hand.x - game_position[0], right_hand.y - game_position[1],
|
||||
right_hand.z - game_position[2] };
|
||||
|
||||
halo_vr_set_wheel(window->local_player_index, left, right);
|
||||
}
|
||||
else
|
||||
{
|
||||
halo_vr_set_wheel(window->local_player_index, NULL, NULL);
|
||||
}
|
||||
}
|
||||
if (halo_vr_multiview())
|
||||
{
|
||||
|
||||
@@ -154,6 +154,11 @@ boolean render_object_vr_seated_body(
|
||||
long object_index);
|
||||
boolean render_object_vr_at_controls(
|
||||
long object_index);
|
||||
/* at an open wheel (a Warthog's), where its drawn hands hold it */
|
||||
boolean render_object_vr_wheel_hands(
|
||||
long object_index,
|
||||
real_point3d *left,
|
||||
real_point3d *right);
|
||||
#endif
|
||||
void render_objects(
|
||||
void);
|
||||
|
||||
@@ -509,6 +509,47 @@ boolean render_object_vr_seated_body(
|
||||
vehicle_vr_encloses_riders(object->object.parent_object_index));
|
||||
}
|
||||
|
||||
/* at an open wheel (a Warthog's): where the driver's hands hold it, as the
|
||||
seat's animation has them this frame (vr.c: the player's own hands turn
|
||||
it) */
|
||||
boolean render_object_vr_wheel_hands(
|
||||
long object_index,
|
||||
real_point3d *left,
|
||||
real_point3d *right)
|
||||
{
|
||||
struct object_datum *object;
|
||||
struct model *model;
|
||||
real_matrix4x3 *nodes;
|
||||
short node_index;
|
||||
short left_index = NONE;
|
||||
short right_index = NONE;
|
||||
|
||||
if (!render_object_vr_seated_body(object_index))
|
||||
return FALSE;
|
||||
object = object_get(object_index);
|
||||
if (!TEST_FLAG(_object_mask_vehicle, object_get(object->object.parent_object_index)->object.type))
|
||||
return FALSE;
|
||||
model = model_definition_get(object_definition_get(object->definition_index)->object.model.index);
|
||||
nodes = render_interpolation_object_node_matrices(object_index);
|
||||
if (!nodes)
|
||||
nodes = object_get_node_matrices(object_index);
|
||||
for (node_index = 0; node_index < model->nodes.count; node_index++)
|
||||
{
|
||||
char const *name = TAG_BLOCK_GET_ELEMENT(&model->nodes, node_index, struct model_node)->name;
|
||||
size_t length = strlen(name);
|
||||
|
||||
if (length >= 6 && !strcmp(name + length - 6, "l hand"))
|
||||
left_index = node_index;
|
||||
if (length >= 6 && !strcmp(name + length - 6, "r hand"))
|
||||
right_index = node_index;
|
||||
}
|
||||
if (left_index == NONE || right_index == NONE)
|
||||
return FALSE;
|
||||
*left = nodes[left_index].position;
|
||||
*right = nodes[right_index].position;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* its node matrices with the head's shrunk to nothing, which takes the
|
||||
helmet out of the eyes' way */
|
||||
static real_matrix4x3 *object_vr_headless_node_matrices(
|
||||
|
||||
Reference in new issue
Block a user